diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..d570088 --- /dev/null +++ b/.npmignore @@ -0,0 +1,2 @@ +node_modules/ + diff --git a/CHANGES.md b/CHANGES.md index c3e06c0..908aa67 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,175 @@ +## 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 @@ -31,8 +203,8 @@ * 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('browserstack-webdriver') - chrome = require('browserstack-webdriver/chrome'); + var webdriver = require('selenium-webdriver') + chrome = require('selenium-webdriver/chrome'); // The following are equivalent. var driver1 = new webdriver.Builder().forBrowser('chrome').build(); @@ -45,7 +217,7 @@ * 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`, - respecitvely. + 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 @@ -135,7 +307,7 @@ ## v2.34.0 -* Added the `browserstack-webdriver/testing/assert` module. This module +* 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. @@ -157,7 +329,7 @@ build(); * Added support for configuring proxies through the builder. For examples, see - `browserstack-webdriver/test/proxy_test`. + `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 @@ -179,9 +351,9 @@ ## v2.32.0 -* Added the `browserstack-webdriver/testing` package, which provides a basic +* Added the `selenium-webdriver/testing` package, which provides a basic framework for writing tests using Mocha. See - `browserstack-webdriver/example/google_search_test.js` for usage. + `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 @@ -219,4 +391,4 @@ available separately) * Initial release for npm: - npm install browserstack-webdriver + npm install selenium-webdriver 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 698b613..c73a9d3 100644 --- a/README.md +++ b/README.md @@ -1,85 +1,380 @@ # 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). +## Usage + +Below 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 = { + 'browser' : 'firefox', + 'browserstack.user' : BROWSERSTACK_USERNAME, + 'browserstack.key' : BROWSERSTACK_KEY + } + var driver = new webdriver.Builder(). + 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.getTitle().then(function(title) { + console.log(title); + }); + driver.quit(); + +## Documentation + +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][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 + +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 -### Running the tests +http://www.apache.org/licenses/LICENSE-2.0 -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`. +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. - npm test browserstack-webdriver +[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 +# 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. -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: +## Installation - export SELENIUM_SERVER_JAR=path/to/selenium-server-standalone-2.33.0.jar - SELENIUM_BROWSER=chrome,firefox npm test browserstack-webdriver +Install via npm with + + npm install browserstack-webdriver ## Usage +Below 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 = { + 'browser' : 'firefox', + 'browserstack.user' : BROWSERSTACK_USERNAME, + 'browserstack.key' : BROWSERSTACK_KEY + } + var driver = new webdriver.Builder(). + 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.getTitle().then(function(title) { + console.log(title); + }); + driver.quit(); + +## Documentation + +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][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 + +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 +# 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. - var webdriver = require('browserstack-webdriver'); +## Installation + +Install via npm with + + npm install browserstack-webdriver + +## Usage - // Input capabilities - var capabilities = { - 'browserName' : 'firefox', +Below 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 = { + 'browser' : 'firefox', 'browserstack.user' : BROWSERSTACK_USERNAME, 'browserstack.key' : BROWSERSTACK_KEY } - var driver = new webdriver.Builder(). - usingServer('http://hub.browserstack.com/wd/hub'). - withCapabilities(capabilities). - build(); - + 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.getTitle().then(function(title) { console.log(title); - }); - + }); driver.quit(); +## Documentation + +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][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 + +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 +# 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 via npm with + + npm install browserstack-webdriver + +## Usage + +Below 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 = { + 'browser' : 'firefox', + 'browserstack.user' : BROWSERSTACK_USERNAME, + 'browserstack.key' : BROWSERSTACK_KEY + } + var driver = new webdriver.Builder(). + 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.getTitle().then(function(title) { + console.log(title); + }); + driver.quit(); ## Documentation -API documentation is included in the docs module. The API documentation for the -current release are also available online from the [Selenium project](http://selenium.googlecode.com/git/docs/api/javascript/index.html "API docs"). A full user guide is available on the -[Selenium project wiki](http://code.google.com/p/selenium/wiki/WebDriverJs "User guide"). +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://code.google.com/p/selenium/issues/list). +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 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. +[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 22b62a0..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 @@ -92,12 +94,21 @@ function Context(opt_configureForTesting) { require: require, Buffer: Buffer, Error: Error, + TypeError: TypeError, CLOSURE_BASE_PATH: path.dirname(CLOSURE_BASE_FILE_PATH) + '/', - CLOSURE_IMPORT_SCRIPT: function(src) { - loadScript(src); + 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; @@ -114,6 +125,16 @@ function Context(opt_configureForTesting) { 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. diff --git a/builder.js b/builder.js index a12369c..788e6f4 100644 --- a/builder.js +++ b/builder.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. var base = require('./_base'), executors = require('./executors'); @@ -24,30 +27,63 @@ var Browser = base.require('webdriver.Browser'), +var seleniumServer; + +/** + * 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. The environment * variables listed below may be used to override a builder's configuration, * allowing quick runtime changes. - *
    - *
  • {@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). * - *
  • {@code SELENIUM_BROWSER}: defines the target browser in the form + * - {@code SELENIUM_BROWSER}: defines the target browser in the form * {@code browser[:version][:platform]}. - *
* - *

Suppose you had mytest.js that created WebDriver with - * {@code var driver = new webdriver.Builder().build();}. + * - {@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 - * {@code SELENIUM_BROWSER=firefox node mytest.js}. + * `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 * - *

Alternatively, you could request Chrome 36 on Linux from a remote - * server with {@code - * 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 */ @@ -59,6 +95,9 @@ var Builder = function() { /** @private {string} */ this.url_ = ''; + /** @private {?string} */ + this.proxy_ = null; + /** @private {!webdriver.Capabilities} */ this.capabilities_ = new Capabilities(); @@ -67,6 +106,30 @@ var Builder = function() { /** @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; }; @@ -75,8 +138,8 @@ var Builder = function() { * 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 - * {@code SELENIUM_REMOTE_URL} environment variable. + * 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. @@ -96,6 +159,29 @@ Builder.prototype.getServerUrl = function() { }; +/** + * 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. @@ -124,9 +210,9 @@ Builder.prototype.getCapabilities = function() { * Any calls to {@link #withCapabilities} after this function will * overwrite these settings. * - *

You may also define the target browser using the {@code SELENIUM_BROWSER} + * You may also define the target browser using the {@code SELENIUM_BROWSER} * environment variable. If set, this environment variable should be of the - * form {@code browser[:[version][:platform]]}. + * 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. @@ -207,10 +293,10 @@ Builder.prototype.setAlertBehavior = function(behavior) { /** - * 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 {@link #setLoggingPrefs} and {@link #setProxy}, - * respectively. + * 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. @@ -222,10 +308,10 @@ Builder.prototype.setChromeOptions = function(options) { /** - * 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 {@link #setLoggingPrefs} and {@link #setProxy}, - * respectively. + * 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. @@ -236,6 +322,51 @@ Builder.prototype.setFirefoxOptions = function(options) { }; +/** + * 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 @@ -262,9 +393,9 @@ Builder.prototype.build = function() { // environment. var capabilities = new Capabilities(this.capabilities_); - var browser = process.env.SELENIUM_BROWSER; - if (browser) { - browser = browser.split(/:/, 3); + 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); @@ -272,24 +403,45 @@ Builder.prototype.build = function() { browser = capabilities.get(Capability.BROWSER_NAME) || capabilities.get(Capability.BROWSER); - if (!browser) { - throw Error( - 'Target browser not defined'); + 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()); - } - if (browser === Browser.FIREFOX && this.firefoxOptions_) { + } 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 = process.env.SELENIUM_REMOTE_URL || this.url_; + 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); + } + } + if (url) { - var executor = executors.createExecutor(url); + var executor = executors.createExecutor(url, this.proxy_); return WebDriver.createSession(executor, capabilities, this.flow_); } @@ -307,12 +459,30 @@ Builder.prototype.build = function() { 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 6a6a7de..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; }; @@ -176,6 +322,7 @@ ServiceBuilder.prototype.build = function() { 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); @@ -229,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); /** @@ -255,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)) { @@ -284,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; }; @@ -298,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; }; @@ -316,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; }; @@ -330,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; }; @@ -342,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; }; @@ -358,6 +539,36 @@ Options.prototype.setLoggingPrefs = function(prefs) { }; +/** + * 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. @@ -365,7 +576,87 @@ Options.prototype.setLoggingPrefs = 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; }; @@ -377,7 +668,61 @@ 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; }; @@ -415,52 +760,40 @@ 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. - * @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. - * @return {!webdriver.WebDriver} A new WebDriver instance. - * @deprecated Use {@link Driver new Driver()}. - */ -function createDriver(opt_options, opt_service, opt_flow) { - return new Driver(opt_options, opt_service, opt_flow); -} - - /** * 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. + * 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 @@ -468,7 +801,7 @@ function createDriver(opt_options, opt_service, opt_flow) { */ 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 capabilities = opt_config instanceof Options ? opt_config.toCapabilities() : @@ -483,12 +816,33 @@ var Driver = function(opt_config, opt_service, 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() { +}; + + +/** + * 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.Driver = Driver; exports.Options = Options; exports.ServiceBuilder = ServiceBuilder; -exports.createDriver = createDriver; exports.getDefaultService = getDefaultService; exports.setDefaultService = setDefaultService; diff --git a/docs/Changes.html b/docs/Changes.html new file mode 100644 index 0000000..70f4cff --- /dev/null +++ b/docs/Changes.html @@ -0,0 +1,317 @@ +Changes

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 fa876ec..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_webdriver_ActionSequence.html b/docs/class_webdriver_ActionSequence.html index 084051b..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 581819e..398446e 100644 --- a/docs/class_webdriver_Alert.html +++ b/docs/class_webdriver_Alert.html @@ -1,12 +1,24 @@ -webdriver.Alert

Class webdriver.Alert

code »

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
The message text displayed with this alert.
Show:

Instance Methods

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.

Instance 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 index d3793cb..cc5e3f3 100644 --- a/docs/class_webdriver_AlertPromise.html +++ b/docs/class_webdriver_AlertPromise.html @@ -1,20 +1,91 @@ -webdriver.AlertPromise

Class webdriver.AlertPromise

code »
webdriver.Alert
-  └ webdriver.AlertPromise
All implemented interfaces:
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();
-     });
- 

Constructor

webdriver.AlertPromise ( driver, alert )
Parameters
driver: !webdriver.WebDriver
The driver controlling the browser this - alert is attached to.
alert: !webdriver.promise.Thenable
A thenable - that will be fulfilled with the promised alert.
Show:

Instance Methods

Defined in webdriver.AlertPromise

code »isPending ( )boolean
code »then ( )webdriver.promise.Promise
code »thenCatch ( )webdriver.promise.Promise
code »thenFinally ( )webdriver.promise.Promise

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.

Instance Properties

Defined in webdriver.Alert

Static Properties

\ No newline at end of file +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_Capabilities.html b/docs/class_webdriver_Capabilities.html index ee5d1f1..e301d52 100644 --- a/docs/class_webdriver_Capabilities.html +++ b/docs/class_webdriver_Capabilities.html @@ -1,15 +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.

Sets the default action to take with an unexpected alert before returning - an error.

Parameters
behavior: string
The desired behavior; should be "accept", "dismiss", - or "ignore". Defaults to "dismiss".
Returns
A self reference.

Sets whether native events should be used.

Parameters
enabled: boolean
Whether to enable native events.
Returns
A self reference.

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
A self reference.

Sets the proxy configuration for this instance.

Parameters
proxy: webdriver.ProxyConfig
The desired proxy configuration.
Returns
A self reference.

Sets how elements should be scrolled into view for interaction.

Parameters
behavior: number
The desired scroll behavior: either 0 to align with - the top of the viewport or 1 to align with the bottom.
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 2b7442b..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 411f192..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_Locator.html b/docs/class_webdriver_Locator.html index 0b349d8..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 220d7a5..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 f3f06da..e2ea0ca 100644 --- a/docs/class_webdriver_UnhandledAlertError.html +++ b/docs/class_webdriver_UnhandledAlertError.html @@ -1,7 +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, text, alert )
Parameters
message: string
The error message.
text: string
The text displayed with the unhandled alert.
alert: !webdriver.Alert
The alert handle.
Show:

Instance Methods

Defined in webdriver.UnhandledAlertError

Deprecated: Use #getAlertText. This method will be removed in - 2.45.0.
Returns
The open alert.
Returns
The text displayed with the unhandled 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 8d793ce..9d778c4 100644 --- a/docs/class_webdriver_WebDriver.html +++ b/docs/class_webdriver_WebDriver.html @@ -1,265 +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 »<T> call ( fn, opt_scope, var_args )!webdriver.promise.Promise.<T>

Schedules a command to execute a custom function.

Parameters
fn: function(...): (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
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.
code »<T> executeAsyncScript ( script, var_args )!webdriver.promise.Promise.<T>

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.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
A promise that will resolve to the - scripts return value.
code »<T> executeScript ( script, var_args )!webdriver.promise.Promise.<T>

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 fulfilled with the located element, or null if the element - could not be found.

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 »<T> schedule ( command, description )!webdriver.promise.Promise.<T>

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. - -

In the event a condition returns a webdriver.promise.Promise, the - polling loop will wait for it to be resolved and use the resolved value for - evaluating whether the condition has been satisfied. The resolution time for - a promise is factored into whether a wait has timed out.

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, opt_flow )!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.
opt_flow: webdriver.promise.ControlFlow=
The control flow all driver - commands should execute under. Defaults to the - currently active control flow.
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.
opt_flow: webdriver.promise.ControlFlow=
The control flow all driver - commands should execute under. Defaults to the - currently active control flow.
Returns
A new client for the specified session.
code »webdriver.WebDriver.createSession ( executor, desiredCapabilities, opt_flow )!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.
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
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 is a WebElement, the return value will be the element's - server ID
  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 8c5295b..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 864fb17..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 f302566..6e0cc55 100644 --- a/docs/class_webdriver_WebDriver_Options.html +++ b/docs/class_webdriver_WebDriver_Options.html @@ -1,17 +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:

Type Definitions

A JSON description of a browser cookie.

Instance Methods

code »addCookie ( name, value, opt_path, opt_domain, opt_isSecure, opt_expiry )!webdriver.promise.Promise.<void>

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 65817d5..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_SUCH_ALERT error if an alert 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.
code »frame ( nameOrIndex )!webdriver.promise.Promise.<void>

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.
code »window ( nameOrHandle )!webdriver.promise.Promise.<void>

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 2f074f0..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 b06f8cc..7d85985 100644 --- a/docs/class_webdriver_WebDriver_Window.html +++ b/docs/class_webdriver_WebDriver_Window.html @@ -1,11 +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.
code »setSize ( width, height )!webdriver.promise.Promise.<void>

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 f043537..283d113 100644 --- a/docs/class_webdriver_WebElement.html +++ b/docs/class_webdriver_WebElement.html @@ -1,158 +1,216 @@ -webdriver.WebElement

Class webdriver.WebElement

code »

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: !(webdriver.promise.Promise.<webdriver.WebElement.Id>|webdriver.WebElement.Id)
The server-assigned opaque ID for the - underlying DOM element.
Show:

Type Definitions

Wire protocol definition of a WebElement ID.

Instance Methods

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.
Returns
A promise - that resolves to this element's JSON representation as defined by the - WebDriver wire protocol.

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 »<T> schedule_ ( command, description )!webdriver.promise.Promise.<T>

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.

Instance Properties

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 index 6c6f9aa..136de10 100644 --- a/docs/class_webdriver_WebElementPromise.html +++ b/docs/class_webdriver_WebElementPromise.html @@ -1,147 +1,266 @@ -webdriver.WebElementPromise

Class webdriver.WebElementPromise

code »
webdriver.WebElement
-  └ webdriver.WebElementPromise
All implemented interfaces:
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();
-     });
- 

Constructor

webdriver.WebElementPromise ( driver, el )
Parameters
driver: !webdriver.WebDriver
The parent WebDriver instance for this - element.
el: !webdriver.promise.Promise
A promise - that will resolve to the promised element.
Show:

Instance Methods

Defined in webdriver.WebElementPromise

code »isPending ( )boolean

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. +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);
 
- 

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'});
- 
+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);
+}
 
- 

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);
+// 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();
+}
 
- 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: +// 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
+}
 
- 

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.
Returns
A promise - that resolves to this element's JSON representation as defined by the - WebDriver wire protocol.

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 »<T> schedule_ ( command, description )!webdriver.promise.Promise.<T>

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.

Instance Properties

Defined in webdriver.WebElement

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

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_Executor.html b/docs/class_webdriver_http_Executor.html index 6082a43..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 d561997..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 6c4b1d4..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_logging_Entry.html b/docs/class_webdriver_logging_Entry.html index dc38ad5..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 index cc633e8..baccf72 100644 --- a/docs/class_webdriver_logging_Preferences.html +++ b/docs/class_webdriver_logging_Preferences.html @@ -1,2 +1,8 @@ -webdriver.logging.Preferences

Class webdriver.logging.Preferences

code »

Describes the log preferences for a WebDriver session.

Constructor

webdriver.logging.Preferences ( )
Show:

Instance Methods

code »setLevel ( type, level )

Sets the desired logging level for a particular log type.

Parameters
type: (string|webdriver.logging.Type)
The log type.
level: !webdriver.logging.Level
The desired log level.

Converts this instance to its JSON representation.

Returns
The JSON representation of this set of - preferences.

Instance Properties

\ No newline at end of file +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_CancellationError.html b/docs/class_webdriver_promise_CancellationError.html new file mode 100644 index 0000000..535bc28 --- /dev/null +++ b/docs/class_webdriver_promise_CancellationError.html @@ -0,0 +1,18 @@ +CancellationError

class CancellationError

final
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. Will trivially return +the error itself if it is an instanceof 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 872a5ab..e8df80c 100644 --- a/docs/class_webdriver_promise_ControlFlow.html +++ b/docs/class_webdriver_promise_ControlFlow.html @@ -1,119 +1,109 @@ -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.

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 »<T> execute ( fn, opt_description )!webdriver.promise.Promise.<T>

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
fn: function(): (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_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

final
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.

+

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.

+

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.

+

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.

+

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 430ae0c..ab17f12 100644 --- a/docs/class_webdriver_promise_Deferred.html +++ b/docs/class_webdriver_promise_Deferred.html @@ -1,27 +1,84 @@ -webdriver.promise.Deferred

Class webdriver.promise.Deferred.<T>

code »
webdriver.promise.Promise.<(T|null)>
-  └ webdriver.promise.Deferred
All implemented interfaces:
webdriver.promise.Thenable.<(T|null)>

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: 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.<(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.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 rejected state, at which point the promise is considered - resolved.

Static Properties

\ No newline at end of file +// 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

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_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_Promise.html b/docs/class_webdriver_promise_Promise.html index 691829b..7a18e13 100644 --- a/docs/class_webdriver_promise_Promise.html +++ b/docs/class_webdriver_promise_Promise.html @@ -1,5 +1,74 @@ -webdriver.promise.Promise

Class webdriver.promise.Promise.<T>

code »
All implemented interfaces:
webdriver.promise.Thenable.<(T|null)>

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 rejected state, at which point the promise is considered - resolved.

Constructor

webdriver.promise.Promise ( )
Show:

Instance Methods

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
\ No newline at end of file +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);
+}
+
+// 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.

+

toString()code »

Returns
string
\ No newline at end of file diff --git a/docs/class_webdriver_stacktrace_Frame.html b/docs/class_webdriver_stacktrace_Frame.html index 7ca7c92..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 479f5d7..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 0a27496..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 1b31176..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 3de5f5f..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 cf528bd..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_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 ba88c19..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");if((b=ub(Bb,{types:b,I:ie,ya:!!c}))&&b.childNodes.length)return ob(d),a.appendChild(b),b;sc(a,!1);return 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 c5bffc4..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 7b80a02..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_webdriver_Browser.html b/docs/enum_webdriver_Browser.html index 37a7bb6..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 8d5524f..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 a6bbb7f..6a15c11 100644 --- a/docs/enum_webdriver_Capability.html +++ b/docs/enum_webdriver_Capability.html @@ -1,16 +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.
    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 webdriver.Capability.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 - webdriver.Capability.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.
    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 5449a9d..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_Key.html b/docs/enum_webdriver_Key.html index afa2867..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_Type.html b/docs/enum_webdriver_logging_Type.html index c4657a1..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 f0bb243..e5bc00a 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.
    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.
    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/index.html b/docs/index.html index e822862..64bb6d9 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,62 +1,123 @@ -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.

    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

    -

    API documentation is included in the docs module. The API documentation for the -current release are also available online from the Selenium project. A full user guide is available on the -Selenium project wiki.

    +

    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_webdriver_CommandExecutor.html b/docs/interface_webdriver_CommandExecutor.html index d70a6bc..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 7d7ee72..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 index aef12e5..6edeec3 100644 --- a/docs/interface_webdriver_promise_Thenable.html +++ b/docs/interface_webdriver_promise_Thenable.html @@ -1,65 +1,77 @@ -webdriver.promise.Thenable

    Interface webdriver.promise.Thenable.<T>

    code »

    Thenable is a promise-like object with a then method which may be - used to schedule callbacks on a promised value.

    Show:

    Instance Methods

    code »cancel ( opt_reason )

    Cancels the computation of this promise's value, rejecting the promise in the - process. This method is a no-op if the promise has alreayd been resolved.

    Parameters
    opt_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 »<R> then ( opt_callback, opt_errback )!webdriver.promise.Promise.<R>

    Registers listeners for when this instance is resolved.

    Parameters
    opt_callback: ?(function(T): (R|webdriver.promise.Promise.<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|webdriver.promise.Promise.<R>))=
    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);
    -   }
    +Thenable

    interface Thenable<T>

    All extended interfaces
    IThenable<T>

    Thenable is a promise-like object with a then method which may be +used to schedule callbacks on a promised value.

    +

    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
    errback: function(*): (R|webdriver.promise.Promise.<R>)
    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().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);
    - 
    +// 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
    +}
     
    - 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: function(): (R|webdriver.promise.Promise.<R>)
    The function - to call when this promise is resolved.
    Returns
    A promise that will be fulfilled - with the callback result.

    Global Functions

    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
    ctor: function(new: webdriver.promise.Thenable, ?)
    The - constructor whose prototype to modify.

    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
    Whether the object is an implementation of the Thenable - interface.

    Global Properties

    Property used to flag constructor's as implementing the Thenable interface - for runtime type checking.

    \ No newline at end of file +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/module_selenium-webdriver.html b/docs/module_selenium-webdriver.html index 6cee24a..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.
    WebElementPromise
    WebElementPromise is a promise that will be fulfilled with a WebElement.

    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 486a8fe..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.

    Classes

    Context
    Maintains a unique context for Closure library-based code.
    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 index 7737dc3..03304a3 100644 --- a/docs/module_selenium-webdriver__base_class_Context.html +++ b/docs/module_selenium-webdriver__base_class_Context.html @@ -1,3 +1,5 @@ -Context

    Class Context

    code »

    Maintains a unique context for Closure library-based code.

    Constructor

    Context ( opt_configureForTesting )
    Parameters
    opt_configureForTesting: boolean=
    Whether to configure a fake DOM - for Closure-testing code that (incorrectly) assumes a DOM is always - present.
    Show:

    Instance Properties

    \ No newline at end of file +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 1eb7c30..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 fdf32f7..33a1d06 100644 --- a/docs/module_selenium-webdriver_builder_class_Builder.html +++ b/docs/module_selenium-webdriver_builder_class_Builder.html @@ -1,56 +1,138 @@ -Builder

    Class Builder

    code »

    Creates new WebDriver instances. The environment - variables listed below may be used to override a builder's configuration, - allowing quick runtime changes. -

      -
    • 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). - -
    • SELENIUM_BROWSER: defines the target browser in the form - browser[:version][:platform]. -
    - -

    Suppose you had mytest.js that created WebDriver with - var driver = new webdriver.Builder().build();. - - This test could be made to use Firefox on the local machine by running with - SELENIUM_BROWSER=firefox node mytest.js. - -

    Alternatively, you could request Chrome 36 on Linux from a remote - server with SELENIUM_BROWSER=chrome:36:LINUX - SELENIUM_REMOTE_URL=http://www.example.com:4444/wd/hub - node mytest.js.

    Constructor

    Builder ( )
    Show:

    Instance Methods

    Creates a new WebDriver client based on this builder's current - configuration.

    Returns
    A new WebDriver instance.
    Throws
    Error
    If the current configuration is invalid.
    code »forBrowser ( name, opt_version, opt_platform )!Builder

    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
    name: (string|webdriver.Browser)
    The name of the target browser; - common defaults are available on the webdriver.Browser enum.
    opt_version: string=
    A desired version; may be omitted if any - version should be used.
    opt_platform: string=
    The desired platform; may be omitted if any - version may be used.
    Returns
    A self reference.

    Returns the base set of capabilities this instance is currently configured - to use.

    Returns
    The current capabilities for this builder.
    Returns
    The URL of the WebDriver server this instance is configured - to use.
    code »setAlertBehavior ( behavior )!Builder

    Sets the default action to take with an unexpected alert before returning - an error.

    Parameters
    behavior
    Returns
    A self reference.
    code »setChromeOptions ( options )!Builder

    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
    options: !chrome.Options
    The ChromeDriver options to use.
    Returns
    A self reference.
    code »setControlFlow ( flow )!Builder

    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
    flow: webdriver.promise.ControlFlow
    The control flow to use, or - null to
    Returns
    A self reference.
    code »setEnableNativeEvents ( enabled )!Builder

    Sets whether native events should be used.

    Parameters
    enabled: boolean
    Whether to enable native events.
    Returns
    A self reference.
    code »setFirefoxOptions ( options )!Builder

    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
    options: !firefox.Options
    The FirefoxDriver options to use.
    Returns
    A self reference.
    code »setLoggingPrefs ( prefs )!Builder

    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
    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: !webdriver.ProxyConfig
    The configuration to use.
    Returns
    A self reference.
    code »setScrollBehavior ( behavior )!Builder

    Sets how elements should be scrolled into view for interaction.

    Parameters
    behavior: number
    The desired scroll behavior: either 0 to align with - the top of the viewport or 1 to align with the bottom.
    Returns
    A self reference.
    code »usingServer ( url )!Builder

    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
    url: string
    The URL of a remote server to use.
    Returns
    A self reference.
    code »withCapabilities ( capabilities )!Builder

    Sets the desired capabilities when requesting a new session. This will - overwrite any previously set capabilities.

    Parameters
    capabilities: !(Object|webdriver.Capabilities)
    The desired - capabilities for a new session.
    Returns
    A self reference.

    Instance Properties

    code »chromeOptions_ : chrome.Options
    code »firefoxOptions_ : firefox.Options
    \ 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 7ceb111..ce0ec26 100644 --- a/docs/module_selenium-webdriver_chrome.html +++ b/docs/module_selenium-webdriver_chrome.html @@ -1,6 +1,90 @@ -selenium-webdriver/chrome

    Module selenium-webdriver/chrome

    code »

    Classes

    Driver
    Creates a new WebDriver client for Chrome.
    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, opt_flow )!webdriver.WebDriver
    Deprecated: Use new Driver().

    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.
    opt_flow: webdriver.promise.ControlFlow=
    The control flow to use, or - null to use the currently active flow.
    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 index b0db80c..61b84dc 100644 --- a/docs/module_selenium-webdriver_chrome_class_Driver.html +++ b/docs/module_selenium-webdriver_chrome_class_Driver.html @@ -1,5 +1,280 @@ -Driver

    Class Driver

    code »
    WebDriver
    -  └ Driver

    Creates a new WebDriver client for Chrome.

    Constructor

    Driver ( opt_config, opt_service, opt_flow )
    Parameters
    opt_config: (webdriver.Capabilities|Options)=
    The configuration - options.
    opt_service: remote.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.
    Show:

    Instance Properties

    \ No newline at end of file +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 61a188e..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 »setLoggingPrefs ( 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: webdriver.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 a53eee9..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..d7119f7 --- /dev/null +++ b/docs/module_selenium-webdriver_class_WebElementPromise.html @@ -0,0 +1,266 @@ +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 541d184..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 7e32da1..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 index 04eec6d..da78326 100644 --- a/docs/module_selenium-webdriver_firefox.html +++ b/docs/module_selenium-webdriver_firefox.html @@ -1 +1,67 @@ -selenium-webdriver/firefox

    Module selenium-webdriver/firefox

    code »

    Classes

    Binary
    Manages a Firefox subprocess configured 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.
    Show:
    \ No newline at end of file +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

    Manages a Firefox subprocess configured 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 index 1c71b16..e59f7fb 100644 --- a/docs/module_selenium-webdriver_firefox_binary.html +++ b/docs/module_selenium-webdriver_firefox_binary.html @@ -1 +1,4 @@ -selenium-webdriver/firefox/binary

    Module selenium-webdriver/firefox/binary

    code »

    Classes

    Binary
    Manages a Firefox subprocess configured for use with WebDriver.
    Show:
    \ No newline at end of file +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

    Manages a Firefox subprocess configured 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 index 207146a..96ba5d1 100644 --- a/docs/module_selenium-webdriver_firefox_binary_class_Binary.html +++ b/docs/module_selenium-webdriver_firefox_binary_class_Binary.html @@ -1,4 +1,21 @@ -Binary

    Class Binary

    code »

    Manages a Firefox subprocess configured for use with WebDriver.

    Constructor

    Binary ( opt_exe )
    Parameters
    opt_exe: string=
    Path to the Firefox binary to use. If not - specified, will attempt to locate Firefox on the current system.
    Show:

    Instance Methods

    code »addArguments ( var_args )

    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.
    code »kill ( )!promise.Promise

    Kills the managed Firefox process.

    Returns
    A promise for when the process has terminated.
    code »launch ( profile )!promise.Promise

    Launches Firefox and eturns a promise that will be fulfilled when the process - terminates.

    Parameters
    profile: string
    Path to the profile directory to use.
    Returns
    A promise for the process result.
    Throws
    Error
    If this instance has already been started.

    Instance Properties

    code »command_ : promise.Promise
    \ No newline at end of file +Binary

    class Binary

    webdriver.Serializable
    +  └ Binary

    Manages a Firefox subprocess configured 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.

    +

    kill()code »

    Kills the managed Firefox process.

    +
    Returns
    webdriver.promise.Promise

    A promise for when the process has terminated.

    +

    launch(profile)code »

    Launches Firefox and eturns 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 process result.

    +
    Throws
    Error

    If this instance has already been started.

    +

    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..7050e61 --- /dev/null +++ b/docs/module_selenium-webdriver_firefox_class_Binary.html @@ -0,0 +1,21 @@ +Binary

    class Binary

    webdriver.Serializable
    +  └ Binary
    Alias for module(selenium-webdriver/firefox/binary).Binary

    Manages a Firefox subprocess configured 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.

    +

    kill()code »

    Kills the managed Firefox process.

    +
    Returns
    webdriver.promise.Promise

    A promise for when the process has terminated.

    +

    launch(profile)code »

    Launches Firefox and eturns 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 process result.

    +
    Throws
    Error

    If this instance has already been started.

    +

    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 index c53f20c..b9d62c9 100644 --- a/docs/module_selenium-webdriver_firefox_class_Driver.html +++ b/docs/module_selenium-webdriver_firefox_class_Driver.html @@ -1,6 +1,276 @@ -Driver

    Class Driver

    code »
    WebDriver
    -  └ Driver

    A WebDriver client for Firefox.

    Constructor

    Driver ( opt_config, opt_flow )
    Parameters
    opt_config: (Options|webdriver.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.
    Show:

    Instance Properties

    \ No newline at end of file +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 index 6f08ef1..7dcd2b8 100644 --- a/docs/module_selenium-webdriver_firefox_class_Options.html +++ b/docs/module_selenium-webdriver_firefox_class_Options.html @@ -1,4 +1,19 @@ -Options

    Class Options

    code »

    Configuration options for the FirefoxDriver.

    Constructor

    Options ( )
    Show:

    Instance Methods

    code »setBinary ( binary )!Options

    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
    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 »setProfile ( profile )!Options

    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
    A self reference.
    code »setProxy ( proxy )!Options

    Sets the proxy to use.

    Parameters
    proxy: webdriver.ProxyConfig
    The proxy configuration to use.
    Returns
    A self reference.

    Converts these options to a webdriver.Capabilities instance.

    Parameters
    opt_remote
    Returns
    A new capabilities object.

    Instance Properties

    code »profile_ : Profile
    \ No newline at end of file +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..6f954fc --- /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 index d905010..a505e00 100644 --- a/docs/module_selenium-webdriver_firefox_extension.html +++ b/docs/module_selenium-webdriver_firefox_extension.html @@ -1,3 +1,8 @@ -selenium-webdriver/firefox/extension

    Module selenium-webdriver/firefox/extension

    code »

    Utilities for working with Firefox extensions.

    Show:

    Type Definitions

    code »AddonDetails : {id: string, name: string, version: string, unpack: boolean}
    Describes a Firefox add-on.

    Functions

    code »install ( extension, dir )!promise.Promise.<string>

    Installs an extension to the given directory.

    Parameters
    extension: string
    Path to the extension to install, as either a xpi - file or a directory.
    dir: string
    Path to the directory to install the extension in.
    Returns
    A promise for the add-on ID once - installed.
    \ No newline at end of file +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 index 83257df..9d07abb 100644 --- a/docs/module_selenium-webdriver_firefox_profile.html +++ b/docs/module_selenium-webdriver_firefox_profile.html @@ -1,4 +1,13 @@ -selenium-webdriver/firefox/profile

    Module selenium-webdriver/firefox/profile

    code »

    Classes

    Profile
    Models a Firefox proifle directory for use with the FirefoxDriver.
    Show:

    Functions

    code »decode ( data )!promise.Promise.<string>

    Decodes a base64 encoded profile.

    Parameters
    data: string
    The base64 encoded string.
    Returns
    A promise for the path to the decoded - profile directory.
    code »loadUserPrefs ( f )!promise.Promise

    Parses a user.js file in a Firefox profile directory.

    Parameters
    f: string
    Path to the file to parse.
    Returns
    A promise for the parsed preferences as - a JSON object. If the file does not exist, an empty object will be - returned.
    \ No newline at end of file +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 index 7083b16..46c45d6 100644 --- a/docs/module_selenium-webdriver_firefox_profile_class_Profile.html +++ b/docs/module_selenium-webdriver_firefox_profile_class_Profile.html @@ -1,18 +1,50 @@ -Profile

    Class Profile

    code »

    Models a Firefox proifle directory for use with the FirefoxDriver. The - Proifle directory uses an in-memory model until #writeToDisk - is called.

    Constructor

    Profile ( opt_dir )
    Parameters
    opt_dir: string=
    Path to an existing Firefox profile directory to - use a template for this profile. If not specified, a blank profile will - be used.
    Show:

    Instance Methods

    Returns
    Whether the FirefoxDriver is configured to automatically - accept untrusted SSL certificates.
    code »addExtension ( extension )

    Registers an extension to be included with this profile.

    Parameters
    extension: string
    Path to the extension to include, as either an - unpacked extension directory or the path to a xpi file.
    Returns
    Whether to assume untrusted certs come from untrusted - issuers.
    code »encode ( )!promise.Promise.<string>

    Encodes this profile as a zipped, base64 encoded directory.

    Returns
    A promise for the encoded profile.
    Returns
    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.

    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
    key: string
    The desired preference.
    Returns
    The current value of the - requested preference.

    Returns whether native events are enabled in this profile.

    Returns
    .

    Sets whether the FirefoxDriver should automatically accept untrusted SSL - certificates.

    Parameters
    value: boolean
    .

    Sets whether to assume untrusted certificates come from untrusted issuers.

    Parameters
    value: boolean
    .

    Sets whether to use native events with this profile.

    Parameters
    enabled: boolean
    .

    Sets the port to use for the WebDriver extension loaded by this profile.

    Parameters
    port: number
    The desired port, or 0 to use any free port.
    code »setPreference ( key, value )

    Sets a desired preference for this profile.

    Parameters
    key: string
    The preference key.
    value: (string|number|boolean)
    The preference value.
    Throws
    Error
    If attempting to set a frozen preference.
    code »writeToDisk ( opt_excludeWebDriverExt )!promise.Promise.<string>

    Writes this profile to disk.

    Parameters
    opt_excludeWebDriverExt: boolean=
    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
    A promise for the path to the new - profile directory.

    Instance Properties

    \ No newline at end of file +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 6f3a09e..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..95f2c00 --- /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 e438a1f..e6bf828 100644 --- a/docs/module_selenium-webdriver_http_class_HttpClient.html +++ b/docs/module_selenium-webdriver_http_class_HttpClient.html @@ -1,3 +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, opt_agent )
    Parameters
    serverUrl: string
    URL for the WebDriver server to send commands to.
    opt_agent: http.Agent=
    The agent to use for each request. - Defaults to http.globalAgent.
    Show:

    Instance Methods

    code »send ( httpRequest, callback )
    Parameters
    httpRequest
    callback

    Instance Properties

    code »agent_ : http.Agent

    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..615b730 --- /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..54249a6 --- /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 30483d3..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 b4a26e4..78f6b5e 100644 --- a/docs/module_selenium-webdriver_io.html +++ b/docs/module_selenium-webdriver_io.html @@ -1,9 +1,37 @@ -selenium-webdriver/io

    Module selenium-webdriver/io

    code »
    Show:

    Functions

    code »copy ( src, dst )!promise.Promise.<string>

    Copies one file to another.

    Parameters
    src: string
    The source file.
    dst: string
    The destination file.
    Returns
    A promise for the copied file's path.
    code »copyDir ( src, dst, opt_exclude )!promise.Promise.<string>

    Recursively copies the contents of one directory to another.

    Parameters
    src: string
    The source directory to copy.
    dst: string
    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
    A promise for the destination - directory's path once all files have been copied.
    code »exists ( path )!promise.Promise.<boolean>

    Tests if a file path exists.

    Parameters
    path: string
    The path to test.
    Returns
    A promise for whether the file exists.
    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.
    code »tmpDir ( )!promise.Promise.<string>
    Returns
    A promise for the path to a temporary - directory.
    code »tmpFile ( )!promise.Promise.<string>
    Returns
    A promise for the path to a temporary - file.
    \ 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 index 9049294..db2facd 100644 --- a/docs/module_selenium-webdriver_io_exec.html +++ b/docs/module_selenium-webdriver_io_exec.html @@ -1,10 +1,6 @@ -selenium-webdriver/io/exec

    Module selenium-webdriver/io/exec

    code »

    Main

    exec ( command, opt_options )!Command
    Parameters
    command: string
    The executable to spawn.
    opt_options: Options=
    The command options.
    Returns
    The launched command.
    Show:

    Type Definitions

    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. -
    \ No newline at end of file +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..7537593 --- /dev/null +++ b/docs/module_selenium-webdriver_namespace_promise.html @@ -0,0 +1,162 @@ +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.

    +
    Promise

    Represents the eventual value of a completed operation.

    +
    Thenable

    Thenable is a promise-like object with a then method which may be +used to schedule callbacks on a promised value.

    +
    \ 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 ecc2a59..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 bdca435..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 2f4a828..a43f7d7 100644 --- a/docs/module_selenium-webdriver_phantomjs.html +++ b/docs/module_selenium-webdriver_phantomjs.html @@ -1,2 +1,2 @@ -selenium-webdriver/phantomjs

    Module selenium-webdriver/phantomjs

    code »

    Classes

    Driver
    Creates a new WebDriver client for PhantomJS.
    Show:

    Functions

    code »createDriver ( opt_capabilities, opt_flow )!webdriver.WebDriver
    Deprecated: Use Driver.

    Creates a new PhantomJS WebDriver client.

    Parameters
    opt_capabilities: webdriver.Capabilities=
    The desired capabilities.
    opt_flow: webdriver.promise.ControlFlow=
    The control flow to use, or - null to use the currently active flow.
    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 index 2ad20b1..69b5e42 100644 --- a/docs/module_selenium-webdriver_phantomjs_class_Driver.html +++ b/docs/module_selenium-webdriver_phantomjs_class_Driver.html @@ -1,3 +1,299 @@ -Driver

    Class Driver

    code »
    WebDriver
    -  └ Driver

    Creates a new WebDriver client for PhantomJS.

    Constructor

    Driver ( opt_capabilities, opt_flow )
    Parameters
    opt_capabilities: webdriver.Capabilities=
    The desired capabilities.
    opt_flow: webdriver.promise.ControlFlow=
    The control flow to use, or - null to use the currently active flow.
    Show:

    Instance Properties

    Defined in Driver

    Defined in WebDriver

    \ No newline at end of file +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 af44d59..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:

    Functions

    Configures WebDriver to bypass all browser proxies.

    Returns
    A new proxy configuration object.

    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.

    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.

    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 f0ab6ae..73a0e92 100644 --- a/docs/module_selenium-webdriver_remote.html +++ b/docs/module_selenium-webdriver_remote.html @@ -1,19 +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. -
      -
    • -
    • 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. -
    \ 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 7656f1f..d5c56e7 100644 --- a/docs/module_selenium-webdriver_remote_class_DriverService.html +++ b/docs/module_selenium-webdriver_remote_class_DriverService.html @@ -1,21 +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 process is still running. This does not take - into account whether the process is in the process of shutting down.

    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.
    code »start ( opt_timeoutMs )!promise.Promise.<string>

    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

    code »address_ : promise.Promise.<string>

    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.

    code »command_ : promise.Promise

    A promise for the managed subprocess, or null if the server has not been - started yet. This promise will never be rejected.

    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), 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..0bb166f --- /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 951e0eb..9576f9c 100644 --- a/docs/module_selenium-webdriver_remote_class_SeleniumServer.html +++ b/docs/module_selenium-webdriver_remote_class_SeleniumServer.html @@ -1,33 +1,40 @@ -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 process is still running. This does not take - into account whether the process is in the process of shutting down.

    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.
    code »start ( opt_timeoutMs )!promise.Promise.<string>

    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

    code »address_ : promise.Promise.<string>

    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.

    code »command_ : promise.Promise

    A promise for the managed subprocess, or null if the server has not been - started yet. This promise will never be rejected.

    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), 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), port: (number|webdriver.promise.Promise), stdio: (string|Array<?(string|number|Stream)>|undefined)}

    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.
    +
    \ 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 736cd9a..bb12638 100644 --- a/docs/module_selenium-webdriver_testing.html +++ b/docs/module_selenium-webdriver_testing.html @@ -1,73 +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'),
    -     portprober = require('selenium-webdriver/net/portprober'),
    -     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(
    -         'path/to/selenium-server-standalone.jar',
    -         {port: portprober.findFreePort()});
    -     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 022374c..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_bot.html b/docs/namespace_bot.html index b31a7eb..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 589e154..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 e41e031..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 817bcc2..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_webdriver.html b/docs/namespace_webdriver.html index 6c6332f..562a2c6 100644 --- a/docs/namespace_webdriver.html +++ b/docs/namespace_webdriver.html @@ -1,5 +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.AlertPromise
    AlertPromise is a promise that will be fulfilled with an Alert.
    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.
    webdriver.WebElementPromise
    WebElementPromise is a promise that will be fulfilled with a WebElement.

    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:

    Type Definitions

    code »webdriver.ProxyConfig : ({proxyType: string}|{proxyType: string, proxyAutoconfigUrl: string}|{proxyType: string, ftpProxy: string, httpProxy: string, sslProxy: string, noProxy: 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 +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 297c445..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 20a60aa..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 2fec207..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.
    webdriver.logging.Preferences
    Describes the log preferences for a WebDriver session.

    Enumerations

    webdriver.logging.Level
    Logging levels.
    webdriver.logging.Type
    Common log types.
    Show:

    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_promise.html b/docs/namespace_webdriver_promise.html index 3533da8..e172abd 100644 --- a/docs/namespace_webdriver_promise.html +++ b/docs/namespace_webdriver_promise.html @@ -1,112 +1,165 @@ -webdriver.promise

    Namespace webdriver.promise

    code »

    Interfaces

    webdriver.promise.Thenable
    Thenable is a promise-like object with a then method which may be - used to schedule callbacks on a promised value.

    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.
    var_args: ...?
    The arguments to apply to the function, excluding the - final callback.
    Returns
    A promise that will be resolved with the - result of the provided function's callback.
    code »webdriver.promise.consume ( generatorFn, opt_self, var_args )!webdriver.promise.Promise

    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. -

    
    - webdriver.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. -

    
    - webdriver.promise.consume(function* () {
    -   yield webdriver.promise.delayed(250).then(function() {
    -     throw Error('boom');
    -   });
    - }).thenCatch(function(e) {
    -   console.log(e.toString());  // Error: boom
    - });
    - 
    Parameters
    generatorFn: !Function
    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
    A promise that will resolve to the - generator's final result.
    Throws
    TypeError
    If the given function is not a generator.
    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: T=
    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.

    Tests is a function is a generator.

    Parameters
    fn: !Function
    The function to test.
    Returns
    Whether the function is a generator.

    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

    A promise implementation based on the CommonJS promise/A and +promise/B proposals. For more information, see +http://wiki.commonjs.org/wiki/Promises.

    +

    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.

    +
    Promise

    Represents the eventual value of a completed operation.

    +
    Thenable

    Thenable is a promise-like object with a then method which may be +used to schedule callbacks on a promised value.

    +
    \ No newline at end of file diff --git a/docs/namespace_webdriver_stacktrace.html b/docs/namespace_webdriver_stacktrace.html index 53b7733..bd526fd 100644 --- a/docs/namespace_webdriver_stacktrace.html +++ b/docs/namespace_webdriver_stacktrace.html @@ -1,40 +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

    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 creates 2 submatches on the (optional) context and function name, - matching identifiers like 'foo.Bar.prototype.baz', '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 3615aa0..07bbe37 100644 --- a/docs/namespace_webdriver_testing.html +++ b/docs/namespace_webdriver_testing.html @@ -1,2 +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.
    webdriver.testing.TestCase
    Constructs a test case that synchronizes each test case with the singleton - webdriver.promise.ControlFlow.
    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 d33cd72..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 2c72ff8..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 e344d7d..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 * Maintains a unique context for Closure library-based code.
    79 * @param {boolean=} opt_configureForTesting Whether to configure a fake DOM
    80 * for Closure-testing code that (incorrectly) assumes a DOM is always
    81 * present.
    82 * @constructor
    83 */
    84function Context(opt_configureForTesting) {
    85 var closure = this.closure = vm.createContext({
    86 console: console,
    87 setTimeout: setTimeout,
    88 setInterval: setInterval,
    89 clearTimeout: clearTimeout,
    90 clearInterval: clearInterval,
    91 process: process,
    92 require: require,
    93 Buffer: Buffer,
    94 Error: Error,
    95 CLOSURE_BASE_PATH: path.dirname(CLOSURE_BASE_FILE_PATH) + '/',
    96 CLOSURE_IMPORT_SCRIPT: function(src) {
    97 loadScript(src);
    98 return true;
    99 },
    100 CLOSURE_NO_DEPS: !isDevMode(),
    101 goog: {}
    102 });
    103 closure.window = closure.top = closure;
    104
    105 if (opt_configureForTesting) {
    106 closure.document = {
    107 body: {},
    108 createElement: function() { return {}; },
    109 getElementsByTagName: function() { return []; }
    110 };
    111 closure.document.body.ownerDocument = closure.document;
    112 }
    113
    114 loadScript(CLOSURE_BASE_FILE_PATH);
    115 loadScript(DEPS_FILE_PATH);
    116
    117 /**
    118 * Synchronously loads a script into the protected Closure context.
    119 * @param {string} src Path to the file to load.
    120 */
    121 function loadScript(src) {
    122 src = path.normalize(src);
    123 var contents = fs.readFileSync(src, 'utf8');
    124 vm.runInContext(contents, closure, src);
    125 }
    126}
    127
    128
    129var context = new Context();
    130
    131
    132/**
    133 * Loads a symbol by name from the protected Closure context.
    134 * @param {string} symbol The symbol to load.
    135 * @return {?} The loaded symbol, or {@code null} if not found.
    136 * @throws {Error} If the symbol has not been defined.
    137 */
    138function closureRequire(symbol) {
    139 context.closure.goog.require(symbol);
    140 return context.closure.goog.getObjectByName(symbol);
    141}
    142
    143
    144// PUBLIC API
    145
    146
    147/**
    148 * Loads a symbol by name from the protected Closure context and exports its
    149 * public API to the provided object. This function relies on Closure code
    150 * conventions to define the public API of an object as those properties whose
    151 * name does not end with "_".
    152 * @param {string} symbol The symbol to load. This must resolve to an object.
    153 * @return {!Object} An object with the exported API.
    154 * @throws {Error} If the symbol has not been defined or does not resolve to
    155 * an object.
    156 */
    157exports.exportPublicApi = function(symbol) {
    158 var src = closureRequire(symbol);
    159 if (typeof src != 'object' || src === null) {
    160 throw Error('"' + symbol + '" must resolve to an object');
    161 }
    162
    163 var dest = {};
    164 Object.keys(src).forEach(function(key) {
    165 if (key[key.length - 1] != '_') {
    166 dest[key] = src[key];
    167 }
    168 });
    169
    170 return dest;
    171};
    172
    173
    174if (isDevMode()) {
    175 exports.closure = context.closure;
    176}
    177exports.Context = Context;
    178exports.isDevMode = isDevMode;
    179exports.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 7580634..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
    18// Use base.require to avoid circular references between index and this module.
    19var Browser = base.require('webdriver.Browser'),
    20 Capabilities = base.require('webdriver.Capabilities'),
    21 Capability = base.require('webdriver.Capability'),
    22 WebDriver = base.require('webdriver.WebDriver'),
    23 promise = base.require('webdriver.promise');
    24
    25
    26
    27/**
    28 * Creates new {@link webdriver.WebDriver WebDriver} instances. The environment
    29 * variables listed below may be used to override a builder's configuration,
    30 * allowing quick runtime changes.
    31 * <ul>
    32 * <li>{@code SELENIUM_REMOTE_URL}: defines the remote URL for all builder
    33 * instances. This environment variable should be set to a fully qualified
    34 * URL for a WebDriver server (e.g. http://localhost:4444/wd/hub).
    35 *
    36 * <li>{@code SELENIUM_BROWSER}: defines the target browser in the form
    37 * {@code browser[:version][:platform]}.
    38 * </ul>
    39 *
    40 * <p>Suppose you had mytest.js that created WebDriver with
    41 * {@code var driver = new webdriver.Builder().build();}.
    42 *
    43 * This test could be made to use Firefox on the local machine by running with
    44 * {@code SELENIUM_BROWSER=firefox node mytest.js}.
    45 *
    46 * <p>Alternatively, you could request Chrome 36 on Linux from a remote
    47 * server with {@code
    48 * SELENIUM_BROWSER=chrome:36:LINUX
    49 * SELENIUM_REMOTE_URL=http://www.example.com:4444/wd/hub
    50 * node mytest.js}.
    51 *
    52 * @constructor
    53 */
    54var Builder = function() {
    55
    56 /** @private {webdriver.promise.ControlFlow} */
    57 this.flow_ = null;
    58
    59 /** @private {string} */
    60 this.url_ = '';
    61
    62 /** @private {!webdriver.Capabilities} */
    63 this.capabilities_ = new Capabilities();
    64
    65 /** @private {chrome.Options} */
    66 this.chromeOptions_ = null;
    67
    68 /** @private {firefox.Options} */
    69 this.firefoxOptions_ = null;
    70};
    71
    72
    73/**
    74 * Sets the URL of a remote WebDriver server to use. Once a remote URL has been
    75 * specified, the builder direct all new clients to that server. If this method
    76 * is never called, the Builder will attempt to create all clients locally.
    77 *
    78 * <p>As an alternative to this method, you may also set the
    79 * {@code SELENIUM_REMOTE_URL} environment variable.
    80 *
    81 * @param {string} url The URL of a remote server to use.
    82 * @return {!Builder} A self reference.
    83 */
    84Builder.prototype.usingServer = function(url) {
    85 this.url_ = url;
    86 return this;
    87};
    88
    89
    90/**
    91 * @return {string} The URL of the WebDriver server this instance is configured
    92 * to use.
    93 */
    94Builder.prototype.getServerUrl = function() {
    95 return this.url_;
    96};
    97
    98
    99/**
    100 * Sets the desired capabilities when requesting a new session. This will
    101 * overwrite any previously set capabilities.
    102 * @param {!(Object|webdriver.Capabilities)} capabilities The desired
    103 * capabilities for a new session.
    104 * @return {!Builder} A self reference.
    105 */
    106Builder.prototype.withCapabilities = function(capabilities) {
    107 this.capabilities_ = new Capabilities(capabilities);
    108 return this;
    109};
    110
    111
    112/**
    113 * Returns the base set of capabilities this instance is currently configured
    114 * to use.
    115 * @return {!webdriver.Capabilities} The current capabilities for this builder.
    116 */
    117Builder.prototype.getCapabilities = function() {
    118 return this.capabilities_;
    119};
    120
    121
    122/**
    123 * Configures the target browser for clients created by this instance.
    124 * Any calls to {@link #withCapabilities} after this function will
    125 * overwrite these settings.
    126 *
    127 * <p>You may also define the target browser using the {@code SELENIUM_BROWSER}
    128 * environment variable. If set, this environment variable should be of the
    129 * form {@code browser[:[version][:platform]]}.
    130 *
    131 * @param {(string|webdriver.Browser)} name The name of the target browser;
    132 * common defaults are available on the {@link webdriver.Browser} enum.
    133 * @param {string=} opt_version A desired version; may be omitted if any
    134 * version should be used.
    135 * @param {string=} opt_platform The desired platform; may be omitted if any
    136 * version may be used.
    137 * @return {!Builder} A self reference.
    138 */
    139Builder.prototype.forBrowser = function(name, opt_version, opt_platform) {
    140 this.capabilities_.set(Capability.BROWSER_NAME, name);
    141 this.capabilities_.set(Capability.VERSION, opt_version || null);
    142 this.capabilities_.set(Capability.PLATFORM, opt_platform || null);
    143 return this;
    144};
    145
    146
    147/**
    148 * Sets the proxy configuration to use for WebDriver clients created by this
    149 * builder. Any calls to {@link #withCapabilities} after this function will
    150 * overwrite these settings.
    151 * @param {!webdriver.ProxyConfig} config The configuration to use.
    152 * @return {!Builder} A self reference.
    153 */
    154Builder.prototype.setProxy = function(config) {
    155 this.capabilities_.setProxy(config);
    156 return this;
    157};
    158
    159
    160/**
    161 * Sets the logging preferences for the created session. Preferences may be
    162 * changed by repeated calls, or by calling {@link #withCapabilities}.
    163 * @param {!(webdriver.logging.Preferences|Object.<string, string>)} prefs The
    164 * desired logging preferences.
    165 * @return {!Builder} A self reference.
    166 */
    167Builder.prototype.setLoggingPrefs = function(prefs) {
    168 this.capabilities_.setLoggingPrefs(prefs);
    169 return this;
    170};
    171
    172
    173/**
    174 * Sets whether native events should be used.
    175 * @param {boolean} enabled Whether to enable native events.
    176 * @return {!Builder} A self reference.
    177 */
    178Builder.prototype.setEnableNativeEvents = function(enabled) {
    179 this.capabilities_.setEnableNativeEvents(enabled);
    180 return this;
    181};
    182
    183
    184/**
    185 * Sets how elements should be scrolled into view for interaction.
    186 * @param {number} behavior The desired scroll behavior: either 0 to align with
    187 * the top of the viewport or 1 to align with the bottom.
    188 * @return {!Builder} A self reference.
    189 */
    190Builder.prototype.setScrollBehavior = function(behavior) {
    191 this.capabilities_.setScrollBehavior(behavior);
    192 return this;
    193};
    194
    195
    196/**
    197 * Sets the default action to take with an unexpected alert before returning
    198 * an error.
    199 * @param {string} beahvior The desired behavior; should be "accept", "dismiss",
    200 * or "ignore". Defaults to "dismiss".
    201 * @return {!Builder} A self reference.
    202 */
    203Builder.prototype.setAlertBehavior = function(behavior) {
    204 this.capabilities_.setAlertBehavior(behavior);
    205 return this;
    206};
    207
    208
    209/**
    210 * Sets Chrome-specific options for drivers created by this builder. Any
    211 * logging or proxy settings defined on the given options will take precedence
    212 * over those set through {@link #setLoggingPrefs} and {@link #setProxy},
    213 * respectively.
    214 *
    215 * @param {!chrome.Options} options The ChromeDriver options to use.
    216 * @return {!Builder} A self reference.
    217 */
    218Builder.prototype.setChromeOptions = function(options) {
    219 this.chromeOptions_ = options;
    220 return this;
    221};
    222
    223
    224/**
    225 * Sets Firefox-specific options for drivers created by this builder. Any
    226 * logging or proxy settings defined on the given options will take precedence
    227 * over those set through {@link #setLoggingPrefs} and {@link #setProxy},
    228 * respectively.
    229 *
    230 * @param {!firefox.Options} options The FirefoxDriver options to use.
    231 * @return {!Builder} A self reference.
    232 */
    233Builder.prototype.setFirefoxOptions = function(options) {
    234 this.firefoxOptions_ = options;
    235 return this;
    236};
    237
    238
    239/**
    240 * Sets the control flow that created drivers should execute actions in. If
    241 * the flow is never set, or is set to {@code null}, it will use the active
    242 * flow at the time {@link #build()} is called.
    243 * @param {webdriver.promise.ControlFlow} flow The control flow to use, or
    244 * {@code null} to
    245 * @return {!Builder} A self reference.
    246 */
    247Builder.prototype.setControlFlow = function(flow) {
    248 this.flow_ = flow;
    249 return this;
    250};
    251
    252
    253/**
    254 * Creates a new WebDriver client based on this builder's current
    255 * configuration.
    256 *
    257 * @return {!webdriver.WebDriver} A new WebDriver instance.
    258 * @throws {Error} If the current configuration is invalid.
    259 */
    260Builder.prototype.build = function() {
    261 // Create a copy for any changes we may need to make based on the current
    262 // environment.
    263 var capabilities = new Capabilities(this.capabilities_);
    264
    265 var browser = process.env.SELENIUM_BROWSER;
    266 if (browser) {
    267 browser = browser.split(/:/, 3);
    268 capabilities.set(Capability.BROWSER_NAME, browser[0]);
    269 capabilities.set(Capability.VERSION, browser[1] || null);
    270 capabilities.set(Capability.PLATFORM, browser[2] || null);
    271 }
    272
    273 browser = capabilities.get(Capability.BROWSER_NAME);
    274
    275 if (!browser) {
    276 throw Error(
    277 'Target browser not defined; did you forget to call forBrowser()?');
    278 }
    279
    280 // Apply browser specific overrides.
    281 if (browser === Browser.CHROME && this.chromeOptions_) {
    282 capabilities.merge(this.chromeOptions_.toCapabilities());
    283 }
    284
    285 if (browser === Browser.FIREFOX && this.firefoxOptions_) {
    286 capabilities.merge(this.firefoxOptions_.toCapabilities());
    287 }
    288
    289 // Check for a remote browser.
    290 var url = process.env.SELENIUM_REMOTE_URL || this.url_;
    291 if (url) {
    292 var executor = executors.createExecutor(url);
    293 return WebDriver.createSession(executor, capabilities, this.flow_);
    294 }
    295
    296 // Check for a native browser.
    297 switch (browser) {
    298 case Browser.CHROME:
    299 // Requiring 'chrome' above would create a cycle:
    300 // index -> builder -> chrome -> index
    301 var chrome = require('./chrome');
    302 return new chrome.Driver(capabilities, null, this.flow_);
    303
    304 case Browser.FIREFOX:
    305 // Requiring 'firefox' above would create a cycle:
    306 // index -> builder -> firefox -> index
    307 var firefox = require('./firefox');
    308 return new firefox.Driver(capabilities, this.flow_);
    309
    310 case Browser.PHANTOM_JS:
    311 // Requiring 'phantomjs' would create a cycle:
    312 // index -> builder -> phantomjs -> index
    313 var phantomjs = require('./phantomjs');
    314 return new phantomjs.Driver(capabilities, this.flow_);
    315
    316 default:
    317 throw new Error('Do not know how to build driver: ' + browser
    318 + '; did you forget to call usingServer(url)?');
    319 }
    320};
    321
    322
    323// PUBLIC API
    324
    325
    326exports.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 4cd3d28..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 loopback: true,
    179 port: port,
    180 args: webdriver.promise.when(port, function(port) {
    181 return args.concat('--port=' + port);
    182 }),
    183 env: this.env_,
    184 stdio: this.stdio_
    185 });
    186};
    187
    188
    189/** @type {remote.DriverService} */
    190var defaultService = null;
    191
    192
    193/**
    194 * Sets the default service to use for new ChromeDriver instances.
    195 * @param {!remote.DriverService} service The service to use.
    196 * @throws {Error} If the default service is currently running.
    197 */
    198function setDefaultService(service) {
    199 if (defaultService && defaultService.isRunning()) {
    200 throw Error(
    201 'The previously configured ChromeDriver service is still running. ' +
    202 'You must shut it down before you may adjust its configuration.');
    203 }
    204 defaultService = service;
    205}
    206
    207
    208/**
    209 * Returns the default ChromeDriver service. If such a service has not been
    210 * configured, one will be constructed using the default configuration for
    211 * a ChromeDriver executable found on the system PATH.
    212 * @return {!remote.DriverService} The default ChromeDriver service.
    213 */
    214function getDefaultService() {
    215 if (!defaultService) {
    216 defaultService = new ServiceBuilder().build();
    217 }
    218 return defaultService;
    219}
    220
    221
    222/**
    223 * @type {string}
    224 * @const
    225 */
    226var OPTIONS_CAPABILITY_KEY = 'chromeOptions';
    227
    228
    229/**
    230 * Class for managing ChromeDriver specific options.
    231 * @constructor
    232 */
    233var Options = function() {
    234 /** @private {!Array.<string>} */
    235 this.args_ = [];
    236
    237 /** @private {!Array.<(string|!Buffer)>} */
    238 this.extensions_ = [];
    239};
    240
    241
    242/**
    243 * Extracts the ChromeDriver specific options from the given capabilities
    244 * object.
    245 * @param {!webdriver.Capabilities} capabilities The capabilities object.
    246 * @return {!Options} The ChromeDriver options.
    247 */
    248Options.fromCapabilities = function(capabilities) {
    249 var options = new Options();
    250
    251 var o = capabilities.get(OPTIONS_CAPABILITY_KEY);
    252 if (o instanceof Options) {
    253 options = o;
    254 } else if (o) {
    255 options.
    256 addArguments(o.args || []).
    257 addExtensions(o.extensions || []).
    258 detachDriver(!!o.detach).
    259 setChromeBinaryPath(o.binary).
    260 setChromeLogFile(o.logFile).
    261 setLocalState(o.localState).
    262 setUserPreferences(o.prefs);
    263 }
    264
    265 if (capabilities.has(webdriver.Capability.PROXY)) {
    266 options.setProxy(capabilities.get(webdriver.Capability.PROXY));
    267 }
    268
    269 if (capabilities.has(webdriver.Capability.LOGGING_PREFS)) {
    270 options.setLoggingPrefs(
    271 capabilities.get(webdriver.Capability.LOGGING_PREFS));
    272 }
    273
    274 return options;
    275};
    276
    277
    278/**
    279 * Add additional command line arguments to use when launching the Chrome
    280 * browser. Each argument may be specified with or without the "--" prefix
    281 * (e.g. "--foo" and "foo"). Arguments with an associated value should be
    282 * delimited by an "=": "foo=bar".
    283 * @param {...(string|!Array.<string>)} var_args The arguments to add.
    284 * @return {!Options} A self reference.
    285 */
    286Options.prototype.addArguments = function(var_args) {
    287 this.args_ = this.args_.concat.apply(this.args_, arguments);
    288 return this;
    289};
    290
    291
    292/**
    293 * Add additional extensions to install when launching Chrome. Each extension
    294 * should be specified as the path to the packed CRX file, or a Buffer for an
    295 * extension.
    296 * @param {...(string|!Buffer|!Array.<(string|!Buffer)>)} var_args The
    297 * extensions to add.
    298 * @return {!Options} A self reference.
    299 */
    300Options.prototype.addExtensions = function(var_args) {
    301 this.extensions_ = this.extensions_.concat.apply(
    302 this.extensions_, arguments);
    303 return this;
    304};
    305
    306
    307/**
    308 * Sets the path to the Chrome binary to use. On Mac OS X, this path should
    309 * reference the actual Chrome executable, not just the application binary
    310 * (e.g. "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome").
    311 *
    312 * The binary path be absolute or relative to the chromedriver server
    313 * executable, but it must exist on the machine that will launch Chrome.
    314 *
    315 * @param {string} path The path to the Chrome binary to use.
    316 * @return {!Options} A self reference.
    317 */
    318Options.prototype.setChromeBinaryPath = function(path) {
    319 this.binary_ = path;
    320 return this;
    321};
    322
    323
    324/**
    325 * Sets whether to leave the started Chrome browser running if the controlling
    326 * ChromeDriver service is killed before {@link webdriver.WebDriver#quit()} is
    327 * called.
    328 * @param {boolean} detach Whether to leave the browser running if the
    329 * chromedriver service is killed before the session.
    330 * @return {!Options} A self reference.
    331 */
    332Options.prototype.detachDriver = function(detach) {
    333 this.detach_ = detach;
    334 return this;
    335};
    336
    337
    338/**
    339 * Sets the user preferences for Chrome's user profile. See the "Preferences"
    340 * file in Chrome's user data directory for examples.
    341 * @param {!Object} prefs Dictionary of user preferences to use.
    342 * @return {!Options} A self reference.
    343 */
    344Options.prototype.setUserPreferences = function(prefs) {
    345 this.prefs_ = prefs;
    346 return this;
    347};
    348
    349
    350/**
    351 * Sets the logging preferences for the new session.
    352 * @param {!webdriver.logging.Preferences} prefs The logging preferences.
    353 * @return {!Options} A self reference.
    354 */
    355Options.prototype.setLoggingPrefs = function(prefs) {
    356 this.logPrefs_ = prefs;
    357 return this;
    358};
    359
    360
    361/**
    362 * Sets preferences for the "Local State" file in Chrome's user data
    363 * directory.
    364 * @param {!Object} state Dictionary of local state preferences.
    365 * @return {!Options} A self reference.
    366 */
    367Options.prototype.setLocalState = function(state) {
    368 this.localState_ = state;
    369 return this;
    370};
    371
    372
    373/**
    374 * Sets the path to Chrome's log file. This path should exist on the machine
    375 * that will launch Chrome.
    376 * @param {string} path Path to the log file to use.
    377 * @return {!Options} A self reference.
    378 */
    379Options.prototype.setChromeLogFile = function(path) {
    380 this.logFile_ = path;
    381 return this;
    382};
    383
    384
    385/**
    386 * Sets the proxy settings for the new session.
    387 * @param {webdriver.ProxyConfig} proxy The proxy configuration to use.
    388 * @return {!Options} A self reference.
    389 */
    390Options.prototype.setProxy = function(proxy) {
    391 this.proxy_ = proxy;
    392 return this;
    393};
    394
    395
    396/**
    397 * Converts this options instance to a {@link webdriver.Capabilities} object.
    398 * @param {webdriver.Capabilities=} opt_capabilities The capabilities to merge
    399 * these options into, if any.
    400 * @return {!webdriver.Capabilities} The capabilities.
    401 */
    402Options.prototype.toCapabilities = function(opt_capabilities) {
    403 var capabilities = opt_capabilities || webdriver.Capabilities.chrome();
    404 capabilities.
    405 set(webdriver.Capability.PROXY, this.proxy_).
    406 set(webdriver.Capability.LOGGING_PREFS, this.logPrefs_).
    407 set(OPTIONS_CAPABILITY_KEY, this);
    408 return capabilities;
    409};
    410
    411
    412/**
    413 * Converts this instance to its JSON wire protocol representation. Note this
    414 * function is an implementation not intended for general use.
    415 * @return {{args: !Array.<string>,
    416 * binary: (string|undefined),
    417 * detach: boolean,
    418 * extensions: !Array.<string>,
    419 * localState: (Object|undefined),
    420 * logFile: (string|undefined),
    421 * prefs: (Object|undefined)}} The JSON wire protocol representation
    422 * of this instance.
    423 */
    424Options.prototype.toJSON = function() {
    425 return {
    426 args: this.args_,
    427 binary: this.binary_,
    428 detach: !!this.detach_,
    429 extensions: this.extensions_.map(function(extension) {
    430 if (Buffer.isBuffer(extension)) {
    431 return extension.toString('base64');
    432 }
    433 return fs.readFileSync(extension, 'base64');
    434 }),
    435 localState: this.localState_,
    436 logFile: this.logFile_,
    437 prefs: this.prefs_
    438 };
    439};
    440
    441
    442/**
    443 * Creates a new ChromeDriver session.
    444 * @param {(webdriver.Capabilities|Options)=} opt_options The session options.
    445 * @param {remote.DriverService=} opt_service The session to use; will use
    446 * the {@link getDefaultService default service} by default.
    447 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or
    448 * {@code null} to use the currently active flow.
    449 * @return {!webdriver.WebDriver} A new WebDriver instance.
    450 * @deprecated Use {@link Driver new Driver()}.
    451 */
    452function createDriver(opt_options, opt_service, opt_flow) {
    453 return new Driver(opt_options, opt_service, opt_flow);
    454}
    455
    456
    457/**
    458 * Creates a new WebDriver client for Chrome.
    459 *
    460 * @param {(webdriver.Capabilities|Options)=} opt_config The configuration
    461 * options.
    462 * @param {remote.DriverService=} opt_service The session to use; will use
    463 * the {@link getDefaultService default service} by default.
    464 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or
    465 * {@code null} to use the currently active flow.
    466 * @constructor
    467 * @extends {webdriver.WebDriver}
    468 */
    469var Driver = function(opt_config, opt_service, opt_flow) {
    470 var service = opt_service || getDefaultService();
    471 var executor = executors.createExecutor(service.start());
    472
    473 var capabilities =
    474 opt_config instanceof Options ? opt_config.toCapabilities() :
    475 (opt_config || webdriver.Capabilities.chrome());
    476
    477 var driver = webdriver.WebDriver.createSession(
    478 executor, capabilities, opt_flow);
    479
    480 webdriver.WebDriver.call(
    481 this, driver.getSession(), executor, driver.controlFlow());
    482};
    483util.inherits(Driver, webdriver.WebDriver);
    484
    485
    486// PUBLIC API
    487
    488
    489exports.Driver = Driver;
    490exports.Options = Options;
    491exports.ServiceBuilder = ServiceBuilder;
    492exports.createDriver = createDriver;
    493exports.getDefaultService = getDefaultService;
    494exports.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 aeb0bff..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 0c892dc..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 index b823052..e35d854 100644 --- a/docs/source/firefox/binary.js.src.html +++ b/docs/source/firefox/binary.js.src.html @@ -1 +1 @@ -binary.js

    firefox/binary.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 child = require('child_process'),
    19 fs = require('fs'),
    20 path = require('path'),
    21 util = require('util');
    22
    23var promise = require('..').promise,
    24 _base = require('../_base'),
    25 io = require('../io'),
    26 exec = require('../io/exec');
    27
    28
    29
    30/** @const */
    31var NO_FOCUS_LIB_X86 = _base.isDevMode() ?
    32 path.join(__dirname, '../../../../cpp/prebuilt/i386/libnoblur.so') :
    33 path.join(__dirname, '../lib/firefox/i386/libnoblur.so') ;
    34
    35/** @const */
    36var NO_FOCUS_LIB_AMD64 = _base.isDevMode() ?
    37 path.join(__dirname, '../../../../cpp/prebuilt/amd64/libnoblur64.so') :
    38 path.join(__dirname, '../lib/firefox/amd64/libnoblur64.so') ;
    39
    40var X_IGNORE_NO_FOCUS_LIB = 'x_ignore_nofocus.so';
    41
    42var foundBinary = null;
    43
    44
    45/**
    46 * Checks the default Windows Firefox locations in Program Files.
    47 * @return {!promise.Promise.<?string>} A promise for the located executable.
    48 * The promise will resolve to {@code null} if Fireox was not found.
    49 */
    50function defaultWindowsLocation() {
    51 var files = [
    52 process.env['PROGRAMFILES'] || 'C:\\Program Files',
    53 process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)'
    54 ].map(function(prefix) {
    55 return path.join(prefix, 'Mozilla Firefox\\firefox.exe');
    56 });
    57 return io.exists(files[0]).then(function(exists) {
    58 return exists ? files[0] : io.exists(files[1]).then(function(exists) {
    59 return exists ? files[1] : null;
    60 });
    61 });
    62}
    63
    64
    65/**
    66 * Locates the Firefox binary for the current system.
    67 * @return {!promise.Promise.<string>} A promise for the located binary. The
    68 * promise will be rejected if Firefox cannot be located.
    69 */
    70function findFirefox() {
    71 if (foundBinary) {
    72 return foundBinary;
    73 }
    74
    75 if (process.platform === 'darwin') {
    76 var osxExe = '/Applications/Firefox.app/Contents/MacOS/firefox-bin';
    77 foundBinary = io.exists(osxExe).then(function(exists) {
    78 return exists ? osxExe : null;
    79 });
    80 } else if (process.platform === 'win32') {
    81 foundBinary = defaultWindowsLocation();
    82 } else {
    83 foundBinary = promise.fulfilled(io.findInPath('firefox'));
    84 }
    85
    86 return foundBinary = foundBinary.then(function(found) {
    87 if (found) {
    88 return found;
    89 }
    90 throw Error('Could not locate Firefox on the current system');
    91 });
    92}
    93
    94
    95/**
    96 * Copies the no focus libs into the given profile directory.
    97 * @param {string} profileDir Path to the profile directory to install into.
    98 * @return {!promise.Promise.<string>} The LD_LIBRARY_PATH prefix string to use
    99 * for the installed libs.
    100 */
    101function installNoFocusLibs(profileDir) {
    102 var x86 = path.join(profileDir, 'x86');
    103 var amd64 = path.join(profileDir, 'amd64');
    104
    105 return mkdir(x86)
    106 .then(copyLib.bind(null, NO_FOCUS_LIB_X86, x86))
    107 .then(mkdir.bind(null, amd64))
    108 .then(copyLib.bind(null, NO_FOCUS_LIB_AMD64, amd64))
    109 .then(function() {
    110 return x86 + ':' + amd64;
    111 });
    112
    113 function mkdir(dir) {
    114 return io.exists(dir).then(function(exists) {
    115 if (!exists) {
    116 return promise.checkedNodeCall(fs.mkdir, dir);
    117 }
    118 });
    119 }
    120
    121 function copyLib(src, dir) {
    122 return io.copy(src, path.join(dir, X_IGNORE_NO_FOCUS_LIB));
    123 }
    124}
    125
    126
    127/**
    128 * Silently runs Firefox to install a profile directory (which is assumed to be
    129 * defined in the given environment variables).
    130 * @param {string} firefox Path to the Firefox executable.
    131 * @param {!Object.<string, string>} env The environment variables to use.
    132 * @return {!promise.Promise} A promise for when the profile has been installed.
    133 */
    134function installProfile(firefox, env) {
    135 var installed = promise.defer();
    136 child.exec(firefox + ' -silent', {env: env, timeout: 180 * 1000},
    137 function(err) {
    138 if (err) {
    139 installed.reject(new Error(
    140 'Failed to install Firefox profile: ' + err));
    141 return;
    142 }
    143 installed.fulfill();
    144 });
    145 return installed.promise;
    146}
    147
    148
    149/**
    150 * Manages a Firefox subprocess configured for use with WebDriver.
    151 * @param {string=} opt_exe Path to the Firefox binary to use. If not
    152 * specified, will attempt to locate Firefox on the current system.
    153 * @constructor
    154 */
    155var Binary = function(opt_exe) {
    156 /** @private {(string|undefined)} */
    157 this.exe_ = opt_exe;
    158
    159 /** @private {!Array.<string>} */
    160 this.args_ = [];
    161
    162 /** @private {!Object.<string, string>} */
    163 this.env_ = {};
    164 Object.keys(process.env).forEach(function(key) {
    165 this.env_[key] = process.env[key];
    166 }.bind(this));
    167 this.env_['MOZ_CRASHREPORTER_DISABLE'] = '1';
    168 this.env_['MOZ_NO_REMOTE'] = '1';
    169 this.env_['NO_EM_RESTART'] = '1';
    170
    171 /** @private {promise.Promise.<!exec.Command>} */
    172 this.command_ = null;
    173};
    174
    175
    176/**
    177 * Add arguments to the command line used to start Firefox.
    178 * @param {...(string|!Array.<string>)} var_args Either the arguments to add as
    179 * varargs, or the arguments as an array.
    180 */
    181Binary.prototype.addArguments = function(var_args) {
    182 for (var i = 0; i < arguments.length; i++) {
    183 if (util.isArray(arguments[i])) {
    184 this.args_ = this.args_.concat(arguments[i]);
    185 } else {
    186 this.args_.push(arguments[i]);
    187 }
    188 }
    189};
    190
    191
    192/**
    193 * Launches Firefox and eturns a promise that will be fulfilled when the process
    194 * terminates.
    195 * @param {string} profile Path to the profile directory to use.
    196 * @return {!promise.Promise.<!exec.Result>} A promise for the process result.
    197 * @throws {Error} If this instance has already been started.
    198 */
    199Binary.prototype.launch = function(profile) {
    200 if (this.command_) {
    201 throw Error('Firefox is already running');
    202 }
    203
    204 var env = {};
    205 Object.keys(this.env_).forEach(function(key) {
    206 env[key] = this.env_[key];
    207 }.bind(this));
    208 env['XRE_PROFILE_PATH'] = profile;
    209
    210 var args = ['-foreground'].concat(this.args_);
    211
    212 var self = this;
    213
    214 this.command_ = promise.when(this.exe_ || findFirefox(), function(firefox) {
    215 if (process.platform === 'win32' || process.platform === 'darwin') {
    216 return firefox;
    217 }
    218 return installNoFocusLibs(profile).then(function(ldLibraryPath) {
    219 env['LD_LIBRARY_PATH'] = ldLibraryPath + ':' + env['LD_LIBRARY_PATH'];
    220 env['LD_PRELOAD'] = X_IGNORE_NO_FOCUS_LIB;
    221 return firefox;
    222 });
    223 }).then(function(firefox) {
    224 var install = exec(firefox, {args: ['-silent'], env: env});
    225 return install.result().then(function(result) {
    226 if (result.code !== 0) {
    227 throw Error(
    228 'Failed to install profile; firefox terminated with ' + result);
    229 }
    230
    231 return exec(firefox, {args: args, env: env});
    232 });
    233 });
    234
    235 return this.command_.then(function() {
    236 // Don't return the actual command handle, just a promise to signal it has
    237 // been started.
    238 });
    239};
    240
    241
    242/**
    243 * Kills the managed Firefox process.
    244 * @return {!promise.Promise} A promise for when the process has terminated.
    245 */
    246Binary.prototype.kill = function() {
    247 if (!this.command_) {
    248 return promise.defer(); // Not running.
    249 }
    250 return this.command_.then(function(command) {
    251 command.kill();
    252 return command.result();
    253 });
    254};
    255
    256
    257// PUBLIC API
    258
    259
    260exports.Binary = Binary;
    261
    \ No newline at end of file +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 * Manages a Firefox subprocess configured for use with WebDriver.
    137 *
    138 * @param {string=} opt_exe Path to the Firefox binary to use. If not
    139 * specified, will attempt to locate Firefox on the current system.
    140 * @constructor
    141 * @extends {Serializable.<string>}
    142 */
    143var Binary = function(opt_exe) {
    144 Serializable.call(this);
    145
    146 /** @private {(string|undefined)} */
    147 this.exe_ = opt_exe;
    148
    149 /** @private {!Array.<string>} */
    150 this.args_ = [];
    151
    152 /** @private {!Object.<string, string>} */
    153 this.env_ = {};
    154 Object.keys(process.env).forEach(function(key) {
    155 this.env_[key] = process.env[key];
    156 }.bind(this));
    157 this.env_['MOZ_CRASHREPORTER_DISABLE'] = '1';
    158 this.env_['MOZ_NO_REMOTE'] = '1';
    159 this.env_['NO_EM_RESTART'] = '1';
    160
    161 /** @private {promise.Promise.<!exec.Command>} */
    162 this.command_ = null;
    163};
    164util.inherits(Binary, Serializable);
    165
    166
    167/**
    168 * Add arguments to the command line used to start Firefox.
    169 * @param {...(string|!Array.<string>)} var_args Either the arguments to add as
    170 * varargs, or the arguments as an array.
    171 */
    172Binary.prototype.addArguments = function(var_args) {
    173 for (var i = 0; i < arguments.length; i++) {
    174 if (util.isArray(arguments[i])) {
    175 this.args_ = this.args_.concat(arguments[i]);
    176 } else {
    177 this.args_.push(arguments[i]);
    178 }
    179 }
    180};
    181
    182
    183/**
    184 * Launches Firefox and eturns a promise that will be fulfilled when the process
    185 * terminates.
    186 * @param {string} profile Path to the profile directory to use.
    187 * @return {!promise.Promise.<!exec.Result>} A promise for the process result.
    188 * @throws {Error} If this instance has already been started.
    189 */
    190Binary.prototype.launch = function(profile) {
    191 if (this.command_) {
    192 throw Error('Firefox is already running');
    193 }
    194
    195 var env = {};
    196 Object.keys(this.env_).forEach(function(key) {
    197 env[key] = this.env_[key];
    198 }.bind(this));
    199 env['XRE_PROFILE_PATH'] = profile;
    200
    201 var args = ['-foreground'].concat(this.args_);
    202
    203 this.command_ = promise.when(this.exe_ || findFirefox(), function(firefox) {
    204 if (process.platform === 'win32' || process.platform === 'darwin') {
    205 return exec(firefox, {args: args, env: env});
    206 }
    207 return installNoFocusLibs(profile).then(function(ldLibraryPath) {
    208 env['LD_LIBRARY_PATH'] = ldLibraryPath + ':' + env['LD_LIBRARY_PATH'];
    209 env['LD_PRELOAD'] = X_IGNORE_NO_FOCUS_LIB;
    210 return exec(firefox, {args: args, env: env});
    211 });
    212 });
    213
    214 return this.command_.then(function() {
    215 // Don't return the actual command handle, just a promise to signal it has
    216 // been started.
    217 });
    218};
    219
    220
    221/**
    222 * Kills the managed Firefox process.
    223 * @return {!promise.Promise} A promise for when the process has terminated.
    224 */
    225Binary.prototype.kill = function() {
    226 if (!this.command_) {
    227 return promise.defer(); // Not running.
    228 }
    229 return this.command_.then(function(command) {
    230 command.kill();
    231 return command.result();
    232 });
    233};
    234
    235
    236/**
    237 * Returns a promise for the wire representation of this binary. Note: the
    238 * FirefoxDriver only supports passing the path to the binary executable over
    239 * the wire; all command line arguments and environment variables will be
    240 * discarded.
    241 *
    242 * @return {!promise.Promise.<string>} A promise for this binary's wire
    243 * representation.
    244 * @override
    245 */
    246Binary.prototype.serialize = function() {
    247 return promise.when(this.exe_ || findFirefox());
    248};
    249
    250
    251// PUBLIC API
    252
    253
    254exports.Binary = Binary;
    255
    \ No newline at end of file diff --git a/docs/source/firefox/extension.js.src.html b/docs/source/firefox/extension.js.src.html index c2bc909..aeb0928 100644 --- a/docs/source/firefox/extension.js.src.html +++ b/docs/source/firefox/extension.js.src.html @@ -1 +1 @@ -extension.js

    firefox/extension.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/** @fileoverview Utilities for working with Firefox extensions. */
    17
    18'use strict';
    19
    20var AdmZip = require('adm-zip'),
    21 fs = require('fs'),
    22 path = require('path'),
    23 util = require('util'),
    24 xml = require('xml2js');
    25
    26var promise = require('..').promise,
    27 checkedCall = promise.checkedNodeCall,
    28 io = require('../io');
    29
    30
    31/**
    32 * Thrown when there an add-on is malformed.
    33 * @param {string} msg The error message.
    34 * @constructor
    35 * @extends {Error}
    36 */
    37function AddonFormatError(msg) {
    38 Error.call(this);
    39
    40 Error.captureStackTrace(this, AddonFormatError);
    41
    42 /** @override */
    43 this.name = AddonFormatError.name;
    44
    45 /** @override */
    46 this.message = msg;
    47}
    48util.inherits(AddonFormatError, Error);
    49
    50
    51
    52/**
    53 * Installs an extension to the given directory.
    54 * @param {string} extension Path to the extension to install, as either a xpi
    55 * file or a directory.
    56 * @param {string} dir Path to the directory to install the extension in.
    57 * @return {!promise.Promise.<string>} A promise for the add-on ID once
    58 * installed.
    59 */
    60function install(extension, dir) {
    61 return getDetails(extension).then(function(details) {
    62 function returnId() { return details.id; }
    63
    64 var dst = path.join(dir, details.id);
    65 if (extension.slice(-4) === '.xpi') {
    66 if (!details.unpack) {
    67 return io.copy(extension, dst + '.xpi').then(returnId);
    68 } else {
    69 return checkedCall(fs.readFile, extension).then(function(buff) {
    70 var zip = new AdmZip(buff);
    71 // TODO: find an async library for inflating a zip archive.
    72 new AdmZip(buff).extractAllTo(dst, true);
    73 }).then(returnId);
    74 }
    75 } else {
    76 return io.copyDir(extension, dst).then(returnId);
    77 }
    78 });
    79}
    80
    81
    82/**
    83 * Describes a Firefox add-on.
    84 * @typedef {{id: string, name: string, version: string, unpack: boolean}}
    85 */
    86var AddonDetails;
    87
    88
    89/**
    90 * Extracts the details needed to install an add-on.
    91 * @param {string} addonPath Path to the extension directory.
    92 * @return {!promise.Promise.<!AddonDetails>} A promise for the add-on details.
    93 */
    94function getDetails(addonPath) {
    95 return readManifest(addonPath).then(function(doc) {
    96 var em = getNamespaceId(doc, 'http://www.mozilla.org/2004/em-rdf#');
    97 var rdf = getNamespaceId(
    98 doc, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
    99
    100 var description = doc[rdf + 'RDF'][rdf + 'Description'][0];
    101 var details = {
    102 id: getNodeText(description, em + 'id'),
    103 name: getNodeText(description, em + 'name'),
    104 version: getNodeText(description, em + 'version'),
    105 unpack: getNodeText(description, em + 'unpack') || false
    106 };
    107
    108 if (typeof details.unpack === 'string') {
    109 details.unpack = details.unpack.toLowerCase() === 'true';
    110 }
    111
    112 if (!details.id) {
    113 throw new AddonFormatError('Could not find add-on ID for ' + addonPath);
    114 }
    115
    116 return details;
    117 });
    118
    119 function getNodeText(node, name) {
    120 return node[name] && node[name][0] || '';
    121 }
    122
    123 function getNamespaceId(doc, url) {
    124 var keys = Object.keys(doc);
    125 if (keys.length !== 1) {
    126 throw new AddonFormatError('Malformed manifest for add-on ' + addonPath);
    127 }
    128
    129 var namespaces = doc[keys[0]].$;
    130 var id = '';
    131 Object.keys(namespaces).some(function(ns) {
    132 if (namespaces[ns] !== url) {
    133 return false;
    134 }
    135
    136 if (ns.indexOf(':') != -1) {
    137 id = ns.split(':')[1] + ':';
    138 }
    139 return true;
    140 });
    141 return id;
    142 }
    143}
    144
    145
    146/**
    147 * Reads the manifest for a Firefox add-on.
    148 * @param {string} addonPath Path to a Firefox add-on as a xpi or an extension.
    149 * @return {!promise.Promise.<!Object>} A promise for the parsed manifest.
    150 */
    151function readManifest(addonPath) {
    152 var manifest;
    153
    154 if (addonPath.slice(-4) === '.xpi') {
    155 manifest = checkedCall(fs.readFile, addonPath).then(function(buff) {
    156 var zip = new AdmZip(buff);
    157 if (!zip.getEntry('install.rdf')) {
    158 throw new AddonFormatError(
    159 'Could not find install.rdf in ' + addonPath);
    160 }
    161 var done = promise.defer();
    162 zip.readAsTextAsync('install.rdf', done.fulfill);
    163 return done.promise;
    164 });
    165 } else {
    166 manifest = checkedCall(fs.stat, addonPath).then(function(stats) {
    167 if (!stats.isDirectory()) {
    168 throw Error(
    169 'Add-on path is niether a xpi nor a directory: ' + addonPath);
    170 }
    171 return checkedCall(fs.readFile, path.join(addonPath, 'install.rdf'));
    172 });
    173 }
    174
    175 return manifest.then(function(content) {
    176 return checkedCall(xml.parseString, content);
    177 });
    178}
    179
    180
    181// PUBLIC API
    182
    183
    184exports.install = install;
    \ No newline at end of file +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 var zip = new AdmZip(buff);
    73 // TODO: find an async library for inflating a zip archive.
    74 new AdmZip(buff).extractAllTo(dst, true);
    75 }).then(returnId);
    76 }
    77 } else {
    78 return io.copyDir(extension, dst).then(returnId);
    79 }
    80 });
    81}
    82
    83
    84/**
    85 * Describes a Firefox add-on.
    86 * @typedef {{id: string, name: string, version: string, unpack: boolean}}
    87 */
    88var AddonDetails;
    89
    90
    91/**
    92 * Extracts the details needed to install an add-on.
    93 * @param {string} addonPath Path to the extension directory.
    94 * @return {!promise.Promise.<!AddonDetails>} A promise for the add-on details.
    95 */
    96function getDetails(addonPath) {
    97 return readManifest(addonPath).then(function(doc) {
    98 var em = getNamespaceId(doc, 'http://www.mozilla.org/2004/em-rdf#');
    99 var rdf = getNamespaceId(
    100 doc, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
    101
    102 var description = doc[rdf + 'RDF'][rdf + 'Description'][0];
    103 var details = {
    104 id: getNodeText(description, em + 'id'),
    105 name: getNodeText(description, em + 'name'),
    106 version: getNodeText(description, em + 'version'),
    107 unpack: getNodeText(description, em + 'unpack') || false
    108 };
    109
    110 if (typeof details.unpack === 'string') {
    111 details.unpack = details.unpack.toLowerCase() === 'true';
    112 }
    113
    114 if (!details.id) {
    115 throw new AddonFormatError('Could not find add-on ID for ' + addonPath);
    116 }
    117
    118 return details;
    119 });
    120
    121 function getNodeText(node, name) {
    122 return node[name] && node[name][0] || '';
    123 }
    124
    125 function getNamespaceId(doc, url) {
    126 var keys = Object.keys(doc);
    127 if (keys.length !== 1) {
    128 throw new AddonFormatError('Malformed manifest for add-on ' + addonPath);
    129 }
    130
    131 var namespaces = doc[keys[0]].$;
    132 var id = '';
    133 Object.keys(namespaces).some(function(ns) {
    134 if (namespaces[ns] !== url) {
    135 return false;
    136 }
    137
    138 if (ns.indexOf(':') != -1) {
    139 id = ns.split(':')[1] + ':';
    140 }
    141 return true;
    142 });
    143 return id;
    144 }
    145}
    146
    147
    148/**
    149 * Reads the manifest for a Firefox add-on.
    150 * @param {string} addonPath Path to a Firefox add-on as a xpi or an extension.
    151 * @return {!promise.Promise.<!Object>} A promise for the parsed manifest.
    152 */
    153function readManifest(addonPath) {
    154 var manifest;
    155
    156 if (addonPath.slice(-4) === '.xpi') {
    157 manifest = checkedCall(fs.readFile, addonPath).then(function(buff) {
    158 var zip = new AdmZip(buff);
    159 if (!zip.getEntry('install.rdf')) {
    160 throw new AddonFormatError(
    161 'Could not find install.rdf in ' + addonPath);
    162 }
    163 var done = promise.defer();
    164 zip.readAsTextAsync('install.rdf', done.fulfill);
    165 return done.promise;
    166 });
    167 } else {
    168 manifest = checkedCall(fs.stat, addonPath).then(function(stats) {
    169 if (!stats.isDirectory()) {
    170 throw Error(
    171 'Add-on path is niether a xpi nor a directory: ' + addonPath);
    172 }
    173 return checkedCall(fs.readFile, path.join(addonPath, 'install.rdf'));
    174 });
    175 }
    176
    177 return manifest.then(function(content) {
    178 return checkedCall(xml.parseString, content);
    179 });
    180}
    181
    182
    183// PUBLIC API
    184
    185
    186exports.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 index 5a2037b..0d9cc52 100644 --- a/docs/source/firefox/index.js.src.html +++ b/docs/source/firefox/index.js.src.html @@ -1 +1 @@ -index.js

    firefox/index.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 url = require('url'),
    19 util = require('util');
    20
    21var Binary = require('./binary').Binary,
    22 Profile = require('./profile').Profile,
    23 decodeProfile = require('./profile').decode,
    24 webdriver = require('..'),
    25 executors = require('../executors'),
    26 httpUtil = require('../http/util'),
    27 net = require('../net'),
    28 portprober = require('../net/portprober');
    29
    30
    31/**
    32 * Configuration options for the FirefoxDriver.
    33 * @constructor
    34 */
    35var Options = function() {
    36 /** @private {Profile} */
    37 this.profile_ = null;
    38
    39 /** @private {Binary} */
    40 this.binary_ = null;
    41
    42 /** @private {webdriver.logging.Preferences} */
    43 this.logPrefs_ = null;
    44
    45 /** @private {webdriver.ProxyConfig} */
    46 this.proxy_ = null;
    47};
    48
    49
    50/**
    51 * Sets the profile to use. The profile may be specified as a
    52 * {@link Profile} object or as the path to an existing Firefox profile to use
    53 * as a template.
    54 *
    55 * @param {(string|!Profile)} profile The profile to use.
    56 * @return {!Options} A self reference.
    57 */
    58Options.prototype.setProfile = function(profile) {
    59 if (typeof profile === 'string') {
    60 profile = new Profile(profile);
    61 }
    62 this.profile_ = profile;
    63 return this;
    64};
    65
    66
    67/**
    68 * Sets the binary to use. The binary may be specified as the path to a Firefox
    69 * executable, or as a {@link Binary} object.
    70 *
    71 * @param {(string|!Binary)} binary The binary to use.
    72 * @return {!Options} A self reference.
    73 */
    74Options.prototype.setBinary = function(binary) {
    75 if (typeof binary === 'string') {
    76 binary = new Binary(binary);
    77 }
    78 this.binary_ = binary;
    79 return this;
    80};
    81
    82
    83/**
    84 * Sets the logging preferences for the new session.
    85 * @param {webdriver.logging.Preferences} prefs The logging preferences.
    86 * @return {!Options} A self reference.
    87 */
    88Options.prototype.setLoggingPreferences = function(prefs) {
    89 this.logPrefs_ = prefs;
    90 return this;
    91};
    92
    93
    94/**
    95 * Sets the proxy to use.
    96 *
    97 * @param {webdriver.ProxyConfig} proxy The proxy configuration to use.
    98 * @return {!Options} A self reference.
    99 */
    100Options.prototype.setProxy = function(proxy) {
    101 this.proxy_ = proxy;
    102 return this;
    103};
    104
    105
    106/**
    107 * Converts these options to a {@link webdriver.Capabilities} instance.
    108 *
    109 * @return {!webdriver.Capabilities} A new capabilities object.
    110 */
    111Options.prototype.toCapabilities = function(opt_remote) {
    112 var caps = webdriver.Capabilities.firefox();
    113 if (this.logPrefs_) {
    114 caps.set(webdriver.Capability.LOGGING_PREFS, this.logPrefs_);
    115 }
    116 if (this.proxy_) {
    117 caps.set(webdriver.Capability.PROXY, this.proxy_);
    118 }
    119 if (this.binary_) {
    120 caps.set('firefox_binary', this.binary_);
    121 }
    122 if (this.profile_) {
    123 caps.set('firefox_profile', this.profile_);
    124 }
    125 return caps;
    126};
    127
    128
    129/**
    130 * A WebDriver client for Firefox.
    131 *
    132 * @param {(Options|webdriver.Capabilities|Object)=} opt_config The
    133 * configuration options for this driver, specified as either an
    134 * {@link Options} or {@link webdriver.Capabilities}, or as a raw hash
    135 * object.
    136 * @param {webdriver.promise.ControlFlow=} opt_flow The flow to
    137 * schedule commands through. Defaults to the active flow object.
    138 * @constructor
    139 * @extends {webdriver.WebDriver}
    140 */
    141var Driver = function(opt_config, opt_flow) {
    142 var caps;
    143 if (opt_config instanceof Options) {
    144 caps = opt_config.toCapabilities();
    145 } else {
    146 caps = new webdriver.Capabilities(opt_config);
    147 }
    148
    149 var binary = caps.get('firefox_binary') || new Binary();
    150 if (typeof binary === 'string') {
    151 binary = new Binary(binary);
    152 }
    153
    154 var profile = caps.get('firefox_profile') || new Profile();
    155
    156 caps.set('firefox_binary', null);
    157 caps.set('firefox_profile', null);
    158
    159 var serverUrl = portprober.findFreePort().then(function(port) {
    160 var prepareProfile;
    161 if (typeof profile === 'string') {
    162 prepareProfile = decodeProfile(profile).then(function(dir) {
    163 var profile = new Profile(dir);
    164 profile.setPreference('webdriver_firefox_port', port);
    165 return profile.writeToDisk();
    166 });
    167 } else {
    168 profile.setPreference('webdriver_firefox_port', port);
    169 prepareProfile = profile.writeToDisk();
    170 }
    171
    172 return prepareProfile.then(function(dir) {
    173 return binary.launch(dir);
    174 }).then(function() {
    175 var serverUrl = url.format({
    176 protocol: 'http',
    177 hostname: net.getLoopbackAddress(),
    178 port: port,
    179 pathname: '/hub'
    180 });
    181
    182 return httpUtil.waitForServer(serverUrl, 45 * 1000).then(function() {
    183 return serverUrl;
    184 });
    185 });
    186 });
    187
    188 var executor = executors.createExecutor(serverUrl);
    189 var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
    190
    191 webdriver.WebDriver.call(this, driver.getSession(), executor, opt_flow);
    192};
    193util.inherits(Driver, webdriver.WebDriver);
    194
    195
    196// PUBLIC API
    197
    198
    199exports.Binary = Binary;
    200exports.Driver = Driver;
    201exports.Options = Options;
    202exports.Profile = Profile;
    \ No newline at end of file +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 /** @private {!Binary} */
    243 this.binary_ = binary;
    244
    245 var self = this;
    246 var serverUrl = portprober.findFreePort().then(function(port) {
    247 var prepareProfile;
    248 if (typeof profile === 'string') {
    249 prepareProfile = decodeProfile(profile).then(function(dir) {
    250 var profile = new Profile(dir);
    251 profile.setPreference('webdriver_firefox_port', port);
    252 return profile.writeToDisk();
    253 });
    254 } else {
    255 profile.setPreference('webdriver_firefox_port', port);
    256 prepareProfile = profile.writeToDisk();
    257 }
    258
    259 return prepareProfile.then(function(dir) {
    260 self.profilePath_ = dir;
    261 return binary.launch(dir);
    262 }).then(function() {
    263 var serverUrl = url.format({
    264 protocol: 'http',
    265 hostname: net.getLoopbackAddress(),
    266 port: port,
    267 pathname: '/hub'
    268 });
    269
    270 return httpUtil.waitForServer(serverUrl, 45 * 1000).then(function() {
    271 return serverUrl;
    272 });
    273 });
    274 });
    275
    276 var executor = executors.createExecutor(serverUrl);
    277 var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
    278
    279 webdriver.WebDriver.call(this, driver.getSession(), executor, opt_flow);
    280};
    281util.inherits(Driver, webdriver.WebDriver);
    282
    283
    284/**
    285 * This function is a no-op as file detectors are not supported by this
    286 * implementation.
    287 * @override
    288 */
    289Driver.prototype.setFileDetector = function() {
    290};
    291
    292
    293/** @override */
    294Driver.prototype.quit = function() {
    295 return this.call(function() {
    296 var self = this;
    297 return Driver.super_.prototype.quit.call(this)
    298 .thenFinally(function() {
    299 return self.binary_.kill();
    300 })
    301 .thenFinally(function() {
    302 if (self.profilePath_) {
    303 return io.rmDir(self.profilePath_);
    304 }
    305 });
    306 }, this);
    307};
    308
    309
    310// PUBLIC API
    311
    312
    313exports.Binary = Binary;
    314exports.Driver = Driver;
    315exports.Options = Options;
    316exports.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 index bca8f4a..6ff1c3c 100644 --- a/docs/source/firefox/profile.js.src.html +++ b/docs/source/firefox/profile.js.src.html @@ -1 +1 @@ -profile.js

    firefox/profile.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 AdmZip = require('adm-zip'),
    19 fs = require('fs'),
    20 path = require('path'),
    21 vm = require('vm');
    22
    23var promise = require('..').promise,
    24 _base = require('../_base'),
    25 io = require('../io'),
    26 extension = require('./extension');
    27
    28
    29/** @const */
    30var WEBDRIVER_PREFERENCES_PATH = _base.isDevMode()
    31 ? path.join(__dirname, '../../../firefox-driver/webdriver.json')
    32 : path.join(__dirname, '../lib/firefox/webdriver.json');
    33
    34/** @const */
    35var WEBDRIVER_EXTENSION_PATH = _base.isDevMode()
    36 ? path.join(__dirname,
    37 '../../../../build/javascript/firefox-driver/webdriver.xpi')
    38 : path.join(__dirname, '../lib/firefox/webdriver.xpi');
    39
    40/** @const */
    41var WEBDRIVER_EXTENSION_NAME = 'fxdriver@googlecode.com';
    42
    43
    44
    45/** @type {Object} */
    46var defaultPreferences = null;
    47
    48/**
    49 * Synchronously loads the default preferences used for the FirefoxDriver.
    50 * @return {!Object} The default preferences JSON object.
    51 */
    52function getDefaultPreferences() {
    53 if (!defaultPreferences) {
    54 var contents = fs.readFileSync(WEBDRIVER_PREFERENCES_PATH, 'utf8');
    55 defaultPreferences = JSON.parse(contents);
    56 }
    57 return defaultPreferences;
    58}
    59
    60
    61/**
    62 * Parses a user.js file in a Firefox profile directory.
    63 * @param {string} f Path to the file to parse.
    64 * @return {!promise.Promise.<!Object>} A promise for the parsed preferences as
    65 * a JSON object. If the file does not exist, an empty object will be
    66 * returned.
    67 */
    68function loadUserPrefs(f) {
    69 var done = promise.defer();
    70 fs.readFile(f, function(err, contents) {
    71 if (err && err.code === 'ENOENT') {
    72 done.fulfill({});
    73 return;
    74 }
    75
    76 if (err) {
    77 done.reject(err);
    78 return;
    79 }
    80
    81 var prefs = {};
    82 var context = vm.createContext({
    83 'user_pref': function(key, value) {
    84 prefs[key] = value;
    85 }
    86 });
    87
    88 vm.runInContext(contents, context, f);
    89 done.fulfill(prefs);
    90 });
    91 return done.promise;
    92}
    93
    94
    95/**
    96 * Copies the properties of one object into another.
    97 * @param {!Object} a The destination object.
    98 * @param {!Object} b The source object to apply as a mixin.
    99 */
    100function mixin(a, b) {
    101 Object.keys(b).forEach(function(key) {
    102 a[key] = b[key];
    103 });
    104}
    105
    106
    107/**
    108 * @param {!Object} defaults The default preferences to write. Will be
    109 * overridden by user.js preferences in the template directory and the
    110 * frozen preferences required by WebDriver.
    111 * @param {string} dir Path to the directory write the file to.
    112 * @return {!promise.Promise.<string>} A promise for the profile directory,
    113 * to be fulfilled when user preferences have been written.
    114 */
    115function writeUserPrefs(prefs, dir) {
    116 var userPrefs = path.join(dir, 'user.js');
    117 return loadUserPrefs(userPrefs).then(function(overrides) {
    118 mixin(prefs, overrides);
    119 mixin(prefs, getDefaultPreferences()['frozen']);
    120
    121 var contents = Object.keys(prefs).map(function(key) {
    122 return 'user_pref(' + JSON.stringify(key) + ', ' +
    123 JSON.stringify(prefs[key]) + ');';
    124 }).join('\n');
    125
    126 var done = promise.defer();
    127 fs.writeFile(userPrefs, contents, function(err) {
    128 err && done.reject(err) || done.fulfill(dir);
    129 });
    130 return done.promise;
    131 });
    132};
    133
    134
    135/**
    136 * Installs a group of extensions in the given profile directory. If the
    137 * WebDriver extension is not included in this set, the default version
    138 * bundled with this package will be installed.
    139 * @param {!Array.<string>} extensions The extensions to install, as a
    140 * path to an unpacked extension directory or a path to a xpi file.
    141 * @param {string} dir The profile directory to install to.
    142 * @param {boolean=} opt_excludeWebDriverExt Whether to skip installation of
    143 * the default WebDriver extension.
    144 * @return {!promise.Promise.<string>} A promise for the main profile directory
    145 * once all extensions have been installed.
    146 */
    147function installExtensions(extensions, dir, opt_excludeWebDriverExt) {
    148 var hasWebDriver = !!opt_excludeWebDriverExt;
    149 var next = 0;
    150 var extensionDir = path.join(dir, 'extensions');
    151 var done = promise.defer();
    152
    153 return io.exists(extensionDir).then(function(exists) {
    154 if (!exists) {
    155 return promise.checkedNodeCall(fs.mkdir, extensionDir);
    156 }
    157 }).then(function() {
    158 installNext();
    159 return done.promise;
    160 });
    161
    162 function installNext() {
    163 if (!done.isPending()) {
    164 return;
    165 }
    166
    167 if (next >= extensions.length) {
    168 if (hasWebDriver) {
    169 done.fulfill(dir);
    170 } else {
    171 install(WEBDRIVER_EXTENSION_PATH);
    172 }
    173 } else {
    174 install(extensions[next++]);
    175 }
    176 }
    177
    178 function install(ext) {
    179 extension.install(ext, extensionDir).then(function(id) {
    180 hasWebDriver = hasWebDriver || (id === WEBDRIVER_EXTENSION_NAME);
    181 installNext();
    182 }, done.reject);
    183 }
    184}
    185
    186
    187/**
    188 * Decodes a base64 encoded profile.
    189 * @param {string} data The base64 encoded string.
    190 * @return {!promise.Promise.<string>} A promise for the path to the decoded
    191 * profile directory.
    192 */
    193function decode(data) {
    194 return io.tmpFile().then(function(file) {
    195 var buf = new Buffer(data, 'base64');
    196 return promise.checkedNodeCall(fs.writeFile, file, buf).then(function() {
    197 return io.tmpDir();
    198 }).then(function(dir) {
    199 var zip = new AdmZip(file);
    200 zip.extractAllTo(dir); // Sync only? Why?? :-(
    201 return dir;
    202 });
    203 });
    204}
    205
    206
    207
    208/**
    209 * Models a Firefox proifle directory for use with the FirefoxDriver. The
    210 * {@code Proifle} directory uses an in-memory model until {@link #writeToDisk}
    211 * is called.
    212 * @param {string=} opt_dir Path to an existing Firefox profile directory to
    213 * use a template for this profile. If not specified, a blank profile will
    214 * be used.
    215 * @constructor
    216 */
    217var Profile = function(opt_dir) {
    218 /** @private {!Object} */
    219 this.preferences_ = {};
    220
    221 mixin(this.preferences_, getDefaultPreferences()['mutable']);
    222 mixin(this.preferences_, getDefaultPreferences()['frozen']);
    223
    224 /** @private {boolean} */
    225 this.nativeEventsEnabled_ = true;
    226
    227 /** @private {(string|undefined)} */
    228 this.template_ = opt_dir;
    229
    230 /** @private {number} */
    231 this.port_ = 0;
    232
    233 /** @private {!Array.<string>} */
    234 this.extensions_ = [];
    235};
    236
    237
    238/**
    239 * Registers an extension to be included with this profile.
    240 * @param {string} extension Path to the extension to include, as either an
    241 * unpacked extension directory or the path to a xpi file.
    242 */
    243Profile.prototype.addExtension = function(extension) {
    244 this.extensions_.push(extension);
    245};
    246
    247
    248/**
    249 * Sets a desired preference for this profile.
    250 * @param {string} key The preference key.
    251 * @param {(string|number|boolean)} value The preference value.
    252 * @throws {Error} If attempting to set a frozen preference.
    253 */
    254Profile.prototype.setPreference = function(key, value) {
    255 var frozen = getDefaultPreferences()['frozen'];
    256 if (frozen.hasOwnProperty(key) && frozen[key] !== value) {
    257 throw Error('You may not set ' + key + '=' + JSON.stringify(value)
    258 + '; value is frozen for proper WebDriver functionality ('
    259 + key + '=' + JSON.stringify(frozen[key]) + ')');
    260 }
    261 this.preferences_[key] = value;
    262};
    263
    264
    265/**
    266 * Returns the currently configured value of a profile preference. This does
    267 * not include any defaults defined in the profile's template directory user.js
    268 * file (if a template were specified on construction).
    269 * @param {string} key The desired preference.
    270 * @return {(string|number|boolean|undefined)} The current value of the
    271 * requested preference.
    272 */
    273Profile.prototype.getPreference = function(key) {
    274 return this.preferences_[key];
    275};
    276
    277
    278/**
    279 * @return {number} The port this profile is currently configured to use, or
    280 * 0 if the port will be selected at random when the profile is written
    281 * to disk.
    282 */
    283Profile.prototype.getPort = function() {
    284 return this.port_;
    285};
    286
    287
    288/**
    289 * Sets the port to use for the WebDriver extension loaded by this profile.
    290 * @param {number} port The desired port, or 0 to use any free port.
    291 */
    292Profile.prototype.setPort = function(port) {
    293 this.port_ = port;
    294};
    295
    296
    297/**
    298 * @return {boolean} Whether the FirefoxDriver is configured to automatically
    299 * accept untrusted SSL certificates.
    300 */
    301Profile.prototype.acceptUntrustedCerts = function() {
    302 return !!this.preferences_['webdriver_accept_untrusted_certs'];
    303};
    304
    305
    306/**
    307 * Sets whether the FirefoxDriver should automatically accept untrusted SSL
    308 * certificates.
    309 * @param {boolean} value .
    310 */
    311Profile.prototype.setAcceptUntrustedCerts = function(value) {
    312 this.preferences_['webdriver_accept_untrusted_certs'] = !!value;
    313};
    314
    315
    316/**
    317 * Sets whether to assume untrusted certificates come from untrusted issuers.
    318 * @param {boolean} value .
    319 */
    320Profile.prototype.setAssumeUntrustedCertIssuer = function(value) {
    321 this.preferences_['webdriver_assume_untrusted_issuer'] = !!value;
    322};
    323
    324
    325/**
    326 * @return {boolean} Whether to assume untrusted certs come from untrusted
    327 * issuers.
    328 */
    329Profile.prototype.assumeUntrustedCertIssuer = function() {
    330 return !!this.preferences_['webdriver_assume_untrusted_issuer'];
    331};
    332
    333
    334/**
    335 * Sets whether to use native events with this profile.
    336 * @param {boolean} enabled .
    337 */
    338Profile.prototype.setNativeEventsEnabled = function(enabled) {
    339 this.nativeEventsEnabled_ = enabled;
    340};
    341
    342
    343/**
    344 * Returns whether native events are enabled in this profile.
    345 * @return {boolean} .
    346 */
    347Profile.prototype.nativeEventsEnabled = function() {
    348 return this.nativeEventsEnabled_;
    349};
    350
    351
    352/**
    353 * Writes this profile to disk.
    354 * @param {boolean=} opt_excludeWebDriverExt Whether to exclude the WebDriver
    355 * extension from the generated profile. Used to reduce the size of an
    356 * {@link #encode() encoded profile} since the server will always install
    357 * the extension itself.
    358 * @return {!promise.Promise.<string>} A promise for the path to the new
    359 * profile directory.
    360 */
    361Profile.prototype.writeToDisk = function(opt_excludeWebDriverExt) {
    362 var profileDir = io.tmpDir();
    363 if (this.template_) {
    364 profileDir = profileDir.then(function(dir) {
    365 return io.copyDir(
    366 this.template_, dir, /(parent\.lock|lock|\.parentlock)/);
    367 }.bind(this));
    368 }
    369
    370 // Freeze preferences for async operations.
    371 var prefs = {};
    372 mixin(prefs, this.preferences_);
    373
    374 // Freeze extensions for async operations.
    375 var extensions = this.extensions_.concat();
    376
    377 return profileDir.then(function(dir) {
    378 return writeUserPrefs(prefs, dir);
    379 }).then(function(dir) {
    380 return installExtensions(extensions, dir, !!opt_excludeWebDriverExt);
    381 });
    382};
    383
    384
    385/**
    386 * Encodes this profile as a zipped, base64 encoded directory.
    387 * @return {!promise.Promise.<string>} A promise for the encoded profile.
    388 */
    389Profile.prototype.encode = function() {
    390 return this.writeToDisk(true).then(function(dir) {
    391 var zip = new AdmZip();
    392 zip.addLocalFolder(dir, '');
    393 return io.tmpFile().then(function(file) {
    394 zip.writeZip(file); // Sync! Why oh why :-(
    395 return promise.checkedNodeCall(fs.readFile, file);
    396 });
    397 }).then(function(data) {
    398 return new Buffer(data).toString('base64');
    399 });
    400};
    401
    402
    403// PUBLIC API
    404
    405
    406exports.Profile = Profile;
    407exports.decode = decode;
    408exports.loadUserPrefs = loadUserPrefs;
    \ No newline at end of file +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 fs = require('fs'),
    27 path = require('path'),
    28 util = require('util'),
    29 vm = require('vm');
    30
    31var Serializable = require('..').Serializable,
    32 promise = require('..').promise,
    33 _base = require('../_base'),
    34 io = require('../io'),
    35 extension = require('./extension');
    36
    37
    38/** @const */
    39var WEBDRIVER_PREFERENCES_PATH = _base.isDevMode()
    40 ? path.join(__dirname, '../../../firefox-driver/webdriver.json')
    41 : path.join(__dirname, '../lib/firefox/webdriver.json');
    42
    43/** @const */
    44var WEBDRIVER_EXTENSION_PATH = _base.isDevMode()
    45 ? path.join(__dirname,
    46 '../../../../build/javascript/firefox-driver/webdriver.xpi')
    47 : path.join(__dirname, '../lib/firefox/webdriver.xpi');
    48
    49/** @const */
    50var WEBDRIVER_EXTENSION_NAME = 'fxdriver@googlecode.com';
    51
    52
    53
    54/** @type {Object} */
    55var defaultPreferences = null;
    56
    57/**
    58 * Synchronously loads the default preferences used for the FirefoxDriver.
    59 * @return {!Object} The default preferences JSON object.
    60 */
    61function getDefaultPreferences() {
    62 if (!defaultPreferences) {
    63 var contents = fs.readFileSync(WEBDRIVER_PREFERENCES_PATH, 'utf8');
    64 defaultPreferences = JSON.parse(contents);
    65 }
    66 return defaultPreferences;
    67}
    68
    69
    70/**
    71 * Parses a user.js file in a Firefox profile directory.
    72 * @param {string} f Path to the file to parse.
    73 * @return {!promise.Promise.<!Object>} A promise for the parsed preferences as
    74 * a JSON object. If the file does not exist, an empty object will be
    75 * returned.
    76 */
    77function loadUserPrefs(f) {
    78 var done = promise.defer();
    79 fs.readFile(f, function(err, contents) {
    80 if (err && err.code === 'ENOENT') {
    81 done.fulfill({});
    82 return;
    83 }
    84
    85 if (err) {
    86 done.reject(err);
    87 return;
    88 }
    89
    90 var prefs = {};
    91 var context = vm.createContext({
    92 'user_pref': function(key, value) {
    93 prefs[key] = value;
    94 }
    95 });
    96
    97 vm.runInContext(contents, context, f);
    98 done.fulfill(prefs);
    99 });
    100 return done.promise;
    101}
    102
    103
    104/**
    105 * Copies the properties of one object into another.
    106 * @param {!Object} a The destination object.
    107 * @param {!Object} b The source object to apply as a mixin.
    108 */
    109function mixin(a, b) {
    110 Object.keys(b).forEach(function(key) {
    111 a[key] = b[key];
    112 });
    113}
    114
    115
    116/**
    117 * @param {!Object} defaults The default preferences to write. Will be
    118 * overridden by user.js preferences in the template directory and the
    119 * frozen preferences required by WebDriver.
    120 * @param {string} dir Path to the directory write the file to.
    121 * @return {!promise.Promise.<string>} A promise for the profile directory,
    122 * to be fulfilled when user preferences have been written.
    123 */
    124function writeUserPrefs(prefs, dir) {
    125 var userPrefs = path.join(dir, 'user.js');
    126 return loadUserPrefs(userPrefs).then(function(overrides) {
    127 mixin(prefs, overrides);
    128 mixin(prefs, getDefaultPreferences()['frozen']);
    129
    130 var contents = Object.keys(prefs).map(function(key) {
    131 return 'user_pref(' + JSON.stringify(key) + ', ' +
    132 JSON.stringify(prefs[key]) + ');';
    133 }).join('\n');
    134
    135 var done = promise.defer();
    136 fs.writeFile(userPrefs, contents, function(err) {
    137 err && done.reject(err) || done.fulfill(dir);
    138 });
    139 return done.promise;
    140 });
    141};
    142
    143
    144/**
    145 * Installs a group of extensions in the given profile directory. If the
    146 * WebDriver extension is not included in this set, the default version
    147 * bundled with this package will be installed.
    148 * @param {!Array.<string>} extensions The extensions to install, as a
    149 * path to an unpacked extension directory or a path to a xpi file.
    150 * @param {string} dir The profile directory to install to.
    151 * @param {boolean=} opt_excludeWebDriverExt Whether to skip installation of
    152 * the default WebDriver extension.
    153 * @return {!promise.Promise.<string>} A promise for the main profile directory
    154 * once all extensions have been installed.
    155 */
    156function installExtensions(extensions, dir, opt_excludeWebDriverExt) {
    157 var hasWebDriver = !!opt_excludeWebDriverExt;
    158 var next = 0;
    159 var extensionDir = path.join(dir, 'extensions');
    160 var done = promise.defer();
    161
    162 return io.exists(extensionDir).then(function(exists) {
    163 if (!exists) {
    164 return promise.checkedNodeCall(fs.mkdir, extensionDir);
    165 }
    166 }).then(function() {
    167 installNext();
    168 return done.promise;
    169 });
    170
    171 function installNext() {
    172 if (!done.isPending()) {
    173 return;
    174 }
    175
    176 if (next >= extensions.length) {
    177 if (hasWebDriver) {
    178 done.fulfill(dir);
    179 } else {
    180 install(WEBDRIVER_EXTENSION_PATH);
    181 }
    182 } else {
    183 install(extensions[next++]);
    184 }
    185 }
    186
    187 function install(ext) {
    188 extension.install(ext, extensionDir).then(function(id) {
    189 hasWebDriver = hasWebDriver || (id === WEBDRIVER_EXTENSION_NAME);
    190 installNext();
    191 }, done.reject);
    192 }
    193}
    194
    195
    196/**
    197 * Decodes a base64 encoded profile.
    198 * @param {string} data The base64 encoded string.
    199 * @return {!promise.Promise.<string>} A promise for the path to the decoded
    200 * profile directory.
    201 */
    202function decode(data) {
    203 return io.tmpFile().then(function(file) {
    204 var buf = new Buffer(data, 'base64');
    205 return promise.checkedNodeCall(fs.writeFile, file, buf).then(function() {
    206 return io.tmpDir();
    207 }).then(function(dir) {
    208 var zip = new AdmZip(file);
    209 zip.extractAllTo(dir); // Sync only? Why?? :-(
    210 return dir;
    211 });
    212 });
    213}
    214
    215
    216
    217/**
    218 * Models a Firefox proifle directory for use with the FirefoxDriver. The
    219 * {@code Proifle} directory uses an in-memory model until {@link #writeToDisk}
    220 * is called.
    221 * @param {string=} opt_dir Path to an existing Firefox profile directory to
    222 * use a template for this profile. If not specified, a blank profile will
    223 * be used.
    224 * @constructor
    225 * @extends {Serializable.<string>}
    226 */
    227var Profile = function(opt_dir) {
    228 Serializable.call(this);
    229
    230 /** @private {!Object} */
    231 this.preferences_ = {};
    232
    233 mixin(this.preferences_, getDefaultPreferences()['mutable']);
    234 mixin(this.preferences_, getDefaultPreferences()['frozen']);
    235
    236 /** @private {boolean} */
    237 this.nativeEventsEnabled_ = true;
    238
    239 /** @private {(string|undefined)} */
    240 this.template_ = opt_dir;
    241
    242 /** @private {number} */
    243 this.port_ = 0;
    244
    245 /** @private {!Array.<string>} */
    246 this.extensions_ = [];
    247};
    248util.inherits(Profile, Serializable);
    249
    250
    251/**
    252 * Registers an extension to be included with this profile.
    253 * @param {string} extension Path to the extension to include, as either an
    254 * unpacked extension directory or the path to a xpi file.
    255 */
    256Profile.prototype.addExtension = function(extension) {
    257 this.extensions_.push(extension);
    258};
    259
    260
    261/**
    262 * Sets a desired preference for this profile.
    263 * @param {string} key The preference key.
    264 * @param {(string|number|boolean)} value The preference value.
    265 * @throws {Error} If attempting to set a frozen preference.
    266 */
    267Profile.prototype.setPreference = function(key, value) {
    268 var frozen = getDefaultPreferences()['frozen'];
    269 if (frozen.hasOwnProperty(key) && frozen[key] !== value) {
    270 throw Error('You may not set ' + key + '=' + JSON.stringify(value)
    271 + '; value is frozen for proper WebDriver functionality ('
    272 + key + '=' + JSON.stringify(frozen[key]) + ')');
    273 }
    274 this.preferences_[key] = value;
    275};
    276
    277
    278/**
    279 * Returns the currently configured value of a profile preference. This does
    280 * not include any defaults defined in the profile's template directory user.js
    281 * file (if a template were specified on construction).
    282 * @param {string} key The desired preference.
    283 * @return {(string|number|boolean|undefined)} The current value of the
    284 * requested preference.
    285 */
    286Profile.prototype.getPreference = function(key) {
    287 return this.preferences_[key];
    288};
    289
    290
    291/**
    292 * @return {number} The port this profile is currently configured to use, or
    293 * 0 if the port will be selected at random when the profile is written
    294 * to disk.
    295 */
    296Profile.prototype.getPort = function() {
    297 return this.port_;
    298};
    299
    300
    301/**
    302 * Sets the port to use for the WebDriver extension loaded by this profile.
    303 * @param {number} port The desired port, or 0 to use any free port.
    304 */
    305Profile.prototype.setPort = function(port) {
    306 this.port_ = port;
    307};
    308
    309
    310/**
    311 * @return {boolean} Whether the FirefoxDriver is configured to automatically
    312 * accept untrusted SSL certificates.
    313 */
    314Profile.prototype.acceptUntrustedCerts = function() {
    315 return !!this.preferences_['webdriver_accept_untrusted_certs'];
    316};
    317
    318
    319/**
    320 * Sets whether the FirefoxDriver should automatically accept untrusted SSL
    321 * certificates.
    322 * @param {boolean} value .
    323 */
    324Profile.prototype.setAcceptUntrustedCerts = function(value) {
    325 this.preferences_['webdriver_accept_untrusted_certs'] = !!value;
    326};
    327
    328
    329/**
    330 * Sets whether to assume untrusted certificates come from untrusted issuers.
    331 * @param {boolean} value .
    332 */
    333Profile.prototype.setAssumeUntrustedCertIssuer = function(value) {
    334 this.preferences_['webdriver_assume_untrusted_issuer'] = !!value;
    335};
    336
    337
    338/**
    339 * @return {boolean} Whether to assume untrusted certs come from untrusted
    340 * issuers.
    341 */
    342Profile.prototype.assumeUntrustedCertIssuer = function() {
    343 return !!this.preferences_['webdriver_assume_untrusted_issuer'];
    344};
    345
    346
    347/**
    348 * Sets whether to use native events with this profile.
    349 * @param {boolean} enabled .
    350 */
    351Profile.prototype.setNativeEventsEnabled = function(enabled) {
    352 this.nativeEventsEnabled_ = enabled;
    353};
    354
    355
    356/**
    357 * Returns whether native events are enabled in this profile.
    358 * @return {boolean} .
    359 */
    360Profile.prototype.nativeEventsEnabled = function() {
    361 return this.nativeEventsEnabled_;
    362};
    363
    364
    365/**
    366 * Writes this profile to disk.
    367 * @param {boolean=} opt_excludeWebDriverExt Whether to exclude the WebDriver
    368 * extension from the generated profile. Used to reduce the size of an
    369 * {@link #encode() encoded profile} since the server will always install
    370 * the extension itself.
    371 * @return {!promise.Promise.<string>} A promise for the path to the new
    372 * profile directory.
    373 */
    374Profile.prototype.writeToDisk = function(opt_excludeWebDriverExt) {
    375 var profileDir = io.tmpDir();
    376 if (this.template_) {
    377 profileDir = profileDir.then(function(dir) {
    378 return io.copyDir(
    379 this.template_, dir, /(parent\.lock|lock|\.parentlock)/);
    380 }.bind(this));
    381 }
    382
    383 // Freeze preferences for async operations.
    384 var prefs = {};
    385 mixin(prefs, this.preferences_);
    386
    387 // Freeze extensions for async operations.
    388 var extensions = this.extensions_.concat();
    389
    390 return profileDir.then(function(dir) {
    391 return writeUserPrefs(prefs, dir);
    392 }).then(function(dir) {
    393 return installExtensions(extensions, dir, !!opt_excludeWebDriverExt);
    394 });
    395};
    396
    397
    398/**
    399 * Encodes this profile as a zipped, base64 encoded directory.
    400 * @return {!promise.Promise.<string>} A promise for the encoded profile.
    401 */
    402Profile.prototype.encode = function() {
    403 return this.writeToDisk(true).then(function(dir) {
    404 var zip = new AdmZip();
    405 zip.addLocalFolder(dir, '');
    406 return io.tmpFile().then(function(file) {
    407 zip.writeZip(file); // Sync! Why oh why :-(
    408 return promise.checkedNodeCall(fs.readFile, file);
    409 });
    410 }).then(function(data) {
    411 return new Buffer(data).toString('base64');
    412 });
    413};
    414
    415
    416/**
    417 * Encodes this profile as a zipped, base64 encoded directory.
    418 * @return {!promise.Promise.<string>} A promise for the encoded profile.
    419 * @override
    420 */
    421Profile.prototype.serialize = function() {
    422 return this.encode();
    423};
    424
    425
    426// PUBLIC API
    427
    428
    429exports.Profile = Profile;
    430exports.decode = decode;
    431exports.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 1e60a93..e3f93fe 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 * @param {http.Agent=} opt_agent The agent to use for each request.
    32 * Defaults to {@code http.globalAgent}.
    33 * @constructor
    34 * @implements {webdriver.http.Client}
    35 */
    36var HttpClient = function(serverUrl, opt_agent) {
    37 var parsedUrl = url.parse(serverUrl);
    38 if (!parsedUrl.hostname) {
    39 throw new Error('Invalid server URL: ' + serverUrl);
    40 }
    41
    42 /** @private {http.Agent} */
    43 this.agent_ = opt_agent;
    44
    45 /**
    46 * Base options for each request.
    47 * @private {!Object}
    48 */
    49 this.options_ = {
    50 host: parsedUrl.hostname,
    51 path: parsedUrl.pathname,
    52 port: parsedUrl.port
    53 };
    54};
    55
    56
    57/** @override */
    58HttpClient.prototype.send = function(httpRequest, callback) {
    59 var data;
    60 httpRequest.headers['Content-Length'] = 0;
    61 if (httpRequest.method == 'POST' || httpRequest.method == 'PUT') {
    62 data = JSON.stringify(httpRequest.data);
    63 httpRequest.headers['Content-Length'] = Buffer.byteLength(data, 'utf8');
    64 httpRequest.headers['Content-Type'] = 'application/json;charset=UTF-8';
    65 }
    66
    67 var path = this.options_.path;
    68 if (path[path.length - 1] === '/' && httpRequest.path[0] === '/') {
    69 path += httpRequest.path.substring(1);
    70 } else {
    71 path += httpRequest.path;
    72 }
    73
    74 var options = {
    75 method: httpRequest.method,
    76 host: this.options_.host,
    77 port: this.options_.port,
    78 path: path,
    79 headers: httpRequest.headers
    80 };
    81 if (this.agent_) {
    82 options.agent = this.agent_;
    83 }
    84 sendRequest(options, callback, data);
    85};
    86
    87
    88/**
    89 * Sends a single HTTP request.
    90 * @param {!Object} options The request options.
    91 * @param {function(Error, !webdriver.http.Response=)} callback The function to
    92 * invoke with the server's response.
    93 * @param {string=} opt_data The data to send with the request.
    94 */
    95var sendRequest = function(options, callback, opt_data) {
    96 var request = http.request(options, function(response) {
    97 if (response.statusCode == 302 || response.statusCode == 303) {
    98 try {
    99 var location = url.parse(response.headers['location']);
    100 } catch (ex) {
    101 callback(Error(
    102 'Failed to parse "Location" header for server redirect: ' +
    103 ex.message + '\nResponse was: \n' +
    104 new HttpResponse(response.statusCode, response.headers, '')));
    105 return;
    106 }
    107
    108 if (!location.hostname) {
    109 location.hostname = options.host;
    110 location.port = options.port;
    111 }
    112
    113 request.abort();
    114 sendRequest({
    115 method: 'GET',
    116 host: location.hostname,
    117 path: location.pathname + (location.search || ''),
    118 port: location.port,
    119 headers: {
    120 'Accept': 'application/json; charset=utf-8'
    121 }
    122 }, callback);
    123 return;
    124 }
    125
    126 var body = [];
    127 response.on('data', body.push.bind(body));
    128 response.on('end', function() {
    129 var resp = new HttpResponse(response.statusCode,
    130 response.headers, body.join('').replace(/\0/g, ''));
    131 callback(null, resp);
    132 });
    133 });
    134
    135 request.on('error', function(e) {
    136 if (e.code === 'ECONNRESET') {
    137 setTimeout(function() {
    138 sendRequest(options, callback, opt_data);
    139 }, 15);
    140 } else {
    141 var message = e.message;
    142 if (e.code) {
    143 message = e.code + ' ' + message;
    144 }
    145 callback(new Error(message));
    146 }
    147 });
    148
    149 if (opt_data) {
    150 request.write(opt_data);
    151 }
    152
    153 request.end();
    154};
    155
    156
    157// PUBLIC API
    158
    159/** @type {webdriver.http.Executor.} */
    160exports.Executor = base.require('webdriver.http.Executor');
    161
    162/** @type {webdriver.http.Request.} */
    163exports.Request = base.require('webdriver.http.Request');
    164
    165/** @type {webdriver.http.Response.} */
    166exports.Response = base.require('webdriver.http.Response');
    167
    168exports.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 host: parsedUrl.hostname,
    59 path: parsedUrl.pathname,
    60 port: parsedUrl.port
    61 };
    62};
    63
    64
    65/** @override */
    66HttpClient.prototype.send = function(httpRequest, callback) {
    67 var data;
    68 httpRequest.headers['Content-Length'] = 0;
    69 if (httpRequest.method == 'POST' || httpRequest.method == 'PUT') {
    70 data = JSON.stringify(httpRequest.data);
    71 httpRequest.headers['Content-Length'] = Buffer.byteLength(data, 'utf8');
    72 httpRequest.headers['Content-Type'] = 'application/json;charset=UTF-8';
    73 }
    74
    75 var path = this.options_.path;
    76 if (path[path.length - 1] === '/' && httpRequest.path[0] === '/') {
    77 path += httpRequest.path.substring(1);
    78 } else {
    79 path += httpRequest.path;
    80 }
    81
    82 var options = {
    83 method: httpRequest.method,
    84 host: this.options_.host,
    85 port: this.options_.port,
    86 path: path,
    87 headers: httpRequest.headers
    88 };
    89
    90 if (this.agent_) {
    91 options.agent = this.agent_;
    92 }
    93
    94 sendRequest(options, callback, data, this.proxy_);
    95};
    96
    97
    98/**
    99 * Sends a single HTTP request.
    100 * @param {!Object} options The request options.
    101 * @param {function(Error, !webdriver.http.Response=)} callback The function to
    102 * invoke with the server's response.
    103 * @param {string=} opt_data The data to send with the request.
    104 * @param {string=} opt_proxy The proxy server to use for the request.
    105 */
    106var sendRequest = function(options, callback, opt_data, opt_proxy) {
    107 var host = options.host;
    108 var port = options.port;
    109
    110 if (opt_proxy) {
    111 var proxy = url.parse(opt_proxy);
    112
    113 options.headers['Host'] = options.host;
    114 options.host = proxy.hostname;
    115 options.port = proxy.port;
    116
    117 if (proxy.auth) {
    118 options.headers['Proxy-Authorization'] =
    119 'Basic ' + new Buffer(proxy.auth).toString('base64');
    120 }
    121 }
    122
    123 var request = http.request(options, function(response) {
    124 if (response.statusCode == 302 || response.statusCode == 303) {
    125 try {
    126 var location = url.parse(response.headers['location']);
    127 } catch (ex) {
    128 callback(Error(
    129 'Failed to parse "Location" header for server redirect: ' +
    130 ex.message + '\nResponse was: \n' +
    131 new HttpResponse(response.statusCode, response.headers, '')));
    132 return;
    133 }
    134
    135 if (!location.hostname) {
    136 location.hostname = host;
    137 location.port = port;
    138 }
    139
    140 request.abort();
    141 sendRequest({
    142 method: 'GET',
    143 host: location.hostname,
    144 path: location.pathname + (location.search || ''),
    145 port: location.port,
    146 headers: {
    147 'Accept': 'application/json; charset=utf-8'
    148 }
    149 }, callback, undefined, opt_proxy);
    150 return;
    151 }
    152
    153 var body = [];
    154 response.on('data', body.push.bind(body));
    155 response.on('end', function() {
    156 var resp = new HttpResponse(response.statusCode,
    157 response.headers, body.join('').replace(/\0/g, ''));
    158 callback(null, resp);
    159 });
    160 });
    161
    162 request.on('error', function(e) {
    163 if (e.code === 'ECONNRESET') {
    164 setTimeout(function() {
    165 sendRequest(options, callback, opt_data, opt_proxy);
    166 }, 15);
    167 } else {
    168 var message = e.message;
    169 if (e.code) {
    170 message = e.code + ' ' + message;
    171 }
    172 callback(new Error(message));
    173 }
    174 });
    175
    176 if (opt_data) {
    177 request.write(opt_data);
    178 }
    179
    180 request.end();
    181};
    182
    183
    184// PUBLIC API
    185
    186/** @type {webdriver.http.Executor.} */
    187exports.Executor = base.require('webdriver.http.Executor');
    188
    189/** @type {webdriver.http.Request.} */
    190exports.Request = base.require('webdriver.http.Request');
    191
    192/** @type {webdriver.http.Response.} */
    193exports.Response = base.require('webdriver.http.Response');
    194
    195exports.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 908cc31..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 fea47af..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/** @type {function(new: webdriver.WebElementPromise)} */
    68exports.WebElementPromise = base.require('webdriver.WebElementPromise');
    69
    70
    71// Export the remainder of our API through getters to keep things cleaner
    72// when this module is used in a REPL environment.
    73
    74
    75/** @type {webdriver.Browser.} */
    76(exports.__defineGetter__('Browser', function() {
    77 return base.require('webdriver.Browser');
    78}));
    79
    80
    81/** @type {webdriver.Button.} */
    82(exports.__defineGetter__('Button', function() {
    83 return base.require('webdriver.Button');
    84}));
    85
    86
    87/** @type {webdriver.Capability.} */
    88(exports.__defineGetter__('Capability', function() {
    89 return base.require('webdriver.Capability');
    90}));
    91
    92
    93/** @type {webdriver.CommandName.} */
    94(exports.__defineGetter__('CommandName', function() {
    95 return base.require('webdriver.CommandName');
    96}));
    97
    98
    99/** @type {webdriver.Key.} */
    100(exports.__defineGetter__('Key', function() {
    101 return base.require('webdriver.Key');
    102}));
    103
    104
    105/** @type {error.} */
    106(exports.__defineGetter__('error', function() {
    107 return error;
    108}));
    109
    110
    111/** @type {error.} */
    112(exports.__defineGetter__('error', function() {
    113 return error;
    114}));
    115
    116
    117/** @type {webdriver.logging.} */
    118(exports.__defineGetter__('logging', function() {
    119 return base.exportPublicApi('webdriver.logging');
    120}));
    121
    122
    123/** @type {webdriver.promise.} */
    124(exports.__defineGetter__('promise', function() {
    125 return base.exportPublicApi('webdriver.promise');
    126}));
    127
    128
    129/** @type {webdriver.stacktrace.} */
    130(exports.__defineGetter__('stacktrace', function() {
    131 return base.exportPublicApi('webdriver.stacktrace');
    132}));
    \ 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 index 3e08cb0..9063c7e 100644 --- a/docs/source/io/exec.js.src.html +++ b/docs/source/io/exec.js.src.html @@ -1 +1 @@ -exec.js

    io/exec.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 childProcess = require('child_process');
    19
    20var promise = require('..').promise;
    21
    22
    23/**
    24 * A hash with configuration options for an executed command.
    25 * <ul>
    26 * <li>
    27 * <li>{@code args} - Command line arguments.
    28 * <li>{@code env} - Command environment; will inherit from the current process
    29 * if missing.
    30 * <li>{@code stdio} - IO configuration for the spawned server process. For
    31 * more information, refer to the documentation of
    32 * {@code child_process.spawn}.
    33 * </ul>
    34 *
    35 * @typedef {{
    36 * args: (!Array.<string>|undefined),
    37 * env: (!Object.<string, string>|undefined),
    38 * stdio: (string|!Array.<string|number|!Stream|null|undefined>|undefined)
    39 * }}
    40 */
    41var Options;
    42
    43
    44/**
    45 * Describes a command's termination conditions.
    46 * @param {?number} code The exit code, or {@code null} if the command did not
    47 * exit normally.
    48 * @param {?string} signal The signal used to kill the command, or
    49 * {@code null}.
    50 * @constructor
    51 */
    52var Result = function(code, signal) {
    53 /** @type {?number} */
    54 this.code = code;
    55
    56 /** @type {?string} */
    57 this.signal = signal;
    58};
    59
    60
    61/** @override */
    62Result.prototype.toString = function() {
    63 return 'Result(code=' + this.code + ', signal=' + this.signal + ')';
    64};
    65
    66
    67
    68/**
    69 * Represents a command running in a sub-process.
    70 * @param {!promise.Promise.<!Result>} result The command result.
    71 * @constructor
    72 */
    73var Command = function(result, onKill) {
    74 /** @return {boolean} Whether this command is still running. */
    75 this.isRunning = function() {
    76 return result.isPending();
    77 };
    78
    79 /**
    80 * @return {!promise.Promise.<!Result>} A promise for the result of this
    81 * command.
    82 */
    83 this.result = function() {
    84 return result;
    85 };
    86
    87 /**
    88 * Sends a signal to the underlying process.
    89 * @param {string=} opt_signal The signal to send; defaults to
    90 * {@code SIGTERM}.
    91 */
    92 this.kill = function(opt_signal) {
    93 onKill(opt_signal || 'SIGTERM');
    94 };
    95};
    96
    97
    98// PUBLIC API
    99
    100
    101/**
    102 * Spawns a child process. The returned {@link Command} may be used to wait
    103 * for the process result or to send signals to the process.
    104 *
    105 * @param {string} command The executable to spawn.
    106 * @param {Options=} opt_options The command options.
    107 * @return {!Command} The launched command.
    108 */
    109module.exports = function(command, opt_options) {
    110 var options = opt_options || {};
    111
    112 var proc = childProcess.spawn(command, options.args || [], {
    113 env: options.env || process.env,
    114 stdio: options.stdio || 'ignore'
    115 }).once('exit', onExit);
    116
    117 // This process should not wait on the spawned child, however, we do
    118 // want to ensure the child is killed when this process exits.
    119 proc.unref();
    120 process.once('exit', killCommand);
    121
    122 var result = promise.defer();
    123 var cmd = new Command(result.promise, function(signal) {
    124 if (!result.isPending() || !proc) {
    125 return; // No longer running.
    126 }
    127 proc.kill(signal);
    128 });
    129 return cmd;
    130
    131 function onExit(code, signal) {
    132 proc = null;
    133 process.removeListener('exit', killCommand);
    134 result.fulfill(new Result(code, signal));
    135 }
    136
    137 function killCommand() {
    138 process.removeListener('exit', killCommand);
    139 proc && proc.kill('SIGTERM');
    140 }
    141};
    \ No newline at end of file +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 4f563e8..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 tmp = require('tmp');
    19
    20var promise = require('..').promise;
    21
    22
    23var PATH_SEPARATOR = process.platform === 'win32' ? ';' : ':';
    24
    25
    26// PUBLIC API
    27
    28
    29
    30/**
    31 * Copies one file to another.
    32 * @param {string} src The source file.
    33 * @param {string} dst The destination file.
    34 * @return {!promise.Promise.<string>} A promise for the copied file's path.
    35 */
    36exports.copy = function(src, dst) {
    37 var copied = promise.defer();
    38
    39 var rs = fs.createReadStream(src);
    40 rs.on('error', copied.reject);
    41 rs.on('end', function() {
    42 copied.fulfill(dst);
    43 });
    44
    45 var ws = fs.createWriteStream(dst);
    46 ws.on('error', copied.reject);
    47
    48 rs.pipe(ws);
    49
    50 return copied.promise;
    51};
    52
    53
    54/**
    55 * Recursively copies the contents of one directory to another.
    56 * @param {string} src The source directory to copy.
    57 * @param {string} dst The directory to copy into.
    58 * @param {(RegEx|function(string): boolean)=} opt_exclude An exclusion filter
    59 * as either a regex or predicate function. All files matching this filter
    60 * will not be copied.
    61 * @return {!promise.Promise.<string>} A promise for the destination
    62 * directory's path once all files have been copied.
    63 */
    64exports.copyDir = function(src, dst, opt_exclude) {
    65 var predicate = opt_exclude;
    66 if (opt_exclude && typeof opt_exclude !== 'function') {
    67 predicate = function(p) {
    68 return !opt_exclude.test(p);
    69 };
    70 }
    71
    72 // TODO(jleyba): Make this function completely async.
    73 if (!fs.existsSync(dst)) {
    74 fs.mkdirSync(dst);
    75 }
    76
    77 var files = fs.readdirSync(src);
    78 files = files.map(function(file) {
    79 return path.join(src, file);
    80 });
    81
    82 if (predicate) {
    83 files = files.filter(predicate);
    84 }
    85
    86 var results = [];
    87 files.forEach(function(file) {
    88 var stats = fs.statSync(file);
    89 var target = path.join(dst, path.basename(file));
    90
    91 if (stats.isDirectory()) {
    92 if (!fs.existsSync(target)) {
    93 fs.mkdirSync(target, stats.mode);
    94 }
    95 results.push(exports.copyDir(file, target, predicate));
    96 } else {
    97 results.push(exports.copy(file, target));
    98 }
    99 });
    100
    101 return promise.all(results).then(function() {
    102 return dst;
    103 });
    104};
    105
    106
    107/**
    108 * Tests if a file path exists.
    109 * @param {string} path The path to test.
    110 * @return {!promise.Promise.<boolean>} A promise for whether the file exists.
    111 */
    112exports.exists = function(path) {
    113 var result = promise.defer();
    114 fs.exists(path, result.fulfill);
    115 return result.promise;
    116};
    117
    118
    119/**
    120 * @return {!promise.Promise.<string>} A promise for the path to a temporary
    121 * directory.
    122 * @see https://www.npmjs.org/package/tmp
    123 */
    124exports.tmpDir = function() {
    125 return promise.checkedNodeCall(tmp.dir);
    126};
    127
    128
    129/**
    130 * @return {!promise.Promise.<string>} A promise for the path to a temporary
    131 * file.
    132 * @see https://www.npmjs.org/package/tmp
    133 */
    134exports.tmpFile = function() {
    135 return promise.checkedNodeCall(tmp.file);
    136};
    137
    138
    139/**
    140 * Searches the {@code PATH} environment variable for the given file.
    141 * @param {string} file The file to locate on the PATH.
    142 * @param {boolean=} opt_checkCwd Whether to always start with the search with
    143 * the current working directory, regardless of whether it is explicitly
    144 * listed on the PATH.
    145 * @return {?string} Path to the located file, or {@code null} if it could
    146 * not be found.
    147 */
    148exports.findInPath = function(file, opt_checkCwd) {
    149 if (opt_checkCwd) {
    150 var tmp = path.join(process.cwd(), file);
    151 if (fs.existsSync(tmp)) {
    152 return tmp;
    153 }
    154 }
    155
    156 var dirs = process.env['PATH'].split(PATH_SEPARATOR);
    157 var found = null;
    158 dirs.forEach(function(dir) {
    159 var tmp = path.join(dir, file);
    160 if (!found && fs.existsSync(tmp)) {
    161 found = tmp;
    162 }
    163 });
    164 return found;
    165};
    \ 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 c9d443e..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 /** @deprecated */
    50 MODAL_DIALOG_OPENED: 26,
    51 UNEXPECTED_ALERT_OPEN: 26,
    52 NO_SUCH_ALERT: 27,
    53 /** @deprecated */
    54 NO_MODAL_DIALOG_OPEN: 27,
    55 SCRIPT_TIMEOUT: 28,
    56 INVALID_ELEMENT_COORDINATES: 29,
    57 IME_NOT_AVAILABLE: 30,
    58 IME_ENGINE_ACTIVATION_FAILED: 31,
    59 INVALID_SELECTOR_ERROR: 32,
    60 SESSION_NOT_CREATED: 33,
    61 MOVE_TARGET_OUT_OF_BOUNDS: 34,
    62 SQL_DATABASE_ERROR: 35,
    63 INVALID_XPATH_SELECTOR: 51,
    64 INVALID_XPATH_SELECTOR_RETURN_TYPE: 52,
    65 // The following error codes are derived straight from HTTP return codes.
    66 METHOD_NOT_ALLOWED: 405
    67};
    68
    69
    70
    71/**
    72 * Error extension that includes error status codes from the WebDriver wire
    73 * protocol:
    74 * http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
    75 *
    76 * @param {!bot.ErrorCode} code The error's status code.
    77 * @param {string=} opt_message Optional error message.
    78 * @constructor
    79 * @extends {Error}
    80 */
    81bot.Error = function(code, opt_message) {
    82
    83 /**
    84 * This error's status code.
    85 * @type {!bot.ErrorCode}
    86 */
    87 this.code = code;
    88
    89 /** @type {string} */
    90 this.state =
    91 bot.Error.CODE_TO_STATE_[code] || bot.Error.State.UNKNOWN_ERROR;
    92
    93 /** @override */
    94 this.message = opt_message || '';
    95
    96 var name = this.state.replace(/((?:^|\s+)[a-z])/g, function(str) {
    97 // IE<9 does not support String#trim(). Also, IE does not include 0xa0
    98 // (the non-breaking-space) in the \s character class, so we have to
    99 // explicitly include it.
    100 return str.toUpperCase().replace(/^[\s\xa0]+/g, '');
    101 });
    102
    103 var l = name.length - 'Error'.length;
    104 if (l < 0 || name.indexOf('Error', l) != l) {
    105 name += 'Error';
    106 }
    107
    108 /** @override */
    109 this.name = name;
    110
    111 // Generate a stacktrace for our custom error; ensure the error has our
    112 // custom name and message so the stack prints correctly in all browsers.
    113 var template = new Error(this.message);
    114 template.name = this.name;
    115
    116 /** @override */
    117 this.stack = template.stack || '';
    118};
    119goog.inherits(bot.Error, Error);
    120
    121
    122/**
    123 * Status strings enumerated in the W3C WebDriver working draft.
    124 * @enum {string}
    125 * @see http://www.w3.org/TR/webdriver/#status-codes
    126 */
    127bot.Error.State = {
    128 ELEMENT_NOT_SELECTABLE: 'element not selectable',
    129 ELEMENT_NOT_VISIBLE: 'element not visible',
    130 IME_ENGINE_ACTIVATION_FAILED: 'ime engine activation failed',
    131 IME_NOT_AVAILABLE: 'ime not available',
    132 INVALID_COOKIE_DOMAIN: 'invalid cookie domain',
    133 INVALID_ELEMENT_COORDINATES: 'invalid element coordinates',
    134 INVALID_ELEMENT_STATE: 'invalid element state',
    135 INVALID_SELECTOR: 'invalid selector',
    136 JAVASCRIPT_ERROR: 'javascript error',
    137 MOVE_TARGET_OUT_OF_BOUNDS: 'move target out of bounds',
    138 NO_SUCH_ALERT: 'no such alert',
    139 NO_SUCH_DOM: 'no such dom',
    140 NO_SUCH_ELEMENT: 'no such element',
    141 NO_SUCH_FRAME: 'no such frame',
    142 NO_SUCH_WINDOW: 'no such window',
    143 SCRIPT_TIMEOUT: 'script timeout',
    144 SESSION_NOT_CREATED: 'session not created',
    145 STALE_ELEMENT_REFERENCE: 'stale element reference',
    146 SUCCESS: 'success',
    147 TIMEOUT: 'timeout',
    148 UNABLE_TO_SET_COOKIE: 'unable to set cookie',
    149 UNEXPECTED_ALERT_OPEN: 'unexpected alert open',
    150 UNKNOWN_COMMAND: 'unknown command',
    151 UNKNOWN_ERROR: 'unknown error',
    152 UNSUPPORTED_OPERATION: 'unsupported operation'
    153};
    154
    155
    156/**
    157 * A map of error codes to state string.
    158 * @private {!Object.<bot.ErrorCode, bot.Error.State>}
    159 */
    160bot.Error.CODE_TO_STATE_ = {};
    161goog.scope(function() {
    162 var map = bot.Error.CODE_TO_STATE_;
    163 var code = bot.ErrorCode;
    164 var state = bot.Error.State;
    165
    166 map[code.ELEMENT_NOT_SELECTABLE] = state.ELEMENT_NOT_SELECTABLE;
    167 map[code.ELEMENT_NOT_VISIBLE] = state.ELEMENT_NOT_VISIBLE;
    168 map[code.IME_ENGINE_ACTIVATION_FAILED] = state.IME_ENGINE_ACTIVATION_FAILED;
    169 map[code.IME_NOT_AVAILABLE] = state.IME_NOT_AVAILABLE;
    170 map[code.INVALID_COOKIE_DOMAIN] = state.INVALID_COOKIE_DOMAIN;
    171 map[code.INVALID_ELEMENT_COORDINATES] = state.INVALID_ELEMENT_COORDINATES;
    172 map[code.INVALID_ELEMENT_STATE] = state.INVALID_ELEMENT_STATE;
    173 map[code.INVALID_SELECTOR_ERROR] = state.INVALID_SELECTOR;
    174 map[code.INVALID_XPATH_SELECTOR] = state.INVALID_SELECTOR;
    175 map[code.INVALID_XPATH_SELECTOR_RETURN_TYPE] = state.INVALID_SELECTOR;
    176 map[code.JAVASCRIPT_ERROR] = state.JAVASCRIPT_ERROR;
    177 map[code.METHOD_NOT_ALLOWED] = state.UNSUPPORTED_OPERATION;
    178 map[code.MOVE_TARGET_OUT_OF_BOUNDS] = state.MOVE_TARGET_OUT_OF_BOUNDS;
    179 map[code.NO_MODAL_DIALOG_OPEN] = state.NO_SUCH_ALERT;
    180 map[code.NO_SUCH_ALERT] = state.NO_SUCH_ALERT;
    181 map[code.NO_SUCH_ELEMENT] = state.NO_SUCH_ELEMENT;
    182 map[code.NO_SUCH_FRAME] = state.NO_SUCH_FRAME;
    183 map[code.NO_SUCH_WINDOW] = state.NO_SUCH_WINDOW;
    184 map[code.SCRIPT_TIMEOUT] = state.SCRIPT_TIMEOUT;
    185 map[code.SESSION_NOT_CREATED] = state.SESSION_NOT_CREATED;
    186 map[code.STALE_ELEMENT_REFERENCE] = state.STALE_ELEMENT_REFERENCE;
    187 map[code.SUCCESS] = state.SUCCESS;
    188 map[code.TIMEOUT] = state.TIMEOUT;
    189 map[code.UNABLE_TO_SET_COOKIE] = state.UNABLE_TO_SET_COOKIE;
    190 map[code.MODAL_DIALOG_OPENED] = state.UNEXPECTED_ALERT_OPEN;
    191 map[code.UNEXPECTED_ALERT_OPEN] = state.UNEXPECTED_ALERT_OPEN
    192 map[code.UNKNOWN_ERROR] = state.UNKNOWN_ERROR;
    193 map[code.UNSUPPORTED_OPERATION] = state.UNKNOWN_COMMAND;
    194}); // goog.scope
    195
    196
    197/**
    198 * Flag used for duck-typing when this code is embedded in a Firefox extension.
    199 * This is required since an Error thrown in one component and then reported
    200 * to another will fail instanceof checks in the second component.
    201 * @type {boolean}
    202 */
    203bot.Error.prototype.isAutomationError = true;
    204
    205
    206if (goog.DEBUG) {
    207 /** @return {string} The string representation of this error. */
    208 bot.Error.prototype.toString = function() {
    209 return this.name + ': ' + this.message;
    210 };
    211}
    \ 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 d69afe4..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 231d73a..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 451a876..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 5dc3b26..5b88d26 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 * @define {boolean} If true, JSCompiler will use the native implementation of
    47 * array functions where appropriate (e.g., {@code Array#filter}) and remove the
    48 * unused pure JS implementation.
    49 */
    50goog.define('goog.array.ASSUME_NATIVE_FUNCTIONS', false);
    51
    52
    53/**
    54 * @typedef {Array|NodeList|Arguments|{length: number}}
    55 */
    56goog.array.ArrayLike;
    57
    58
    59/**
    60 * Returns the last element in an array without removing it.
    61 * Same as goog.array.last.
    62 * @param {Array.<T>|goog.array.ArrayLike} array The array.
    63 * @return {T} Last item in array.
    64 * @template T
    65 */
    66goog.array.peek = function(array) {
    67 return array[array.length - 1];
    68};
    69
    70
    71/**
    72 * Returns the last element in an array without removing it.
    73 * Same as goog.array.peek.
    74 * @param {Array.<T>|goog.array.ArrayLike} array The array.
    75 * @return {T} Last item in array.
    76 * @template T
    77 */
    78goog.array.last = goog.array.peek;
    79
    80
    81/**
    82 * Reference to the original {@code Array.prototype}.
    83 * @private
    84 */
    85goog.array.ARRAY_PROTOTYPE_ = Array.prototype;
    86
    87
    88// NOTE(arv): Since most of the array functions are generic it allows you to
    89// pass an array-like object. Strings have a length and are considered array-
    90// like. However, the 'in' operator does not work on strings so we cannot just
    91// use the array path even if the browser supports indexing into strings. We
    92// therefore end up splitting the string.
    93
    94
    95/**
    96 * Returns the index of the first element of an array with a specified value, or
    97 * -1 if the element is not present in the array.
    98 *
    99 * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof}
    100 *
    101 * @param {Array.<T>|goog.array.ArrayLike} arr The array to be searched.
    102 * @param {T} obj The object for which we are searching.
    103 * @param {number=} opt_fromIndex The index at which to start the search. If
    104 * omitted the search starts at index 0.
    105 * @return {number} The index of the first matching array element.
    106 * @template T
    107 */
    108goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES &&
    109 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
    110 goog.array.ARRAY_PROTOTYPE_.indexOf) ?
    111 function(arr, obj, opt_fromIndex) {
    112 goog.asserts.assert(arr.length != null);
    113
    114 return goog.array.ARRAY_PROTOTYPE_.indexOf.call(arr, obj, opt_fromIndex);
    115 } :
    116 function(arr, obj, opt_fromIndex) {
    117 var fromIndex = opt_fromIndex == null ?
    118 0 : (opt_fromIndex < 0 ?
    119 Math.max(0, arr.length + opt_fromIndex) : opt_fromIndex);
    120
    121 if (goog.isString(arr)) {
    122 // Array.prototype.indexOf uses === so only strings should be found.
    123 if (!goog.isString(obj) || obj.length != 1) {
    124 return -1;
    125 }
    126 return arr.indexOf(obj, fromIndex);
    127 }
    128
    129 for (var i = fromIndex; i < arr.length; i++) {
    130 if (i in arr && arr[i] === obj)
    131 return i;
    132 }
    133 return -1;
    134 };
    135
    136
    137/**
    138 * Returns the index of the last element of an array with a specified value, or
    139 * -1 if the element is not present in the array.
    140 *
    141 * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof}
    142 *
    143 * @param {!Array.<T>|!goog.array.ArrayLike} arr The array to be searched.
    144 * @param {T} obj The object for which we are searching.
    145 * @param {?number=} opt_fromIndex The index at which to start the search. If
    146 * omitted the search starts at the end of the array.
    147 * @return {number} The index of the last matching array element.
    148 * @template T
    149 */
    150goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES &&
    151 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
    152 goog.array.ARRAY_PROTOTYPE_.lastIndexOf) ?
    153 function(arr, obj, opt_fromIndex) {
    154 goog.asserts.assert(arr.length != null);
    155
    156 // Firefox treats undefined and null as 0 in the fromIndex argument which
    157 // leads it to always return -1
    158 var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;
    159 return goog.array.ARRAY_PROTOTYPE_.lastIndexOf.call(arr, obj, fromIndex);
    160 } :
    161 function(arr, obj, opt_fromIndex) {
    162 var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;
    163
    164 if (fromIndex < 0) {
    165 fromIndex = Math.max(0, arr.length + fromIndex);
    166 }
    167
    168 if (goog.isString(arr)) {
    169 // Array.prototype.lastIndexOf uses === so only strings should be found.
    170 if (!goog.isString(obj) || obj.length != 1) {
    171 return -1;
    172 }
    173 return arr.lastIndexOf(obj, fromIndex);
    174 }
    175
    176 for (var i = fromIndex; i >= 0; i--) {
    177 if (i in arr && arr[i] === obj)
    178 return i;
    179 }
    180 return -1;
    181 };
    182
    183
    184/**
    185 * Calls a function for each element in an array. Skips holes in the array.
    186 * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach}
    187 *
    188 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array like object over
    189 * which to iterate.
    190 * @param {?function(this: S, T, number, ?): ?} f The function to call for every
    191 * element. This function takes 3 arguments (the element, the index and the
    192 * array). The return value is ignored.
    193 * @param {S=} opt_obj The object to be used as the value of 'this' within f.
    194 * @template T,S
    195 */
    196goog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES &&
    197 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
    198 goog.array.ARRAY_PROTOTYPE_.forEach) ?
    199 function(arr, f, opt_obj) {
    200 goog.asserts.assert(arr.length != null);
    201
    202 goog.array.ARRAY_PROTOTYPE_.forEach.call(arr, f, opt_obj);
    203 } :
    204 function(arr, f, opt_obj) {
    205 var l = arr.length; // must be fixed during loop... see docs
    206 var arr2 = goog.isString(arr) ? arr.split('') : arr;
    207 for (var i = 0; i < l; i++) {
    208 if (i in arr2) {
    209 f.call(opt_obj, arr2[i], i, arr);
    210 }
    211 }
    212 };
    213
    214
    215/**
    216 * Calls a function for each element in an array, starting from the last
    217 * element rather than the first.
    218 *
    219 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
    220 * like object over which to iterate.
    221 * @param {?function(this: S, T, number, ?): ?} f The function to call for every
    222 * element. This function
    223 * takes 3 arguments (the element, the index and the array). The return
    224 * value is ignored.
    225 * @param {S=} opt_obj The object to be used as the value of 'this'
    226 * within f.
    227 * @template T,S
    228 */
    229goog.array.forEachRight = function(arr, f, opt_obj) {
    230 var l = arr.length; // must be fixed during loop... see docs
    231 var arr2 = goog.isString(arr) ? arr.split('') : arr;
    232 for (var i = l - 1; i >= 0; --i) {
    233 if (i in arr2) {
    234 f.call(opt_obj, arr2[i], i, arr);
    235 }
    236 }
    237};
    238
    239
    240/**
    241 * Calls a function for each element in an array, and if the function returns
    242 * true adds the element to a new array.
    243 *
    244 * See {@link http://tinyurl.com/developer-mozilla-org-array-filter}
    245 *
    246 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
    247 * like object over which to iterate.
    248 * @param {?function(this:S, T, number, ?):boolean} f The function to call for
    249 * every element. This function
    250 * takes 3 arguments (the element, the index and the array) and must
    251 * return a Boolean. If the return value is true the element is added to the
    252 * result array. If it is false the element is not included.
    253 * @param {S=} opt_obj The object to be used as the value of 'this'
    254 * within f.
    255 * @return {!Array.<T>} a new array in which only elements that passed the test
    256 * are present.
    257 * @template T,S
    258 */
    259goog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES &&
    260 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
    261 goog.array.ARRAY_PROTOTYPE_.filter) ?
    262 function(arr, f, opt_obj) {
    263 goog.asserts.assert(arr.length != null);
    264
    265 return goog.array.ARRAY_PROTOTYPE_.filter.call(arr, f, opt_obj);
    266 } :
    267 function(arr, f, opt_obj) {
    268 var l = arr.length; // must be fixed during loop... see docs
    269 var res = [];
    270 var resLength = 0;
    271 var arr2 = goog.isString(arr) ? arr.split('') : arr;
    272 for (var i = 0; i < l; i++) {
    273 if (i in arr2) {
    274 var val = arr2[i]; // in case f mutates arr2
    275 if (f.call(opt_obj, val, i, arr)) {
    276 res[resLength++] = val;
    277 }
    278 }
    279 }
    280 return res;
    281 };
    282
    283
    284/**
    285 * Calls a function for each element in an array and inserts the result into a
    286 * new array.
    287 *
    288 * See {@link http://tinyurl.com/developer-mozilla-org-array-map}
    289 *
    290 * @param {Array.<VALUE>|goog.array.ArrayLike} arr Array or array like object
    291 * over which to iterate.
    292 * @param {function(this:THIS, VALUE, number, ?): RESULT} f The function to call
    293 * for every element. This function takes 3 arguments (the element,
    294 * the index and the array) and should return something. The result will be
    295 * inserted into a new array.
    296 * @param {THIS=} opt_obj The object to be used as the value of 'this' within f.
    297 * @return {!Array.<RESULT>} a new array with the results from f.
    298 * @template THIS, VALUE, RESULT
    299 */
    300goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES &&
    301 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
    302 goog.array.ARRAY_PROTOTYPE_.map) ?
    303 function(arr, f, opt_obj) {
    304 goog.asserts.assert(arr.length != null);
    305
    306 return goog.array.ARRAY_PROTOTYPE_.map.call(arr, f, opt_obj);
    307 } :
    308 function(arr, f, opt_obj) {
    309 var l = arr.length; // must be fixed during loop... see docs
    310 var res = new Array(l);
    311 var arr2 = goog.isString(arr) ? arr.split('') : arr;
    312 for (var i = 0; i < l; i++) {
    313 if (i in arr2) {
    314 res[i] = f.call(opt_obj, arr2[i], i, arr);
    315 }
    316 }
    317 return res;
    318 };
    319
    320
    321/**
    322 * Passes every element of an array into a function and accumulates the result.
    323 *
    324 * See {@link http://tinyurl.com/developer-mozilla-org-array-reduce}
    325 *
    326 * For example:
    327 * var a = [1, 2, 3, 4];
    328 * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0);
    329 * returns 10
    330 *
    331 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
    332 * like object over which to iterate.
    333 * @param {?function(this:S, R, T, number, ?) : R} f The function to call for
    334 * every element. This function
    335 * takes 4 arguments (the function's previous result or the initial value,
    336 * the value of the current array element, the current array index, and the
    337 * array itself)
    338 * function(previousValue, currentValue, index, array).
    339 * @param {?} val The initial value to pass into the function on the first call.
    340 * @param {S=} opt_obj The object to be used as the value of 'this'
    341 * within f.
    342 * @return {R} Result of evaluating f repeatedly across the values of the array.
    343 * @template T,S,R
    344 */
    345goog.array.reduce = goog.NATIVE_ARRAY_PROTOTYPES &&
    346 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
    347 goog.array.ARRAY_PROTOTYPE_.reduce) ?
    348 function(arr, f, val, opt_obj) {
    349 goog.asserts.assert(arr.length != null);
    350 if (opt_obj) {
    351 f = goog.bind(f, opt_obj);
    352 }
    353 return goog.array.ARRAY_PROTOTYPE_.reduce.call(arr, f, val);
    354 } :
    355 function(arr, f, val, opt_obj) {
    356 var rval = val;
    357 goog.array.forEach(arr, function(val, index) {
    358 rval = f.call(opt_obj, rval, val, index, arr);
    359 });
    360 return rval;
    361 };
    362
    363
    364/**
    365 * Passes every element of an array into a function and accumulates the result,
    366 * starting from the last element and working towards the first.
    367 *
    368 * See {@link http://tinyurl.com/developer-mozilla-org-array-reduceright}
    369 *
    370 * For example:
    371 * var a = ['a', 'b', 'c'];
    372 * goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, '');
    373 * returns 'cba'
    374 *
    375 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
    376 * like object over which to iterate.
    377 * @param {?function(this:S, R, T, number, ?) : R} f The function to call for
    378 * every element. This function
    379 * takes 4 arguments (the function's previous result or the initial value,
    380 * the value of the current array element, the current array index, and the
    381 * array itself)
    382 * function(previousValue, currentValue, index, array).
    383 * @param {?} val The initial value to pass into the function on the first call.
    384 * @param {S=} opt_obj The object to be used as the value of 'this'
    385 * within f.
    386 * @return {R} Object returned as a result of evaluating f repeatedly across the
    387 * values of the array.
    388 * @template T,S,R
    389 */
    390goog.array.reduceRight = goog.NATIVE_ARRAY_PROTOTYPES &&
    391 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
    392 goog.array.ARRAY_PROTOTYPE_.reduceRight) ?
    393 function(arr, f, val, opt_obj) {
    394 goog.asserts.assert(arr.length != null);
    395 if (opt_obj) {
    396 f = goog.bind(f, opt_obj);
    397 }
    398 return goog.array.ARRAY_PROTOTYPE_.reduceRight.call(arr, f, val);
    399 } :
    400 function(arr, f, val, opt_obj) {
    401 var rval = val;
    402 goog.array.forEachRight(arr, function(val, index) {
    403 rval = f.call(opt_obj, rval, val, index, arr);
    404 });
    405 return rval;
    406 };
    407
    408
    409/**
    410 * Calls f for each element of an array. If any call returns true, some()
    411 * returns true (without checking the remaining elements). If all calls
    412 * return false, some() returns false.
    413 *
    414 * See {@link http://tinyurl.com/developer-mozilla-org-array-some}
    415 *
    416 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
    417 * like object over which to iterate.
    418 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
    419 * for every element. This function takes 3 arguments (the element, the
    420 * index and the array) and should return a boolean.
    421 * @param {S=} opt_obj The object to be used as the value of 'this'
    422 * within f.
    423 * @return {boolean} true if any element passes the test.
    424 * @template T,S
    425 */
    426goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES &&
    427 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
    428 goog.array.ARRAY_PROTOTYPE_.some) ?
    429 function(arr, f, opt_obj) {
    430 goog.asserts.assert(arr.length != null);
    431
    432 return goog.array.ARRAY_PROTOTYPE_.some.call(arr, f, opt_obj);
    433 } :
    434 function(arr, f, opt_obj) {
    435 var l = arr.length; // must be fixed during loop... see docs
    436 var arr2 = goog.isString(arr) ? arr.split('') : arr;
    437 for (var i = 0; i < l; i++) {
    438 if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {
    439 return true;
    440 }
    441 }
    442 return false;
    443 };
    444
    445
    446/**
    447 * Call f for each element of an array. If all calls return true, every()
    448 * returns true. If any call returns false, every() returns false and
    449 * does not continue to check the remaining elements.
    450 *
    451 * See {@link http://tinyurl.com/developer-mozilla-org-array-every}
    452 *
    453 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
    454 * like object over which to iterate.
    455 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
    456 * for every element. This function takes 3 arguments (the element, the
    457 * index and the array) and should return a boolean.
    458 * @param {S=} opt_obj The object to be used as the value of 'this'
    459 * within f.
    460 * @return {boolean} false if any element fails the test.
    461 * @template T,S
    462 */
    463goog.array.every = goog.NATIVE_ARRAY_PROTOTYPES &&
    464 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
    465 goog.array.ARRAY_PROTOTYPE_.every) ?
    466 function(arr, f, opt_obj) {
    467 goog.asserts.assert(arr.length != null);
    468
    469 return goog.array.ARRAY_PROTOTYPE_.every.call(arr, f, opt_obj);
    470 } :
    471 function(arr, f, opt_obj) {
    472 var l = arr.length; // must be fixed during loop... see docs
    473 var arr2 = goog.isString(arr) ? arr.split('') : arr;
    474 for (var i = 0; i < l; i++) {
    475 if (i in arr2 && !f.call(opt_obj, arr2[i], i, arr)) {
    476 return false;
    477 }
    478 }
    479 return true;
    480 };
    481
    482
    483/**
    484 * Counts the array elements that fulfill the predicate, i.e. for which the
    485 * callback function returns true. Skips holes in the array.
    486 *
    487 * @param {!(Array.<T>|goog.array.ArrayLike)} arr Array or array like object
    488 * over which to iterate.
    489 * @param {function(this: S, T, number, ?): boolean} f The function to call for
    490 * every element. Takes 3 arguments (the element, the index and the array).
    491 * @param {S=} opt_obj The object to be used as the value of 'this' within f.
    492 * @return {number} The number of the matching elements.
    493 * @template T,S
    494 */
    495goog.array.count = function(arr, f, opt_obj) {
    496 var count = 0;
    497 goog.array.forEach(arr, function(element, index, arr) {
    498 if (f.call(opt_obj, element, index, arr)) {
    499 ++count;
    500 }
    501 }, opt_obj);
    502 return count;
    503};
    504
    505
    506/**
    507 * Search an array for the first element that satisfies a given condition and
    508 * return that element.
    509 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
    510 * like object over which to iterate.
    511 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
    512 * for every element. This function takes 3 arguments (the element, the
    513 * index and the array) and should return a boolean.
    514 * @param {S=} opt_obj An optional "this" context for the function.
    515 * @return {?T} The first array element that passes the test, or null if no
    516 * element is found.
    517 * @template T,S
    518 */
    519goog.array.find = function(arr, f, opt_obj) {
    520 var i = goog.array.findIndex(arr, f, opt_obj);
    521 return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i];
    522};
    523
    524
    525/**
    526 * Search an array for the first element that satisfies a given condition and
    527 * return its index.
    528 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
    529 * like object over which to iterate.
    530 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
    531 * every element. This function
    532 * takes 3 arguments (the element, the index and the array) and should
    533 * return a boolean.
    534 * @param {S=} opt_obj An optional "this" context for the function.
    535 * @return {number} The index of the first array element that passes the test,
    536 * or -1 if no element is found.
    537 * @template T,S
    538 */
    539goog.array.findIndex = function(arr, f, opt_obj) {
    540 var l = arr.length; // must be fixed during loop... see docs
    541 var arr2 = goog.isString(arr) ? arr.split('') : arr;
    542 for (var i = 0; i < l; i++) {
    543 if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {
    544 return i;
    545 }
    546 }
    547 return -1;
    548};
    549
    550
    551/**
    552 * Search an array (in reverse order) for the last element that satisfies a
    553 * given condition and return that element.
    554 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
    555 * like object over which to iterate.
    556 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
    557 * for every element. This function
    558 * takes 3 arguments (the element, the index and the array) and should
    559 * return a boolean.
    560 * @param {S=} opt_obj An optional "this" context for the function.
    561 * @return {?T} The last array element that passes the test, or null if no
    562 * element is found.
    563 * @template T,S
    564 */
    565goog.array.findRight = function(arr, f, opt_obj) {
    566 var i = goog.array.findIndexRight(arr, f, opt_obj);
    567 return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i];
    568};
    569
    570
    571/**
    572 * Search an array (in reverse order) for the last element that satisfies a
    573 * given condition and return its index.
    574 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
    575 * like object over which to iterate.
    576 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
    577 * for every element. This function
    578 * takes 3 arguments (the element, the index and the array) and should
    579 * return a boolean.
    580 * @param {Object=} opt_obj An optional "this" context for the function.
    581 * @return {number} The index of the last array element that passes the test,
    582 * or -1 if no element is found.
    583 * @template T,S
    584 */
    585goog.array.findIndexRight = function(arr, f, opt_obj) {
    586 var l = arr.length; // must be fixed during loop... see docs
    587 var arr2 = goog.isString(arr) ? arr.split('') : arr;
    588 for (var i = l - 1; i >= 0; i--) {
    589 if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {
    590 return i;
    591 }
    592 }
    593 return -1;
    594};
    595
    596
    597/**
    598 * Whether the array contains the given object.
    599 * @param {goog.array.ArrayLike} arr The array to test for the presence of the
    600 * element.
    601 * @param {*} obj The object for which to test.
    602 * @return {boolean} true if obj is present.
    603 */
    604goog.array.contains = function(arr, obj) {
    605 return goog.array.indexOf(arr, obj) >= 0;
    606};
    607
    608
    609/**
    610 * Whether the array is empty.
    611 * @param {goog.array.ArrayLike} arr The array to test.
    612 * @return {boolean} true if empty.
    613 */
    614goog.array.isEmpty = function(arr) {
    615 return arr.length == 0;
    616};
    617
    618
    619/**
    620 * Clears the array.
    621 * @param {goog.array.ArrayLike} arr Array or array like object to clear.
    622 */
    623goog.array.clear = function(arr) {
    624 // For non real arrays we don't have the magic length so we delete the
    625 // indices.
    626 if (!goog.isArray(arr)) {
    627 for (var i = arr.length - 1; i >= 0; i--) {
    628 delete arr[i];
    629 }
    630 }
    631 arr.length = 0;
    632};
    633
    634
    635/**
    636 * Pushes an item into an array, if it's not already in the array.
    637 * @param {Array.<T>} arr Array into which to insert the item.
    638 * @param {T} obj Value to add.
    639 * @template T
    640 */
    641goog.array.insert = function(arr, obj) {
    642 if (!goog.array.contains(arr, obj)) {
    643 arr.push(obj);
    644 }
    645};
    646
    647
    648/**
    649 * Inserts an object at the given index of the array.
    650 * @param {goog.array.ArrayLike} arr The array to modify.
    651 * @param {*} obj The object to insert.
    652 * @param {number=} opt_i The index at which to insert the object. If omitted,
    653 * treated as 0. A negative index is counted from the end of the array.
    654 */
    655goog.array.insertAt = function(arr, obj, opt_i) {
    656 goog.array.splice(arr, opt_i, 0, obj);
    657};
    658
    659
    660/**
    661 * Inserts at the given index of the array, all elements of another array.
    662 * @param {goog.array.ArrayLike} arr The array to modify.
    663 * @param {goog.array.ArrayLike} elementsToAdd The array of elements to add.
    664 * @param {number=} opt_i The index at which to insert the object. If omitted,
    665 * treated as 0. A negative index is counted from the end of the array.
    666 */
    667goog.array.insertArrayAt = function(arr, elementsToAdd, opt_i) {
    668 goog.partial(goog.array.splice, arr, opt_i, 0).apply(null, elementsToAdd);
    669};
    670
    671
    672/**
    673 * Inserts an object into an array before a specified object.
    674 * @param {Array.<T>} arr The array to modify.
    675 * @param {T} obj The object to insert.
    676 * @param {T=} opt_obj2 The object before which obj should be inserted. If obj2
    677 * is omitted or not found, obj is inserted at the end of the array.
    678 * @template T
    679 */
    680goog.array.insertBefore = function(arr, obj, opt_obj2) {
    681 var i;
    682 if (arguments.length == 2 || (i = goog.array.indexOf(arr, opt_obj2)) < 0) {
    683 arr.push(obj);
    684 } else {
    685 goog.array.insertAt(arr, obj, i);
    686 }
    687};
    688
    689
    690/**
    691 * Removes the first occurrence of a particular value from an array.
    692 * @param {Array.<T>|goog.array.ArrayLike} arr Array from which to remove
    693 * value.
    694 * @param {T} obj Object to remove.
    695 * @return {boolean} True if an element was removed.
    696 * @template T
    697 */
    698goog.array.remove = function(arr, obj) {
    699 var i = goog.array.indexOf(arr, obj);
    700 var rv;
    701 if ((rv = i >= 0)) {
    702 goog.array.removeAt(arr, i);
    703 }
    704 return rv;
    705};
    706
    707
    708/**
    709 * Removes from an array the element at index i
    710 * @param {goog.array.ArrayLike} arr Array or array like object from which to
    711 * remove value.
    712 * @param {number} i The index to remove.
    713 * @return {boolean} True if an element was removed.
    714 */
    715goog.array.removeAt = function(arr, i) {
    716 goog.asserts.assert(arr.length != null);
    717
    718 // use generic form of splice
    719 // splice returns the removed items and if successful the length of that
    720 // will be 1
    721 return goog.array.ARRAY_PROTOTYPE_.splice.call(arr, i, 1).length == 1;
    722};
    723
    724
    725/**
    726 * Removes the first value that satisfies the given condition.
    727 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
    728 * like object over which to iterate.
    729 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
    730 * for every element. This function
    731 * takes 3 arguments (the element, the index and the array) and should
    732 * return a boolean.
    733 * @param {S=} opt_obj An optional "this" context for the function.
    734 * @return {boolean} True if an element was removed.
    735 * @template T,S
    736 */
    737goog.array.removeIf = function(arr, f, opt_obj) {
    738 var i = goog.array.findIndex(arr, f, opt_obj);
    739 if (i >= 0) {
    740 goog.array.removeAt(arr, i);
    741 return true;
    742 }
    743 return false;
    744};
    745
    746
    747/**
    748 * Removes all values that satisfy the given condition.
    749 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
    750 * like object over which to iterate.
    751 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
    752 * for every element. This function
    753 * takes 3 arguments (the element, the index and the array) and should
    754 * return a boolean.
    755 * @param {S=} opt_obj An optional "this" context for the function.
    756 * @return {number} The number of items removed
    757 * @template T,S
    758 */
    759goog.array.removeAllIf = function(arr, f, opt_obj) {
    760 var removedCount = 0;
    761 goog.array.forEachRight(arr, function(val, index) {
    762 if (f.call(opt_obj, val, index, arr)) {
    763 if (goog.array.removeAt(arr, index)) {
    764 removedCount++;
    765 }
    766 }
    767 });
    768 return removedCount;
    769};
    770
    771
    772/**
    773 * Returns a new array that is the result of joining the arguments. If arrays
    774 * are passed then their items are added, however, if non-arrays are passed they
    775 * will be added to the return array as is.
    776 *
    777 * Note that ArrayLike objects will be added as is, rather than having their
    778 * items added.
    779 *
    780 * goog.array.concat([1, 2], [3, 4]) -> [1, 2, 3, 4]
    781 * goog.array.concat(0, [1, 2]) -> [0, 1, 2]
    782 * goog.array.concat([1, 2], null) -> [1, 2, null]
    783 *
    784 * There is bug in all current versions of IE (6, 7 and 8) where arrays created
    785 * in an iframe become corrupted soon (not immediately) after the iframe is
    786 * destroyed. This is common if loading data via goog.net.IframeIo, for example.
    787 * This corruption only affects the concat method which will start throwing
    788 * Catastrophic Errors (#-2147418113).
    789 *
    790 * See http://endoflow.com/scratch/corrupted-arrays.html for a test case.
    791 *
    792 * Internally goog.array should use this, so that all methods will continue to
    793 * work on these broken array objects.
    794 *
    795 * @param {...*} var_args Items to concatenate. Arrays will have each item
    796 * added, while primitives and objects will be added as is.
    797 * @return {!Array} The new resultant array.
    798 */
    799goog.array.concat = function(var_args) {
    800 return goog.array.ARRAY_PROTOTYPE_.concat.apply(
    801 goog.array.ARRAY_PROTOTYPE_, arguments);
    802};
    803
    804
    805/**
    806 * Returns a new array that contains the contents of all the arrays passed.
    807 * @param {...!Array.<T>} var_args
    808 * @return {!Array.<T>}
    809 * @template T
    810 */
    811goog.array.join = function(var_args) {
    812 return goog.array.ARRAY_PROTOTYPE_.concat.apply(
    813 goog.array.ARRAY_PROTOTYPE_, arguments);
    814};
    815
    816
    817/**
    818 * Converts an object to an array.
    819 * @param {Array.<T>|goog.array.ArrayLike} object The object to convert to an
    820 * array.
    821 * @return {!Array.<T>} The object converted into an array. If object has a
    822 * length property, every property indexed with a non-negative number
    823 * less than length will be included in the result. If object does not
    824 * have a length property, an empty array will be returned.
    825 * @template T
    826 */
    827goog.array.toArray = function(object) {
    828 var length = object.length;
    829
    830 // If length is not a number the following it false. This case is kept for
    831 // backwards compatibility since there are callers that pass objects that are
    832 // not array like.
    833 if (length > 0) {
    834 var rv = new Array(length);
    835 for (var i = 0; i < length; i++) {
    836 rv[i] = object[i];
    837 }
    838 return rv;
    839 }
    840 return [];
    841};
    842
    843
    844/**
    845 * Does a shallow copy of an array.
    846 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array-like object to
    847 * clone.
    848 * @return {!Array.<T>} Clone of the input array.
    849 * @template T
    850 */
    851goog.array.clone = goog.array.toArray;
    852
    853
    854/**
    855 * Extends an array with another array, element, or "array like" object.
    856 * This function operates 'in-place', it does not create a new Array.
    857 *
    858 * Example:
    859 * var a = [];
    860 * goog.array.extend(a, [0, 1]);
    861 * a; // [0, 1]
    862 * goog.array.extend(a, 2);
    863 * a; // [0, 1, 2]
    864 *
    865 * @param {Array.<VALUE>} arr1 The array to modify.
    866 * @param {...(Array.<VALUE>|VALUE)} var_args The elements or arrays of elements
    867 * to add to arr1.
    868 * @template VALUE
    869 */
    870goog.array.extend = function(arr1, var_args) {
    871 for (var i = 1; i < arguments.length; i++) {
    872 var arr2 = arguments[i];
    873 // If we have an Array or an Arguments object we can just call push
    874 // directly.
    875 var isArrayLike;
    876 if (goog.isArray(arr2) ||
    877 // Detect Arguments. ES5 says that the [[Class]] of an Arguments object
    878 // is "Arguments" but only V8 and JSC/Safari gets this right. We instead
    879 // detect Arguments by checking for array like and presence of "callee".
    880 (isArrayLike = goog.isArrayLike(arr2)) &&
    881 // The getter for callee throws an exception in strict mode
    882 // according to section 10.6 in ES5 so check for presence instead.
    883 Object.prototype.hasOwnProperty.call(arr2, 'callee')) {
    884 arr1.push.apply(arr1, arr2);
    885 } else if (isArrayLike) {
    886 // Otherwise loop over arr2 to prevent copying the object.
    887 var len1 = arr1.length;
    888 var len2 = arr2.length;
    889 for (var j = 0; j < len2; j++) {
    890 arr1[len1 + j] = arr2[j];
    891 }
    892 } else {
    893 arr1.push(arr2);
    894 }
    895 }
    896};
    897
    898
    899/**
    900 * Adds or removes elements from an array. This is a generic version of Array
    901 * splice. This means that it might work on other objects similar to arrays,
    902 * such as the arguments object.
    903 *
    904 * @param {Array.<T>|goog.array.ArrayLike} arr The array to modify.
    905 * @param {number|undefined} index The index at which to start changing the
    906 * array. If not defined, treated as 0.
    907 * @param {number} howMany How many elements to remove (0 means no removal. A
    908 * value below 0 is treated as zero and so is any other non number. Numbers
    909 * are floored).
    910 * @param {...T} var_args Optional, additional elements to insert into the
    911 * array.
    912 * @return {!Array.<T>} the removed elements.
    913 * @template T
    914 */
    915goog.array.splice = function(arr, index, howMany, var_args) {
    916 goog.asserts.assert(arr.length != null);
    917
    918 return goog.array.ARRAY_PROTOTYPE_.splice.apply(
    919 arr, goog.array.slice(arguments, 1));
    920};
    921
    922
    923/**
    924 * Returns a new array from a segment of an array. This is a generic version of
    925 * Array slice. This means that it might work on other objects similar to
    926 * arrays, such as the arguments object.
    927 *
    928 * @param {Array.<T>|goog.array.ArrayLike} arr The array from
    929 * which to copy a segment.
    930 * @param {number} start The index of the first element to copy.
    931 * @param {number=} opt_end The index after the last element to copy.
    932 * @return {!Array.<T>} A new array containing the specified segment of the
    933 * original array.
    934 * @template T
    935 */
    936goog.array.slice = function(arr, start, opt_end) {
    937 goog.asserts.assert(arr.length != null);
    938
    939 // passing 1 arg to slice is not the same as passing 2 where the second is
    940 // null or undefined (in that case the second argument is treated as 0).
    941 // we could use slice on the arguments object and then use apply instead of
    942 // testing the length
    943 if (arguments.length <= 2) {
    944 return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start);
    945 } else {
    946 return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start, opt_end);
    947 }
    948};
    949
    950
    951/**
    952 * Removes all duplicates from an array (retaining only the first
    953 * occurrence of each array element). This function modifies the
    954 * array in place and doesn't change the order of the non-duplicate items.
    955 *
    956 * For objects, duplicates are identified as having the same unique ID as
    957 * defined by {@link goog.getUid}.
    958 *
    959 * Alternatively you can specify a custom hash function that returns a unique
    960 * value for each item in the array it should consider unique.
    961 *
    962 * Runtime: N,
    963 * Worstcase space: 2N (no dupes)
    964 *
    965 * @param {Array.<T>|goog.array.ArrayLike} arr The array from which to remove
    966 * duplicates.
    967 * @param {Array=} opt_rv An optional array in which to return the results,
    968 * instead of performing the removal inplace. If specified, the original
    969 * array will remain unchanged.
    970 * @param {function(T):string=} opt_hashFn An optional function to use to
    971 * apply to every item in the array. This function should return a unique
    972 * value for each item in the array it should consider unique.
    973 * @template T
    974 */
    975goog.array.removeDuplicates = function(arr, opt_rv, opt_hashFn) {
    976 var returnArray = opt_rv || arr;
    977 var defaultHashFn = function(item) {
    978 // Prefix each type with a single character representing the type to
    979 // prevent conflicting keys (e.g. true and 'true').
    980 return goog.isObject(current) ? 'o' + goog.getUid(current) :
    981 (typeof current).charAt(0) + current;
    982 };
    983 var hashFn = opt_hashFn || defaultHashFn;
    984
    985 var seen = {}, cursorInsert = 0, cursorRead = 0;
    986 while (cursorRead < arr.length) {
    987 var current = arr[cursorRead++];
    988 var key = hashFn(current);
    989 if (!Object.prototype.hasOwnProperty.call(seen, key)) {
    990 seen[key] = true;
    991 returnArray[cursorInsert++] = current;
    992 }
    993 }
    994 returnArray.length = cursorInsert;
    995};
    996
    997
    998/**
    999 * Searches the specified array for the specified target using the binary
    1000 * search algorithm. If no opt_compareFn is specified, elements are compared
    1001 * using <code>goog.array.defaultCompare</code>, which compares the elements
    1002 * using the built in < and > operators. This will produce the expected
    1003 * behavior for homogeneous arrays of String(s) and Number(s). The array
    1004 * specified <b>must</b> be sorted in ascending order (as defined by the
    1005 * comparison function). If the array is not sorted, results are undefined.
    1006 * If the array contains multiple instances of the specified target value, any
    1007 * of these instances may be found.
    1008 *
    1009 * Runtime: O(log n)
    1010 *
    1011 * @param {Array.<VALUE>|goog.array.ArrayLike} arr The array to be searched.
    1012 * @param {TARGET} target The sought value.
    1013 * @param {function(TARGET, VALUE): number=} opt_compareFn Optional comparison
    1014 * function by which the array is ordered. Should take 2 arguments to
    1015 * compare, and return a negative number, zero, or a positive number
    1016 * depending on whether the first argument is less than, equal to, or
    1017 * greater than the second.
    1018 * @return {number} Lowest index of the target value if found, otherwise
    1019 * (-(insertion point) - 1). The insertion point is where the value should
    1020 * be inserted into arr to preserve the sorted property. Return value >= 0
    1021 * iff target is found.
    1022 * @template TARGET, VALUE
    1023 */
    1024goog.array.binarySearch = function(arr, target, opt_compareFn) {
    1025 return goog.array.binarySearch_(arr,
    1026 opt_compareFn || goog.array.defaultCompare, false /* isEvaluator */,
    1027 target);
    1028};
    1029
    1030
    1031/**
    1032 * Selects an index in the specified array using the binary search algorithm.
    1033 * The evaluator receives an element and determines whether the desired index
    1034 * is before, at, or after it. The evaluator must be consistent (formally,
    1035 * goog.array.map(goog.array.map(arr, evaluator, opt_obj), goog.math.sign)
    1036 * must be monotonically non-increasing).
    1037 *
    1038 * Runtime: O(log n)
    1039 *
    1040 * @param {Array.<VALUE>|goog.array.ArrayLike} arr The array to be searched.
    1041 * @param {function(this:THIS, VALUE, number, ?): number} evaluator
    1042 * Evaluator function that receives 3 arguments (the element, the index and
    1043 * the array). Should return a negative number, zero, or a positive number
    1044 * depending on whether the desired index is before, at, or after the
    1045 * element passed to it.
    1046 * @param {THIS=} opt_obj The object to be used as the value of 'this'
    1047 * within evaluator.
    1048 * @return {number} Index of the leftmost element matched by the evaluator, if
    1049 * such exists; otherwise (-(insertion point) - 1). The insertion point is
    1050 * the index of the first element for which the evaluator returns negative,
    1051 * or arr.length if no such element exists. The return value is non-negative
    1052 * iff a match is found.
    1053 * @template THIS, VALUE
    1054 */
    1055goog.array.binarySelect = function(arr, evaluator, opt_obj) {
    1056 return goog.array.binarySearch_(arr, evaluator, true /* isEvaluator */,
    1057 undefined /* opt_target */, opt_obj);
    1058};
    1059
    1060
    1061/**
    1062 * Implementation of a binary search algorithm which knows how to use both
    1063 * comparison functions and evaluators. If an evaluator is provided, will call
    1064 * the evaluator with the given optional data object, conforming to the
    1065 * interface defined in binarySelect. Otherwise, if a comparison function is
    1066 * provided, will call the comparison function against the given data object.
    1067 *
    1068 * This implementation purposefully does not use goog.bind or goog.partial for
    1069 * performance reasons.
    1070 *
    1071 * Runtime: O(log n)
    1072 *
    1073 * @param {Array.<VALUE>|goog.array.ArrayLike} arr The array to be searched.
    1074 * @param {function(TARGET, VALUE): number|
    1075 * function(this:THIS, VALUE, number, ?): number} compareFn Either an
    1076 * evaluator or a comparison function, as defined by binarySearch
    1077 * and binarySelect above.
    1078 * @param {boolean} isEvaluator Whether the function is an evaluator or a
    1079 * comparison function.
    1080 * @param {TARGET=} opt_target If the function is a comparison function, then
    1081 * this is the target to binary search for.
    1082 * @param {THIS=} opt_selfObj If the function is an evaluator, this is an
    1083 * optional this object for the evaluator.
    1084 * @return {number} Lowest index of the target value if found, otherwise
    1085 * (-(insertion point) - 1). The insertion point is where the value should
    1086 * be inserted into arr to preserve the sorted property. Return value >= 0
    1087 * iff target is found.
    1088 * @template THIS, VALUE, TARGET
    1089 * @private
    1090 */
    1091goog.array.binarySearch_ = function(arr, compareFn, isEvaluator, opt_target,
    1092 opt_selfObj) {
    1093 var left = 0; // inclusive
    1094 var right = arr.length; // exclusive
    1095 var found;
    1096 while (left < right) {
    1097 var middle = (left + right) >> 1;
    1098 var compareResult;
    1099 if (isEvaluator) {
    1100 compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr);
    1101 } else {
    1102 compareResult = compareFn(opt_target, arr[middle]);
    1103 }
    1104 if (compareResult > 0) {
    1105 left = middle + 1;
    1106 } else {
    1107 right = middle;
    1108 // We are looking for the lowest index so we can't return immediately.
    1109 found = !compareResult;
    1110 }
    1111 }
    1112 // left is the index if found, or the insertion point otherwise.
    1113 // ~left is a shorthand for -left - 1.
    1114 return found ? left : ~left;
    1115};
    1116
    1117
    1118/**
    1119 * Sorts the specified array into ascending order. If no opt_compareFn is
    1120 * specified, elements are compared using
    1121 * <code>goog.array.defaultCompare</code>, which compares the elements using
    1122 * the built in < and > operators. This will produce the expected behavior
    1123 * for homogeneous arrays of String(s) and Number(s), unlike the native sort,
    1124 * but will give unpredictable results for heterogenous lists of strings and
    1125 * numbers with different numbers of digits.
    1126 *
    1127 * This sort is not guaranteed to be stable.
    1128 *
    1129 * Runtime: Same as <code>Array.prototype.sort</code>
    1130 *
    1131 * @param {Array.<T>} arr The array to be sorted.
    1132 * @param {?function(T,T):number=} opt_compareFn Optional comparison
    1133 * function by which the
    1134 * array is to be ordered. Should take 2 arguments to compare, and return a
    1135 * negative number, zero, or a positive number depending on whether the
    1136 * first argument is less than, equal to, or greater than the second.
    1137 * @template T
    1138 */
    1139goog.array.sort = function(arr, opt_compareFn) {
    1140 // TODO(arv): Update type annotation since null is not accepted.
    1141 arr.sort(opt_compareFn || goog.array.defaultCompare);
    1142};
    1143
    1144
    1145/**
    1146 * Sorts the specified array into ascending order in a stable way. If no
    1147 * opt_compareFn is specified, elements are compared using
    1148 * <code>goog.array.defaultCompare</code>, which compares the elements using
    1149 * the built in < and > operators. This will produce the expected behavior
    1150 * for homogeneous arrays of String(s) and Number(s).
    1151 *
    1152 * Runtime: Same as <code>Array.prototype.sort</code>, plus an additional
    1153 * O(n) overhead of copying the array twice.
    1154 *
    1155 * @param {Array.<T>} arr The array to be sorted.
    1156 * @param {?function(T, T): number=} opt_compareFn Optional comparison function
    1157 * by which the array is to be ordered. Should take 2 arguments to compare,
    1158 * and return a negative number, zero, or a positive number depending on
    1159 * whether the first argument is less than, equal to, or greater than the
    1160 * second.
    1161 * @template T
    1162 */
    1163goog.array.stableSort = function(arr, opt_compareFn) {
    1164 for (var i = 0; i < arr.length; i++) {
    1165 arr[i] = {index: i, value: arr[i]};
    1166 }
    1167 var valueCompareFn = opt_compareFn || goog.array.defaultCompare;
    1168 function stableCompareFn(obj1, obj2) {
    1169 return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index;
    1170 };
    1171 goog.array.sort(arr, stableCompareFn);
    1172 for (var i = 0; i < arr.length; i++) {
    1173 arr[i] = arr[i].value;
    1174 }
    1175};
    1176
    1177
    1178/**
    1179 * Sorts an array of objects by the specified object key and compare
    1180 * function. If no compare function is provided, the key values are
    1181 * compared in ascending order using <code>goog.array.defaultCompare</code>.
    1182 * This won't work for keys that get renamed by the compiler. So use
    1183 * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}.
    1184 * @param {Array.<Object>} arr An array of objects to sort.
    1185 * @param {string} key The object key to sort by.
    1186 * @param {Function=} opt_compareFn The function to use to compare key
    1187 * values.
    1188 */
    1189goog.array.sortObjectsByKey = function(arr, key, opt_compareFn) {
    1190 var compare = opt_compareFn || goog.array.defaultCompare;
    1191 goog.array.sort(arr, function(a, b) {
    1192 return compare(a[key], b[key]);
    1193 });
    1194};
    1195
    1196
    1197/**
    1198 * Tells if the array is sorted.
    1199 * @param {!Array.<T>} arr The array.
    1200 * @param {?function(T,T):number=} opt_compareFn Function to compare the
    1201 * array elements.
    1202 * Should take 2 arguments to compare, and return a negative number, zero,
    1203 * or a positive number depending on whether the first argument is less
    1204 * than, equal to, or greater than the second.
    1205 * @param {boolean=} opt_strict If true no equal elements are allowed.
    1206 * @return {boolean} Whether the array is sorted.
    1207 * @template T
    1208 */
    1209goog.array.isSorted = function(arr, opt_compareFn, opt_strict) {
    1210 var compare = opt_compareFn || goog.array.defaultCompare;
    1211 for (var i = 1; i < arr.length; i++) {
    1212 var compareResult = compare(arr[i - 1], arr[i]);
    1213 if (compareResult > 0 || compareResult == 0 && opt_strict) {
    1214 return false;
    1215 }
    1216 }
    1217 return true;
    1218};
    1219
    1220
    1221/**
    1222 * Compares two arrays for equality. Two arrays are considered equal if they
    1223 * have the same length and their corresponding elements are equal according to
    1224 * the comparison function.
    1225 *
    1226 * @param {goog.array.ArrayLike} arr1 The first array to compare.
    1227 * @param {goog.array.ArrayLike} arr2 The second array to compare.
    1228 * @param {Function=} opt_equalsFn Optional comparison function.
    1229 * Should take 2 arguments to compare, and return true if the arguments
    1230 * are equal. Defaults to {@link goog.array.defaultCompareEquality} which
    1231 * compares the elements using the built-in '===' operator.
    1232 * @return {boolean} Whether the two arrays are equal.
    1233 */
    1234goog.array.equals = function(arr1, arr2, opt_equalsFn) {
    1235 if (!goog.isArrayLike(arr1) || !goog.isArrayLike(arr2) ||
    1236 arr1.length != arr2.length) {
    1237 return false;
    1238 }
    1239 var l = arr1.length;
    1240 var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality;
    1241 for (var i = 0; i < l; i++) {
    1242 if (!equalsFn(arr1[i], arr2[i])) {
    1243 return false;
    1244 }
    1245 }
    1246 return true;
    1247};
    1248
    1249
    1250/**
    1251 * 3-way array compare function.
    1252 * @param {!Array.<VALUE>|!goog.array.ArrayLike} arr1 The first array to
    1253 * compare.
    1254 * @param {!Array.<VALUE>|!goog.array.ArrayLike} arr2 The second array to
    1255 * compare.
    1256 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
    1257 * function by which the array is to be ordered. Should take 2 arguments to
    1258 * compare, and return a negative number, zero, or a positive number
    1259 * depending on whether the first argument is less than, equal to, or
    1260 * greater than the second.
    1261 * @return {number} Negative number, zero, or a positive number depending on
    1262 * whether the first argument is less than, equal to, or greater than the
    1263 * second.
    1264 * @template VALUE
    1265 */
    1266goog.array.compare3 = function(arr1, arr2, opt_compareFn) {
    1267 var compare = opt_compareFn || goog.array.defaultCompare;
    1268 var l = Math.min(arr1.length, arr2.length);
    1269 for (var i = 0; i < l; i++) {
    1270 var result = compare(arr1[i], arr2[i]);
    1271 if (result != 0) {
    1272 return result;
    1273 }
    1274 }
    1275 return goog.array.defaultCompare(arr1.length, arr2.length);
    1276};
    1277
    1278
    1279/**
    1280 * Compares its two arguments for order, using the built in < and >
    1281 * operators.
    1282 * @param {VALUE} a The first object to be compared.
    1283 * @param {VALUE} b The second object to be compared.
    1284 * @return {number} A negative number, zero, or a positive number as the first
    1285 * argument is less than, equal to, or greater than the second.
    1286 * @template VALUE
    1287 */
    1288goog.array.defaultCompare = function(a, b) {
    1289 return a > b ? 1 : a < b ? -1 : 0;
    1290};
    1291
    1292
    1293/**
    1294 * Compares its two arguments for equality, using the built in === operator.
    1295 * @param {*} a The first object to compare.
    1296 * @param {*} b The second object to compare.
    1297 * @return {boolean} True if the two arguments are equal, false otherwise.
    1298 */
    1299goog.array.defaultCompareEquality = function(a, b) {
    1300 return a === b;
    1301};
    1302
    1303
    1304/**
    1305 * Inserts a value into a sorted array. The array is not modified if the
    1306 * value is already present.
    1307 * @param {Array.<VALUE>|goog.array.ArrayLike} array The array to modify.
    1308 * @param {VALUE} value The object to insert.
    1309 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
    1310 * function by which the array is ordered. Should take 2 arguments to
    1311 * compare, and return a negative number, zero, or a positive number
    1312 * depending on whether the first argument is less than, equal to, or
    1313 * greater than the second.
    1314 * @return {boolean} True if an element was inserted.
    1315 * @template VALUE
    1316 */
    1317goog.array.binaryInsert = function(array, value, opt_compareFn) {
    1318 var index = goog.array.binarySearch(array, value, opt_compareFn);
    1319 if (index < 0) {
    1320 goog.array.insertAt(array, value, -(index + 1));
    1321 return true;
    1322 }
    1323 return false;
    1324};
    1325
    1326
    1327/**
    1328 * Removes a value from a sorted array.
    1329 * @param {!Array.<VALUE>|!goog.array.ArrayLike} array The array to modify.
    1330 * @param {VALUE} value The object to remove.
    1331 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
    1332 * function by which the array is ordered. Should take 2 arguments to
    1333 * compare, and return a negative number, zero, or a positive number
    1334 * depending on whether the first argument is less than, equal to, or
    1335 * greater than the second.
    1336 * @return {boolean} True if an element was removed.
    1337 * @template VALUE
    1338 */
    1339goog.array.binaryRemove = function(array, value, opt_compareFn) {
    1340 var index = goog.array.binarySearch(array, value, opt_compareFn);
    1341 return (index >= 0) ? goog.array.removeAt(array, index) : false;
    1342};
    1343
    1344
    1345/**
    1346 * Splits an array into disjoint buckets according to a splitting function.
    1347 * @param {Array.<T>} array The array.
    1348 * @param {function(this:S, T,number,Array.<T>):?} sorter Function to call for
    1349 * every element. This takes 3 arguments (the element, the index and the
    1350 * array) and must return a valid object key (a string, number, etc), or
    1351 * undefined, if that object should not be placed in a bucket.
    1352 * @param {S=} opt_obj The object to be used as the value of 'this' within
    1353 * sorter.
    1354 * @return {!Object} An object, with keys being all of the unique return values
    1355 * of sorter, and values being arrays containing the items for
    1356 * which the splitter returned that key.
    1357 * @template T,S
    1358 */
    1359goog.array.bucket = function(array, sorter, opt_obj) {
    1360 var buckets = {};
    1361
    1362 for (var i = 0; i < array.length; i++) {
    1363 var value = array[i];
    1364 var key = sorter.call(opt_obj, value, i, array);
    1365 if (goog.isDef(key)) {
    1366 // Push the value to the right bucket, creating it if necessary.
    1367 var bucket = buckets[key] || (buckets[key] = []);
    1368 bucket.push(value);
    1369 }
    1370 }
    1371
    1372 return buckets;
    1373};
    1374
    1375
    1376/**
    1377 * Creates a new object built from the provided array and the key-generation
    1378 * function.
    1379 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array like object over
    1380 * which to iterate whose elements will be the values in the new object.
    1381 * @param {?function(this:S, T, number, ?) : string} keyFunc The function to
    1382 * call for every element. This function takes 3 arguments (the element, the
    1383 * index and the array) and should return a string that will be used as the
    1384 * key for the element in the new object. If the function returns the same
    1385 * key for more than one element, the value for that key is
    1386 * implementation-defined.
    1387 * @param {S=} opt_obj The object to be used as the value of 'this'
    1388 * within keyFunc.
    1389 * @return {!Object.<T>} The new object.
    1390 * @template T,S
    1391 */
    1392goog.array.toObject = function(arr, keyFunc, opt_obj) {
    1393 var ret = {};
    1394 goog.array.forEach(arr, function(element, index) {
    1395 ret[keyFunc.call(opt_obj, element, index, arr)] = element;
    1396 });
    1397 return ret;
    1398};
    1399
    1400
    1401/**
    1402 * Creates a range of numbers in an arithmetic progression.
    1403 *
    1404 * Range takes 1, 2, or 3 arguments:
    1405 * <pre>
    1406 * range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4]
    1407 * range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4]
    1408 * range(-2, -5, -1) produces [-2, -3, -4]
    1409 * range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5.
    1410 * </pre>
    1411 *
    1412 * @param {number} startOrEnd The starting value of the range if an end argument
    1413 * is provided. Otherwise, the start value is 0, and this is the end value.
    1414 * @param {number=} opt_end The optional end value of the range.
    1415 * @param {number=} opt_step The step size between range values. Defaults to 1
    1416 * if opt_step is undefined or 0.
    1417 * @return {!Array.<number>} An array of numbers for the requested range. May be
    1418 * an empty array if adding the step would not converge toward the end
    1419 * value.
    1420 */
    1421goog.array.range = function(startOrEnd, opt_end, opt_step) {
    1422 var array = [];
    1423 var start = 0;
    1424 var end = startOrEnd;
    1425 var step = opt_step || 1;
    1426 if (opt_end !== undefined) {
    1427 start = startOrEnd;
    1428 end = opt_end;
    1429 }
    1430
    1431 if (step * (end - start) < 0) {
    1432 // Sign mismatch: start + step will never reach the end value.
    1433 return [];
    1434 }
    1435
    1436 if (step > 0) {
    1437 for (var i = start; i < end; i += step) {
    1438 array.push(i);
    1439 }
    1440 } else {
    1441 for (var i = start; i > end; i += step) {
    1442 array.push(i);
    1443 }
    1444 }
    1445 return array;
    1446};
    1447
    1448
    1449/**
    1450 * Returns an array consisting of the given value repeated N times.
    1451 *
    1452 * @param {VALUE} value The value to repeat.
    1453 * @param {number} n The repeat count.
    1454 * @return {!Array.<VALUE>} An array with the repeated value.
    1455 * @template VALUE
    1456 */
    1457goog.array.repeat = function(value, n) {
    1458 var array = [];
    1459 for (var i = 0; i < n; i++) {
    1460 array[i] = value;
    1461 }
    1462 return array;
    1463};
    1464
    1465
    1466/**
    1467 * Returns an array consisting of every argument with all arrays
    1468 * expanded in-place recursively.
    1469 *
    1470 * @param {...*} var_args The values to flatten.
    1471 * @return {!Array} An array containing the flattened values.
    1472 */
    1473goog.array.flatten = function(var_args) {
    1474 var result = [];
    1475 for (var i = 0; i < arguments.length; i++) {
    1476 var element = arguments[i];
    1477 if (goog.isArray(element)) {
    1478 result.push.apply(result, goog.array.flatten.apply(null, element));
    1479 } else {
    1480 result.push(element);
    1481 }
    1482 }
    1483 return result;
    1484};
    1485
    1486
    1487/**
    1488 * Rotates an array in-place. After calling this method, the element at
    1489 * index i will be the element previously at index (i - n) %
    1490 * array.length, for all values of i between 0 and array.length - 1,
    1491 * inclusive.
    1492 *
    1493 * For example, suppose list comprises [t, a, n, k, s]. After invoking
    1494 * rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k].
    1495 *
    1496 * @param {!Array.<T>} array The array to rotate.
    1497 * @param {number} n The amount to rotate.
    1498 * @return {!Array.<T>} The array.
    1499 * @template T
    1500 */
    1501goog.array.rotate = function(array, n) {
    1502 goog.asserts.assert(array.length != null);
    1503
    1504 if (array.length) {
    1505 n %= array.length;
    1506 if (n > 0) {
    1507 goog.array.ARRAY_PROTOTYPE_.unshift.apply(array, array.splice(-n, n));
    1508 } else if (n < 0) {
    1509 goog.array.ARRAY_PROTOTYPE_.push.apply(array, array.splice(0, -n));
    1510 }
    1511 }
    1512 return array;
    1513};
    1514
    1515
    1516/**
    1517 * Moves one item of an array to a new position keeping the order of the rest
    1518 * of the items. Example use case: keeping a list of JavaScript objects
    1519 * synchronized with the corresponding list of DOM elements after one of the
    1520 * elements has been dragged to a new position.
    1521 * @param {!(Array|Arguments|{length:number})} arr The array to modify.
    1522 * @param {number} fromIndex Index of the item to move between 0 and
    1523 * {@code arr.length - 1}.
    1524 * @param {number} toIndex Target index between 0 and {@code arr.length - 1}.
    1525 */
    1526goog.array.moveItem = function(arr, fromIndex, toIndex) {
    1527 goog.asserts.assert(fromIndex >= 0 && fromIndex < arr.length);
    1528 goog.asserts.assert(toIndex >= 0 && toIndex < arr.length);
    1529 // Remove 1 item at fromIndex.
    1530 var removedItems = goog.array.ARRAY_PROTOTYPE_.splice.call(arr, fromIndex, 1);
    1531 // Insert the removed item at toIndex.
    1532 goog.array.ARRAY_PROTOTYPE_.splice.call(arr, toIndex, 0, removedItems[0]);
    1533 // We don't use goog.array.insertAt and goog.array.removeAt, because they're
    1534 // significantly slower than splice.
    1535};
    1536
    1537
    1538/**
    1539 * Creates a new array for which the element at position i is an array of the
    1540 * ith element of the provided arrays. The returned array will only be as long
    1541 * as the shortest array provided; additional values are ignored. For example,
    1542 * the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]].
    1543 *
    1544 * This is similar to the zip() function in Python. See {@link
    1545 * http://docs.python.org/library/functions.html#zip}
    1546 *
    1547 * @param {...!goog.array.ArrayLike} var_args Arrays to be combined.
    1548 * @return {!Array.<!Array>} A new array of arrays created from provided arrays.
    1549 */
    1550goog.array.zip = function(var_args) {
    1551 if (!arguments.length) {
    1552 return [];
    1553 }
    1554 var result = [];
    1555 for (var i = 0; true; i++) {
    1556 var value = [];
    1557 for (var j = 0; j < arguments.length; j++) {
    1558 var arr = arguments[j];
    1559 // If i is larger than the array length, this is the shortest array.
    1560 if (i >= arr.length) {
    1561 return result;
    1562 }
    1563 value.push(arr[i]);
    1564 }
    1565 result.push(value);
    1566 }
    1567};
    1568
    1569
    1570/**
    1571 * Shuffles the values in the specified array using the Fisher-Yates in-place
    1572 * shuffle (also known as the Knuth Shuffle). By default, calls Math.random()
    1573 * and so resets the state of that random number generator. Similarly, may reset
    1574 * the state of the any other specified random number generator.
    1575 *
    1576 * Runtime: O(n)
    1577 *
    1578 * @param {!Array} arr The array to be shuffled.
    1579 * @param {function():number=} opt_randFn Optional random function to use for
    1580 * shuffling.
    1581 * Takes no arguments, and returns a random number on the interval [0, 1).
    1582 * Defaults to Math.random() using JavaScript's built-in Math library.
    1583 */
    1584goog.array.shuffle = function(arr, opt_randFn) {
    1585 var randFn = opt_randFn || Math.random;
    1586
    1587 for (var i = arr.length - 1; i > 0; i--) {
    1588 // Choose a random array index in [0, i] (inclusive with i).
    1589 var j = Math.floor(randFn() * (i + 1));
    1590
    1591 var tmp = arr[i];
    1592 arr[i] = arr[j];
    1593 arr[j] = tmp;
    1594 }
    1595};
    \ 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
    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(current) ? 'o' + goog.getUid(current) :
    970 (typeof current).charAt(0) + current;
    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<VALUE>|goog.array.ArrayLike} arr The array to be searched.
    1063 * @param {function(TARGET, VALUE): number|
    1064 * function(this:THIS, VALUE, number, ?): number} compareFn Either an
    1065 * evaluator or a comparison function, as defined by binarySearch
    1066 * and binarySelect above.
    1067 * @param {boolean} isEvaluator Whether the function is an evaluator or a
    1068 * comparison function.
    1069 * @param {TARGET=} opt_target If the function is a comparison function, then
    1070 * this is the target to binary search for.
    1071 * @param {THIS=} opt_selfObj If the function is an evaluator, this is an
    1072 * optional this object for the evaluator.
    1073 * @return {number} Lowest index of the target value if found, otherwise
    1074 * (-(insertion point) - 1). The insertion point is where the value should
    1075 * be inserted into arr to preserve the sorted property. Return value >= 0
    1076 * iff target is found.
    1077 * @template THIS, VALUE, TARGET
    1078 * @private
    1079 */
    1080goog.array.binarySearch_ = function(arr, compareFn, isEvaluator, opt_target,
    1081 opt_selfObj) {
    1082 var left = 0; // inclusive
    1083 var right = arr.length; // exclusive
    1084 var found;
    1085 while (left < right) {
    1086 var middle = (left + right) >> 1;
    1087 var compareResult;
    1088 if (isEvaluator) {
    1089 compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr);
    1090 } else {
    1091 compareResult = compareFn(opt_target, arr[middle]);
    1092 }
    1093 if (compareResult > 0) {
    1094 left = middle + 1;
    1095 } else {
    1096 right = middle;
    1097 // We are looking for the lowest index so we can't return immediately.
    1098 found = !compareResult;
    1099 }
    1100 }
    1101 // left is the index if found, or the insertion point otherwise.
    1102 // ~left is a shorthand for -left - 1.
    1103 return found ? left : ~left;
    1104};
    1105
    1106
    1107/**
    1108 * Sorts the specified array into ascending order. If no opt_compareFn is
    1109 * specified, elements are compared using
    1110 * <code>goog.array.defaultCompare</code>, which compares the elements using
    1111 * the built in < and > operators. This will produce the expected behavior
    1112 * for homogeneous arrays of String(s) and Number(s), unlike the native sort,
    1113 * but will give unpredictable results for heterogenous lists of strings and
    1114 * numbers with different numbers of digits.
    1115 *
    1116 * This sort is not guaranteed to be stable.
    1117 *
    1118 * Runtime: Same as <code>Array.prototype.sort</code>
    1119 *
    1120 * @param {Array<T>} arr The array to be sorted.
    1121 * @param {?function(T,T):number=} opt_compareFn Optional comparison
    1122 * function by which the
    1123 * array is to be ordered. Should take 2 arguments to compare, and return a
    1124 * negative number, zero, or a positive number depending on whether the
    1125 * first argument is less than, equal to, or greater than the second.
    1126 * @template T
    1127 */
    1128goog.array.sort = function(arr, opt_compareFn) {
    1129 // TODO(arv): Update type annotation since null is not accepted.
    1130 arr.sort(opt_compareFn || goog.array.defaultCompare);
    1131};
    1132
    1133
    1134/**
    1135 * Sorts the specified array into ascending order in a stable way. If no
    1136 * opt_compareFn is specified, elements are compared using
    1137 * <code>goog.array.defaultCompare</code>, which compares the elements using
    1138 * the built in < and > operators. This will produce the expected behavior
    1139 * for homogeneous arrays of String(s) and Number(s).
    1140 *
    1141 * Runtime: Same as <code>Array.prototype.sort</code>, plus an additional
    1142 * O(n) overhead of copying the array twice.
    1143 *
    1144 * @param {Array<T>} arr The array to be sorted.
    1145 * @param {?function(T, T): number=} opt_compareFn Optional comparison function
    1146 * by which the array is to be ordered. Should take 2 arguments to compare,
    1147 * and return a negative number, zero, or a positive number depending on
    1148 * whether the first argument is less than, equal to, or greater than the
    1149 * second.
    1150 * @template T
    1151 */
    1152goog.array.stableSort = function(arr, opt_compareFn) {
    1153 for (var i = 0; i < arr.length; i++) {
    1154 arr[i] = {index: i, value: arr[i]};
    1155 }
    1156 var valueCompareFn = opt_compareFn || goog.array.defaultCompare;
    1157 function stableCompareFn(obj1, obj2) {
    1158 return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index;
    1159 };
    1160 goog.array.sort(arr, stableCompareFn);
    1161 for (var i = 0; i < arr.length; i++) {
    1162 arr[i] = arr[i].value;
    1163 }
    1164};
    1165
    1166
    1167/**
    1168 * Sort the specified array into ascending order based on item keys
    1169 * returned by the specified key function.
    1170 * If no opt_compareFn is specified, the keys are compared in ascending order
    1171 * using <code>goog.array.defaultCompare</code>.
    1172 *
    1173 * Runtime: O(S(f(n)), where S is runtime of <code>goog.array.sort</code>
    1174 * and f(n) is runtime of the key function.
    1175 *
    1176 * @param {Array<T>} arr The array to be sorted.
    1177 * @param {function(T): K} keyFn Function taking array element and returning
    1178 * a key used for sorting this element.
    1179 * @param {?function(K, K): number=} opt_compareFn Optional comparison function
    1180 * by which the keys are to be ordered. Should take 2 arguments to compare,
    1181 * and return a negative number, zero, or a positive number depending on
    1182 * whether the first argument is less than, equal to, or greater than the
    1183 * second.
    1184 * @template T
    1185 * @template K
    1186 */
    1187goog.array.sortByKey = function(arr, keyFn, opt_compareFn) {
    1188 var keyCompareFn = opt_compareFn || goog.array.defaultCompare;
    1189 goog.array.sort(arr, function(a, b) {
    1190 return keyCompareFn(keyFn(a), keyFn(b));
    1191 });
    1192};
    1193
    1194
    1195/**
    1196 * Sorts an array of objects by the specified object key and compare
    1197 * function. If no compare function is provided, the key values are
    1198 * compared in ascending order using <code>goog.array.defaultCompare</code>.
    1199 * This won't work for keys that get renamed by the compiler. So use
    1200 * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}.
    1201 * @param {Array<Object>} arr An array of objects to sort.
    1202 * @param {string} key The object key to sort by.
    1203 * @param {Function=} opt_compareFn The function to use to compare key
    1204 * values.
    1205 */
    1206goog.array.sortObjectsByKey = function(arr, key, opt_compareFn) {
    1207 goog.array.sortByKey(arr,
    1208 function(obj) { return obj[key]; },
    1209 opt_compareFn);
    1210};
    1211
    1212
    1213/**
    1214 * Tells if the array is sorted.
    1215 * @param {!Array<T>} arr The array.
    1216 * @param {?function(T,T):number=} opt_compareFn Function to compare the
    1217 * array elements.
    1218 * Should take 2 arguments to compare, and return a negative number, zero,
    1219 * or a positive number depending on whether the first argument is less
    1220 * than, equal to, or greater than the second.
    1221 * @param {boolean=} opt_strict If true no equal elements are allowed.
    1222 * @return {boolean} Whether the array is sorted.
    1223 * @template T
    1224 */
    1225goog.array.isSorted = function(arr, opt_compareFn, opt_strict) {
    1226 var compare = opt_compareFn || goog.array.defaultCompare;
    1227 for (var i = 1; i < arr.length; i++) {
    1228 var compareResult = compare(arr[i - 1], arr[i]);
    1229 if (compareResult > 0 || compareResult == 0 && opt_strict) {
    1230 return false;
    1231 }
    1232 }
    1233 return true;
    1234};
    1235
    1236
    1237/**
    1238 * Compares two arrays for equality. Two arrays are considered equal if they
    1239 * have the same length and their corresponding elements are equal according to
    1240 * the comparison function.
    1241 *
    1242 * @param {goog.array.ArrayLike} arr1 The first array to compare.
    1243 * @param {goog.array.ArrayLike} arr2 The second array to compare.
    1244 * @param {Function=} opt_equalsFn Optional comparison function.
    1245 * Should take 2 arguments to compare, and return true if the arguments
    1246 * are equal. Defaults to {@link goog.array.defaultCompareEquality} which
    1247 * compares the elements using the built-in '===' operator.
    1248 * @return {boolean} Whether the two arrays are equal.
    1249 */
    1250goog.array.equals = function(arr1, arr2, opt_equalsFn) {
    1251 if (!goog.isArrayLike(arr1) || !goog.isArrayLike(arr2) ||
    1252 arr1.length != arr2.length) {
    1253 return false;
    1254 }
    1255 var l = arr1.length;
    1256 var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality;
    1257 for (var i = 0; i < l; i++) {
    1258 if (!equalsFn(arr1[i], arr2[i])) {
    1259 return false;
    1260 }
    1261 }
    1262 return true;
    1263};
    1264
    1265
    1266/**
    1267 * 3-way array compare function.
    1268 * @param {!Array<VALUE>|!goog.array.ArrayLike} arr1 The first array to
    1269 * compare.
    1270 * @param {!Array<VALUE>|!goog.array.ArrayLike} arr2 The second array to
    1271 * compare.
    1272 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
    1273 * function by which the array is to be ordered. Should take 2 arguments to
    1274 * compare, and return a negative number, zero, or a positive number
    1275 * depending on whether the first argument is less than, equal to, or
    1276 * greater than the second.
    1277 * @return {number} Negative number, zero, or a positive number depending on
    1278 * whether the first argument is less than, equal to, or greater than the
    1279 * second.
    1280 * @template VALUE
    1281 */
    1282goog.array.compare3 = function(arr1, arr2, opt_compareFn) {
    1283 var compare = opt_compareFn || goog.array.defaultCompare;
    1284 var l = Math.min(arr1.length, arr2.length);
    1285 for (var i = 0; i < l; i++) {
    1286 var result = compare(arr1[i], arr2[i]);
    1287 if (result != 0) {
    1288 return result;
    1289 }
    1290 }
    1291 return goog.array.defaultCompare(arr1.length, arr2.length);
    1292};
    1293
    1294
    1295/**
    1296 * Compares its two arguments for order, using the built in < and >
    1297 * operators.
    1298 * @param {VALUE} a The first object to be compared.
    1299 * @param {VALUE} b The second object to be compared.
    1300 * @return {number} A negative number, zero, or a positive number as the first
    1301 * argument is less than, equal to, or greater than the second,
    1302 * respectively.
    1303 * @template VALUE
    1304 */
    1305goog.array.defaultCompare = function(a, b) {
    1306 return a > b ? 1 : a < b ? -1 : 0;
    1307};
    1308
    1309
    1310/**
    1311 * Compares its two arguments for inverse order, using the built in < and >
    1312 * operators.
    1313 * @param {VALUE} a The first object to be compared.
    1314 * @param {VALUE} b The second object to be compared.
    1315 * @return {number} A negative number, zero, or a positive number as the first
    1316 * argument is greater than, equal to, or less than the second,
    1317 * respectively.
    1318 * @template VALUE
    1319 */
    1320goog.array.inverseDefaultCompare = function(a, b) {
    1321 return -goog.array.defaultCompare(a, b);
    1322};
    1323
    1324
    1325/**
    1326 * Compares its two arguments for equality, using the built in === operator.
    1327 * @param {*} a The first object to compare.
    1328 * @param {*} b The second object to compare.
    1329 * @return {boolean} True if the two arguments are equal, false otherwise.
    1330 */
    1331goog.array.defaultCompareEquality = function(a, b) {
    1332 return a === b;
    1333};
    1334
    1335
    1336/**
    1337 * Inserts a value into a sorted array. The array is not modified if the
    1338 * value is already present.
    1339 * @param {Array<VALUE>|goog.array.ArrayLike} array The array to modify.
    1340 * @param {VALUE} value The object to insert.
    1341 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
    1342 * function by which the array is ordered. Should take 2 arguments to
    1343 * compare, and return a negative number, zero, or a positive number
    1344 * depending on whether the first argument is less than, equal to, or
    1345 * greater than the second.
    1346 * @return {boolean} True if an element was inserted.
    1347 * @template VALUE
    1348 */
    1349goog.array.binaryInsert = function(array, value, opt_compareFn) {
    1350 var index = goog.array.binarySearch(array, value, opt_compareFn);
    1351 if (index < 0) {
    1352 goog.array.insertAt(array, value, -(index + 1));
    1353 return true;
    1354 }
    1355 return false;
    1356};
    1357
    1358
    1359/**
    1360 * Removes a value from a sorted array.
    1361 * @param {!Array<VALUE>|!goog.array.ArrayLike} array The array to modify.
    1362 * @param {VALUE} value The object to remove.
    1363 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
    1364 * function by which the array is ordered. Should take 2 arguments to
    1365 * compare, and return a negative number, zero, or a positive number
    1366 * depending on whether the first argument is less than, equal to, or
    1367 * greater than the second.
    1368 * @return {boolean} True if an element was removed.
    1369 * @template VALUE
    1370 */
    1371goog.array.binaryRemove = function(array, value, opt_compareFn) {
    1372 var index = goog.array.binarySearch(array, value, opt_compareFn);
    1373 return (index >= 0) ? goog.array.removeAt(array, index) : false;
    1374};
    1375
    1376
    1377/**
    1378 * Splits an array into disjoint buckets according to a splitting function.
    1379 * @param {Array<T>} array The array.
    1380 * @param {function(this:S, T,number,Array<T>):?} sorter Function to call for
    1381 * every element. This takes 3 arguments (the element, the index and the
    1382 * array) and must return a valid object key (a string, number, etc), or
    1383 * undefined, if that object should not be placed in a bucket.
    1384 * @param {S=} opt_obj The object to be used as the value of 'this' within
    1385 * sorter.
    1386 * @return {!Object} An object, with keys being all of the unique return values
    1387 * of sorter, and values being arrays containing the items for
    1388 * which the splitter returned that key.
    1389 * @template T,S
    1390 */
    1391goog.array.bucket = function(array, sorter, opt_obj) {
    1392 var buckets = {};
    1393
    1394 for (var i = 0; i < array.length; i++) {
    1395 var value = array[i];
    1396 var key = sorter.call(opt_obj, value, i, array);
    1397 if (goog.isDef(key)) {
    1398 // Push the value to the right bucket, creating it if necessary.
    1399 var bucket = buckets[key] || (buckets[key] = []);
    1400 bucket.push(value);
    1401 }
    1402 }
    1403
    1404 return buckets;
    1405};
    1406
    1407
    1408/**
    1409 * Creates a new object built from the provided array and the key-generation
    1410 * function.
    1411 * @param {Array<T>|goog.array.ArrayLike} arr Array or array like object over
    1412 * which to iterate whose elements will be the values in the new object.
    1413 * @param {?function(this:S, T, number, ?) : string} keyFunc The function to
    1414 * call for every element. This function takes 3 arguments (the element, the
    1415 * index and the array) and should return a string that will be used as the
    1416 * key for the element in the new object. If the function returns the same
    1417 * key for more than one element, the value for that key is
    1418 * implementation-defined.
    1419 * @param {S=} opt_obj The object to be used as the value of 'this'
    1420 * within keyFunc.
    1421 * @return {!Object<T>} The new object.
    1422 * @template T,S
    1423 */
    1424goog.array.toObject = function(arr, keyFunc, opt_obj) {
    1425 var ret = {};
    1426 goog.array.forEach(arr, function(element, index) {
    1427 ret[keyFunc.call(opt_obj, element, index, arr)] = element;
    1428 });
    1429 return ret;
    1430};
    1431
    1432
    1433/**
    1434 * Creates a range of numbers in an arithmetic progression.
    1435 *
    1436 * Range takes 1, 2, or 3 arguments:
    1437 * <pre>
    1438 * range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4]
    1439 * range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4]
    1440 * range(-2, -5, -1) produces [-2, -3, -4]
    1441 * range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5.
    1442 * </pre>
    1443 *
    1444 * @param {number} startOrEnd The starting value of the range if an end argument
    1445 * is provided. Otherwise, the start value is 0, and this is the end value.
    1446 * @param {number=} opt_end The optional end value of the range.
    1447 * @param {number=} opt_step The step size between range values. Defaults to 1
    1448 * if opt_step is undefined or 0.
    1449 * @return {!Array<number>} An array of numbers for the requested range. May be
    1450 * an empty array if adding the step would not converge toward the end
    1451 * value.
    1452 */
    1453goog.array.range = function(startOrEnd, opt_end, opt_step) {
    1454 var array = [];
    1455 var start = 0;
    1456 var end = startOrEnd;
    1457 var step = opt_step || 1;
    1458 if (opt_end !== undefined) {
    1459 start = startOrEnd;
    1460 end = opt_end;
    1461 }
    1462
    1463 if (step * (end - start) < 0) {
    1464 // Sign mismatch: start + step will never reach the end value.
    1465 return [];
    1466 }
    1467
    1468 if (step > 0) {
    1469 for (var i = start; i < end; i += step) {
    1470 array.push(i);
    1471 }
    1472 } else {
    1473 for (var i = start; i > end; i += step) {
    1474 array.push(i);
    1475 }
    1476 }
    1477 return array;
    1478};
    1479
    1480
    1481/**
    1482 * Returns an array consisting of the given value repeated N times.
    1483 *
    1484 * @param {VALUE} value The value to repeat.
    1485 * @param {number} n The repeat count.
    1486 * @return {!Array<VALUE>} An array with the repeated value.
    1487 * @template VALUE
    1488 */
    1489goog.array.repeat = function(value, n) {
    1490 var array = [];
    1491 for (var i = 0; i < n; i++) {
    1492 array[i] = value;
    1493 }
    1494 return array;
    1495};
    1496
    1497
    1498/**
    1499 * Returns an array consisting of every argument with all arrays
    1500 * expanded in-place recursively.
    1501 *
    1502 * @param {...*} var_args The values to flatten.
    1503 * @return {!Array<?>} An array containing the flattened values.
    1504 */
    1505goog.array.flatten = function(var_args) {
    1506 var CHUNK_SIZE = 8192;
    1507
    1508 var result = [];
    1509 for (var i = 0; i < arguments.length; i++) {
    1510 var element = arguments[i];
    1511 if (goog.isArray(element)) {
    1512 for (var c = 0; c < element.length; c += CHUNK_SIZE) {
    1513 var chunk = goog.array.slice(element, c, c + CHUNK_SIZE);
    1514 var recurseResult = goog.array.flatten.apply(null, chunk);
    1515 for (var r = 0; r < recurseResult.length; r++) {
    1516 result.push(recurseResult[r]);
    1517 }
    1518 }
    1519 } else {
    1520 result.push(element);
    1521 }
    1522 }
    1523 return result;
    1524};
    1525
    1526
    1527/**
    1528 * Rotates an array in-place. After calling this method, the element at
    1529 * index i will be the element previously at index (i - n) %
    1530 * array.length, for all values of i between 0 and array.length - 1,
    1531 * inclusive.
    1532 *
    1533 * For example, suppose list comprises [t, a, n, k, s]. After invoking
    1534 * rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k].
    1535 *
    1536 * @param {!Array<T>} array The array to rotate.
    1537 * @param {number} n The amount to rotate.
    1538 * @return {!Array<T>} The array.
    1539 * @template T
    1540 */
    1541goog.array.rotate = function(array, n) {
    1542 goog.asserts.assert(array.length != null);
    1543
    1544 if (array.length) {
    1545 n %= array.length;
    1546 if (n > 0) {
    1547 goog.array.ARRAY_PROTOTYPE_.unshift.apply(array, array.splice(-n, n));
    1548 } else if (n < 0) {
    1549 goog.array.ARRAY_PROTOTYPE_.push.apply(array, array.splice(0, -n));
    1550 }
    1551 }
    1552 return array;
    1553};
    1554
    1555
    1556/**
    1557 * Moves one item of an array to a new position keeping the order of the rest
    1558 * of the items. Example use case: keeping a list of JavaScript objects
    1559 * synchronized with the corresponding list of DOM elements after one of the
    1560 * elements has been dragged to a new position.
    1561 * @param {!(Array|Arguments|{length:number})} arr The array to modify.
    1562 * @param {number} fromIndex Index of the item to move between 0 and
    1563 * {@code arr.length - 1}.
    1564 * @param {number} toIndex Target index between 0 and {@code arr.length - 1}.
    1565 */
    1566goog.array.moveItem = function(arr, fromIndex, toIndex) {
    1567 goog.asserts.assert(fromIndex >= 0 && fromIndex < arr.length);
    1568 goog.asserts.assert(toIndex >= 0 && toIndex < arr.length);
    1569 // Remove 1 item at fromIndex.
    1570 var removedItems = goog.array.ARRAY_PROTOTYPE_.splice.call(arr, fromIndex, 1);
    1571 // Insert the removed item at toIndex.
    1572 goog.array.ARRAY_PROTOTYPE_.splice.call(arr, toIndex, 0, removedItems[0]);
    1573 // We don't use goog.array.insertAt and goog.array.removeAt, because they're
    1574 // significantly slower than splice.
    1575};
    1576
    1577
    1578/**
    1579 * Creates a new array for which the element at position i is an array of the
    1580 * ith element of the provided arrays. The returned array will only be as long
    1581 * as the shortest array provided; additional values are ignored. For example,
    1582 * the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]].
    1583 *
    1584 * This is similar to the zip() function in Python. See {@link
    1585 * http://docs.python.org/library/functions.html#zip}
    1586 *
    1587 * @param {...!goog.array.ArrayLike} var_args Arrays to be combined.
    1588 * @return {!Array<!Array<?>>} A new array of arrays created from
    1589 * provided arrays.
    1590 */
    1591goog.array.zip = function(var_args) {
    1592 if (!arguments.length) {
    1593 return [];
    1594 }
    1595 var result = [];
    1596 for (var i = 0; true; i++) {
    1597 var value = [];
    1598 for (var j = 0; j < arguments.length; j++) {
    1599 var arr = arguments[j];
    1600 // If i is larger than the array length, this is the shortest array.
    1601 if (i >= arr.length) {
    1602 return result;
    1603 }
    1604 value.push(arr[i]);
    1605 }
    1606 result.push(value);
    1607 }
    1608};
    1609
    1610
    1611/**
    1612 * Shuffles the values in the specified array using the Fisher-Yates in-place
    1613 * shuffle (also known as the Knuth Shuffle). By default, calls Math.random()
    1614 * and so resets the state of that random number generator. Similarly, may reset
    1615 * the state of the any other specified random number generator.
    1616 *
    1617 * Runtime: O(n)
    1618 *
    1619 * @param {!Array<?>} arr The array to be shuffled.
    1620 * @param {function():number=} opt_randFn Optional random function to use for
    1621 * shuffling.
    1622 * Takes no arguments, and returns a random number on the interval [0, 1).
    1623 * Defaults to Math.random() using JavaScript's built-in Math library.
    1624 */
    1625goog.array.shuffle = function(arr, opt_randFn) {
    1626 var randFn = opt_randFn || Math.random;
    1627
    1628 for (var i = arr.length - 1; i > 0; i--) {
    1629 // Choose a random array index in [0, i] (inclusive with i).
    1630 var j = Math.floor(randFn() * (i + 1));
    1631
    1632 var tmp = arr[i];
    1633 arr[i] = arr[j];
    1634 arr[j] = tmp;
    1635 }
    1636};
    1637
    1638
    1639/**
    1640 * Returns a new array of elements from arr, based on the indexes of elements
    1641 * provided by index_arr. For example, the result of index copying
    1642 * ['a', 'b', 'c'] with index_arr [1,0,0,2] is ['b', 'a', 'a', 'c'].
    1643 *
    1644 * @param {!Array<T>} arr The array to get a indexed copy from.
    1645 * @param {!Array<number>} index_arr An array of indexes to get from arr.
    1646 * @return {!Array<T>} A new array of elements from arr in index_arr order.
    1647 * @template T
    1648 */
    1649goog.array.copyByIndex = function(arr, index_arr) {
    1650 var result = [];
    1651 goog.array.forEach(index_arr, function(index) {
    1652 result.push(arr[index]);
    1653 });
    1654 return result;
    1655};
    \ 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 23b87ed..29fa28d 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.dom.NodeType');
    41goog.require('goog.string');
    42
    43
    44/**
    45 * @define {boolean} Whether to strip out asserts or to leave them in.
    46 */
    47goog.define('goog.asserts.ENABLE_ASSERTS', goog.DEBUG);
    48
    49
    50
    51/**
    52 * Error object for failed assertions.
    53 * @param {string} messagePattern The pattern that was used to form message.
    54 * @param {!Array.<*>} messageArgs The items to substitute into the pattern.
    55 * @constructor
    56 * @extends {goog.debug.Error}
    57 * @final
    58 */
    59goog.asserts.AssertionError = function(messagePattern, messageArgs) {
    60 messageArgs.unshift(messagePattern);
    61 goog.debug.Error.call(this, goog.string.subs.apply(null, messageArgs));
    62 // Remove the messagePattern afterwards to avoid permenantly modifying the
    63 // passed in array.
    64 messageArgs.shift();
    65
    66 /**
    67 * The message pattern used to format the error message. Error handlers can
    68 * use this to uniquely identify the assertion.
    69 * @type {string}
    70 */
    71 this.messagePattern = messagePattern;
    72};
    73goog.inherits(goog.asserts.AssertionError, goog.debug.Error);
    74
    75
    76/** @override */
    77goog.asserts.AssertionError.prototype.name = 'AssertionError';
    78
    79
    80/**
    81 * The default error handler.
    82 * @param {!goog.asserts.AssertionError} e The exception to be handled.
    83 */
    84goog.asserts.DEFAULT_ERROR_HANDLER = function(e) { throw e; };
    85
    86
    87/**
    88 * The handler responsible for throwing or logging assertion errors.
    89 * @private {function(!goog.asserts.AssertionError)}
    90 */
    91goog.asserts.errorHandler_ = goog.asserts.DEFAULT_ERROR_HANDLER;
    92
    93
    94/**
    95 * Throws an exception with the given message and "Assertion failed" prefixed
    96 * onto it.
    97 * @param {string} defaultMessage The message to use if givenMessage is empty.
    98 * @param {Array.<*>} defaultArgs The substitution arguments for defaultMessage.
    99 * @param {string|undefined} givenMessage Message supplied by the caller.
    100 * @param {Array.<*>} givenArgs The substitution arguments for givenMessage.
    101 * @throws {goog.asserts.AssertionError} When the value is not a number.
    102 * @private
    103 */
    104goog.asserts.doAssertFailure_ =
    105 function(defaultMessage, defaultArgs, givenMessage, givenArgs) {
    106 var message = 'Assertion failed';
    107 if (givenMessage) {
    108 message += ': ' + givenMessage;
    109 var args = givenArgs;
    110 } else if (defaultMessage) {
    111 message += ': ' + defaultMessage;
    112 args = defaultArgs;
    113 }
    114 // The '' + works around an Opera 10 bug in the unit tests. Without it,
    115 // a stack trace is added to var message above. With this, a stack trace is
    116 // not added until this line (it causes the extra garbage to be added after
    117 // the assertion message instead of in the middle of it).
    118 var e = new goog.asserts.AssertionError('' + message, args || []);
    119 goog.asserts.errorHandler_(e);
    120};
    121
    122
    123/**
    124 * Sets a custom error handler that can be used to customize the behavior of
    125 * assertion failures, for example by turning all assertion failures into log
    126 * messages.
    127 * @param {function(goog.asserts.AssertionError)} errorHandler
    128 */
    129goog.asserts.setErrorHandler = function(errorHandler) {
    130 if (goog.asserts.ENABLE_ASSERTS) {
    131 goog.asserts.errorHandler_ = errorHandler;
    132 }
    133};
    134
    135
    136/**
    137 * Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is
    138 * true.
    139 * @template T
    140 * @param {T} condition The condition to check.
    141 * @param {string=} opt_message Error message in case of failure.
    142 * @param {...*} var_args The items to substitute into the failure message.
    143 * @return {T} The value of the condition.
    144 * @throws {goog.asserts.AssertionError} When the condition evaluates to false.
    145 */
    146goog.asserts.assert = function(condition, opt_message, var_args) {
    147 if (goog.asserts.ENABLE_ASSERTS && !condition) {
    148 goog.asserts.doAssertFailure_('', null, opt_message,
    149 Array.prototype.slice.call(arguments, 2));
    150 }
    151 return condition;
    152};
    153
    154
    155/**
    156 * Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case
    157 * when we want to add a check in the unreachable area like switch-case
    158 * statement:
    159 *
    160 * <pre>
    161 * switch(type) {
    162 * case FOO: doSomething(); break;
    163 * case BAR: doSomethingElse(); break;
    164 * default: goog.assert.fail('Unrecognized type: ' + type);
    165 * // We have only 2 types - "default:" section is unreachable code.
    166 * }
    167 * </pre>
    168 *
    169 * @param {string=} opt_message Error message in case of failure.
    170 * @param {...*} var_args The items to substitute into the failure message.
    171 * @throws {goog.asserts.AssertionError} Failure.
    172 */
    173goog.asserts.fail = function(opt_message, var_args) {
    174 if (goog.asserts.ENABLE_ASSERTS) {
    175 goog.asserts.errorHandler_(new goog.asserts.AssertionError(
    176 'Failure' + (opt_message ? ': ' + opt_message : ''),
    177 Array.prototype.slice.call(arguments, 1)));
    178 }
    179};
    180
    181
    182/**
    183 * Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true.
    184 * @param {*} value The value to check.
    185 * @param {string=} opt_message Error message in case of failure.
    186 * @param {...*} var_args The items to substitute into the failure message.
    187 * @return {number} The value, guaranteed to be a number when asserts enabled.
    188 * @throws {goog.asserts.AssertionError} When the value is not a number.
    189 */
    190goog.asserts.assertNumber = function(value, opt_message, var_args) {
    191 if (goog.asserts.ENABLE_ASSERTS && !goog.isNumber(value)) {
    192 goog.asserts.doAssertFailure_('Expected number but got %s: %s.',
    193 [goog.typeOf(value), value], opt_message,
    194 Array.prototype.slice.call(arguments, 2));
    195 }
    196 return /** @type {number} */ (value);
    197};
    198
    199
    200/**
    201 * Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true.
    202 * @param {*} value The value to check.
    203 * @param {string=} opt_message Error message in case of failure.
    204 * @param {...*} var_args The items to substitute into the failure message.
    205 * @return {string} The value, guaranteed to be a string when asserts enabled.
    206 * @throws {goog.asserts.AssertionError} When the value is not a string.
    207 */
    208goog.asserts.assertString = function(value, opt_message, var_args) {
    209 if (goog.asserts.ENABLE_ASSERTS && !goog.isString(value)) {
    210 goog.asserts.doAssertFailure_('Expected string but got %s: %s.',
    211 [goog.typeOf(value), value], opt_message,
    212 Array.prototype.slice.call(arguments, 2));
    213 }
    214 return /** @type {string} */ (value);
    215};
    216
    217
    218/**
    219 * Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true.
    220 * @param {*} value The value to check.
    221 * @param {string=} opt_message Error message in case of failure.
    222 * @param {...*} var_args The items to substitute into the failure message.
    223 * @return {!Function} The value, guaranteed to be a function when asserts
    224 * enabled.
    225 * @throws {goog.asserts.AssertionError} When the value is not a function.
    226 */
    227goog.asserts.assertFunction = function(value, opt_message, var_args) {
    228 if (goog.asserts.ENABLE_ASSERTS && !goog.isFunction(value)) {
    229 goog.asserts.doAssertFailure_('Expected function but got %s: %s.',
    230 [goog.typeOf(value), value], opt_message,
    231 Array.prototype.slice.call(arguments, 2));
    232 }
    233 return /** @type {!Function} */ (value);
    234};
    235
    236
    237/**
    238 * Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true.
    239 * @param {*} value The value to check.
    240 * @param {string=} opt_message Error message in case of failure.
    241 * @param {...*} var_args The items to substitute into the failure message.
    242 * @return {!Object} The value, guaranteed to be a non-null object.
    243 * @throws {goog.asserts.AssertionError} When the value is not an object.
    244 */
    245goog.asserts.assertObject = function(value, opt_message, var_args) {
    246 if (goog.asserts.ENABLE_ASSERTS && !goog.isObject(value)) {
    247 goog.asserts.doAssertFailure_('Expected object but got %s: %s.',
    248 [goog.typeOf(value), value],
    249 opt_message, Array.prototype.slice.call(arguments, 2));
    250 }
    251 return /** @type {!Object} */ (value);
    252};
    253
    254
    255/**
    256 * Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true.
    257 * @param {*} value The value to check.
    258 * @param {string=} opt_message Error message in case of failure.
    259 * @param {...*} var_args The items to substitute into the failure message.
    260 * @return {!Array} The value, guaranteed to be a non-null array.
    261 * @throws {goog.asserts.AssertionError} When the value is not an array.
    262 */
    263goog.asserts.assertArray = function(value, opt_message, var_args) {
    264 if (goog.asserts.ENABLE_ASSERTS && !goog.isArray(value)) {
    265 goog.asserts.doAssertFailure_('Expected array but got %s: %s.',
    266 [goog.typeOf(value), value], opt_message,
    267 Array.prototype.slice.call(arguments, 2));
    268 }
    269 return /** @type {!Array} */ (value);
    270};
    271
    272
    273/**
    274 * Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true.
    275 * @param {*} value The value to check.
    276 * @param {string=} opt_message Error message in case of failure.
    277 * @param {...*} var_args The items to substitute into the failure message.
    278 * @return {boolean} The value, guaranteed to be a boolean when asserts are
    279 * enabled.
    280 * @throws {goog.asserts.AssertionError} When the value is not a boolean.
    281 */
    282goog.asserts.assertBoolean = function(value, opt_message, var_args) {
    283 if (goog.asserts.ENABLE_ASSERTS && !goog.isBoolean(value)) {
    284 goog.asserts.doAssertFailure_('Expected boolean but got %s: %s.',
    285 [goog.typeOf(value), value], opt_message,
    286 Array.prototype.slice.call(arguments, 2));
    287 }
    288 return /** @type {boolean} */ (value);
    289};
    290
    291
    292/**
    293 * Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true.
    294 * @param {*} value The value to check.
    295 * @param {string=} opt_message Error message in case of failure.
    296 * @param {...*} var_args The items to substitute into the failure message.
    297 * @return {!Element} The value, likely to be a DOM Element when asserts are
    298 * enabled.
    299 * @throws {goog.asserts.AssertionError} When the value is not a boolean.
    300 */
    301goog.asserts.assertElement = function(value, opt_message, var_args) {
    302 if (goog.asserts.ENABLE_ASSERTS && (!goog.isObject(value) ||
    303 value.nodeType != goog.dom.NodeType.ELEMENT)) {
    304 goog.asserts.doAssertFailure_('Expected Element but got %s: %s.',
    305 [goog.typeOf(value), value], opt_message,
    306 Array.prototype.slice.call(arguments, 2));
    307 }
    308 return /** @type {!Element} */ (value);
    309};
    310
    311
    312/**
    313 * Checks if the value is an instance of the user-defined type if
    314 * goog.asserts.ENABLE_ASSERTS is true.
    315 *
    316 * The compiler may tighten the type returned by this function.
    317 *
    318 * @param {*} value The value to check.
    319 * @param {function(new: T, ...)} type A user-defined constructor.
    320 * @param {string=} opt_message Error message in case of failure.
    321 * @param {...*} var_args The items to substitute into the failure message.
    322 * @throws {goog.asserts.AssertionError} When the value is not an instance of
    323 * type.
    324 * @return {!T}
    325 * @template T
    326 */
    327goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) {
    328 if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) {
    329 goog.asserts.doAssertFailure_('instanceof check failed.', null,
    330 opt_message, Array.prototype.slice.call(arguments, 3));
    331 }
    332 return value;
    333};
    334
    335
    336/**
    337 * Checks that no enumerable keys are present in Object.prototype. Such keys
    338 * would break most code that use {@code for (var ... in ...)} loops.
    339 */
    340goog.asserts.assertObjectPrototypeIsIntact = function() {
    341 for (var key in Object.prototype) {
    342 goog.asserts.fail(key + ' should not be enumerable in Object.prototype.');
    343 }
    344};
    \ 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 permenantly 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 index 1923c53..c1b8f1d 100644 --- a/docs/source/lib/goog/async/nexttick.js.src.html +++ b/docs/source/lib/goog/async/nexttick.js.src.html @@ -1 +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.functions');
    27goog.require('goog.labs.userAgent.browser');
    28
    29
    30/**
    31 * Throw an item without interrupting the current execution context. For
    32 * example, if processing a group of items in a loop, sometimes it is useful
    33 * to report an error while still allowing the rest of the batch to be
    34 * processed.
    35 * @param {*} exception
    36 */
    37goog.async.throwException = function(exception) {
    38 // Each throw needs to be in its own context.
    39 goog.global.setTimeout(function() { throw exception; }, 0);
    40};
    41
    42
    43/**
    44 * Fires the provided callbacks as soon as possible after the current JS
    45 * execution context. setTimeout(…, 0) takes at least 4ms when called from
    46 * within another setTimeout(…, 0) for legacy reasons.
    47 *
    48 * This will not schedule the callback as a microtask (i.e. a task that can
    49 * preempt user input or networking callbacks). It is meant to emulate what
    50 * setTimeout(_, 0) would do if it were not throttled. If you desire microtask
    51 * behavior, use {@see goog.Promise} instead.
    52 *
    53 * @param {function(this:SCOPE)} callback Callback function to fire as soon as
    54 * possible.
    55 * @param {SCOPE=} opt_context Object in whose scope to call the listener.
    56 * @template SCOPE
    57 */
    58goog.async.nextTick = function(callback, opt_context) {
    59 var cb = callback;
    60 if (opt_context) {
    61 cb = goog.bind(callback, opt_context);
    62 }
    63 cb = goog.async.nextTick.wrapCallback_(cb);
    64 // window.setImmediate was introduced and currently only supported by IE10+,
    65 // but due to a bug in the implementation it is not guaranteed that
    66 // setImmediate is faster than setTimeout nor that setImmediate N is before
    67 // setImmediate N+1. That is why we do not use the native version if
    68 // available. We do, however, call setImmediate if it is a normal function
    69 // because that indicates that it has been replaced by goog.testing.MockClock
    70 // which we do want to support.
    71 // See
    72 // http://connect.microsoft.com/IE/feedback/details/801823/setimmediate-and-messagechannel-are-broken-in-ie10
    73 if (goog.isFunction(goog.global.setImmediate) && (!goog.global.Window ||
    74 goog.global.Window.prototype.setImmediate != goog.global.setImmediate)) {
    75 goog.global.setImmediate(cb);
    76 return;
    77 }
    78 // Look for and cache the custom fallback version of setImmediate.
    79 if (!goog.async.nextTick.setImmediate_) {
    80 goog.async.nextTick.setImmediate_ =
    81 goog.async.nextTick.getSetImmediateEmulator_();
    82 }
    83 goog.async.nextTick.setImmediate_(cb);
    84};
    85
    86
    87/**
    88 * Cache for the setImmediate implementation.
    89 * @type {function(function())}
    90 * @private
    91 */
    92goog.async.nextTick.setImmediate_;
    93
    94
    95/**
    96 * Determines the best possible implementation to run a function as soon as
    97 * the JS event loop is idle.
    98 * @return {function(function())} The "setImmediate" implementation.
    99 * @private
    100 */
    101goog.async.nextTick.getSetImmediateEmulator_ = function() {
    102 // Create a private message channel and use it to postMessage empty messages
    103 // to ourselves.
    104 var Channel = goog.global['MessageChannel'];
    105 // If MessageChannel is not available and we are in a browser, implement
    106 // an iframe based polyfill in browsers that have postMessage and
    107 // document.addEventListener. The latter excludes IE8 because it has a
    108 // synchronous postMessage implementation.
    109 if (typeof Channel === 'undefined' && typeof window !== 'undefined' &&
    110 window.postMessage && window.addEventListener) {
    111 /** @constructor */
    112 Channel = function() {
    113 // Make an empty, invisible iframe.
    114 var iframe = document.createElement('iframe');
    115 iframe.style.display = 'none';
    116 iframe.src = '';
    117 document.documentElement.appendChild(iframe);
    118 var win = iframe.contentWindow;
    119 var doc = win.document;
    120 doc.open();
    121 doc.write('');
    122 doc.close();
    123 // Do not post anything sensitive over this channel, as the workaround for
    124 // pages with file: origin could allow that information to be modified or
    125 // intercepted.
    126 var message = 'callImmediate' + Math.random();
    127 // The same origin policy rejects attempts to postMessage from file: urls
    128 // unless the origin is '*'.
    129 // TODO(b/16335441): Use '*' origin for data: and other similar protocols.
    130 var origin = win.location.protocol == 'file:' ?
    131 '*' : win.location.protocol + '//' + win.location.host;
    132 var onmessage = goog.bind(function(e) {
    133 // Validate origin and message to make sure that this message was
    134 // intended for us.
    135 if (e.origin != origin && e.data != message) {
    136 return;
    137 }
    138 this['port1'].onmessage();
    139 }, this);
    140 win.addEventListener('message', onmessage, false);
    141 this['port1'] = {};
    142 this['port2'] = {
    143 postMessage: function() {
    144 win.postMessage(message, origin);
    145 }
    146 };
    147 };
    148 }
    149 if (typeof Channel !== 'undefined' &&
    150 // Exclude all of IE due to
    151 // http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/
    152 // which allows starving postMessage with a busy setTimeout loop.
    153 // This currently affects IE10 and IE11 which would otherwise be able
    154 // to use the postMessage based fallbacks.
    155 !goog.labs.userAgent.browser.isIE()) {
    156 var channel = new Channel();
    157 // Use a fifo linked list to call callbacks in the right order.
    158 var head = {};
    159 var tail = head;
    160 channel['port1'].onmessage = function() {
    161 head = head.next;
    162 var cb = head.cb;
    163 head.cb = null;
    164 cb();
    165 };
    166 return function(cb) {
    167 tail.next = {
    168 cb: cb
    169 };
    170 tail = tail.next;
    171 channel['port2'].postMessage(0);
    172 };
    173 }
    174 // Implementation for IE6+: Script elements fire an asynchronous
    175 // onreadystatechange event when inserted into the DOM.
    176 if (typeof document !== 'undefined' && 'onreadystatechange' in
    177 document.createElement('script')) {
    178 return function(cb) {
    179 var script = document.createElement('script');
    180 script.onreadystatechange = function() {
    181 // Clean up and call the callback.
    182 script.onreadystatechange = null;
    183 script.parentNode.removeChild(script);
    184 script = null;
    185 cb();
    186 cb = null;
    187 };
    188 document.documentElement.appendChild(script);
    189 };
    190 }
    191 // Fall back to setTimeout with 0. In browsers this creates a delay of 5ms
    192 // or more.
    193 return function(cb) {
    194 goog.global.setTimeout(cb, 0);
    195 };
    196};
    197
    198
    199/**
    200 * Helper function that is overrided to protect callbacks with entry point
    201 * monitor if the application monitors entry points.
    202 * @param {function()} callback Callback function to fire as soon as possible.
    203 * @return {function()} The wrapped callback.
    204 * @private
    205 */
    206goog.async.nextTick.wrapCallback_ = goog.functions.identity;
    207
    208
    209// Register the callback function as an entry point, so that it can be
    210// monitored for exception handling, etc. This has to be done in this file
    211// since it requires special code to handle all browsers.
    212goog.debug.entryPointRegistry.register(
    213 /**
    214 * @param {function(!Function): !Function} transformer The transforming
    215 * function.
    216 */
    217 function(transformer) {
    218 goog.async.nextTick.wrapCallback_ = transformer;
    219 });
    \ No newline at end of file +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 index c74a4e8..aa75694 100644 --- a/docs/source/lib/goog/async/run.js.src.html +++ b/docs/source/lib/goog/async/run.js.src.html @@ -1 +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.nextTick');
    18goog.require('goog.async.throwException');
    19goog.require('goog.testing.watchers');
    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_.push(
    41 new goog.async.run.WorkItem_(callback, opt_context));
    42};
    43
    44
    45/**
    46 * Initializes the function to use to process the work queue.
    47 * @private
    48 */
    49goog.async.run.initializeRunner_ = function() {
    50 // If native Promises are available in the browser, just schedule the callback
    51 // on a fulfilled promise, which is specified to be async, but as fast as
    52 // possible.
    53 if (goog.global.Promise && goog.global.Promise.resolve) {
    54 var promise = goog.global.Promise.resolve();
    55 goog.async.run.schedule_ = function() {
    56 promise.then(goog.async.run.processWorkQueue);
    57 };
    58 } else {
    59 goog.async.run.schedule_ = function() {
    60 goog.async.nextTick(goog.async.run.processWorkQueue);
    61 };
    62 }
    63};
    64
    65
    66/**
    67 * Forces goog.async.run to use nextTick instead of Promise.
    68 *
    69 * This should only be done in unit tests. It's useful because MockClock
    70 * replaces nextTick, but not the browser Promise implementation, so it allows
    71 * Promise-based code to be tested with MockClock.
    72 */
    73goog.async.run.forceNextTick = function() {
    74 goog.async.run.schedule_ = function() {
    75 goog.async.nextTick(goog.async.run.processWorkQueue);
    76 };
    77};
    78
    79
    80/**
    81 * The function used to schedule work asynchronousely.
    82 * @private {function()}
    83 */
    84goog.async.run.schedule_;
    85
    86
    87/** @private {boolean} */
    88goog.async.run.workQueueScheduled_ = false;
    89
    90
    91/** @private {!Array.<!goog.async.run.WorkItem_>} */
    92goog.async.run.workQueue_ = [];
    93
    94
    95if (goog.DEBUG) {
    96 /**
    97 * Reset the event queue.
    98 * @private
    99 */
    100 goog.async.run.resetQueue_ = function() {
    101 goog.async.run.workQueueScheduled_ = false;
    102 goog.async.run.workQueue_ = [];
    103 };
    104
    105 // If there is a clock implemenation in use for testing
    106 // and it is reset, reset the queue.
    107 goog.testing.watchers.watchClockReset(goog.async.run.resetQueue_);
    108}
    109
    110
    111/**
    112 * Run any pending goog.async.run work items. This function is not intended
    113 * for general use, but for use by entry point handlers to run items ahead of
    114 * goog.async.nextTick.
    115 */
    116goog.async.run.processWorkQueue = function() {
    117 // NOTE: additional work queue items may be pushed while processing.
    118 while (goog.async.run.workQueue_.length) {
    119 // Don't let the work queue grow indefinitely.
    120 var workItems = goog.async.run.workQueue_;
    121 goog.async.run.workQueue_ = [];
    122 for (var i = 0; i < workItems.length; i++) {
    123 var workItem = workItems[i];
    124 try {
    125 workItem.fn.call(workItem.scope);
    126 } catch (e) {
    127 goog.async.throwException(e);
    128 }
    129 }
    130 }
    131
    132 // There are no more work items, reset the work queue.
    133 goog.async.run.workQueueScheduled_ = false;
    134};
    135
    136
    137
    138/**
    139 * @constructor
    140 * @final
    141 * @struct
    142 * @private
    143 *
    144 * @param {function()} fn
    145 * @param {Object|null|undefined} scope
    146 */
    147goog.async.run.WorkItem_ = function(fn, scope) {
    148 /** @const */ this.fn = fn;
    149 /** @const */ this.scope = scope;
    150};
    \ No newline at end of file +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');
    20goog.require('goog.testing.watchers');
    21
    22
    23/**
    24 * Fires the provided callback just before the current callstack unwinds, or as
    25 * soon as possible after the current JS execution context.
    26 * @param {function(this:THIS)} callback
    27 * @param {THIS=} opt_context Object to use as the "this value" when calling
    28 * the provided function.
    29 * @template THIS
    30 */
    31goog.async.run = function(callback, opt_context) {
    32 if (!goog.async.run.schedule_) {
    33 goog.async.run.initializeRunner_();
    34 }
    35 if (!goog.async.run.workQueueScheduled_) {
    36 // Nothing is currently scheduled, schedule it now.
    37 goog.async.run.schedule_();
    38 goog.async.run.workQueueScheduled_ = true;
    39 }
    40
    41 goog.async.run.workQueue_.add(callback, opt_context);
    42};
    43
    44
    45/**
    46 * Initializes the function to use to process the work queue.
    47 * @private
    48 */
    49goog.async.run.initializeRunner_ = function() {
    50 // If native Promises are available in the browser, just schedule the callback
    51 // on a fulfilled promise, which is specified to be async, but as fast as
    52 // possible.
    53 if (goog.global.Promise && goog.global.Promise.resolve) {
    54 var promise = goog.global.Promise.resolve();
    55 goog.async.run.schedule_ = function() {
    56 promise.then(goog.async.run.processWorkQueue);
    57 };
    58 } else {
    59 goog.async.run.schedule_ = function() {
    60 goog.async.nextTick(goog.async.run.processWorkQueue);
    61 };
    62 }
    63};
    64
    65
    66/**
    67 * Forces goog.async.run to use nextTick instead of Promise.
    68 *
    69 * This should only be done in unit tests. It's useful because MockClock
    70 * replaces nextTick, but not the browser Promise implementation, so it allows
    71 * Promise-based code to be tested with MockClock.
    72 *
    73 * However, we also want to run promises if the MockClock is no longer in
    74 * control so we schedule a backup "setTimeout" to the unmocked timeout if
    75 * provided.
    76 *
    77 * @param {function(function())=} opt_realSetTimeout
    78 */
    79goog.async.run.forceNextTick = function(opt_realSetTimeout) {
    80 goog.async.run.schedule_ = function() {
    81 goog.async.nextTick(goog.async.run.processWorkQueue);
    82 if (opt_realSetTimeout) {
    83 opt_realSetTimeout(goog.async.run.processWorkQueue);
    84 }
    85 };
    86};
    87
    88
    89/**
    90 * The function used to schedule work asynchronousely.
    91 * @private {function()}
    92 */
    93goog.async.run.schedule_;
    94
    95
    96/** @private {boolean} */
    97goog.async.run.workQueueScheduled_ = false;
    98
    99
    100/** @private {!goog.async.WorkQueue} */
    101goog.async.run.workQueue_ = new goog.async.WorkQueue();
    102
    103
    104if (goog.DEBUG) {
    105 /**
    106 * Reset the work queue.
    107 * @private
    108 */
    109 goog.async.run.resetQueue_ = function() {
    110 goog.async.run.workQueueScheduled_ = false;
    111 goog.async.run.workQueue_ = new goog.async.WorkQueue();
    112 };
    113
    114 // If there is a clock implemenation in use for testing
    115 // and it is reset, reset the queue.
    116 goog.testing.watchers.watchClockReset(goog.async.run.resetQueue_);
    117}
    118
    119
    120/**
    121 * Run any pending goog.async.run work items. This function is not intended
    122 * for general use, but for use by entry point handlers to run items ahead of
    123 * goog.async.nextTick.
    124 */
    125goog.async.run.processWorkQueue = function() {
    126 // NOTE: additional work queue items may be added while processing.
    127 var item = null;
    128 while (item = goog.async.run.workQueue_.remove()) {
    129 try {
    130 item.fn.call(item.scope);
    131 } catch (e) {
    132 goog.async.throwException(e);
    133 }
    134 goog.async.run.workQueue_.returnUnused(item);
    135 }
    136
    137 // There are no more work items, allow processing to be scheduled again.
    138 goog.async.run.workQueueScheduled_ = false;
    139};
    \ 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 5bcc4b1..cbe7f1b 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 already
    36 * defined in the current scope before assigning to prevent clobbering if
    37 * base.js is loaded more than once.
    38 *
    39 * @const
    40 */
    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_UNCOMPILED_DEFINES} may be defined before
    54 * loading base.js. If a key is defined in {@code CLOSURE_UNCOMPILED_DEFINES},
    55 * {@code goog.define} will use the value instead of the default value. This
    56 * allows flags to be overwritten without compilation (this is normally
    57 * accomplished with the compiler's "define" flag).
    58 *
    59 * Example:
    60 * <pre>
    61 * var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};
    62 * </pre>
    63 *
    64 * @type {Object.<string, (string|number|boolean)>|undefined}
    65 */
    66goog.global.CLOSURE_UNCOMPILED_DEFINES;
    67
    68
    69/**
    70 * A hook for overriding the define values in uncompiled or compiled mode,
    71 * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In
    72 * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence.
    73 *
    74 * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or
    75 * string literals or the compiler will emit an error.
    76 *
    77 * While any @define value may be set, only those set with goog.define will be
    78 * effective for uncompiled code.
    79 *
    80 * Example:
    81 * <pre>
    82 * var CLOSURE_DEFINES = {'goog.DEBUG': false};
    83 * </pre>
    84 *
    85 * @type {Object.<string, (string|number|boolean)>|undefined}
    86 */
    87goog.global.CLOSURE_DEFINES;
    88
    89
    90/**
    91 * Returns true if the specified value is not undefined.
    92 * WARNING: Do not use this to test if an object has a property. Use the in
    93 * operator instead.
    94 *
    95 * @param {?} val Variable to test.
    96 * @return {boolean} Whether variable is defined.
    97 */
    98goog.isDef = function(val) {
    99 // void 0 always evaluates to undefined and hence we do not need to depend on
    100 // the definition of the global variable named 'undefined'.
    101 return val !== void 0;
    102};
    103
    104
    105/**
    106 * Builds an object structure for the provided namespace path, ensuring that
    107 * names that already exist are not overwritten. For example:
    108 * "a.b.c" -> a = {};a.b={};a.b.c={};
    109 * Used by goog.provide and goog.exportSymbol.
    110 * @param {string} name name of the object that this file defines.
    111 * @param {*=} opt_object the object to expose at the end of the path.
    112 * @param {Object=} opt_objectToExportTo The object to add the path to; default
    113 * is |goog.global|.
    114 * @private
    115 */
    116goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) {
    117 var parts = name.split('.');
    118 var cur = opt_objectToExportTo || goog.global;
    119
    120 // Internet Explorer exhibits strange behavior when throwing errors from
    121 // methods externed in this manner. See the testExportSymbolExceptions in
    122 // base_test.html for an example.
    123 if (!(parts[0] in cur) && cur.execScript) {
    124 cur.execScript('var ' + parts[0]);
    125 }
    126
    127 // Certain browsers cannot parse code in the form for((a in b); c;);
    128 // This pattern is produced by the JSCompiler when it collapses the
    129 // statement above into the conditional loop below. To prevent this from
    130 // happening, use a for-loop and reserve the init logic as below.
    131
    132 // Parentheses added to eliminate strict JS warning in Firefox.
    133 for (var part; parts.length && (part = parts.shift());) {
    134 if (!parts.length && goog.isDef(opt_object)) {
    135 // last part and we have an object; use it
    136 cur[part] = opt_object;
    137 } else if (cur[part]) {
    138 cur = cur[part];
    139 } else {
    140 cur = cur[part] = {};
    141 }
    142 }
    143};
    144
    145
    146/**
    147 * Defines a named value. In uncompiled mode, the value is retreived from
    148 * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and
    149 * has the property specified, and otherwise used the defined defaultValue.
    150 * When compiled, the default can be overridden using compiler command-line
    151 * options.
    152 *
    153 * @param {string} name The distinguished name to provide.
    154 * @param {string|number|boolean} defaultValue
    155 */
    156goog.define = function(name, defaultValue) {
    157 var value = defaultValue;
    158 if (!COMPILED) {
    159 if (goog.global.CLOSURE_UNCOMPILED_DEFINES &&
    160 Object.prototype.hasOwnProperty.call(
    161 goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) {
    162 value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name];
    163 } else if (goog.global.CLOSURE_DEFINES &&
    164 Object.prototype.hasOwnProperty.call(
    165 goog.global.CLOSURE_DEFINES, name)) {
    166 value = goog.global.CLOSURE_DEFINES[name];
    167 }
    168 }
    169 goog.exportPath_(name, value);
    170};
    171
    172
    173/**
    174 * @define {boolean} DEBUG is provided as a convenience so that debugging code
    175 * that should not be included in a production js_binary can be easily stripped
    176 * by specifying --define goog.DEBUG=false to the JSCompiler. For example, most
    177 * toString() methods should be declared inside an "if (goog.DEBUG)" conditional
    178 * because they are generally used for debugging purposes and it is difficult
    179 * for the JSCompiler to statically determine whether they are used.
    180 */
    181goog.DEBUG = true;
    182
    183
    184/**
    185 * @define {string} LOCALE defines the locale being used for compilation. It is
    186 * used to select locale specific data to be compiled in js binary. BUILD rule
    187 * can specify this value by "--define goog.LOCALE=<locale_name>" as JSCompiler
    188 * option.
    189 *
    190 * Take into account that the locale code format is important. You should use
    191 * the canonical Unicode format with hyphen as a delimiter. Language must be
    192 * lowercase, Language Script - Capitalized, Region - UPPERCASE.
    193 * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN.
    194 *
    195 * See more info about locale codes here:
    196 * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers
    197 *
    198 * For language codes you should use values defined by ISO 693-1. See it here
    199 * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from
    200 * this rule: the Hebrew language. For legacy reasons the old code (iw) should
    201 * be used instead of the new code (he), see http://wiki/Main/IIISynonyms.
    202 */
    203goog.define('goog.LOCALE', 'en'); // default to en
    204
    205
    206/**
    207 * @define {boolean} Whether this code is running on trusted sites.
    208 *
    209 * On untrusted sites, several native functions can be defined or overridden by
    210 * external libraries like Prototype, Datejs, and JQuery and setting this flag
    211 * to false forces closure to use its own implementations when possible.
    212 *
    213 * If your JavaScript can be loaded by a third party site and you are wary about
    214 * relying on non-standard implementations, specify
    215 * "--define goog.TRUSTED_SITE=false" to the JSCompiler.
    216 */
    217goog.define('goog.TRUSTED_SITE', true);
    218
    219
    220/**
    221 * @define {boolean} Whether a project is expected to be running in strict mode.
    222 *
    223 * This define can be used to trigger alternate implementations compatible with
    224 * running in EcmaScript Strict mode or warn about unavailable functionality.
    225 * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode
    226 */
    227goog.define('goog.STRICT_MODE_COMPATIBLE', false);
    228
    229
    230/**
    231 * Creates object stubs for a namespace. The presence of one or more
    232 * goog.provide() calls indicate that the file defines the given
    233 * objects/namespaces. Provided objects must not be null or undefined.
    234 * Build tools also scan for provide/require statements
    235 * to discern dependencies, build dependency files (see deps.js), etc.
    236 * @see goog.require
    237 * @param {string} name Namespace provided by this file in the form
    238 * "goog.package.part".
    239 */
    240goog.provide = function(name) {
    241 if (!COMPILED) {
    242 // Ensure that the same namespace isn't provided twice.
    243 // A goog.module/goog.provide maps a goog.require to a specific file
    244 if (goog.isProvided_(name)) {
    245 throw Error('Namespace "' + name + '" already declared.');
    246 }
    247 delete goog.implicitNamespaces_[name];
    248
    249 var namespace = name;
    250 while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {
    251 if (goog.getObjectByName(namespace)) {
    252 break;
    253 }
    254 goog.implicitNamespaces_[namespace] = true;
    255 }
    256 }
    257
    258 goog.exportPath_(name);
    259};
    260
    261
    262/**
    263 * goog.module serves two purposes:
    264 * - marks a file that must be loaded as a module
    265 * - reserves a namespace (it can not also be goog.provided)
    266 * and has three requirements:
    267 * - goog.module may not be used in the same file as goog.provide.
    268 * - goog.module must be the first statement in the file.
    269 * - only one goog.module is allowed per file.
    270 * When a goog.module annotated file is loaded, it is loaded enclosed in
    271 * a strict function closure. This means that:
    272 * - any variable declared in a goog.module file are private to the file,
    273 * not global. Although the compiler is expected to inline the module.
    274 * - The code must obey all the rules of "strict" JavaScript.
    275 * - the file will be marked as "use strict"
    276 *
    277 * NOTE: unlike goog.provide, goog.module does not declare any symbols by
    278 * itself.
    279 *
    280 * @param {string} name Namespace provided by this file in the form
    281 * "goog.package.part", is expected but not required.
    282 */
    283goog.module = function(name) {
    284 if (!goog.isString(name) || !name) {
    285 throw Error('Invalid module identifier');
    286 }
    287 if (!goog.isInModuleLoader_()) {
    288 throw Error('Module ' + name + ' has been loaded incorrectly.');
    289 }
    290 if (goog.moduleLoaderState_.moduleName) {
    291 throw Error('goog.module may only be called once per module.');
    292 }
    293
    294 // Store the module name for the loader.
    295 goog.moduleLoaderState_.moduleName = name;
    296 if (!COMPILED) {
    297 // Ensure that the same namespace isn't provided twice.
    298 // A goog.module/goog.provide maps a goog.require to a specific file
    299 if (goog.isProvided_(name)) {
    300 throw Error('Namespace "' + name + '" already declared.');
    301 }
    302 delete goog.implicitNamespaces_[name];
    303 }
    304};
    305
    306
    307/** @private {{
    308 * moduleName:(string|undefined),
    309 * exportTestMethods:boolean}|null}}
    310 */
    311goog.moduleLoaderState_ = null;
    312
    313
    314/**
    315 * @private
    316 * @return {boolean} Whether a goog.module is currently being initialized.
    317 */
    318goog.isInModuleLoader_ = function() {
    319 return goog.moduleLoaderState_ != null;
    320};
    321
    322
    323/**
    324 * Indicate that a module's exports that are known test methods should
    325 * be copied to the global object. This makes the test methods visible to
    326 * test runners that inspect the global object.
    327 *
    328 * TODO(johnlenz): Make the test framework aware of goog.module so
    329 * that this isn't necessary. Alternately combine this with goog.setTestOnly
    330 * to minimize boiler plate.
    331 */
    332goog.module.exportTestMethods = function() {
    333 if (!goog.isInModuleLoader_()) {
    334 throw new Error('goog.module.exportTestMethods must be called from ' +
    335 'within a goog.module');
    336 }
    337 goog.moduleLoaderState_.exportTestMethods = true;
    338};
    339
    340
    341/**
    342 * Marks that the current file should only be used for testing, and never for
    343 * live code in production.
    344 *
    345 * In the case of unit tests, the message may optionally be an exact namespace
    346 * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra
    347 * provide (if not explicitly defined in the code).
    348 *
    349 * @param {string=} opt_message Optional message to add to the error that's
    350 * raised when used in production code.
    351 */
    352goog.setTestOnly = function(opt_message) {
    353 if (COMPILED && !goog.DEBUG) {
    354 opt_message = opt_message || '';
    355 throw Error('Importing test-only code into non-debug environment' +
    356 (opt_message ? ': ' + opt_message : '.'));
    357 }
    358};
    359
    360
    361/**
    362 * Forward declares a symbol. This is an indication to the compiler that the
    363 * symbol may be used in the source yet is not required and may not be provided
    364 * in compilation.
    365 *
    366 * The most common usage of forward declaration is code that takes a type as a
    367 * function parameter but does not need to require it. By forward declaring
    368 * instead of requiring, no hard dependency is made, and (if not required
    369 * elsewhere) the namespace may never be required and thus, not be pulled
    370 * into the JavaScript binary. If it is required elsewhere, it will be type
    371 * checked as normal.
    372 *
    373 *
    374 * @param {string} name The namespace to forward declare in the form of
    375 * "goog.package.part".
    376 */
    377goog.forwardDeclare = function(name) {};
    378
    379
    380if (!COMPILED) {
    381
    382 /**
    383 * Check if the given name has been goog.provided. This will return false for
    384 * names that are available only as implicit namespaces.
    385 * @param {string} name name of the object to look for.
    386 * @return {boolean} Whether the name has been provided.
    387 * @private
    388 */
    389 goog.isProvided_ = function(name) {
    390 return (name in goog.loadedModules_) ||
    391 (!goog.implicitNamespaces_[name] &&
    392 goog.isDefAndNotNull(goog.getObjectByName(name)));
    393 };
    394
    395 /**
    396 * Namespaces implicitly defined by goog.provide. For example,
    397 * goog.provide('goog.events.Event') implicitly declares that 'goog' and
    398 * 'goog.events' must be namespaces.
    399 *
    400 * @type {Object.<string, (boolean|undefined)>}
    401 * @private
    402 */
    403 goog.implicitNamespaces_ = {'goog.module': true};
    404
    405 // NOTE: We add goog.module as an implicit namespace as goog.module is defined
    406 // here and because the existing module package has not been moved yet out of
    407 // the goog.module namespace. This satisifies both the debug loader and
    408 // ahead-of-time dependency management.
    409}
    410
    411
    412/**
    413 * Returns an object based on its fully qualified external name. The object
    414 * is not found if null or undefined. If you are using a compilation pass that
    415 * renames property names beware that using this function will not find renamed
    416 * properties.
    417 *
    418 * @param {string} name The fully qualified name.
    419 * @param {Object=} opt_obj The object within which to look; default is
    420 * |goog.global|.
    421 * @return {?} The value (object or primitive) or, if not found, null.
    422 */
    423goog.getObjectByName = function(name, opt_obj) {
    424 var parts = name.split('.');
    425 var cur = opt_obj || goog.global;
    426 for (var part; part = parts.shift(); ) {
    427 if (goog.isDefAndNotNull(cur[part])) {
    428 cur = cur[part];
    429 } else {
    430 return null;
    431 }
    432 }
    433 return cur;
    434};
    435
    436
    437/**
    438 * Globalizes a whole namespace, such as goog or goog.lang.
    439 *
    440 * @param {Object} obj The namespace to globalize.
    441 * @param {Object=} opt_global The object to add the properties to.
    442 * @deprecated Properties may be explicitly exported to the global scope, but
    443 * this should no longer be done in bulk.
    444 */
    445goog.globalize = function(obj, opt_global) {
    446 var global = opt_global || goog.global;
    447 for (var x in obj) {
    448 global[x] = obj[x];
    449 }
    450};
    451
    452
    453/**
    454 * Adds a dependency from a file to the files it requires.
    455 * @param {string} relPath The path to the js file.
    456 * @param {Array} provides An array of strings with the names of the objects
    457 * this file provides.
    458 * @param {Array} requires An array of strings with the names of the objects
    459 * this file requires.
    460 * @param {boolean=} opt_isModule Whether this dependency must be loaded as
    461 * a module as declared by goog.module.
    462 */
    463goog.addDependency = function(relPath, provides, requires, opt_isModule) {
    464 if (goog.DEPENDENCIES_ENABLED) {
    465 var provide, require;
    466 var path = relPath.replace(/\\/g, '/');
    467 var deps = goog.dependencies_;
    468 for (var i = 0; provide = provides[i]; i++) {
    469 deps.nameToPath[provide] = path;
    470 deps.pathIsModule[path] = !!opt_isModule;
    471 }
    472 for (var j = 0; require = requires[j]; j++) {
    473 if (!(path in deps.requires)) {
    474 deps.requires[path] = {};
    475 }
    476 deps.requires[path][require] = true;
    477 }
    478 }
    479};
    480
    481
    482
    483
    484// NOTE(nnaze): The debug DOM loader was included in base.js as an original way
    485// to do "debug-mode" development. The dependency system can sometimes be
    486// confusing, as can the debug DOM loader's asynchronous nature.
    487//
    488// With the DOM loader, a call to goog.require() is not blocking -- the script
    489// will not load until some point after the current script. If a namespace is
    490// needed at runtime, it needs to be defined in a previous script, or loaded via
    491// require() with its registered dependencies.
    492// User-defined namespaces may need their own deps file. See http://go/js_deps,
    493// http://go/genjsdeps, or, externally, DepsWriter.
    494// https://developers.google.com/closure/library/docs/depswriter
    495//
    496// Because of legacy clients, the DOM loader can't be easily removed from
    497// base.js. Work is being done to make it disableable or replaceable for
    498// different environments (DOM-less JavaScript interpreters like Rhino or V8,
    499// for example). See bootstrap/ for more information.
    500
    501
    502/**
    503 * @define {boolean} Whether to enable the debug loader.
    504 *
    505 * If enabled, a call to goog.require() will attempt to load the namespace by
    506 * appending a script tag to the DOM (if the namespace has been registered).
    507 *
    508 * If disabled, goog.require() will simply assert that the namespace has been
    509 * provided (and depend on the fact that some outside tool correctly ordered
    510 * the script).
    511 */
    512goog.define('goog.ENABLE_DEBUG_LOADER', true);
    513
    514
    515/**
    516 * @param {string} msg
    517 * @private
    518 */
    519goog.logToConsole_ = function(msg) {
    520 if (goog.global.console) {
    521 goog.global.console['error'](msg);
    522 }
    523};
    524
    525
    526/**
    527 * Implements a system for the dynamic resolution of dependencies that works in
    528 * parallel with the BUILD system. Note that all calls to goog.require will be
    529 * stripped by the JSCompiler when the --closure_pass option is used.
    530 * @see goog.provide
    531 * @param {string} name Namespace to include (as was given in goog.provide()) in
    532 * the form "goog.package.part".
    533 * @return {?} If called within a goog.module file, the associated namespace or
    534 * module otherwise null.
    535 */
    536goog.require = function(name) {
    537
    538 // If the object already exists we do not need do do anything.
    539 if (!COMPILED) {
    540 if (goog.isProvided_(name)) {
    541 if (goog.isInModuleLoader_()) {
    542 // goog.require only return a value with-in goog.module files.
    543 return name in goog.loadedModules_ ?
    544 goog.loadedModules_[name] :
    545 goog.getObjectByName(name);
    546 } else {
    547 return null;
    548 }
    549 }
    550
    551 if (goog.ENABLE_DEBUG_LOADER) {
    552 var path = goog.getPathFromDeps_(name);
    553 if (path) {
    554 goog.included_[path] = true;
    555 goog.writeScripts_();
    556 return null;
    557 }
    558 }
    559
    560 var errorMessage = 'goog.require could not find: ' + name;
    561 goog.logToConsole_(errorMessage);
    562
    563 throw Error(errorMessage);
    564 }
    565};
    566
    567
    568/**
    569 * Path for included scripts.
    570 * @type {string}
    571 */
    572goog.basePath = '';
    573
    574
    575/**
    576 * A hook for overriding the base path.
    577 * @type {string|undefined}
    578 */
    579goog.global.CLOSURE_BASE_PATH;
    580
    581
    582/**
    583 * Whether to write out Closure's deps file. By default, the deps are written.
    584 * @type {boolean|undefined}
    585 */
    586goog.global.CLOSURE_NO_DEPS;
    587
    588
    589/**
    590 * A function to import a single script. This is meant to be overridden when
    591 * Closure is being run in non-HTML contexts, such as web workers. It's defined
    592 * in the global scope so that it can be set before base.js is loaded, which
    593 * allows deps.js to be imported properly.
    594 *
    595 * The function is passed the script source, which is a relative URI. It should
    596 * return true if the script was imported, false otherwise.
    597 * @type {(function(string): boolean)|undefined}
    598 */
    599goog.global.CLOSURE_IMPORT_SCRIPT;
    600
    601
    602/**
    603 * Null function used for default values of callbacks, etc.
    604 * @return {void} Nothing.
    605 */
    606goog.nullFunction = function() {};
    607
    608
    609/**
    610 * The identity function. Returns its first argument.
    611 *
    612 * @param {*=} opt_returnValue The single value that will be returned.
    613 * @param {...*} var_args Optional trailing arguments. These are ignored.
    614 * @return {?} The first argument. We can't know the type -- just pass it along
    615 * without type.
    616 * @deprecated Use goog.functions.identity instead.
    617 */
    618goog.identityFunction = function(opt_returnValue, var_args) {
    619 return opt_returnValue;
    620};
    621
    622
    623/**
    624 * When defining a class Foo with an abstract method bar(), you can do:
    625 * Foo.prototype.bar = goog.abstractMethod
    626 *
    627 * Now if a subclass of Foo fails to override bar(), an error will be thrown
    628 * when bar() is invoked.
    629 *
    630 * Note: This does not take the name of the function to override as an argument
    631 * because that would make it more difficult to obfuscate our JavaScript code.
    632 *
    633 * @type {!Function}
    634 * @throws {Error} when invoked to indicate the method should be overridden.
    635 */
    636goog.abstractMethod = function() {
    637 throw Error('unimplemented abstract method');
    638};
    639
    640
    641/**
    642 * Adds a {@code getInstance} static method that always returns the same
    643 * instance object.
    644 * @param {!Function} ctor The constructor for the class to add the static
    645 * method to.
    646 */
    647goog.addSingletonGetter = function(ctor) {
    648 ctor.getInstance = function() {
    649 if (ctor.instance_) {
    650 return ctor.instance_;
    651 }
    652 if (goog.DEBUG) {
    653 // NOTE: JSCompiler can't optimize away Array#push.
    654 goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor;
    655 }
    656 return ctor.instance_ = new ctor;
    657 };
    658};
    659
    660
    661/**
    662 * All singleton classes that have been instantiated, for testing. Don't read
    663 * it directly, use the {@code goog.testing.singleton} module. The compiler
    664 * removes this variable if unused.
    665 * @type {!Array.<!Function>}
    666 * @private
    667 */
    668goog.instantiatedSingletons_ = [];
    669
    670
    671/**
    672 * @define {boolean} Whether to load goog.modules using {@code eval} when using
    673 * the debug loader. This provides a better debugging experience as the
    674 * source is unmodified and can be edited using Chrome Workspaces or
    675 * similiar. However in some environments the use of {@code eval} is banned
    676 * so we provide an alternative.
    677 */
    678goog.define('goog.LOAD_MODULE_USING_EVAL', true);
    679
    680
    681/**
    682 * The registry of initialized modules:
    683 * the module identifier to module exports map.
    684 * @private @const {Object.<string, ?>}
    685 */
    686goog.loadedModules_ = {};
    687
    688
    689/**
    690 * True if goog.dependencies_ is available.
    691 * @const {boolean}
    692 */
    693goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER;
    694
    695
    696if (goog.DEPENDENCIES_ENABLED) {
    697 /**
    698 * Object used to keep track of urls that have already been added. This record
    699 * allows the prevention of circular dependencies.
    700 * @type {Object}
    701 * @private
    702 */
    703 goog.included_ = {};
    704
    705
    706 /**
    707 * This object is used to keep track of dependencies and other data that is
    708 * used for loading scripts.
    709 * @private
    710 * @type {Object}
    711 */
    712 goog.dependencies_ = {
    713 pathIsModule: {}, // 1 to 1
    714 nameToPath: {}, // many to 1
    715 requires: {}, // 1 to many
    716 // Used when resolving dependencies to prevent us from visiting file twice.
    717 visited: {},
    718 written: {} // Used to keep track of script files we have written.
    719 };
    720
    721
    722 /**
    723 * Tries to detect whether is in the context of an HTML document.
    724 * @return {boolean} True if it looks like HTML document.
    725 * @private
    726 */
    727 goog.inHtmlDocument_ = function() {
    728 var doc = goog.global.document;
    729 return typeof doc != 'undefined' &&
    730 'write' in doc; // XULDocument misses write.
    731 };
    732
    733
    734 /**
    735 * Tries to detect the base path of base.js script that bootstraps Closure.
    736 * @private
    737 */
    738 goog.findBasePath_ = function() {
    739 if (goog.global.CLOSURE_BASE_PATH) {
    740 goog.basePath = goog.global.CLOSURE_BASE_PATH;
    741 return;
    742 } else if (!goog.inHtmlDocument_()) {
    743 return;
    744 }
    745 var doc = goog.global.document;
    746 var scripts = doc.getElementsByTagName('script');
    747 // Search backwards since the current script is in almost all cases the one
    748 // that has base.js.
    749 for (var i = scripts.length - 1; i >= 0; --i) {
    750 var src = scripts[i].src;
    751 var qmark = src.lastIndexOf('?');
    752 var l = qmark == -1 ? src.length : qmark;
    753 if (src.substr(l - 7, 7) == 'base.js') {
    754 goog.basePath = src.substr(0, l - 7);
    755 return;
    756 }
    757 }
    758 };
    759
    760
    761 /**
    762 * Imports a script if, and only if, that script hasn't already been imported.
    763 * (Must be called at execution time)
    764 * @param {string} src Script source.
    765 * @param {string=} opt_sourceText The optionally source text to evaluate
    766 * @private
    767 */
    768 goog.importScript_ = function(src, opt_sourceText) {
    769 var importScript = goog.global.CLOSURE_IMPORT_SCRIPT ||
    770 goog.writeScriptTag_;
    771 if (importScript(src, opt_sourceText)) {
    772 goog.dependencies_.written[src] = true;
    773 }
    774 };
    775
    776
    777 /** @const @private {boolean} */
    778 goog.IS_OLD_IE_ = goog.global.document &&
    779 goog.global.document.all && !goog.global.atob;
    780
    781
    782 /**
    783 * Given a URL initiate retrieval and execution of the module.
    784 * @param {string} src Script source URL.
    785 * @private
    786 */
    787 goog.importModule_ = function(src) {
    788 // In an attempt to keep browsers from timing out loading scripts using
    789 // synchronous XHRs, put each load in its own script block.
    790 var bootstrap = 'goog.retrieveAndExecModule_("' + src + '");';
    791
    792 if (goog.importScript_('', bootstrap)) {
    793 goog.dependencies_.written[src] = true;
    794 }
    795 };
    796
    797
    798 /** @private {Array.<string>} */
    799 goog.queuedModules_ = [];
    800
    801
    802 /**
    803 * Retrieve and execute a module.
    804 * @param {string} src Script source URL.
    805 * @private
    806 */
    807 goog.retrieveAndExecModule_ = function(src) {
    808 var importScript = goog.global.CLOSURE_IMPORT_SCRIPT ||
    809 goog.writeScriptTag_;
    810
    811 var scriptText = null;
    812
    813 var xhr = new goog.global['XMLHttpRequest']();
    814
    815 /** @this {Object} */
    816 xhr.onload = function() {
    817 scriptText = this.responseText;
    818 };
    819 xhr.open('get', src, false);
    820 xhr.send();
    821
    822 scriptText = xhr.responseText;
    823
    824 if (scriptText != null) {
    825 var execModuleScript = goog.wrapModule_(src, scriptText);
    826 var isOldIE = goog.IS_OLD_IE_;
    827 if (isOldIE) {
    828 goog.queuedModules_.push(execModuleScript);
    829 } else {
    830 importScript(src, execModuleScript);
    831 }
    832 goog.dependencies_.written[src] = true;
    833 } else {
    834 throw new Error('load of ' + src + 'failed');
    835 }
    836 };
    837
    838
    839 /**
    840 * Return an appropriate module text. Suitable to insert into
    841 * a script tag (that is unescaped).
    842 * @param {string} srcUrl
    843 * @param {string} scriptText
    844 * @return {string}
    845 * @private
    846 */
    847 goog.wrapModule_ = function(srcUrl, scriptText) {
    848 if (!goog.LOAD_MODULE_USING_EVAL || !goog.isDef(goog.global.JSON)) {
    849 return '' +
    850 'goog.loadModule(function(exports) {' +
    851 '"use strict";' +
    852 scriptText +
    853 '\n' + // terminate any trailing single line comment.
    854 ';return exports' +
    855 '});' +
    856 '\n//# sourceURL=' + srcUrl + '\n';
    857 } else {
    858 return '' +
    859 'goog.loadModule(' +
    860 goog.global.JSON.stringify(
    861 scriptText + '\n//# sourceURL=' + srcUrl + '\n') +
    862 ');';
    863 }
    864 };
    865
    866
    867 /**
    868 * Load any deferred goog.module loads.
    869 * @private
    870 */
    871 goog.loadQueuedModules_ = function() {
    872 var count = goog.queuedModules_.length;
    873 if (count > 0) {
    874 var queue = goog.queuedModules_;
    875 goog.queuedModules_ = [];
    876 for (var i = 0; i < count; i++) {
    877 var entry = queue[i];
    878 goog.globalEval(entry);
    879 }
    880 }
    881 };
    882
    883
    884 /**
    885 * @param {function(?):?|string} moduleDef The module definition.
    886 */
    887 goog.loadModule = function(moduleDef) {
    888 // NOTE: we allow function definitions to be either in the from
    889 // of a string to eval (which keeps the original source intact) or
    890 // in a eval forbidden environment (CSP) we allow a function definition
    891 // which in its body must call {@code goog.module}, and return the exports
    892 // of the module.
    893 try {
    894 goog.moduleLoaderState_ = {
    895 moduleName: undefined, exportTestMethods: false};
    896 var exports;
    897 if (goog.isFunction(moduleDef)) {
    898 exports = moduleDef.call(goog.global, {});
    899 } else if (goog.isString(moduleDef)) {
    900 exports = goog.loadModuleFromSource_.call(goog.global, moduleDef);
    901 } else {
    902 throw Error('Invalid module definition');
    903 }
    904
    905 if (Object.seal) {
    906 Object.seal(exports);
    907 }
    908 var moduleName = goog.moduleLoaderState_.moduleName;
    909 if (!goog.isString(moduleName) || !moduleName) {
    910 throw Error('Invalid module name \"' + moduleName + '\"');
    911 }
    912
    913 goog.loadedModules_[moduleName] = exports;
    914 if (goog.moduleLoaderState_.exportTestMethods) {
    915 for (var entry in exports) {
    916 if (entry.indexOf('test', 0) === 0 ||
    917 entry == 'tearDown' ||
    918 entry == 'setup') {
    919 goog.global[entry] = exports[entry];
    920 }
    921 }
    922 }
    923 } finally {
    924 goog.moduleLoaderState_ = null;
    925 }
    926 };
    927
    928
    929 /**
    930 * @private @const {function(string):?}
    931 */
    932 goog.loadModuleFromSource_ = function() {
    933 // NOTE: we avoid declaring parameters or local variables here to avoid
    934 // masking globals or leaking values into the module definition.
    935 'use strict';
    936 var exports = {};
    937 eval(arguments[0]);
    938 return exports;
    939 };
    940
    941
    942 /**
    943 * The default implementation of the import function. Writes a script tag to
    944 * import the script.
    945 *
    946 * @param {string} src The script url.
    947 * @param {string=} opt_sourceText The optionally source text to evaluate
    948 * @return {boolean} True if the script was imported, false otherwise.
    949 * @private
    950 */
    951 goog.writeScriptTag_ = function(src, opt_sourceText) {
    952 if (goog.inHtmlDocument_()) {
    953 var doc = goog.global.document;
    954
    955 // If the user tries to require a new symbol after document load,
    956 // something has gone terribly wrong. Doing a document.write would
    957 // wipe out the page.
    958 if (doc.readyState == 'complete') {
    959 // Certain test frameworks load base.js multiple times, which tries
    960 // to write deps.js each time. If that happens, just fail silently.
    961 // These frameworks wipe the page between each load of base.js, so this
    962 // is OK.
    963 var isDeps = /\bdeps.js$/.test(src);
    964 if (isDeps) {
    965 return false;
    966 } else {
    967 throw Error('Cannot write "' + src + '" after document load');
    968 }
    969 }
    970
    971 var isOldIE = goog.IS_OLD_IE_;
    972
    973 if (opt_sourceText === undefined) {
    974 if (!isOldIE) {
    975 doc.write(
    976 '<script type="text/javascript" src="' +
    977 src + '"></' + 'script>');
    978 } else {
    979 var state = " onreadystatechange='goog.onScriptLoad_(this, " +
    980 ++goog.lastNonModuleScriptIndex_ + ")' ";
    981 doc.write(
    982 '<script type="text/javascript" src="' +
    983 src + '"' + state + '></' + 'script>');
    984 }
    985 } else {
    986 doc.write(
    987 '<script type="text/javascript">' +
    988 opt_sourceText +
    989 '</' + 'script>');
    990 }
    991 return true;
    992 } else {
    993 return false;
    994 }
    995 };
    996
    997
    998 /** @private {number} */
    999 goog.lastNonModuleScriptIndex_ = 0;
    1000
    1001
    1002 /**
    1003 * A readystatechange handler for legacy IE
    1004 * @param {HTMLScriptElement} script
    1005 * @param {number} scriptIndex
    1006 * @return {boolean}
    1007 * @private
    1008 */
    1009 goog.onScriptLoad_ = function(script, scriptIndex) {
    1010 // for now load the modules when we reach the last script,
    1011 // later allow more inter-mingling.
    1012 if (script.readyState == 'complete' &&
    1013 goog.lastNonModuleScriptIndex_ == scriptIndex) {
    1014 goog.loadQueuedModules_();
    1015 }
    1016 return true;
    1017 };
    1018
    1019 /**
    1020 * Resolves dependencies based on the dependencies added using addDependency
    1021 * and calls importScript_ in the correct order.
    1022 * @private
    1023 */
    1024 goog.writeScripts_ = function() {
    1025 // The scripts we need to write this time.
    1026 var scripts = [];
    1027 var seenScript = {};
    1028 var deps = goog.dependencies_;
    1029
    1030 function visitNode(path) {
    1031 if (path in deps.written) {
    1032 return;
    1033 }
    1034
    1035 // We have already visited this one. We can get here if we have cyclic
    1036 // dependencies.
    1037 if (path in deps.visited) {
    1038 if (!(path in seenScript)) {
    1039 seenScript[path] = true;
    1040 scripts.push(path);
    1041 }
    1042 return;
    1043 }
    1044
    1045 deps.visited[path] = true;
    1046
    1047 if (path in deps.requires) {
    1048 for (var requireName in deps.requires[path]) {
    1049 // If the required name is defined, we assume that it was already
    1050 // bootstrapped by other means.
    1051 if (!goog.isProvided_(requireName)) {
    1052 if (requireName in deps.nameToPath) {
    1053 visitNode(deps.nameToPath[requireName]);
    1054 } else {
    1055 throw Error('Undefined nameToPath for ' + requireName);
    1056 }
    1057 }
    1058 }
    1059 }
    1060
    1061 if (!(path in seenScript)) {
    1062 seenScript[path] = true;
    1063 scripts.push(path);
    1064 }
    1065 }
    1066
    1067 for (var path in goog.included_) {
    1068 if (!deps.written[path]) {
    1069 visitNode(path);
    1070 }
    1071 }
    1072
    1073 // record that we are going to load all these scripts.
    1074 for (var i = 0; i < scripts.length; i++) {
    1075 var path = scripts[i];
    1076 goog.dependencies_.written[path] = true;
    1077 }
    1078
    1079 // If a module is loaded synchronously then we need to
    1080 // clear the current inModuleLoader value, and restore it when we are
    1081 // done loading the current "requires".
    1082 var moduleState = goog.moduleLoaderState_;
    1083 goog.moduleLoaderState_ = null;
    1084
    1085 var loadingModule = false;
    1086 for (var i = 0; i < scripts.length; i++) {
    1087 var path = scripts[i];
    1088 if (path) {
    1089 if (!deps.pathIsModule[path]) {
    1090 goog.importScript_(goog.basePath + path);
    1091 } else {
    1092 loadingModule = true;
    1093 goog.importModule_(goog.basePath + path);
    1094 }
    1095 } else {
    1096 goog.moduleLoaderState_ = moduleState;
    1097 throw Error('Undefined script input');
    1098 }
    1099 }
    1100
    1101 // restore the current "module loading state"
    1102 goog.moduleLoaderState_ = moduleState;
    1103 };
    1104
    1105
    1106 /**
    1107 * Looks at the dependency rules and tries to determine the script file that
    1108 * fulfills a particular rule.
    1109 * @param {string} rule In the form goog.namespace.Class or project.script.
    1110 * @return {?string} Url corresponding to the rule, or null.
    1111 * @private
    1112 */
    1113 goog.getPathFromDeps_ = function(rule) {
    1114 if (rule in goog.dependencies_.nameToPath) {
    1115 return goog.dependencies_.nameToPath[rule];
    1116 } else {
    1117 return null;
    1118 }
    1119 };
    1120
    1121 goog.findBasePath_();
    1122
    1123 // Allow projects to manage the deps files themselves.
    1124 if (!goog.global.CLOSURE_NO_DEPS) {
    1125 goog.importScript_(goog.basePath + 'deps.js');
    1126 }
    1127}
    1128
    1129
    1130
    1131//==============================================================================
    1132// Language Enhancements
    1133//==============================================================================
    1134
    1135
    1136/**
    1137 * This is a "fixed" version of the typeof operator. It differs from the typeof
    1138 * operator in such a way that null returns 'null' and arrays return 'array'.
    1139 * @param {*} value The value to get the type of.
    1140 * @return {string} The name of the type.
    1141 */
    1142goog.typeOf = function(value) {
    1143 var s = typeof value;
    1144 if (s == 'object') {
    1145 if (value) {
    1146 // Check these first, so we can avoid calling Object.prototype.toString if
    1147 // possible.
    1148 //
    1149 // IE improperly marshals tyepof across execution contexts, but a
    1150 // cross-context object will still return false for "instanceof Object".
    1151 if (value instanceof Array) {
    1152 return 'array';
    1153 } else if (value instanceof Object) {
    1154 return s;
    1155 }
    1156
    1157 // HACK: In order to use an Object prototype method on the arbitrary
    1158 // value, the compiler requires the value be cast to type Object,
    1159 // even though the ECMA spec explicitly allows it.
    1160 var className = Object.prototype.toString.call(
    1161 /** @type {Object} */ (value));
    1162 // In Firefox 3.6, attempting to access iframe window objects' length
    1163 // property throws an NS_ERROR_FAILURE, so we need to special-case it
    1164 // here.
    1165 if (className == '[object Window]') {
    1166 return 'object';
    1167 }
    1168
    1169 // We cannot always use constructor == Array or instanceof Array because
    1170 // different frames have different Array objects. In IE6, if the iframe
    1171 // where the array was created is destroyed, the array loses its
    1172 // prototype. Then dereferencing val.splice here throws an exception, so
    1173 // we can't use goog.isFunction. Calling typeof directly returns 'unknown'
    1174 // so that will work. In this case, this function will return false and
    1175 // most array functions will still work because the array is still
    1176 // array-like (supports length and []) even though it has lost its
    1177 // prototype.
    1178 // Mark Miller noticed that Object.prototype.toString
    1179 // allows access to the unforgeable [[Class]] property.
    1180 // 15.2.4.2 Object.prototype.toString ( )
    1181 // When the toString method is called, the following steps are taken:
    1182 // 1. Get the [[Class]] property of this object.
    1183 // 2. Compute a string value by concatenating the three strings
    1184 // "[object ", Result(1), and "]".
    1185 // 3. Return Result(2).
    1186 // and this behavior survives the destruction of the execution context.
    1187 if ((className == '[object Array]' ||
    1188 // In IE all non value types are wrapped as objects across window
    1189 // boundaries (not iframe though) so we have to do object detection
    1190 // for this edge case.
    1191 typeof value.length == 'number' &&
    1192 typeof value.splice != 'undefined' &&
    1193 typeof value.propertyIsEnumerable != 'undefined' &&
    1194 !value.propertyIsEnumerable('splice')
    1195
    1196 )) {
    1197 return 'array';
    1198 }
    1199 // HACK: There is still an array case that fails.
    1200 // function ArrayImpostor() {}
    1201 // ArrayImpostor.prototype = [];
    1202 // var impostor = new ArrayImpostor;
    1203 // this can be fixed by getting rid of the fast path
    1204 // (value instanceof Array) and solely relying on
    1205 // (value && Object.prototype.toString.vall(value) === '[object Array]')
    1206 // but that would require many more function calls and is not warranted
    1207 // unless closure code is receiving objects from untrusted sources.
    1208
    1209 // IE in cross-window calls does not correctly marshal the function type
    1210 // (it appears just as an object) so we cannot use just typeof val ==
    1211 // 'function'. However, if the object has a call property, it is a
    1212 // function.
    1213 if ((className == '[object Function]' ||
    1214 typeof value.call != 'undefined' &&
    1215 typeof value.propertyIsEnumerable != 'undefined' &&
    1216 !value.propertyIsEnumerable('call'))) {
    1217 return 'function';
    1218 }
    1219
    1220 } else {
    1221 return 'null';
    1222 }
    1223
    1224 } else if (s == 'function' && typeof value.call == 'undefined') {
    1225 // In Safari typeof nodeList returns 'function', and on Firefox typeof
    1226 // behaves similarly for HTML{Applet,Embed,Object}, Elements and RegExps. We
    1227 // would like to return object for those and we can detect an invalid
    1228 // function by making sure that the function object has a call method.
    1229 return 'object';
    1230 }
    1231 return s;
    1232};
    1233
    1234
    1235/**
    1236 * Returns true if the specified value is null.
    1237 * @param {?} val Variable to test.
    1238 * @return {boolean} Whether variable is null.
    1239 */
    1240goog.isNull = function(val) {
    1241 return val === null;
    1242};
    1243
    1244
    1245/**
    1246 * Returns true if the specified value is defined and not null.
    1247 * @param {?} val Variable to test.
    1248 * @return {boolean} Whether variable is defined and not null.
    1249 */
    1250goog.isDefAndNotNull = function(val) {
    1251 // Note that undefined == null.
    1252 return val != null;
    1253};
    1254
    1255
    1256/**
    1257 * Returns true if the specified value is an array.
    1258 * @param {?} val Variable to test.
    1259 * @return {boolean} Whether variable is an array.
    1260 */
    1261goog.isArray = function(val) {
    1262 return goog.typeOf(val) == 'array';
    1263};
    1264
    1265
    1266/**
    1267 * Returns true if the object looks like an array. To qualify as array like
    1268 * the value needs to be either a NodeList or an object with a Number length
    1269 * property.
    1270 * @param {?} val Variable to test.
    1271 * @return {boolean} Whether variable is an array.
    1272 */
    1273goog.isArrayLike = function(val) {
    1274 var type = goog.typeOf(val);
    1275 return type == 'array' || type == 'object' && typeof val.length == 'number';
    1276};
    1277
    1278
    1279/**
    1280 * Returns true if the object looks like a Date. To qualify as Date-like the
    1281 * value needs to be an object and have a getFullYear() function.
    1282 * @param {?} val Variable to test.
    1283 * @return {boolean} Whether variable is a like a Date.
    1284 */
    1285goog.isDateLike = function(val) {
    1286 return goog.isObject(val) && typeof val.getFullYear == 'function';
    1287};
    1288
    1289
    1290/**
    1291 * Returns true if the specified value is a string.
    1292 * @param {?} val Variable to test.
    1293 * @return {boolean} Whether variable is a string.
    1294 */
    1295goog.isString = function(val) {
    1296 return typeof val == 'string';
    1297};
    1298
    1299
    1300/**
    1301 * Returns true if the specified value is a boolean.
    1302 * @param {?} val Variable to test.
    1303 * @return {boolean} Whether variable is boolean.
    1304 */
    1305goog.isBoolean = function(val) {
    1306 return typeof val == 'boolean';
    1307};
    1308
    1309
    1310/**
    1311 * Returns true if the specified value is a number.
    1312 * @param {?} val Variable to test.
    1313 * @return {boolean} Whether variable is a number.
    1314 */
    1315goog.isNumber = function(val) {
    1316 return typeof val == 'number';
    1317};
    1318
    1319
    1320/**
    1321 * Returns true if the specified value is a function.
    1322 * @param {?} val Variable to test.
    1323 * @return {boolean} Whether variable is a function.
    1324 */
    1325goog.isFunction = function(val) {
    1326 return goog.typeOf(val) == 'function';
    1327};
    1328
    1329
    1330/**
    1331 * Returns true if the specified value is an object. This includes arrays and
    1332 * functions.
    1333 * @param {?} val Variable to test.
    1334 * @return {boolean} Whether variable is an object.
    1335 */
    1336goog.isObject = function(val) {
    1337 var type = typeof val;
    1338 return type == 'object' && val != null || type == 'function';
    1339 // return Object(val) === val also works, but is slower, especially if val is
    1340 // not an object.
    1341};
    1342
    1343
    1344/**
    1345 * Gets a unique ID for an object. This mutates the object so that further calls
    1346 * with the same object as a parameter returns the same value. The unique ID is
    1347 * guaranteed to be unique across the current session amongst objects that are
    1348 * passed into {@code getUid}. There is no guarantee that the ID is unique or
    1349 * consistent across sessions. It is unsafe to generate unique ID for function
    1350 * prototypes.
    1351 *
    1352 * @param {Object} obj The object to get the unique ID for.
    1353 * @return {number} The unique ID for the object.
    1354 */
    1355goog.getUid = function(obj) {
    1356 // TODO(arv): Make the type stricter, do not accept null.
    1357
    1358 // In Opera window.hasOwnProperty exists but always returns false so we avoid
    1359 // using it. As a consequence the unique ID generated for BaseClass.prototype
    1360 // and SubClass.prototype will be the same.
    1361 return obj[goog.UID_PROPERTY_] ||
    1362 (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_);
    1363};
    1364
    1365
    1366/**
    1367 * Whether the given object is alreay assigned a unique ID.
    1368 *
    1369 * This does not modify the object.
    1370 *
    1371 * @param {Object} obj The object to check.
    1372 * @return {boolean} Whether there an assigned unique id for the object.
    1373 */
    1374goog.hasUid = function(obj) {
    1375 return !!obj[goog.UID_PROPERTY_];
    1376};
    1377
    1378
    1379/**
    1380 * Removes the unique ID from an object. This is useful if the object was
    1381 * previously mutated using {@code goog.getUid} in which case the mutation is
    1382 * undone.
    1383 * @param {Object} obj The object to remove the unique ID field from.
    1384 */
    1385goog.removeUid = function(obj) {
    1386 // TODO(arv): Make the type stricter, do not accept null.
    1387
    1388 // In IE, DOM nodes are not instances of Object and throw an exception if we
    1389 // try to delete. Instead we try to use removeAttribute.
    1390 if ('removeAttribute' in obj) {
    1391 obj.removeAttribute(goog.UID_PROPERTY_);
    1392 }
    1393 /** @preserveTry */
    1394 try {
    1395 delete obj[goog.UID_PROPERTY_];
    1396 } catch (ex) {
    1397 }
    1398};
    1399
    1400
    1401/**
    1402 * Name for unique ID property. Initialized in a way to help avoid collisions
    1403 * with other closure JavaScript on the same page.
    1404 * @type {string}
    1405 * @private
    1406 */
    1407goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0);
    1408
    1409
    1410/**
    1411 * Counter for UID.
    1412 * @type {number}
    1413 * @private
    1414 */
    1415goog.uidCounter_ = 0;
    1416
    1417
    1418/**
    1419 * Adds a hash code field to an object. The hash code is unique for the
    1420 * given object.
    1421 * @param {Object} obj The object to get the hash code for.
    1422 * @return {number} The hash code for the object.
    1423 * @deprecated Use goog.getUid instead.
    1424 */
    1425goog.getHashCode = goog.getUid;
    1426
    1427
    1428/**
    1429 * Removes the hash code field from an object.
    1430 * @param {Object} obj The object to remove the field from.
    1431 * @deprecated Use goog.removeUid instead.
    1432 */
    1433goog.removeHashCode = goog.removeUid;
    1434
    1435
    1436/**
    1437 * Clones a value. The input may be an Object, Array, or basic type. Objects and
    1438 * arrays will be cloned recursively.
    1439 *
    1440 * WARNINGS:
    1441 * <code>goog.cloneObject</code> does not detect reference loops. Objects that
    1442 * refer to themselves will cause infinite recursion.
    1443 *
    1444 * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies
    1445 * UIDs created by <code>getUid</code> into cloned results.
    1446 *
    1447 * @param {*} obj The value to clone.
    1448 * @return {*} A clone of the input value.
    1449 * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods.
    1450 */
    1451goog.cloneObject = function(obj) {
    1452 var type = goog.typeOf(obj);
    1453 if (type == 'object' || type == 'array') {
    1454 if (obj.clone) {
    1455 return obj.clone();
    1456 }
    1457 var clone = type == 'array' ? [] : {};
    1458 for (var key in obj) {
    1459 clone[key] = goog.cloneObject(obj[key]);
    1460 }
    1461 return clone;
    1462 }
    1463
    1464 return obj;
    1465};
    1466
    1467
    1468/**
    1469 * A native implementation of goog.bind.
    1470 * @param {Function} fn A function to partially apply.
    1471 * @param {Object|undefined} selfObj Specifies the object which this should
    1472 * point to when the function is run.
    1473 * @param {...*} var_args Additional arguments that are partially applied to the
    1474 * function.
    1475 * @return {!Function} A partially-applied form of the function bind() was
    1476 * invoked as a method of.
    1477 * @private
    1478 * @suppress {deprecated} The compiler thinks that Function.prototype.bind is
    1479 * deprecated because some people have declared a pure-JS version.
    1480 * Only the pure-JS version is truly deprecated.
    1481 */
    1482goog.bindNative_ = function(fn, selfObj, var_args) {
    1483 return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
    1484};
    1485
    1486
    1487/**
    1488 * A pure-JS implementation of goog.bind.
    1489 * @param {Function} fn A function to partially apply.
    1490 * @param {Object|undefined} selfObj Specifies the object which this should
    1491 * point to when the function is run.
    1492 * @param {...*} var_args Additional arguments that are partially applied to the
    1493 * function.
    1494 * @return {!Function} A partially-applied form of the function bind() was
    1495 * invoked as a method of.
    1496 * @private
    1497 */
    1498goog.bindJs_ = function(fn, selfObj, var_args) {
    1499 if (!fn) {
    1500 throw new Error();
    1501 }
    1502
    1503 if (arguments.length > 2) {
    1504 var boundArgs = Array.prototype.slice.call(arguments, 2);
    1505 return function() {
    1506 // Prepend the bound arguments to the current arguments.
    1507 var newArgs = Array.prototype.slice.call(arguments);
    1508 Array.prototype.unshift.apply(newArgs, boundArgs);
    1509 return fn.apply(selfObj, newArgs);
    1510 };
    1511
    1512 } else {
    1513 return function() {
    1514 return fn.apply(selfObj, arguments);
    1515 };
    1516 }
    1517};
    1518
    1519
    1520/**
    1521 * Partially applies this function to a particular 'this object' and zero or
    1522 * more arguments. The result is a new function with some arguments of the first
    1523 * function pre-filled and the value of this 'pre-specified'.
    1524 *
    1525 * Remaining arguments specified at call-time are appended to the pre-specified
    1526 * ones.
    1527 *
    1528 * Also see: {@link #partial}.
    1529 *
    1530 * Usage:
    1531 * <pre>var barMethBound = bind(myFunction, myObj, 'arg1', 'arg2');
    1532 * barMethBound('arg3', 'arg4');</pre>
    1533 *
    1534 * @param {?function(this:T, ...)} fn A function to partially apply.
    1535 * @param {T} selfObj Specifies the object which this should point to when the
    1536 * function is run.
    1537 * @param {...*} var_args Additional arguments that are partially applied to the
    1538 * function.
    1539 * @return {!Function} A partially-applied form of the function bind() was
    1540 * invoked as a method of.
    1541 * @template T
    1542 * @suppress {deprecated} See above.
    1543 */
    1544goog.bind = function(fn, selfObj, var_args) {
    1545 // TODO(nicksantos): narrow the type signature.
    1546 if (Function.prototype.bind &&
    1547 // NOTE(nicksantos): Somebody pulled base.js into the default Chrome
    1548 // extension environment. This means that for Chrome extensions, they get
    1549 // the implementation of Function.prototype.bind that calls goog.bind
    1550 // instead of the native one. Even worse, we don't want to introduce a
    1551 // circular dependency between goog.bind and Function.prototype.bind, so
    1552 // we have to hack this to make sure it works correctly.
    1553 Function.prototype.bind.toString().indexOf('native code') != -1) {
    1554 goog.bind = goog.bindNative_;
    1555 } else {
    1556 goog.bind = goog.bindJs_;
    1557 }
    1558 return goog.bind.apply(null, arguments);
    1559};
    1560
    1561
    1562/**
    1563 * Like bind(), except that a 'this object' is not required. Useful when the
    1564 * target function is already bound.
    1565 *
    1566 * Usage:
    1567 * var g = partial(f, arg1, arg2);
    1568 * g(arg3, arg4);
    1569 *
    1570 * @param {Function} fn A function to partially apply.
    1571 * @param {...*} var_args Additional arguments that are partially applied to fn.
    1572 * @return {!Function} A partially-applied form of the function bind() was
    1573 * invoked as a method of.
    1574 */
    1575goog.partial = function(fn, var_args) {
    1576 var args = Array.prototype.slice.call(arguments, 1);
    1577 return function() {
    1578 // Clone the array (with slice()) and append additional arguments
    1579 // to the existing arguments.
    1580 var newArgs = args.slice();
    1581 newArgs.push.apply(newArgs, arguments);
    1582 return fn.apply(this, newArgs);
    1583 };
    1584};
    1585
    1586
    1587/**
    1588 * Copies all the members of a source object to a target object. This method
    1589 * does not work on all browsers for all objects that contain keys such as
    1590 * toString or hasOwnProperty. Use goog.object.extend for this purpose.
    1591 * @param {Object} target Target.
    1592 * @param {Object} source Source.
    1593 */
    1594goog.mixin = function(target, source) {
    1595 for (var x in source) {
    1596 target[x] = source[x];
    1597 }
    1598
    1599 // For IE7 or lower, the for-in-loop does not contain any properties that are
    1600 // not enumerable on the prototype object (for example, isPrototypeOf from
    1601 // Object.prototype) but also it will not include 'replace' on objects that
    1602 // extend String and change 'replace' (not that it is common for anyone to
    1603 // extend anything except Object).
    1604};
    1605
    1606
    1607/**
    1608 * @return {number} An integer value representing the number of milliseconds
    1609 * between midnight, January 1, 1970 and the current time.
    1610 */
    1611goog.now = (goog.TRUSTED_SITE && Date.now) || (function() {
    1612 // Unary plus operator converts its operand to a number which in the case of
    1613 // a date is done by calling getTime().
    1614 return +new Date();
    1615});
    1616
    1617
    1618/**
    1619 * Evals JavaScript in the global scope. In IE this uses execScript, other
    1620 * browsers use goog.global.eval. If goog.global.eval does not evaluate in the
    1621 * global scope (for example, in Safari), appends a script tag instead.
    1622 * Throws an exception if neither execScript or eval is defined.
    1623 * @param {string} script JavaScript string.
    1624 */
    1625goog.globalEval = function(script) {
    1626 if (goog.global.execScript) {
    1627 goog.global.execScript(script, 'JavaScript');
    1628 } else if (goog.global.eval) {
    1629 // Test to see if eval works
    1630 if (goog.evalWorksForGlobals_ == null) {
    1631 goog.global.eval('var _et_ = 1;');
    1632 if (typeof goog.global['_et_'] != 'undefined') {
    1633 delete goog.global['_et_'];
    1634 goog.evalWorksForGlobals_ = true;
    1635 } else {
    1636 goog.evalWorksForGlobals_ = false;
    1637 }
    1638 }
    1639
    1640 if (goog.evalWorksForGlobals_) {
    1641 goog.global.eval(script);
    1642 } else {
    1643 var doc = goog.global.document;
    1644 var scriptElt = doc.createElement('script');
    1645 scriptElt.type = 'text/javascript';
    1646 scriptElt.defer = false;
    1647 // Note(user): can't use .innerHTML since "t('<test>')" will fail and
    1648 // .text doesn't work in Safari 2. Therefore we append a text node.
    1649 scriptElt.appendChild(doc.createTextNode(script));
    1650 doc.body.appendChild(scriptElt);
    1651 doc.body.removeChild(scriptElt);
    1652 }
    1653 } else {
    1654 throw Error('goog.globalEval not available');
    1655 }
    1656};
    1657
    1658
    1659/**
    1660 * Indicates whether or not we can call 'eval' directly to eval code in the
    1661 * global scope. Set to a Boolean by the first call to goog.globalEval (which
    1662 * empirically tests whether eval works for globals). @see goog.globalEval
    1663 * @type {?boolean}
    1664 * @private
    1665 */
    1666goog.evalWorksForGlobals_ = null;
    1667
    1668
    1669/**
    1670 * Optional map of CSS class names to obfuscated names used with
    1671 * goog.getCssName().
    1672 * @type {Object|undefined}
    1673 * @private
    1674 * @see goog.setCssNameMapping
    1675 */
    1676goog.cssNameMapping_;
    1677
    1678
    1679/**
    1680 * Optional obfuscation style for CSS class names. Should be set to either
    1681 * 'BY_WHOLE' or 'BY_PART' if defined.
    1682 * @type {string|undefined}
    1683 * @private
    1684 * @see goog.setCssNameMapping
    1685 */
    1686goog.cssNameMappingStyle_;
    1687
    1688
    1689/**
    1690 * Handles strings that are intended to be used as CSS class names.
    1691 *
    1692 * This function works in tandem with @see goog.setCssNameMapping.
    1693 *
    1694 * Without any mapping set, the arguments are simple joined with a hyphen and
    1695 * passed through unaltered.
    1696 *
    1697 * When there is a mapping, there are two possible styles in which these
    1698 * mappings are used. In the BY_PART style, each part (i.e. in between hyphens)
    1699 * of the passed in css name is rewritten according to the map. In the BY_WHOLE
    1700 * style, the full css name is looked up in the map directly. If a rewrite is
    1701 * not specified by the map, the compiler will output a warning.
    1702 *
    1703 * When the mapping is passed to the compiler, it will replace calls to
    1704 * goog.getCssName with the strings from the mapping, e.g.
    1705 * var x = goog.getCssName('foo');
    1706 * var y = goog.getCssName(this.baseClass, 'active');
    1707 * becomes:
    1708 * var x= 'foo';
    1709 * var y = this.baseClass + '-active';
    1710 *
    1711 * If one argument is passed it will be processed, if two are passed only the
    1712 * modifier will be processed, as it is assumed the first argument was generated
    1713 * as a result of calling goog.getCssName.
    1714 *
    1715 * @param {string} className The class name.
    1716 * @param {string=} opt_modifier A modifier to be appended to the class name.
    1717 * @return {string} The class name or the concatenation of the class name and
    1718 * the modifier.
    1719 */
    1720goog.getCssName = function(className, opt_modifier) {
    1721 var getMapping = function(cssName) {
    1722 return goog.cssNameMapping_[cssName] || cssName;
    1723 };
    1724
    1725 var renameByParts = function(cssName) {
    1726 // Remap all the parts individually.
    1727 var parts = cssName.split('-');
    1728 var mapped = [];
    1729 for (var i = 0; i < parts.length; i++) {
    1730 mapped.push(getMapping(parts[i]));
    1731 }
    1732 return mapped.join('-');
    1733 };
    1734
    1735 var rename;
    1736 if (goog.cssNameMapping_) {
    1737 rename = goog.cssNameMappingStyle_ == 'BY_WHOLE' ?
    1738 getMapping : renameByParts;
    1739 } else {
    1740 rename = function(a) {
    1741 return a;
    1742 };
    1743 }
    1744
    1745 if (opt_modifier) {
    1746 return className + '-' + rename(opt_modifier);
    1747 } else {
    1748 return rename(className);
    1749 }
    1750};
    1751
    1752
    1753/**
    1754 * Sets the map to check when returning a value from goog.getCssName(). Example:
    1755 * <pre>
    1756 * goog.setCssNameMapping({
    1757 * "goog": "a",
    1758 * "disabled": "b",
    1759 * });
    1760 *
    1761 * var x = goog.getCssName('goog');
    1762 * // The following evaluates to: "a a-b".
    1763 * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')
    1764 * </pre>
    1765 * When declared as a map of string literals to string literals, the JSCompiler
    1766 * will replace all calls to goog.getCssName() using the supplied map if the
    1767 * --closure_pass flag is set.
    1768 *
    1769 * @param {!Object} mapping A map of strings to strings where keys are possible
    1770 * arguments to goog.getCssName() and values are the corresponding values
    1771 * that should be returned.
    1772 * @param {string=} opt_style The style of css name mapping. There are two valid
    1773 * options: 'BY_PART', and 'BY_WHOLE'.
    1774 * @see goog.getCssName for a description.
    1775 */
    1776goog.setCssNameMapping = function(mapping, opt_style) {
    1777 goog.cssNameMapping_ = mapping;
    1778 goog.cssNameMappingStyle_ = opt_style;
    1779};
    1780
    1781
    1782/**
    1783 * To use CSS renaming in compiled mode, one of the input files should have a
    1784 * call to goog.setCssNameMapping() with an object literal that the JSCompiler
    1785 * can extract and use to replace all calls to goog.getCssName(). In uncompiled
    1786 * mode, JavaScript code should be loaded before this base.js file that declares
    1787 * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is
    1788 * to ensure that the mapping is loaded before any calls to goog.getCssName()
    1789 * are made in uncompiled mode.
    1790 *
    1791 * A hook for overriding the CSS name mapping.
    1792 * @type {Object|undefined}
    1793 */
    1794goog.global.CLOSURE_CSS_NAME_MAPPING;
    1795
    1796
    1797if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) {
    1798 // This does not call goog.setCssNameMapping() because the JSCompiler
    1799 // requires that goog.setCssNameMapping() be called with an object literal.
    1800 goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING;
    1801}
    1802
    1803
    1804/**
    1805 * Gets a localized message.
    1806 *
    1807 * This function is a compiler primitive. If you give the compiler a localized
    1808 * message bundle, it will replace the string at compile-time with a localized
    1809 * version, and expand goog.getMsg call to a concatenated string.
    1810 *
    1811 * Messages must be initialized in the form:
    1812 * <code>
    1813 * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'});
    1814 * </code>
    1815 *
    1816 * @param {string} str Translatable string, places holders in the form {$foo}.
    1817 * @param {Object=} opt_values Map of place holder name to value.
    1818 * @return {string} message with placeholders filled.
    1819 */
    1820goog.getMsg = function(str, opt_values) {
    1821 if (opt_values) {
    1822 str = str.replace(/\{\$([^}]+)}/g, function(match, key) {
    1823 return key in opt_values ? opt_values[key] : match;
    1824 });
    1825 }
    1826 return str;
    1827};
    1828
    1829
    1830/**
    1831 * Gets a localized message. If the message does not have a translation, gives a
    1832 * fallback message.
    1833 *
    1834 * This is useful when introducing a new message that has not yet been
    1835 * translated into all languages.
    1836 *
    1837 * This function is a compiler primitive. Must be used in the form:
    1838 * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code>
    1839 * where MSG_A and MSG_B were initialized with goog.getMsg.
    1840 *
    1841 * @param {string} a The preferred message.
    1842 * @param {string} b The fallback message.
    1843 * @return {string} The best translated message.
    1844 */
    1845goog.getMsgWithFallback = function(a, b) {
    1846 return a;
    1847};
    1848
    1849
    1850/**
    1851 * Exposes an unobfuscated global namespace path for the given object.
    1852 * Note that fields of the exported object *will* be obfuscated, unless they are
    1853 * exported in turn via this function or goog.exportProperty.
    1854 *
    1855 * Also handy for making public items that are defined in anonymous closures.
    1856 *
    1857 * ex. goog.exportSymbol('public.path.Foo', Foo);
    1858 *
    1859 * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction);
    1860 * public.path.Foo.staticFunction();
    1861 *
    1862 * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod',
    1863 * Foo.prototype.myMethod);
    1864 * new public.path.Foo().myMethod();
    1865 *
    1866 * @param {string} publicPath Unobfuscated name to export.
    1867 * @param {*} object Object the name should point to.
    1868 * @param {Object=} opt_objectToExportTo The object to add the path to; default
    1869 * is goog.global.
    1870 */
    1871goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) {
    1872 goog.exportPath_(publicPath, object, opt_objectToExportTo);
    1873};
    1874
    1875
    1876/**
    1877 * Exports a property unobfuscated into the object's namespace.
    1878 * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);
    1879 * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);
    1880 * @param {Object} object Object whose static property is being exported.
    1881 * @param {string} publicName Unobfuscated name to export.
    1882 * @param {*} symbol Object the name should point to.
    1883 */
    1884goog.exportProperty = function(object, publicName, symbol) {
    1885 object[publicName] = symbol;
    1886};
    1887
    1888
    1889/**
    1890 * Inherit the prototype methods from one constructor into another.
    1891 *
    1892 * Usage:
    1893 * <pre>
    1894 * function ParentClass(a, b) { }
    1895 * ParentClass.prototype.foo = function(a) { };
    1896 *
    1897 * function ChildClass(a, b, c) {
    1898 * ChildClass.base(this, 'constructor', a, b);
    1899 * }
    1900 * goog.inherits(ChildClass, ParentClass);
    1901 *
    1902 * var child = new ChildClass('a', 'b', 'see');
    1903 * child.foo(); // This works.
    1904 * </pre>
    1905 *
    1906 * @param {Function} childCtor Child class.
    1907 * @param {Function} parentCtor Parent class.
    1908 */
    1909goog.inherits = function(childCtor, parentCtor) {
    1910 /** @constructor */
    1911 function tempCtor() {};
    1912 tempCtor.prototype = parentCtor.prototype;
    1913 childCtor.superClass_ = parentCtor.prototype;
    1914 childCtor.prototype = new tempCtor();
    1915 /** @override */
    1916 childCtor.prototype.constructor = childCtor;
    1917
    1918 /**
    1919 * Calls superclass constructor/method.
    1920 *
    1921 * This function is only available if you use goog.inherits to
    1922 * express inheritance relationships between classes.
    1923 *
    1924 * NOTE: This is a replacement for goog.base and for superClass_
    1925 * property defined in childCtor.
    1926 *
    1927 * @param {!Object} me Should always be "this".
    1928 * @param {string} methodName The method name to call. Calling
    1929 * superclass constructor can be done with the special string
    1930 * 'constructor'.
    1931 * @param {...*} var_args The arguments to pass to superclass
    1932 * method/constructor.
    1933 * @return {*} The return value of the superclass method/constructor.
    1934 */
    1935 childCtor.base = function(me, methodName, var_args) {
    1936 var args = Array.prototype.slice.call(arguments, 2);
    1937 return parentCtor.prototype[methodName].apply(me, args);
    1938 };
    1939};
    1940
    1941
    1942/**
    1943 * Call up to the superclass.
    1944 *
    1945 * If this is called from a constructor, then this calls the superclass
    1946 * constructor with arguments 1-N.
    1947 *
    1948 * If this is called from a prototype method, then you must pass the name of the
    1949 * method as the second argument to this function. If you do not, you will get a
    1950 * runtime error. This calls the superclass' method with arguments 2-N.
    1951 *
    1952 * This function only works if you use goog.inherits to express inheritance
    1953 * relationships between your classes.
    1954 *
    1955 * This function is a compiler primitive. At compile-time, the compiler will do
    1956 * macro expansion to remove a lot of the extra overhead that this function
    1957 * introduces. The compiler will also enforce a lot of the assumptions that this
    1958 * function makes, and treat it as a compiler error if you break them.
    1959 *
    1960 * @param {!Object} me Should always be "this".
    1961 * @param {*=} opt_methodName The method name if calling a super method.
    1962 * @param {...*} var_args The rest of the arguments.
    1963 * @return {*} The return value of the superclass method.
    1964 * @suppress {es5Strict} This method can not be used in strict mode, but
    1965 * all Closure Library consumers must depend on this file.
    1966 */
    1967goog.base = function(me, opt_methodName, var_args) {
    1968 var caller = arguments.callee.caller;
    1969
    1970 if (goog.STRICT_MODE_COMPATIBLE || (goog.DEBUG && !caller)) {
    1971 throw Error('arguments.caller not defined. goog.base() cannot be used ' +
    1972 'with strict mode code. See ' +
    1973 'http://www.ecma-international.org/ecma-262/5.1/#sec-C');
    1974 }
    1975
    1976 if (caller.superClass_) {
    1977 // This is a constructor. Call the superclass constructor.
    1978 return caller.superClass_.constructor.apply(
    1979 me, Array.prototype.slice.call(arguments, 1));
    1980 }
    1981
    1982 var args = Array.prototype.slice.call(arguments, 2);
    1983 var foundCaller = false;
    1984 for (var ctor = me.constructor;
    1985 ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) {
    1986 if (ctor.prototype[opt_methodName] === caller) {
    1987 foundCaller = true;
    1988 } else if (foundCaller) {
    1989 return ctor.prototype[opt_methodName].apply(me, args);
    1990 }
    1991 }
    1992
    1993 // If we did not find the caller in the prototype chain, then one of two
    1994 // things happened:
    1995 // 1) The caller is an instance method.
    1996 // 2) This method was not called by the right caller.
    1997 if (me[opt_methodName] === caller) {
    1998 return me.constructor.prototype[opt_methodName].apply(me, args);
    1999 } else {
    2000 throw Error(
    2001 'goog.base called from a method of one name ' +
    2002 'to a method of a different name');
    2003 }
    2004};
    2005
    2006
    2007/**
    2008 * Allow for aliasing within scope functions. This function exists for
    2009 * uncompiled code - in compiled code the calls will be inlined and the aliases
    2010 * applied. In uncompiled code the function is simply run since the aliases as
    2011 * written are valid JavaScript.
    2012 *
    2013 *
    2014 * @param {function()} fn Function to call. This function can contain aliases
    2015 * to namespaces (e.g. "var dom = goog.dom") or classes
    2016 * (e.g. "var Timer = goog.Timer").
    2017 */
    2018goog.scope = function(fn) {
    2019 fn.call(goog.global);
    2020};
    2021
    2022
    2023/*
    2024 * To support uncompiled, strict mode bundles that use eval to divide source
    2025 * like so:
    2026 * eval('someSource;//# sourceUrl sourcefile.js');
    2027 * We need to export the globally defined symbols "goog" and "COMPILED".
    2028 * Exporting "goog" breaks the compiler optimizations, so we required that
    2029 * be defined externally.
    2030 * NOTE: We don't use goog.exportSymbol here because we don't want to trigger
    2031 * extern generation when that compiler option is enabled.
    2032 */
    2033if (!COMPILED) {
    2034 goog.global['COMPILED'] = COMPILED;
    2035}
    2036
    2037
    2038
    2039//==============================================================================
    2040// goog.defineClass implementation
    2041//==============================================================================
    2042
    2043/**
    2044 * Creates a restricted form of a Closure "class":
    2045 * - from the compiler's perspective, the instance returned from the
    2046 * constructor is sealed (no new properties may be added). This enables
    2047 * better checks.
    2048 * - the compiler will rewrite this definition to a form that is optimal
    2049 * for type checking and optimization (initially this will be a more
    2050 * traditional form).
    2051 *
    2052 * @param {Function} superClass The superclass, Object or null.
    2053 * @param {goog.defineClass.ClassDescriptor} def
    2054 * An object literal describing the
    2055 * the class. It may have the following properties:
    2056 * "constructor": the constructor function
    2057 * "statics": an object literal containing methods to add to the constructor
    2058 * as "static" methods or a function that will receive the constructor
    2059 * function as its only parameter to which static properties can
    2060 * be added.
    2061 * all other properties are added to the prototype.
    2062 * @return {!Function} The class constructor.
    2063 */
    2064goog.defineClass = function(superClass, def) {
    2065 // TODO(johnlenz): consider making the superClass an optional parameter.
    2066 var constructor = def.constructor;
    2067 var statics = def.statics;
    2068 // Wrap the constructor prior to setting up the prototype and static methods.
    2069 if (!constructor || constructor == Object.prototype.constructor) {
    2070 constructor = function() {
    2071 throw Error('cannot instantiate an interface (no constructor defined).');
    2072 };
    2073 }
    2074
    2075 var cls = goog.defineClass.createSealingConstructor_(constructor, superClass);
    2076 if (superClass) {
    2077 goog.inherits(cls, superClass);
    2078 }
    2079
    2080 // Remove all the properties that should not be copied to the prototype.
    2081 delete def.constructor;
    2082 delete def.statics;
    2083
    2084 goog.defineClass.applyProperties_(cls.prototype, def);
    2085 if (statics != null) {
    2086 if (statics instanceof Function) {
    2087 statics(cls);
    2088 } else {
    2089 goog.defineClass.applyProperties_(cls, statics);
    2090 }
    2091 }
    2092
    2093 return cls;
    2094};
    2095
    2096
    2097/**
    2098 * @typedef {
    2099 * !Object|
    2100 * {constructor:!Function}|
    2101 * {constructor:!Function, statics:(Object|function(Function):void)}}
    2102 */
    2103goog.defineClass.ClassDescriptor;
    2104
    2105
    2106/**
    2107 * @define {boolean} Whether the instances returned by
    2108 * goog.defineClass should be sealed when possible.
    2109 */
    2110goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG);
    2111
    2112
    2113/**
    2114 * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is
    2115 * defined, this function will wrap the constructor in a function that seals the
    2116 * results of the provided constructor function.
    2117 *
    2118 * @param {!Function} ctr The constructor whose results maybe be sealed.
    2119 * @param {Function} superClass The superclass constructor.
    2120 * @return {!Function} The replacement constructor.
    2121 * @private
    2122 */
    2123goog.defineClass.createSealingConstructor_ = function(ctr, superClass) {
    2124 if (goog.defineClass.SEAL_CLASS_INSTANCES &&
    2125 Object.seal instanceof Function) {
    2126 // Don't seal subclasses of unsealable-tagged legacy classes.
    2127 if (superClass && superClass.prototype &&
    2128 superClass.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_]) {
    2129 return ctr;
    2130 }
    2131 /** @this {*} */
    2132 var wrappedCtr = function() {
    2133 // Don't seal an instance of a subclass when it calls the constructor of
    2134 // its super class as there is most likely still setup to do.
    2135 var instance = ctr.apply(this, arguments) || this;
    2136 instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_];
    2137 if (this.constructor === wrappedCtr) {
    2138 Object.seal(instance);
    2139 }
    2140 return instance;
    2141 };
    2142 return wrappedCtr;
    2143 }
    2144 return ctr;
    2145};
    2146
    2147
    2148// TODO(johnlenz): share these values with the goog.object
    2149/**
    2150 * The names of the fields that are defined on Object.prototype.
    2151 * @type {!Array.<string>}
    2152 * @private
    2153 * @const
    2154 */
    2155goog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [
    2156 'constructor',
    2157 'hasOwnProperty',
    2158 'isPrototypeOf',
    2159 'propertyIsEnumerable',
    2160 'toLocaleString',
    2161 'toString',
    2162 'valueOf'
    2163];
    2164
    2165
    2166// TODO(johnlenz): share this function with the goog.object
    2167/**
    2168 * @param {!Object} target The object to add properties to.
    2169 * @param {!Object} source The object to copy properites from.
    2170 * @private
    2171 */
    2172goog.defineClass.applyProperties_ = function(target, source) {
    2173 // TODO(johnlenz): update this to support ES5 getters/setters
    2174
    2175 var key;
    2176 for (key in source) {
    2177 if (Object.prototype.hasOwnProperty.call(source, key)) {
    2178 target[key] = source[key];
    2179 }
    2180 }
    2181
    2182 // For IE the for-in-loop does not contain any properties that are not
    2183 // enumerable on the prototype object (for example isPrototypeOf from
    2184 // Object.prototype) and it will also not include 'replace' on objects that
    2185 // extend String and change 'replace' (not that it is common for anyone to
    2186 // extend anything except Object).
    2187 for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) {
    2188 key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i];
    2189 if (Object.prototype.hasOwnProperty.call(source, key)) {
    2190 target[key] = source[key];
    2191 }
    2192 }
    2193};
    2194
    2195
    2196/**
    2197 * Sealing classes breaks the older idiom of assigning properties on the
    2198 * prototype rather than in the constructor. As such, goog.defineClass
    2199 * must not seal subclasses of these old-style classes until they are fixed.
    2200 * Until then, this marks a class as "broken", instructing defineClass
    2201 * not to seal subclasses.
    2202 * @param {!Function} ctr The legacy constructor to tag as unsealable.
    2203 */
    2204goog.tagUnsealableClass = function(ctr) {
    2205 if (!COMPILED && goog.defineClass.SEAL_CLASS_INSTANCES) {
    2206 ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_] = true;
    2207 }
    2208};
    2209
    2210
    2211/**
    2212 * Name for unsealable tag property.
    2213 * @const @private {string}
    2214 */
    2215goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_ = 'goog_defineClass_legacy_unsealable';
    \ 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 --closure_pass
    30 * or --mark_as_compiled 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 {?{
    407 * moduleName: (string|undefined),
    408 * declareTestMethods: boolean
    409 * }}
    410 */
    411goog.moduleLoaderState_ = null;
    412
    413
    414/**
    415 * @private
    416 * @return {boolean} Whether a goog.module is currently being initialized.
    417 */
    418goog.isInModuleLoader_ = function() {
    419 return goog.moduleLoaderState_ != null;
    420};
    421
    422
    423/**
    424 * Indicate that a module's exports that are known test methods should
    425 * be copied to the global object. This makes the test methods visible to
    426 * test runners that inspect the global object.
    427 *
    428 * TODO(johnlenz): Make the test framework aware of goog.module so
    429 * that this isn't necessary. Alternately combine this with goog.setTestOnly
    430 * to minimize boiler plate.
    431 * @suppress {missingProvide}
    432 * @deprecated This approach does not translate to ES6 module syntax, instead
    433 * use goog.testing.testSuite to declare the test methods.
    434 */
    435goog.module.declareTestMethods = function() {
    436 if (!goog.isInModuleLoader_()) {
    437 throw new Error('goog.module.declareTestMethods must be called from ' +
    438 'within a goog.module');
    439 }
    440 goog.moduleLoaderState_.declareTestMethods = true;
    441};
    442
    443
    444/**
    445 * Provide the module's exports as a globally accessible object under the
    446 * module's declared name. This is intended to ease migration to goog.module
    447 * for files that have existing usages.
    448 * @suppress {missingProvide}
    449 */
    450goog.module.declareLegacyNamespace = function() {
    451 if (!COMPILED && !goog.isInModuleLoader_()) {
    452 throw new Error('goog.module.declareLegacyNamespace must be called from ' +
    453 'within a goog.module');
    454 }
    455 if (!COMPILED && !goog.moduleLoaderState_.moduleName) {
    456 throw Error('goog.module must be called prior to ' +
    457 'goog.module.declareLegacyNamespace.');
    458 }
    459 goog.moduleLoaderState_.declareLegacyNamespace = true;
    460};
    461
    462
    463/**
    464 * Marks that the current file should only be used for testing, and never for
    465 * live code in production.
    466 *
    467 * In the case of unit tests, the message may optionally be an exact namespace
    468 * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra
    469 * provide (if not explicitly defined in the code).
    470 *
    471 * @param {string=} opt_message Optional message to add to the error that's
    472 * raised when used in production code.
    473 */
    474goog.setTestOnly = function(opt_message) {
    475 if (goog.DISALLOW_TEST_ONLY_CODE) {
    476 opt_message = opt_message || '';
    477 throw Error('Importing test-only code into non-debug environment' +
    478 (opt_message ? ': ' + opt_message : '.'));
    479 }
    480};
    481
    482
    483/**
    484 * Forward declares a symbol. This is an indication to the compiler that the
    485 * symbol may be used in the source yet is not required and may not be provided
    486 * in compilation.
    487 *
    488 * The most common usage of forward declaration is code that takes a type as a
    489 * function parameter but does not need to require it. By forward declaring
    490 * instead of requiring, no hard dependency is made, and (if not required
    491 * elsewhere) the namespace may never be required and thus, not be pulled
    492 * into the JavaScript binary. If it is required elsewhere, it will be type
    493 * checked as normal.
    494 *
    495 *
    496 * @param {string} name The namespace to forward declare in the form of
    497 * "goog.package.part".
    498 */
    499goog.forwardDeclare = function(name) {};
    500
    501
    502if (!COMPILED) {
    503
    504 /**
    505 * Check if the given name has been goog.provided. This will return false for
    506 * names that are available only as implicit namespaces.
    507 * @param {string} name name of the object to look for.
    508 * @return {boolean} Whether the name has been provided.
    509 * @private
    510 */
    511 goog.isProvided_ = function(name) {
    512 return (name in goog.loadedModules_) ||
    513 (!goog.implicitNamespaces_[name] &&
    514 goog.isDefAndNotNull(goog.getObjectByName(name)));
    515 };
    516
    517 /**
    518 * Namespaces implicitly defined by goog.provide. For example,
    519 * goog.provide('goog.events.Event') implicitly declares that 'goog' and
    520 * 'goog.events' must be namespaces.
    521 *
    522 * @type {!Object<string, (boolean|undefined)>}
    523 * @private
    524 */
    525 goog.implicitNamespaces_ = {'goog.module': true};
    526
    527 // NOTE: We add goog.module as an implicit namespace as goog.module is defined
    528 // here and because the existing module package has not been moved yet out of
    529 // the goog.module namespace. This satisifies both the debug loader and
    530 // ahead-of-time dependency management.
    531}
    532
    533
    534/**
    535 * Returns an object based on its fully qualified external name. The object
    536 * is not found if null or undefined. If you are using a compilation pass that
    537 * renames property names beware that using this function will not find renamed
    538 * properties.
    539 *
    540 * @param {string} name The fully qualified name.
    541 * @param {Object=} opt_obj The object within which to look; default is
    542 * |goog.global|.
    543 * @return {?} The value (object or primitive) or, if not found, null.
    544 */
    545goog.getObjectByName = function(name, opt_obj) {
    546 var parts = name.split('.');
    547 var cur = opt_obj || goog.global;
    548 for (var part; part = parts.shift(); ) {
    549 if (goog.isDefAndNotNull(cur[part])) {
    550 cur = cur[part];
    551 } else {
    552 return null;
    553 }
    554 }
    555 return cur;
    556};
    557
    558
    559/**
    560 * Globalizes a whole namespace, such as goog or goog.lang.
    561 *
    562 * @param {!Object} obj The namespace to globalize.
    563 * @param {Object=} opt_global The object to add the properties to.
    564 * @deprecated Properties may be explicitly exported to the global scope, but
    565 * this should no longer be done in bulk.
    566 */
    567goog.globalize = function(obj, opt_global) {
    568 var global = opt_global || goog.global;
    569 for (var x in obj) {
    570 global[x] = obj[x];
    571 }
    572};
    573
    574
    575/**
    576 * Adds a dependency from a file to the files it requires.
    577 * @param {string} relPath The path to the js file.
    578 * @param {!Array<string>} provides An array of strings with
    579 * the names of the objects this file provides.
    580 * @param {!Array<string>} requires An array of strings with
    581 * the names of the objects this file requires.
    582 * @param {boolean=} opt_isModule Whether this dependency must be loaded as
    583 * a module as declared by goog.module.
    584 */
    585goog.addDependency = function(relPath, provides, requires, opt_isModule) {
    586 if (goog.DEPENDENCIES_ENABLED) {
    587 var provide, require;
    588 var path = relPath.replace(/\\/g, '/');
    589 var deps = goog.dependencies_;
    590 for (var i = 0; provide = provides[i]; i++) {
    591 deps.nameToPath[provide] = path;
    592 deps.pathIsModule[path] = !!opt_isModule;
    593 }
    594 for (var j = 0; require = requires[j]; j++) {
    595 if (!(path in deps.requires)) {
    596 deps.requires[path] = {};
    597 }
    598 deps.requires[path][require] = true;
    599 }
    600 }
    601};
    602
    603
    604
    605
    606// NOTE(nnaze): The debug DOM loader was included in base.js as an original way
    607// to do "debug-mode" development. The dependency system can sometimes be
    608// confusing, as can the debug DOM loader's asynchronous nature.
    609//
    610// With the DOM loader, a call to goog.require() is not blocking -- the script
    611// will not load until some point after the current script. If a namespace is
    612// needed at runtime, it needs to be defined in a previous script, or loaded via
    613// require() with its registered dependencies.
    614// User-defined namespaces may need their own deps file. See http://go/js_deps,
    615// http://go/genjsdeps, or, externally, DepsWriter.
    616// https://developers.google.com/closure/library/docs/depswriter
    617//
    618// Because of legacy clients, the DOM loader can't be easily removed from
    619// base.js. Work is being done to make it disableable or replaceable for
    620// different environments (DOM-less JavaScript interpreters like Rhino or V8,
    621// for example). See bootstrap/ for more information.
    622
    623
    624/**
    625 * @define {boolean} Whether to enable the debug loader.
    626 *
    627 * If enabled, a call to goog.require() will attempt to load the namespace by
    628 * appending a script tag to the DOM (if the namespace has been registered).
    629 *
    630 * If disabled, goog.require() will simply assert that the namespace has been
    631 * provided (and depend on the fact that some outside tool correctly ordered
    632 * the script).
    633 */
    634goog.define('goog.ENABLE_DEBUG_LOADER', true);
    635
    636
    637/**
    638 * @param {string} msg
    639 * @private
    640 */
    641goog.logToConsole_ = function(msg) {
    642 if (goog.global.console) {
    643 goog.global.console['error'](msg);
    644 }
    645};
    646
    647
    648/**
    649 * Implements a system for the dynamic resolution of dependencies that works in
    650 * parallel with the BUILD system. Note that all calls to goog.require will be
    651 * stripped by the JSCompiler when the --closure_pass option is used.
    652 * @see goog.provide
    653 * @param {string} name Namespace to include (as was given in goog.provide()) in
    654 * the form "goog.package.part".
    655 * @return {?} If called within a goog.module file, the associated namespace or
    656 * module otherwise null.
    657 */
    658goog.require = function(name) {
    659
    660 // If the object already exists we do not need do do anything.
    661 if (!COMPILED) {
    662 if (goog.ENABLE_DEBUG_LOADER && goog.IS_OLD_IE_) {
    663 goog.maybeProcessDeferredDep_(name);
    664 }
    665
    666 if (goog.isProvided_(name)) {
    667 if (goog.isInModuleLoader_()) {
    668 return goog.module.getInternal_(name);
    669 } else {
    670 return null;
    671 }
    672 }
    673
    674 if (goog.ENABLE_DEBUG_LOADER) {
    675 var path = goog.getPathFromDeps_(name);
    676 if (path) {
    677 goog.included_[path] = true;
    678 goog.writeScripts_();
    679 return null;
    680 }
    681 }
    682
    683 var errorMessage = 'goog.require could not find: ' + name;
    684 goog.logToConsole_(errorMessage);
    685
    686 throw Error(errorMessage);
    687 }
    688};
    689
    690
    691/**
    692 * Path for included scripts.
    693 * @type {string}
    694 */
    695goog.basePath = '';
    696
    697
    698/**
    699 * A hook for overriding the base path.
    700 * @type {string|undefined}
    701 */
    702goog.global.CLOSURE_BASE_PATH;
    703
    704
    705/**
    706 * Whether to write out Closure's deps file. By default, the deps are written.
    707 * @type {boolean|undefined}
    708 */
    709goog.global.CLOSURE_NO_DEPS;
    710
    711
    712/**
    713 * A function to import a single script. This is meant to be overridden when
    714 * Closure is being run in non-HTML contexts, such as web workers. It's defined
    715 * in the global scope so that it can be set before base.js is loaded, which
    716 * allows deps.js to be imported properly.
    717 *
    718 * The function is passed the script source, which is a relative URI. It should
    719 * return true if the script was imported, false otherwise.
    720 * @type {(function(string): boolean)|undefined}
    721 */
    722goog.global.CLOSURE_IMPORT_SCRIPT;
    723
    724
    725/**
    726 * Null function used for default values of callbacks, etc.
    727 * @return {void} Nothing.
    728 */
    729goog.nullFunction = function() {};
    730
    731
    732
    733/**
    734 * When defining a class Foo with an abstract method bar(), you can do:
    735 * Foo.prototype.bar = goog.abstractMethod
    736 *
    737 * Now if a subclass of Foo fails to override bar(), an error will be thrown
    738 * when bar() is invoked.
    739 *
    740 * Note: This does not take the name of the function to override as an argument
    741 * because that would make it more difficult to obfuscate our JavaScript code.
    742 *
    743 * @type {!Function}
    744 * @throws {Error} when invoked to indicate the method should be overridden.
    745 */
    746goog.abstractMethod = function() {
    747 throw Error('unimplemented abstract method');
    748};
    749
    750
    751/**
    752 * Adds a {@code getInstance} static method that always returns the same
    753 * instance object.
    754 * @param {!Function} ctor The constructor for the class to add the static
    755 * method to.
    756 */
    757goog.addSingletonGetter = function(ctor) {
    758 ctor.getInstance = function() {
    759 if (ctor.instance_) {
    760 return ctor.instance_;
    761 }
    762 if (goog.DEBUG) {
    763 // NOTE: JSCompiler can't optimize away Array#push.
    764 goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor;
    765 }
    766 return ctor.instance_ = new ctor;
    767 };
    768};
    769
    770
    771/**
    772 * All singleton classes that have been instantiated, for testing. Don't read
    773 * it directly, use the {@code goog.testing.singleton} module. The compiler
    774 * removes this variable if unused.
    775 * @type {!Array<!Function>}
    776 * @private
    777 */
    778goog.instantiatedSingletons_ = [];
    779
    780
    781/**
    782 * @define {boolean} Whether to load goog.modules using {@code eval} when using
    783 * the debug loader. This provides a better debugging experience as the
    784 * source is unmodified and can be edited using Chrome Workspaces or similar.
    785 * However in some environments the use of {@code eval} is banned
    786 * so we provide an alternative.
    787 */
    788goog.define('goog.LOAD_MODULE_USING_EVAL', true);
    789
    790
    791/**
    792 * @define {boolean} Whether the exports of goog.modules should be sealed when
    793 * possible.
    794 */
    795goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG);
    796
    797
    798/**
    799 * The registry of initialized modules:
    800 * the module identifier to module exports map.
    801 * @private @const {!Object<string, ?>}
    802 */
    803goog.loadedModules_ = {};
    804
    805
    806/**
    807 * True if goog.dependencies_ is available.
    808 * @const {boolean}
    809 */
    810goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER;
    811
    812
    813if (goog.DEPENDENCIES_ENABLED) {
    814 /**
    815 * Object used to keep track of urls that have already been added. This record
    816 * allows the prevention of circular dependencies.
    817 * @private {!Object<string, boolean>}
    818 */
    819 goog.included_ = {};
    820
    821
    822 /**
    823 * This object is used to keep track of dependencies and other data that is
    824 * used for loading scripts.
    825 * @private
    826 * @type {{
    827 * pathIsModule: !Object<string, boolean>,
    828 * nameToPath: !Object<string, string>,
    829 * requires: !Object<string, !Object<string, boolean>>,
    830 * visited: !Object<string, boolean>,
    831 * written: !Object<string, boolean>,
    832 * deferred: !Object<string, string>
    833 * }}
    834 */
    835 goog.dependencies_ = {
    836 pathIsModule: {}, // 1 to 1
    837
    838 nameToPath: {}, // 1 to 1
    839
    840 requires: {}, // 1 to many
    841
    842 // Used when resolving dependencies to prevent us from visiting file twice.
    843 visited: {},
    844
    845 written: {}, // Used to keep track of script files we have written.
    846
    847 deferred: {} // Used to track deferred module evaluations in old IEs
    848 };
    849
    850
    851 /**
    852 * Tries to detect whether is in the context of an HTML document.
    853 * @return {boolean} True if it looks like HTML document.
    854 * @private
    855 */
    856 goog.inHtmlDocument_ = function() {
    857 var doc = goog.global.document;
    858 return typeof doc != 'undefined' &&
    859 'write' in doc; // XULDocument misses write.
    860 };
    861
    862
    863 /**
    864 * Tries to detect the base path of base.js script that bootstraps Closure.
    865 * @private
    866 */
    867 goog.findBasePath_ = function() {
    868 if (goog.global.CLOSURE_BASE_PATH) {
    869 goog.basePath = goog.global.CLOSURE_BASE_PATH;
    870 return;
    871 } else if (!goog.inHtmlDocument_()) {
    872 return;
    873 }
    874 var doc = goog.global.document;
    875 var scripts = doc.getElementsByTagName('SCRIPT');
    876 // Search backwards since the current script is in almost all cases the one
    877 // that has base.js.
    878 for (var i = scripts.length - 1; i >= 0; --i) {
    879 var script = /** @type {!HTMLScriptElement} */ (scripts[i]);
    880 var src = script.src;
    881 var qmark = src.lastIndexOf('?');
    882 var l = qmark == -1 ? src.length : qmark;
    883 if (src.substr(l - 7, 7) == 'base.js') {
    884 goog.basePath = src.substr(0, l - 7);
    885 return;
    886 }
    887 }
    888 };
    889
    890
    891 /**
    892 * Imports a script if, and only if, that script hasn't already been imported.
    893 * (Must be called at execution time)
    894 * @param {string} src Script source.
    895 * @param {string=} opt_sourceText The optionally source text to evaluate
    896 * @private
    897 */
    898 goog.importScript_ = function(src, opt_sourceText) {
    899 var importScript = goog.global.CLOSURE_IMPORT_SCRIPT ||
    900 goog.writeScriptTag_;
    901 if (importScript(src, opt_sourceText)) {
    902 goog.dependencies_.written[src] = true;
    903 }
    904 };
    905
    906
    907 /** @const @private {boolean} */
    908 goog.IS_OLD_IE_ = !goog.global.atob && goog.global.document &&
    909 goog.global.document.all;
    910
    911
    912 /**
    913 * Given a URL initiate retrieval and execution of the module.
    914 * @param {string} src Script source URL.
    915 * @private
    916 */
    917 goog.importModule_ = function(src) {
    918 // In an attempt to keep browsers from timing out loading scripts using
    919 // synchronous XHRs, put each load in its own script block.
    920 var bootstrap = 'goog.retrieveAndExecModule_("' + src + '");';
    921
    922 if (goog.importScript_('', bootstrap)) {
    923 goog.dependencies_.written[src] = true;
    924 }
    925 };
    926
    927
    928 /** @private {!Array<string>} */
    929 goog.queuedModules_ = [];
    930
    931
    932 /**
    933 * Return an appropriate module text. Suitable to insert into
    934 * a script tag (that is unescaped).
    935 * @param {string} srcUrl
    936 * @param {string} scriptText
    937 * @return {string}
    938 * @private
    939 */
    940 goog.wrapModule_ = function(srcUrl, scriptText) {
    941 if (!goog.LOAD_MODULE_USING_EVAL || !goog.isDef(goog.global.JSON)) {
    942 return '' +
    943 'goog.loadModule(function(exports) {' +
    944 '"use strict";' +
    945 scriptText +
    946 '\n' + // terminate any trailing single line comment.
    947 ';return exports' +
    948 '});' +
    949 '\n//# sourceURL=' + srcUrl + '\n';
    950 } else {
    951 return '' +
    952 'goog.loadModule(' +
    953 goog.global.JSON.stringify(
    954 scriptText + '\n//# sourceURL=' + srcUrl + '\n') +
    955 ');';
    956 }
    957 };
    958
    959 // On IE9 and earlier, it is necessary to handle
    960 // deferred module loads. In later browsers, the
    961 // code to be evaluated is simply inserted as a script
    962 // block in the correct order. To eval deferred
    963 // code at the right time, we piggy back on goog.require to call
    964 // goog.maybeProcessDeferredDep_.
    965 //
    966 // The goog.requires are used both to bootstrap
    967 // the loading process (when no deps are available) and
    968 // declare that they should be available.
    969 //
    970 // Here we eval the sources, if all the deps are available
    971 // either already eval'd or goog.require'd. This will
    972 // be the case when all the dependencies have already
    973 // been loaded, and the dependent module is loaded.
    974 //
    975 // But this alone isn't sufficient because it is also
    976 // necessary to handle the case where there is no root
    977 // that is not deferred. For that there we register for an event
    978 // and trigger goog.loadQueuedModules_ handle any remaining deferred
    979 // evaluations.
    980
    981 /**
    982 * Handle any remaining deferred goog.module evals.
    983 * @private
    984 */
    985 goog.loadQueuedModules_ = function() {
    986 var count = goog.queuedModules_.length;
    987 if (count > 0) {
    988 var queue = goog.queuedModules_;
    989 goog.queuedModules_ = [];
    990 for (var i = 0; i < count; i++) {
    991 var path = queue[i];
    992 goog.maybeProcessDeferredPath_(path);
    993 }
    994 }
    995 };
    996
    997
    998 /**
    999 * Eval the named module if its dependencies are
    1000 * available.
    1001 * @param {string} name The module to load.
    1002 * @private
    1003 */
    1004 goog.maybeProcessDeferredDep_ = function(name) {
    1005 if (goog.isDeferredModule_(name) &&
    1006 goog.allDepsAreAvailable_(name)) {
    1007 var path = goog.getPathFromDeps_(name);
    1008 goog.maybeProcessDeferredPath_(goog.basePath + path);
    1009 }
    1010 };
    1011
    1012 /**
    1013 * @param {string} name The module to check.
    1014 * @return {boolean} Whether the name represents a
    1015 * module whose evaluation has been deferred.
    1016 * @private
    1017 */
    1018 goog.isDeferredModule_ = function(name) {
    1019 var path = goog.getPathFromDeps_(name);
    1020 if (path && goog.dependencies_.pathIsModule[path]) {
    1021 var abspath = goog.basePath + path;
    1022 return (abspath) in goog.dependencies_.deferred;
    1023 }
    1024 return false;
    1025 };
    1026
    1027 /**
    1028 * @param {string} name The module to check.
    1029 * @return {boolean} Whether the name represents a
    1030 * module whose declared dependencies have all been loaded
    1031 * (eval'd or a deferred module load)
    1032 * @private
    1033 */
    1034 goog.allDepsAreAvailable_ = function(name) {
    1035 var path = goog.getPathFromDeps_(name);
    1036 if (path && (path in goog.dependencies_.requires)) {
    1037 for (var requireName in goog.dependencies_.requires[path]) {
    1038 if (!goog.isProvided_(requireName) &&
    1039 !goog.isDeferredModule_(requireName)) {
    1040 return false;
    1041 }
    1042 }
    1043 }
    1044 return true;
    1045 };
    1046
    1047
    1048 /**
    1049 * @param {string} abspath
    1050 * @private
    1051 */
    1052 goog.maybeProcessDeferredPath_ = function(abspath) {
    1053 if (abspath in goog.dependencies_.deferred) {
    1054 var src = goog.dependencies_.deferred[abspath];
    1055 delete goog.dependencies_.deferred[abspath];
    1056 goog.globalEval(src);
    1057 }
    1058 };
    1059
    1060
    1061 /**
    1062 * @param {function(?):?|string} moduleDef The module definition.
    1063 */
    1064 goog.loadModule = function(moduleDef) {
    1065 // NOTE: we allow function definitions to be either in the from
    1066 // of a string to eval (which keeps the original source intact) or
    1067 // in a eval forbidden environment (CSP) we allow a function definition
    1068 // which in its body must call {@code goog.module}, and return the exports
    1069 // of the module.
    1070 var previousState = goog.moduleLoaderState_;
    1071 try {
    1072 goog.moduleLoaderState_ = {
    1073 moduleName: undefined, declareTestMethods: false};
    1074 var exports;
    1075 if (goog.isFunction(moduleDef)) {
    1076 exports = moduleDef.call(goog.global, {});
    1077 } else if (goog.isString(moduleDef)) {
    1078 exports = goog.loadModuleFromSource_.call(goog.global, moduleDef);
    1079 } else {
    1080 throw Error('Invalid module definition');
    1081 }
    1082
    1083 var moduleName = goog.moduleLoaderState_.moduleName;
    1084 if (!goog.isString(moduleName) || !moduleName) {
    1085 throw Error('Invalid module name \"' + moduleName + '\"');
    1086 }
    1087
    1088 // Don't seal legacy namespaces as they may be uses as a parent of
    1089 // another namespace
    1090 if (goog.moduleLoaderState_.declareLegacyNamespace) {
    1091 goog.constructNamespace_(moduleName, exports);
    1092 } else if (goog.SEAL_MODULE_EXPORTS && Object.seal) {
    1093 Object.seal(exports);
    1094 }
    1095
    1096 goog.loadedModules_[moduleName] = exports;
    1097 if (goog.moduleLoaderState_.declareTestMethods) {
    1098 for (var entry in exports) {
    1099 if (entry.indexOf('test', 0) === 0 ||
    1100 entry == 'tearDown' ||
    1101 entry == 'setUp' ||
    1102 entry == 'setUpPage' ||
    1103 entry == 'tearDownPage') {
    1104 goog.global[entry] = exports[entry];
    1105 }
    1106 }
    1107 }
    1108 } finally {
    1109 goog.moduleLoaderState_ = previousState;
    1110 }
    1111 };
    1112
    1113
    1114 /**
    1115 * @param {string} source
    1116 * @return {!Object}
    1117 * @private
    1118 */
    1119 goog.loadModuleFromSource_ = function(source) {
    1120 // NOTE: we avoid declaring parameters or local variables here to avoid
    1121 // masking globals or leaking values into the module definition.
    1122 'use strict';
    1123 var exports = {};
    1124 eval(arguments[0]);
    1125 return exports;
    1126 };
    1127
    1128
    1129 /**
    1130 * Writes a new script pointing to {@code src} directly into the DOM.
    1131 *
    1132 * NOTE: This method is not CSP-compliant. @see goog.appendScriptSrcNode_ for
    1133 * the fallback mechanism.
    1134 *
    1135 * @param {string} src The script URL.
    1136 * @private
    1137 */
    1138 goog.writeScriptSrcNode_ = function(src) {
    1139 goog.global.document.write(
    1140 '<script type="text/javascript" src="' + src + '"></' + 'script>');
    1141 };
    1142
    1143
    1144 /**
    1145 * Appends a new script node to the DOM using a CSP-compliant mechanism. This
    1146 * method exists as a fallback for document.write (which is not allowed in a
    1147 * strict CSP context, e.g., Chrome apps).
    1148 *
    1149 * NOTE: This method is not analogous to using document.write to insert a
    1150 * <script> tag; specifically, the user agent will execute a script added by
    1151 * document.write immediately after the current script block finishes
    1152 * executing, whereas the DOM-appended script node will not be executed until
    1153 * the entire document is parsed and executed. That is to say, this script is
    1154 * added to the end of the script execution queue.
    1155 *
    1156 * The page must not attempt to call goog.required entities until after the
    1157 * document has loaded, e.g., in or after the window.onload callback.
    1158 *
    1159 * @param {string} src The script URL.
    1160 * @private
    1161 */
    1162 goog.appendScriptSrcNode_ = function(src) {
    1163 var doc = goog.global.document;
    1164 var scriptEl = doc.createElement('script');
    1165 scriptEl.type = 'text/javascript';
    1166 scriptEl.src = src;
    1167 scriptEl.defer = false;
    1168 scriptEl.async = false;
    1169 doc.head.appendChild(scriptEl);
    1170 };
    1171
    1172
    1173 /**
    1174 * The default implementation of the import function. Writes a script tag to
    1175 * import the script.
    1176 *
    1177 * @param {string} src The script url.
    1178 * @param {string=} opt_sourceText The optionally source text to evaluate
    1179 * @return {boolean} True if the script was imported, false otherwise.
    1180 * @private
    1181 */
    1182 goog.writeScriptTag_ = function(src, opt_sourceText) {
    1183 if (goog.inHtmlDocument_()) {
    1184 var doc = goog.global.document;
    1185
    1186 // If the user tries to require a new symbol after document load,
    1187 // something has gone terribly wrong. Doing a document.write would
    1188 // wipe out the page. This does not apply to the CSP-compliant method
    1189 // of writing script tags.
    1190 if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING &&
    1191 doc.readyState == 'complete') {
    1192 // Certain test frameworks load base.js multiple times, which tries
    1193 // to write deps.js each time. If that happens, just fail silently.
    1194 // These frameworks wipe the page between each load of base.js, so this
    1195 // is OK.
    1196 var isDeps = /\bdeps.js$/.test(src);
    1197 if (isDeps) {
    1198 return false;
    1199 } else {
    1200 throw Error('Cannot write "' + src + '" after document load');
    1201 }
    1202 }
    1203
    1204 var isOldIE = goog.IS_OLD_IE_;
    1205
    1206 if (opt_sourceText === undefined) {
    1207 if (!isOldIE) {
    1208 if (goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) {
    1209 goog.appendScriptSrcNode_(src);
    1210 } else {
    1211 goog.writeScriptSrcNode_(src);
    1212 }
    1213 } else {
    1214 var state = " onreadystatechange='goog.onScriptLoad_(this, " +
    1215 ++goog.lastNonModuleScriptIndex_ + ")' ";
    1216 doc.write(
    1217 '<script type="text/javascript" src="' +
    1218 src + '"' + state + '></' + 'script>');
    1219 }
    1220 } else {
    1221 doc.write(
    1222 '<script type="text/javascript">' +
    1223 opt_sourceText +
    1224 '</' + 'script>');
    1225 }
    1226 return true;
    1227 } else {
    1228 return false;
    1229 }
    1230 };
    1231
    1232
    1233 /** @private {number} */
    1234 goog.lastNonModuleScriptIndex_ = 0;
    1235
    1236
    1237 /**
    1238 * A readystatechange handler for legacy IE
    1239 * @param {!HTMLScriptElement} script
    1240 * @param {number} scriptIndex
    1241 * @return {boolean}
    1242 * @private
    1243 */
    1244 goog.onScriptLoad_ = function(script, scriptIndex) {
    1245 // for now load the modules when we reach the last script,
    1246 // later allow more inter-mingling.
    1247 if (script.readyState == 'complete' &&
    1248 goog.lastNonModuleScriptIndex_ == scriptIndex) {
    1249 goog.loadQueuedModules_();
    1250 }
    1251 return true;
    1252 };
    1253
    1254 /**
    1255 * Resolves dependencies based on the dependencies added using addDependency
    1256 * and calls importScript_ in the correct order.
    1257 * @private
    1258 */
    1259 goog.writeScripts_ = function() {
    1260 /** @type {!Array<string>} The scripts we need to write this time. */
    1261 var scripts = [];
    1262 var seenScript = {};
    1263 var deps = goog.dependencies_;
    1264
    1265 /** @param {string} path */
    1266 function visitNode(path) {
    1267 if (path in deps.written) {
    1268 return;
    1269 }
    1270
    1271 // We have already visited this one. We can get here if we have cyclic
    1272 // dependencies.
    1273 if (path in deps.visited) {
    1274 if (!(path in seenScript)) {
    1275 seenScript[path] = true;
    1276 scripts.push(path);
    1277 }
    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 for (var path in goog.included_) {
    1304 if (!deps.written[path]) {
    1305 visitNode(path);
    1306 }
    1307 }
    1308
    1309 // record that we are going to load all these scripts.
    1310 for (var i = 0; i < scripts.length; i++) {
    1311 var path = scripts[i];
    1312 goog.dependencies_.written[path] = true;
    1313 }
    1314
    1315 // If a module is loaded synchronously then we need to
    1316 // clear the current inModuleLoader value, and restore it when we are
    1317 // done loading the current "requires".
    1318 var moduleState = goog.moduleLoaderState_;
    1319 goog.moduleLoaderState_ = null;
    1320
    1321 var loadingModule = false;
    1322 for (var i = 0; i < scripts.length; i++) {
    1323 var path = scripts[i];
    1324 if (path) {
    1325 if (!deps.pathIsModule[path]) {
    1326 goog.importScript_(goog.basePath + path);
    1327 } else {
    1328 loadingModule = true;
    1329 goog.importModule_(goog.basePath + path);
    1330 }
    1331 } else {
    1332 goog.moduleLoaderState_ = moduleState;
    1333 throw Error('Undefined script input');
    1334 }
    1335 }
    1336
    1337 // restore the current "module loading state"
    1338 goog.moduleLoaderState_ = moduleState;
    1339 };
    1340
    1341
    1342 /**
    1343 * Looks at the dependency rules and tries to determine the script file that
    1344 * fulfills a particular rule.
    1345 * @param {string} rule In the form goog.namespace.Class or project.script.
    1346 * @return {?string} Url corresponding to the rule, or null.
    1347 * @private
    1348 */
    1349 goog.getPathFromDeps_ = function(rule) {
    1350 if (rule in goog.dependencies_.nameToPath) {
    1351 return goog.dependencies_.nameToPath[rule];
    1352 } else {
    1353 return null;
    1354 }
    1355 };
    1356
    1357 goog.findBasePath_();
    1358
    1359 // Allow projects to manage the deps files themselves.
    1360 if (!goog.global.CLOSURE_NO_DEPS) {
    1361 goog.importScript_(goog.basePath + 'deps.js');
    1362 }
    1363}
    1364
    1365
    1366/**
    1367 * Normalize a file path by removing redundant ".." and extraneous "." file
    1368 * path components.
    1369 * @param {string} path
    1370 * @return {string}
    1371 * @private
    1372 */
    1373goog.normalizePath_ = function(path) {
    1374 var components = path.split('/');
    1375 var i = 0;
    1376 while (i < components.length) {
    1377 if (components[i] == '.') {
    1378 components.splice(i, 1);
    1379 } else if (i && components[i] == '..' &&
    1380 components[i - 1] && components[i - 1] != '..') {
    1381 components.splice(--i, 2);
    1382 } else {
    1383 i++;
    1384 }
    1385 }
    1386 return components.join('/');
    1387};
    1388
    1389
    1390/**
    1391 * Loads file by synchronous XHR. Should not be used in production environments.
    1392 * @param {string} src Source URL.
    1393 * @return {string} File contents.
    1394 * @private
    1395 */
    1396goog.loadFileSync_ = function(src) {
    1397 if (goog.global.CLOSURE_LOAD_FILE_SYNC) {
    1398 return goog.global.CLOSURE_LOAD_FILE_SYNC(src);
    1399 } else {
    1400 var xhr = new goog.global['XMLHttpRequest']();
    1401 xhr.open('get', src, false);
    1402 xhr.send();
    1403 return xhr.responseText;
    1404 }
    1405};
    1406
    1407
    1408/**
    1409 * Retrieve and execute a module.
    1410 * @param {string} src Script source URL.
    1411 * @private
    1412 */
    1413goog.retrieveAndExecModule_ = function(src) {
    1414 if (!COMPILED) {
    1415 // The full but non-canonicalized URL for later use.
    1416 var originalPath = src;
    1417 // Canonicalize the path, removing any /./ or /../ since Chrome's debugging
    1418 // console doesn't auto-canonicalize XHR loads as it does <script> srcs.
    1419 src = goog.normalizePath_(src);
    1420
    1421 var importScript = goog.global.CLOSURE_IMPORT_SCRIPT ||
    1422 goog.writeScriptTag_;
    1423
    1424 var scriptText = goog.loadFileSync_(src);
    1425
    1426 if (scriptText != null) {
    1427 var execModuleScript = goog.wrapModule_(src, scriptText);
    1428 var isOldIE = goog.IS_OLD_IE_;
    1429 if (isOldIE) {
    1430 goog.dependencies_.deferred[originalPath] = execModuleScript;
    1431 goog.queuedModules_.push(originalPath);
    1432 } else {
    1433 importScript(src, execModuleScript);
    1434 }
    1435 } else {
    1436 throw new Error('load of ' + src + 'failed');
    1437 }
    1438 }
    1439};
    1440
    1441
    1442//==============================================================================
    1443// Language Enhancements
    1444//==============================================================================
    1445
    1446
    1447/**
    1448 * This is a "fixed" version of the typeof operator. It differs from the typeof
    1449 * operator in such a way that null returns 'null' and arrays return 'array'.
    1450 * @param {*} value The value to get the type of.
    1451 * @return {string} The name of the type.
    1452 */
    1453goog.typeOf = function(value) {
    1454 var s = typeof value;
    1455 if (s == 'object') {
    1456 if (value) {
    1457 // Check these first, so we can avoid calling Object.prototype.toString if
    1458 // possible.
    1459 //
    1460 // IE improperly marshals tyepof across execution contexts, but a
    1461 // cross-context object will still return false for "instanceof Object".
    1462 if (value instanceof Array) {
    1463 return 'array';
    1464 } else if (value instanceof Object) {
    1465 return s;
    1466 }
    1467
    1468 // HACK: In order to use an Object prototype method on the arbitrary
    1469 // value, the compiler requires the value be cast to type Object,
    1470 // even though the ECMA spec explicitly allows it.
    1471 var className = Object.prototype.toString.call(
    1472 /** @type {Object} */ (value));
    1473 // In Firefox 3.6, attempting to access iframe window objects' length
    1474 // property throws an NS_ERROR_FAILURE, so we need to special-case it
    1475 // here.
    1476 if (className == '[object Window]') {
    1477 return 'object';
    1478 }
    1479
    1480 // We cannot always use constructor == Array or instanceof Array because
    1481 // different frames have different Array objects. In IE6, if the iframe
    1482 // where the array was created is destroyed, the array loses its
    1483 // prototype. Then dereferencing val.splice here throws an exception, so
    1484 // we can't use goog.isFunction. Calling typeof directly returns 'unknown'
    1485 // so that will work. In this case, this function will return false and
    1486 // most array functions will still work because the array is still
    1487 // array-like (supports length and []) even though it has lost its
    1488 // prototype.
    1489 // Mark Miller noticed that Object.prototype.toString
    1490 // allows access to the unforgeable [[Class]] property.
    1491 // 15.2.4.2 Object.prototype.toString ( )
    1492 // When the toString method is called, the following steps are taken:
    1493 // 1. Get the [[Class]] property of this object.
    1494 // 2. Compute a string value by concatenating the three strings
    1495 // "[object ", Result(1), and "]".
    1496 // 3. Return Result(2).
    1497 // and this behavior survives the destruction of the execution context.
    1498 if ((className == '[object Array]' ||
    1499 // In IE all non value types are wrapped as objects across window
    1500 // boundaries (not iframe though) so we have to do object detection
    1501 // for this edge case.
    1502 typeof value.length == 'number' &&
    1503 typeof value.splice != 'undefined' &&
    1504 typeof value.propertyIsEnumerable != 'undefined' &&
    1505 !value.propertyIsEnumerable('splice')
    1506
    1507 )) {
    1508 return 'array';
    1509 }
    1510 // HACK: There is still an array case that fails.
    1511 // function ArrayImpostor() {}
    1512 // ArrayImpostor.prototype = [];
    1513 // var impostor = new ArrayImpostor;
    1514 // this can be fixed by getting rid of the fast path
    1515 // (value instanceof Array) and solely relying on
    1516 // (value && Object.prototype.toString.vall(value) === '[object Array]')
    1517 // but that would require many more function calls and is not warranted
    1518 // unless closure code is receiving objects from untrusted sources.
    1519
    1520 // IE in cross-window calls does not correctly marshal the function type
    1521 // (it appears just as an object) so we cannot use just typeof val ==
    1522 // 'function'. However, if the object has a call property, it is a
    1523 // function.
    1524 if ((className == '[object Function]' ||
    1525 typeof value.call != 'undefined' &&
    1526 typeof value.propertyIsEnumerable != 'undefined' &&
    1527 !value.propertyIsEnumerable('call'))) {
    1528 return 'function';
    1529 }
    1530
    1531 } else {
    1532 return 'null';
    1533 }
    1534
    1535 } else if (s == 'function' && typeof value.call == 'undefined') {
    1536 // In Safari typeof nodeList returns 'function', and on Firefox typeof
    1537 // behaves similarly for HTML{Applet,Embed,Object}, Elements and RegExps. We
    1538 // would like to return object for those and we can detect an invalid
    1539 // function by making sure that the function object has a call method.
    1540 return 'object';
    1541 }
    1542 return s;
    1543};
    1544
    1545
    1546/**
    1547 * Returns true if the specified value is null.
    1548 * @param {?} val Variable to test.
    1549 * @return {boolean} Whether variable is null.
    1550 */
    1551goog.isNull = function(val) {
    1552 return val === null;
    1553};
    1554
    1555
    1556/**
    1557 * Returns true if the specified value is defined and not null.
    1558 * @param {?} val Variable to test.
    1559 * @return {boolean} Whether variable is defined and not null.
    1560 */
    1561goog.isDefAndNotNull = function(val) {
    1562 // Note that undefined == null.
    1563 return val != null;
    1564};
    1565
    1566
    1567/**
    1568 * Returns true if the specified value is an array.
    1569 * @param {?} val Variable to test.
    1570 * @return {boolean} Whether variable is an array.
    1571 */
    1572goog.isArray = function(val) {
    1573 return goog.typeOf(val) == 'array';
    1574};
    1575
    1576
    1577/**
    1578 * Returns true if the object looks like an array. To qualify as array like
    1579 * the value needs to be either a NodeList or an object with a Number length
    1580 * property. As a special case, a function value is not array like, because its
    1581 * length property is fixed to correspond to the number of expected arguments.
    1582 * @param {?} val Variable to test.
    1583 * @return {boolean} Whether variable is an array.
    1584 */
    1585goog.isArrayLike = function(val) {
    1586 var type = goog.typeOf(val);
    1587 // We do not use goog.isObject here in order to exclude function values.
    1588 return type == 'array' || type == 'object' && typeof val.length == 'number';
    1589};
    1590
    1591
    1592/**
    1593 * Returns true if the object looks like a Date. To qualify as Date-like the
    1594 * value needs to be an object and have a getFullYear() function.
    1595 * @param {?} val Variable to test.
    1596 * @return {boolean} Whether variable is a like a Date.
    1597 */
    1598goog.isDateLike = function(val) {
    1599 return goog.isObject(val) && typeof val.getFullYear == 'function';
    1600};
    1601
    1602
    1603/**
    1604 * Returns true if the specified value is a string.
    1605 * @param {?} val Variable to test.
    1606 * @return {boolean} Whether variable is a string.
    1607 */
    1608goog.isString = function(val) {
    1609 return typeof val == 'string';
    1610};
    1611
    1612
    1613/**
    1614 * Returns true if the specified value is a boolean.
    1615 * @param {?} val Variable to test.
    1616 * @return {boolean} Whether variable is boolean.
    1617 */
    1618goog.isBoolean = function(val) {
    1619 return typeof val == 'boolean';
    1620};
    1621
    1622
    1623/**
    1624 * Returns true if the specified value is a number.
    1625 * @param {?} val Variable to test.
    1626 * @return {boolean} Whether variable is a number.
    1627 */
    1628goog.isNumber = function(val) {
    1629 return typeof val == 'number';
    1630};
    1631
    1632
    1633/**
    1634 * Returns true if the specified value is a function.
    1635 * @param {?} val Variable to test.
    1636 * @return {boolean} Whether variable is a function.
    1637 */
    1638goog.isFunction = function(val) {
    1639 return goog.typeOf(val) == 'function';
    1640};
    1641
    1642
    1643/**
    1644 * Returns true if the specified value is an object. This includes arrays and
    1645 * functions.
    1646 * @param {?} val Variable to test.
    1647 * @return {boolean} Whether variable is an object.
    1648 */
    1649goog.isObject = function(val) {
    1650 var type = typeof val;
    1651 return type == 'object' && val != null || type == 'function';
    1652 // return Object(val) === val also works, but is slower, especially if val is
    1653 // not an object.
    1654};
    1655
    1656
    1657/**
    1658 * Gets a unique ID for an object. This mutates the object so that further calls
    1659 * with the same object as a parameter returns the same value. The unique ID is
    1660 * guaranteed to be unique across the current session amongst objects that are
    1661 * passed into {@code getUid}. There is no guarantee that the ID is unique or
    1662 * consistent across sessions. It is unsafe to generate unique ID for function
    1663 * prototypes.
    1664 *
    1665 * @param {Object} obj The object to get the unique ID for.
    1666 * @return {number} The unique ID for the object.
    1667 */
    1668goog.getUid = function(obj) {
    1669 // TODO(arv): Make the type stricter, do not accept null.
    1670
    1671 // In Opera window.hasOwnProperty exists but always returns false so we avoid
    1672 // using it. As a consequence the unique ID generated for BaseClass.prototype
    1673 // and SubClass.prototype will be the same.
    1674 return obj[goog.UID_PROPERTY_] ||
    1675 (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_);
    1676};
    1677
    1678
    1679/**
    1680 * Whether the given object is already assigned a unique ID.
    1681 *
    1682 * This does not modify the object.
    1683 *
    1684 * @param {!Object} obj The object to check.
    1685 * @return {boolean} Whether there is an assigned unique id for the object.
    1686 */
    1687goog.hasUid = function(obj) {
    1688 return !!obj[goog.UID_PROPERTY_];
    1689};
    1690
    1691
    1692/**
    1693 * Removes the unique ID from an object. This is useful if the object was
    1694 * previously mutated using {@code goog.getUid} in which case the mutation is
    1695 * undone.
    1696 * @param {Object} obj The object to remove the unique ID field from.
    1697 */
    1698goog.removeUid = function(obj) {
    1699 // TODO(arv): Make the type stricter, do not accept null.
    1700
    1701 // In IE, DOM nodes are not instances of Object and throw an exception if we
    1702 // try to delete. Instead we try to use removeAttribute.
    1703 if ('removeAttribute' in obj) {
    1704 obj.removeAttribute(goog.UID_PROPERTY_);
    1705 }
    1706 /** @preserveTry */
    1707 try {
    1708 delete obj[goog.UID_PROPERTY_];
    1709 } catch (ex) {
    1710 }
    1711};
    1712
    1713
    1714/**
    1715 * Name for unique ID property. Initialized in a way to help avoid collisions
    1716 * with other closure JavaScript on the same page.
    1717 * @type {string}
    1718 * @private
    1719 */
    1720goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0);
    1721
    1722
    1723/**
    1724 * Counter for UID.
    1725 * @type {number}
    1726 * @private
    1727 */
    1728goog.uidCounter_ = 0;
    1729
    1730
    1731/**
    1732 * Adds a hash code field to an object. The hash code is unique for the
    1733 * given object.
    1734 * @param {Object} obj The object to get the hash code for.
    1735 * @return {number} The hash code for the object.
    1736 * @deprecated Use goog.getUid instead.
    1737 */
    1738goog.getHashCode = goog.getUid;
    1739
    1740
    1741/**
    1742 * Removes the hash code field from an object.
    1743 * @param {Object} obj The object to remove the field from.
    1744 * @deprecated Use goog.removeUid instead.
    1745 */
    1746goog.removeHashCode = goog.removeUid;
    1747
    1748
    1749/**
    1750 * Clones a value. The input may be an Object, Array, or basic type. Objects and
    1751 * arrays will be cloned recursively.
    1752 *
    1753 * WARNINGS:
    1754 * <code>goog.cloneObject</code> does not detect reference loops. Objects that
    1755 * refer to themselves will cause infinite recursion.
    1756 *
    1757 * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies
    1758 * UIDs created by <code>getUid</code> into cloned results.
    1759 *
    1760 * @param {*} obj The value to clone.
    1761 * @return {*} A clone of the input value.
    1762 * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods.
    1763 */
    1764goog.cloneObject = function(obj) {
    1765 var type = goog.typeOf(obj);
    1766 if (type == 'object' || type == 'array') {
    1767 if (obj.clone) {
    1768 return obj.clone();
    1769 }
    1770 var clone = type == 'array' ? [] : {};
    1771 for (var key in obj) {
    1772 clone[key] = goog.cloneObject(obj[key]);
    1773 }
    1774 return clone;
    1775 }
    1776
    1777 return obj;
    1778};
    1779
    1780
    1781/**
    1782 * A native implementation of goog.bind.
    1783 * @param {Function} fn A function to partially apply.
    1784 * @param {Object|undefined} selfObj Specifies the object which this should
    1785 * point to when the function is run.
    1786 * @param {...*} var_args Additional arguments that are partially applied to the
    1787 * function.
    1788 * @return {!Function} A partially-applied form of the function bind() was
    1789 * invoked as a method of.
    1790 * @private
    1791 * @suppress {deprecated} The compiler thinks that Function.prototype.bind is
    1792 * deprecated because some people have declared a pure-JS version.
    1793 * Only the pure-JS version is truly deprecated.
    1794 */
    1795goog.bindNative_ = function(fn, selfObj, var_args) {
    1796 return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
    1797};
    1798
    1799
    1800/**
    1801 * A pure-JS implementation of goog.bind.
    1802 * @param {Function} fn A function to partially apply.
    1803 * @param {Object|undefined} selfObj Specifies the object which this should
    1804 * point to when the function is run.
    1805 * @param {...*} var_args Additional arguments that are partially applied to the
    1806 * function.
    1807 * @return {!Function} A partially-applied form of the function bind() was
    1808 * invoked as a method of.
    1809 * @private
    1810 */
    1811goog.bindJs_ = function(fn, selfObj, var_args) {
    1812 if (!fn) {
    1813 throw new Error();
    1814 }
    1815
    1816 if (arguments.length > 2) {
    1817 var boundArgs = Array.prototype.slice.call(arguments, 2);
    1818 return function() {
    1819 // Prepend the bound arguments to the current arguments.
    1820 var newArgs = Array.prototype.slice.call(arguments);
    1821 Array.prototype.unshift.apply(newArgs, boundArgs);
    1822 return fn.apply(selfObj, newArgs);
    1823 };
    1824
    1825 } else {
    1826 return function() {
    1827 return fn.apply(selfObj, arguments);
    1828 };
    1829 }
    1830};
    1831
    1832
    1833/**
    1834 * Partially applies this function to a particular 'this object' and zero or
    1835 * more arguments. The result is a new function with some arguments of the first
    1836 * function pre-filled and the value of this 'pre-specified'.
    1837 *
    1838 * Remaining arguments specified at call-time are appended to the pre-specified
    1839 * ones.
    1840 *
    1841 * Also see: {@link #partial}.
    1842 *
    1843 * Usage:
    1844 * <pre>var barMethBound = bind(myFunction, myObj, 'arg1', 'arg2');
    1845 * barMethBound('arg3', 'arg4');</pre>
    1846 *
    1847 * @param {?function(this:T, ...)} fn A function to partially apply.
    1848 * @param {T} selfObj Specifies the object which this should point to when the
    1849 * function is run.
    1850 * @param {...*} var_args Additional arguments that are partially applied to the
    1851 * function.
    1852 * @return {!Function} A partially-applied form of the function bind() was
    1853 * invoked as a method of.
    1854 * @template T
    1855 * @suppress {deprecated} See above.
    1856 */
    1857goog.bind = function(fn, selfObj, var_args) {
    1858 // TODO(nicksantos): narrow the type signature.
    1859 if (Function.prototype.bind &&
    1860 // NOTE(nicksantos): Somebody pulled base.js into the default Chrome
    1861 // extension environment. This means that for Chrome extensions, they get
    1862 // the implementation of Function.prototype.bind that calls goog.bind
    1863 // instead of the native one. Even worse, we don't want to introduce a
    1864 // circular dependency between goog.bind and Function.prototype.bind, so
    1865 // we have to hack this to make sure it works correctly.
    1866 Function.prototype.bind.toString().indexOf('native code') != -1) {
    1867 goog.bind = goog.bindNative_;
    1868 } else {
    1869 goog.bind = goog.bindJs_;
    1870 }
    1871 return goog.bind.apply(null, arguments);
    1872};
    1873
    1874
    1875/**
    1876 * Like bind(), except that a 'this object' is not required. Useful when the
    1877 * target function is already bound.
    1878 *
    1879 * Usage:
    1880 * var g = partial(f, arg1, arg2);
    1881 * g(arg3, arg4);
    1882 *
    1883 * @param {Function} fn A function to partially apply.
    1884 * @param {...*} var_args Additional arguments that are partially applied to fn.
    1885 * @return {!Function} A partially-applied form of the function bind() was
    1886 * invoked as a method of.
    1887 */
    1888goog.partial = function(fn, var_args) {
    1889 var args = Array.prototype.slice.call(arguments, 1);
    1890 return function() {
    1891 // Clone the array (with slice()) and append additional arguments
    1892 // to the existing arguments.
    1893 var newArgs = args.slice();
    1894 newArgs.push.apply(newArgs, arguments);
    1895 return fn.apply(this, newArgs);
    1896 };
    1897};
    1898
    1899
    1900/**
    1901 * Copies all the members of a source object to a target object. This method
    1902 * does not work on all browsers for all objects that contain keys such as
    1903 * toString or hasOwnProperty. Use goog.object.extend for this purpose.
    1904 * @param {Object} target Target.
    1905 * @param {Object} source Source.
    1906 */
    1907goog.mixin = function(target, source) {
    1908 for (var x in source) {
    1909 target[x] = source[x];
    1910 }
    1911
    1912 // For IE7 or lower, the for-in-loop does not contain any properties that are
    1913 // not enumerable on the prototype object (for example, isPrototypeOf from
    1914 // Object.prototype) but also it will not include 'replace' on objects that
    1915 // extend String and change 'replace' (not that it is common for anyone to
    1916 // extend anything except Object).
    1917};
    1918
    1919
    1920/**
    1921 * @return {number} An integer value representing the number of milliseconds
    1922 * between midnight, January 1, 1970 and the current time.
    1923 */
    1924goog.now = (goog.TRUSTED_SITE && Date.now) || (function() {
    1925 // Unary plus operator converts its operand to a number which in the case of
    1926 // a date is done by calling getTime().
    1927 return +new Date();
    1928});
    1929
    1930
    1931/**
    1932 * Evals JavaScript in the global scope. In IE this uses execScript, other
    1933 * browsers use goog.global.eval. If goog.global.eval does not evaluate in the
    1934 * global scope (for example, in Safari), appends a script tag instead.
    1935 * Throws an exception if neither execScript or eval is defined.
    1936 * @param {string} script JavaScript string.
    1937 */
    1938goog.globalEval = function(script) {
    1939 if (goog.global.execScript) {
    1940 goog.global.execScript(script, 'JavaScript');
    1941 } else if (goog.global.eval) {
    1942 // Test to see if eval works
    1943 if (goog.evalWorksForGlobals_ == null) {
    1944 goog.global.eval('var _et_ = 1;');
    1945 if (typeof goog.global['_et_'] != 'undefined') {
    1946 delete goog.global['_et_'];
    1947 goog.evalWorksForGlobals_ = true;
    1948 } else {
    1949 goog.evalWorksForGlobals_ = false;
    1950 }
    1951 }
    1952
    1953 if (goog.evalWorksForGlobals_) {
    1954 goog.global.eval(script);
    1955 } else {
    1956 var doc = goog.global.document;
    1957 var scriptElt = doc.createElement('SCRIPT');
    1958 scriptElt.type = 'text/javascript';
    1959 scriptElt.defer = false;
    1960 // Note(user): can't use .innerHTML since "t('<test>')" will fail and
    1961 // .text doesn't work in Safari 2. Therefore we append a text node.
    1962 scriptElt.appendChild(doc.createTextNode(script));
    1963 doc.body.appendChild(scriptElt);
    1964 doc.body.removeChild(scriptElt);
    1965 }
    1966 } else {
    1967 throw Error('goog.globalEval not available');
    1968 }
    1969};
    1970
    1971
    1972/**
    1973 * Indicates whether or not we can call 'eval' directly to eval code in the
    1974 * global scope. Set to a Boolean by the first call to goog.globalEval (which
    1975 * empirically tests whether eval works for globals). @see goog.globalEval
    1976 * @type {?boolean}
    1977 * @private
    1978 */
    1979goog.evalWorksForGlobals_ = null;
    1980
    1981
    1982/**
    1983 * Optional map of CSS class names to obfuscated names used with
    1984 * goog.getCssName().
    1985 * @private {!Object<string, string>|undefined}
    1986 * @see goog.setCssNameMapping
    1987 */
    1988goog.cssNameMapping_;
    1989
    1990
    1991/**
    1992 * Optional obfuscation style for CSS class names. Should be set to either
    1993 * 'BY_WHOLE' or 'BY_PART' if defined.
    1994 * @type {string|undefined}
    1995 * @private
    1996 * @see goog.setCssNameMapping
    1997 */
    1998goog.cssNameMappingStyle_;
    1999
    2000
    2001/**
    2002 * Handles strings that are intended to be used as CSS class names.
    2003 *
    2004 * This function works in tandem with @see goog.setCssNameMapping.
    2005 *
    2006 * Without any mapping set, the arguments are simple joined with a hyphen and
    2007 * passed through unaltered.
    2008 *
    2009 * When there is a mapping, there are two possible styles in which these
    2010 * mappings are used. In the BY_PART style, each part (i.e. in between hyphens)
    2011 * of the passed in css name is rewritten according to the map. In the BY_WHOLE
    2012 * style, the full css name is looked up in the map directly. If a rewrite is
    2013 * not specified by the map, the compiler will output a warning.
    2014 *
    2015 * When the mapping is passed to the compiler, it will replace calls to
    2016 * goog.getCssName with the strings from the mapping, e.g.
    2017 * var x = goog.getCssName('foo');
    2018 * var y = goog.getCssName(this.baseClass, 'active');
    2019 * becomes:
    2020 * var x= 'foo';
    2021 * var y = this.baseClass + '-active';
    2022 *
    2023 * If one argument is passed it will be processed, if two are passed only the
    2024 * modifier will be processed, as it is assumed the first argument was generated
    2025 * as a result of calling goog.getCssName.
    2026 *
    2027 * @param {string} className The class name.
    2028 * @param {string=} opt_modifier A modifier to be appended to the class name.
    2029 * @return {string} The class name or the concatenation of the class name and
    2030 * the modifier.
    2031 */
    2032goog.getCssName = function(className, opt_modifier) {
    2033 var getMapping = function(cssName) {
    2034 return goog.cssNameMapping_[cssName] || cssName;
    2035 };
    2036
    2037 var renameByParts = function(cssName) {
    2038 // Remap all the parts individually.
    2039 var parts = cssName.split('-');
    2040 var mapped = [];
    2041 for (var i = 0; i < parts.length; i++) {
    2042 mapped.push(getMapping(parts[i]));
    2043 }
    2044 return mapped.join('-');
    2045 };
    2046
    2047 var rename;
    2048 if (goog.cssNameMapping_) {
    2049 rename = goog.cssNameMappingStyle_ == 'BY_WHOLE' ?
    2050 getMapping : renameByParts;
    2051 } else {
    2052 rename = function(a) {
    2053 return a;
    2054 };
    2055 }
    2056
    2057 if (opt_modifier) {
    2058 return className + '-' + rename(opt_modifier);
    2059 } else {
    2060 return rename(className);
    2061 }
    2062};
    2063
    2064
    2065/**
    2066 * Sets the map to check when returning a value from goog.getCssName(). Example:
    2067 * <pre>
    2068 * goog.setCssNameMapping({
    2069 * "goog": "a",
    2070 * "disabled": "b",
    2071 * });
    2072 *
    2073 * var x = goog.getCssName('goog');
    2074 * // The following evaluates to: "a a-b".
    2075 * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')
    2076 * </pre>
    2077 * When declared as a map of string literals to string literals, the JSCompiler
    2078 * will replace all calls to goog.getCssName() using the supplied map if the
    2079 * --closure_pass flag is set.
    2080 *
    2081 * @param {!Object} mapping A map of strings to strings where keys are possible
    2082 * arguments to goog.getCssName() and values are the corresponding values
    2083 * that should be returned.
    2084 * @param {string=} opt_style The style of css name mapping. There are two valid
    2085 * options: 'BY_PART', and 'BY_WHOLE'.
    2086 * @see goog.getCssName for a description.
    2087 */
    2088goog.setCssNameMapping = function(mapping, opt_style) {
    2089 goog.cssNameMapping_ = mapping;
    2090 goog.cssNameMappingStyle_ = opt_style;
    2091};
    2092
    2093
    2094/**
    2095 * To use CSS renaming in compiled mode, one of the input files should have a
    2096 * call to goog.setCssNameMapping() with an object literal that the JSCompiler
    2097 * can extract and use to replace all calls to goog.getCssName(). In uncompiled
    2098 * mode, JavaScript code should be loaded before this base.js file that declares
    2099 * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is
    2100 * to ensure that the mapping is loaded before any calls to goog.getCssName()
    2101 * are made in uncompiled mode.
    2102 *
    2103 * A hook for overriding the CSS name mapping.
    2104 * @type {!Object<string, string>|undefined}
    2105 */
    2106goog.global.CLOSURE_CSS_NAME_MAPPING;
    2107
    2108
    2109if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) {
    2110 // This does not call goog.setCssNameMapping() because the JSCompiler
    2111 // requires that goog.setCssNameMapping() be called with an object literal.
    2112 goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING;
    2113}
    2114
    2115
    2116/**
    2117 * Gets a localized message.
    2118 *
    2119 * This function is a compiler primitive. If you give the compiler a localized
    2120 * message bundle, it will replace the string at compile-time with a localized
    2121 * version, and expand goog.getMsg call to a concatenated string.
    2122 *
    2123 * Messages must be initialized in the form:
    2124 * <code>
    2125 * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'});
    2126 * </code>
    2127 *
    2128 * @param {string} str Translatable string, places holders in the form {$foo}.
    2129 * @param {Object<string, string>=} opt_values Maps place holder name to value.
    2130 * @return {string} message with placeholders filled.
    2131 */
    2132goog.getMsg = function(str, opt_values) {
    2133 if (opt_values) {
    2134 str = str.replace(/\{\$([^}]+)}/g, function(match, key) {
    2135 return key in opt_values ? opt_values[key] : match;
    2136 });
    2137 }
    2138 return str;
    2139};
    2140
    2141
    2142/**
    2143 * Gets a localized message. If the message does not have a translation, gives a
    2144 * fallback message.
    2145 *
    2146 * This is useful when introducing a new message that has not yet been
    2147 * translated into all languages.
    2148 *
    2149 * This function is a compiler primitive. Must be used in the form:
    2150 * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code>
    2151 * where MSG_A and MSG_B were initialized with goog.getMsg.
    2152 *
    2153 * @param {string} a The preferred message.
    2154 * @param {string} b The fallback message.
    2155 * @return {string} The best translated message.
    2156 */
    2157goog.getMsgWithFallback = function(a, b) {
    2158 return a;
    2159};
    2160
    2161
    2162/**
    2163 * Exposes an unobfuscated global namespace path for the given object.
    2164 * Note that fields of the exported object *will* be obfuscated, unless they are
    2165 * exported in turn via this function or goog.exportProperty.
    2166 *
    2167 * Also handy for making public items that are defined in anonymous closures.
    2168 *
    2169 * ex. goog.exportSymbol('public.path.Foo', Foo);
    2170 *
    2171 * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction);
    2172 * public.path.Foo.staticFunction();
    2173 *
    2174 * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod',
    2175 * Foo.prototype.myMethod);
    2176 * new public.path.Foo().myMethod();
    2177 *
    2178 * @param {string} publicPath Unobfuscated name to export.
    2179 * @param {*} object Object the name should point to.
    2180 * @param {Object=} opt_objectToExportTo The object to add the path to; default
    2181 * is goog.global.
    2182 */
    2183goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) {
    2184 goog.exportPath_(publicPath, object, opt_objectToExportTo);
    2185};
    2186
    2187
    2188/**
    2189 * Exports a property unobfuscated into the object's namespace.
    2190 * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);
    2191 * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);
    2192 * @param {Object} object Object whose static property is being exported.
    2193 * @param {string} publicName Unobfuscated name to export.
    2194 * @param {*} symbol Object the name should point to.
    2195 */
    2196goog.exportProperty = function(object, publicName, symbol) {
    2197 object[publicName] = symbol;
    2198};
    2199
    2200
    2201/**
    2202 * Inherit the prototype methods from one constructor into another.
    2203 *
    2204 * Usage:
    2205 * <pre>
    2206 * function ParentClass(a, b) { }
    2207 * ParentClass.prototype.foo = function(a) { };
    2208 *
    2209 * function ChildClass(a, b, c) {
    2210 * ChildClass.base(this, 'constructor', a, b);
    2211 * }
    2212 * goog.inherits(ChildClass, ParentClass);
    2213 *
    2214 * var child = new ChildClass('a', 'b', 'see');
    2215 * child.foo(); // This works.
    2216 * </pre>
    2217 *
    2218 * @param {Function} childCtor Child class.
    2219 * @param {Function} parentCtor Parent class.
    2220 */
    2221goog.inherits = function(childCtor, parentCtor) {
    2222 /** @constructor */
    2223 function tempCtor() {};
    2224 tempCtor.prototype = parentCtor.prototype;
    2225 childCtor.superClass_ = parentCtor.prototype;
    2226 childCtor.prototype = new tempCtor();
    2227 /** @override */
    2228 childCtor.prototype.constructor = childCtor;
    2229
    2230 /**
    2231 * Calls superclass constructor/method.
    2232 *
    2233 * This function is only available if you use goog.inherits to
    2234 * express inheritance relationships between classes.
    2235 *
    2236 * NOTE: This is a replacement for goog.base and for superClass_
    2237 * property defined in childCtor.
    2238 *
    2239 * @param {!Object} me Should always be "this".
    2240 * @param {string} methodName The method name to call. Calling
    2241 * superclass constructor can be done with the special string
    2242 * 'constructor'.
    2243 * @param {...*} var_args The arguments to pass to superclass
    2244 * method/constructor.
    2245 * @return {*} The return value of the superclass method/constructor.
    2246 */
    2247 childCtor.base = function(me, methodName, var_args) {
    2248 // Copying using loop to avoid deop due to passing arguments object to
    2249 // function. This is faster in many JS engines as of late 2014.
    2250 var args = new Array(arguments.length - 2);
    2251 for (var i = 2; i < arguments.length; i++) {
    2252 args[i - 2] = arguments[i];
    2253 }
    2254 return parentCtor.prototype[methodName].apply(me, args);
    2255 };
    2256};
    2257
    2258
    2259/**
    2260 * Call up to the superclass.
    2261 *
    2262 * If this is called from a constructor, then this calls the superclass
    2263 * constructor with arguments 1-N.
    2264 *
    2265 * If this is called from a prototype method, then you must pass the name of the
    2266 * method as the second argument to this function. If you do not, you will get a
    2267 * runtime error. This calls the superclass' method with arguments 2-N.
    2268 *
    2269 * This function only works if you use goog.inherits to express inheritance
    2270 * relationships between your classes.
    2271 *
    2272 * This function is a compiler primitive. At compile-time, the compiler will do
    2273 * macro expansion to remove a lot of the extra overhead that this function
    2274 * introduces. The compiler will also enforce a lot of the assumptions that this
    2275 * function makes, and treat it as a compiler error if you break them.
    2276 *
    2277 * @param {!Object} me Should always be "this".
    2278 * @param {*=} opt_methodName The method name if calling a super method.
    2279 * @param {...*} var_args The rest of the arguments.
    2280 * @return {*} The return value of the superclass method.
    2281 * @suppress {es5Strict} This method can not be used in strict mode, but
    2282 * all Closure Library consumers must depend on this file.
    2283 */
    2284goog.base = function(me, opt_methodName, var_args) {
    2285 var caller = arguments.callee.caller;
    2286
    2287 if (goog.STRICT_MODE_COMPATIBLE || (goog.DEBUG && !caller)) {
    2288 throw Error('arguments.caller not defined. goog.base() cannot be used ' +
    2289 'with strict mode code. See ' +
    2290 'http://www.ecma-international.org/ecma-262/5.1/#sec-C');
    2291 }
    2292
    2293 if (caller.superClass_) {
    2294 // Copying using loop to avoid deop due to passing arguments object to
    2295 // function. This is faster in many JS engines as of late 2014.
    2296 var ctorArgs = new Array(arguments.length - 1);
    2297 for (var i = 1; i < arguments.length; i++) {
    2298 ctorArgs[i - 1] = arguments[i];
    2299 }
    2300 // This is a constructor. Call the superclass constructor.
    2301 return caller.superClass_.constructor.apply(me, ctorArgs);
    2302 }
    2303
    2304 // Copying using loop to avoid deop due to passing arguments object to
    2305 // function. This is faster in many JS engines as of late 2014.
    2306 var args = new Array(arguments.length - 2);
    2307 for (var i = 2; i < arguments.length; i++) {
    2308 args[i - 2] = arguments[i];
    2309 }
    2310 var foundCaller = false;
    2311 for (var ctor = me.constructor;
    2312 ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) {
    2313 if (ctor.prototype[opt_methodName] === caller) {
    2314 foundCaller = true;
    2315 } else if (foundCaller) {
    2316 return ctor.prototype[opt_methodName].apply(me, args);
    2317 }
    2318 }
    2319
    2320 // If we did not find the caller in the prototype chain, then one of two
    2321 // things happened:
    2322 // 1) The caller is an instance method.
    2323 // 2) This method was not called by the right caller.
    2324 if (me[opt_methodName] === caller) {
    2325 return me.constructor.prototype[opt_methodName].apply(me, args);
    2326 } else {
    2327 throw Error(
    2328 'goog.base called from a method of one name ' +
    2329 'to a method of a different name');
    2330 }
    2331};
    2332
    2333
    2334/**
    2335 * Allow for aliasing within scope functions. This function exists for
    2336 * uncompiled code - in compiled code the calls will be inlined and the aliases
    2337 * applied. In uncompiled code the function is simply run since the aliases as
    2338 * written are valid JavaScript.
    2339 *
    2340 *
    2341 * @param {function()} fn Function to call. This function can contain aliases
    2342 * to namespaces (e.g. "var dom = goog.dom") or classes
    2343 * (e.g. "var Timer = goog.Timer").
    2344 */
    2345goog.scope = function(fn) {
    2346 fn.call(goog.global);
    2347};
    2348
    2349
    2350/*
    2351 * To support uncompiled, strict mode bundles that use eval to divide source
    2352 * like so:
    2353 * eval('someSource;//# sourceUrl sourcefile.js');
    2354 * We need to export the globally defined symbols "goog" and "COMPILED".
    2355 * Exporting "goog" breaks the compiler optimizations, so we required that
    2356 * be defined externally.
    2357 * NOTE: We don't use goog.exportSymbol here because we don't want to trigger
    2358 * extern generation when that compiler option is enabled.
    2359 */
    2360if (!COMPILED) {
    2361 goog.global['COMPILED'] = COMPILED;
    2362}
    2363
    2364
    2365
    2366//==============================================================================
    2367// goog.defineClass implementation
    2368//==============================================================================
    2369
    2370
    2371/**
    2372 * Creates a restricted form of a Closure "class":
    2373 * - from the compiler's perspective, the instance returned from the
    2374 * constructor is sealed (no new properties may be added). This enables
    2375 * better checks.
    2376 * - the compiler will rewrite this definition to a form that is optimal
    2377 * for type checking and optimization (initially this will be a more
    2378 * traditional form).
    2379 *
    2380 * @param {Function} superClass The superclass, Object or null.
    2381 * @param {goog.defineClass.ClassDescriptor} def
    2382 * An object literal describing
    2383 * the class. It may have the following properties:
    2384 * "constructor": the constructor function
    2385 * "statics": an object literal containing methods to add to the constructor
    2386 * as "static" methods or a function that will receive the constructor
    2387 * function as its only parameter to which static properties can
    2388 * be added.
    2389 * all other properties are added to the prototype.
    2390 * @return {!Function} The class constructor.
    2391 */
    2392goog.defineClass = function(superClass, def) {
    2393 // TODO(johnlenz): consider making the superClass an optional parameter.
    2394 var constructor = def.constructor;
    2395 var statics = def.statics;
    2396 // Wrap the constructor prior to setting up the prototype and static methods.
    2397 if (!constructor || constructor == Object.prototype.constructor) {
    2398 constructor = function() {
    2399 throw Error('cannot instantiate an interface (no constructor defined).');
    2400 };
    2401 }
    2402
    2403 var cls = goog.defineClass.createSealingConstructor_(constructor, superClass);
    2404 if (superClass) {
    2405 goog.inherits(cls, superClass);
    2406 }
    2407
    2408 // Remove all the properties that should not be copied to the prototype.
    2409 delete def.constructor;
    2410 delete def.statics;
    2411
    2412 goog.defineClass.applyProperties_(cls.prototype, def);
    2413 if (statics != null) {
    2414 if (statics instanceof Function) {
    2415 statics(cls);
    2416 } else {
    2417 goog.defineClass.applyProperties_(cls, statics);
    2418 }
    2419 }
    2420
    2421 return cls;
    2422};
    2423
    2424
    2425/**
    2426 * @typedef {
    2427 * !Object|
    2428 * {constructor:!Function}|
    2429 * {constructor:!Function, statics:(Object|function(Function):void)}}
    2430 * @suppress {missingProvide}
    2431 */
    2432goog.defineClass.ClassDescriptor;
    2433
    2434
    2435/**
    2436 * @define {boolean} Whether the instances returned by
    2437 * goog.defineClass should be sealed when possible.
    2438 */
    2439goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG);
    2440
    2441
    2442/**
    2443 * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is
    2444 * defined, this function will wrap the constructor in a function that seals the
    2445 * results of the provided constructor function.
    2446 *
    2447 * @param {!Function} ctr The constructor whose results maybe be sealed.
    2448 * @param {Function} superClass The superclass constructor.
    2449 * @return {!Function} The replacement constructor.
    2450 * @private
    2451 */
    2452goog.defineClass.createSealingConstructor_ = function(ctr, superClass) {
    2453 if (goog.defineClass.SEAL_CLASS_INSTANCES &&
    2454 Object.seal instanceof Function) {
    2455 // Don't seal subclasses of unsealable-tagged legacy classes.
    2456 if (superClass && superClass.prototype &&
    2457 superClass.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_]) {
    2458 return ctr;
    2459 }
    2460 /**
    2461 * @this {Object}
    2462 * @return {?}
    2463 */
    2464 var wrappedCtr = function() {
    2465 // Don't seal an instance of a subclass when it calls the constructor of
    2466 // its super class as there is most likely still setup to do.
    2467 var instance = ctr.apply(this, arguments) || this;
    2468 instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_];
    2469 if (this.constructor === wrappedCtr) {
    2470 Object.seal(instance);
    2471 }
    2472 return instance;
    2473 };
    2474 return wrappedCtr;
    2475 }
    2476 return ctr;
    2477};
    2478
    2479
    2480// TODO(johnlenz): share these values with the goog.object
    2481/**
    2482 * The names of the fields that are defined on Object.prototype.
    2483 * @type {!Array<string>}
    2484 * @private
    2485 * @const
    2486 */
    2487goog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [
    2488 'constructor',
    2489 'hasOwnProperty',
    2490 'isPrototypeOf',
    2491 'propertyIsEnumerable',
    2492 'toLocaleString',
    2493 'toString',
    2494 'valueOf'
    2495];
    2496
    2497
    2498// TODO(johnlenz): share this function with the goog.object
    2499/**
    2500 * @param {!Object} target The object to add properties to.
    2501 * @param {!Object} source The object to copy properties from.
    2502 * @private
    2503 */
    2504goog.defineClass.applyProperties_ = function(target, source) {
    2505 // TODO(johnlenz): update this to support ES5 getters/setters
    2506
    2507 var key;
    2508 for (key in source) {
    2509 if (Object.prototype.hasOwnProperty.call(source, key)) {
    2510 target[key] = source[key];
    2511 }
    2512 }
    2513
    2514 // For IE the for-in-loop does not contain any properties that are not
    2515 // enumerable on the prototype object (for example isPrototypeOf from
    2516 // Object.prototype) and it will also not include 'replace' on objects that
    2517 // extend String and change 'replace' (not that it is common for anyone to
    2518 // extend anything except Object).
    2519 for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) {
    2520 key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i];
    2521 if (Object.prototype.hasOwnProperty.call(source, key)) {
    2522 target[key] = source[key];
    2523 }
    2524 }
    2525};
    2526
    2527
    2528/**
    2529 * Sealing classes breaks the older idiom of assigning properties on the
    2530 * prototype rather than in the constructor. As such, goog.defineClass
    2531 * must not seal subclasses of these old-style classes until they are fixed.
    2532 * Until then, this marks a class as "broken", instructing defineClass
    2533 * not to seal subclasses.
    2534 * @param {!Function} ctr The legacy constructor to tag as unsealable.
    2535 */
    2536goog.tagUnsealableClass = function(ctr) {
    2537 if (!COMPILED && goog.defineClass.SEAL_CLASS_INSTANCES) {
    2538 ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_] = true;
    2539 }
    2540};
    2541
    2542
    2543/**
    2544 * Name for unsealable tag property.
    2545 * @const @private {string}
    2546 */
    2547goog.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 index 1a2daf4..11ee87e 100644 --- a/docs/source/lib/goog/debug/debug.js.src.html +++ b/docs/source/lib/goog/debug/debug.js.src.html @@ -1 +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.string');
    25goog.require('goog.structs.Set');
    26goog.require('goog.userAgent');
    27
    28
    29/** @define {boolean} Whether logging should be enabled. */
    30goog.define('goog.debug.LOGGING_ENABLED', goog.DEBUG);
    31
    32
    33/**
    34 * Catches onerror events fired by windows and similar objects.
    35 * @param {function(Object)} logFunc The function to call with the error
    36 * information.
    37 * @param {boolean=} opt_cancel Whether to stop the error from reaching the
    38 * browser.
    39 * @param {Object=} opt_target Object that fires onerror events.
    40 */
    41goog.debug.catchErrors = function(logFunc, opt_cancel, opt_target) {
    42 var target = opt_target || goog.global;
    43 var oldErrorHandler = target.onerror;
    44 var retVal = !!opt_cancel;
    45
    46 // Chrome interprets onerror return value backwards (http://crbug.com/92062)
    47 // until it was fixed in webkit revision r94061 (Webkit 535.3). This
    48 // workaround still needs to be skipped in Safari after the webkit change
    49 // gets pushed out in Safari.
    50 // See https://bugs.webkit.org/show_bug.cgi?id=67119
    51 if (goog.userAgent.WEBKIT &&
    52 !goog.userAgent.isVersionOrHigher('535.3')) {
    53 retVal = !retVal;
    54 }
    55
    56 /**
    57 * New onerror handler for this target. This onerror handler follows the spec
    58 * according to
    59 * http://www.whatwg.org/specs/web-apps/current-work/#runtime-script-errors
    60 * The spec was changed in August 2013 to support receiving column information
    61 * and an error object for all scripts on the same origin or cross origin
    62 * scripts with the proper headers. See
    63 * https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror
    64 *
    65 * @param {string} message The error message. For cross-origin errors, this
    66 * will be scrubbed to just "Script error.". For new browsers that have
    67 * updated to follow the latest spec, errors that come from origins that
    68 * have proper cross origin headers will not be scrubbed.
    69 * @param {string} url The URL of the script that caused the error. The URL
    70 * will be scrubbed to "" for cross origin scripts unless the script has
    71 * proper cross origin headers and the browser has updated to the latest
    72 * spec.
    73 * @param {number} line The line number in the script that the error
    74 * occurred on.
    75 * @param {number=} opt_col The optional column number that the error
    76 * occurred on. Only browsers that have updated to the latest spec will
    77 * include this.
    78 * @param {Error=} opt_error The optional actual error object for this
    79 * error that should include the stack. Only browsers that have updated
    80 * to the latest spec will inlude this parameter.
    81 * @return {boolean} Whether to prevent the error from reaching the browser.
    82 */
    83 target.onerror = function(message, url, line, opt_col, opt_error) {
    84 if (oldErrorHandler) {
    85 oldErrorHandler(message, url, line, opt_col, opt_error);
    86 }
    87 logFunc({
    88 message: message,
    89 fileName: url,
    90 line: line,
    91 col: opt_col,
    92 error: opt_error
    93 });
    94 return retVal;
    95 };
    96};
    97
    98
    99/**
    100 * Creates a string representing an object and all its properties.
    101 * @param {Object|null|undefined} obj Object to expose.
    102 * @param {boolean=} opt_showFn Show the functions as well as the properties,
    103 * default is false.
    104 * @return {string} The string representation of {@code obj}.
    105 */
    106goog.debug.expose = function(obj, opt_showFn) {
    107 if (typeof obj == 'undefined') {
    108 return 'undefined';
    109 }
    110 if (obj == null) {
    111 return 'NULL';
    112 }
    113 var str = [];
    114
    115 for (var x in obj) {
    116 if (!opt_showFn && goog.isFunction(obj[x])) {
    117 continue;
    118 }
    119 var s = x + ' = ';
    120 /** @preserveTry */
    121 try {
    122 s += obj[x];
    123 } catch (e) {
    124 s += '*** ' + e + ' ***';
    125 }
    126 str.push(s);
    127 }
    128 return str.join('\n');
    129};
    130
    131
    132/**
    133 * Creates a string representing a given primitive or object, and for an
    134 * object, all its properties and nested objects. WARNING: If an object is
    135 * given, it and all its nested objects will be modified. To detect reference
    136 * cycles, this method identifies objects using goog.getUid() which mutates the
    137 * object.
    138 * @param {*} obj Object to expose.
    139 * @param {boolean=} opt_showFn Also show properties that are functions (by
    140 * default, functions are omitted).
    141 * @return {string} A string representation of {@code obj}.
    142 */
    143goog.debug.deepExpose = function(obj, opt_showFn) {
    144 var str = [];
    145
    146 var helper = function(obj, space, parentSeen) {
    147 var nestspace = space + ' ';
    148 var seen = new goog.structs.Set(parentSeen);
    149
    150 var indentMultiline = function(str) {
    151 return str.replace(/\n/g, '\n' + space);
    152 };
    153
    154 /** @preserveTry */
    155 try {
    156 if (!goog.isDef(obj)) {
    157 str.push('undefined');
    158 } else if (goog.isNull(obj)) {
    159 str.push('NULL');
    160 } else if (goog.isString(obj)) {
    161 str.push('"' + indentMultiline(obj) + '"');
    162 } else if (goog.isFunction(obj)) {
    163 str.push(indentMultiline(String(obj)));
    164 } else if (goog.isObject(obj)) {
    165 if (seen.contains(obj)) {
    166 str.push('*** reference loop detected ***');
    167 } else {
    168 seen.add(obj);
    169 str.push('{');
    170 for (var x in obj) {
    171 if (!opt_showFn && goog.isFunction(obj[x])) {
    172 continue;
    173 }
    174 str.push('\n');
    175 str.push(nestspace);
    176 str.push(x + ' = ');
    177 helper(obj[x], nestspace, seen);
    178 }
    179 str.push('\n' + space + '}');
    180 }
    181 } else {
    182 str.push(obj);
    183 }
    184 } catch (e) {
    185 str.push('*** ' + e + ' ***');
    186 }
    187 };
    188
    189 helper(obj, '', new goog.structs.Set());
    190 return str.join('');
    191};
    192
    193
    194/**
    195 * Recursively outputs a nested array as a string.
    196 * @param {Array} arr The array.
    197 * @return {string} String representing nested array.
    198 */
    199goog.debug.exposeArray = function(arr) {
    200 var str = [];
    201 for (var i = 0; i < arr.length; i++) {
    202 if (goog.isArray(arr[i])) {
    203 str.push(goog.debug.exposeArray(arr[i]));
    204 } else {
    205 str.push(arr[i]);
    206 }
    207 }
    208 return '[ ' + str.join(', ') + ' ]';
    209};
    210
    211
    212/**
    213 * Exposes an exception that has been caught by a try...catch and outputs the
    214 * error with a stack trace.
    215 * @param {Object} err Error object or string.
    216 * @param {Function=} opt_fn Optional function to start stack trace from.
    217 * @return {string} Details of exception.
    218 */
    219goog.debug.exposeException = function(err, opt_fn) {
    220 /** @preserveTry */
    221 try {
    222 var e = goog.debug.normalizeErrorObject(err);
    223
    224 // Create the error message
    225 var error = 'Message: ' + goog.string.htmlEscape(e.message) +
    226 '\nUrl: <a href="view-source:' + e.fileName + '" target="_new">' +
    227 e.fileName + '</a>\nLine: ' + e.lineNumber + '\n\nBrowser stack:\n' +
    228 goog.string.htmlEscape(e.stack + '-> ') +
    229 '[end]\n\nJS stack traversal:\n' + goog.string.htmlEscape(
    230 goog.debug.getStacktrace(opt_fn) + '-> ');
    231 return error;
    232 } catch (e2) {
    233 return 'Exception trying to expose exception! You win, we lose. ' + e2;
    234 }
    235};
    236
    237
    238/**
    239 * Normalizes the error/exception object between browsers.
    240 * @param {Object} err Raw error object.
    241 * @return {!Object} Normalized error object.
    242 */
    243goog.debug.normalizeErrorObject = function(err) {
    244 var href = goog.getObjectByName('window.location.href');
    245 if (goog.isString(err)) {
    246 return {
    247 'message': err,
    248 'name': 'Unknown error',
    249 'lineNumber': 'Not available',
    250 'fileName': href,
    251 'stack': 'Not available'
    252 };
    253 }
    254
    255 var lineNumber, fileName;
    256 var threwError = false;
    257
    258 try {
    259 lineNumber = err.lineNumber || err.line || 'Not available';
    260 } catch (e) {
    261 // Firefox 2 sometimes throws an error when accessing 'lineNumber':
    262 // Message: Permission denied to get property UnnamedClass.lineNumber
    263 lineNumber = 'Not available';
    264 threwError = true;
    265 }
    266
    267 try {
    268 fileName = err.fileName || err.filename || err.sourceURL ||
    269 // $googDebugFname may be set before a call to eval to set the filename
    270 // that the eval is supposed to present.
    271 goog.global['$googDebugFname'] || href;
    272 } catch (e) {
    273 // Firefox 2 may also throw an error when accessing 'filename'.
    274 fileName = 'Not available';
    275 threwError = true;
    276 }
    277
    278 // The IE Error object contains only the name and the message.
    279 // The Safari Error object uses the line and sourceURL fields.
    280 if (threwError || !err.lineNumber || !err.fileName || !err.stack ||
    281 !err.message || !err.name) {
    282 return {
    283 'message': err.message || 'Not available',
    284 'name': err.name || 'UnknownError',
    285 'lineNumber': lineNumber,
    286 'fileName': fileName,
    287 'stack': err.stack || 'Not available'
    288 };
    289 }
    290
    291 // Standards error object
    292 return err;
    293};
    294
    295
    296/**
    297 * Converts an object to an Error if it's a String,
    298 * adds a stacktrace if there isn't one,
    299 * and optionally adds an extra message.
    300 * @param {Error|string} err the original thrown object or string.
    301 * @param {string=} opt_message optional additional message to add to the
    302 * error.
    303 * @return {!Error} If err is a string, it is used to create a new Error,
    304 * which is enhanced and returned. Otherwise err itself is enhanced
    305 * and returned.
    306 */
    307goog.debug.enhanceError = function(err, opt_message) {
    308 var error;
    309 if (typeof err == 'string') {
    310 error = Error(err);
    311 if (Error.captureStackTrace) {
    312 // Trim this function off the call stack, if we can.
    313 Error.captureStackTrace(error, goog.debug.enhanceError);
    314 }
    315 } else {
    316 error = err;
    317 }
    318
    319 if (!error.stack) {
    320 error.stack = goog.debug.getStacktrace(goog.debug.enhanceError);
    321 }
    322 if (opt_message) {
    323 // find the first unoccupied 'messageX' property
    324 var x = 0;
    325 while (error['message' + x]) {
    326 ++x;
    327 }
    328 error['message' + x] = String(opt_message);
    329 }
    330 return error;
    331};
    332
    333
    334/**
    335 * Gets the current stack trace. Simple and iterative - doesn't worry about
    336 * catching circular references or getting the args.
    337 * @param {number=} opt_depth Optional maximum depth to trace back to.
    338 * @return {string} A string with the function names of all functions in the
    339 * stack, separated by \n.
    340 * @suppress {es5Strict}
    341 */
    342goog.debug.getStacktraceSimple = function(opt_depth) {
    343 if (goog.STRICT_MODE_COMPATIBLE) {
    344 var stack = goog.debug.getNativeStackTrace_(goog.debug.getStacktraceSimple);
    345 if (stack) {
    346 return stack;
    347 }
    348 // NOTE: browsers that have strict mode support also have native "stack"
    349 // properties. Fall-through for legacy browser support.
    350 }
    351
    352 var sb = [];
    353 var fn = arguments.callee.caller;
    354 var depth = 0;
    355
    356 while (fn && (!opt_depth || depth < opt_depth)) {
    357 sb.push(goog.debug.getFunctionName(fn));
    358 sb.push('()\n');
    359 /** @preserveTry */
    360 try {
    361 fn = fn.caller;
    362 } catch (e) {
    363 sb.push('[exception trying to get caller]\n');
    364 break;
    365 }
    366 depth++;
    367 if (depth >= goog.debug.MAX_STACK_DEPTH) {
    368 sb.push('[...long stack...]');
    369 break;
    370 }
    371 }
    372 if (opt_depth && depth >= opt_depth) {
    373 sb.push('[...reached max depth limit...]');
    374 } else {
    375 sb.push('[end]');
    376 }
    377
    378 return sb.join('');
    379};
    380
    381
    382/**
    383 * Max length of stack to try and output
    384 * @type {number}
    385 */
    386goog.debug.MAX_STACK_DEPTH = 50;
    387
    388
    389/**
    390 * @param {Function} fn The function to start getting the trace from.
    391 * @return {?string}
    392 * @private
    393 */
    394goog.debug.getNativeStackTrace_ = function(fn) {
    395 var tempErr = new Error();
    396 if (Error.captureStackTrace) {
    397 Error.captureStackTrace(tempErr, fn);
    398 return String(tempErr.stack);
    399 } else {
    400 // IE10, only adds stack traces when an exception is thrown.
    401 try {
    402 throw tempErr;
    403 } catch (e) {
    404 tempErr = e;
    405 }
    406 var stack = tempErr.stack;
    407 if (stack) {
    408 return String(stack);
    409 }
    410 }
    411 return null;
    412};
    413
    414
    415/**
    416 * Gets the current stack trace, either starting from the caller or starting
    417 * from a specified function that's currently on the call stack.
    418 * @param {Function=} opt_fn Optional function to start getting the trace from.
    419 * If not provided, defaults to the function that called this.
    420 * @return {string} Stack trace.
    421 * @suppress {es5Strict}
    422 */
    423goog.debug.getStacktrace = function(opt_fn) {
    424 var stack;
    425 if (goog.STRICT_MODE_COMPATIBLE) {
    426 // Try to get the stack trace from the environment if it is available.
    427 var contextFn = opt_fn || goog.debug.getStacktrace;
    428 stack = goog.debug.getNativeStackTrace_(contextFn);
    429 }
    430 if (!stack) {
    431 // NOTE: browsers that have strict mode support also have native "stack"
    432 // properties. This function will throw in strict mode.
    433 stack = goog.debug.getStacktraceHelper_(
    434 opt_fn || arguments.callee.caller, []);
    435 }
    436 return stack;
    437};
    438
    439
    440/**
    441 * Private helper for getStacktrace().
    442 * @param {Function} fn Function to start getting the trace from.
    443 * @param {Array} visited List of functions visited so far.
    444 * @return {string} Stack trace starting from function fn.
    445 * @suppress {es5Strict}
    446 * @private
    447 */
    448goog.debug.getStacktraceHelper_ = function(fn, visited) {
    449 var sb = [];
    450
    451 // Circular reference, certain functions like bind seem to cause a recursive
    452 // loop so we need to catch circular references
    453 if (goog.array.contains(visited, fn)) {
    454 sb.push('[...circular reference...]');
    455
    456 // Traverse the call stack until function not found or max depth is reached
    457 } else if (fn && visited.length < goog.debug.MAX_STACK_DEPTH) {
    458 sb.push(goog.debug.getFunctionName(fn) + '(');
    459 var args = fn.arguments;
    460 // Args may be null for some special functions such as host objects or eval.
    461 for (var i = 0; args && i < args.length; i++) {
    462 if (i > 0) {
    463 sb.push(', ');
    464 }
    465 var argDesc;
    466 var arg = args[i];
    467 switch (typeof arg) {
    468 case 'object':
    469 argDesc = arg ? 'object' : 'null';
    470 break;
    471
    472 case 'string':
    473 argDesc = arg;
    474 break;
    475
    476 case 'number':
    477 argDesc = String(arg);
    478 break;
    479
    480 case 'boolean':
    481 argDesc = arg ? 'true' : 'false';
    482 break;
    483
    484 case 'function':
    485 argDesc = goog.debug.getFunctionName(arg);
    486 argDesc = argDesc ? argDesc : '[fn]';
    487 break;
    488
    489 case 'undefined':
    490 default:
    491 argDesc = typeof arg;
    492 break;
    493 }
    494
    495 if (argDesc.length > 40) {
    496 argDesc = argDesc.substr(0, 40) + '...';
    497 }
    498 sb.push(argDesc);
    499 }
    500 visited.push(fn);
    501 sb.push(')\n');
    502 /** @preserveTry */
    503 try {
    504 sb.push(goog.debug.getStacktraceHelper_(fn.caller, visited));
    505 } catch (e) {
    506 sb.push('[exception trying to get caller]\n');
    507 }
    508
    509 } else if (fn) {
    510 sb.push('[...long stack...]');
    511 } else {
    512 sb.push('[end]');
    513 }
    514 return sb.join('');
    515};
    516
    517
    518/**
    519 * Set a custom function name resolver.
    520 * @param {function(Function): string} resolver Resolves functions to their
    521 * names.
    522 */
    523goog.debug.setFunctionResolver = function(resolver) {
    524 goog.debug.fnNameResolver_ = resolver;
    525};
    526
    527
    528/**
    529 * Gets a function name
    530 * @param {Function} fn Function to get name of.
    531 * @return {string} Function's name.
    532 */
    533goog.debug.getFunctionName = function(fn) {
    534 if (goog.debug.fnNameCache_[fn]) {
    535 return goog.debug.fnNameCache_[fn];
    536 }
    537 if (goog.debug.fnNameResolver_) {
    538 var name = goog.debug.fnNameResolver_(fn);
    539 if (name) {
    540 goog.debug.fnNameCache_[fn] = name;
    541 return name;
    542 }
    543 }
    544
    545 // Heuristically determine function name based on code.
    546 var functionSource = String(fn);
    547 if (!goog.debug.fnNameCache_[functionSource]) {
    548 var matches = /function ([^\(]+)/.exec(functionSource);
    549 if (matches) {
    550 var method = matches[1];
    551 goog.debug.fnNameCache_[functionSource] = method;
    552 } else {
    553 goog.debug.fnNameCache_[functionSource] = '[Anonymous]';
    554 }
    555 }
    556
    557 return goog.debug.fnNameCache_[functionSource];
    558};
    559
    560
    561/**
    562 * Makes whitespace visible by replacing it with printable characters.
    563 * This is useful in finding diffrences between the expected and the actual
    564 * output strings of a testcase.
    565 * @param {string} string whose whitespace needs to be made visible.
    566 * @return {string} string whose whitespace is made visible.
    567 */
    568goog.debug.makeWhitespaceVisible = function(string) {
    569 return string.replace(/ /g, '[_]')
    570 .replace(/\f/g, '[f]')
    571 .replace(/\n/g, '[n]\n')
    572 .replace(/\r/g, '[r]')
    573 .replace(/\t/g, '[t]');
    574};
    575
    576
    577/**
    578 * Hash map for storing function names that have already been looked up.
    579 * @type {Object}
    580 * @private
    581 */
    582goog.debug.fnNameCache_ = {};
    583
    584
    585/**
    586 * Resolves functions to their names. Resolved function names will be cached.
    587 * @type {function(Function):string}
    588 * @private
    589 */
    590goog.debug.fnNameResolver_;
    \ No newline at end of file +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 index f9c3433..9bbf8bb 100644 --- a/docs/source/lib/goog/debug/entrypointregistry.js.src.html +++ b/docs/source/lib/goog/debug/entrypointregistry.js.src.html @@ -1 +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 +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 37dee83..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 // 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};
    50goog.inherits(goog.debug.Error, Error);
    51
    52
    53/** @override */
    54goog.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 index 901499c..0508996 100644 --- a/docs/source/lib/goog/debug/logbuffer.js.src.html +++ b/docs/source/lib/goog/debug/logbuffer.js.src.html @@ -1 +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 +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 index a0c8db1..031db67 100644 --- a/docs/source/lib/goog/debug/logger.js.src.html +++ b/docs/source/lib/goog/debug/logger.js.src.html @@ -1 +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(
    548 level, msg, opt_exception, goog.debug.Logger.prototype.log));
    549 }
    550};
    551
    552
    553/**
    554 * Creates a new log record and adds the exception (if present) to it.
    555 * @param {goog.debug.Logger.Level} level One of the level identifiers.
    556 * @param {string} msg The string message.
    557 * @param {Error|Object=} opt_exception An exception associated with the
    558 * message.
    559 * @param {Function=} opt_fnStackContext A function to use as the base
    560 * of the stack trace used in the log record.
    561 * @return {!goog.debug.LogRecord} A log record.
    562 * @suppress {es5Strict}
    563 */
    564goog.debug.Logger.prototype.getLogRecord = function(
    565 level, msg, opt_exception, opt_fnStackContext) {
    566 if (goog.debug.LogBuffer.isBufferingEnabled()) {
    567 var logRecord =
    568 goog.debug.LogBuffer.getInstance().addRecord(level, msg, this.name_);
    569 } else {
    570 logRecord = new goog.debug.LogRecord(level, String(msg), this.name_);
    571 }
    572 if (opt_exception) {
    573 var context;
    574 if (goog.STRICT_MODE_COMPATIBLE) {
    575 context = opt_fnStackContext || goog.debug.Logger.prototype.getLogRecord;
    576 } else {
    577 context = opt_fnStackContext || arguments.callee.caller;
    578 }
    579
    580 logRecord.setException(opt_exception);
    581 logRecord.setExceptionText(
    582 goog.debug.exposeException(opt_exception,
    583 opt_fnStackContext || goog.debug.Logger.prototype.getLogRecord));
    584 }
    585 return logRecord;
    586};
    587
    588
    589/**
    590 * Logs a message at the Logger.Level.SHOUT level.
    591 * If the logger is currently enabled for the given message level then the
    592 * given message is forwarded to all the registered output Handler objects.
    593 * @param {goog.debug.Loggable} msg The message to log.
    594 * @param {Error=} opt_exception An exception associated with the message.
    595 */
    596goog.debug.Logger.prototype.shout = function(msg, opt_exception) {
    597 if (goog.debug.LOGGING_ENABLED) {
    598 this.log(goog.debug.Logger.Level.SHOUT, msg, opt_exception);
    599 }
    600};
    601
    602
    603/**
    604 * Logs a message at the Logger.Level.SEVERE level.
    605 * If the logger is currently enabled for the given message level then the
    606 * given message is forwarded to all the registered output Handler objects.
    607 * @param {goog.debug.Loggable} msg The message to log.
    608 * @param {Error=} opt_exception An exception associated with the message.
    609 */
    610goog.debug.Logger.prototype.severe = function(msg, opt_exception) {
    611 if (goog.debug.LOGGING_ENABLED) {
    612 this.log(goog.debug.Logger.Level.SEVERE, msg, opt_exception);
    613 }
    614};
    615
    616
    617/**
    618 * Logs a message at the Logger.Level.WARNING level.
    619 * If the logger is currently enabled for the given message level then the
    620 * given message is forwarded to all the registered output Handler objects.
    621 * @param {goog.debug.Loggable} msg The message to log.
    622 * @param {Error=} opt_exception An exception associated with the message.
    623 */
    624goog.debug.Logger.prototype.warning = function(msg, opt_exception) {
    625 if (goog.debug.LOGGING_ENABLED) {
    626 this.log(goog.debug.Logger.Level.WARNING, msg, opt_exception);
    627 }
    628};
    629
    630
    631/**
    632 * Logs a message at the Logger.Level.INFO level.
    633 * If the logger is currently enabled for the given message level then the
    634 * given message is forwarded to all the registered output Handler objects.
    635 * @param {goog.debug.Loggable} msg The message to log.
    636 * @param {Error=} opt_exception An exception associated with the message.
    637 */
    638goog.debug.Logger.prototype.info = function(msg, opt_exception) {
    639 if (goog.debug.LOGGING_ENABLED) {
    640 this.log(goog.debug.Logger.Level.INFO, msg, opt_exception);
    641 }
    642};
    643
    644
    645/**
    646 * Logs a message at the Logger.Level.CONFIG level.
    647 * If the logger is currently enabled for the given message level then the
    648 * given message is forwarded to all the registered output Handler objects.
    649 * @param {goog.debug.Loggable} msg The message to log.
    650 * @param {Error=} opt_exception An exception associated with the message.
    651 */
    652goog.debug.Logger.prototype.config = function(msg, opt_exception) {
    653 if (goog.debug.LOGGING_ENABLED) {
    654 this.log(goog.debug.Logger.Level.CONFIG, msg, opt_exception);
    655 }
    656};
    657
    658
    659/**
    660 * Logs a message at the Logger.Level.FINE level.
    661 * If the logger is currently enabled for the given message level then the
    662 * given message is forwarded to all the registered output Handler objects.
    663 * @param {goog.debug.Loggable} msg The message to log.
    664 * @param {Error=} opt_exception An exception associated with the message.
    665 */
    666goog.debug.Logger.prototype.fine = function(msg, opt_exception) {
    667 if (goog.debug.LOGGING_ENABLED) {
    668 this.log(goog.debug.Logger.Level.FINE, msg, opt_exception);
    669 }
    670};
    671
    672
    673/**
    674 * Logs a message at the Logger.Level.FINER level.
    675 * If the logger is currently enabled for the given message level then the
    676 * given message is forwarded to all the registered output Handler objects.
    677 * @param {goog.debug.Loggable} msg The message to log.
    678 * @param {Error=} opt_exception An exception associated with the message.
    679 */
    680goog.debug.Logger.prototype.finer = function(msg, opt_exception) {
    681 if (goog.debug.LOGGING_ENABLED) {
    682 this.log(goog.debug.Logger.Level.FINER, msg, opt_exception);
    683 }
    684};
    685
    686
    687/**
    688 * Logs a message at the Logger.Level.FINEST level.
    689 * If the logger is currently enabled for the given message level then the
    690 * given message is forwarded to all the registered output Handler objects.
    691 * @param {goog.debug.Loggable} msg The message to log.
    692 * @param {Error=} opt_exception An exception associated with the message.
    693 */
    694goog.debug.Logger.prototype.finest = function(msg, opt_exception) {
    695 if (goog.debug.LOGGING_ENABLED) {
    696 this.log(goog.debug.Logger.Level.FINEST, msg, opt_exception);
    697 }
    698};
    699
    700
    701/**
    702 * Logs a LogRecord. If the logger is currently enabled for the
    703 * given message level then the given message is forwarded to all the
    704 * registered output Handler objects.
    705 * @param {goog.debug.LogRecord} logRecord A log record to log.
    706 */
    707goog.debug.Logger.prototype.logRecord = function(logRecord) {
    708 if (goog.debug.LOGGING_ENABLED && this.isLoggable(logRecord.getLevel())) {
    709 this.doLogRecord_(logRecord);
    710 }
    711};
    712
    713
    714/**
    715 * Logs a LogRecord.
    716 * @param {goog.debug.LogRecord} logRecord A log record to log.
    717 * @private
    718 */
    719goog.debug.Logger.prototype.doLogRecord_ = function(logRecord) {
    720 goog.debug.Logger.logToProfilers('log:' + logRecord.getMessage());
    721 if (goog.debug.Logger.ENABLE_HIERARCHY) {
    722 var target = this;
    723 while (target) {
    724 target.callPublish_(logRecord);
    725 target = target.getParent();
    726 }
    727 } else {
    728 for (var i = 0, handler; handler = goog.debug.Logger.rootHandlers_[i++]; ) {
    729 handler(logRecord);
    730 }
    731 }
    732};
    733
    734
    735/**
    736 * Calls the handlers for publish.
    737 * @param {goog.debug.LogRecord} logRecord The log record to publish.
    738 * @private
    739 */
    740goog.debug.Logger.prototype.callPublish_ = function(logRecord) {
    741 if (this.handlers_) {
    742 for (var i = 0, handler; handler = this.handlers_[i]; i++) {
    743 handler(logRecord);
    744 }
    745 }
    746};
    747
    748
    749/**
    750 * Sets the parent of this logger. This is used for setting up the logger tree.
    751 * @param {goog.debug.Logger} parent The parent logger.
    752 * @private
    753 */
    754goog.debug.Logger.prototype.setParent_ = function(parent) {
    755 this.parent_ = parent;
    756};
    757
    758
    759/**
    760 * Adds a child to this logger. This is used for setting up the logger tree.
    761 * @param {string} name The leaf name of the child.
    762 * @param {goog.debug.Logger} logger The child logger.
    763 * @private
    764 */
    765goog.debug.Logger.prototype.addChild_ = function(name, logger) {
    766 this.getChildren()[name] = logger;
    767};
    768
    769
    770/**
    771 * There is a single global LogManager object that is used to maintain a set of
    772 * shared state about Loggers and log services. This is loosely based on the
    773 * java class java.util.logging.LogManager.
    774 */
    775goog.debug.LogManager = {};
    776
    777
    778/**
    779 * Map of logger names to logger objects.
    780 *
    781 * @type {!Object.<string, !goog.debug.Logger>}
    782 * @private
    783 */
    784goog.debug.LogManager.loggers_ = {};
    785
    786
    787/**
    788 * The root logger which is the root of the logger tree.
    789 * @type {goog.debug.Logger}
    790 * @private
    791 */
    792goog.debug.LogManager.rootLogger_ = null;
    793
    794
    795/**
    796 * Initializes the LogManager if not already initialized.
    797 */
    798goog.debug.LogManager.initialize = function() {
    799 if (!goog.debug.LogManager.rootLogger_) {
    800 goog.debug.LogManager.rootLogger_ = new goog.debug.Logger(
    801 goog.debug.Logger.ROOT_LOGGER_NAME);
    802 goog.debug.LogManager.loggers_[goog.debug.Logger.ROOT_LOGGER_NAME] =
    803 goog.debug.LogManager.rootLogger_;
    804 goog.debug.LogManager.rootLogger_.setLevel(goog.debug.Logger.Level.CONFIG);
    805 }
    806};
    807
    808
    809/**
    810 * Returns all the loggers.
    811 * @return {!Object.<string, !goog.debug.Logger>} Map of logger names to logger
    812 * objects.
    813 */
    814goog.debug.LogManager.getLoggers = function() {
    815 return goog.debug.LogManager.loggers_;
    816};
    817
    818
    819/**
    820 * Returns the root of the logger tree namespace, the logger with the empty
    821 * string as its name.
    822 *
    823 * @return {!goog.debug.Logger} The root logger.
    824 */
    825goog.debug.LogManager.getRoot = function() {
    826 goog.debug.LogManager.initialize();
    827 return /** @type {!goog.debug.Logger} */ (goog.debug.LogManager.rootLogger_);
    828};
    829
    830
    831/**
    832 * Finds a named logger.
    833 *
    834 * @param {string} name A name for the logger. This should be a dot-separated
    835 * name and should normally be based on the package name or class name of the
    836 * subsystem, such as goog.net.BrowserChannel.
    837 * @return {!goog.debug.Logger} The named logger.
    838 */
    839goog.debug.LogManager.getLogger = function(name) {
    840 goog.debug.LogManager.initialize();
    841 var ret = goog.debug.LogManager.loggers_[name];
    842 return ret || goog.debug.LogManager.createLogger_(name);
    843};
    844
    845
    846/**
    847 * Creates a function that can be passed to goog.debug.catchErrors. The function
    848 * will log all reported errors using the given logger.
    849 * @param {goog.debug.Logger=} opt_logger The logger to log the errors to.
    850 * Defaults to the root logger.
    851 * @return {function(Object)} The created function.
    852 */
    853goog.debug.LogManager.createFunctionForCatchErrors = function(opt_logger) {
    854 return function(info) {
    855 var logger = opt_logger || goog.debug.LogManager.getRoot();
    856 logger.severe('Error: ' + info.message + ' (' + info.fileName +
    857 ' @ Line: ' + info.line + ')');
    858 };
    859};
    860
    861
    862/**
    863 * Creates the named logger. Will also create the parents of the named logger
    864 * if they don't yet exist.
    865 * @param {string} name The name of the logger.
    866 * @return {!goog.debug.Logger} The named logger.
    867 * @private
    868 */
    869goog.debug.LogManager.createLogger_ = function(name) {
    870 // find parent logger
    871 var logger = new goog.debug.Logger(name);
    872 if (goog.debug.Logger.ENABLE_HIERARCHY) {
    873 var lastDotIndex = name.lastIndexOf('.');
    874 var parentName = name.substr(0, lastDotIndex);
    875 var leafName = name.substr(lastDotIndex + 1);
    876 var parentLogger = goog.debug.LogManager.getLogger(parentName);
    877
    878 // tell the parent about the child and the child about the parent
    879 parentLogger.addChild_(leafName, logger);
    880 logger.setParent_(parentLogger);
    881 }
    882
    883 goog.debug.LogManager.loggers_[name] = logger;
    884 return logger;
    885};
    \ No newline at end of file +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 index c3b9b67..fc8deb0 100644 --- a/docs/source/lib/goog/debug/logrecord.js.src.html +++ b/docs/source/lib/goog/debug/logrecord.js.src.html @@ -1 +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 * Exception text associated with the record
    95 * @type {?string}
    96 * @private
    97 */
    98goog.debug.LogRecord.prototype.exceptionText_ = null;
    99
    100
    101/**
    102 * @define {boolean} Whether to enable log sequence numbers.
    103 */
    104goog.define('goog.debug.LogRecord.ENABLE_SEQUENCE_NUMBERS', true);
    105
    106
    107/**
    108 * A sequence counter for assigning increasing sequence numbers to LogRecord
    109 * objects.
    110 * @type {number}
    111 * @private
    112 */
    113goog.debug.LogRecord.nextSequenceNumber_ = 0;
    114
    115
    116/**
    117 * Sets all fields of the log record.
    118 * @param {goog.debug.Logger.Level} level One of the level identifiers.
    119 * @param {string} msg The string message.
    120 * @param {string} loggerName The name of the source logger.
    121 * @param {number=} opt_time Time this log record was created if other than now.
    122 * If 0, we use #goog.now.
    123 * @param {number=} opt_sequenceNumber Sequence number of this log record. This
    124 * should only be passed in when restoring a log record from persistence.
    125 */
    126goog.debug.LogRecord.prototype.reset = function(level, msg, loggerName,
    127 opt_time, opt_sequenceNumber) {
    128 if (goog.debug.LogRecord.ENABLE_SEQUENCE_NUMBERS) {
    129 this.sequenceNumber_ = typeof opt_sequenceNumber == 'number' ?
    130 opt_sequenceNumber : goog.debug.LogRecord.nextSequenceNumber_++;
    131 }
    132
    133 this.time_ = opt_time || goog.now();
    134 this.level_ = level;
    135 this.msg_ = msg;
    136 this.loggerName_ = loggerName;
    137 delete this.exception_;
    138 delete this.exceptionText_;
    139};
    140
    141
    142/**
    143 * Get the source Logger's name.
    144 *
    145 * @return {string} source logger name (may be null).
    146 */
    147goog.debug.LogRecord.prototype.getLoggerName = function() {
    148 return this.loggerName_;
    149};
    150
    151
    152/**
    153 * Get the exception that is part of the log record.
    154 *
    155 * @return {Object} the exception.
    156 */
    157goog.debug.LogRecord.prototype.getException = function() {
    158 return this.exception_;
    159};
    160
    161
    162/**
    163 * Set the exception that is part of the log record.
    164 *
    165 * @param {Object} exception the exception.
    166 */
    167goog.debug.LogRecord.prototype.setException = function(exception) {
    168 this.exception_ = exception;
    169};
    170
    171
    172/**
    173 * Get the exception text that is part of the log record.
    174 *
    175 * @return {?string} Exception text.
    176 */
    177goog.debug.LogRecord.prototype.getExceptionText = function() {
    178 return this.exceptionText_;
    179};
    180
    181
    182/**
    183 * Set the exception text that is part of the log record.
    184 *
    185 * @param {string} text The exception text.
    186 */
    187goog.debug.LogRecord.prototype.setExceptionText = function(text) {
    188 this.exceptionText_ = text;
    189};
    190
    191
    192/**
    193 * Get the source Logger's name.
    194 *
    195 * @param {string} loggerName source logger name (may be null).
    196 */
    197goog.debug.LogRecord.prototype.setLoggerName = function(loggerName) {
    198 this.loggerName_ = loggerName;
    199};
    200
    201
    202/**
    203 * Get the logging message level, for example Level.SEVERE.
    204 * @return {goog.debug.Logger.Level} the logging message level.
    205 */
    206goog.debug.LogRecord.prototype.getLevel = function() {
    207 return this.level_;
    208};
    209
    210
    211/**
    212 * Set the logging message level, for example Level.SEVERE.
    213 * @param {goog.debug.Logger.Level} level the logging message level.
    214 */
    215goog.debug.LogRecord.prototype.setLevel = function(level) {
    216 this.level_ = level;
    217};
    218
    219
    220/**
    221 * Get the "raw" log message, before localization or formatting.
    222 *
    223 * @return {string} the raw message string.
    224 */
    225goog.debug.LogRecord.prototype.getMessage = function() {
    226 return this.msg_;
    227};
    228
    229
    230/**
    231 * Set the "raw" log message, before localization or formatting.
    232 *
    233 * @param {string} msg the raw message string.
    234 */
    235goog.debug.LogRecord.prototype.setMessage = function(msg) {
    236 this.msg_ = msg;
    237};
    238
    239
    240/**
    241 * Get event time in milliseconds since 1970.
    242 *
    243 * @return {number} event time in millis since 1970.
    244 */
    245goog.debug.LogRecord.prototype.getMillis = function() {
    246 return this.time_;
    247};
    248
    249
    250/**
    251 * Set event time in milliseconds since 1970.
    252 *
    253 * @param {number} time event time in millis since 1970.
    254 */
    255goog.debug.LogRecord.prototype.setMillis = function(time) {
    256 this.time_ = time;
    257};
    258
    259
    260/**
    261 * Get the sequence number.
    262 * <p>
    263 * Sequence numbers are normally assigned in the LogRecord
    264 * constructor, which assigns unique sequence numbers to
    265 * each new LogRecord in increasing order.
    266 * @return {number} the sequence number.
    267 */
    268goog.debug.LogRecord.prototype.getSequenceNumber = function() {
    269 return this.sequenceNumber_;
    270};
    271
    \ No newline at end of file +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 0658841..13506a0 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("../webdriver/test/capabilities_test.js", [], ["goog.testing.jsunit","webdriver.Capabilities"]);
    3goog.addDependency("testing/jsunit.js", ["goog.testing.jsunit"], ["goog.testing.TestCase","goog.testing.TestRunner"]);
    4goog.addDependency("testing/testcase.js", ["goog.testing.TestCase","goog.testing.TestCase.Error","goog.testing.TestCase.Order","goog.testing.TestCase.Result","goog.testing.TestCase.Test"], ["goog.object","goog.testing.asserts","goog.testing.stacktrace"]);
    5goog.addDependency("object/object.js", ["goog.object"], ["goog.array"]);
    6goog.addDependency("array/array.js", ["goog.array","goog.array.ArrayLike"], ["goog.asserts"]);
    7goog.addDependency("asserts/asserts.js", ["goog.asserts","goog.asserts.AssertionError"], ["goog.debug.Error","goog.dom.NodeType","goog.string"]);
    8goog.addDependency("debug/error.js", ["goog.debug.Error"], []);
    9goog.addDependency("dom/nodetype.js", ["goog.dom.NodeType"], []);
    10goog.addDependency("string/string.js", ["goog.string","goog.string.Unicode"], []);
    11goog.addDependency("testing/asserts.js", ["goog.testing.JsUnitException","goog.testing.asserts"], ["goog.testing.stacktrace"]);
    12goog.addDependency("testing/stacktrace.js", ["goog.testing.stacktrace","goog.testing.stacktrace.Frame"], []);
    13goog.addDependency("testing/testrunner.js", ["goog.testing.TestRunner"], ["goog.testing.TestCase"]);
    14goog.addDependency("../webdriver/capabilities.js", ["webdriver.Browser","webdriver.Capabilities","webdriver.Capability","webdriver.ProxyConfig"], ["webdriver.logging.Preferences"]);
    15goog.addDependency("../webdriver/logging.js", ["webdriver.logging","webdriver.logging.Preferences"], ["goog.object"]);
    16goog.addDependency("../webdriver/test/events_test.js", [], ["goog.testing.jsunit","webdriver.EventEmitter"]);
    17goog.addDependency("../webdriver/events.js", ["webdriver.EventEmitter"], []);
    18goog.addDependency("../webdriver/test/locators_test.js", [], ["goog.json","goog.testing.jsunit","webdriver.By","webdriver.Locator","webdriver.Locator.Strategy","webdriver.test.testutil"]);
    19goog.addDependency("json/json.js", ["goog.json","goog.json.Replacer","goog.json.Reviver","goog.json.Serializer"], []);
    20goog.addDependency("../webdriver/locators.js", ["webdriver.By","webdriver.Locator","webdriver.Locator.Strategy"], ["goog.array","goog.object","goog.string"]);
    21goog.addDependency("../webdriver/test/testutil.js", ["webdriver.test.testutil"], ["goog.array","goog.json","goog.string","goog.testing.MockClock","goog.testing.recordFunction","webdriver.stacktrace"]);
    22goog.addDependency("testing/mockclock.js", ["goog.testing.MockClock"], ["goog.Disposable","goog.async.run","goog.testing.PropertyReplacer","goog.testing.events","goog.testing.events.Event","goog.testing.watchers"]);
    23goog.addDependency("disposable/disposable.js", ["goog.Disposable","goog.dispose","goog.disposeAll"], ["goog.disposable.IDisposable"]);
    24goog.addDependency("disposable/idisposable.js", ["goog.disposable.IDisposable"], []);
    25goog.addDependency("async/run.js", ["goog.async.run"], ["goog.async.nextTick","goog.async.throwException","goog.testing.watchers"]);
    26goog.addDependency("async/nexttick.js", ["goog.async.nextTick","goog.async.throwException"], ["goog.debug.entryPointRegistry","goog.functions","goog.labs.userAgent.browser"]);
    27goog.addDependency("debug/entrypointregistry.js", ["goog.debug.EntryPointMonitor","goog.debug.entryPointRegistry"], ["goog.asserts"]);
    28goog.addDependency("functions/functions.js", ["goog.functions"], []);
    29goog.addDependency("labs/useragent/browser.js", ["goog.labs.userAgent.browser"], ["goog.array","goog.labs.userAgent.util","goog.object","goog.string"]);
    30goog.addDependency("labs/useragent/util.js", ["goog.labs.userAgent.util"], ["goog.string"]);
    31goog.addDependency("testing/watchers.js", ["goog.testing.watchers"], []);
    32goog.addDependency("testing/propertyreplacer.js", ["goog.testing.PropertyReplacer"], ["goog.testing.ObjectPropertyString","goog.userAgent"]);
    33goog.addDependency("testing/objectpropertystring.js", ["goog.testing.ObjectPropertyString"], []);
    34goog.addDependency("useragent/useragent.js", ["goog.userAgent"], ["goog.labs.userAgent.browser","goog.labs.userAgent.engine","goog.labs.userAgent.util","goog.string"]);
    35goog.addDependency("labs/useragent/engine.js", ["goog.labs.userAgent.engine"], ["goog.array","goog.labs.userAgent.util","goog.string"]);
    36goog.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"]);
    37goog.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"]);
    38goog.addDependency("events/browserevent.js", ["goog.events.BrowserEvent","goog.events.BrowserEvent.MouseButton"], ["goog.events.BrowserFeature","goog.events.Event","goog.events.EventType","goog.reflect","goog.userAgent"]);
    39goog.addDependency("events/browserfeature.js", ["goog.events.BrowserFeature"], ["goog.userAgent"]);
    40goog.addDependency("events/event.js", ["goog.events.Event","goog.events.EventLike"], ["goog.Disposable","goog.events.EventId"]);
    41goog.addDependency("events/eventid.js", ["goog.events.EventId"], []);
    42goog.addDependency("events/eventtype.js", ["goog.events.EventType"], ["goog.userAgent"]);
    43goog.addDependency("reflect/reflect.js", ["goog.reflect"], []);
    44goog.addDependency("events/listenable.js", ["goog.events.Listenable","goog.events.ListenableKey"], ["goog.events.EventId"]);
    45goog.addDependency("events/listenermap.js", ["goog.events.ListenerMap"], ["goog.array","goog.events.Listener","goog.object"]);
    46goog.addDependency("events/listener.js", ["goog.events.Listener"], ["goog.events.ListenableKey"]);
    47goog.addDependency("events/eventtarget.js", ["goog.events.EventTarget"], ["goog.Disposable","goog.asserts","goog.events","goog.events.Event","goog.events.Listenable","goog.events.ListenerMap","goog.object"]);
    48goog.addDependency("events/keycodes.js", ["goog.events.KeyCodes"], ["goog.userAgent"]);
    49goog.addDependency("style/style.js", ["goog.style"], ["goog.array","goog.asserts","goog.dom","goog.dom.NodeType","goog.dom.vendor","goog.math.Box","goog.math.Coordinate","goog.math.Rect","goog.math.Size","goog.object","goog.string","goog.userAgent"]);
    50goog.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.math.Coordinate","goog.math.Size","goog.object","goog.string","goog.userAgent"]);
    51goog.addDependency("dom/browserfeature.js", ["goog.dom.BrowserFeature"], ["goog.userAgent"]);
    52goog.addDependency("dom/tagname.js", ["goog.dom.TagName"], []);
    53goog.addDependency("math/coordinate.js", ["goog.math.Coordinate"], ["goog.math"]);
    54goog.addDependency("math/math.js", ["goog.math"], ["goog.array","goog.asserts"]);
    55goog.addDependency("math/size.js", ["goog.math.Size"], []);
    56goog.addDependency("dom/vendor.js", ["goog.dom.vendor"], ["goog.string","goog.userAgent"]);
    57goog.addDependency("math/box.js", ["goog.math.Box"], ["goog.math.Coordinate"]);
    58goog.addDependency("math/rect.js", ["goog.math.Rect"], ["goog.math.Box","goog.math.Coordinate","goog.math.Size"]);
    59goog.addDependency("testing/recordfunction.js", ["goog.testing.FunctionCall","goog.testing.recordConstructor","goog.testing.recordFunction"], ["goog.testing.asserts"]);
    60goog.addDependency("../webdriver/stacktrace.js", ["webdriver.stacktrace","webdriver.stacktrace.Snapshot"], ["goog.array","goog.string","goog.userAgent"]);
    61goog.addDependency("../webdriver/test/logging_test.js", [], ["goog.debug.LogRecord","goog.debug.Logger","goog.testing.jsunit","webdriver.logging"]);
    62goog.addDependency("debug/logrecord.js", ["goog.debug.LogRecord"], []);
    63goog.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"]);
    64goog.addDependency("debug/debug.js", ["goog.debug"], ["goog.array","goog.string","goog.structs.Set","goog.userAgent"]);
    65goog.addDependency("structs/set.js", ["goog.structs.Set"], ["goog.structs","goog.structs.Collection","goog.structs.Map"]);
    66goog.addDependency("structs/structs.js", ["goog.structs"], ["goog.array","goog.object"]);
    67goog.addDependency("structs/collection.js", ["goog.structs.Collection"], []);
    68goog.addDependency("structs/map.js", ["goog.structs.Map"], ["goog.iter.Iterator","goog.iter.StopIteration","goog.object"]);
    69goog.addDependency("iter/iter.js", ["goog.iter","goog.iter.Iterable","goog.iter.Iterator","goog.iter.StopIteration"], ["goog.array","goog.asserts","goog.functions","goog.math"]);
    70goog.addDependency("debug/logbuffer.js", ["goog.debug.LogBuffer"], ["goog.asserts","goog.debug.LogRecord"]);
    71goog.addDependency("../webdriver/test/process_test.js", [], ["goog.testing.PropertyReplacer","goog.testing.jsunit","webdriver.process","webdriver.test.testutil"]);
    72goog.addDependency("../webdriver/process.js", ["webdriver.process"], ["goog.Uri","goog.array","goog.json"]);
    73goog.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"]);
    74goog.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"]);
    75goog.addDependency("../webdriver/test/promise_flow_test.js", [], ["goog.array","goog.functions","goog.string","goog.testing.FunctionMock","goog.testing.jsunit","goog.userAgent","webdriver.promise.ControlFlow","webdriver.stacktrace.Snapshot","webdriver.test.testutil","webdriver.testing.promise.FlowTester"]);
    76goog.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.MockInterface","goog.testing.PropertyReplacer","goog.testing.StrictMock"]);
    77goog.addDependency("testing/loosemock.js", ["goog.testing.LooseExpectationCollection","goog.testing.LooseMock"], ["goog.array","goog.structs.Map","goog.testing.Mock"]);
    78goog.addDependency("testing/mock.js", ["goog.testing.Mock","goog.testing.MockExpectation"], ["goog.array","goog.object","goog.testing.JsUnitException","goog.testing.MockInterface","goog.testing.mockmatchers"]);
    79goog.addDependency("testing/mockinterface.js", ["goog.testing.MockInterface"], []);
    80goog.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"]);
    81goog.addDependency("testing/strictmock.js", ["goog.testing.StrictMock"], ["goog.array","goog.testing.Mock"]);
    82goog.addDependency("../webdriver/promise.js", ["webdriver.promise","webdriver.promise.ControlFlow","webdriver.promise.ControlFlow.Timer","webdriver.promise.Deferred","webdriver.promise.Promise","webdriver.promise.Thenable"], ["goog.array","goog.debug.Error","goog.object","webdriver.EventEmitter","webdriver.stacktrace.Snapshot"]);
    83goog.addDependency("../webdriver/testing/flowtester.js", ["webdriver.testing.Clock","webdriver.testing.promise.FlowTester"], ["goog.array","webdriver.promise.ControlFlow"]);
    84goog.addDependency("../webdriver/test/promise_test.js", [], ["goog.testing.jsunit","webdriver.promise","webdriver.promise.Deferred","webdriver.test.testutil"]);
    85goog.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"]);
    86goog.addDependency("../atoms/error.js", ["bot.Error","bot.ErrorCode"], []);
    87goog.addDependency("../webdriver/test/test_bootstrap.js", [], []);
    88goog.addDependency("../webdriver/test/testutil_test.js", [], ["goog.testing.jsunit","webdriver.test.testutil"]);
    89goog.addDependency("../webdriver/test/webdriver_test.js", [], ["bot.ErrorCode","goog.functions","goog.json","goog.testing.PropertyReplacer","goog.testing.MockControl","goog.testing.jsunit","webdriver.Capabilities","webdriver.Command","webdriver.CommandExecutor","webdriver.CommandName","webdriver.WebDriver","webdriver.Session","webdriver.logging","webdriver.promise","webdriver.promise.ControlFlow","webdriver.promise.Deferred","webdriver.promise.Promise","webdriver.test.testutil","webdriver.testing.promise.FlowTester"]);
    90goog.addDependency("testing/mockcontrol.js", ["goog.testing.MockControl"], ["goog.array","goog.testing","goog.testing.LooseMock","goog.testing.StrictMock"]);
    91goog.addDependency("../webdriver/command.js", ["webdriver.Command","webdriver.CommandExecutor","webdriver.CommandName"], []);
    92goog.addDependency("../webdriver/webdriver.js", ["webdriver.Alert","webdriver.AlertPromise","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.Session","webdriver.logging","webdriver.promise"]);
    93goog.addDependency("../atoms/response.js", ["bot.response","bot.response.ResponseObject"], ["bot.Error","bot.ErrorCode"]);
    94goog.addDependency("../webdriver/actionsequence.js", ["webdriver.ActionSequence"], ["goog.array","webdriver.Button","webdriver.Command","webdriver.CommandName","webdriver.Key"]);
    95goog.addDependency("../webdriver/button.js", ["webdriver.Button"], []);
    96goog.addDependency("../webdriver/key.js", ["webdriver.Key"], []);
    97goog.addDependency("../webdriver/session.js", ["webdriver.Session"], ["webdriver.Capabilities"]);
    98goog.addDependency("../webdriver/test/http/corsclient_test.js", [], ["goog.json","goog.testing.MockControl","goog.testing.PropertyReplacer","goog.testing.jsunit","webdriver.http.CorsClient","webdriver.http.Request","webdriver.test.testutil"]);
    99goog.addDependency("../webdriver/http/corsclient.js", ["webdriver.http.CorsClient"], ["goog.json","webdriver.http.Response"]);
    100goog.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"]);
    101goog.addDependency("../webdriver/test/http/http_test.js", [], ["bot.ErrorCode","goog.Uri","goog.json","goog.testing.MockControl","goog.testing.jsunit","webdriver.Command","webdriver.http.Client","webdriver.http.Executor","webdriver.promise","webdriver.test.testutil"]);
    102goog.addDependency("../webdriver/test/http/xhrclient_test.js", [], ["goog.json","goog.testing.MockControl","goog.testing.PropertyReplacer","goog.testing.jsunit","webdriver.http.Request","webdriver.http.XhrClient","webdriver.promise","webdriver.test.testutil"]);
    103goog.addDependency("../webdriver/http/xhrclient.js", ["webdriver.http.XhrClient"], ["goog.json","goog.net.XmlHttp","webdriver.http.Response"]);
    104goog.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"]);
    105goog.addDependency("net/wrapperxmlhttpfactory.js", ["goog.net.WrapperXmlHttpFactory"], ["goog.net.XhrLike","goog.net.XmlHttpFactory"]);
    106goog.addDependency("net/xhrlike.js", ["goog.net.XhrLike"], []);
    107goog.addDependency("net/xmlhttpfactory.js", ["goog.net.XmlHttpFactory"], ["goog.net.XhrLike"]);
    108goog.addDependency("../webdriver/test/testing/asserts_test.js", [], ["goog.testing.jsunit","webdriver.test.testutil","webdriver.testing.assert","webdriver.testing.asserts"]);
    109goog.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"]);
    110goog.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"]);
    111goog.addDependency("labs/testing/matcher.js", ["goog.labs.testing.Matcher"], []);
    112goog.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"]);
    113goog.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"]);
    114goog.addDependency("labs/testing/logicmatcher.js", ["goog.labs.testing.AllOfMatcher","goog.labs.testing.AnyOfMatcher","goog.labs.testing.IsNotMatcher"], ["goog.array","goog.labs.testing.Matcher"]);
    115goog.addDependency("labs/testing/assertthat.js", ["goog.labs.testing.MatcherError","goog.labs.testing.assertThat"], ["goog.asserts","goog.debug.Error","goog.labs.testing.Matcher"]);
    116goog.addDependency("../webdriver/test/testing/client_test.js", [], ["goog.testing.MockControl","goog.testing.PropertyReplacer","goog.testing.jsunit","webdriver.testing.Client"]);
    117goog.addDependency("../webdriver/testing/client.js", ["webdriver.testing.Client"], ["goog.json","goog.net.XmlHttp"]);
    118goog.addDependency("../webdriver/test/testing/flowtester_test.js", [], ["goog.testing.MockControl","goog.testing.jsunit","webdriver.promise","webdriver.testing.Clock","webdriver.testing.promise.FlowTester"]);
    119goog.addDependency("../webdriver/test/testing/testcase_test.js", [], ["goog.testing.MockControl","goog.testing.PropertyReplacer","goog.testing.mockmatchers","goog.testing.jsunit","goog.testing.recordFunction","webdriver.test.testutil","webdriver.testing.TestCase","webdriver.testing.promise.FlowTester"]);
    120goog.addDependency("../webdriver/testing/testcase.js", ["webdriver.testing.TestCase"], ["goog.testing.TestCase","webdriver.promise.ControlFlow","webdriver.testing.asserts"]);
    121goog.addDependency("../atoms/json.js", ["bot.json"], ["bot.userAgent","goog.json","goog.userAgent"]);
    122goog.addDependency("../atoms/userAgent.js", ["bot.userAgent"], ["goog.string","goog.userAgent","goog.userAgent.product","goog.userAgent.product.isVersion"]);
    123goog.addDependency("useragent/product.js", ["goog.userAgent.product"], ["goog.userAgent"]);
    124goog.addDependency("useragent/product_isversion.js", ["goog.userAgent.product.isVersion"], ["goog.userAgent.product"]);
    125goog.addDependency("../webdriver/abstractbuilder.js", ["webdriver.AbstractBuilder"], ["webdriver.Capabilities","webdriver.process"]);
    126goog.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"]);
    127goog.addDependency("../webdriver/firefoxdomexecutor.js", ["webdriver.FirefoxDomExecutor"], ["bot.response","goog.json","goog.userAgent.product","webdriver.Command","webdriver.CommandName"]);
    128goog.addDependency("../webdriver/testing/jsunit.js", ["webdriver.testing.jsunit","webdriver.testing.jsunit.TestRunner"], ["goog.testing.TestRunner","webdriver.testing.Client","webdriver.testing.TestCase"]);
    129goog.addDependency("../webdriver/testing/window.js", ["webdriver.testing.Window"], ["goog.string","webdriver.promise.Promise"]);
    130goog.addDependency("../webdriver/test/promise_generator_test.js", ["webdriver.test.promise.generator.test"], ["goog.testing.AsyncTestCase","goog.testing.jsunit","webdriver.promise"]);
    131goog.addDependency("testing/asynctestcase.js", ["goog.testing.AsyncTestCase","goog.testing.AsyncTestCase.ControlBreakingException"], ["goog.testing.TestCase","goog.testing.TestCase.Test","goog.testing.asserts"]);
    132goog.addDependency("../webdriver/test/webdriver_generator_test.js", ["webdriver.test.WebDriver.generator.test"], ["goog.testing.AsyncTestCase","goog.testing.jsunit","webdriver.Session","webdriver.WebDriver"]);
    \ 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/events_test.js", [], ["goog.testing.jsunit","webdriver.EventEmitter"], 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","goog.testing.watchers"], 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("testing/watchers.js", ["goog.testing.watchers"], [], false);
    24goog.addDependency("promise/resolver.js", ["goog.promise.Resolver"], [], false);
    25goog.addDependency("testing/asserts.js", ["goog.testing.JsUnitException","goog.testing.asserts","goog.testing.asserts.ArrayLike"], ["goog.testing.stacktrace"], false);
    26goog.addDependency("testing/stacktrace.js", ["goog.testing.stacktrace","goog.testing.stacktrace.Frame"], [], false);
    27goog.addDependency("testing/testrunner.js", ["goog.testing.TestRunner"], ["goog.dom.TagName","goog.testing.TestCase"], false);
    28goog.addDependency("../webdriver/events.js", ["webdriver.EventEmitter"], [], false);
    29goog.addDependency("../webdriver/test/promise_test.js", [], ["goog.testing.MockClock","goog.testing.jsunit","goog.userAgent","webdriver.promise","webdriver.stacktrace","webdriver.test.testutil"], false);
    30goog.addDependency("testing/mockclock.js", ["goog.testing.MockClock"], ["goog.Disposable","goog.async.run","goog.testing.PropertyReplacer","goog.testing.events","goog.testing.events.Event","goog.testing.watchers"], false);
    31goog.addDependency("disposable/disposable.js", ["goog.Disposable","goog.dispose","goog.disposeAll"], ["goog.disposable.IDisposable"], false);
    32goog.addDependency("disposable/idisposable.js", ["goog.disposable.IDisposable"], [], false);
    33goog.addDependency("testing/propertyreplacer.js", ["goog.testing.PropertyReplacer"], ["goog.testing.ObjectPropertyString","goog.userAgent"], false);
    34goog.addDependency("testing/objectpropertystring.js", ["goog.testing.ObjectPropertyString"], [], false);
    35goog.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);
    36goog.addDependency("labs/useragent/platform.js", ["goog.labs.userAgent.platform"], ["goog.labs.userAgent.util","goog.string"], false);
    37goog.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);
    38goog.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);
    39goog.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);
    40goog.addDependency("events/browserfeature.js", ["goog.events.BrowserFeature"], ["goog.userAgent"], false);
    41goog.addDependency("events/event.js", ["goog.events.Event","goog.events.EventLike"], ["goog.Disposable","goog.events.EventId"], false);
    42goog.addDependency("events/eventid.js", ["goog.events.EventId"], [], false);
    43goog.addDependency("events/eventtype.js", ["goog.events.EventType"], ["goog.userAgent"], false);
    44goog.addDependency("reflect/reflect.js", ["goog.reflect"], [], false);
    45goog.addDependency("events/listenable.js", ["goog.events.Listenable","goog.events.ListenableKey"], ["goog.events.EventId"], false);
    46goog.addDependency("events/listenermap.js", ["goog.events.ListenerMap"], ["goog.array","goog.events.Listener","goog.object"], false);
    47goog.addDependency("events/listener.js", ["goog.events.Listener"], ["goog.events.ListenableKey"], false);
    48goog.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);
    49goog.addDependency("events/keycodes.js", ["goog.events.KeyCodes"], ["goog.userAgent"], false);
    50goog.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);
    51goog.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);
    52goog.addDependency("dom/browserfeature.js", ["goog.dom.BrowserFeature"], ["goog.userAgent"], false);
    53goog.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);
    54goog.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);
    55goog.addDependency("dom/tags.js", ["goog.dom.tags"], ["goog.object"], false);
    56goog.addDependency("html/safestyle.js", ["goog.html.SafeStyle"], ["goog.array","goog.asserts","goog.string","goog.string.Const","goog.string.TypedString"], false);
    57goog.addDependency("string/const.js", ["goog.string.Const"], ["goog.asserts","goog.string.TypedString"], false);
    58goog.addDependency("string/typedstring.js", ["goog.string.TypedString"], [], false);
    59goog.addDependency("html/safestylesheet.js", ["goog.html.SafeStyleSheet"], ["goog.array","goog.asserts","goog.string","goog.string.Const","goog.string.TypedString"], false);
    60goog.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);
    61goog.addDependency("fs/url.js", ["goog.fs.url"], [], false);
    62goog.addDependency("i18n/bidi.js", ["goog.i18n.bidi","goog.i18n.bidi.Dir","goog.i18n.bidi.DirectionalString","goog.i18n.bidi.Format"], [], false);
    63goog.addDependency("html/trustedresourceurl.js", ["goog.html.TrustedResourceUrl"], ["goog.asserts","goog.i18n.bidi.Dir","goog.i18n.bidi.DirectionalString","goog.string.Const","goog.string.TypedString"], false);
    64goog.addDependency("math/coordinate.js", ["goog.math.Coordinate"], ["goog.math"], false);
    65goog.addDependency("math/math.js", ["goog.math"], ["goog.array","goog.asserts"], false);
    66goog.addDependency("math/size.js", ["goog.math.Size"], [], false);
    67goog.addDependency("dom/vendor.js", ["goog.dom.vendor"], ["goog.string","goog.userAgent"], false);
    68goog.addDependency("math/box.js", ["goog.math.Box"], ["goog.math.Coordinate"], false);
    69goog.addDependency("math/rect.js", ["goog.math.Rect"], ["goog.math.Box","goog.math.Coordinate","goog.math.Size"], false);
    70goog.addDependency("../webdriver/promise.js", ["webdriver.promise"], ["goog.array","goog.asserts","goog.async.run","goog.async.throwException","goog.debug.Error","goog.object","webdriver.EventEmitter","webdriver.stacktrace"], true);
    71goog.addDependency("../webdriver/stacktrace.js", ["webdriver.stacktrace","webdriver.stacktrace.Snapshot"], ["goog.array","goog.string","goog.userAgent"], false);
    72goog.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);
    73goog.addDependency("testing/recordfunction.js", ["goog.testing.FunctionCall","goog.testing.recordConstructor","goog.testing.recordFunction"], ["goog.testing.asserts"], false);
    74goog.addDependency("../webdriver/test/logging_test.js", [], ["goog.debug.LogRecord","goog.debug.Logger","goog.testing.jsunit","webdriver.logging"], false);
    75goog.addDependency("debug/logrecord.js", ["goog.debug.LogRecord"], [], false);
    76goog.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);
    77goog.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);
    78goog.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);
    79goog.addDependency("html/safescript.js", ["goog.html.SafeScript"], ["goog.asserts","goog.string.Const","goog.string.TypedString"], false);
    80goog.addDependency("structs/set.js", ["goog.structs.Set"], ["goog.structs","goog.structs.Collection","goog.structs.Map"], false);
    81goog.addDependency("structs/structs.js", ["goog.structs"], ["goog.array","goog.object"], false);
    82goog.addDependency("structs/collection.js", ["goog.structs.Collection"], [], false);
    83goog.addDependency("structs/map.js", ["goog.structs.Map"], ["goog.iter.Iterator","goog.iter.StopIteration","goog.object"], false);
    84goog.addDependency("iter/iter.js", ["goog.iter","goog.iter.Iterable","goog.iter.Iterator","goog.iter.StopIteration"], ["goog.array","goog.asserts","goog.functions","goog.math"], false);
    85goog.addDependency("debug/logbuffer.js", ["goog.debug.LogBuffer"], ["goog.asserts","goog.debug.LogRecord"], false);
    86goog.addDependency("../webdriver/logging.js", ["webdriver.logging"], ["goog.debug.LogManager","goog.debug.LogRecord","goog.debug.Logger","goog.object"], true);
    87goog.addDependency("../webdriver/test/test_bootstrap.js", [], [], false);
    88goog.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);
    89goog.addDependency("useragent/product.js", ["goog.userAgent.product"], ["goog.labs.userAgent.browser","goog.labs.userAgent.platform","goog.userAgent"], false);
    90goog.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);
    91goog.addDependency("../webdriver/test/locators_test.js", [], ["goog.testing.jsunit","webdriver.By","webdriver.Locator","webdriver.Locator.Strategy","webdriver.test.testutil"], false);
    92goog.addDependency("../webdriver/locators.js", ["webdriver.By","webdriver.Locator","webdriver.Locator.Strategy"], ["goog.array","goog.object","goog.string"], false);
    93goog.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);
    94goog.addDependency("../atoms/error.js", ["bot.Error","bot.ErrorCode"], [], false);
    95goog.addDependency("testing/mockcontrol.js", ["goog.testing.MockControl"], ["goog.array","goog.testing","goog.testing.LooseMock","goog.testing.StrictMock"], false);
    96goog.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);
    97goog.addDependency("testing/loosemock.js", ["goog.testing.LooseExpectationCollection","goog.testing.LooseMock"], ["goog.array","goog.structs.Map","goog.testing.Mock"], false);
    98goog.addDependency("testing/mock.js", ["goog.testing.Mock","goog.testing.MockExpectation"], ["goog.array","goog.object","goog.testing.JsUnitException","goog.testing.MockInterface","goog.testing.mockmatchers"], false);
    99goog.addDependency("testing/mockinterface.js", ["goog.testing.MockInterface"], [], false);
    100goog.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);
    101goog.addDependency("testing/strictmock.js", ["goog.testing.StrictMock"], ["goog.array","goog.testing.Mock"], false);
    102goog.addDependency("../webdriver/capabilities.js", ["webdriver.Browser","webdriver.Capabilities","webdriver.Capability","webdriver.ProxyConfig"], ["webdriver.Serializable","webdriver.logging"], false);
    103goog.addDependency("../webdriver/serializable.js", ["webdriver.Serializable"], [], false);
    104goog.addDependency("../webdriver/command.js", ["webdriver.Command","webdriver.CommandExecutor","webdriver.CommandName"], [], false);
    105goog.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);
    106goog.addDependency("../atoms/response.js", ["bot.response","bot.response.ResponseObject"], ["bot.Error","bot.ErrorCode"], false);
    107goog.addDependency("../webdriver/actionsequence.js", ["webdriver.ActionSequence"], ["goog.array","webdriver.Button","webdriver.Command","webdriver.CommandName","webdriver.Key"], false);
    108goog.addDependency("../webdriver/button.js", ["webdriver.Button"], [], false);
    109goog.addDependency("../webdriver/key.js", ["webdriver.Key"], [], false);
    110goog.addDependency("../webdriver/session.js", ["webdriver.Session"], ["webdriver.Capabilities"], false);
    111goog.addDependency("../webdriver/touchsequence.js", ["webdriver.TouchSequence"], ["goog.array","webdriver.Command","webdriver.CommandName"], false);
    112goog.addDependency("../webdriver/until.js", ["webdriver.until"], ["bot.ErrorCode","goog.array","goog.string","webdriver.Locator"], false);
    113goog.addDependency("../webdriver/test/testutil_test.js", [], ["goog.testing.jsunit","webdriver.test.testutil"], false);
    114goog.addDependency("../webdriver/test/builder_test.js", [], ["goog.testing.jsunit","webdriver.Builder"], false);
    115goog.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);
    116goog.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);
    117goog.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"], false);
    118goog.addDependency("../webdriver/firefoxdomexecutor.js", ["webdriver.FirefoxDomExecutor"], ["bot.response","goog.userAgent.product","webdriver.Command","webdriver.CommandExecutor","webdriver.CommandName"], false);
    119goog.addDependency("../webdriver/http/corsclient.js", ["webdriver.http.CorsClient"], ["webdriver.http.Client","webdriver.http.Response"], false);
    120goog.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);
    121goog.addDependency("../webdriver/http/xhrclient.js", ["webdriver.http.XhrClient"], ["goog.net.XmlHttp","webdriver.http.Client","webdriver.http.Response"], false);
    122goog.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);
    123goog.addDependency("net/wrapperxmlhttpfactory.js", ["goog.net.WrapperXmlHttpFactory"], ["goog.net.XhrLike","goog.net.XmlHttpFactory"], false);
    124goog.addDependency("net/xhrlike.js", ["goog.net.XhrLike"], [], false);
    125goog.addDependency("net/xmlhttpfactory.js", ["goog.net.XmlHttpFactory"], ["goog.net.XhrLike"], false);
    126goog.addDependency("../webdriver/test/capabilities_test.js", [], ["goog.testing.jsunit","webdriver.Capabilities"], false);
    127goog.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);
    128goog.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);
    129goog.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);
    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/client_test.js", [], ["goog.testing.MockControl","goog.testing.PropertyReplacer","goog.testing.jsunit","goog.userAgent","webdriver.testing.Client"], false);
    132goog.addDependency("../webdriver/testing/client.js", ["webdriver.testing.Client"], ["goog.json","goog.net.XmlHttp"], false);
    133goog.addDependency("json/json.js", ["goog.json","goog.json.Replacer","goog.json.Reviver","goog.json.Serializer"], [], false);
    134goog.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);
    135goog.addDependency("../webdriver/testing/testcase.js", ["webdriver.testing.TestCase"], ["goog.testing.TestCase","webdriver.promise","webdriver.testing.asserts"], false);
    136goog.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);
    137goog.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);
    138goog.addDependency("labs/testing/matcher.js", ["goog.labs.testing.Matcher"], [], false);
    139goog.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);
    140goog.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);
    141goog.addDependency("labs/testing/logicmatcher.js", ["goog.labs.testing.AllOfMatcher","goog.labs.testing.AnyOfMatcher","goog.labs.testing.IsNotMatcher"], ["goog.array","goog.labs.testing.Matcher"], false);
    142goog.addDependency("labs/testing/assertthat.js", ["goog.labs.testing.MatcherError","goog.labs.testing.assertThat"], ["goog.debug.Error"], false);
    143goog.addDependency("../webdriver/test/testing/asserts_test.js", [], ["goog.testing.jsunit","goog.userAgent","webdriver.test.testutil","webdriver.testing.assert","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/window.js", ["webdriver.testing.Window"], ["goog.string","webdriver.promise"], false);
    148goog.addDependency("../webdriver/testing/jsunit.js", ["webdriver.testing.jsunit","webdriver.testing.jsunit.TestRunner"], ["goog.testing.TestRunner","webdriver.testing.Client","webdriver.testing.TestCase"], false);
    149goog.addDependency("../webdriver/test/webdriver_generator_test.js", ["webdriver.test.WebDriver.generator.test"], ["goog.testing.jsunit","webdriver.Session","webdriver.WebDriver"], 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/promise_generator_test.js", ["webdriver.test.promise.generator.test"], ["goog.testing.jsunit","webdriver.promise"], 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 index 903e8b2..60129c2 100644 --- a/docs/source/lib/goog/disposable/disposable.js.src.html +++ b/docs/source/lib/goog/disposable/disposable.js.src.html @@ -1 +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.
    210 * @param {function(this:T):?} callback The callback function.
    211 * @param {T=} opt_scope An optional scope to call the callback in.
    212 * @template T
    213 */
    214goog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) {
    215 if (!this.onDisposeCallbacks_) {
    216 this.onDisposeCallbacks_ = [];
    217 }
    218
    219 this.onDisposeCallbacks_.push(
    220 goog.isDef(opt_scope) ? goog.bind(callback, opt_scope) : callback);
    221};
    222
    223
    224/**
    225 * Deletes or nulls out any references to COM objects, DOM nodes, or other
    226 * disposable objects. Classes that extend {@code goog.Disposable} should
    227 * override this method.
    228 * Not reentrant. To avoid calling it twice, it must only be called from the
    229 * subclass' {@code disposeInternal} method. Everywhere else the public
    230 * {@code dispose} method must be used.
    231 * For example:
    232 * <pre>
    233 * mypackage.MyClass = function() {
    234 * mypackage.MyClass.base(this, 'constructor');
    235 * // Constructor logic specific to MyClass.
    236 * ...
    237 * };
    238 * goog.inherits(mypackage.MyClass, goog.Disposable);
    239 *
    240 * mypackage.MyClass.prototype.disposeInternal = function() {
    241 * // Dispose logic specific to MyClass.
    242 * ...
    243 * // Call superclass's disposeInternal at the end of the subclass's, like
    244 * // in C++, to avoid hard-to-catch issues.
    245 * mypackage.MyClass.base(this, 'disposeInternal');
    246 * };
    247 * </pre>
    248 * @protected
    249 */
    250goog.Disposable.prototype.disposeInternal = function() {
    251 if (this.onDisposeCallbacks_) {
    252 while (this.onDisposeCallbacks_.length) {
    253 this.onDisposeCallbacks_.shift()();
    254 }
    255 }
    256};
    257
    258
    259/**
    260 * Returns True if we can verify the object is disposed.
    261 * Calls {@code isDisposed} on the argument if it supports it. If obj
    262 * is not an object with an isDisposed() method, return false.
    263 * @param {*} obj The object to investigate.
    264 * @return {boolean} True if we can verify the object is disposed.
    265 */
    266goog.Disposable.isDisposed = function(obj) {
    267 if (obj && typeof obj.isDisposed == 'function') {
    268 return obj.isDisposed();
    269 }
    270 return false;
    271};
    272
    273
    274/**
    275 * Calls {@code dispose} on the argument if it supports it. If obj is not an
    276 * object with a dispose() method, this is a no-op.
    277 * @param {*} obj The object to dispose of.
    278 */
    279goog.dispose = function(obj) {
    280 if (obj && typeof obj.dispose == 'function') {
    281 obj.dispose();
    282 }
    283};
    284
    285
    286/**
    287 * Calls {@code dispose} on each member of the list that supports it. (If the
    288 * member is an ArrayLike, then {@code goog.disposeAll()} will be called
    289 * recursively on each of its members.) If the member is not an object with a
    290 * {@code dispose()} method, then it is ignored.
    291 * @param {...*} var_args The list.
    292 */
    293goog.disposeAll = function(var_args) {
    294 for (var i = 0, len = arguments.length; i < len; ++i) {
    295 var disposable = arguments[i];
    296 if (goog.isArrayLike(disposable)) {
    297 goog.disposeAll.apply(null, disposable);
    298 } else {
    299 goog.dispose(disposable);
    300 }
    301 }
    302};
    \ No newline at end of file +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 index 0964c46..a3d4819 100644 --- a/docs/source/lib/goog/disposable/idisposable.js.src.html +++ b/docs/source/lib/goog/disposable/idisposable.js.src.html @@ -1 +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 +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 index 0430c08..0c53d6b 100644 --- a/docs/source/lib/goog/dom/browserfeature.js.src.html +++ b/docs/source/lib/goog/dom/browserfeature.js.src.html @@ -1 +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 +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 index 521ebfc..30765ac 100644 --- a/docs/source/lib/goog/dom/dom.js.src.html +++ b/docs/source/lib/goog/dom/dom.js.src.html @@ -1 +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 */
    24
    25
    26// TODO(arv): Rename/refactor getTextContent and getRawTextContent. The problem
    27// is that getTextContent should mimic the DOM3 textContent. We should add a
    28// getInnerText (or getText) which tries to return the visible text, innerText.
    29
    30
    31goog.provide('goog.dom');
    32goog.provide('goog.dom.Appendable');
    33goog.provide('goog.dom.DomHelper');
    34
    35goog.require('goog.array');
    36goog.require('goog.asserts');
    37goog.require('goog.dom.BrowserFeature');
    38goog.require('goog.dom.NodeType');
    39goog.require('goog.dom.TagName');
    40goog.require('goog.math.Coordinate');
    41goog.require('goog.math.Size');
    42goog.require('goog.object');
    43goog.require('goog.string');
    44goog.require('goog.userAgent');
    45
    46
    47/**
    48 * @define {boolean} Whether we know at compile time that the browser is in
    49 * quirks mode.
    50 */
    51goog.define('goog.dom.ASSUME_QUIRKS_MODE', false);
    52
    53
    54/**
    55 * @define {boolean} Whether we know at compile time that the browser is in
    56 * standards compliance mode.
    57 */
    58goog.define('goog.dom.ASSUME_STANDARDS_MODE', false);
    59
    60
    61/**
    62 * Whether we know the compatibility mode at compile time.
    63 * @type {boolean}
    64 * @private
    65 */
    66goog.dom.COMPAT_MODE_KNOWN_ =
    67 goog.dom.ASSUME_QUIRKS_MODE || goog.dom.ASSUME_STANDARDS_MODE;
    68
    69
    70/**
    71 * Gets the DomHelper object for the document where the element resides.
    72 * @param {(Node|Window)=} opt_element If present, gets the DomHelper for this
    73 * element.
    74 * @return {!goog.dom.DomHelper} The DomHelper.
    75 */
    76goog.dom.getDomHelper = function(opt_element) {
    77 return opt_element ?
    78 new goog.dom.DomHelper(goog.dom.getOwnerDocument(opt_element)) :
    79 (goog.dom.defaultDomHelper_ ||
    80 (goog.dom.defaultDomHelper_ = new goog.dom.DomHelper()));
    81};
    82
    83
    84/**
    85 * Cached default DOM helper.
    86 * @type {goog.dom.DomHelper}
    87 * @private
    88 */
    89goog.dom.defaultDomHelper_;
    90
    91
    92/**
    93 * Gets the document object being used by the dom library.
    94 * @return {!Document} Document object.
    95 */
    96goog.dom.getDocument = function() {
    97 return document;
    98};
    99
    100
    101/**
    102 * Gets an element from the current document by element id.
    103 *
    104 * If an Element is passed in, it is returned.
    105 *
    106 * @param {string|Element} element Element ID or a DOM node.
    107 * @return {Element} The element with the given ID, or the node passed in.
    108 */
    109goog.dom.getElement = function(element) {
    110 return goog.dom.getElementHelper_(document, element);
    111};
    112
    113
    114/**
    115 * Gets an element by id from the given document (if present).
    116 * If an element is given, it is returned.
    117 * @param {!Document} doc
    118 * @param {string|Element} element Element ID or a DOM node.
    119 * @return {Element} The resulting element.
    120 * @private
    121 */
    122goog.dom.getElementHelper_ = function(doc, element) {
    123 return goog.isString(element) ?
    124 doc.getElementById(element) :
    125 element;
    126};
    127
    128
    129/**
    130 * Gets an element by id, asserting that the element is found.
    131 *
    132 * This is used when an element is expected to exist, and should fail with
    133 * an assertion error if it does not (if assertions are enabled).
    134 *
    135 * @param {string} id Element ID.
    136 * @return {!Element} The element with the given ID, if it exists.
    137 */
    138goog.dom.getRequiredElement = function(id) {
    139 return goog.dom.getRequiredElementHelper_(document, id);
    140};
    141
    142
    143/**
    144 * Helper function for getRequiredElementHelper functions, both static and
    145 * on DomHelper. Asserts the element with the given id exists.
    146 * @param {!Document} doc
    147 * @param {string} id
    148 * @return {!Element} The element with the given ID, if it exists.
    149 * @private
    150 */
    151goog.dom.getRequiredElementHelper_ = function(doc, id) {
    152 // To prevent users passing in Elements as is permitted in getElement().
    153 goog.asserts.assertString(id);
    154 var element = goog.dom.getElementHelper_(doc, id);
    155 element = goog.asserts.assertElement(element,
    156 'No element found with id: ' + id);
    157 return element;
    158};
    159
    160
    161/**
    162 * Alias for getElement.
    163 * @param {string|Element} element Element ID or a DOM node.
    164 * @return {Element} The element with the given ID, or the node passed in.
    165 * @deprecated Use {@link goog.dom.getElement} instead.
    166 */
    167goog.dom.$ = goog.dom.getElement;
    168
    169
    170/**
    171 * Looks up elements by both tag and class name, using browser native functions
    172 * ({@code querySelectorAll}, {@code getElementsByTagName} or
    173 * {@code getElementsByClassName}) where possible. This function
    174 * is a useful, if limited, way of collecting a list of DOM elements
    175 * with certain characteristics. {@code goog.dom.query} offers a
    176 * more powerful and general solution which allows matching on CSS3
    177 * selector expressions, but at increased cost in code size. If all you
    178 * need is particular tags belonging to a single class, this function
    179 * is fast and sleek.
    180 *
    181 * Note that tag names are case sensitive in the SVG namespace, and this
    182 * function converts opt_tag to uppercase for comparisons. For queries in the
    183 * SVG namespace you should use querySelector or querySelectorAll instead.
    184 * https://bugzilla.mozilla.org/show_bug.cgi?id=963870
    185 * https://bugs.webkit.org/show_bug.cgi?id=83438
    186 *
    187 * @see {goog.dom.query}
    188 *
    189 * @param {?string=} opt_tag Element tag name.
    190 * @param {?string=} opt_class Optional class name.
    191 * @param {(Document|Element)=} opt_el Optional element to look in.
    192 * @return { {length: number} } Array-like list of elements (only a length
    193 * property and numerical indices are guaranteed to exist).
    194 */
    195goog.dom.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) {
    196 return goog.dom.getElementsByTagNameAndClass_(document, opt_tag, opt_class,
    197 opt_el);
    198};
    199
    200
    201/**
    202 * Returns a static, array-like list of the elements with the provided
    203 * className.
    204 * @see {goog.dom.query}
    205 * @param {string} className the name of the class to look for.
    206 * @param {(Document|Element)=} opt_el Optional element to look in.
    207 * @return { {length: number} } The items found with the class name provided.
    208 */
    209goog.dom.getElementsByClass = function(className, opt_el) {
    210 var parent = opt_el || document;
    211 if (goog.dom.canUseQuerySelector_(parent)) {
    212 return parent.querySelectorAll('.' + className);
    213 }
    214 return goog.dom.getElementsByTagNameAndClass_(
    215 document, '*', className, opt_el);
    216};
    217
    218
    219/**
    220 * Returns the first element with the provided className.
    221 * @see {goog.dom.query}
    222 * @param {string} className the name of the class to look for.
    223 * @param {Element|Document=} opt_el Optional element to look in.
    224 * @return {Element} The first item with the class name provided.
    225 */
    226goog.dom.getElementByClass = function(className, opt_el) {
    227 var parent = opt_el || document;
    228 var retVal = null;
    229 if (goog.dom.canUseQuerySelector_(parent)) {
    230 retVal = parent.querySelector('.' + className);
    231 } else {
    232 retVal = goog.dom.getElementsByTagNameAndClass_(
    233 document, '*', className, opt_el)[0];
    234 }
    235 return retVal || null;
    236};
    237
    238
    239/**
    240 * Ensures an element with the given className exists, and then returns the
    241 * first element with the provided className.
    242 * @see {goog.dom.query}
    243 * @param {string} className the name of the class to look for.
    244 * @param {!Element|!Document=} opt_root Optional element or document to look
    245 * in.
    246 * @return {!Element} The first item with the class name provided.
    247 * @throws {goog.asserts.AssertionError} Thrown if no element is found.
    248 */
    249goog.dom.getRequiredElementByClass = function(className, opt_root) {
    250 var retValue = goog.dom.getElementByClass(className, opt_root);
    251 return goog.asserts.assert(retValue,
    252 'No element found with className: ' + className);
    253};
    254
    255
    256/**
    257 * Prefer the standardized (http://www.w3.org/TR/selectors-api/), native and
    258 * fast W3C Selectors API.
    259 * @param {!(Element|Document)} parent The parent document object.
    260 * @return {boolean} whether or not we can use parent.querySelector* APIs.
    261 * @private
    262 */
    263goog.dom.canUseQuerySelector_ = function(parent) {
    264 return !!(parent.querySelectorAll && parent.querySelector);
    265};
    266
    267
    268/**
    269 * Helper for {@code getElementsByTagNameAndClass}.
    270 * @param {!Document} doc The document to get the elements in.
    271 * @param {?string=} opt_tag Element tag name.
    272 * @param {?string=} opt_class Optional class name.
    273 * @param {(Document|Element)=} opt_el Optional element to look in.
    274 * @return { {length: number} } Array-like list of elements (only a length
    275 * property and numerical indices are guaranteed to exist).
    276 * @private
    277 */
    278goog.dom.getElementsByTagNameAndClass_ = function(doc, opt_tag, opt_class,
    279 opt_el) {
    280 var parent = opt_el || doc;
    281 var tagName = (opt_tag && opt_tag != '*') ? opt_tag.toUpperCase() : '';
    282
    283 if (goog.dom.canUseQuerySelector_(parent) &&
    284 (tagName || opt_class)) {
    285 var query = tagName + (opt_class ? '.' + opt_class : '');
    286 return parent.querySelectorAll(query);
    287 }
    288
    289 // Use the native getElementsByClassName if available, under the assumption
    290 // that even when the tag name is specified, there will be fewer elements to
    291 // filter through when going by class than by tag name
    292 if (opt_class && parent.getElementsByClassName) {
    293 var els = parent.getElementsByClassName(opt_class);
    294
    295 if (tagName) {
    296 var arrayLike = {};
    297 var len = 0;
    298
    299 // Filter for specific tags if requested.
    300 for (var i = 0, el; el = els[i]; i++) {
    301 if (tagName == el.nodeName) {
    302 arrayLike[len++] = el;
    303 }
    304 }
    305 arrayLike.length = len;
    306
    307 return arrayLike;
    308 } else {
    309 return els;
    310 }
    311 }
    312
    313 var els = parent.getElementsByTagName(tagName || '*');
    314
    315 if (opt_class) {
    316 var arrayLike = {};
    317 var len = 0;
    318 for (var i = 0, el; el = els[i]; i++) {
    319 var className = el.className;
    320 // Check if className has a split function since SVG className does not.
    321 if (typeof className.split == 'function' &&
    322 goog.array.contains(className.split(/\s+/), opt_class)) {
    323 arrayLike[len++] = el;
    324 }
    325 }
    326 arrayLike.length = len;
    327 return arrayLike;
    328 } else {
    329 return els;
    330 }
    331};
    332
    333
    334/**
    335 * Alias for {@code getElementsByTagNameAndClass}.
    336 * @param {?string=} opt_tag Element tag name.
    337 * @param {?string=} opt_class Optional class name.
    338 * @param {Element=} opt_el Optional element to look in.
    339 * @return { {length: number} } Array-like list of elements (only a length
    340 * property and numerical indices are guaranteed to exist).
    341 * @deprecated Use {@link goog.dom.getElementsByTagNameAndClass} instead.
    342 */
    343goog.dom.$$ = goog.dom.getElementsByTagNameAndClass;
    344
    345
    346/**
    347 * Sets multiple properties on a node.
    348 * @param {Element} element DOM node to set properties on.
    349 * @param {Object} properties Hash of property:value pairs.
    350 */
    351goog.dom.setProperties = function(element, properties) {
    352 goog.object.forEach(properties, function(val, key) {
    353 if (key == 'style') {
    354 element.style.cssText = val;
    355 } else if (key == 'class') {
    356 element.className = val;
    357 } else if (key == 'for') {
    358 element.htmlFor = val;
    359 } else if (key in goog.dom.DIRECT_ATTRIBUTE_MAP_) {
    360 element.setAttribute(goog.dom.DIRECT_ATTRIBUTE_MAP_[key], val);
    361 } else if (goog.string.startsWith(key, 'aria-') ||
    362 goog.string.startsWith(key, 'data-')) {
    363 element.setAttribute(key, val);
    364 } else {
    365 element[key] = val;
    366 }
    367 });
    368};
    369
    370
    371/**
    372 * Map of attributes that should be set using
    373 * element.setAttribute(key, val) instead of element[key] = val. Used
    374 * by goog.dom.setProperties.
    375 *
    376 * @type {Object}
    377 * @private
    378 */
    379goog.dom.DIRECT_ATTRIBUTE_MAP_ = {
    380 'cellpadding': 'cellPadding',
    381 'cellspacing': 'cellSpacing',
    382 'colspan': 'colSpan',
    383 'frameborder': 'frameBorder',
    384 'height': 'height',
    385 'maxlength': 'maxLength',
    386 'role': 'role',
    387 'rowspan': 'rowSpan',
    388 'type': 'type',
    389 'usemap': 'useMap',
    390 'valign': 'vAlign',
    391 'width': 'width'
    392};
    393
    394
    395/**
    396 * Gets the dimensions of the viewport.
    397 *
    398 * Gecko Standards mode:
    399 * docEl.clientWidth Width of viewport excluding scrollbar.
    400 * win.innerWidth Width of viewport including scrollbar.
    401 * body.clientWidth Width of body element.
    402 *
    403 * docEl.clientHeight Height of viewport excluding scrollbar.
    404 * win.innerHeight Height of viewport including scrollbar.
    405 * body.clientHeight Height of document.
    406 *
    407 * Gecko Backwards compatible mode:
    408 * docEl.clientWidth Width of viewport excluding scrollbar.
    409 * win.innerWidth Width of viewport including scrollbar.
    410 * body.clientWidth Width of viewport excluding scrollbar.
    411 *
    412 * docEl.clientHeight Height of document.
    413 * win.innerHeight Height of viewport including scrollbar.
    414 * body.clientHeight Height of viewport excluding scrollbar.
    415 *
    416 * IE6/7 Standards mode:
    417 * docEl.clientWidth Width of viewport excluding scrollbar.
    418 * win.innerWidth Undefined.
    419 * body.clientWidth Width of body element.
    420 *
    421 * docEl.clientHeight Height of viewport excluding scrollbar.
    422 * win.innerHeight Undefined.
    423 * body.clientHeight Height of document element.
    424 *
    425 * IE5 + IE6/7 Backwards compatible mode:
    426 * docEl.clientWidth 0.
    427 * win.innerWidth Undefined.
    428 * body.clientWidth Width of viewport excluding scrollbar.
    429 *
    430 * docEl.clientHeight 0.
    431 * win.innerHeight Undefined.
    432 * body.clientHeight Height of viewport excluding scrollbar.
    433 *
    434 * Opera 9 Standards and backwards compatible mode:
    435 * docEl.clientWidth Width of viewport excluding scrollbar.
    436 * win.innerWidth Width of viewport including scrollbar.
    437 * body.clientWidth Width of viewport excluding scrollbar.
    438 *
    439 * docEl.clientHeight Height of document.
    440 * win.innerHeight Height of viewport including scrollbar.
    441 * body.clientHeight Height of viewport excluding scrollbar.
    442 *
    443 * WebKit:
    444 * Safari 2
    445 * docEl.clientHeight Same as scrollHeight.
    446 * docEl.clientWidth Same as innerWidth.
    447 * win.innerWidth Width of viewport excluding scrollbar.
    448 * win.innerHeight Height of the viewport including scrollbar.
    449 * frame.innerHeight Height of the viewport exluding scrollbar.
    450 *
    451 * Safari 3 (tested in 522)
    452 *
    453 * docEl.clientWidth Width of viewport excluding scrollbar.
    454 * docEl.clientHeight Height of viewport excluding scrollbar in strict mode.
    455 * body.clientHeight Height of viewport excluding scrollbar in quirks mode.
    456 *
    457 * @param {Window=} opt_window Optional window element to test.
    458 * @return {!goog.math.Size} Object with values 'width' and 'height'.
    459 */
    460goog.dom.getViewportSize = function(opt_window) {
    461 // TODO(arv): This should not take an argument
    462 return goog.dom.getViewportSize_(opt_window || window);
    463};
    464
    465
    466/**
    467 * Helper for {@code getViewportSize}.
    468 * @param {Window} win The window to get the view port size for.
    469 * @return {!goog.math.Size} Object with values 'width' and 'height'.
    470 * @private
    471 */
    472goog.dom.getViewportSize_ = function(win) {
    473 var doc = win.document;
    474 var el = goog.dom.isCss1CompatMode_(doc) ? doc.documentElement : doc.body;
    475 return new goog.math.Size(el.clientWidth, el.clientHeight);
    476};
    477
    478
    479/**
    480 * Calculates the height of the document.
    481 *
    482 * @return {number} The height of the current document.
    483 */
    484goog.dom.getDocumentHeight = function() {
    485 return goog.dom.getDocumentHeight_(window);
    486};
    487
    488
    489/**
    490 * Calculates the height of the document of the given window.
    491 *
    492 * Function code copied from the opensocial gadget api:
    493 * gadgets.window.adjustHeight(opt_height)
    494 *
    495 * @private
    496 * @param {Window} win The window whose document height to retrieve.
    497 * @return {number} The height of the document of the given window.
    498 */
    499goog.dom.getDocumentHeight_ = function(win) {
    500 // NOTE(eae): This method will return the window size rather than the document
    501 // size in webkit quirks mode.
    502 var doc = win.document;
    503 var height = 0;
    504
    505 if (doc) {
    506 // Calculating inner content height is hard and different between
    507 // browsers rendering in Strict vs. Quirks mode. We use a combination of
    508 // three properties within document.body and document.documentElement:
    509 // - scrollHeight
    510 // - offsetHeight
    511 // - clientHeight
    512 // These values differ significantly between browsers and rendering modes.
    513 // But there are patterns. It just takes a lot of time and persistence
    514 // to figure out.
    515
    516 var body = doc.body;
    517 var docEl = doc.documentElement;
    518 if (!(docEl && body)) {
    519 return 0;
    520 }
    521
    522 // Get the height of the viewport
    523 var vh = goog.dom.getViewportSize_(win).height;
    524 if (goog.dom.isCss1CompatMode_(doc) && docEl.scrollHeight) {
    525 // In Strict mode:
    526 // The inner content height is contained in either:
    527 // document.documentElement.scrollHeight
    528 // document.documentElement.offsetHeight
    529 // Based on studying the values output by different browsers,
    530 // use the value that's NOT equal to the viewport height found above.
    531 height = docEl.scrollHeight != vh ?
    532 docEl.scrollHeight : docEl.offsetHeight;
    533 } else {
    534 // In Quirks mode:
    535 // documentElement.clientHeight is equal to documentElement.offsetHeight
    536 // except in IE. In most browsers, document.documentElement can be used
    537 // to calculate the inner content height.
    538 // However, in other browsers (e.g. IE), document.body must be used
    539 // instead. How do we know which one to use?
    540 // If document.documentElement.clientHeight does NOT equal
    541 // document.documentElement.offsetHeight, then use document.body.
    542 var sh = docEl.scrollHeight;
    543 var oh = docEl.offsetHeight;
    544 if (docEl.clientHeight != oh) {
    545 sh = body.scrollHeight;
    546 oh = body.offsetHeight;
    547 }
    548
    549 // Detect whether the inner content height is bigger or smaller
    550 // than the bounding box (viewport). If bigger, take the larger
    551 // value. If smaller, take the smaller value.
    552 if (sh > vh) {
    553 // Content is larger
    554 height = sh > oh ? sh : oh;
    555 } else {
    556 // Content is smaller
    557 height = sh < oh ? sh : oh;
    558 }
    559 }
    560 }
    561
    562 return height;
    563};
    564
    565
    566/**
    567 * Gets the page scroll distance as a coordinate object.
    568 *
    569 * @param {Window=} opt_window Optional window element to test.
    570 * @return {!goog.math.Coordinate} Object with values 'x' and 'y'.
    571 * @deprecated Use {@link goog.dom.getDocumentScroll} instead.
    572 */
    573goog.dom.getPageScroll = function(opt_window) {
    574 var win = opt_window || goog.global || window;
    575 return goog.dom.getDomHelper(win.document).getDocumentScroll();
    576};
    577
    578
    579/**
    580 * Gets the document scroll distance as a coordinate object.
    581 *
    582 * @return {!goog.math.Coordinate} Object with values 'x' and 'y'.
    583 */
    584goog.dom.getDocumentScroll = function() {
    585 return goog.dom.getDocumentScroll_(document);
    586};
    587
    588
    589/**
    590 * Helper for {@code getDocumentScroll}.
    591 *
    592 * @param {!Document} doc The document to get the scroll for.
    593 * @return {!goog.math.Coordinate} Object with values 'x' and 'y'.
    594 * @private
    595 */
    596goog.dom.getDocumentScroll_ = function(doc) {
    597 var el = goog.dom.getDocumentScrollElement_(doc);
    598 var win = goog.dom.getWindow_(doc);
    599 if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('10') &&
    600 win.pageYOffset != el.scrollTop) {
    601 // The keyboard on IE10 touch devices shifts the page using the pageYOffset
    602 // without modifying scrollTop. For this case, we want the body scroll
    603 // offsets.
    604 return new goog.math.Coordinate(el.scrollLeft, el.scrollTop);
    605 }
    606 return new goog.math.Coordinate(win.pageXOffset || el.scrollLeft,
    607 win.pageYOffset || el.scrollTop);
    608};
    609
    610
    611/**
    612 * Gets the document scroll element.
    613 * @return {!Element} Scrolling element.
    614 */
    615goog.dom.getDocumentScrollElement = function() {
    616 return goog.dom.getDocumentScrollElement_(document);
    617};
    618
    619
    620/**
    621 * Helper for {@code getDocumentScrollElement}.
    622 * @param {!Document} doc The document to get the scroll element for.
    623 * @return {!Element} Scrolling element.
    624 * @private
    625 */
    626goog.dom.getDocumentScrollElement_ = function(doc) {
    627 // WebKit needs body.scrollLeft in both quirks mode and strict mode. We also
    628 // default to the documentElement if the document does not have a body (e.g.
    629 // a SVG document).
    630 if (!goog.userAgent.WEBKIT && goog.dom.isCss1CompatMode_(doc)) {
    631 return doc.documentElement;
    632 }
    633 return doc.body || doc.documentElement;
    634};
    635
    636
    637/**
    638 * Gets the window object associated with the given document.
    639 *
    640 * @param {Document=} opt_doc Document object to get window for.
    641 * @return {!Window} The window associated with the given document.
    642 */
    643goog.dom.getWindow = function(opt_doc) {
    644 // TODO(arv): This should not take an argument.
    645 return opt_doc ? goog.dom.getWindow_(opt_doc) : window;
    646};
    647
    648
    649/**
    650 * Helper for {@code getWindow}.
    651 *
    652 * @param {!Document} doc Document object to get window for.
    653 * @return {!Window} The window associated with the given document.
    654 * @private
    655 */
    656goog.dom.getWindow_ = function(doc) {
    657 return doc.parentWindow || doc.defaultView;
    658};
    659
    660
    661/**
    662 * Returns a dom node with a set of attributes. This function accepts varargs
    663 * for subsequent nodes to be added. Subsequent nodes will be added to the
    664 * first node as childNodes.
    665 *
    666 * So:
    667 * <code>createDom('div', null, createDom('p'), createDom('p'));</code>
    668 * would return a div with two child paragraphs
    669 *
    670 * @param {string} tagName Tag to create.
    671 * @param {(Object|Array.<string>|string)=} opt_attributes If object, then a map
    672 * of name-value pairs for attributes. If a string, then this is the
    673 * className of the new element. If an array, the elements will be joined
    674 * together as the className of the new element.
    675 * @param {...(Object|string|Array|NodeList)} var_args Further DOM nodes or
    676 * strings for text nodes. If one of the var_args is an array or NodeList,i
    677 * its elements will be added as childNodes instead.
    678 * @return {!Element} Reference to a DOM node.
    679 */
    680goog.dom.createDom = function(tagName, opt_attributes, var_args) {
    681 return goog.dom.createDom_(document, arguments);
    682};
    683
    684
    685/**
    686 * Helper for {@code createDom}.
    687 * @param {!Document} doc The document to create the DOM in.
    688 * @param {!Arguments} args Argument object passed from the callers. See
    689 * {@code goog.dom.createDom} for details.
    690 * @return {!Element} Reference to a DOM node.
    691 * @private
    692 */
    693goog.dom.createDom_ = function(doc, args) {
    694 var tagName = args[0];
    695 var attributes = args[1];
    696
    697 // Internet Explorer is dumb: http://msdn.microsoft.com/workshop/author/
    698 // dhtml/reference/properties/name_2.asp
    699 // Also does not allow setting of 'type' attribute on 'input' or 'button'.
    700 if (!goog.dom.BrowserFeature.CAN_ADD_NAME_OR_TYPE_ATTRIBUTES && attributes &&
    701 (attributes.name || attributes.type)) {
    702 var tagNameArr = ['<', tagName];
    703 if (attributes.name) {
    704 tagNameArr.push(' name="', goog.string.htmlEscape(attributes.name),
    705 '"');
    706 }
    707 if (attributes.type) {
    708 tagNameArr.push(' type="', goog.string.htmlEscape(attributes.type),
    709 '"');
    710
    711 // Clone attributes map to remove 'type' without mutating the input.
    712 var clone = {};
    713 goog.object.extend(clone, attributes);
    714
    715 // JSCompiler can't see how goog.object.extend added this property,
    716 // because it was essentially added by reflection.
    717 // So it needs to be quoted.
    718 delete clone['type'];
    719
    720 attributes = clone;
    721 }
    722 tagNameArr.push('>');
    723 tagName = tagNameArr.join('');
    724 }
    725
    726 var element = doc.createElement(tagName);
    727
    728 if (attributes) {
    729 if (goog.isString(attributes)) {
    730 element.className = attributes;
    731 } else if (goog.isArray(attributes)) {
    732 element.className = attributes.join(' ');
    733 } else {
    734 goog.dom.setProperties(element, attributes);
    735 }
    736 }
    737
    738 if (args.length > 2) {
    739 goog.dom.append_(doc, element, args, 2);
    740 }
    741
    742 return element;
    743};
    744
    745
    746/**
    747 * Appends a node with text or other nodes.
    748 * @param {!Document} doc The document to create new nodes in.
    749 * @param {!Node} parent The node to append nodes to.
    750 * @param {!Arguments} args The values to add. See {@code goog.dom.append}.
    751 * @param {number} startIndex The index of the array to start from.
    752 * @private
    753 */
    754goog.dom.append_ = function(doc, parent, args, startIndex) {
    755 function childHandler(child) {
    756 // TODO(user): More coercion, ala MochiKit?
    757 if (child) {
    758 parent.appendChild(goog.isString(child) ?
    759 doc.createTextNode(child) : child);
    760 }
    761 }
    762
    763 for (var i = startIndex; i < args.length; i++) {
    764 var arg = args[i];
    765 // TODO(attila): Fix isArrayLike to return false for a text node.
    766 if (goog.isArrayLike(arg) && !goog.dom.isNodeLike(arg)) {
    767 // If the argument is a node list, not a real array, use a clone,
    768 // because forEach can't be used to mutate a NodeList.
    769 goog.array.forEach(goog.dom.isNodeList(arg) ?
    770 goog.array.toArray(arg) : arg,
    771 childHandler);
    772 } else {
    773 childHandler(arg);
    774 }
    775 }
    776};
    777
    778
    779/**
    780 * Alias for {@code createDom}.
    781 * @param {string} tagName Tag to create.
    782 * @param {(string|Object)=} opt_attributes If object, then a map of name-value
    783 * pairs for attributes. If a string, then this is the className of the new
    784 * element.
    785 * @param {...(Object|string|Array|NodeList)} var_args Further DOM nodes or
    786 * strings for text nodes. If one of the var_args is an array, its
    787 * children will be added as childNodes instead.
    788 * @return {!Element} Reference to a DOM node.
    789 * @deprecated Use {@link goog.dom.createDom} instead.
    790 */
    791goog.dom.$dom = goog.dom.createDom;
    792
    793
    794/**
    795 * Creates a new element.
    796 * @param {string} name Tag name.
    797 * @return {!Element} The new element.
    798 */
    799goog.dom.createElement = function(name) {
    800 return document.createElement(name);
    801};
    802
    803
    804/**
    805 * Creates a new text node.
    806 * @param {number|string} content Content.
    807 * @return {!Text} The new text node.
    808 */
    809goog.dom.createTextNode = function(content) {
    810 return document.createTextNode(String(content));
    811};
    812
    813
    814/**
    815 * Create a table.
    816 * @param {number} rows The number of rows in the table. Must be >= 1.
    817 * @param {number} columns The number of columns in the table. Must be >= 1.
    818 * @param {boolean=} opt_fillWithNbsp If true, fills table entries with nsbps.
    819 * @return {!Element} The created table.
    820 */
    821goog.dom.createTable = function(rows, columns, opt_fillWithNbsp) {
    822 return goog.dom.createTable_(document, rows, columns, !!opt_fillWithNbsp);
    823};
    824
    825
    826/**
    827 * Create a table.
    828 * @param {!Document} doc Document object to use to create the table.
    829 * @param {number} rows The number of rows in the table. Must be >= 1.
    830 * @param {number} columns The number of columns in the table. Must be >= 1.
    831 * @param {boolean} fillWithNbsp If true, fills table entries with nsbps.
    832 * @return {!Element} The created table.
    833 * @private
    834 */
    835goog.dom.createTable_ = function(doc, rows, columns, fillWithNbsp) {
    836 var rowHtml = ['<tr>'];
    837 for (var i = 0; i < columns; i++) {
    838 rowHtml.push(fillWithNbsp ? '<td>&nbsp;</td>' : '<td></td>');
    839 }
    840 rowHtml.push('</tr>');
    841 rowHtml = rowHtml.join('');
    842 var totalHtml = ['<table>'];
    843 for (i = 0; i < rows; i++) {
    844 totalHtml.push(rowHtml);
    845 }
    846 totalHtml.push('</table>');
    847
    848 var elem = doc.createElement(goog.dom.TagName.DIV);
    849 elem.innerHTML = totalHtml.join('');
    850 return /** @type {!Element} */ (elem.removeChild(elem.firstChild));
    851};
    852
    853
    854/**
    855 * Converts an HTML string into a document fragment. The string must be
    856 * sanitized in order to avoid cross-site scripting. For example
    857 * {@code goog.dom.htmlToDocumentFragment('&lt;img src=x onerror=alert(0)&gt;')}
    858 * triggers an alert in all browsers, even if the returned document fragment
    859 * is thrown away immediately.
    860 *
    861 * @param {string} htmlString The HTML string to convert.
    862 * @return {!Node} The resulting document fragment.
    863 */
    864goog.dom.htmlToDocumentFragment = function(htmlString) {
    865 return goog.dom.htmlToDocumentFragment_(document, htmlString);
    866};
    867
    868
    869/**
    870 * Helper for {@code htmlToDocumentFragment}.
    871 *
    872 * @param {!Document} doc The document.
    873 * @param {string} htmlString The HTML string to convert.
    874 * @return {!Node} The resulting document fragment.
    875 * @private
    876 */
    877goog.dom.htmlToDocumentFragment_ = function(doc, htmlString) {
    878 var tempDiv = doc.createElement('div');
    879 if (goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT) {
    880 tempDiv.innerHTML = '<br>' + htmlString;
    881 tempDiv.removeChild(tempDiv.firstChild);
    882 } else {
    883 tempDiv.innerHTML = htmlString;
    884 }
    885 if (tempDiv.childNodes.length == 1) {
    886 return /** @type {!Node} */ (tempDiv.removeChild(tempDiv.firstChild));
    887 } else {
    888 var fragment = doc.createDocumentFragment();
    889 while (tempDiv.firstChild) {
    890 fragment.appendChild(tempDiv.firstChild);
    891 }
    892 return fragment;
    893 }
    894};
    895
    896
    897/**
    898 * Returns true if the browser is in "CSS1-compatible" (standards-compliant)
    899 * mode, false otherwise.
    900 * @return {boolean} True if in CSS1-compatible mode.
    901 */
    902goog.dom.isCss1CompatMode = function() {
    903 return goog.dom.isCss1CompatMode_(document);
    904};
    905
    906
    907/**
    908 * Returns true if the browser is in "CSS1-compatible" (standards-compliant)
    909 * mode, false otherwise.
    910 * @param {Document} doc The document to check.
    911 * @return {boolean} True if in CSS1-compatible mode.
    912 * @private
    913 */
    914goog.dom.isCss1CompatMode_ = function(doc) {
    915 if (goog.dom.COMPAT_MODE_KNOWN_) {
    916 return goog.dom.ASSUME_STANDARDS_MODE;
    917 }
    918
    919 return doc.compatMode == 'CSS1Compat';
    920};
    921
    922
    923/**
    924 * Determines if the given node can contain children, intended to be used for
    925 * HTML generation.
    926 *
    927 * IE natively supports node.canHaveChildren but has inconsistent behavior.
    928 * Prior to IE8 the base tag allows children and in IE9 all nodes return true
    929 * for canHaveChildren.
    930 *
    931 * In practice all non-IE browsers allow you to add children to any node, but
    932 * the behavior is inconsistent:
    933 *
    934 * <pre>
    935 * var a = document.createElement('br');
    936 * a.appendChild(document.createTextNode('foo'));
    937 * a.appendChild(document.createTextNode('bar'));
    938 * console.log(a.childNodes.length); // 2
    939 * console.log(a.innerHTML); // Chrome: "", IE9: "foobar", FF3.5: "foobar"
    940 * </pre>
    941 *
    942 * For more information, see:
    943 * http://dev.w3.org/html5/markup/syntax.html#syntax-elements
    944 *
    945 * TODO(user): Rename shouldAllowChildren() ?
    946 *
    947 * @param {Node} node The node to check.
    948 * @return {boolean} Whether the node can contain children.
    949 */
    950goog.dom.canHaveChildren = function(node) {
    951 if (node.nodeType != goog.dom.NodeType.ELEMENT) {
    952 return false;
    953 }
    954 switch (node.tagName) {
    955 case goog.dom.TagName.APPLET:
    956 case goog.dom.TagName.AREA:
    957 case goog.dom.TagName.BASE:
    958 case goog.dom.TagName.BR:
    959 case goog.dom.TagName.COL:
    960 case goog.dom.TagName.COMMAND:
    961 case goog.dom.TagName.EMBED:
    962 case goog.dom.TagName.FRAME:
    963 case goog.dom.TagName.HR:
    964 case goog.dom.TagName.IMG:
    965 case goog.dom.TagName.INPUT:
    966 case goog.dom.TagName.IFRAME:
    967 case goog.dom.TagName.ISINDEX:
    968 case goog.dom.TagName.KEYGEN:
    969 case goog.dom.TagName.LINK:
    970 case goog.dom.TagName.NOFRAMES:
    971 case goog.dom.TagName.NOSCRIPT:
    972 case goog.dom.TagName.META:
    973 case goog.dom.TagName.OBJECT:
    974 case goog.dom.TagName.PARAM:
    975 case goog.dom.TagName.SCRIPT:
    976 case goog.dom.TagName.SOURCE:
    977 case goog.dom.TagName.STYLE:
    978 case goog.dom.TagName.TRACK:
    979 case goog.dom.TagName.WBR:
    980 return false;
    981 }
    982 return true;
    983};
    984
    985
    986/**
    987 * Appends a child to a node.
    988 * @param {Node} parent Parent.
    989 * @param {Node} child Child.
    990 */
    991goog.dom.appendChild = function(parent, child) {
    992 parent.appendChild(child);
    993};
    994
    995
    996/**
    997 * Appends a node with text or other nodes.
    998 * @param {!Node} parent The node to append nodes to.
    999 * @param {...goog.dom.Appendable} var_args The things to append to the node.
    1000 * If this is a Node it is appended as is.
    1001 * If this is a string then a text node is appended.
    1002 * If this is an array like object then fields 0 to length - 1 are appended.
    1003 */
    1004goog.dom.append = function(parent, var_args) {
    1005 goog.dom.append_(goog.dom.getOwnerDocument(parent), parent, arguments, 1);
    1006};
    1007
    1008
    1009/**
    1010 * Removes all the child nodes on a DOM node.
    1011 * @param {Node} node Node to remove children from.
    1012 */
    1013goog.dom.removeChildren = function(node) {
    1014 // Note: Iterations over live collections can be slow, this is the fastest
    1015 // we could find. The double parenthesis are used to prevent JsCompiler and
    1016 // strict warnings.
    1017 var child;
    1018 while ((child = node.firstChild)) {
    1019 node.removeChild(child);
    1020 }
    1021};
    1022
    1023
    1024/**
    1025 * Inserts a new node before an existing reference node (i.e. as the previous
    1026 * sibling). If the reference node has no parent, then does nothing.
    1027 * @param {Node} newNode Node to insert.
    1028 * @param {Node} refNode Reference node to insert before.
    1029 */
    1030goog.dom.insertSiblingBefore = function(newNode, refNode) {
    1031 if (refNode.parentNode) {
    1032 refNode.parentNode.insertBefore(newNode, refNode);
    1033 }
    1034};
    1035
    1036
    1037/**
    1038 * Inserts a new node after an existing reference node (i.e. as the next
    1039 * sibling). If the reference node has no parent, then does nothing.
    1040 * @param {Node} newNode Node to insert.
    1041 * @param {Node} refNode Reference node to insert after.
    1042 */
    1043goog.dom.insertSiblingAfter = function(newNode, refNode) {
    1044 if (refNode.parentNode) {
    1045 refNode.parentNode.insertBefore(newNode, refNode.nextSibling);
    1046 }
    1047};
    1048
    1049
    1050/**
    1051 * Insert a child at a given index. If index is larger than the number of child
    1052 * nodes that the parent currently has, the node is inserted as the last child
    1053 * node.
    1054 * @param {Element} parent The element into which to insert the child.
    1055 * @param {Node} child The element to insert.
    1056 * @param {number} index The index at which to insert the new child node. Must
    1057 * not be negative.
    1058 */
    1059goog.dom.insertChildAt = function(parent, child, index) {
    1060 // Note that if the second argument is null, insertBefore
    1061 // will append the child at the end of the list of children.
    1062 parent.insertBefore(child, parent.childNodes[index] || null);
    1063};
    1064
    1065
    1066/**
    1067 * Removes a node from its parent.
    1068 * @param {Node} node The node to remove.
    1069 * @return {Node} The node removed if removed; else, null.
    1070 */
    1071goog.dom.removeNode = function(node) {
    1072 return node && node.parentNode ? node.parentNode.removeChild(node) : null;
    1073};
    1074
    1075
    1076/**
    1077 * Replaces a node in the DOM tree. Will do nothing if {@code oldNode} has no
    1078 * parent.
    1079 * @param {Node} newNode Node to insert.
    1080 * @param {Node} oldNode Node to replace.
    1081 */
    1082goog.dom.replaceNode = function(newNode, oldNode) {
    1083 var parent = oldNode.parentNode;
    1084 if (parent) {
    1085 parent.replaceChild(newNode, oldNode);
    1086 }
    1087};
    1088
    1089
    1090/**
    1091 * Flattens an element. That is, removes it and replace it with its children.
    1092 * Does nothing if the element is not in the document.
    1093 * @param {Element} element The element to flatten.
    1094 * @return {Element|undefined} The original element, detached from the document
    1095 * tree, sans children; or undefined, if the element was not in the document
    1096 * to begin with.
    1097 */
    1098goog.dom.flattenElement = function(element) {
    1099 var child, parent = element.parentNode;
    1100 if (parent && parent.nodeType != goog.dom.NodeType.DOCUMENT_FRAGMENT) {
    1101 // Use IE DOM method (supported by Opera too) if available
    1102 if (element.removeNode) {
    1103 return /** @type {Element} */ (element.removeNode(false));
    1104 } else {
    1105 // Move all children of the original node up one level.
    1106 while ((child = element.firstChild)) {
    1107 parent.insertBefore(child, element);
    1108 }
    1109
    1110 // Detach the original element.
    1111 return /** @type {Element} */ (goog.dom.removeNode(element));
    1112 }
    1113 }
    1114};
    1115
    1116
    1117/**
    1118 * Returns an array containing just the element children of the given element.
    1119 * @param {Element} element The element whose element children we want.
    1120 * @return {!(Array|NodeList)} An array or array-like list of just the element
    1121 * children of the given element.
    1122 */
    1123goog.dom.getChildren = function(element) {
    1124 // We check if the children attribute is supported for child elements
    1125 // since IE8 misuses the attribute by also including comments.
    1126 if (goog.dom.BrowserFeature.CAN_USE_CHILDREN_ATTRIBUTE &&
    1127 element.children != undefined) {
    1128 return element.children;
    1129 }
    1130 // Fall back to manually filtering the element's child nodes.
    1131 return goog.array.filter(element.childNodes, function(node) {
    1132 return node.nodeType == goog.dom.NodeType.ELEMENT;
    1133 });
    1134};
    1135
    1136
    1137/**
    1138 * Returns the first child node that is an element.
    1139 * @param {Node} node The node to get the first child element of.
    1140 * @return {Element} The first child node of {@code node} that is an element.
    1141 */
    1142goog.dom.getFirstElementChild = function(node) {
    1143 if (node.firstElementChild != undefined) {
    1144 return /** @type {Element} */(node).firstElementChild;
    1145 }
    1146 return goog.dom.getNextElementNode_(node.firstChild, true);
    1147};
    1148
    1149
    1150/**
    1151 * Returns the last child node that is an element.
    1152 * @param {Node} node The node to get the last child element of.
    1153 * @return {Element} The last child node of {@code node} that is an element.
    1154 */
    1155goog.dom.getLastElementChild = function(node) {
    1156 if (node.lastElementChild != undefined) {
    1157 return /** @type {Element} */(node).lastElementChild;
    1158 }
    1159 return goog.dom.getNextElementNode_(node.lastChild, false);
    1160};
    1161
    1162
    1163/**
    1164 * Returns the first next sibling that is an element.
    1165 * @param {Node} node The node to get the next sibling element of.
    1166 * @return {Element} The next sibling of {@code node} that is an element.
    1167 */
    1168goog.dom.getNextElementSibling = function(node) {
    1169 if (node.nextElementSibling != undefined) {
    1170 return /** @type {Element} */(node).nextElementSibling;
    1171 }
    1172 return goog.dom.getNextElementNode_(node.nextSibling, true);
    1173};
    1174
    1175
    1176/**
    1177 * Returns the first previous sibling that is an element.
    1178 * @param {Node} node The node to get the previous sibling element of.
    1179 * @return {Element} The first previous sibling of {@code node} that is
    1180 * an element.
    1181 */
    1182goog.dom.getPreviousElementSibling = function(node) {
    1183 if (node.previousElementSibling != undefined) {
    1184 return /** @type {Element} */(node).previousElementSibling;
    1185 }
    1186 return goog.dom.getNextElementNode_(node.previousSibling, false);
    1187};
    1188
    1189
    1190/**
    1191 * Returns the first node that is an element in the specified direction,
    1192 * starting with {@code node}.
    1193 * @param {Node} node The node to get the next element from.
    1194 * @param {boolean} forward Whether to look forwards or backwards.
    1195 * @return {Element} The first element.
    1196 * @private
    1197 */
    1198goog.dom.getNextElementNode_ = function(node, forward) {
    1199 while (node && node.nodeType != goog.dom.NodeType.ELEMENT) {
    1200 node = forward ? node.nextSibling : node.previousSibling;
    1201 }
    1202
    1203 return /** @type {Element} */ (node);
    1204};
    1205
    1206
    1207/**
    1208 * Returns the next node in source order from the given node.
    1209 * @param {Node} node The node.
    1210 * @return {Node} The next node in the DOM tree, or null if this was the last
    1211 * node.
    1212 */
    1213goog.dom.getNextNode = function(node) {
    1214 if (!node) {
    1215 return null;
    1216 }
    1217
    1218 if (node.firstChild) {
    1219 return node.firstChild;
    1220 }
    1221
    1222 while (node && !node.nextSibling) {
    1223 node = node.parentNode;
    1224 }
    1225
    1226 return node ? node.nextSibling : null;
    1227};
    1228
    1229
    1230/**
    1231 * Returns the previous node in source order from the given node.
    1232 * @param {Node} node The node.
    1233 * @return {Node} The previous node in the DOM tree, or null if this was the
    1234 * first node.
    1235 */
    1236goog.dom.getPreviousNode = function(node) {
    1237 if (!node) {
    1238 return null;
    1239 }
    1240
    1241 if (!node.previousSibling) {
    1242 return node.parentNode;
    1243 }
    1244
    1245 node = node.previousSibling;
    1246 while (node && node.lastChild) {
    1247 node = node.lastChild;
    1248 }
    1249
    1250 return node;
    1251};
    1252
    1253
    1254/**
    1255 * Whether the object looks like a DOM node.
    1256 * @param {?} obj The object being tested for node likeness.
    1257 * @return {boolean} Whether the object looks like a DOM node.
    1258 */
    1259goog.dom.isNodeLike = function(obj) {
    1260 return goog.isObject(obj) && obj.nodeType > 0;
    1261};
    1262
    1263
    1264/**
    1265 * Whether the object looks like an Element.
    1266 * @param {?} obj The object being tested for Element likeness.
    1267 * @return {boolean} Whether the object looks like an Element.
    1268 */
    1269goog.dom.isElement = function(obj) {
    1270 return goog.isObject(obj) && obj.nodeType == goog.dom.NodeType.ELEMENT;
    1271};
    1272
    1273
    1274/**
    1275 * Returns true if the specified value is a Window object. This includes the
    1276 * global window for HTML pages, and iframe windows.
    1277 * @param {?} obj Variable to test.
    1278 * @return {boolean} Whether the variable is a window.
    1279 */
    1280goog.dom.isWindow = function(obj) {
    1281 return goog.isObject(obj) && obj['window'] == obj;
    1282};
    1283
    1284
    1285/**
    1286 * Returns an element's parent, if it's an Element.
    1287 * @param {Element} element The DOM element.
    1288 * @return {Element} The parent, or null if not an Element.
    1289 */
    1290goog.dom.getParentElement = function(element) {
    1291 var parent;
    1292 if (goog.dom.BrowserFeature.CAN_USE_PARENT_ELEMENT_PROPERTY) {
    1293 var isIe9 = goog.userAgent.IE &&
    1294 goog.userAgent.isVersionOrHigher('9') &&
    1295 !goog.userAgent.isVersionOrHigher('10');
    1296 // SVG elements in IE9 can't use the parentElement property.
    1297 // goog.global['SVGElement'] is not defined in IE9 quirks mode.
    1298 if (!(isIe9 && goog.global['SVGElement'] &&
    1299 element instanceof goog.global['SVGElement'])) {
    1300 parent = element.parentElement;
    1301 if (parent) {
    1302 return parent;
    1303 }
    1304 }
    1305 }
    1306 parent = element.parentNode;
    1307 return goog.dom.isElement(parent) ? /** @type {!Element} */ (parent) : null;
    1308};
    1309
    1310
    1311/**
    1312 * Whether a node contains another node.
    1313 * @param {Node} parent The node that should contain the other node.
    1314 * @param {Node} descendant The node to test presence of.
    1315 * @return {boolean} Whether the parent node contains the descendent node.
    1316 */
    1317goog.dom.contains = function(parent, descendant) {
    1318 // We use browser specific methods for this if available since it is faster
    1319 // that way.
    1320
    1321 // IE DOM
    1322 if (parent.contains && descendant.nodeType == goog.dom.NodeType.ELEMENT) {
    1323 return parent == descendant || parent.contains(descendant);
    1324 }
    1325
    1326 // W3C DOM Level 3
    1327 if (typeof parent.compareDocumentPosition != 'undefined') {
    1328 return parent == descendant ||
    1329 Boolean(parent.compareDocumentPosition(descendant) & 16);
    1330 }
    1331
    1332 // W3C DOM Level 1
    1333 while (descendant && parent != descendant) {
    1334 descendant = descendant.parentNode;
    1335 }
    1336 return descendant == parent;
    1337};
    1338
    1339
    1340/**
    1341 * Compares the document order of two nodes, returning 0 if they are the same
    1342 * node, a negative number if node1 is before node2, and a positive number if
    1343 * node2 is before node1. Note that we compare the order the tags appear in the
    1344 * document so in the tree <b><i>text</i></b> the B node is considered to be
    1345 * before the I node.
    1346 *
    1347 * @param {Node} node1 The first node to compare.
    1348 * @param {Node} node2 The second node to compare.
    1349 * @return {number} 0 if the nodes are the same node, a negative number if node1
    1350 * is before node2, and a positive number if node2 is before node1.
    1351 */
    1352goog.dom.compareNodeOrder = function(node1, node2) {
    1353 // Fall out quickly for equality.
    1354 if (node1 == node2) {
    1355 return 0;
    1356 }
    1357
    1358 // Use compareDocumentPosition where available
    1359 if (node1.compareDocumentPosition) {
    1360 // 4 is the bitmask for FOLLOWS.
    1361 return node1.compareDocumentPosition(node2) & 2 ? 1 : -1;
    1362 }
    1363
    1364 // Special case for document nodes on IE 7 and 8.
    1365 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
    1366 if (node1.nodeType == goog.dom.NodeType.DOCUMENT) {
    1367 return -1;
    1368 }
    1369 if (node2.nodeType == goog.dom.NodeType.DOCUMENT) {
    1370 return 1;
    1371 }
    1372 }
    1373
    1374 // Process in IE using sourceIndex - we check to see if the first node has
    1375 // a source index or if its parent has one.
    1376 if ('sourceIndex' in node1 ||
    1377 (node1.parentNode && 'sourceIndex' in node1.parentNode)) {
    1378 var isElement1 = node1.nodeType == goog.dom.NodeType.ELEMENT;
    1379 var isElement2 = node2.nodeType == goog.dom.NodeType.ELEMENT;
    1380
    1381 if (isElement1 && isElement2) {
    1382 return node1.sourceIndex - node2.sourceIndex;
    1383 } else {
    1384 var parent1 = node1.parentNode;
    1385 var parent2 = node2.parentNode;
    1386
    1387 if (parent1 == parent2) {
    1388 return goog.dom.compareSiblingOrder_(node1, node2);
    1389 }
    1390
    1391 if (!isElement1 && goog.dom.contains(parent1, node2)) {
    1392 return -1 * goog.dom.compareParentsDescendantNodeIe_(node1, node2);
    1393 }
    1394
    1395
    1396 if (!isElement2 && goog.dom.contains(parent2, node1)) {
    1397 return goog.dom.compareParentsDescendantNodeIe_(node2, node1);
    1398 }
    1399
    1400 return (isElement1 ? node1.sourceIndex : parent1.sourceIndex) -
    1401 (isElement2 ? node2.sourceIndex : parent2.sourceIndex);
    1402 }
    1403 }
    1404
    1405 // For Safari, we compare ranges.
    1406 var doc = goog.dom.getOwnerDocument(node1);
    1407
    1408 var range1, range2;
    1409 range1 = doc.createRange();
    1410 range1.selectNode(node1);
    1411 range1.collapse(true);
    1412
    1413 range2 = doc.createRange();
    1414 range2.selectNode(node2);
    1415 range2.collapse(true);
    1416
    1417 return range1.compareBoundaryPoints(goog.global['Range'].START_TO_END,
    1418 range2);
    1419};
    1420
    1421
    1422/**
    1423 * Utility function to compare the position of two nodes, when
    1424 * {@code textNode}'s parent is an ancestor of {@code node}. If this entry
    1425 * condition is not met, this function will attempt to reference a null object.
    1426 * @param {Node} textNode The textNode to compare.
    1427 * @param {Node} node The node to compare.
    1428 * @return {number} -1 if node is before textNode, +1 otherwise.
    1429 * @private
    1430 */
    1431goog.dom.compareParentsDescendantNodeIe_ = function(textNode, node) {
    1432 var parent = textNode.parentNode;
    1433 if (parent == node) {
    1434 // If textNode is a child of node, then node comes first.
    1435 return -1;
    1436 }
    1437 var sibling = node;
    1438 while (sibling.parentNode != parent) {
    1439 sibling = sibling.parentNode;
    1440 }
    1441 return goog.dom.compareSiblingOrder_(sibling, textNode);
    1442};
    1443
    1444
    1445/**
    1446 * Utility function to compare the position of two nodes known to be non-equal
    1447 * siblings.
    1448 * @param {Node} node1 The first node to compare.
    1449 * @param {Node} node2 The second node to compare.
    1450 * @return {number} -1 if node1 is before node2, +1 otherwise.
    1451 * @private
    1452 */
    1453goog.dom.compareSiblingOrder_ = function(node1, node2) {
    1454 var s = node2;
    1455 while ((s = s.previousSibling)) {
    1456 if (s == node1) {
    1457 // We just found node1 before node2.
    1458 return -1;
    1459 }
    1460 }
    1461
    1462 // Since we didn't find it, node1 must be after node2.
    1463 return 1;
    1464};
    1465
    1466
    1467/**
    1468 * Find the deepest common ancestor of the given nodes.
    1469 * @param {...Node} var_args The nodes to find a common ancestor of.
    1470 * @return {Node} The common ancestor of the nodes, or null if there is none.
    1471 * null will only be returned if two or more of the nodes are from different
    1472 * documents.
    1473 */
    1474goog.dom.findCommonAncestor = function(var_args) {
    1475 var i, count = arguments.length;
    1476 if (!count) {
    1477 return null;
    1478 } else if (count == 1) {
    1479 return arguments[0];
    1480 }
    1481
    1482 var paths = [];
    1483 var minLength = Infinity;
    1484 for (i = 0; i < count; i++) {
    1485 // Compute the list of ancestors.
    1486 var ancestors = [];
    1487 var node = arguments[i];
    1488 while (node) {
    1489 ancestors.unshift(node);
    1490 node = node.parentNode;
    1491 }
    1492
    1493 // Save the list for comparison.
    1494 paths.push(ancestors);
    1495 minLength = Math.min(minLength, ancestors.length);
    1496 }
    1497 var output = null;
    1498 for (i = 0; i < minLength; i++) {
    1499 var first = paths[0][i];
    1500 for (var j = 1; j < count; j++) {
    1501 if (first != paths[j][i]) {
    1502 return output;
    1503 }
    1504 }
    1505 output = first;
    1506 }
    1507 return output;
    1508};
    1509
    1510
    1511/**
    1512 * Returns the owner document for a node.
    1513 * @param {Node|Window} node The node to get the document for.
    1514 * @return {!Document} The document owning the node.
    1515 */
    1516goog.dom.getOwnerDocument = function(node) {
    1517 // TODO(nnaze): Update param signature to be non-nullable.
    1518 goog.asserts.assert(node, 'Node cannot be null or undefined.');
    1519 return /** @type {!Document} */ (
    1520 node.nodeType == goog.dom.NodeType.DOCUMENT ? node :
    1521 node.ownerDocument || node.document);
    1522};
    1523
    1524
    1525/**
    1526 * Cross-browser function for getting the document element of a frame or iframe.
    1527 * @param {Element} frame Frame element.
    1528 * @return {!Document} The frame content document.
    1529 */
    1530goog.dom.getFrameContentDocument = function(frame) {
    1531 var doc = frame.contentDocument || frame.contentWindow.document;
    1532 return doc;
    1533};
    1534
    1535
    1536/**
    1537 * Cross-browser function for getting the window of a frame or iframe.
    1538 * @param {Element} frame Frame element.
    1539 * @return {Window} The window associated with the given frame.
    1540 */
    1541goog.dom.getFrameContentWindow = function(frame) {
    1542 return frame.contentWindow ||
    1543 goog.dom.getWindow(goog.dom.getFrameContentDocument(frame));
    1544};
    1545
    1546
    1547/**
    1548 * Sets the text content of a node, with cross-browser support.
    1549 * @param {Node} node The node to change the text content of.
    1550 * @param {string|number} text The value that should replace the node's content.
    1551 */
    1552goog.dom.setTextContent = function(node, text) {
    1553 goog.asserts.assert(node != null,
    1554 'goog.dom.setTextContent expects a non-null value for node');
    1555
    1556 if ('textContent' in node) {
    1557 node.textContent = text;
    1558 } else if (node.nodeType == goog.dom.NodeType.TEXT) {
    1559 node.data = text;
    1560 } else if (node.firstChild &&
    1561 node.firstChild.nodeType == goog.dom.NodeType.TEXT) {
    1562 // If the first child is a text node we just change its data and remove the
    1563 // rest of the children.
    1564 while (node.lastChild != node.firstChild) {
    1565 node.removeChild(node.lastChild);
    1566 }
    1567 node.firstChild.data = text;
    1568 } else {
    1569 goog.dom.removeChildren(node);
    1570 var doc = goog.dom.getOwnerDocument(node);
    1571 node.appendChild(doc.createTextNode(String(text)));
    1572 }
    1573};
    1574
    1575
    1576/**
    1577 * Gets the outerHTML of a node, which islike innerHTML, except that it
    1578 * actually contains the HTML of the node itself.
    1579 * @param {Element} element The element to get the HTML of.
    1580 * @return {string} The outerHTML of the given element.
    1581 */
    1582goog.dom.getOuterHtml = function(element) {
    1583 // IE, Opera and WebKit all have outerHTML.
    1584 if ('outerHTML' in element) {
    1585 return element.outerHTML;
    1586 } else {
    1587 var doc = goog.dom.getOwnerDocument(element);
    1588 var div = doc.createElement('div');
    1589 div.appendChild(element.cloneNode(true));
    1590 return div.innerHTML;
    1591 }
    1592};
    1593
    1594
    1595/**
    1596 * Finds the first descendant node that matches the filter function, using
    1597 * a depth first search. This function offers the most general purpose way
    1598 * of finding a matching element. You may also wish to consider
    1599 * {@code goog.dom.query} which can express many matching criteria using
    1600 * CSS selector expressions. These expressions often result in a more
    1601 * compact representation of the desired result.
    1602 * @see goog.dom.query
    1603 *
    1604 * @param {Node} root The root of the tree to search.
    1605 * @param {function(Node) : boolean} p The filter function.
    1606 * @return {Node|undefined} The found node or undefined if none is found.
    1607 */
    1608goog.dom.findNode = function(root, p) {
    1609 var rv = [];
    1610 var found = goog.dom.findNodes_(root, p, rv, true);
    1611 return found ? rv[0] : undefined;
    1612};
    1613
    1614
    1615/**
    1616 * Finds all the descendant nodes that match the filter function, using a
    1617 * a depth first search. This function offers the most general-purpose way
    1618 * of finding a set of matching elements. You may also wish to consider
    1619 * {@code goog.dom.query} which can express many matching criteria using
    1620 * CSS selector expressions. These expressions often result in a more
    1621 * compact representation of the desired result.
    1622
    1623 * @param {Node} root The root of the tree to search.
    1624 * @param {function(Node) : boolean} p The filter function.
    1625 * @return {!Array.<!Node>} The found nodes or an empty array if none are found.
    1626 */
    1627goog.dom.findNodes = function(root, p) {
    1628 var rv = [];
    1629 goog.dom.findNodes_(root, p, rv, false);
    1630 return rv;
    1631};
    1632
    1633
    1634/**
    1635 * Finds the first or all the descendant nodes that match the filter function,
    1636 * using a depth first search.
    1637 * @param {Node} root The root of the tree to search.
    1638 * @param {function(Node) : boolean} p The filter function.
    1639 * @param {!Array.<!Node>} rv The found nodes are added to this array.
    1640 * @param {boolean} findOne If true we exit after the first found node.
    1641 * @return {boolean} Whether the search is complete or not. True in case findOne
    1642 * is true and the node is found. False otherwise.
    1643 * @private
    1644 */
    1645goog.dom.findNodes_ = function(root, p, rv, findOne) {
    1646 if (root != null) {
    1647 var child = root.firstChild;
    1648 while (child) {
    1649 if (p(child)) {
    1650 rv.push(child);
    1651 if (findOne) {
    1652 return true;
    1653 }
    1654 }
    1655 if (goog.dom.findNodes_(child, p, rv, findOne)) {
    1656 return true;
    1657 }
    1658 child = child.nextSibling;
    1659 }
    1660 }
    1661 return false;
    1662};
    1663
    1664
    1665/**
    1666 * Map of tags whose content to ignore when calculating text length.
    1667 * @type {Object}
    1668 * @private
    1669 */
    1670goog.dom.TAGS_TO_IGNORE_ = {
    1671 'SCRIPT': 1,
    1672 'STYLE': 1,
    1673 'HEAD': 1,
    1674 'IFRAME': 1,
    1675 'OBJECT': 1
    1676};
    1677
    1678
    1679/**
    1680 * Map of tags which have predefined values with regard to whitespace.
    1681 * @type {Object}
    1682 * @private
    1683 */
    1684goog.dom.PREDEFINED_TAG_VALUES_ = {'IMG': ' ', 'BR': '\n'};
    1685
    1686
    1687/**
    1688 * Returns true if the element has a tab index that allows it to receive
    1689 * keyboard focus (tabIndex >= 0), false otherwise. Note that some elements
    1690 * natively support keyboard focus, even if they have no tab index.
    1691 * @param {Element} element Element to check.
    1692 * @return {boolean} Whether the element has a tab index that allows keyboard
    1693 * focus.
    1694 * @see http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
    1695 */
    1696goog.dom.isFocusableTabIndex = function(element) {
    1697 return goog.dom.hasSpecifiedTabIndex_(element) &&
    1698 goog.dom.isTabIndexFocusable_(element);
    1699};
    1700
    1701
    1702/**
    1703 * Enables or disables keyboard focus support on the element via its tab index.
    1704 * Only elements for which {@link goog.dom.isFocusableTabIndex} returns true
    1705 * (or elements that natively support keyboard focus, like form elements) can
    1706 * receive keyboard focus. See http://go/tabindex for more info.
    1707 * @param {Element} element Element whose tab index is to be changed.
    1708 * @param {boolean} enable Whether to set or remove a tab index on the element
    1709 * that supports keyboard focus.
    1710 */
    1711goog.dom.setFocusableTabIndex = function(element, enable) {
    1712 if (enable) {
    1713 element.tabIndex = 0;
    1714 } else {
    1715 // Set tabIndex to -1 first, then remove it. This is a workaround for
    1716 // Safari (confirmed in version 4 on Windows). When removing the attribute
    1717 // without setting it to -1 first, the element remains keyboard focusable
    1718 // despite not having a tabIndex attribute anymore.
    1719 element.tabIndex = -1;
    1720 element.removeAttribute('tabIndex'); // Must be camelCase!
    1721 }
    1722};
    1723
    1724
    1725/**
    1726 * Returns true if the element can be focused, i.e. it has a tab index that
    1727 * allows it to receive keyboard focus (tabIndex >= 0), or it is an element
    1728 * that natively supports keyboard focus.
    1729 * @param {Element} element Element to check.
    1730 * @return {boolean} Whether the element allows keyboard focus.
    1731 */
    1732goog.dom.isFocusable = function(element) {
    1733 var focusable;
    1734 // Some elements can have unspecified tab index and still receive focus.
    1735 if (goog.dom.nativelySupportsFocus_(element)) {
    1736 // Make sure the element is not disabled ...
    1737 focusable = !element.disabled &&
    1738 // ... and if a tab index is specified, it allows focus.
    1739 (!goog.dom.hasSpecifiedTabIndex_(element) ||
    1740 goog.dom.isTabIndexFocusable_(element));
    1741 } else {
    1742 focusable = goog.dom.isFocusableTabIndex(element);
    1743 }
    1744
    1745 // IE requires elements to be visible in order to focus them.
    1746 return focusable && goog.userAgent.IE ?
    1747 goog.dom.hasNonZeroBoundingRect_(element) : focusable;
    1748};
    1749
    1750
    1751/**
    1752 * Returns true if the element has a specified tab index.
    1753 * @param {Element} element Element to check.
    1754 * @return {boolean} Whether the element has a specified tab index.
    1755 * @private
    1756 */
    1757goog.dom.hasSpecifiedTabIndex_ = function(element) {
    1758 // IE returns 0 for an unset tabIndex, so we must use getAttributeNode(),
    1759 // which returns an object with a 'specified' property if tabIndex is
    1760 // specified. This works on other browsers, too.
    1761 var attrNode = element.getAttributeNode('tabindex'); // Must be lowercase!
    1762 return goog.isDefAndNotNull(attrNode) && attrNode.specified;
    1763};
    1764
    1765
    1766/**
    1767 * Returns true if the element's tab index allows the element to be focused.
    1768 * @param {Element} element Element to check.
    1769 * @return {boolean} Whether the element's tab index allows focus.
    1770 * @private
    1771 */
    1772goog.dom.isTabIndexFocusable_ = function(element) {
    1773 var index = element.tabIndex;
    1774 // NOTE: IE9 puts tabIndex in 16-bit int, e.g. -2 is 65534.
    1775 return goog.isNumber(index) && index >= 0 && index < 32768;
    1776};
    1777
    1778
    1779/**
    1780 * Returns true if the element is focusable even when tabIndex is not set.
    1781 * @param {Element} element Element to check.
    1782 * @return {boolean} Whether the element natively supports focus.
    1783 * @private
    1784 */
    1785goog.dom.nativelySupportsFocus_ = function(element) {
    1786 return element.tagName == goog.dom.TagName.A ||
    1787 element.tagName == goog.dom.TagName.INPUT ||
    1788 element.tagName == goog.dom.TagName.TEXTAREA ||
    1789 element.tagName == goog.dom.TagName.SELECT ||
    1790 element.tagName == goog.dom.TagName.BUTTON;
    1791};
    1792
    1793
    1794/**
    1795 * Returns true if the element has a bounding rectangle that would be visible
    1796 * (i.e. its width and height are greater than zero).
    1797 * @param {Element} element Element to check.
    1798 * @return {boolean} Whether the element has a non-zero bounding rectangle.
    1799 * @private
    1800 */
    1801goog.dom.hasNonZeroBoundingRect_ = function(element) {
    1802 var rect = goog.isFunction(element['getBoundingClientRect']) ?
    1803 element.getBoundingClientRect() :
    1804 {'height': element.offsetHeight, 'width': element.offsetWidth};
    1805 return goog.isDefAndNotNull(rect) && rect.height > 0 && rect.width > 0;
    1806};
    1807
    1808
    1809/**
    1810 * Returns the text content of the current node, without markup and invisible
    1811 * symbols. New lines are stripped and whitespace is collapsed,
    1812 * such that each character would be visible.
    1813 *
    1814 * In browsers that support it, innerText is used. Other browsers attempt to
    1815 * simulate it via node traversal. Line breaks are canonicalized in IE.
    1816 *
    1817 * @param {Node} node The node from which we are getting content.
    1818 * @return {string} The text content.
    1819 */
    1820goog.dom.getTextContent = function(node) {
    1821 var textContent;
    1822 // Note(arv): IE9, Opera, and Safari 3 support innerText but they include
    1823 // text nodes in script tags. So we revert to use a user agent test here.
    1824 if (goog.dom.BrowserFeature.CAN_USE_INNER_TEXT && ('innerText' in node)) {
    1825 textContent = goog.string.canonicalizeNewlines(node.innerText);
    1826 // Unfortunately .innerText() returns text with &shy; symbols
    1827 // We need to filter it out and then remove duplicate whitespaces
    1828 } else {
    1829 var buf = [];
    1830 goog.dom.getTextContent_(node, buf, true);
    1831 textContent = buf.join('');
    1832 }
    1833
    1834 // Strip &shy; entities. goog.format.insertWordBreaks inserts them in Opera.
    1835 textContent = textContent.replace(/ \xAD /g, ' ').replace(/\xAD/g, '');
    1836 // Strip &#8203; entities. goog.format.insertWordBreaks inserts them in IE8.
    1837 textContent = textContent.replace(/\u200B/g, '');
    1838
    1839 // Skip this replacement on old browsers with working innerText, which
    1840 // automatically turns &nbsp; into ' ' and / +/ into ' ' when reading
    1841 // innerText.
    1842 if (!goog.dom.BrowserFeature.CAN_USE_INNER_TEXT) {
    1843 textContent = textContent.replace(/ +/g, ' ');
    1844 }
    1845 if (textContent != ' ') {
    1846 textContent = textContent.replace(/^\s*/, '');
    1847 }
    1848
    1849 return textContent;
    1850};
    1851
    1852
    1853/**
    1854 * Returns the text content of the current node, without markup.
    1855 *
    1856 * Unlike {@code getTextContent} this method does not collapse whitespaces
    1857 * or normalize lines breaks.
    1858 *
    1859 * @param {Node} node The node from which we are getting content.
    1860 * @return {string} The raw text content.
    1861 */
    1862goog.dom.getRawTextContent = function(node) {
    1863 var buf = [];
    1864 goog.dom.getTextContent_(node, buf, false);
    1865
    1866 return buf.join('');
    1867};
    1868
    1869
    1870/**
    1871 * Recursive support function for text content retrieval.
    1872 *
    1873 * @param {Node} node The node from which we are getting content.
    1874 * @param {Array} buf string buffer.
    1875 * @param {boolean} normalizeWhitespace Whether to normalize whitespace.
    1876 * @private
    1877 */
    1878goog.dom.getTextContent_ = function(node, buf, normalizeWhitespace) {
    1879 if (node.nodeName in goog.dom.TAGS_TO_IGNORE_) {
    1880 // ignore certain tags
    1881 } else if (node.nodeType == goog.dom.NodeType.TEXT) {
    1882 if (normalizeWhitespace) {
    1883 buf.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, ''));
    1884 } else {
    1885 buf.push(node.nodeValue);
    1886 }
    1887 } else if (node.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {
    1888 buf.push(goog.dom.PREDEFINED_TAG_VALUES_[node.nodeName]);
    1889 } else {
    1890 var child = node.firstChild;
    1891 while (child) {
    1892 goog.dom.getTextContent_(child, buf, normalizeWhitespace);
    1893 child = child.nextSibling;
    1894 }
    1895 }
    1896};
    1897
    1898
    1899/**
    1900 * Returns the text length of the text contained in a node, without markup. This
    1901 * is equivalent to the selection length if the node was selected, or the number
    1902 * of cursor movements to traverse the node. Images & BRs take one space. New
    1903 * lines are ignored.
    1904 *
    1905 * @param {Node} node The node whose text content length is being calculated.
    1906 * @return {number} The length of {@code node}'s text content.
    1907 */
    1908goog.dom.getNodeTextLength = function(node) {
    1909 return goog.dom.getTextContent(node).length;
    1910};
    1911
    1912
    1913/**
    1914 * Returns the text offset of a node relative to one of its ancestors. The text
    1915 * length is the same as the length calculated by goog.dom.getNodeTextLength.
    1916 *
    1917 * @param {Node} node The node whose offset is being calculated.
    1918 * @param {Node=} opt_offsetParent The node relative to which the offset will
    1919 * be calculated. Defaults to the node's owner document's body.
    1920 * @return {number} The text offset.
    1921 */
    1922goog.dom.getNodeTextOffset = function(node, opt_offsetParent) {
    1923 var root = opt_offsetParent || goog.dom.getOwnerDocument(node).body;
    1924 var buf = [];
    1925 while (node && node != root) {
    1926 var cur = node;
    1927 while ((cur = cur.previousSibling)) {
    1928 buf.unshift(goog.dom.getTextContent(cur));
    1929 }
    1930 node = node.parentNode;
    1931 }
    1932 // Trim left to deal with FF cases when there might be line breaks and empty
    1933 // nodes at the front of the text
    1934 return goog.string.trimLeft(buf.join('')).replace(/ +/g, ' ').length;
    1935};
    1936
    1937
    1938/**
    1939 * Returns the node at a given offset in a parent node. If an object is
    1940 * provided for the optional third parameter, the node and the remainder of the
    1941 * offset will stored as properties of this object.
    1942 * @param {Node} parent The parent node.
    1943 * @param {number} offset The offset into the parent node.
    1944 * @param {Object=} opt_result Object to be used to store the return value. The
    1945 * return value will be stored in the form {node: Node, remainder: number}
    1946 * if this object is provided.
    1947 * @return {Node} The node at the given offset.
    1948 */
    1949goog.dom.getNodeAtOffset = function(parent, offset, opt_result) {
    1950 var stack = [parent], pos = 0, cur = null;
    1951 while (stack.length > 0 && pos < offset) {
    1952 cur = stack.pop();
    1953 if (cur.nodeName in goog.dom.TAGS_TO_IGNORE_) {
    1954 // ignore certain tags
    1955 } else if (cur.nodeType == goog.dom.NodeType.TEXT) {
    1956 var text = cur.nodeValue.replace(/(\r\n|\r|\n)/g, '').replace(/ +/g, ' ');
    1957 pos += text.length;
    1958 } else if (cur.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {
    1959 pos += goog.dom.PREDEFINED_TAG_VALUES_[cur.nodeName].length;
    1960 } else {
    1961 for (var i = cur.childNodes.length - 1; i >= 0; i--) {
    1962 stack.push(cur.childNodes[i]);
    1963 }
    1964 }
    1965 }
    1966 if (goog.isObject(opt_result)) {
    1967 opt_result.remainder = cur ? cur.nodeValue.length + offset - pos - 1 : 0;
    1968 opt_result.node = cur;
    1969 }
    1970
    1971 return cur;
    1972};
    1973
    1974
    1975/**
    1976 * Returns true if the object is a {@code NodeList}. To qualify as a NodeList,
    1977 * the object must have a numeric length property and an item function (which
    1978 * has type 'string' on IE for some reason).
    1979 * @param {Object} val Object to test.
    1980 * @return {boolean} Whether the object is a NodeList.
    1981 */
    1982goog.dom.isNodeList = function(val) {
    1983 // TODO(attila): Now the isNodeList is part of goog.dom we can use
    1984 // goog.userAgent to make this simpler.
    1985 // A NodeList must have a length property of type 'number' on all platforms.
    1986 if (val && typeof val.length == 'number') {
    1987 // A NodeList is an object everywhere except Safari, where it's a function.
    1988 if (goog.isObject(val)) {
    1989 // A NodeList must have an item function (on non-IE platforms) or an item
    1990 // property of type 'string' (on IE).
    1991 return typeof val.item == 'function' || typeof val.item == 'string';
    1992 } else if (goog.isFunction(val)) {
    1993 // On Safari, a NodeList is a function with an item property that is also
    1994 // a function.
    1995 return typeof val.item == 'function';
    1996 }
    1997 }
    1998
    1999 // Not a NodeList.
    2000 return false;
    2001};
    2002
    2003
    2004/**
    2005 * Walks up the DOM hierarchy returning the first ancestor that has the passed
    2006 * tag name and/or class name. If the passed element matches the specified
    2007 * criteria, the element itself is returned.
    2008 * @param {Node} element The DOM node to start with.
    2009 * @param {?(goog.dom.TagName|string)=} opt_tag The tag name to match (or
    2010 * null/undefined to match only based on class name).
    2011 * @param {?string=} opt_class The class name to match (or null/undefined to
    2012 * match only based on tag name).
    2013 * @return {Element} The first ancestor that matches the passed criteria, or
    2014 * null if no match is found.
    2015 */
    2016goog.dom.getAncestorByTagNameAndClass = function(element, opt_tag, opt_class) {
    2017 if (!opt_tag && !opt_class) {
    2018 return null;
    2019 }
    2020 var tagName = opt_tag ? opt_tag.toUpperCase() : null;
    2021 return /** @type {Element} */ (goog.dom.getAncestor(element,
    2022 function(node) {
    2023 return (!tagName || node.nodeName == tagName) &&
    2024 (!opt_class || goog.isString(node.className) &&
    2025 goog.array.contains(node.className.split(/\s+/), opt_class));
    2026 }, true));
    2027};
    2028
    2029
    2030/**
    2031 * Walks up the DOM hierarchy returning the first ancestor that has the passed
    2032 * class name. If the passed element matches the specified criteria, the
    2033 * element itself is returned.
    2034 * @param {Node} element The DOM node to start with.
    2035 * @param {string} className The class name to match.
    2036 * @return {Element} The first ancestor that matches the passed criteria, or
    2037 * null if none match.
    2038 */
    2039goog.dom.getAncestorByClass = function(element, className) {
    2040 return goog.dom.getAncestorByTagNameAndClass(element, null, className);
    2041};
    2042
    2043
    2044/**
    2045 * Walks up the DOM hierarchy returning the first ancestor that passes the
    2046 * matcher function.
    2047 * @param {Node} element The DOM node to start with.
    2048 * @param {function(Node) : boolean} matcher A function that returns true if the
    2049 * passed node matches the desired criteria.
    2050 * @param {boolean=} opt_includeNode If true, the node itself is included in
    2051 * the search (the first call to the matcher will pass startElement as
    2052 * the node to test).
    2053 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
    2054 * dom.
    2055 * @return {Node} DOM node that matched the matcher, or null if there was
    2056 * no match.
    2057 */
    2058goog.dom.getAncestor = function(
    2059 element, matcher, opt_includeNode, opt_maxSearchSteps) {
    2060 if (!opt_includeNode) {
    2061 element = element.parentNode;
    2062 }
    2063 var ignoreSearchSteps = opt_maxSearchSteps == null;
    2064 var steps = 0;
    2065 while (element && (ignoreSearchSteps || steps <= opt_maxSearchSteps)) {
    2066 if (matcher(element)) {
    2067 return element;
    2068 }
    2069 element = element.parentNode;
    2070 steps++;
    2071 }
    2072 // Reached the root of the DOM without a match
    2073 return null;
    2074};
    2075
    2076
    2077/**
    2078 * Determines the active element in the given document.
    2079 * @param {Document} doc The document to look in.
    2080 * @return {Element} The active element.
    2081 */
    2082goog.dom.getActiveElement = function(doc) {
    2083 try {
    2084 return doc && doc.activeElement;
    2085 } catch (e) {
    2086 // NOTE(nicksantos): Sometimes, evaluating document.activeElement in IE
    2087 // throws an exception. I'm not 100% sure why, but I suspect it chokes
    2088 // on document.activeElement if the activeElement has been recently
    2089 // removed from the DOM by a JS operation.
    2090 //
    2091 // We assume that an exception here simply means
    2092 // "there is no active element."
    2093 }
    2094
    2095 return null;
    2096};
    2097
    2098
    2099/**
    2100 * Gives the current devicePixelRatio.
    2101 *
    2102 * By default, this is the value of window.devicePixelRatio (which should be
    2103 * preferred if present).
    2104 *
    2105 * If window.devicePixelRatio is not present, the ratio is calculated with
    2106 * window.matchMedia, if present. Otherwise, gives 1.0.
    2107 *
    2108 * Some browsers (including Chrome) consider the browser zoom level in the pixel
    2109 * ratio, so the value may change across multiple calls.
    2110 *
    2111 * @return {number} The number of actual pixels per virtual pixel.
    2112 */
    2113goog.dom.getPixelRatio = function() {
    2114 var win = goog.dom.getWindow();
    2115
    2116 // devicePixelRatio does not work on Mobile firefox.
    2117 // TODO(user): Enable this check on a known working mobile Gecko version.
    2118 // Filed a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=896804
    2119 var isFirefoxMobile = goog.userAgent.GECKO && goog.userAgent.MOBILE;
    2120
    2121 if (goog.isDef(win.devicePixelRatio) && !isFirefoxMobile) {
    2122 return win.devicePixelRatio;
    2123 } else if (win.matchMedia) {
    2124 return goog.dom.matchesPixelRatio_(.75) ||
    2125 goog.dom.matchesPixelRatio_(1.5) ||
    2126 goog.dom.matchesPixelRatio_(2) ||
    2127 goog.dom.matchesPixelRatio_(3) || 1;
    2128 }
    2129 return 1;
    2130};
    2131
    2132
    2133/**
    2134 * Calculates a mediaQuery to check if the current device supports the
    2135 * given actual to virtual pixel ratio.
    2136 * @param {number} pixelRatio The ratio of actual pixels to virtual pixels.
    2137 * @return {number} pixelRatio if applicable, otherwise 0.
    2138 * @private
    2139 */
    2140goog.dom.matchesPixelRatio_ = function(pixelRatio) {
    2141 var win = goog.dom.getWindow();
    2142 var query = ('(-webkit-min-device-pixel-ratio: ' + pixelRatio + '),' +
    2143 '(min--moz-device-pixel-ratio: ' + pixelRatio + '),' +
    2144 '(min-resolution: ' + pixelRatio + 'dppx)');
    2145 return win.matchMedia(query).matches ? pixelRatio : 0;
    2146};
    2147
    2148
    2149
    2150/**
    2151 * Create an instance of a DOM helper with a new document object.
    2152 * @param {Document=} opt_document Document object to associate with this
    2153 * DOM helper.
    2154 * @constructor
    2155 */
    2156goog.dom.DomHelper = function(opt_document) {
    2157 /**
    2158 * Reference to the document object to use
    2159 * @type {!Document}
    2160 * @private
    2161 */
    2162 this.document_ = opt_document || goog.global.document || document;
    2163};
    2164
    2165
    2166/**
    2167 * Gets the dom helper object for the document where the element resides.
    2168 * @param {Node=} opt_node If present, gets the DomHelper for this node.
    2169 * @return {!goog.dom.DomHelper} The DomHelper.
    2170 */
    2171goog.dom.DomHelper.prototype.getDomHelper = goog.dom.getDomHelper;
    2172
    2173
    2174/**
    2175 * Sets the document object.
    2176 * @param {!Document} document Document object.
    2177 */
    2178goog.dom.DomHelper.prototype.setDocument = function(document) {
    2179 this.document_ = document;
    2180};
    2181
    2182
    2183/**
    2184 * Gets the document object being used by the dom library.
    2185 * @return {!Document} Document object.
    2186 */
    2187goog.dom.DomHelper.prototype.getDocument = function() {
    2188 return this.document_;
    2189};
    2190
    2191
    2192/**
    2193 * Alias for {@code getElementById}. If a DOM node is passed in then we just
    2194 * return that.
    2195 * @param {string|Element} element Element ID or a DOM node.
    2196 * @return {Element} The element with the given ID, or the node passed in.
    2197 */
    2198goog.dom.DomHelper.prototype.getElement = function(element) {
    2199 return goog.dom.getElementHelper_(this.document_, element);
    2200};
    2201
    2202
    2203/**
    2204 * Gets an element by id, asserting that the element is found.
    2205 *
    2206 * This is used when an element is expected to exist, and should fail with
    2207 * an assertion error if it does not (if assertions are enabled).
    2208 *
    2209 * @param {string} id Element ID.
    2210 * @return {!Element} The element with the given ID, if it exists.
    2211 */
    2212goog.dom.DomHelper.prototype.getRequiredElement = function(id) {
    2213 return goog.dom.getRequiredElementHelper_(this.document_, id);
    2214};
    2215
    2216
    2217/**
    2218 * Alias for {@code getElement}.
    2219 * @param {string|Element} element Element ID or a DOM node.
    2220 * @return {Element} The element with the given ID, or the node passed in.
    2221 * @deprecated Use {@link goog.dom.DomHelper.prototype.getElement} instead.
    2222 */
    2223goog.dom.DomHelper.prototype.$ = goog.dom.DomHelper.prototype.getElement;
    2224
    2225
    2226/**
    2227 * Looks up elements by both tag and class name, using browser native functions
    2228 * ({@code querySelectorAll}, {@code getElementsByTagName} or
    2229 * {@code getElementsByClassName}) where possible. The returned array is a live
    2230 * NodeList or a static list depending on the code path taken.
    2231 *
    2232 * @see goog.dom.query
    2233 *
    2234 * @param {?string=} opt_tag Element tag name or * for all tags.
    2235 * @param {?string=} opt_class Optional class name.
    2236 * @param {(Document|Element)=} opt_el Optional element to look in.
    2237 * @return { {length: number} } Array-like list of elements (only a length
    2238 * property and numerical indices are guaranteed to exist).
    2239 */
    2240goog.dom.DomHelper.prototype.getElementsByTagNameAndClass = function(opt_tag,
    2241 opt_class,
    2242 opt_el) {
    2243 return goog.dom.getElementsByTagNameAndClass_(this.document_, opt_tag,
    2244 opt_class, opt_el);
    2245};
    2246
    2247
    2248/**
    2249 * Returns an array of all the elements with the provided className.
    2250 * @see {goog.dom.query}
    2251 * @param {string} className the name of the class to look for.
    2252 * @param {Element|Document=} opt_el Optional element to look in.
    2253 * @return { {length: number} } The items found with the class name provided.
    2254 */
    2255goog.dom.DomHelper.prototype.getElementsByClass = function(className, opt_el) {
    2256 var doc = opt_el || this.document_;
    2257 return goog.dom.getElementsByClass(className, doc);
    2258};
    2259
    2260
    2261/**
    2262 * Returns the first element we find matching the provided class name.
    2263 * @see {goog.dom.query}
    2264 * @param {string} className the name of the class to look for.
    2265 * @param {(Element|Document)=} opt_el Optional element to look in.
    2266 * @return {Element} The first item found with the class name provided.
    2267 */
    2268goog.dom.DomHelper.prototype.getElementByClass = function(className, opt_el) {
    2269 var doc = opt_el || this.document_;
    2270 return goog.dom.getElementByClass(className, doc);
    2271};
    2272
    2273
    2274/**
    2275 * Ensures an element with the given className exists, and then returns the
    2276 * first element with the provided className.
    2277 * @see {goog.dom.query}
    2278 * @param {string} className the name of the class to look for.
    2279 * @param {(!Element|!Document)=} opt_root Optional element or document to look
    2280 * in.
    2281 * @return {!Element} The first item found with the class name provided.
    2282 * @throws {goog.asserts.AssertionError} Thrown if no element is found.
    2283 */
    2284goog.dom.DomHelper.prototype.getRequiredElementByClass = function(className,
    2285 opt_root) {
    2286 var root = opt_root || this.document_;
    2287 return goog.dom.getRequiredElementByClass(className, root);
    2288};
    2289
    2290
    2291/**
    2292 * Alias for {@code getElementsByTagNameAndClass}.
    2293 * @deprecated Use DomHelper getElementsByTagNameAndClass.
    2294 * @see goog.dom.query
    2295 *
    2296 * @param {?string=} opt_tag Element tag name.
    2297 * @param {?string=} opt_class Optional class name.
    2298 * @param {Element=} opt_el Optional element to look in.
    2299 * @return { {length: number} } Array-like list of elements (only a length
    2300 * property and numerical indices are guaranteed to exist).
    2301 */
    2302goog.dom.DomHelper.prototype.$$ =
    2303 goog.dom.DomHelper.prototype.getElementsByTagNameAndClass;
    2304
    2305
    2306/**
    2307 * Sets a number of properties on a node.
    2308 * @param {Element} element DOM node to set properties on.
    2309 * @param {Object} properties Hash of property:value pairs.
    2310 */
    2311goog.dom.DomHelper.prototype.setProperties = goog.dom.setProperties;
    2312
    2313
    2314/**
    2315 * Gets the dimensions of the viewport.
    2316 * @param {Window=} opt_window Optional window element to test. Defaults to
    2317 * the window of the Dom Helper.
    2318 * @return {!goog.math.Size} Object with values 'width' and 'height'.
    2319 */
    2320goog.dom.DomHelper.prototype.getViewportSize = function(opt_window) {
    2321 // TODO(arv): This should not take an argument. That breaks the rule of a
    2322 // a DomHelper representing a single frame/window/document.
    2323 return goog.dom.getViewportSize(opt_window || this.getWindow());
    2324};
    2325
    2326
    2327/**
    2328 * Calculates the height of the document.
    2329 *
    2330 * @return {number} The height of the document.
    2331 */
    2332goog.dom.DomHelper.prototype.getDocumentHeight = function() {
    2333 return goog.dom.getDocumentHeight_(this.getWindow());
    2334};
    2335
    2336
    2337/**
    2338 * Typedef for use with goog.dom.createDom and goog.dom.append.
    2339 * @typedef {Object|string|Array|NodeList}
    2340 */
    2341goog.dom.Appendable;
    2342
    2343
    2344/**
    2345 * Returns a dom node with a set of attributes. This function accepts varargs
    2346 * for subsequent nodes to be added. Subsequent nodes will be added to the
    2347 * first node as childNodes.
    2348 *
    2349 * So:
    2350 * <code>createDom('div', null, createDom('p'), createDom('p'));</code>
    2351 * would return a div with two child paragraphs
    2352 *
    2353 * An easy way to move all child nodes of an existing element to a new parent
    2354 * element is:
    2355 * <code>createDom('div', null, oldElement.childNodes);</code>
    2356 * which will remove all child nodes from the old element and add them as
    2357 * child nodes of the new DIV.
    2358 *
    2359 * @param {string} tagName Tag to create.
    2360 * @param {Object|string=} opt_attributes If object, then a map of name-value
    2361 * pairs for attributes. If a string, then this is the className of the new
    2362 * element.
    2363 * @param {...goog.dom.Appendable} var_args Further DOM nodes or
    2364 * strings for text nodes. If one of the var_args is an array or
    2365 * NodeList, its elements will be added as childNodes instead.
    2366 * @return {!Element} Reference to a DOM node.
    2367 */
    2368goog.dom.DomHelper.prototype.createDom = function(tagName,
    2369 opt_attributes,
    2370 var_args) {
    2371 return goog.dom.createDom_(this.document_, arguments);
    2372};
    2373
    2374
    2375/**
    2376 * Alias for {@code createDom}.
    2377 * @param {string} tagName Tag to create.
    2378 * @param {(Object|string)=} opt_attributes If object, then a map of name-value
    2379 * pairs for attributes. If a string, then this is the className of the new
    2380 * element.
    2381 * @param {...goog.dom.Appendable} var_args Further DOM nodes or strings for
    2382 * text nodes. If one of the var_args is an array, its children will be
    2383 * added as childNodes instead.
    2384 * @return {!Element} Reference to a DOM node.
    2385 * @deprecated Use {@link goog.dom.DomHelper.prototype.createDom} instead.
    2386 */
    2387goog.dom.DomHelper.prototype.$dom = goog.dom.DomHelper.prototype.createDom;
    2388
    2389
    2390/**
    2391 * Creates a new element.
    2392 * @param {string} name Tag name.
    2393 * @return {!Element} The new element.
    2394 */
    2395goog.dom.DomHelper.prototype.createElement = function(name) {
    2396 return this.document_.createElement(name);
    2397};
    2398
    2399
    2400/**
    2401 * Creates a new text node.
    2402 * @param {number|string} content Content.
    2403 * @return {!Text} The new text node.
    2404 */
    2405goog.dom.DomHelper.prototype.createTextNode = function(content) {
    2406 return this.document_.createTextNode(String(content));
    2407};
    2408
    2409
    2410/**
    2411 * Create a table.
    2412 * @param {number} rows The number of rows in the table. Must be >= 1.
    2413 * @param {number} columns The number of columns in the table. Must be >= 1.
    2414 * @param {boolean=} opt_fillWithNbsp If true, fills table entries with nsbps.
    2415 * @return {!Element} The created table.
    2416 */
    2417goog.dom.DomHelper.prototype.createTable = function(rows, columns,
    2418 opt_fillWithNbsp) {
    2419 return goog.dom.createTable_(this.document_, rows, columns,
    2420 !!opt_fillWithNbsp);
    2421};
    2422
    2423
    2424/**
    2425 * Converts an HTML string into a node or a document fragment. A single Node
    2426 * is used if the {@code htmlString} only generates a single node. If the
    2427 * {@code htmlString} generates multiple nodes then these are put inside a
    2428 * {@code DocumentFragment}.
    2429 *
    2430 * @param {string} htmlString The HTML string to convert.
    2431 * @return {!Node} The resulting node.
    2432 */
    2433goog.dom.DomHelper.prototype.htmlToDocumentFragment = function(htmlString) {
    2434 return goog.dom.htmlToDocumentFragment_(this.document_, htmlString);
    2435};
    2436
    2437
    2438/**
    2439 * Returns true if the browser is in "CSS1-compatible" (standards-compliant)
    2440 * mode, false otherwise.
    2441 * @return {boolean} True if in CSS1-compatible mode.
    2442 */
    2443goog.dom.DomHelper.prototype.isCss1CompatMode = function() {
    2444 return goog.dom.isCss1CompatMode_(this.document_);
    2445};
    2446
    2447
    2448/**
    2449 * Gets the window object associated with the document.
    2450 * @return {!Window} The window associated with the given document.
    2451 */
    2452goog.dom.DomHelper.prototype.getWindow = function() {
    2453 return goog.dom.getWindow_(this.document_);
    2454};
    2455
    2456
    2457/**
    2458 * Gets the document scroll element.
    2459 * @return {!Element} Scrolling element.
    2460 */
    2461goog.dom.DomHelper.prototype.getDocumentScrollElement = function() {
    2462 return goog.dom.getDocumentScrollElement_(this.document_);
    2463};
    2464
    2465
    2466/**
    2467 * Gets the document scroll distance as a coordinate object.
    2468 * @return {!goog.math.Coordinate} Object with properties 'x' and 'y'.
    2469 */
    2470goog.dom.DomHelper.prototype.getDocumentScroll = function() {
    2471 return goog.dom.getDocumentScroll_(this.document_);
    2472};
    2473
    2474
    2475/**
    2476 * Determines the active element in the given document.
    2477 * @param {Document=} opt_doc The document to look in.
    2478 * @return {Element} The active element.
    2479 */
    2480goog.dom.DomHelper.prototype.getActiveElement = function(opt_doc) {
    2481 return goog.dom.getActiveElement(opt_doc || this.document_);
    2482};
    2483
    2484
    2485/**
    2486 * Appends a child to a node.
    2487 * @param {Node} parent Parent.
    2488 * @param {Node} child Child.
    2489 */
    2490goog.dom.DomHelper.prototype.appendChild = goog.dom.appendChild;
    2491
    2492
    2493/**
    2494 * Appends a node with text or other nodes.
    2495 * @param {!Node} parent The node to append nodes to.
    2496 * @param {...goog.dom.Appendable} var_args The things to append to the node.
    2497 * If this is a Node it is appended as is.
    2498 * If this is a string then a text node is appended.
    2499 * If this is an array like object then fields 0 to length - 1 are appended.
    2500 */
    2501goog.dom.DomHelper.prototype.append = goog.dom.append;
    2502
    2503
    2504/**
    2505 * Determines if the given node can contain children, intended to be used for
    2506 * HTML generation.
    2507 *
    2508 * @param {Node} node The node to check.
    2509 * @return {boolean} Whether the node can contain children.
    2510 */
    2511goog.dom.DomHelper.prototype.canHaveChildren = goog.dom.canHaveChildren;
    2512
    2513
    2514/**
    2515 * Removes all the child nodes on a DOM node.
    2516 * @param {Node} node Node to remove children from.
    2517 */
    2518goog.dom.DomHelper.prototype.removeChildren = goog.dom.removeChildren;
    2519
    2520
    2521/**
    2522 * Inserts a new node before an existing reference node (i.e., as the previous
    2523 * sibling). If the reference node has no parent, then does nothing.
    2524 * @param {Node} newNode Node to insert.
    2525 * @param {Node} refNode Reference node to insert before.
    2526 */
    2527goog.dom.DomHelper.prototype.insertSiblingBefore = goog.dom.insertSiblingBefore;
    2528
    2529
    2530/**
    2531 * Inserts a new node after an existing reference node (i.e., as the next
    2532 * sibling). If the reference node has no parent, then does nothing.
    2533 * @param {Node} newNode Node to insert.
    2534 * @param {Node} refNode Reference node to insert after.
    2535 */
    2536goog.dom.DomHelper.prototype.insertSiblingAfter = goog.dom.insertSiblingAfter;
    2537
    2538
    2539/**
    2540 * Insert a child at a given index. If index is larger than the number of child
    2541 * nodes that the parent currently has, the node is inserted as the last child
    2542 * node.
    2543 * @param {Element} parent The element into which to insert the child.
    2544 * @param {Node} child The element to insert.
    2545 * @param {number} index The index at which to insert the new child node. Must
    2546 * not be negative.
    2547 */
    2548goog.dom.DomHelper.prototype.insertChildAt = goog.dom.insertChildAt;
    2549
    2550
    2551/**
    2552 * Removes a node from its parent.
    2553 * @param {Node} node The node to remove.
    2554 * @return {Node} The node removed if removed; else, null.
    2555 */
    2556goog.dom.DomHelper.prototype.removeNode = goog.dom.removeNode;
    2557
    2558
    2559/**
    2560 * Replaces a node in the DOM tree. Will do nothing if {@code oldNode} has no
    2561 * parent.
    2562 * @param {Node} newNode Node to insert.
    2563 * @param {Node} oldNode Node to replace.
    2564 */
    2565goog.dom.DomHelper.prototype.replaceNode = goog.dom.replaceNode;
    2566
    2567
    2568/**
    2569 * Flattens an element. That is, removes it and replace it with its children.
    2570 * @param {Element} element The element to flatten.
    2571 * @return {Element|undefined} The original element, detached from the document
    2572 * tree, sans children, or undefined if the element was already not in the
    2573 * document.
    2574 */
    2575goog.dom.DomHelper.prototype.flattenElement = goog.dom.flattenElement;
    2576
    2577
    2578/**
    2579 * Returns an array containing just the element children of the given element.
    2580 * @param {Element} element The element whose element children we want.
    2581 * @return {!(Array|NodeList)} An array or array-like list of just the element
    2582 * children of the given element.
    2583 */
    2584goog.dom.DomHelper.prototype.getChildren = goog.dom.getChildren;
    2585
    2586
    2587/**
    2588 * Returns the first child node that is an element.
    2589 * @param {Node} node The node to get the first child element of.
    2590 * @return {Element} The first child node of {@code node} that is an element.
    2591 */
    2592goog.dom.DomHelper.prototype.getFirstElementChild =
    2593 goog.dom.getFirstElementChild;
    2594
    2595
    2596/**
    2597 * Returns the last child node that is an element.
    2598 * @param {Node} node The node to get the last child element of.
    2599 * @return {Element} The last child node of {@code node} that is an element.
    2600 */
    2601goog.dom.DomHelper.prototype.getLastElementChild = goog.dom.getLastElementChild;
    2602
    2603
    2604/**
    2605 * Returns the first next sibling that is an element.
    2606 * @param {Node} node The node to get the next sibling element of.
    2607 * @return {Element} The next sibling of {@code node} that is an element.
    2608 */
    2609goog.dom.DomHelper.prototype.getNextElementSibling =
    2610 goog.dom.getNextElementSibling;
    2611
    2612
    2613/**
    2614 * Returns the first previous sibling that is an element.
    2615 * @param {Node} node The node to get the previous sibling element of.
    2616 * @return {Element} The first previous sibling of {@code node} that is
    2617 * an element.
    2618 */
    2619goog.dom.DomHelper.prototype.getPreviousElementSibling =
    2620 goog.dom.getPreviousElementSibling;
    2621
    2622
    2623/**
    2624 * Returns the next node in source order from the given node.
    2625 * @param {Node} node The node.
    2626 * @return {Node} The next node in the DOM tree, or null if this was the last
    2627 * node.
    2628 */
    2629goog.dom.DomHelper.prototype.getNextNode = goog.dom.getNextNode;
    2630
    2631
    2632/**
    2633 * Returns the previous node in source order from the given node.
    2634 * @param {Node} node The node.
    2635 * @return {Node} The previous node in the DOM tree, or null if this was the
    2636 * first node.
    2637 */
    2638goog.dom.DomHelper.prototype.getPreviousNode = goog.dom.getPreviousNode;
    2639
    2640
    2641/**
    2642 * Whether the object looks like a DOM node.
    2643 * @param {?} obj The object being tested for node likeness.
    2644 * @return {boolean} Whether the object looks like a DOM node.
    2645 */
    2646goog.dom.DomHelper.prototype.isNodeLike = goog.dom.isNodeLike;
    2647
    2648
    2649/**
    2650 * Whether the object looks like an Element.
    2651 * @param {?} obj The object being tested for Element likeness.
    2652 * @return {boolean} Whether the object looks like an Element.
    2653 */
    2654goog.dom.DomHelper.prototype.isElement = goog.dom.isElement;
    2655
    2656
    2657/**
    2658 * Returns true if the specified value is a Window object. This includes the
    2659 * global window for HTML pages, and iframe windows.
    2660 * @param {?} obj Variable to test.
    2661 * @return {boolean} Whether the variable is a window.
    2662 */
    2663goog.dom.DomHelper.prototype.isWindow = goog.dom.isWindow;
    2664
    2665
    2666/**
    2667 * Returns an element's parent, if it's an Element.
    2668 * @param {Element} element The DOM element.
    2669 * @return {Element} The parent, or null if not an Element.
    2670 */
    2671goog.dom.DomHelper.prototype.getParentElement = goog.dom.getParentElement;
    2672
    2673
    2674/**
    2675 * Whether a node contains another node.
    2676 * @param {Node} parent The node that should contain the other node.
    2677 * @param {Node} descendant The node to test presence of.
    2678 * @return {boolean} Whether the parent node contains the descendent node.
    2679 */
    2680goog.dom.DomHelper.prototype.contains = goog.dom.contains;
    2681
    2682
    2683/**
    2684 * Compares the document order of two nodes, returning 0 if they are the same
    2685 * node, a negative number if node1 is before node2, and a positive number if
    2686 * node2 is before node1. Note that we compare the order the tags appear in the
    2687 * document so in the tree <b><i>text</i></b> the B node is considered to be
    2688 * before the I node.
    2689 *
    2690 * @param {Node} node1 The first node to compare.
    2691 * @param {Node} node2 The second node to compare.
    2692 * @return {number} 0 if the nodes are the same node, a negative number if node1
    2693 * is before node2, and a positive number if node2 is before node1.
    2694 */
    2695goog.dom.DomHelper.prototype.compareNodeOrder = goog.dom.compareNodeOrder;
    2696
    2697
    2698/**
    2699 * Find the deepest common ancestor of the given nodes.
    2700 * @param {...Node} var_args The nodes to find a common ancestor of.
    2701 * @return {Node} The common ancestor of the nodes, or null if there is none.
    2702 * null will only be returned if two or more of the nodes are from different
    2703 * documents.
    2704 */
    2705goog.dom.DomHelper.prototype.findCommonAncestor = goog.dom.findCommonAncestor;
    2706
    2707
    2708/**
    2709 * Returns the owner document for a node.
    2710 * @param {Node} node The node to get the document for.
    2711 * @return {!Document} The document owning the node.
    2712 */
    2713goog.dom.DomHelper.prototype.getOwnerDocument = goog.dom.getOwnerDocument;
    2714
    2715
    2716/**
    2717 * Cross browser function for getting the document element of an iframe.
    2718 * @param {Element} iframe Iframe element.
    2719 * @return {!Document} The frame content document.
    2720 */
    2721goog.dom.DomHelper.prototype.getFrameContentDocument =
    2722 goog.dom.getFrameContentDocument;
    2723
    2724
    2725/**
    2726 * Cross browser function for getting the window of a frame or iframe.
    2727 * @param {Element} frame Frame element.
    2728 * @return {Window} The window associated with the given frame.
    2729 */
    2730goog.dom.DomHelper.prototype.getFrameContentWindow =
    2731 goog.dom.getFrameContentWindow;
    2732
    2733
    2734/**
    2735 * Sets the text content of a node, with cross-browser support.
    2736 * @param {Node} node The node to change the text content of.
    2737 * @param {string|number} text The value that should replace the node's content.
    2738 */
    2739goog.dom.DomHelper.prototype.setTextContent = goog.dom.setTextContent;
    2740
    2741
    2742/**
    2743 * Gets the outerHTML of a node, which islike innerHTML, except that it
    2744 * actually contains the HTML of the node itself.
    2745 * @param {Element} element The element to get the HTML of.
    2746 * @return {string} The outerHTML of the given element.
    2747 */
    2748goog.dom.DomHelper.prototype.getOuterHtml = goog.dom.getOuterHtml;
    2749
    2750
    2751/**
    2752 * Finds the first descendant node that matches the filter function. This does
    2753 * a depth first search.
    2754 * @param {Node} root The root of the tree to search.
    2755 * @param {function(Node) : boolean} p The filter function.
    2756 * @return {Node|undefined} The found node or undefined if none is found.
    2757 */
    2758goog.dom.DomHelper.prototype.findNode = goog.dom.findNode;
    2759
    2760
    2761/**
    2762 * Finds all the descendant nodes that matches the filter function. This does a
    2763 * depth first search.
    2764 * @param {Node} root The root of the tree to search.
    2765 * @param {function(Node) : boolean} p The filter function.
    2766 * @return {Array.<Node>} The found nodes or an empty array if none are found.
    2767 */
    2768goog.dom.DomHelper.prototype.findNodes = goog.dom.findNodes;
    2769
    2770
    2771/**
    2772 * Returns true if the element has a tab index that allows it to receive
    2773 * keyboard focus (tabIndex >= 0), false otherwise. Note that some elements
    2774 * natively support keyboard focus, even if they have no tab index.
    2775 * @param {Element} element Element to check.
    2776 * @return {boolean} Whether the element has a tab index that allows keyboard
    2777 * focus.
    2778 */
    2779goog.dom.DomHelper.prototype.isFocusableTabIndex = goog.dom.isFocusableTabIndex;
    2780
    2781
    2782/**
    2783 * Enables or disables keyboard focus support on the element via its tab index.
    2784 * Only elements for which {@link goog.dom.isFocusableTabIndex} returns true
    2785 * (or elements that natively support keyboard focus, like form elements) can
    2786 * receive keyboard focus. See http://go/tabindex for more info.
    2787 * @param {Element} element Element whose tab index is to be changed.
    2788 * @param {boolean} enable Whether to set or remove a tab index on the element
    2789 * that supports keyboard focus.
    2790 */
    2791goog.dom.DomHelper.prototype.setFocusableTabIndex =
    2792 goog.dom.setFocusableTabIndex;
    2793
    2794
    2795/**
    2796 * Returns true if the element can be focused, i.e. it has a tab index that
    2797 * allows it to receive keyboard focus (tabIndex >= 0), or it is an element
    2798 * that natively supports keyboard focus.
    2799 * @param {Element} element Element to check.
    2800 * @return {boolean} Whether the element allows keyboard focus.
    2801 */
    2802goog.dom.DomHelper.prototype.isFocusable = goog.dom.isFocusable;
    2803
    2804
    2805/**
    2806 * Returns the text contents of the current node, without markup. New lines are
    2807 * stripped and whitespace is collapsed, such that each character would be
    2808 * visible.
    2809 *
    2810 * In browsers that support it, innerText is used. Other browsers attempt to
    2811 * simulate it via node traversal. Line breaks are canonicalized in IE.
    2812 *
    2813 * @param {Node} node The node from which we are getting content.
    2814 * @return {string} The text content.
    2815 */
    2816goog.dom.DomHelper.prototype.getTextContent = goog.dom.getTextContent;
    2817
    2818
    2819/**
    2820 * Returns the text length of the text contained in a node, without markup. This
    2821 * is equivalent to the selection length if the node was selected, or the number
    2822 * of cursor movements to traverse the node. Images & BRs take one space. New
    2823 * lines are ignored.
    2824 *
    2825 * @param {Node} node The node whose text content length is being calculated.
    2826 * @return {number} The length of {@code node}'s text content.
    2827 */
    2828goog.dom.DomHelper.prototype.getNodeTextLength = goog.dom.getNodeTextLength;
    2829
    2830
    2831/**
    2832 * Returns the text offset of a node relative to one of its ancestors. The text
    2833 * length is the same as the length calculated by
    2834 * {@code goog.dom.getNodeTextLength}.
    2835 *
    2836 * @param {Node} node The node whose offset is being calculated.
    2837 * @param {Node=} opt_offsetParent Defaults to the node's owner document's body.
    2838 * @return {number} The text offset.
    2839 */
    2840goog.dom.DomHelper.prototype.getNodeTextOffset = goog.dom.getNodeTextOffset;
    2841
    2842
    2843/**
    2844 * Returns the node at a given offset in a parent node. If an object is
    2845 * provided for the optional third parameter, the node and the remainder of the
    2846 * offset will stored as properties of this object.
    2847 * @param {Node} parent The parent node.
    2848 * @param {number} offset The offset into the parent node.
    2849 * @param {Object=} opt_result Object to be used to store the return value. The
    2850 * return value will be stored in the form {node: Node, remainder: number}
    2851 * if this object is provided.
    2852 * @return {Node} The node at the given offset.
    2853 */
    2854goog.dom.DomHelper.prototype.getNodeAtOffset = goog.dom.getNodeAtOffset;
    2855
    2856
    2857/**
    2858 * Returns true if the object is a {@code NodeList}. To qualify as a NodeList,
    2859 * the object must have a numeric length property and an item function (which
    2860 * has type 'string' on IE for some reason).
    2861 * @param {Object} val Object to test.
    2862 * @return {boolean} Whether the object is a NodeList.
    2863 */
    2864goog.dom.DomHelper.prototype.isNodeList = goog.dom.isNodeList;
    2865
    2866
    2867/**
    2868 * Walks up the DOM hierarchy returning the first ancestor that has the passed
    2869 * tag name and/or class name. If the passed element matches the specified
    2870 * criteria, the element itself is returned.
    2871 * @param {Node} element The DOM node to start with.
    2872 * @param {?(goog.dom.TagName|string)=} opt_tag The tag name to match (or
    2873 * null/undefined to match only based on class name).
    2874 * @param {?string=} opt_class The class name to match (or null/undefined to
    2875 * match only based on tag name).
    2876 * @return {Element} The first ancestor that matches the passed criteria, or
    2877 * null if no match is found.
    2878 */
    2879goog.dom.DomHelper.prototype.getAncestorByTagNameAndClass =
    2880 goog.dom.getAncestorByTagNameAndClass;
    2881
    2882
    2883/**
    2884 * Walks up the DOM hierarchy returning the first ancestor that has the passed
    2885 * class name. If the passed element matches the specified criteria, the
    2886 * element itself is returned.
    2887 * @param {Node} element The DOM node to start with.
    2888 * @param {string} class The class name to match.
    2889 * @return {Element} The first ancestor that matches the passed criteria, or
    2890 * null if none match.
    2891 */
    2892goog.dom.DomHelper.prototype.getAncestorByClass =
    2893 goog.dom.getAncestorByClass;
    2894
    2895
    2896/**
    2897 * Walks up the DOM hierarchy returning the first ancestor that passes the
    2898 * matcher function.
    2899 * @param {Node} element The DOM node to start with.
    2900 * @param {function(Node) : boolean} matcher A function that returns true if the
    2901 * passed node matches the desired criteria.
    2902 * @param {boolean=} opt_includeNode If true, the node itself is included in
    2903 * the search (the first call to the matcher will pass startElement as
    2904 * the node to test).
    2905 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
    2906 * dom.
    2907 * @return {Node} DOM node that matched the matcher, or null if there was
    2908 * no match.
    2909 */
    2910goog.dom.DomHelper.prototype.getAncestor = goog.dom.getAncestor;
    \ No newline at end of file +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 (key in goog.dom.DIRECT_ATTRIBUTE_MAP_) {
    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 = 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,i
    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: http://msdn.microsoft.com/workshop/author/
    709 // dhtml/reference/properties/name_2.asp
    710 // Also does not allow setting of 'type' attribute on 'input' or 'button'.
    711 if (!goog.dom.BrowserFeature.CAN_ADD_NAME_OR_TYPE_ATTRIBUTES && attributes &&
    712 (attributes.name || attributes.type)) {
    713 var tagNameArr = ['<', tagName];
    714 if (attributes.name) {
    715 tagNameArr.push(' name="', goog.string.htmlEscape(attributes.name),
    716 '"');
    717 }
    718 if (attributes.type) {
    719 tagNameArr.push(' type="', goog.string.htmlEscape(attributes.type),
    720 '"');
    721
    722 // Clone attributes map to remove 'type' without mutating the input.
    723 var clone = {};
    724 goog.object.extend(clone, attributes);
    725
    726 // JSCompiler can't see how goog.object.extend added this property,
    727 // because it was essentially added by reflection.
    728 // So it needs to be quoted.
    729 delete clone['type'];
    730
    731 attributes = clone;
    732 }
    733 tagNameArr.push('>');
    734 tagName = tagNameArr.join('');
    735 }
    736
    737 var element = doc.createElement(tagName);
    738
    739 if (attributes) {
    740 if (goog.isString(attributes)) {
    741 element.className = attributes;
    742 } else if (goog.isArray(attributes)) {
    743 element.className = attributes.join(' ');
    744 } else {
    745 goog.dom.setProperties(element, attributes);
    746 }
    747 }
    748
    749 if (args.length > 2) {
    750 goog.dom.append_(doc, element, args, 2);
    751 }
    752
    753 return element;
    754};
    755
    756
    757/**
    758 * Appends a node with text or other nodes.
    759 * @param {!Document} doc The document to create new nodes in.
    760 * @param {!Node} parent The node to append nodes to.
    761 * @param {!Arguments} args The values to add. See {@code goog.dom.append}.
    762 * @param {number} startIndex The index of the array to start from.
    763 * @private
    764 */
    765goog.dom.append_ = function(doc, parent, args, startIndex) {
    766 function childHandler(child) {
    767 // TODO(user): More coercion, ala MochiKit?
    768 if (child) {
    769 parent.appendChild(goog.isString(child) ?
    770 doc.createTextNode(child) : child);
    771 }
    772 }
    773
    774 for (var i = startIndex; i < args.length; i++) {
    775 var arg = args[i];
    776 // TODO(attila): Fix isArrayLike to return false for a text node.
    777 if (goog.isArrayLike(arg) && !goog.dom.isNodeLike(arg)) {
    778 // If the argument is a node list, not a real array, use a clone,
    779 // because forEach can't be used to mutate a NodeList.
    780 goog.array.forEach(goog.dom.isNodeList(arg) ?
    781 goog.array.toArray(arg) : arg,
    782 childHandler);
    783 } else {
    784 childHandler(arg);
    785 }
    786 }
    787};
    788
    789
    790/**
    791 * Alias for {@code createDom}.
    792 * @param {string} tagName Tag to create.
    793 * @param {(string|Object)=} opt_attributes If object, then a map of name-value
    794 * pairs for attributes. If a string, then this is the className of the new
    795 * element.
    796 * @param {...(Object|string|Array|NodeList)} var_args Further DOM nodes or
    797 * strings for text nodes. If one of the var_args is an array, its
    798 * children will be added as childNodes instead.
    799 * @return {!Element} Reference to a DOM node.
    800 * @deprecated Use {@link goog.dom.createDom} instead.
    801 */
    802goog.dom.$dom = goog.dom.createDom;
    803
    804
    805/**
    806 * Creates a new element.
    807 * @param {string} name Tag name.
    808 * @return {!Element} The new element.
    809 */
    810goog.dom.createElement = function(name) {
    811 return document.createElement(name);
    812};
    813
    814
    815/**
    816 * Creates a new text node.
    817 * @param {number|string} content Content.
    818 * @return {!Text} The new text node.
    819 */
    820goog.dom.createTextNode = function(content) {
    821 return document.createTextNode(String(content));
    822};
    823
    824
    825/**
    826 * Create a table.
    827 * @param {number} rows The number of rows in the table. Must be >= 1.
    828 * @param {number} columns The number of columns in the table. Must be >= 1.
    829 * @param {boolean=} opt_fillWithNbsp If true, fills table entries with
    830 * {@code goog.string.Unicode.NBSP} characters.
    831 * @return {!Element} The created table.
    832 */
    833goog.dom.createTable = function(rows, columns, opt_fillWithNbsp) {
    834 // TODO(user): Return HTMLTableElement, also in prototype function.
    835 // Callers need to be updated to e.g. not assign numbers to table.cellSpacing.
    836 return goog.dom.createTable_(document, rows, columns, !!opt_fillWithNbsp);
    837};
    838
    839
    840/**
    841 * Create a table.
    842 * @param {!Document} doc Document object to use to create the table.
    843 * @param {number} rows The number of rows in the table. Must be >= 1.
    844 * @param {number} columns The number of columns in the table. Must be >= 1.
    845 * @param {boolean} fillWithNbsp If true, fills table entries with
    846 * {@code goog.string.Unicode.NBSP} characters.
    847 * @return {!HTMLTableElement} The created table.
    848 * @private
    849 */
    850goog.dom.createTable_ = function(doc, rows, columns, fillWithNbsp) {
    851 var table = /** @type {!HTMLTableElement} */
    852 (doc.createElement(goog.dom.TagName.TABLE));
    853 var tbody = table.appendChild(doc.createElement(goog.dom.TagName.TBODY));
    854 for (var i = 0; i < rows; i++) {
    855 var tr = doc.createElement(goog.dom.TagName.TR);
    856 for (var j = 0; j < columns; j++) {
    857 var td = doc.createElement(goog.dom.TagName.TD);
    858 // IE <= 9 will create a text node if we set text content to the empty
    859 // string, so we avoid doing it unless necessary. This ensures that the
    860 // same DOM tree is returned on all browsers.
    861 if (fillWithNbsp) {
    862 goog.dom.setTextContent(td, goog.string.Unicode.NBSP);
    863 }
    864 tr.appendChild(td);
    865 }
    866 tbody.appendChild(tr);
    867 }
    868 return table;
    869};
    870
    871
    872/**
    873 * Converts HTML markup into a node.
    874 * @param {!goog.html.SafeHtml} html The HTML markup to convert.
    875 * @return {!Node} The resulting node.
    876 */
    877goog.dom.safeHtmlToNode = function(html) {
    878 return goog.dom.safeHtmlToNode_(document, html);
    879};
    880
    881
    882/**
    883 * Helper for {@code safeHtmlToNode}.
    884 * @param {!Document} doc The document.
    885 * @param {!goog.html.SafeHtml} html The HTML markup to convert.
    886 * @return {!Node} The resulting node.
    887 * @private
    888 */
    889goog.dom.safeHtmlToNode_ = function(doc, html) {
    890 var tempDiv = doc.createElement(goog.dom.TagName.DIV);
    891 if (goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT) {
    892 goog.dom.safe.setInnerHtml(tempDiv,
    893 goog.html.SafeHtml.concat(goog.html.SafeHtml.create('br'), html));
    894 tempDiv.removeChild(tempDiv.firstChild);
    895 } else {
    896 goog.dom.safe.setInnerHtml(tempDiv, html);
    897 }
    898 return goog.dom.childrenToNode_(doc, tempDiv);
    899};
    900
    901
    902/**
    903 * Converts an HTML string into a document fragment. The string must be
    904 * sanitized in order to avoid cross-site scripting. For example
    905 * {@code goog.dom.htmlToDocumentFragment('&lt;img src=x onerror=alert(0)&gt;')}
    906 * triggers an alert in all browsers, even if the returned document fragment
    907 * is thrown away immediately.
    908 *
    909 * @param {string} htmlString The HTML string to convert.
    910 * @return {!Node} The resulting document fragment.
    911 */
    912goog.dom.htmlToDocumentFragment = function(htmlString) {
    913 return goog.dom.htmlToDocumentFragment_(document, htmlString);
    914};
    915
    916
    917// TODO(jakubvrana): Merge with {@code safeHtmlToNode_}.
    918/**
    919 * Helper for {@code htmlToDocumentFragment}.
    920 *
    921 * @param {!Document} doc The document.
    922 * @param {string} htmlString The HTML string to convert.
    923 * @return {!Node} The resulting document fragment.
    924 * @private
    925 */
    926goog.dom.htmlToDocumentFragment_ = function(doc, htmlString) {
    927 var tempDiv = doc.createElement(goog.dom.TagName.DIV);
    928 if (goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT) {
    929 tempDiv.innerHTML = '<br>' + htmlString;
    930 tempDiv.removeChild(tempDiv.firstChild);
    931 } else {
    932 tempDiv.innerHTML = htmlString;
    933 }
    934 return goog.dom.childrenToNode_(doc, tempDiv);
    935};
    936
    937
    938/**
    939 * Helper for {@code htmlToDocumentFragment_}.
    940 * @param {!Document} doc The document.
    941 * @param {!Node} tempDiv The input node.
    942 * @return {!Node} The resulting node.
    943 * @private
    944 */
    945goog.dom.childrenToNode_ = function(doc, tempDiv) {
    946 if (tempDiv.childNodes.length == 1) {
    947 return tempDiv.removeChild(tempDiv.firstChild);
    948 } else {
    949 var fragment = doc.createDocumentFragment();
    950 while (tempDiv.firstChild) {
    951 fragment.appendChild(tempDiv.firstChild);
    952 }
    953 return fragment;
    954 }
    955};
    956
    957
    958/**
    959 * Returns true if the browser is in "CSS1-compatible" (standards-compliant)
    960 * mode, false otherwise.
    961 * @return {boolean} True if in CSS1-compatible mode.
    962 */
    963goog.dom.isCss1CompatMode = function() {
    964 return goog.dom.isCss1CompatMode_(document);
    965};
    966
    967
    968/**
    969 * Returns true if the browser is in "CSS1-compatible" (standards-compliant)
    970 * mode, false otherwise.
    971 * @param {!Document} doc The document to check.
    972 * @return {boolean} True if in CSS1-compatible mode.
    973 * @private
    974 */
    975goog.dom.isCss1CompatMode_ = function(doc) {
    976 if (goog.dom.COMPAT_MODE_KNOWN_) {
    977 return goog.dom.ASSUME_STANDARDS_MODE;
    978 }
    979
    980 return doc.compatMode == 'CSS1Compat';
    981};
    982
    983
    984/**
    985 * Determines if the given node can contain children, intended to be used for
    986 * HTML generation.
    987 *
    988 * IE natively supports node.canHaveChildren but has inconsistent behavior.
    989 * Prior to IE8 the base tag allows children and in IE9 all nodes return true
    990 * for canHaveChildren.
    991 *
    992 * In practice all non-IE browsers allow you to add children to any node, but
    993 * the behavior is inconsistent:
    994 *
    995 * <pre>
    996 * var a = document.createElement(goog.dom.TagName.BR);
    997 * a.appendChild(document.createTextNode('foo'));
    998 * a.appendChild(document.createTextNode('bar'));
    999 * console.log(a.childNodes.length); // 2
    1000 * console.log(a.innerHTML); // Chrome: "", IE9: "foobar", FF3.5: "foobar"
    1001 * </pre>
    1002 *
    1003 * For more information, see:
    1004 * http://dev.w3.org/html5/markup/syntax.html#syntax-elements
    1005 *
    1006 * TODO(user): Rename shouldAllowChildren() ?
    1007 *
    1008 * @param {Node} node The node to check.
    1009 * @return {boolean} Whether the node can contain children.
    1010 */
    1011goog.dom.canHaveChildren = function(node) {
    1012 if (node.nodeType != goog.dom.NodeType.ELEMENT) {
    1013 return false;
    1014 }
    1015 switch (node.tagName) {
    1016 case goog.dom.TagName.APPLET:
    1017 case goog.dom.TagName.AREA:
    1018 case goog.dom.TagName.BASE:
    1019 case goog.dom.TagName.BR:
    1020 case goog.dom.TagName.COL:
    1021 case goog.dom.TagName.COMMAND:
    1022 case goog.dom.TagName.EMBED:
    1023 case goog.dom.TagName.FRAME:
    1024 case goog.dom.TagName.HR:
    1025 case goog.dom.TagName.IMG:
    1026 case goog.dom.TagName.INPUT:
    1027 case goog.dom.TagName.IFRAME:
    1028 case goog.dom.TagName.ISINDEX:
    1029 case goog.dom.TagName.KEYGEN:
    1030 case goog.dom.TagName.LINK:
    1031 case goog.dom.TagName.NOFRAMES:
    1032 case goog.dom.TagName.NOSCRIPT:
    1033 case goog.dom.TagName.META:
    1034 case goog.dom.TagName.OBJECT:
    1035 case goog.dom.TagName.PARAM:
    1036 case goog.dom.TagName.SCRIPT:
    1037 case goog.dom.TagName.SOURCE:
    1038 case goog.dom.TagName.STYLE:
    1039 case goog.dom.TagName.TRACK:
    1040 case goog.dom.TagName.WBR:
    1041 return false;
    1042 }
    1043 return true;
    1044};
    1045
    1046
    1047/**
    1048 * Appends a child to a node.
    1049 * @param {Node} parent Parent.
    1050 * @param {Node} child Child.
    1051 */
    1052goog.dom.appendChild = function(parent, child) {
    1053 parent.appendChild(child);
    1054};
    1055
    1056
    1057/**
    1058 * Appends a node with text or other nodes.
    1059 * @param {!Node} parent The node to append nodes to.
    1060 * @param {...goog.dom.Appendable} var_args The things to append to the node.
    1061 * If this is a Node it is appended as is.
    1062 * If this is a string then a text node is appended.
    1063 * If this is an array like object then fields 0 to length - 1 are appended.
    1064 */
    1065goog.dom.append = function(parent, var_args) {
    1066 goog.dom.append_(goog.dom.getOwnerDocument(parent), parent, arguments, 1);
    1067};
    1068
    1069
    1070/**
    1071 * Removes all the child nodes on a DOM node.
    1072 * @param {Node} node Node to remove children from.
    1073 */
    1074goog.dom.removeChildren = function(node) {
    1075 // Note: Iterations over live collections can be slow, this is the fastest
    1076 // we could find. The double parenthesis are used to prevent JsCompiler and
    1077 // strict warnings.
    1078 var child;
    1079 while ((child = node.firstChild)) {
    1080 node.removeChild(child);
    1081 }
    1082};
    1083
    1084
    1085/**
    1086 * Inserts a new node before an existing reference node (i.e. as the previous
    1087 * sibling). If the reference node has no parent, then does nothing.
    1088 * @param {Node} newNode Node to insert.
    1089 * @param {Node} refNode Reference node to insert before.
    1090 */
    1091goog.dom.insertSiblingBefore = function(newNode, refNode) {
    1092 if (refNode.parentNode) {
    1093 refNode.parentNode.insertBefore(newNode, refNode);
    1094 }
    1095};
    1096
    1097
    1098/**
    1099 * Inserts a new node after an existing reference node (i.e. as the next
    1100 * sibling). If the reference node has no parent, then does nothing.
    1101 * @param {Node} newNode Node to insert.
    1102 * @param {Node} refNode Reference node to insert after.
    1103 */
    1104goog.dom.insertSiblingAfter = function(newNode, refNode) {
    1105 if (refNode.parentNode) {
    1106 refNode.parentNode.insertBefore(newNode, refNode.nextSibling);
    1107 }
    1108};
    1109
    1110
    1111/**
    1112 * Insert a child at a given index. If index is larger than the number of child
    1113 * nodes that the parent currently has, the node is inserted as the last child
    1114 * node.
    1115 * @param {Element} parent The element into which to insert the child.
    1116 * @param {Node} child The element to insert.
    1117 * @param {number} index The index at which to insert the new child node. Must
    1118 * not be negative.
    1119 */
    1120goog.dom.insertChildAt = function(parent, child, index) {
    1121 // Note that if the second argument is null, insertBefore
    1122 // will append the child at the end of the list of children.
    1123 parent.insertBefore(child, parent.childNodes[index] || null);
    1124};
    1125
    1126
    1127/**
    1128 * Removes a node from its parent.
    1129 * @param {Node} node The node to remove.
    1130 * @return {Node} The node removed if removed; else, null.
    1131 */
    1132goog.dom.removeNode = function(node) {
    1133 return node && node.parentNode ? node.parentNode.removeChild(node) : null;
    1134};
    1135
    1136
    1137/**
    1138 * Replaces a node in the DOM tree. Will do nothing if {@code oldNode} has no
    1139 * parent.
    1140 * @param {Node} newNode Node to insert.
    1141 * @param {Node} oldNode Node to replace.
    1142 */
    1143goog.dom.replaceNode = function(newNode, oldNode) {
    1144 var parent = oldNode.parentNode;
    1145 if (parent) {
    1146 parent.replaceChild(newNode, oldNode);
    1147 }
    1148};
    1149
    1150
    1151/**
    1152 * Flattens an element. That is, removes it and replace it with its children.
    1153 * Does nothing if the element is not in the document.
    1154 * @param {Element} element The element to flatten.
    1155 * @return {Element|undefined} The original element, detached from the document
    1156 * tree, sans children; or undefined, if the element was not in the document
    1157 * to begin with.
    1158 */
    1159goog.dom.flattenElement = function(element) {
    1160 var child, parent = element.parentNode;
    1161 if (parent && parent.nodeType != goog.dom.NodeType.DOCUMENT_FRAGMENT) {
    1162 // Use IE DOM method (supported by Opera too) if available
    1163 if (element.removeNode) {
    1164 return /** @type {Element} */ (element.removeNode(false));
    1165 } else {
    1166 // Move all children of the original node up one level.
    1167 while ((child = element.firstChild)) {
    1168 parent.insertBefore(child, element);
    1169 }
    1170
    1171 // Detach the original element.
    1172 return /** @type {Element} */ (goog.dom.removeNode(element));
    1173 }
    1174 }
    1175};
    1176
    1177
    1178/**
    1179 * Returns an array containing just the element children of the given element.
    1180 * @param {Element} element The element whose element children we want.
    1181 * @return {!(Array|NodeList)} An array or array-like list of just the element
    1182 * children of the given element.
    1183 */
    1184goog.dom.getChildren = function(element) {
    1185 // We check if the children attribute is supported for child elements
    1186 // since IE8 misuses the attribute by also including comments.
    1187 if (goog.dom.BrowserFeature.CAN_USE_CHILDREN_ATTRIBUTE &&
    1188 element.children != undefined) {
    1189 return element.children;
    1190 }
    1191 // Fall back to manually filtering the element's child nodes.
    1192 return goog.array.filter(element.childNodes, function(node) {
    1193 return node.nodeType == goog.dom.NodeType.ELEMENT;
    1194 });
    1195};
    1196
    1197
    1198/**
    1199 * Returns the first child node that is an element.
    1200 * @param {Node} node The node to get the first child element of.
    1201 * @return {Element} The first child node of {@code node} that is an element.
    1202 */
    1203goog.dom.getFirstElementChild = function(node) {
    1204 if (node.firstElementChild != undefined) {
    1205 return /** @type {!Element} */(node).firstElementChild;
    1206 }
    1207 return goog.dom.getNextElementNode_(node.firstChild, true);
    1208};
    1209
    1210
    1211/**
    1212 * Returns the last child node that is an element.
    1213 * @param {Node} node The node to get the last child element of.
    1214 * @return {Element} The last child node of {@code node} that is an element.
    1215 */
    1216goog.dom.getLastElementChild = function(node) {
    1217 if (node.lastElementChild != undefined) {
    1218 return /** @type {!Element} */(node).lastElementChild;
    1219 }
    1220 return goog.dom.getNextElementNode_(node.lastChild, false);
    1221};
    1222
    1223
    1224/**
    1225 * Returns the first next sibling that is an element.
    1226 * @param {Node} node The node to get the next sibling element of.
    1227 * @return {Element} The next sibling of {@code node} that is an element.
    1228 */
    1229goog.dom.getNextElementSibling = function(node) {
    1230 if (node.nextElementSibling != undefined) {
    1231 return /** @type {!Element} */(node).nextElementSibling;
    1232 }
    1233 return goog.dom.getNextElementNode_(node.nextSibling, true);
    1234};
    1235
    1236
    1237/**
    1238 * Returns the first previous sibling that is an element.
    1239 * @param {Node} node The node to get the previous sibling element of.
    1240 * @return {Element} The first previous sibling of {@code node} that is
    1241 * an element.
    1242 */
    1243goog.dom.getPreviousElementSibling = function(node) {
    1244 if (node.previousElementSibling != undefined) {
    1245 return /** @type {!Element} */(node).previousElementSibling;
    1246 }
    1247 return goog.dom.getNextElementNode_(node.previousSibling, false);
    1248};
    1249
    1250
    1251/**
    1252 * Returns the first node that is an element in the specified direction,
    1253 * starting with {@code node}.
    1254 * @param {Node} node The node to get the next element from.
    1255 * @param {boolean} forward Whether to look forwards or backwards.
    1256 * @return {Element} The first element.
    1257 * @private
    1258 */
    1259goog.dom.getNextElementNode_ = function(node, forward) {
    1260 while (node && node.nodeType != goog.dom.NodeType.ELEMENT) {
    1261 node = forward ? node.nextSibling : node.previousSibling;
    1262 }
    1263
    1264 return /** @type {Element} */ (node);
    1265};
    1266
    1267
    1268/**
    1269 * Returns the next node in source order from the given node.
    1270 * @param {Node} node The node.
    1271 * @return {Node} The next node in the DOM tree, or null if this was the last
    1272 * node.
    1273 */
    1274goog.dom.getNextNode = function(node) {
    1275 if (!node) {
    1276 return null;
    1277 }
    1278
    1279 if (node.firstChild) {
    1280 return node.firstChild;
    1281 }
    1282
    1283 while (node && !node.nextSibling) {
    1284 node = node.parentNode;
    1285 }
    1286
    1287 return node ? node.nextSibling : null;
    1288};
    1289
    1290
    1291/**
    1292 * Returns the previous node in source order from the given node.
    1293 * @param {Node} node The node.
    1294 * @return {Node} The previous node in the DOM tree, or null if this was the
    1295 * first node.
    1296 */
    1297goog.dom.getPreviousNode = function(node) {
    1298 if (!node) {
    1299 return null;
    1300 }
    1301
    1302 if (!node.previousSibling) {
    1303 return node.parentNode;
    1304 }
    1305
    1306 node = node.previousSibling;
    1307 while (node && node.lastChild) {
    1308 node = node.lastChild;
    1309 }
    1310
    1311 return node;
    1312};
    1313
    1314
    1315/**
    1316 * Whether the object looks like a DOM node.
    1317 * @param {?} obj The object being tested for node likeness.
    1318 * @return {boolean} Whether the object looks like a DOM node.
    1319 */
    1320goog.dom.isNodeLike = function(obj) {
    1321 return goog.isObject(obj) && obj.nodeType > 0;
    1322};
    1323
    1324
    1325/**
    1326 * Whether the object looks like an Element.
    1327 * @param {?} obj The object being tested for Element likeness.
    1328 * @return {boolean} Whether the object looks like an Element.
    1329 */
    1330goog.dom.isElement = function(obj) {
    1331 return goog.isObject(obj) && obj.nodeType == goog.dom.NodeType.ELEMENT;
    1332};
    1333
    1334
    1335/**
    1336 * Returns true if the specified value is a Window object. This includes the
    1337 * global window for HTML pages, and iframe windows.
    1338 * @param {?} obj Variable to test.
    1339 * @return {boolean} Whether the variable is a window.
    1340 */
    1341goog.dom.isWindow = function(obj) {
    1342 return goog.isObject(obj) && obj['window'] == obj;
    1343};
    1344
    1345
    1346/**
    1347 * Returns an element's parent, if it's an Element.
    1348 * @param {Element} element The DOM element.
    1349 * @return {Element} The parent, or null if not an Element.
    1350 */
    1351goog.dom.getParentElement = function(element) {
    1352 var parent;
    1353 if (goog.dom.BrowserFeature.CAN_USE_PARENT_ELEMENT_PROPERTY) {
    1354 var isIe9 = goog.userAgent.IE &&
    1355 goog.userAgent.isVersionOrHigher('9') &&
    1356 !goog.userAgent.isVersionOrHigher('10');
    1357 // SVG elements in IE9 can't use the parentElement property.
    1358 // goog.global['SVGElement'] is not defined in IE9 quirks mode.
    1359 if (!(isIe9 && goog.global['SVGElement'] &&
    1360 element instanceof goog.global['SVGElement'])) {
    1361 parent = element.parentElement;
    1362 if (parent) {
    1363 return parent;
    1364 }
    1365 }
    1366 }
    1367 parent = element.parentNode;
    1368 return goog.dom.isElement(parent) ? /** @type {!Element} */ (parent) : null;
    1369};
    1370
    1371
    1372/**
    1373 * Whether a node contains another node.
    1374 * @param {Node} parent The node that should contain the other node.
    1375 * @param {Node} descendant The node to test presence of.
    1376 * @return {boolean} Whether the parent node contains the descendent node.
    1377 */
    1378goog.dom.contains = function(parent, descendant) {
    1379 // We use browser specific methods for this if available since it is faster
    1380 // that way.
    1381
    1382 // IE DOM
    1383 if (parent.contains && descendant.nodeType == goog.dom.NodeType.ELEMENT) {
    1384 return parent == descendant || parent.contains(descendant);
    1385 }
    1386
    1387 // W3C DOM Level 3
    1388 if (typeof parent.compareDocumentPosition != 'undefined') {
    1389 return parent == descendant ||
    1390 Boolean(parent.compareDocumentPosition(descendant) & 16);
    1391 }
    1392
    1393 // W3C DOM Level 1
    1394 while (descendant && parent != descendant) {
    1395 descendant = descendant.parentNode;
    1396 }
    1397 return descendant == parent;
    1398};
    1399
    1400
    1401/**
    1402 * Compares the document order of two nodes, returning 0 if they are the same
    1403 * node, a negative number if node1 is before node2, and a positive number if
    1404 * node2 is before node1. Note that we compare the order the tags appear in the
    1405 * document so in the tree <b><i>text</i></b> the B node is considered to be
    1406 * before the I node.
    1407 *
    1408 * @param {Node} node1 The first node to compare.
    1409 * @param {Node} node2 The second node to compare.
    1410 * @return {number} 0 if the nodes are the same node, a negative number if node1
    1411 * is before node2, and a positive number if node2 is before node1.
    1412 */
    1413goog.dom.compareNodeOrder = function(node1, node2) {
    1414 // Fall out quickly for equality.
    1415 if (node1 == node2) {
    1416 return 0;
    1417 }
    1418
    1419 // Use compareDocumentPosition where available
    1420 if (node1.compareDocumentPosition) {
    1421 // 4 is the bitmask for FOLLOWS.
    1422 return node1.compareDocumentPosition(node2) & 2 ? 1 : -1;
    1423 }
    1424
    1425 // Special case for document nodes on IE 7 and 8.
    1426 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
    1427 if (node1.nodeType == goog.dom.NodeType.DOCUMENT) {
    1428 return -1;
    1429 }
    1430 if (node2.nodeType == goog.dom.NodeType.DOCUMENT) {
    1431 return 1;
    1432 }
    1433 }
    1434
    1435 // Process in IE using sourceIndex - we check to see if the first node has
    1436 // a source index or if its parent has one.
    1437 if ('sourceIndex' in node1 ||
    1438 (node1.parentNode && 'sourceIndex' in node1.parentNode)) {
    1439 var isElement1 = node1.nodeType == goog.dom.NodeType.ELEMENT;
    1440 var isElement2 = node2.nodeType == goog.dom.NodeType.ELEMENT;
    1441
    1442 if (isElement1 && isElement2) {
    1443 return node1.sourceIndex - node2.sourceIndex;
    1444 } else {
    1445 var parent1 = node1.parentNode;
    1446 var parent2 = node2.parentNode;
    1447
    1448 if (parent1 == parent2) {
    1449 return goog.dom.compareSiblingOrder_(node1, node2);
    1450 }
    1451
    1452 if (!isElement1 && goog.dom.contains(parent1, node2)) {
    1453 return -1 * goog.dom.compareParentsDescendantNodeIe_(node1, node2);
    1454 }
    1455
    1456
    1457 if (!isElement2 && goog.dom.contains(parent2, node1)) {
    1458 return goog.dom.compareParentsDescendantNodeIe_(node2, node1);
    1459 }
    1460
    1461 return (isElement1 ? node1.sourceIndex : parent1.sourceIndex) -
    1462 (isElement2 ? node2.sourceIndex : parent2.sourceIndex);
    1463 }
    1464 }
    1465
    1466 // For Safari, we compare ranges.
    1467 var doc = goog.dom.getOwnerDocument(node1);
    1468
    1469 var range1, range2;
    1470 range1 = doc.createRange();
    1471 range1.selectNode(node1);
    1472 range1.collapse(true);
    1473
    1474 range2 = doc.createRange();
    1475 range2.selectNode(node2);
    1476 range2.collapse(true);
    1477
    1478 return range1.compareBoundaryPoints(goog.global['Range'].START_TO_END,
    1479 range2);
    1480};
    1481
    1482
    1483/**
    1484 * Utility function to compare the position of two nodes, when
    1485 * {@code textNode}'s parent is an ancestor of {@code node}. If this entry
    1486 * condition is not met, this function will attempt to reference a null object.
    1487 * @param {!Node} textNode The textNode to compare.
    1488 * @param {Node} node The node to compare.
    1489 * @return {number} -1 if node is before textNode, +1 otherwise.
    1490 * @private
    1491 */
    1492goog.dom.compareParentsDescendantNodeIe_ = function(textNode, node) {
    1493 var parent = textNode.parentNode;
    1494 if (parent == node) {
    1495 // If textNode is a child of node, then node comes first.
    1496 return -1;
    1497 }
    1498 var sibling = node;
    1499 while (sibling.parentNode != parent) {
    1500 sibling = sibling.parentNode;
    1501 }
    1502 return goog.dom.compareSiblingOrder_(sibling, textNode);
    1503};
    1504
    1505
    1506/**
    1507 * Utility function to compare the position of two nodes known to be non-equal
    1508 * siblings.
    1509 * @param {Node} node1 The first node to compare.
    1510 * @param {!Node} node2 The second node to compare.
    1511 * @return {number} -1 if node1 is before node2, +1 otherwise.
    1512 * @private
    1513 */
    1514goog.dom.compareSiblingOrder_ = function(node1, node2) {
    1515 var s = node2;
    1516 while ((s = s.previousSibling)) {
    1517 if (s == node1) {
    1518 // We just found node1 before node2.
    1519 return -1;
    1520 }
    1521 }
    1522
    1523 // Since we didn't find it, node1 must be after node2.
    1524 return 1;
    1525};
    1526
    1527
    1528/**
    1529 * Find the deepest common ancestor of the given nodes.
    1530 * @param {...Node} var_args The nodes to find a common ancestor of.
    1531 * @return {Node} The common ancestor of the nodes, or null if there is none.
    1532 * null will only be returned if two or more of the nodes are from different
    1533 * documents.
    1534 */
    1535goog.dom.findCommonAncestor = function(var_args) {
    1536 var i, count = arguments.length;
    1537 if (!count) {
    1538 return null;
    1539 } else if (count == 1) {
    1540 return arguments[0];
    1541 }
    1542
    1543 var paths = [];
    1544 var minLength = Infinity;
    1545 for (i = 0; i < count; i++) {
    1546 // Compute the list of ancestors.
    1547 var ancestors = [];
    1548 var node = arguments[i];
    1549 while (node) {
    1550 ancestors.unshift(node);
    1551 node = node.parentNode;
    1552 }
    1553
    1554 // Save the list for comparison.
    1555 paths.push(ancestors);
    1556 minLength = Math.min(minLength, ancestors.length);
    1557 }
    1558 var output = null;
    1559 for (i = 0; i < minLength; i++) {
    1560 var first = paths[0][i];
    1561 for (var j = 1; j < count; j++) {
    1562 if (first != paths[j][i]) {
    1563 return output;
    1564 }
    1565 }
    1566 output = first;
    1567 }
    1568 return output;
    1569};
    1570
    1571
    1572/**
    1573 * Returns the owner document for a node.
    1574 * @param {Node|Window} node The node to get the document for.
    1575 * @return {!Document} The document owning the node.
    1576 */
    1577goog.dom.getOwnerDocument = function(node) {
    1578 // TODO(nnaze): Update param signature to be non-nullable.
    1579 goog.asserts.assert(node, 'Node cannot be null or undefined.');
    1580 return /** @type {!Document} */ (
    1581 node.nodeType == goog.dom.NodeType.DOCUMENT ? node :
    1582 node.ownerDocument || node.document);
    1583};
    1584
    1585
    1586/**
    1587 * Cross-browser function for getting the document element of a frame or iframe.
    1588 * @param {Element} frame Frame element.
    1589 * @return {!Document} The frame content document.
    1590 */
    1591goog.dom.getFrameContentDocument = function(frame) {
    1592 var doc = frame.contentDocument || frame.contentWindow.document;
    1593 return doc;
    1594};
    1595
    1596
    1597/**
    1598 * Cross-browser function for getting the window of a frame or iframe.
    1599 * @param {Element} frame Frame element.
    1600 * @return {Window} The window associated with the given frame.
    1601 */
    1602goog.dom.getFrameContentWindow = function(frame) {
    1603 return frame.contentWindow ||
    1604 goog.dom.getWindow(goog.dom.getFrameContentDocument(frame));
    1605};
    1606
    1607
    1608/**
    1609 * Sets the text content of a node, with cross-browser support.
    1610 * @param {Node} node The node to change the text content of.
    1611 * @param {string|number} text The value that should replace the node's content.
    1612 */
    1613goog.dom.setTextContent = function(node, text) {
    1614 goog.asserts.assert(node != null,
    1615 'goog.dom.setTextContent expects a non-null value for node');
    1616
    1617 if ('textContent' in node) {
    1618 node.textContent = text;
    1619 } else if (node.nodeType == goog.dom.NodeType.TEXT) {
    1620 node.data = text;
    1621 } else if (node.firstChild &&
    1622 node.firstChild.nodeType == goog.dom.NodeType.TEXT) {
    1623 // If the first child is a text node we just change its data and remove the
    1624 // rest of the children.
    1625 while (node.lastChild != node.firstChild) {
    1626 node.removeChild(node.lastChild);
    1627 }
    1628 node.firstChild.data = text;
    1629 } else {
    1630 goog.dom.removeChildren(node);
    1631 var doc = goog.dom.getOwnerDocument(node);
    1632 node.appendChild(doc.createTextNode(String(text)));
    1633 }
    1634};
    1635
    1636
    1637/**
    1638 * Gets the outerHTML of a node, which islike innerHTML, except that it
    1639 * actually contains the HTML of the node itself.
    1640 * @param {Element} element The element to get the HTML of.
    1641 * @return {string} The outerHTML of the given element.
    1642 */
    1643goog.dom.getOuterHtml = function(element) {
    1644 // IE, Opera and WebKit all have outerHTML.
    1645 if ('outerHTML' in element) {
    1646 return element.outerHTML;
    1647 } else {
    1648 var doc = goog.dom.getOwnerDocument(element);
    1649 var div = doc.createElement(goog.dom.TagName.DIV);
    1650 div.appendChild(element.cloneNode(true));
    1651 return div.innerHTML;
    1652 }
    1653};
    1654
    1655
    1656/**
    1657 * Finds the first descendant node that matches the filter function, using
    1658 * a depth first search. This function offers the most general purpose way
    1659 * of finding a matching element. You may also wish to consider
    1660 * {@code goog.dom.query} which can express many matching criteria using
    1661 * CSS selector expressions. These expressions often result in a more
    1662 * compact representation of the desired result.
    1663 * @see goog.dom.query
    1664 *
    1665 * @param {Node} root The root of the tree to search.
    1666 * @param {function(Node) : boolean} p The filter function.
    1667 * @return {Node|undefined} The found node or undefined if none is found.
    1668 */
    1669goog.dom.findNode = function(root, p) {
    1670 var rv = [];
    1671 var found = goog.dom.findNodes_(root, p, rv, true);
    1672 return found ? rv[0] : undefined;
    1673};
    1674
    1675
    1676/**
    1677 * Finds all the descendant nodes that match the filter function, using a
    1678 * a depth first search. This function offers the most general-purpose way
    1679 * of finding a set of matching elements. You may also wish to consider
    1680 * {@code goog.dom.query} which can express many matching criteria using
    1681 * CSS selector expressions. These expressions often result in a more
    1682 * compact representation of the desired result.
    1683
    1684 * @param {Node} root The root of the tree to search.
    1685 * @param {function(Node) : boolean} p The filter function.
    1686 * @return {!Array<!Node>} The found nodes or an empty array if none are found.
    1687 */
    1688goog.dom.findNodes = function(root, p) {
    1689 var rv = [];
    1690 goog.dom.findNodes_(root, p, rv, false);
    1691 return rv;
    1692};
    1693
    1694
    1695/**
    1696 * Finds the first or all the descendant nodes that match the filter function,
    1697 * using a depth first search.
    1698 * @param {Node} root The root of the tree to search.
    1699 * @param {function(Node) : boolean} p The filter function.
    1700 * @param {!Array<!Node>} rv The found nodes are added to this array.
    1701 * @param {boolean} findOne If true we exit after the first found node.
    1702 * @return {boolean} Whether the search is complete or not. True in case findOne
    1703 * is true and the node is found. False otherwise.
    1704 * @private
    1705 */
    1706goog.dom.findNodes_ = function(root, p, rv, findOne) {
    1707 if (root != null) {
    1708 var child = root.firstChild;
    1709 while (child) {
    1710 if (p(child)) {
    1711 rv.push(child);
    1712 if (findOne) {
    1713 return true;
    1714 }
    1715 }
    1716 if (goog.dom.findNodes_(child, p, rv, findOne)) {
    1717 return true;
    1718 }
    1719 child = child.nextSibling;
    1720 }
    1721 }
    1722 return false;
    1723};
    1724
    1725
    1726/**
    1727 * Map of tags whose content to ignore when calculating text length.
    1728 * @private {!Object<string, number>}
    1729 * @const
    1730 */
    1731goog.dom.TAGS_TO_IGNORE_ = {
    1732 'SCRIPT': 1,
    1733 'STYLE': 1,
    1734 'HEAD': 1,
    1735 'IFRAME': 1,
    1736 'OBJECT': 1
    1737};
    1738
    1739
    1740/**
    1741 * Map of tags which have predefined values with regard to whitespace.
    1742 * @private {!Object<string, string>}
    1743 * @const
    1744 */
    1745goog.dom.PREDEFINED_TAG_VALUES_ = {'IMG': ' ', 'BR': '\n'};
    1746
    1747
    1748/**
    1749 * Returns true if the element has a tab index that allows it to receive
    1750 * keyboard focus (tabIndex >= 0), false otherwise. Note that some elements
    1751 * natively support keyboard focus, even if they have no tab index.
    1752 * @param {!Element} element Element to check.
    1753 * @return {boolean} Whether the element has a tab index that allows keyboard
    1754 * focus.
    1755 * @see http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
    1756 */
    1757goog.dom.isFocusableTabIndex = function(element) {
    1758 return goog.dom.hasSpecifiedTabIndex_(element) &&
    1759 goog.dom.isTabIndexFocusable_(element);
    1760};
    1761
    1762
    1763/**
    1764 * Enables or disables keyboard focus support on the element via its tab index.
    1765 * Only elements for which {@link goog.dom.isFocusableTabIndex} returns true
    1766 * (or elements that natively support keyboard focus, like form elements) can
    1767 * receive keyboard focus. See http://go/tabindex for more info.
    1768 * @param {Element} element Element whose tab index is to be changed.
    1769 * @param {boolean} enable Whether to set or remove a tab index on the element
    1770 * that supports keyboard focus.
    1771 */
    1772goog.dom.setFocusableTabIndex = function(element, enable) {
    1773 if (enable) {
    1774 element.tabIndex = 0;
    1775 } else {
    1776 // Set tabIndex to -1 first, then remove it. This is a workaround for
    1777 // Safari (confirmed in version 4 on Windows). When removing the attribute
    1778 // without setting it to -1 first, the element remains keyboard focusable
    1779 // despite not having a tabIndex attribute anymore.
    1780 element.tabIndex = -1;
    1781 element.removeAttribute('tabIndex'); // Must be camelCase!
    1782 }
    1783};
    1784
    1785
    1786/**
    1787 * Returns true if the element can be focused, i.e. it has a tab index that
    1788 * allows it to receive keyboard focus (tabIndex >= 0), or it is an element
    1789 * that natively supports keyboard focus.
    1790 * @param {!Element} element Element to check.
    1791 * @return {boolean} Whether the element allows keyboard focus.
    1792 */
    1793goog.dom.isFocusable = function(element) {
    1794 var focusable;
    1795 // Some elements can have unspecified tab index and still receive focus.
    1796 if (goog.dom.nativelySupportsFocus_(element)) {
    1797 // Make sure the element is not disabled ...
    1798 focusable = !element.disabled &&
    1799 // ... and if a tab index is specified, it allows focus.
    1800 (!goog.dom.hasSpecifiedTabIndex_(element) ||
    1801 goog.dom.isTabIndexFocusable_(element));
    1802 } else {
    1803 focusable = goog.dom.isFocusableTabIndex(element);
    1804 }
    1805
    1806 // IE requires elements to be visible in order to focus them.
    1807 return focusable && goog.userAgent.IE ?
    1808 goog.dom.hasNonZeroBoundingRect_(element) : focusable;
    1809};
    1810
    1811
    1812/**
    1813 * Returns true if the element has a specified tab index.
    1814 * @param {!Element} element Element to check.
    1815 * @return {boolean} Whether the element has a specified tab index.
    1816 * @private
    1817 */
    1818goog.dom.hasSpecifiedTabIndex_ = function(element) {
    1819 // IE returns 0 for an unset tabIndex, so we must use getAttributeNode(),
    1820 // which returns an object with a 'specified' property if tabIndex is
    1821 // specified. This works on other browsers, too.
    1822 var attrNode = element.getAttributeNode('tabindex'); // Must be lowercase!
    1823 return goog.isDefAndNotNull(attrNode) && attrNode.specified;
    1824};
    1825
    1826
    1827/**
    1828 * Returns true if the element's tab index allows the element to be focused.
    1829 * @param {!Element} element Element to check.
    1830 * @return {boolean} Whether the element's tab index allows focus.
    1831 * @private
    1832 */
    1833goog.dom.isTabIndexFocusable_ = function(element) {
    1834 var index = element.tabIndex;
    1835 // NOTE: IE9 puts tabIndex in 16-bit int, e.g. -2 is 65534.
    1836 return goog.isNumber(index) && index >= 0 && index < 32768;
    1837};
    1838
    1839
    1840/**
    1841 * Returns true if the element is focusable even when tabIndex is not set.
    1842 * @param {!Element} element Element to check.
    1843 * @return {boolean} Whether the element natively supports focus.
    1844 * @private
    1845 */
    1846goog.dom.nativelySupportsFocus_ = function(element) {
    1847 return element.tagName == goog.dom.TagName.A ||
    1848 element.tagName == goog.dom.TagName.INPUT ||
    1849 element.tagName == goog.dom.TagName.TEXTAREA ||
    1850 element.tagName == goog.dom.TagName.SELECT ||
    1851 element.tagName == goog.dom.TagName.BUTTON;
    1852};
    1853
    1854
    1855/**
    1856 * Returns true if the element has a bounding rectangle that would be visible
    1857 * (i.e. its width and height are greater than zero).
    1858 * @param {!Element} element Element to check.
    1859 * @return {boolean} Whether the element has a non-zero bounding rectangle.
    1860 * @private
    1861 */
    1862goog.dom.hasNonZeroBoundingRect_ = function(element) {
    1863 var rect = goog.isFunction(element['getBoundingClientRect']) ?
    1864 element.getBoundingClientRect() :
    1865 {'height': element.offsetHeight, 'width': element.offsetWidth};
    1866 return goog.isDefAndNotNull(rect) && rect.height > 0 && rect.width > 0;
    1867};
    1868
    1869
    1870/**
    1871 * Returns the text content of the current node, without markup and invisible
    1872 * symbols. New lines are stripped and whitespace is collapsed,
    1873 * such that each character would be visible.
    1874 *
    1875 * In browsers that support it, innerText is used. Other browsers attempt to
    1876 * simulate it via node traversal. Line breaks are canonicalized in IE.
    1877 *
    1878 * @param {Node} node The node from which we are getting content.
    1879 * @return {string} The text content.
    1880 */
    1881goog.dom.getTextContent = function(node) {
    1882 var textContent;
    1883 // Note(arv): IE9, Opera, and Safari 3 support innerText but they include
    1884 // text nodes in script tags. So we revert to use a user agent test here.
    1885 if (goog.dom.BrowserFeature.CAN_USE_INNER_TEXT && ('innerText' in node)) {
    1886 textContent = goog.string.canonicalizeNewlines(node.innerText);
    1887 // Unfortunately .innerText() returns text with &shy; symbols
    1888 // We need to filter it out and then remove duplicate whitespaces
    1889 } else {
    1890 var buf = [];
    1891 goog.dom.getTextContent_(node, buf, true);
    1892 textContent = buf.join('');
    1893 }
    1894
    1895 // Strip &shy; entities. goog.format.insertWordBreaks inserts them in Opera.
    1896 textContent = textContent.replace(/ \xAD /g, ' ').replace(/\xAD/g, '');
    1897 // Strip &#8203; entities. goog.format.insertWordBreaks inserts them in IE8.
    1898 textContent = textContent.replace(/\u200B/g, '');
    1899
    1900 // Skip this replacement on old browsers with working innerText, which
    1901 // automatically turns &nbsp; into ' ' and / +/ into ' ' when reading
    1902 // innerText.
    1903 if (!goog.dom.BrowserFeature.CAN_USE_INNER_TEXT) {
    1904 textContent = textContent.replace(/ +/g, ' ');
    1905 }
    1906 if (textContent != ' ') {
    1907 textContent = textContent.replace(/^\s*/, '');
    1908 }
    1909
    1910 return textContent;
    1911};
    1912
    1913
    1914/**
    1915 * Returns the text content of the current node, without markup.
    1916 *
    1917 * Unlike {@code getTextContent} this method does not collapse whitespaces
    1918 * or normalize lines breaks.
    1919 *
    1920 * @param {Node} node The node from which we are getting content.
    1921 * @return {string} The raw text content.
    1922 */
    1923goog.dom.getRawTextContent = function(node) {
    1924 var buf = [];
    1925 goog.dom.getTextContent_(node, buf, false);
    1926
    1927 return buf.join('');
    1928};
    1929
    1930
    1931/**
    1932 * Recursive support function for text content retrieval.
    1933 *
    1934 * @param {Node} node The node from which we are getting content.
    1935 * @param {Array<string>} buf string buffer.
    1936 * @param {boolean} normalizeWhitespace Whether to normalize whitespace.
    1937 * @private
    1938 */
    1939goog.dom.getTextContent_ = function(node, buf, normalizeWhitespace) {
    1940 if (node.nodeName in goog.dom.TAGS_TO_IGNORE_) {
    1941 // ignore certain tags
    1942 } else if (node.nodeType == goog.dom.NodeType.TEXT) {
    1943 if (normalizeWhitespace) {
    1944 buf.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, ''));
    1945 } else {
    1946 buf.push(node.nodeValue);
    1947 }
    1948 } else if (node.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {
    1949 buf.push(goog.dom.PREDEFINED_TAG_VALUES_[node.nodeName]);
    1950 } else {
    1951 var child = node.firstChild;
    1952 while (child) {
    1953 goog.dom.getTextContent_(child, buf, normalizeWhitespace);
    1954 child = child.nextSibling;
    1955 }
    1956 }
    1957};
    1958
    1959
    1960/**
    1961 * Returns the text length of the text contained in a node, without markup. This
    1962 * is equivalent to the selection length if the node was selected, or the number
    1963 * of cursor movements to traverse the node. Images & BRs take one space. New
    1964 * lines are ignored.
    1965 *
    1966 * @param {Node} node The node whose text content length is being calculated.
    1967 * @return {number} The length of {@code node}'s text content.
    1968 */
    1969goog.dom.getNodeTextLength = function(node) {
    1970 return goog.dom.getTextContent(node).length;
    1971};
    1972
    1973
    1974/**
    1975 * Returns the text offset of a node relative to one of its ancestors. The text
    1976 * length is the same as the length calculated by goog.dom.getNodeTextLength.
    1977 *
    1978 * @param {Node} node The node whose offset is being calculated.
    1979 * @param {Node=} opt_offsetParent The node relative to which the offset will
    1980 * be calculated. Defaults to the node's owner document's body.
    1981 * @return {number} The text offset.
    1982 */
    1983goog.dom.getNodeTextOffset = function(node, opt_offsetParent) {
    1984 var root = opt_offsetParent || goog.dom.getOwnerDocument(node).body;
    1985 var buf = [];
    1986 while (node && node != root) {
    1987 var cur = node;
    1988 while ((cur = cur.previousSibling)) {
    1989 buf.unshift(goog.dom.getTextContent(cur));
    1990 }
    1991 node = node.parentNode;
    1992 }
    1993 // Trim left to deal with FF cases when there might be line breaks and empty
    1994 // nodes at the front of the text
    1995 return goog.string.trimLeft(buf.join('')).replace(/ +/g, ' ').length;
    1996};
    1997
    1998
    1999/**
    2000 * Returns the node at a given offset in a parent node. If an object is
    2001 * provided for the optional third parameter, the node and the remainder of the
    2002 * offset will stored as properties of this object.
    2003 * @param {Node} parent The parent node.
    2004 * @param {number} offset The offset into the parent node.
    2005 * @param {Object=} opt_result Object to be used to store the return value. The
    2006 * return value will be stored in the form {node: Node, remainder: number}
    2007 * if this object is provided.
    2008 * @return {Node} The node at the given offset.
    2009 */
    2010goog.dom.getNodeAtOffset = function(parent, offset, opt_result) {
    2011 var stack = [parent], pos = 0, cur = null;
    2012 while (stack.length > 0 && pos < offset) {
    2013 cur = stack.pop();
    2014 if (cur.nodeName in goog.dom.TAGS_TO_IGNORE_) {
    2015 // ignore certain tags
    2016 } else if (cur.nodeType == goog.dom.NodeType.TEXT) {
    2017 var text = cur.nodeValue.replace(/(\r\n|\r|\n)/g, '').replace(/ +/g, ' ');
    2018 pos += text.length;
    2019 } else if (cur.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {
    2020 pos += goog.dom.PREDEFINED_TAG_VALUES_[cur.nodeName].length;
    2021 } else {
    2022 for (var i = cur.childNodes.length - 1; i >= 0; i--) {
    2023 stack.push(cur.childNodes[i]);
    2024 }
    2025 }
    2026 }
    2027 if (goog.isObject(opt_result)) {
    2028 opt_result.remainder = cur ? cur.nodeValue.length + offset - pos - 1 : 0;
    2029 opt_result.node = cur;
    2030 }
    2031
    2032 return cur;
    2033};
    2034
    2035
    2036/**
    2037 * Returns true if the object is a {@code NodeList}. To qualify as a NodeList,
    2038 * the object must have a numeric length property and an item function (which
    2039 * has type 'string' on IE for some reason).
    2040 * @param {Object} val Object to test.
    2041 * @return {boolean} Whether the object is a NodeList.
    2042 */
    2043goog.dom.isNodeList = function(val) {
    2044 // TODO(attila): Now the isNodeList is part of goog.dom we can use
    2045 // goog.userAgent to make this simpler.
    2046 // A NodeList must have a length property of type 'number' on all platforms.
    2047 if (val && typeof val.length == 'number') {
    2048 // A NodeList is an object everywhere except Safari, where it's a function.
    2049 if (goog.isObject(val)) {
    2050 // A NodeList must have an item function (on non-IE platforms) or an item
    2051 // property of type 'string' (on IE).
    2052 return typeof val.item == 'function' || typeof val.item == 'string';
    2053 } else if (goog.isFunction(val)) {
    2054 // On Safari, a NodeList is a function with an item property that is also
    2055 // a function.
    2056 return typeof val.item == 'function';
    2057 }
    2058 }
    2059
    2060 // Not a NodeList.
    2061 return false;
    2062};
    2063
    2064
    2065/**
    2066 * Walks up the DOM hierarchy returning the first ancestor that has the passed
    2067 * tag name and/or class name. If the passed element matches the specified
    2068 * criteria, the element itself is returned.
    2069 * @param {Node} element The DOM node to start with.
    2070 * @param {?(goog.dom.TagName|string)=} opt_tag The tag name to match (or
    2071 * null/undefined to match only based on class name).
    2072 * @param {?string=} opt_class The class name to match (or null/undefined to
    2073 * match only based on tag name).
    2074 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
    2075 * dom.
    2076 * @return {Element} The first ancestor that matches the passed criteria, or
    2077 * null if no match is found.
    2078 */
    2079goog.dom.getAncestorByTagNameAndClass = function(element, opt_tag, opt_class,
    2080 opt_maxSearchSteps) {
    2081 if (!opt_tag && !opt_class) {
    2082 return null;
    2083 }
    2084 var tagName = opt_tag ? opt_tag.toUpperCase() : null;
    2085 return /** @type {Element} */ (goog.dom.getAncestor(element,
    2086 function(node) {
    2087 return (!tagName || node.nodeName == tagName) &&
    2088 (!opt_class || goog.isString(node.className) &&
    2089 goog.array.contains(node.className.split(/\s+/), opt_class));
    2090 }, true, opt_maxSearchSteps));
    2091};
    2092
    2093
    2094/**
    2095 * Walks up the DOM hierarchy returning the first ancestor that has the passed
    2096 * class name. If the passed element matches the specified criteria, the
    2097 * element itself is returned.
    2098 * @param {Node} element The DOM node to start with.
    2099 * @param {string} className The class name to match.
    2100 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
    2101 * dom.
    2102 * @return {Element} The first ancestor that matches the passed criteria, or
    2103 * null if none match.
    2104 */
    2105goog.dom.getAncestorByClass = function(element, className, opt_maxSearchSteps) {
    2106 return goog.dom.getAncestorByTagNameAndClass(element, null, className,
    2107 opt_maxSearchSteps);
    2108};
    2109
    2110
    2111/**
    2112 * Walks up the DOM hierarchy returning the first ancestor that passes the
    2113 * matcher function.
    2114 * @param {Node} element The DOM node to start with.
    2115 * @param {function(Node) : boolean} matcher A function that returns true if the
    2116 * passed node matches the desired criteria.
    2117 * @param {boolean=} opt_includeNode If true, the node itself is included in
    2118 * the search (the first call to the matcher will pass startElement as
    2119 * the node to test).
    2120 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
    2121 * dom.
    2122 * @return {Node} DOM node that matched the matcher, or null if there was
    2123 * no match.
    2124 */
    2125goog.dom.getAncestor = function(
    2126 element, matcher, opt_includeNode, opt_maxSearchSteps) {
    2127 if (!opt_includeNode) {
    2128 element = element.parentNode;
    2129 }
    2130 var ignoreSearchSteps = opt_maxSearchSteps == null;
    2131 var steps = 0;
    2132 while (element && (ignoreSearchSteps || steps <= opt_maxSearchSteps)) {
    2133 goog.asserts.assert(element.name != 'parentNode');
    2134 if (matcher(element)) {
    2135 return element;
    2136 }
    2137 element = element.parentNode;
    2138 steps++;
    2139 }
    2140 // Reached the root of the DOM without a match
    2141 return null;
    2142};
    2143
    2144
    2145/**
    2146 * Determines the active element in the given document.
    2147 * @param {Document} doc The document to look in.
    2148 * @return {Element} The active element.
    2149 */
    2150goog.dom.getActiveElement = function(doc) {
    2151 try {
    2152 return doc && doc.activeElement;
    2153 } catch (e) {
    2154 // NOTE(nicksantos): Sometimes, evaluating document.activeElement in IE
    2155 // throws an exception. I'm not 100% sure why, but I suspect it chokes
    2156 // on document.activeElement if the activeElement has been recently
    2157 // removed from the DOM by a JS operation.
    2158 //
    2159 // We assume that an exception here simply means
    2160 // "there is no active element."
    2161 }
    2162
    2163 return null;
    2164};
    2165
    2166
    2167/**
    2168 * Gives the current devicePixelRatio.
    2169 *
    2170 * By default, this is the value of window.devicePixelRatio (which should be
    2171 * preferred if present).
    2172 *
    2173 * If window.devicePixelRatio is not present, the ratio is calculated with
    2174 * window.matchMedia, if present. Otherwise, gives 1.0.
    2175 *
    2176 * Some browsers (including Chrome) consider the browser zoom level in the pixel
    2177 * ratio, so the value may change across multiple calls.
    2178 *
    2179 * @return {number} The number of actual pixels per virtual pixel.
    2180 */
    2181goog.dom.getPixelRatio = function() {
    2182 var win = goog.dom.getWindow();
    2183
    2184 // devicePixelRatio does not work on Mobile firefox.
    2185 // TODO(user): Enable this check on a known working mobile Gecko version.
    2186 // Filed a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=896804
    2187 var isFirefoxMobile = goog.userAgent.GECKO && goog.userAgent.MOBILE;
    2188
    2189 if (goog.isDef(win.devicePixelRatio) && !isFirefoxMobile) {
    2190 return win.devicePixelRatio;
    2191 } else if (win.matchMedia) {
    2192 return goog.dom.matchesPixelRatio_(.75) ||
    2193 goog.dom.matchesPixelRatio_(1.5) ||
    2194 goog.dom.matchesPixelRatio_(2) ||
    2195 goog.dom.matchesPixelRatio_(3) || 1;
    2196 }
    2197 return 1;
    2198};
    2199
    2200
    2201/**
    2202 * Calculates a mediaQuery to check if the current device supports the
    2203 * given actual to virtual pixel ratio.
    2204 * @param {number} pixelRatio The ratio of actual pixels to virtual pixels.
    2205 * @return {number} pixelRatio if applicable, otherwise 0.
    2206 * @private
    2207 */
    2208goog.dom.matchesPixelRatio_ = function(pixelRatio) {
    2209 var win = goog.dom.getWindow();
    2210 var query = ('(-webkit-min-device-pixel-ratio: ' + pixelRatio + '),' +
    2211 '(min--moz-device-pixel-ratio: ' + pixelRatio + '),' +
    2212 '(min-resolution: ' + pixelRatio + 'dppx)');
    2213 return win.matchMedia(query).matches ? pixelRatio : 0;
    2214};
    2215
    2216
    2217
    2218/**
    2219 * Create an instance of a DOM helper with a new document object.
    2220 * @param {Document=} opt_document Document object to associate with this
    2221 * DOM helper.
    2222 * @constructor
    2223 */
    2224goog.dom.DomHelper = function(opt_document) {
    2225 /**
    2226 * Reference to the document object to use
    2227 * @type {!Document}
    2228 * @private
    2229 */
    2230 this.document_ = opt_document || goog.global.document || document;
    2231};
    2232
    2233
    2234/**
    2235 * Gets the dom helper object for the document where the element resides.
    2236 * @param {Node=} opt_node If present, gets the DomHelper for this node.
    2237 * @return {!goog.dom.DomHelper} The DomHelper.
    2238 */
    2239goog.dom.DomHelper.prototype.getDomHelper = goog.dom.getDomHelper;
    2240
    2241
    2242/**
    2243 * Sets the document object.
    2244 * @param {!Document} document Document object.
    2245 */
    2246goog.dom.DomHelper.prototype.setDocument = function(document) {
    2247 this.document_ = document;
    2248};
    2249
    2250
    2251/**
    2252 * Gets the document object being used by the dom library.
    2253 * @return {!Document} Document object.
    2254 */
    2255goog.dom.DomHelper.prototype.getDocument = function() {
    2256 return this.document_;
    2257};
    2258
    2259
    2260/**
    2261 * Alias for {@code getElementById}. If a DOM node is passed in then we just
    2262 * return that.
    2263 * @param {string|Element} element Element ID or a DOM node.
    2264 * @return {Element} The element with the given ID, or the node passed in.
    2265 */
    2266goog.dom.DomHelper.prototype.getElement = function(element) {
    2267 return goog.dom.getElementHelper_(this.document_, element);
    2268};
    2269
    2270
    2271/**
    2272 * Gets an element by id, asserting that the element is found.
    2273 *
    2274 * This is used when an element is expected to exist, and should fail with
    2275 * an assertion error if it does not (if assertions are enabled).
    2276 *
    2277 * @param {string} id Element ID.
    2278 * @return {!Element} The element with the given ID, if it exists.
    2279 */
    2280goog.dom.DomHelper.prototype.getRequiredElement = function(id) {
    2281 return goog.dom.getRequiredElementHelper_(this.document_, id);
    2282};
    2283
    2284
    2285/**
    2286 * Alias for {@code getElement}.
    2287 * @param {string|Element} element Element ID or a DOM node.
    2288 * @return {Element} The element with the given ID, or the node passed in.
    2289 * @deprecated Use {@link goog.dom.DomHelper.prototype.getElement} instead.
    2290 */
    2291goog.dom.DomHelper.prototype.$ = goog.dom.DomHelper.prototype.getElement;
    2292
    2293
    2294/**
    2295 * Looks up elements by both tag and class name, using browser native functions
    2296 * ({@code querySelectorAll}, {@code getElementsByTagName} or
    2297 * {@code getElementsByClassName}) where possible. The returned array is a live
    2298 * NodeList or a static list depending on the code path taken.
    2299 *
    2300 * @see goog.dom.query
    2301 *
    2302 * @param {?string=} opt_tag Element tag name or * for all tags.
    2303 * @param {?string=} opt_class Optional class name.
    2304 * @param {(Document|Element)=} opt_el Optional element to look in.
    2305 * @return { {length: number} } Array-like list of elements (only a length
    2306 * property and numerical indices are guaranteed to exist).
    2307 */
    2308goog.dom.DomHelper.prototype.getElementsByTagNameAndClass = function(opt_tag,
    2309 opt_class,
    2310 opt_el) {
    2311 return goog.dom.getElementsByTagNameAndClass_(this.document_, opt_tag,
    2312 opt_class, opt_el);
    2313};
    2314
    2315
    2316/**
    2317 * Returns an array of all the elements with the provided className.
    2318 * @see {goog.dom.query}
    2319 * @param {string} className the name of the class to look for.
    2320 * @param {Element|Document=} opt_el Optional element to look in.
    2321 * @return { {length: number} } The items found with the class name provided.
    2322 */
    2323goog.dom.DomHelper.prototype.getElementsByClass = function(className, opt_el) {
    2324 var doc = opt_el || this.document_;
    2325 return goog.dom.getElementsByClass(className, doc);
    2326};
    2327
    2328
    2329/**
    2330 * Returns the first element we find matching the provided class name.
    2331 * @see {goog.dom.query}
    2332 * @param {string} className the name of the class to look for.
    2333 * @param {(Element|Document)=} opt_el Optional element to look in.
    2334 * @return {Element} The first item found with the class name provided.
    2335 */
    2336goog.dom.DomHelper.prototype.getElementByClass = function(className, opt_el) {
    2337 var doc = opt_el || this.document_;
    2338 return goog.dom.getElementByClass(className, doc);
    2339};
    2340
    2341
    2342/**
    2343 * Ensures an element with the given className exists, and then returns the
    2344 * first element with the provided className.
    2345 * @see {goog.dom.query}
    2346 * @param {string} className the name of the class to look for.
    2347 * @param {(!Element|!Document)=} opt_root Optional element or document to look
    2348 * in.
    2349 * @return {!Element} The first item found with the class name provided.
    2350 * @throws {goog.asserts.AssertionError} Thrown if no element is found.
    2351 */
    2352goog.dom.DomHelper.prototype.getRequiredElementByClass = function(className,
    2353 opt_root) {
    2354 var root = opt_root || this.document_;
    2355 return goog.dom.getRequiredElementByClass(className, root);
    2356};
    2357
    2358
    2359/**
    2360 * Alias for {@code getElementsByTagNameAndClass}.
    2361 * @deprecated Use DomHelper getElementsByTagNameAndClass.
    2362 * @see goog.dom.query
    2363 *
    2364 * @param {?string=} opt_tag Element tag name.
    2365 * @param {?string=} opt_class Optional class name.
    2366 * @param {Element=} opt_el Optional element to look in.
    2367 * @return { {length: number} } Array-like list of elements (only a length
    2368 * property and numerical indices are guaranteed to exist).
    2369 */
    2370goog.dom.DomHelper.prototype.$$ =
    2371 goog.dom.DomHelper.prototype.getElementsByTagNameAndClass;
    2372
    2373
    2374/**
    2375 * Sets a number of properties on a node.
    2376 * @param {Element} element DOM node to set properties on.
    2377 * @param {Object} properties Hash of property:value pairs.
    2378 */
    2379goog.dom.DomHelper.prototype.setProperties = goog.dom.setProperties;
    2380
    2381
    2382/**
    2383 * Gets the dimensions of the viewport.
    2384 * @param {Window=} opt_window Optional window element to test. Defaults to
    2385 * the window of the Dom Helper.
    2386 * @return {!goog.math.Size} Object with values 'width' and 'height'.
    2387 */
    2388goog.dom.DomHelper.prototype.getViewportSize = function(opt_window) {
    2389 // TODO(arv): This should not take an argument. That breaks the rule of a
    2390 // a DomHelper representing a single frame/window/document.
    2391 return goog.dom.getViewportSize(opt_window || this.getWindow());
    2392};
    2393
    2394
    2395/**
    2396 * Calculates the height of the document.
    2397 *
    2398 * @return {number} The height of the document.
    2399 */
    2400goog.dom.DomHelper.prototype.getDocumentHeight = function() {
    2401 return goog.dom.getDocumentHeight_(this.getWindow());
    2402};
    2403
    2404
    2405/**
    2406 * Typedef for use with goog.dom.createDom and goog.dom.append.
    2407 * @typedef {Object|string|Array|NodeList}
    2408 */
    2409goog.dom.Appendable;
    2410
    2411
    2412/**
    2413 * Returns a dom node with a set of attributes. This function accepts varargs
    2414 * for subsequent nodes to be added. Subsequent nodes will be added to the
    2415 * first node as childNodes.
    2416 *
    2417 * So:
    2418 * <code>createDom('div', null, createDom('p'), createDom('p'));</code>
    2419 * would return a div with two child paragraphs
    2420 *
    2421 * An easy way to move all child nodes of an existing element to a new parent
    2422 * element is:
    2423 * <code>createDom('div', null, oldElement.childNodes);</code>
    2424 * which will remove all child nodes from the old element and add them as
    2425 * child nodes of the new DIV.
    2426 *
    2427 * @param {string} tagName Tag to create.
    2428 * @param {Object|string=} opt_attributes If object, then a map of name-value
    2429 * pairs for attributes. If a string, then this is the className of the new
    2430 * element.
    2431 * @param {...goog.dom.Appendable} var_args Further DOM nodes or
    2432 * strings for text nodes. If one of the var_args is an array or
    2433 * NodeList, its elements will be added as childNodes instead.
    2434 * @return {!Element} Reference to a DOM node.
    2435 */
    2436goog.dom.DomHelper.prototype.createDom = function(tagName,
    2437 opt_attributes,
    2438 var_args) {
    2439 return goog.dom.createDom_(this.document_, arguments);
    2440};
    2441
    2442
    2443/**
    2444 * Alias for {@code createDom}.
    2445 * @param {string} tagName Tag to create.
    2446 * @param {(Object|string)=} opt_attributes If object, then a map of name-value
    2447 * pairs for attributes. If a string, then this is the className of the new
    2448 * element.
    2449 * @param {...goog.dom.Appendable} var_args Further DOM nodes or strings for
    2450 * text nodes. If one of the var_args is an array, its children will be
    2451 * added as childNodes instead.
    2452 * @return {!Element} Reference to a DOM node.
    2453 * @deprecated Use {@link goog.dom.DomHelper.prototype.createDom} instead.
    2454 */
    2455goog.dom.DomHelper.prototype.$dom = goog.dom.DomHelper.prototype.createDom;
    2456
    2457
    2458/**
    2459 * Creates a new element.
    2460 * @param {string} name Tag name.
    2461 * @return {!Element} The new element.
    2462 */
    2463goog.dom.DomHelper.prototype.createElement = function(name) {
    2464 return this.document_.createElement(name);
    2465};
    2466
    2467
    2468/**
    2469 * Creates a new text node.
    2470 * @param {number|string} content Content.
    2471 * @return {!Text} The new text node.
    2472 */
    2473goog.dom.DomHelper.prototype.createTextNode = function(content) {
    2474 return this.document_.createTextNode(String(content));
    2475};
    2476
    2477
    2478/**
    2479 * Create a table.
    2480 * @param {number} rows The number of rows in the table. Must be >= 1.
    2481 * @param {number} columns The number of columns in the table. Must be >= 1.
    2482 * @param {boolean=} opt_fillWithNbsp If true, fills table entries with
    2483 * {@code goog.string.Unicode.NBSP} characters.
    2484 * @return {!HTMLElement} The created table.
    2485 */
    2486goog.dom.DomHelper.prototype.createTable = function(rows, columns,
    2487 opt_fillWithNbsp) {
    2488 return goog.dom.createTable_(this.document_, rows, columns,
    2489 !!opt_fillWithNbsp);
    2490};
    2491
    2492
    2493/**
    2494 * Converts an HTML into a node or a document fragment. A single Node is used if
    2495 * {@code html} only generates a single node. If {@code html} generates multiple
    2496 * nodes then these are put inside a {@code DocumentFragment}.
    2497 * @param {!goog.html.SafeHtml} html The HTML markup to convert.
    2498 * @return {!Node} The resulting node.
    2499 */
    2500goog.dom.DomHelper.prototype.safeHtmlToNode = function(html) {
    2501 return goog.dom.safeHtmlToNode_(this.document_, html);
    2502};
    2503
    2504
    2505/**
    2506 * Converts an HTML string into a node or a document fragment. A single Node
    2507 * is used if the {@code htmlString} only generates a single node. If the
    2508 * {@code htmlString} generates multiple nodes then these are put inside a
    2509 * {@code DocumentFragment}.
    2510 *
    2511 * @param {string} htmlString The HTML string to convert.
    2512 * @return {!Node} The resulting node.
    2513 */
    2514goog.dom.DomHelper.prototype.htmlToDocumentFragment = function(htmlString) {
    2515 return goog.dom.htmlToDocumentFragment_(this.document_, htmlString);
    2516};
    2517
    2518
    2519/**
    2520 * Returns true if the browser is in "CSS1-compatible" (standards-compliant)
    2521 * mode, false otherwise.
    2522 * @return {boolean} True if in CSS1-compatible mode.
    2523 */
    2524goog.dom.DomHelper.prototype.isCss1CompatMode = function() {
    2525 return goog.dom.isCss1CompatMode_(this.document_);
    2526};
    2527
    2528
    2529/**
    2530 * Gets the window object associated with the document.
    2531 * @return {!Window} The window associated with the given document.
    2532 */
    2533goog.dom.DomHelper.prototype.getWindow = function() {
    2534 return goog.dom.getWindow_(this.document_);
    2535};
    2536
    2537
    2538/**
    2539 * Gets the document scroll element.
    2540 * @return {!Element} Scrolling element.
    2541 */
    2542goog.dom.DomHelper.prototype.getDocumentScrollElement = function() {
    2543 return goog.dom.getDocumentScrollElement_(this.document_);
    2544};
    2545
    2546
    2547/**
    2548 * Gets the document scroll distance as a coordinate object.
    2549 * @return {!goog.math.Coordinate} Object with properties 'x' and 'y'.
    2550 */
    2551goog.dom.DomHelper.prototype.getDocumentScroll = function() {
    2552 return goog.dom.getDocumentScroll_(this.document_);
    2553};
    2554
    2555
    2556/**
    2557 * Determines the active element in the given document.
    2558 * @param {Document=} opt_doc The document to look in.
    2559 * @return {Element} The active element.
    2560 */
    2561goog.dom.DomHelper.prototype.getActiveElement = function(opt_doc) {
    2562 return goog.dom.getActiveElement(opt_doc || this.document_);
    2563};
    2564
    2565
    2566/**
    2567 * Appends a child to a node.
    2568 * @param {Node} parent Parent.
    2569 * @param {Node} child Child.
    2570 */
    2571goog.dom.DomHelper.prototype.appendChild = goog.dom.appendChild;
    2572
    2573
    2574/**
    2575 * Appends a node with text or other nodes.
    2576 * @param {!Node} parent The node to append nodes to.
    2577 * @param {...goog.dom.Appendable} var_args The things to append to the node.
    2578 * If this is a Node it is appended as is.
    2579 * If this is a string then a text node is appended.
    2580 * If this is an array like object then fields 0 to length - 1 are appended.
    2581 */
    2582goog.dom.DomHelper.prototype.append = goog.dom.append;
    2583
    2584
    2585/**
    2586 * Determines if the given node can contain children, intended to be used for
    2587 * HTML generation.
    2588 *
    2589 * @param {Node} node The node to check.
    2590 * @return {boolean} Whether the node can contain children.
    2591 */
    2592goog.dom.DomHelper.prototype.canHaveChildren = goog.dom.canHaveChildren;
    2593
    2594
    2595/**
    2596 * Removes all the child nodes on a DOM node.
    2597 * @param {Node} node Node to remove children from.
    2598 */
    2599goog.dom.DomHelper.prototype.removeChildren = goog.dom.removeChildren;
    2600
    2601
    2602/**
    2603 * Inserts a new node before an existing reference node (i.e., as the previous
    2604 * sibling). If the reference node has no parent, then does nothing.
    2605 * @param {Node} newNode Node to insert.
    2606 * @param {Node} refNode Reference node to insert before.
    2607 */
    2608goog.dom.DomHelper.prototype.insertSiblingBefore = goog.dom.insertSiblingBefore;
    2609
    2610
    2611/**
    2612 * Inserts a new node after an existing reference node (i.e., as the next
    2613 * sibling). If the reference node has no parent, then does nothing.
    2614 * @param {Node} newNode Node to insert.
    2615 * @param {Node} refNode Reference node to insert after.
    2616 */
    2617goog.dom.DomHelper.prototype.insertSiblingAfter = goog.dom.insertSiblingAfter;
    2618
    2619
    2620/**
    2621 * Insert a child at a given index. If index is larger than the number of child
    2622 * nodes that the parent currently has, the node is inserted as the last child
    2623 * node.
    2624 * @param {Element} parent The element into which to insert the child.
    2625 * @param {Node} child The element to insert.
    2626 * @param {number} index The index at which to insert the new child node. Must
    2627 * not be negative.
    2628 */
    2629goog.dom.DomHelper.prototype.insertChildAt = goog.dom.insertChildAt;
    2630
    2631
    2632/**
    2633 * Removes a node from its parent.
    2634 * @param {Node} node The node to remove.
    2635 * @return {Node} The node removed if removed; else, null.
    2636 */
    2637goog.dom.DomHelper.prototype.removeNode = goog.dom.removeNode;
    2638
    2639
    2640/**
    2641 * Replaces a node in the DOM tree. Will do nothing if {@code oldNode} has no
    2642 * parent.
    2643 * @param {Node} newNode Node to insert.
    2644 * @param {Node} oldNode Node to replace.
    2645 */
    2646goog.dom.DomHelper.prototype.replaceNode = goog.dom.replaceNode;
    2647
    2648
    2649/**
    2650 * Flattens an element. That is, removes it and replace it with its children.
    2651 * @param {Element} element The element to flatten.
    2652 * @return {Element|undefined} The original element, detached from the document
    2653 * tree, sans children, or undefined if the element was already not in the
    2654 * document.
    2655 */
    2656goog.dom.DomHelper.prototype.flattenElement = goog.dom.flattenElement;
    2657
    2658
    2659/**
    2660 * Returns an array containing just the element children of the given element.
    2661 * @param {Element} element The element whose element children we want.
    2662 * @return {!(Array|NodeList)} An array or array-like list of just the element
    2663 * children of the given element.
    2664 */
    2665goog.dom.DomHelper.prototype.getChildren = goog.dom.getChildren;
    2666
    2667
    2668/**
    2669 * Returns the first child node that is an element.
    2670 * @param {Node} node The node to get the first child element of.
    2671 * @return {Element} The first child node of {@code node} that is an element.
    2672 */
    2673goog.dom.DomHelper.prototype.getFirstElementChild =
    2674 goog.dom.getFirstElementChild;
    2675
    2676
    2677/**
    2678 * Returns the last child node that is an element.
    2679 * @param {Node} node The node to get the last child element of.
    2680 * @return {Element} The last child node of {@code node} that is an element.
    2681 */
    2682goog.dom.DomHelper.prototype.getLastElementChild = goog.dom.getLastElementChild;
    2683
    2684
    2685/**
    2686 * Returns the first next sibling that is an element.
    2687 * @param {Node} node The node to get the next sibling element of.
    2688 * @return {Element} The next sibling of {@code node} that is an element.
    2689 */
    2690goog.dom.DomHelper.prototype.getNextElementSibling =
    2691 goog.dom.getNextElementSibling;
    2692
    2693
    2694/**
    2695 * Returns the first previous sibling that is an element.
    2696 * @param {Node} node The node to get the previous sibling element of.
    2697 * @return {Element} The first previous sibling of {@code node} that is
    2698 * an element.
    2699 */
    2700goog.dom.DomHelper.prototype.getPreviousElementSibling =
    2701 goog.dom.getPreviousElementSibling;
    2702
    2703
    2704/**
    2705 * Returns the next node in source order from the given node.
    2706 * @param {Node} node The node.
    2707 * @return {Node} The next node in the DOM tree, or null if this was the last
    2708 * node.
    2709 */
    2710goog.dom.DomHelper.prototype.getNextNode = goog.dom.getNextNode;
    2711
    2712
    2713/**
    2714 * Returns the previous node in source order from the given node.
    2715 * @param {Node} node The node.
    2716 * @return {Node} The previous node in the DOM tree, or null if this was the
    2717 * first node.
    2718 */
    2719goog.dom.DomHelper.prototype.getPreviousNode = goog.dom.getPreviousNode;
    2720
    2721
    2722/**
    2723 * Whether the object looks like a DOM node.
    2724 * @param {?} obj The object being tested for node likeness.
    2725 * @return {boolean} Whether the object looks like a DOM node.
    2726 */
    2727goog.dom.DomHelper.prototype.isNodeLike = goog.dom.isNodeLike;
    2728
    2729
    2730/**
    2731 * Whether the object looks like an Element.
    2732 * @param {?} obj The object being tested for Element likeness.
    2733 * @return {boolean} Whether the object looks like an Element.
    2734 */
    2735goog.dom.DomHelper.prototype.isElement = goog.dom.isElement;
    2736
    2737
    2738/**
    2739 * Returns true if the specified value is a Window object. This includes the
    2740 * global window for HTML pages, and iframe windows.
    2741 * @param {?} obj Variable to test.
    2742 * @return {boolean} Whether the variable is a window.
    2743 */
    2744goog.dom.DomHelper.prototype.isWindow = goog.dom.isWindow;
    2745
    2746
    2747/**
    2748 * Returns an element's parent, if it's an Element.
    2749 * @param {Element} element The DOM element.
    2750 * @return {Element} The parent, or null if not an Element.
    2751 */
    2752goog.dom.DomHelper.prototype.getParentElement = goog.dom.getParentElement;
    2753
    2754
    2755/**
    2756 * Whether a node contains another node.
    2757 * @param {Node} parent The node that should contain the other node.
    2758 * @param {Node} descendant The node to test presence of.
    2759 * @return {boolean} Whether the parent node contains the descendent node.
    2760 */
    2761goog.dom.DomHelper.prototype.contains = goog.dom.contains;
    2762
    2763
    2764/**
    2765 * Compares the document order of two nodes, returning 0 if they are the same
    2766 * node, a negative number if node1 is before node2, and a positive number if
    2767 * node2 is before node1. Note that we compare the order the tags appear in the
    2768 * document so in the tree <b><i>text</i></b> the B node is considered to be
    2769 * before the I node.
    2770 *
    2771 * @param {Node} node1 The first node to compare.
    2772 * @param {Node} node2 The second node to compare.
    2773 * @return {number} 0 if the nodes are the same node, a negative number if node1
    2774 * is before node2, and a positive number if node2 is before node1.
    2775 */
    2776goog.dom.DomHelper.prototype.compareNodeOrder = goog.dom.compareNodeOrder;
    2777
    2778
    2779/**
    2780 * Find the deepest common ancestor of the given nodes.
    2781 * @param {...Node} var_args The nodes to find a common ancestor of.
    2782 * @return {Node} The common ancestor of the nodes, or null if there is none.
    2783 * null will only be returned if two or more of the nodes are from different
    2784 * documents.
    2785 */
    2786goog.dom.DomHelper.prototype.findCommonAncestor = goog.dom.findCommonAncestor;
    2787
    2788
    2789/**
    2790 * Returns the owner document for a node.
    2791 * @param {Node} node The node to get the document for.
    2792 * @return {!Document} The document owning the node.
    2793 */
    2794goog.dom.DomHelper.prototype.getOwnerDocument = goog.dom.getOwnerDocument;
    2795
    2796
    2797/**
    2798 * Cross browser function for getting the document element of an iframe.
    2799 * @param {Element} iframe Iframe element.
    2800 * @return {!Document} The frame content document.
    2801 */
    2802goog.dom.DomHelper.prototype.getFrameContentDocument =
    2803 goog.dom.getFrameContentDocument;
    2804
    2805
    2806/**
    2807 * Cross browser function for getting the window of a frame or iframe.
    2808 * @param {Element} frame Frame element.
    2809 * @return {Window} The window associated with the given frame.
    2810 */
    2811goog.dom.DomHelper.prototype.getFrameContentWindow =
    2812 goog.dom.getFrameContentWindow;
    2813
    2814
    2815/**
    2816 * Sets the text content of a node, with cross-browser support.
    2817 * @param {Node} node The node to change the text content of.
    2818 * @param {string|number} text The value that should replace the node's content.
    2819 */
    2820goog.dom.DomHelper.prototype.setTextContent = goog.dom.setTextContent;
    2821
    2822
    2823/**
    2824 * Gets the outerHTML of a node, which islike innerHTML, except that it
    2825 * actually contains the HTML of the node itself.
    2826 * @param {Element} element The element to get the HTML of.
    2827 * @return {string} The outerHTML of the given element.
    2828 */
    2829goog.dom.DomHelper.prototype.getOuterHtml = goog.dom.getOuterHtml;
    2830
    2831
    2832/**
    2833 * Finds the first descendant node that matches the filter function. This does
    2834 * a depth first search.
    2835 * @param {Node} root The root of the tree to search.
    2836 * @param {function(Node) : boolean} p The filter function.
    2837 * @return {Node|undefined} The found node or undefined if none is found.
    2838 */
    2839goog.dom.DomHelper.prototype.findNode = goog.dom.findNode;
    2840
    2841
    2842/**
    2843 * Finds all the descendant nodes that matches the filter function. This does a
    2844 * depth first search.
    2845 * @param {Node} root The root of the tree to search.
    2846 * @param {function(Node) : boolean} p The filter function.
    2847 * @return {Array<Node>} The found nodes or an empty array if none are found.
    2848 */
    2849goog.dom.DomHelper.prototype.findNodes = goog.dom.findNodes;
    2850
    2851
    2852/**
    2853 * Returns true if the element has a tab index that allows it to receive
    2854 * keyboard focus (tabIndex >= 0), false otherwise. Note that some elements
    2855 * natively support keyboard focus, even if they have no tab index.
    2856 * @param {!Element} element Element to check.
    2857 * @return {boolean} Whether the element has a tab index that allows keyboard
    2858 * focus.
    2859 */
    2860goog.dom.DomHelper.prototype.isFocusableTabIndex = goog.dom.isFocusableTabIndex;
    2861
    2862
    2863/**
    2864 * Enables or disables keyboard focus support on the element via its tab index.
    2865 * Only elements for which {@link goog.dom.isFocusableTabIndex} returns true
    2866 * (or elements that natively support keyboard focus, like form elements) can
    2867 * receive keyboard focus. See http://go/tabindex for more info.
    2868 * @param {Element} element Element whose tab index is to be changed.
    2869 * @param {boolean} enable Whether to set or remove a tab index on the element
    2870 * that supports keyboard focus.
    2871 */
    2872goog.dom.DomHelper.prototype.setFocusableTabIndex =
    2873 goog.dom.setFocusableTabIndex;
    2874
    2875
    2876/**
    2877 * Returns true if the element can be focused, i.e. it has a tab index that
    2878 * allows it to receive keyboard focus (tabIndex >= 0), or it is an element
    2879 * that natively supports keyboard focus.
    2880 * @param {!Element} element Element to check.
    2881 * @return {boolean} Whether the element allows keyboard focus.
    2882 */
    2883goog.dom.DomHelper.prototype.isFocusable = goog.dom.isFocusable;
    2884
    2885
    2886/**
    2887 * Returns the text contents of the current node, without markup. New lines are
    2888 * stripped and whitespace is collapsed, such that each character would be
    2889 * visible.
    2890 *
    2891 * In browsers that support it, innerText is used. Other browsers attempt to
    2892 * simulate it via node traversal. Line breaks are canonicalized in IE.
    2893 *
    2894 * @param {Node} node The node from which we are getting content.
    2895 * @return {string} The text content.
    2896 */
    2897goog.dom.DomHelper.prototype.getTextContent = goog.dom.getTextContent;
    2898
    2899
    2900/**
    2901 * Returns the text length of the text contained in a node, without markup. This
    2902 * is equivalent to the selection length if the node was selected, or the number
    2903 * of cursor movements to traverse the node. Images & BRs take one space. New
    2904 * lines are ignored.
    2905 *
    2906 * @param {Node} node The node whose text content length is being calculated.
    2907 * @return {number} The length of {@code node}'s text content.
    2908 */
    2909goog.dom.DomHelper.prototype.getNodeTextLength = goog.dom.getNodeTextLength;
    2910
    2911
    2912/**
    2913 * Returns the text offset of a node relative to one of its ancestors. The text
    2914 * length is the same as the length calculated by
    2915 * {@code goog.dom.getNodeTextLength}.
    2916 *
    2917 * @param {Node} node The node whose offset is being calculated.
    2918 * @param {Node=} opt_offsetParent Defaults to the node's owner document's body.
    2919 * @return {number} The text offset.
    2920 */
    2921goog.dom.DomHelper.prototype.getNodeTextOffset = goog.dom.getNodeTextOffset;
    2922
    2923
    2924/**
    2925 * Returns the node at a given offset in a parent node. If an object is
    2926 * provided for the optional third parameter, the node and the remainder of the
    2927 * offset will stored as properties of this object.
    2928 * @param {Node} parent The parent node.
    2929 * @param {number} offset The offset into the parent node.
    2930 * @param {Object=} opt_result Object to be used to store the return value. The
    2931 * return value will be stored in the form {node: Node, remainder: number}
    2932 * if this object is provided.
    2933 * @return {Node} The node at the given offset.
    2934 */
    2935goog.dom.DomHelper.prototype.getNodeAtOffset = goog.dom.getNodeAtOffset;
    2936
    2937
    2938/**
    2939 * Returns true if the object is a {@code NodeList}. To qualify as a NodeList,
    2940 * the object must have a numeric length property and an item function (which
    2941 * has type 'string' on IE for some reason).
    2942 * @param {Object} val Object to test.
    2943 * @return {boolean} Whether the object is a NodeList.
    2944 */
    2945goog.dom.DomHelper.prototype.isNodeList = goog.dom.isNodeList;
    2946
    2947
    2948/**
    2949 * Walks up the DOM hierarchy returning the first ancestor that has the passed
    2950 * tag name and/or class name. If the passed element matches the specified
    2951 * criteria, the element itself is returned.
    2952 * @param {Node} element The DOM node to start with.
    2953 * @param {?(goog.dom.TagName|string)=} opt_tag The tag name to match (or
    2954 * null/undefined to match only based on class name).
    2955 * @param {?string=} opt_class The class name to match (or null/undefined to
    2956 * match only based on tag name).
    2957 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
    2958 * dom.
    2959 * @return {Element} The first ancestor that matches the passed criteria, or
    2960 * null if no match is found.
    2961 */
    2962goog.dom.DomHelper.prototype.getAncestorByTagNameAndClass =
    2963 goog.dom.getAncestorByTagNameAndClass;
    2964
    2965
    2966/**
    2967 * Walks up the DOM hierarchy returning the first ancestor that has the passed
    2968 * class name. If the passed element matches the specified criteria, the
    2969 * element itself is returned.
    2970 * @param {Node} element The DOM node to start with.
    2971 * @param {string} class The class name to match.
    2972 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
    2973 * dom.
    2974 * @return {Element} The first ancestor that matches the passed criteria, or
    2975 * null if none match.
    2976 */
    2977goog.dom.DomHelper.prototype.getAncestorByClass =
    2978 goog.dom.getAncestorByClass;
    2979
    2980
    2981/**
    2982 * Walks up the DOM hierarchy returning the first ancestor that passes the
    2983 * matcher function.
    2984 * @param {Node} element The DOM node to start with.
    2985 * @param {function(Node) : boolean} matcher A function that returns true if the
    2986 * passed node matches the desired criteria.
    2987 * @param {boolean=} opt_includeNode If true, the node itself is included in
    2988 * the search (the first call to the matcher will pass startElement as
    2989 * the node to test).
    2990 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
    2991 * dom.
    2992 * @return {Node} DOM node that matched the matcher, or null if there was
    2993 * no match.
    2994 */
    2995goog.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 index f6c86b9..587e392 100644 --- a/docs/source/lib/goog/dom/nodetype.js.src.html +++ b/docs/source/lib/goog/dom/nodetype.js.src.html @@ -1 +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 +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 index d4aae79..871a3c4 100644 --- a/docs/source/lib/goog/dom/tagname.js.src.html +++ b/docs/source/lib/goog/dom/tagname.js.src.html @@ -1 +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 TEXTAREA: 'TEXTAREA',
    146 TFOOT: 'TFOOT',
    147 TH: 'TH',
    148 THEAD: 'THEAD',
    149 TIME: 'TIME',
    150 TITLE: 'TITLE',
    151 TR: 'TR',
    152 TRACK: 'TRACK',
    153 TT: 'TT',
    154 U: 'U',
    155 UL: 'UL',
    156 VAR: 'VAR',
    157 VIDEO: 'VIDEO',
    158 WBR: 'WBR'
    159};
    \ No newline at end of file +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..cf3bd61 --- /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
    27 * @type {!Object}
    28 * @private
    29 */
    30goog.dom.tags.VOID_TAGS_ = goog.object.createSet(('area,base,br,col,command,' +
    31 'embed,hr,img,input,keygen,link,meta,param,source,track,wbr').split(','));
    32
    33
    34/**
    35 * Checks whether the tag is void (with no contents allowed and no legal end
    36 * tag), for example 'br'.
    37 * @param {string} tagName The tag name in lower case.
    38 * @return {boolean}
    39 */
    40goog.dom.tags.isVoidTag = function(tagName) {
    41 return goog.dom.tags.VOID_TAGS_[tagName] === true;
    42};
    \ 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 index 7a45ef7..d97e89d 100644 --- a/docs/source/lib/goog/dom/vendor.js.src.html +++ b/docs/source/lib/goog/dom/vendor.js.src.html @@ -1 +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 +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 index da1a36b..7088496 100644 --- a/docs/source/lib/goog/events/browserevent.js.src.html +++ b/docs/source/lib/goog/events/browserevent.js.src.html @@ -1 +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 */
    44
    45goog.provide('goog.events.BrowserEvent');
    46goog.provide('goog.events.BrowserEvent.MouseButton');
    47
    48goog.require('goog.events.BrowserFeature');
    49goog.require('goog.events.Event');
    50goog.require('goog.events.EventType');
    51goog.require('goog.reflect');
    52goog.require('goog.userAgent');
    53
    54
    55
    56/**
    57 * Accepts a browser event object and creates a patched, cross browser event
    58 * object.
    59 * The content of this object will not be initialized if no event object is
    60 * provided. If this is the case, init() needs to be invoked separately.
    61 * @param {Event=} opt_e Browser event object.
    62 * @param {EventTarget=} opt_currentTarget Current target for event.
    63 * @constructor
    64 * @extends {goog.events.Event}
    65 */
    66goog.events.BrowserEvent = function(opt_e, opt_currentTarget) {
    67 goog.events.BrowserEvent.base(this, 'constructor', opt_e ? opt_e.type : '');
    68
    69 /**
    70 * Target that fired the event.
    71 * @override
    72 * @type {Node}
    73 */
    74 this.target = null;
    75
    76 /**
    77 * Node that had the listener attached.
    78 * @override
    79 * @type {Node|undefined}
    80 */
    81 this.currentTarget = null;
    82
    83 /**
    84 * For mouseover and mouseout events, the related object for the event.
    85 * @type {Node}
    86 */
    87 this.relatedTarget = null;
    88
    89 /**
    90 * X-coordinate relative to target.
    91 * @type {number}
    92 */
    93 this.offsetX = 0;
    94
    95 /**
    96 * Y-coordinate relative to target.
    97 * @type {number}
    98 */
    99 this.offsetY = 0;
    100
    101 /**
    102 * X-coordinate relative to the window.
    103 * @type {number}
    104 */
    105 this.clientX = 0;
    106
    107 /**
    108 * Y-coordinate relative to the window.
    109 * @type {number}
    110 */
    111 this.clientY = 0;
    112
    113 /**
    114 * X-coordinate relative to the monitor.
    115 * @type {number}
    116 */
    117 this.screenX = 0;
    118
    119 /**
    120 * Y-coordinate relative to the monitor.
    121 * @type {number}
    122 */
    123 this.screenY = 0;
    124
    125 /**
    126 * Which mouse button was pressed.
    127 * @type {number}
    128 */
    129 this.button = 0;
    130
    131 /**
    132 * Keycode of key press.
    133 * @type {number}
    134 */
    135 this.keyCode = 0;
    136
    137 /**
    138 * Keycode of key press.
    139 * @type {number}
    140 */
    141 this.charCode = 0;
    142
    143 /**
    144 * Whether control was pressed at time of event.
    145 * @type {boolean}
    146 */
    147 this.ctrlKey = false;
    148
    149 /**
    150 * Whether alt was pressed at time of event.
    151 * @type {boolean}
    152 */
    153 this.altKey = false;
    154
    155 /**
    156 * Whether shift was pressed at time of event.
    157 * @type {boolean}
    158 */
    159 this.shiftKey = false;
    160
    161 /**
    162 * Whether the meta key was pressed at time of event.
    163 * @type {boolean}
    164 */
    165 this.metaKey = false;
    166
    167 /**
    168 * History state object, only set for PopState events where it's a copy of the
    169 * state object provided to pushState or replaceState.
    170 * @type {Object}
    171 */
    172 this.state = null;
    173
    174 /**
    175 * Whether the default platform modifier key was pressed at time of event.
    176 * (This is control for all platforms except Mac, where it's Meta.)
    177 * @type {boolean}
    178 */
    179 this.platformModifierKey = false;
    180
    181 /**
    182 * The browser event object.
    183 * @private {Event}
    184 */
    185 this.event_ = null;
    186
    187 if (opt_e) {
    188 this.init(opt_e, opt_currentTarget);
    189 }
    190};
    191goog.inherits(goog.events.BrowserEvent, goog.events.Event);
    192
    193
    194/**
    195 * Normalized button constants for the mouse.
    196 * @enum {number}
    197 */
    198goog.events.BrowserEvent.MouseButton = {
    199 LEFT: 0,
    200 MIDDLE: 1,
    201 RIGHT: 2
    202};
    203
    204
    205/**
    206 * Static data for mapping mouse buttons.
    207 * @type {!Array.<number>}
    208 */
    209goog.events.BrowserEvent.IEButtonMap = [
    210 1, // LEFT
    211 4, // MIDDLE
    212 2 // RIGHT
    213];
    214
    215
    216/**
    217 * Accepts a browser event object and creates a patched, cross browser event
    218 * object.
    219 * @param {Event} e Browser event object.
    220 * @param {EventTarget=} opt_currentTarget Current target for event.
    221 */
    222goog.events.BrowserEvent.prototype.init = function(e, opt_currentTarget) {
    223 var type = this.type = e.type;
    224
    225 // TODO(nicksantos): Change this.target to type EventTarget.
    226 this.target = /** @type {Node} */ (e.target) || e.srcElement;
    227
    228 // TODO(nicksantos): Change this.currentTarget to type EventTarget.
    229 this.currentTarget = /** @type {Node} */ (opt_currentTarget);
    230
    231 var relatedTarget = /** @type {Node} */ (e.relatedTarget);
    232 if (relatedTarget) {
    233 // There's a bug in FireFox where sometimes, relatedTarget will be a
    234 // chrome element, and accessing any property of it will get a permission
    235 // denied exception. See:
    236 // https://bugzilla.mozilla.org/show_bug.cgi?id=497780
    237 if (goog.userAgent.GECKO) {
    238 if (!goog.reflect.canAccessProperty(relatedTarget, 'nodeName')) {
    239 relatedTarget = null;
    240 }
    241 }
    242 // TODO(arv): Use goog.events.EventType when it has been refactored into its
    243 // own file.
    244 } else if (type == goog.events.EventType.MOUSEOVER) {
    245 relatedTarget = e.fromElement;
    246 } else if (type == goog.events.EventType.MOUSEOUT) {
    247 relatedTarget = e.toElement;
    248 }
    249
    250 this.relatedTarget = relatedTarget;
    251
    252 // Webkit emits a lame warning whenever layerX/layerY is accessed.
    253 // http://code.google.com/p/chromium/issues/detail?id=101733
    254 this.offsetX = (goog.userAgent.WEBKIT || e.offsetX !== undefined) ?
    255 e.offsetX : e.layerX;
    256 this.offsetY = (goog.userAgent.WEBKIT || e.offsetY !== undefined) ?
    257 e.offsetY : e.layerY;
    258
    259 this.clientX = e.clientX !== undefined ? e.clientX : e.pageX;
    260 this.clientY = e.clientY !== undefined ? e.clientY : e.pageY;
    261 this.screenX = e.screenX || 0;
    262 this.screenY = e.screenY || 0;
    263
    264 this.button = e.button;
    265
    266 this.keyCode = e.keyCode || 0;
    267 this.charCode = e.charCode || (type == 'keypress' ? e.keyCode : 0);
    268 this.ctrlKey = e.ctrlKey;
    269 this.altKey = e.altKey;
    270 this.shiftKey = e.shiftKey;
    271 this.metaKey = e.metaKey;
    272 this.platformModifierKey = goog.userAgent.MAC ? e.metaKey : e.ctrlKey;
    273 this.state = e.state;
    274 this.event_ = e;
    275 if (e.defaultPrevented) {
    276 this.preventDefault();
    277 }
    278};
    279
    280
    281/**
    282 * Tests to see which button was pressed during the event. This is really only
    283 * useful in IE and Gecko browsers. And in IE, it's only useful for
    284 * mousedown/mouseup events, because click only fires for the left mouse button.
    285 *
    286 * Safari 2 only reports the left button being clicked, and uses the value '1'
    287 * instead of 0. Opera only reports a mousedown event for the middle button, and
    288 * no mouse events for the right button. Opera has default behavior for left and
    289 * middle click that can only be overridden via a configuration setting.
    290 *
    291 * There's a nice table of this mess at http://www.unixpapa.com/js/mouse.html.
    292 *
    293 * @param {goog.events.BrowserEvent.MouseButton} button The button
    294 * to test for.
    295 * @return {boolean} True if button was pressed.
    296 */
    297goog.events.BrowserEvent.prototype.isButton = function(button) {
    298 if (!goog.events.BrowserFeature.HAS_W3C_BUTTON) {
    299 if (this.type == 'click') {
    300 return button == goog.events.BrowserEvent.MouseButton.LEFT;
    301 } else {
    302 return !!(this.event_.button &
    303 goog.events.BrowserEvent.IEButtonMap[button]);
    304 }
    305 } else {
    306 return this.event_.button == button;
    307 }
    308};
    309
    310
    311/**
    312 * Whether this has an "action"-producing mouse button.
    313 *
    314 * By definition, this includes left-click on windows/linux, and left-click
    315 * without the ctrl key on Macs.
    316 *
    317 * @return {boolean} The result.
    318 */
    319goog.events.BrowserEvent.prototype.isMouseActionButton = function() {
    320 // Webkit does not ctrl+click to be a right-click, so we
    321 // normalize it to behave like Gecko and Opera.
    322 return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT) &&
    323 !(goog.userAgent.WEBKIT && goog.userAgent.MAC && this.ctrlKey);
    324};
    325
    326
    327/**
    328 * @override
    329 */
    330goog.events.BrowserEvent.prototype.stopPropagation = function() {
    331 goog.events.BrowserEvent.superClass_.stopPropagation.call(this);
    332 if (this.event_.stopPropagation) {
    333 this.event_.stopPropagation();
    334 } else {
    335 this.event_.cancelBubble = true;
    336 }
    337};
    338
    339
    340/**
    341 * @override
    342 */
    343goog.events.BrowserEvent.prototype.preventDefault = function() {
    344 goog.events.BrowserEvent.superClass_.preventDefault.call(this);
    345 var be = this.event_;
    346 if (!be.preventDefault) {
    347 be.returnValue = false;
    348 if (goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT) {
    349 /** @preserveTry */
    350 try {
    351 // Most keys can be prevented using returnValue. Some special keys
    352 // require setting the keyCode to -1 as well:
    353 //
    354 // In IE7:
    355 // F3, F5, F10, F11, Ctrl+P, Crtl+O, Ctrl+F (these are taken from IE6)
    356 //
    357 // In IE8:
    358 // Ctrl+P, Crtl+O, Ctrl+F (F1-F12 cannot be stopped through the event)
    359 //
    360 // We therefore do this for all function keys as well as when Ctrl key
    361 // is pressed.
    362 var VK_F1 = 112;
    363 var VK_F12 = 123;
    364 if (be.ctrlKey || be.keyCode >= VK_F1 && be.keyCode <= VK_F12) {
    365 be.keyCode = -1;
    366 }
    367 } catch (ex) {
    368 // IE throws an 'access denied' exception when trying to change
    369 // keyCode in some situations (e.g. srcElement is input[type=file],
    370 // or srcElement is an anchor tag rewritten by parent's innerHTML).
    371 // Do nothing in this case.
    372 }
    373 }
    374 } else {
    375 be.preventDefault();
    376 }
    377};
    378
    379
    380/**
    381 * @return {Event} The underlying browser event object.
    382 */
    383goog.events.BrowserEvent.prototype.getBrowserEvent = function() {
    384 return this.event_;
    385};
    386
    387
    388/** @override */
    389goog.events.BrowserEvent.prototype.disposeInternal = function() {
    390};
    \ No newline at end of file +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 // TODO(nicksantos): Change this.target to type EventTarget.
    227 this.target = /** @type {Node} */ (e.target) || e.srcElement;
    228
    229 // TODO(nicksantos): Change this.currentTarget to type EventTarget.
    230 this.currentTarget = /** @type {Node} */ (opt_currentTarget);
    231
    232 var relatedTarget = /** @type {Node} */ (e.relatedTarget);
    233 if (relatedTarget) {
    234 // There's a bug in FireFox where sometimes, relatedTarget will be a
    235 // chrome element, and accessing any property of it will get a permission
    236 // denied exception. See:
    237 // https://bugzilla.mozilla.org/show_bug.cgi?id=497780
    238 if (goog.userAgent.GECKO) {
    239 if (!goog.reflect.canAccessProperty(relatedTarget, 'nodeName')) {
    240 relatedTarget = null;
    241 }
    242 }
    243 // TODO(arv): Use goog.events.EventType when it has been refactored into its
    244 // own file.
    245 } else if (type == goog.events.EventType.MOUSEOVER) {
    246 relatedTarget = e.fromElement;
    247 } else if (type == goog.events.EventType.MOUSEOUT) {
    248 relatedTarget = e.toElement;
    249 }
    250
    251 this.relatedTarget = relatedTarget;
    252
    253 // Webkit emits a lame warning whenever layerX/layerY is accessed.
    254 // http://code.google.com/p/chromium/issues/detail?id=101733
    255 this.offsetX = (goog.userAgent.WEBKIT || e.offsetX !== undefined) ?
    256 e.offsetX : e.layerX;
    257 this.offsetY = (goog.userAgent.WEBKIT || e.offsetY !== undefined) ?
    258 e.offsetY : e.layerY;
    259
    260 this.clientX = e.clientX !== undefined ? e.clientX : e.pageX;
    261 this.clientY = e.clientY !== undefined ? e.clientY : e.pageY;
    262 this.screenX = e.screenX || 0;
    263 this.screenY = e.screenY || 0;
    264
    265 this.button = e.button;
    266
    267 this.keyCode = e.keyCode || 0;
    268 this.charCode = e.charCode || (type == 'keypress' ? e.keyCode : 0);
    269 this.ctrlKey = e.ctrlKey;
    270 this.altKey = e.altKey;
    271 this.shiftKey = e.shiftKey;
    272 this.metaKey = e.metaKey;
    273 this.platformModifierKey = goog.userAgent.MAC ? e.metaKey : e.ctrlKey;
    274 this.state = e.state;
    275 this.event_ = e;
    276 if (e.defaultPrevented) {
    277 this.preventDefault();
    278 }
    279};
    280
    281
    282/**
    283 * Tests to see which button was pressed during the event. This is really only
    284 * useful in IE and Gecko browsers. And in IE, it's only useful for
    285 * mousedown/mouseup events, because click only fires for the left mouse button.
    286 *
    287 * Safari 2 only reports the left button being clicked, and uses the value '1'
    288 * instead of 0. Opera only reports a mousedown event for the middle button, and
    289 * no mouse events for the right button. Opera has default behavior for left and
    290 * middle click that can only be overridden via a configuration setting.
    291 *
    292 * There's a nice table of this mess at http://www.unixpapa.com/js/mouse.html.
    293 *
    294 * @param {goog.events.BrowserEvent.MouseButton} button The button
    295 * to test for.
    296 * @return {boolean} True if button was pressed.
    297 */
    298goog.events.BrowserEvent.prototype.isButton = function(button) {
    299 if (!goog.events.BrowserFeature.HAS_W3C_BUTTON) {
    300 if (this.type == 'click') {
    301 return button == goog.events.BrowserEvent.MouseButton.LEFT;
    302 } else {
    303 return !!(this.event_.button &
    304 goog.events.BrowserEvent.IEButtonMap[button]);
    305 }
    306 } else {
    307 return this.event_.button == button;
    308 }
    309};
    310
    311
    312/**
    313 * Whether this has an "action"-producing mouse button.
    314 *
    315 * By definition, this includes left-click on windows/linux, and left-click
    316 * without the ctrl key on Macs.
    317 *
    318 * @return {boolean} The result.
    319 */
    320goog.events.BrowserEvent.prototype.isMouseActionButton = function() {
    321 // Webkit does not ctrl+click to be a right-click, so we
    322 // normalize it to behave like Gecko and Opera.
    323 return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT) &&
    324 !(goog.userAgent.WEBKIT && goog.userAgent.MAC && this.ctrlKey);
    325};
    326
    327
    328/**
    329 * @override
    330 */
    331goog.events.BrowserEvent.prototype.stopPropagation = function() {
    332 goog.events.BrowserEvent.superClass_.stopPropagation.call(this);
    333 if (this.event_.stopPropagation) {
    334 this.event_.stopPropagation();
    335 } else {
    336 this.event_.cancelBubble = true;
    337 }
    338};
    339
    340
    341/**
    342 * @override
    343 */
    344goog.events.BrowserEvent.prototype.preventDefault = function() {
    345 goog.events.BrowserEvent.superClass_.preventDefault.call(this);
    346 var be = this.event_;
    347 if (!be.preventDefault) {
    348 be.returnValue = false;
    349 if (goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT) {
    350 /** @preserveTry */
    351 try {
    352 // Most keys can be prevented using returnValue. Some special keys
    353 // require setting the keyCode to -1 as well:
    354 //
    355 // In IE7:
    356 // F3, F5, F10, F11, Ctrl+P, Crtl+O, Ctrl+F (these are taken from IE6)
    357 //
    358 // In IE8:
    359 // Ctrl+P, Crtl+O, Ctrl+F (F1-F12 cannot be stopped through the event)
    360 //
    361 // We therefore do this for all function keys as well as when Ctrl key
    362 // is pressed.
    363 var VK_F1 = 112;
    364 var VK_F12 = 123;
    365 if (be.ctrlKey || be.keyCode >= VK_F1 && be.keyCode <= VK_F12) {
    366 be.keyCode = -1;
    367 }
    368 } catch (ex) {
    369 // IE throws an 'access denied' exception when trying to change
    370 // keyCode in some situations (e.g. srcElement is input[type=file],
    371 // or srcElement is an anchor tag rewritten by parent's innerHTML).
    372 // Do nothing in this case.
    373 }
    374 }
    375 } else {
    376 be.preventDefault();
    377 }
    378};
    379
    380
    381/**
    382 * @return {Event} The underlying browser event object.
    383 */
    384goog.events.BrowserEvent.prototype.getBrowserEvent = function() {
    385 return this.event_;
    386};
    \ 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 index 16c87d9..e7faa18 100644 --- a/docs/source/lib/goog/events/browserfeature.js.src.html +++ b/docs/source/lib/goog/events/browserfeature.js.src.html @@ -1 +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 +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 index d314255..23bb1d5 100644 --- a/docs/source/lib/goog/events/event.js.src.html +++ b/docs/source/lib/goog/events/event.js.src.html @@ -1 +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(user): 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 * For backwards compatibility (goog.events.Event used to inherit
    109 * goog.Disposable).
    110 * @deprecated Events don't need to be disposed.
    111 */
    112goog.events.Event.prototype.disposeInternal = function() {
    113};
    114
    115
    116/**
    117 * For backwards compatibility (goog.events.Event used to inherit
    118 * goog.Disposable).
    119 * @deprecated Events don't need to be disposed.
    120 */
    121goog.events.Event.prototype.dispose = function() {
    122};
    123
    124
    125/**
    126 * Stops event propagation.
    127 */
    128goog.events.Event.prototype.stopPropagation = function() {
    129 this.propagationStopped_ = true;
    130};
    131
    132
    133/**
    134 * Prevents the default action, for example a link redirecting to a url.
    135 */
    136goog.events.Event.prototype.preventDefault = function() {
    137 this.defaultPrevented = true;
    138 this.returnValue_ = false;
    139};
    140
    141
    142/**
    143 * Stops the propagation of the event. It is equivalent to
    144 * {@code e.stopPropagation()}, but can be used as the callback argument of
    145 * {@link goog.events.listen} without declaring another function.
    146 * @param {!goog.events.Event} e An event.
    147 */
    148goog.events.Event.stopPropagation = function(e) {
    149 e.stopPropagation();
    150};
    151
    152
    153/**
    154 * Prevents the default action. It is equivalent to
    155 * {@code e.preventDefault()}, but can be used as the callback argument of
    156 * {@link goog.events.listen} without declaring another function.
    157 * @param {!goog.events.Event} e An event.
    158 */
    159goog.events.Event.preventDefault = function(e) {
    160 e.preventDefault();
    161};
    \ No newline at end of file +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 index 7a5d3bc..b6ed918 100644 --- a/docs/source/lib/goog/events/eventid.js.src.html +++ b/docs/source/lib/goog/events/eventid.js.src.html @@ -1 +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 +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 index 2faec05..7706988 100644 --- a/docs/source/lib/goog/events/events.js.src.html +++ b/docs/source/lib/goog/events/events.js.src.html @@ -1 +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 *
    42 * @see ../demos/events.html
    43 * @see ../demos/event-propagation.html
    44 * @see ../demos/stopevent.html
    45 */
    46
    47// IMPLEMENTATION NOTES:
    48// goog.events stores an auxiliary data structure on each EventTarget
    49// source being listened on. This allows us to take advantage of GC,
    50// having the data structure GC'd when the EventTarget is GC'd. This
    51// GC behavior is equivalent to using W3C DOM Events directly.
    52
    53goog.provide('goog.events');
    54goog.provide('goog.events.CaptureSimulationMode');
    55goog.provide('goog.events.Key');
    56goog.provide('goog.events.ListenableType');
    57
    58goog.require('goog.asserts');
    59goog.require('goog.debug.entryPointRegistry');
    60goog.require('goog.events.BrowserEvent');
    61goog.require('goog.events.BrowserFeature');
    62goog.require('goog.events.Listenable');
    63goog.require('goog.events.ListenerMap');
    64
    65goog.forwardDeclare('goog.debug.ErrorHandler');
    66goog.forwardDeclare('goog.events.EventWrapper');
    67
    68
    69/**
    70 * @typedef {number|goog.events.ListenableKey}
    71 */
    72goog.events.Key;
    73
    74
    75/**
    76 * @typedef {EventTarget|goog.events.Listenable}
    77 */
    78goog.events.ListenableType;
    79
    80
    81/**
    82 * Property name on a native event target for the listener map
    83 * associated with the event target.
    84 * @private @const {string}
    85 */
    86goog.events.LISTENER_MAP_PROP_ = 'closure_lm_' + ((Math.random() * 1e6) | 0);
    87
    88
    89/**
    90 * String used to prepend to IE event types.
    91 * @const
    92 * @private
    93 */
    94goog.events.onString_ = 'on';
    95
    96
    97/**
    98 * Map of computed "on<eventname>" strings for IE event types. Caching
    99 * this removes an extra object allocation in goog.events.listen which
    100 * improves IE6 performance.
    101 * @const
    102 * @dict
    103 * @private
    104 */
    105goog.events.onStringMap_ = {};
    106
    107
    108/**
    109 * @enum {number} Different capture simulation mode for IE8-.
    110 */
    111goog.events.CaptureSimulationMode = {
    112 /**
    113 * Does not perform capture simulation. Will asserts in IE8- when you
    114 * add capture listeners.
    115 */
    116 OFF_AND_FAIL: 0,
    117
    118 /**
    119 * Does not perform capture simulation, silently ignore capture
    120 * listeners.
    121 */
    122 OFF_AND_SILENT: 1,
    123
    124 /**
    125 * Performs capture simulation.
    126 */
    127 ON: 2
    128};
    129
    130
    131/**
    132 * @define {number} The capture simulation mode for IE8-. By default,
    133 * this is ON.
    134 */
    135goog.define('goog.events.CAPTURE_SIMULATION_MODE', 2);
    136
    137
    138/**
    139 * Estimated count of total native listeners.
    140 * @private {number}
    141 */
    142goog.events.listenerCountEstimate_ = 0;
    143
    144
    145/**
    146 * Adds an event listener for a specific event on a native event
    147 * target (such as a DOM element) or an object that has implemented
    148 * {@link goog.events.Listenable}. A listener can only be added once
    149 * to an object and if it is added again the key for the listener is
    150 * returned. Note that if the existing listener is a one-off listener
    151 * (registered via listenOnce), it will no longer be a one-off
    152 * listener after a call to listen().
    153 *
    154 * @param {EventTarget|goog.events.Listenable} src The node to listen
    155 * to events on.
    156 * @param {string|Array.<string>|
    157 * !goog.events.EventId.<EVENTOBJ>|!Array.<!goog.events.EventId.<EVENTOBJ>>}
    158 * type Event type or array of event types.
    159 * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}
    160 * listener Callback method, or an object with a handleEvent function.
    161 * WARNING: passing an Object is now softly deprecated.
    162 * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
    163 * false).
    164 * @param {T=} opt_handler Element in whose scope to call the listener.
    165 * @return {goog.events.Key} Unique key for the listener.
    166 * @template T,EVENTOBJ
    167 */
    168goog.events.listen = function(src, type, listener, opt_capt, opt_handler) {
    169 if (goog.isArray(type)) {
    170 for (var i = 0; i < type.length; i++) {
    171 goog.events.listen(src, type[i], listener, opt_capt, opt_handler);
    172 }
    173 return null;
    174 }
    175
    176 listener = goog.events.wrapListener(listener);
    177 if (goog.events.Listenable.isImplementedBy(src)) {
    178 return src.listen(
    179 /** @type {string|!goog.events.EventId} */ (type),
    180 listener, opt_capt, opt_handler);
    181 } else {
    182 return goog.events.listen_(
    183 /** @type {EventTarget} */ (src),
    184 /** @type {string|!goog.events.EventId} */ (type),
    185 listener, /* callOnce */ false, opt_capt, opt_handler);
    186 }
    187};
    188
    189
    190/**
    191 * Adds an event listener for a specific event on a native event
    192 * target. A listener can only be added once to an object and if it
    193 * is added again the key for the listener is returned.
    194 *
    195 * Note that a one-off listener will not change an existing listener,
    196 * if any. On the other hand a normal listener will change existing
    197 * one-off listener to become a normal listener.
    198 *
    199 * @param {EventTarget} src The node to listen to events on.
    200 * @param {string|!goog.events.EventId} type Event type.
    201 * @param {!Function} listener Callback function.
    202 * @param {boolean} callOnce Whether the listener is a one-off
    203 * listener or otherwise.
    204 * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
    205 * false).
    206 * @param {Object=} opt_handler Element in whose scope to call the listener.
    207 * @return {goog.events.ListenableKey} Unique key for the listener.
    208 * @private
    209 */
    210goog.events.listen_ = function(
    211 src, type, listener, callOnce, opt_capt, opt_handler) {
    212 if (!type) {
    213 throw Error('Invalid event type');
    214 }
    215
    216 var capture = !!opt_capt;
    217 if (capture && !goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {
    218 if (goog.events.CAPTURE_SIMULATION_MODE ==
    219 goog.events.CaptureSimulationMode.OFF_AND_FAIL) {
    220 goog.asserts.fail('Can not register capture listener in IE8-.');
    221 return null;
    222 } else if (goog.events.CAPTURE_SIMULATION_MODE ==
    223 goog.events.CaptureSimulationMode.OFF_AND_SILENT) {
    224 return null;
    225 }
    226 }
    227
    228 var listenerMap = goog.events.getListenerMap_(src);
    229 if (!listenerMap) {
    230 src[goog.events.LISTENER_MAP_PROP_] = listenerMap =
    231 new goog.events.ListenerMap(src);
    232 }
    233
    234 var listenerObj = listenerMap.add(
    235 type, listener, callOnce, opt_capt, opt_handler);
    236
    237 // If the listenerObj already has a proxy, it has been set up
    238 // previously. We simply return.
    239 if (listenerObj.proxy) {
    240 return listenerObj;
    241 }
    242
    243 var proxy = goog.events.getProxy();
    244 listenerObj.proxy = proxy;
    245
    246 proxy.src = src;
    247 proxy.listener = listenerObj;
    248
    249 // Attach the proxy through the browser's API
    250 if (src.addEventListener) {
    251 src.addEventListener(type.toString(), proxy, capture);
    252 } else {
    253 // The else above used to be else if (src.attachEvent) and then there was
    254 // another else statement that threw an exception warning the developer
    255 // they made a mistake. This resulted in an extra object allocation in IE6
    256 // due to a wrapper object that had to be implemented around the element
    257 // and so was removed.
    258 src.attachEvent(goog.events.getOnString_(type.toString()), proxy);
    259 }
    260
    261 goog.events.listenerCountEstimate_++;
    262 return listenerObj;
    263};
    264
    265
    266/**
    267 * Helper function for returning a proxy function.
    268 * @return {!Function} A new or reused function object.
    269 */
    270goog.events.getProxy = function() {
    271 var proxyCallbackFunction = goog.events.handleBrowserEvent_;
    272 // Use a local var f to prevent one allocation.
    273 var f = goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT ?
    274 function(eventObject) {
    275 return proxyCallbackFunction.call(f.src, f.listener, eventObject);
    276 } :
    277 function(eventObject) {
    278 var v = proxyCallbackFunction.call(f.src, f.listener, eventObject);
    279 // NOTE(user): In IE, we hack in a capture phase. However, if
    280 // there is inline event handler which tries to prevent default (for
    281 // example <a href="..." onclick="return false">...</a>) in a
    282 // descendant element, the prevent default will be overridden
    283 // by this listener if this listener were to return true. Hence, we
    284 // return undefined.
    285 if (!v) return v;
    286 };
    287 return f;
    288};
    289
    290
    291/**
    292 * Adds an event listener for a specific event on a native event
    293 * target (such as a DOM element) or an object that has implemented
    294 * {@link goog.events.Listenable}. After the event has fired the event
    295 * listener is removed from the target.
    296 *
    297 * If an existing listener already exists, listenOnce will do
    298 * nothing. In particular, if the listener was previously registered
    299 * via listen(), listenOnce() will not turn the listener into a
    300 * one-off listener. Similarly, if there is already an existing
    301 * one-off listener, listenOnce does not modify the listeners (it is
    302 * still a once listener).
    303 *
    304 * @param {EventTarget|goog.events.Listenable} src The node to listen
    305 * to events on.
    306 * @param {string|Array.<string>|
    307 * !goog.events.EventId.<EVENTOBJ>|!Array.<!goog.events.EventId.<EVENTOBJ>>}
    308 * type Event type or array of event types.
    309 * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}
    310 * listener Callback method.
    311 * @param {boolean=} opt_capt Fire in capture phase?.
    312 * @param {T=} opt_handler Element in whose scope to call the listener.
    313 * @return {goog.events.Key} Unique key for the listener.
    314 * @template T,EVENTOBJ
    315 */
    316goog.events.listenOnce = function(src, type, listener, opt_capt, opt_handler) {
    317 if (goog.isArray(type)) {
    318 for (var i = 0; i < type.length; i++) {
    319 goog.events.listenOnce(src, type[i], listener, opt_capt, opt_handler);
    320 }
    321 return null;
    322 }
    323
    324 listener = goog.events.wrapListener(listener);
    325 if (goog.events.Listenable.isImplementedBy(src)) {
    326 return src.listenOnce(
    327 /** @type {string|!goog.events.EventId} */ (type),
    328 listener, opt_capt, opt_handler);
    329 } else {
    330 return goog.events.listen_(
    331 /** @type {EventTarget} */ (src),
    332 /** @type {string|!goog.events.EventId} */ (type),
    333 listener, /* callOnce */ true, opt_capt, opt_handler);
    334 }
    335};
    336
    337
    338/**
    339 * Adds an event listener with a specific event wrapper on a DOM Node or an
    340 * object that has implemented {@link goog.events.Listenable}. A listener can
    341 * only be added once to an object.
    342 *
    343 * @param {EventTarget|goog.events.Listenable} src The target to
    344 * listen to events on.
    345 * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
    346 * @param {function(this:T, ?):?|{handleEvent:function(?):?}|null} listener
    347 * Callback method, or an object with a handleEvent function.
    348 * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
    349 * false).
    350 * @param {T=} opt_handler Element in whose scope to call the listener.
    351 * @template T
    352 */
    353goog.events.listenWithWrapper = function(src, wrapper, listener, opt_capt,
    354 opt_handler) {
    355 wrapper.listen(src, listener, opt_capt, opt_handler);
    356};
    357
    358
    359/**
    360 * Removes an event listener which was added with listen().
    361 *
    362 * @param {EventTarget|goog.events.Listenable} src The target to stop
    363 * listening to events on.
    364 * @param {string|Array.<string>|
    365 * !goog.events.EventId.<EVENTOBJ>|!Array.<!goog.events.EventId.<EVENTOBJ>>}
    366 * type Event type or array of event types to unlisten to.
    367 * @param {function(?):?|{handleEvent:function(?):?}|null} listener The
    368 * listener function to remove.
    369 * @param {boolean=} opt_capt In DOM-compliant browsers, this determines
    370 * whether the listener is fired during the capture or bubble phase of the
    371 * event.
    372 * @param {Object=} opt_handler Element in whose scope to call the listener.
    373 * @return {?boolean} indicating whether the listener was there to remove.
    374 * @template EVENTOBJ
    375 */
    376goog.events.unlisten = function(src, type, listener, opt_capt, opt_handler) {
    377 if (goog.isArray(type)) {
    378 for (var i = 0; i < type.length; i++) {
    379 goog.events.unlisten(src, type[i], listener, opt_capt, opt_handler);
    380 }
    381 return null;
    382 }
    383
    384 listener = goog.events.wrapListener(listener);
    385 if (goog.events.Listenable.isImplementedBy(src)) {
    386 return src.unlisten(
    387 /** @type {string|!goog.events.EventId} */ (type),
    388 listener, opt_capt, opt_handler);
    389 }
    390
    391 if (!src) {
    392 // TODO(user): We should tighten the API to only accept
    393 // non-null objects, or add an assertion here.
    394 return false;
    395 }
    396
    397 var capture = !!opt_capt;
    398 var listenerMap = goog.events.getListenerMap_(
    399 /** @type {EventTarget} */ (src));
    400 if (listenerMap) {
    401 var listenerObj = listenerMap.getListener(
    402 /** @type {string|!goog.events.EventId} */ (type),
    403 listener, capture, opt_handler);
    404 if (listenerObj) {
    405 return goog.events.unlistenByKey(listenerObj);
    406 }
    407 }
    408
    409 return false;
    410};
    411
    412
    413/**
    414 * Removes an event listener which was added with listen() by the key
    415 * returned by listen().
    416 *
    417 * @param {goog.events.Key} key The key returned by listen() for this
    418 * event listener.
    419 * @return {boolean} indicating whether the listener was there to remove.
    420 */
    421goog.events.unlistenByKey = function(key) {
    422 // TODO(user): Remove this check when tests that rely on this
    423 // are fixed.
    424 if (goog.isNumber(key)) {
    425 return false;
    426 }
    427
    428 var listener = /** @type {goog.events.ListenableKey} */ (key);
    429 if (!listener || listener.removed) {
    430 return false;
    431 }
    432
    433 var src = listener.src;
    434 if (goog.events.Listenable.isImplementedBy(src)) {
    435 return src.unlistenByKey(listener);
    436 }
    437
    438 var type = listener.type;
    439 var proxy = listener.proxy;
    440 if (src.removeEventListener) {
    441 src.removeEventListener(type, proxy, listener.capture);
    442 } else if (src.detachEvent) {
    443 src.detachEvent(goog.events.getOnString_(type), proxy);
    444 }
    445 goog.events.listenerCountEstimate_--;
    446
    447 var listenerMap = goog.events.getListenerMap_(
    448 /** @type {EventTarget} */ (src));
    449 // TODO(user): Try to remove this conditional and execute the
    450 // first branch always. This should be safe.
    451 if (listenerMap) {
    452 listenerMap.removeByKey(listener);
    453 if (listenerMap.getTypeCount() == 0) {
    454 // Null the src, just because this is simple to do (and useful
    455 // for IE <= 7).
    456 listenerMap.src = null;
    457 // We don't use delete here because IE does not allow delete
    458 // on a window object.
    459 src[goog.events.LISTENER_MAP_PROP_] = null;
    460 }
    461 } else {
    462 listener.markAsRemoved();
    463 }
    464
    465 return true;
    466};
    467
    468
    469/**
    470 * Removes an event listener which was added with listenWithWrapper().
    471 *
    472 * @param {EventTarget|goog.events.Listenable} src The target to stop
    473 * listening to events on.
    474 * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
    475 * @param {function(?):?|{handleEvent:function(?):?}|null} listener The
    476 * listener function to remove.
    477 * @param {boolean=} opt_capt In DOM-compliant browsers, this determines
    478 * whether the listener is fired during the capture or bubble phase of the
    479 * event.
    480 * @param {Object=} opt_handler Element in whose scope to call the listener.
    481 */
    482goog.events.unlistenWithWrapper = function(src, wrapper, listener, opt_capt,
    483 opt_handler) {
    484 wrapper.unlisten(src, listener, opt_capt, opt_handler);
    485};
    486
    487
    488/**
    489 * Removes all listeners from an object. You can also optionally
    490 * remove listeners of a particular type.
    491 *
    492 * @param {Object|undefined} obj Object to remove listeners from. Must be an
    493 * EventTarget or a goog.events.Listenable.
    494 * @param {string|!goog.events.EventId=} opt_type Type of event to remove.
    495 * Default is all types.
    496 * @return {number} Number of listeners removed.
    497 */
    498goog.events.removeAll = function(obj, opt_type) {
    499 // TODO(user): Change the type of obj to
    500 // (!EventTarget|!goog.events.Listenable).
    501
    502 if (!obj) {
    503 return 0;
    504 }
    505
    506 if (goog.events.Listenable.isImplementedBy(obj)) {
    507 return obj.removeAllListeners(opt_type);
    508 }
    509
    510 var listenerMap = goog.events.getListenerMap_(
    511 /** @type {EventTarget} */ (obj));
    512 if (!listenerMap) {
    513 return 0;
    514 }
    515
    516 var count = 0;
    517 var typeStr = opt_type && opt_type.toString();
    518 for (var type in listenerMap.listeners) {
    519 if (!typeStr || type == typeStr) {
    520 // Clone so that we don't need to worry about unlistenByKey
    521 // changing the content of the ListenerMap.
    522 var listeners = listenerMap.listeners[type].concat();
    523 for (var i = 0; i < listeners.length; ++i) {
    524 if (goog.events.unlistenByKey(listeners[i])) {
    525 ++count;
    526 }
    527 }
    528 }
    529 }
    530 return count;
    531};
    532
    533
    534/**
    535 * Removes all native listeners registered via goog.events. Native
    536 * listeners are listeners on native browser objects (such as DOM
    537 * elements). In particular, goog.events.Listenable and
    538 * goog.events.EventTarget listeners will NOT be removed.
    539 * @return {number} Number of listeners removed.
    540 * @deprecated This doesn't do anything, now that Closure no longer
    541 * stores a central listener registry.
    542 */
    543goog.events.removeAllNativeListeners = function() {
    544 goog.events.listenerCountEstimate_ = 0;
    545 return 0;
    546};
    547
    548
    549/**
    550 * Gets the listeners for a given object, type and capture phase.
    551 *
    552 * @param {Object} obj Object to get listeners for.
    553 * @param {string|!goog.events.EventId} type Event type.
    554 * @param {boolean} capture Capture phase?.
    555 * @return {Array.<goog.events.Listener>} Array of listener objects.
    556 */
    557goog.events.getListeners = function(obj, type, capture) {
    558 if (goog.events.Listenable.isImplementedBy(obj)) {
    559 return obj.getListeners(type, capture);
    560 } else {
    561 if (!obj) {
    562 // TODO(user): We should tighten the API to accept
    563 // !EventTarget|goog.events.Listenable, and add an assertion here.
    564 return [];
    565 }
    566
    567 var listenerMap = goog.events.getListenerMap_(
    568 /** @type {EventTarget} */ (obj));
    569 return listenerMap ? listenerMap.getListeners(type, capture) : [];
    570 }
    571};
    572
    573
    574/**
    575 * Gets the goog.events.Listener for the event or null if no such listener is
    576 * in use.
    577 *
    578 * @param {EventTarget|goog.events.Listenable} src The target from
    579 * which to get listeners.
    580 * @param {?string|!goog.events.EventId.<EVENTOBJ>} type The type of the event.
    581 * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null} listener The
    582 * listener function to get.
    583 * @param {boolean=} opt_capt In DOM-compliant browsers, this determines
    584 * whether the listener is fired during the
    585 * capture or bubble phase of the event.
    586 * @param {Object=} opt_handler Element in whose scope to call the listener.
    587 * @return {goog.events.ListenableKey} the found listener or null if not found.
    588 * @template EVENTOBJ
    589 */
    590goog.events.getListener = function(src, type, listener, opt_capt, opt_handler) {
    591 // TODO(user): Change type from ?string to string, or add assertion.
    592 type = /** @type {string} */ (type);
    593 listener = goog.events.wrapListener(listener);
    594 var capture = !!opt_capt;
    595 if (goog.events.Listenable.isImplementedBy(src)) {
    596 return src.getListener(type, listener, capture, opt_handler);
    597 }
    598
    599 if (!src) {
    600 // TODO(user): We should tighten the API to only accept
    601 // non-null objects, or add an assertion here.
    602 return null;
    603 }
    604
    605 var listenerMap = goog.events.getListenerMap_(
    606 /** @type {EventTarget} */ (src));
    607 if (listenerMap) {
    608 return listenerMap.getListener(type, listener, capture, opt_handler);
    609 }
    610 return null;
    611};
    612
    613
    614/**
    615 * Returns whether an event target has any active listeners matching the
    616 * specified signature. If either the type or capture parameters are
    617 * unspecified, the function will match on the remaining criteria.
    618 *
    619 * @param {EventTarget|goog.events.Listenable} obj Target to get
    620 * listeners for.
    621 * @param {string|!goog.events.EventId=} opt_type Event type.
    622 * @param {boolean=} opt_capture Whether to check for capture or bubble-phase
    623 * listeners.
    624 * @return {boolean} Whether an event target has one or more listeners matching
    625 * the requested type and/or capture phase.
    626 */
    627goog.events.hasListener = function(obj, opt_type, opt_capture) {
    628 if (goog.events.Listenable.isImplementedBy(obj)) {
    629 return obj.hasListener(opt_type, opt_capture);
    630 }
    631
    632 var listenerMap = goog.events.getListenerMap_(
    633 /** @type {EventTarget} */ (obj));
    634 return !!listenerMap && listenerMap.hasListener(opt_type, opt_capture);
    635};
    636
    637
    638/**
    639 * Provides a nice string showing the normalized event objects public members
    640 * @param {Object} e Event Object.
    641 * @return {string} String of the public members of the normalized event object.
    642 */
    643goog.events.expose = function(e) {
    644 var str = [];
    645 for (var key in e) {
    646 if (e[key] && e[key].id) {
    647 str.push(key + ' = ' + e[key] + ' (' + e[key].id + ')');
    648 } else {
    649 str.push(key + ' = ' + e[key]);
    650 }
    651 }
    652 return str.join('\n');
    653};
    654
    655
    656/**
    657 * Returns a string with on prepended to the specified type. This is used for IE
    658 * which expects "on" to be prepended. This function caches the string in order
    659 * to avoid extra allocations in steady state.
    660 * @param {string} type Event type.
    661 * @return {string} The type string with 'on' prepended.
    662 * @private
    663 */
    664goog.events.getOnString_ = function(type) {
    665 if (type in goog.events.onStringMap_) {
    666 return goog.events.onStringMap_[type];
    667 }
    668 return goog.events.onStringMap_[type] = goog.events.onString_ + type;
    669};
    670
    671
    672/**
    673 * Fires an object's listeners of a particular type and phase
    674 *
    675 * @param {Object} obj Object whose listeners to call.
    676 * @param {string|!goog.events.EventId} type Event type.
    677 * @param {boolean} capture Which event phase.
    678 * @param {Object} eventObject Event object to be passed to listener.
    679 * @return {boolean} True if all listeners returned true else false.
    680 */
    681goog.events.fireListeners = function(obj, type, capture, eventObject) {
    682 if (goog.events.Listenable.isImplementedBy(obj)) {
    683 return obj.fireListeners(type, capture, eventObject);
    684 }
    685
    686 return goog.events.fireListeners_(obj, type, capture, eventObject);
    687};
    688
    689
    690/**
    691 * Fires an object's listeners of a particular type and phase.
    692 * @param {Object} obj Object whose listeners to call.
    693 * @param {string|!goog.events.EventId} type Event type.
    694 * @param {boolean} capture Which event phase.
    695 * @param {Object} eventObject Event object to be passed to listener.
    696 * @return {boolean} True if all listeners returned true else false.
    697 * @private
    698 */
    699goog.events.fireListeners_ = function(obj, type, capture, eventObject) {
    700 var retval = 1;
    701
    702 var listenerMap = goog.events.getListenerMap_(
    703 /** @type {EventTarget} */ (obj));
    704 if (listenerMap) {
    705 // TODO(user): Original code avoids array creation when there
    706 // is no listener, so we do the same. If this optimization turns
    707 // out to be not required, we can replace this with
    708 // listenerMap.getListeners(type, capture) instead, which is simpler.
    709 var listenerArray = listenerMap.listeners[type.toString()];
    710 if (listenerArray) {
    711 listenerArray = listenerArray.concat();
    712 for (var i = 0; i < listenerArray.length; i++) {
    713 var listener = listenerArray[i];
    714 // We might not have a listener if the listener was removed.
    715 if (listener && listener.capture == capture && !listener.removed) {
    716 retval &=
    717 goog.events.fireListener(listener, eventObject) !== false;
    718 }
    719 }
    720 }
    721 }
    722 return Boolean(retval);
    723};
    724
    725
    726/**
    727 * Fires a listener with a set of arguments
    728 *
    729 * @param {goog.events.Listener} listener The listener object to call.
    730 * @param {Object} eventObject The event object to pass to the listener.
    731 * @return {boolean} Result of listener.
    732 */
    733goog.events.fireListener = function(listener, eventObject) {
    734 var listenerFn = listener.listener;
    735 var listenerHandler = listener.handler || listener.src;
    736
    737 if (listener.callOnce) {
    738 goog.events.unlistenByKey(listener);
    739 }
    740 return listenerFn.call(listenerHandler, eventObject);
    741};
    742
    743
    744/**
    745 * Gets the total number of listeners currently in the system.
    746 * @return {number} Number of listeners.
    747 * @deprecated This returns estimated count, now that Closure no longer
    748 * stores a central listener registry. We still return an estimation
    749 * to keep existing listener-related tests passing. In the near future,
    750 * this function will be removed.
    751 */
    752goog.events.getTotalListenerCount = function() {
    753 return goog.events.listenerCountEstimate_;
    754};
    755
    756
    757/**
    758 * Dispatches an event (or event like object) and calls all listeners
    759 * listening for events of this type. The type of the event is decided by the
    760 * type property on the event object.
    761 *
    762 * If any of the listeners returns false OR calls preventDefault then this
    763 * function will return false. If one of the capture listeners calls
    764 * stopPropagation, then the bubble listeners won't fire.
    765 *
    766 * @param {goog.events.Listenable} src The event target.
    767 * @param {goog.events.EventLike} e Event object.
    768 * @return {boolean} If anyone called preventDefault on the event object (or
    769 * if any of the handlers returns false) this will also return false.
    770 * If there are no handlers, or if all handlers return true, this returns
    771 * true.
    772 */
    773goog.events.dispatchEvent = function(src, e) {
    774 goog.asserts.assert(
    775 goog.events.Listenable.isImplementedBy(src),
    776 'Can not use goog.events.dispatchEvent with ' +
    777 'non-goog.events.Listenable instance.');
    778 return src.dispatchEvent(e);
    779};
    780
    781
    782/**
    783 * Installs exception protection for the browser event entry point using the
    784 * given error handler.
    785 *
    786 * @param {goog.debug.ErrorHandler} errorHandler Error handler with which to
    787 * protect the entry point.
    788 */
    789goog.events.protectBrowserEventEntryPoint = function(errorHandler) {
    790 goog.events.handleBrowserEvent_ = errorHandler.protectEntryPoint(
    791 goog.events.handleBrowserEvent_);
    792};
    793
    794
    795/**
    796 * Handles an event and dispatches it to the correct listeners. This
    797 * function is a proxy for the real listener the user specified.
    798 *
    799 * @param {goog.events.Listener} listener The listener object.
    800 * @param {Event=} opt_evt Optional event object that gets passed in via the
    801 * native event handlers.
    802 * @return {boolean} Result of the event handler.
    803 * @this {EventTarget} The object or Element that fired the event.
    804 * @private
    805 */
    806goog.events.handleBrowserEvent_ = function(listener, opt_evt) {
    807 if (listener.removed) {
    808 return true;
    809 }
    810
    811 // Synthesize event propagation if the browser does not support W3C
    812 // event model.
    813 if (!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {
    814 var ieEvent = opt_evt ||
    815 /** @type {Event} */ (goog.getObjectByName('window.event'));
    816 var evt = new goog.events.BrowserEvent(ieEvent, this);
    817 var retval = true;
    818
    819 if (goog.events.CAPTURE_SIMULATION_MODE ==
    820 goog.events.CaptureSimulationMode.ON) {
    821 // If we have not marked this event yet, we should perform capture
    822 // simulation.
    823 if (!goog.events.isMarkedIeEvent_(ieEvent)) {
    824 goog.events.markIeEvent_(ieEvent);
    825
    826 var ancestors = [];
    827 for (var parent = evt.currentTarget; parent;
    828 parent = parent.parentNode) {
    829 ancestors.push(parent);
    830 }
    831
    832 // Fire capture listeners.
    833 var type = listener.type;
    834 for (var i = ancestors.length - 1; !evt.propagationStopped_ && i >= 0;
    835 i--) {
    836 evt.currentTarget = ancestors[i];
    837 retval &= goog.events.fireListeners_(ancestors[i], type, true, evt);
    838 }
    839
    840 // Fire bubble listeners.
    841 //
    842 // We can technically rely on IE to perform bubble event
    843 // propagation. However, it turns out that IE fires events in
    844 // opposite order of attachEvent registration, which broke
    845 // some code and tests that rely on the order. (While W3C DOM
    846 // Level 2 Events TR leaves the event ordering unspecified,
    847 // modern browsers and W3C DOM Level 3 Events Working Draft
    848 // actually specify the order as the registration order.)
    849 for (var i = 0; !evt.propagationStopped_ && i < ancestors.length; i++) {
    850 evt.currentTarget = ancestors[i];
    851 retval &= goog.events.fireListeners_(ancestors[i], type, false, evt);
    852 }
    853 }
    854 } else {
    855 retval = goog.events.fireListener(listener, evt);
    856 }
    857 return retval;
    858 }
    859
    860 // Otherwise, simply fire the listener.
    861 return goog.events.fireListener(
    862 listener, new goog.events.BrowserEvent(opt_evt, this));
    863};
    864
    865
    866/**
    867 * This is used to mark the IE event object so we do not do the Closure pass
    868 * twice for a bubbling event.
    869 * @param {Event} e The IE browser event.
    870 * @private
    871 */
    872goog.events.markIeEvent_ = function(e) {
    873 // Only the keyCode and the returnValue can be changed. We use keyCode for
    874 // non keyboard events.
    875 // event.returnValue is a bit more tricky. It is undefined by default. A
    876 // boolean false prevents the default action. In a window.onbeforeunload and
    877 // the returnValue is non undefined it will be alerted. However, we will only
    878 // modify the returnValue for keyboard events. We can get a problem if non
    879 // closure events sets the keyCode or the returnValue
    880
    881 var useReturnValue = false;
    882
    883 if (e.keyCode == 0) {
    884 // We cannot change the keyCode in case that srcElement is input[type=file].
    885 // We could test that that is the case but that would allocate 3 objects.
    886 // If we use try/catch we will only allocate extra objects in the case of a
    887 // failure.
    888 /** @preserveTry */
    889 try {
    890 e.keyCode = -1;
    891 return;
    892 } catch (ex) {
    893 useReturnValue = true;
    894 }
    895 }
    896
    897 if (useReturnValue ||
    898 /** @type {boolean|undefined} */ (e.returnValue) == undefined) {
    899 e.returnValue = true;
    900 }
    901};
    902
    903
    904/**
    905 * This is used to check if an IE event has already been handled by the Closure
    906 * system so we do not do the Closure pass twice for a bubbling event.
    907 * @param {Event} e The IE browser event.
    908 * @return {boolean} True if the event object has been marked.
    909 * @private
    910 */
    911goog.events.isMarkedIeEvent_ = function(e) {
    912 return e.keyCode < 0 || e.returnValue != undefined;
    913};
    914
    915
    916/**
    917 * Counter to create unique event ids.
    918 * @private {number}
    919 */
    920goog.events.uniqueIdCounter_ = 0;
    921
    922
    923/**
    924 * Creates a unique event id.
    925 *
    926 * @param {string} identifier The identifier.
    927 * @return {string} A unique identifier.
    928 * @idGenerator
    929 */
    930goog.events.getUniqueId = function(identifier) {
    931 return identifier + '_' + goog.events.uniqueIdCounter_++;
    932};
    933
    934
    935/**
    936 * @param {EventTarget} src The source object.
    937 * @return {goog.events.ListenerMap} A listener map for the given
    938 * source object, or null if none exists.
    939 * @private
    940 */
    941goog.events.getListenerMap_ = function(src) {
    942 var listenerMap = src[goog.events.LISTENER_MAP_PROP_];
    943 // IE serializes the property as well (e.g. when serializing outer
    944 // HTML). So we must check that the value is of the correct type.
    945 return listenerMap instanceof goog.events.ListenerMap ? listenerMap : null;
    946};
    947
    948
    949/**
    950 * Expando property for listener function wrapper for Object with
    951 * handleEvent.
    952 * @private @const {string}
    953 */
    954goog.events.LISTENER_WRAPPER_PROP_ = '__closure_events_fn_' +
    955 ((Math.random() * 1e9) >>> 0);
    956
    957
    958/**
    959 * @param {Object|Function} listener The listener function or an
    960 * object that contains handleEvent method.
    961 * @return {!Function} Either the original function or a function that
    962 * calls obj.handleEvent. If the same listener is passed to this
    963 * function more than once, the same function is guaranteed to be
    964 * returned.
    965 */
    966goog.events.wrapListener = function(listener) {
    967 goog.asserts.assert(listener, 'Listener can not be null.');
    968
    969 if (goog.isFunction(listener)) {
    970 return listener;
    971 }
    972
    973 goog.asserts.assert(
    974 listener.handleEvent, 'An object listener must have handleEvent method.');
    975 if (!listener[goog.events.LISTENER_WRAPPER_PROP_]) {
    976 listener[goog.events.LISTENER_WRAPPER_PROP_] =
    977 function(e) { return listener.handleEvent(e); };
    978 }
    979 return listener[goog.events.LISTENER_WRAPPER_PROP_];
    980};
    981
    982
    983// Register the browser event handler as an entry point, so that
    984// it can be monitored for exception handling, etc.
    985goog.debug.entryPointRegistry.register(
    986 /**
    987 * @param {function(!Function): !Function} transformer The transforming
    988 * function.
    989 */
    990 function(transformer) {
    991 goog.events.handleBrowserEvent_ = transformer(
    992 goog.events.handleBrowserEvent_);
    993 });
    \ No newline at end of file +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 = /** @type {goog.events.ListenableKey} */ (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 index 88ce69d..ea50cac 100644 --- a/docs/source/lib/goog/events/eventtarget.js.src.html +++ b/docs/source/lib/goog/events/eventtarget.js.src.html @@ -1 +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 * @author pupius@google.com (Daniel Pupius) [Port to use goog.events]
    22 * @see ../demos/eventtarget.html
    23 * @see goog.events.Listenable
    24 */
    25
    26goog.provide('goog.events.EventTarget');
    27
    28goog.require('goog.Disposable');
    29goog.require('goog.asserts');
    30goog.require('goog.events');
    31goog.require('goog.events.Event');
    32goog.require('goog.events.Listenable');
    33goog.require('goog.events.ListenerMap');
    34goog.require('goog.object');
    35
    36
    37
    38/**
    39 * An implementation of {@code goog.events.Listenable} with full W3C
    40 * EventTarget-like support (capture/bubble mechanism, stopping event
    41 * propagation, preventing default actions).
    42 *
    43 * You may subclass this class to turn your class into a Listenable.
    44 *
    45 * Unless propagation is stopped, an event dispatched by an
    46 * EventTarget will bubble to the parent returned by
    47 * {@code getParentEventTarget}. To set the parent, call
    48 * {@code setParentEventTarget}. Subclasses that don't support
    49 * changing the parent can override the setter to throw an error.
    50 *
    51 * Example usage:
    52 * <pre>
    53 * var source = new goog.events.EventTarget();
    54 * function handleEvent(e) {
    55 * alert('Type: ' + e.type + '; Target: ' + e.target);
    56 * }
    57 * source.listen('foo', handleEvent);
    58 * // Or: goog.events.listen(source, 'foo', handleEvent);
    59 * ...
    60 * source.dispatchEvent('foo'); // will call handleEvent
    61 * ...
    62 * source.unlisten('foo', handleEvent);
    63 * // Or: goog.events.unlisten(source, 'foo', handleEvent);
    64 * </pre>
    65 *
    66 * @constructor
    67 * @extends {goog.Disposable}
    68 * @implements {goog.events.Listenable}
    69 */
    70goog.events.EventTarget = function() {
    71 goog.Disposable.call(this);
    72
    73 /**
    74 * Maps of event type to an array of listeners.
    75 * @private {!goog.events.ListenerMap}
    76 */
    77 this.eventTargetListeners_ = new goog.events.ListenerMap(this);
    78
    79 /**
    80 * The object to use for event.target. Useful when mixing in an
    81 * EventTarget to another object.
    82 * @private {!Object}
    83 */
    84 this.actualEventTarget_ = this;
    85
    86 /**
    87 * Parent event target, used during event bubbling.
    88 *
    89 * TODO(user): Change this to goog.events.Listenable. This
    90 * currently breaks people who expect getParentEventTarget to return
    91 * goog.events.EventTarget.
    92 *
    93 * @private {goog.events.EventTarget}
    94 */
    95 this.parentEventTarget_ = null;
    96};
    97goog.inherits(goog.events.EventTarget, goog.Disposable);
    98goog.events.Listenable.addImplementation(goog.events.EventTarget);
    99
    100
    101/**
    102 * An artificial cap on the number of ancestors you can have. This is mainly
    103 * for loop detection.
    104 * @const {number}
    105 * @private
    106 */
    107goog.events.EventTarget.MAX_ANCESTORS_ = 1000;
    108
    109
    110/**
    111 * Returns the parent of this event target to use for bubbling.
    112 *
    113 * @return {goog.events.EventTarget} The parent EventTarget or null if
    114 * there is no parent.
    115 * @override
    116 */
    117goog.events.EventTarget.prototype.getParentEventTarget = function() {
    118 return this.parentEventTarget_;
    119};
    120
    121
    122/**
    123 * Sets the parent of this event target to use for capture/bubble
    124 * mechanism.
    125 * @param {goog.events.EventTarget} parent Parent listenable (null if none).
    126 */
    127goog.events.EventTarget.prototype.setParentEventTarget = function(parent) {
    128 this.parentEventTarget_ = parent;
    129};
    130
    131
    132/**
    133 * Adds an event listener to the event target. The same handler can only be
    134 * added once per the type. Even if you add the same handler multiple times
    135 * using the same type then it will only be called once when the event is
    136 * dispatched.
    137 *
    138 * @param {string} type The type of the event to listen for.
    139 * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function
    140 * to handle the event. The handler can also be an object that implements
    141 * the handleEvent method which takes the event object as argument.
    142 * @param {boolean=} opt_capture In DOM-compliant browsers, this determines
    143 * whether the listener is fired during the capture or bubble phase
    144 * of the event.
    145 * @param {Object=} opt_handlerScope Object in whose scope to call
    146 * the listener.
    147 * @deprecated Use {@code #listen} instead, when possible. Otherwise, use
    148 * {@code goog.events.listen} if you are passing Object
    149 * (instead of Function) as handler.
    150 */
    151goog.events.EventTarget.prototype.addEventListener = function(
    152 type, handler, opt_capture, opt_handlerScope) {
    153 goog.events.listen(this, type, handler, opt_capture, opt_handlerScope);
    154};
    155
    156
    157/**
    158 * Removes an event listener from the event target. The handler must be the
    159 * same object as the one added. If the handler has not been added then
    160 * nothing is done.
    161 *
    162 * @param {string} type The type of the event to listen for.
    163 * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function
    164 * to handle the event. The handler can also be an object that implements
    165 * the handleEvent method which takes the event object as argument.
    166 * @param {boolean=} opt_capture In DOM-compliant browsers, this determines
    167 * whether the listener is fired during the capture or bubble phase
    168 * of the event.
    169 * @param {Object=} opt_handlerScope Object in whose scope to call
    170 * the listener.
    171 * @deprecated Use {@code #unlisten} instead, when possible. Otherwise, use
    172 * {@code goog.events.unlisten} if you are passing Object
    173 * (instead of Function) as handler.
    174 */
    175goog.events.EventTarget.prototype.removeEventListener = function(
    176 type, handler, opt_capture, opt_handlerScope) {
    177 goog.events.unlisten(this, type, handler, opt_capture, opt_handlerScope);
    178};
    179
    180
    181/** @override */
    182goog.events.EventTarget.prototype.dispatchEvent = function(e) {
    183 this.assertInitialized_();
    184
    185 var ancestorsTree, ancestor = this.getParentEventTarget();
    186 if (ancestor) {
    187 ancestorsTree = [];
    188 var ancestorCount = 1;
    189 for (; ancestor; ancestor = ancestor.getParentEventTarget()) {
    190 ancestorsTree.push(ancestor);
    191 goog.asserts.assert(
    192 (++ancestorCount < goog.events.EventTarget.MAX_ANCESTORS_),
    193 'infinite loop');
    194 }
    195 }
    196
    197 return goog.events.EventTarget.dispatchEventInternal_(
    198 this.actualEventTarget_, e, ancestorsTree);
    199};
    200
    201
    202/**
    203 * Removes listeners from this object. Classes that extend EventTarget may
    204 * need to override this method in order to remove references to DOM Elements
    205 * and additional listeners.
    206 * @override
    207 */
    208goog.events.EventTarget.prototype.disposeInternal = function() {
    209 goog.events.EventTarget.superClass_.disposeInternal.call(this);
    210
    211 this.removeAllListeners();
    212 this.parentEventTarget_ = null;
    213};
    214
    215
    216/** @override */
    217goog.events.EventTarget.prototype.listen = function(
    218 type, listener, opt_useCapture, opt_listenerScope) {
    219 this.assertInitialized_();
    220 return this.eventTargetListeners_.add(
    221 String(type), listener, false /* callOnce */, opt_useCapture,
    222 opt_listenerScope);
    223};
    224
    225
    226/** @override */
    227goog.events.EventTarget.prototype.listenOnce = function(
    228 type, listener, opt_useCapture, opt_listenerScope) {
    229 return this.eventTargetListeners_.add(
    230 String(type), listener, true /* callOnce */, opt_useCapture,
    231 opt_listenerScope);
    232};
    233
    234
    235/** @override */
    236goog.events.EventTarget.prototype.unlisten = function(
    237 type, listener, opt_useCapture, opt_listenerScope) {
    238 return this.eventTargetListeners_.remove(
    239 String(type), listener, opt_useCapture, opt_listenerScope);
    240};
    241
    242
    243/** @override */
    244goog.events.EventTarget.prototype.unlistenByKey = function(key) {
    245 return this.eventTargetListeners_.removeByKey(key);
    246};
    247
    248
    249/** @override */
    250goog.events.EventTarget.prototype.removeAllListeners = function(opt_type) {
    251 // TODO(user): Previously, removeAllListeners can be called on
    252 // uninitialized EventTarget, so we preserve that behavior. We
    253 // should remove this when usages that rely on that fact are purged.
    254 if (!this.eventTargetListeners_) {
    255 return 0;
    256 }
    257 return this.eventTargetListeners_.removeAll(opt_type);
    258};
    259
    260
    261/** @override */
    262goog.events.EventTarget.prototype.fireListeners = function(
    263 type, capture, eventObject) {
    264 // TODO(user): Original code avoids array creation when there
    265 // is no listener, so we do the same. If this optimization turns
    266 // out to be not required, we can replace this with
    267 // getListeners(type, capture) instead, which is simpler.
    268 var listenerArray = this.eventTargetListeners_.listeners[String(type)];
    269 if (!listenerArray) {
    270 return true;
    271 }
    272 listenerArray = listenerArray.concat();
    273
    274 var rv = true;
    275 for (var i = 0; i < listenerArray.length; ++i) {
    276 var listener = listenerArray[i];
    277 // We might not have a listener if the listener was removed.
    278 if (listener && !listener.removed && listener.capture == capture) {
    279 var listenerFn = listener.listener;
    280 var listenerHandler = listener.handler || listener.src;
    281
    282 if (listener.callOnce) {
    283 this.unlistenByKey(listener);
    284 }
    285 rv = listenerFn.call(listenerHandler, eventObject) !== false && rv;
    286 }
    287 }
    288
    289 return rv && eventObject.returnValue_ != false;
    290};
    291
    292
    293/** @override */
    294goog.events.EventTarget.prototype.getListeners = function(type, capture) {
    295 return this.eventTargetListeners_.getListeners(String(type), capture);
    296};
    297
    298
    299/** @override */
    300goog.events.EventTarget.prototype.getListener = function(
    301 type, listener, capture, opt_listenerScope) {
    302 return this.eventTargetListeners_.getListener(
    303 String(type), listener, capture, opt_listenerScope);
    304};
    305
    306
    307/** @override */
    308goog.events.EventTarget.prototype.hasListener = function(
    309 opt_type, opt_capture) {
    310 var id = goog.isDef(opt_type) ? String(opt_type) : undefined;
    311 return this.eventTargetListeners_.hasListener(id, opt_capture);
    312};
    313
    314
    315/**
    316 * Sets the target to be used for {@code event.target} when firing
    317 * event. Mainly used for testing. For example, see
    318 * {@code goog.testing.events.mixinListenable}.
    319 * @param {!Object} target The target.
    320 */
    321goog.events.EventTarget.prototype.setTargetForTesting = function(target) {
    322 this.actualEventTarget_ = target;
    323};
    324
    325
    326/**
    327 * Asserts that the event target instance is initialized properly.
    328 * @private
    329 */
    330goog.events.EventTarget.prototype.assertInitialized_ = function() {
    331 goog.asserts.assert(
    332 this.eventTargetListeners_,
    333 'Event target is not initialized. Did you call the superclass ' +
    334 '(goog.events.EventTarget) constructor?');
    335};
    336
    337
    338/**
    339 * Dispatches the given event on the ancestorsTree.
    340 *
    341 * @param {!Object} target The target to dispatch on.
    342 * @param {goog.events.Event|Object|string} e The event object.
    343 * @param {Array.<goog.events.Listenable>=} opt_ancestorsTree The ancestors
    344 * tree of the target, in reverse order from the closest ancestor
    345 * to the root event target. May be null if the target has no ancestor.
    346 * @return {boolean} If anyone called preventDefault on the event object (or
    347 * if any of the listeners returns false) this will also return false.
    348 * @private
    349 */
    350goog.events.EventTarget.dispatchEventInternal_ = function(
    351 target, e, opt_ancestorsTree) {
    352 var type = e.type || /** @type {string} */ (e);
    353
    354 // If accepting a string or object, create a custom event object so that
    355 // preventDefault and stopPropagation work with the event.
    356 if (goog.isString(e)) {
    357 e = new goog.events.Event(e, target);
    358 } else if (!(e instanceof goog.events.Event)) {
    359 var oldEvent = e;
    360 e = new goog.events.Event(type, target);
    361 goog.object.extend(e, oldEvent);
    362 } else {
    363 e.target = e.target || target;
    364 }
    365
    366 var rv = true, currentTarget;
    367
    368 // Executes all capture listeners on the ancestors, if any.
    369 if (opt_ancestorsTree) {
    370 for (var i = opt_ancestorsTree.length - 1; !e.propagationStopped_ && i >= 0;
    371 i--) {
    372 currentTarget = e.currentTarget = opt_ancestorsTree[i];
    373 rv = currentTarget.fireListeners(type, true, e) && rv;
    374 }
    375 }
    376
    377 // Executes capture and bubble listeners on the target.
    378 if (!e.propagationStopped_) {
    379 currentTarget = e.currentTarget = target;
    380 rv = currentTarget.fireListeners(type, true, e) && rv;
    381 if (!e.propagationStopped_) {
    382 rv = currentTarget.fireListeners(type, false, e) && rv;
    383 }
    384 }
    385
    386 // Executes all bubble listeners on the ancestors, if any.
    387 if (opt_ancestorsTree) {
    388 for (i = 0; !e.propagationStopped_ && i < opt_ancestorsTree.length; i++) {
    389 currentTarget = e.currentTarget = opt_ancestorsTree[i];
    390 rv = currentTarget.fireListeners(type, false, e) && rv;
    391 }
    392 }
    393
    394 return rv;
    395};
    \ No newline at end of file +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 index 6c9af2a..0ff725e 100644 --- a/docs/source/lib/goog/events/eventtype.js.src.html +++ b/docs/source/lib/goog/events/eventtype.js.src.html @@ -1 +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 * @author mirkov@google.com (Mirko Visontai)
    20 */
    21
    22
    23goog.provide('goog.events.EventType');
    24
    25goog.require('goog.userAgent');
    26
    27
    28/**
    29 * Returns a prefixed event name for the current browser.
    30 * @param {string} eventName The name of the event.
    31 * @return {string} The prefixed event name.
    32 * @suppress {missingRequire|missingProvide}
    33 * @private
    34 */
    35goog.events.getVendorPrefixedName_ = function(eventName) {
    36 return goog.userAgent.WEBKIT ? 'webkit' + eventName :
    37 (goog.userAgent.OPERA ? 'o' + eventName.toLowerCase() :
    38 eventName.toLowerCase());
    39};
    40
    41
    42/**
    43 * Constants for event names.
    44 * @enum {string}
    45 */
    46goog.events.EventType = {
    47 // Mouse events
    48 CLICK: 'click',
    49 RIGHTCLICK: 'rightclick',
    50 DBLCLICK: 'dblclick',
    51 MOUSEDOWN: 'mousedown',
    52 MOUSEUP: 'mouseup',
    53 MOUSEOVER: 'mouseover',
    54 MOUSEOUT: 'mouseout',
    55 MOUSEMOVE: 'mousemove',
    56 MOUSEENTER: 'mouseenter',
    57 MOUSELEAVE: 'mouseleave',
    58 // Select start is non-standard.
    59 // See http://msdn.microsoft.com/en-us/library/ie/ms536969(v=vs.85).aspx.
    60 SELECTSTART: 'selectstart', // IE, Safari, Chrome
    61
    62 // Key events
    63 KEYPRESS: 'keypress',
    64 KEYDOWN: 'keydown',
    65 KEYUP: 'keyup',
    66
    67 // Focus
    68 BLUR: 'blur',
    69 FOCUS: 'focus',
    70 DEACTIVATE: 'deactivate', // IE only
    71 // NOTE: The following two events are not stable in cross-browser usage.
    72 // WebKit and Opera implement DOMFocusIn/Out.
    73 // IE implements focusin/out.
    74 // Gecko implements neither see bug at
    75 // https://bugzilla.mozilla.org/show_bug.cgi?id=396927.
    76 // The DOM Events Level 3 Draft deprecates DOMFocusIn in favor of focusin:
    77 // http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
    78 // You can use FOCUS in Capture phase until implementations converge.
    79 FOCUSIN: goog.userAgent.IE ? 'focusin' : 'DOMFocusIn',
    80 FOCUSOUT: goog.userAgent.IE ? 'focusout' : 'DOMFocusOut',
    81
    82 // Forms
    83 CHANGE: 'change',
    84 SELECT: 'select',
    85 SUBMIT: 'submit',
    86 INPUT: 'input',
    87 PROPERTYCHANGE: 'propertychange', // IE only
    88
    89 // Drag and drop
    90 DRAGSTART: 'dragstart',
    91 DRAG: 'drag',
    92 DRAGENTER: 'dragenter',
    93 DRAGOVER: 'dragover',
    94 DRAGLEAVE: 'dragleave',
    95 DROP: 'drop',
    96 DRAGEND: 'dragend',
    97
    98 // WebKit touch events.
    99 TOUCHSTART: 'touchstart',
    100 TOUCHMOVE: 'touchmove',
    101 TOUCHEND: 'touchend',
    102 TOUCHCANCEL: 'touchcancel',
    103
    104 // Misc
    105 BEFOREUNLOAD: 'beforeunload',
    106 CONSOLEMESSAGE: 'consolemessage',
    107 CONTEXTMENU: 'contextmenu',
    108 DOMCONTENTLOADED: 'DOMContentLoaded',
    109 ERROR: 'error',
    110 HELP: 'help',
    111 LOAD: 'load',
    112 LOSECAPTURE: 'losecapture',
    113 ORIENTATIONCHANGE: 'orientationchange',
    114 READYSTATECHANGE: 'readystatechange',
    115 RESIZE: 'resize',
    116 SCROLL: 'scroll',
    117 UNLOAD: 'unload',
    118
    119 // HTML 5 History events
    120 // See http://www.w3.org/TR/html5/history.html#event-definitions
    121 HASHCHANGE: 'hashchange',
    122 PAGEHIDE: 'pagehide',
    123 PAGESHOW: 'pageshow',
    124 POPSTATE: 'popstate',
    125
    126 // Copy and Paste
    127 // Support is limited. Make sure it works on your favorite browser
    128 // before using.
    129 // http://www.quirksmode.org/dom/events/cutcopypaste.html
    130 COPY: 'copy',
    131 PASTE: 'paste',
    132 CUT: 'cut',
    133 BEFORECOPY: 'beforecopy',
    134 BEFORECUT: 'beforecut',
    135 BEFOREPASTE: 'beforepaste',
    136
    137 // HTML5 online/offline events.
    138 // http://www.w3.org/TR/offline-webapps/#related
    139 ONLINE: 'online',
    140 OFFLINE: 'offline',
    141
    142 // HTML 5 worker events
    143 MESSAGE: 'message',
    144 CONNECT: 'connect',
    145
    146 // CSS animation events.
    147 /** @suppress {missingRequire} */
    148 ANIMATIONSTART: goog.events.getVendorPrefixedName_('AnimationStart'),
    149 /** @suppress {missingRequire} */
    150 ANIMATIONEND: goog.events.getVendorPrefixedName_('AnimationEnd'),
    151 /** @suppress {missingRequire} */
    152 ANIMATIONITERATION: goog.events.getVendorPrefixedName_('AnimationIteration'),
    153
    154 // CSS transition events. Based on the browser support described at:
    155 // https://developer.mozilla.org/en/css/css_transitions#Browser_compatibility
    156 /** @suppress {missingRequire} */
    157 TRANSITIONEND: goog.events.getVendorPrefixedName_('TransitionEnd'),
    158
    159 // W3C Pointer Events
    160 // http://www.w3.org/TR/pointerevents/
    161 POINTERDOWN: 'pointerdown',
    162 POINTERUP: 'pointerup',
    163 POINTERCANCEL: 'pointercancel',
    164 POINTERMOVE: 'pointermove',
    165 POINTEROVER: 'pointerover',
    166 POINTEROUT: 'pointerout',
    167 POINTERENTER: 'pointerenter',
    168 POINTERLEAVE: 'pointerleave',
    169 GOTPOINTERCAPTURE: 'gotpointercapture',
    170 LOSTPOINTERCAPTURE: 'lostpointercapture',
    171
    172 // IE specific events.
    173 // See http://msdn.microsoft.com/en-us/library/ie/hh772103(v=vs.85).aspx
    174 // Note: these events will be supplanted in IE11.
    175 MSGESTURECHANGE: 'MSGestureChange',
    176 MSGESTUREEND: 'MSGestureEnd',
    177 MSGESTUREHOLD: 'MSGestureHold',
    178 MSGESTURESTART: 'MSGestureStart',
    179 MSGESTURETAP: 'MSGestureTap',
    180 MSGOTPOINTERCAPTURE: 'MSGotPointerCapture',
    181 MSINERTIASTART: 'MSInertiaStart',
    182 MSLOSTPOINTERCAPTURE: 'MSLostPointerCapture',
    183 MSPOINTERCANCEL: 'MSPointerCancel',
    184 MSPOINTERDOWN: 'MSPointerDown',
    185 MSPOINTERENTER: 'MSPointerEnter',
    186 MSPOINTERHOVER: 'MSPointerHover',
    187 MSPOINTERLEAVE: 'MSPointerLeave',
    188 MSPOINTERMOVE: 'MSPointerMove',
    189 MSPOINTEROUT: 'MSPointerOut',
    190 MSPOINTEROVER: 'MSPointerOver',
    191 MSPOINTERUP: 'MSPointerUp',
    192
    193 // Native IMEs/input tools events.
    194 TEXTINPUT: 'textinput',
    195 COMPOSITIONSTART: 'compositionstart',
    196 COMPOSITIONUPDATE: 'compositionupdate',
    197 COMPOSITIONEND: 'compositionend',
    198
    199 // Webview tag events
    200 // See http://developer.chrome.com/dev/apps/webview_tag.html
    201 EXIT: 'exit',
    202 LOADABORT: 'loadabort',
    203 LOADCOMMIT: 'loadcommit',
    204 LOADREDIRECT: 'loadredirect',
    205 LOADSTART: 'loadstart',
    206 LOADSTOP: 'loadstop',
    207 RESPONSIVE: 'responsive',
    208 SIZECHANGED: 'sizechanged',
    209 UNRESPONSIVE: 'unresponsive',
    210
    211 // HTML5 Page Visibility API. See details at
    212 // {@code goog.labs.dom.PageVisibilityMonitor}.
    213 VISIBILITYCHANGE: 'visibilitychange',
    214
    215 // LocalStorage event.
    216 STORAGE: 'storage',
    217
    218 // DOM Level 2 mutation events (deprecated).
    219 DOMSUBTREEMODIFIED: 'DOMSubtreeModified',
    220 DOMNODEINSERTED: 'DOMNodeInserted',
    221 DOMNODEREMOVED: 'DOMNodeRemoved',
    222 DOMNODEREMOVEDFROMDOCUMENT: 'DOMNodeRemovedFromDocument',
    223 DOMNODEINSERTEDINTODOCUMENT: 'DOMNodeInsertedIntoDocument',
    224 DOMATTRMODIFIED: 'DOMAttrModified',
    225 DOMCHARACTERDATAMODIFIED: 'DOMCharacterDataModified'
    226};
    \ No newline at end of file +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/history.html#event-definitions
    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};
    \ 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 index 71f59de..6f9e252 100644 --- a/docs/source/lib/goog/events/keycodes.js.src.html +++ b/docs/source/lib/goog/events/keycodes.js.src.html @@ -1 +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
    26
    27/**
    28 * Key codes for common characters.
    29 *
    30 * This list is not localized and therefore some of the key codes are not
    31 * correct for non US keyboard layouts. See comments below.
    32 *
    33 * @enum {number}
    34 */
    35goog.events.KeyCodes = {
    36 WIN_KEY_FF_LINUX: 0,
    37 MAC_ENTER: 3,
    38 BACKSPACE: 8,
    39 TAB: 9,
    40 NUM_CENTER: 12, // NUMLOCK on FF/Safari Mac
    41 ENTER: 13,
    42 SHIFT: 16,
    43 CTRL: 17,
    44 ALT: 18,
    45 PAUSE: 19,
    46 CAPS_LOCK: 20,
    47 ESC: 27,
    48 SPACE: 32,
    49 PAGE_UP: 33, // also NUM_NORTH_EAST
    50 PAGE_DOWN: 34, // also NUM_SOUTH_EAST
    51 END: 35, // also NUM_SOUTH_WEST
    52 HOME: 36, // also NUM_NORTH_WEST
    53 LEFT: 37, // also NUM_WEST
    54 UP: 38, // also NUM_NORTH
    55 RIGHT: 39, // also NUM_EAST
    56 DOWN: 40, // also NUM_SOUTH
    57 PRINT_SCREEN: 44,
    58 INSERT: 45, // also NUM_INSERT
    59 DELETE: 46, // also NUM_DELETE
    60 ZERO: 48,
    61 ONE: 49,
    62 TWO: 50,
    63 THREE: 51,
    64 FOUR: 52,
    65 FIVE: 53,
    66 SIX: 54,
    67 SEVEN: 55,
    68 EIGHT: 56,
    69 NINE: 57,
    70 FF_SEMICOLON: 59, // Firefox (Gecko) fires this for semicolon instead of 186
    71 FF_EQUALS: 61, // Firefox (Gecko) fires this for equals instead of 187
    72 FF_DASH: 173, // Firefox (Gecko) fires this for dash instead of 189
    73 QUESTION_MARK: 63, // needs localization
    74 A: 65,
    75 B: 66,
    76 C: 67,
    77 D: 68,
    78 E: 69,
    79 F: 70,
    80 G: 71,
    81 H: 72,
    82 I: 73,
    83 J: 74,
    84 K: 75,
    85 L: 76,
    86 M: 77,
    87 N: 78,
    88 O: 79,
    89 P: 80,
    90 Q: 81,
    91 R: 82,
    92 S: 83,
    93 T: 84,
    94 U: 85,
    95 V: 86,
    96 W: 87,
    97 X: 88,
    98 Y: 89,
    99 Z: 90,
    100 META: 91, // WIN_KEY_LEFT
    101 WIN_KEY_RIGHT: 92,
    102 CONTEXT_MENU: 93,
    103 NUM_ZERO: 96,
    104 NUM_ONE: 97,
    105 NUM_TWO: 98,
    106 NUM_THREE: 99,
    107 NUM_FOUR: 100,
    108 NUM_FIVE: 101,
    109 NUM_SIX: 102,
    110 NUM_SEVEN: 103,
    111 NUM_EIGHT: 104,
    112 NUM_NINE: 105,
    113 NUM_MULTIPLY: 106,
    114 NUM_PLUS: 107,
    115 NUM_MINUS: 109,
    116 NUM_PERIOD: 110,
    117 NUM_DIVISION: 111,
    118 F1: 112,
    119 F2: 113,
    120 F3: 114,
    121 F4: 115,
    122 F5: 116,
    123 F6: 117,
    124 F7: 118,
    125 F8: 119,
    126 F9: 120,
    127 F10: 121,
    128 F11: 122,
    129 F12: 123,
    130 NUMLOCK: 144,
    131 SCROLL_LOCK: 145,
    132
    133 // OS-specific media keys like volume controls and browser controls.
    134 FIRST_MEDIA_KEY: 166,
    135 LAST_MEDIA_KEY: 183,
    136
    137 SEMICOLON: 186, // needs localization
    138 DASH: 189, // needs localization
    139 EQUALS: 187, // needs localization
    140 COMMA: 188, // needs localization
    141 PERIOD: 190, // needs localization
    142 SLASH: 191, // needs localization
    143 APOSTROPHE: 192, // needs localization
    144 TILDE: 192, // needs localization
    145 SINGLE_QUOTE: 222, // needs localization
    146 OPEN_SQUARE_BRACKET: 219, // needs localization
    147 BACKSLASH: 220, // needs localization
    148 CLOSE_SQUARE_BRACKET: 221, // needs localization
    149 WIN_KEY: 224,
    150 MAC_FF_META: 224, // Firefox (Gecko) fires this for the meta key instead of 91
    151 MAC_WK_CMD_LEFT: 91, // WebKit Left Command key fired, same as META
    152 MAC_WK_CMD_RIGHT: 93, // WebKit Right Command key fired, different from META
    153 WIN_IME: 229,
    154
    155 // We've seen users whose machines fire this keycode at regular one
    156 // second intervals. The common thread among these users is that
    157 // they're all using Dell Inspiron laptops, so we suspect that this
    158 // indicates a hardware/bios problem.
    159 // http://en.community.dell.com/support-forums/laptop/f/3518/p/19285957/19523128.aspx
    160 PHANTOM: 255
    161};
    162
    163
    164/**
    165 * Returns true if the event contains a text modifying key.
    166 * @param {goog.events.BrowserEvent} e A key event.
    167 * @return {boolean} Whether it's a text modifying key.
    168 */
    169goog.events.KeyCodes.isTextModifyingKeyEvent = function(e) {
    170 if (e.altKey && !e.ctrlKey ||
    171 e.metaKey ||
    172 // Function keys don't generate text
    173 e.keyCode >= goog.events.KeyCodes.F1 &&
    174 e.keyCode <= goog.events.KeyCodes.F12) {
    175 return false;
    176 }
    177
    178 // The following keys are quite harmless, even in combination with
    179 // CTRL, ALT or SHIFT.
    180 switch (e.keyCode) {
    181 case goog.events.KeyCodes.ALT:
    182 case goog.events.KeyCodes.CAPS_LOCK:
    183 case goog.events.KeyCodes.CONTEXT_MENU:
    184 case goog.events.KeyCodes.CTRL:
    185 case goog.events.KeyCodes.DOWN:
    186 case goog.events.KeyCodes.END:
    187 case goog.events.KeyCodes.ESC:
    188 case goog.events.KeyCodes.HOME:
    189 case goog.events.KeyCodes.INSERT:
    190 case goog.events.KeyCodes.LEFT:
    191 case goog.events.KeyCodes.MAC_FF_META:
    192 case goog.events.KeyCodes.META:
    193 case goog.events.KeyCodes.NUMLOCK:
    194 case goog.events.KeyCodes.NUM_CENTER:
    195 case goog.events.KeyCodes.PAGE_DOWN:
    196 case goog.events.KeyCodes.PAGE_UP:
    197 case goog.events.KeyCodes.PAUSE:
    198 case goog.events.KeyCodes.PHANTOM:
    199 case goog.events.KeyCodes.PRINT_SCREEN:
    200 case goog.events.KeyCodes.RIGHT:
    201 case goog.events.KeyCodes.SCROLL_LOCK:
    202 case goog.events.KeyCodes.SHIFT:
    203 case goog.events.KeyCodes.UP:
    204 case goog.events.KeyCodes.WIN_KEY:
    205 case goog.events.KeyCodes.WIN_KEY_RIGHT:
    206 return false;
    207 case goog.events.KeyCodes.WIN_KEY_FF_LINUX:
    208 return !goog.userAgent.GECKO;
    209 default:
    210 return e.keyCode < goog.events.KeyCodes.FIRST_MEDIA_KEY ||
    211 e.keyCode > goog.events.KeyCodes.LAST_MEDIA_KEY;
    212 }
    213};
    214
    215
    216/**
    217 * Returns true if the key fires a keypress event in the current browser.
    218 *
    219 * Accoridng to MSDN [1] IE only fires keypress events for the following keys:
    220 * - Letters: A - Z (uppercase and lowercase)
    221 * - Numerals: 0 - 9
    222 * - Symbols: ! @ # $ % ^ & * ( ) _ - + = < [ ] { } , . / ? \ | ' ` " ~
    223 * - System: ESC, SPACEBAR, ENTER
    224 *
    225 * That's not entirely correct though, for instance there's no distinction
    226 * between upper and lower case letters.
    227 *
    228 * [1] http://msdn2.microsoft.com/en-us/library/ms536939(VS.85).aspx)
    229 *
    230 * Safari is similar to IE, but does not fire keypress for ESC.
    231 *
    232 * Additionally, IE6 does not fire keydown or keypress events for letters when
    233 * the control or alt keys are held down and the shift key is not. IE7 does
    234 * fire keydown in these cases, though, but not keypress.
    235 *
    236 * @param {number} keyCode A key code.
    237 * @param {number=} opt_heldKeyCode Key code of a currently-held key.
    238 * @param {boolean=} opt_shiftKey Whether the shift key is held down.
    239 * @param {boolean=} opt_ctrlKey Whether the control key is held down.
    240 * @param {boolean=} opt_altKey Whether the alt key is held down.
    241 * @return {boolean} Whether it's a key that fires a keypress event.
    242 */
    243goog.events.KeyCodes.firesKeyPressEvent = function(keyCode, opt_heldKeyCode,
    244 opt_shiftKey, opt_ctrlKey, opt_altKey) {
    245 if (!goog.userAgent.IE &&
    246 !(goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('525'))) {
    247 return true;
    248 }
    249
    250 if (goog.userAgent.MAC && opt_altKey) {
    251 return goog.events.KeyCodes.isCharacterKey(keyCode);
    252 }
    253
    254 // Alt but not AltGr which is represented as Alt+Ctrl.
    255 if (opt_altKey && !opt_ctrlKey) {
    256 return false;
    257 }
    258
    259 // Saves Ctrl or Alt + key for IE and WebKit 525+, which won't fire keypress.
    260 // Non-IE browsers and WebKit prior to 525 won't get this far so no need to
    261 // check the user agent.
    262 if (goog.isNumber(opt_heldKeyCode)) {
    263 opt_heldKeyCode = goog.events.KeyCodes.normalizeKeyCode(opt_heldKeyCode);
    264 }
    265 if (!opt_shiftKey &&
    266 (opt_heldKeyCode == goog.events.KeyCodes.CTRL ||
    267 opt_heldKeyCode == goog.events.KeyCodes.ALT ||
    268 goog.userAgent.MAC &&
    269 opt_heldKeyCode == goog.events.KeyCodes.META)) {
    270 return false;
    271 }
    272
    273 // Some keys with Ctrl/Shift do not issue keypress in WEBKIT.
    274 if (goog.userAgent.WEBKIT && opt_ctrlKey && opt_shiftKey) {
    275 switch (keyCode) {
    276 case goog.events.KeyCodes.BACKSLASH:
    277 case goog.events.KeyCodes.OPEN_SQUARE_BRACKET:
    278 case goog.events.KeyCodes.CLOSE_SQUARE_BRACKET:
    279 case goog.events.KeyCodes.TILDE:
    280 case goog.events.KeyCodes.SEMICOLON:
    281 case goog.events.KeyCodes.DASH:
    282 case goog.events.KeyCodes.EQUALS:
    283 case goog.events.KeyCodes.COMMA:
    284 case goog.events.KeyCodes.PERIOD:
    285 case goog.events.KeyCodes.SLASH:
    286 case goog.events.KeyCodes.APOSTROPHE:
    287 case goog.events.KeyCodes.SINGLE_QUOTE:
    288 return false;
    289 }
    290 }
    291
    292 // When Ctrl+<somekey> is held in IE, it only fires a keypress once, but it
    293 // continues to fire keydown events as the event repeats.
    294 if (goog.userAgent.IE && opt_ctrlKey && opt_heldKeyCode == keyCode) {
    295 return false;
    296 }
    297
    298 switch (keyCode) {
    299 case goog.events.KeyCodes.ENTER:
    300 return true;
    301 case goog.events.KeyCodes.ESC:
    302 return !goog.userAgent.WEBKIT;
    303 }
    304
    305 return goog.events.KeyCodes.isCharacterKey(keyCode);
    306};
    307
    308
    309/**
    310 * Returns true if the key produces a character.
    311 * This does not cover characters on non-US keyboards (Russian, Hebrew, etc.).
    312 *
    313 * @param {number} keyCode A key code.
    314 * @return {boolean} Whether it's a character key.
    315 */
    316goog.events.KeyCodes.isCharacterKey = function(keyCode) {
    317 if (keyCode >= goog.events.KeyCodes.ZERO &&
    318 keyCode <= goog.events.KeyCodes.NINE) {
    319 return true;
    320 }
    321
    322 if (keyCode >= goog.events.KeyCodes.NUM_ZERO &&
    323 keyCode <= goog.events.KeyCodes.NUM_MULTIPLY) {
    324 return true;
    325 }
    326
    327 if (keyCode >= goog.events.KeyCodes.A &&
    328 keyCode <= goog.events.KeyCodes.Z) {
    329 return true;
    330 }
    331
    332 // Safari sends zero key code for non-latin characters.
    333 if (goog.userAgent.WEBKIT && keyCode == 0) {
    334 return true;
    335 }
    336
    337 switch (keyCode) {
    338 case goog.events.KeyCodes.SPACE:
    339 case goog.events.KeyCodes.QUESTION_MARK:
    340 case goog.events.KeyCodes.NUM_PLUS:
    341 case goog.events.KeyCodes.NUM_MINUS:
    342 case goog.events.KeyCodes.NUM_PERIOD:
    343 case goog.events.KeyCodes.NUM_DIVISION:
    344 case goog.events.KeyCodes.SEMICOLON:
    345 case goog.events.KeyCodes.FF_SEMICOLON:
    346 case goog.events.KeyCodes.DASH:
    347 case goog.events.KeyCodes.EQUALS:
    348 case goog.events.KeyCodes.FF_EQUALS:
    349 case goog.events.KeyCodes.COMMA:
    350 case goog.events.KeyCodes.PERIOD:
    351 case goog.events.KeyCodes.SLASH:
    352 case goog.events.KeyCodes.APOSTROPHE:
    353 case goog.events.KeyCodes.SINGLE_QUOTE:
    354 case goog.events.KeyCodes.OPEN_SQUARE_BRACKET:
    355 case goog.events.KeyCodes.BACKSLASH:
    356 case goog.events.KeyCodes.CLOSE_SQUARE_BRACKET:
    357 return true;
    358 default:
    359 return false;
    360 }
    361};
    362
    363
    364/**
    365 * Normalizes key codes from OS/Browser-specific value to the general one.
    366 * @param {number} keyCode The native key code.
    367 * @return {number} The normalized key code.
    368 */
    369goog.events.KeyCodes.normalizeKeyCode = function(keyCode) {
    370 if (goog.userAgent.GECKO) {
    371 return goog.events.KeyCodes.normalizeGeckoKeyCode(keyCode);
    372 } else if (goog.userAgent.MAC && goog.userAgent.WEBKIT) {
    373 return goog.events.KeyCodes.normalizeMacWebKitKeyCode(keyCode);
    374 } else {
    375 return keyCode;
    376 }
    377};
    378
    379
    380/**
    381 * Normalizes key codes from their Gecko-specific value to the general one.
    382 * @param {number} keyCode The native key code.
    383 * @return {number} The normalized key code.
    384 */
    385goog.events.KeyCodes.normalizeGeckoKeyCode = function(keyCode) {
    386 switch (keyCode) {
    387 case goog.events.KeyCodes.FF_EQUALS:
    388 return goog.events.KeyCodes.EQUALS;
    389 case goog.events.KeyCodes.FF_SEMICOLON:
    390 return goog.events.KeyCodes.SEMICOLON;
    391 case goog.events.KeyCodes.FF_DASH:
    392 return goog.events.KeyCodes.DASH;
    393 case goog.events.KeyCodes.MAC_FF_META:
    394 return goog.events.KeyCodes.META;
    395 case goog.events.KeyCodes.WIN_KEY_FF_LINUX:
    396 return goog.events.KeyCodes.WIN_KEY;
    397 default:
    398 return keyCode;
    399 }
    400};
    401
    402
    403/**
    404 * Normalizes key codes from their Mac WebKit-specific value to the general one.
    405 * @param {number} keyCode The native key code.
    406 * @return {number} The normalized key code.
    407 */
    408goog.events.KeyCodes.normalizeMacWebKitKeyCode = function(keyCode) {
    409 switch (keyCode) {
    410 case goog.events.KeyCodes.MAC_WK_CMD_RIGHT: // 93
    411 return goog.events.KeyCodes.META; // 91
    412 default:
    413 return keyCode;
    414 }
    415};
    \ No newline at end of file +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
    26
    27/**
    28 * Key codes for common characters.
    29 *
    30 * This list is not localized and therefore some of the key codes are not
    31 * correct for non US keyboard layouts. See comments below.
    32 *
    33 * @enum {number}
    34 */
    35goog.events.KeyCodes = {
    36 WIN_KEY_FF_LINUX: 0,
    37 MAC_ENTER: 3,
    38 BACKSPACE: 8,
    39 TAB: 9,
    40 NUM_CENTER: 12, // NUMLOCK on FF/Safari Mac
    41 ENTER: 13,
    42 SHIFT: 16,
    43 CTRL: 17,
    44 ALT: 18,
    45 PAUSE: 19,
    46 CAPS_LOCK: 20,
    47 ESC: 27,
    48 SPACE: 32,
    49 PAGE_UP: 33, // also NUM_NORTH_EAST
    50 PAGE_DOWN: 34, // also NUM_SOUTH_EAST
    51 END: 35, // also NUM_SOUTH_WEST
    52 HOME: 36, // also NUM_NORTH_WEST
    53 LEFT: 37, // also NUM_WEST
    54 UP: 38, // also NUM_NORTH
    55 RIGHT: 39, // also NUM_EAST
    56 DOWN: 40, // also NUM_SOUTH
    57 PRINT_SCREEN: 44,
    58 INSERT: 45, // also NUM_INSERT
    59 DELETE: 46, // also NUM_DELETE
    60 ZERO: 48,
    61 ONE: 49,
    62 TWO: 50,
    63 THREE: 51,
    64 FOUR: 52,
    65 FIVE: 53,
    66 SIX: 54,
    67 SEVEN: 55,
    68 EIGHT: 56,
    69 NINE: 57,
    70 FF_SEMICOLON: 59, // Firefox (Gecko) fires this for semicolon instead of 186
    71 FF_EQUALS: 61, // Firefox (Gecko) fires this for equals instead of 187
    72 FF_DASH: 173, // Firefox (Gecko) fires this for dash instead of 189
    73 QUESTION_MARK: 63, // needs localization
    74 A: 65,
    75 B: 66,
    76 C: 67,
    77 D: 68,
    78 E: 69,
    79 F: 70,
    80 G: 71,
    81 H: 72,
    82 I: 73,
    83 J: 74,
    84 K: 75,
    85 L: 76,
    86 M: 77,
    87 N: 78,
    88 O: 79,
    89 P: 80,
    90 Q: 81,
    91 R: 82,
    92 S: 83,
    93 T: 84,
    94 U: 85,
    95 V: 86,
    96 W: 87,
    97 X: 88,
    98 Y: 89,
    99 Z: 90,
    100 META: 91, // WIN_KEY_LEFT
    101 WIN_KEY_RIGHT: 92,
    102 CONTEXT_MENU: 93,
    103 NUM_ZERO: 96,
    104 NUM_ONE: 97,
    105 NUM_TWO: 98,
    106 NUM_THREE: 99,
    107 NUM_FOUR: 100,
    108 NUM_FIVE: 101,
    109 NUM_SIX: 102,
    110 NUM_SEVEN: 103,
    111 NUM_EIGHT: 104,
    112 NUM_NINE: 105,
    113 NUM_MULTIPLY: 106,
    114 NUM_PLUS: 107,
    115 NUM_MINUS: 109,
    116 NUM_PERIOD: 110,
    117 NUM_DIVISION: 111,
    118 F1: 112,
    119 F2: 113,
    120 F3: 114,
    121 F4: 115,
    122 F5: 116,
    123 F6: 117,
    124 F7: 118,
    125 F8: 119,
    126 F9: 120,
    127 F10: 121,
    128 F11: 122,
    129 F12: 123,
    130 NUMLOCK: 144,
    131 SCROLL_LOCK: 145,
    132
    133 // OS-specific media keys like volume controls and browser controls.
    134 FIRST_MEDIA_KEY: 166,
    135 LAST_MEDIA_KEY: 183,
    136
    137 SEMICOLON: 186, // needs localization
    138 DASH: 189, // needs localization
    139 EQUALS: 187, // needs localization
    140 COMMA: 188, // needs localization
    141 PERIOD: 190, // needs localization
    142 SLASH: 191, // needs localization
    143 APOSTROPHE: 192, // needs localization
    144 TILDE: 192, // needs localization
    145 SINGLE_QUOTE: 222, // needs localization
    146 OPEN_SQUARE_BRACKET: 219, // needs localization
    147 BACKSLASH: 220, // needs localization
    148 CLOSE_SQUARE_BRACKET: 221, // needs localization
    149 WIN_KEY: 224,
    150 MAC_FF_META: 224, // Firefox (Gecko) fires this for the meta key instead of 91
    151 MAC_WK_CMD_LEFT: 91, // WebKit Left Command key fired, same as META
    152 MAC_WK_CMD_RIGHT: 93, // WebKit Right Command key fired, different from META
    153 WIN_IME: 229,
    154
    155 // "Reserved for future use". Some programs (e.g. the SlingPlayer 2.4 ActiveX
    156 // control) fire this as a hacky way to disable screensavers.
    157 VK_NONAME: 252,
    158
    159 // We've seen users whose machines fire this keycode at regular one
    160 // second intervals. The common thread among these users is that
    161 // they're all using Dell Inspiron laptops, so we suspect that this
    162 // indicates a hardware/bios problem.
    163 // http://en.community.dell.com/support-forums/laptop/f/3518/p/19285957/19523128.aspx
    164 PHANTOM: 255
    165};
    166
    167
    168/**
    169 * Returns true if the event contains a text modifying key.
    170 * @param {goog.events.BrowserEvent} e A key event.
    171 * @return {boolean} Whether it's a text modifying key.
    172 */
    173goog.events.KeyCodes.isTextModifyingKeyEvent = function(e) {
    174 if (e.altKey && !e.ctrlKey ||
    175 e.metaKey ||
    176 // Function keys don't generate text
    177 e.keyCode >= goog.events.KeyCodes.F1 &&
    178 e.keyCode <= goog.events.KeyCodes.F12) {
    179 return false;
    180 }
    181
    182 // The following keys are quite harmless, even in combination with
    183 // CTRL, ALT or SHIFT.
    184 switch (e.keyCode) {
    185 case goog.events.KeyCodes.ALT:
    186 case goog.events.KeyCodes.CAPS_LOCK:
    187 case goog.events.KeyCodes.CONTEXT_MENU:
    188 case goog.events.KeyCodes.CTRL:
    189 case goog.events.KeyCodes.DOWN:
    190 case goog.events.KeyCodes.END:
    191 case goog.events.KeyCodes.ESC:
    192 case goog.events.KeyCodes.HOME:
    193 case goog.events.KeyCodes.INSERT:
    194 case goog.events.KeyCodes.LEFT:
    195 case goog.events.KeyCodes.MAC_FF_META:
    196 case goog.events.KeyCodes.META:
    197 case goog.events.KeyCodes.NUMLOCK:
    198 case goog.events.KeyCodes.NUM_CENTER:
    199 case goog.events.KeyCodes.PAGE_DOWN:
    200 case goog.events.KeyCodes.PAGE_UP:
    201 case goog.events.KeyCodes.PAUSE:
    202 case goog.events.KeyCodes.PHANTOM:
    203 case goog.events.KeyCodes.PRINT_SCREEN:
    204 case goog.events.KeyCodes.RIGHT:
    205 case goog.events.KeyCodes.SCROLL_LOCK:
    206 case goog.events.KeyCodes.SHIFT:
    207 case goog.events.KeyCodes.UP:
    208 case goog.events.KeyCodes.VK_NONAME:
    209 case goog.events.KeyCodes.WIN_KEY:
    210 case goog.events.KeyCodes.WIN_KEY_RIGHT:
    211 return false;
    212 case goog.events.KeyCodes.WIN_KEY_FF_LINUX:
    213 return !goog.userAgent.GECKO;
    214 default:
    215 return e.keyCode < goog.events.KeyCodes.FIRST_MEDIA_KEY ||
    216 e.keyCode > goog.events.KeyCodes.LAST_MEDIA_KEY;
    217 }
    218};
    219
    220
    221/**
    222 * Returns true if the key fires a keypress event in the current browser.
    223 *
    224 * Accoridng to MSDN [1] IE only fires keypress events for the following keys:
    225 * - Letters: A - Z (uppercase and lowercase)
    226 * - Numerals: 0 - 9
    227 * - Symbols: ! @ # $ % ^ & * ( ) _ - + = < [ ] { } , . / ? \ | ' ` " ~
    228 * - System: ESC, SPACEBAR, ENTER
    229 *
    230 * That's not entirely correct though, for instance there's no distinction
    231 * between upper and lower case letters.
    232 *
    233 * [1] http://msdn2.microsoft.com/en-us/library/ms536939(VS.85).aspx)
    234 *
    235 * Safari is similar to IE, but does not fire keypress for ESC.
    236 *
    237 * Additionally, IE6 does not fire keydown or keypress events for letters when
    238 * the control or alt keys are held down and the shift key is not. IE7 does
    239 * fire keydown in these cases, though, but not keypress.
    240 *
    241 * @param {number} keyCode A key code.
    242 * @param {number=} opt_heldKeyCode Key code of a currently-held key.
    243 * @param {boolean=} opt_shiftKey Whether the shift key is held down.
    244 * @param {boolean=} opt_ctrlKey Whether the control key is held down.
    245 * @param {boolean=} opt_altKey Whether the alt key is held down.
    246 * @return {boolean} Whether it's a key that fires a keypress event.
    247 */
    248goog.events.KeyCodes.firesKeyPressEvent = function(keyCode, opt_heldKeyCode,
    249 opt_shiftKey, opt_ctrlKey, opt_altKey) {
    250 if (!goog.userAgent.IE &&
    251 !(goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('525'))) {
    252 return true;
    253 }
    254
    255 if (goog.userAgent.MAC && opt_altKey) {
    256 return goog.events.KeyCodes.isCharacterKey(keyCode);
    257 }
    258
    259 // Alt but not AltGr which is represented as Alt+Ctrl.
    260 if (opt_altKey && !opt_ctrlKey) {
    261 return false;
    262 }
    263
    264 // Saves Ctrl or Alt + key for IE and WebKit 525+, which won't fire keypress.
    265 // Non-IE browsers and WebKit prior to 525 won't get this far so no need to
    266 // check the user agent.
    267 if (goog.isNumber(opt_heldKeyCode)) {
    268 opt_heldKeyCode = goog.events.KeyCodes.normalizeKeyCode(opt_heldKeyCode);
    269 }
    270 if (!opt_shiftKey &&
    271 (opt_heldKeyCode == goog.events.KeyCodes.CTRL ||
    272 opt_heldKeyCode == goog.events.KeyCodes.ALT ||
    273 goog.userAgent.MAC &&
    274 opt_heldKeyCode == goog.events.KeyCodes.META)) {
    275 return false;
    276 }
    277
    278 // Some keys with Ctrl/Shift do not issue keypress in WEBKIT.
    279 if (goog.userAgent.WEBKIT && opt_ctrlKey && opt_shiftKey) {
    280 switch (keyCode) {
    281 case goog.events.KeyCodes.BACKSLASH:
    282 case goog.events.KeyCodes.OPEN_SQUARE_BRACKET:
    283 case goog.events.KeyCodes.CLOSE_SQUARE_BRACKET:
    284 case goog.events.KeyCodes.TILDE:
    285 case goog.events.KeyCodes.SEMICOLON:
    286 case goog.events.KeyCodes.DASH:
    287 case goog.events.KeyCodes.EQUALS:
    288 case goog.events.KeyCodes.COMMA:
    289 case goog.events.KeyCodes.PERIOD:
    290 case goog.events.KeyCodes.SLASH:
    291 case goog.events.KeyCodes.APOSTROPHE:
    292 case goog.events.KeyCodes.SINGLE_QUOTE:
    293 return false;
    294 }
    295 }
    296
    297 // When Ctrl+<somekey> is held in IE, it only fires a keypress once, but it
    298 // continues to fire keydown events as the event repeats.
    299 if (goog.userAgent.IE && opt_ctrlKey && opt_heldKeyCode == keyCode) {
    300 return false;
    301 }
    302
    303 switch (keyCode) {
    304 case goog.events.KeyCodes.ENTER:
    305 return true;
    306 case goog.events.KeyCodes.ESC:
    307 return !goog.userAgent.WEBKIT;
    308 }
    309
    310 return goog.events.KeyCodes.isCharacterKey(keyCode);
    311};
    312
    313
    314/**
    315 * Returns true if the key produces a character.
    316 * This does not cover characters on non-US keyboards (Russian, Hebrew, etc.).
    317 *
    318 * @param {number} keyCode A key code.
    319 * @return {boolean} Whether it's a character key.
    320 */
    321goog.events.KeyCodes.isCharacterKey = function(keyCode) {
    322 if (keyCode >= goog.events.KeyCodes.ZERO &&
    323 keyCode <= goog.events.KeyCodes.NINE) {
    324 return true;
    325 }
    326
    327 if (keyCode >= goog.events.KeyCodes.NUM_ZERO &&
    328 keyCode <= goog.events.KeyCodes.NUM_MULTIPLY) {
    329 return true;
    330 }
    331
    332 if (keyCode >= goog.events.KeyCodes.A &&
    333 keyCode <= goog.events.KeyCodes.Z) {
    334 return true;
    335 }
    336
    337 // Safari sends zero key code for non-latin characters.
    338 if (goog.userAgent.WEBKIT && keyCode == 0) {
    339 return true;
    340 }
    341
    342 switch (keyCode) {
    343 case goog.events.KeyCodes.SPACE:
    344 case goog.events.KeyCodes.QUESTION_MARK:
    345 case goog.events.KeyCodes.NUM_PLUS:
    346 case goog.events.KeyCodes.NUM_MINUS:
    347 case goog.events.KeyCodes.NUM_PERIOD:
    348 case goog.events.KeyCodes.NUM_DIVISION:
    349 case goog.events.KeyCodes.SEMICOLON:
    350 case goog.events.KeyCodes.FF_SEMICOLON:
    351 case goog.events.KeyCodes.DASH:
    352 case goog.events.KeyCodes.EQUALS:
    353 case goog.events.KeyCodes.FF_EQUALS:
    354 case goog.events.KeyCodes.COMMA:
    355 case goog.events.KeyCodes.PERIOD:
    356 case goog.events.KeyCodes.SLASH:
    357 case goog.events.KeyCodes.APOSTROPHE:
    358 case goog.events.KeyCodes.SINGLE_QUOTE:
    359 case goog.events.KeyCodes.OPEN_SQUARE_BRACKET:
    360 case goog.events.KeyCodes.BACKSLASH:
    361 case goog.events.KeyCodes.CLOSE_SQUARE_BRACKET:
    362 return true;
    363 default:
    364 return false;
    365 }
    366};
    367
    368
    369/**
    370 * Normalizes key codes from OS/Browser-specific value to the general one.
    371 * @param {number} keyCode The native key code.
    372 * @return {number} The normalized key code.
    373 */
    374goog.events.KeyCodes.normalizeKeyCode = function(keyCode) {
    375 if (goog.userAgent.GECKO) {
    376 return goog.events.KeyCodes.normalizeGeckoKeyCode(keyCode);
    377 } else if (goog.userAgent.MAC && goog.userAgent.WEBKIT) {
    378 return goog.events.KeyCodes.normalizeMacWebKitKeyCode(keyCode);
    379 } else {
    380 return keyCode;
    381 }
    382};
    383
    384
    385/**
    386 * Normalizes key codes from their Gecko-specific value to the general one.
    387 * @param {number} keyCode The native key code.
    388 * @return {number} The normalized key code.
    389 */
    390goog.events.KeyCodes.normalizeGeckoKeyCode = function(keyCode) {
    391 switch (keyCode) {
    392 case goog.events.KeyCodes.FF_EQUALS:
    393 return goog.events.KeyCodes.EQUALS;
    394 case goog.events.KeyCodes.FF_SEMICOLON:
    395 return goog.events.KeyCodes.SEMICOLON;
    396 case goog.events.KeyCodes.FF_DASH:
    397 return goog.events.KeyCodes.DASH;
    398 case goog.events.KeyCodes.MAC_FF_META:
    399 return goog.events.KeyCodes.META;
    400 case goog.events.KeyCodes.WIN_KEY_FF_LINUX:
    401 return goog.events.KeyCodes.WIN_KEY;
    402 default:
    403 return keyCode;
    404 }
    405};
    406
    407
    408/**
    409 * Normalizes key codes from their Mac WebKit-specific value to the general one.
    410 * @param {number} keyCode The native key code.
    411 * @return {number} The normalized key code.
    412 */
    413goog.events.KeyCodes.normalizeMacWebKitKeyCode = function(keyCode) {
    414 switch (keyCode) {
    415 case goog.events.KeyCodes.MAC_WK_CMD_RIGHT: // 93
    416 return goog.events.KeyCodes.META; // 91
    417 default:
    418 return keyCode;
    419 }
    420};
    \ 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 index b4c178d..784c459 100644 --- a/docs/source/lib/goog/events/listenable.js.src.html +++ b/docs/source/lib/goog/events/listenable.js.src.html @@ -1 +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 */
    18
    19goog.provide('goog.events.Listenable');
    20goog.provide('goog.events.ListenableKey');
    21
    22/** @suppress {extraRequire} */
    23goog.require('goog.events.EventId');
    24
    25
    26
    27/**
    28 * A listenable interface. A listenable is an object with the ability
    29 * to dispatch/broadcast events to "event listeners" registered via
    30 * listen/listenOnce.
    31 *
    32 * The interface allows for an event propagation mechanism similar
    33 * to one offered by native browser event targets, such as
    34 * capture/bubble mechanism, stopping propagation, and preventing
    35 * default actions. Capture/bubble mechanism depends on the ancestor
    36 * tree constructed via {@code #getParentEventTarget}; this tree
    37 * must be directed acyclic graph. The meaning of default action(s)
    38 * in preventDefault is specific to a particular use case.
    39 *
    40 * Implementations that do not support capture/bubble or can not have
    41 * a parent listenable can simply not implement any ability to set the
    42 * parent listenable (and have {@code #getParentEventTarget} return
    43 * null).
    44 *
    45 * Implementation of this class can be used with or independently from
    46 * goog.events.
    47 *
    48 * Implementation must call {@code #addImplementation(implClass)}.
    49 *
    50 * @interface
    51 * @see goog.events
    52 * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html
    53 */
    54goog.events.Listenable = function() {};
    55
    56
    57/**
    58 * An expando property to indicate that an object implements
    59 * goog.events.Listenable.
    60 *
    61 * See addImplementation/isImplementedBy.
    62 *
    63 * @type {string}
    64 * @const
    65 */
    66goog.events.Listenable.IMPLEMENTED_BY_PROP =
    67 'closure_listenable_' + ((Math.random() * 1e6) | 0);
    68
    69
    70/**
    71 * Marks a given class (constructor) as an implementation of
    72 * Listenable, do that we can query that fact at runtime. The class
    73 * must have already implemented the interface.
    74 * @param {!Function} cls The class constructor. The corresponding
    75 * class must have already implemented the interface.
    76 */
    77goog.events.Listenable.addImplementation = function(cls) {
    78 cls.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP] = true;
    79};
    80
    81
    82/**
    83 * @param {Object} obj The object to check.
    84 * @return {boolean} Whether a given instance implements Listenable. The
    85 * class/superclass of the instance must call addImplementation.
    86 */
    87goog.events.Listenable.isImplementedBy = function(obj) {
    88 return !!(obj && obj[goog.events.Listenable.IMPLEMENTED_BY_PROP]);
    89};
    90
    91
    92/**
    93 * Adds an event listener. A listener can only be added once to an
    94 * object and if it is added again the key for the listener is
    95 * returned. Note that if the existing listener is a one-off listener
    96 * (registered via listenOnce), it will no longer be a one-off
    97 * listener after a call to listen().
    98 *
    99 * @param {string|!goog.events.EventId.<EVENTOBJ>} type The event type id.
    100 * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
    101 * method.
    102 * @param {boolean=} opt_useCapture Whether to fire in capture phase
    103 * (defaults to false).
    104 * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
    105 * listener.
    106 * @return {goog.events.ListenableKey} Unique key for the listener.
    107 * @template SCOPE,EVENTOBJ
    108 */
    109goog.events.Listenable.prototype.listen;
    110
    111
    112/**
    113 * Adds an event listener that is removed automatically after the
    114 * listener fired once.
    115 *
    116 * If an existing listener already exists, listenOnce will do
    117 * nothing. In particular, if the listener was previously registered
    118 * via listen(), listenOnce() will not turn the listener into a
    119 * one-off listener. Similarly, if there is already an existing
    120 * one-off listener, listenOnce does not modify the listeners (it is
    121 * still a once listener).
    122 *
    123 * @param {string|!goog.events.EventId.<EVENTOBJ>} type The event type id.
    124 * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
    125 * method.
    126 * @param {boolean=} opt_useCapture Whether to fire in capture phase
    127 * (defaults to false).
    128 * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
    129 * listener.
    130 * @return {goog.events.ListenableKey} Unique key for the listener.
    131 * @template SCOPE,EVENTOBJ
    132 */
    133goog.events.Listenable.prototype.listenOnce;
    134
    135
    136/**
    137 * Removes an event listener which was added with listen() or listenOnce().
    138 *
    139 * @param {string|!goog.events.EventId.<EVENTOBJ>} type The event type id.
    140 * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
    141 * method.
    142 * @param {boolean=} opt_useCapture Whether to fire in capture phase
    143 * (defaults to false).
    144 * @param {SCOPE=} opt_listenerScope Object in whose scope to call
    145 * the listener.
    146 * @return {boolean} Whether any listener was removed.
    147 * @template SCOPE,EVENTOBJ
    148 */
    149goog.events.Listenable.prototype.unlisten;
    150
    151
    152/**
    153 * Removes an event listener which was added with listen() by the key
    154 * returned by listen().
    155 *
    156 * @param {goog.events.ListenableKey} key The key returned by
    157 * listen() or listenOnce().
    158 * @return {boolean} Whether any listener was removed.
    159 */
    160goog.events.Listenable.prototype.unlistenByKey;
    161
    162
    163/**
    164 * Dispatches an event (or event like object) and calls all listeners
    165 * listening for events of this type. The type of the event is decided by the
    166 * type property on the event object.
    167 *
    168 * If any of the listeners returns false OR calls preventDefault then this
    169 * function will return false. If one of the capture listeners calls
    170 * stopPropagation, then the bubble listeners won't fire.
    171 *
    172 * @param {goog.events.EventLike} e Event object.
    173 * @return {boolean} If anyone called preventDefault on the event object (or
    174 * if any of the listeners returns false) this will also return false.
    175 */
    176goog.events.Listenable.prototype.dispatchEvent;
    177
    178
    179/**
    180 * Removes all listeners from this listenable. If type is specified,
    181 * it will only remove listeners of the particular type. otherwise all
    182 * registered listeners will be removed.
    183 *
    184 * @param {string=} opt_type Type of event to remove, default is to
    185 * remove all types.
    186 * @return {number} Number of listeners removed.
    187 */
    188goog.events.Listenable.prototype.removeAllListeners;
    189
    190
    191/**
    192 * Returns the parent of this event target to use for capture/bubble
    193 * mechanism.
    194 *
    195 * NOTE(user): The name reflects the original implementation of
    196 * custom event target ({@code goog.events.EventTarget}). We decided
    197 * that changing the name is not worth it.
    198 *
    199 * @return {goog.events.Listenable} The parent EventTarget or null if
    200 * there is no parent.
    201 */
    202goog.events.Listenable.prototype.getParentEventTarget;
    203
    204
    205/**
    206 * Fires all registered listeners in this listenable for the given
    207 * type and capture mode, passing them the given eventObject. This
    208 * does not perform actual capture/bubble. Only implementors of the
    209 * interface should be using this.
    210 *
    211 * @param {string|!goog.events.EventId.<EVENTOBJ>} type The type of the
    212 * listeners to fire.
    213 * @param {boolean} capture The capture mode of the listeners to fire.
    214 * @param {EVENTOBJ} eventObject The event object to fire.
    215 * @return {boolean} Whether all listeners succeeded without
    216 * attempting to prevent default behavior. If any listener returns
    217 * false or called goog.events.Event#preventDefault, this returns
    218 * false.
    219 * @template EVENTOBJ
    220 */
    221goog.events.Listenable.prototype.fireListeners;
    222
    223
    224/**
    225 * Gets all listeners in this listenable for the given type and
    226 * capture mode.
    227 *
    228 * @param {string|!goog.events.EventId} type The type of the listeners to fire.
    229 * @param {boolean} capture The capture mode of the listeners to fire.
    230 * @return {!Array.<goog.events.ListenableKey>} An array of registered
    231 * listeners.
    232 * @template EVENTOBJ
    233 */
    234goog.events.Listenable.prototype.getListeners;
    235
    236
    237/**
    238 * Gets the goog.events.ListenableKey for the event or null if no such
    239 * listener is in use.
    240 *
    241 * @param {string|!goog.events.EventId.<EVENTOBJ>} type The name of the event
    242 * without the 'on' prefix.
    243 * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener The
    244 * listener function to get.
    245 * @param {boolean} capture Whether the listener is a capturing listener.
    246 * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
    247 * listener.
    248 * @return {goog.events.ListenableKey} the found listener or null if not found.
    249 * @template SCOPE,EVENTOBJ
    250 */
    251goog.events.Listenable.prototype.getListener;
    252
    253
    254/**
    255 * Whether there is any active listeners matching the specified
    256 * signature. If either the type or capture parameters are
    257 * unspecified, the function will match on the remaining criteria.
    258 *
    259 * @param {string|!goog.events.EventId.<EVENTOBJ>=} opt_type Event type.
    260 * @param {boolean=} opt_capture Whether to check for capture or bubble
    261 * listeners.
    262 * @return {boolean} Whether there is any active listeners matching
    263 * the requested type and/or capture phase.
    264 * @template EVENTOBJ
    265 */
    266goog.events.Listenable.prototype.hasListener;
    267
    268
    269
    270/**
    271 * An interface that describes a single registered listener.
    272 * @interface
    273 */
    274goog.events.ListenableKey = function() {};
    275
    276
    277/**
    278 * Counter used to create a unique key
    279 * @type {number}
    280 * @private
    281 */
    282goog.events.ListenableKey.counter_ = 0;
    283
    284
    285/**
    286 * Reserves a key to be used for ListenableKey#key field.
    287 * @return {number} A number to be used to fill ListenableKey#key
    288 * field.
    289 */
    290goog.events.ListenableKey.reserveKey = function() {
    291 return ++goog.events.ListenableKey.counter_;
    292};
    293
    294
    295/**
    296 * The source event target.
    297 * @type {!(Object|goog.events.Listenable|goog.events.EventTarget)}
    298 */
    299goog.events.ListenableKey.prototype.src;
    300
    301
    302/**
    303 * The event type the listener is listening to.
    304 * @type {string}
    305 */
    306goog.events.ListenableKey.prototype.type;
    307
    308
    309/**
    310 * The listener function.
    311 * @type {function(?):?|{handleEvent:function(?):?}|null}
    312 */
    313goog.events.ListenableKey.prototype.listener;
    314
    315
    316/**
    317 * Whether the listener works on capture phase.
    318 * @type {boolean}
    319 */
    320goog.events.ListenableKey.prototype.capture;
    321
    322
    323/**
    324 * The 'this' object for the listener function's scope.
    325 * @type {Object}
    326 */
    327goog.events.ListenableKey.prototype.handler;
    328
    329
    330/**
    331 * A globally unique number to identify the key.
    332 * @type {number}
    333 */
    334goog.events.ListenableKey.prototype.key;
    \ No newline at end of file +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}
    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 index 7ed2f62..22d0c1e 100644 --- a/docs/source/lib/goog/events/listener.js.src.html +++ b/docs/source/lib/goog/events/listener.js.src.html @@ -1 +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 +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 index a0d26e7..67b711e 100644 --- a/docs/source/lib/goog/events/listenermap.js.src.html +++ b/docs/source/lib/goog/events/listenermap.js.src.html @@ -1 +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 +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..53fc875 --- /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 * @private
    77 */
    78goog.fs.url.findUrlObject_ = function() {
    79 // This is what the spec says to do
    80 // http://dev.w3.org/2006/webapi/FileAPI/#dfn-createObjectURL
    81 if (goog.isDef(goog.global.URL) &&
    82 goog.isDef(goog.global.URL.createObjectURL)) {
    83 return /** @type {goog.fs.url.UrlObject_} */ (goog.global.URL);
    84 // This is what Chrome does (as of 10.0.648.6 dev)
    85 } else if (goog.isDef(goog.global.webkitURL) &&
    86 goog.isDef(goog.global.webkitURL.createObjectURL)) {
    87 return /** @type {goog.fs.url.UrlObject_} */ (goog.global.webkitURL);
    88 // This is what the spec used to say to do
    89 } else if (goog.isDef(goog.global.createObjectURL)) {
    90 return /** @type {goog.fs.url.UrlObject_} */ (goog.global);
    91 } else {
    92 return null;
    93 }
    94};
    95
    96
    97/**
    98 * Checks whether this browser supports Object Urls. If not, calls to
    99 * createObjectUrl and revokeObjectUrl will result in an error.
    100 *
    101 * @return {boolean} True if this browser supports Object Urls.
    102 */
    103goog.fs.url.browserSupportsObjectUrls = function() {
    104 return goog.fs.url.findUrlObject_() != null;
    105};
    \ 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 index ecd96de..6adbc1f 100644 --- a/docs/source/lib/goog/functions/functions.js.src.html +++ b/docs/source/lib/goog/functions/functions.js.src.html @@ -1 +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 the composition of the functions passed in.
    140 * For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)).
    141 * @param {function(...[?]):T} fn The final function.
    142 * @param {...Function} var_args A list of functions.
    143 * @return {function(...[?]):T} The composition of all inputs.
    144 * @template T
    145 */
    146goog.functions.compose = function(fn, var_args) {
    147 var functions = arguments;
    148 var length = functions.length;
    149 return function() {
    150 var result;
    151 if (length) {
    152 result = functions[length - 1].apply(this, arguments);
    153 }
    154
    155 for (var i = length - 2; i >= 0; i--) {
    156 result = functions[i].call(this, result);
    157 }
    158 return result;
    159 };
    160};
    161
    162
    163/**
    164 * Creates a function that calls the functions passed in in sequence, and
    165 * returns the value of the last function. For example,
    166 * (goog.functions.sequence(f, g))(x) is equivalent to f(x),g(x).
    167 * @param {...Function} var_args A list of functions.
    168 * @return {!Function} A function that calls all inputs in sequence.
    169 */
    170goog.functions.sequence = function(var_args) {
    171 var functions = arguments;
    172 var length = functions.length;
    173 return function() {
    174 var result;
    175 for (var i = 0; i < length; i++) {
    176 result = functions[i].apply(this, arguments);
    177 }
    178 return result;
    179 };
    180};
    181
    182
    183/**
    184 * Creates a function that returns true if each of its components evaluates
    185 * to true. The components are evaluated in order, and the evaluation will be
    186 * short-circuited as soon as a function returns false.
    187 * For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x).
    188 * @param {...Function} var_args A list of functions.
    189 * @return {function(...[?]):boolean} A function that ANDs its component
    190 * functions.
    191 */
    192goog.functions.and = function(var_args) {
    193 var functions = arguments;
    194 var length = functions.length;
    195 return function() {
    196 for (var i = 0; i < length; i++) {
    197 if (!functions[i].apply(this, arguments)) {
    198 return false;
    199 }
    200 }
    201 return true;
    202 };
    203};
    204
    205
    206/**
    207 * Creates a function that returns true if any of its components evaluates
    208 * to true. The components are evaluated in order, and the evaluation will be
    209 * short-circuited as soon as a function returns true.
    210 * For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x).
    211 * @param {...Function} var_args A list of functions.
    212 * @return {function(...[?]):boolean} A function that ORs its component
    213 * functions.
    214 */
    215goog.functions.or = function(var_args) {
    216 var functions = arguments;
    217 var length = functions.length;
    218 return function() {
    219 for (var i = 0; i < length; i++) {
    220 if (functions[i].apply(this, arguments)) {
    221 return true;
    222 }
    223 }
    224 return false;
    225 };
    226};
    227
    228
    229/**
    230 * Creates a function that returns the Boolean opposite of a provided function.
    231 * For example, (goog.functions.not(f))(x) is equivalent to !f(x).
    232 * @param {!Function} f The original function.
    233 * @return {function(...[?]):boolean} A function that delegates to f and returns
    234 * opposite.
    235 */
    236goog.functions.not = function(f) {
    237 return function() {
    238 return !f.apply(this, arguments);
    239 };
    240};
    241
    242
    243/**
    244 * Generic factory function to construct an object given the constructor
    245 * and the arguments. Intended to be bound to create object factories.
    246 *
    247 * Callers should cast the result to the appropriate type for proper type
    248 * checking by the compiler.
    249 * @param {!Function} constructor The constructor for the Object.
    250 * @param {...*} var_args The arguments to be passed to the constructor.
    251 * @return {!Object} A new instance of the class given in {@code constructor}.
    252 */
    253goog.functions.create = function(constructor, var_args) {
    254 /**
    255 * @constructor
    256 * @final
    257 */
    258 var temp = function() {};
    259 temp.prototype = constructor.prototype;
    260
    261 // obj will have constructor's prototype in its chain and
    262 // 'obj instanceof constructor' will be true.
    263 var obj = new temp();
    264
    265 // obj is initialized by constructor.
    266 // arguments is only array-like so lacks shift(), but can be used with
    267 // the Array prototype function.
    268 constructor.apply(obj, Array.prototype.slice.call(arguments, 1));
    269 return obj;
    270};
    271
    272
    273/**
    274 * @define {boolean} Whether the return value cache should be used.
    275 * This should only be used to disable caches when testing.
    276 */
    277goog.define('goog.functions.CACHE_RETURN_VALUE', true);
    278
    279
    280/**
    281 * Gives a wrapper function that caches the return value of a parameterless
    282 * function when first called.
    283 *
    284 * When called for the first time, the given function is called and its
    285 * return value is cached (thus this is only appropriate for idempotent
    286 * functions). Subsequent calls will return the cached return value. This
    287 * allows the evaluation of expensive functions to be delayed until first used.
    288 *
    289 * To cache the return values of functions with parameters, see goog.memoize.
    290 *
    291 * @param {!function():T} fn A function to lazily evaluate.
    292 * @return {!function():T} A wrapped version the function.
    293 * @template T
    294 */
    295goog.functions.cacheReturnValue = function(fn) {
    296 var called = false;
    297 var value;
    298
    299 return function() {
    300 if (!goog.functions.CACHE_RETURN_VALUE) {
    301 return fn();
    302 }
    303
    304 if (!called) {
    305 value = fn();
    306 called = true;
    307 }
    308
    309 return value;
    310 }
    311};
    \ No newline at end of file +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};
    \ 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..f4b4c2a --- /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(user): 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 or goog.string.Const is required.
    355 * - For tags which can load code, more specific goog.html.SafeHtml.create*()
    356 * functions must be used. Tags which can load code and are not supported by
    357 * this function are embed, iframe, link, object, script, style, and template.
    358 *
    359 * @param {string} tagName The name of the tag. Only tag names consisting of
    360 * [a-zA-Z0-9-] are allowed. Tag names documented above are disallowed.
    361 * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
    362 * opt_attributes Mapping from attribute names to their values. Only
    363 * attribute names consisting of [a-zA-Z0-9-] are allowed. Value of null or
    364 * undefined causes the attribute to be omitted.
    365 * @param {!goog.html.SafeHtml.TextOrHtml_|
    366 * !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
    367 * HTML-escape and put inside the tag. This must be empty for void tags
    368 * like <br>. Array elements are concatenated.
    369 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
    370 * @throws {Error} If invalid tag name, attribute name, or attribute value is
    371 * provided.
    372 * @throws {goog.asserts.AssertionError} If content for void tag is provided.
    373 */
    374goog.html.SafeHtml.create = function(tagName, opt_attributes, opt_content) {
    375 if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(tagName)) {
    376 throw Error('Invalid tag name <' + tagName + '>.');
    377 }
    378 if (tagName.toUpperCase() in goog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_) {
    379 throw Error('Tag name <' + tagName + '> is not allowed for SafeHtml.');
    380 }
    381 return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
    382 tagName, opt_attributes, opt_content);
    383};
    384
    385
    386/**
    387 * Creates a SafeHtml representing an iframe tag.
    388 *
    389 * By default the sandbox attribute is set to an empty value, which is the most
    390 * secure option, as it confers the iframe the least privileges. If this
    391 * is too restrictive then granting individual privileges is the preferable
    392 * option. Unsetting the attribute entirely is the least secure option and
    393 * should never be done unless it's stricly necessary.
    394 *
    395 * @param {goog.html.TrustedResourceUrl=} opt_src The value of the src
    396 * attribute. If null or undefined src will not be set.
    397 * @param {goog.html.SafeHtml=} opt_srcdoc The value of the srcdoc attribute.
    398 * If null or undefined srcdoc will not be set.
    399 * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
    400 * opt_attributes Mapping from attribute names to their values. Only
    401 * attribute names consisting of [a-zA-Z0-9-] are allowed. Value of null or
    402 * undefined causes the attribute to be omitted.
    403 * @param {!goog.html.SafeHtml.TextOrHtml_|
    404 * !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
    405 * HTML-escape and put inside the tag. Array elements are concatenated.
    406 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
    407 * @throws {Error} If invalid tag name, attribute name, or attribute value is
    408 * provided. If opt_attributes contains the src or srcdoc attributes.
    409 */
    410goog.html.SafeHtml.createIframe = function(
    411 opt_src, opt_srcdoc, opt_attributes, opt_content) {
    412 var fixedAttributes = {};
    413 fixedAttributes['src'] = opt_src || null;
    414 fixedAttributes['srcdoc'] = opt_srcdoc || null;
    415 var defaultAttributes = {'sandbox': ''};
    416 var attributes = goog.html.SafeHtml.combineAttributes(
    417 fixedAttributes, defaultAttributes, opt_attributes);
    418 return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
    419 'iframe', attributes, opt_content);
    420};
    421
    422
    423/**
    424 * Creates a SafeHtml representing a style tag. The type attribute is set
    425 * to "text/css".
    426 * @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>}
    427 * styleSheet Content to put inside the tag. Array elements are
    428 * concatenated.
    429 * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
    430 * opt_attributes Mapping from attribute names to their values. Only
    431 * attribute names consisting of [a-zA-Z0-9-] are allowed. Value of null or
    432 * undefined causes the attribute to be omitted.
    433 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
    434 * @throws {Error} If invalid attribute name or attribute value is provided. If
    435 * opt_attributes contains the type attribute.
    436 */
    437goog.html.SafeHtml.createStyle = function(styleSheet, opt_attributes) {
    438 var fixedAttributes = {'type': 'text/css'};
    439 var defaultAttributes = {};
    440 var attributes = goog.html.SafeHtml.combineAttributes(
    441 fixedAttributes, defaultAttributes, opt_attributes);
    442
    443 var content = '';
    444 styleSheet = goog.array.concat(styleSheet);
    445 for (var i = 0; i < styleSheet.length; i++) {
    446 content += goog.html.SafeStyleSheet.unwrap(styleSheet[i]);
    447 }
    448 // Convert to SafeHtml so that it's not HTML-escaped.
    449 var htmlContent = goog.html.SafeHtml
    450 .createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    451 content, goog.i18n.bidi.Dir.NEUTRAL);
    452 return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
    453 'style', attributes, htmlContent);
    454};
    455
    456
    457/**
    458 * @param {string} tagName The tag name.
    459 * @param {string} name The attribute name.
    460 * @param {!goog.html.SafeHtml.AttributeValue_} value The attribute value.
    461 * @return {string} A "name=value" string.
    462 * @throws {Error} If attribute value is unsafe for the given tag and attribute.
    463 * @private
    464 */
    465goog.html.SafeHtml.getAttrNameAndValue_ = function(tagName, name, value) {
    466 // If it's goog.string.Const, allow any valid attribute name.
    467 if (value instanceof goog.string.Const) {
    468 value = goog.string.Const.unwrap(value);
    469 } else if (name.toLowerCase() == 'style') {
    470 value = goog.html.SafeHtml.getStyleValue_(value);
    471 } else if (/^on/i.test(name)) {
    472 // TODO(jakubvrana): Disallow more attributes with a special meaning.
    473 throw Error('Attribute "' + name +
    474 '" requires goog.string.Const value, "' + value + '" given.');
    475 // URL attributes handled differently accroding to tag.
    476 } else if (name.toLowerCase() in goog.html.SafeHtml.URL_ATTRIBUTES_) {
    477 if (value instanceof goog.html.TrustedResourceUrl) {
    478 value = goog.html.TrustedResourceUrl.unwrap(value);
    479 } else if (value instanceof goog.html.SafeUrl) {
    480 value = goog.html.SafeUrl.unwrap(value);
    481 } else {
    482 // TODO(user): Allow strings and sanitize them automatically,
    483 // so that it's consistent with accepting a map directly for "style".
    484 throw Error('Attribute "' + name + '" on tag "' + tagName +
    485 '" requires goog.html.SafeUrl or goog.string.Const value, "' +
    486 value + '" given.');
    487 }
    488 }
    489
    490 // Accept SafeUrl, TrustedResourceUrl, etc. for attributes which only require
    491 // HTML-escaping.
    492 if (value.implementsGoogStringTypedString) {
    493 // Ok to call getTypedStringValue() since there's no reliance on the type
    494 // contract for security here.
    495 value = value.getTypedStringValue();
    496 }
    497
    498 goog.asserts.assert(goog.isString(value) || goog.isNumber(value),
    499 'String or number value expected, got ' +
    500 (typeof value) + ' with value: ' + value);
    501 return name + '="' + goog.string.htmlEscape(String(value)) + '"';
    502};
    503
    504
    505/**
    506 * Gets value allowed in "style" attribute.
    507 * @param {goog.html.SafeHtml.AttributeValue_} value It could be SafeStyle or a
    508 * map which will be passed to goog.html.SafeStyle.create.
    509 * @return {string} Unwrapped value.
    510 * @throws {Error} If string value is given.
    511 * @private
    512 */
    513goog.html.SafeHtml.getStyleValue_ = function(value) {
    514 if (!goog.isObject(value)) {
    515 throw Error('The "style" attribute requires goog.html.SafeStyle or map ' +
    516 'of style properties, ' + (typeof value) + ' given: ' + value);
    517 }
    518 if (!(value instanceof goog.html.SafeStyle)) {
    519 // Process the property bag into a style object.
    520 value = goog.html.SafeStyle.create(value);
    521 }
    522 return goog.html.SafeStyle.unwrap(value);
    523};
    524
    525
    526/**
    527 * Creates a SafeHtml content with known directionality consisting of a tag with
    528 * optional attributes and optional content.
    529 * @param {!goog.i18n.bidi.Dir} dir Directionality.
    530 * @param {string} tagName
    531 * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=} opt_attributes
    532 * @param {!goog.html.SafeHtml.TextOrHtml_|
    533 * !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content
    534 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
    535 */
    536goog.html.SafeHtml.createWithDir = function(dir, tagName, opt_attributes,
    537 opt_content) {
    538 var html = goog.html.SafeHtml.create(tagName, opt_attributes, opt_content);
    539 html.dir_ = dir;
    540 return html;
    541};
    542
    543
    544/**
    545 * Creates a new SafeHtml object by concatenating values.
    546 * @param {...(!goog.html.SafeHtml.TextOrHtml_|
    547 * !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Values to concatenate.
    548 * @return {!goog.html.SafeHtml}
    549 */
    550goog.html.SafeHtml.concat = function(var_args) {
    551 var dir = goog.i18n.bidi.Dir.NEUTRAL;
    552 var content = '';
    553
    554 /**
    555 * @param {!goog.html.SafeHtml.TextOrHtml_|
    556 * !Array<!goog.html.SafeHtml.TextOrHtml_>} argument
    557 */
    558 var addArgument = function(argument) {
    559 if (goog.isArray(argument)) {
    560 goog.array.forEach(argument, addArgument);
    561 } else {
    562 var html = goog.html.SafeHtml.htmlEscape(argument);
    563 content += goog.html.SafeHtml.unwrap(html);
    564 var htmlDir = html.getDirection();
    565 if (dir == goog.i18n.bidi.Dir.NEUTRAL) {
    566 dir = htmlDir;
    567 } else if (htmlDir != goog.i18n.bidi.Dir.NEUTRAL && dir != htmlDir) {
    568 dir = null;
    569 }
    570 }
    571 };
    572
    573 goog.array.forEach(arguments, addArgument);
    574 return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    575 content, dir);
    576};
    577
    578
    579/**
    580 * Creates a new SafeHtml object with known directionality by concatenating the
    581 * values.
    582 * @param {!goog.i18n.bidi.Dir} dir Directionality.
    583 * @param {...(!goog.html.SafeHtml.TextOrHtml_|
    584 * !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Elements of array
    585 * arguments would be processed recursively.
    586 * @return {!goog.html.SafeHtml}
    587 */
    588goog.html.SafeHtml.concatWithDir = function(dir, var_args) {
    589 var html = goog.html.SafeHtml.concat(goog.array.slice(arguments, 1));
    590 html.dir_ = dir;
    591 return html;
    592};
    593
    594
    595/**
    596 * Type marker for the SafeHtml type, used to implement additional run-time
    597 * type checking.
    598 * @const
    599 * @private
    600 */
    601goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
    602
    603
    604/**
    605 * Package-internal utility method to create SafeHtml instances.
    606 *
    607 * @param {string} html The string to initialize the SafeHtml object with.
    608 * @param {?goog.i18n.bidi.Dir} dir The directionality of the SafeHtml to be
    609 * constructed, or null if unknown.
    610 * @return {!goog.html.SafeHtml} The initialized SafeHtml object.
    611 * @package
    612 */
    613goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse = function(
    614 html, dir) {
    615 return new goog.html.SafeHtml().initSecurityPrivateDoNotAccessOrElse_(
    616 html, dir);
    617};
    618
    619
    620/**
    621 * Called from createSafeHtmlSecurityPrivateDoNotAccessOrElse(). This
    622 * method exists only so that the compiler can dead code eliminate static
    623 * fields (like EMPTY) when they're not accessed.
    624 * @param {string} html
    625 * @param {?goog.i18n.bidi.Dir} dir
    626 * @return {!goog.html.SafeHtml}
    627 * @private
    628 */
    629goog.html.SafeHtml.prototype.initSecurityPrivateDoNotAccessOrElse_ = function(
    630 html, dir) {
    631 this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = html;
    632 this.dir_ = dir;
    633 return this;
    634};
    635
    636
    637/**
    638 * Like create() but does not restrict which tags can be constructed.
    639 *
    640 * @param {string} tagName Tag name. Set or validated by caller.
    641 * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=} opt_attributes
    642 * @param {(!goog.html.SafeHtml.TextOrHtml_|
    643 * !Array<!goog.html.SafeHtml.TextOrHtml_>)=} opt_content
    644 * @return {!goog.html.SafeHtml}
    645 * @throws {Error} If invalid or unsafe attribute name or value is provided.
    646 * @throws {goog.asserts.AssertionError} If content for void tag is provided.
    647 * @package
    648 */
    649goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse =
    650 function(tagName, opt_attributes, opt_content) {
    651 var dir = null;
    652 var result = '<' + tagName;
    653
    654 if (opt_attributes) {
    655 for (var name in opt_attributes) {
    656 if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(name)) {
    657 throw Error('Invalid attribute name "' + name + '".');
    658 }
    659 var value = opt_attributes[name];
    660 if (!goog.isDefAndNotNull(value)) {
    661 continue;
    662 }
    663 result += ' ' +
    664 goog.html.SafeHtml.getAttrNameAndValue_(tagName, name, value);
    665 }
    666 }
    667
    668 var content = opt_content;
    669 if (!goog.isDefAndNotNull(content)) {
    670 content = [];
    671 } else if (!goog.isArray(content)) {
    672 content = [content];
    673 }
    674
    675 if (goog.dom.tags.isVoidTag(tagName.toLowerCase())) {
    676 goog.asserts.assert(!content.length,
    677 'Void tag <' + tagName + '> does not allow content.');
    678 result += '>';
    679 } else {
    680 var html = goog.html.SafeHtml.concat(content);
    681 result += '>' + goog.html.SafeHtml.unwrap(html) + '</' + tagName + '>';
    682 dir = html.getDirection();
    683 }
    684
    685 var dirAttribute = opt_attributes && opt_attributes['dir'];
    686 if (dirAttribute) {
    687 if (/^(ltr|rtl|auto)$/i.test(dirAttribute)) {
    688 // If the tag has the "dir" attribute specified then its direction is
    689 // neutral because it can be safely used in any context.
    690 dir = goog.i18n.bidi.Dir.NEUTRAL;
    691 } else {
    692 dir = null;
    693 }
    694 }
    695
    696 return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    697 result, dir);
    698};
    699
    700
    701/**
    702 * @param {!Object<string, string>} fixedAttributes
    703 * @param {!Object<string, string>} defaultAttributes
    704 * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
    705 * opt_attributes Optional attributes passed to create*().
    706 * @return {!Object<string, goog.html.SafeHtml.AttributeValue_>}
    707 * @throws {Error} If opt_attributes contains an attribute with the same name
    708 * as an attribute in fixedAttributes.
    709 * @package
    710 */
    711goog.html.SafeHtml.combineAttributes = function(
    712 fixedAttributes, defaultAttributes, opt_attributes) {
    713 var combinedAttributes = {};
    714 var name;
    715
    716 for (name in fixedAttributes) {
    717 goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');
    718 combinedAttributes[name] = fixedAttributes[name];
    719 }
    720 for (name in defaultAttributes) {
    721 goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');
    722 combinedAttributes[name] = defaultAttributes[name];
    723 }
    724
    725 for (name in opt_attributes) {
    726 var nameLower = name.toLowerCase();
    727 if (nameLower in fixedAttributes) {
    728 throw Error('Cannot override "' + nameLower + '" attribute, got "' +
    729 name + '" with value "' + opt_attributes[name] + '"');
    730 }
    731 if (nameLower in defaultAttributes) {
    732 delete combinedAttributes[nameLower];
    733 }
    734 combinedAttributes[name] = opt_attributes[name];
    735 }
    736
    737 return combinedAttributes;
    738};
    739
    740
    741/**
    742 * A SafeHtml instance corresponding to the HTML doctype: "<!DOCTYPE html>".
    743 * @const {!goog.html.SafeHtml}
    744 */
    745goog.html.SafeHtml.DOCTYPE_HTML =
    746 goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    747 '<!DOCTYPE html>', goog.i18n.bidi.Dir.NEUTRAL);
    748
    749
    750/**
    751 * A SafeHtml instance corresponding to the empty string.
    752 * @const {!goog.html.SafeHtml}
    753 */
    754goog.html.SafeHtml.EMPTY =
    755 goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    756 '', 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..bf0aff1 --- /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(user): 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
    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..e97a899 --- /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(user): 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 #getSafeStyleString()}) 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
    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..cd99ed1 --- /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(user): 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
    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..732f40c --- /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(user): 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 * Note that the returned value does not necessarily correspond to the string
    197 * with which the SafeUrl was constructed, since goog.html.SafeUrl.sanitize
    198 * will percent-encode many characters.
    199 *
    200 * @param {!goog.html.SafeUrl} safeUrl The object to extract from.
    201 * @return {string} The SafeUrl object's contained string, unless the run-time
    202 * type check fails. In that case, {@code unwrap} returns an innocuous
    203 * string, or, if assertions are enabled, throws
    204 * {@code goog.asserts.AssertionError}.
    205 */
    206goog.html.SafeUrl.unwrap = function(safeUrl) {
    207 // Perform additional Run-time type-checking to ensure that safeUrl is indeed
    208 // an instance of the expected type. This provides some additional protection
    209 // against security bugs due to application code that disables type checks.
    210 // Specifically, the following checks are performed:
    211 // 1. The object is an instance of the expected type.
    212 // 2. The object is not an instance of a subclass.
    213 // 3. The object carries a type marker for the expected type. "Faking" an
    214 // object requires a reference to the type marker, which has names intended
    215 // to stand out in code reviews.
    216 if (safeUrl instanceof goog.html.SafeUrl &&
    217 safeUrl.constructor === goog.html.SafeUrl &&
    218 safeUrl.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
    219 goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
    220 return safeUrl.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
    221 } else {
    222 goog.asserts.fail('expected object of type SafeUrl, got \'' +
    223 safeUrl + '\'');
    224 return 'type_error:SafeUrl';
    225
    226 }
    227};
    228
    229
    230/**
    231 * Creates a SafeUrl object from a compile-time constant string.
    232 *
    233 * Compile-time constant strings are inherently program-controlled and hence
    234 * trusted.
    235 *
    236 * @param {!goog.string.Const} url A compile-time-constant string from which to
    237 * create a SafeUrl.
    238 * @return {!goog.html.SafeUrl} A SafeUrl object initialized to {@code url}.
    239 */
    240goog.html.SafeUrl.fromConstant = function(url) {
    241 return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
    242 goog.string.Const.unwrap(url));
    243};
    244
    245
    246/**
    247 * A pattern that matches Blob types that can have SafeUrls created from
    248 * URL.createObjectURL(blob). Only matches image types, currently.
    249 * @const
    250 * @private
    251 */
    252goog.html.SAFE_BLOB_TYPE_PATTERN_ =
    253 /^image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)$/i;
    254
    255
    256/**
    257 * Creates a SafeUrl wrapping a blob URL for the given {@code blob}. The
    258 * blob URL is created with {@code URL.createObjectURL}. If the MIME type
    259 * for {@code blob} is not of a known safe image MIME type, then the
    260 * SafeUrl will wrap {@link #INNOCUOUS_STRING}.
    261 * @see http://www.w3.org/TR/FileAPI/#url
    262 * @param {!Blob} blob
    263 * @return {!goog.html.SafeUrl} The blob URL, or an innocuous string wrapped
    264 * as a SafeUrl.
    265 */
    266goog.html.SafeUrl.fromBlob = function(blob) {
    267 var url = goog.html.SAFE_BLOB_TYPE_PATTERN_.test(blob.type) ?
    268 goog.fs.url.createObjectUrl(blob) : goog.html.SafeUrl.INNOCUOUS_STRING;
    269 return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
    270};
    271
    272
    273/**
    274 * A pattern that recognizes a commonly useful subset of URLs that satisfy
    275 * the SafeUrl contract.
    276 *
    277 * This regular expression matches a subset of URLs that will not cause script
    278 * execution if used in URL context within a HTML document. Specifically, this
    279 * regular expression matches if (comment from here on and regex copied from
    280 * Soy's EscapingConventions):
    281 * (1) Either a protocol in a whitelist (http, https, mailto or ftp).
    282 * (2) or no protocol. A protocol must be followed by a colon. The below
    283 * allows that by allowing colons only after one of the characters [/?#].
    284 * A colon after a hash (#) must be in the fragment.
    285 * Otherwise, a colon after a (?) must be in a query.
    286 * Otherwise, a colon after a single solidus (/) must be in a path.
    287 * Otherwise, a colon after a double solidus (//) must be in the authority
    288 * (before port).
    289 *
    290 * The pattern disallows &, used in HTML entity declarations before
    291 * one of the characters in [/?#]. This disallows HTML entities used in the
    292 * protocol name, which should never happen, e.g. "h&#116;tp" for "http".
    293 * It also disallows HTML entities in the first path part of a relative path,
    294 * e.g. "foo&lt;bar/baz". Our existing escaping functions should not produce
    295 * that. More importantly, it disallows masking of a colon,
    296 * e.g. "javascript&#58;...".
    297 *
    298 * @private
    299 * @const {!RegExp}
    300 */
    301goog.html.SAFE_URL_PATTERN_ =
    302 /^(?:(?:https?|mailto|ftp):|[^&:/?#]*(?:[/?#]|$))/i;
    303
    304
    305/**
    306 * Creates a SafeUrl object from {@code url}. If {@code url} is a
    307 * goog.html.SafeUrl then it is simply returned. Otherwise the input string is
    308 * validated to match a pattern of commonly used safe URLs. The string is
    309 * converted to UTF-8 and non-whitelisted characters are percent-encoded. The
    310 * string wrapped by the created SafeUrl will thus contain only ASCII printable
    311 * characters.
    312 *
    313 * {@code url} may be a URL with the http, https, mailto or ftp scheme,
    314 * or a relative URL (i.e., a URL without a scheme; specifically, a
    315 * scheme-relative, absolute-path-relative, or path-relative URL).
    316 *
    317 * {@code url} is converted to UTF-8 and non-whitelisted characters are
    318 * percent-encoded. Whitelisted characters are '%' and, from RFC 3986,
    319 * unreserved characters and reserved characters, with the exception of '\'',
    320 * '(' and ')'. This ensures the the SafeUrl contains only ASCII-printable
    321 * characters and reduces the chance of security bugs were it to be
    322 * interpolated into a specific context without the necessary escaping.
    323 *
    324 * If {@code url} fails validation or does not UTF-16 decode correctly
    325 * (JavaScript strings are UTF-16 encoded), this function returns a SafeUrl
    326 * object containing an innocuous string, goog.html.SafeUrl.INNOCUOUS_STRING.
    327 *
    328 * @see http://url.spec.whatwg.org/#concept-relative-url
    329 * @param {string|!goog.string.TypedString} url The URL to validate.
    330 * @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.
    331 */
    332goog.html.SafeUrl.sanitize = function(url) {
    333 if (url instanceof goog.html.SafeUrl) {
    334 return url;
    335 }
    336 else if (url.implementsGoogStringTypedString) {
    337 url = url.getTypedStringValue();
    338 } else {
    339 url = String(url);
    340 }
    341 if (!goog.html.SAFE_URL_PATTERN_.test(url)) {
    342 url = goog.html.SafeUrl.INNOCUOUS_STRING;
    343 } else {
    344 url = goog.html.SafeUrl.normalize_(url);
    345 }
    346 return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
    347};
    348
    349
    350/**
    351 * Normalizes {@code url} the UTF-8 encoding of url, using a whitelist of
    352 * characters. Whitelisted characters are not percent-encoded.
    353 * @param {string} url The URL to normalize.
    354 * @return {string} The normalized URL.
    355 * @private
    356 */
    357goog.html.SafeUrl.normalize_ = function(url) {
    358 try {
    359 var normalized = encodeURI(url);
    360 } catch (e) { // Happens if url contains invalid surrogate sequences.
    361 return goog.html.SafeUrl.INNOCUOUS_STRING;
    362 }
    363
    364 return normalized.replace(
    365 goog.html.SafeUrl.NORMALIZE_MATCHER_,
    366 function(match) {
    367 return goog.html.SafeUrl.NORMALIZE_REPLACER_MAP_[match];
    368 });
    369};
    370
    371
    372/**
    373 * Matches characters and strings which need to be replaced in the string
    374 * generated by encodeURI. Specifically:
    375 *
    376 * - '\'', '(' and ')' are not encoded. They are part of the reserved
    377 * characters group in RFC 3986 but only appear in the obsolete mark
    378 * production in Appendix D.2 of RFC 3986, so they can be encoded without
    379 * changing semantics.
    380 * - '[' and ']' are encoded by encodeURI, despite being reserved characters
    381 * which can be used to represent IPv6 addresses. So they need to be decoded.
    382 * - '%' is encoded by encodeURI. However, encoding '%' characters that are
    383 * already part of a valid percent-encoded sequence changes the semantics of a
    384 * URL, and hence we need to preserve them. Note that this may allow
    385 * non-encoded '%' characters to remain in the URL (i.e., occurrences of '%'
    386 * that are not part of a valid percent-encoded sequence, for example,
    387 * 'ab%xy').
    388 *
    389 * @const {!RegExp}
    390 * @private
    391 */
    392goog.html.SafeUrl.NORMALIZE_MATCHER_ = /[()']|%5B|%5D|%25/g;
    393
    394
    395/**
    396 * Map of replacements to be done in string generated by encodeURI.
    397 * @const {!Object<string, string>}
    398 * @private
    399 */
    400goog.html.SafeUrl.NORMALIZE_REPLACER_MAP_ = {
    401 '\'': '%27',
    402 '(': '%28',
    403 ')': '%29',
    404 '%5B': '[',
    405 '%5D': ']',
    406 '%25': '%'
    407};
    408
    409
    410/**
    411 * Type marker for the SafeUrl type, used to implement additional run-time
    412 * type checking.
    413 * @const
    414 * @private
    415 */
    416goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
    417
    418
    419/**
    420 * Package-internal utility method to create SafeUrl instances.
    421 *
    422 * @param {string} url The string to initialize the SafeUrl object with.
    423 * @return {!goog.html.SafeUrl} The initialized SafeUrl object.
    424 * @package
    425 */
    426goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse = function(
    427 url) {
    428 var safeUrl = new goog.html.SafeUrl();
    429 safeUrl.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = url;
    430 return safeUrl;
    431};
    \ 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..2ee9254 --- /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(user): 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
    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..e2b9ed7 --- /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 * @visibility {//closure/goog/html:approved_for_unchecked_conversion}
    28 * @visibility {//closure/goog/bin/sizetests:__pkg__}
    29 */
    30
    31
    32goog.provide('goog.html.uncheckedconversions');
    33
    34goog.require('goog.asserts');
    35goog.require('goog.html.SafeHtml');
    36goog.require('goog.html.SafeScript');
    37goog.require('goog.html.SafeStyle');
    38goog.require('goog.html.SafeStyleSheet');
    39goog.require('goog.html.SafeUrl');
    40goog.require('goog.html.TrustedResourceUrl');
    41goog.require('goog.string');
    42goog.require('goog.string.Const');
    43
    44
    45/**
    46 * Performs an "unchecked conversion" to SafeHtml from a plain string that is
    47 * known to satisfy the SafeHtml type contract.
    48 *
    49 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
    50 * that the value of {@code html} satisfies the SafeHtml type contract in all
    51 * possible program states.
    52 *
    53 *
    54 * @param {!goog.string.Const} justification A constant string explaining why
    55 * this use of this method is safe. May include a security review ticket
    56 * number.
    57 * @param {string} html A string that is claimed to adhere to the SafeHtml
    58 * contract.
    59 * @param {?goog.i18n.bidi.Dir=} opt_dir The optional directionality of the
    60 * SafeHtml to be constructed. A null or undefined value signifies an
    61 * unknown directionality.
    62 * @return {!goog.html.SafeHtml} The value of html, wrapped in a SafeHtml
    63 * object.
    64 * @suppress {visibility} For access to SafeHtml.create... Note that this
    65 * use is appropriate since this method is intended to be "package private"
    66 * withing goog.html. DO NOT call SafeHtml.create... from outside this
    67 * package; use appropriate wrappers instead.
    68 */
    69goog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract =
    70 function(justification, html, opt_dir) {
    71 // unwrap() called inside an assert so that justification can be optimized
    72 // away in production code.
    73 goog.asserts.assertString(goog.string.Const.unwrap(justification),
    74 'must provide justification');
    75 goog.asserts.assert(
    76 !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
    77 'must provide non-empty justification');
    78 return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    79 html, opt_dir || null);
    80};
    81
    82
    83/**
    84 * Performs an "unchecked conversion" to SafeScript from a plain string that is
    85 * known to satisfy the SafeScript type contract.
    86 *
    87 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
    88 * that the value of {@code script} satisfies the SafeScript type contract in
    89 * all possible program states.
    90 *
    91 *
    92 * @param {!goog.string.Const} justification A constant string explaining why
    93 * this use of this method is safe. May include a security review ticket
    94 * number.
    95 * @param {string} script The string to wrap as a SafeScript.
    96 * @return {!goog.html.SafeScript} The value of {@code script}, wrapped in a
    97 * SafeScript object.
    98 */
    99goog.html.uncheckedconversions.safeScriptFromStringKnownToSatisfyTypeContract =
    100 function(justification, script) {
    101 // unwrap() called inside an assert so that justification can be optimized
    102 // away in production code.
    103 goog.asserts.assertString(goog.string.Const.unwrap(justification),
    104 'must provide justification');
    105 goog.asserts.assert(
    106 !goog.string.isEmpty(goog.string.Const.unwrap(justification)),
    107 'must provide non-empty justification');
    108 return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
    109 script);
    110};
    111
    112
    113/**
    114 * Performs an "unchecked conversion" to SafeStyle from a plain string that is
    115 * known to satisfy the SafeStyle type contract.
    116 *
    117 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
    118 * that the value of {@code style} satisfies the SafeUrl type contract in all
    119 * possible program states.
    120 *
    121 *
    122 * @param {!goog.string.Const} justification A constant string explaining why
    123 * this use of this method is safe. May include a security review ticket
    124 * number.
    125 * @param {string} style The string to wrap as a SafeStyle.
    126 * @return {!goog.html.SafeStyle} The value of {@code style}, wrapped in a
    127 * SafeStyle object.
    128 */
    129goog.html.uncheckedconversions.safeStyleFromStringKnownToSatisfyTypeContract =
    130 function(justification, style) {
    131 // unwrap() called inside an assert so that justification can be optimized
    132 // away in production code.
    133 goog.asserts.assertString(goog.string.Const.unwrap(justification),
    134 'must provide justification');
    135 goog.asserts.assert(
    136 !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
    137 'must provide non-empty justification');
    138 return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
    139 style);
    140};
    141
    142
    143/**
    144 * Performs an "unchecked conversion" to SafeStyleSheet from a plain string
    145 * that is known to satisfy the SafeStyleSheet type contract.
    146 *
    147 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
    148 * that the value of {@code styleSheet} satisfies the SafeUrl type contract in
    149 * all possible program states.
    150 *
    151 *
    152 * @param {!goog.string.Const} justification A constant string explaining why
    153 * this use of this method is safe. May include a security review ticket
    154 * number.
    155 * @param {string} styleSheet The string to wrap as a SafeStyleSheet.
    156 * @return {!goog.html.SafeStyleSheet} The value of {@code styleSheet}, wrapped
    157 * in a SafeStyleSheet object.
    158 */
    159goog.html.uncheckedconversions.
    160 safeStyleSheetFromStringKnownToSatisfyTypeContract =
    161 function(justification, styleSheet) {
    162 // unwrap() called inside an assert so that justification can be optimized
    163 // away in production code.
    164 goog.asserts.assertString(goog.string.Const.unwrap(justification),
    165 'must provide justification');
    166 goog.asserts.assert(
    167 !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
    168 'must provide non-empty justification');
    169 return goog.html.SafeStyleSheet.
    170 createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet);
    171};
    172
    173
    174/**
    175 * Performs an "unchecked conversion" to SafeUrl from a plain string that is
    176 * known to satisfy the SafeUrl type contract.
    177 *
    178 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
    179 * that the value of {@code url} satisfies the SafeUrl type contract in all
    180 * possible program states.
    181 *
    182 *
    183 * @param {!goog.string.Const} justification A constant string explaining why
    184 * this use of this method is safe. May include a security review ticket
    185 * number.
    186 * @param {string} url The string to wrap as a SafeUrl.
    187 * @return {!goog.html.SafeUrl} The value of {@code url}, wrapped in a SafeUrl
    188 * object.
    189 */
    190goog.html.uncheckedconversions.safeUrlFromStringKnownToSatisfyTypeContract =
    191 function(justification, url) {
    192 // unwrap() called inside an assert so that justification can be optimized
    193 // away in production code.
    194 goog.asserts.assertString(goog.string.Const.unwrap(justification),
    195 'must provide justification');
    196 goog.asserts.assert(
    197 !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
    198 'must provide non-empty justification');
    199 return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
    200};
    201
    202
    203/**
    204 * Performs an "unchecked conversion" to TrustedResourceUrl from a plain string
    205 * that is known to satisfy the TrustedResourceUrl type contract.
    206 *
    207 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
    208 * that the value of {@code url} satisfies the TrustedResourceUrl type contract
    209 * in all possible program states.
    210 *
    211 *
    212 * @param {!goog.string.Const} justification A constant string explaining why
    213 * this use of this method is safe. May include a security review ticket
    214 * number.
    215 * @param {string} url The string to wrap as a TrustedResourceUrl.
    216 * @return {!goog.html.TrustedResourceUrl} The value of {@code url}, wrapped in
    217 * a TrustedResourceUrl object.
    218 */
    219goog.html.uncheckedconversions.
    220 trustedResourceUrlFromStringKnownToSatisfyTypeContract =
    221 function(justification, url) {
    222 // unwrap() called inside an assert so that justification can be optimized
    223 // away in production code.
    224 goog.asserts.assertString(goog.string.Const.unwrap(justification),
    225 'must provide justification');
    226 goog.asserts.assert(
    227 !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
    228 'must provide non-empty justification');
    229 return goog.html.TrustedResourceUrl.
    230 createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);
    231};
    \ 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 3221113..b0446c4 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.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// For script engines that already support iterators.
    39if ('StopIteration' in goog.global) {
    40 /**
    41 * Singleton Error object that is used to terminate iterations.
    42 * @type {Error}
    43 */
    44 goog.iter.StopIteration = goog.global['StopIteration'];
    45} else {
    46 /**
    47 * Singleton Error object that is used to terminate iterations.
    48 * @type {Error}
    49 * @suppress {duplicate}
    50 */
    51 goog.iter.StopIteration = Error('StopIteration');
    52}
    53
    54
    55
    56/**
    57 * Class/interface for iterators. An iterator needs to implement a {@code next}
    58 * method and it needs to throw a {@code goog.iter.StopIteration} when the
    59 * iteration passes beyond the end. Iterators have no {@code hasNext} method.
    60 * It is recommended to always use the helper functions to iterate over the
    61 * iterator or in case you are only targeting JavaScript 1.7 for in loops.
    62 * @constructor
    63 * @template VALUE
    64 */
    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 {VALUE} 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.<VALUE>} 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.Iterator.<VALUE>|goog.iter.Iterable} iterable If the
    96 * object is an iterator it will be returned as is. If the object has an
    97 * {@code __iterator__} method that will be called to get the value
    98 * iterator. If the object is an array-like object we create an iterator
    99 * for that.
    100 * @return {!goog.iter.Iterator.<VALUE>} An iterator that knows how to iterate
    101 * over the values in {@code iterable}.
    102 * @template VALUE
    103 */
    104goog.iter.toIterator = function(iterable) {
    105 if (iterable instanceof goog.iter.Iterator) {
    106 return iterable;
    107 }
    108 if (typeof iterable.__iterator__ == 'function') {
    109 return iterable.__iterator__(false);
    110 }
    111 if (goog.isArrayLike(iterable)) {
    112 var i = 0;
    113 var newIter = new goog.iter.Iterator;
    114 newIter.next = function() {
    115 while (true) {
    116 if (i >= iterable.length) {
    117 throw goog.iter.StopIteration;
    118 }
    119 // Don't include deleted elements.
    120 if (!(i in iterable)) {
    121 i++;
    122 continue;
    123 }
    124 return iterable[i++];
    125 }
    126 };
    127 return newIter;
    128 }
    129
    130
    131 // TODO(arv): Should we fall back on goog.structs.getValues()?
    132 throw Error('Not implemented');
    133};
    134
    135
    136/**
    137 * Calls a function for each element in the iterator with the element of the
    138 * iterator passed as argument.
    139 *
    140 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator
    141 * to iterate over. If the iterable is an object {@code toIterator} will be
    142 * called on it.
    143 * @param {function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>)|
    144 * function(this:THIS,number,undefined,goog.iter.Iterator.<VALUE>)} f
    145 * The function to call for every element. This function takes 3 arguments
    146 * (the element, undefined, and the iterator) and the return value is
    147 * irrelevant. The reason for passing undefined as the second argument is
    148 * so that the same function can be used in {@see goog.array#forEach} as
    149 * well as others.
    150 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    151 * {@code f}.
    152 * @template THIS, VALUE
    153 */
    154goog.iter.forEach = function(iterable, f, opt_obj) {
    155 if (goog.isArrayLike(iterable)) {
    156 /** @preserveTry */
    157 try {
    158 // NOTES: this passes the index number to the second parameter
    159 // of the callback contrary to the documentation above.
    160 goog.array.forEach(/** @type {goog.array.ArrayLike} */(iterable), f,
    161 opt_obj);
    162 } catch (ex) {
    163 if (ex !== goog.iter.StopIteration) {
    164 throw ex;
    165 }
    166 }
    167 } else {
    168 iterable = goog.iter.toIterator(iterable);
    169 /** @preserveTry */
    170 try {
    171 while (true) {
    172 f.call(opt_obj, iterable.next(), undefined, iterable);
    173 }
    174 } catch (ex) {
    175 if (ex !== goog.iter.StopIteration) {
    176 throw ex;
    177 }
    178 }
    179 }
    180};
    181
    182
    183/**
    184 * Calls a function for every element in the iterator, and if the function
    185 * returns true adds the element to a new iterator.
    186 *
    187 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator
    188 * to iterate over.
    189 * @param {
    190 * function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f
    191 * The function to call for every element. This function takes 3 arguments
    192 * (the element, undefined, and the iterator) and should return a boolean.
    193 * If the return value is true the element will be included in the returned
    194 * iterator. If it is false the element is not included.
    195 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    196 * {@code f}.
    197 * @return {!goog.iter.Iterator.<VALUE>} A new iterator in which only elements
    198 * that passed the test are present.
    199 * @template THIS, VALUE
    200 */
    201goog.iter.filter = function(iterable, f, opt_obj) {
    202 var iterator = goog.iter.toIterator(iterable);
    203 var newIter = new goog.iter.Iterator;
    204 newIter.next = function() {
    205 while (true) {
    206 var val = iterator.next();
    207 if (f.call(opt_obj, val, undefined, iterator)) {
    208 return val;
    209 }
    210 }
    211 };
    212 return newIter;
    213};
    214
    215
    216/**
    217 * Calls a function for every element in the iterator, and if the function
    218 * returns false adds the element to a new iterator.
    219 *
    220 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator
    221 * to iterate over.
    222 * @param {
    223 * function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f
    224 * The function to call for every element. This function takes 3 arguments
    225 * (the element, undefined, and the iterator) and should return a boolean.
    226 * If the return value is false the element will be included in the returned
    227 * iterator. If it is true the element is not included.
    228 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    229 * {@code f}.
    230 * @return {!goog.iter.Iterator.<VALUE>} A new iterator in which only elements
    231 * that did not pass the test are present.
    232 * @template THIS, VALUE
    233 */
    234goog.iter.filterFalse = function(iterable, f, opt_obj) {
    235 return goog.iter.filter(iterable, goog.functions.not(f), opt_obj);
    236};
    237
    238
    239/**
    240 * Creates a new iterator that returns the values in a range. This function
    241 * can take 1, 2 or 3 arguments:
    242 * <pre>
    243 * range(5) same as range(0, 5, 1)
    244 * range(2, 5) same as range(2, 5, 1)
    245 * </pre>
    246 *
    247 * @param {number} startOrStop The stop value if only one argument is provided.
    248 * The start value if 2 or more arguments are provided. If only one
    249 * argument is used the start value is 0.
    250 * @param {number=} opt_stop The stop value. If left out then the first
    251 * argument is used as the stop value.
    252 * @param {number=} opt_step The number to increment with between each call to
    253 * next. This can be negative.
    254 * @return {!goog.iter.Iterator.<number>} A new iterator that returns the values
    255 * in the range.
    256 */
    257goog.iter.range = function(startOrStop, opt_stop, opt_step) {
    258 var start = 0;
    259 var stop = startOrStop;
    260 var step = opt_step || 1;
    261 if (arguments.length > 1) {
    262 start = startOrStop;
    263 stop = opt_stop;
    264 }
    265 if (step == 0) {
    266 throw Error('Range step argument must not be zero');
    267 }
    268
    269 var newIter = new goog.iter.Iterator;
    270 newIter.next = function() {
    271 if (step > 0 && start >= stop || step < 0 && start <= stop) {
    272 throw goog.iter.StopIteration;
    273 }
    274 var rv = start;
    275 start += step;
    276 return rv;
    277 };
    278 return newIter;
    279};
    280
    281
    282/**
    283 * Joins the values in a iterator with a delimiter.
    284 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator
    285 * to get the values from.
    286 * @param {string} deliminator The text to put between the values.
    287 * @return {string} The joined value string.
    288 * @template VALUE
    289 */
    290goog.iter.join = function(iterable, deliminator) {
    291 return goog.iter.toArray(iterable).join(deliminator);
    292};
    293
    294
    295/**
    296 * For every element in the iterator call a function and return a new iterator
    297 * with that value.
    298 *
    299 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The
    300 * iterator to iterate over.
    301 * @param {
    302 * function(this:THIS,VALUE,undefined,!goog.iter.Iterator.<VALUE>):RESULT} f
    303 * The function to call for every element. This function takes 3 arguments
    304 * (the element, undefined, and the iterator) and should return a new value.
    305 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    306 * {@code f}.
    307 * @return {!goog.iter.Iterator.<RESULT>} A new iterator that returns the
    308 * results of applying the function to each element in the original
    309 * iterator.
    310 * @template THIS, VALUE, RESULT
    311 */
    312goog.iter.map = function(iterable, f, opt_obj) {
    313 var iterator = goog.iter.toIterator(iterable);
    314 var newIter = new goog.iter.Iterator;
    315 newIter.next = function() {
    316 var val = iterator.next();
    317 return f.call(opt_obj, val, undefined, iterator);
    318 };
    319 return newIter;
    320};
    321
    322
    323/**
    324 * Passes every element of an iterator into a function and accumulates the
    325 * result.
    326 *
    327 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator
    328 * to iterate over.
    329 * @param {function(this:THIS,VALUE,VALUE):VALUE} f The function to call for
    330 * every element. This function takes 2 arguments (the function's previous
    331 * result or the initial value, and the value of the current element).
    332 * function(previousValue, currentElement) : newValue.
    333 * @param {VALUE} val The initial value to pass into the function on the first
    334 * call.
    335 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    336 * f.
    337 * @return {VALUE} Result of evaluating f repeatedly across the values of
    338 * the iterator.
    339 * @template THIS, VALUE
    340 */
    341goog.iter.reduce = function(iterable, f, val, opt_obj) {
    342 var rval = val;
    343 goog.iter.forEach(iterable, function(val) {
    344 rval = f.call(opt_obj, rval, val);
    345 });
    346 return rval;
    347};
    348
    349
    350/**
    351 * Goes through the values in the iterator. Calls f for each of these, and if
    352 * any of them returns true, this returns true (without checking the rest). If
    353 * all return false this will return false.
    354 *
    355 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator
    356 * object.
    357 * @param {
    358 * function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f
    359 * The function to call for every value. This function takes 3 arguments
    360 * (the value, undefined, and the iterator) and should return a boolean.
    361 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    362 * {@code f}.
    363 * @return {boolean} true if any value passes the test.
    364 * @template THIS, VALUE
    365 */
    366goog.iter.some = function(iterable, f, opt_obj) {
    367 iterable = goog.iter.toIterator(iterable);
    368 /** @preserveTry */
    369 try {
    370 while (true) {
    371 if (f.call(opt_obj, iterable.next(), undefined, iterable)) {
    372 return true;
    373 }
    374 }
    375 } catch (ex) {
    376 if (ex !== goog.iter.StopIteration) {
    377 throw ex;
    378 }
    379 }
    380 return false;
    381};
    382
    383
    384/**
    385 * Goes through the values in the iterator. Calls f for each of these and if any
    386 * of them returns false this returns false (without checking the rest). If all
    387 * return true this will return true.
    388 *
    389 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator
    390 * object.
    391 * @param {
    392 * function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f
    393 * The function to call for every value. This function takes 3 arguments
    394 * (the value, undefined, and the iterator) and should return a boolean.
    395 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    396 * {@code f}.
    397 * @return {boolean} true if every value passes the test.
    398 * @template THIS, VALUE
    399 */
    400goog.iter.every = function(iterable, f, opt_obj) {
    401 iterable = goog.iter.toIterator(iterable);
    402 /** @preserveTry */
    403 try {
    404 while (true) {
    405 if (!f.call(opt_obj, iterable.next(), undefined, iterable)) {
    406 return false;
    407 }
    408 }
    409 } catch (ex) {
    410 if (ex !== goog.iter.StopIteration) {
    411 throw ex;
    412 }
    413 }
    414 return true;
    415};
    416
    417
    418/**
    419 * Takes zero or more iterables and returns one iterator that will iterate over
    420 * them in the order chained.
    421 * @param {...!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} var_args Any
    422 * number of iterable objects.
    423 * @return {!goog.iter.Iterator.<VALUE>} Returns a new iterator that will
    424 * iterate over all the given iterables' contents.
    425 * @template VALUE
    426 */
    427goog.iter.chain = function(var_args) {
    428 var iterator = goog.iter.toIterator(arguments);
    429 var iter = new goog.iter.Iterator();
    430 var current = null;
    431
    432 iter.next = function() {
    433 while (true) {
    434 if (current == null) {
    435 var it = iterator.next();
    436 current = goog.iter.toIterator(it);
    437 }
    438 try {
    439 return current.next();
    440 } catch (ex) {
    441 if (ex !== goog.iter.StopIteration) {
    442 throw ex;
    443 }
    444 current = null;
    445 }
    446 }
    447 };
    448
    449 return iter;
    450};
    451
    452
    453/**
    454 * Takes a single iterable containing zero or more iterables and returns one
    455 * iterator that will iterate over each one in the order given.
    456 * @see http://docs.python.org/2/library/itertools.html#itertools.chain.from_iterable
    457 * @param {goog.iter.Iterable} iterable The iterable of iterables to chain.
    458 * @return {!goog.iter.Iterator.<VALUE>} Returns a new iterator that will
    459 * iterate over all the contents of the iterables contained within
    460 * {@code iterable}.
    461 * @template VALUE
    462 */
    463goog.iter.chainFromIterable = function(iterable) {
    464 return goog.iter.chain.apply(undefined, iterable);
    465};
    466
    467
    468/**
    469 * Builds a new iterator that iterates over the original, but skips elements as
    470 * long as a supplied function returns true.
    471 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator
    472 * object.
    473 * @param {
    474 * function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f
    475 * The function to call for every value. This function takes 3 arguments
    476 * (the value, undefined, and the iterator) and should return a boolean.
    477 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    478 * {@code f}.
    479 * @return {!goog.iter.Iterator.<VALUE>} A new iterator that drops elements from
    480 * the original iterator as long as {@code f} is true.
    481 * @template THIS, VALUE
    482 */
    483goog.iter.dropWhile = function(iterable, f, opt_obj) {
    484 var iterator = goog.iter.toIterator(iterable);
    485 var newIter = new goog.iter.Iterator;
    486 var dropping = true;
    487 newIter.next = function() {
    488 while (true) {
    489 var val = iterator.next();
    490 if (dropping && f.call(opt_obj, val, undefined, iterator)) {
    491 continue;
    492 } else {
    493 dropping = false;
    494 }
    495 return val;
    496 }
    497 };
    498 return newIter;
    499};
    500
    501
    502/**
    503 * Builds a new iterator that iterates over the original, but only as long as a
    504 * supplied function returns true.
    505 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator
    506 * object.
    507 * @param {
    508 * function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f
    509 * The function to call for every value. This function takes 3 arguments
    510 * (the value, undefined, and the iterator) and should return a boolean.
    511 * @param {THIS=} opt_obj This is used as the 'this' object in f when called.
    512 * @return {!goog.iter.Iterator.<VALUE>} A new iterator that keeps elements in
    513 * the original iterator as long as the function is true.
    514 * @template THIS, VALUE
    515 */
    516goog.iter.takeWhile = function(iterable, f, opt_obj) {
    517 var iterator = goog.iter.toIterator(iterable);
    518 var newIter = new goog.iter.Iterator;
    519 var taking = true;
    520 newIter.next = function() {
    521 while (true) {
    522 if (taking) {
    523 var val = iterator.next();
    524 if (f.call(opt_obj, val, undefined, iterator)) {
    525 return val;
    526 } else {
    527 taking = false;
    528 }
    529 } else {
    530 throw goog.iter.StopIteration;
    531 }
    532 }
    533 };
    534 return newIter;
    535};
    536
    537
    538/**
    539 * Converts the iterator to an array
    540 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator
    541 * to convert to an array.
    542 * @return {!Array.<VALUE>} An array of the elements the iterator iterates over.
    543 * @template VALUE
    544 */
    545goog.iter.toArray = function(iterable) {
    546 // Fast path for array-like.
    547 if (goog.isArrayLike(iterable)) {
    548 return goog.array.toArray(/** @type {!goog.array.ArrayLike} */(iterable));
    549 }
    550 iterable = goog.iter.toIterator(iterable);
    551 var array = [];
    552 goog.iter.forEach(iterable, function(val) {
    553 array.push(val);
    554 });
    555 return array;
    556};
    557
    558
    559/**
    560 * Iterates over two iterables and returns true if they contain the same
    561 * sequence of elements and have the same length.
    562 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable1 The first
    563 * iterable object.
    564 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable2 The second
    565 * iterable object.
    566 * @return {boolean} true if the iterables contain the same sequence of elements
    567 * and have the same length.
    568 * @template VALUE
    569 */
    570goog.iter.equals = function(iterable1, iterable2) {
    571 var fillValue = {};
    572 var pairs = goog.iter.zipLongest(fillValue, iterable1, iterable2);
    573 return goog.iter.every(pairs, function(pair) {
    574 return pair[0] == pair[1];
    575 });
    576};
    577
    578
    579/**
    580 * Advances the iterator to the next position, returning the given default value
    581 * instead of throwing an exception if the iterator has no more entries.
    582 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterable
    583 * object.
    584 * @param {VALUE} defaultValue The value to return if the iterator is empty.
    585 * @return {VALUE} The next item in the iteration, or defaultValue if the
    586 * iterator was empty.
    587 * @template VALUE
    588 */
    589goog.iter.nextOrValue = function(iterable, defaultValue) {
    590 try {
    591 return goog.iter.toIterator(iterable).next();
    592 } catch (e) {
    593 if (e != goog.iter.StopIteration) {
    594 throw e;
    595 }
    596 return defaultValue;
    597 }
    598};
    599
    600
    601/**
    602 * Cartesian product of zero or more sets. Gives an iterator that gives every
    603 * combination of one element chosen from each set. For example,
    604 * ([1, 2], [3, 4]) gives ([1, 3], [1, 4], [2, 3], [2, 4]).
    605 * @see http://docs.python.org/library/itertools.html#itertools.product
    606 * @param {...!goog.array.ArrayLike.<VALUE>} var_args Zero or more sets, as
    607 * arrays.
    608 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} An iterator that gives each
    609 * n-tuple (as an array).
    610 * @template VALUE
    611 */
    612goog.iter.product = function(var_args) {
    613 var someArrayEmpty = goog.array.some(arguments, function(arr) {
    614 return !arr.length;
    615 });
    616
    617 // An empty set in a cartesian product gives an empty set.
    618 if (someArrayEmpty || !arguments.length) {
    619 return new goog.iter.Iterator();
    620 }
    621
    622 var iter = new goog.iter.Iterator();
    623 var arrays = arguments;
    624
    625 // The first indices are [0, 0, ...]
    626 var indicies = goog.array.repeat(0, arrays.length);
    627
    628 iter.next = function() {
    629
    630 if (indicies) {
    631 var retVal = goog.array.map(indicies, function(valueIndex, arrayIndex) {
    632 return arrays[arrayIndex][valueIndex];
    633 });
    634
    635 // Generate the next-largest indices for the next call.
    636 // Increase the rightmost index. If it goes over, increase the next
    637 // rightmost (like carry-over addition).
    638 for (var i = indicies.length - 1; i >= 0; i--) {
    639 // Assertion prevents compiler warning below.
    640 goog.asserts.assert(indicies);
    641 if (indicies[i] < arrays[i].length - 1) {
    642 indicies[i]++;
    643 break;
    644 }
    645
    646 // We're at the last indices (the last element of every array), so
    647 // the iteration is over on the next call.
    648 if (i == 0) {
    649 indicies = null;
    650 break;
    651 }
    652 // Reset the index in this column and loop back to increment the
    653 // next one.
    654 indicies[i] = 0;
    655 }
    656 return retVal;
    657 }
    658
    659 throw goog.iter.StopIteration;
    660 };
    661
    662 return iter;
    663};
    664
    665
    666/**
    667 * Create an iterator to cycle over the iterable's elements indefinitely.
    668 * For example, ([1, 2, 3]) would return : 1, 2, 3, 1, 2, 3, ...
    669 * @see: http://docs.python.org/library/itertools.html#itertools.cycle.
    670 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The
    671 * iterable object.
    672 * @return {!goog.iter.Iterator.<VALUE>} An iterator that iterates indefinitely
    673 * over the values in {@code iterable}.
    674 * @template VALUE
    675 */
    676goog.iter.cycle = function(iterable) {
    677 var baseIterator = goog.iter.toIterator(iterable);
    678
    679 // We maintain a cache to store the iterable elements as we iterate
    680 // over them. The cache is used to return elements once we have
    681 // iterated over the iterable once.
    682 var cache = [];
    683 var cacheIndex = 0;
    684
    685 var iter = new goog.iter.Iterator();
    686
    687 // This flag is set after the iterable is iterated over once
    688 var useCache = false;
    689
    690 iter.next = function() {
    691 var returnElement = null;
    692
    693 // Pull elements off the original iterator if not using cache
    694 if (!useCache) {
    695 try {
    696 // Return the element from the iterable
    697 returnElement = baseIterator.next();
    698 cache.push(returnElement);
    699 return returnElement;
    700 } catch (e) {
    701 // If an exception other than StopIteration is thrown
    702 // or if there are no elements to iterate over (the iterable was empty)
    703 // throw an exception
    704 if (e != goog.iter.StopIteration || goog.array.isEmpty(cache)) {
    705 throw e;
    706 }
    707 // set useCache to true after we know that a 'StopIteration' exception
    708 // was thrown and the cache is not empty (to handle the 'empty iterable'
    709 // use case)
    710 useCache = true;
    711 }
    712 }
    713
    714 returnElement = cache[cacheIndex];
    715 cacheIndex = (cacheIndex + 1) % cache.length;
    716
    717 return returnElement;
    718 };
    719
    720 return iter;
    721};
    722
    723
    724/**
    725 * Creates an iterator that counts indefinitely from a starting value.
    726 * @see http://docs.python.org/2/library/itertools.html#itertools.count
    727 * @param {number=} opt_start The starting value. Default is 0.
    728 * @param {number=} opt_step The number to increment with between each call to
    729 * next. Negative and floating point numbers are allowed. Default is 1.
    730 * @return {!goog.iter.Iterator.<number>} A new iterator that returns the values
    731 * in the series.
    732 */
    733goog.iter.count = function(opt_start, opt_step) {
    734 var counter = opt_start || 0;
    735 var step = goog.isDef(opt_step) ? opt_step : 1;
    736 var iter = new goog.iter.Iterator();
    737
    738 iter.next = function() {
    739 var returnValue = counter;
    740 counter += step;
    741 return returnValue;
    742 };
    743
    744 return iter;
    745};
    746
    747
    748/**
    749 * Creates an iterator that returns the same object or value repeatedly.
    750 * @param {VALUE} value Any object or value to repeat.
    751 * @return {!goog.iter.Iterator.<VALUE>} A new iterator that returns the
    752 * repeated value.
    753 * @template VALUE
    754 */
    755goog.iter.repeat = function(value) {
    756 var iter = new goog.iter.Iterator();
    757
    758 iter.next = goog.functions.constant(value);
    759
    760 return iter;
    761};
    762
    763
    764/**
    765 * Creates an iterator that returns running totals from the numbers in
    766 * {@code iterable}. For example, the array {@code [1, 2, 3, 4, 5]} yields
    767 * {@code 1 -> 3 -> 6 -> 10 -> 15}.
    768 * @see http://docs.python.org/3.2/library/itertools.html#itertools.accumulate
    769 * @param {!goog.iter.Iterable.<number>} iterable The iterable of numbers to
    770 * accumulate.
    771 * @return {!goog.iter.Iterator.<number>} A new iterator that returns the
    772 * numbers in the series.
    773 */
    774goog.iter.accumulate = function(iterable) {
    775 var iterator = goog.iter.toIterator(iterable);
    776 var total = 0;
    777 var iter = new goog.iter.Iterator();
    778
    779 iter.next = function() {
    780 total += iterator.next();
    781 return total;
    782 };
    783
    784 return iter;
    785};
    786
    787
    788/**
    789 * Creates an iterator that returns arrays containing the ith elements from the
    790 * provided iterables. The returned arrays will be the same size as the number
    791 * of iterables given in {@code var_args}. Once the shortest iterable is
    792 * exhausted, subsequent calls to {@code next()} will throw
    793 * {@code goog.iter.StopIteration}.
    794 * @see http://docs.python.org/2/library/itertools.html#itertools.izip
    795 * @param {...!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} var_args Any
    796 * number of iterable objects.
    797 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator that returns
    798 * arrays of elements from the provided iterables.
    799 * @template VALUE
    800 */
    801goog.iter.zip = function(var_args) {
    802 var args = arguments;
    803 var iter = new goog.iter.Iterator();
    804
    805 if (args.length > 0) {
    806 var iterators = goog.array.map(args, goog.iter.toIterator);
    807 iter.next = function() {
    808 var arr = goog.array.map(iterators, function(it) {
    809 return it.next();
    810 });
    811 return arr;
    812 };
    813 }
    814
    815 return iter;
    816};
    817
    818
    819/**
    820 * Creates an iterator that returns arrays containing the ith elements from the
    821 * provided iterables. The returned arrays will be the same size as the number
    822 * of iterables given in {@code var_args}. Shorter iterables will be extended
    823 * with {@code fillValue}. Once the longest iterable is exhausted, subsequent
    824 * calls to {@code next()} will throw {@code goog.iter.StopIteration}.
    825 * @see http://docs.python.org/2/library/itertools.html#itertools.izip_longest
    826 * @param {VALUE} fillValue The object or value used to fill shorter iterables.
    827 * @param {...!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} var_args Any
    828 * number of iterable objects.
    829 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator that returns
    830 * arrays of elements from the provided iterables.
    831 * @template VALUE
    832 */
    833goog.iter.zipLongest = function(fillValue, var_args) {
    834 var args = goog.array.slice(arguments, 1);
    835 var iter = new goog.iter.Iterator();
    836
    837 if (args.length > 0) {
    838 var iterators = goog.array.map(args, goog.iter.toIterator);
    839
    840 iter.next = function() {
    841 var iteratorsHaveValues = false; // false when all iterators are empty.
    842 var arr = goog.array.map(iterators, function(it) {
    843 var returnValue;
    844 try {
    845 returnValue = it.next();
    846 // Iterator had a value, so we've not exhausted the iterators.
    847 // Set flag accordingly.
    848 iteratorsHaveValues = true;
    849 } catch (ex) {
    850 if (ex !== goog.iter.StopIteration) {
    851 throw ex;
    852 }
    853 returnValue = fillValue;
    854 }
    855 return returnValue;
    856 });
    857
    858 if (!iteratorsHaveValues) {
    859 throw goog.iter.StopIteration;
    860 }
    861 return arr;
    862 };
    863 }
    864
    865 return iter;
    866};
    867
    868
    869/**
    870 * Creates an iterator that filters {@code iterable} based on a series of
    871 * {@code selectors}. On each call to {@code next()}, one item is taken from
    872 * both the {@code iterable} and {@code selectors} iterators. If the item from
    873 * {@code selectors} evaluates to true, the item from {@code iterable} is given.
    874 * Otherwise, it is skipped. Once either {@code iterable} or {@code selectors}
    875 * is exhausted, subsequent calls to {@code next()} will throw
    876 * {@code goog.iter.StopIteration}.
    877 * @see http://docs.python.org/2/library/itertools.html#itertools.compress
    878 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The
    879 * iterable to filter.
    880 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} selectors An
    881 * iterable of items to be evaluated in a boolean context to determine if
    882 * the corresponding element in {@code iterable} should be included in the
    883 * result.
    884 * @return {!goog.iter.Iterator.<VALUE>} A new iterator that returns the
    885 * filtered values.
    886 * @template VALUE
    887 */
    888goog.iter.compress = function(iterable, selectors) {
    889 var selectorIterator = goog.iter.toIterator(selectors);
    890
    891 return goog.iter.filter(iterable, function() {
    892 return !!selectorIterator.next();
    893 });
    894};
    895
    896
    897
    898/**
    899 * Implements the {@code goog.iter.groupBy} iterator.
    900 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The
    901 * iterable to group.
    902 * @param {function(...[VALUE]): KEY=} opt_keyFunc Optional function for
    903 * determining the key value for each group in the {@code iterable}. Default
    904 * is the identity function.
    905 * @constructor
    906 * @extends {goog.iter.Iterator.<!Array>}
    907 * @template KEY, VALUE
    908 * @private
    909 */
    910goog.iter.GroupByIterator_ = function(iterable, opt_keyFunc) {
    911
    912 /**
    913 * The iterable to group, coerced to an iterator.
    914 * @type {!goog.iter.Iterator}
    915 */
    916 this.iterator = goog.iter.toIterator(iterable);
    917
    918 /**
    919 * A function for determining the key value for each element in the iterable.
    920 * If no function is provided, the identity function is used and returns the
    921 * element unchanged.
    922 * @type {function(...[VALUE]): KEY}
    923 */
    924 this.keyFunc = opt_keyFunc || goog.functions.identity;
    925
    926 /**
    927 * The target key for determining the start of a group.
    928 * @type {KEY}
    929 */
    930 this.targetKey;
    931
    932 /**
    933 * The current key visited during iteration.
    934 * @type {KEY}
    935 */
    936 this.currentKey;
    937
    938 /**
    939 * The current value being added to the group.
    940 * @type {VALUE}
    941 */
    942 this.currentValue;
    943};
    944goog.inherits(goog.iter.GroupByIterator_, goog.iter.Iterator);
    945
    946
    947/** @override */
    948goog.iter.GroupByIterator_.prototype.next = function() {
    949 while (this.currentKey == this.targetKey) {
    950 this.currentValue = this.iterator.next(); // Exits on StopIteration
    951 this.currentKey = this.keyFunc(this.currentValue);
    952 }
    953 this.targetKey = this.currentKey;
    954 return [this.currentKey, this.groupItems_(this.targetKey)];
    955};
    956
    957
    958/**
    959 * Performs the grouping of objects using the given key.
    960 * @param {KEY} targetKey The target key object for the group.
    961 * @return {!Array.<VALUE>} An array of grouped objects.
    962 * @private
    963 */
    964goog.iter.GroupByIterator_.prototype.groupItems_ = function(targetKey) {
    965 var arr = [];
    966 while (this.currentKey == targetKey) {
    967 arr.push(this.currentValue);
    968 try {
    969 this.currentValue = this.iterator.next();
    970 } catch (ex) {
    971 if (ex !== goog.iter.StopIteration) {
    972 throw ex;
    973 }
    974 break;
    975 }
    976 this.currentKey = this.keyFunc(this.currentValue);
    977 }
    978 return arr;
    979};
    980
    981
    982/**
    983 * Creates an iterator that returns arrays containing elements from the
    984 * {@code iterable} grouped by a key value. For iterables with repeated
    985 * elements (i.e. sorted according to a particular key function), this function
    986 * has a {@code uniq}-like effect. For example, grouping the array:
    987 * {@code [A, B, B, C, C, A]} produces
    988 * {@code [A, [A]], [B, [B, B]], [C, [C, C]], [A, [A]]}.
    989 * @see http://docs.python.org/2/library/itertools.html#itertools.groupby
    990 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The
    991 * iterable to group.
    992 * @param {function(...[VALUE]): KEY=} opt_keyFunc Optional function for
    993 * determining the key value for each group in the {@code iterable}. Default
    994 * is the identity function.
    995 * @return {!goog.iter.Iterator.<!Array>} A new iterator that returns arrays of
    996 * consecutive key and groups.
    997 * @template KEY, VALUE
    998 */
    999goog.iter.groupBy = function(iterable, opt_keyFunc) {
    1000 return new goog.iter.GroupByIterator_(iterable, opt_keyFunc);
    1001};
    1002
    1003
    1004/**
    1005 * Gives an iterator that gives the result of calling the given function
    1006 * <code>f</code> with the arguments taken from the next element from
    1007 * <code>iterable</code> (the elements are expected to also be iterables).
    1008 *
    1009 * Similar to {@see goog.iter#map} but allows the function to accept multiple
    1010 * arguments from the iterable.
    1011 *
    1012 * @param {!goog.iter.Iterable.<!goog.iter.Iterable>} iterable The iterable of
    1013 * iterables to iterate over.
    1014 * @param {function(this:THIS,...[*]):RESULT} f The function to call for every
    1015 * element. This function takes N+2 arguments, where N represents the
    1016 * number of items from the next element of the iterable. The two
    1017 * additional arguments passed to the function are undefined and the
    1018 * iterator itself. The function should return a new value.
    1019 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    1020 * {@code f}.
    1021 * @return {!goog.iter.Iterator.<RESULT>} A new iterator that returns the
    1022 * results of applying the function to each element in the original
    1023 * iterator.
    1024 * @template THIS, RESULT
    1025 */
    1026goog.iter.starMap = function(iterable, f, opt_obj) {
    1027 var iterator = goog.iter.toIterator(iterable);
    1028 var iter = new goog.iter.Iterator();
    1029
    1030 iter.next = function() {
    1031 var args = goog.iter.toArray(iterator.next());
    1032 return f.apply(opt_obj, goog.array.concat(args, undefined, iterator));
    1033 };
    1034
    1035 return iter;
    1036};
    1037
    1038
    1039/**
    1040 * Returns an array of iterators each of which can iterate over the values in
    1041 * {@code iterable} without advancing the others.
    1042 * @see http://docs.python.org/2/library/itertools.html#itertools.tee
    1043 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The
    1044 * iterable to tee.
    1045 * @param {number=} opt_num The number of iterators to create. Default is 2.
    1046 * @return {!Array.<goog.iter.Iterator.<VALUE>>} An array of iterators.
    1047 * @template VALUE
    1048 */
    1049goog.iter.tee = function(iterable, opt_num) {
    1050 var iterator = goog.iter.toIterator(iterable);
    1051 var num = goog.isNumber(opt_num) ? opt_num : 2;
    1052 var buffers = goog.array.map(goog.array.range(num), function() {
    1053 return [];
    1054 });
    1055
    1056 var addNextIteratorValueToBuffers = function() {
    1057 var val = iterator.next();
    1058 goog.array.forEach(buffers, function(buffer) {
    1059 buffer.push(val);
    1060 });
    1061 };
    1062
    1063 var createIterator = function(buffer) {
    1064 // Each tee'd iterator has an associated buffer (initially empty). When a
    1065 // tee'd iterator's buffer is empty, it calls
    1066 // addNextIteratorValueToBuffers(), adding the next value to all tee'd
    1067 // iterators' buffers, and then returns that value. This allows each
    1068 // iterator to be advanced independently.
    1069 var iter = new goog.iter.Iterator();
    1070
    1071 iter.next = function() {
    1072 if (goog.array.isEmpty(buffer)) {
    1073 addNextIteratorValueToBuffers();
    1074 }
    1075 goog.asserts.assert(!goog.array.isEmpty(buffer));
    1076 return buffer.shift();
    1077 };
    1078
    1079 return iter;
    1080 };
    1081
    1082 return goog.array.map(buffers, createIterator);
    1083};
    1084
    1085
    1086/**
    1087 * Creates an iterator that returns arrays containing a count and an element
    1088 * obtained from the given {@code iterable}.
    1089 * @see http://docs.python.org/2/library/functions.html#enumerate
    1090 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The
    1091 * iterable to enumerate.
    1092 * @param {number=} opt_start Optional starting value. Default is 0.
    1093 * @return {!goog.iter.Iterator.<!Array>} A new iterator containing count/item
    1094 * pairs.
    1095 * @template VALUE
    1096 */
    1097goog.iter.enumerate = function(iterable, opt_start) {
    1098 return goog.iter.zip(goog.iter.count(opt_start), iterable);
    1099};
    1100
    1101
    1102/**
    1103 * Creates an iterator that returns the first {@code limitSize} elements from an
    1104 * iterable. If this number is greater than the number of elements in the
    1105 * iterable, all the elements are returned.
    1106 * @see http://goo.gl/V0sihp Inspired by the limit iterator in Guava.
    1107 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The
    1108 * iterable to limit.
    1109 * @param {number} limitSize The maximum number of elements to return.
    1110 * @return {!goog.iter.Iterator.<VALUE>} A new iterator containing
    1111 * {@code limitSize} elements.
    1112 * @template VALUE
    1113 */
    1114goog.iter.limit = function(iterable, limitSize) {
    1115 goog.asserts.assert(goog.math.isInt(limitSize) && limitSize >= 0);
    1116
    1117 var iterator = goog.iter.toIterator(iterable);
    1118
    1119 var iter = new goog.iter.Iterator();
    1120 var remaining = limitSize;
    1121
    1122 iter.next = function() {
    1123 if (remaining-- > 0) {
    1124 return iterator.next();
    1125 }
    1126 throw goog.iter.StopIteration;
    1127 };
    1128
    1129 return iter;
    1130};
    1131
    1132
    1133/**
    1134 * Creates an iterator that is advanced {@code count} steps ahead. Consumed
    1135 * values are silently discarded. If {@code count} is greater than the number
    1136 * of elements in {@code iterable}, an empty iterator is returned. Subsequent
    1137 * calls to {@code next()} will throw {@code goog.iter.StopIteration}.
    1138 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The
    1139 * iterable to consume.
    1140 * @param {number} count The number of elements to consume from the iterator.
    1141 * @return {!goog.iter.Iterator.<VALUE>} An iterator advanced zero or more steps
    1142 * ahead.
    1143 * @template VALUE
    1144 */
    1145goog.iter.consume = function(iterable, count) {
    1146 goog.asserts.assert(goog.math.isInt(count) && count >= 0);
    1147
    1148 var iterator = goog.iter.toIterator(iterable);
    1149
    1150 while (count-- > 0) {
    1151 goog.iter.nextOrValue(iterator, null);
    1152 }
    1153
    1154 return iterator;
    1155};
    1156
    1157
    1158/**
    1159 * Creates an iterator that returns a range of elements from an iterable.
    1160 * Similar to {@see goog.array#slice} but does not support negative indexes.
    1161 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The
    1162 * iterable to slice.
    1163 * @param {number} start The index of the first element to return.
    1164 * @param {number=} opt_end The index after the last element to return. If
    1165 * defined, must be greater than or equal to {@code start}.
    1166 * @return {!goog.iter.Iterator.<VALUE>} A new iterator containing a slice of
    1167 * the original.
    1168 * @template VALUE
    1169 */
    1170goog.iter.slice = function(iterable, start, opt_end) {
    1171 goog.asserts.assert(goog.math.isInt(start) && start >= 0);
    1172
    1173 var iterator = goog.iter.consume(iterable, start);
    1174
    1175 if (goog.isNumber(opt_end)) {
    1176 goog.asserts.assert(
    1177 goog.math.isInt(/** @type {number} */ (opt_end)) && opt_end >= start);
    1178 iterator = goog.iter.limit(iterator, opt_end - start /* limitSize */);
    1179 }
    1180
    1181 return iterator;
    1182};
    1183
    1184
    1185/**
    1186 * Checks an array for duplicate elements.
    1187 * @param {Array.<VALUE>|goog.array.ArrayLike} arr The array to check for
    1188 * duplicates.
    1189 * @return {boolean} True, if the array contains duplicates, false otherwise.
    1190 * @private
    1191 * @template VALUE
    1192 */
    1193// TODO(user): Consider moving this into goog.array as a public function.
    1194goog.iter.hasDuplicates_ = function(arr) {
    1195 var deduped = [];
    1196 goog.array.removeDuplicates(arr, deduped);
    1197 return arr.length != deduped.length;
    1198};
    1199
    1200
    1201/**
    1202 * Creates an iterator that returns permutations of elements in
    1203 * {@code iterable}.
    1204 *
    1205 * Permutations are obtained by taking the Cartesian product of
    1206 * {@code opt_length} iterables and filtering out those with repeated
    1207 * elements. For example, the permutations of {@code [1,2,3]} are
    1208 * {@code [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]}.
    1209 * @see http://docs.python.org/2/library/itertools.html#itertools.permutations
    1210 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The
    1211 * iterable from which to generate permutations.
    1212 * @param {number=} opt_length Length of each permutation. If omitted, defaults
    1213 * to the length of {@code iterable}.
    1214 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator containing the
    1215 * permutations of {@code iterable}.
    1216 * @template VALUE
    1217 */
    1218goog.iter.permutations = function(iterable, opt_length) {
    1219 var elements = goog.iter.toArray(iterable);
    1220 var length = goog.isNumber(opt_length) ? opt_length : elements.length;
    1221
    1222 var sets = goog.array.repeat(elements, length);
    1223 var product = goog.iter.product.apply(undefined, sets);
    1224
    1225 return goog.iter.filter(product, function(arr) {
    1226 return !goog.iter.hasDuplicates_(arr);
    1227 });
    1228};
    1229
    1230
    1231/**
    1232 * Creates an iterator that returns combinations of elements from
    1233 * {@code iterable}.
    1234 *
    1235 * Combinations are obtained by taking the {@see goog.iter#permutations} of
    1236 * {@code iterable} and filtering those whose elements appear in the order they
    1237 * are encountered in {@code iterable}. For example, the 3-length combinations
    1238 * of {@code [0,1,2,3]} are {@code [[0,1,2], [0,1,3], [0,2,3], [1,2,3]]}.
    1239 * @see http://docs.python.org/2/library/itertools.html#itertools.combinations
    1240 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The
    1241 * iterable from which to generate combinations.
    1242 * @param {number} length The length of each combination.
    1243 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator containing
    1244 * combinations from the {@code iterable}.
    1245 * @template VALUE
    1246 */
    1247goog.iter.combinations = function(iterable, length) {
    1248 var elements = goog.iter.toArray(iterable);
    1249 var indexes = goog.iter.range(elements.length);
    1250 var indexIterator = goog.iter.permutations(indexes, length);
    1251 // sortedIndexIterator will now give arrays of with the given length that
    1252 // indicate what indexes into "elements" should be returned on each iteration.
    1253 var sortedIndexIterator = goog.iter.filter(indexIterator, function(arr) {
    1254 return goog.array.isSorted(arr);
    1255 });
    1256
    1257 var iter = new goog.iter.Iterator();
    1258
    1259 function getIndexFromElements(index) {
    1260 return elements[index];
    1261 }
    1262
    1263 iter.next = function() {
    1264 return goog.array.map(
    1265 /** @type {!Array.<number>} */
    1266 (sortedIndexIterator.next()), getIndexFromElements);
    1267 };
    1268
    1269 return iter;
    1270};
    1271
    1272
    1273/**
    1274 * Creates an iterator that returns combinations of elements from
    1275 * {@code iterable}, with repeated elements possible.
    1276 *
    1277 * Combinations are obtained by taking the Cartesian product of {@code length}
    1278 * iterables and filtering those whose elements appear in the order they are
    1279 * encountered in {@code iterable}. For example, the 2-length combinations of
    1280 * {@code [1,2,3]} are {@code [[1,1], [1,2], [1,3], [2,2], [2,3], [3,3]]}.
    1281 * @see http://docs.python.org/2/library/itertools.html#itertools.combinations_with_replacement
    1282 * @see http://en.wikipedia.org/wiki/Combination#Number_of_combinations_with_repetition
    1283 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The
    1284 * iterable to combine.
    1285 * @param {number} length The length of each combination.
    1286 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator containing
    1287 * combinations from the {@code iterable}.
    1288 * @template VALUE
    1289 */
    1290goog.iter.combinationsWithReplacement = function(iterable, length) {
    1291 var elements = goog.iter.toArray(iterable);
    1292 var indexes = goog.array.range(elements.length);
    1293 var sets = goog.array.repeat(indexes, length);
    1294 var indexIterator = goog.iter.product.apply(undefined, sets);
    1295 // sortedIndexIterator will now give arrays of with the given length that
    1296 // indicate what indexes into "elements" should be returned on each iteration.
    1297 var sortedIndexIterator = goog.iter.filter(indexIterator, function(arr) {
    1298 return goog.array.isSorted(arr);
    1299 });
    1300
    1301 var iter = new goog.iter.Iterator();
    1302
    1303 function getIndexFromElements(index) {
    1304 return elements[index];
    1305 }
    1306
    1307 iter.next = function() {
    1308 return goog.array.map(
    1309 /** @type {!Array.<number>} */
    1310 (sortedIndexIterator.next()), getIndexFromElements);
    1311 };
    1312
    1313 return iter;
    1314};
    \ 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(
    1168 goog.math.isInt(/** @type {number} */ (opt_end)) && opt_end >= start);
    1169 iterator = goog.iter.limit(iterator, opt_end - start /* limitSize */);
    1170 }
    1171
    1172 return iterator;
    1173};
    1174
    1175
    1176/**
    1177 * Checks an array for duplicate elements.
    1178 * @param {Array<VALUE>|goog.array.ArrayLike} arr The array to check for
    1179 * duplicates.
    1180 * @return {boolean} True, if the array contains duplicates, false otherwise.
    1181 * @private
    1182 * @template VALUE
    1183 */
    1184// TODO(user): Consider moving this into goog.array as a public function.
    1185goog.iter.hasDuplicates_ = function(arr) {
    1186 var deduped = [];
    1187 goog.array.removeDuplicates(arr, deduped);
    1188 return arr.length != deduped.length;
    1189};
    1190
    1191
    1192/**
    1193 * Creates an iterator that returns permutations of elements in
    1194 * {@code iterable}.
    1195 *
    1196 * Permutations are obtained by taking the Cartesian product of
    1197 * {@code opt_length} iterables and filtering out those with repeated
    1198 * elements. For example, the permutations of {@code [1,2,3]} are
    1199 * {@code [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]}.
    1200 * @see http://docs.python.org/2/library/itertools.html#itertools.permutations
    1201 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    1202 * iterable from which to generate permutations.
    1203 * @param {number=} opt_length Length of each permutation. If omitted, defaults
    1204 * to the length of {@code iterable}.
    1205 * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing the
    1206 * permutations of {@code iterable}.
    1207 * @template VALUE
    1208 */
    1209goog.iter.permutations = function(iterable, opt_length) {
    1210 var elements = goog.iter.toArray(iterable);
    1211 var length = goog.isNumber(opt_length) ? opt_length : elements.length;
    1212
    1213 var sets = goog.array.repeat(elements, length);
    1214 var product = goog.iter.product.apply(undefined, sets);
    1215
    1216 return goog.iter.filter(product, function(arr) {
    1217 return !goog.iter.hasDuplicates_(arr);
    1218 });
    1219};
    1220
    1221
    1222/**
    1223 * Creates an iterator that returns combinations of elements from
    1224 * {@code iterable}.
    1225 *
    1226 * Combinations are obtained by taking the {@see goog.iter#permutations} of
    1227 * {@code iterable} and filtering those whose elements appear in the order they
    1228 * are encountered in {@code iterable}. For example, the 3-length combinations
    1229 * of {@code [0,1,2,3]} are {@code [[0,1,2], [0,1,3], [0,2,3], [1,2,3]]}.
    1230 * @see http://docs.python.org/2/library/itertools.html#itertools.combinations
    1231 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    1232 * iterable from which to generate combinations.
    1233 * @param {number} length The length of each combination.
    1234 * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing
    1235 * combinations from the {@code iterable}.
    1236 * @template VALUE
    1237 */
    1238goog.iter.combinations = function(iterable, length) {
    1239 var elements = goog.iter.toArray(iterable);
    1240 var indexes = goog.iter.range(elements.length);
    1241 var indexIterator = goog.iter.permutations(indexes, length);
    1242 // sortedIndexIterator will now give arrays of with the given length that
    1243 // indicate what indexes into "elements" should be returned on each iteration.
    1244 var sortedIndexIterator = goog.iter.filter(indexIterator, function(arr) {
    1245 return goog.array.isSorted(arr);
    1246 });
    1247
    1248 var iter = new goog.iter.Iterator();
    1249
    1250 function getIndexFromElements(index) {
    1251 return elements[index];
    1252 }
    1253
    1254 iter.next = function() {
    1255 return goog.array.map(
    1256 /** @type {!Array<number>} */
    1257 (sortedIndexIterator.next()), getIndexFromElements);
    1258 };
    1259
    1260 return iter;
    1261};
    1262
    1263
    1264/**
    1265 * Creates an iterator that returns combinations of elements from
    1266 * {@code iterable}, with repeated elements possible.
    1267 *
    1268 * Combinations are obtained by taking the Cartesian product of {@code length}
    1269 * iterables and filtering those whose elements appear in the order they are
    1270 * encountered in {@code iterable}. For example, the 2-length combinations of
    1271 * {@code [1,2,3]} are {@code [[1,1], [1,2], [1,3], [2,2], [2,3], [3,3]]}.
    1272 * @see http://docs.python.org/2/library/itertools.html#itertools.combinations_with_replacement
    1273 * @see http://en.wikipedia.org/wiki/Combination#Number_of_combinations_with_repetition
    1274 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    1275 * iterable to combine.
    1276 * @param {number} length The length of each combination.
    1277 * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing
    1278 * combinations from the {@code iterable}.
    1279 * @template VALUE
    1280 */
    1281goog.iter.combinationsWithReplacement = function(iterable, length) {
    1282 var elements = goog.iter.toArray(iterable);
    1283 var indexes = goog.array.range(elements.length);
    1284 var sets = goog.array.repeat(indexes, length);
    1285 var indexIterator = goog.iter.product.apply(undefined, sets);
    1286 // sortedIndexIterator will now give arrays of with the given length that
    1287 // indicate what indexes into "elements" should be returned on each iteration.
    1288 var sortedIndexIterator = goog.iter.filter(indexIterator, function(arr) {
    1289 return goog.array.isSorted(arr);
    1290 });
    1291
    1292 var iter = new goog.iter.Iterator();
    1293
    1294 function getIndexFromElements(index) {
    1295 return elements[index];
    1296 }
    1297
    1298 iter.next = function() {
    1299 return goog.array.map(
    1300 /** @type {!Array<number>} */
    1301 (sortedIndexIterator.next()), getIndexFromElements);
    1302 };
    1303
    1304 return iter;
    1305};
    \ 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 2de98ab..86b8db8 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.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(user): EXPERIMENTAL, handle with care. Setting this to true might
    30 * break your code. The default {@code goog.json.parse} implementation is able
    31 * to handle invalid JSON, such as JSPB.
    32 */
    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} 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 switch (typeof object) {
    210 case 'string':
    211 this.serializeString_(/** @type {string} */ (object), sb);
    212 break;
    213 case 'number':
    214 this.serializeNumber_(/** @type {number} */ (object), sb);
    215 break;
    216 case 'boolean':
    217 sb.push(object);
    218 break;
    219 case 'undefined':
    220 sb.push('null');
    221 break;
    222 case 'object':
    223 if (object == null) {
    224 sb.push('null');
    225 break;
    226 }
    227 if (goog.isArray(object)) {
    228 this.serializeArray(/** @type {!Array} */ (object), sb);
    229 break;
    230 }
    231 // should we allow new String, new Number and new Boolean to be treated
    232 // as string, number and boolean? Most implementations do not and the
    233 // need is not very big
    234 this.serializeObject_(/** @type {Object} */ (object), sb);
    235 break;
    236 case 'function':
    237 // Skip functions.
    238 // TODO(user) Should we return something here?
    239 break;
    240 default:
    241 throw Error('Unknown type: ' + typeof object);
    242 }
    243};
    244
    245
    246/**
    247 * Character mappings used internally for goog.string.quote
    248 * @private
    249 * @type {!Object}
    250 */
    251goog.json.Serializer.charToJsonCharCache_ = {
    252 '\"': '\\"',
    253 '\\': '\\\\',
    254 '/': '\\/',
    255 '\b': '\\b',
    256 '\f': '\\f',
    257 '\n': '\\n',
    258 '\r': '\\r',
    259 '\t': '\\t',
    260
    261 '\x0B': '\\u000b' // '\v' is not supported in JScript
    262};
    263
    264
    265/**
    266 * Regular expression used to match characters that need to be replaced.
    267 * The S60 browser has a bug where unicode characters are not matched by
    268 * regular expressions. The condition below detects such behaviour and
    269 * adjusts the regular expression accordingly.
    270 * @private
    271 * @type {!RegExp}
    272 */
    273goog.json.Serializer.charsToReplace_ = /\uffff/.test('\uffff') ?
    274 /[\\\"\x00-\x1f\x7f-\uffff]/g : /[\\\"\x00-\x1f\x7f-\xff]/g;
    275
    276
    277/**
    278 * Serializes a string to a JSON string
    279 * @private
    280 * @param {string} s The string to serialize.
    281 * @param {Array} sb Array used as a string builder.
    282 */
    283goog.json.Serializer.prototype.serializeString_ = function(s, sb) {
    284 // The official JSON implementation does not work with international
    285 // characters.
    286 sb.push('"', s.replace(goog.json.Serializer.charsToReplace_, function(c) {
    287 // caching the result improves performance by a factor 2-3
    288 if (c in goog.json.Serializer.charToJsonCharCache_) {
    289 return goog.json.Serializer.charToJsonCharCache_[c];
    290 }
    291
    292 var cc = c.charCodeAt(0);
    293 var rv = '\\u';
    294 if (cc < 16) {
    295 rv += '000';
    296 } else if (cc < 256) {
    297 rv += '00';
    298 } else if (cc < 4096) { // \u1000
    299 rv += '0';
    300 }
    301 return goog.json.Serializer.charToJsonCharCache_[c] = rv + cc.toString(16);
    302 }), '"');
    303};
    304
    305
    306/**
    307 * Serializes a number to a JSON string
    308 * @private
    309 * @param {number} n The number to serialize.
    310 * @param {Array} sb Array used as a string builder.
    311 */
    312goog.json.Serializer.prototype.serializeNumber_ = function(n, sb) {
    313 sb.push(isFinite(n) && !isNaN(n) ? n : 'null');
    314};
    315
    316
    317/**
    318 * Serializes an array to a JSON string
    319 * @param {Array} arr The array to serialize.
    320 * @param {Array} sb Array used as a string builder.
    321 * @protected
    322 */
    323goog.json.Serializer.prototype.serializeArray = function(arr, sb) {
    324 var l = arr.length;
    325 sb.push('[');
    326 var sep = '';
    327 for (var i = 0; i < l; i++) {
    328 sb.push(sep);
    329
    330 var value = arr[i];
    331 this.serializeInternal(
    332 this.replacer_ ? this.replacer_.call(arr, String(i), value) : value,
    333 sb);
    334
    335 sep = ',';
    336 }
    337 sb.push(']');
    338};
    339
    340
    341/**
    342 * Serializes an object to a JSON string
    343 * @private
    344 * @param {Object} obj The object to serialize.
    345 * @param {Array} sb Array used as a string builder.
    346 */
    347goog.json.Serializer.prototype.serializeObject_ = function(obj, sb) {
    348 sb.push('{');
    349 var sep = '';
    350 for (var key in obj) {
    351 if (Object.prototype.hasOwnProperty.call(obj, key)) {
    352 var value = obj[key];
    353 // Skip functions.
    354 // TODO(ptucker) Should we return something for function properties?
    355 if (typeof value != 'function') {
    356 sb.push(sep);
    357 this.serializeString_(key, sb);
    358 sb.push(':');
    359
    360 this.serializeInternal(
    361 this.replacer_ ? this.replacer_.call(obj, key, value) : value,
    362 sb);
    363
    364 sep = ',';
    365 }
    366 }
    367 }
    368 sb.push('}');
    369};
    \ 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(/** @type {!Array<?>} */ (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_(/** @type {string} */ (object), sb);
    233 break;
    234 case 'number':
    235 this.serializeNumber_(/** @type {number} */ (object), sb);
    236 break;
    237 case 'boolean':
    238 sb.push(object);
    239 break;
    240 case 'function':
    241 // Skip functions.
    242 // TODO(user) Should we return something here?
    243 break;
    244 default:
    245 throw Error('Unknown type: ' + typeof object);
    246 }
    247};
    248
    249
    250/**
    251 * Character mappings used internally for goog.string.quote
    252 * @private
    253 * @type {!Object}
    254 */
    255goog.json.Serializer.charToJsonCharCache_ = {
    256 '\"': '\\"',
    257 '\\': '\\\\',
    258 '/': '\\/',
    259 '\b': '\\b',
    260 '\f': '\\f',
    261 '\n': '\\n',
    262 '\r': '\\r',
    263 '\t': '\\t',
    264
    265 '\x0B': '\\u000b' // '\v' is not supported in JScript
    266};
    267
    268
    269/**
    270 * Regular expression used to match characters that need to be replaced.
    271 * The S60 browser has a bug where unicode characters are not matched by
    272 * regular expressions. The condition below detects such behaviour and
    273 * adjusts the regular expression accordingly.
    274 * @private
    275 * @type {!RegExp}
    276 */
    277goog.json.Serializer.charsToReplace_ = /\uffff/.test('\uffff') ?
    278 /[\\\"\x00-\x1f\x7f-\uffff]/g : /[\\\"\x00-\x1f\x7f-\xff]/g;
    279
    280
    281/**
    282 * Serializes a string to a JSON string
    283 * @private
    284 * @param {string} s The string to serialize.
    285 * @param {Array<string>} sb Array used as a string builder.
    286 */
    287goog.json.Serializer.prototype.serializeString_ = function(s, sb) {
    288 // The official JSON implementation does not work with international
    289 // characters.
    290 sb.push('"', s.replace(goog.json.Serializer.charsToReplace_, function(c) {
    291 // caching the result improves performance by a factor 2-3
    292 var rv = goog.json.Serializer.charToJsonCharCache_[c];
    293 if (!rv) {
    294 rv = '\\u' + (c.charCodeAt(0) | 0x10000).toString(16).substr(1);
    295 goog.json.Serializer.charToJsonCharCache_[c] = rv;
    296 }
    297 return rv;
    298 }), '"');
    299};
    300
    301
    302/**
    303 * Serializes a number to a JSON string
    304 * @private
    305 * @param {number} n The number to serialize.
    306 * @param {Array<string>} sb Array used as a string builder.
    307 */
    308goog.json.Serializer.prototype.serializeNumber_ = function(n, sb) {
    309 sb.push(isFinite(n) && !isNaN(n) ? n : 'null');
    310};
    311
    312
    313/**
    314 * Serializes an array to a JSON string
    315 * @param {Array<string>} arr The array to serialize.
    316 * @param {Array<string>} sb Array used as a string builder.
    317 * @protected
    318 */
    319goog.json.Serializer.prototype.serializeArray = function(arr, sb) {
    320 var l = arr.length;
    321 sb.push('[');
    322 var sep = '';
    323 for (var i = 0; i < l; i++) {
    324 sb.push(sep);
    325
    326 var value = arr[i];
    327 this.serializeInternal(
    328 this.replacer_ ? this.replacer_.call(arr, String(i), value) : value,
    329 sb);
    330
    331 sep = ',';
    332 }
    333 sb.push(']');
    334};
    335
    336
    337/**
    338 * Serializes an object to a JSON string
    339 * @private
    340 * @param {Object} obj The object to serialize.
    341 * @param {Array<string>} sb Array used as a string builder.
    342 */
    343goog.json.Serializer.prototype.serializeObject_ = function(obj, sb) {
    344 sb.push('{');
    345 var sep = '';
    346 for (var key in obj) {
    347 if (Object.prototype.hasOwnProperty.call(obj, key)) {
    348 var value = obj[key];
    349 // Skip functions.
    350 // TODO(ptucker) Should we return something for function properties?
    351 if (typeof value != 'function') {
    352 sb.push(sep);
    353 this.serializeString_(key, sb);
    354 sb.push(':');
    355
    356 this.serializeInternal(
    357 this.replacer_ ? this.replacer_.call(obj, key, value) : value,
    358 sb);
    359
    360 sep = ',';
    361 }
    362 }
    363 }
    364 sb.push('}');
    365};
    \ 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 b0c94bd..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 * @final
    56 */
    57goog.labs.testing.MatcherError = function(opt_message) {
    58 goog.labs.testing.MatcherError.base(this, 'constructor', opt_message);
    59};
    60goog.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 732ea86..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 * @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 +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 5a4455b..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) {};
    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 +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 a369788..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 * @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 +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 4424c71..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 * @struct
    42 * @implements {goog.labs.testing.Matcher}
    43 * @final
    44 */
    45goog.labs.testing.ObjectEqualsMatcher = function(expectedObject) {
    46 /**
    47 * @type {!Object}
    48 * @private
    49 */
    50 this.object_ = expectedObject;
    51};
    52
    53
    54/**
    55 * Determines if two objects are the same.
    56 *
    57 * @override
    58 */
    59goog.labs.testing.ObjectEqualsMatcher.prototype.matches =
    60 function(actualObject) {
    61 return actualObject === this.object_;
    62};
    63
    64
    65/**
    66 * @override
    67 */
    68goog.labs.testing.ObjectEqualsMatcher.prototype.describe =
    69 function(actualObject) {
    70 return 'Input object is not the same as the expected object.';
    71};
    72
    73
    74
    75/**
    76 * The HasProperty matcher.
    77 *
    78 * @param {string} property Name of the property to test.
    79 *
    80 * @constructor
    81 * @struct
    82 * @implements {goog.labs.testing.Matcher}
    83 * @final
    84 */
    85goog.labs.testing.HasPropertyMatcher = function(property) {
    86 /**
    87 * @type {string}
    88 * @private
    89 */
    90 this.property_ = property;
    91};
    92
    93
    94/**
    95 * Determines if an object has a property.
    96 *
    97 * @override
    98 */
    99goog.labs.testing.HasPropertyMatcher.prototype.matches =
    100 function(actualObject) {
    101 return this.property_ in actualObject;
    102};
    103
    104
    105/**
    106 * @override
    107 */
    108goog.labs.testing.HasPropertyMatcher.prototype.describe =
    109 function(actualObject) {
    110 return 'Object does not have property: ' + this.property_;
    111};
    112
    113
    114
    115/**
    116 * The InstanceOf matcher.
    117 *
    118 * @param {!Object} object The expected class object.
    119 *
    120 * @constructor
    121 * @struct
    122 * @implements {goog.labs.testing.Matcher}
    123 * @final
    124 */
    125goog.labs.testing.InstanceOfMatcher = function(object) {
    126 /**
    127 * @type {!Object}
    128 * @private
    129 */
    130 this.object_ = object;
    131};
    132
    133
    134/**
    135 * Determines if an object is an instance of another object.
    136 *
    137 * @override
    138 */
    139goog.labs.testing.InstanceOfMatcher.prototype.matches =
    140 function(actualObject) {
    141 return actualObject instanceof this.object_;
    142};
    143
    144
    145/**
    146 * @override
    147 */
    148goog.labs.testing.InstanceOfMatcher.prototype.describe =
    149 function(actualObject) {
    150 return 'Input object is not an instance of the expected object';
    151};
    152
    153
    154
    155/**
    156 * The IsNullOrUndefined matcher.
    157 *
    158 * @constructor
    159 * @struct
    160 * @implements {goog.labs.testing.Matcher}
    161 * @final
    162 */
    163goog.labs.testing.IsNullOrUndefinedMatcher = function() {};
    164
    165
    166/**
    167 * Determines if input value is null or undefined.
    168 *
    169 * @override
    170 */
    171goog.labs.testing.IsNullOrUndefinedMatcher.prototype.matches =
    172 function(actualValue) {
    173 return !goog.isDefAndNotNull(actualValue);
    174};
    175
    176
    177/**
    178 * @override
    179 */
    180goog.labs.testing.IsNullOrUndefinedMatcher.prototype.describe =
    181 function(actualValue) {
    182 return actualValue + ' is not null or undefined.';
    183};
    184
    185
    186
    187/**
    188 * The IsNull matcher.
    189 *
    190 * @constructor
    191 * @struct
    192 * @implements {goog.labs.testing.Matcher}
    193 * @final
    194 */
    195goog.labs.testing.IsNullMatcher = function() {};
    196
    197
    198/**
    199 * Determines if input value is null.
    200 *
    201 * @override
    202 */
    203goog.labs.testing.IsNullMatcher.prototype.matches =
    204 function(actualValue) {
    205 return goog.isNull(actualValue);
    206};
    207
    208
    209/**
    210 * @override
    211 */
    212goog.labs.testing.IsNullMatcher.prototype.describe =
    213 function(actualValue) {
    214 return actualValue + ' is not null.';
    215};
    216
    217
    218
    219/**
    220 * The IsUndefined matcher.
    221 *
    222 * @constructor
    223 * @struct
    224 * @implements {goog.labs.testing.Matcher}
    225 * @final
    226 */
    227goog.labs.testing.IsUndefinedMatcher = function() {};
    228
    229
    230/**
    231 * Determines if input value is undefined.
    232 *
    233 * @override
    234 */
    235goog.labs.testing.IsUndefinedMatcher.prototype.matches =
    236 function(actualValue) {
    237 return !goog.isDef(actualValue);
    238};
    239
    240
    241/**
    242 * @override
    243 */
    244goog.labs.testing.IsUndefinedMatcher.prototype.describe =
    245 function(actualValue) {
    246 return actualValue + ' is not undefined.';
    247};
    248
    249
    250/**
    251 * Returns a matcher that matches objects that are equal to the input object.
    252 * Equality in this case means the two objects are references to the same
    253 * object.
    254 *
    255 * @param {!Object} object The expected object.
    256 *
    257 * @return {!goog.labs.testing.ObjectEqualsMatcher} A
    258 * ObjectEqualsMatcher.
    259 */
    260function equalsObject(object) {
    261 return new goog.labs.testing.ObjectEqualsMatcher(object);
    262}
    263
    264
    265/**
    266 * Returns a matcher that matches objects that contain the input property.
    267 *
    268 * @param {string} property The property name to check.
    269 *
    270 * @return {!goog.labs.testing.HasPropertyMatcher} A HasPropertyMatcher.
    271 */
    272function hasProperty(property) {
    273 return new goog.labs.testing.HasPropertyMatcher(property);
    274}
    275
    276
    277/**
    278 * Returns a matcher that matches instances of the input class.
    279 *
    280 * @param {!Object} object The class object.
    281 *
    282 * @return {!goog.labs.testing.InstanceOfMatcher} A
    283 * InstanceOfMatcher.
    284 */
    285function instanceOfClass(object) {
    286 return new goog.labs.testing.InstanceOfMatcher(object);
    287}
    288
    289
    290/**
    291 * Returns a matcher that matches all null values.
    292 *
    293 * @return {!goog.labs.testing.IsNullMatcher} A IsNullMatcher.
    294 */
    295function isNull() {
    296 return new goog.labs.testing.IsNullMatcher();
    297}
    298
    299
    300/**
    301 * Returns a matcher that matches all null and undefined values.
    302 *
    303 * @return {!goog.labs.testing.IsNullOrUndefinedMatcher} A
    304 * IsNullOrUndefinedMatcher.
    305 */
    306function isNullOrUndefined() {
    307 return new goog.labs.testing.IsNullOrUndefinedMatcher();
    308}
    309
    310
    311/**
    312 * Returns a matcher that matches undefined values.
    313 *
    314 * @return {!goog.labs.testing.IsUndefinedMatcher} A IsUndefinedMatcher.
    315 */
    316function isUndefined() {
    317 return new goog.labs.testing.IsUndefinedMatcher();
    318}
    \ 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 34f8202..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 * @struct
    44 * @implements {goog.labs.testing.Matcher}
    45 * @final
    46 */
    47goog.labs.testing.ContainsStringMatcher = function(value) {
    48 /**
    49 * @type {string}
    50 * @private
    51 */
    52 this.value_ = value;
    53};
    54
    55
    56/**
    57 * Determines if input string contains the expected string.
    58 *
    59 * @override
    60 */
    61goog.labs.testing.ContainsStringMatcher.prototype.matches =
    62 function(actualValue) {
    63 goog.asserts.assertString(actualValue);
    64 return goog.string.contains(actualValue, this.value_);
    65};
    66
    67
    68/**
    69 * @override
    70 */
    71goog.labs.testing.ContainsStringMatcher.prototype.describe =
    72 function(actualValue) {
    73 return actualValue + ' does not contain ' + this.value_;
    74};
    75
    76
    77
    78/**
    79 * The EndsWith matcher.
    80 *
    81 * @param {string} value The expected string.
    82 *
    83 * @constructor
    84 * @struct
    85 * @implements {goog.labs.testing.Matcher}
    86 * @final
    87 */
    88goog.labs.testing.EndsWithMatcher = function(value) {
    89 /**
    90 * @type {string}
    91 * @private
    92 */
    93 this.value_ = value;
    94};
    95
    96
    97/**
    98 * Determines if input string ends with the expected string.
    99 *
    100 * @override
    101 */
    102goog.labs.testing.EndsWithMatcher.prototype.matches = function(actualValue) {
    103 goog.asserts.assertString(actualValue);
    104 return goog.string.endsWith(actualValue, this.value_);
    105};
    106
    107
    108/**
    109 * @override
    110 */
    111goog.labs.testing.EndsWithMatcher.prototype.describe =
    112 function(actualValue) {
    113 return actualValue + ' does not end with ' + this.value_;
    114};
    115
    116
    117
    118/**
    119 * The EqualToIgnoringWhitespace matcher.
    120 *
    121 * @param {string} value The expected string.
    122 *
    123 * @constructor
    124 * @struct
    125 * @implements {goog.labs.testing.Matcher}
    126 * @final
    127 */
    128goog.labs.testing.EqualToIgnoringWhitespaceMatcher = function(value) {
    129 /**
    130 * @type {string}
    131 * @private
    132 */
    133 this.value_ = value;
    134};
    135
    136
    137/**
    138 * Determines if input string contains the expected string.
    139 *
    140 * @override
    141 */
    142goog.labs.testing.EqualToIgnoringWhitespaceMatcher.prototype.matches =
    143 function(actualValue) {
    144 goog.asserts.assertString(actualValue);
    145 var string1 = goog.string.collapseWhitespace(actualValue);
    146
    147 return goog.string.caseInsensitiveCompare(this.value_, string1) === 0;
    148};
    149
    150
    151/**
    152 * @override
    153 */
    154goog.labs.testing.EqualToIgnoringWhitespaceMatcher.prototype.describe =
    155 function(actualValue) {
    156 return actualValue + ' is not equal(ignoring whitespace) to ' + this.value_;
    157};
    158
    159
    160
    161/**
    162 * The Equals matcher.
    163 *
    164 * @param {string} value The expected string.
    165 *
    166 * @constructor
    167 * @struct
    168 * @implements {goog.labs.testing.Matcher}
    169 * @final
    170 */
    171goog.labs.testing.EqualsMatcher = function(value) {
    172 /**
    173 * @type {string}
    174 * @private
    175 */
    176 this.value_ = value;
    177};
    178
    179
    180/**
    181 * Determines if input string is equal to the expected string.
    182 *
    183 * @override
    184 */
    185goog.labs.testing.EqualsMatcher.prototype.matches = function(actualValue) {
    186 goog.asserts.assertString(actualValue);
    187 return this.value_ === actualValue;
    188};
    189
    190
    191/**
    192 * @override
    193 */
    194goog.labs.testing.EqualsMatcher.prototype.describe =
    195 function(actualValue) {
    196 return actualValue + ' is not equal to ' + this.value_;
    197};
    198
    199
    200
    201/**
    202 * The MatchesRegex matcher.
    203 *
    204 * @param {!RegExp} regex The expected regex.
    205 *
    206 * @constructor
    207 * @struct
    208 * @implements {goog.labs.testing.Matcher}
    209 * @final
    210 */
    211goog.labs.testing.RegexMatcher = function(regex) {
    212 /**
    213 * @type {!RegExp}
    214 * @private
    215 */
    216 this.regex_ = regex;
    217};
    218
    219
    220/**
    221 * Determines if input string is equal to the expected string.
    222 *
    223 * @override
    224 */
    225goog.labs.testing.RegexMatcher.prototype.matches = function(
    226 actualValue) {
    227 goog.asserts.assertString(actualValue);
    228 return this.regex_.test(actualValue);
    229};
    230
    231
    232/**
    233 * @override
    234 */
    235goog.labs.testing.RegexMatcher.prototype.describe =
    236 function(actualValue) {
    237 return actualValue + ' does not match ' + this.regex_;
    238};
    239
    240
    241
    242/**
    243 * The StartsWith matcher.
    244 *
    245 * @param {string} value The expected string.
    246 *
    247 * @constructor
    248 * @struct
    249 * @implements {goog.labs.testing.Matcher}
    250 * @final
    251 */
    252goog.labs.testing.StartsWithMatcher = function(value) {
    253 /**
    254 * @type {string}
    255 * @private
    256 */
    257 this.value_ = value;
    258};
    259
    260
    261/**
    262 * Determines if input string starts with the expected string.
    263 *
    264 * @override
    265 */
    266goog.labs.testing.StartsWithMatcher.prototype.matches = function(actualValue) {
    267 goog.asserts.assertString(actualValue);
    268 return goog.string.startsWith(actualValue, this.value_);
    269};
    270
    271
    272/**
    273 * @override
    274 */
    275goog.labs.testing.StartsWithMatcher.prototype.describe =
    276 function(actualValue) {
    277 return actualValue + ' does not start with ' + this.value_;
    278};
    279
    280
    281
    282/**
    283 * The StringContainsInOrdermatcher.
    284 *
    285 * @param {Array.<string>} values The expected string values.
    286 *
    287 * @constructor
    288 * @struct
    289 * @implements {goog.labs.testing.Matcher}
    290 * @final
    291 */
    292goog.labs.testing.StringContainsInOrderMatcher = function(values) {
    293 /**
    294 * @type {Array.<string>}
    295 * @private
    296 */
    297 this.values_ = values;
    298};
    299
    300
    301/**
    302 * Determines if input string contains, in order, the expected array of strings.
    303 *
    304 * @override
    305 */
    306goog.labs.testing.StringContainsInOrderMatcher.prototype.matches =
    307 function(actualValue) {
    308 goog.asserts.assertString(actualValue);
    309 var currentIndex, previousIndex = 0;
    310 for (var i = 0; i < this.values_.length; i++) {
    311 currentIndex = goog.string.contains(actualValue, this.values_[i]);
    312 if (currentIndex < 0 || currentIndex < previousIndex) {
    313 return false;
    314 }
    315 previousIndex = currentIndex;
    316 }
    317 return true;
    318};
    319
    320
    321/**
    322 * @override
    323 */
    324goog.labs.testing.StringContainsInOrderMatcher.prototype.describe =
    325 function(actualValue) {
    326 return actualValue + ' does not contain the expected values in order.';
    327};
    328
    329
    330/**
    331 * Matches a string containing the given string.
    332 *
    333 * @param {string} value The expected value.
    334 *
    335 * @return {!goog.labs.testing.ContainsStringMatcher} A
    336 * ContainsStringMatcher.
    337 */
    338function containsString(value) {
    339 return new goog.labs.testing.ContainsStringMatcher(value);
    340}
    341
    342
    343/**
    344 * Matches a string that ends with the given string.
    345 *
    346 * @param {string} value The expected value.
    347 *
    348 * @return {!goog.labs.testing.EndsWithMatcher} A
    349 * EndsWithMatcher.
    350 */
    351function endsWith(value) {
    352 return new goog.labs.testing.EndsWithMatcher(value);
    353}
    354
    355
    356/**
    357 * Matches a string that equals (ignoring whitespace) the given string.
    358 *
    359 * @param {string} value The expected value.
    360 *
    361 * @return {!goog.labs.testing.EqualToIgnoringWhitespaceMatcher} A
    362 * EqualToIgnoringWhitespaceMatcher.
    363 */
    364function equalToIgnoringWhitespace(value) {
    365 return new goog.labs.testing.EqualToIgnoringWhitespaceMatcher(value);
    366}
    367
    368
    369/**
    370 * Matches a string that equals the given string.
    371 *
    372 * @param {string} value The expected value.
    373 *
    374 * @return {!goog.labs.testing.EqualsMatcher} A EqualsMatcher.
    375 */
    376function equals(value) {
    377 return new goog.labs.testing.EqualsMatcher(value);
    378}
    379
    380
    381/**
    382 * Matches a string against a regular expression.
    383 *
    384 * @param {!RegExp} regex The expected regex.
    385 *
    386 * @return {!goog.labs.testing.RegexMatcher} A RegexMatcher.
    387 */
    388function matchesRegex(regex) {
    389 return new goog.labs.testing.RegexMatcher(regex);
    390}
    391
    392
    393/**
    394 * Matches a string that starts with the given string.
    395 *
    396 * @param {string} value The expected value.
    397 *
    398 * @return {!goog.labs.testing.StartsWithMatcher} A
    399 * StartsWithMatcher.
    400 */
    401function startsWith(value) {
    402 return new goog.labs.testing.StartsWithMatcher(value);
    403}
    404
    405
    406/**
    407 * Matches a string that contains the given strings in order.
    408 *
    409 * @param {Array.<string>} values The expected value.
    410 *
    411 * @return {!goog.labs.testing.StringContainsInOrderMatcher} A
    412 * StringContainsInOrderMatcher.
    413 */
    414function stringContainsInOrder(values) {
    415 return new goog.labs.testing.StringContainsInOrderMatcher(values);
    416}
    \ 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 index 3ec8b12..9ee8ef3 100644 --- a/docs/source/lib/goog/labs/useragent/browser.js.src.html +++ b/docs/source/lib/goog/labs/useragent/browser.js.src.html @@ -1 +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 */
    23
    24goog.provide('goog.labs.userAgent.browser');
    25
    26goog.require('goog.array');
    27goog.require('goog.labs.userAgent.util');
    28goog.require('goog.object');
    29goog.require('goog.string');
    30
    31
    32/**
    33 * @return {boolean} Whether the user's browser is Opera.
    34 * @private
    35 */
    36goog.labs.userAgent.browser.matchOpera_ = function() {
    37 return goog.labs.userAgent.util.matchUserAgent('Opera') ||
    38 goog.labs.userAgent.util.matchUserAgent('OPR');
    39};
    40
    41
    42/**
    43 * @return {boolean} Whether the user's browser is IE.
    44 * @private
    45 */
    46goog.labs.userAgent.browser.matchIE_ = function() {
    47 return goog.labs.userAgent.util.matchUserAgent('Trident') ||
    48 goog.labs.userAgent.util.matchUserAgent('MSIE');
    49};
    50
    51
    52/**
    53 * @return {boolean} Whether the user's browser is Firefox.
    54 * @private
    55 */
    56goog.labs.userAgent.browser.matchFirefox_ = function() {
    57 return goog.labs.userAgent.util.matchUserAgent('Firefox');
    58};
    59
    60
    61/**
    62 * @return {boolean} Whether the user's browser is Safari.
    63 * @private
    64 */
    65goog.labs.userAgent.browser.matchSafari_ = function() {
    66 return goog.labs.userAgent.util.matchUserAgent('Safari') &&
    67 !goog.labs.userAgent.util.matchUserAgent('Chrome') &&
    68 !goog.labs.userAgent.util.matchUserAgent('CriOS') &&
    69 !goog.labs.userAgent.util.matchUserAgent('Android');
    70};
    71
    72
    73/**
    74 * @return {boolean} Whether the user's browser is Chrome.
    75 * @private
    76 */
    77goog.labs.userAgent.browser.matchChrome_ = function() {
    78 return goog.labs.userAgent.util.matchUserAgent('Chrome') ||
    79 goog.labs.userAgent.util.matchUserAgent('CriOS');
    80};
    81
    82
    83/**
    84 * @return {boolean} Whether the user's browser is the Android browser.
    85 * @private
    86 */
    87goog.labs.userAgent.browser.matchAndroidBrowser_ = function() {
    88 // Android can appear in the user agent string for Chrome on Android.
    89 // This is not the Android standalone browser if it does.
    90 return !goog.labs.userAgent.browser.isChrome() &&
    91 goog.labs.userAgent.util.matchUserAgent('Android');
    92
    93};
    94
    95
    96/**
    97 * @return {boolean} Whether the user's browser is Opera.
    98 */
    99goog.labs.userAgent.browser.isOpera = goog.labs.userAgent.browser.matchOpera_;
    100
    101
    102/**
    103 * @return {boolean} Whether the user's browser is IE.
    104 */
    105goog.labs.userAgent.browser.isIE = goog.labs.userAgent.browser.matchIE_;
    106
    107
    108/**
    109 * @return {boolean} Whether the user's browser is Firefox.
    110 */
    111goog.labs.userAgent.browser.isFirefox =
    112 goog.labs.userAgent.browser.matchFirefox_;
    113
    114
    115/**
    116 * @return {boolean} Whether the user's browser is Safari.
    117 */
    118goog.labs.userAgent.browser.isSafari =
    119 goog.labs.userAgent.browser.matchSafari_;
    120
    121
    122/**
    123 * @return {boolean} Whether the user's browser is Chrome.
    124 */
    125goog.labs.userAgent.browser.isChrome =
    126 goog.labs.userAgent.browser.matchChrome_;
    127
    128
    129/**
    130 * @return {boolean} Whether the user's browser is the Android browser.
    131 */
    132goog.labs.userAgent.browser.isAndroidBrowser =
    133 goog.labs.userAgent.browser.matchAndroidBrowser_;
    134
    135
    136/**
    137 * For more information, see:
    138 * http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
    139 * @return {boolean} Whether the user's browser is Silk.
    140 */
    141goog.labs.userAgent.browser.isSilk = function() {
    142 return goog.labs.userAgent.util.matchUserAgent('Silk');
    143};
    144
    145
    146/**
    147 * @return {string} The browser version or empty string if version cannot be
    148 * determined. Note that for Internet Explorer, this returns the version of
    149 * the browser, not the version of the rendering engine. (IE 8 in
    150 * compatibility mode will return 8.0 rather than 7.0. To determine the
    151 * rendering engine version, look at document.documentMode instead. See
    152 * http://msdn.microsoft.com/en-us/library/cc196988(v=vs.85).aspx for more
    153 * details.)
    154 */
    155goog.labs.userAgent.browser.getVersion = function() {
    156 var userAgentString = goog.labs.userAgent.util.getUserAgent();
    157 // Special case IE since IE's version is inside the parenthesis and
    158 // without the '/'.
    159 if (goog.labs.userAgent.browser.isIE()) {
    160 return goog.labs.userAgent.browser.getIEVersion_(userAgentString);
    161 }
    162
    163 var versionTuples = goog.labs.userAgent.util.extractVersionTuples(
    164 userAgentString);
    165
    166 // Construct a map for easy lookup.
    167 var versionMap = {};
    168 goog.array.forEach(versionTuples, function(tuple) {
    169 // Note that the tuple is of length three, but we only care about the
    170 // first two.
    171 var key = tuple[0];
    172 var value = tuple[1];
    173 versionMap[key] = value;
    174 });
    175
    176 var versionMapHasKey = goog.partial(goog.object.containsKey, versionMap);
    177
    178 // Gives the value with the first key it finds, otherwise empty string.
    179 function lookUpValueWithKeys(keys) {
    180 var key = goog.array.find(keys, versionMapHasKey);
    181 return versionMap[key] || '';
    182 }
    183
    184 // Check Opera before Chrome since Opera 15+ has "Chrome" in the string.
    185 // See
    186 // http://my.opera.com/ODIN/blog/2013/07/15/opera-user-agent-strings-opera-15-and-beyond
    187 if (goog.labs.userAgent.browser.isOpera()) {
    188 // Opera 10 has Version/10.0 but Opera/9.8, so look for "Version" first.
    189 // Opera uses 'OPR' for more recent UAs.
    190 return lookUpValueWithKeys(['Version', 'Opera', 'OPR']);
    191 }
    192
    193 if (goog.labs.userAgent.browser.isChrome()) {
    194 return lookUpValueWithKeys(['Chrome', 'CriOS']);
    195 }
    196
    197 // Usually products browser versions are in the third tuple after "Mozilla"
    198 // and the engine.
    199 var tuple = versionTuples[2];
    200 return tuple && tuple[1] || '';
    201};
    202
    203
    204/**
    205 * @param {string|number} version The version to check.
    206 * @return {boolean} Whether the browser version is higher or the same as the
    207 * given version.
    208 */
    209goog.labs.userAgent.browser.isVersionOrHigher = function(version) {
    210 return goog.string.compareVersions(goog.labs.userAgent.browser.getVersion(),
    211 version) >= 0;
    212};
    213
    214
    215/**
    216 * Determines IE version. More information:
    217 * http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#uaString
    218 * http://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
    219 * http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx
    220 * http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx
    221 *
    222 * @param {string} userAgent the User-Agent.
    223 * @return {string}
    224 * @private
    225 */
    226goog.labs.userAgent.browser.getIEVersion_ = function(userAgent) {
    227 // IE11 may identify itself as MSIE 9.0 or MSIE 10.0 due to an IE 11 upgrade
    228 // bug. Example UA:
    229 // Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; rv:11.0)
    230 // like Gecko.
    231 // See http://www.whatismybrowser.com/developers/unknown-user-agent-fragments.
    232 var rv = /rv: *([\d\.]*)/.exec(userAgent);
    233 if (rv && rv[1]) {
    234 return rv[1];
    235 }
    236
    237 var version = '';
    238 var msie = /MSIE +([\d\.]+)/.exec(userAgent);
    239 if (msie && msie[1]) {
    240 // IE in compatibility mode usually identifies itself as MSIE 7.0; in this
    241 // case, use the Trident version to determine the version of IE. For more
    242 // details, see the links above.
    243 var tridentVersion = /Trident\/(\d.\d)/.exec(userAgent);
    244 if (msie[1] == '7.0') {
    245 if (tridentVersion && tridentVersion[1]) {
    246 switch (tridentVersion[1]) {
    247 case '4.0':
    248 version = '8.0';
    249 break;
    250 case '5.0':
    251 version = '9.0';
    252 break;
    253 case '6.0':
    254 version = '10.0';
    255 break;
    256 case '7.0':
    257 version = '11.0';
    258 break;
    259 }
    260 } else {
    261 version = '7.0';
    262 }
    263 } else {
    264 version = msie[1];
    265 }
    266 }
    267 return version;
    268};
    \ No newline at end of file +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('Edge') ||
    53 goog.labs.userAgent.util.matchUserAgent('Trident') ||
    54 goog.labs.userAgent.util.matchUserAgent('MSIE');
    55};
    56
    57
    58/**
    59 * @return {boolean} Whether the user's browser is Firefox.
    60 * @private
    61 */
    62goog.labs.userAgent.browser.matchFirefox_ = function() {
    63 return goog.labs.userAgent.util.matchUserAgent('Firefox');
    64};
    65
    66
    67/**
    68 * @return {boolean} Whether the user's browser is Safari.
    69 * @private
    70 */
    71goog.labs.userAgent.browser.matchSafari_ = function() {
    72 return goog.labs.userAgent.util.matchUserAgent('Safari') &&
    73 !(goog.labs.userAgent.browser.matchChrome_() ||
    74 goog.labs.userAgent.browser.matchCoast_() ||
    75 goog.labs.userAgent.browser.matchOpera_() ||
    76 goog.labs.userAgent.browser.matchIE_() ||
    77 goog.labs.userAgent.browser.isSilk() ||
    78 goog.labs.userAgent.util.matchUserAgent('Android'));
    79};
    80
    81
    82/**
    83 * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
    84 * iOS browser).
    85 * @private
    86 */
    87goog.labs.userAgent.browser.matchCoast_ = function() {
    88 return goog.labs.userAgent.util.matchUserAgent('Coast');
    89};
    90
    91
    92/**
    93 * @return {boolean} Whether the user's browser is iOS Webview.
    94 * @private
    95 */
    96goog.labs.userAgent.browser.matchIosWebview_ = function() {
    97 // iOS Webview does not show up as Chrome or Safari. Also check for Opera's
    98 // WebKit-based iOS browser, Coast.
    99 return (goog.labs.userAgent.util.matchUserAgent('iPad') ||
    100 goog.labs.userAgent.util.matchUserAgent('iPhone')) &&
    101 !goog.labs.userAgent.browser.matchSafari_() &&
    102 !goog.labs.userAgent.browser.matchChrome_() &&
    103 !goog.labs.userAgent.browser.matchCoast_() &&
    104 goog.labs.userAgent.util.matchUserAgent('AppleWebKit');
    105};
    106
    107
    108/**
    109 * @return {boolean} Whether the user's browser is Chrome.
    110 * @private
    111 */
    112goog.labs.userAgent.browser.matchChrome_ = function() {
    113 return (goog.labs.userAgent.util.matchUserAgent('Chrome') ||
    114 goog.labs.userAgent.util.matchUserAgent('CriOS')) &&
    115 !goog.labs.userAgent.browser.matchOpera_() &&
    116 !goog.labs.userAgent.browser.matchIE_();
    117};
    118
    119
    120/**
    121 * @return {boolean} Whether the user's browser is the Android browser.
    122 * @private
    123 */
    124goog.labs.userAgent.browser.matchAndroidBrowser_ = function() {
    125 // Android can appear in the user agent string for Chrome on Android.
    126 // This is not the Android standalone browser if it does.
    127 return goog.labs.userAgent.util.matchUserAgent('Android') &&
    128 !(goog.labs.userAgent.browser.isChrome() ||
    129 goog.labs.userAgent.browser.isFirefox() ||
    130 goog.labs.userAgent.browser.isOpera() ||
    131 goog.labs.userAgent.browser.isSilk());
    132};
    133
    134
    135/**
    136 * @return {boolean} Whether the user's browser is Opera.
    137 */
    138goog.labs.userAgent.browser.isOpera = goog.labs.userAgent.browser.matchOpera_;
    139
    140
    141/**
    142 * @return {boolean} Whether the user's browser is IE.
    143 */
    144goog.labs.userAgent.browser.isIE = goog.labs.userAgent.browser.matchIE_;
    145
    146
    147/**
    148 * @return {boolean} Whether the user's browser is Firefox.
    149 */
    150goog.labs.userAgent.browser.isFirefox =
    151 goog.labs.userAgent.browser.matchFirefox_;
    152
    153
    154/**
    155 * @return {boolean} Whether the user's browser is Safari.
    156 */
    157goog.labs.userAgent.browser.isSafari =
    158 goog.labs.userAgent.browser.matchSafari_;
    159
    160
    161/**
    162 * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
    163 * iOS browser).
    164 */
    165goog.labs.userAgent.browser.isCoast =
    166 goog.labs.userAgent.browser.matchCoast_;
    167
    168
    169/**
    170 * @return {boolean} Whether the user's browser is iOS Webview.
    171 */
    172goog.labs.userAgent.browser.isIosWebview =
    173 goog.labs.userAgent.browser.matchIosWebview_;
    174
    175
    176/**
    177 * @return {boolean} Whether the user's browser is Chrome.
    178 */
    179goog.labs.userAgent.browser.isChrome =
    180 goog.labs.userAgent.browser.matchChrome_;
    181
    182
    183/**
    184 * @return {boolean} Whether the user's browser is the Android browser.
    185 */
    186goog.labs.userAgent.browser.isAndroidBrowser =
    187 goog.labs.userAgent.browser.matchAndroidBrowser_;
    188
    189
    190/**
    191 * For more information, see:
    192 * http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
    193 * @return {boolean} Whether the user's browser is Silk.
    194 */
    195goog.labs.userAgent.browser.isSilk = function() {
    196 return goog.labs.userAgent.util.matchUserAgent('Silk');
    197};
    198
    199
    200/**
    201 * @return {string} The browser version or empty string if version cannot be
    202 * determined. Note that for Internet Explorer, this returns the version of
    203 * the browser, not the version of the rendering engine. (IE 8 in
    204 * compatibility mode will return 8.0 rather than 7.0. To determine the
    205 * rendering engine version, look at document.documentMode instead. See
    206 * http://msdn.microsoft.com/en-us/library/cc196988(v=vs.85).aspx for more
    207 * details.)
    208 */
    209goog.labs.userAgent.browser.getVersion = function() {
    210 var userAgentString = goog.labs.userAgent.util.getUserAgent();
    211 // Special case IE since IE's version is inside the parenthesis and
    212 // without the '/'.
    213 if (goog.labs.userAgent.browser.isIE()) {
    214 return goog.labs.userAgent.browser.getIEVersion_(userAgentString);
    215 }
    216
    217 var versionTuples = goog.labs.userAgent.util.extractVersionTuples(
    218 userAgentString);
    219
    220 // Construct a map for easy lookup.
    221 var versionMap = {};
    222 goog.array.forEach(versionTuples, function(tuple) {
    223 // Note that the tuple is of length three, but we only care about the
    224 // first two.
    225 var key = tuple[0];
    226 var value = tuple[1];
    227 versionMap[key] = value;
    228 });
    229
    230 var versionMapHasKey = goog.partial(goog.object.containsKey, versionMap);
    231
    232 // Gives the value with the first key it finds, otherwise empty string.
    233 function lookUpValueWithKeys(keys) {
    234 var key = goog.array.find(keys, versionMapHasKey);
    235 return versionMap[key] || '';
    236 }
    237
    238 // Check Opera before Chrome since Opera 15+ has "Chrome" in the string.
    239 // See
    240 // http://my.opera.com/ODIN/blog/2013/07/15/opera-user-agent-strings-opera-15-and-beyond
    241 if (goog.labs.userAgent.browser.isOpera()) {
    242 // Opera 10 has Version/10.0 but Opera/9.8, so look for "Version" first.
    243 // Opera uses 'OPR' for more recent UAs.
    244 return lookUpValueWithKeys(['Version', 'Opera', 'OPR']);
    245 }
    246
    247 if (goog.labs.userAgent.browser.isChrome()) {
    248 return lookUpValueWithKeys(['Chrome', 'CriOS']);
    249 }
    250
    251 // Usually products browser versions are in the third tuple after "Mozilla"
    252 // and the engine.
    253 var tuple = versionTuples[2];
    254 return tuple && tuple[1] || '';
    255};
    256
    257
    258/**
    259 * @param {string|number} version The version to check.
    260 * @return {boolean} Whether the browser version is higher or the same as the
    261 * given version.
    262 */
    263goog.labs.userAgent.browser.isVersionOrHigher = function(version) {
    264 return goog.string.compareVersions(goog.labs.userAgent.browser.getVersion(),
    265 version) >= 0;
    266};
    267
    268
    269/**
    270 * Determines IE version. More information:
    271 * http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#uaString
    272 * http://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
    273 * http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx
    274 * http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx
    275 *
    276 * @param {string} userAgent the User-Agent.
    277 * @return {string}
    278 * @private
    279 */
    280goog.labs.userAgent.browser.getIEVersion_ = function(userAgent) {
    281 // IE11 may identify itself as MSIE 9.0 or MSIE 10.0 due to an IE 11 upgrade
    282 // bug. Example UA:
    283 // Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; rv:11.0)
    284 // like Gecko.
    285 // See http://www.whatismybrowser.com/developers/unknown-user-agent-fragments.
    286 var rv = /rv: *([\d\.]*)/.exec(userAgent);
    287 if (rv && rv[1]) {
    288 return rv[1];
    289 }
    290
    291 var edge = /Edge\/([\d\.]+)/.exec(userAgent);
    292 if (edge) {
    293 return edge[1];
    294 }
    295
    296 var version = '';
    297 var msie = /MSIE +([\d\.]+)/.exec(userAgent);
    298 if (msie && msie[1]) {
    299 // IE in compatibility mode usually identifies itself as MSIE 7.0; in this
    300 // case, use the Trident version to determine the version of IE. For more
    301 // details, see the links above.
    302 var tridentVersion = /Trident\/(\d.\d)/.exec(userAgent);
    303 if (msie[1] == '7.0') {
    304 if (tridentVersion && tridentVersion[1]) {
    305 switch (tridentVersion[1]) {
    306 case '4.0':
    307 version = '8.0';
    308 break;
    309 case '5.0':
    310 version = '9.0';
    311 break;
    312 case '6.0':
    313 version = '10.0';
    314 break;
    315 case '7.0':
    316 version = '11.0';
    317 break;
    318 }
    319 } else {
    320 version = '7.0';
    321 }
    322 } else {
    323 version = msie[1];
    324 }
    325 }
    326 return version;
    327};
    \ 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 index 60b5048..2077e6d 100644 --- a/docs/source/lib/goog/labs/useragent/engine.js.src.html +++ b/docs/source/lib/goog/labs/useragent/engine.js.src.html @@ -1 +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 WebKit.
    50 */
    51goog.labs.userAgent.engine.isWebKit = function() {
    52 return goog.labs.userAgent.util.matchUserAgentIgnoreCase('WebKit');
    53};
    54
    55
    56/**
    57 * @return {boolean} Whether the rendering engine is Gecko.
    58 */
    59goog.labs.userAgent.engine.isGecko = function() {
    60 return goog.labs.userAgent.util.matchUserAgent('Gecko') &&
    61 !goog.labs.userAgent.engine.isWebKit() &&
    62 !goog.labs.userAgent.engine.isTrident();
    63};
    64
    65
    66/**
    67 * @return {string} The rendering engine's version or empty string if version
    68 * can't be determined.
    69 */
    70goog.labs.userAgent.engine.getVersion = function() {
    71 var userAgentString = goog.labs.userAgent.util.getUserAgent();
    72 if (userAgentString) {
    73 var tuples = goog.labs.userAgent.util.extractVersionTuples(
    74 userAgentString);
    75
    76 var engineTuple = tuples[1];
    77 if (engineTuple) {
    78 // In Gecko, the version string is either in the browser info or the
    79 // Firefox version. See Gecko user agent string reference:
    80 // http://goo.gl/mULqa
    81 if (engineTuple[0] == 'Gecko') {
    82 return goog.labs.userAgent.engine.getVersionForKey_(
    83 tuples, 'Firefox');
    84 }
    85
    86 return engineTuple[1];
    87 }
    88
    89 // IE has only one version identifier, and the Trident version is
    90 // specified in the parenthetical.
    91 var browserTuple = tuples[0];
    92 var info;
    93 if (browserTuple && (info = browserTuple[2])) {
    94 var match = /Trident\/([^\s;]+)/.exec(info);
    95 if (match) {
    96 return match[1];
    97 }
    98 }
    99 }
    100 return '';
    101};
    102
    103
    104/**
    105 * @param {string|number} version The version to check.
    106 * @return {boolean} Whether the rendering engine version is higher or the same
    107 * as the given version.
    108 */
    109goog.labs.userAgent.engine.isVersionOrHigher = function(version) {
    110 return goog.string.compareVersions(goog.labs.userAgent.engine.getVersion(),
    111 version) >= 0;
    112};
    113
    114
    115/**
    116 * @param {!Array.<!Array.<string>>} tuples Version tuples.
    117 * @param {string} key The key to look for.
    118 * @return {string} The version string of the given key, if present.
    119 * Otherwise, the empty string.
    120 * @private
    121 */
    122goog.labs.userAgent.engine.getVersionForKey_ = function(tuples, key) {
    123 // TODO(nnaze): Move to util if useful elsewhere.
    124
    125 var pair = goog.array.find(tuples, function(pair) {
    126 return key == pair[0];
    127 });
    128
    129 return pair && pair[1] || '';
    130};
    \ No newline at end of file +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 index 45f284e..6f4e40e 100644 --- a/docs/source/lib/goog/labs/useragent/util.js.src.html +++ b/docs/source/lib/goog/labs/useragent/util.js.src.html @@ -1 +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 * @visibility {//closure/goog/bin/sizetests:__pkg__}
    20 * @visibility {//closure/goog/dom:__subpackages__}
    21 * @visibility {//closure/goog/style:__pkg__}
    22 * @visibility {//closure/goog/testing:__pkg__}
    23 * @visibility {//closure/goog/useragent:__subpackages__}
    24 * @visibility {//testing/puppet/modules:__pkg__} *
    25 *
    26 * @author nnaze@google.com (Nathan Naze)
    27 */
    28
    29goog.provide('goog.labs.userAgent.util');
    30
    31goog.require('goog.string');
    32
    33
    34/**
    35 * Gets the native userAgent string from navigator if it exists.
    36 * If navigator or navigator.userAgent string is missing, returns an empty
    37 * string.
    38 * @return {string}
    39 * @private
    40 */
    41goog.labs.userAgent.util.getNativeUserAgentString_ = function() {
    42 var navigator = goog.labs.userAgent.util.getNavigator_();
    43 if (navigator) {
    44 var userAgent = navigator.userAgent;
    45 if (userAgent) {
    46 return userAgent;
    47 }
    48 }
    49 return '';
    50};
    51
    52
    53/**
    54 * Getter for the native navigator.
    55 * This is a separate function so it can be stubbed out in testing.
    56 * @return {Navigator}
    57 * @private
    58 */
    59goog.labs.userAgent.util.getNavigator_ = function() {
    60 return goog.global.navigator;
    61};
    62
    63
    64/**
    65 * A possible override for applications which wish to not check
    66 * navigator.userAgent but use a specified value for detection instead.
    67 * @private {string}
    68 */
    69goog.labs.userAgent.util.userAgent_ =
    70 goog.labs.userAgent.util.getNativeUserAgentString_();
    71
    72
    73/**
    74 * Applications may override browser detection on the built in
    75 * navigator.userAgent object by setting this string. Set to null to use the
    76 * browser object instead.
    77 * @param {?string=} opt_userAgent The User-Agent override.
    78 */
    79goog.labs.userAgent.util.setUserAgent = function(opt_userAgent) {
    80 goog.labs.userAgent.util.userAgent_ = opt_userAgent ||
    81 goog.labs.userAgent.util.getNativeUserAgentString_();
    82};
    83
    84
    85/**
    86 * @return {string} The user agent string.
    87 */
    88goog.labs.userAgent.util.getUserAgent = function() {
    89 return goog.labs.userAgent.util.userAgent_;
    90};
    91
    92
    93/**
    94 * @param {string} str
    95 * @return {boolean} Whether the user agent contains the given string, ignoring
    96 * case.
    97 */
    98goog.labs.userAgent.util.matchUserAgent = function(str) {
    99 var userAgent = goog.labs.userAgent.util.getUserAgent();
    100 return goog.string.contains(userAgent, str);
    101};
    102
    103
    104/**
    105 * @param {string} str
    106 * @return {boolean} Whether the user agent contains the given string.
    107 */
    108goog.labs.userAgent.util.matchUserAgentIgnoreCase = function(str) {
    109 var userAgent = goog.labs.userAgent.util.getUserAgent();
    110 return goog.string.caseInsensitiveContains(userAgent, str);
    111};
    112
    113
    114/**
    115 * Parses the user agent into tuples for each section.
    116 * @param {string} userAgent
    117 * @return {!Array.<!Array.<string>>} Tuples of key, version, and the contents
    118 * of the parenthetical.
    119 */
    120goog.labs.userAgent.util.extractVersionTuples = function(userAgent) {
    121 // Matches each section of a user agent string.
    122 // Example UA:
    123 // Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us)
    124 // AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405
    125 // This has three version tuples: Mozilla, AppleWebKit, and Mobile.
    126
    127 var versionRegExp = new RegExp(
    128 // Key. Note that a key may have a space.
    129 // (i.e. 'Mobile Safari' in 'Mobile Safari/5.0')
    130 '(\\w[\\w ]+)' +
    131
    132 '/' + // slash
    133 '([^\\s]+)' + // version (i.e. '5.0b')
    134 '\\s*' + // whitespace
    135 '(?:\\((.*?)\\))?', // parenthetical info. parentheses not matched.
    136 'g');
    137
    138 var data = [];
    139 var match;
    140
    141 // Iterate and collect the version tuples. Each iteration will be the
    142 // next regex match.
    143 while (match = versionRegExp.exec(userAgent)) {
    144 data.push([
    145 match[1], // key
    146 match[2], // value
    147 // || undefined as this is not undefined in IE7 and IE8
    148 match[3] || undefined // info
    149 ]);
    150 }
    151
    152 return data;
    153};
    154
    \ No newline at end of file +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 index 77c185a..cb48d73 100644 --- a/docs/source/lib/goog/math/box.js.src.html +++ b/docs/source/lib/goog/math/box.js.src.html @@ -1 +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 * @constructor
    38 */
    39goog.math.Box = function(top, right, bottom, left) {
    40 /**
    41 * Top
    42 * @type {number}
    43 */
    44 this.top = top;
    45
    46 /**
    47 * Right
    48 * @type {number}
    49 */
    50 this.right = right;
    51
    52 /**
    53 * Bottom
    54 * @type {number}
    55 */
    56 this.bottom = bottom;
    57
    58 /**
    59 * Left
    60 * @type {number}
    61 */
    62 this.left = left;
    63};
    64
    65
    66/**
    67 * Creates a Box by bounding a collection of goog.math.Coordinate objects
    68 * @param {...goog.math.Coordinate} var_args Coordinates to be included inside
    69 * the box.
    70 * @return {!goog.math.Box} A Box containing all the specified Coordinates.
    71 */
    72goog.math.Box.boundingBox = function(var_args) {
    73 var box = new goog.math.Box(arguments[0].y, arguments[0].x,
    74 arguments[0].y, arguments[0].x);
    75 for (var i = 1; i < arguments.length; i++) {
    76 var coord = arguments[i];
    77 box.top = Math.min(box.top, coord.y);
    78 box.right = Math.max(box.right, coord.x);
    79 box.bottom = Math.max(box.bottom, coord.y);
    80 box.left = Math.min(box.left, coord.x);
    81 }
    82 return box;
    83};
    84
    85
    86/**
    87 * @return {number} width The width of this Box.
    88 */
    89goog.math.Box.prototype.getWidth = function() {
    90 return this.right - this.left;
    91};
    92
    93
    94/**
    95 * @return {number} height The height of this Box.
    96 */
    97goog.math.Box.prototype.getHeight = function() {
    98 return this.bottom - this.top;
    99};
    100
    101
    102/**
    103 * Creates a copy of the box with the same dimensions.
    104 * @return {!goog.math.Box} A clone of this Box.
    105 */
    106goog.math.Box.prototype.clone = function() {
    107 return new goog.math.Box(this.top, this.right, this.bottom, this.left);
    108};
    109
    110
    111if (goog.DEBUG) {
    112 /**
    113 * Returns a nice string representing the box.
    114 * @return {string} In the form (50t, 73r, 24b, 13l).
    115 * @override
    116 */
    117 goog.math.Box.prototype.toString = function() {
    118 return '(' + this.top + 't, ' + this.right + 'r, ' + this.bottom + 'b, ' +
    119 this.left + 'l)';
    120 };
    121}
    122
    123
    124/**
    125 * Returns whether the box contains a coordinate or another box.
    126 *
    127 * @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
    128 * @return {boolean} Whether the box contains the coordinate or other box.
    129 */
    130goog.math.Box.prototype.contains = function(other) {
    131 return goog.math.Box.contains(this, other);
    132};
    133
    134
    135/**
    136 * Expands box with the given margins.
    137 *
    138 * @param {number|goog.math.Box} top Top margin or box with all margins.
    139 * @param {number=} opt_right Right margin.
    140 * @param {number=} opt_bottom Bottom margin.
    141 * @param {number=} opt_left Left margin.
    142 * @return {!goog.math.Box} A reference to this Box.
    143 */
    144goog.math.Box.prototype.expand = function(top, opt_right, opt_bottom,
    145 opt_left) {
    146 if (goog.isObject(top)) {
    147 this.top -= top.top;
    148 this.right += top.right;
    149 this.bottom += top.bottom;
    150 this.left -= top.left;
    151 } else {
    152 this.top -= top;
    153 this.right += opt_right;
    154 this.bottom += opt_bottom;
    155 this.left -= opt_left;
    156 }
    157
    158 return this;
    159};
    160
    161
    162/**
    163 * Expand this box to include another box.
    164 * NOTE(user): This is used in code that needs to be very fast, please don't
    165 * add functionality to this function at the expense of speed (variable
    166 * arguments, accepting multiple argument types, etc).
    167 * @param {goog.math.Box} box The box to include in this one.
    168 */
    169goog.math.Box.prototype.expandToInclude = function(box) {
    170 this.left = Math.min(this.left, box.left);
    171 this.top = Math.min(this.top, box.top);
    172 this.right = Math.max(this.right, box.right);
    173 this.bottom = Math.max(this.bottom, box.bottom);
    174};
    175
    176
    177/**
    178 * Compares boxes for equality.
    179 * @param {goog.math.Box} a A Box.
    180 * @param {goog.math.Box} b A Box.
    181 * @return {boolean} True iff the boxes are equal, or if both are null.
    182 */
    183goog.math.Box.equals = function(a, b) {
    184 if (a == b) {
    185 return true;
    186 }
    187 if (!a || !b) {
    188 return false;
    189 }
    190 return a.top == b.top && a.right == b.right &&
    191 a.bottom == b.bottom && a.left == b.left;
    192};
    193
    194
    195/**
    196 * Returns whether a box contains a coordinate or another box.
    197 *
    198 * @param {goog.math.Box} box A Box.
    199 * @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
    200 * @return {boolean} Whether the box contains the coordinate or other box.
    201 */
    202goog.math.Box.contains = function(box, other) {
    203 if (!box || !other) {
    204 return false;
    205 }
    206
    207 if (other instanceof goog.math.Box) {
    208 return other.left >= box.left && other.right <= box.right &&
    209 other.top >= box.top && other.bottom <= box.bottom;
    210 }
    211
    212 // other is a Coordinate.
    213 return other.x >= box.left && other.x <= box.right &&
    214 other.y >= box.top && other.y <= box.bottom;
    215};
    216
    217
    218/**
    219 * Returns the relative x position of a coordinate compared to a box. Returns
    220 * zero if the coordinate is inside the box.
    221 *
    222 * @param {goog.math.Box} box A Box.
    223 * @param {goog.math.Coordinate} coord A Coordinate.
    224 * @return {number} The x position of {@code coord} relative to the nearest
    225 * side of {@code box}, or zero if {@code coord} is inside {@code box}.
    226 */
    227goog.math.Box.relativePositionX = function(box, coord) {
    228 if (coord.x < box.left) {
    229 return coord.x - box.left;
    230 } else if (coord.x > box.right) {
    231 return coord.x - box.right;
    232 }
    233 return 0;
    234};
    235
    236
    237/**
    238 * Returns the relative y position of a coordinate compared to a box. Returns
    239 * zero if the coordinate is inside the box.
    240 *
    241 * @param {goog.math.Box} box A Box.
    242 * @param {goog.math.Coordinate} coord A Coordinate.
    243 * @return {number} The y position of {@code coord} relative to the nearest
    244 * side of {@code box}, or zero if {@code coord} is inside {@code box}.
    245 */
    246goog.math.Box.relativePositionY = function(box, coord) {
    247 if (coord.y < box.top) {
    248 return coord.y - box.top;
    249 } else if (coord.y > box.bottom) {
    250 return coord.y - box.bottom;
    251 }
    252 return 0;
    253};
    254
    255
    256/**
    257 * Returns the distance between a coordinate and the nearest corner/side of a
    258 * box. Returns zero if the coordinate is inside the box.
    259 *
    260 * @param {goog.math.Box} box A Box.
    261 * @param {goog.math.Coordinate} coord A Coordinate.
    262 * @return {number} The distance between {@code coord} and the nearest
    263 * corner/side of {@code box}, or zero if {@code coord} is inside
    264 * {@code box}.
    265 */
    266goog.math.Box.distance = function(box, coord) {
    267 var x = goog.math.Box.relativePositionX(box, coord);
    268 var y = goog.math.Box.relativePositionY(box, coord);
    269 return Math.sqrt(x * x + y * y);
    270};
    271
    272
    273/**
    274 * Returns whether two boxes intersect.
    275 *
    276 * @param {goog.math.Box} a A Box.
    277 * @param {goog.math.Box} b A second Box.
    278 * @return {boolean} Whether the boxes intersect.
    279 */
    280goog.math.Box.intersects = function(a, b) {
    281 return (a.left <= b.right && b.left <= a.right &&
    282 a.top <= b.bottom && b.top <= a.bottom);
    283};
    284
    285
    286/**
    287 * Returns whether two boxes would intersect with additional padding.
    288 *
    289 * @param {goog.math.Box} a A Box.
    290 * @param {goog.math.Box} b A second Box.
    291 * @param {number} padding The additional padding.
    292 * @return {boolean} Whether the boxes intersect.
    293 */
    294goog.math.Box.intersectsWithPadding = function(a, b, padding) {
    295 return (a.left <= b.right + padding && b.left <= a.right + padding &&
    296 a.top <= b.bottom + padding && b.top <= a.bottom + padding);
    297};
    298
    299
    300/**
    301 * Rounds the fields to the next larger integer values.
    302 *
    303 * @return {!goog.math.Box} This box with ceil'd fields.
    304 */
    305goog.math.Box.prototype.ceil = function() {
    306 this.top = Math.ceil(this.top);
    307 this.right = Math.ceil(this.right);
    308 this.bottom = Math.ceil(this.bottom);
    309 this.left = Math.ceil(this.left);
    310 return this;
    311};
    312
    313
    314/**
    315 * Rounds the fields to the next smaller integer values.
    316 *
    317 * @return {!goog.math.Box} This box with floored fields.
    318 */
    319goog.math.Box.prototype.floor = function() {
    320 this.top = Math.floor(this.top);
    321 this.right = Math.floor(this.right);
    322 this.bottom = Math.floor(this.bottom);
    323 this.left = Math.floor(this.left);
    324 return this;
    325};
    326
    327
    328/**
    329 * Rounds the fields to nearest integer values.
    330 *
    331 * @return {!goog.math.Box} This box with rounded fields.
    332 */
    333goog.math.Box.prototype.round = function() {
    334 this.top = Math.round(this.top);
    335 this.right = Math.round(this.right);
    336 this.bottom = Math.round(this.bottom);
    337 this.left = Math.round(this.left);
    338 return this;
    339};
    340
    341
    342/**
    343 * Translates this box by the given offsets. If a {@code goog.math.Coordinate}
    344 * is given, then the left and right values are translated by the coordinate's
    345 * x value and the top and bottom values are translated by the coordinate's y
    346 * value. Otherwise, {@code tx} and {@code opt_ty} are used to translate the x
    347 * and y dimension values.
    348 *
    349 * @param {number|goog.math.Coordinate} tx The value to translate the x
    350 * dimension values by or the the coordinate to translate this box by.
    351 * @param {number=} opt_ty The value to translate y dimension values by.
    352 * @return {!goog.math.Box} This box after translating.
    353 */
    354goog.math.Box.prototype.translate = function(tx, opt_ty) {
    355 if (tx instanceof goog.math.Coordinate) {
    356 this.left += tx.x;
    357 this.right += tx.x;
    358 this.top += tx.y;
    359 this.bottom += tx.y;
    360 } else {
    361 this.left += tx;
    362 this.right += tx;
    363 if (goog.isNumber(opt_ty)) {
    364 this.top += opt_ty;
    365 this.bottom += opt_ty;
    366 }
    367 }
    368 return this;
    369};
    370
    371
    372/**
    373 * Scales this coordinate by the given scale factors. The x and y dimension
    374 * values are scaled by {@code sx} and {@code opt_sy} respectively.
    375 * If {@code opt_sy} is not given, then {@code sx} is used for both x and y.
    376 *
    377 * @param {number} sx The scale factor to use for the x dimension.
    378 * @param {number=} opt_sy The scale factor to use for the y dimension.
    379 * @return {!goog.math.Box} This box after scaling.
    380 */
    381goog.math.Box.prototype.scale = function(sx, opt_sy) {
    382 var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
    383 this.left *= sx;
    384 this.right *= sx;
    385 this.top *= sy;
    386 this.bottom *= sy;
    387 return this;
    388};
    \ No newline at end of file +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 var coord = arguments[i];
    78 box.top = Math.min(box.top, coord.y);
    79 box.right = Math.max(box.right, coord.x);
    80 box.bottom = Math.max(box.bottom, coord.y);
    81 box.left = Math.min(box.left, coord.x);
    82 }
    83 return box;
    84};
    85
    86
    87/**
    88 * @return {number} width The width of this Box.
    89 */
    90goog.math.Box.prototype.getWidth = function() {
    91 return this.right - this.left;
    92};
    93
    94
    95/**
    96 * @return {number} height The height of this Box.
    97 */
    98goog.math.Box.prototype.getHeight = function() {
    99 return this.bottom - this.top;
    100};
    101
    102
    103/**
    104 * Creates a copy of the box with the same dimensions.
    105 * @return {!goog.math.Box} A clone of this Box.
    106 */
    107goog.math.Box.prototype.clone = function() {
    108 return new goog.math.Box(this.top, this.right, this.bottom, this.left);
    109};
    110
    111
    112if (goog.DEBUG) {
    113 /**
    114 * Returns a nice string representing the box.
    115 * @return {string} In the form (50t, 73r, 24b, 13l).
    116 * @override
    117 */
    118 goog.math.Box.prototype.toString = function() {
    119 return '(' + this.top + 't, ' + this.right + 'r, ' + this.bottom + 'b, ' +
    120 this.left + 'l)';
    121 };
    122}
    123
    124
    125/**
    126 * Returns whether the box contains a coordinate or another box.
    127 *
    128 * @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
    129 * @return {boolean} Whether the box contains the coordinate or other box.
    130 */
    131goog.math.Box.prototype.contains = function(other) {
    132 return goog.math.Box.contains(this, other);
    133};
    134
    135
    136/**
    137 * Expands box with the given margins.
    138 *
    139 * @param {number|goog.math.Box} top Top margin or box with all margins.
    140 * @param {number=} opt_right Right margin.
    141 * @param {number=} opt_bottom Bottom margin.
    142 * @param {number=} opt_left Left margin.
    143 * @return {!goog.math.Box} A reference to this Box.
    144 */
    145goog.math.Box.prototype.expand = function(top, opt_right, opt_bottom,
    146 opt_left) {
    147 if (goog.isObject(top)) {
    148 this.top -= top.top;
    149 this.right += top.right;
    150 this.bottom += top.bottom;
    151 this.left -= top.left;
    152 } else {
    153 this.top -= top;
    154 this.right += opt_right;
    155 this.bottom += opt_bottom;
    156 this.left -= opt_left;
    157 }
    158
    159 return this;
    160};
    161
    162
    163/**
    164 * Expand this box to include another box.
    165 * NOTE(user): This is used in code that needs to be very fast, please don't
    166 * add functionality to this function at the expense of speed (variable
    167 * arguments, accepting multiple argument types, etc).
    168 * @param {goog.math.Box} box The box to include in this one.
    169 */
    170goog.math.Box.prototype.expandToInclude = function(box) {
    171 this.left = Math.min(this.left, box.left);
    172 this.top = Math.min(this.top, box.top);
    173 this.right = Math.max(this.right, box.right);
    174 this.bottom = Math.max(this.bottom, box.bottom);
    175};
    176
    177
    178/**
    179 * Compares boxes for equality.
    180 * @param {goog.math.Box} a A Box.
    181 * @param {goog.math.Box} b A Box.
    182 * @return {boolean} True iff the boxes are equal, or if both are null.
    183 */
    184goog.math.Box.equals = function(a, b) {
    185 if (a == b) {
    186 return true;
    187 }
    188 if (!a || !b) {
    189 return false;
    190 }
    191 return a.top == b.top && a.right == b.right &&
    192 a.bottom == b.bottom && a.left == b.left;
    193};
    194
    195
    196/**
    197 * Returns whether a box contains a coordinate or another box.
    198 *
    199 * @param {goog.math.Box} box A Box.
    200 * @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
    201 * @return {boolean} Whether the box contains the coordinate or other box.
    202 */
    203goog.math.Box.contains = function(box, other) {
    204 if (!box || !other) {
    205 return false;
    206 }
    207
    208 if (other instanceof goog.math.Box) {
    209 return other.left >= box.left && other.right <= box.right &&
    210 other.top >= box.top && other.bottom <= box.bottom;
    211 }
    212
    213 // other is a Coordinate.
    214 return other.x >= box.left && other.x <= box.right &&
    215 other.y >= box.top && other.y <= box.bottom;
    216};
    217
    218
    219/**
    220 * Returns the relative x position of a coordinate compared to a box. Returns
    221 * zero if the coordinate is inside the box.
    222 *
    223 * @param {goog.math.Box} box A Box.
    224 * @param {goog.math.Coordinate} coord A Coordinate.
    225 * @return {number} The x position of {@code coord} relative to the nearest
    226 * side of {@code box}, or zero if {@code coord} is inside {@code box}.
    227 */
    228goog.math.Box.relativePositionX = function(box, coord) {
    229 if (coord.x < box.left) {
    230 return coord.x - box.left;
    231 } else if (coord.x > box.right) {
    232 return coord.x - box.right;
    233 }
    234 return 0;
    235};
    236
    237
    238/**
    239 * Returns the relative y position of a coordinate compared to a box. Returns
    240 * zero if the coordinate is inside the box.
    241 *
    242 * @param {goog.math.Box} box A Box.
    243 * @param {goog.math.Coordinate} coord A Coordinate.
    244 * @return {number} The y position of {@code coord} relative to the nearest
    245 * side of {@code box}, or zero if {@code coord} is inside {@code box}.
    246 */
    247goog.math.Box.relativePositionY = function(box, coord) {
    248 if (coord.y < box.top) {
    249 return coord.y - box.top;
    250 } else if (coord.y > box.bottom) {
    251 return coord.y - box.bottom;
    252 }
    253 return 0;
    254};
    255
    256
    257/**
    258 * Returns the distance between a coordinate and the nearest corner/side of a
    259 * box. Returns zero if the coordinate is inside the box.
    260 *
    261 * @param {goog.math.Box} box A Box.
    262 * @param {goog.math.Coordinate} coord A Coordinate.
    263 * @return {number} The distance between {@code coord} and the nearest
    264 * corner/side of {@code box}, or zero if {@code coord} is inside
    265 * {@code box}.
    266 */
    267goog.math.Box.distance = function(box, coord) {
    268 var x = goog.math.Box.relativePositionX(box, coord);
    269 var y = goog.math.Box.relativePositionY(box, coord);
    270 return Math.sqrt(x * x + y * y);
    271};
    272
    273
    274/**
    275 * Returns whether two boxes intersect.
    276 *
    277 * @param {goog.math.Box} a A Box.
    278 * @param {goog.math.Box} b A second Box.
    279 * @return {boolean} Whether the boxes intersect.
    280 */
    281goog.math.Box.intersects = function(a, b) {
    282 return (a.left <= b.right && b.left <= a.right &&
    283 a.top <= b.bottom && b.top <= a.bottom);
    284};
    285
    286
    287/**
    288 * Returns whether two boxes would intersect with additional padding.
    289 *
    290 * @param {goog.math.Box} a A Box.
    291 * @param {goog.math.Box} b A second Box.
    292 * @param {number} padding The additional padding.
    293 * @return {boolean} Whether the boxes intersect.
    294 */
    295goog.math.Box.intersectsWithPadding = function(a, b, padding) {
    296 return (a.left <= b.right + padding && b.left <= a.right + padding &&
    297 a.top <= b.bottom + padding && b.top <= a.bottom + padding);
    298};
    299
    300
    301/**
    302 * Rounds the fields to the next larger integer values.
    303 *
    304 * @return {!goog.math.Box} This box with ceil'd fields.
    305 */
    306goog.math.Box.prototype.ceil = function() {
    307 this.top = Math.ceil(this.top);
    308 this.right = Math.ceil(this.right);
    309 this.bottom = Math.ceil(this.bottom);
    310 this.left = Math.ceil(this.left);
    311 return this;
    312};
    313
    314
    315/**
    316 * Rounds the fields to the next smaller integer values.
    317 *
    318 * @return {!goog.math.Box} This box with floored fields.
    319 */
    320goog.math.Box.prototype.floor = function() {
    321 this.top = Math.floor(this.top);
    322 this.right = Math.floor(this.right);
    323 this.bottom = Math.floor(this.bottom);
    324 this.left = Math.floor(this.left);
    325 return this;
    326};
    327
    328
    329/**
    330 * Rounds the fields to nearest integer values.
    331 *
    332 * @return {!goog.math.Box} This box with rounded fields.
    333 */
    334goog.math.Box.prototype.round = function() {
    335 this.top = Math.round(this.top);
    336 this.right = Math.round(this.right);
    337 this.bottom = Math.round(this.bottom);
    338 this.left = Math.round(this.left);
    339 return this;
    340};
    341
    342
    343/**
    344 * Translates this box by the given offsets. If a {@code goog.math.Coordinate}
    345 * is given, then the left and right values are translated by the coordinate's
    346 * x value and the top and bottom values are translated by the coordinate's y
    347 * value. Otherwise, {@code tx} and {@code opt_ty} are used to translate the x
    348 * and y dimension values.
    349 *
    350 * @param {number|goog.math.Coordinate} tx The value to translate the x
    351 * dimension values by or the the coordinate to translate this box by.
    352 * @param {number=} opt_ty The value to translate y dimension values by.
    353 * @return {!goog.math.Box} This box after translating.
    354 */
    355goog.math.Box.prototype.translate = function(tx, opt_ty) {
    356 if (tx instanceof goog.math.Coordinate) {
    357 this.left += tx.x;
    358 this.right += tx.x;
    359 this.top += tx.y;
    360 this.bottom += tx.y;
    361 } else {
    362 this.left += tx;
    363 this.right += tx;
    364 if (goog.isNumber(opt_ty)) {
    365 this.top += opt_ty;
    366 this.bottom += opt_ty;
    367 }
    368 }
    369 return this;
    370};
    371
    372
    373/**
    374 * Scales this coordinate by the given scale factors. The x and y dimension
    375 * values are scaled by {@code sx} and {@code opt_sy} respectively.
    376 * If {@code opt_sy} is not given, then {@code sx} is used for both x and y.
    377 *
    378 * @param {number} sx The scale factor to use for the x dimension.
    379 * @param {number=} opt_sy The scale factor to use for the y dimension.
    380 * @return {!goog.math.Box} This box after scaling.
    381 */
    382goog.math.Box.prototype.scale = function(sx, opt_sy) {
    383 var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
    384 this.left *= sx;
    385 this.right *= sx;
    386 this.top *= sy;
    387 this.bottom *= sy;
    388 return this;
    389};
    \ 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 index 70ab6df..3fcf521 100644 --- a/docs/source/lib/goog/math/coordinate.js.src.html +++ b/docs/source/lib/goog/math/coordinate.js.src.html @@ -1 +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 * @constructor
    31 */
    32goog.math.Coordinate = function(opt_x, opt_y) {
    33 /**
    34 * X-value
    35 * @type {number}
    36 */
    37 this.x = goog.isDef(opt_x) ? opt_x : 0;
    38
    39 /**
    40 * Y-value
    41 * @type {number}
    42 */
    43 this.y = goog.isDef(opt_y) ? opt_y : 0;
    44};
    45
    46
    47/**
    48 * Returns a new copy of the coordinate.
    49 * @return {!goog.math.Coordinate} A clone of this coordinate.
    50 */
    51goog.math.Coordinate.prototype.clone = function() {
    52 return new goog.math.Coordinate(this.x, this.y);
    53};
    54
    55
    56if (goog.DEBUG) {
    57 /**
    58 * Returns a nice string representing the coordinate.
    59 * @return {string} In the form (50, 73).
    60 * @override
    61 */
    62 goog.math.Coordinate.prototype.toString = function() {
    63 return '(' + this.x + ', ' + this.y + ')';
    64 };
    65}
    66
    67
    68/**
    69 * Compares coordinates for equality.
    70 * @param {goog.math.Coordinate} a A Coordinate.
    71 * @param {goog.math.Coordinate} b A Coordinate.
    72 * @return {boolean} True iff the coordinates are equal, or if both are null.
    73 */
    74goog.math.Coordinate.equals = function(a, b) {
    75 if (a == b) {
    76 return true;
    77 }
    78 if (!a || !b) {
    79 return false;
    80 }
    81 return a.x == b.x && a.y == b.y;
    82};
    83
    84
    85/**
    86 * Returns the distance between two coordinates.
    87 * @param {!goog.math.Coordinate} a A Coordinate.
    88 * @param {!goog.math.Coordinate} b A Coordinate.
    89 * @return {number} The distance between {@code a} and {@code b}.
    90 */
    91goog.math.Coordinate.distance = function(a, b) {
    92 var dx = a.x - b.x;
    93 var dy = a.y - b.y;
    94 return Math.sqrt(dx * dx + dy * dy);
    95};
    96
    97
    98/**
    99 * Returns the magnitude of a coordinate.
    100 * @param {!goog.math.Coordinate} a A Coordinate.
    101 * @return {number} The distance between the origin and {@code a}.
    102 */
    103goog.math.Coordinate.magnitude = function(a) {
    104 return Math.sqrt(a.x * a.x + a.y * a.y);
    105};
    106
    107
    108/**
    109 * Returns the angle from the origin to a coordinate.
    110 * @param {!goog.math.Coordinate} a A Coordinate.
    111 * @return {number} The angle, in degrees, clockwise from the positive X
    112 * axis to {@code a}.
    113 */
    114goog.math.Coordinate.azimuth = function(a) {
    115 return goog.math.angle(0, 0, a.x, a.y);
    116};
    117
    118
    119/**
    120 * Returns the squared distance between two coordinates. Squared distances can
    121 * be used for comparisons when the actual value is not required.
    122 *
    123 * Performance note: eliminating the square root is an optimization often used
    124 * in lower-level languages, but the speed difference is not nearly as
    125 * pronounced in JavaScript (only a few percent.)
    126 *
    127 * @param {!goog.math.Coordinate} a A Coordinate.
    128 * @param {!goog.math.Coordinate} b A Coordinate.
    129 * @return {number} The squared distance between {@code a} and {@code b}.
    130 */
    131goog.math.Coordinate.squaredDistance = function(a, b) {
    132 var dx = a.x - b.x;
    133 var dy = a.y - b.y;
    134 return dx * dx + dy * dy;
    135};
    136
    137
    138/**
    139 * Returns the difference between two coordinates as a new
    140 * goog.math.Coordinate.
    141 * @param {!goog.math.Coordinate} a A Coordinate.
    142 * @param {!goog.math.Coordinate} b A Coordinate.
    143 * @return {!goog.math.Coordinate} A Coordinate representing the difference
    144 * between {@code a} and {@code b}.
    145 */
    146goog.math.Coordinate.difference = function(a, b) {
    147 return new goog.math.Coordinate(a.x - b.x, a.y - b.y);
    148};
    149
    150
    151/**
    152 * Returns the sum of two coordinates as a new goog.math.Coordinate.
    153 * @param {!goog.math.Coordinate} a A Coordinate.
    154 * @param {!goog.math.Coordinate} b A Coordinate.
    155 * @return {!goog.math.Coordinate} A Coordinate representing the sum of the two
    156 * coordinates.
    157 */
    158goog.math.Coordinate.sum = function(a, b) {
    159 return new goog.math.Coordinate(a.x + b.x, a.y + b.y);
    160};
    161
    162
    163/**
    164 * Rounds the x and y fields to the next larger integer values.
    165 * @return {!goog.math.Coordinate} This coordinate with ceil'd fields.
    166 */
    167goog.math.Coordinate.prototype.ceil = function() {
    168 this.x = Math.ceil(this.x);
    169 this.y = Math.ceil(this.y);
    170 return this;
    171};
    172
    173
    174/**
    175 * Rounds the x and y fields to the next smaller integer values.
    176 * @return {!goog.math.Coordinate} This coordinate with floored fields.
    177 */
    178goog.math.Coordinate.prototype.floor = function() {
    179 this.x = Math.floor(this.x);
    180 this.y = Math.floor(this.y);
    181 return this;
    182};
    183
    184
    185/**
    186 * Rounds the x and y fields to the nearest integer values.
    187 * @return {!goog.math.Coordinate} This coordinate with rounded fields.
    188 */
    189goog.math.Coordinate.prototype.round = function() {
    190 this.x = Math.round(this.x);
    191 this.y = Math.round(this.y);
    192 return this;
    193};
    194
    195
    196/**
    197 * Translates this box by the given offsets. If a {@code goog.math.Coordinate}
    198 * is given, then the x and y values are translated by the coordinate's x and y.
    199 * Otherwise, x and y are translated by {@code tx} and {@code opt_ty}
    200 * respectively.
    201 * @param {number|goog.math.Coordinate} tx The value to translate x by or the
    202 * the coordinate to translate this coordinate by.
    203 * @param {number=} opt_ty The value to translate y by.
    204 * @return {!goog.math.Coordinate} This coordinate after translating.
    205 */
    206goog.math.Coordinate.prototype.translate = function(tx, opt_ty) {
    207 if (tx instanceof goog.math.Coordinate) {
    208 this.x += tx.x;
    209 this.y += tx.y;
    210 } else {
    211 this.x += tx;
    212 if (goog.isNumber(opt_ty)) {
    213 this.y += opt_ty;
    214 }
    215 }
    216 return this;
    217};
    218
    219
    220/**
    221 * Scales this coordinate by the given scale factors. The x and y values are
    222 * scaled by {@code sx} and {@code opt_sy} respectively. If {@code opt_sy}
    223 * is not given, then {@code sx} is used for both x and y.
    224 * @param {number} sx The scale factor to use for the x dimension.
    225 * @param {number=} opt_sy The scale factor to use for the y dimension.
    226 * @return {!goog.math.Coordinate} This coordinate after scaling.
    227 */
    228goog.math.Coordinate.prototype.scale = function(sx, opt_sy) {
    229 var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
    230 this.x *= sx;
    231 this.y *= sy;
    232 return this;
    233};
    234
    235
    236/**
    237 * Rotates this coordinate clockwise about the origin (or, optionally, the given
    238 * center) by the given angle, in radians.
    239 * @param {number} radians The angle by which to rotate this coordinate
    240 * clockwise about the given center, in radians.
    241 * @param {!goog.math.Coordinate=} opt_center The center of rotation. Defaults
    242 * to (0, 0) if not given.
    243 */
    244goog.math.Coordinate.prototype.rotateRadians = function(radians, opt_center) {
    245 var center = opt_center || new goog.math.Coordinate(0, 0);
    246
    247 var x = this.x;
    248 var y = this.y;
    249 var cos = Math.cos(radians);
    250 var sin = Math.sin(radians);
    251
    252 this.x = (x - center.x) * cos - (y - center.y) * sin + center.x;
    253 this.y = (x - center.x) * sin + (y - center.y) * cos + center.y;
    254};
    255
    256
    257/**
    258 * Rotates this coordinate clockwise about the origin (or, optionally, the given
    259 * center) by the given angle, in degrees.
    260 * @param {number} degrees The angle by which to rotate this coordinate
    261 * clockwise about the given center, in degrees.
    262 * @param {!goog.math.Coordinate=} opt_center The center of rotation. Defaults
    263 * to (0, 0) if not given.
    264 */
    265goog.math.Coordinate.prototype.rotateDegrees = function(degrees, opt_center) {
    266 this.rotateRadians(goog.math.toRadians(degrees), opt_center);
    267};
    \ No newline at end of file +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 index 9a911cd..e4dc5c4 100644 --- a/docs/source/lib/goog/math/math.js.src.html +++ b/docs/source/lib/goog/math/math.js.src.html @@ -1 +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.
    221 */
    222goog.math.sign = function(x) {
    223 return x == 0 ? 0 : (x < 0 ? -1 : 1);
    224};
    225
    226
    227/**
    228 * JavaScript implementation of Longest Common Subsequence problem.
    229 * http://en.wikipedia.org/wiki/Longest_common_subsequence
    230 *
    231 * Returns the longest possible array that is subarray of both of given arrays.
    232 *
    233 * @param {Array.<Object>} array1 First array of objects.
    234 * @param {Array.<Object>} array2 Second array of objects.
    235 * @param {Function=} opt_compareFn Function that acts as a custom comparator
    236 * for the array ojects. Function should return true if objects are equal,
    237 * otherwise false.
    238 * @param {Function=} opt_collectorFn Function used to decide what to return
    239 * as a result subsequence. It accepts 2 arguments: index of common element
    240 * in the first array and index in the second. The default function returns
    241 * element from the first array.
    242 * @return {!Array.<Object>} A list of objects that are common to both arrays
    243 * such that there is no common subsequence with size greater than the
    244 * length of the list.
    245 */
    246goog.math.longestCommonSubsequence = function(
    247 array1, array2, opt_compareFn, opt_collectorFn) {
    248
    249 var compare = opt_compareFn || function(a, b) {
    250 return a == b;
    251 };
    252
    253 var collect = opt_collectorFn || function(i1, i2) {
    254 return array1[i1];
    255 };
    256
    257 var length1 = array1.length;
    258 var length2 = array2.length;
    259
    260 var arr = [];
    261 for (var i = 0; i < length1 + 1; i++) {
    262 arr[i] = [];
    263 arr[i][0] = 0;
    264 }
    265
    266 for (var j = 0; j < length2 + 1; j++) {
    267 arr[0][j] = 0;
    268 }
    269
    270 for (i = 1; i <= length1; i++) {
    271 for (j = 1; j <= length2; j++) {
    272 if (compare(array1[i - 1], array2[j - 1])) {
    273 arr[i][j] = arr[i - 1][j - 1] + 1;
    274 } else {
    275 arr[i][j] = Math.max(arr[i - 1][j], arr[i][j - 1]);
    276 }
    277 }
    278 }
    279
    280 // Backtracking
    281 var result = [];
    282 var i = length1, j = length2;
    283 while (i > 0 && j > 0) {
    284 if (compare(array1[i - 1], array2[j - 1])) {
    285 result.unshift(collect(i - 1, j - 1));
    286 i--;
    287 j--;
    288 } else {
    289 if (arr[i - 1][j] > arr[i][j - 1]) {
    290 i--;
    291 } else {
    292 j--;
    293 }
    294 }
    295 }
    296
    297 return result;
    298};
    299
    300
    301/**
    302 * Returns the sum of the arguments.
    303 * @param {...number} var_args Numbers to add.
    304 * @return {number} The sum of the arguments (0 if no arguments were provided,
    305 * {@code NaN} if any of the arguments is not a valid number).
    306 */
    307goog.math.sum = function(var_args) {
    308 return /** @type {number} */ (goog.array.reduce(arguments,
    309 function(sum, value) {
    310 return sum + value;
    311 }, 0));
    312};
    313
    314
    315/**
    316 * Returns the arithmetic mean of the arguments.
    317 * @param {...number} var_args Numbers to average.
    318 * @return {number} The average of the arguments ({@code NaN} if no arguments
    319 * were provided or any of the arguments is not a valid number).
    320 */
    321goog.math.average = function(var_args) {
    322 return goog.math.sum.apply(null, arguments) / arguments.length;
    323};
    324
    325
    326/**
    327 * Returns the unbiased sample variance of the arguments. For a definition,
    328 * see e.g. http://en.wikipedia.org/wiki/Variance
    329 * @param {...number} var_args Number samples to analyze.
    330 * @return {number} The unbiased sample variance of the arguments (0 if fewer
    331 * than two samples were provided, or {@code NaN} if any of the samples is
    332 * not a valid number).
    333 */
    334goog.math.sampleVariance = function(var_args) {
    335 var sampleSize = arguments.length;
    336 if (sampleSize < 2) {
    337 return 0;
    338 }
    339
    340 var mean = goog.math.average.apply(null, arguments);
    341 var variance = goog.math.sum.apply(null, goog.array.map(arguments,
    342 function(val) {
    343 return Math.pow(val - mean, 2);
    344 })) / (sampleSize - 1);
    345
    346 return variance;
    347};
    348
    349
    350/**
    351 * Returns the sample standard deviation of the arguments. For a definition of
    352 * sample standard deviation, see e.g.
    353 * http://en.wikipedia.org/wiki/Standard_deviation
    354 * @param {...number} var_args Number samples to analyze.
    355 * @return {number} The sample standard deviation of the arguments (0 if fewer
    356 * than two samples were provided, or {@code NaN} if any of the samples is
    357 * not a valid number).
    358 */
    359goog.math.standardDeviation = function(var_args) {
    360 return Math.sqrt(goog.math.sampleVariance.apply(null, arguments));
    361};
    362
    363
    364/**
    365 * Returns whether the supplied number represents an integer, i.e. that is has
    366 * no fractional component. No range-checking is performed on the number.
    367 * @param {number} num The number to test.
    368 * @return {boolean} Whether {@code num} is an integer.
    369 */
    370goog.math.isInt = function(num) {
    371 return isFinite(num) && num % 1 == 0;
    372};
    373
    374
    375/**
    376 * Returns whether the supplied number is finite and not NaN.
    377 * @param {number} num The number to test.
    378 * @return {boolean} Whether {@code num} is a finite number.
    379 */
    380goog.math.isFiniteNumber = function(num) {
    381 return isFinite(num) && !isNaN(num);
    382};
    383
    384
    385/**
    386 * Returns the precise value of floor(log10(num)).
    387 * Simpler implementations didn't work because of floating point rounding
    388 * errors. For example
    389 * <ul>
    390 * <li>Math.floor(Math.log(num) / Math.LN10) is off by one for num == 1e+3.
    391 * <li>Math.floor(Math.log(num) * Math.LOG10E) is off by one for num == 1e+15.
    392 * <li>Math.floor(Math.log10(num)) is off by one for num == 1e+15 - 1.
    393 * </ul>
    394 * @param {number} num A floating point number.
    395 * @return {number} Its logarithm to base 10 rounded down to the nearest
    396 * integer if num > 0. -Infinity if num == 0. NaN if num < 0.
    397 */
    398goog.math.log10Floor = function(num) {
    399 if (num > 0) {
    400 var x = Math.round(Math.log(num) * Math.LOG10E);
    401 return x - (parseFloat('1e' + x) > num);
    402 }
    403 return num == 0 ? -Infinity : NaN;
    404};
    405
    406
    407/**
    408 * A tweaked variant of {@code Math.floor} which tolerates if the passed number
    409 * is infinitesimally smaller than the closest integer. It often happens with
    410 * the results of floating point calculations because of the finite precision
    411 * of the intermediate results. For example {@code Math.floor(Math.log(1000) /
    412 * Math.LN10) == 2}, not 3 as one would expect.
    413 * @param {number} num A number.
    414 * @param {number=} opt_epsilon An infinitesimally small positive number, the
    415 * rounding error to tolerate.
    416 * @return {number} The largest integer less than or equal to {@code num}.
    417 */
    418goog.math.safeFloor = function(num, opt_epsilon) {
    419 goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
    420 return Math.floor(num + (opt_epsilon || 2e-15));
    421};
    422
    423
    424/**
    425 * A tweaked variant of {@code Math.ceil}. See {@code goog.math.safeFloor} for
    426 * details.
    427 * @param {number} num A number.
    428 * @param {number=} opt_epsilon An infinitesimally small positive number, the
    429 * rounding error to tolerate.
    430 * @return {number} The smallest integer greater than or equal to {@code num}.
    431 */
    432goog.math.safeCeil = function(num, opt_epsilon) {
    433 goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
    434 return Math.ceil(num - (opt_epsilon || 2e-15));
    435};
    \ No newline at end of file +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);
    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 index 4427f71..0ee35fb 100644 --- a/docs/source/lib/goog/math/rect.js.src.html +++ b/docs/source/lib/goog/math/rect.js.src.html @@ -1 +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 * @constructor
    34 */
    35goog.math.Rect = function(x, y, w, h) {
    36 /** @type {number} */
    37 this.left = x;
    38
    39 /** @type {number} */
    40 this.top = y;
    41
    42 /** @type {number} */
    43 this.width = w;
    44
    45 /** @type {number} */
    46 this.height = h;
    47};
    48
    49
    50/**
    51 * @return {!goog.math.Rect} A new copy of this Rectangle.
    52 */
    53goog.math.Rect.prototype.clone = function() {
    54 return new goog.math.Rect(this.left, this.top, this.width, this.height);
    55};
    56
    57
    58/**
    59 * Returns a new Box object with the same position and dimensions as this
    60 * rectangle.
    61 * @return {!goog.math.Box} A new Box representation of this Rectangle.
    62 */
    63goog.math.Rect.prototype.toBox = function() {
    64 var right = this.left + this.width;
    65 var bottom = this.top + this.height;
    66 return new goog.math.Box(this.top,
    67 right,
    68 bottom,
    69 this.left);
    70};
    71
    72
    73/**
    74 * Creates a new Rect object with the same position and dimensions as a given
    75 * Box. Note that this is only the inverse of toBox if left/top are defined.
    76 * @param {goog.math.Box} box A box.
    77 * @return {!goog.math.Rect} A new Rect initialized with the box's position
    78 * and size.
    79 */
    80goog.math.Rect.createFromBox = function(box) {
    81 return new goog.math.Rect(box.left, box.top,
    82 box.right - box.left, box.bottom - box.top);
    83};
    84
    85
    86if (goog.DEBUG) {
    87 /**
    88 * Returns a nice string representing size and dimensions of rectangle.
    89 * @return {string} In the form (50, 73 - 75w x 25h).
    90 * @override
    91 */
    92 goog.math.Rect.prototype.toString = function() {
    93 return '(' + this.left + ', ' + this.top + ' - ' + this.width + 'w x ' +
    94 this.height + 'h)';
    95 };
    96}
    97
    98
    99/**
    100 * Compares rectangles for equality.
    101 * @param {goog.math.Rect} a A Rectangle.
    102 * @param {goog.math.Rect} b A Rectangle.
    103 * @return {boolean} True iff the rectangles have the same left, top, width,
    104 * and height, or if both are null.
    105 */
    106goog.math.Rect.equals = function(a, b) {
    107 if (a == b) {
    108 return true;
    109 }
    110 if (!a || !b) {
    111 return false;
    112 }
    113 return a.left == b.left && a.width == b.width &&
    114 a.top == b.top && a.height == b.height;
    115};
    116
    117
    118/**
    119 * Computes the intersection of this rectangle and the rectangle parameter. If
    120 * there is no intersection, returns false and leaves this rectangle as is.
    121 * @param {goog.math.Rect} rect A Rectangle.
    122 * @return {boolean} True iff this rectangle intersects with the parameter.
    123 */
    124goog.math.Rect.prototype.intersection = function(rect) {
    125 var x0 = Math.max(this.left, rect.left);
    126 var x1 = Math.min(this.left + this.width, rect.left + rect.width);
    127
    128 if (x0 <= x1) {
    129 var y0 = Math.max(this.top, rect.top);
    130 var y1 = Math.min(this.top + this.height, rect.top + rect.height);
    131
    132 if (y0 <= y1) {
    133 this.left = x0;
    134 this.top = y0;
    135 this.width = x1 - x0;
    136 this.height = y1 - y0;
    137
    138 return true;
    139 }
    140 }
    141 return false;
    142};
    143
    144
    145/**
    146 * Returns the intersection of two rectangles. Two rectangles intersect if they
    147 * touch at all, for example, two zero width and height rectangles would
    148 * intersect if they had the same top and left.
    149 * @param {goog.math.Rect} a A Rectangle.
    150 * @param {goog.math.Rect} b A Rectangle.
    151 * @return {goog.math.Rect} A new intersection rect (even if width and height
    152 * are 0), or null if there is no intersection.
    153 */
    154goog.math.Rect.intersection = function(a, b) {
    155 // There is no nice way to do intersection via a clone, because any such
    156 // clone might be unnecessary if this function returns null. So, we duplicate
    157 // code from above.
    158
    159 var x0 = Math.max(a.left, b.left);
    160 var x1 = Math.min(a.left + a.width, b.left + b.width);
    161
    162 if (x0 <= x1) {
    163 var y0 = Math.max(a.top, b.top);
    164 var y1 = Math.min(a.top + a.height, b.top + b.height);
    165
    166 if (y0 <= y1) {
    167 return new goog.math.Rect(x0, y0, x1 - x0, y1 - y0);
    168 }
    169 }
    170 return null;
    171};
    172
    173
    174/**
    175 * Returns whether two rectangles intersect. Two rectangles intersect if they
    176 * touch at all, for example, two zero width and height rectangles would
    177 * intersect if they had the same top and left.
    178 * @param {goog.math.Rect} a A Rectangle.
    179 * @param {goog.math.Rect} b A Rectangle.
    180 * @return {boolean} Whether a and b intersect.
    181 */
    182goog.math.Rect.intersects = function(a, b) {
    183 return (a.left <= b.left + b.width && b.left <= a.left + a.width &&
    184 a.top <= b.top + b.height && b.top <= a.top + a.height);
    185};
    186
    187
    188/**
    189 * Returns whether a rectangle intersects this rectangle.
    190 * @param {goog.math.Rect} rect A rectangle.
    191 * @return {boolean} Whether rect intersects this rectangle.
    192 */
    193goog.math.Rect.prototype.intersects = function(rect) {
    194 return goog.math.Rect.intersects(this, rect);
    195};
    196
    197
    198/**
    199 * Computes the difference regions between two rectangles. The return value is
    200 * an array of 0 to 4 rectangles defining the remaining regions of the first
    201 * rectangle after the second has been subtracted.
    202 * @param {goog.math.Rect} a A Rectangle.
    203 * @param {goog.math.Rect} b A Rectangle.
    204 * @return {!Array.<!goog.math.Rect>} An array with 0 to 4 rectangles which
    205 * together define the difference area of rectangle a minus rectangle b.
    206 */
    207goog.math.Rect.difference = function(a, b) {
    208 var intersection = goog.math.Rect.intersection(a, b);
    209 if (!intersection || !intersection.height || !intersection.width) {
    210 return [a.clone()];
    211 }
    212
    213 var result = [];
    214
    215 var top = a.top;
    216 var height = a.height;
    217
    218 var ar = a.left + a.width;
    219 var ab = a.top + a.height;
    220
    221 var br = b.left + b.width;
    222 var bb = b.top + b.height;
    223
    224 // Subtract off any area on top where A extends past B
    225 if (b.top > a.top) {
    226 result.push(new goog.math.Rect(a.left, a.top, a.width, b.top - a.top));
    227 top = b.top;
    228 // If we're moving the top down, we also need to subtract the height diff.
    229 height -= b.top - a.top;
    230 }
    231 // Subtract off any area on bottom where A extends past B
    232 if (bb < ab) {
    233 result.push(new goog.math.Rect(a.left, bb, a.width, ab - bb));
    234 height = bb - top;
    235 }
    236 // Subtract any area on left where A extends past B
    237 if (b.left > a.left) {
    238 result.push(new goog.math.Rect(a.left, top, b.left - a.left, height));
    239 }
    240 // Subtract any area on right where A extends past B
    241 if (br < ar) {
    242 result.push(new goog.math.Rect(br, top, ar - br, height));
    243 }
    244
    245 return result;
    246};
    247
    248
    249/**
    250 * Computes the difference regions between this rectangle and {@code rect}. The
    251 * return value is an array of 0 to 4 rectangles defining the remaining regions
    252 * of this rectangle after the other has been subtracted.
    253 * @param {goog.math.Rect} rect A Rectangle.
    254 * @return {!Array.<!goog.math.Rect>} An array with 0 to 4 rectangles which
    255 * together define the difference area of rectangle a minus rectangle b.
    256 */
    257goog.math.Rect.prototype.difference = function(rect) {
    258 return goog.math.Rect.difference(this, rect);
    259};
    260
    261
    262/**
    263 * Expand this rectangle to also include the area of the given rectangle.
    264 * @param {goog.math.Rect} rect The other rectangle.
    265 */
    266goog.math.Rect.prototype.boundingRect = function(rect) {
    267 // We compute right and bottom before we change left and top below.
    268 var right = Math.max(this.left + this.width, rect.left + rect.width);
    269 var bottom = Math.max(this.top + this.height, rect.top + rect.height);
    270
    271 this.left = Math.min(this.left, rect.left);
    272 this.top = Math.min(this.top, rect.top);
    273
    274 this.width = right - this.left;
    275 this.height = bottom - this.top;
    276};
    277
    278
    279/**
    280 * Returns a new rectangle which completely contains both input rectangles.
    281 * @param {goog.math.Rect} a A rectangle.
    282 * @param {goog.math.Rect} b A rectangle.
    283 * @return {goog.math.Rect} A new bounding rect, or null if either rect is
    284 * null.
    285 */
    286goog.math.Rect.boundingRect = function(a, b) {
    287 if (!a || !b) {
    288 return null;
    289 }
    290
    291 var clone = a.clone();
    292 clone.boundingRect(b);
    293
    294 return clone;
    295};
    296
    297
    298/**
    299 * Tests whether this rectangle entirely contains another rectangle or
    300 * coordinate.
    301 *
    302 * @param {goog.math.Rect|goog.math.Coordinate} another The rectangle or
    303 * coordinate to test for containment.
    304 * @return {boolean} Whether this rectangle contains given rectangle or
    305 * coordinate.
    306 */
    307goog.math.Rect.prototype.contains = function(another) {
    308 if (another instanceof goog.math.Rect) {
    309 return this.left <= another.left &&
    310 this.left + this.width >= another.left + another.width &&
    311 this.top <= another.top &&
    312 this.top + this.height >= another.top + another.height;
    313 } else { // (another instanceof goog.math.Coordinate)
    314 return another.x >= this.left &&
    315 another.x <= this.left + this.width &&
    316 another.y >= this.top &&
    317 another.y <= this.top + this.height;
    318 }
    319};
    320
    321
    322/**
    323 * @param {!goog.math.Coordinate} point A coordinate.
    324 * @return {number} The squared distance between the point and the closest
    325 * point inside the rectangle. Returns 0 if the point is inside the
    326 * rectangle.
    327 */
    328goog.math.Rect.prototype.squaredDistance = function(point) {
    329 var dx = point.x < this.left ?
    330 this.left - point.x : Math.max(point.x - (this.left + this.width), 0);
    331 var dy = point.y < this.top ?
    332 this.top - point.y : Math.max(point.y - (this.top + this.height), 0);
    333 return dx * dx + dy * dy;
    334};
    335
    336
    337/**
    338 * @param {!goog.math.Coordinate} point A coordinate.
    339 * @return {number} The distance between the point and the closest point
    340 * inside the rectangle. Returns 0 if the point is inside the rectangle.
    341 */
    342goog.math.Rect.prototype.distance = function(point) {
    343 return Math.sqrt(this.squaredDistance(point));
    344};
    345
    346
    347/**
    348 * @return {!goog.math.Size} The size of this rectangle.
    349 */
    350goog.math.Rect.prototype.getSize = function() {
    351 return new goog.math.Size(this.width, this.height);
    352};
    353
    354
    355/**
    356 * @return {!goog.math.Coordinate} A new coordinate for the top-left corner of
    357 * the rectangle.
    358 */
    359goog.math.Rect.prototype.getTopLeft = function() {
    360 return new goog.math.Coordinate(this.left, this.top);
    361};
    362
    363
    364/**
    365 * @return {!goog.math.Coordinate} A new coordinate for the center of the
    366 * rectangle.
    367 */
    368goog.math.Rect.prototype.getCenter = function() {
    369 return new goog.math.Coordinate(
    370 this.left + this.width / 2, this.top + this.height / 2);
    371};
    372
    373
    374/**
    375 * @return {!goog.math.Coordinate} A new coordinate for the bottom-right corner
    376 * of the rectangle.
    377 */
    378goog.math.Rect.prototype.getBottomRight = function() {
    379 return new goog.math.Coordinate(
    380 this.left + this.width, this.top + this.height);
    381};
    382
    383
    384/**
    385 * Rounds the fields to the next larger integer values.
    386 * @return {!goog.math.Rect} This rectangle with ceil'd fields.
    387 */
    388goog.math.Rect.prototype.ceil = function() {
    389 this.left = Math.ceil(this.left);
    390 this.top = Math.ceil(this.top);
    391 this.width = Math.ceil(this.width);
    392 this.height = Math.ceil(this.height);
    393 return this;
    394};
    395
    396
    397/**
    398 * Rounds the fields to the next smaller integer values.
    399 * @return {!goog.math.Rect} This rectangle with floored fields.
    400 */
    401goog.math.Rect.prototype.floor = function() {
    402 this.left = Math.floor(this.left);
    403 this.top = Math.floor(this.top);
    404 this.width = Math.floor(this.width);
    405 this.height = Math.floor(this.height);
    406 return this;
    407};
    408
    409
    410/**
    411 * Rounds the fields to nearest integer values.
    412 * @return {!goog.math.Rect} This rectangle with rounded fields.
    413 */
    414goog.math.Rect.prototype.round = function() {
    415 this.left = Math.round(this.left);
    416 this.top = Math.round(this.top);
    417 this.width = Math.round(this.width);
    418 this.height = Math.round(this.height);
    419 return this;
    420};
    421
    422
    423/**
    424 * Translates this rectangle by the given offsets. If a
    425 * {@code goog.math.Coordinate} is given, then the left and top values are
    426 * translated by the coordinate's x and y values. Otherwise, top and left are
    427 * translated by {@code tx} and {@code opt_ty} respectively.
    428 * @param {number|goog.math.Coordinate} tx The value to translate left by or the
    429 * the coordinate to translate this rect by.
    430 * @param {number=} opt_ty The value to translate top by.
    431 * @return {!goog.math.Rect} This rectangle after translating.
    432 */
    433goog.math.Rect.prototype.translate = function(tx, opt_ty) {
    434 if (tx instanceof goog.math.Coordinate) {
    435 this.left += tx.x;
    436 this.top += tx.y;
    437 } else {
    438 this.left += tx;
    439 if (goog.isNumber(opt_ty)) {
    440 this.top += opt_ty;
    441 }
    442 }
    443 return this;
    444};
    445
    446
    447/**
    448 * Scales this rectangle by the given scale factors. The left and width values
    449 * are scaled by {@code sx} and the top and height values are scaled by
    450 * {@code opt_sy}. If {@code opt_sy} is not given, then all fields are scaled
    451 * by {@code sx}.
    452 * @param {number} sx The scale factor to use for the x dimension.
    453 * @param {number=} opt_sy The scale factor to use for the y dimension.
    454 * @return {!goog.math.Rect} This rectangle after scaling.
    455 */
    456goog.math.Rect.prototype.scale = function(sx, opt_sy) {
    457 var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
    458 this.left *= sx;
    459 this.width *= sx;
    460 this.top *= sy;
    461 this.height *= sy;
    462 return this;
    463};
    \ No newline at end of file +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 index d26bc98..d35a7f5 100644 --- a/docs/source/lib/goog/math/size.js.src.html +++ b/docs/source/lib/goog/math/size.js.src.html @@ -1 +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 */
    18
    19
    20goog.provide('goog.math.Size');
    21
    22
    23
    24/**
    25 * Class for representing sizes consisting of a width and height. Undefined
    26 * width and height support is deprecated and results in compiler warning.
    27 * @param {number} width Width.
    28 * @param {number} height Height.
    29 * @constructor
    30 */
    31goog.math.Size = function(width, height) {
    32 /**
    33 * Width
    34 * @type {number}
    35 */
    36 this.width = width;
    37
    38 /**
    39 * Height
    40 * @type {number}
    41 */
    42 this.height = height;
    43};
    44
    45
    46/**
    47 * Compares sizes for equality.
    48 * @param {goog.math.Size} a A Size.
    49 * @param {goog.math.Size} b A Size.
    50 * @return {boolean} True iff the sizes have equal widths and equal
    51 * heights, or if both are null.
    52 */
    53goog.math.Size.equals = function(a, b) {
    54 if (a == b) {
    55 return true;
    56 }
    57 if (!a || !b) {
    58 return false;
    59 }
    60 return a.width == b.width && a.height == b.height;
    61};
    62
    63
    64/**
    65 * @return {!goog.math.Size} A new copy of the Size.
    66 */
    67goog.math.Size.prototype.clone = function() {
    68 return new goog.math.Size(this.width, this.height);
    69};
    70
    71
    72if (goog.DEBUG) {
    73 /**
    74 * Returns a nice string representing size.
    75 * @return {string} In the form (50 x 73).
    76 * @override
    77 */
    78 goog.math.Size.prototype.toString = function() {
    79 return '(' + this.width + ' x ' + this.height + ')';
    80 };
    81}
    82
    83
    84/**
    85 * @return {number} The longer of the two dimensions in the size.
    86 */
    87goog.math.Size.prototype.getLongest = function() {
    88 return Math.max(this.width, this.height);
    89};
    90
    91
    92/**
    93 * @return {number} The shorter of the two dimensions in the size.
    94 */
    95goog.math.Size.prototype.getShortest = function() {
    96 return Math.min(this.width, this.height);
    97};
    98
    99
    100/**
    101 * @return {number} The area of the size (width * height).
    102 */
    103goog.math.Size.prototype.area = function() {
    104 return this.width * this.height;
    105};
    106
    107
    108/**
    109 * @return {number} The perimeter of the size (width + height) * 2.
    110 */
    111goog.math.Size.prototype.perimeter = function() {
    112 return (this.width + this.height) * 2;
    113};
    114
    115
    116/**
    117 * @return {number} The ratio of the size's width to its height.
    118 */
    119goog.math.Size.prototype.aspectRatio = function() {
    120 return this.width / this.height;
    121};
    122
    123
    124/**
    125 * @return {boolean} True if the size has zero area, false if both dimensions
    126 * are non-zero numbers.
    127 */
    128goog.math.Size.prototype.isEmpty = function() {
    129 return !this.area();
    130};
    131
    132
    133/**
    134 * Clamps the width and height parameters upward to integer values.
    135 * @return {!goog.math.Size} This size with ceil'd components.
    136 */
    137goog.math.Size.prototype.ceil = function() {
    138 this.width = Math.ceil(this.width);
    139 this.height = Math.ceil(this.height);
    140 return this;
    141};
    142
    143
    144/**
    145 * @param {!goog.math.Size} target The target size.
    146 * @return {boolean} True if this Size is the same size or smaller than the
    147 * target size in both dimensions.
    148 */
    149goog.math.Size.prototype.fitsInside = function(target) {
    150 return this.width <= target.width && this.height <= target.height;
    151};
    152
    153
    154/**
    155 * Clamps the width and height parameters downward to integer values.
    156 * @return {!goog.math.Size} This size with floored components.
    157 */
    158goog.math.Size.prototype.floor = function() {
    159 this.width = Math.floor(this.width);
    160 this.height = Math.floor(this.height);
    161 return this;
    162};
    163
    164
    165/**
    166 * Rounds the width and height parameters to integer values.
    167 * @return {!goog.math.Size} This size with rounded components.
    168 */
    169goog.math.Size.prototype.round = function() {
    170 this.width = Math.round(this.width);
    171 this.height = Math.round(this.height);
    172 return this;
    173};
    174
    175
    176/**
    177 * Scales this size by the given scale factors. The width and height are scaled
    178 * by {@code sx} and {@code opt_sy} respectively. If {@code opt_sy} is not
    179 * given, then {@code sx} is used for both the width and height.
    180 * @param {number} sx The scale factor to use for the width.
    181 * @param {number=} opt_sy The scale factor to use for the height.
    182 * @return {!goog.math.Size} This Size object after scaling.
    183 */
    184goog.math.Size.prototype.scale = function(sx, opt_sy) {
    185 var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
    186 this.width *= sx;
    187 this.height *= sy;
    188 return this;
    189};
    190
    191
    192/**
    193 * Uniformly scales the size to fit inside the dimensions of a given size. The
    194 * original aspect ratio will be preserved.
    195 *
    196 * This function assumes that both Sizes contain strictly positive dimensions.
    197 * @param {!goog.math.Size} target The target size.
    198 * @return {!goog.math.Size} This Size object, after optional scaling.
    199 */
    200goog.math.Size.prototype.scaleToFit = function(target) {
    201 var s = this.aspectRatio() > target.aspectRatio() ?
    202 target.width / this.width :
    203 target.height / this.height;
    204
    205 return this.scale(s);
    206};
    \ No newline at end of file +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 71eee01..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
    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 +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 index d04d146..39baf98 100644 --- a/docs/source/lib/goog/net/xhrlike.js.src.html +++ b/docs/source/lib/goog/net/xhrlike.js.src.html @@ -1 +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 +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 8e86474..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');
    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(user): Due to the way JSCompiler works, this define *will not* strip
    45 * out the ActiveX probing code from binaries. To achieve this, use
    46 * {@code goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR} instead.
    47 * TODO(user): Collapse both defines.
    48 */
    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 +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 f022159..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/** @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 +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 7d341cf..7dfe649 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
    21goog.require('goog.array');
    22
    23
    24/**
    25 * Calls a function for each element in an object/map/hash.
    26 *
    27 * @param {Object.<K,V>} obj The object over which to iterate.
    28 * @param {function(this:T,V,?,Object.<K,V>):?} f The function to call
    29 * for every element. This function takes 3 arguments (the element, the
    30 * index and the object) and the return value is ignored.
    31 * @param {T=} opt_obj This is used as the 'this' object within f.
    32 * @template T,K,V
    33 */
    34goog.object.forEach = function(obj, f, opt_obj) {
    35 for (var key in obj) {
    36 f.call(opt_obj, obj[key], key, obj);
    37 }
    38};
    39
    40
    41/**
    42 * Calls a function for each element in an object/map/hash. If that call returns
    43 * true, adds the element to a new object.
    44 *
    45 * @param {Object.<K,V>} obj The object over which to iterate.
    46 * @param {function(this:T,V,?,Object.<K,V>):boolean} f The function to call
    47 * for every element. This
    48 * function takes 3 arguments (the element, the index and the object)
    49 * and should return a boolean. If the return value is true the
    50 * element is added to the result object. If it is false the
    51 * element is not included.
    52 * @param {T=} opt_obj This is used as the 'this' object within f.
    53 * @return {!Object.<K,V>} a new object in which only elements that passed the
    54 * test are present.
    55 * @template T,K,V
    56 */
    57goog.object.filter = function(obj, f, opt_obj) {
    58 var res = {};
    59 for (var key in obj) {
    60 if (f.call(opt_obj, obj[key], key, obj)) {
    61 res[key] = obj[key];
    62 }
    63 }
    64 return res;
    65};
    66
    67
    68/**
    69 * For every element in an object/map/hash calls a function and inserts the
    70 * result into a new object.
    71 *
    72 * @param {Object.<K,V>} obj The object over which to iterate.
    73 * @param {function(this:T,V,?,Object.<K,V>):R} f The function to call
    74 * for every element. This function
    75 * takes 3 arguments (the element, the index and the object)
    76 * and should return something. The result will be inserted
    77 * into a new object.
    78 * @param {T=} opt_obj This is used as the 'this' object within f.
    79 * @return {!Object.<K,R>} a new object with the results from f.
    80 * @template T,K,V,R
    81 */
    82goog.object.map = function(obj, f, opt_obj) {
    83 var res = {};
    84 for (var key in obj) {
    85 res[key] = f.call(opt_obj, obj[key], key, obj);
    86 }
    87 return res;
    88};
    89
    90
    91/**
    92 * Calls a function for each element in an object/map/hash. If any
    93 * call returns true, returns true (without checking the rest). If
    94 * all calls return false, returns false.
    95 *
    96 * @param {Object.<K,V>} obj The object to check.
    97 * @param {function(this:T,V,?,Object.<K,V>):boolean} f The function to
    98 * call for every element. This function
    99 * takes 3 arguments (the element, the index and the object) and should
    100 * return a boolean.
    101 * @param {T=} opt_obj This is used as the 'this' object within f.
    102 * @return {boolean} true if any element passes the test.
    103 * @template T,K,V
    104 */
    105goog.object.some = function(obj, f, opt_obj) {
    106 for (var key in obj) {
    107 if (f.call(opt_obj, obj[key], key, obj)) {
    108 return true;
    109 }
    110 }
    111 return false;
    112};
    113
    114
    115/**
    116 * Calls a function for each element in an object/map/hash. If
    117 * all calls return true, returns true. If any call returns false, returns
    118 * false at this point and does not continue to check the remaining elements.
    119 *
    120 * @param {Object.<K,V>} obj The object to check.
    121 * @param {?function(this:T,V,?,Object.<K,V>):boolean} f The function to
    122 * call for every element. This function
    123 * takes 3 arguments (the element, the index and the object) and should
    124 * return a boolean.
    125 * @param {T=} opt_obj This is used as the 'this' object within f.
    126 * @return {boolean} false if any element fails the test.
    127 * @template T,K,V
    128 */
    129goog.object.every = function(obj, f, opt_obj) {
    130 for (var key in obj) {
    131 if (!f.call(opt_obj, obj[key], key, obj)) {
    132 return false;
    133 }
    134 }
    135 return true;
    136};
    137
    138
    139/**
    140 * Returns the number of key-value pairs in the object map.
    141 *
    142 * @param {Object} obj The object for which to get the number of key-value
    143 * pairs.
    144 * @return {number} The number of key-value pairs in the object map.
    145 */
    146goog.object.getCount = function(obj) {
    147 // JS1.5 has __count__ but it has been deprecated so it raises a warning...
    148 // in other words do not use. Also __count__ only includes the fields on the
    149 // actual object and not in the prototype chain.
    150 var rv = 0;
    151 for (var key in obj) {
    152 rv++;
    153 }
    154 return rv;
    155};
    156
    157
    158/**
    159 * Returns one key from the object map, if any exists.
    160 * For map literals the returned key will be the first one in most of the
    161 * browsers (a know exception is Konqueror).
    162 *
    163 * @param {Object} obj The object to pick a key from.
    164 * @return {string|undefined} The key or undefined if the object is empty.
    165 */
    166goog.object.getAnyKey = function(obj) {
    167 for (var key in obj) {
    168 return key;
    169 }
    170};
    171
    172
    173/**
    174 * Returns one value from the object map, if any exists.
    175 * For map literals the returned value will be the first one in most of the
    176 * browsers (a know exception is Konqueror).
    177 *
    178 * @param {Object.<K,V>} obj The object to pick a value from.
    179 * @return {V|undefined} The value or undefined if the object is empty.
    180 * @template K,V
    181 */
    182goog.object.getAnyValue = function(obj) {
    183 for (var key in obj) {
    184 return obj[key];
    185 }
    186};
    187
    188
    189/**
    190 * Whether the object/hash/map contains the given object as a value.
    191 * An alias for goog.object.containsValue(obj, val).
    192 *
    193 * @param {Object.<K,V>} obj The object in which to look for val.
    194 * @param {V} val The object for which to check.
    195 * @return {boolean} true if val is present.
    196 * @template K,V
    197 */
    198goog.object.contains = function(obj, val) {
    199 return goog.object.containsValue(obj, val);
    200};
    201
    202
    203/**
    204 * Returns the values of the object/map/hash.
    205 *
    206 * @param {Object.<K,V>} obj The object from which to get the values.
    207 * @return {!Array.<V>} The values in the object/map/hash.
    208 * @template K,V
    209 */
    210goog.object.getValues = function(obj) {
    211 var res = [];
    212 var i = 0;
    213 for (var key in obj) {
    214 res[i++] = obj[key];
    215 }
    216 return res;
    217};
    218
    219
    220/**
    221 * Returns the keys of the object/map/hash.
    222 *
    223 * @param {Object} obj The object from which to get the keys.
    224 * @return {!Array.<string>} Array of property keys.
    225 */
    226goog.object.getKeys = function(obj) {
    227 var res = [];
    228 var i = 0;
    229 for (var key in obj) {
    230 res[i++] = key;
    231 }
    232 return res;
    233};
    234
    235
    236/**
    237 * Get a value from an object multiple levels deep. This is useful for
    238 * pulling values from deeply nested objects, such as JSON responses.
    239 * Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3)
    240 *
    241 * @param {!Object} obj An object to get the value from. Can be array-like.
    242 * @param {...(string|number|!Array.<number|string>)} var_args A number of keys
    243 * (as strings, or numbers, for array-like objects). Can also be
    244 * specified as a single array of keys.
    245 * @return {*} The resulting value. If, at any point, the value for a key
    246 * is undefined, returns undefined.
    247 */
    248goog.object.getValueByKeys = function(obj, var_args) {
    249 var isArrayLike = goog.isArrayLike(var_args);
    250 var keys = isArrayLike ? var_args : arguments;
    251
    252 // Start with the 2nd parameter for the variable parameters syntax.
    253 for (var i = isArrayLike ? 0 : 1; i < keys.length; i++) {
    254 obj = obj[keys[i]];
    255 if (!goog.isDef(obj)) {
    256 break;
    257 }
    258 }
    259
    260 return obj;
    261};
    262
    263
    264/**
    265 * Whether the object/map/hash contains the given key.
    266 *
    267 * @param {Object} obj The object in which to look for key.
    268 * @param {*} key The key for which to check.
    269 * @return {boolean} true If the map contains the key.
    270 */
    271goog.object.containsKey = function(obj, key) {
    272 return key in obj;
    273};
    274
    275
    276/**
    277 * Whether the object/map/hash contains the given value. This is O(n).
    278 *
    279 * @param {Object.<K,V>} obj The object in which to look for val.
    280 * @param {V} val The value for which to check.
    281 * @return {boolean} true If the map contains the value.
    282 * @template K,V
    283 */
    284goog.object.containsValue = function(obj, val) {
    285 for (var key in obj) {
    286 if (obj[key] == val) {
    287 return true;
    288 }
    289 }
    290 return false;
    291};
    292
    293
    294/**
    295 * Searches an object for an element that satisfies the given condition and
    296 * returns its key.
    297 * @param {Object.<K,V>} obj The object to search in.
    298 * @param {function(this:T,V,string,Object.<K,V>):boolean} f The
    299 * function to call for every element. Takes 3 arguments (the value,
    300 * the key and the object) and should return a boolean.
    301 * @param {T=} opt_this An optional "this" context for the function.
    302 * @return {string|undefined} The key of an element for which the function
    303 * returns true or undefined if no such element is found.
    304 * @template T,K,V
    305 */
    306goog.object.findKey = function(obj, f, opt_this) {
    307 for (var key in obj) {
    308 if (f.call(opt_this, obj[key], key, obj)) {
    309 return key;
    310 }
    311 }
    312 return undefined;
    313};
    314
    315
    316/**
    317 * Searches an object for an element that satisfies the given condition and
    318 * returns its value.
    319 * @param {Object.<K,V>} obj The object to search in.
    320 * @param {function(this:T,V,string,Object.<K,V>):boolean} f The function
    321 * to call for every element. Takes 3 arguments (the value, the key
    322 * and the object) and should return a boolean.
    323 * @param {T=} opt_this An optional "this" context for the function.
    324 * @return {V} The value of an element for which the function returns true or
    325 * undefined if no such element is found.
    326 * @template T,K,V
    327 */
    328goog.object.findValue = function(obj, f, opt_this) {
    329 var key = goog.object.findKey(obj, f, opt_this);
    330 return key && obj[key];
    331};
    332
    333
    334/**
    335 * Whether the object/map/hash is empty.
    336 *
    337 * @param {Object} obj The object to test.
    338 * @return {boolean} true if obj is empty.
    339 */
    340goog.object.isEmpty = function(obj) {
    341 for (var key in obj) {
    342 return false;
    343 }
    344 return true;
    345};
    346
    347
    348/**
    349 * Removes all key value pairs from the object/map/hash.
    350 *
    351 * @param {Object} obj The object to clear.
    352 */
    353goog.object.clear = function(obj) {
    354 for (var i in obj) {
    355 delete obj[i];
    356 }
    357};
    358
    359
    360/**
    361 * Removes a key-value pair based on the key.
    362 *
    363 * @param {Object} obj The object from which to remove the key.
    364 * @param {*} key The key to remove.
    365 * @return {boolean} Whether an element was removed.
    366 */
    367goog.object.remove = function(obj, key) {
    368 var rv;
    369 if ((rv = key in obj)) {
    370 delete obj[key];
    371 }
    372 return rv;
    373};
    374
    375
    376/**
    377 * Adds a key-value pair to the object. Throws an exception if the key is
    378 * already in use. Use set if you want to change an existing pair.
    379 *
    380 * @param {Object.<K,V>} obj The object to which to add the key-value pair.
    381 * @param {string} key The key to add.
    382 * @param {V} val The value to add.
    383 * @template K,V
    384 */
    385goog.object.add = function(obj, key, val) {
    386 if (key in obj) {
    387 throw Error('The object already contains the key "' + key + '"');
    388 }
    389 goog.object.set(obj, key, val);
    390};
    391
    392
    393/**
    394 * Returns the value for the given key.
    395 *
    396 * @param {Object.<K,V>} obj The object from which to get the value.
    397 * @param {string} key The key for which to get the value.
    398 * @param {R=} opt_val The value to return if no item is found for the given
    399 * key (default is undefined).
    400 * @return {V|R|undefined} The value for the given key.
    401 * @template K,V,R
    402 */
    403goog.object.get = function(obj, key, opt_val) {
    404 if (key in obj) {
    405 return obj[key];
    406 }
    407 return opt_val;
    408};
    409
    410
    411/**
    412 * Adds a key-value pair to the object/map/hash.
    413 *
    414 * @param {Object.<K,V>} obj The object to which to add the key-value pair.
    415 * @param {string} key The key to add.
    416 * @param {V} value The value to add.
    417 * @template K,V
    418 */
    419goog.object.set = function(obj, key, value) {
    420 obj[key] = value;
    421};
    422
    423
    424/**
    425 * Adds a key-value pair to the object/map/hash if it doesn't exist yet.
    426 *
    427 * @param {Object.<K,V>} obj The object to which to add the key-value pair.
    428 * @param {string} key The key to add.
    429 * @param {V} value The value to add if the key wasn't present.
    430 * @return {V} The value of the entry at the end of the function.
    431 * @template K,V
    432 */
    433goog.object.setIfUndefined = function(obj, key, value) {
    434 return key in obj ? obj[key] : (obj[key] = value);
    435};
    436
    437
    438/**
    439 * Compares two objects for equality using === on the values.
    440 *
    441 * @param {!Object.<K,V>} a
    442 * @param {!Object.<K,V>} b
    443 * @return {boolean}
    444 * @template K,V
    445 */
    446goog.object.equals = function(a, b) {
    447 if (!goog.array.equals(goog.object.getKeys(a), goog.object.getKeys(b))) {
    448 return false;
    449 }
    450 for (var k in a) {
    451 if (a[k] !== b[k]) {
    452 return false;
    453 }
    454 }
    455 return true;
    456};
    457
    458
    459/**
    460 * Does a flat clone of the object.
    461 *
    462 * @param {Object.<K,V>} obj Object to clone.
    463 * @return {!Object.<K,V>} Clone of the input object.
    464 * @template K,V
    465 */
    466goog.object.clone = function(obj) {
    467 // We cannot use the prototype trick because a lot of methods depend on where
    468 // the actual key is set.
    469
    470 var res = {};
    471 for (var key in obj) {
    472 res[key] = obj[key];
    473 }
    474 return res;
    475 // We could also use goog.mixin but I wanted this to be independent from that.
    476};
    477
    478
    479/**
    480 * Clones a value. The input may be an Object, Array, or basic type. Objects and
    481 * arrays will be cloned recursively.
    482 *
    483 * WARNINGS:
    484 * <code>goog.object.unsafeClone</code> does not detect reference loops. Objects
    485 * that refer to themselves will cause infinite recursion.
    486 *
    487 * <code>goog.object.unsafeClone</code> is unaware of unique identifiers, and
    488 * copies UIDs created by <code>getUid</code> into cloned results.
    489 *
    490 * @param {*} obj The value to clone.
    491 * @return {*} A clone of the input value.
    492 */
    493goog.object.unsafeClone = function(obj) {
    494 var type = goog.typeOf(obj);
    495 if (type == 'object' || type == 'array') {
    496 if (obj.clone) {
    497 return obj.clone();
    498 }
    499 var clone = type == 'array' ? [] : {};
    500 for (var key in obj) {
    501 clone[key] = goog.object.unsafeClone(obj[key]);
    502 }
    503 return clone;
    504 }
    505
    506 return obj;
    507};
    508
    509
    510/**
    511 * Returns a new object in which all the keys and values are interchanged
    512 * (keys become values and values become keys). If multiple keys map to the
    513 * same value, the chosen transposed value is implementation-dependent.
    514 *
    515 * @param {Object} obj The object to transpose.
    516 * @return {!Object} The transposed object.
    517 */
    518goog.object.transpose = function(obj) {
    519 var transposed = {};
    520 for (var key in obj) {
    521 transposed[obj[key]] = key;
    522 }
    523 return transposed;
    524};
    525
    526
    527/**
    528 * The names of the fields that are defined on Object.prototype.
    529 * @type {Array.<string>}
    530 * @private
    531 */
    532goog.object.PROTOTYPE_FIELDS_ = [
    533 'constructor',
    534 'hasOwnProperty',
    535 'isPrototypeOf',
    536 'propertyIsEnumerable',
    537 'toLocaleString',
    538 'toString',
    539 'valueOf'
    540];
    541
    542
    543/**
    544 * Extends an object with another object.
    545 * This operates 'in-place'; it does not create a new Object.
    546 *
    547 * Example:
    548 * var o = {};
    549 * goog.object.extend(o, {a: 0, b: 1});
    550 * o; // {a: 0, b: 1}
    551 * goog.object.extend(o, {b: 2, c: 3});
    552 * o; // {a: 0, b: 2, c: 3}
    553 *
    554 * @param {Object} target The object to modify. Existing properties will be
    555 * overwritten if they are also present in one of the objects in
    556 * {@code var_args}.
    557 * @param {...Object} var_args The objects from which values will be copied.
    558 */
    559goog.object.extend = function(target, var_args) {
    560 var key, source;
    561 for (var i = 1; i < arguments.length; i++) {
    562 source = arguments[i];
    563 for (key in source) {
    564 target[key] = source[key];
    565 }
    566
    567 // For IE the for-in-loop does not contain any properties that are not
    568 // enumerable on the prototype object (for example isPrototypeOf from
    569 // Object.prototype) and it will also not include 'replace' on objects that
    570 // extend String and change 'replace' (not that it is common for anyone to
    571 // extend anything except Object).
    572
    573 for (var j = 0; j < goog.object.PROTOTYPE_FIELDS_.length; j++) {
    574 key = goog.object.PROTOTYPE_FIELDS_[j];
    575 if (Object.prototype.hasOwnProperty.call(source, key)) {
    576 target[key] = source[key];
    577 }
    578 }
    579 }
    580};
    581
    582
    583/**
    584 * Creates a new object built from the key-value pairs provided as arguments.
    585 * @param {...*} var_args If only one argument is provided and it is an array
    586 * then this is used as the arguments, otherwise even arguments are used as
    587 * the property names and odd arguments are used as the property values.
    588 * @return {!Object} The new object.
    589 * @throws {Error} If there are uneven number of arguments or there is only one
    590 * non array argument.
    591 */
    592goog.object.create = function(var_args) {
    593 var argLength = arguments.length;
    594 if (argLength == 1 && goog.isArray(arguments[0])) {
    595 return goog.object.create.apply(null, arguments[0]);
    596 }
    597
    598 if (argLength % 2) {
    599 throw Error('Uneven number of arguments');
    600 }
    601
    602 var rv = {};
    603 for (var i = 0; i < argLength; i += 2) {
    604 rv[arguments[i]] = arguments[i + 1];
    605 }
    606 return rv;
    607};
    608
    609
    610/**
    611 * Creates a new object where the property names come from the arguments but
    612 * the value is always set to true
    613 * @param {...*} var_args If only one argument is provided and it is an array
    614 * then this is used as the arguments, otherwise the arguments are used
    615 * as the property names.
    616 * @return {!Object} The new object.
    617 */
    618goog.object.createSet = function(var_args) {
    619 var argLength = arguments.length;
    620 if (argLength == 1 && goog.isArray(arguments[0])) {
    621 return goog.object.createSet.apply(null, arguments[0]);
    622 }
    623
    624 var rv = {};
    625 for (var i = 0; i < argLength; i++) {
    626 rv[arguments[i]] = true;
    627 }
    628 return rv;
    629};
    630
    631
    632/**
    633 * Creates an immutable view of the underlying object, if the browser
    634 * supports immutable objects.
    635 *
    636 * In default mode, writes to this view will fail silently. In strict mode,
    637 * they will throw an error.
    638 *
    639 * @param {!Object.<K,V>} obj An object.
    640 * @return {!Object.<K,V>} An immutable view of that object, or the
    641 * original object if this browser does not support immutables.
    642 * @template K,V
    643 */
    644goog.object.createImmutableView = function(obj) {
    645 var result = obj;
    646 if (Object.isFrozen && !Object.isFrozen(obj)) {
    647 result = Object.create(obj);
    648 Object.freeze(result);
    649 }
    650 return result;
    651};
    652
    653
    654/**
    655 * @param {!Object} obj An object.
    656 * @return {boolean} Whether this is an immutable view of the object.
    657 */
    658goog.object.isImmutableView = function(obj) {
    659 return !!Object.isFrozen && Object.isFrozen(obj);
    660};
    \ 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 element, the
    29 * index 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 element, the index 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 element, the index 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 element, the index 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 element, the index 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 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 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 (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 (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 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 (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..f1c2dce --- /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 if (resolver == goog.Promise.RESOLVE_FAST_PATH_) {
    159 // If the special sentinel resolver value is passed (from
    160 // goog.Promise.resolve) we short cut to immediately resolve the promise
    161 // using the value passed as opt_context. Don't try this at home.
    162 this.resolve_(goog.Promise.State_.FULFILLED, opt_context);
    163 } else {
    164 try {
    165 var self = this;
    166 resolver.call(
    167 opt_context,
    168 function(value) {
    169 self.resolve_(goog.Promise.State_.FULFILLED, value);
    170 },
    171 function(reason) {
    172 if (goog.DEBUG &&
    173 !(reason instanceof goog.Promise.CancellationError)) {
    174 try {
    175 // Promise was rejected. Step up one call frame to see why.
    176 if (reason instanceof Error) {
    177 throw reason;
    178 } else {
    179 throw new Error('Promise rejected.');
    180 }
    181 } catch (e) {
    182 // Only thrown so browser dev tools can catch rejections of
    183 // promises when the option to break on caught exceptions is
    184 // activated.
    185 }
    186 }
    187 self.resolve_(goog.Promise.State_.REJECTED, reason);
    188 });
    189 } catch (e) {
    190 this.resolve_(goog.Promise.State_.REJECTED, e);
    191 }
    192 }
    193};
    194
    195
    196/**
    197 * @define {boolean} Whether traces of {@code then} calls should be included in
    198 * exceptions thrown
    199 */
    200goog.define('goog.Promise.LONG_STACK_TRACES', false);
    201
    202
    203/**
    204 * @define {number} The delay in milliseconds before a rejected Promise's reason
    205 * is passed to the rejection handler. By default, the rejection handler
    206 * rethrows the rejection reason so that it appears in the developer console or
    207 * {@code window.onerror} handler.
    208 *
    209 * Rejections are rethrown as quickly as possible by default. A negative value
    210 * disables rejection handling entirely.
    211 */
    212goog.define('goog.Promise.UNHANDLED_REJECTION_DELAY', 0);
    213
    214
    215/**
    216 * The possible internal states for a Promise. These states are not directly
    217 * observable to external callers.
    218 * @enum {number}
    219 * @private
    220 */
    221goog.Promise.State_ = {
    222 /** The Promise is waiting for resolution. */
    223 PENDING: 0,
    224
    225 /** The Promise is blocked waiting for the result of another Thenable. */
    226 BLOCKED: 1,
    227
    228 /** The Promise has been resolved with a fulfillment value. */
    229 FULFILLED: 2,
    230
    231 /** The Promise has been resolved with a rejection reason. */
    232 REJECTED: 3
    233};
    234
    235
    236
    237/**
    238 * Entries in the callback chain. Each call to {@code then},
    239 * {@code thenCatch}, or {@code thenAlways} creates an entry containing the
    240 * functions that may be invoked once the Promise is settled.
    241 *
    242 * @private @final @struct @constructor
    243 */
    244goog.Promise.CallbackEntry_ = function() {
    245 /** @type {?goog.Promise} */
    246 this.child = null;
    247 /** @type {Function} */
    248 this.onFulfilled = null;
    249 /** @type {Function} */
    250 this.onRejected = null;
    251 /** @type {?} */
    252 this.context = null;
    253 /** @type {?goog.Promise.CallbackEntry_} */
    254 this.next = null;
    255
    256 /**
    257 * A boolean value to indicate this is a "thenAlways" callback entry.
    258 * Unlike a normal "then/thenVoid" a "thenAlways doesn't participate
    259 * in "cancel" considerations but is simply an observer and requires
    260 * special handling.
    261 * @type {boolean}
    262 */
    263 this.always = false;
    264};
    265
    266
    267/** clear the object prior to reuse */
    268goog.Promise.CallbackEntry_.prototype.reset = function() {
    269 this.child = null;
    270 this.onFulfilled = null;
    271 this.onRejected = null;
    272 this.context = null;
    273 this.always = false;
    274};
    275
    276
    277/**
    278 * @define {number} The number of currently unused objects to keep around for
    279 * reuse.
    280 */
    281goog.define('goog.Promise.DEFAULT_MAX_UNUSED', 100);
    282
    283
    284/** @const @private {goog.async.FreeList<!goog.Promise.CallbackEntry_>} */
    285goog.Promise.freelist_ = new goog.async.FreeList(
    286 function() {
    287 return new goog.Promise.CallbackEntry_();
    288 },
    289 function(item) {
    290 item.reset();
    291 },
    292 goog.Promise.DEFAULT_MAX_UNUSED);
    293
    294
    295/**
    296 * @param {Function} onFulfilled
    297 * @param {Function} onRejected
    298 * @param {?} context
    299 * @return {!goog.Promise.CallbackEntry_}
    300 * @private
    301 */
    302goog.Promise.getCallbackEntry_ = function(onFulfilled, onRejected, context) {
    303 var entry = goog.Promise.freelist_.get();
    304 entry.onFulfilled = onFulfilled;
    305 entry.onRejected = onRejected;
    306 entry.context = context;
    307 return entry;
    308};
    309
    310
    311/**
    312 * @param {!goog.Promise.CallbackEntry_} entry
    313 * @private
    314 */
    315goog.Promise.returnEntry_ = function(entry) {
    316 goog.Promise.freelist_.put(entry);
    317};
    318
    319
    320/**
    321 * If this passed as the first argument to the {@link goog.Promise} constructor
    322 * the the opt_context is (against its primary use) used to immediately resolve
    323 * the promise. This is used from {@link goog.Promise.resolve} as an
    324 * optimization to avoid allocating 3 closures that are never really needed.
    325 * @private @const {!Function}
    326 */
    327goog.Promise.RESOLVE_FAST_PATH_ = function() {};
    328
    329
    330/**
    331 * @param {(TYPE|goog.Thenable<TYPE>|Thenable)=} opt_value
    332 * @return {!goog.Promise<TYPE>} A new Promise that is immediately resolved
    333 * with the given value. If the input value is already a goog.Promise, it
    334 * will be returned immediately without creating a new instance.
    335 * @template TYPE
    336 */
    337goog.Promise.resolve = function(opt_value) {
    338 if (opt_value instanceof goog.Promise) {
    339 // Avoid creating a new object if we already have a promise object
    340 // of the correct type.
    341 return opt_value;
    342 }
    343
    344 // Passes the value as the context, which is a special fast pass when
    345 // goog.Promise.RESOLVE_FAST_PATH_ is passed as the first argument.
    346 return new goog.Promise(goog.Promise.RESOLVE_FAST_PATH_, opt_value);
    347};
    348
    349
    350/**
    351 * @param {*=} opt_reason
    352 * @return {!goog.Promise} A new Promise that is immediately rejected with the
    353 * given reason.
    354 */
    355goog.Promise.reject = function(opt_reason) {
    356 return new goog.Promise(function(resolve, reject) {
    357 reject(opt_reason);
    358 });
    359};
    360
    361
    362/**
    363 * @param {!Array<!(goog.Thenable<TYPE>|Thenable)>} promises
    364 * @return {!goog.Promise<TYPE>} A Promise that receives the result of the
    365 * first Promise (or Promise-like) input to settle immediately after it
    366 * settles.
    367 * @template TYPE
    368 */
    369goog.Promise.race = function(promises) {
    370 return new goog.Promise(function(resolve, reject) {
    371 if (!promises.length) {
    372 resolve(undefined);
    373 }
    374 for (var i = 0, promise; promise = promises[i]; i++) {
    375 goog.Promise.maybeThenVoid_(promise, resolve, reject);
    376 }
    377 });
    378};
    379
    380
    381/**
    382 * @param {!Array<!(goog.Thenable<TYPE>|Thenable)>} promises
    383 * @return {!goog.Promise<!Array<TYPE>>} A Promise that receives a list of
    384 * every fulfilled value once every input Promise (or Promise-like) is
    385 * successfully fulfilled, or is rejected with the first rejection reason
    386 * immediately after it is rejected.
    387 * @template TYPE
    388 */
    389goog.Promise.all = function(promises) {
    390 return new goog.Promise(function(resolve, reject) {
    391 var toFulfill = promises.length;
    392 var values = [];
    393
    394 if (!toFulfill) {
    395 resolve(values);
    396 return;
    397 }
    398
    399 var onFulfill = function(index, value) {
    400 toFulfill--;
    401 values[index] = value;
    402 if (toFulfill == 0) {
    403 resolve(values);
    404 }
    405 };
    406
    407 var onReject = function(reason) {
    408 reject(reason);
    409 };
    410
    411 for (var i = 0, promise; promise = promises[i]; i++) {
    412 goog.Promise.maybeThenVoid_(
    413 promise, goog.partial(onFulfill, i), onReject);
    414 }
    415 });
    416};
    417
    418
    419/**
    420 * @param {!Array<!(goog.Thenable<TYPE>|Thenable)>} promises
    421 * @return {!goog.Promise<!Array<{
    422 * fulfilled: boolean,
    423 * value: (TYPE|undefined),
    424 * reason: (*|undefined)}>>} A Promise that resolves with a list of
    425 * result objects once all input Promises (or Promise-like) have
    426 * settled. Each result object contains a 'fulfilled' boolean indicating
    427 * whether an input Promise was fulfilled or rejected. For fulfilled
    428 * Promises, the resulting value is stored in the 'value' field. For
    429 * rejected Promises, the rejection reason is stored in the 'reason'
    430 * field.
    431 * @template TYPE
    432 */
    433goog.Promise.allSettled = function(promises) {
    434 return new goog.Promise(function(resolve, reject) {
    435 var toSettle = promises.length;
    436 var results = [];
    437
    438 if (!toSettle) {
    439 resolve(results);
    440 return;
    441 }
    442
    443 var onSettled = function(index, fulfilled, result) {
    444 toSettle--;
    445 results[index] = fulfilled ?
    446 {fulfilled: true, value: result} :
    447 {fulfilled: false, reason: result};
    448 if (toSettle == 0) {
    449 resolve(results);
    450 }
    451 };
    452
    453 for (var i = 0, promise; promise = promises[i]; i++) {
    454 goog.Promise.maybeThenVoid_(promise,
    455 goog.partial(onSettled, i, true /* fulfilled */),
    456 goog.partial(onSettled, i, false /* fulfilled */));
    457 }
    458 });
    459};
    460
    461
    462/**
    463 * @param {!Array<!(goog.Thenable<TYPE>|Thenable)>} promises
    464 * @return {!goog.Promise<TYPE>} A Promise that receives the value of the first
    465 * input to be fulfilled, or is rejected with a list of every rejection
    466 * reason if all inputs are rejected.
    467 * @template TYPE
    468 */
    469goog.Promise.firstFulfilled = function(promises) {
    470 return new goog.Promise(function(resolve, reject) {
    471 var toReject = promises.length;
    472 var reasons = [];
    473
    474 if (!toReject) {
    475 resolve(undefined);
    476 return;
    477 }
    478
    479 var onFulfill = function(value) {
    480 resolve(value);
    481 };
    482
    483 var onReject = function(index, reason) {
    484 toReject--;
    485 reasons[index] = reason;
    486 if (toReject == 0) {
    487 reject(reasons);
    488 }
    489 };
    490
    491 for (var i = 0, promise; promise = promises[i]; i++) {
    492 goog.Promise.maybeThenVoid_(
    493 promise, onFulfill, goog.partial(onReject, i));
    494 }
    495 });
    496};
    497
    498
    499/**
    500 * @return {!goog.promise.Resolver<TYPE>} Resolver wrapping the promise and its
    501 * resolve / reject functions. Resolving or rejecting the resolver
    502 * resolves or rejects the promise.
    503 * @template TYPE
    504 */
    505goog.Promise.withResolver = function() {
    506 var resolve, reject;
    507 var promise = new goog.Promise(function(rs, rj) {
    508 resolve = rs;
    509 reject = rj;
    510 });
    511 return new goog.Promise.Resolver_(promise, resolve, reject);
    512};
    513
    514
    515/**
    516 * Adds callbacks that will operate on the result of the Promise, returning a
    517 * new child Promise.
    518 *
    519 * If the Promise is fulfilled, the {@code onFulfilled} callback will be invoked
    520 * with the fulfillment value as argument, and the child Promise will be
    521 * fulfilled with the return value of the callback. If the callback throws an
    522 * exception, the child Promise will be rejected with the thrown value instead.
    523 *
    524 * If the Promise is rejected, the {@code onRejected} callback will be invoked
    525 * with the rejection reason as argument, and the child Promise will be resolved
    526 * with the return value or rejected with the thrown value of the callback.
    527 *
    528 * @override
    529 */
    530goog.Promise.prototype.then = function(
    531 opt_onFulfilled, opt_onRejected, opt_context) {
    532
    533 if (opt_onFulfilled != null) {
    534 goog.asserts.assertFunction(opt_onFulfilled,
    535 'opt_onFulfilled should be a function.');
    536 }
    537 if (opt_onRejected != null) {
    538 goog.asserts.assertFunction(opt_onRejected,
    539 'opt_onRejected should be a function. Did you pass opt_context ' +
    540 'as the second argument instead of the third?');
    541 }
    542
    543 if (goog.Promise.LONG_STACK_TRACES) {
    544 this.addStackTrace_(new Error('then'));
    545 }
    546
    547 return this.addChildPromise_(
    548 goog.isFunction(opt_onFulfilled) ? opt_onFulfilled : null,
    549 goog.isFunction(opt_onRejected) ? opt_onRejected : null,
    550 opt_context);
    551};
    552goog.Thenable.addImplementation(goog.Promise);
    553
    554
    555/**
    556 * Adds callbacks that will operate on the result of the Promise without
    557 * returning a child Promise (unlike "then").
    558 *
    559 * If the Promise is fulfilled, the {@code onFulfilled} callback will be invoked
    560 * with the fulfillment value as argument.
    561 *
    562 * If the Promise is rejected, the {@code onRejected} callback will be invoked
    563 * with the rejection reason as argument.
    564 *
    565 * @param {?(function(this:THIS, TYPE):?)=} opt_onFulfilled A
    566 * function that will be invoked with the fulfillment value if the Promise
    567 * is fulfilled.
    568 * @param {?(function(this:THIS, *): *)=} opt_onRejected A function that will
    569 * be invoked with the rejection reason if the Promise is rejected.
    570 * @param {THIS=} opt_context An optional context object that will be the
    571 * execution context for the callbacks. By default, functions are executed
    572 * with the default this.
    573 * @package
    574 * @template THIS
    575 */
    576goog.Promise.prototype.thenVoid = function(
    577 opt_onFulfilled, opt_onRejected, opt_context) {
    578
    579 if (opt_onFulfilled != null) {
    580 goog.asserts.assertFunction(opt_onFulfilled,
    581 'opt_onFulfilled should be a function.');
    582 }
    583 if (opt_onRejected != null) {
    584 goog.asserts.assertFunction(opt_onRejected,
    585 'opt_onRejected should be a function. Did you pass opt_context ' +
    586 'as the second argument instead of the third?');
    587 }
    588
    589 if (goog.Promise.LONG_STACK_TRACES) {
    590 this.addStackTrace_(new Error('then'));
    591 }
    592
    593 // Note: no default rejection handler is provided here as we need to
    594 // distinguish unhandled rejections.
    595 this.addCallbackEntry_(goog.Promise.getCallbackEntry_(
    596 opt_onFulfilled || goog.nullFunction,
    597 opt_onRejected || null,
    598 opt_context));
    599};
    600
    601
    602/**
    603 * Calls "thenVoid" if possible to avoid allocating memory. Otherwise calls
    604 * "then".
    605 * @param {(goog.Thenable<TYPE>|Thenable)} promise
    606 * @param {function(this:THIS, TYPE): ?} onFulfilled
    607 * @param {function(this:THIS, *): *} onRejected
    608 * @param {THIS=} opt_context
    609 * @template THIS,TYPE
    610 * @private
    611 */
    612goog.Promise.maybeThenVoid_ = function(
    613 promise, onFulfilled, onRejected, opt_context) {
    614 if (promise instanceof goog.Promise) {
    615 promise.thenVoid(onFulfilled, onRejected, opt_context);
    616 } else {
    617 promise.then(onFulfilled, onRejected, opt_context);
    618 }
    619};
    620
    621
    622/**
    623 * Adds a callback that will be invoked when the Promise is settled (fulfilled
    624 * or rejected). The callback receives no argument, and no new child Promise is
    625 * created. This is useful for ensuring that cleanup takes place after certain
    626 * asynchronous operations. Callbacks added with {@code thenAlways} will be
    627 * executed in the same order with other calls to {@code then},
    628 * {@code thenAlways}, or {@code thenCatch}.
    629 *
    630 * Since it does not produce a new child Promise, cancellation propagation is
    631 * not prevented by adding callbacks with {@code thenAlways}. A Promise that has
    632 * a cleanup handler added with {@code thenAlways} will be canceled if all of
    633 * its children created by {@code then} (or {@code thenCatch}) are canceled.
    634 * Additionally, since any rejections are not passed to the callback, it does
    635 * not stop the unhandled rejection handler from running.
    636 *
    637 * @param {function(this:THIS): void} onSettled A function that will be invoked
    638 * when the Promise is settled (fulfilled or rejected).
    639 * @param {THIS=} opt_context An optional context object that will be the
    640 * execution context for the callbacks. By default, functions are executed
    641 * in the global scope.
    642 * @return {!goog.Promise<TYPE>} This Promise, for chaining additional calls.
    643 * @template THIS
    644 */
    645goog.Promise.prototype.thenAlways = function(onSettled, opt_context) {
    646 if (goog.Promise.LONG_STACK_TRACES) {
    647 this.addStackTrace_(new Error('thenAlways'));
    648 }
    649
    650 var entry = goog.Promise.getCallbackEntry_(onSettled, onSettled, opt_context);
    651 entry.always = true;
    652 this.addCallbackEntry_(entry);
    653 return this;
    654};
    655
    656
    657/**
    658 * Adds a callback that will be invoked only if the Promise is rejected. This
    659 * is equivalent to {@code then(null, onRejected)}.
    660 *
    661 * @param {!function(this:THIS, *): *} onRejected A function that will be
    662 * invoked with the rejection reason if the Promise is rejected.
    663 * @param {THIS=} opt_context An optional context object that will be the
    664 * execution context for the callbacks. By default, functions are executed
    665 * in the global scope.
    666 * @return {!goog.Promise} A new Promise that will receive the result of the
    667 * callback.
    668 * @template THIS
    669 */
    670goog.Promise.prototype.thenCatch = function(onRejected, opt_context) {
    671 if (goog.Promise.LONG_STACK_TRACES) {
    672 this.addStackTrace_(new Error('thenCatch'));
    673 }
    674 return this.addChildPromise_(null, onRejected, opt_context);
    675};
    676
    677
    678/**
    679 * Cancels the Promise if it is still pending by rejecting it with a cancel
    680 * Error. No action is performed if the Promise is already resolved.
    681 *
    682 * All child Promises of the canceled Promise will be rejected with the same
    683 * cancel error, as with normal Promise rejection. If the Promise to be canceled
    684 * is the only child of a pending Promise, the parent Promise will also be
    685 * canceled. Cancellation may propagate upward through multiple generations.
    686 *
    687 * @param {string=} opt_message An optional debugging message for describing the
    688 * cancellation reason.
    689 */
    690goog.Promise.prototype.cancel = function(opt_message) {
    691 if (this.state_ == goog.Promise.State_.PENDING) {
    692 goog.async.run(function() {
    693 var err = new goog.Promise.CancellationError(opt_message);
    694 this.cancelInternal_(err);
    695 }, this);
    696 }
    697};
    698
    699
    700/**
    701 * Cancels this Promise with the given error.
    702 *
    703 * @param {!Error} err The cancellation error.
    704 * @private
    705 */
    706goog.Promise.prototype.cancelInternal_ = function(err) {
    707 if (this.state_ == goog.Promise.State_.PENDING) {
    708 if (this.parent_) {
    709 // Cancel the Promise and remove it from the parent's child list.
    710 this.parent_.cancelChild_(this, err);
    711 this.parent_ = null;
    712 } else {
    713 this.resolve_(goog.Promise.State_.REJECTED, err);
    714 }
    715 }
    716};
    717
    718
    719/**
    720 * Cancels a child Promise from the list of callback entries. If the Promise has
    721 * not already been resolved, reject it with a cancel error. If there are no
    722 * other children in the list of callback entries, propagate the cancellation
    723 * by canceling this Promise as well.
    724 *
    725 * @param {!goog.Promise} childPromise The Promise to cancel.
    726 * @param {!Error} err The cancel error to use for rejecting the Promise.
    727 * @private
    728 */
    729goog.Promise.prototype.cancelChild_ = function(childPromise, err) {
    730 if (!this.callbackEntries_) {
    731 return;
    732 }
    733 var childCount = 0;
    734 var childEntry = null;
    735 var beforeChildEntry = null;
    736
    737 // Find the callback entry for the childPromise, and count whether there are
    738 // additional child Promises.
    739 for (var entry = this.callbackEntries_; entry; entry = entry.next) {
    740 if (!entry.always) {
    741 childCount++;
    742 if (entry.child == childPromise) {
    743 childEntry = entry;
    744 }
    745 if (childEntry && childCount > 1) {
    746 break;
    747 }
    748 }
    749 if (!childEntry) {
    750 beforeChildEntry = entry;
    751 }
    752 }
    753
    754 // Can a child entry be missing?
    755
    756 // If the child Promise was the only child, cancel this Promise as well.
    757 // Otherwise, reject only the child Promise with the cancel error.
    758 if (childEntry) {
    759 if (this.state_ == goog.Promise.State_.PENDING && childCount == 1) {
    760 this.cancelInternal_(err);
    761 } else {
    762 if (beforeChildEntry) {
    763 this.removeEntryAfter_(beforeChildEntry);
    764 } else {
    765 this.popEntry_();
    766 }
    767
    768 this.executeCallback_(
    769 childEntry, goog.Promise.State_.REJECTED, err);
    770 }
    771 }
    772};
    773
    774
    775/**
    776 * Adds a callback entry to the current Promise, and schedules callback
    777 * execution if the Promise has already been settled.
    778 *
    779 * @param {goog.Promise.CallbackEntry_} callbackEntry Record containing
    780 * {@code onFulfilled} and {@code onRejected} callbacks to execute after
    781 * the Promise is settled.
    782 * @private
    783 */
    784goog.Promise.prototype.addCallbackEntry_ = function(callbackEntry) {
    785 if (!this.hasEntry_() &&
    786 (this.state_ == goog.Promise.State_.FULFILLED ||
    787 this.state_ == goog.Promise.State_.REJECTED)) {
    788 this.scheduleCallbacks_();
    789 }
    790 this.queueEntry_(callbackEntry);
    791};
    792
    793
    794/**
    795 * Creates a child Promise and adds it to the callback entry list. The result of
    796 * the child Promise is determined by the state of the parent Promise and the
    797 * result of the {@code onFulfilled} or {@code onRejected} callbacks as
    798 * specified in the Promise resolution procedure.
    799 *
    800 * @see http://promisesaplus.com/#the__method
    801 *
    802 * @param {?function(this:THIS, TYPE):
    803 * (RESULT|goog.Promise<RESULT>|Thenable)} onFulfilled A callback that
    804 * will be invoked if the Promise is fullfilled, or null.
    805 * @param {?function(this:THIS, *): *} onRejected A callback that will be
    806 * invoked if the Promise is rejected, or null.
    807 * @param {THIS=} opt_context An optional execution context for the callbacks.
    808 * in the default calling context.
    809 * @return {!goog.Promise} The child Promise.
    810 * @template RESULT,THIS
    811 * @private
    812 */
    813goog.Promise.prototype.addChildPromise_ = function(
    814 onFulfilled, onRejected, opt_context) {
    815
    816 /** @type {goog.Promise.CallbackEntry_} */
    817 var callbackEntry = goog.Promise.getCallbackEntry_(null, null, null);
    818
    819 callbackEntry.child = new goog.Promise(function(resolve, reject) {
    820 // Invoke onFulfilled, or resolve with the parent's value if absent.
    821 callbackEntry.onFulfilled = onFulfilled ? function(value) {
    822 try {
    823 var result = onFulfilled.call(opt_context, value);
    824 resolve(result);
    825 } catch (err) {
    826 reject(err);
    827 }
    828 } : resolve;
    829
    830 // Invoke onRejected, or reject with the parent's reason if absent.
    831 callbackEntry.onRejected = onRejected ? function(reason) {
    832 try {
    833 var result = onRejected.call(opt_context, reason);
    834 if (!goog.isDef(result) &&
    835 reason instanceof goog.Promise.CancellationError) {
    836 // Propagate cancellation to children if no other result is returned.
    837 reject(reason);
    838 } else {
    839 resolve(result);
    840 }
    841 } catch (err) {
    842 reject(err);
    843 }
    844 } : reject;
    845 });
    846
    847 callbackEntry.child.parent_ = this;
    848 this.addCallbackEntry_(callbackEntry);
    849 return callbackEntry.child;
    850};
    851
    852
    853/**
    854 * Unblocks the Promise and fulfills it with the given value.
    855 *
    856 * @param {TYPE} value
    857 * @private
    858 */
    859goog.Promise.prototype.unblockAndFulfill_ = function(value) {
    860 goog.asserts.assert(this.state_ == goog.Promise.State_.BLOCKED);
    861 this.state_ = goog.Promise.State_.PENDING;
    862 this.resolve_(goog.Promise.State_.FULFILLED, value);
    863};
    864
    865
    866/**
    867 * Unblocks the Promise and rejects it with the given rejection reason.
    868 *
    869 * @param {*} reason
    870 * @private
    871 */
    872goog.Promise.prototype.unblockAndReject_ = function(reason) {
    873 goog.asserts.assert(this.state_ == goog.Promise.State_.BLOCKED);
    874 this.state_ = goog.Promise.State_.PENDING;
    875 this.resolve_(goog.Promise.State_.REJECTED, reason);
    876};
    877
    878
    879/**
    880 * Attempts to resolve a Promise with a given resolution state and value. This
    881 * is a no-op if the given Promise has already been resolved.
    882 *
    883 * If the given result is a Thenable (such as another Promise), the Promise will
    884 * be settled with the same state and result as the Thenable once it is itself
    885 * settled.
    886 *
    887 * If the given result is not a Thenable, the Promise will be settled (fulfilled
    888 * or rejected) with that result based on the given state.
    889 *
    890 * @see http://promisesaplus.com/#the_promise_resolution_procedure
    891 *
    892 * @param {goog.Promise.State_} state
    893 * @param {*} x The result to apply to the Promise.
    894 * @private
    895 */
    896goog.Promise.prototype.resolve_ = function(state, x) {
    897 if (this.state_ != goog.Promise.State_.PENDING) {
    898 return;
    899 }
    900
    901 if (this == x) {
    902 state = goog.Promise.State_.REJECTED;
    903 x = new TypeError('Promise cannot resolve to itself');
    904
    905 } else if (goog.Thenable.isImplementedBy(x)) {
    906 x = /** @type {!goog.Thenable} */ (x);
    907 this.state_ = goog.Promise.State_.BLOCKED;
    908 goog.Promise.maybeThenVoid_(
    909 x, this.unblockAndFulfill_, this.unblockAndReject_, this);
    910 return;
    911 } else if (goog.isObject(x)) {
    912 try {
    913 var then = x['then'];
    914 if (goog.isFunction(then)) {
    915 this.tryThen_(x, then);
    916 return;
    917 }
    918 } catch (e) {
    919 state = goog.Promise.State_.REJECTED;
    920 x = e;
    921 }
    922 }
    923
    924 this.result_ = x;
    925 this.state_ = state;
    926 // Since we can no longer be cancelled, remove link to parent, so that the
    927 // child promise does not keep the parent promise alive.
    928 this.parent_ = null;
    929 this.scheduleCallbacks_();
    930
    931 if (state == goog.Promise.State_.REJECTED &&
    932 !(x instanceof goog.Promise.CancellationError)) {
    933 goog.Promise.addUnhandledRejection_(this, x);
    934 }
    935};
    936
    937
    938/**
    939 * Attempts to call the {@code then} method on an object in the hopes that it is
    940 * a Promise-compatible instance. This allows interoperation between different
    941 * Promise implementations, however a non-compliant object may cause a Promise
    942 * to hang indefinitely. If the {@code then} method throws an exception, the
    943 * dependent Promise will be rejected with the thrown value.
    944 *
    945 * @see http://promisesaplus.com/#point-70
    946 *
    947 * @param {Thenable} thenable An object with a {@code then} method that may be
    948 * compatible with the Promise/A+ specification.
    949 * @param {!Function} then The {@code then} method of the Thenable object.
    950 * @private
    951 */
    952goog.Promise.prototype.tryThen_ = function(thenable, then) {
    953 this.state_ = goog.Promise.State_.BLOCKED;
    954 var promise = this;
    955 var called = false;
    956
    957 var resolve = function(value) {
    958 if (!called) {
    959 called = true;
    960 promise.unblockAndFulfill_(value);
    961 }
    962 };
    963
    964 var reject = function(reason) {
    965 if (!called) {
    966 called = true;
    967 promise.unblockAndReject_(reason);
    968 }
    969 };
    970
    971 try {
    972 then.call(thenable, resolve, reject);
    973 } catch (e) {
    974 reject(e);
    975 }
    976};
    977
    978
    979/**
    980 * Executes the pending callbacks of a settled Promise after a timeout.
    981 *
    982 * Section 2.2.4 of the Promises/A+ specification requires that Promise
    983 * callbacks must only be invoked from a call stack that only contains Promise
    984 * implementation code, which we accomplish by invoking callback execution after
    985 * a timeout. If {@code startExecution_} is called multiple times for the same
    986 * Promise, the callback chain will be evaluated only once. Additional callbacks
    987 * may be added during the evaluation phase, and will be executed in the same
    988 * event loop.
    989 *
    990 * All Promises added to the waiting list during the same browser event loop
    991 * will be executed in one batch to avoid using a separate timeout per Promise.
    992 *
    993 * @private
    994 */
    995goog.Promise.prototype.scheduleCallbacks_ = function() {
    996 if (!this.executing_) {
    997 this.executing_ = true;
    998 goog.async.run(this.executeCallbacks_, this);
    999 }
    1000};
    1001
    1002
    1003/**
    1004 * @return {boolean} Whether there are any pending callbacks queued.
    1005 * @private
    1006 */
    1007goog.Promise.prototype.hasEntry_ = function() {
    1008 return !!this.callbackEntries_;
    1009};
    1010
    1011
    1012/**
    1013 * @param {goog.Promise.CallbackEntry_} entry
    1014 * @private
    1015 */
    1016goog.Promise.prototype.queueEntry_ = function(entry) {
    1017 goog.asserts.assert(entry.onFulfilled != null);
    1018
    1019 if (this.callbackEntriesTail_) {
    1020 this.callbackEntriesTail_.next = entry;
    1021 this.callbackEntriesTail_ = entry;
    1022 } else {
    1023 // It the work queue was empty set the head too.
    1024 this.callbackEntries_ = entry;
    1025 this.callbackEntriesTail_ = entry;
    1026 }
    1027};
    1028
    1029
    1030/**
    1031 * @return {goog.Promise.CallbackEntry_} entry
    1032 * @private
    1033 */
    1034goog.Promise.prototype.popEntry_ = function() {
    1035 var entry = null;
    1036 if (this.callbackEntries_) {
    1037 entry = this.callbackEntries_;
    1038 this.callbackEntries_ = entry.next;
    1039 entry.next = null;
    1040 }
    1041 // It the work queue is empty clear the tail too.
    1042 if (!this.callbackEntries_) {
    1043 this.callbackEntriesTail_ = null;
    1044 }
    1045
    1046 if (entry != null) {
    1047 goog.asserts.assert(entry.onFulfilled != null);
    1048 }
    1049 return entry;
    1050};
    1051
    1052
    1053/**
    1054 * @param {goog.Promise.CallbackEntry_} previous
    1055 * @private
    1056 */
    1057goog.Promise.prototype.removeEntryAfter_ = function(previous) {
    1058 goog.asserts.assert(this.callbackEntries_);
    1059 goog.asserts.assert(previous != null);
    1060 // If the last entry is being removed, update the tail
    1061 if (previous.next == this.callbackEntriesTail_) {
    1062 this.callbackEntriesTail_ = previous;
    1063 }
    1064
    1065 previous.next = previous.next.next;
    1066};
    1067
    1068
    1069/**
    1070 * Executes all pending callbacks for this Promise.
    1071 *
    1072 * @private
    1073 */
    1074goog.Promise.prototype.executeCallbacks_ = function() {
    1075 var entry = null;
    1076 while (entry = this.popEntry_()) {
    1077 if (goog.Promise.LONG_STACK_TRACES) {
    1078 this.currentStep_++;
    1079 }
    1080 this.executeCallback_(entry, this.state_, this.result_);
    1081 }
    1082 this.executing_ = false;
    1083};
    1084
    1085
    1086/**
    1087 * Executes a pending callback for this Promise. Invokes an {@code onFulfilled}
    1088 * or {@code onRejected} callback based on the settled state of the Promise.
    1089 *
    1090 * @param {!goog.Promise.CallbackEntry_} callbackEntry An entry containing the
    1091 * onFulfilled and/or onRejected callbacks for this step.
    1092 * @param {goog.Promise.State_} state The resolution status of the Promise,
    1093 * either FULFILLED or REJECTED.
    1094 * @param {*} result The settled result of the Promise.
    1095 * @private
    1096 */
    1097goog.Promise.prototype.executeCallback_ = function(
    1098 callbackEntry, state, result) {
    1099 // Cancel an unhandled rejection if the then/thenVoid call had an onRejected.
    1100 if (state == goog.Promise.State_.REJECTED &&
    1101 callbackEntry.onRejected && !callbackEntry.always) {
    1102 this.removeUnhandledRejection_();
    1103 }
    1104
    1105 if (callbackEntry.child) {
    1106 // When the parent is settled, the child no longer needs to hold on to it,
    1107 // as the parent can no longer be canceled.
    1108 callbackEntry.child.parent_ = null;
    1109 goog.Promise.invokeCallback_(callbackEntry, state, result);
    1110 } else {
    1111 // Callbacks created with thenAlways or thenVoid do not have the rejection
    1112 // handling code normally set up in the child Promise.
    1113 try {
    1114 callbackEntry.always ?
    1115 callbackEntry.onFulfilled.call(callbackEntry.context) :
    1116 goog.Promise.invokeCallback_(callbackEntry, state, result);
    1117 } catch (err) {
    1118 goog.Promise.handleRejection_.call(null, err);
    1119 }
    1120 }
    1121 goog.Promise.returnEntry_(callbackEntry);
    1122};
    1123
    1124
    1125/**
    1126 * Executes the onFulfilled or onRejected callback for a callbackEntry.
    1127 *
    1128 * @param {!goog.Promise.CallbackEntry_} callbackEntry
    1129 * @param {goog.Promise.State_} state
    1130 * @param {*} result
    1131 * @private
    1132 */
    1133goog.Promise.invokeCallback_ = function(callbackEntry, state, result) {
    1134 if (state == goog.Promise.State_.FULFILLED) {
    1135 callbackEntry.onFulfilled.call(callbackEntry.context, result);
    1136 } else if (callbackEntry.onRejected) {
    1137 callbackEntry.onRejected.call(callbackEntry.context, result);
    1138 }
    1139};
    1140
    1141
    1142/**
    1143 * Records a stack trace entry for functions that call {@code then} or the
    1144 * Promise constructor. May be disabled by unsetting {@code LONG_STACK_TRACES}.
    1145 *
    1146 * @param {!Error} err An Error object created by the calling function for
    1147 * providing a stack trace.
    1148 * @private
    1149 */
    1150goog.Promise.prototype.addStackTrace_ = function(err) {
    1151 if (goog.Promise.LONG_STACK_TRACES && goog.isString(err.stack)) {
    1152 // Extract the third line of the stack trace, which is the entry for the
    1153 // user function that called into Promise code.
    1154 var trace = err.stack.split('\n', 4)[3];
    1155 var message = err.message;
    1156
    1157 // Pad the message to align the traces.
    1158 message += Array(11 - message.length).join(' ');
    1159 this.stack_.push(message + trace);
    1160 }
    1161};
    1162
    1163
    1164/**
    1165 * Adds extra stack trace information to an exception for the list of
    1166 * asynchronous {@code then} calls that have been run for this Promise. Stack
    1167 * trace information is recorded in {@see #addStackTrace_}, and appended to
    1168 * rethrown errors when {@code LONG_STACK_TRACES} is enabled.
    1169 *
    1170 * @param {*} err An unhandled exception captured during callback execution.
    1171 * @private
    1172 */
    1173goog.Promise.prototype.appendLongStack_ = function(err) {
    1174 if (goog.Promise.LONG_STACK_TRACES &&
    1175 err && goog.isString(err.stack) && this.stack_.length) {
    1176 var longTrace = ['Promise trace:'];
    1177
    1178 for (var promise = this; promise; promise = promise.parent_) {
    1179 for (var i = this.currentStep_; i >= 0; i--) {
    1180 longTrace.push(promise.stack_[i]);
    1181 }
    1182 longTrace.push('Value: ' +
    1183 '[' + (promise.state_ == goog.Promise.State_.REJECTED ?
    1184 'REJECTED' : 'FULFILLED') + '] ' +
    1185 '<' + String(promise.result_) + '>');
    1186 }
    1187 err.stack += '\n\n' + longTrace.join('\n');
    1188 }
    1189};
    1190
    1191
    1192/**
    1193 * Marks this rejected Promise as having being handled. Also marks any parent
    1194 * Promises in the rejected state as handled. The rejection handler will no
    1195 * longer be invoked for this Promise (if it has not been called already).
    1196 *
    1197 * @private
    1198 */
    1199goog.Promise.prototype.removeUnhandledRejection_ = function() {
    1200 if (goog.Promise.UNHANDLED_REJECTION_DELAY > 0) {
    1201 for (var p = this; p && p.unhandledRejectionId_; p = p.parent_) {
    1202 goog.global.clearTimeout(p.unhandledRejectionId_);
    1203 p.unhandledRejectionId_ = 0;
    1204 }
    1205 } else if (goog.Promise.UNHANDLED_REJECTION_DELAY == 0) {
    1206 for (var p = this; p && p.hadUnhandledRejection_; p = p.parent_) {
    1207 p.hadUnhandledRejection_ = false;
    1208 }
    1209 }
    1210};
    1211
    1212
    1213/**
    1214 * Marks this rejected Promise as unhandled. If no {@code onRejected} callback
    1215 * is called for this Promise before the {@code UNHANDLED_REJECTION_DELAY}
    1216 * expires, the reason will be passed to the unhandled rejection handler. The
    1217 * handler typically rethrows the rejection reason so that it becomes visible in
    1218 * the developer console.
    1219 *
    1220 * @param {!goog.Promise} promise The rejected Promise.
    1221 * @param {*} reason The Promise rejection reason.
    1222 * @private
    1223 */
    1224goog.Promise.addUnhandledRejection_ = function(promise, reason) {
    1225 if (goog.Promise.UNHANDLED_REJECTION_DELAY > 0) {
    1226 promise.unhandledRejectionId_ = goog.global.setTimeout(function() {
    1227 promise.appendLongStack_(reason);
    1228 goog.Promise.handleRejection_.call(null, reason);
    1229 }, goog.Promise.UNHANDLED_REJECTION_DELAY);
    1230
    1231 } else if (goog.Promise.UNHANDLED_REJECTION_DELAY == 0) {
    1232 promise.hadUnhandledRejection_ = true;
    1233 goog.async.run(function() {
    1234 if (promise.hadUnhandledRejection_) {
    1235 promise.appendLongStack_(reason);
    1236 goog.Promise.handleRejection_.call(null, reason);
    1237 }
    1238 });
    1239 }
    1240};
    1241
    1242
    1243/**
    1244 * A method that is invoked with the rejection reasons for Promises that are
    1245 * rejected but have no {@code onRejected} callbacks registered yet.
    1246 * @type {function(*)}
    1247 * @private
    1248 */
    1249goog.Promise.handleRejection_ = goog.async.throwException;
    1250
    1251
    1252/**
    1253 * Sets a handler that will be called with reasons from unhandled rejected
    1254 * Promises. If the rejected Promise (or one of its descendants) has an
    1255 * {@code onRejected} callback registered, the rejection will be considered
    1256 * handled, and the rejection handler will not be called.
    1257 *
    1258 * By default, unhandled rejections are rethrown so that the error may be
    1259 * captured by the developer console or a {@code window.onerror} handler.
    1260 *
    1261 * @param {function(*)} handler A function that will be called with reasons from
    1262 * rejected Promises. Defaults to {@code goog.async.throwException}.
    1263 */
    1264goog.Promise.setUnhandledRejectionHandler = function(handler) {
    1265 goog.Promise.handleRejection_ = handler;
    1266};
    1267
    1268
    1269
    1270/**
    1271 * Error used as a rejection reason for canceled Promises.
    1272 *
    1273 * @param {string=} opt_message
    1274 * @constructor
    1275 * @extends {goog.debug.Error}
    1276 * @final
    1277 */
    1278goog.Promise.CancellationError = function(opt_message) {
    1279 goog.Promise.CancellationError.base(this, 'constructor', opt_message);
    1280};
    1281goog.inherits(goog.Promise.CancellationError, goog.debug.Error);
    1282
    1283
    1284/** @override */
    1285goog.Promise.CancellationError.prototype.name = 'cancel';
    1286
    1287
    1288
    1289/**
    1290 * Internal implementation of the resolver interface.
    1291 *
    1292 * @param {!goog.Promise<TYPE>} promise
    1293 * @param {function((TYPE|goog.Promise<TYPE>|Thenable)=)} resolve
    1294 * @param {function(*=): void} reject
    1295 * @implements {goog.promise.Resolver<TYPE>}
    1296 * @final @struct
    1297 * @constructor
    1298 * @private
    1299 * @template TYPE
    1300 */
    1301goog.Promise.Resolver_ = function(promise, resolve, reject) {
    1302 /** @const */
    1303 this.promise = promise;
    1304
    1305 /** @const */
    1306 this.resolve = resolve;
    1307
    1308 /** @const */
    1309 this.reject = reject;
    1310};
    \ 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 index 96e523d..0d50a56 100644 --- a/docs/source/lib/goog/reflect/reflect.js.src.html +++ b/docs/source/lib/goog/reflect/reflect.js.src.html @@ -1 +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 */
    19
    20goog.provide('goog.reflect');
    21
    22
    23/**
    24 * Syntax for object literal casts.
    25 * @see http://go/jscompiler-renaming
    26 * @see http://code.google.com/p/closure-compiler/wiki/
    27 * ExperimentalTypeBasedPropertyRenaming
    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 +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 http://code.google.com/p/closure-compiler/wiki/
    28 * ExperimentalTypeBasedPropertyRenaming
    29 *
    30 * Use this if you have an object literal whose keys need to have the same names
    31 * as the properties of some class even after they are renamed by the compiler.
    32 *
    33 * @param {!Function} type Type to cast to.
    34 * @param {Object} object Object literal to cast.
    35 * @return {Object} The object literal.
    36 */
    37goog.reflect.object = function(type, object) {
    38 return object;
    39};
    40
    41
    42/**
    43 * To assert to the compiler that an operation is needed when it would
    44 * otherwise be stripped. For example:
    45 * <code>
    46 * // Force a layout
    47 * goog.reflect.sinkValue(dialog.offsetHeight);
    48 * </code>
    49 * @type {!Function}
    50 */
    51goog.reflect.sinkValue = function(x) {
    52 goog.reflect.sinkValue[' '](x);
    53 return x;
    54};
    55
    56
    57/**
    58 * The compiler should optimize this function away iff no one ever uses
    59 * goog.reflect.sinkValue.
    60 */
    61goog.reflect.sinkValue[' '] = goog.nullFunction;
    62
    63
    64/**
    65 * Check if a property can be accessed without throwing an exception.
    66 * @param {Object} obj The owner of the property.
    67 * @param {string} prop The property name.
    68 * @return {boolean} Whether the property is accessible. Will also return true
    69 * if obj is null.
    70 */
    71goog.reflect.canAccessProperty = function(obj, prop) {
    72 /** @preserveTry */
    73 try {
    74 goog.reflect.sinkValue(obj[prop]);
    75 return true;
    76 } catch (e) {}
    77 return false;
    78};
    \ 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..09902da --- /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(user): 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
    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 3b56dc4..340e35a 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 * @define {boolean} Enables HTML escaping of lowercase letter "e" which helps
    29 * with detection of double-escaping as this letter is frequently used.
    30 */
    31goog.define('goog.string.DETECT_DOUBLE_ESCAPING', false);
    32
    33
    34/**
    35 * Common Unicode string characters.
    36 * @enum {string}
    37 */
    38goog.string.Unicode = {
    39 NBSP: '\xa0'
    40};
    41
    42
    43/**
    44 * Fast prefix-checker.
    45 * @param {string} str The string to check.
    46 * @param {string} prefix A string to look for at the start of {@code str}.
    47 * @return {boolean} True if {@code str} begins with {@code prefix}.
    48 */
    49goog.string.startsWith = function(str, prefix) {
    50 return str.lastIndexOf(prefix, 0) == 0;
    51};
    52
    53
    54/**
    55 * Fast suffix-checker.
    56 * @param {string} str The string to check.
    57 * @param {string} suffix A string to look for at the end of {@code str}.
    58 * @return {boolean} True if {@code str} ends with {@code suffix}.
    59 */
    60goog.string.endsWith = function(str, suffix) {
    61 var l = str.length - suffix.length;
    62 return l >= 0 && str.indexOf(suffix, l) == l;
    63};
    64
    65
    66/**
    67 * Case-insensitive prefix-checker.
    68 * @param {string} str The string to check.
    69 * @param {string} prefix A string to look for at the end of {@code str}.
    70 * @return {boolean} True if {@code str} begins with {@code prefix} (ignoring
    71 * case).
    72 */
    73goog.string.caseInsensitiveStartsWith = function(str, prefix) {
    74 return goog.string.caseInsensitiveCompare(
    75 prefix, str.substr(0, prefix.length)) == 0;
    76};
    77
    78
    79/**
    80 * Case-insensitive suffix-checker.
    81 * @param {string} str The string to check.
    82 * @param {string} suffix A string to look for at the end of {@code str}.
    83 * @return {boolean} True if {@code str} ends with {@code suffix} (ignoring
    84 * case).
    85 */
    86goog.string.caseInsensitiveEndsWith = function(str, suffix) {
    87 return goog.string.caseInsensitiveCompare(
    88 suffix, str.substr(str.length - suffix.length, suffix.length)) == 0;
    89};
    90
    91
    92/**
    93 * Case-insensitive equality checker.
    94 * @param {string} str1 First string to check.
    95 * @param {string} str2 Second string to check.
    96 * @return {boolean} True if {@code str1} and {@code str2} are the same string,
    97 * ignoring case.
    98 */
    99goog.string.caseInsensitiveEquals = function(str1, str2) {
    100 return str1.toLowerCase() == str2.toLowerCase();
    101};
    102
    103
    104/**
    105 * Does simple python-style string substitution.
    106 * subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog".
    107 * @param {string} str The string containing the pattern.
    108 * @param {...*} var_args The items to substitute into the pattern.
    109 * @return {string} A copy of {@code str} in which each occurrence of
    110 * {@code %s} has been replaced an argument from {@code var_args}.
    111 */
    112goog.string.subs = function(str, var_args) {
    113 var splitParts = str.split('%s');
    114 var returnString = '';
    115
    116 var subsArguments = Array.prototype.slice.call(arguments, 1);
    117 while (subsArguments.length &&
    118 // Replace up to the last split part. We are inserting in the
    119 // positions between split parts.
    120 splitParts.length > 1) {
    121 returnString += splitParts.shift() + subsArguments.shift();
    122 }
    123
    124 return returnString + splitParts.join('%s'); // Join unused '%s'
    125};
    126
    127
    128/**
    129 * Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines
    130 * and tabs) to a single space, and strips leading and trailing whitespace.
    131 * @param {string} str Input string.
    132 * @return {string} A copy of {@code str} with collapsed whitespace.
    133 */
    134goog.string.collapseWhitespace = function(str) {
    135 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
    136 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
    137 // include it in the regexp to enforce consistent cross-browser behavior.
    138 return str.replace(/[\s\xa0]+/g, ' ').replace(/^\s+|\s+$/g, '');
    139};
    140
    141
    142/**
    143 * Checks if a string is empty or contains only whitespaces.
    144 * @param {string} str The string to check.
    145 * @return {boolean} True if {@code str} is empty or whitespace only.
    146 */
    147goog.string.isEmpty = function(str) {
    148 // testing length == 0 first is actually slower in all browsers (about the
    149 // same in Opera).
    150 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
    151 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
    152 // include it in the regexp to enforce consistent cross-browser behavior.
    153 return /^[\s\xa0]*$/.test(str);
    154};
    155
    156
    157/**
    158 * Checks if a string is null, undefined, empty or contains only whitespaces.
    159 * @param {*} str The string to check.
    160 * @return {boolean} True if{@code str} is null, undefined, empty, or
    161 * whitespace only.
    162 */
    163goog.string.isEmptySafe = function(str) {
    164 return goog.string.isEmpty(goog.string.makeSafe(str));
    165};
    166
    167
    168/**
    169 * Checks if a string is all breaking whitespace.
    170 * @param {string} str The string to check.
    171 * @return {boolean} Whether the string is all breaking whitespace.
    172 */
    173goog.string.isBreakingWhitespace = function(str) {
    174 return !/[^\t\n\r ]/.test(str);
    175};
    176
    177
    178/**
    179 * Checks if a string contains all letters.
    180 * @param {string} str string to check.
    181 * @return {boolean} True if {@code str} consists entirely of letters.
    182 */
    183goog.string.isAlpha = function(str) {
    184 return !/[^a-zA-Z]/.test(str);
    185};
    186
    187
    188/**
    189 * Checks if a string contains only numbers.
    190 * @param {*} str string to check. If not a string, it will be
    191 * casted to one.
    192 * @return {boolean} True if {@code str} is numeric.
    193 */
    194goog.string.isNumeric = function(str) {
    195 return !/[^0-9]/.test(str);
    196};
    197
    198
    199/**
    200 * Checks if a string contains only numbers or letters.
    201 * @param {string} str string to check.
    202 * @return {boolean} True if {@code str} is alphanumeric.
    203 */
    204goog.string.isAlphaNumeric = function(str) {
    205 return !/[^a-zA-Z0-9]/.test(str);
    206};
    207
    208
    209/**
    210 * Checks if a character is a space character.
    211 * @param {string} ch Character to check.
    212 * @return {boolean} True if {code ch} is a space.
    213 */
    214goog.string.isSpace = function(ch) {
    215 return ch == ' ';
    216};
    217
    218
    219/**
    220 * Checks if a character is a valid unicode character.
    221 * @param {string} ch Character to check.
    222 * @return {boolean} True if {code ch} is a valid unicode character.
    223 */
    224goog.string.isUnicodeChar = function(ch) {
    225 return ch.length == 1 && ch >= ' ' && ch <= '~' ||
    226 ch >= '\u0080' && ch <= '\uFFFD';
    227};
    228
    229
    230/**
    231 * Takes a string and replaces newlines with a space. Multiple lines are
    232 * replaced with a single space.
    233 * @param {string} str The string from which to strip newlines.
    234 * @return {string} A copy of {@code str} stripped of newlines.
    235 */
    236goog.string.stripNewlines = function(str) {
    237 return str.replace(/(\r\n|\r|\n)+/g, ' ');
    238};
    239
    240
    241/**
    242 * Replaces Windows and Mac new lines with unix style: \r or \r\n with \n.
    243 * @param {string} str The string to in which to canonicalize newlines.
    244 * @return {string} {@code str} A copy of {@code} with canonicalized newlines.
    245 */
    246goog.string.canonicalizeNewlines = function(str) {
    247 return str.replace(/(\r\n|\r|\n)/g, '\n');
    248};
    249
    250
    251/**
    252 * Normalizes whitespace in a string, replacing all whitespace chars with
    253 * a space.
    254 * @param {string} str The string in which to normalize whitespace.
    255 * @return {string} A copy of {@code str} with all whitespace normalized.
    256 */
    257goog.string.normalizeWhitespace = function(str) {
    258 return str.replace(/\xa0|\s/g, ' ');
    259};
    260
    261
    262/**
    263 * Normalizes spaces in a string, replacing all consecutive spaces and tabs
    264 * with a single space. Replaces non-breaking space with a space.
    265 * @param {string} str The string in which to normalize spaces.
    266 * @return {string} A copy of {@code str} with all consecutive spaces and tabs
    267 * replaced with a single space.
    268 */
    269goog.string.normalizeSpaces = function(str) {
    270 return str.replace(/\xa0|[ \t]+/g, ' ');
    271};
    272
    273
    274/**
    275 * Removes the breaking spaces from the left and right of the string and
    276 * collapses the sequences of breaking spaces in the middle into single spaces.
    277 * The original and the result strings render the same way in HTML.
    278 * @param {string} str A string in which to collapse spaces.
    279 * @return {string} Copy of the string with normalized breaking spaces.
    280 */
    281goog.string.collapseBreakingSpaces = function(str) {
    282 return str.replace(/[\t\r\n ]+/g, ' ').replace(
    283 /^[\t\r\n ]+|[\t\r\n ]+$/g, '');
    284};
    285
    286
    287/**
    288 * Trims white spaces to the left and right of a string.
    289 * @param {string} str The string to trim.
    290 * @return {string} A trimmed copy of {@code str}.
    291 */
    292goog.string.trim = function(str) {
    293 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
    294 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
    295 // include it in the regexp to enforce consistent cross-browser behavior.
    296 return str.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
    297};
    298
    299
    300/**
    301 * Trims whitespaces at the left end of a string.
    302 * @param {string} str The string to left trim.
    303 * @return {string} A trimmed copy of {@code str}.
    304 */
    305goog.string.trimLeft = function(str) {
    306 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
    307 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
    308 // include it in the regexp to enforce consistent cross-browser behavior.
    309 return str.replace(/^[\s\xa0]+/, '');
    310};
    311
    312
    313/**
    314 * Trims whitespaces at the right end of a string.
    315 * @param {string} str The string to right trim.
    316 * @return {string} A trimmed copy of {@code str}.
    317 */
    318goog.string.trimRight = function(str) {
    319 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
    320 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
    321 // include it in the regexp to enforce consistent cross-browser behavior.
    322 return str.replace(/[\s\xa0]+$/, '');
    323};
    324
    325
    326/**
    327 * A string comparator that ignores case.
    328 * -1 = str1 less than str2
    329 * 0 = str1 equals str2
    330 * 1 = str1 greater than str2
    331 *
    332 * @param {string} str1 The string to compare.
    333 * @param {string} str2 The string to compare {@code str1} to.
    334 * @return {number} The comparator result, as described above.
    335 */
    336goog.string.caseInsensitiveCompare = function(str1, str2) {
    337 var test1 = String(str1).toLowerCase();
    338 var test2 = String(str2).toLowerCase();
    339
    340 if (test1 < test2) {
    341 return -1;
    342 } else if (test1 == test2) {
    343 return 0;
    344 } else {
    345 return 1;
    346 }
    347};
    348
    349
    350/**
    351 * Regular expression used for splitting a string into substrings of fractional
    352 * numbers, integers, and non-numeric characters.
    353 * @type {RegExp}
    354 * @private
    355 */
    356goog.string.numerateCompareRegExp_ = /(\.\d+)|(\d+)|(\D+)/g;
    357
    358
    359/**
    360 * String comparison function that handles numbers in a way humans might expect.
    361 * Using this function, the string "File 2.jpg" sorts before "File 10.jpg". The
    362 * comparison is mostly case-insensitive, though strings that are identical
    363 * except for case are sorted with the upper-case strings before lower-case.
    364 *
    365 * This comparison function is significantly slower (about 500x) than either
    366 * the default or the case-insensitive compare. It should not be used in
    367 * time-critical code, but should be fast enough to sort several hundred short
    368 * strings (like filenames) with a reasonable delay.
    369 *
    370 * @param {string} str1 The string to compare in a numerically sensitive way.
    371 * @param {string} str2 The string to compare {@code str1} to.
    372 * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than
    373 * 0 if str1 > str2.
    374 */
    375goog.string.numerateCompare = function(str1, str2) {
    376 if (str1 == str2) {
    377 return 0;
    378 }
    379 if (!str1) {
    380 return -1;
    381 }
    382 if (!str2) {
    383 return 1;
    384 }
    385
    386 // Using match to split the entire string ahead of time turns out to be faster
    387 // for most inputs than using RegExp.exec or iterating over each character.
    388 var tokens1 = str1.toLowerCase().match(goog.string.numerateCompareRegExp_);
    389 var tokens2 = str2.toLowerCase().match(goog.string.numerateCompareRegExp_);
    390
    391 var count = Math.min(tokens1.length, tokens2.length);
    392
    393 for (var i = 0; i < count; i++) {
    394 var a = tokens1[i];
    395 var b = tokens2[i];
    396
    397 // Compare pairs of tokens, returning if one token sorts before the other.
    398 if (a != b) {
    399
    400 // Only if both tokens are integers is a special comparison required.
    401 // Decimal numbers are sorted as strings (e.g., '.09' < '.1').
    402 var num1 = parseInt(a, 10);
    403 if (!isNaN(num1)) {
    404 var num2 = parseInt(b, 10);
    405 if (!isNaN(num2) && num1 - num2) {
    406 return num1 - num2;
    407 }
    408 }
    409 return a < b ? -1 : 1;
    410 }
    411 }
    412
    413 // If one string is a substring of the other, the shorter string sorts first.
    414 if (tokens1.length != tokens2.length) {
    415 return tokens1.length - tokens2.length;
    416 }
    417
    418 // The two strings must be equivalent except for case (perfect equality is
    419 // tested at the head of the function.) Revert to default ASCII-betical string
    420 // comparison to stablize the sort.
    421 return str1 < str2 ? -1 : 1;
    422};
    423
    424
    425/**
    426 * URL-encodes a string
    427 * @param {*} str The string to url-encode.
    428 * @return {string} An encoded copy of {@code str} that is safe for urls.
    429 * Note that '#', ':', and other characters used to delimit portions
    430 * of URLs *will* be encoded.
    431 */
    432goog.string.urlEncode = function(str) {
    433 return encodeURIComponent(String(str));
    434};
    435
    436
    437/**
    438 * URL-decodes the string. We need to specially handle '+'s because
    439 * the javascript library doesn't convert them to spaces.
    440 * @param {string} str The string to url decode.
    441 * @return {string} The decoded {@code str}.
    442 */
    443goog.string.urlDecode = function(str) {
    444 return decodeURIComponent(str.replace(/\+/g, ' '));
    445};
    446
    447
    448/**
    449 * Converts \n to <br>s or <br />s.
    450 * @param {string} str The string in which to convert newlines.
    451 * @param {boolean=} opt_xml Whether to use XML compatible tags.
    452 * @return {string} A copy of {@code str} with converted newlines.
    453 */
    454goog.string.newLineToBr = function(str, opt_xml) {
    455 return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>');
    456};
    457
    458
    459/**
    460 * Escapes double quote '"' and single quote '\'' characters in addition to
    461 * '&', '<', and '>' so that a string can be included in an HTML tag attribute
    462 * value within double or single quotes.
    463 *
    464 * It should be noted that > doesn't need to be escaped for the HTML or XML to
    465 * be valid, but it has been decided to escape it for consistency with other
    466 * implementations.
    467 *
    468 * With goog.string.DETECT_DOUBLE_ESCAPING, this function escapes also the
    469 * lowercase letter "e".
    470 *
    471 * NOTE(user):
    472 * HtmlEscape is often called during the generation of large blocks of HTML.
    473 * Using statics for the regular expressions and strings is an optimization
    474 * that can more than half the amount of time IE spends in this function for
    475 * large apps, since strings and regexes both contribute to GC allocations.
    476 *
    477 * Testing for the presence of a character before escaping increases the number
    478 * of function calls, but actually provides a speed increase for the average
    479 * case -- since the average case often doesn't require the escaping of all 4
    480 * characters and indexOf() is much cheaper than replace().
    481 * The worst case does suffer slightly from the additional calls, therefore the
    482 * opt_isLikelyToContainHtmlChars option has been included for situations
    483 * where all 4 HTML entities are very likely to be present and need escaping.
    484 *
    485 * Some benchmarks (times tended to fluctuate +-0.05ms):
    486 * FireFox IE6
    487 * (no chars / average (mix of cases) / all 4 chars)
    488 * no checks 0.13 / 0.22 / 0.22 0.23 / 0.53 / 0.80
    489 * indexOf 0.08 / 0.17 / 0.26 0.22 / 0.54 / 0.84
    490 * indexOf + re test 0.07 / 0.17 / 0.28 0.19 / 0.50 / 0.85
    491 *
    492 * An additional advantage of checking if replace actually needs to be called
    493 * is a reduction in the number of object allocations, so as the size of the
    494 * application grows the difference between the various methods would increase.
    495 *
    496 * @param {string} str string to be escaped.
    497 * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see
    498 * if the character needs replacing - use this option if you expect each of
    499 * the characters to appear often. Leave false if you expect few html
    500 * characters to occur in your strings, such as if you are escaping HTML.
    501 * @return {string} An escaped copy of {@code str}.
    502 */
    503goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) {
    504
    505 if (opt_isLikelyToContainHtmlChars) {
    506 str = str.replace(goog.string.AMP_RE_, '&amp;')
    507 .replace(goog.string.LT_RE_, '&lt;')
    508 .replace(goog.string.GT_RE_, '&gt;')
    509 .replace(goog.string.QUOT_RE_, '&quot;')
    510 .replace(goog.string.SINGLE_QUOTE_RE_, '&#39;')
    511 .replace(goog.string.NULL_RE_, '&#0;');
    512 if (goog.string.DETECT_DOUBLE_ESCAPING) {
    513 str = str.replace(goog.string.E_RE_, '&#101;');
    514 }
    515 return str;
    516
    517 } else {
    518 // quick test helps in the case when there are no chars to replace, in
    519 // worst case this makes barely a difference to the time taken
    520 if (!goog.string.ALL_RE_.test(str)) return str;
    521
    522 // str.indexOf is faster than regex.test in this case
    523 if (str.indexOf('&') != -1) {
    524 str = str.replace(goog.string.AMP_RE_, '&amp;');
    525 }
    526 if (str.indexOf('<') != -1) {
    527 str = str.replace(goog.string.LT_RE_, '&lt;');
    528 }
    529 if (str.indexOf('>') != -1) {
    530 str = str.replace(goog.string.GT_RE_, '&gt;');
    531 }
    532 if (str.indexOf('"') != -1) {
    533 str = str.replace(goog.string.QUOT_RE_, '&quot;');
    534 }
    535 if (str.indexOf('\'') != -1) {
    536 str = str.replace(goog.string.SINGLE_QUOTE_RE_, '&#39;');
    537 }
    538 if (str.indexOf('\x00') != -1) {
    539 str = str.replace(goog.string.NULL_RE_, '&#0;');
    540 }
    541 if (goog.string.DETECT_DOUBLE_ESCAPING && str.indexOf('e') != -1) {
    542 str = str.replace(goog.string.E_RE_, '&#101;');
    543 }
    544 return str;
    545 }
    546};
    547
    548
    549/**
    550 * Regular expression that matches an ampersand, for use in escaping.
    551 * @const {!RegExp}
    552 * @private
    553 */
    554goog.string.AMP_RE_ = /&/g;
    555
    556
    557/**
    558 * Regular expression that matches a less than sign, for use in escaping.
    559 * @const {!RegExp}
    560 * @private
    561 */
    562goog.string.LT_RE_ = /</g;
    563
    564
    565/**
    566 * Regular expression that matches a greater than sign, for use in escaping.
    567 * @const {!RegExp}
    568 * @private
    569 */
    570goog.string.GT_RE_ = />/g;
    571
    572
    573/**
    574 * Regular expression that matches a double quote, for use in escaping.
    575 * @const {!RegExp}
    576 * @private
    577 */
    578goog.string.QUOT_RE_ = /"/g;
    579
    580
    581/**
    582 * Regular expression that matches a single quote, for use in escaping.
    583 * @const {!RegExp}
    584 * @private
    585 */
    586goog.string.SINGLE_QUOTE_RE_ = /'/g;
    587
    588
    589/**
    590 * Regular expression that matches null character, for use in escaping.
    591 * @const {!RegExp}
    592 * @private
    593 */
    594goog.string.NULL_RE_ = /\x00/g;
    595
    596
    597/**
    598 * Regular expression that matches a lowercase letter "e", for use in escaping.
    599 * @const {!RegExp}
    600 * @private
    601 */
    602goog.string.E_RE_ = /e/g;
    603
    604
    605/**
    606 * Regular expression that matches any character that needs to be escaped.
    607 * @const {!RegExp}
    608 * @private
    609 */
    610goog.string.ALL_RE_ = (goog.string.DETECT_DOUBLE_ESCAPING ?
    611 /[\x00&<>"'e]/ :
    612 /[\x00&<>"']/);
    613
    614
    615/**
    616 * Unescapes an HTML string.
    617 *
    618 * @param {string} str The string to unescape.
    619 * @return {string} An unescaped copy of {@code str}.
    620 */
    621goog.string.unescapeEntities = function(str) {
    622 if (goog.string.contains(str, '&')) {
    623 // We are careful not to use a DOM if we do not have one. We use the []
    624 // notation so that the JSCompiler will not complain about these objects and
    625 // fields in the case where we have no DOM.
    626 if ('document' in goog.global) {
    627 return goog.string.unescapeEntitiesUsingDom_(str);
    628 } else {
    629 // Fall back on pure XML entities
    630 return goog.string.unescapePureXmlEntities_(str);
    631 }
    632 }
    633 return str;
    634};
    635
    636
    637/**
    638 * Unescapes a HTML string using the provided document.
    639 *
    640 * @param {string} str The string to unescape.
    641 * @param {!Document} document A document to use in escaping the string.
    642 * @return {string} An unescaped copy of {@code str}.
    643 */
    644goog.string.unescapeEntitiesWithDocument = function(str, document) {
    645 if (goog.string.contains(str, '&')) {
    646 return goog.string.unescapeEntitiesUsingDom_(str, document);
    647 }
    648 return str;
    649};
    650
    651
    652/**
    653 * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric
    654 * entities. This function is XSS-safe and whitespace-preserving.
    655 * @private
    656 * @param {string} str The string to unescape.
    657 * @param {Document=} opt_document An optional document to use for creating
    658 * elements. If this is not specified then the default window.document
    659 * will be used.
    660 * @return {string} The unescaped {@code str} string.
    661 */
    662goog.string.unescapeEntitiesUsingDom_ = function(str, opt_document) {
    663 var seen = {'&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"'};
    664 var div;
    665 if (opt_document) {
    666 div = opt_document.createElement('div');
    667 } else {
    668 div = goog.global.document.createElement('div');
    669 }
    670 // Match as many valid entity characters as possible. If the actual entity
    671 // happens to be shorter, it will still work as innerHTML will return the
    672 // trailing characters unchanged. Since the entity characters do not include
    673 // open angle bracket, there is no chance of XSS from the innerHTML use.
    674 // Since no whitespace is passed to innerHTML, whitespace is preserved.
    675 return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) {
    676 // Check for cached entity.
    677 var value = seen[s];
    678 if (value) {
    679 return value;
    680 }
    681 // Check for numeric entity.
    682 if (entity.charAt(0) == '#') {
    683 // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex numbers.
    684 var n = Number('0' + entity.substr(1));
    685 if (!isNaN(n)) {
    686 value = String.fromCharCode(n);
    687 }
    688 }
    689 // Fall back to innerHTML otherwise.
    690 if (!value) {
    691 // Append a non-entity character to avoid a bug in Webkit that parses
    692 // an invalid entity at the end of innerHTML text as the empty string.
    693 div.innerHTML = s + ' ';
    694 // Then remove the trailing character from the result.
    695 value = div.firstChild.nodeValue.slice(0, -1);
    696 }
    697 // Cache and return.
    698 return seen[s] = value;
    699 });
    700};
    701
    702
    703/**
    704 * Unescapes XML entities.
    705 * @private
    706 * @param {string} str The string to unescape.
    707 * @return {string} An unescaped copy of {@code str}.
    708 */
    709goog.string.unescapePureXmlEntities_ = function(str) {
    710 return str.replace(/&([^;]+);/g, function(s, entity) {
    711 switch (entity) {
    712 case 'amp':
    713 return '&';
    714 case 'lt':
    715 return '<';
    716 case 'gt':
    717 return '>';
    718 case 'quot':
    719 return '"';
    720 default:
    721 if (entity.charAt(0) == '#') {
    722 // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex.
    723 var n = Number('0' + entity.substr(1));
    724 if (!isNaN(n)) {
    725 return String.fromCharCode(n);
    726 }
    727 }
    728 // For invalid entities we just return the entity
    729 return s;
    730 }
    731 });
    732};
    733
    734
    735/**
    736 * Regular expression that matches an HTML entity.
    737 * See also HTML5: Tokenization / Tokenizing character references.
    738 * @private
    739 * @type {!RegExp}
    740 */
    741goog.string.HTML_ENTITY_PATTERN_ = /&([^;\s<&]+);?/g;
    742
    743
    744/**
    745 * Do escaping of whitespace to preserve spatial formatting. We use character
    746 * entity #160 to make it safer for xml.
    747 * @param {string} str The string in which to escape whitespace.
    748 * @param {boolean=} opt_xml Whether to use XML compatible tags.
    749 * @return {string} An escaped copy of {@code str}.
    750 */
    751goog.string.whitespaceEscape = function(str, opt_xml) {
    752 // This doesn't use goog.string.preserveSpaces for backwards compatibility.
    753 return goog.string.newLineToBr(str.replace(/ /g, ' &#160;'), opt_xml);
    754};
    755
    756
    757/**
    758 * Preserve spaces that would be otherwise collapsed in HTML by replacing them
    759 * with non-breaking space Unicode characters.
    760 * @param {string} str The string in which to preserve whitespace.
    761 * @return {string} A copy of {@code str} with preserved whitespace.
    762 */
    763goog.string.preserveSpaces = function(str) {
    764 return str.replace(/(^|[\n ]) /g, '$1' + goog.string.Unicode.NBSP);
    765};
    766
    767
    768/**
    769 * Strip quote characters around a string. The second argument is a string of
    770 * characters to treat as quotes. This can be a single character or a string of
    771 * multiple character and in that case each of those are treated as possible
    772 * quote characters. For example:
    773 *
    774 * <pre>
    775 * goog.string.stripQuotes('"abc"', '"`') --> 'abc'
    776 * goog.string.stripQuotes('`abc`', '"`') --> 'abc'
    777 * </pre>
    778 *
    779 * @param {string} str The string to strip.
    780 * @param {string} quoteChars The quote characters to strip.
    781 * @return {string} A copy of {@code str} without the quotes.
    782 */
    783goog.string.stripQuotes = function(str, quoteChars) {
    784 var length = quoteChars.length;
    785 for (var i = 0; i < length; i++) {
    786 var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i);
    787 if (str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) {
    788 return str.substring(1, str.length - 1);
    789 }
    790 }
    791 return str;
    792};
    793
    794
    795/**
    796 * Truncates a string to a certain length and adds '...' if necessary. The
    797 * length also accounts for the ellipsis, so a maximum length of 10 and a string
    798 * 'Hello World!' produces 'Hello W...'.
    799 * @param {string} str The string to truncate.
    800 * @param {number} chars Max number of characters.
    801 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
    802 * characters from being cut off in the middle.
    803 * @return {string} The truncated {@code str} string.
    804 */
    805goog.string.truncate = function(str, chars, opt_protectEscapedCharacters) {
    806 if (opt_protectEscapedCharacters) {
    807 str = goog.string.unescapeEntities(str);
    808 }
    809
    810 if (str.length > chars) {
    811 str = str.substring(0, chars - 3) + '...';
    812 }
    813
    814 if (opt_protectEscapedCharacters) {
    815 str = goog.string.htmlEscape(str);
    816 }
    817
    818 return str;
    819};
    820
    821
    822/**
    823 * Truncate a string in the middle, adding "..." if necessary,
    824 * and favoring the beginning of the string.
    825 * @param {string} str The string to truncate the middle of.
    826 * @param {number} chars Max number of characters.
    827 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
    828 * characters from being cutoff in the middle.
    829 * @param {number=} opt_trailingChars Optional number of trailing characters to
    830 * leave at the end of the string, instead of truncating as close to the
    831 * middle as possible.
    832 * @return {string} A truncated copy of {@code str}.
    833 */
    834goog.string.truncateMiddle = function(str, chars,
    835 opt_protectEscapedCharacters, opt_trailingChars) {
    836 if (opt_protectEscapedCharacters) {
    837 str = goog.string.unescapeEntities(str);
    838 }
    839
    840 if (opt_trailingChars && str.length > chars) {
    841 if (opt_trailingChars > chars) {
    842 opt_trailingChars = chars;
    843 }
    844 var endPoint = str.length - opt_trailingChars;
    845 var startPoint = chars - opt_trailingChars;
    846 str = str.substring(0, startPoint) + '...' + str.substring(endPoint);
    847 } else if (str.length > chars) {
    848 // Favor the beginning of the string:
    849 var half = Math.floor(chars / 2);
    850 var endPos = str.length - half;
    851 half += chars % 2;
    852 str = str.substring(0, half) + '...' + str.substring(endPos);
    853 }
    854
    855 if (opt_protectEscapedCharacters) {
    856 str = goog.string.htmlEscape(str);
    857 }
    858
    859 return str;
    860};
    861
    862
    863/**
    864 * Special chars that need to be escaped for goog.string.quote.
    865 * @private
    866 * @type {Object}
    867 */
    868goog.string.specialEscapeChars_ = {
    869 '\0': '\\0',
    870 '\b': '\\b',
    871 '\f': '\\f',
    872 '\n': '\\n',
    873 '\r': '\\r',
    874 '\t': '\\t',
    875 '\x0B': '\\x0B', // '\v' is not supported in JScript
    876 '"': '\\"',
    877 '\\': '\\\\'
    878};
    879
    880
    881/**
    882 * Character mappings used internally for goog.string.escapeChar.
    883 * @private
    884 * @type {Object}
    885 */
    886goog.string.jsEscapeCache_ = {
    887 '\'': '\\\''
    888};
    889
    890
    891/**
    892 * Encloses a string in double quotes and escapes characters so that the
    893 * string is a valid JS string.
    894 * @param {string} s The string to quote.
    895 * @return {string} A copy of {@code s} surrounded by double quotes.
    896 */
    897goog.string.quote = function(s) {
    898 s = String(s);
    899 if (s.quote) {
    900 return s.quote();
    901 } else {
    902 var sb = ['"'];
    903 for (var i = 0; i < s.length; i++) {
    904 var ch = s.charAt(i);
    905 var cc = ch.charCodeAt(0);
    906 sb[i + 1] = goog.string.specialEscapeChars_[ch] ||
    907 ((cc > 31 && cc < 127) ? ch : goog.string.escapeChar(ch));
    908 }
    909 sb.push('"');
    910 return sb.join('');
    911 }
    912};
    913
    914
    915/**
    916 * Takes a string and returns the escaped string for that character.
    917 * @param {string} str The string to escape.
    918 * @return {string} An escaped string representing {@code str}.
    919 */
    920goog.string.escapeString = function(str) {
    921 var sb = [];
    922 for (var i = 0; i < str.length; i++) {
    923 sb[i] = goog.string.escapeChar(str.charAt(i));
    924 }
    925 return sb.join('');
    926};
    927
    928
    929/**
    930 * Takes a character and returns the escaped string for that character. For
    931 * example escapeChar(String.fromCharCode(15)) -> "\\x0E".
    932 * @param {string} c The character to escape.
    933 * @return {string} An escaped string representing {@code c}.
    934 */
    935goog.string.escapeChar = function(c) {
    936 if (c in goog.string.jsEscapeCache_) {
    937 return goog.string.jsEscapeCache_[c];
    938 }
    939
    940 if (c in goog.string.specialEscapeChars_) {
    941 return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c];
    942 }
    943
    944 var rv = c;
    945 var cc = c.charCodeAt(0);
    946 if (cc > 31 && cc < 127) {
    947 rv = c;
    948 } else {
    949 // tab is 9 but handled above
    950 if (cc < 256) {
    951 rv = '\\x';
    952 if (cc < 16 || cc > 256) {
    953 rv += '0';
    954 }
    955 } else {
    956 rv = '\\u';
    957 if (cc < 4096) { // \u1000
    958 rv += '0';
    959 }
    960 }
    961 rv += cc.toString(16).toUpperCase();
    962 }
    963
    964 return goog.string.jsEscapeCache_[c] = rv;
    965};
    966
    967
    968/**
    969 * Determines whether a string contains a substring.
    970 * @param {string} str The string to search.
    971 * @param {string} subString The substring to search for.
    972 * @return {boolean} Whether {@code str} contains {@code subString}.
    973 */
    974goog.string.contains = function(str, subString) {
    975 return str.indexOf(subString) != -1;
    976};
    977
    978
    979/**
    980 * Determines whether a string contains a substring, ignoring case.
    981 * @param {string} str The string to search.
    982 * @param {string} subString The substring to search for.
    983 * @return {boolean} Whether {@code str} contains {@code subString}.
    984 */
    985goog.string.caseInsensitiveContains = function(str, subString) {
    986 return goog.string.contains(str.toLowerCase(), subString.toLowerCase());
    987};
    988
    989
    990/**
    991 * Returns the non-overlapping occurrences of ss in s.
    992 * If either s or ss evalutes to false, then returns zero.
    993 * @param {string} s The string to look in.
    994 * @param {string} ss The string to look for.
    995 * @return {number} Number of occurrences of ss in s.
    996 */
    997goog.string.countOf = function(s, ss) {
    998 return s && ss ? s.split(ss).length - 1 : 0;
    999};
    1000
    1001
    1002/**
    1003 * Removes a substring of a specified length at a specific
    1004 * index in a string.
    1005 * @param {string} s The base string from which to remove.
    1006 * @param {number} index The index at which to remove the substring.
    1007 * @param {number} stringLength The length of the substring to remove.
    1008 * @return {string} A copy of {@code s} with the substring removed or the full
    1009 * string if nothing is removed or the input is invalid.
    1010 */
    1011goog.string.removeAt = function(s, index, stringLength) {
    1012 var resultStr = s;
    1013 // If the index is greater or equal to 0 then remove substring
    1014 if (index >= 0 && index < s.length && stringLength > 0) {
    1015 resultStr = s.substr(0, index) +
    1016 s.substr(index + stringLength, s.length - index - stringLength);
    1017 }
    1018 return resultStr;
    1019};
    1020
    1021
    1022/**
    1023 * Removes the first occurrence of a substring from a string.
    1024 * @param {string} s The base string from which to remove.
    1025 * @param {string} ss The string to remove.
    1026 * @return {string} A copy of {@code s} with {@code ss} removed or the full
    1027 * string if nothing is removed.
    1028 */
    1029goog.string.remove = function(s, ss) {
    1030 var re = new RegExp(goog.string.regExpEscape(ss), '');
    1031 return s.replace(re, '');
    1032};
    1033
    1034
    1035/**
    1036 * Removes all occurrences of a substring from a string.
    1037 * @param {string} s The base string from which to remove.
    1038 * @param {string} ss The string to remove.
    1039 * @return {string} A copy of {@code s} with {@code ss} removed or the full
    1040 * string if nothing is removed.
    1041 */
    1042goog.string.removeAll = function(s, ss) {
    1043 var re = new RegExp(goog.string.regExpEscape(ss), 'g');
    1044 return s.replace(re, '');
    1045};
    1046
    1047
    1048/**
    1049 * Escapes characters in the string that are not safe to use in a RegExp.
    1050 * @param {*} s The string to escape. If not a string, it will be casted
    1051 * to one.
    1052 * @return {string} A RegExp safe, escaped copy of {@code s}.
    1053 */
    1054goog.string.regExpEscape = function(s) {
    1055 return String(s).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
    1056 replace(/\x08/g, '\\x08');
    1057};
    1058
    1059
    1060/**
    1061 * Repeats a string n times.
    1062 * @param {string} string The string to repeat.
    1063 * @param {number} length The number of times to repeat.
    1064 * @return {string} A string containing {@code length} repetitions of
    1065 * {@code string}.
    1066 */
    1067goog.string.repeat = function(string, length) {
    1068 return new Array(length + 1).join(string);
    1069};
    1070
    1071
    1072/**
    1073 * Pads number to given length and optionally rounds it to a given precision.
    1074 * For example:
    1075 * <pre>padNumber(1.25, 2, 3) -> '01.250'
    1076 * padNumber(1.25, 2) -> '01.25'
    1077 * padNumber(1.25, 2, 1) -> '01.3'
    1078 * padNumber(1.25, 0) -> '1.25'</pre>
    1079 *
    1080 * @param {number} num The number to pad.
    1081 * @param {number} length The desired length.
    1082 * @param {number=} opt_precision The desired precision.
    1083 * @return {string} {@code num} as a string with the given options.
    1084 */
    1085goog.string.padNumber = function(num, length, opt_precision) {
    1086 var s = goog.isDef(opt_precision) ? num.toFixed(opt_precision) : String(num);
    1087 var index = s.indexOf('.');
    1088 if (index == -1) {
    1089 index = s.length;
    1090 }
    1091 return goog.string.repeat('0', Math.max(0, length - index)) + s;
    1092};
    1093
    1094
    1095/**
    1096 * Returns a string representation of the given object, with
    1097 * null and undefined being returned as the empty string.
    1098 *
    1099 * @param {*} obj The object to convert.
    1100 * @return {string} A string representation of the {@code obj}.
    1101 */
    1102goog.string.makeSafe = function(obj) {
    1103 return obj == null ? '' : String(obj);
    1104};
    1105
    1106
    1107/**
    1108 * Concatenates string expressions. This is useful
    1109 * since some browsers are very inefficient when it comes to using plus to
    1110 * concat strings. Be careful when using null and undefined here since
    1111 * these will not be included in the result. If you need to represent these
    1112 * be sure to cast the argument to a String first.
    1113 * For example:
    1114 * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd'
    1115 * buildString(null, undefined) -> ''
    1116 * </pre>
    1117 * @param {...*} var_args A list of strings to concatenate. If not a string,
    1118 * it will be casted to one.
    1119 * @return {string} The concatenation of {@code var_args}.
    1120 */
    1121goog.string.buildString = function(var_args) {
    1122 return Array.prototype.join.call(arguments, '');
    1123};
    1124
    1125
    1126/**
    1127 * Returns a string with at least 64-bits of randomness.
    1128 *
    1129 * Doesn't trust Javascript's random function entirely. Uses a combination of
    1130 * random and current timestamp, and then encodes the string in base-36 to
    1131 * make it shorter.
    1132 *
    1133 * @return {string} A random string, e.g. sn1s7vb4gcic.
    1134 */
    1135goog.string.getRandomString = function() {
    1136 var x = 2147483648;
    1137 return Math.floor(Math.random() * x).toString(36) +
    1138 Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36);
    1139};
    1140
    1141
    1142/**
    1143 * Compares two version numbers.
    1144 *
    1145 * @param {string|number} version1 Version of first item.
    1146 * @param {string|number} version2 Version of second item.
    1147 *
    1148 * @return {number} 1 if {@code version1} is higher.
    1149 * 0 if arguments are equal.
    1150 * -1 if {@code version2} is higher.
    1151 */
    1152goog.string.compareVersions = function(version1, version2) {
    1153 var order = 0;
    1154 // Trim leading and trailing whitespace and split the versions into
    1155 // subversions.
    1156 var v1Subs = goog.string.trim(String(version1)).split('.');
    1157 var v2Subs = goog.string.trim(String(version2)).split('.');
    1158 var subCount = Math.max(v1Subs.length, v2Subs.length);
    1159
    1160 // Iterate over the subversions, as long as they appear to be equivalent.
    1161 for (var subIdx = 0; order == 0 && subIdx < subCount; subIdx++) {
    1162 var v1Sub = v1Subs[subIdx] || '';
    1163 var v2Sub = v2Subs[subIdx] || '';
    1164
    1165 // Split the subversions into pairs of numbers and qualifiers (like 'b').
    1166 // Two different RegExp objects are needed because they are both using
    1167 // the 'g' flag.
    1168 var v1CompParser = new RegExp('(\\d*)(\\D*)', 'g');
    1169 var v2CompParser = new RegExp('(\\d*)(\\D*)', 'g');
    1170 do {
    1171 var v1Comp = v1CompParser.exec(v1Sub) || ['', '', ''];
    1172 var v2Comp = v2CompParser.exec(v2Sub) || ['', '', ''];
    1173 // Break if there are no more matches.
    1174 if (v1Comp[0].length == 0 && v2Comp[0].length == 0) {
    1175 break;
    1176 }
    1177
    1178 // Parse the numeric part of the subversion. A missing number is
    1179 // equivalent to 0.
    1180 var v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10);
    1181 var v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10);
    1182
    1183 // Compare the subversion components. The number has the highest
    1184 // precedence. Next, if the numbers are equal, a subversion without any
    1185 // qualifier is always higher than a subversion with any qualifier. Next,
    1186 // the qualifiers are compared as strings.
    1187 order = goog.string.compareElements_(v1CompNum, v2CompNum) ||
    1188 goog.string.compareElements_(v1Comp[2].length == 0,
    1189 v2Comp[2].length == 0) ||
    1190 goog.string.compareElements_(v1Comp[2], v2Comp[2]);
    1191 // Stop as soon as an inequality is discovered.
    1192 } while (order == 0);
    1193 }
    1194
    1195 return order;
    1196};
    1197
    1198
    1199/**
    1200 * Compares elements of a version number.
    1201 *
    1202 * @param {string|number|boolean} left An element from a version number.
    1203 * @param {string|number|boolean} right An element from a version number.
    1204 *
    1205 * @return {number} 1 if {@code left} is higher.
    1206 * 0 if arguments are equal.
    1207 * -1 if {@code right} is higher.
    1208 * @private
    1209 */
    1210goog.string.compareElements_ = function(left, right) {
    1211 if (left < right) {
    1212 return -1;
    1213 } else if (left > right) {
    1214 return 1;
    1215 }
    1216 return 0;
    1217};
    1218
    1219
    1220/**
    1221 * Maximum value of #goog.string.hashCode, exclusive. 2^32.
    1222 * @type {number}
    1223 * @private
    1224 */
    1225goog.string.HASHCODE_MAX_ = 0x100000000;
    1226
    1227
    1228/**
    1229 * String hash function similar to java.lang.String.hashCode().
    1230 * The hash code for a string is computed as
    1231 * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],
    1232 * where s[i] is the ith character of the string and n is the length of
    1233 * the string. We mod the result to make it between 0 (inclusive) and 2^32
    1234 * (exclusive).
    1235 * @param {string} str A string.
    1236 * @return {number} Hash value for {@code str}, between 0 (inclusive) and 2^32
    1237 * (exclusive). The empty string returns 0.
    1238 */
    1239goog.string.hashCode = function(str) {
    1240 var result = 0;
    1241 for (var i = 0; i < str.length; ++i) {
    1242 result = 31 * result + str.charCodeAt(i);
    1243 // Normalize to 4 byte range, 0 ... 2^32.
    1244 result %= goog.string.HASHCODE_MAX_;
    1245 }
    1246 return result;
    1247};
    1248
    1249
    1250/**
    1251 * The most recent unique ID. |0 is equivalent to Math.floor in this case.
    1252 * @type {number}
    1253 * @private
    1254 */
    1255goog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0;
    1256
    1257
    1258/**
    1259 * Generates and returns a string which is unique in the current document.
    1260 * This is useful, for example, to create unique IDs for DOM elements.
    1261 * @return {string} A unique id.
    1262 */
    1263goog.string.createUniqueString = function() {
    1264 return 'goog_' + goog.string.uniqueStringCounter_++;
    1265};
    1266
    1267
    1268/**
    1269 * Converts the supplied string to a number, which may be Infinity or NaN.
    1270 * This function strips whitespace: (toNumber(' 123') === 123)
    1271 * This function accepts scientific notation: (toNumber('1e1') === 10)
    1272 *
    1273 * This is better than Javascript's built-in conversions because, sadly:
    1274 * (Number(' ') === 0) and (parseFloat('123a') === 123)
    1275 *
    1276 * @param {string} str The string to convert.
    1277 * @return {number} The number the supplied string represents, or NaN.
    1278 */
    1279goog.string.toNumber = function(str) {
    1280 var num = Number(str);
    1281 if (num == 0 && goog.string.isEmpty(str)) {
    1282 return NaN;
    1283 }
    1284 return num;
    1285};
    1286
    1287
    1288/**
    1289 * Returns whether the given string is lower camel case (e.g. "isFooBar").
    1290 *
    1291 * Note that this assumes the string is entirely letters.
    1292 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
    1293 *
    1294 * @param {string} str String to test.
    1295 * @return {boolean} Whether the string is lower camel case.
    1296 */
    1297goog.string.isLowerCamelCase = function(str) {
    1298 return /^[a-z]+([A-Z][a-z]*)*$/.test(str);
    1299};
    1300
    1301
    1302/**
    1303 * Returns whether the given string is upper camel case (e.g. "FooBarBaz").
    1304 *
    1305 * Note that this assumes the string is entirely letters.
    1306 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
    1307 *
    1308 * @param {string} str String to test.
    1309 * @return {boolean} Whether the string is upper camel case.
    1310 */
    1311goog.string.isUpperCamelCase = function(str) {
    1312 return /^([A-Z][a-z]*)+$/.test(str);
    1313};
    1314
    1315
    1316/**
    1317 * Converts a string from selector-case to camelCase (e.g. from
    1318 * "multi-part-string" to "multiPartString"), useful for converting
    1319 * CSS selectors and HTML dataset keys to their equivalent JS properties.
    1320 * @param {string} str The string in selector-case form.
    1321 * @return {string} The string in camelCase form.
    1322 */
    1323goog.string.toCamelCase = function(str) {
    1324 return String(str).replace(/\-([a-z])/g, function(all, match) {
    1325 return match.toUpperCase();
    1326 });
    1327};
    1328
    1329
    1330/**
    1331 * Converts a string from camelCase to selector-case (e.g. from
    1332 * "multiPartString" to "multi-part-string"), useful for converting JS
    1333 * style and dataset properties to equivalent CSS selectors and HTML keys.
    1334 * @param {string} str The string in camelCase form.
    1335 * @return {string} The string in selector-case form.
    1336 */
    1337goog.string.toSelectorCase = function(str) {
    1338 return String(str).replace(/([A-Z])/g, '-$1').toLowerCase();
    1339};
    1340
    1341
    1342/**
    1343 * Converts a string into TitleCase. First character of the string is always
    1344 * capitalized in addition to the first letter of every subsequent word.
    1345 * Words are delimited by one or more whitespaces by default. Custom delimiters
    1346 * can optionally be specified to replace the default, which doesn't preserve
    1347 * whitespace delimiters and instead must be explicitly included if needed.
    1348 *
    1349 * Default delimiter => " ":
    1350 * goog.string.toTitleCase('oneTwoThree') => 'OneTwoThree'
    1351 * goog.string.toTitleCase('one two three') => 'One Two Three'
    1352 * goog.string.toTitleCase(' one two ') => ' One Two '
    1353 * goog.string.toTitleCase('one_two_three') => 'One_two_three'
    1354 * goog.string.toTitleCase('one-two-three') => 'One-two-three'
    1355 *
    1356 * Custom delimiter => "_-.":
    1357 * goog.string.toTitleCase('oneTwoThree', '_-.') => 'OneTwoThree'
    1358 * goog.string.toTitleCase('one two three', '_-.') => 'One two three'
    1359 * goog.string.toTitleCase(' one two ', '_-.') => ' one two '
    1360 * goog.string.toTitleCase('one_two_three', '_-.') => 'One_Two_Three'
    1361 * goog.string.toTitleCase('one-two-three', '_-.') => 'One-Two-Three'
    1362 * goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three'
    1363 * goog.string.toTitleCase('one. two. three', '_-.') => 'One. two. three'
    1364 * goog.string.toTitleCase('one-two.three', '_-.') => 'One-Two.Three'
    1365 *
    1366 * @param {string} str String value in camelCase form.
    1367 * @param {string=} opt_delimiters Custom delimiter character set used to
    1368 * distinguish words in the string value. Each character represents a
    1369 * single delimiter. When provided, default whitespace delimiter is
    1370 * overridden and must be explicitly included if needed.
    1371 * @return {string} String value in TitleCase form.
    1372 */
    1373goog.string.toTitleCase = function(str, opt_delimiters) {
    1374 var delimiters = goog.isString(opt_delimiters) ?
    1375 goog.string.regExpEscape(opt_delimiters) : '\\s';
    1376
    1377 // For IE8, we need to prevent using an empty character set. Otherwise,
    1378 // incorrect matching will occur.
    1379 delimiters = delimiters ? '|[' + delimiters + ']+' : '';
    1380
    1381 var regexp = new RegExp('(^' + delimiters + ')([a-z])', 'g');
    1382 return str.replace(regexp, function(all, p1, p2) {
    1383 return p1 + p2.toUpperCase();
    1384 });
    1385};
    1386
    1387
    1388/**
    1389 * Parse a string in decimal or hexidecimal ('0xFFFF') form.
    1390 *
    1391 * To parse a particular radix, please use parseInt(string, radix) directly. See
    1392 * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt
    1393 *
    1394 * This is a wrapper for the built-in parseInt function that will only parse
    1395 * numbers as base 10 or base 16. Some JS implementations assume strings
    1396 * starting with "0" are intended to be octal. ES3 allowed but discouraged
    1397 * this behavior. ES5 forbids it. This function emulates the ES5 behavior.
    1398 *
    1399 * For more information, see Mozilla JS Reference: http://goo.gl/8RiFj
    1400 *
    1401 * @param {string|number|null|undefined} value The value to be parsed.
    1402 * @return {number} The number, parsed. If the string failed to parse, this
    1403 * will be NaN.
    1404 */
    1405goog.string.parseInt = function(value) {
    1406 // Force finite numbers to strings.
    1407 if (isFinite(value)) {
    1408 value = String(value);
    1409 }
    1410
    1411 if (goog.isString(value)) {
    1412 // If the string starts with '0x' or '-0x', parse as hex.
    1413 return /^\s*-?0x/i.test(value) ?
    1414 parseInt(value, 16) : parseInt(value, 10);
    1415 }
    1416
    1417 return NaN;
    1418};
    1419
    1420
    1421/**
    1422 * Splits a string on a separator a limited number of times.
    1423 *
    1424 * This implementation is more similar to Python or Java, where the limit
    1425 * parameter specifies the maximum number of splits rather than truncating
    1426 * the number of results.
    1427 *
    1428 * See http://docs.python.org/2/library/stdtypes.html#str.split
    1429 * See JavaDoc: http://goo.gl/F2AsY
    1430 * See Mozilla reference: http://goo.gl/dZdZs
    1431 *
    1432 * @param {string} str String to split.
    1433 * @param {string} separator The separator.
    1434 * @param {number} limit The limit to the number of splits. The resulting array
    1435 * will have a maximum length of limit+1. Negative numbers are the same
    1436 * as zero.
    1437 * @return {!Array.<string>} The string, split.
    1438 */
    1439
    1440goog.string.splitLimit = function(str, separator, limit) {
    1441 var parts = str.split(separator);
    1442 var returnVal = [];
    1443
    1444 // Only continue doing this while we haven't hit the limit and we have
    1445 // parts left.
    1446 while (limit > 0 && parts.length) {
    1447 returnVal.push(parts.shift());
    1448 limit--;
    1449 }
    1450
    1451 // If there are remaining parts, append them to the end.
    1452 if (parts.length) {
    1453 returnVal.push(parts.join(separator));
    1454 }
    1455
    1456 return returnVal;
    1457};
    \ 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 * Regular expression used for splitting a string into substrings of fractional
    401 * numbers, integers, and non-numeric characters.
    402 * @type {RegExp}
    403 * @private
    404 */
    405goog.string.numerateCompareRegExp_ = /(\.\d+)|(\d+)|(\D+)/g;
    406
    407
    408/**
    409 * String comparison function that handles numbers in a way humans might expect.
    410 * Using this function, the string "File 2.jpg" sorts before "File 10.jpg". The
    411 * comparison is mostly case-insensitive, though strings that are identical
    412 * except for case are sorted with the upper-case strings before lower-case.
    413 *
    414 * This comparison function is significantly slower (about 500x) than either
    415 * the default or the case-insensitive compare. It should not be used in
    416 * time-critical code, but should be fast enough to sort several hundred short
    417 * strings (like filenames) with a reasonable delay.
    418 *
    419 * @param {string} str1 The string to compare in a numerically sensitive way.
    420 * @param {string} str2 The string to compare {@code str1} to.
    421 * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than
    422 * 0 if str1 > str2.
    423 */
    424goog.string.numerateCompare = function(str1, str2) {
    425 if (str1 == str2) {
    426 return 0;
    427 }
    428 if (!str1) {
    429 return -1;
    430 }
    431 if (!str2) {
    432 return 1;
    433 }
    434
    435 // Using match to split the entire string ahead of time turns out to be faster
    436 // for most inputs than using RegExp.exec or iterating over each character.
    437 var tokens1 = str1.toLowerCase().match(goog.string.numerateCompareRegExp_);
    438 var tokens2 = str2.toLowerCase().match(goog.string.numerateCompareRegExp_);
    439
    440 var count = Math.min(tokens1.length, tokens2.length);
    441
    442 for (var i = 0; i < count; i++) {
    443 var a = tokens1[i];
    444 var b = tokens2[i];
    445
    446 // Compare pairs of tokens, returning if one token sorts before the other.
    447 if (a != b) {
    448
    449 // Only if both tokens are integers is a special comparison required.
    450 // Decimal numbers are sorted as strings (e.g., '.09' < '.1').
    451 var num1 = parseInt(a, 10);
    452 if (!isNaN(num1)) {
    453 var num2 = parseInt(b, 10);
    454 if (!isNaN(num2) && num1 - num2) {
    455 return num1 - num2;
    456 }
    457 }
    458 return a < b ? -1 : 1;
    459 }
    460 }
    461
    462 // If one string is a substring of the other, the shorter string sorts first.
    463 if (tokens1.length != tokens2.length) {
    464 return tokens1.length - tokens2.length;
    465 }
    466
    467 // The two strings must be equivalent except for case (perfect equality is
    468 // tested at the head of the function.) Revert to default ASCII-betical string
    469 // comparison to stablize the sort.
    470 return str1 < str2 ? -1 : 1;
    471};
    472
    473
    474/**
    475 * URL-encodes a string
    476 * @param {*} str The string to url-encode.
    477 * @return {string} An encoded copy of {@code str} that is safe for urls.
    478 * Note that '#', ':', and other characters used to delimit portions
    479 * of URLs *will* be encoded.
    480 */
    481goog.string.urlEncode = function(str) {
    482 return encodeURIComponent(String(str));
    483};
    484
    485
    486/**
    487 * URL-decodes the string. We need to specially handle '+'s because
    488 * the javascript library doesn't convert them to spaces.
    489 * @param {string} str The string to url decode.
    490 * @return {string} The decoded {@code str}.
    491 */
    492goog.string.urlDecode = function(str) {
    493 return decodeURIComponent(str.replace(/\+/g, ' '));
    494};
    495
    496
    497/**
    498 * Converts \n to <br>s or <br />s.
    499 * @param {string} str The string in which to convert newlines.
    500 * @param {boolean=} opt_xml Whether to use XML compatible tags.
    501 * @return {string} A copy of {@code str} with converted newlines.
    502 */
    503goog.string.newLineToBr = function(str, opt_xml) {
    504 return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>');
    505};
    506
    507
    508/**
    509 * Escapes double quote '"' and single quote '\'' characters in addition to
    510 * '&', '<', and '>' so that a string can be included in an HTML tag attribute
    511 * value within double or single quotes.
    512 *
    513 * It should be noted that > doesn't need to be escaped for the HTML or XML to
    514 * be valid, but it has been decided to escape it for consistency with other
    515 * implementations.
    516 *
    517 * With goog.string.DETECT_DOUBLE_ESCAPING, this function escapes also the
    518 * lowercase letter "e".
    519 *
    520 * NOTE(user):
    521 * HtmlEscape is often called during the generation of large blocks of HTML.
    522 * Using statics for the regular expressions and strings is an optimization
    523 * that can more than half the amount of time IE spends in this function for
    524 * large apps, since strings and regexes both contribute to GC allocations.
    525 *
    526 * Testing for the presence of a character before escaping increases the number
    527 * of function calls, but actually provides a speed increase for the average
    528 * case -- since the average case often doesn't require the escaping of all 4
    529 * characters and indexOf() is much cheaper than replace().
    530 * The worst case does suffer slightly from the additional calls, therefore the
    531 * opt_isLikelyToContainHtmlChars option has been included for situations
    532 * where all 4 HTML entities are very likely to be present and need escaping.
    533 *
    534 * Some benchmarks (times tended to fluctuate +-0.05ms):
    535 * FireFox IE6
    536 * (no chars / average (mix of cases) / all 4 chars)
    537 * no checks 0.13 / 0.22 / 0.22 0.23 / 0.53 / 0.80
    538 * indexOf 0.08 / 0.17 / 0.26 0.22 / 0.54 / 0.84
    539 * indexOf + re test 0.07 / 0.17 / 0.28 0.19 / 0.50 / 0.85
    540 *
    541 * An additional advantage of checking if replace actually needs to be called
    542 * is a reduction in the number of object allocations, so as the size of the
    543 * application grows the difference between the various methods would increase.
    544 *
    545 * @param {string} str string to be escaped.
    546 * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see
    547 * if the character needs replacing - use this option if you expect each of
    548 * the characters to appear often. Leave false if you expect few html
    549 * characters to occur in your strings, such as if you are escaping HTML.
    550 * @return {string} An escaped copy of {@code str}.
    551 */
    552goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) {
    553
    554 if (opt_isLikelyToContainHtmlChars) {
    555 str = str.replace(goog.string.AMP_RE_, '&amp;')
    556 .replace(goog.string.LT_RE_, '&lt;')
    557 .replace(goog.string.GT_RE_, '&gt;')
    558 .replace(goog.string.QUOT_RE_, '&quot;')
    559 .replace(goog.string.SINGLE_QUOTE_RE_, '&#39;')
    560 .replace(goog.string.NULL_RE_, '&#0;');
    561 if (goog.string.DETECT_DOUBLE_ESCAPING) {
    562 str = str.replace(goog.string.E_RE_, '&#101;');
    563 }
    564 return str;
    565
    566 } else {
    567 // quick test helps in the case when there are no chars to replace, in
    568 // worst case this makes barely a difference to the time taken
    569 if (!goog.string.ALL_RE_.test(str)) return str;
    570
    571 // str.indexOf is faster than regex.test in this case
    572 if (str.indexOf('&') != -1) {
    573 str = str.replace(goog.string.AMP_RE_, '&amp;');
    574 }
    575 if (str.indexOf('<') != -1) {
    576 str = str.replace(goog.string.LT_RE_, '&lt;');
    577 }
    578 if (str.indexOf('>') != -1) {
    579 str = str.replace(goog.string.GT_RE_, '&gt;');
    580 }
    581 if (str.indexOf('"') != -1) {
    582 str = str.replace(goog.string.QUOT_RE_, '&quot;');
    583 }
    584 if (str.indexOf('\'') != -1) {
    585 str = str.replace(goog.string.SINGLE_QUOTE_RE_, '&#39;');
    586 }
    587 if (str.indexOf('\x00') != -1) {
    588 str = str.replace(goog.string.NULL_RE_, '&#0;');
    589 }
    590 if (goog.string.DETECT_DOUBLE_ESCAPING && str.indexOf('e') != -1) {
    591 str = str.replace(goog.string.E_RE_, '&#101;');
    592 }
    593 return str;
    594 }
    595};
    596
    597
    598/**
    599 * Regular expression that matches an ampersand, for use in escaping.
    600 * @const {!RegExp}
    601 * @private
    602 */
    603goog.string.AMP_RE_ = /&/g;
    604
    605
    606/**
    607 * Regular expression that matches a less than sign, for use in escaping.
    608 * @const {!RegExp}
    609 * @private
    610 */
    611goog.string.LT_RE_ = /</g;
    612
    613
    614/**
    615 * Regular expression that matches a greater than sign, for use in escaping.
    616 * @const {!RegExp}
    617 * @private
    618 */
    619goog.string.GT_RE_ = />/g;
    620
    621
    622/**
    623 * Regular expression that matches a double quote, for use in escaping.
    624 * @const {!RegExp}
    625 * @private
    626 */
    627goog.string.QUOT_RE_ = /"/g;
    628
    629
    630/**
    631 * Regular expression that matches a single quote, for use in escaping.
    632 * @const {!RegExp}
    633 * @private
    634 */
    635goog.string.SINGLE_QUOTE_RE_ = /'/g;
    636
    637
    638/**
    639 * Regular expression that matches null character, for use in escaping.
    640 * @const {!RegExp}
    641 * @private
    642 */
    643goog.string.NULL_RE_ = /\x00/g;
    644
    645
    646/**
    647 * Regular expression that matches a lowercase letter "e", for use in escaping.
    648 * @const {!RegExp}
    649 * @private
    650 */
    651goog.string.E_RE_ = /e/g;
    652
    653
    654/**
    655 * Regular expression that matches any character that needs to be escaped.
    656 * @const {!RegExp}
    657 * @private
    658 */
    659goog.string.ALL_RE_ = (goog.string.DETECT_DOUBLE_ESCAPING ?
    660 /[\x00&<>"'e]/ :
    661 /[\x00&<>"']/);
    662
    663
    664/**
    665 * Unescapes an HTML string.
    666 *
    667 * @param {string} str The string to unescape.
    668 * @return {string} An unescaped copy of {@code str}.
    669 */
    670goog.string.unescapeEntities = function(str) {
    671 if (goog.string.contains(str, '&')) {
    672 // We are careful not to use a DOM if we do not have one or we explicitly
    673 // requested non-DOM html unescaping.
    674 if (!goog.string.FORCE_NON_DOM_HTML_UNESCAPING &&
    675 'document' in goog.global) {
    676 return goog.string.unescapeEntitiesUsingDom_(str);
    677 } else {
    678 // Fall back on pure XML entities
    679 return goog.string.unescapePureXmlEntities_(str);
    680 }
    681 }
    682 return str;
    683};
    684
    685
    686/**
    687 * Unescapes a HTML string using the provided document.
    688 *
    689 * @param {string} str The string to unescape.
    690 * @param {!Document} document A document to use in escaping the string.
    691 * @return {string} An unescaped copy of {@code str}.
    692 */
    693goog.string.unescapeEntitiesWithDocument = function(str, document) {
    694 if (goog.string.contains(str, '&')) {
    695 return goog.string.unescapeEntitiesUsingDom_(str, document);
    696 }
    697 return str;
    698};
    699
    700
    701/**
    702 * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric
    703 * entities. This function is XSS-safe and whitespace-preserving.
    704 * @private
    705 * @param {string} str The string to unescape.
    706 * @param {Document=} opt_document An optional document to use for creating
    707 * elements. If this is not specified then the default window.document
    708 * will be used.
    709 * @return {string} The unescaped {@code str} string.
    710 */
    711goog.string.unescapeEntitiesUsingDom_ = function(str, opt_document) {
    712 /** @type {!Object<string, string>} */
    713 var seen = {'&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"'};
    714 var div;
    715 if (opt_document) {
    716 div = opt_document.createElement('div');
    717 } else {
    718 div = goog.global.document.createElement('div');
    719 }
    720 // Match as many valid entity characters as possible. If the actual entity
    721 // happens to be shorter, it will still work as innerHTML will return the
    722 // trailing characters unchanged. Since the entity characters do not include
    723 // open angle bracket, there is no chance of XSS from the innerHTML use.
    724 // Since no whitespace is passed to innerHTML, whitespace is preserved.
    725 return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) {
    726 // Check for cached entity.
    727 var value = seen[s];
    728 if (value) {
    729 return value;
    730 }
    731 // Check for numeric entity.
    732 if (entity.charAt(0) == '#') {
    733 // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex numbers.
    734 var n = Number('0' + entity.substr(1));
    735 if (!isNaN(n)) {
    736 value = String.fromCharCode(n);
    737 }
    738 }
    739 // Fall back to innerHTML otherwise.
    740 if (!value) {
    741 // Append a non-entity character to avoid a bug in Webkit that parses
    742 // an invalid entity at the end of innerHTML text as the empty string.
    743 div.innerHTML = s + ' ';
    744 // Then remove the trailing character from the result.
    745 value = div.firstChild.nodeValue.slice(0, -1);
    746 }
    747 // Cache and return.
    748 return seen[s] = value;
    749 });
    750};
    751
    752
    753/**
    754 * Unescapes XML entities.
    755 * @private
    756 * @param {string} str The string to unescape.
    757 * @return {string} An unescaped copy of {@code str}.
    758 */
    759goog.string.unescapePureXmlEntities_ = function(str) {
    760 return str.replace(/&([^;]+);/g, function(s, entity) {
    761 switch (entity) {
    762 case 'amp':
    763 return '&';
    764 case 'lt':
    765 return '<';
    766 case 'gt':
    767 return '>';
    768 case 'quot':
    769 return '"';
    770 default:
    771 if (entity.charAt(0) == '#') {
    772 // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex.
    773 var n = Number('0' + entity.substr(1));
    774 if (!isNaN(n)) {
    775 return String.fromCharCode(n);
    776 }
    777 }
    778 // For invalid entities we just return the entity
    779 return s;
    780 }
    781 });
    782};
    783
    784
    785/**
    786 * Regular expression that matches an HTML entity.
    787 * See also HTML5: Tokenization / Tokenizing character references.
    788 * @private
    789 * @type {!RegExp}
    790 */
    791goog.string.HTML_ENTITY_PATTERN_ = /&([^;\s<&]+);?/g;
    792
    793
    794/**
    795 * Do escaping of whitespace to preserve spatial formatting. We use character
    796 * entity #160 to make it safer for xml.
    797 * @param {string} str The string in which to escape whitespace.
    798 * @param {boolean=} opt_xml Whether to use XML compatible tags.
    799 * @return {string} An escaped copy of {@code str}.
    800 */
    801goog.string.whitespaceEscape = function(str, opt_xml) {
    802 // This doesn't use goog.string.preserveSpaces for backwards compatibility.
    803 return goog.string.newLineToBr(str.replace(/ /g, ' &#160;'), opt_xml);
    804};
    805
    806
    807/**
    808 * Preserve spaces that would be otherwise collapsed in HTML by replacing them
    809 * with non-breaking space Unicode characters.
    810 * @param {string} str The string in which to preserve whitespace.
    811 * @return {string} A copy of {@code str} with preserved whitespace.
    812 */
    813goog.string.preserveSpaces = function(str) {
    814 return str.replace(/(^|[\n ]) /g, '$1' + goog.string.Unicode.NBSP);
    815};
    816
    817
    818/**
    819 * Strip quote characters around a string. The second argument is a string of
    820 * characters to treat as quotes. This can be a single character or a string of
    821 * multiple character and in that case each of those are treated as possible
    822 * quote characters. For example:
    823 *
    824 * <pre>
    825 * goog.string.stripQuotes('"abc"', '"`') --> 'abc'
    826 * goog.string.stripQuotes('`abc`', '"`') --> 'abc'
    827 * </pre>
    828 *
    829 * @param {string} str The string to strip.
    830 * @param {string} quoteChars The quote characters to strip.
    831 * @return {string} A copy of {@code str} without the quotes.
    832 */
    833goog.string.stripQuotes = function(str, quoteChars) {
    834 var length = quoteChars.length;
    835 for (var i = 0; i < length; i++) {
    836 var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i);
    837 if (str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) {
    838 return str.substring(1, str.length - 1);
    839 }
    840 }
    841 return str;
    842};
    843
    844
    845/**
    846 * Truncates a string to a certain length and adds '...' if necessary. The
    847 * length also accounts for the ellipsis, so a maximum length of 10 and a string
    848 * 'Hello World!' produces 'Hello W...'.
    849 * @param {string} str The string to truncate.
    850 * @param {number} chars Max number of characters.
    851 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
    852 * characters from being cut off in the middle.
    853 * @return {string} The truncated {@code str} string.
    854 */
    855goog.string.truncate = function(str, chars, opt_protectEscapedCharacters) {
    856 if (opt_protectEscapedCharacters) {
    857 str = goog.string.unescapeEntities(str);
    858 }
    859
    860 if (str.length > chars) {
    861 str = str.substring(0, chars - 3) + '...';
    862 }
    863
    864 if (opt_protectEscapedCharacters) {
    865 str = goog.string.htmlEscape(str);
    866 }
    867
    868 return str;
    869};
    870
    871
    872/**
    873 * Truncate a string in the middle, adding "..." if necessary,
    874 * and favoring the beginning of the string.
    875 * @param {string} str The string to truncate the middle of.
    876 * @param {number} chars Max number of characters.
    877 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
    878 * characters from being cutoff in the middle.
    879 * @param {number=} opt_trailingChars Optional number of trailing characters to
    880 * leave at the end of the string, instead of truncating as close to the
    881 * middle as possible.
    882 * @return {string} A truncated copy of {@code str}.
    883 */
    884goog.string.truncateMiddle = function(str, chars,
    885 opt_protectEscapedCharacters, opt_trailingChars) {
    886 if (opt_protectEscapedCharacters) {
    887 str = goog.string.unescapeEntities(str);
    888 }
    889
    890 if (opt_trailingChars && str.length > chars) {
    891 if (opt_trailingChars > chars) {
    892 opt_trailingChars = chars;
    893 }
    894 var endPoint = str.length - opt_trailingChars;
    895 var startPoint = chars - opt_trailingChars;
    896 str = str.substring(0, startPoint) + '...' + str.substring(endPoint);
    897 } else if (str.length > chars) {
    898 // Favor the beginning of the string:
    899 var half = Math.floor(chars / 2);
    900 var endPos = str.length - half;
    901 half += chars % 2;
    902 str = str.substring(0, half) + '...' + str.substring(endPos);
    903 }
    904
    905 if (opt_protectEscapedCharacters) {
    906 str = goog.string.htmlEscape(str);
    907 }
    908
    909 return str;
    910};
    911
    912
    913/**
    914 * Special chars that need to be escaped for goog.string.quote.
    915 * @private {!Object<string, string>}
    916 */
    917goog.string.specialEscapeChars_ = {
    918 '\0': '\\0',
    919 '\b': '\\b',
    920 '\f': '\\f',
    921 '\n': '\\n',
    922 '\r': '\\r',
    923 '\t': '\\t',
    924 '\x0B': '\\x0B', // '\v' is not supported in JScript
    925 '"': '\\"',
    926 '\\': '\\\\'
    927};
    928
    929
    930/**
    931 * Character mappings used internally for goog.string.escapeChar.
    932 * @private {!Object<string, string>}
    933 */
    934goog.string.jsEscapeCache_ = {
    935 '\'': '\\\''
    936};
    937
    938
    939/**
    940 * Encloses a string in double quotes and escapes characters so that the
    941 * string is a valid JS string.
    942 * @param {string} s The string to quote.
    943 * @return {string} A copy of {@code s} surrounded by double quotes.
    944 */
    945goog.string.quote = function(s) {
    946 s = String(s);
    947 if (s.quote) {
    948 return s.quote();
    949 } else {
    950 var sb = ['"'];
    951 for (var i = 0; i < s.length; i++) {
    952 var ch = s.charAt(i);
    953 var cc = ch.charCodeAt(0);
    954 sb[i + 1] = goog.string.specialEscapeChars_[ch] ||
    955 ((cc > 31 && cc < 127) ? ch : goog.string.escapeChar(ch));
    956 }
    957 sb.push('"');
    958 return sb.join('');
    959 }
    960};
    961
    962
    963/**
    964 * Takes a string and returns the escaped string for that character.
    965 * @param {string} str The string to escape.
    966 * @return {string} An escaped string representing {@code str}.
    967 */
    968goog.string.escapeString = function(str) {
    969 var sb = [];
    970 for (var i = 0; i < str.length; i++) {
    971 sb[i] = goog.string.escapeChar(str.charAt(i));
    972 }
    973 return sb.join('');
    974};
    975
    976
    977/**
    978 * Takes a character and returns the escaped string for that character. For
    979 * example escapeChar(String.fromCharCode(15)) -> "\\x0E".
    980 * @param {string} c The character to escape.
    981 * @return {string} An escaped string representing {@code c}.
    982 */
    983goog.string.escapeChar = function(c) {
    984 if (c in goog.string.jsEscapeCache_) {
    985 return goog.string.jsEscapeCache_[c];
    986 }
    987
    988 if (c in goog.string.specialEscapeChars_) {
    989 return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c];
    990 }
    991
    992 var rv = c;
    993 var cc = c.charCodeAt(0);
    994 if (cc > 31 && cc < 127) {
    995 rv = c;
    996 } else {
    997 // tab is 9 but handled above
    998 if (cc < 256) {
    999 rv = '\\x';
    1000 if (cc < 16 || cc > 256) {
    1001 rv += '0';
    1002 }
    1003 } else {
    1004 rv = '\\u';
    1005 if (cc < 4096) { // \u1000
    1006 rv += '0';
    1007 }
    1008 }
    1009 rv += cc.toString(16).toUpperCase();
    1010 }
    1011
    1012 return goog.string.jsEscapeCache_[c] = rv;
    1013};
    1014
    1015
    1016/**
    1017 * Determines whether a string contains a substring.
    1018 * @param {string} str The string to search.
    1019 * @param {string} subString The substring to search for.
    1020 * @return {boolean} Whether {@code str} contains {@code subString}.
    1021 */
    1022goog.string.contains = function(str, subString) {
    1023 return str.indexOf(subString) != -1;
    1024};
    1025
    1026
    1027/**
    1028 * Determines whether a string contains a substring, ignoring case.
    1029 * @param {string} str The string to search.
    1030 * @param {string} subString The substring to search for.
    1031 * @return {boolean} Whether {@code str} contains {@code subString}.
    1032 */
    1033goog.string.caseInsensitiveContains = function(str, subString) {
    1034 return goog.string.contains(str.toLowerCase(), subString.toLowerCase());
    1035};
    1036
    1037
    1038/**
    1039 * Returns the non-overlapping occurrences of ss in s.
    1040 * If either s or ss evalutes to false, then returns zero.
    1041 * @param {string} s The string to look in.
    1042 * @param {string} ss The string to look for.
    1043 * @return {number} Number of occurrences of ss in s.
    1044 */
    1045goog.string.countOf = function(s, ss) {
    1046 return s && ss ? s.split(ss).length - 1 : 0;
    1047};
    1048
    1049
    1050/**
    1051 * Removes a substring of a specified length at a specific
    1052 * index in a string.
    1053 * @param {string} s The base string from which to remove.
    1054 * @param {number} index The index at which to remove the substring.
    1055 * @param {number} stringLength The length of the substring to remove.
    1056 * @return {string} A copy of {@code s} with the substring removed or the full
    1057 * string if nothing is removed or the input is invalid.
    1058 */
    1059goog.string.removeAt = function(s, index, stringLength) {
    1060 var resultStr = s;
    1061 // If the index is greater or equal to 0 then remove substring
    1062 if (index >= 0 && index < s.length && stringLength > 0) {
    1063 resultStr = s.substr(0, index) +
    1064 s.substr(index + stringLength, s.length - index - stringLength);
    1065 }
    1066 return resultStr;
    1067};
    1068
    1069
    1070/**
    1071 * Removes the first occurrence of a substring from a string.
    1072 * @param {string} s The base string from which to remove.
    1073 * @param {string} ss The string to remove.
    1074 * @return {string} A copy of {@code s} with {@code ss} removed or the full
    1075 * string if nothing is removed.
    1076 */
    1077goog.string.remove = function(s, ss) {
    1078 var re = new RegExp(goog.string.regExpEscape(ss), '');
    1079 return s.replace(re, '');
    1080};
    1081
    1082
    1083/**
    1084 * Removes all occurrences of a substring from a string.
    1085 * @param {string} s The base string from which to remove.
    1086 * @param {string} ss The string to remove.
    1087 * @return {string} A copy of {@code s} with {@code ss} removed or the full
    1088 * string if nothing is removed.
    1089 */
    1090goog.string.removeAll = function(s, ss) {
    1091 var re = new RegExp(goog.string.regExpEscape(ss), 'g');
    1092 return s.replace(re, '');
    1093};
    1094
    1095
    1096/**
    1097 * Escapes characters in the string that are not safe to use in a RegExp.
    1098 * @param {*} s The string to escape. If not a string, it will be casted
    1099 * to one.
    1100 * @return {string} A RegExp safe, escaped copy of {@code s}.
    1101 */
    1102goog.string.regExpEscape = function(s) {
    1103 return String(s).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
    1104 replace(/\x08/g, '\\x08');
    1105};
    1106
    1107
    1108/**
    1109 * Repeats a string n times.
    1110 * @param {string} string The string to repeat.
    1111 * @param {number} length The number of times to repeat.
    1112 * @return {string} A string containing {@code length} repetitions of
    1113 * {@code string}.
    1114 */
    1115goog.string.repeat = function(string, length) {
    1116 return new Array(length + 1).join(string);
    1117};
    1118
    1119
    1120/**
    1121 * Pads number to given length and optionally rounds it to a given precision.
    1122 * For example:
    1123 * <pre>padNumber(1.25, 2, 3) -> '01.250'
    1124 * padNumber(1.25, 2) -> '01.25'
    1125 * padNumber(1.25, 2, 1) -> '01.3'
    1126 * padNumber(1.25, 0) -> '1.25'</pre>
    1127 *
    1128 * @param {number} num The number to pad.
    1129 * @param {number} length The desired length.
    1130 * @param {number=} opt_precision The desired precision.
    1131 * @return {string} {@code num} as a string with the given options.
    1132 */
    1133goog.string.padNumber = function(num, length, opt_precision) {
    1134 var s = goog.isDef(opt_precision) ? num.toFixed(opt_precision) : String(num);
    1135 var index = s.indexOf('.');
    1136 if (index == -1) {
    1137 index = s.length;
    1138 }
    1139 return goog.string.repeat('0', Math.max(0, length - index)) + s;
    1140};
    1141
    1142
    1143/**
    1144 * Returns a string representation of the given object, with
    1145 * null and undefined being returned as the empty string.
    1146 *
    1147 * @param {*} obj The object to convert.
    1148 * @return {string} A string representation of the {@code obj}.
    1149 */
    1150goog.string.makeSafe = function(obj) {
    1151 return obj == null ? '' : String(obj);
    1152};
    1153
    1154
    1155/**
    1156 * Concatenates string expressions. This is useful
    1157 * since some browsers are very inefficient when it comes to using plus to
    1158 * concat strings. Be careful when using null and undefined here since
    1159 * these will not be included in the result. If you need to represent these
    1160 * be sure to cast the argument to a String first.
    1161 * For example:
    1162 * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd'
    1163 * buildString(null, undefined) -> ''
    1164 * </pre>
    1165 * @param {...*} var_args A list of strings to concatenate. If not a string,
    1166 * it will be casted to one.
    1167 * @return {string} The concatenation of {@code var_args}.
    1168 */
    1169goog.string.buildString = function(var_args) {
    1170 return Array.prototype.join.call(arguments, '');
    1171};
    1172
    1173
    1174/**
    1175 * Returns a string with at least 64-bits of randomness.
    1176 *
    1177 * Doesn't trust Javascript's random function entirely. Uses a combination of
    1178 * random and current timestamp, and then encodes the string in base-36 to
    1179 * make it shorter.
    1180 *
    1181 * @return {string} A random string, e.g. sn1s7vb4gcic.
    1182 */
    1183goog.string.getRandomString = function() {
    1184 var x = 2147483648;
    1185 return Math.floor(Math.random() * x).toString(36) +
    1186 Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36);
    1187};
    1188
    1189
    1190/**
    1191 * Compares two version numbers.
    1192 *
    1193 * @param {string|number} version1 Version of first item.
    1194 * @param {string|number} version2 Version of second item.
    1195 *
    1196 * @return {number} 1 if {@code version1} is higher.
    1197 * 0 if arguments are equal.
    1198 * -1 if {@code version2} is higher.
    1199 */
    1200goog.string.compareVersions = function(version1, version2) {
    1201 var order = 0;
    1202 // Trim leading and trailing whitespace and split the versions into
    1203 // subversions.
    1204 var v1Subs = goog.string.trim(String(version1)).split('.');
    1205 var v2Subs = goog.string.trim(String(version2)).split('.');
    1206 var subCount = Math.max(v1Subs.length, v2Subs.length);
    1207
    1208 // Iterate over the subversions, as long as they appear to be equivalent.
    1209 for (var subIdx = 0; order == 0 && subIdx < subCount; subIdx++) {
    1210 var v1Sub = v1Subs[subIdx] || '';
    1211 var v2Sub = v2Subs[subIdx] || '';
    1212
    1213 // Split the subversions into pairs of numbers and qualifiers (like 'b').
    1214 // Two different RegExp objects are needed because they are both using
    1215 // the 'g' flag.
    1216 var v1CompParser = new RegExp('(\\d*)(\\D*)', 'g');
    1217 var v2CompParser = new RegExp('(\\d*)(\\D*)', 'g');
    1218 do {
    1219 var v1Comp = v1CompParser.exec(v1Sub) || ['', '', ''];
    1220 var v2Comp = v2CompParser.exec(v2Sub) || ['', '', ''];
    1221 // Break if there are no more matches.
    1222 if (v1Comp[0].length == 0 && v2Comp[0].length == 0) {
    1223 break;
    1224 }
    1225
    1226 // Parse the numeric part of the subversion. A missing number is
    1227 // equivalent to 0.
    1228 var v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10);
    1229 var v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10);
    1230
    1231 // Compare the subversion components. The number has the highest
    1232 // precedence. Next, if the numbers are equal, a subversion without any
    1233 // qualifier is always higher than a subversion with any qualifier. Next,
    1234 // the qualifiers are compared as strings.
    1235 order = goog.string.compareElements_(v1CompNum, v2CompNum) ||
    1236 goog.string.compareElements_(v1Comp[2].length == 0,
    1237 v2Comp[2].length == 0) ||
    1238 goog.string.compareElements_(v1Comp[2], v2Comp[2]);
    1239 // Stop as soon as an inequality is discovered.
    1240 } while (order == 0);
    1241 }
    1242
    1243 return order;
    1244};
    1245
    1246
    1247/**
    1248 * Compares elements of a version number.
    1249 *
    1250 * @param {string|number|boolean} left An element from a version number.
    1251 * @param {string|number|boolean} right An element from a version number.
    1252 *
    1253 * @return {number} 1 if {@code left} is higher.
    1254 * 0 if arguments are equal.
    1255 * -1 if {@code right} is higher.
    1256 * @private
    1257 */
    1258goog.string.compareElements_ = function(left, right) {
    1259 if (left < right) {
    1260 return -1;
    1261 } else if (left > right) {
    1262 return 1;
    1263 }
    1264 return 0;
    1265};
    1266
    1267
    1268/**
    1269 * Maximum value of #goog.string.hashCode, exclusive. 2^32.
    1270 * @type {number}
    1271 * @private
    1272 */
    1273goog.string.HASHCODE_MAX_ = 0x100000000;
    1274
    1275
    1276/**
    1277 * String hash function similar to java.lang.String.hashCode().
    1278 * The hash code for a string is computed as
    1279 * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],
    1280 * where s[i] is the ith character of the string and n is the length of
    1281 * the string. We mod the result to make it between 0 (inclusive) and 2^32
    1282 * (exclusive).
    1283 * @param {string} str A string.
    1284 * @return {number} Hash value for {@code str}, between 0 (inclusive) and 2^32
    1285 * (exclusive). The empty string returns 0.
    1286 */
    1287goog.string.hashCode = function(str) {
    1288 var result = 0;
    1289 for (var i = 0; i < str.length; ++i) {
    1290 result = 31 * result + str.charCodeAt(i);
    1291 // Normalize to 4 byte range, 0 ... 2^32.
    1292 result %= goog.string.HASHCODE_MAX_;
    1293 }
    1294 return result;
    1295};
    1296
    1297
    1298/**
    1299 * The most recent unique ID. |0 is equivalent to Math.floor in this case.
    1300 * @type {number}
    1301 * @private
    1302 */
    1303goog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0;
    1304
    1305
    1306/**
    1307 * Generates and returns a string which is unique in the current document.
    1308 * This is useful, for example, to create unique IDs for DOM elements.
    1309 * @return {string} A unique id.
    1310 */
    1311goog.string.createUniqueString = function() {
    1312 return 'goog_' + goog.string.uniqueStringCounter_++;
    1313};
    1314
    1315
    1316/**
    1317 * Converts the supplied string to a number, which may be Infinity or NaN.
    1318 * This function strips whitespace: (toNumber(' 123') === 123)
    1319 * This function accepts scientific notation: (toNumber('1e1') === 10)
    1320 *
    1321 * This is better than Javascript's built-in conversions because, sadly:
    1322 * (Number(' ') === 0) and (parseFloat('123a') === 123)
    1323 *
    1324 * @param {string} str The string to convert.
    1325 * @return {number} The number the supplied string represents, or NaN.
    1326 */
    1327goog.string.toNumber = function(str) {
    1328 var num = Number(str);
    1329 if (num == 0 && goog.string.isEmptyOrWhitespace(str)) {
    1330 return NaN;
    1331 }
    1332 return num;
    1333};
    1334
    1335
    1336/**
    1337 * Returns whether the given string is lower camel case (e.g. "isFooBar").
    1338 *
    1339 * Note that this assumes the string is entirely letters.
    1340 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
    1341 *
    1342 * @param {string} str String to test.
    1343 * @return {boolean} Whether the string is lower camel case.
    1344 */
    1345goog.string.isLowerCamelCase = function(str) {
    1346 return /^[a-z]+([A-Z][a-z]*)*$/.test(str);
    1347};
    1348
    1349
    1350/**
    1351 * Returns whether the given string is upper camel case (e.g. "FooBarBaz").
    1352 *
    1353 * Note that this assumes the string is entirely letters.
    1354 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
    1355 *
    1356 * @param {string} str String to test.
    1357 * @return {boolean} Whether the string is upper camel case.
    1358 */
    1359goog.string.isUpperCamelCase = function(str) {
    1360 return /^([A-Z][a-z]*)+$/.test(str);
    1361};
    1362
    1363
    1364/**
    1365 * Converts a string from selector-case to camelCase (e.g. from
    1366 * "multi-part-string" to "multiPartString"), useful for converting
    1367 * CSS selectors and HTML dataset keys to their equivalent JS properties.
    1368 * @param {string} str The string in selector-case form.
    1369 * @return {string} The string in camelCase form.
    1370 */
    1371goog.string.toCamelCase = function(str) {
    1372 return String(str).replace(/\-([a-z])/g, function(all, match) {
    1373 return match.toUpperCase();
    1374 });
    1375};
    1376
    1377
    1378/**
    1379 * Converts a string from camelCase to selector-case (e.g. from
    1380 * "multiPartString" to "multi-part-string"), useful for converting JS
    1381 * style and dataset properties to equivalent CSS selectors and HTML keys.
    1382 * @param {string} str The string in camelCase form.
    1383 * @return {string} The string in selector-case form.
    1384 */
    1385goog.string.toSelectorCase = function(str) {
    1386 return String(str).replace(/([A-Z])/g, '-$1').toLowerCase();
    1387};
    1388
    1389
    1390/**
    1391 * Converts a string into TitleCase. First character of the string is always
    1392 * capitalized in addition to the first letter of every subsequent word.
    1393 * Words are delimited by one or more whitespaces by default. Custom delimiters
    1394 * can optionally be specified to replace the default, which doesn't preserve
    1395 * whitespace delimiters and instead must be explicitly included if needed.
    1396 *
    1397 * Default delimiter => " ":
    1398 * goog.string.toTitleCase('oneTwoThree') => 'OneTwoThree'
    1399 * goog.string.toTitleCase('one two three') => 'One Two Three'
    1400 * goog.string.toTitleCase(' one two ') => ' One Two '
    1401 * goog.string.toTitleCase('one_two_three') => 'One_two_three'
    1402 * goog.string.toTitleCase('one-two-three') => 'One-two-three'
    1403 *
    1404 * Custom delimiter => "_-.":
    1405 * goog.string.toTitleCase('oneTwoThree', '_-.') => 'OneTwoThree'
    1406 * goog.string.toTitleCase('one two three', '_-.') => 'One two three'
    1407 * goog.string.toTitleCase(' one two ', '_-.') => ' one two '
    1408 * goog.string.toTitleCase('one_two_three', '_-.') => 'One_Two_Three'
    1409 * goog.string.toTitleCase('one-two-three', '_-.') => 'One-Two-Three'
    1410 * goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three'
    1411 * goog.string.toTitleCase('one. two. three', '_-.') => 'One. two. three'
    1412 * goog.string.toTitleCase('one-two.three', '_-.') => 'One-Two.Three'
    1413 *
    1414 * @param {string} str String value in camelCase form.
    1415 * @param {string=} opt_delimiters Custom delimiter character set used to
    1416 * distinguish words in the string value. Each character represents a
    1417 * single delimiter. When provided, default whitespace delimiter is
    1418 * overridden and must be explicitly included if needed.
    1419 * @return {string} String value in TitleCase form.
    1420 */
    1421goog.string.toTitleCase = function(str, opt_delimiters) {
    1422 var delimiters = goog.isString(opt_delimiters) ?
    1423 goog.string.regExpEscape(opt_delimiters) : '\\s';
    1424
    1425 // For IE8, we need to prevent using an empty character set. Otherwise,
    1426 // incorrect matching will occur.
    1427 delimiters = delimiters ? '|[' + delimiters + ']+' : '';
    1428
    1429 var regexp = new RegExp('(^' + delimiters + ')([a-z])', 'g');
    1430 return str.replace(regexp, function(all, p1, p2) {
    1431 return p1 + p2.toUpperCase();
    1432 });
    1433};
    1434
    1435
    1436/**
    1437 * Capitalizes a string, i.e. converts the first letter to uppercase
    1438 * and all other letters to lowercase, e.g.:
    1439 *
    1440 * goog.string.capitalize('one') => 'One'
    1441 * goog.string.capitalize('ONE') => 'One'
    1442 * goog.string.capitalize('one two') => 'One two'
    1443 *
    1444 * Note that this function does not trim initial whitespace.
    1445 *
    1446 * @param {string} str String value to capitalize.
    1447 * @return {string} String value with first letter in uppercase.
    1448 */
    1449goog.string.capitalize = function(str) {
    1450 return String(str.charAt(0)).toUpperCase() +
    1451 String(str.substr(1)).toLowerCase();
    1452};
    1453
    1454
    1455/**
    1456 * Parse a string in decimal or hexidecimal ('0xFFFF') form.
    1457 *
    1458 * To parse a particular radix, please use parseInt(string, radix) directly. See
    1459 * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt
    1460 *
    1461 * This is a wrapper for the built-in parseInt function that will only parse
    1462 * numbers as base 10 or base 16. Some JS implementations assume strings
    1463 * starting with "0" are intended to be octal. ES3 allowed but discouraged
    1464 * this behavior. ES5 forbids it. This function emulates the ES5 behavior.
    1465 *
    1466 * For more information, see Mozilla JS Reference: http://goo.gl/8RiFj
    1467 *
    1468 * @param {string|number|null|undefined} value The value to be parsed.
    1469 * @return {number} The number, parsed. If the string failed to parse, this
    1470 * will be NaN.
    1471 */
    1472goog.string.parseInt = function(value) {
    1473 // Force finite numbers to strings.
    1474 if (isFinite(value)) {
    1475 value = String(value);
    1476 }
    1477
    1478 if (goog.isString(value)) {
    1479 // If the string starts with '0x' or '-0x', parse as hex.
    1480 return /^\s*-?0x/i.test(value) ?
    1481 parseInt(value, 16) : parseInt(value, 10);
    1482 }
    1483
    1484 return NaN;
    1485};
    1486
    1487
    1488/**
    1489 * Splits a string on a separator a limited number of times.
    1490 *
    1491 * This implementation is more similar to Python or Java, where the limit
    1492 * parameter specifies the maximum number of splits rather than truncating
    1493 * the number of results.
    1494 *
    1495 * See http://docs.python.org/2/library/stdtypes.html#str.split
    1496 * See JavaDoc: http://goo.gl/F2AsY
    1497 * See Mozilla reference: http://goo.gl/dZdZs
    1498 *
    1499 * @param {string} str String to split.
    1500 * @param {string} separator The separator.
    1501 * @param {number} limit The limit to the number of splits. The resulting array
    1502 * will have a maximum length of limit+1. Negative numbers are the same
    1503 * as zero.
    1504 * @return {!Array<string>} The string, split.
    1505 */
    1506
    1507goog.string.splitLimit = function(str, separator, limit) {
    1508 var parts = str.split(separator);
    1509 var returnVal = [];
    1510
    1511 // Only continue doing this while we haven't hit the limit and we have
    1512 // parts left.
    1513 while (limit > 0 && parts.length) {
    1514 returnVal.push(parts.shift());
    1515 limit--;
    1516 }
    1517
    1518 // If there are remaining parts, append them to the end.
    1519 if (parts.length) {
    1520 returnVal.push(parts.join(separator));
    1521 }
    1522
    1523 return returnVal;
    1524};
    1525
    1526
    1527/**
    1528 * Computes the Levenshtein edit distance between two strings.
    1529 * @param {string} a
    1530 * @param {string} b
    1531 * @return {number} The edit distance between the two strings.
    1532 */
    1533goog.string.editDistance = function(a, b) {
    1534 var v0 = [];
    1535 var v1 = [];
    1536
    1537 if (a == b) {
    1538 return 0;
    1539 }
    1540
    1541 if (!a.length || !b.length) {
    1542 return Math.max(a.length, b.length);
    1543 }
    1544
    1545 for (var i = 0; i < b.length + 1; i++) {
    1546 v0[i] = i;
    1547 }
    1548
    1549 for (var i = 0; i < a.length; i++) {
    1550 v1[0] = i + 1;
    1551
    1552 for (var j = 0; j < b.length; j++) {
    1553 var cost = a[i] != b[j];
    1554 // Cost for the substring is the minimum of adding one character, removing
    1555 // one character, or a swap.
    1556 v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost);
    1557 }
    1558
    1559 for (var j = 0; j < v0.length; j++) {
    1560 v0[j] = v1[j];
    1561 }
    1562 }
    1563
    1564 return v1[b.length];
    1565};
    \ 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 index 1dec5f7..59ede41 100644 --- a/docs/source/lib/goog/structs/collection.js.src.html +++ b/docs/source/lib/goog/structs/collection.js.src.html @@ -1 +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 +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 d1e4bb0..5beaae0 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 * @template K, V
    44 */
    45goog.structs.Map = function(opt_map, var_args) {
    46
    47 /**
    48 * Underlying JS object used to implement the map.
    49 * @private {!Object}
    50 */
    51 this.map_ = {};
    52
    53 /**
    54 * An array of keys. This is necessary for two reasons:
    55 * 1. Iterating the keys using for (var key in this.map_) allocates an
    56 * object for every key in IE which is really bad for IE6 GC perf.
    57 * 2. Without a side data structure, we would need to escape all the keys
    58 * as that would be the only way we could tell during iteration if the
    59 * key was an internal key or a property of the object.
    60 *
    61 * This array can contain deleted keys so it's necessary to check the map
    62 * as well to see if the key is still in the map (this doesn't require a
    63 * memory allocation in IE).
    64 * @private {!Array.<string>}
    65 */
    66 this.keys_ = [];
    67
    68 /**
    69 * The number of key value pairs in the map.
    70 * @private {number}
    71 */
    72 this.count_ = 0;
    73
    74 /**
    75 * Version used to detect changes while iterating.
    76 * @private {number}
    77 */
    78 this.version_ = 0;
    79
    80 var argLength = arguments.length;
    81
    82 if (argLength > 1) {
    83 if (argLength % 2) {
    84 throw Error('Uneven number of arguments');
    85 }
    86 for (var i = 0; i < argLength; i += 2) {
    87 this.set(arguments[i], arguments[i + 1]);
    88 }
    89 } else if (opt_map) {
    90 this.addAll(/** @type {Object} */ (opt_map));
    91 }
    92};
    93
    94
    95/**
    96 * @return {number} The number of key-value pairs in the map.
    97 */
    98goog.structs.Map.prototype.getCount = function() {
    99 return this.count_;
    100};
    101
    102
    103/**
    104 * Returns the values of the map.
    105 * @return {!Array.<V>} The values in the map.
    106 */
    107goog.structs.Map.prototype.getValues = function() {
    108 this.cleanupKeysArray_();
    109
    110 var rv = [];
    111 for (var i = 0; i < this.keys_.length; i++) {
    112 var key = this.keys_[i];
    113 rv.push(this.map_[key]);
    114 }
    115 return rv;
    116};
    117
    118
    119/**
    120 * Returns the keys of the map.
    121 * @return {!Array.<string>} Array of string values.
    122 */
    123goog.structs.Map.prototype.getKeys = function() {
    124 this.cleanupKeysArray_();
    125 return /** @type {!Array.<string>} */ (this.keys_.concat());
    126};
    127
    128
    129/**
    130 * Whether the map contains the given key.
    131 * @param {*} key The key to check for.
    132 * @return {boolean} Whether the map contains the key.
    133 */
    134goog.structs.Map.prototype.containsKey = function(key) {
    135 return goog.structs.Map.hasKey_(this.map_, key);
    136};
    137
    138
    139/**
    140 * Whether the map contains the given value. This is O(n).
    141 * @param {V} val The value to check for.
    142 * @return {boolean} Whether the map contains the value.
    143 */
    144goog.structs.Map.prototype.containsValue = function(val) {
    145 for (var i = 0; i < this.keys_.length; i++) {
    146 var key = this.keys_[i];
    147 if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) {
    148 return true;
    149 }
    150 }
    151 return false;
    152};
    153
    154
    155/**
    156 * Whether this map is equal to the argument map.
    157 * @param {goog.structs.Map} otherMap The map against which to test equality.
    158 * @param {function(V, V): boolean=} opt_equalityFn Optional equality function
    159 * to test equality of values. If not specified, this will test whether
    160 * the values contained in each map are identical objects.
    161 * @return {boolean} Whether the maps are equal.
    162 */
    163goog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) {
    164 if (this === otherMap) {
    165 return true;
    166 }
    167
    168 if (this.count_ != otherMap.getCount()) {
    169 return false;
    170 }
    171
    172 var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals;
    173
    174 this.cleanupKeysArray_();
    175 for (var key, i = 0; key = this.keys_[i]; i++) {
    176 if (!equalityFn(this.get(key), otherMap.get(key))) {
    177 return false;
    178 }
    179 }
    180
    181 return true;
    182};
    183
    184
    185/**
    186 * Default equality test for values.
    187 * @param {*} a The first value.
    188 * @param {*} b The second value.
    189 * @return {boolean} Whether a and b reference the same object.
    190 */
    191goog.structs.Map.defaultEquals = function(a, b) {
    192 return a === b;
    193};
    194
    195
    196/**
    197 * @return {boolean} Whether the map is empty.
    198 */
    199goog.structs.Map.prototype.isEmpty = function() {
    200 return this.count_ == 0;
    201};
    202
    203
    204/**
    205 * Removes all key-value pairs from the map.
    206 */
    207goog.structs.Map.prototype.clear = function() {
    208 this.map_ = {};
    209 this.keys_.length = 0;
    210 this.count_ = 0;
    211 this.version_ = 0;
    212};
    213
    214
    215/**
    216 * Removes a key-value pair based on the key. This is O(logN) amortized due to
    217 * updating the keys array whenever the count becomes half the size of the keys
    218 * in the keys array.
    219 * @param {*} key The key to remove.
    220 * @return {boolean} Whether object was removed.
    221 */
    222goog.structs.Map.prototype.remove = function(key) {
    223 if (goog.structs.Map.hasKey_(this.map_, key)) {
    224 delete this.map_[key];
    225 this.count_--;
    226 this.version_++;
    227
    228 // clean up the keys array if the threshhold is hit
    229 if (this.keys_.length > 2 * this.count_) {
    230 this.cleanupKeysArray_();
    231 }
    232
    233 return true;
    234 }
    235 return false;
    236};
    237
    238
    239/**
    240 * Cleans up the temp keys array by removing entries that are no longer in the
    241 * map.
    242 * @private
    243 */
    244goog.structs.Map.prototype.cleanupKeysArray_ = function() {
    245 if (this.count_ != this.keys_.length) {
    246 // First remove keys that are no longer in the map.
    247 var srcIndex = 0;
    248 var destIndex = 0;
    249 while (srcIndex < this.keys_.length) {
    250 var key = this.keys_[srcIndex];
    251 if (goog.structs.Map.hasKey_(this.map_, key)) {
    252 this.keys_[destIndex++] = key;
    253 }
    254 srcIndex++;
    255 }
    256 this.keys_.length = destIndex;
    257 }
    258
    259 if (this.count_ != this.keys_.length) {
    260 // If the count still isn't correct, that means we have duplicates. This can
    261 // happen when the same key is added and removed multiple times. Now we have
    262 // to allocate one extra Object to remove the duplicates. This could have
    263 // been done in the first pass, but in the common case, we can avoid
    264 // allocating an extra object by only doing this when necessary.
    265 var seen = {};
    266 var srcIndex = 0;
    267 var destIndex = 0;
    268 while (srcIndex < this.keys_.length) {
    269 var key = this.keys_[srcIndex];
    270 if (!(goog.structs.Map.hasKey_(seen, key))) {
    271 this.keys_[destIndex++] = key;
    272 seen[key] = 1;
    273 }
    274 srcIndex++;
    275 }
    276 this.keys_.length = destIndex;
    277 }
    278};
    279
    280
    281/**
    282 * Returns the value for the given key. If the key is not found and the default
    283 * value is not given this will return {@code undefined}.
    284 * @param {*} key The key to get the value for.
    285 * @param {DEFAULT=} opt_val The value to return if no item is found for the
    286 * given key, defaults to undefined.
    287 * @return {V|DEFAULT} The value for the given key.
    288 * @template DEFAULT
    289 */
    290goog.structs.Map.prototype.get = function(key, opt_val) {
    291 if (goog.structs.Map.hasKey_(this.map_, key)) {
    292 return this.map_[key];
    293 }
    294 return opt_val;
    295};
    296
    297
    298/**
    299 * Adds a key-value pair to the map.
    300 * @param {*} key The key.
    301 * @param {V} value The value to add.
    302 * @return {*} Some subclasses return a value.
    303 */
    304goog.structs.Map.prototype.set = function(key, value) {
    305 if (!(goog.structs.Map.hasKey_(this.map_, key))) {
    306 this.count_++;
    307 this.keys_.push(key);
    308 // Only change the version if we add a new key.
    309 this.version_++;
    310 }
    311 this.map_[key] = value;
    312};
    313
    314
    315/**
    316 * Adds multiple key-value pairs from another goog.structs.Map or Object.
    317 * @param {Object} map Object containing the data to add.
    318 */
    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 keys = this.keys_;
    430 var map = this.map_;
    431 var version = this.version_;
    432 var selfObj = this;
    433
    434 var newIter = new goog.iter.Iterator;
    435 newIter.next = function() {
    436 while (true) {
    437 if (version != selfObj.version_) {
    438 throw Error('The map has changed since the iterator was created');
    439 }
    440 if (i >= keys.length) {
    441 throw goog.iter.StopIteration;
    442 }
    443 var key = keys[i++];
    444 return opt_keys ? key : map[key];
    445 }
    446 };
    447 return newIter;
    448};
    449
    450
    451/**
    452 * Safe way to test for hasOwnProperty. It even allows testing for
    453 * 'hasOwnProperty'.
    454 * @param {Object} obj The object to test for presence of the given key.
    455 * @param {*} key The key to check for.
    456 * @return {boolean} Whether the object has the key.
    457 * @private
    458 */
    459goog.structs.Map.hasKey_ = function(obj, key) {
    460 return Object.prototype.hasOwnProperty.call(obj, key);
    461};
    \ 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, and where special names like __proto__ are not a concern, consider
    24 * using the lighter-weight utilities in goog.object.
    25 */
    26
    27
    28goog.provide('goog.structs.Map');
    29
    30goog.require('goog.iter.Iterator');
    31goog.require('goog.iter.StopIteration');
    32goog.require('goog.object');
    33
    34
    35
    36/**
    37 * Class for Hash Map datastructure.
    38 * @param {*=} opt_map Map or Object to initialize the map with.
    39 * @param {...*} var_args If 2 or more arguments are present then they
    40 * will be used as key-value pairs.
    41 * @constructor
    42 * @template K, V
    43 */
    44goog.structs.Map = function(opt_map, var_args) {
    45
    46 /**
    47 * Underlying JS object used to implement the map.
    48 * @private {!Object}
    49 */
    50 this.map_ = {};
    51
    52 /**
    53 * An array of keys. This is necessary for two reasons:
    54 * 1. Iterating the keys using for (var key in this.map_) allocates an
    55 * object for every key in IE which is really bad for IE6 GC perf.
    56 * 2. Without a side data structure, we would need to escape all the keys
    57 * as that would be the only way we could tell during iteration if the
    58 * key was an internal key or a property of the object.
    59 *
    60 * This array can contain deleted keys so it's necessary to check the map
    61 * as well to see if the key is still in the map (this doesn't require a
    62 * memory allocation in IE).
    63 * @private {!Array<string>}
    64 */
    65 this.keys_ = [];
    66
    67 /**
    68 * The number of key value pairs in the map.
    69 * @private {number}
    70 */
    71 this.count_ = 0;
    72
    73 /**
    74 * Version used to detect changes while iterating.
    75 * @private {number}
    76 */
    77 this.version_ = 0;
    78
    79 var argLength = arguments.length;
    80
    81 if (argLength > 1) {
    82 if (argLength % 2) {
    83 throw Error('Uneven number of arguments');
    84 }
    85 for (var i = 0; i < argLength; i += 2) {
    86 this.set(arguments[i], arguments[i + 1]);
    87 }
    88 } else if (opt_map) {
    89 this.addAll(/** @type {Object} */ (opt_map));
    90 }
    91};
    92
    93
    94/**
    95 * @return {number} The number of key-value pairs in the map.
    96 */
    97goog.structs.Map.prototype.getCount = function() {
    98 return this.count_;
    99};
    100
    101
    102/**
    103 * Returns the values of the map.
    104 * @return {!Array<V>} The values in the map.
    105 */
    106goog.structs.Map.prototype.getValues = function() {
    107 this.cleanupKeysArray_();
    108
    109 var rv = [];
    110 for (var i = 0; i < this.keys_.length; i++) {
    111 var key = this.keys_[i];
    112 rv.push(this.map_[key]);
    113 }
    114 return rv;
    115};
    116
    117
    118/**
    119 * Returns the keys of the map.
    120 * @return {!Array<string>} Array of string values.
    121 */
    122goog.structs.Map.prototype.getKeys = function() {
    123 this.cleanupKeysArray_();
    124 return /** @type {!Array<string>} */ (this.keys_.concat());
    125};
    126
    127
    128/**
    129 * Whether the map contains the given key.
    130 * @param {*} key The key to check for.
    131 * @return {boolean} Whether the map contains the key.
    132 */
    133goog.structs.Map.prototype.containsKey = function(key) {
    134 return goog.structs.Map.hasKey_(this.map_, key);
    135};
    136
    137
    138/**
    139 * Whether the map contains the given value. This is O(n).
    140 * @param {V} val The value to check for.
    141 * @return {boolean} Whether the map contains the value.
    142 */
    143goog.structs.Map.prototype.containsValue = function(val) {
    144 for (var i = 0; i < this.keys_.length; i++) {
    145 var key = this.keys_[i];
    146 if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) {
    147 return true;
    148 }
    149 }
    150 return false;
    151};
    152
    153
    154/**
    155 * Whether this map is equal to the argument map.
    156 * @param {goog.structs.Map} otherMap The map against which to test equality.
    157 * @param {function(V, V): boolean=} opt_equalityFn Optional equality function
    158 * to test equality of values. If not specified, this will test whether
    159 * the values contained in each map are identical objects.
    160 * @return {boolean} Whether the maps are equal.
    161 */
    162goog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) {
    163 if (this === otherMap) {
    164 return true;
    165 }
    166
    167 if (this.count_ != otherMap.getCount()) {
    168 return false;
    169 }
    170
    171 var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals;
    172
    173 this.cleanupKeysArray_();
    174 for (var key, i = 0; key = this.keys_[i]; i++) {
    175 if (!equalityFn(this.get(key), otherMap.get(key))) {
    176 return false;
    177 }
    178 }
    179
    180 return true;
    181};
    182
    183
    184/**
    185 * Default equality test for values.
    186 * @param {*} a The first value.
    187 * @param {*} b The second value.
    188 * @return {boolean} Whether a and b reference the same object.
    189 */
    190goog.structs.Map.defaultEquals = function(a, b) {
    191 return a === b;
    192};
    193
    194
    195/**
    196 * @return {boolean} Whether the map is empty.
    197 */
    198goog.structs.Map.prototype.isEmpty = function() {
    199 return this.count_ == 0;
    200};
    201
    202
    203/**
    204 * Removes all key-value pairs from the map.
    205 */
    206goog.structs.Map.prototype.clear = function() {
    207 this.map_ = {};
    208 this.keys_.length = 0;
    209 this.count_ = 0;
    210 this.version_ = 0;
    211};
    212
    213
    214/**
    215 * Removes a key-value pair based on the key. This is O(logN) amortized due to
    216 * updating the keys array whenever the count becomes half the size of the keys
    217 * in the keys array.
    218 * @param {*} key The key to remove.
    219 * @return {boolean} Whether object was removed.
    220 */
    221goog.structs.Map.prototype.remove = function(key) {
    222 if (goog.structs.Map.hasKey_(this.map_, key)) {
    223 delete this.map_[key];
    224 this.count_--;
    225 this.version_++;
    226
    227 // clean up the keys array if the threshhold is hit
    228 if (this.keys_.length > 2 * this.count_) {
    229 this.cleanupKeysArray_();
    230 }
    231
    232 return true;
    233 }
    234 return false;
    235};
    236
    237
    238/**
    239 * Cleans up the temp keys array by removing entries that are no longer in the
    240 * map.
    241 * @private
    242 */
    243goog.structs.Map.prototype.cleanupKeysArray_ = function() {
    244 if (this.count_ != this.keys_.length) {
    245 // First remove keys that are no longer in the map.
    246 var srcIndex = 0;
    247 var destIndex = 0;
    248 while (srcIndex < this.keys_.length) {
    249 var key = this.keys_[srcIndex];
    250 if (goog.structs.Map.hasKey_(this.map_, key)) {
    251 this.keys_[destIndex++] = key;
    252 }
    253 srcIndex++;
    254 }
    255 this.keys_.length = destIndex;
    256 }
    257
    258 if (this.count_ != this.keys_.length) {
    259 // If the count still isn't correct, that means we have duplicates. This can
    260 // happen when the same key is added and removed multiple times. Now we have
    261 // to allocate one extra Object to remove the duplicates. This could have
    262 // been done in the first pass, but in the common case, we can avoid
    263 // allocating an extra object by only doing this when necessary.
    264 var seen = {};
    265 var srcIndex = 0;
    266 var destIndex = 0;
    267 while (srcIndex < this.keys_.length) {
    268 var key = this.keys_[srcIndex];
    269 if (!(goog.structs.Map.hasKey_(seen, key))) {
    270 this.keys_[destIndex++] = key;
    271 seen[key] = 1;
    272 }
    273 srcIndex++;
    274 }
    275 this.keys_.length = destIndex;
    276 }
    277};
    278
    279
    280/**
    281 * Returns the value for the given key. If the key is not found and the default
    282 * value is not given this will return {@code undefined}.
    283 * @param {*} key The key to get the value for.
    284 * @param {DEFAULT=} opt_val The value to return if no item is found for the
    285 * given key, defaults to undefined.
    286 * @return {V|DEFAULT} The value for the given key.
    287 * @template DEFAULT
    288 */
    289goog.structs.Map.prototype.get = function(key, opt_val) {
    290 if (goog.structs.Map.hasKey_(this.map_, key)) {
    291 return this.map_[key];
    292 }
    293 return opt_val;
    294};
    295
    296
    297/**
    298 * Adds a key-value pair to the map.
    299 * @param {*} key The key.
    300 * @param {V} value The value to add.
    301 * @return {*} Some subclasses return a value.
    302 */
    303goog.structs.Map.prototype.set = function(key, value) {
    304 if (!(goog.structs.Map.hasKey_(this.map_, key))) {
    305 this.count_++;
    306 this.keys_.push(key);
    307 // Only change the version if we add a new key.
    308 this.version_++;
    309 }
    310 this.map_[key] = value;
    311};
    312
    313
    314/**
    315 * Adds multiple key-value pairs from another goog.structs.Map or Object.
    316 * @param {Object} map Object containing the data to add.
    317 */
    318goog.structs.Map.prototype.addAll = function(map) {
    319 var keys, values;
    320 if (map instanceof goog.structs.Map) {
    321 keys = map.getKeys();
    322 values = map.getValues();
    323 } else {
    324 keys = goog.object.getKeys(map);
    325 values = goog.object.getValues(map);
    326 }
    327 // we could use goog.array.forEach here but I don't want to introduce that
    328 // dependency just for this.
    329 for (var i = 0; i < keys.length; i++) {
    330 this.set(keys[i], values[i]);
    331 }
    332};
    333
    334
    335/**
    336 * Calls the given function on each entry in the map.
    337 * @param {function(this:T, V, K, goog.structs.Map<K,V>)} f
    338 * @param {T=} opt_obj The value of "this" inside f.
    339 * @template T
    340 */
    341goog.structs.Map.prototype.forEach = function(f, opt_obj) {
    342 var keys = this.getKeys();
    343 for (var i = 0; i < keys.length; i++) {
    344 var key = keys[i];
    345 var value = this.get(key);
    346 f.call(opt_obj, value, key, this);
    347 }
    348};
    349
    350
    351/**
    352 * Clones a map and returns a new map.
    353 * @return {!goog.structs.Map} A new map with the same key-value pairs.
    354 */
    355goog.structs.Map.prototype.clone = function() {
    356 return new goog.structs.Map(this);
    357};
    358
    359
    360/**
    361 * Returns a new map in which all the keys and values are interchanged
    362 * (keys become values and values become keys). If multiple keys map to the
    363 * same value, the chosen transposed value is implementation-dependent.
    364 *
    365 * It acts very similarly to {goog.object.transpose(Object)}.
    366 *
    367 * @return {!goog.structs.Map} The transposed map.
    368 */
    369goog.structs.Map.prototype.transpose = function() {
    370 var transposed = new goog.structs.Map();
    371 for (var i = 0; i < this.keys_.length; i++) {
    372 var key = this.keys_[i];
    373 var value = this.map_[key];
    374 transposed.set(value, key);
    375 }
    376
    377 return transposed;
    378};
    379
    380
    381/**
    382 * @return {!Object} Object representation of the map.
    383 */
    384goog.structs.Map.prototype.toObject = function() {
    385 this.cleanupKeysArray_();
    386 var obj = {};
    387 for (var i = 0; i < this.keys_.length; i++) {
    388 var key = this.keys_[i];
    389 obj[key] = this.map_[key];
    390 }
    391 return obj;
    392};
    393
    394
    395/**
    396 * Returns an iterator that iterates over the keys in the map. Removal of keys
    397 * while iterating might have undesired side effects.
    398 * @return {!goog.iter.Iterator} An iterator over the keys in the map.
    399 */
    400goog.structs.Map.prototype.getKeyIterator = function() {
    401 return this.__iterator__(true);
    402};
    403
    404
    405/**
    406 * Returns an iterator that iterates over the values in the map. Removal of
    407 * keys while iterating might have undesired side effects.
    408 * @return {!goog.iter.Iterator} An iterator over the values in the map.
    409 */
    410goog.structs.Map.prototype.getValueIterator = function() {
    411 return this.__iterator__(false);
    412};
    413
    414
    415/**
    416 * Returns an iterator that iterates over the values or the keys in the map.
    417 * This throws an exception if the map was mutated since the iterator was
    418 * created.
    419 * @param {boolean=} opt_keys True to iterate over the keys. False to iterate
    420 * over the values. The default value is false.
    421 * @return {!goog.iter.Iterator} An iterator over the values or keys in the map.
    422 */
    423goog.structs.Map.prototype.__iterator__ = function(opt_keys) {
    424 // Clean up keys to minimize the risk of iterating over dead keys.
    425 this.cleanupKeysArray_();
    426
    427 var i = 0;
    428 var version = this.version_;
    429 var selfObj = this;
    430
    431 var newIter = new goog.iter.Iterator;
    432 newIter.next = function() {
    433 if (version != selfObj.version_) {
    434 throw Error('The map has changed since the iterator was created');
    435 }
    436 if (i >= selfObj.keys_.length) {
    437 throw goog.iter.StopIteration;
    438 }
    439 var key = selfObj.keys_[i++];
    440 return opt_keys ? key : selfObj.map_[key];
    441 };
    442 return newIter;
    443};
    444
    445
    446/**
    447 * Safe way to test for hasOwnProperty. It even allows testing for
    448 * 'hasOwnProperty'.
    449 * @param {Object} obj The object to test for presence of the given key.
    450 * @param {*} key The key to check for.
    451 * @return {boolean} Whether the object has the key.
    452 * @private
    453 */
    454goog.structs.Map.hasKey_ = function(obj, key) {
    455 return Object.prototype.hasOwnProperty.call(obj, key);
    456};
    \ 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 index e5566ef..5c0c4e3 100644 --- a/docs/source/lib/goog/structs/set.js.src.html +++ b/docs/source/lib/goog/structs/set.js.src.html @@ -1 +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 * @author pallosp@google.com (Peter Pallos)
    20 *
    21 * This class implements a set data structure. Adding and removing is O(1). It
    22 * supports both object and primitive values. Be careful because you can add
    23 * both 1 and new Number(1), because these are not the same. You can even add
    24 * multiple new Number(1) because these are not equal.
    25 */
    26
    27
    28goog.provide('goog.structs.Set');
    29
    30goog.require('goog.structs');
    31goog.require('goog.structs.Collection');
    32goog.require('goog.structs.Map');
    33
    34
    35
    36/**
    37 * A set that can contain both primitives and objects. Adding and removing
    38 * elements is O(1). Primitives are treated as identical if they have the same
    39 * type and convert to the same string. Objects are treated as identical only
    40 * if they are references to the same object. WARNING: A goog.structs.Set can
    41 * contain both 1 and (new Number(1)), because they are not the same. WARNING:
    42 * Adding (new Number(1)) twice will yield two distinct elements, because they
    43 * are two different objects. WARNING: Any object that is added to a
    44 * goog.structs.Set will be modified! Because goog.getUid() is used to
    45 * identify objects, every object in the set will be mutated.
    46 * @param {Array.<T>|Object.<?,T>=} opt_values Initial values to start with.
    47 * @constructor
    48 * @implements {goog.structs.Collection.<T>}
    49 * @final
    50 * @template T
    51 */
    52goog.structs.Set = function(opt_values) {
    53 this.map_ = new goog.structs.Map;
    54 if (opt_values) {
    55 this.addAll(opt_values);
    56 }
    57};
    58
    59
    60/**
    61 * Obtains a unique key for an element of the set. Primitives will yield the
    62 * same key if they have the same type and convert to the same string. Object
    63 * references will yield the same key only if they refer to the same object.
    64 * @param {*} val Object or primitive value to get a key for.
    65 * @return {string} A unique key for this value/object.
    66 * @private
    67 */
    68goog.structs.Set.getKey_ = function(val) {
    69 var type = typeof val;
    70 if (type == 'object' && val || type == 'function') {
    71 return 'o' + goog.getUid(/** @type {Object} */ (val));
    72 } else {
    73 return type.substr(0, 1) + val;
    74 }
    75};
    76
    77
    78/**
    79 * @return {number} The number of elements in the set.
    80 * @override
    81 */
    82goog.structs.Set.prototype.getCount = function() {
    83 return this.map_.getCount();
    84};
    85
    86
    87/**
    88 * Add a primitive or an object to the set.
    89 * @param {T} element The primitive or object to add.
    90 * @override
    91 */
    92goog.structs.Set.prototype.add = function(element) {
    93 this.map_.set(goog.structs.Set.getKey_(element), element);
    94};
    95
    96
    97/**
    98 * Adds all the values in the given collection to this set.
    99 * @param {Array.<T>|goog.structs.Collection.<T>|Object.<?,T>} col A collection
    100 * containing the elements to add.
    101 */
    102goog.structs.Set.prototype.addAll = function(col) {
    103 var values = goog.structs.getValues(col);
    104 var l = values.length;
    105 for (var i = 0; i < l; i++) {
    106 this.add(values[i]);
    107 }
    108};
    109
    110
    111/**
    112 * Removes all values in the given collection from this set.
    113 * @param {Array.<T>|goog.structs.Collection.<T>|Object.<?,T>} col A collection
    114 * containing the elements to remove.
    115 */
    116goog.structs.Set.prototype.removeAll = function(col) {
    117 var values = goog.structs.getValues(col);
    118 var l = values.length;
    119 for (var i = 0; i < l; i++) {
    120 this.remove(values[i]);
    121 }
    122};
    123
    124
    125/**
    126 * Removes the given element from this set.
    127 * @param {T} element The primitive or object to remove.
    128 * @return {boolean} Whether the element was found and removed.
    129 * @override
    130 */
    131goog.structs.Set.prototype.remove = function(element) {
    132 return this.map_.remove(goog.structs.Set.getKey_(element));
    133};
    134
    135
    136/**
    137 * Removes all elements from this set.
    138 */
    139goog.structs.Set.prototype.clear = function() {
    140 this.map_.clear();
    141};
    142
    143
    144/**
    145 * Tests whether this set is empty.
    146 * @return {boolean} True if there are no elements in this set.
    147 */
    148goog.structs.Set.prototype.isEmpty = function() {
    149 return this.map_.isEmpty();
    150};
    151
    152
    153/**
    154 * Tests whether this set contains the given element.
    155 * @param {T} element The primitive or object to test for.
    156 * @return {boolean} True if this set contains the given element.
    157 * @override
    158 */
    159goog.structs.Set.prototype.contains = function(element) {
    160 return this.map_.containsKey(goog.structs.Set.getKey_(element));
    161};
    162
    163
    164/**
    165 * Tests whether this set contains all the values in a given collection.
    166 * Repeated elements in the collection are ignored, e.g. (new
    167 * goog.structs.Set([1, 2])).containsAll([1, 1]) is True.
    168 * @param {goog.structs.Collection.<T>|Object} col A collection-like object.
    169 * @return {boolean} True if the set contains all elements.
    170 */
    171goog.structs.Set.prototype.containsAll = function(col) {
    172 return goog.structs.every(col, this.contains, this);
    173};
    174
    175
    176/**
    177 * Finds all values that are present in both this set and the given collection.
    178 * @param {Array.<S>|Object.<?,S>} col A collection.
    179 * @return {!goog.structs.Set.<T|S>} A new set containing all the values
    180 * (primitives or objects) present in both this set and the given
    181 * collection.
    182 * @template S
    183 */
    184goog.structs.Set.prototype.intersection = function(col) {
    185 var result = new goog.structs.Set();
    186
    187 var values = goog.structs.getValues(col);
    188 for (var i = 0; i < values.length; i++) {
    189 var value = values[i];
    190 if (this.contains(value)) {
    191 result.add(value);
    192 }
    193 }
    194
    195 return result;
    196};
    197
    198
    199/**
    200 * Finds all values that are present in this set and not in the given
    201 * collection.
    202 * @param {Array.<T>|goog.structs.Collection.<T>|Object.<?,T>} col A collection.
    203 * @return {!goog.structs.Set} A new set containing all the values
    204 * (primitives or objects) present in this set but not in the given
    205 * collection.
    206 */
    207goog.structs.Set.prototype.difference = function(col) {
    208 var result = this.clone();
    209 result.removeAll(col);
    210 return result;
    211};
    212
    213
    214/**
    215 * Returns an array containing all the elements in this set.
    216 * @return {!Array.<T>} An array containing all the elements in this set.
    217 */
    218goog.structs.Set.prototype.getValues = function() {
    219 return this.map_.getValues();
    220};
    221
    222
    223/**
    224 * Creates a shallow clone of this set.
    225 * @return {!goog.structs.Set.<T>} A new set containing all the same elements as
    226 * this set.
    227 */
    228goog.structs.Set.prototype.clone = function() {
    229 return new goog.structs.Set(this);
    230};
    231
    232
    233/**
    234 * Tests whether the given collection consists of the same elements as this set,
    235 * regardless of order, without repetition. Primitives are treated as equal if
    236 * they have the same type and convert to the same string; objects are treated
    237 * as equal if they are references to the same object. This operation is O(n).
    238 * @param {goog.structs.Collection.<T>|Object} col A collection.
    239 * @return {boolean} True if the given collection consists of the same elements
    240 * as this set, regardless of order, without repetition.
    241 */
    242goog.structs.Set.prototype.equals = function(col) {
    243 return this.getCount() == goog.structs.getCount(col) && this.isSubsetOf(col);
    244};
    245
    246
    247/**
    248 * Tests whether the given collection contains all the elements in this set.
    249 * Primitives are treated as equal if they have the same type and convert to the
    250 * same string; objects are treated as equal if they are references to the same
    251 * object. This operation is O(n).
    252 * @param {goog.structs.Collection.<T>|Object} col A collection.
    253 * @return {boolean} True if this set is a subset of the given collection.
    254 */
    255goog.structs.Set.prototype.isSubsetOf = function(col) {
    256 var colCount = goog.structs.getCount(col);
    257 if (this.getCount() > colCount) {
    258 return false;
    259 }
    260 // TODO(user) Find the minimal collection size where the conversion makes
    261 // the contains() method faster.
    262 if (!(col instanceof goog.structs.Set) && colCount > 5) {
    263 // Convert to a goog.structs.Set so that goog.structs.contains runs in
    264 // O(1) time instead of O(n) time.
    265 col = new goog.structs.Set(col);
    266 }
    267 return goog.structs.every(this, function(value) {
    268 return goog.structs.contains(col, value);
    269 });
    270};
    271
    272
    273/**
    274 * Returns an iterator that iterates over the elements in this set.
    275 * @param {boolean=} opt_keys This argument is ignored.
    276 * @return {!goog.iter.Iterator} An iterator over the elements in this set.
    277 */
    278goog.structs.Set.prototype.__iterator__ = function(opt_keys) {
    279 return this.map_.__iterator__(false);
    280};
    \ No newline at end of file +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 aa633fa..23c3d6e 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 * 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 (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 +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.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 (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 (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 index 124235a..c55016d 100644 --- a/docs/source/lib/goog/style/style.js.src.html +++ b/docs/source/lib/goog/style/style.js.src.html @@ -1 +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 * @see ../demos/inline_block_quirks.html
    19 * @see ../demos/inline_block_standards.html
    20 * @see ../demos/style_viewport.html
    21 */
    22
    23goog.provide('goog.style');
    24
    25
    26goog.require('goog.array');
    27goog.require('goog.asserts');
    28goog.require('goog.dom');
    29goog.require('goog.dom.NodeType');
    30goog.require('goog.dom.vendor');
    31goog.require('goog.math.Box');
    32goog.require('goog.math.Coordinate');
    33goog.require('goog.math.Rect');
    34goog.require('goog.math.Size');
    35goog.require('goog.object');
    36goog.require('goog.string');
    37goog.require('goog.userAgent');
    38
    39
    40/**
    41 * @define {boolean} Whether we know at compile time that
    42 * getBoundingClientRect() is present and bug-free on the browser.
    43 */
    44goog.define('goog.style.GET_BOUNDING_CLIENT_RECT_ALWAYS_EXISTS', false);
    45
    46
    47/**
    48 * Sets a style value on an element.
    49 *
    50 * This function is not indended to patch issues in the browser's style
    51 * handling, but to allow easy programmatic access to setting dash-separated
    52 * style properties. An example is setting a batch of properties from a data
    53 * object without overwriting old styles. When possible, use native APIs:
    54 * elem.style.propertyKey = 'value' or (if obliterating old styles is fine)
    55 * elem.style.cssText = 'property1: value1; property2: value2'.
    56 *
    57 * @param {Element} element The element to change.
    58 * @param {string|Object} style If a string, a style name. If an object, a hash
    59 * of style names to style values.
    60 * @param {string|number|boolean=} opt_value If style was a string, then this
    61 * should be the value.
    62 */
    63goog.style.setStyle = function(element, style, opt_value) {
    64 if (goog.isString(style)) {
    65 goog.style.setStyle_(element, opt_value, style);
    66 } else {
    67 for (var key in style) {
    68 goog.style.setStyle_(element, style[key], key);
    69 }
    70 }
    71};
    72
    73
    74/**
    75 * Sets a style value on an element, with parameters swapped to work with
    76 * {@code goog.object.forEach()}. Prepends a vendor-specific prefix when
    77 * necessary.
    78 * @param {Element} element The element to change.
    79 * @param {string|number|boolean|undefined} value Style value.
    80 * @param {string} style Style name.
    81 * @private
    82 */
    83goog.style.setStyle_ = function(element, value, style) {
    84 var propertyName = goog.style.getVendorJsStyleName_(element, style);
    85
    86 if (propertyName) {
    87 element.style[propertyName] = value;
    88 }
    89};
    90
    91
    92/**
    93 * Returns the style property name in camel-case. If it does not exist and a
    94 * vendor-specific version of the property does exist, then return the vendor-
    95 * specific property name instead.
    96 * @param {Element} element The element to change.
    97 * @param {string} style Style name.
    98 * @return {string} Vendor-specific style.
    99 * @private
    100 */
    101goog.style.getVendorJsStyleName_ = function(element, style) {
    102 var camelStyle = goog.string.toCamelCase(style);
    103
    104 if (element.style[camelStyle] === undefined) {
    105 var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +
    106 goog.string.toTitleCase(camelStyle);
    107
    108 if (element.style[prefixedStyle] !== undefined) {
    109 return prefixedStyle;
    110 }
    111 }
    112
    113 return camelStyle;
    114};
    115
    116
    117/**
    118 * Returns the style property name in CSS notation. If it does not exist and a
    119 * vendor-specific version of the property does exist, then return the vendor-
    120 * specific property name instead.
    121 * @param {Element} element The element to change.
    122 * @param {string} style Style name.
    123 * @return {string} Vendor-specific style.
    124 * @private
    125 */
    126goog.style.getVendorStyleName_ = function(element, style) {
    127 var camelStyle = goog.string.toCamelCase(style);
    128
    129 if (element.style[camelStyle] === undefined) {
    130 var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +
    131 goog.string.toTitleCase(camelStyle);
    132
    133 if (element.style[prefixedStyle] !== undefined) {
    134 return goog.dom.vendor.getVendorPrefix() + '-' + style;
    135 }
    136 }
    137
    138 return style;
    139};
    140
    141
    142/**
    143 * Retrieves an explicitly-set style value of a node. This returns '' if there
    144 * isn't a style attribute on the element or if this style property has not been
    145 * explicitly set in script.
    146 *
    147 * @param {Element} element Element to get style of.
    148 * @param {string} property Property to get, css-style (if you have a camel-case
    149 * property, use element.style[style]).
    150 * @return {string} Style value.
    151 */
    152goog.style.getStyle = function(element, property) {
    153 // element.style is '' for well-known properties which are unset.
    154 // For for browser specific styles as 'filter' is undefined
    155 // so we need to return '' explicitly to make it consistent across
    156 // browsers.
    157 var styleValue = element.style[goog.string.toCamelCase(property)];
    158
    159 // Using typeof here because of a bug in Safari 5.1, where this value
    160 // was undefined, but === undefined returned false.
    161 if (typeof(styleValue) !== 'undefined') {
    162 return styleValue;
    163 }
    164
    165 return element.style[goog.style.getVendorJsStyleName_(element, property)] ||
    166 '';
    167};
    168
    169
    170/**
    171 * Retrieves a computed style value of a node. It returns empty string if the
    172 * value cannot be computed (which will be the case in Internet Explorer) or
    173 * "none" if the property requested is an SVG one and it has not been
    174 * explicitly set (firefox and webkit).
    175 *
    176 * @param {Element} element Element to get style of.
    177 * @param {string} property Property to get (camel-case).
    178 * @return {string} Style value.
    179 */
    180goog.style.getComputedStyle = function(element, property) {
    181 var doc = goog.dom.getOwnerDocument(element);
    182 if (doc.defaultView && doc.defaultView.getComputedStyle) {
    183 var styles = doc.defaultView.getComputedStyle(element, null);
    184 if (styles) {
    185 // element.style[..] is undefined for browser specific styles
    186 // as 'filter'.
    187 return styles[property] || styles.getPropertyValue(property) || '';
    188 }
    189 }
    190
    191 return '';
    192};
    193
    194
    195/**
    196 * Gets the cascaded style value of a node, or null if the value cannot be
    197 * computed (only Internet Explorer can do this).
    198 *
    199 * @param {Element} element Element to get style of.
    200 * @param {string} style Property to get (camel-case).
    201 * @return {string} Style value.
    202 */
    203goog.style.getCascadedStyle = function(element, style) {
    204 // TODO(nicksantos): This should be documented to return null. #fixTypes
    205 return element.currentStyle ? element.currentStyle[style] : null;
    206};
    207
    208
    209/**
    210 * Cross-browser pseudo get computed style. It returns the computed style where
    211 * available. If not available it tries the cascaded style value (IE
    212 * currentStyle) and in worst case the inline style value. It shouldn't be
    213 * called directly, see http://wiki/Main/ComputedStyleVsCascadedStyle for
    214 * discussion.
    215 *
    216 * @param {Element} element Element to get style of.
    217 * @param {string} style Property to get (must be camelCase, not css-style.).
    218 * @return {string} Style value.
    219 * @private
    220 */
    221goog.style.getStyle_ = function(element, style) {
    222 return goog.style.getComputedStyle(element, style) ||
    223 goog.style.getCascadedStyle(element, style) ||
    224 (element.style && element.style[style]);
    225};
    226
    227
    228/**
    229 * Retrieves the computed value of the box-sizing CSS attribute.
    230 * Browser support: http://caniuse.com/css3-boxsizing.
    231 * @param {!Element} element The element whose box-sizing to get.
    232 * @return {?string} 'content-box', 'border-box' or 'padding-box'. null if
    233 * box-sizing is not supported (IE7 and below).
    234 */
    235goog.style.getComputedBoxSizing = function(element) {
    236 return goog.style.getStyle_(element, 'boxSizing') ||
    237 goog.style.getStyle_(element, 'MozBoxSizing') ||
    238 goog.style.getStyle_(element, 'WebkitBoxSizing') || null;
    239};
    240
    241
    242/**
    243 * Retrieves the computed value of the position CSS attribute.
    244 * @param {Element} element The element to get the position of.
    245 * @return {string} Position value.
    246 */
    247goog.style.getComputedPosition = function(element) {
    248 return goog.style.getStyle_(element, 'position');
    249};
    250
    251
    252/**
    253 * Retrieves the computed background color string for a given element. The
    254 * string returned is suitable for assigning to another element's
    255 * background-color, but is not guaranteed to be in any particular string
    256 * format. Accessing the color in a numeric form may not be possible in all
    257 * browsers or with all input.
    258 *
    259 * If the background color for the element is defined as a hexadecimal value,
    260 * the resulting string can be parsed by goog.color.parse in all supported
    261 * browsers.
    262 *
    263 * Whether named colors like "red" or "lightblue" get translated into a
    264 * format which can be parsed is browser dependent. Calling this function on
    265 * transparent elements will return "transparent" in most browsers or
    266 * "rgba(0, 0, 0, 0)" in WebKit.
    267 * @param {Element} element The element to get the background color of.
    268 * @return {string} The computed string value of the background color.
    269 */
    270goog.style.getBackgroundColor = function(element) {
    271 return goog.style.getStyle_(element, 'backgroundColor');
    272};
    273
    274
    275/**
    276 * Retrieves the computed value of the overflow-x CSS attribute.
    277 * @param {Element} element The element to get the overflow-x of.
    278 * @return {string} The computed string value of the overflow-x attribute.
    279 */
    280goog.style.getComputedOverflowX = function(element) {
    281 return goog.style.getStyle_(element, 'overflowX');
    282};
    283
    284
    285/**
    286 * Retrieves the computed value of the overflow-y CSS attribute.
    287 * @param {Element} element The element to get the overflow-y of.
    288 * @return {string} The computed string value of the overflow-y attribute.
    289 */
    290goog.style.getComputedOverflowY = function(element) {
    291 return goog.style.getStyle_(element, 'overflowY');
    292};
    293
    294
    295/**
    296 * Retrieves the computed value of the z-index CSS attribute.
    297 * @param {Element} element The element to get the z-index of.
    298 * @return {string|number} The computed value of the z-index attribute.
    299 */
    300goog.style.getComputedZIndex = function(element) {
    301 return goog.style.getStyle_(element, 'zIndex');
    302};
    303
    304
    305/**
    306 * Retrieves the computed value of the text-align CSS attribute.
    307 * @param {Element} element The element to get the text-align of.
    308 * @return {string} The computed string value of the text-align attribute.
    309 */
    310goog.style.getComputedTextAlign = function(element) {
    311 return goog.style.getStyle_(element, 'textAlign');
    312};
    313
    314
    315/**
    316 * Retrieves the computed value of the cursor CSS attribute.
    317 * @param {Element} element The element to get the cursor of.
    318 * @return {string} The computed string value of the cursor attribute.
    319 */
    320goog.style.getComputedCursor = function(element) {
    321 return goog.style.getStyle_(element, 'cursor');
    322};
    323
    324
    325/**
    326 * Retrieves the computed value of the CSS transform attribute.
    327 * @param {Element} element The element to get the transform of.
    328 * @return {string} The computed string representation of the transform matrix.
    329 */
    330goog.style.getComputedTransform = function(element) {
    331 var property = goog.style.getVendorStyleName_(element, 'transform');
    332 return goog.style.getStyle_(element, property) ||
    333 goog.style.getStyle_(element, 'transform');
    334};
    335
    336
    337/**
    338 * Sets the top/left values of an element. If no unit is specified in the
    339 * argument then it will add px. The second argument is required if the first
    340 * argument is a string or number and is ignored if the first argument
    341 * is a coordinate.
    342 * @param {Element} el Element to move.
    343 * @param {string|number|goog.math.Coordinate} arg1 Left position or coordinate.
    344 * @param {string|number=} opt_arg2 Top position.
    345 */
    346goog.style.setPosition = function(el, arg1, opt_arg2) {
    347 var x, y;
    348 var buggyGeckoSubPixelPos = goog.userAgent.GECKO &&
    349 (goog.userAgent.MAC || goog.userAgent.X11) &&
    350 goog.userAgent.isVersionOrHigher('1.9');
    351
    352 if (arg1 instanceof goog.math.Coordinate) {
    353 x = arg1.x;
    354 y = arg1.y;
    355 } else {
    356 x = arg1;
    357 y = opt_arg2;
    358 }
    359
    360 // Round to the nearest pixel for buggy sub-pixel support.
    361 el.style.left = goog.style.getPixelStyleValue_(
    362 /** @type {number|string} */ (x), buggyGeckoSubPixelPos);
    363 el.style.top = goog.style.getPixelStyleValue_(
    364 /** @type {number|string} */ (y), buggyGeckoSubPixelPos);
    365};
    366
    367
    368/**
    369 * Gets the offsetLeft and offsetTop properties of an element and returns them
    370 * in a Coordinate object
    371 * @param {Element} element Element.
    372 * @return {!goog.math.Coordinate} The position.
    373 */
    374goog.style.getPosition = function(element) {
    375 return new goog.math.Coordinate(element.offsetLeft, element.offsetTop);
    376};
    377
    378
    379/**
    380 * Returns the viewport element for a particular document
    381 * @param {Node=} opt_node DOM node (Document is OK) to get the viewport element
    382 * of.
    383 * @return {Element} document.documentElement or document.body.
    384 */
    385goog.style.getClientViewportElement = function(opt_node) {
    386 var doc;
    387 if (opt_node) {
    388 doc = goog.dom.getOwnerDocument(opt_node);
    389 } else {
    390 doc = goog.dom.getDocument();
    391 }
    392
    393 // In old IE versions the document.body represented the viewport
    394 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&
    395 !goog.dom.getDomHelper(doc).isCss1CompatMode()) {
    396 return doc.body;
    397 }
    398 return doc.documentElement;
    399};
    400
    401
    402/**
    403 * Calculates the viewport coordinates relative to the page/document
    404 * containing the node. The viewport may be the browser viewport for
    405 * non-iframe document, or the iframe container for iframe'd document.
    406 * @param {!Document} doc The document to use as the reference point.
    407 * @return {!goog.math.Coordinate} The page offset of the viewport.
    408 */
    409goog.style.getViewportPageOffset = function(doc) {
    410 var body = doc.body;
    411 var documentElement = doc.documentElement;
    412 var scrollLeft = body.scrollLeft || documentElement.scrollLeft;
    413 var scrollTop = body.scrollTop || documentElement.scrollTop;
    414 return new goog.math.Coordinate(scrollLeft, scrollTop);
    415};
    416
    417
    418/**
    419 * Gets the client rectangle of the DOM element.
    420 *
    421 * getBoundingClientRect is part of a new CSS object model draft (with a
    422 * long-time presence in IE), replacing the error-prone parent offset
    423 * computation and the now-deprecated Gecko getBoxObjectFor.
    424 *
    425 * This utility patches common browser bugs in getBoundingClientRect. It
    426 * will fail if getBoundingClientRect is unsupported.
    427 *
    428 * If the element is not in the DOM, the result is undefined, and an error may
    429 * be thrown depending on user agent.
    430 *
    431 * @param {!Element} el The element whose bounding rectangle is being queried.
    432 * @return {Object} A native bounding rectangle with numerical left, top,
    433 * right, and bottom. Reported by Firefox to be of object type ClientRect.
    434 * @private
    435 */
    436goog.style.getBoundingClientRect_ = function(el) {
    437 var rect;
    438 try {
    439 rect = el.getBoundingClientRect();
    440 } catch (e) {
    441 // In IE < 9, calling getBoundingClientRect on an orphan element raises an
    442 // "Unspecified Error". All other browsers return zeros.
    443 return {'left': 0, 'top': 0, 'right': 0, 'bottom': 0};
    444 }
    445
    446 // Patch the result in IE only, so that this function can be inlined if
    447 // compiled for non-IE.
    448 if (goog.userAgent.IE && el.ownerDocument.body) {
    449
    450 // In IE, most of the time, 2 extra pixels are added to the top and left
    451 // due to the implicit 2-pixel inset border. In IE6/7 quirks mode and
    452 // IE6 standards mode, this border can be overridden by setting the
    453 // document element's border to zero -- thus, we cannot rely on the
    454 // offset always being 2 pixels.
    455
    456 // In quirks mode, the offset can be determined by querying the body's
    457 // clientLeft/clientTop, but in standards mode, it is found by querying
    458 // the document element's clientLeft/clientTop. Since we already called
    459 // getBoundingClientRect we have already forced a reflow, so it is not
    460 // too expensive just to query them all.
    461
    462 // See: http://msdn.microsoft.com/en-us/library/ms536433(VS.85).aspx
    463 var doc = el.ownerDocument;
    464 rect.left -= doc.documentElement.clientLeft + doc.body.clientLeft;
    465 rect.top -= doc.documentElement.clientTop + doc.body.clientTop;
    466 }
    467 return /** @type {Object} */ (rect);
    468};
    469
    470
    471/**
    472 * Returns the first parent that could affect the position of a given element.
    473 * @param {Element} element The element to get the offset parent for.
    474 * @return {Element} The first offset parent or null if one cannot be found.
    475 */
    476goog.style.getOffsetParent = function(element) {
    477 // element.offsetParent does the right thing in IE7 and below. In other
    478 // browsers it only includes elements with position absolute, relative or
    479 // fixed, not elements with overflow set to auto or scroll.
    480 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(8)) {
    481 return element.offsetParent;
    482 }
    483
    484 var doc = goog.dom.getOwnerDocument(element);
    485 var positionStyle = goog.style.getStyle_(element, 'position');
    486 var skipStatic = positionStyle == 'fixed' || positionStyle == 'absolute';
    487 for (var parent = element.parentNode; parent && parent != doc;
    488 parent = parent.parentNode) {
    489 positionStyle =
    490 goog.style.getStyle_(/** @type {!Element} */ (parent), 'position');
    491 skipStatic = skipStatic && positionStyle == 'static' &&
    492 parent != doc.documentElement && parent != doc.body;
    493 if (!skipStatic && (parent.scrollWidth > parent.clientWidth ||
    494 parent.scrollHeight > parent.clientHeight ||
    495 positionStyle == 'fixed' ||
    496 positionStyle == 'absolute' ||
    497 positionStyle == 'relative')) {
    498 return /** @type {!Element} */ (parent);
    499 }
    500 }
    501 return null;
    502};
    503
    504
    505/**
    506 * Calculates and returns the visible rectangle for a given element. Returns a
    507 * box describing the visible portion of the nearest scrollable offset ancestor.
    508 * Coordinates are given relative to the document.
    509 *
    510 * @param {Element} element Element to get the visible rect for.
    511 * @return {goog.math.Box} Bounding elementBox describing the visible rect or
    512 * null if scrollable ancestor isn't inside the visible viewport.
    513 */
    514goog.style.getVisibleRectForElement = function(element) {
    515 var visibleRect = new goog.math.Box(0, Infinity, Infinity, 0);
    516 var dom = goog.dom.getDomHelper(element);
    517 var body = dom.getDocument().body;
    518 var documentElement = dom.getDocument().documentElement;
    519 var scrollEl = dom.getDocumentScrollElement();
    520
    521 // Determine the size of the visible rect by climbing the dom accounting for
    522 // all scrollable containers.
    523 for (var el = element; el = goog.style.getOffsetParent(el); ) {
    524 // clientWidth is zero for inline block elements in IE.
    525 // on WEBKIT, body element can have clientHeight = 0 and scrollHeight > 0
    526 if ((!goog.userAgent.IE || el.clientWidth != 0) &&
    527 (!goog.userAgent.WEBKIT || el.clientHeight != 0 || el != body) &&
    528 // body may have overflow set on it, yet we still get the entire
    529 // viewport. In some browsers, el.offsetParent may be
    530 // document.documentElement, so check for that too.
    531 (el != body && el != documentElement &&
    532 goog.style.getStyle_(el, 'overflow') != 'visible')) {
    533 var pos = goog.style.getPageOffset(el);
    534 var client = goog.style.getClientLeftTop(el);
    535 pos.x += client.x;
    536 pos.y += client.y;
    537
    538 visibleRect.top = Math.max(visibleRect.top, pos.y);
    539 visibleRect.right = Math.min(visibleRect.right,
    540 pos.x + el.clientWidth);
    541 visibleRect.bottom = Math.min(visibleRect.bottom,
    542 pos.y + el.clientHeight);
    543 visibleRect.left = Math.max(visibleRect.left, pos.x);
    544 }
    545 }
    546
    547 // Clip by window's viewport.
    548 var scrollX = scrollEl.scrollLeft, scrollY = scrollEl.scrollTop;
    549 visibleRect.left = Math.max(visibleRect.left, scrollX);
    550 visibleRect.top = Math.max(visibleRect.top, scrollY);
    551 var winSize = dom.getViewportSize();
    552 visibleRect.right = Math.min(visibleRect.right, scrollX + winSize.width);
    553 visibleRect.bottom = Math.min(visibleRect.bottom, scrollY + winSize.height);
    554 return visibleRect.top >= 0 && visibleRect.left >= 0 &&
    555 visibleRect.bottom > visibleRect.top &&
    556 visibleRect.right > visibleRect.left ?
    557 visibleRect : null;
    558};
    559
    560
    561/**
    562 * Calculate the scroll position of {@code container} with the minimum amount so
    563 * that the content and the borders of the given {@code element} become visible.
    564 * If the element is bigger than the container, its top left corner will be
    565 * aligned as close to the container's top left corner as possible.
    566 *
    567 * @param {Element} element The element to make visible.
    568 * @param {Element} container The container to scroll.
    569 * @param {boolean=} opt_center Whether to center the element in the container.
    570 * Defaults to false.
    571 * @return {!goog.math.Coordinate} The new scroll position of the container,
    572 * in form of goog.math.Coordinate(scrollLeft, scrollTop).
    573 */
    574goog.style.getContainerOffsetToScrollInto =
    575 function(element, container, opt_center) {
    576 // Absolute position of the element's border's top left corner.
    577 var elementPos = goog.style.getPageOffset(element);
    578 // Absolute position of the container's border's top left corner.
    579 var containerPos = goog.style.getPageOffset(container);
    580 var containerBorder = goog.style.getBorderBox(container);
    581 // Relative pos. of the element's border box to the container's content box.
    582 var relX = elementPos.x - containerPos.x - containerBorder.left;
    583 var relY = elementPos.y - containerPos.y - containerBorder.top;
    584 // How much the element can move in the container, i.e. the difference between
    585 // the element's bottom-right-most and top-left-most position where it's
    586 // fully visible.
    587 var spaceX = container.clientWidth - element.offsetWidth;
    588 var spaceY = container.clientHeight - element.offsetHeight;
    589
    590 var scrollLeft = container.scrollLeft;
    591 var scrollTop = container.scrollTop;
    592 if (opt_center) {
    593 // All browsers round non-integer scroll positions down.
    594 scrollLeft += relX - spaceX / 2;
    595 scrollTop += relY - spaceY / 2;
    596 } else {
    597 // This formula was designed to give the correct scroll values in the
    598 // following cases:
    599 // - element is higher than container (spaceY < 0) => scroll down by relY
    600 // - element is not higher that container (spaceY >= 0):
    601 // - it is above container (relY < 0) => scroll up by abs(relY)
    602 // - it is below container (relY > spaceY) => scroll down by relY - spaceY
    603 // - it is in the container => don't scroll
    604 scrollLeft += Math.min(relX, Math.max(relX - spaceX, 0));
    605 scrollTop += Math.min(relY, Math.max(relY - spaceY, 0));
    606 }
    607 return new goog.math.Coordinate(scrollLeft, scrollTop);
    608};
    609
    610
    611/**
    612 * Changes the scroll position of {@code container} with the minimum amount so
    613 * that the content and the borders of the given {@code element} become visible.
    614 * If the element is bigger than the container, its top left corner will be
    615 * aligned as close to the container's top left corner as possible.
    616 *
    617 * @param {Element} element The element to make visible.
    618 * @param {Element} container The container to scroll.
    619 * @param {boolean=} opt_center Whether to center the element in the container.
    620 * Defaults to false.
    621 */
    622goog.style.scrollIntoContainerView = function(element, container, opt_center) {
    623 var offset =
    624 goog.style.getContainerOffsetToScrollInto(element, container, opt_center);
    625 container.scrollLeft = offset.x;
    626 container.scrollTop = offset.y;
    627};
    628
    629
    630/**
    631 * Returns clientLeft (width of the left border and, if the directionality is
    632 * right to left, the vertical scrollbar) and clientTop as a coordinate object.
    633 *
    634 * @param {Element} el Element to get clientLeft for.
    635 * @return {!goog.math.Coordinate} Client left and top.
    636 */
    637goog.style.getClientLeftTop = function(el) {
    638 // NOTE(eae): Gecko prior to 1.9 doesn't support clientTop/Left, see
    639 // https://bugzilla.mozilla.org/show_bug.cgi?id=111207
    640 if (goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('1.9')) {
    641 var left = parseFloat(goog.style.getComputedStyle(el, 'borderLeftWidth'));
    642 if (goog.style.isRightToLeft(el)) {
    643 var scrollbarWidth = el.offsetWidth - el.clientWidth - left -
    644 parseFloat(goog.style.getComputedStyle(el, 'borderRightWidth'));
    645 left += scrollbarWidth;
    646 }
    647 return new goog.math.Coordinate(left,
    648 parseFloat(goog.style.getComputedStyle(el, 'borderTopWidth')));
    649 }
    650
    651 return new goog.math.Coordinate(el.clientLeft, el.clientTop);
    652};
    653
    654
    655/**
    656 * Returns a Coordinate object relative to the top-left of the HTML document.
    657 * Implemented as a single function to save having to do two recursive loops in
    658 * opera and safari just to get both coordinates. If you just want one value do
    659 * use goog.style.getPageOffsetLeft() and goog.style.getPageOffsetTop(), but
    660 * note if you call both those methods the tree will be analysed twice.
    661 *
    662 * @param {Element} el Element to get the page offset for.
    663 * @return {!goog.math.Coordinate} The page offset.
    664 */
    665goog.style.getPageOffset = function(el) {
    666 var box, doc = goog.dom.getOwnerDocument(el);
    667 var positionStyle = goog.style.getStyle_(el, 'position');
    668 // TODO(gboyer): Update the jsdoc in a way that doesn't break the universe.
    669 goog.asserts.assertObject(el, 'Parameter is required');
    670
    671 // NOTE(eae): Gecko pre 1.9 normally use getBoxObjectFor to calculate the
    672 // position. When invoked for an element with position absolute and a negative
    673 // position though it can be off by one. Therefor the recursive implementation
    674 // is used in those (relatively rare) cases.
    675 var BUGGY_GECKO_BOX_OBJECT =
    676 !goog.style.GET_BOUNDING_CLIENT_RECT_ALWAYS_EXISTS &&
    677 goog.userAgent.GECKO && doc.getBoxObjectFor &&
    678 !el.getBoundingClientRect && positionStyle == 'absolute' &&
    679 (box = doc.getBoxObjectFor(el)) && (box.screenX < 0 || box.screenY < 0);
    680
    681 // NOTE(arv): If element is hidden (display none or disconnected or any the
    682 // ancestors are hidden) we get (0,0) by default but we still do the
    683 // accumulation of scroll position.
    684
    685 // TODO(arv): Should we check if the node is disconnected and in that case
    686 // return (0,0)?
    687
    688 var pos = new goog.math.Coordinate(0, 0);
    689 var viewportElement = goog.style.getClientViewportElement(doc);
    690 if (el == viewportElement) {
    691 // viewport is always at 0,0 as that defined the coordinate system for this
    692 // function - this avoids special case checks in the code below
    693 return pos;
    694 }
    695
    696 // IE, Gecko 1.9+, and most modern WebKit.
    697 if (goog.style.GET_BOUNDING_CLIENT_RECT_ALWAYS_EXISTS ||
    698 el.getBoundingClientRect) {
    699 box = goog.style.getBoundingClientRect_(el);
    700 // Must add the scroll coordinates in to get the absolute page offset
    701 // of element since getBoundingClientRect returns relative coordinates to
    702 // the viewport.
    703 var scrollCoord = goog.dom.getDomHelper(doc).getDocumentScroll();
    704 pos.x = box.left + scrollCoord.x;
    705 pos.y = box.top + scrollCoord.y;
    706
    707 // Gecko prior to 1.9.
    708 } else if (doc.getBoxObjectFor && !BUGGY_GECKO_BOX_OBJECT) {
    709 // Gecko ignores the scroll values for ancestors, up to 1.9. See:
    710 // https://bugzilla.mozilla.org/show_bug.cgi?id=328881 and
    711 // https://bugzilla.mozilla.org/show_bug.cgi?id=330619
    712
    713 box = doc.getBoxObjectFor(el);
    714 // TODO(user): Fix the off-by-one error when window is scrolled down
    715 // or right more than 1 pixel. The viewport offset does not move in lock
    716 // step with the window scroll; it moves in increments of 2px and at
    717 // somewhat random intervals.
    718 var vpBox = doc.getBoxObjectFor(viewportElement);
    719 pos.x = box.screenX - vpBox.screenX;
    720 pos.y = box.screenY - vpBox.screenY;
    721
    722 // Safari, Opera and Camino up to 1.0.4.
    723 } else {
    724 var parent = el;
    725 do {
    726 pos.x += parent.offsetLeft;
    727 pos.y += parent.offsetTop;
    728 // For safari/chrome, we need to add parent's clientLeft/Top as well.
    729 if (parent != el) {
    730 pos.x += parent.clientLeft || 0;
    731 pos.y += parent.clientTop || 0;
    732 }
    733 // In Safari when hit a position fixed element the rest of the offsets
    734 // are not correct.
    735 if (goog.userAgent.WEBKIT &&
    736 goog.style.getComputedPosition(parent) == 'fixed') {
    737 pos.x += doc.body.scrollLeft;
    738 pos.y += doc.body.scrollTop;
    739 break;
    740 }
    741 parent = parent.offsetParent;
    742 } while (parent && parent != el);
    743
    744 // Opera & (safari absolute) incorrectly account for body offsetTop.
    745 if (goog.userAgent.OPERA || (goog.userAgent.WEBKIT &&
    746 positionStyle == 'absolute')) {
    747 pos.y -= doc.body.offsetTop;
    748 }
    749
    750 for (parent = el; (parent = goog.style.getOffsetParent(parent)) &&
    751 parent != doc.body && parent != viewportElement; ) {
    752 pos.x -= parent.scrollLeft;
    753 // Workaround for a bug in Opera 9.2 (and earlier) where table rows may
    754 // report an invalid scroll top value. The bug was fixed in Opera 9.5
    755 // however as that version supports getBoundingClientRect it won't
    756 // trigger this code path. https://bugs.opera.com/show_bug.cgi?id=249965
    757 if (!goog.userAgent.OPERA || parent.tagName != 'TR') {
    758 pos.y -= parent.scrollTop;
    759 }
    760 }
    761 }
    762
    763 return pos;
    764};
    765
    766
    767/**
    768 * Returns the left coordinate of an element relative to the HTML document
    769 * @param {Element} el Elements.
    770 * @return {number} The left coordinate.
    771 */
    772goog.style.getPageOffsetLeft = function(el) {
    773 return goog.style.getPageOffset(el).x;
    774};
    775
    776
    777/**
    778 * Returns the top coordinate of an element relative to the HTML document
    779 * @param {Element} el Elements.
    780 * @return {number} The top coordinate.
    781 */
    782goog.style.getPageOffsetTop = function(el) {
    783 return goog.style.getPageOffset(el).y;
    784};
    785
    786
    787/**
    788 * Returns a Coordinate object relative to the top-left of an HTML document
    789 * in an ancestor frame of this element. Used for measuring the position of
    790 * an element inside a frame relative to a containing frame.
    791 *
    792 * @param {Element} el Element to get the page offset for.
    793 * @param {Window} relativeWin The window to measure relative to. If relativeWin
    794 * is not in the ancestor frame chain of the element, we measure relative to
    795 * the top-most window.
    796 * @return {!goog.math.Coordinate} The page offset.
    797 */
    798goog.style.getFramedPageOffset = function(el, relativeWin) {
    799 var position = new goog.math.Coordinate(0, 0);
    800
    801 // Iterate up the ancestor frame chain, keeping track of the current window
    802 // and the current element in that window.
    803 var currentWin = goog.dom.getWindow(goog.dom.getOwnerDocument(el));
    804 var currentEl = el;
    805 do {
    806 // if we're at the top window, we want to get the page offset.
    807 // if we're at an inner frame, we only want to get the window position
    808 // so that we can determine the actual page offset in the context of
    809 // the outer window.
    810 var offset = currentWin == relativeWin ?
    811 goog.style.getPageOffset(currentEl) :
    812 goog.style.getClientPositionForElement_(
    813 goog.asserts.assert(currentEl));
    814
    815 position.x += offset.x;
    816 position.y += offset.y;
    817 } while (currentWin && currentWin != relativeWin &&
    818 (currentEl = currentWin.frameElement) &&
    819 (currentWin = currentWin.parent));
    820
    821 return position;
    822};
    823
    824
    825/**
    826 * Translates the specified rect relative to origBase page, for newBase page.
    827 * If origBase and newBase are the same, this function does nothing.
    828 *
    829 * @param {goog.math.Rect} rect The source rectangle relative to origBase page,
    830 * and it will have the translated result.
    831 * @param {goog.dom.DomHelper} origBase The DomHelper for the input rectangle.
    832 * @param {goog.dom.DomHelper} newBase The DomHelper for the resultant
    833 * coordinate. This must be a DOM for an ancestor frame of origBase
    834 * or the same as origBase.
    835 */
    836goog.style.translateRectForAnotherFrame = function(rect, origBase, newBase) {
    837 if (origBase.getDocument() != newBase.getDocument()) {
    838 var body = origBase.getDocument().body;
    839 var pos = goog.style.getFramedPageOffset(body, newBase.getWindow());
    840
    841 // Adjust Body's margin.
    842 pos = goog.math.Coordinate.difference(pos, goog.style.getPageOffset(body));
    843
    844 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&
    845 !origBase.isCss1CompatMode()) {
    846 pos = goog.math.Coordinate.difference(pos, origBase.getDocumentScroll());
    847 }
    848
    849 rect.left += pos.x;
    850 rect.top += pos.y;
    851 }
    852};
    853
    854
    855/**
    856 * Returns the position of an element relative to another element in the
    857 * document. A relative to B
    858 * @param {Element|Event|goog.events.Event} a Element or mouse event whose
    859 * position we're calculating.
    860 * @param {Element|Event|goog.events.Event} b Element or mouse event position
    861 * is relative to.
    862 * @return {!goog.math.Coordinate} The relative position.
    863 */
    864goog.style.getRelativePosition = function(a, b) {
    865 var ap = goog.style.getClientPosition(a);
    866 var bp = goog.style.getClientPosition(b);
    867 return new goog.math.Coordinate(ap.x - bp.x, ap.y - bp.y);
    868};
    869
    870
    871/**
    872 * Returns the position of the event or the element's border box relative to
    873 * the client viewport.
    874 * @param {!Element} el Element whose position to get.
    875 * @return {!goog.math.Coordinate} The position.
    876 * @private
    877 */
    878goog.style.getClientPositionForElement_ = function(el) {
    879 var pos;
    880 if (goog.style.GET_BOUNDING_CLIENT_RECT_ALWAYS_EXISTS ||
    881 el.getBoundingClientRect) {
    882 // IE, Gecko 1.9+, and most modern WebKit
    883 var box = goog.style.getBoundingClientRect_(el);
    884 pos = new goog.math.Coordinate(box.left, box.top);
    885 } else {
    886 var scrollCoord = goog.dom.getDomHelper(el).getDocumentScroll();
    887 var pageCoord = goog.style.getPageOffset(el);
    888 pos = new goog.math.Coordinate(
    889 pageCoord.x - scrollCoord.x,
    890 pageCoord.y - scrollCoord.y);
    891 }
    892
    893 // Gecko below version 12 doesn't add CSS translation to the client position
    894 // (using either getBoundingClientRect or getBoxOffsetFor) so we need to do
    895 // so manually.
    896 if (goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher(12)) {
    897 return goog.math.Coordinate.sum(pos, goog.style.getCssTranslation(el));
    898 } else {
    899 return pos;
    900 }
    901};
    902
    903
    904/**
    905 * Returns the position of the event or the element's border box relative to
    906 * the client viewport.
    907 * @param {Element|Event|goog.events.Event} el Element or a mouse / touch event.
    908 * @return {!goog.math.Coordinate} The position.
    909 */
    910goog.style.getClientPosition = function(el) {
    911 goog.asserts.assert(el);
    912 if (el.nodeType == goog.dom.NodeType.ELEMENT) {
    913 return goog.style.getClientPositionForElement_(
    914 /** @type {!Element} */ (el));
    915 } else {
    916 var isAbstractedEvent = goog.isFunction(el.getBrowserEvent);
    917 var be = /** @type {!goog.events.BrowserEvent} */ (el);
    918 var targetEvent = el;
    919
    920 if (el.targetTouches && el.targetTouches.length) {
    921 targetEvent = el.targetTouches[0];
    922 } else if (isAbstractedEvent && be.getBrowserEvent().targetTouches &&
    923 be.getBrowserEvent().targetTouches.length) {
    924 targetEvent = be.getBrowserEvent().targetTouches[0];
    925 }
    926
    927 return new goog.math.Coordinate(
    928 targetEvent.clientX,
    929 targetEvent.clientY);
    930 }
    931};
    932
    933
    934/**
    935 * Moves an element to the given coordinates relative to the client viewport.
    936 * @param {Element} el Absolutely positioned element to set page offset for.
    937 * It must be in the document.
    938 * @param {number|goog.math.Coordinate} x Left position of the element's margin
    939 * box or a coordinate object.
    940 * @param {number=} opt_y Top position of the element's margin box.
    941 */
    942goog.style.setPageOffset = function(el, x, opt_y) {
    943 // Get current pageoffset
    944 var cur = goog.style.getPageOffset(el);
    945
    946 if (x instanceof goog.math.Coordinate) {
    947 opt_y = x.y;
    948 x = x.x;
    949 }
    950
    951 // NOTE(arv): We cannot allow strings for x and y. We could but that would
    952 // require us to manually transform between different units
    953
    954 // Work out deltas
    955 var dx = x - cur.x;
    956 var dy = opt_y - cur.y;
    957
    958 // Set position to current left/top + delta
    959 goog.style.setPosition(el, el.offsetLeft + dx, el.offsetTop + dy);
    960};
    961
    962
    963/**
    964 * Sets the width/height values of an element. If an argument is numeric,
    965 * or a goog.math.Size is passed, it is assumed to be pixels and will add
    966 * 'px' after converting it to an integer in string form. (This just sets the
    967 * CSS width and height properties so it might set content-box or border-box
    968 * size depending on the box model the browser is using.)
    969 *
    970 * @param {Element} element Element to set the size of.
    971 * @param {string|number|goog.math.Size} w Width of the element, or a
    972 * size object.
    973 * @param {string|number=} opt_h Height of the element. Required if w is not a
    974 * size object.
    975 */
    976goog.style.setSize = function(element, w, opt_h) {
    977 var h;
    978 if (w instanceof goog.math.Size) {
    979 h = w.height;
    980 w = w.width;
    981 } else {
    982 if (opt_h == undefined) {
    983 throw Error('missing height argument');
    984 }
    985 h = opt_h;
    986 }
    987
    988 goog.style.setWidth(element, /** @type {string|number} */ (w));
    989 goog.style.setHeight(element, /** @type {string|number} */ (h));
    990};
    991
    992
    993/**
    994 * Helper function to create a string to be set into a pixel-value style
    995 * property of an element. Can round to the nearest integer value.
    996 *
    997 * @param {string|number} value The style value to be used. If a number,
    998 * 'px' will be appended, otherwise the value will be applied directly.
    999 * @param {boolean} round Whether to round the nearest integer (if property
    1000 * is a number).
    1001 * @return {string} The string value for the property.
    1002 * @private
    1003 */
    1004goog.style.getPixelStyleValue_ = function(value, round) {
    1005 if (typeof value == 'number') {
    1006 value = (round ? Math.round(value) : value) + 'px';
    1007 }
    1008
    1009 return value;
    1010};
    1011
    1012
    1013/**
    1014 * Set the height of an element. Sets the element's style property.
    1015 * @param {Element} element Element to set the height of.
    1016 * @param {string|number} height The height value to set. If a number, 'px'
    1017 * will be appended, otherwise the value will be applied directly.
    1018 */
    1019goog.style.setHeight = function(element, height) {
    1020 element.style.height = goog.style.getPixelStyleValue_(height, true);
    1021};
    1022
    1023
    1024/**
    1025 * Set the width of an element. Sets the element's style property.
    1026 * @param {Element} element Element to set the width of.
    1027 * @param {string|number} width The width value to set. If a number, 'px'
    1028 * will be appended, otherwise the value will be applied directly.
    1029 */
    1030goog.style.setWidth = function(element, width) {
    1031 element.style.width = goog.style.getPixelStyleValue_(width, true);
    1032};
    1033
    1034
    1035/**
    1036 * Gets the height and width of an element, even if its display is none.
    1037 *
    1038 * Specifically, this returns the height and width of the border box,
    1039 * irrespective of the box model in effect.
    1040 *
    1041 * Note that this function does not take CSS transforms into account. Please see
    1042 * {@code goog.style.getTransformedSize}.
    1043 * @param {Element} element Element to get size of.
    1044 * @return {!goog.math.Size} Object with width/height properties.
    1045 */
    1046goog.style.getSize = function(element) {
    1047 return goog.style.evaluateWithTemporaryDisplay_(
    1048 goog.style.getSizeWithDisplay_, /** @type {!Element} */ (element));
    1049};
    1050
    1051
    1052/**
    1053 * Call {@code fn} on {@code element} such that {@code element}'s dimensions are
    1054 * accurate when it's passed to {@code fn}.
    1055 * @param {function(!Element): T} fn Function to call with {@code element} as
    1056 * an argument after temporarily changing {@code element}'s display such
    1057 * that its dimensions are accurate.
    1058 * @param {!Element} element Element (which may have display none) to use as
    1059 * argument to {@code fn}.
    1060 * @return {T} Value returned by calling {@code fn} with {@code element}.
    1061 * @template T
    1062 * @private
    1063 */
    1064goog.style.evaluateWithTemporaryDisplay_ = function(fn, element) {
    1065 if (goog.style.getStyle_(element, 'display') != 'none') {
    1066 return fn(element);
    1067 }
    1068
    1069 var style = element.style;
    1070 var originalDisplay = style.display;
    1071 var originalVisibility = style.visibility;
    1072 var originalPosition = style.position;
    1073
    1074 style.visibility = 'hidden';
    1075 style.position = 'absolute';
    1076 style.display = 'inline';
    1077
    1078 var retVal = fn(element);
    1079
    1080 style.display = originalDisplay;
    1081 style.position = originalPosition;
    1082 style.visibility = originalVisibility;
    1083
    1084 return retVal;
    1085};
    1086
    1087
    1088/**
    1089 * Gets the height and width of an element when the display is not none.
    1090 * @param {Element} element Element to get size of.
    1091 * @return {!goog.math.Size} Object with width/height properties.
    1092 * @private
    1093 */
    1094goog.style.getSizeWithDisplay_ = function(element) {
    1095 var offsetWidth = element.offsetWidth;
    1096 var offsetHeight = element.offsetHeight;
    1097 var webkitOffsetsZero =
    1098 goog.userAgent.WEBKIT && !offsetWidth && !offsetHeight;
    1099 if ((!goog.isDef(offsetWidth) || webkitOffsetsZero) &&
    1100 element.getBoundingClientRect) {
    1101 // Fall back to calling getBoundingClientRect when offsetWidth or
    1102 // offsetHeight are not defined, or when they are zero in WebKit browsers.
    1103 // This makes sure that we return for the correct size for SVG elements, but
    1104 // will still return 0 on Webkit prior to 534.8, see
    1105 // http://trac.webkit.org/changeset/67252.
    1106 var clientRect = goog.style.getBoundingClientRect_(element);
    1107 return new goog.math.Size(clientRect.right - clientRect.left,
    1108 clientRect.bottom - clientRect.top);
    1109 }
    1110 return new goog.math.Size(offsetWidth, offsetHeight);
    1111};
    1112
    1113
    1114/**
    1115 * Gets the height and width of an element, post transform, even if its display
    1116 * is none.
    1117 *
    1118 * This is like {@code goog.style.getSize}, except:
    1119 * <ol>
    1120 * <li>Takes webkitTransforms such as rotate and scale into account.
    1121 * <li>Will return null if {@code element} doesn't respond to
    1122 * {@code getBoundingClientRect}.
    1123 * <li>Currently doesn't make sense on non-WebKit browsers which don't support
    1124 * webkitTransforms.
    1125 * </ol>
    1126 * @param {!Element} element Element to get size of.
    1127 * @return {goog.math.Size} Object with width/height properties.
    1128 */
    1129goog.style.getTransformedSize = function(element) {
    1130 if (!element.getBoundingClientRect) {
    1131 return null;
    1132 }
    1133
    1134 var clientRect = goog.style.evaluateWithTemporaryDisplay_(
    1135 goog.style.getBoundingClientRect_, element);
    1136 return new goog.math.Size(clientRect.right - clientRect.left,
    1137 clientRect.bottom - clientRect.top);
    1138};
    1139
    1140
    1141/**
    1142 * Returns a bounding rectangle for a given element in page space.
    1143 * @param {Element} element Element to get bounds of. Must not be display none.
    1144 * @return {!goog.math.Rect} Bounding rectangle for the element.
    1145 */
    1146goog.style.getBounds = function(element) {
    1147 var o = goog.style.getPageOffset(element);
    1148 var s = goog.style.getSize(element);
    1149 return new goog.math.Rect(o.x, o.y, s.width, s.height);
    1150};
    1151
    1152
    1153/**
    1154 * Converts a CSS selector in the form style-property to styleProperty.
    1155 * @param {*} selector CSS Selector.
    1156 * @return {string} Camel case selector.
    1157 * @deprecated Use goog.string.toCamelCase instead.
    1158 */
    1159goog.style.toCamelCase = function(selector) {
    1160 return goog.string.toCamelCase(String(selector));
    1161};
    1162
    1163
    1164/**
    1165 * Converts a CSS selector in the form styleProperty to style-property.
    1166 * @param {string} selector Camel case selector.
    1167 * @return {string} Selector cased.
    1168 * @deprecated Use goog.string.toSelectorCase instead.
    1169 */
    1170goog.style.toSelectorCase = function(selector) {
    1171 return goog.string.toSelectorCase(selector);
    1172};
    1173
    1174
    1175/**
    1176 * Gets the opacity of a node (x-browser). This gets the inline style opacity
    1177 * of the node, and does not take into account the cascaded or the computed
    1178 * style for this node.
    1179 * @param {Element} el Element whose opacity has to be found.
    1180 * @return {number|string} Opacity between 0 and 1 or an empty string {@code ''}
    1181 * if the opacity is not set.
    1182 */
    1183goog.style.getOpacity = function(el) {
    1184 var style = el.style;
    1185 var result = '';
    1186 if ('opacity' in style) {
    1187 result = style.opacity;
    1188 } else if ('MozOpacity' in style) {
    1189 result = style.MozOpacity;
    1190 } else if ('filter' in style) {
    1191 var match = style.filter.match(/alpha\(opacity=([\d.]+)\)/);
    1192 if (match) {
    1193 result = String(match[1] / 100);
    1194 }
    1195 }
    1196 return result == '' ? result : Number(result);
    1197};
    1198
    1199
    1200/**
    1201 * Sets the opacity of a node (x-browser).
    1202 * @param {Element} el Elements whose opacity has to be set.
    1203 * @param {number|string} alpha Opacity between 0 and 1 or an empty string
    1204 * {@code ''} to clear the opacity.
    1205 */
    1206goog.style.setOpacity = function(el, alpha) {
    1207 var style = el.style;
    1208 if ('opacity' in style) {
    1209 style.opacity = alpha;
    1210 } else if ('MozOpacity' in style) {
    1211 style.MozOpacity = alpha;
    1212 } else if ('filter' in style) {
    1213 // TODO(arv): Overwriting the filter might have undesired side effects.
    1214 if (alpha === '') {
    1215 style.filter = '';
    1216 } else {
    1217 style.filter = 'alpha(opacity=' + alpha * 100 + ')';
    1218 }
    1219 }
    1220};
    1221
    1222
    1223/**
    1224 * Sets the background of an element to a transparent image in a browser-
    1225 * independent manner.
    1226 *
    1227 * This function does not support repeating backgrounds or alternate background
    1228 * positions to match the behavior of Internet Explorer. It also does not
    1229 * support sizingMethods other than crop since they cannot be replicated in
    1230 * browsers other than Internet Explorer.
    1231 *
    1232 * @param {Element} el The element to set background on.
    1233 * @param {string} src The image source URL.
    1234 */
    1235goog.style.setTransparentBackgroundImage = function(el, src) {
    1236 var style = el.style;
    1237 // It is safe to use the style.filter in IE only. In Safari 'filter' is in
    1238 // style object but access to style.filter causes it to throw an exception.
    1239 // Note: IE8 supports images with an alpha channel.
    1240 if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
    1241 // See TODO in setOpacity.
    1242 style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(' +
    1243 'src="' + src + '", sizingMethod="crop")';
    1244 } else {
    1245 // Set style properties individually instead of using background shorthand
    1246 // to prevent overwriting a pre-existing background color.
    1247 style.backgroundImage = 'url(' + src + ')';
    1248 style.backgroundPosition = 'top left';
    1249 style.backgroundRepeat = 'no-repeat';
    1250 }
    1251};
    1252
    1253
    1254/**
    1255 * Clears the background image of an element in a browser independent manner.
    1256 * @param {Element} el The element to clear background image for.
    1257 */
    1258goog.style.clearTransparentBackgroundImage = function(el) {
    1259 var style = el.style;
    1260 if ('filter' in style) {
    1261 // See TODO in setOpacity.
    1262 style.filter = '';
    1263 } else {
    1264 // Set style properties individually instead of using background shorthand
    1265 // to prevent overwriting a pre-existing background color.
    1266 style.backgroundImage = 'none';
    1267 }
    1268};
    1269
    1270
    1271/**
    1272 * Shows or hides an element from the page. Hiding the element is done by
    1273 * setting the display property to "none", removing the element from the
    1274 * rendering hierarchy so it takes up no space. To show the element, the default
    1275 * inherited display property is restored (defined either in stylesheets or by
    1276 * the browser's default style rules.)
    1277 *
    1278 * Caveat 1: if the inherited display property for the element is set to "none"
    1279 * by the stylesheets, that is the property that will be restored by a call to
    1280 * showElement(), effectively toggling the display between "none" and "none".
    1281 *
    1282 * Caveat 2: if the element display style is set inline (by setting either
    1283 * element.style.display or a style attribute in the HTML), a call to
    1284 * showElement will clear that setting and defer to the inherited style in the
    1285 * stylesheet.
    1286 * @param {Element} el Element to show or hide.
    1287 * @param {*} display True to render the element in its default style,
    1288 * false to disable rendering the element.
    1289 * @deprecated Use goog.style.setElementShown instead.
    1290 */
    1291goog.style.showElement = function(el, display) {
    1292 goog.style.setElementShown(el, display);
    1293};
    1294
    1295
    1296/**
    1297 * Shows or hides an element from the page. Hiding the element is done by
    1298 * setting the display property to "none", removing the element from the
    1299 * rendering hierarchy so it takes up no space. To show the element, the default
    1300 * inherited display property is restored (defined either in stylesheets or by
    1301 * the browser's default style rules).
    1302 *
    1303 * Caveat 1: if the inherited display property for the element is set to "none"
    1304 * by the stylesheets, that is the property that will be restored by a call to
    1305 * setElementShown(), effectively toggling the display between "none" and
    1306 * "none".
    1307 *
    1308 * Caveat 2: if the element display style is set inline (by setting either
    1309 * element.style.display or a style attribute in the HTML), a call to
    1310 * setElementShown will clear that setting and defer to the inherited style in
    1311 * the stylesheet.
    1312 * @param {Element} el Element to show or hide.
    1313 * @param {*} isShown True to render the element in its default style,
    1314 * false to disable rendering the element.
    1315 */
    1316goog.style.setElementShown = function(el, isShown) {
    1317 el.style.display = isShown ? '' : 'none';
    1318};
    1319
    1320
    1321/**
    1322 * Test whether the given element has been shown or hidden via a call to
    1323 * {@link #setElementShown}.
    1324 *
    1325 * Note this is strictly a companion method for a call
    1326 * to {@link #setElementShown} and the same caveats apply; in particular, this
    1327 * method does not guarantee that the return value will be consistent with
    1328 * whether or not the element is actually visible.
    1329 *
    1330 * @param {Element} el The element to test.
    1331 * @return {boolean} Whether the element has been shown.
    1332 * @see #setElementShown
    1333 */
    1334goog.style.isElementShown = function(el) {
    1335 return el.style.display != 'none';
    1336};
    1337
    1338
    1339/**
    1340 * Installs the styles string into the window that contains opt_element. If
    1341 * opt_element is null, the main window is used.
    1342 * @param {string} stylesString The style string to install.
    1343 * @param {Node=} opt_node Node whose parent document should have the
    1344 * styles installed.
    1345 * @return {Element|StyleSheet} The style element created.
    1346 */
    1347goog.style.installStyles = function(stylesString, opt_node) {
    1348 var dh = goog.dom.getDomHelper(opt_node);
    1349 var styleSheet = null;
    1350
    1351 // IE < 11 requires createStyleSheet. Note that doc.createStyleSheet will be
    1352 // undefined as of IE 11.
    1353 var doc = dh.getDocument();
    1354 if (goog.userAgent.IE && doc.createStyleSheet) {
    1355 styleSheet = doc.createStyleSheet();
    1356 goog.style.setStyles(styleSheet, stylesString);
    1357 } else {
    1358 var head = dh.getElementsByTagNameAndClass('head')[0];
    1359
    1360 // In opera documents are not guaranteed to have a head element, thus we
    1361 // have to make sure one exists before using it.
    1362 if (!head) {
    1363 var body = dh.getElementsByTagNameAndClass('body')[0];
    1364 head = dh.createDom('head');
    1365 body.parentNode.insertBefore(head, body);
    1366 }
    1367 styleSheet = dh.createDom('style');
    1368 // NOTE(user): Setting styles after the style element has been appended
    1369 // to the head results in a nasty Webkit bug in certain scenarios. Please
    1370 // refer to https://bugs.webkit.org/show_bug.cgi?id=26307 for additional
    1371 // details.
    1372 goog.style.setStyles(styleSheet, stylesString);
    1373 dh.appendChild(head, styleSheet);
    1374 }
    1375 return styleSheet;
    1376};
    1377
    1378
    1379/**
    1380 * Removes the styles added by {@link #installStyles}.
    1381 * @param {Element|StyleSheet} styleSheet The value returned by
    1382 * {@link #installStyles}.
    1383 */
    1384goog.style.uninstallStyles = function(styleSheet) {
    1385 var node = styleSheet.ownerNode || styleSheet.owningElement ||
    1386 /** @type {Element} */ (styleSheet);
    1387 goog.dom.removeNode(node);
    1388};
    1389
    1390
    1391/**
    1392 * Sets the content of a style element. The style element can be any valid
    1393 * style element. This element will have its content completely replaced by
    1394 * the new stylesString.
    1395 * @param {Element|StyleSheet} element A stylesheet element as returned by
    1396 * installStyles.
    1397 * @param {string} stylesString The new content of the stylesheet.
    1398 */
    1399goog.style.setStyles = function(element, stylesString) {
    1400 if (goog.userAgent.IE && goog.isDef(element.cssText)) {
    1401 // Adding the selectors individually caused the browser to hang if the
    1402 // selector was invalid or there were CSS comments. Setting the cssText of
    1403 // the style node works fine and ignores CSS that IE doesn't understand.
    1404 // However IE >= 11 doesn't support cssText any more, so we make sure that
    1405 // cssText is a defined property and otherwise fall back to innerHTML.
    1406 element.cssText = stylesString;
    1407 } else {
    1408 element.innerHTML = stylesString;
    1409 }
    1410};
    1411
    1412
    1413/**
    1414 * Sets 'white-space: pre-wrap' for a node (x-browser).
    1415 *
    1416 * There are as many ways of specifying pre-wrap as there are browsers.
    1417 *
    1418 * CSS3/IE8: white-space: pre-wrap;
    1419 * Mozilla: white-space: -moz-pre-wrap;
    1420 * Opera: white-space: -o-pre-wrap;
    1421 * IE6/7: white-space: pre; word-wrap: break-word;
    1422 *
    1423 * @param {Element} el Element to enable pre-wrap for.
    1424 */
    1425goog.style.setPreWrap = function(el) {
    1426 var style = el.style;
    1427 if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
    1428 style.whiteSpace = 'pre';
    1429 style.wordWrap = 'break-word';
    1430 } else if (goog.userAgent.GECKO) {
    1431 style.whiteSpace = '-moz-pre-wrap';
    1432 } else {
    1433 style.whiteSpace = 'pre-wrap';
    1434 }
    1435};
    1436
    1437
    1438/**
    1439 * Sets 'display: inline-block' for an element (cross-browser).
    1440 * @param {Element} el Element to which the inline-block display style is to be
    1441 * applied.
    1442 * @see ../demos/inline_block_quirks.html
    1443 * @see ../demos/inline_block_standards.html
    1444 */
    1445goog.style.setInlineBlock = function(el) {
    1446 var style = el.style;
    1447 // Without position:relative, weirdness ensues. Just accept it and move on.
    1448 style.position = 'relative';
    1449
    1450 if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
    1451 // IE8 supports inline-block so fall through to the else
    1452 // Zoom:1 forces hasLayout, display:inline gives inline behavior.
    1453 style.zoom = '1';
    1454 style.display = 'inline';
    1455 } else if (goog.userAgent.GECKO) {
    1456 // Pre-Firefox 3, Gecko doesn't support inline-block, but -moz-inline-box
    1457 // is close enough.
    1458 style.display = goog.userAgent.isVersionOrHigher('1.9a') ? 'inline-block' :
    1459 '-moz-inline-box';
    1460 } else {
    1461 // Opera, Webkit, and Safari seem to do OK with the standard inline-block
    1462 // style.
    1463 style.display = 'inline-block';
    1464 }
    1465};
    1466
    1467
    1468/**
    1469 * Returns true if the element is using right to left (rtl) direction.
    1470 * @param {Element} el The element to test.
    1471 * @return {boolean} True for right to left, false for left to right.
    1472 */
    1473goog.style.isRightToLeft = function(el) {
    1474 return 'rtl' == goog.style.getStyle_(el, 'direction');
    1475};
    1476
    1477
    1478/**
    1479 * The CSS style property corresponding to an element being
    1480 * unselectable on the current browser platform (null if none).
    1481 * Opera and IE instead use a DOM attribute 'unselectable'.
    1482 * @type {?string}
    1483 * @private
    1484 */
    1485goog.style.unselectableStyle_ =
    1486 goog.userAgent.GECKO ? 'MozUserSelect' :
    1487 goog.userAgent.WEBKIT ? 'WebkitUserSelect' :
    1488 null;
    1489
    1490
    1491/**
    1492 * Returns true if the element is set to be unselectable, false otherwise.
    1493 * Note that on some platforms (e.g. Mozilla), even if an element isn't set
    1494 * to be unselectable, it will behave as such if any of its ancestors is
    1495 * unselectable.
    1496 * @param {Element} el Element to check.
    1497 * @return {boolean} Whether the element is set to be unselectable.
    1498 */
    1499goog.style.isUnselectable = function(el) {
    1500 if (goog.style.unselectableStyle_) {
    1501 return el.style[goog.style.unselectableStyle_].toLowerCase() == 'none';
    1502 } else if (goog.userAgent.IE || goog.userAgent.OPERA) {
    1503 return el.getAttribute('unselectable') == 'on';
    1504 }
    1505 return false;
    1506};
    1507
    1508
    1509/**
    1510 * Makes the element and its descendants selectable or unselectable. Note
    1511 * that on some platforms (e.g. Mozilla), even if an element isn't set to
    1512 * be unselectable, it will behave as such if any of its ancestors is
    1513 * unselectable.
    1514 * @param {Element} el The element to alter.
    1515 * @param {boolean} unselectable Whether the element and its descendants
    1516 * should be made unselectable.
    1517 * @param {boolean=} opt_noRecurse Whether to only alter the element's own
    1518 * selectable state, and leave its descendants alone; defaults to false.
    1519 */
    1520goog.style.setUnselectable = function(el, unselectable, opt_noRecurse) {
    1521 // TODO(attila): Do we need all of TR_DomUtil.makeUnselectable() in Closure?
    1522 var descendants = !opt_noRecurse ? el.getElementsByTagName('*') : null;
    1523 var name = goog.style.unselectableStyle_;
    1524 if (name) {
    1525 // Add/remove the appropriate CSS style to/from the element and its
    1526 // descendants.
    1527 var value = unselectable ? 'none' : '';
    1528 el.style[name] = value;
    1529 if (descendants) {
    1530 for (var i = 0, descendant; descendant = descendants[i]; i++) {
    1531 descendant.style[name] = value;
    1532 }
    1533 }
    1534 } else if (goog.userAgent.IE || goog.userAgent.OPERA) {
    1535 // Toggle the 'unselectable' attribute on the element and its descendants.
    1536 var value = unselectable ? 'on' : '';
    1537 el.setAttribute('unselectable', value);
    1538 if (descendants) {
    1539 for (var i = 0, descendant; descendant = descendants[i]; i++) {
    1540 descendant.setAttribute('unselectable', value);
    1541 }
    1542 }
    1543 }
    1544};
    1545
    1546
    1547/**
    1548 * Gets the border box size for an element.
    1549 * @param {Element} element The element to get the size for.
    1550 * @return {!goog.math.Size} The border box size.
    1551 */
    1552goog.style.getBorderBoxSize = function(element) {
    1553 return new goog.math.Size(element.offsetWidth, element.offsetHeight);
    1554};
    1555
    1556
    1557/**
    1558 * Sets the border box size of an element. This is potentially expensive in IE
    1559 * if the document is CSS1Compat mode
    1560 * @param {Element} element The element to set the size on.
    1561 * @param {goog.math.Size} size The new size.
    1562 */
    1563goog.style.setBorderBoxSize = function(element, size) {
    1564 var doc = goog.dom.getOwnerDocument(element);
    1565 var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode();
    1566
    1567 if (goog.userAgent.IE &&
    1568 !goog.userAgent.isVersionOrHigher('10') &&
    1569 (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) {
    1570 var style = element.style;
    1571 if (isCss1CompatMode) {
    1572 var paddingBox = goog.style.getPaddingBox(element);
    1573 var borderBox = goog.style.getBorderBox(element);
    1574 style.pixelWidth = size.width - borderBox.left - paddingBox.left -
    1575 paddingBox.right - borderBox.right;
    1576 style.pixelHeight = size.height - borderBox.top - paddingBox.top -
    1577 paddingBox.bottom - borderBox.bottom;
    1578 } else {
    1579 style.pixelWidth = size.width;
    1580 style.pixelHeight = size.height;
    1581 }
    1582 } else {
    1583 goog.style.setBoxSizingSize_(element, size, 'border-box');
    1584 }
    1585};
    1586
    1587
    1588/**
    1589 * Gets the content box size for an element. This is potentially expensive in
    1590 * all browsers.
    1591 * @param {Element} element The element to get the size for.
    1592 * @return {!goog.math.Size} The content box size.
    1593 */
    1594goog.style.getContentBoxSize = function(element) {
    1595 var doc = goog.dom.getOwnerDocument(element);
    1596 var ieCurrentStyle = goog.userAgent.IE && element.currentStyle;
    1597 if (ieCurrentStyle &&
    1598 goog.dom.getDomHelper(doc).isCss1CompatMode() &&
    1599 ieCurrentStyle.width != 'auto' && ieCurrentStyle.height != 'auto' &&
    1600 !ieCurrentStyle.boxSizing) {
    1601 // If IE in CSS1Compat mode than just use the width and height.
    1602 // If we have a boxSizing then fall back on measuring the borders etc.
    1603 var width = goog.style.getIePixelValue_(element, ieCurrentStyle.width,
    1604 'width', 'pixelWidth');
    1605 var height = goog.style.getIePixelValue_(element, ieCurrentStyle.height,
    1606 'height', 'pixelHeight');
    1607 return new goog.math.Size(width, height);
    1608 } else {
    1609 var borderBoxSize = goog.style.getBorderBoxSize(element);
    1610 var paddingBox = goog.style.getPaddingBox(element);
    1611 var borderBox = goog.style.getBorderBox(element);
    1612 return new goog.math.Size(borderBoxSize.width -
    1613 borderBox.left - paddingBox.left -
    1614 paddingBox.right - borderBox.right,
    1615 borderBoxSize.height -
    1616 borderBox.top - paddingBox.top -
    1617 paddingBox.bottom - borderBox.bottom);
    1618 }
    1619};
    1620
    1621
    1622/**
    1623 * Sets the content box size of an element. This is potentially expensive in IE
    1624 * if the document is BackCompat mode.
    1625 * @param {Element} element The element to set the size on.
    1626 * @param {goog.math.Size} size The new size.
    1627 */
    1628goog.style.setContentBoxSize = function(element, size) {
    1629 var doc = goog.dom.getOwnerDocument(element);
    1630 var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode();
    1631 if (goog.userAgent.IE &&
    1632 !goog.userAgent.isVersionOrHigher('10') &&
    1633 (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) {
    1634 var style = element.style;
    1635 if (isCss1CompatMode) {
    1636 style.pixelWidth = size.width;
    1637 style.pixelHeight = size.height;
    1638 } else {
    1639 var paddingBox = goog.style.getPaddingBox(element);
    1640 var borderBox = goog.style.getBorderBox(element);
    1641 style.pixelWidth = size.width + borderBox.left + paddingBox.left +
    1642 paddingBox.right + borderBox.right;
    1643 style.pixelHeight = size.height + borderBox.top + paddingBox.top +
    1644 paddingBox.bottom + borderBox.bottom;
    1645 }
    1646 } else {
    1647 goog.style.setBoxSizingSize_(element, size, 'content-box');
    1648 }
    1649};
    1650
    1651
    1652/**
    1653 * Helper function that sets the box sizing as well as the width and height
    1654 * @param {Element} element The element to set the size on.
    1655 * @param {goog.math.Size} size The new size to set.
    1656 * @param {string} boxSizing The box-sizing value.
    1657 * @private
    1658 */
    1659goog.style.setBoxSizingSize_ = function(element, size, boxSizing) {
    1660 var style = element.style;
    1661 if (goog.userAgent.GECKO) {
    1662 style.MozBoxSizing = boxSizing;
    1663 } else if (goog.userAgent.WEBKIT) {
    1664 style.WebkitBoxSizing = boxSizing;
    1665 } else {
    1666 // Includes IE8 and Opera 9.50+
    1667 style.boxSizing = boxSizing;
    1668 }
    1669
    1670 // Setting this to a negative value will throw an exception on IE
    1671 // (and doesn't do anything different than setting it to 0).
    1672 style.width = Math.max(size.width, 0) + 'px';
    1673 style.height = Math.max(size.height, 0) + 'px';
    1674};
    1675
    1676
    1677/**
    1678 * IE specific function that converts a non pixel unit to pixels.
    1679 * @param {Element} element The element to convert the value for.
    1680 * @param {string} value The current value as a string. The value must not be
    1681 * ''.
    1682 * @param {string} name The CSS property name to use for the converstion. This
    1683 * should be 'left', 'top', 'width' or 'height'.
    1684 * @param {string} pixelName The CSS pixel property name to use to get the
    1685 * value in pixels.
    1686 * @return {number} The value in pixels.
    1687 * @private
    1688 */
    1689goog.style.getIePixelValue_ = function(element, value, name, pixelName) {
    1690 // Try if we already have a pixel value. IE does not do half pixels so we
    1691 // only check if it matches a number followed by 'px'.
    1692 if (/^\d+px?$/.test(value)) {
    1693 return parseInt(value, 10);
    1694 } else {
    1695 var oldStyleValue = element.style[name];
    1696 var oldRuntimeValue = element.runtimeStyle[name];
    1697 // set runtime style to prevent changes
    1698 element.runtimeStyle[name] = element.currentStyle[name];
    1699 element.style[name] = value;
    1700 var pixelValue = element.style[pixelName];
    1701 // restore
    1702 element.style[name] = oldStyleValue;
    1703 element.runtimeStyle[name] = oldRuntimeValue;
    1704 return pixelValue;
    1705 }
    1706};
    1707
    1708
    1709/**
    1710 * Helper function for getting the pixel padding or margin for IE.
    1711 * @param {Element} element The element to get the padding for.
    1712 * @param {string} propName The property name.
    1713 * @return {number} The pixel padding.
    1714 * @private
    1715 */
    1716goog.style.getIePixelDistance_ = function(element, propName) {
    1717 var value = goog.style.getCascadedStyle(element, propName);
    1718 return value ?
    1719 goog.style.getIePixelValue_(element, value, 'left', 'pixelLeft') : 0;
    1720};
    1721
    1722
    1723/**
    1724 * Gets the computed paddings or margins (on all sides) in pixels.
    1725 * @param {Element} element The element to get the padding for.
    1726 * @param {string} stylePrefix Pass 'padding' to retrieve the padding box,
    1727 * or 'margin' to retrieve the margin box.
    1728 * @return {!goog.math.Box} The computed paddings or margins.
    1729 * @private
    1730 */
    1731goog.style.getBox_ = function(element, stylePrefix) {
    1732 if (goog.userAgent.IE) {
    1733 var left = goog.style.getIePixelDistance_(element, stylePrefix + 'Left');
    1734 var right = goog.style.getIePixelDistance_(element, stylePrefix + 'Right');
    1735 var top = goog.style.getIePixelDistance_(element, stylePrefix + 'Top');
    1736 var bottom = goog.style.getIePixelDistance_(
    1737 element, stylePrefix + 'Bottom');
    1738 return new goog.math.Box(top, right, bottom, left);
    1739 } else {
    1740 // On non-IE browsers, getComputedStyle is always non-null.
    1741 var left = /** @type {string} */ (
    1742 goog.style.getComputedStyle(element, stylePrefix + 'Left'));
    1743 var right = /** @type {string} */ (
    1744 goog.style.getComputedStyle(element, stylePrefix + 'Right'));
    1745 var top = /** @type {string} */ (
    1746 goog.style.getComputedStyle(element, stylePrefix + 'Top'));
    1747 var bottom = /** @type {string} */ (
    1748 goog.style.getComputedStyle(element, stylePrefix + 'Bottom'));
    1749
    1750 // NOTE(arv): Gecko can return floating point numbers for the computed
    1751 // style values.
    1752 return new goog.math.Box(parseFloat(top),
    1753 parseFloat(right),
    1754 parseFloat(bottom),
    1755 parseFloat(left));
    1756 }
    1757};
    1758
    1759
    1760/**
    1761 * Gets the computed paddings (on all sides) in pixels.
    1762 * @param {Element} element The element to get the padding for.
    1763 * @return {!goog.math.Box} The computed paddings.
    1764 */
    1765goog.style.getPaddingBox = function(element) {
    1766 return goog.style.getBox_(element, 'padding');
    1767};
    1768
    1769
    1770/**
    1771 * Gets the computed margins (on all sides) in pixels.
    1772 * @param {Element} element The element to get the margins for.
    1773 * @return {!goog.math.Box} The computed margins.
    1774 */
    1775goog.style.getMarginBox = function(element) {
    1776 return goog.style.getBox_(element, 'margin');
    1777};
    1778
    1779
    1780/**
    1781 * A map used to map the border width keywords to a pixel width.
    1782 * @type {Object}
    1783 * @private
    1784 */
    1785goog.style.ieBorderWidthKeywords_ = {
    1786 'thin': 2,
    1787 'medium': 4,
    1788 'thick': 6
    1789};
    1790
    1791
    1792/**
    1793 * Helper function for IE to get the pixel border.
    1794 * @param {Element} element The element to get the pixel border for.
    1795 * @param {string} prop The part of the property name.
    1796 * @return {number} The value in pixels.
    1797 * @private
    1798 */
    1799goog.style.getIePixelBorder_ = function(element, prop) {
    1800 if (goog.style.getCascadedStyle(element, prop + 'Style') == 'none') {
    1801 return 0;
    1802 }
    1803 var width = goog.style.getCascadedStyle(element, prop + 'Width');
    1804 if (width in goog.style.ieBorderWidthKeywords_) {
    1805 return goog.style.ieBorderWidthKeywords_[width];
    1806 }
    1807 return goog.style.getIePixelValue_(element, width, 'left', 'pixelLeft');
    1808};
    1809
    1810
    1811/**
    1812 * Gets the computed border widths (on all sides) in pixels
    1813 * @param {Element} element The element to get the border widths for.
    1814 * @return {!goog.math.Box} The computed border widths.
    1815 */
    1816goog.style.getBorderBox = function(element) {
    1817 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
    1818 var left = goog.style.getIePixelBorder_(element, 'borderLeft');
    1819 var right = goog.style.getIePixelBorder_(element, 'borderRight');
    1820 var top = goog.style.getIePixelBorder_(element, 'borderTop');
    1821 var bottom = goog.style.getIePixelBorder_(element, 'borderBottom');
    1822 return new goog.math.Box(top, right, bottom, left);
    1823 } else {
    1824 // On non-IE browsers, getComputedStyle is always non-null.
    1825 var left = /** @type {string} */ (
    1826 goog.style.getComputedStyle(element, 'borderLeftWidth'));
    1827 var right = /** @type {string} */ (
    1828 goog.style.getComputedStyle(element, 'borderRightWidth'));
    1829 var top = /** @type {string} */ (
    1830 goog.style.getComputedStyle(element, 'borderTopWidth'));
    1831 var bottom = /** @type {string} */ (
    1832 goog.style.getComputedStyle(element, 'borderBottomWidth'));
    1833
    1834 return new goog.math.Box(parseFloat(top),
    1835 parseFloat(right),
    1836 parseFloat(bottom),
    1837 parseFloat(left));
    1838 }
    1839};
    1840
    1841
    1842/**
    1843 * Returns the font face applied to a given node. Opera and IE should return
    1844 * the font actually displayed. Firefox returns the author's most-preferred
    1845 * font (whether the browser is capable of displaying it or not.)
    1846 * @param {Element} el The element whose font family is returned.
    1847 * @return {string} The font family applied to el.
    1848 */
    1849goog.style.getFontFamily = function(el) {
    1850 var doc = goog.dom.getOwnerDocument(el);
    1851 var font = '';
    1852 // The moveToElementText method from the TextRange only works if the element
    1853 // is attached to the owner document.
    1854 if (doc.body.createTextRange && goog.dom.contains(doc, el)) {
    1855 var range = doc.body.createTextRange();
    1856 range.moveToElementText(el);
    1857 /** @preserveTry */
    1858 try {
    1859 font = range.queryCommandValue('FontName');
    1860 } catch (e) {
    1861 // This is a workaround for a awkward exception.
    1862 // On some IE, there is an exception coming from it.
    1863 // The error description from this exception is:
    1864 // This window has already been registered as a drop target
    1865 // This is bogus description, likely due to a bug in ie.
    1866 font = '';
    1867 }
    1868 }
    1869 if (!font) {
    1870 // Note if for some reason IE can't derive FontName with a TextRange, we
    1871 // fallback to using currentStyle
    1872 font = goog.style.getStyle_(el, 'fontFamily');
    1873 }
    1874
    1875 // Firefox returns the applied font-family string (author's list of
    1876 // preferred fonts.) We want to return the most-preferred font, in lieu of
    1877 // the *actually* applied font.
    1878 var fontsArray = font.split(',');
    1879 if (fontsArray.length > 1) font = fontsArray[0];
    1880
    1881 // Sanitize for x-browser consistency:
    1882 // Strip quotes because browsers aren't consistent with how they're
    1883 // applied; Opera always encloses, Firefox sometimes, and IE never.
    1884 return goog.string.stripQuotes(font, '"\'');
    1885};
    1886
    1887
    1888/**
    1889 * Regular expression used for getLengthUnits.
    1890 * @type {RegExp}
    1891 * @private
    1892 */
    1893goog.style.lengthUnitRegex_ = /[^\d]+$/;
    1894
    1895
    1896/**
    1897 * Returns the units used for a CSS length measurement.
    1898 * @param {string} value A CSS length quantity.
    1899 * @return {?string} The units of measurement.
    1900 */
    1901goog.style.getLengthUnits = function(value) {
    1902 var units = value.match(goog.style.lengthUnitRegex_);
    1903 return units && units[0] || null;
    1904};
    1905
    1906
    1907/**
    1908 * Map of absolute CSS length units
    1909 * @type {Object}
    1910 * @private
    1911 */
    1912goog.style.ABSOLUTE_CSS_LENGTH_UNITS_ = {
    1913 'cm' : 1,
    1914 'in' : 1,
    1915 'mm' : 1,
    1916 'pc' : 1,
    1917 'pt' : 1
    1918};
    1919
    1920
    1921/**
    1922 * Map of relative CSS length units that can be accurately converted to px
    1923 * font-size values using getIePixelValue_. Only units that are defined in
    1924 * relation to a font size are convertible (%, small, etc. are not).
    1925 * @type {Object}
    1926 * @private
    1927 */
    1928goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_ = {
    1929 'em' : 1,
    1930 'ex' : 1
    1931};
    1932
    1933
    1934/**
    1935 * Returns the font size, in pixels, of text in an element.
    1936 * @param {Element} el The element whose font size is returned.
    1937 * @return {number} The font size (in pixels).
    1938 */
    1939goog.style.getFontSize = function(el) {
    1940 var fontSize = goog.style.getStyle_(el, 'fontSize');
    1941 var sizeUnits = goog.style.getLengthUnits(fontSize);
    1942 if (fontSize && 'px' == sizeUnits) {
    1943 // NOTE(user): This could be parseFloat instead, but IE doesn't return
    1944 // decimal fractions in getStyle_ and Firefox reports the fractions, but
    1945 // ignores them when rendering. Interestingly enough, when we force the
    1946 // issue and size something to e.g., 50% of 25px, the browsers round in
    1947 // opposite directions with Firefox reporting 12px and IE 13px. I punt.
    1948 return parseInt(fontSize, 10);
    1949 }
    1950
    1951 // In IE, we can convert absolute length units to a px value using
    1952 // goog.style.getIePixelValue_. Units defined in relation to a font size
    1953 // (em, ex) are applied relative to the element's parentNode and can also
    1954 // be converted.
    1955 if (goog.userAgent.IE) {
    1956 if (sizeUnits in goog.style.ABSOLUTE_CSS_LENGTH_UNITS_) {
    1957 return goog.style.getIePixelValue_(el,
    1958 fontSize,
    1959 'left',
    1960 'pixelLeft');
    1961 } else if (el.parentNode &&
    1962 el.parentNode.nodeType == goog.dom.NodeType.ELEMENT &&
    1963 sizeUnits in goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_) {
    1964 // Check the parent size - if it is the same it means the relative size
    1965 // value is inherited and we therefore don't want to count it twice. If
    1966 // it is different, this element either has explicit style or has a CSS
    1967 // rule applying to it.
    1968 var parentElement = /** @type {Element} */ (el.parentNode);
    1969 var parentSize = goog.style.getStyle_(parentElement, 'fontSize');
    1970 return goog.style.getIePixelValue_(parentElement,
    1971 fontSize == parentSize ?
    1972 '1em' : fontSize,
    1973 'left',
    1974 'pixelLeft');
    1975 }
    1976 }
    1977
    1978 // Sometimes we can't cleanly find the font size (some units relative to a
    1979 // node's parent's font size are difficult: %, smaller et al), so we create
    1980 // an invisible, absolutely-positioned span sized to be the height of an 'M'
    1981 // rendered in its parent's (i.e., our target element's) font size. This is
    1982 // the definition of CSS's font size attribute.
    1983 var sizeElement = goog.dom.createDom(
    1984 'span',
    1985 {'style': 'visibility:hidden;position:absolute;' +
    1986 'line-height:0;padding:0;margin:0;border:0;height:1em;'});
    1987 goog.dom.appendChild(el, sizeElement);
    1988 fontSize = sizeElement.offsetHeight;
    1989 goog.dom.removeNode(sizeElement);
    1990
    1991 return fontSize;
    1992};
    1993
    1994
    1995/**
    1996 * Parses a style attribute value. Converts CSS property names to camel case.
    1997 * @param {string} value The style attribute value.
    1998 * @return {!Object} Map of CSS properties to string values.
    1999 */
    2000goog.style.parseStyleAttribute = function(value) {
    2001 var result = {};
    2002 goog.array.forEach(value.split(/\s*;\s*/), function(pair) {
    2003 var keyValue = pair.split(/\s*:\s*/);
    2004 if (keyValue.length == 2) {
    2005 result[goog.string.toCamelCase(keyValue[0].toLowerCase())] = keyValue[1];
    2006 }
    2007 });
    2008 return result;
    2009};
    2010
    2011
    2012/**
    2013 * Reverse of parseStyleAttribute; that is, takes a style object and returns the
    2014 * corresponding attribute value. Converts camel case property names to proper
    2015 * CSS selector names.
    2016 * @param {Object} obj Map of CSS properties to values.
    2017 * @return {string} The style attribute value.
    2018 */
    2019goog.style.toStyleAttribute = function(obj) {
    2020 var buffer = [];
    2021 goog.object.forEach(obj, function(value, key) {
    2022 buffer.push(goog.string.toSelectorCase(key), ':', value, ';');
    2023 });
    2024 return buffer.join('');
    2025};
    2026
    2027
    2028/**
    2029 * Sets CSS float property on an element.
    2030 * @param {Element} el The element to set float property on.
    2031 * @param {string} value The value of float CSS property to set on this element.
    2032 */
    2033goog.style.setFloat = function(el, value) {
    2034 el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] = value;
    2035};
    2036
    2037
    2038/**
    2039 * Gets value of explicitly-set float CSS property on an element.
    2040 * @param {Element} el The element to get float property of.
    2041 * @return {string} The value of explicitly-set float CSS property on this
    2042 * element.
    2043 */
    2044goog.style.getFloat = function(el) {
    2045 return el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] || '';
    2046};
    2047
    2048
    2049/**
    2050 * Returns the scroll bar width (represents the width of both horizontal
    2051 * and vertical scroll).
    2052 *
    2053 * @param {string=} opt_className An optional class name (or names) to apply
    2054 * to the invisible div created to measure the scrollbar. This is necessary
    2055 * if some scrollbars are styled differently than others.
    2056 * @return {number} The scroll bar width in px.
    2057 */
    2058goog.style.getScrollbarWidth = function(opt_className) {
    2059 // Add two hidden divs. The child div is larger than the parent and
    2060 // forces scrollbars to appear on it.
    2061 // Using overflow:scroll does not work consistently with scrollbars that
    2062 // are styled with ::-webkit-scrollbar.
    2063 var outerDiv = goog.dom.createElement('div');
    2064 if (opt_className) {
    2065 outerDiv.className = opt_className;
    2066 }
    2067 outerDiv.style.cssText = 'overflow:auto;' +
    2068 'position:absolute;top:0;width:100px;height:100px';
    2069 var innerDiv = goog.dom.createElement('div');
    2070 goog.style.setSize(innerDiv, '200px', '200px');
    2071 outerDiv.appendChild(innerDiv);
    2072 goog.dom.appendChild(goog.dom.getDocument().body, outerDiv);
    2073 var width = outerDiv.offsetWidth - outerDiv.clientWidth;
    2074 goog.dom.removeNode(outerDiv);
    2075 return width;
    2076};
    2077
    2078
    2079/**
    2080 * Regular expression to extract x and y translation components from a CSS
    2081 * transform Matrix representation.
    2082 *
    2083 * @type {!RegExp}
    2084 * @const
    2085 * @private
    2086 */
    2087goog.style.MATRIX_TRANSLATION_REGEX_ =
    2088 new RegExp('matrix\\([0-9\\.\\-]+, [0-9\\.\\-]+, ' +
    2089 '[0-9\\.\\-]+, [0-9\\.\\-]+, ' +
    2090 '([0-9\\.\\-]+)p?x?, ([0-9\\.\\-]+)p?x?\\)');
    2091
    2092
    2093/**
    2094 * Returns the x,y translation component of any CSS transforms applied to the
    2095 * element, in pixels.
    2096 *
    2097 * @param {!Element} element The element to get the translation of.
    2098 * @return {!goog.math.Coordinate} The CSS translation of the element in px.
    2099 */
    2100goog.style.getCssTranslation = function(element) {
    2101 var transform = goog.style.getComputedTransform(element);
    2102 if (!transform) {
    2103 return new goog.math.Coordinate(0, 0);
    2104 }
    2105 var matches = transform.match(goog.style.MATRIX_TRANSLATION_REGEX_);
    2106 if (!matches) {
    2107 return new goog.math.Coordinate(0, 0);
    2108 }
    2109 return new goog.math.Coordinate(parseFloat(matches[1]),
    2110 parseFloat(matches[2]));
    2111};
    \ No newline at end of file +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
    42/*
    43With the next two lines Selenium's //javascript/firefox-driver:command-processor
    44fails to compile with "ERROR - Malformed goog.forwardDeclaration".
    45TODO: Make it compile and restore next two lines to so we don't have to maintain the diff.
    46goog.forwardDeclare('goog.events.BrowserEvent');
    47goog.forwardDeclare('goog.events.Event');
    48*/
    49
    50/**
    51 * Sets a style value on an element.
    52 *
    53 * This function is not indended to patch issues in the browser's style
    54 * handling, but to allow easy programmatic access to setting dash-separated
    55 * style properties. An example is setting a batch of properties from a data
    56 * object without overwriting old styles. When possible, use native APIs:
    57 * elem.style.propertyKey = 'value' or (if obliterating old styles is fine)
    58 * elem.style.cssText = 'property1: value1; property2: value2'.
    59 *
    60 * @param {Element} element The element to change.
    61 * @param {string|Object} style If a string, a style name. If an object, a hash
    62 * of style names to style values.
    63 * @param {string|number|boolean=} opt_value If style was a string, then this
    64 * should be the value.
    65 */
    66goog.style.setStyle = function(element, style, opt_value) {
    67 if (goog.isString(style)) {
    68 goog.style.setStyle_(element, opt_value, style);
    69 } else {
    70 for (var key in style) {
    71 goog.style.setStyle_(element, style[key], key);
    72 }
    73 }
    74};
    75
    76
    77/**
    78 * Sets a style value on an element, with parameters swapped to work with
    79 * {@code goog.object.forEach()}. Prepends a vendor-specific prefix when
    80 * necessary.
    81 * @param {Element} element The element to change.
    82 * @param {string|number|boolean|undefined} value Style value.
    83 * @param {string} style Style name.
    84 * @private
    85 */
    86goog.style.setStyle_ = function(element, value, style) {
    87 var propertyName = goog.style.getVendorJsStyleName_(element, style);
    88
    89 if (propertyName) {
    90 element.style[propertyName] = value;
    91 }
    92};
    93
    94
    95/**
    96 * Style name cache that stores previous property name lookups.
    97 *
    98 * This is used by setStyle to speed up property lookups, entries look like:
    99 * { StyleName: ActualPropertyName }
    100 *
    101 * @private {!Object<string, string>}
    102 */
    103goog.style.styleNameCache_ = {};
    104
    105
    106/**
    107 * Returns the style property name in camel-case. If it does not exist and a
    108 * vendor-specific version of the property does exist, then return the vendor-
    109 * specific property name instead.
    110 * @param {Element} element The element to change.
    111 * @param {string} style Style name.
    112 * @return {string} Vendor-specific style.
    113 * @private
    114 */
    115goog.style.getVendorJsStyleName_ = function(element, style) {
    116 var propertyName = goog.style.styleNameCache_[style];
    117 if (!propertyName) {
    118 var camelStyle = goog.string.toCamelCase(style);
    119 propertyName = camelStyle;
    120
    121 if (element.style[camelStyle] === undefined) {
    122 var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +
    123 goog.string.toTitleCase(camelStyle);
    124
    125 if (element.style[prefixedStyle] !== undefined) {
    126 propertyName = prefixedStyle;
    127 }
    128 }
    129 goog.style.styleNameCache_[style] = propertyName;
    130 }
    131
    132 return propertyName;
    133};
    134
    135
    136/**
    137 * Returns the style property name in CSS notation. If it does not exist and a
    138 * vendor-specific version of the property does exist, then return the vendor-
    139 * specific property name instead.
    140 * @param {Element} element The element to change.
    141 * @param {string} style Style name.
    142 * @return {string} Vendor-specific style.
    143 * @private
    144 */
    145goog.style.getVendorStyleName_ = function(element, style) {
    146 var camelStyle = goog.string.toCamelCase(style);
    147
    148 if (element.style[camelStyle] === undefined) {
    149 var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +
    150 goog.string.toTitleCase(camelStyle);
    151
    152 if (element.style[prefixedStyle] !== undefined) {
    153 return goog.dom.vendor.getVendorPrefix() + '-' + style;
    154 }
    155 }
    156
    157 return style;
    158};
    159
    160
    161/**
    162 * Retrieves an explicitly-set style value of a node. This returns '' if there
    163 * isn't a style attribute on the element or if this style property has not been
    164 * explicitly set in script.
    165 *
    166 * @param {Element} element Element to get style of.
    167 * @param {string} property Property to get, css-style (if you have a camel-case
    168 * property, use element.style[style]).
    169 * @return {string} Style value.
    170 */
    171goog.style.getStyle = function(element, property) {
    172 // element.style is '' for well-known properties which are unset.
    173 // For for browser specific styles as 'filter' is undefined
    174 // so we need to return '' explicitly to make it consistent across
    175 // browsers.
    176 var styleValue = element.style[goog.string.toCamelCase(property)];
    177
    178 // Using typeof here because of a bug in Safari 5.1, where this value
    179 // was undefined, but === undefined returned false.
    180 if (typeof(styleValue) !== 'undefined') {
    181 return styleValue;
    182 }
    183
    184 return element.style[goog.style.getVendorJsStyleName_(element, property)] ||
    185 '';
    186};
    187
    188
    189/**
    190 * Retrieves a computed style value of a node. It returns empty string if the
    191 * value cannot be computed (which will be the case in Internet Explorer) or
    192 * "none" if the property requested is an SVG one and it has not been
    193 * explicitly set (firefox and webkit).
    194 *
    195 * @param {Element} element Element to get style of.
    196 * @param {string} property Property to get (camel-case).
    197 * @return {string} Style value.
    198 */
    199goog.style.getComputedStyle = function(element, property) {
    200 var doc = goog.dom.getOwnerDocument(element);
    201 if (doc.defaultView && doc.defaultView.getComputedStyle) {
    202 var styles = doc.defaultView.getComputedStyle(element, null);
    203 if (styles) {
    204 // element.style[..] is undefined for browser specific styles
    205 // as 'filter'.
    206 return styles[property] || styles.getPropertyValue(property) || '';
    207 }
    208 }
    209
    210 return '';
    211};
    212
    213
    214/**
    215 * Gets the cascaded style value of a node, or null if the value cannot be
    216 * computed (only Internet Explorer can do this).
    217 *
    218 * @param {Element} element Element to get style of.
    219 * @param {string} style Property to get (camel-case).
    220 * @return {string} Style value.
    221 */
    222goog.style.getCascadedStyle = function(element, style) {
    223 // TODO(nicksantos): This should be documented to return null. #fixTypes
    224 return element.currentStyle ? element.currentStyle[style] : null;
    225};
    226
    227
    228/**
    229 * Cross-browser pseudo get computed style. It returns the computed style where
    230 * available. If not available it tries the cascaded style value (IE
    231 * currentStyle) and in worst case the inline style value. It shouldn't be
    232 * called directly, see http://wiki/Main/ComputedStyleVsCascadedStyle for
    233 * discussion.
    234 *
    235 * @param {Element} element Element to get style of.
    236 * @param {string} style Property to get (must be camelCase, not css-style.).
    237 * @return {string} Style value.
    238 * @private
    239 */
    240goog.style.getStyle_ = function(element, style) {
    241 return goog.style.getComputedStyle(element, style) ||
    242 goog.style.getCascadedStyle(element, style) ||
    243 (element.style && element.style[style]);
    244};
    245
    246
    247/**
    248 * Retrieves the computed value of the box-sizing CSS attribute.
    249 * Browser support: http://caniuse.com/css3-boxsizing.
    250 * @param {!Element} element The element whose box-sizing to get.
    251 * @return {?string} 'content-box', 'border-box' or 'padding-box'. null if
    252 * box-sizing is not supported (IE7 and below).
    253 */
    254goog.style.getComputedBoxSizing = function(element) {
    255 return goog.style.getStyle_(element, 'boxSizing') ||
    256 goog.style.getStyle_(element, 'MozBoxSizing') ||
    257 goog.style.getStyle_(element, 'WebkitBoxSizing') || null;
    258};
    259
    260
    261/**
    262 * Retrieves the computed value of the position CSS attribute.
    263 * @param {Element} element The element to get the position of.
    264 * @return {string} Position value.
    265 */
    266goog.style.getComputedPosition = function(element) {
    267 return goog.style.getStyle_(element, 'position');
    268};
    269
    270
    271/**
    272 * Retrieves the computed background color string for a given element. The
    273 * string returned is suitable for assigning to another element's
    274 * background-color, but is not guaranteed to be in any particular string
    275 * format. Accessing the color in a numeric form may not be possible in all
    276 * browsers or with all input.
    277 *
    278 * If the background color for the element is defined as a hexadecimal value,
    279 * the resulting string can be parsed by goog.color.parse in all supported
    280 * browsers.
    281 *
    282 * Whether named colors like "red" or "lightblue" get translated into a
    283 * format which can be parsed is browser dependent. Calling this function on
    284 * transparent elements will return "transparent" in most browsers or
    285 * "rgba(0, 0, 0, 0)" in WebKit.
    286 * @param {Element} element The element to get the background color of.
    287 * @return {string} The computed string value of the background color.
    288 */
    289goog.style.getBackgroundColor = function(element) {
    290 return goog.style.getStyle_(element, 'backgroundColor');
    291};
    292
    293
    294/**
    295 * Retrieves the computed value of the overflow-x CSS attribute.
    296 * @param {Element} element The element to get the overflow-x of.
    297 * @return {string} The computed string value of the overflow-x attribute.
    298 */
    299goog.style.getComputedOverflowX = function(element) {
    300 return goog.style.getStyle_(element, 'overflowX');
    301};
    302
    303
    304/**
    305 * Retrieves the computed value of the overflow-y CSS attribute.
    306 * @param {Element} element The element to get the overflow-y of.
    307 * @return {string} The computed string value of the overflow-y attribute.
    308 */
    309goog.style.getComputedOverflowY = function(element) {
    310 return goog.style.getStyle_(element, 'overflowY');
    311};
    312
    313
    314/**
    315 * Retrieves the computed value of the z-index CSS attribute.
    316 * @param {Element} element The element to get the z-index of.
    317 * @return {string|number} The computed value of the z-index attribute.
    318 */
    319goog.style.getComputedZIndex = function(element) {
    320 return goog.style.getStyle_(element, 'zIndex');
    321};
    322
    323
    324/**
    325 * Retrieves the computed value of the text-align CSS attribute.
    326 * @param {Element} element The element to get the text-align of.
    327 * @return {string} The computed string value of the text-align attribute.
    328 */
    329goog.style.getComputedTextAlign = function(element) {
    330 return goog.style.getStyle_(element, 'textAlign');
    331};
    332
    333
    334/**
    335 * Retrieves the computed value of the cursor CSS attribute.
    336 * @param {Element} element The element to get the cursor of.
    337 * @return {string} The computed string value of the cursor attribute.
    338 */
    339goog.style.getComputedCursor = function(element) {
    340 return goog.style.getStyle_(element, 'cursor');
    341};
    342
    343
    344/**
    345 * Retrieves the computed value of the CSS transform attribute.
    346 * @param {Element} element The element to get the transform of.
    347 * @return {string} The computed string representation of the transform matrix.
    348 */
    349goog.style.getComputedTransform = function(element) {
    350 var property = goog.style.getVendorStyleName_(element, 'transform');
    351 return goog.style.getStyle_(element, property) ||
    352 goog.style.getStyle_(element, 'transform');
    353};
    354
    355
    356/**
    357 * Sets the top/left values of an element. If no unit is specified in the
    358 * argument then it will add px. The second argument is required if the first
    359 * argument is a string or number and is ignored if the first argument
    360 * is a coordinate.
    361 * @param {Element} el Element to move.
    362 * @param {string|number|goog.math.Coordinate} arg1 Left position or coordinate.
    363 * @param {string|number=} opt_arg2 Top position.
    364 */
    365goog.style.setPosition = function(el, arg1, opt_arg2) {
    366 var x, y;
    367
    368 if (arg1 instanceof goog.math.Coordinate) {
    369 x = arg1.x;
    370 y = arg1.y;
    371 } else {
    372 x = arg1;
    373 y = opt_arg2;
    374 }
    375
    376 el.style.left = goog.style.getPixelStyleValue_(
    377 /** @type {number|string} */ (x), false);
    378 el.style.top = goog.style.getPixelStyleValue_(
    379 /** @type {number|string} */ (y), false);
    380};
    381
    382
    383/**
    384 * Gets the offsetLeft and offsetTop properties of an element and returns them
    385 * in a Coordinate object
    386 * @param {Element} element Element.
    387 * @return {!goog.math.Coordinate} The position.
    388 */
    389goog.style.getPosition = function(element) {
    390 return new goog.math.Coordinate(element.offsetLeft, element.offsetTop);
    391};
    392
    393
    394/**
    395 * Returns the viewport element for a particular document
    396 * @param {Node=} opt_node DOM node (Document is OK) to get the viewport element
    397 * of.
    398 * @return {Element} document.documentElement or document.body.
    399 */
    400goog.style.getClientViewportElement = function(opt_node) {
    401 var doc;
    402 if (opt_node) {
    403 doc = goog.dom.getOwnerDocument(opt_node);
    404 } else {
    405 doc = goog.dom.getDocument();
    406 }
    407
    408 // In old IE versions the document.body represented the viewport
    409 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&
    410 !goog.dom.getDomHelper(doc).isCss1CompatMode()) {
    411 return doc.body;
    412 }
    413 return doc.documentElement;
    414};
    415
    416
    417/**
    418 * Calculates the viewport coordinates relative to the page/document
    419 * containing the node. The viewport may be the browser viewport for
    420 * non-iframe document, or the iframe container for iframe'd document.
    421 * @param {!Document} doc The document to use as the reference point.
    422 * @return {!goog.math.Coordinate} The page offset of the viewport.
    423 */
    424goog.style.getViewportPageOffset = function(doc) {
    425 var body = doc.body;
    426 var documentElement = doc.documentElement;
    427 var scrollLeft = body.scrollLeft || documentElement.scrollLeft;
    428 var scrollTop = body.scrollTop || documentElement.scrollTop;
    429 return new goog.math.Coordinate(scrollLeft, scrollTop);
    430};
    431
    432
    433/**
    434 * Gets the client rectangle of the DOM element.
    435 *
    436 * getBoundingClientRect is part of a new CSS object model draft (with a
    437 * long-time presence in IE), replacing the error-prone parent offset
    438 * computation and the now-deprecated Gecko getBoxObjectFor.
    439 *
    440 * This utility patches common browser bugs in getBoundingClientRect. It
    441 * will fail if getBoundingClientRect is unsupported.
    442 *
    443 * If the element is not in the DOM, the result is undefined, and an error may
    444 * be thrown depending on user agent.
    445 *
    446 * @param {!Element} el The element whose bounding rectangle is being queried.
    447 * @return {Object} A native bounding rectangle with numerical left, top,
    448 * right, and bottom. Reported by Firefox to be of object type ClientRect.
    449 * @private
    450 */
    451goog.style.getBoundingClientRect_ = function(el) {
    452 var rect;
    453 try {
    454 rect = el.getBoundingClientRect();
    455 } catch (e) {
    456 // In IE < 9, calling getBoundingClientRect on an orphan element raises an
    457 // "Unspecified Error". All other browsers return zeros.
    458 return {'left': 0, 'top': 0, 'right': 0, 'bottom': 0};
    459 }
    460
    461 // Patch the result in IE only, so that this function can be inlined if
    462 // compiled for non-IE.
    463 if (goog.userAgent.IE && el.ownerDocument.body) {
    464
    465 // In IE, most of the time, 2 extra pixels are added to the top and left
    466 // due to the implicit 2-pixel inset border. In IE6/7 quirks mode and
    467 // IE6 standards mode, this border can be overridden by setting the
    468 // document element's border to zero -- thus, we cannot rely on the
    469 // offset always being 2 pixels.
    470
    471 // In quirks mode, the offset can be determined by querying the body's
    472 // clientLeft/clientTop, but in standards mode, it is found by querying
    473 // the document element's clientLeft/clientTop. Since we already called
    474 // getBoundingClientRect we have already forced a reflow, so it is not
    475 // too expensive just to query them all.
    476
    477 // See: http://msdn.microsoft.com/en-us/library/ms536433(VS.85).aspx
    478 var doc = el.ownerDocument;
    479 rect.left -= doc.documentElement.clientLeft + doc.body.clientLeft;
    480 rect.top -= doc.documentElement.clientTop + doc.body.clientTop;
    481 }
    482 return /** @type {Object} */ (rect);
    483};
    484
    485
    486/**
    487 * Returns the first parent that could affect the position of a given element.
    488 * @param {Element} element The element to get the offset parent for.
    489 * @return {Element} The first offset parent or null if one cannot be found.
    490 */
    491goog.style.getOffsetParent = function(element) {
    492 // element.offsetParent does the right thing in IE7 and below. In other
    493 // browsers it only includes elements with position absolute, relative or
    494 // fixed, not elements with overflow set to auto or scroll.
    495 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(8)) {
    496 return element.offsetParent;
    497 }
    498
    499 var doc = goog.dom.getOwnerDocument(element);
    500 var positionStyle = goog.style.getStyle_(element, 'position');
    501 var skipStatic = positionStyle == 'fixed' || positionStyle == 'absolute';
    502 for (var parent = element.parentNode; parent && parent != doc;
    503 parent = parent.parentNode) {
    504 // Skip shadowDOM roots.
    505 if (parent.nodeType == goog.dom.NodeType.DOCUMENT_FRAGMENT &&
    506 parent.host) {
    507 parent = parent.host;
    508 }
    509 positionStyle =
    510 goog.style.getStyle_(/** @type {!Element} */ (parent), 'position');
    511 skipStatic = skipStatic && positionStyle == 'static' &&
    512 parent != doc.documentElement && parent != doc.body;
    513 if (!skipStatic && (parent.scrollWidth > parent.clientWidth ||
    514 parent.scrollHeight > parent.clientHeight ||
    515 positionStyle == 'fixed' ||
    516 positionStyle == 'absolute' ||
    517 positionStyle == 'relative')) {
    518 return /** @type {!Element} */ (parent);
    519 }
    520 }
    521 return null;
    522};
    523
    524
    525/**
    526 * Calculates and returns the visible rectangle for a given element. Returns a
    527 * box describing the visible portion of the nearest scrollable offset ancestor.
    528 * Coordinates are given relative to the document.
    529 *
    530 * @param {Element} element Element to get the visible rect for.
    531 * @return {goog.math.Box} Bounding elementBox describing the visible rect or
    532 * null if scrollable ancestor isn't inside the visible viewport.
    533 */
    534goog.style.getVisibleRectForElement = function(element) {
    535 var visibleRect = new goog.math.Box(0, Infinity, Infinity, 0);
    536 var dom = goog.dom.getDomHelper(element);
    537 var body = dom.getDocument().body;
    538 var documentElement = dom.getDocument().documentElement;
    539 var scrollEl = dom.getDocumentScrollElement();
    540
    541 // Determine the size of the visible rect by climbing the dom accounting for
    542 // all scrollable containers.
    543 for (var el = element; el = goog.style.getOffsetParent(el); ) {
    544 // clientWidth is zero for inline block elements in IE.
    545 // on WEBKIT, body element can have clientHeight = 0 and scrollHeight > 0
    546 if ((!goog.userAgent.IE || el.clientWidth != 0) &&
    547 (!goog.userAgent.WEBKIT || el.clientHeight != 0 || el != body) &&
    548 // body may have overflow set on it, yet we still get the entire
    549 // viewport. In some browsers, el.offsetParent may be
    550 // document.documentElement, so check for that too.
    551 (el != body && el != documentElement &&
    552 goog.style.getStyle_(el, 'overflow') != 'visible')) {
    553 var pos = goog.style.getPageOffset(el);
    554 var client = goog.style.getClientLeftTop(el);
    555 pos.x += client.x;
    556 pos.y += client.y;
    557
    558 visibleRect.top = Math.max(visibleRect.top, pos.y);
    559 visibleRect.right = Math.min(visibleRect.right,
    560 pos.x + el.clientWidth);
    561 visibleRect.bottom = Math.min(visibleRect.bottom,
    562 pos.y + el.clientHeight);
    563 visibleRect.left = Math.max(visibleRect.left, pos.x);
    564 }
    565 }
    566
    567 // Clip by window's viewport.
    568 var scrollX = scrollEl.scrollLeft, scrollY = scrollEl.scrollTop;
    569 visibleRect.left = Math.max(visibleRect.left, scrollX);
    570 visibleRect.top = Math.max(visibleRect.top, scrollY);
    571 var winSize = dom.getViewportSize();
    572 visibleRect.right = Math.min(visibleRect.right, scrollX + winSize.width);
    573 visibleRect.bottom = Math.min(visibleRect.bottom, scrollY + winSize.height);
    574 return visibleRect.top >= 0 && visibleRect.left >= 0 &&
    575 visibleRect.bottom > visibleRect.top &&
    576 visibleRect.right > visibleRect.left ?
    577 visibleRect : null;
    578};
    579
    580
    581/**
    582 * Calculate the scroll position of {@code container} with the minimum amount so
    583 * that the content and the borders of the given {@code element} become visible.
    584 * If the element is bigger than the container, its top left corner will be
    585 * aligned as close to the container's top left corner as possible.
    586 *
    587 * @param {Element} element The element to make visible.
    588 * @param {Element} container The container to scroll.
    589 * @param {boolean=} opt_center Whether to center the element in the container.
    590 * Defaults to false.
    591 * @return {!goog.math.Coordinate} The new scroll position of the container,
    592 * in form of goog.math.Coordinate(scrollLeft, scrollTop).
    593 */
    594goog.style.getContainerOffsetToScrollInto =
    595 function(element, container, opt_center) {
    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 // Relative pos. of the element's border box to the container's content box.
    602 var relX = elementPos.x - containerPos.x - containerBorder.left;
    603 var relY = elementPos.y - containerPos.y - containerBorder.top;
    604 // How much the element can move in the container, i.e. the difference between
    605 // the element's bottom-right-most and top-left-most position where it's
    606 // fully visible.
    607 var spaceX = container.clientWidth - element.offsetWidth;
    608 var spaceY = container.clientHeight - element.offsetHeight;
    609
    610 var scrollLeft = container.scrollLeft;
    611 var scrollTop = container.scrollTop;
    612 if (container == goog.dom.getDocument().body ||
    613 container == goog.dom.getDocument().documentElement) {
    614 // If the container is the document scroll element (usually <body>),
    615 // getPageOffset(element) is already relative to it and there is no need to
    616 // consider the current scroll.
    617 scrollLeft = containerPos.x + containerBorder.left;
    618 scrollTop = containerPos.y + containerBorder.top;
    619
    620 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {
    621 // In older versions of IE getPageOffset(element) does not include the
    622 // continaer border so it has to be added to accomodate.
    623 scrollLeft += containerBorder.left;
    624 scrollTop += containerBorder.top;
    625 }
    626 }
    627 if (opt_center) {
    628 // All browsers round non-integer scroll positions down.
    629 scrollLeft += relX - spaceX / 2;
    630 scrollTop += relY - spaceY / 2;
    631 } else {
    632 // This formula was designed to give the correct scroll values in the
    633 // following cases:
    634 // - element is higher than container (spaceY < 0) => scroll down by relY
    635 // - element is not higher that container (spaceY >= 0):
    636 // - it is above container (relY < 0) => scroll up by abs(relY)
    637 // - it is below container (relY > spaceY) => scroll down by relY - spaceY
    638 // - it is in the container => don't scroll
    639 scrollLeft += Math.min(relX, Math.max(relX - spaceX, 0));
    640 scrollTop += Math.min(relY, Math.max(relY - spaceY, 0));
    641 }
    642 return new goog.math.Coordinate(scrollLeft, scrollTop);
    643};
    644
    645
    646/**
    647 * Changes the scroll position of {@code container} with the minimum amount so
    648 * that the content and the borders of the given {@code element} become visible.
    649 * If the element is bigger than the container, its top left corner will be
    650 * aligned as close to the container's top left corner as possible.
    651 *
    652 * @param {Element} element The element to make visible.
    653 * @param {Element} container The container to scroll.
    654 * @param {boolean=} opt_center Whether to center the element in the container.
    655 * Defaults to false.
    656 */
    657goog.style.scrollIntoContainerView = function(element, container, opt_center) {
    658 var offset =
    659 goog.style.getContainerOffsetToScrollInto(element, container, opt_center);
    660 container.scrollLeft = offset.x;
    661 container.scrollTop = offset.y;
    662};
    663
    664
    665/**
    666 * Returns clientLeft (width of the left border and, if the directionality is
    667 * right to left, the vertical scrollbar) and clientTop as a coordinate object.
    668 *
    669 * @param {Element} el Element to get clientLeft for.
    670 * @return {!goog.math.Coordinate} Client left and top.
    671 */
    672goog.style.getClientLeftTop = function(el) {
    673 return new goog.math.Coordinate(el.clientLeft, el.clientTop);
    674};
    675
    676
    677/**
    678 * Returns a Coordinate object relative to the top-left of the HTML document.
    679 * Implemented as a single function to save having to do two recursive loops in
    680 * opera and safari just to get both coordinates. If you just want one value do
    681 * use goog.style.getPageOffsetLeft() and goog.style.getPageOffsetTop(), but
    682 * note if you call both those methods the tree will be analysed twice.
    683 *
    684 * @param {Element} el Element to get the page offset for.
    685 * @return {!goog.math.Coordinate} The page offset.
    686 */
    687goog.style.getPageOffset = function(el) {
    688 var doc = goog.dom.getOwnerDocument(el);
    689 // TODO(gboyer): Update the jsdoc in a way that doesn't break the universe.
    690 goog.asserts.assertObject(el, 'Parameter is required');
    691
    692 // NOTE(arv): If element is hidden (display none or disconnected or any the
    693 // ancestors are hidden) we get (0,0) by default but we still do the
    694 // accumulation of scroll position.
    695
    696 // TODO(arv): Should we check if the node is disconnected and in that case
    697 // return (0,0)?
    698
    699 var pos = new goog.math.Coordinate(0, 0);
    700 var viewportElement = goog.style.getClientViewportElement(doc);
    701 if (el == viewportElement) {
    702 // viewport is always at 0,0 as that defined the coordinate system for this
    703 // function - this avoids special case checks in the code below
    704 return pos;
    705 }
    706
    707 var box = goog.style.getBoundingClientRect_(el);
    708 // Must add the scroll coordinates in to get the absolute page offset
    709 // of element since getBoundingClientRect returns relative coordinates to
    710 // the viewport.
    711 var scrollCoord = goog.dom.getDomHelper(doc).getDocumentScroll();
    712 pos.x = box.left + scrollCoord.x;
    713 pos.y = box.top + scrollCoord.y;
    714
    715 return pos;
    716};
    717
    718
    719/**
    720 * Returns the left coordinate of an element relative to the HTML document
    721 * @param {Element} el Elements.
    722 * @return {number} The left coordinate.
    723 */
    724goog.style.getPageOffsetLeft = function(el) {
    725 return goog.style.getPageOffset(el).x;
    726};
    727
    728
    729/**
    730 * Returns the top coordinate of an element relative to the HTML document
    731 * @param {Element} el Elements.
    732 * @return {number} The top coordinate.
    733 */
    734goog.style.getPageOffsetTop = function(el) {
    735 return goog.style.getPageOffset(el).y;
    736};
    737
    738
    739/**
    740 * Returns a Coordinate object relative to the top-left of an HTML document
    741 * in an ancestor frame of this element. Used for measuring the position of
    742 * an element inside a frame relative to a containing frame.
    743 *
    744 * @param {Element} el Element to get the page offset for.
    745 * @param {Window} relativeWin The window to measure relative to. If relativeWin
    746 * is not in the ancestor frame chain of the element, we measure relative to
    747 * the top-most window.
    748 * @return {!goog.math.Coordinate} The page offset.
    749 */
    750goog.style.getFramedPageOffset = function(el, relativeWin) {
    751 var position = new goog.math.Coordinate(0, 0);
    752
    753 // Iterate up the ancestor frame chain, keeping track of the current window
    754 // and the current element in that window.
    755 var currentWin = goog.dom.getWindow(goog.dom.getOwnerDocument(el));
    756 var currentEl = el;
    757 do {
    758 // if we're at the top window, we want to get the page offset.
    759 // if we're at an inner frame, we only want to get the window position
    760 // so that we can determine the actual page offset in the context of
    761 // the outer window.
    762 var offset = currentWin == relativeWin ?
    763 goog.style.getPageOffset(currentEl) :
    764 goog.style.getClientPositionForElement_(
    765 goog.asserts.assert(currentEl));
    766
    767 position.x += offset.x;
    768 position.y += offset.y;
    769 } while (currentWin && currentWin != relativeWin &&
    770 currentWin != currentWin.parent &&
    771 (currentEl = currentWin.frameElement) &&
    772 (currentWin = currentWin.parent));
    773
    774 return position;
    775};
    776
    777
    778/**
    779 * Translates the specified rect relative to origBase page, for newBase page.
    780 * If origBase and newBase are the same, this function does nothing.
    781 *
    782 * @param {goog.math.Rect} rect The source rectangle relative to origBase page,
    783 * and it will have the translated result.
    784 * @param {goog.dom.DomHelper} origBase The DomHelper for the input rectangle.
    785 * @param {goog.dom.DomHelper} newBase The DomHelper for the resultant
    786 * coordinate. This must be a DOM for an ancestor frame of origBase
    787 * or the same as origBase.
    788 */
    789goog.style.translateRectForAnotherFrame = function(rect, origBase, newBase) {
    790 if (origBase.getDocument() != newBase.getDocument()) {
    791 var body = origBase.getDocument().body;
    792 var pos = goog.style.getFramedPageOffset(body, newBase.getWindow());
    793
    794 // Adjust Body's margin.
    795 pos = goog.math.Coordinate.difference(pos, goog.style.getPageOffset(body));
    796
    797 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&
    798 !origBase.isCss1CompatMode()) {
    799 pos = goog.math.Coordinate.difference(pos, origBase.getDocumentScroll());
    800 }
    801
    802 rect.left += pos.x;
    803 rect.top += pos.y;
    804 }
    805};
    806
    807
    808/**
    809 * Returns the position of an element relative to another element in the
    810 * document. A relative to B
    811 * @param {Element|Event|goog.events.Event} a Element or mouse event whose
    812 * position we're calculating.
    813 * @param {Element|Event|goog.events.Event} b Element or mouse event position
    814 * is relative to.
    815 * @return {!goog.math.Coordinate} The relative position.
    816 */
    817goog.style.getRelativePosition = function(a, b) {
    818 var ap = goog.style.getClientPosition(a);
    819 var bp = goog.style.getClientPosition(b);
    820 return new goog.math.Coordinate(ap.x - bp.x, ap.y - bp.y);
    821};
    822
    823
    824/**
    825 * Returns the position of the event or the element's border box relative to
    826 * the client viewport.
    827 * @param {!Element} el Element whose position to get.
    828 * @return {!goog.math.Coordinate} The position.
    829 * @private
    830 */
    831goog.style.getClientPositionForElement_ = function(el) {
    832 var box = goog.style.getBoundingClientRect_(el);
    833 return new goog.math.Coordinate(box.left, box.top);
    834};
    835
    836
    837/**
    838 * Returns the position of the event or the element's border box relative to
    839 * the client viewport.
    840 * @param {Element|Event|goog.events.Event} el Element or a mouse / touch event.
    841 * @return {!goog.math.Coordinate} The position.
    842 */
    843goog.style.getClientPosition = function(el) {
    844 goog.asserts.assert(el);
    845 if (el.nodeType == goog.dom.NodeType.ELEMENT) {
    846 return goog.style.getClientPositionForElement_(
    847 /** @type {!Element} */ (el));
    848 } else {
    849 var isAbstractedEvent = goog.isFunction(el.getBrowserEvent);
    850 var be = /** @type {!goog.events.BrowserEvent} */ (el);
    851 var targetEvent = el;
    852
    853 if (el.targetTouches && el.targetTouches.length) {
    854 targetEvent = el.targetTouches[0];
    855 } else if (isAbstractedEvent && be.getBrowserEvent().targetTouches &&
    856 be.getBrowserEvent().targetTouches.length) {
    857 targetEvent = be.getBrowserEvent().targetTouches[0];
    858 }
    859
    860 return new goog.math.Coordinate(
    861 targetEvent.clientX,
    862 targetEvent.clientY);
    863 }
    864};
    865
    866
    867/**
    868 * Moves an element to the given coordinates relative to the client viewport.
    869 * @param {Element} el Absolutely positioned element to set page offset for.
    870 * It must be in the document.
    871 * @param {number|goog.math.Coordinate} x Left position of the element's margin
    872 * box or a coordinate object.
    873 * @param {number=} opt_y Top position of the element's margin box.
    874 */
    875goog.style.setPageOffset = function(el, x, opt_y) {
    876 // Get current pageoffset
    877 var cur = goog.style.getPageOffset(el);
    878
    879 if (x instanceof goog.math.Coordinate) {
    880 opt_y = x.y;
    881 x = x.x;
    882 }
    883
    884 // NOTE(arv): We cannot allow strings for x and y. We could but that would
    885 // require us to manually transform between different units
    886
    887 // Work out deltas
    888 var dx = x - cur.x;
    889 var dy = opt_y - cur.y;
    890
    891 // Set position to current left/top + delta
    892 goog.style.setPosition(el, el.offsetLeft + dx, el.offsetTop + dy);
    893};
    894
    895
    896/**
    897 * Sets the width/height values of an element. If an argument is numeric,
    898 * or a goog.math.Size is passed, it is assumed to be pixels and will add
    899 * 'px' after converting it to an integer in string form. (This just sets the
    900 * CSS width and height properties so it might set content-box or border-box
    901 * size depending on the box model the browser is using.)
    902 *
    903 * @param {Element} element Element to set the size of.
    904 * @param {string|number|goog.math.Size} w Width of the element, or a
    905 * size object.
    906 * @param {string|number=} opt_h Height of the element. Required if w is not a
    907 * size object.
    908 */
    909goog.style.setSize = function(element, w, opt_h) {
    910 var h;
    911 if (w instanceof goog.math.Size) {
    912 h = w.height;
    913 w = w.width;
    914 } else {
    915 if (opt_h == undefined) {
    916 throw Error('missing height argument');
    917 }
    918 h = opt_h;
    919 }
    920
    921 goog.style.setWidth(element, /** @type {string|number} */ (w));
    922 goog.style.setHeight(element, /** @type {string|number} */ (h));
    923};
    924
    925
    926/**
    927 * Helper function to create a string to be set into a pixel-value style
    928 * property of an element. Can round to the nearest integer value.
    929 *
    930 * @param {string|number} value The style value to be used. If a number,
    931 * 'px' will be appended, otherwise the value will be applied directly.
    932 * @param {boolean} round Whether to round the nearest integer (if property
    933 * is a number).
    934 * @return {string} The string value for the property.
    935 * @private
    936 */
    937goog.style.getPixelStyleValue_ = function(value, round) {
    938 if (typeof value == 'number') {
    939 value = (round ? Math.round(value) : value) + 'px';
    940 }
    941
    942 return value;
    943};
    944
    945
    946/**
    947 * Set the height of an element. Sets the element's style property.
    948 * @param {Element} element Element to set the height of.
    949 * @param {string|number} height The height value to set. If a number, 'px'
    950 * will be appended, otherwise the value will be applied directly.
    951 */
    952goog.style.setHeight = function(element, height) {
    953 element.style.height = goog.style.getPixelStyleValue_(height, true);
    954};
    955
    956
    957/**
    958 * Set the width of an element. Sets the element's style property.
    959 * @param {Element} element Element to set the width of.
    960 * @param {string|number} width The width value to set. If a number, 'px'
    961 * will be appended, otherwise the value will be applied directly.
    962 */
    963goog.style.setWidth = function(element, width) {
    964 element.style.width = goog.style.getPixelStyleValue_(width, true);
    965};
    966
    967
    968/**
    969 * Gets the height and width of an element, even if its display is none.
    970 *
    971 * Specifically, this returns the height and width of the border box,
    972 * irrespective of the box model in effect.
    973 *
    974 * Note that this function does not take CSS transforms into account. Please see
    975 * {@code goog.style.getTransformedSize}.
    976 * @param {Element} element Element to get size of.
    977 * @return {!goog.math.Size} Object with width/height properties.
    978 */
    979goog.style.getSize = function(element) {
    980 return goog.style.evaluateWithTemporaryDisplay_(
    981 goog.style.getSizeWithDisplay_, /** @type {!Element} */ (element));
    982};
    983
    984
    985/**
    986 * Call {@code fn} on {@code element} such that {@code element}'s dimensions are
    987 * accurate when it's passed to {@code fn}.
    988 * @param {function(!Element): T} fn Function to call with {@code element} as
    989 * an argument after temporarily changing {@code element}'s display such
    990 * that its dimensions are accurate.
    991 * @param {!Element} element Element (which may have display none) to use as
    992 * argument to {@code fn}.
    993 * @return {T} Value returned by calling {@code fn} with {@code element}.
    994 * @template T
    995 * @private
    996 */
    997goog.style.evaluateWithTemporaryDisplay_ = function(fn, element) {
    998 if (goog.style.getStyle_(element, 'display') != 'none') {
    999 return fn(element);
    1000 }
    1001
    1002 var style = element.style;
    1003 var originalDisplay = style.display;
    1004 var originalVisibility = style.visibility;
    1005 var originalPosition = style.position;
    1006
    1007 style.visibility = 'hidden';
    1008 style.position = 'absolute';
    1009 style.display = 'inline';
    1010
    1011 var retVal = fn(element);
    1012
    1013 style.display = originalDisplay;
    1014 style.position = originalPosition;
    1015 style.visibility = originalVisibility;
    1016
    1017 return retVal;
    1018};
    1019
    1020
    1021/**
    1022 * Gets the height and width of an element when the display is not none.
    1023 * @param {Element} element Element to get size of.
    1024 * @return {!goog.math.Size} Object with width/height properties.
    1025 * @private
    1026 */
    1027goog.style.getSizeWithDisplay_ = function(element) {
    1028 var offsetWidth = element.offsetWidth;
    1029 var offsetHeight = element.offsetHeight;
    1030 var webkitOffsetsZero =
    1031 goog.userAgent.WEBKIT && !offsetWidth && !offsetHeight;
    1032 if ((!goog.isDef(offsetWidth) || webkitOffsetsZero) &&
    1033 element.getBoundingClientRect) {
    1034 // Fall back to calling getBoundingClientRect when offsetWidth or
    1035 // offsetHeight are not defined, or when they are zero in WebKit browsers.
    1036 // This makes sure that we return for the correct size for SVG elements, but
    1037 // will still return 0 on Webkit prior to 534.8, see
    1038 // http://trac.webkit.org/changeset/67252.
    1039 var clientRect = goog.style.getBoundingClientRect_(element);
    1040 return new goog.math.Size(clientRect.right - clientRect.left,
    1041 clientRect.bottom - clientRect.top);
    1042 }
    1043 return new goog.math.Size(offsetWidth, offsetHeight);
    1044};
    1045
    1046
    1047/**
    1048 * Gets the height and width of an element, post transform, even if its display
    1049 * is none.
    1050 *
    1051 * This is like {@code goog.style.getSize}, except:
    1052 * <ol>
    1053 * <li>Takes webkitTransforms such as rotate and scale into account.
    1054 * <li>Will return null if {@code element} doesn't respond to
    1055 * {@code getBoundingClientRect}.
    1056 * <li>Currently doesn't make sense on non-WebKit browsers which don't support
    1057 * webkitTransforms.
    1058 * </ol>
    1059 * @param {!Element} element Element to get size of.
    1060 * @return {goog.math.Size} Object with width/height properties.
    1061 */
    1062goog.style.getTransformedSize = function(element) {
    1063 if (!element.getBoundingClientRect) {
    1064 return null;
    1065 }
    1066
    1067 var clientRect = goog.style.evaluateWithTemporaryDisplay_(
    1068 goog.style.getBoundingClientRect_, element);
    1069 return new goog.math.Size(clientRect.right - clientRect.left,
    1070 clientRect.bottom - clientRect.top);
    1071};
    1072
    1073
    1074/**
    1075 * Returns a bounding rectangle for a given element in page space.
    1076 * @param {Element} element Element to get bounds of. Must not be display none.
    1077 * @return {!goog.math.Rect} Bounding rectangle for the element.
    1078 */
    1079goog.style.getBounds = function(element) {
    1080 var o = goog.style.getPageOffset(element);
    1081 var s = goog.style.getSize(element);
    1082 return new goog.math.Rect(o.x, o.y, s.width, s.height);
    1083};
    1084
    1085
    1086/**
    1087 * Converts a CSS selector in the form style-property to styleProperty.
    1088 * @param {*} selector CSS Selector.
    1089 * @return {string} Camel case selector.
    1090 * @deprecated Use goog.string.toCamelCase instead.
    1091 */
    1092goog.style.toCamelCase = function(selector) {
    1093 return goog.string.toCamelCase(String(selector));
    1094};
    1095
    1096
    1097/**
    1098 * Converts a CSS selector in the form styleProperty to style-property.
    1099 * @param {string} selector Camel case selector.
    1100 * @return {string} Selector cased.
    1101 * @deprecated Use goog.string.toSelectorCase instead.
    1102 */
    1103goog.style.toSelectorCase = function(selector) {
    1104 return goog.string.toSelectorCase(selector);
    1105};
    1106
    1107
    1108/**
    1109 * Gets the opacity of a node (x-browser). This gets the inline style opacity
    1110 * of the node, and does not take into account the cascaded or the computed
    1111 * style for this node.
    1112 * @param {Element} el Element whose opacity has to be found.
    1113 * @return {number|string} Opacity between 0 and 1 or an empty string {@code ''}
    1114 * if the opacity is not set.
    1115 */
    1116goog.style.getOpacity = function(el) {
    1117 var style = el.style;
    1118 var result = '';
    1119 if ('opacity' in style) {
    1120 result = style.opacity;
    1121 } else if ('MozOpacity' in style) {
    1122 result = style.MozOpacity;
    1123 } else if ('filter' in style) {
    1124 var match = style.filter.match(/alpha\(opacity=([\d.]+)\)/);
    1125 if (match) {
    1126 result = String(match[1] / 100);
    1127 }
    1128 }
    1129 return result == '' ? result : Number(result);
    1130};
    1131
    1132
    1133/**
    1134 * Sets the opacity of a node (x-browser).
    1135 * @param {Element} el Elements whose opacity has to be set.
    1136 * @param {number|string} alpha Opacity between 0 and 1 or an empty string
    1137 * {@code ''} to clear the opacity.
    1138 */
    1139goog.style.setOpacity = function(el, alpha) {
    1140 var style = el.style;
    1141 if ('opacity' in style) {
    1142 style.opacity = alpha;
    1143 } else if ('MozOpacity' in style) {
    1144 style.MozOpacity = alpha;
    1145 } else if ('filter' in style) {
    1146 // TODO(arv): Overwriting the filter might have undesired side effects.
    1147 if (alpha === '') {
    1148 style.filter = '';
    1149 } else {
    1150 style.filter = 'alpha(opacity=' + alpha * 100 + ')';
    1151 }
    1152 }
    1153};
    1154
    1155
    1156/**
    1157 * Sets the background of an element to a transparent image in a browser-
    1158 * independent manner.
    1159 *
    1160 * This function does not support repeating backgrounds or alternate background
    1161 * positions to match the behavior of Internet Explorer. It also does not
    1162 * support sizingMethods other than crop since they cannot be replicated in
    1163 * browsers other than Internet Explorer.
    1164 *
    1165 * @param {Element} el The element to set background on.
    1166 * @param {string} src The image source URL.
    1167 */
    1168goog.style.setTransparentBackgroundImage = function(el, src) {
    1169 var style = el.style;
    1170 // It is safe to use the style.filter in IE only. In Safari 'filter' is in
    1171 // style object but access to style.filter causes it to throw an exception.
    1172 // Note: IE8 supports images with an alpha channel.
    1173 if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
    1174 // See TODO in setOpacity.
    1175 style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(' +
    1176 'src="' + src + '", sizingMethod="crop")';
    1177 } else {
    1178 // Set style properties individually instead of using background shorthand
    1179 // to prevent overwriting a pre-existing background color.
    1180 style.backgroundImage = 'url(' + src + ')';
    1181 style.backgroundPosition = 'top left';
    1182 style.backgroundRepeat = 'no-repeat';
    1183 }
    1184};
    1185
    1186
    1187/**
    1188 * Clears the background image of an element in a browser independent manner.
    1189 * @param {Element} el The element to clear background image for.
    1190 */
    1191goog.style.clearTransparentBackgroundImage = function(el) {
    1192 var style = el.style;
    1193 if ('filter' in style) {
    1194 // See TODO in setOpacity.
    1195 style.filter = '';
    1196 } else {
    1197 // Set style properties individually instead of using background shorthand
    1198 // to prevent overwriting a pre-existing background color.
    1199 style.backgroundImage = 'none';
    1200 }
    1201};
    1202
    1203
    1204/**
    1205 * Shows or hides an element from the page. Hiding the element is done by
    1206 * setting the display property to "none", removing the element from the
    1207 * rendering hierarchy so it takes up no space. To show the element, the default
    1208 * inherited display property is restored (defined either in stylesheets or by
    1209 * the browser's default style rules.)
    1210 *
    1211 * Caveat 1: if the inherited display property for the element is set to "none"
    1212 * by the stylesheets, that is the property that will be restored by a call to
    1213 * showElement(), effectively toggling the display between "none" and "none".
    1214 *
    1215 * Caveat 2: if the element display style is set inline (by setting either
    1216 * element.style.display or a style attribute in the HTML), a call to
    1217 * showElement will clear that setting and defer to the inherited style in the
    1218 * stylesheet.
    1219 * @param {Element} el Element to show or hide.
    1220 * @param {*} display True to render the element in its default style,
    1221 * false to disable rendering the element.
    1222 * @deprecated Use goog.style.setElementShown instead.
    1223 */
    1224goog.style.showElement = function(el, display) {
    1225 goog.style.setElementShown(el, display);
    1226};
    1227
    1228
    1229/**
    1230 * Shows or hides an element from the page. Hiding the element is done by
    1231 * setting the display property to "none", removing the element from the
    1232 * rendering hierarchy so it takes up no space. To show the element, the default
    1233 * inherited display property is restored (defined either in stylesheets or by
    1234 * the browser's default style rules).
    1235 *
    1236 * Caveat 1: if the inherited display property for the element is set to "none"
    1237 * by the stylesheets, that is the property that will be restored by a call to
    1238 * setElementShown(), effectively toggling the display between "none" and
    1239 * "none".
    1240 *
    1241 * Caveat 2: if the element display style is set inline (by setting either
    1242 * element.style.display or a style attribute in the HTML), a call to
    1243 * setElementShown will clear that setting and defer to the inherited style in
    1244 * the stylesheet.
    1245 * @param {Element} el Element to show or hide.
    1246 * @param {*} isShown True to render the element in its default style,
    1247 * false to disable rendering the element.
    1248 */
    1249goog.style.setElementShown = function(el, isShown) {
    1250 el.style.display = isShown ? '' : 'none';
    1251};
    1252
    1253
    1254/**
    1255 * Test whether the given element has been shown or hidden via a call to
    1256 * {@link #setElementShown}.
    1257 *
    1258 * Note this is strictly a companion method for a call
    1259 * to {@link #setElementShown} and the same caveats apply; in particular, this
    1260 * method does not guarantee that the return value will be consistent with
    1261 * whether or not the element is actually visible.
    1262 *
    1263 * @param {Element} el The element to test.
    1264 * @return {boolean} Whether the element has been shown.
    1265 * @see #setElementShown
    1266 */
    1267goog.style.isElementShown = function(el) {
    1268 return el.style.display != 'none';
    1269};
    1270
    1271
    1272/**
    1273 * Installs the styles string into the window that contains opt_element. If
    1274 * opt_element is null, the main window is used.
    1275 * @param {string} stylesString The style string to install.
    1276 * @param {Node=} opt_node Node whose parent document should have the
    1277 * styles installed.
    1278 * @return {Element|StyleSheet} The style element created.
    1279 */
    1280goog.style.installStyles = function(stylesString, opt_node) {
    1281 var dh = goog.dom.getDomHelper(opt_node);
    1282 var styleSheet = null;
    1283
    1284 // IE < 11 requires createStyleSheet. Note that doc.createStyleSheet will be
    1285 // undefined as of IE 11.
    1286 var doc = dh.getDocument();
    1287 if (goog.userAgent.IE && doc.createStyleSheet) {
    1288 styleSheet = doc.createStyleSheet();
    1289 goog.style.setStyles(styleSheet, stylesString);
    1290 } else {
    1291 var head = dh.getElementsByTagNameAndClass(goog.dom.TagName.HEAD)[0];
    1292
    1293 // In opera documents are not guaranteed to have a head element, thus we
    1294 // have to make sure one exists before using it.
    1295 if (!head) {
    1296 var body = dh.getElementsByTagNameAndClass(goog.dom.TagName.BODY)[0];
    1297 head = dh.createDom(goog.dom.TagName.HEAD);
    1298 body.parentNode.insertBefore(head, body);
    1299 }
    1300 styleSheet = dh.createDom(goog.dom.TagName.STYLE);
    1301 // NOTE(user): Setting styles after the style element has been appended
    1302 // to the head results in a nasty Webkit bug in certain scenarios. Please
    1303 // refer to https://bugs.webkit.org/show_bug.cgi?id=26307 for additional
    1304 // details.
    1305 goog.style.setStyles(styleSheet, stylesString);
    1306 dh.appendChild(head, styleSheet);
    1307 }
    1308 return styleSheet;
    1309};
    1310
    1311
    1312/**
    1313 * Removes the styles added by {@link #installStyles}.
    1314 * @param {Element|StyleSheet} styleSheet The value returned by
    1315 * {@link #installStyles}.
    1316 */
    1317goog.style.uninstallStyles = function(styleSheet) {
    1318 var node = styleSheet.ownerNode || styleSheet.owningElement ||
    1319 /** @type {Element} */ (styleSheet);
    1320 goog.dom.removeNode(node);
    1321};
    1322
    1323
    1324/**
    1325 * Sets the content of a style element. The style element can be any valid
    1326 * style element. This element will have its content completely replaced by
    1327 * the new stylesString.
    1328 * @param {Element|StyleSheet} element A stylesheet element as returned by
    1329 * installStyles.
    1330 * @param {string} stylesString The new content of the stylesheet.
    1331 */
    1332goog.style.setStyles = function(element, stylesString) {
    1333 if (goog.userAgent.IE && goog.isDef(element.cssText)) {
    1334 // Adding the selectors individually caused the browser to hang if the
    1335 // selector was invalid or there were CSS comments. Setting the cssText of
    1336 // the style node works fine and ignores CSS that IE doesn't understand.
    1337 // However IE >= 11 doesn't support cssText any more, so we make sure that
    1338 // cssText is a defined property and otherwise fall back to innerHTML.
    1339 element.cssText = stylesString;
    1340 } else {
    1341 element.innerHTML = stylesString;
    1342 }
    1343};
    1344
    1345
    1346/**
    1347 * Sets 'white-space: pre-wrap' for a node (x-browser).
    1348 *
    1349 * There are as many ways of specifying pre-wrap as there are browsers.
    1350 *
    1351 * CSS3/IE8: white-space: pre-wrap;
    1352 * Mozilla: white-space: -moz-pre-wrap;
    1353 * Opera: white-space: -o-pre-wrap;
    1354 * IE6/7: white-space: pre; word-wrap: break-word;
    1355 *
    1356 * @param {Element} el Element to enable pre-wrap for.
    1357 */
    1358goog.style.setPreWrap = function(el) {
    1359 var style = el.style;
    1360 if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
    1361 style.whiteSpace = 'pre';
    1362 style.wordWrap = 'break-word';
    1363 } else if (goog.userAgent.GECKO) {
    1364 style.whiteSpace = '-moz-pre-wrap';
    1365 } else {
    1366 style.whiteSpace = 'pre-wrap';
    1367 }
    1368};
    1369
    1370
    1371/**
    1372 * Sets 'display: inline-block' for an element (cross-browser).
    1373 * @param {Element} el Element to which the inline-block display style is to be
    1374 * applied.
    1375 * @see ../demos/inline_block_quirks.html
    1376 * @see ../demos/inline_block_standards.html
    1377 */
    1378goog.style.setInlineBlock = function(el) {
    1379 var style = el.style;
    1380 // Without position:relative, weirdness ensues. Just accept it and move on.
    1381 style.position = 'relative';
    1382
    1383 if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
    1384 // IE8 supports inline-block so fall through to the else
    1385 // Zoom:1 forces hasLayout, display:inline gives inline behavior.
    1386 style.zoom = '1';
    1387 style.display = 'inline';
    1388 } else {
    1389 // Opera, Webkit, and Safari seem to do OK with the standard inline-block
    1390 // style.
    1391 style.display = 'inline-block';
    1392 }
    1393};
    1394
    1395
    1396/**
    1397 * Returns true if the element is using right to left (rtl) direction.
    1398 * @param {Element} el The element to test.
    1399 * @return {boolean} True for right to left, false for left to right.
    1400 */
    1401goog.style.isRightToLeft = function(el) {
    1402 return 'rtl' == goog.style.getStyle_(el, 'direction');
    1403};
    1404
    1405
    1406/**
    1407 * The CSS style property corresponding to an element being
    1408 * unselectable on the current browser platform (null if none).
    1409 * Opera and IE instead use a DOM attribute 'unselectable'.
    1410 * @type {?string}
    1411 * @private
    1412 */
    1413goog.style.unselectableStyle_ =
    1414 goog.userAgent.GECKO ? 'MozUserSelect' :
    1415 goog.userAgent.WEBKIT ? 'WebkitUserSelect' :
    1416 null;
    1417
    1418
    1419/**
    1420 * Returns true if the element is set to be unselectable, false otherwise.
    1421 * Note that on some platforms (e.g. Mozilla), even if an element isn't set
    1422 * to be unselectable, it will behave as such if any of its ancestors is
    1423 * unselectable.
    1424 * @param {Element} el Element to check.
    1425 * @return {boolean} Whether the element is set to be unselectable.
    1426 */
    1427goog.style.isUnselectable = function(el) {
    1428 if (goog.style.unselectableStyle_) {
    1429 return el.style[goog.style.unselectableStyle_].toLowerCase() == 'none';
    1430 } else if (goog.userAgent.IE || goog.userAgent.OPERA) {
    1431 return el.getAttribute('unselectable') == 'on';
    1432 }
    1433 return false;
    1434};
    1435
    1436
    1437/**
    1438 * Makes the element and its descendants selectable or unselectable. Note
    1439 * that on some platforms (e.g. Mozilla), even if an element isn't set to
    1440 * be unselectable, it will behave as such if any of its ancestors is
    1441 * unselectable.
    1442 * @param {Element} el The element to alter.
    1443 * @param {boolean} unselectable Whether the element and its descendants
    1444 * should be made unselectable.
    1445 * @param {boolean=} opt_noRecurse Whether to only alter the element's own
    1446 * selectable state, and leave its descendants alone; defaults to false.
    1447 */
    1448goog.style.setUnselectable = function(el, unselectable, opt_noRecurse) {
    1449 // TODO(attila): Do we need all of TR_DomUtil.makeUnselectable() in Closure?
    1450 var descendants = !opt_noRecurse ? el.getElementsByTagName('*') : null;
    1451 var name = goog.style.unselectableStyle_;
    1452 if (name) {
    1453 // Add/remove the appropriate CSS style to/from the element and its
    1454 // descendants.
    1455 var value = unselectable ? 'none' : '';
    1456 // MathML elements do not have a style property. Verify before setting.
    1457 if (el.style) {
    1458 el.style[name] = value;
    1459 }
    1460 if (descendants) {
    1461 for (var i = 0, descendant; descendant = descendants[i]; i++) {
    1462 if (descendant.style) {
    1463 descendant.style[name] = value;
    1464 }
    1465 }
    1466 }
    1467 } else if (goog.userAgent.IE || goog.userAgent.OPERA) {
    1468 // Toggle the 'unselectable' attribute on the element and its descendants.
    1469 var value = unselectable ? 'on' : '';
    1470 el.setAttribute('unselectable', value);
    1471 if (descendants) {
    1472 for (var i = 0, descendant; descendant = descendants[i]; i++) {
    1473 descendant.setAttribute('unselectable', value);
    1474 }
    1475 }
    1476 }
    1477};
    1478
    1479
    1480/**
    1481 * Gets the border box size for an element.
    1482 * @param {Element} element The element to get the size for.
    1483 * @return {!goog.math.Size} The border box size.
    1484 */
    1485goog.style.getBorderBoxSize = function(element) {
    1486 return new goog.math.Size(element.offsetWidth, element.offsetHeight);
    1487};
    1488
    1489
    1490/**
    1491 * Sets the border box size of an element. This is potentially expensive in IE
    1492 * if the document is CSS1Compat mode
    1493 * @param {Element} element The element to set the size on.
    1494 * @param {goog.math.Size} size The new size.
    1495 */
    1496goog.style.setBorderBoxSize = function(element, size) {
    1497 var doc = goog.dom.getOwnerDocument(element);
    1498 var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode();
    1499
    1500 if (goog.userAgent.IE &&
    1501 !goog.userAgent.isVersionOrHigher('10') &&
    1502 (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) {
    1503 var style = element.style;
    1504 if (isCss1CompatMode) {
    1505 var paddingBox = goog.style.getPaddingBox(element);
    1506 var borderBox = goog.style.getBorderBox(element);
    1507 style.pixelWidth = size.width - borderBox.left - paddingBox.left -
    1508 paddingBox.right - borderBox.right;
    1509 style.pixelHeight = size.height - borderBox.top - paddingBox.top -
    1510 paddingBox.bottom - borderBox.bottom;
    1511 } else {
    1512 style.pixelWidth = size.width;
    1513 style.pixelHeight = size.height;
    1514 }
    1515 } else {
    1516 goog.style.setBoxSizingSize_(element, size, 'border-box');
    1517 }
    1518};
    1519
    1520
    1521/**
    1522 * Gets the content box size for an element. This is potentially expensive in
    1523 * all browsers.
    1524 * @param {Element} element The element to get the size for.
    1525 * @return {!goog.math.Size} The content box size.
    1526 */
    1527goog.style.getContentBoxSize = function(element) {
    1528 var doc = goog.dom.getOwnerDocument(element);
    1529 var ieCurrentStyle = goog.userAgent.IE && element.currentStyle;
    1530 if (ieCurrentStyle &&
    1531 goog.dom.getDomHelper(doc).isCss1CompatMode() &&
    1532 ieCurrentStyle.width != 'auto' && ieCurrentStyle.height != 'auto' &&
    1533 !ieCurrentStyle.boxSizing) {
    1534 // If IE in CSS1Compat mode than just use the width and height.
    1535 // If we have a boxSizing then fall back on measuring the borders etc.
    1536 var width = goog.style.getIePixelValue_(element, ieCurrentStyle.width,
    1537 'width', 'pixelWidth');
    1538 var height = goog.style.getIePixelValue_(element, ieCurrentStyle.height,
    1539 'height', 'pixelHeight');
    1540 return new goog.math.Size(width, height);
    1541 } else {
    1542 var borderBoxSize = goog.style.getBorderBoxSize(element);
    1543 var paddingBox = goog.style.getPaddingBox(element);
    1544 var borderBox = goog.style.getBorderBox(element);
    1545 return new goog.math.Size(borderBoxSize.width -
    1546 borderBox.left - paddingBox.left -
    1547 paddingBox.right - borderBox.right,
    1548 borderBoxSize.height -
    1549 borderBox.top - paddingBox.top -
    1550 paddingBox.bottom - borderBox.bottom);
    1551 }
    1552};
    1553
    1554
    1555/**
    1556 * Sets the content box size of an element. This is potentially expensive in IE
    1557 * if the document is BackCompat mode.
    1558 * @param {Element} element The element to set the size on.
    1559 * @param {goog.math.Size} size The new size.
    1560 */
    1561goog.style.setContentBoxSize = function(element, size) {
    1562 var doc = goog.dom.getOwnerDocument(element);
    1563 var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode();
    1564 if (goog.userAgent.IE &&
    1565 !goog.userAgent.isVersionOrHigher('10') &&
    1566 (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) {
    1567 var style = element.style;
    1568 if (isCss1CompatMode) {
    1569 style.pixelWidth = size.width;
    1570 style.pixelHeight = size.height;
    1571 } else {
    1572 var paddingBox = goog.style.getPaddingBox(element);
    1573 var borderBox = goog.style.getBorderBox(element);
    1574 style.pixelWidth = size.width + borderBox.left + paddingBox.left +
    1575 paddingBox.right + borderBox.right;
    1576 style.pixelHeight = size.height + borderBox.top + paddingBox.top +
    1577 paddingBox.bottom + borderBox.bottom;
    1578 }
    1579 } else {
    1580 goog.style.setBoxSizingSize_(element, size, 'content-box');
    1581 }
    1582};
    1583
    1584
    1585/**
    1586 * Helper function that sets the box sizing as well as the width and height
    1587 * @param {Element} element The element to set the size on.
    1588 * @param {goog.math.Size} size The new size to set.
    1589 * @param {string} boxSizing The box-sizing value.
    1590 * @private
    1591 */
    1592goog.style.setBoxSizingSize_ = function(element, size, boxSizing) {
    1593 var style = element.style;
    1594 if (goog.userAgent.GECKO) {
    1595 style.MozBoxSizing = boxSizing;
    1596 } else if (goog.userAgent.WEBKIT) {
    1597 style.WebkitBoxSizing = boxSizing;
    1598 } else {
    1599 // Includes IE8 and Opera 9.50+
    1600 style.boxSizing = boxSizing;
    1601 }
    1602
    1603 // Setting this to a negative value will throw an exception on IE
    1604 // (and doesn't do anything different than setting it to 0).
    1605 style.width = Math.max(size.width, 0) + 'px';
    1606 style.height = Math.max(size.height, 0) + 'px';
    1607};
    1608
    1609
    1610/**
    1611 * IE specific function that converts a non pixel unit to pixels.
    1612 * @param {Element} element The element to convert the value for.
    1613 * @param {string} value The current value as a string. The value must not be
    1614 * ''.
    1615 * @param {string} name The CSS property name to use for the converstion. This
    1616 * should be 'left', 'top', 'width' or 'height'.
    1617 * @param {string} pixelName The CSS pixel property name to use to get the
    1618 * value in pixels.
    1619 * @return {number} The value in pixels.
    1620 * @private
    1621 */
    1622goog.style.getIePixelValue_ = function(element, value, name, pixelName) {
    1623 // Try if we already have a pixel value. IE does not do half pixels so we
    1624 // only check if it matches a number followed by 'px'.
    1625 if (/^\d+px?$/.test(value)) {
    1626 return parseInt(value, 10);
    1627 } else {
    1628 var oldStyleValue = element.style[name];
    1629 var oldRuntimeValue = element.runtimeStyle[name];
    1630 // set runtime style to prevent changes
    1631 element.runtimeStyle[name] = element.currentStyle[name];
    1632 element.style[name] = value;
    1633 var pixelValue = element.style[pixelName];
    1634 // restore
    1635 element.style[name] = oldStyleValue;
    1636 element.runtimeStyle[name] = oldRuntimeValue;
    1637 return pixelValue;
    1638 }
    1639};
    1640
    1641
    1642/**
    1643 * Helper function for getting the pixel padding or margin for IE.
    1644 * @param {Element} element The element to get the padding for.
    1645 * @param {string} propName The property name.
    1646 * @return {number} The pixel padding.
    1647 * @private
    1648 */
    1649goog.style.getIePixelDistance_ = function(element, propName) {
    1650 var value = goog.style.getCascadedStyle(element, propName);
    1651 return value ?
    1652 goog.style.getIePixelValue_(element, value, 'left', 'pixelLeft') : 0;
    1653};
    1654
    1655
    1656/**
    1657 * Gets the computed paddings or margins (on all sides) in pixels.
    1658 * @param {Element} element The element to get the padding for.
    1659 * @param {string} stylePrefix Pass 'padding' to retrieve the padding box,
    1660 * or 'margin' to retrieve the margin box.
    1661 * @return {!goog.math.Box} The computed paddings or margins.
    1662 * @private
    1663 */
    1664goog.style.getBox_ = function(element, stylePrefix) {
    1665 if (goog.userAgent.IE) {
    1666 var left = goog.style.getIePixelDistance_(element, stylePrefix + 'Left');
    1667 var right = goog.style.getIePixelDistance_(element, stylePrefix + 'Right');
    1668 var top = goog.style.getIePixelDistance_(element, stylePrefix + 'Top');
    1669 var bottom = goog.style.getIePixelDistance_(
    1670 element, stylePrefix + 'Bottom');
    1671 return new goog.math.Box(top, right, bottom, left);
    1672 } else {
    1673 // On non-IE browsers, getComputedStyle is always non-null.
    1674 var left = /** @type {string} */ (
    1675 goog.style.getComputedStyle(element, stylePrefix + 'Left'));
    1676 var right = /** @type {string} */ (
    1677 goog.style.getComputedStyle(element, stylePrefix + 'Right'));
    1678 var top = /** @type {string} */ (
    1679 goog.style.getComputedStyle(element, stylePrefix + 'Top'));
    1680 var bottom = /** @type {string} */ (
    1681 goog.style.getComputedStyle(element, stylePrefix + 'Bottom'));
    1682
    1683 // NOTE(arv): Gecko can return floating point numbers for the computed
    1684 // style values.
    1685 return new goog.math.Box(parseFloat(top),
    1686 parseFloat(right),
    1687 parseFloat(bottom),
    1688 parseFloat(left));
    1689 }
    1690};
    1691
    1692
    1693/**
    1694 * Gets the computed paddings (on all sides) in pixels.
    1695 * @param {Element} element The element to get the padding for.
    1696 * @return {!goog.math.Box} The computed paddings.
    1697 */
    1698goog.style.getPaddingBox = function(element) {
    1699 return goog.style.getBox_(element, 'padding');
    1700};
    1701
    1702
    1703/**
    1704 * Gets the computed margins (on all sides) in pixels.
    1705 * @param {Element} element The element to get the margins for.
    1706 * @return {!goog.math.Box} The computed margins.
    1707 */
    1708goog.style.getMarginBox = function(element) {
    1709 return goog.style.getBox_(element, 'margin');
    1710};
    1711
    1712
    1713/**
    1714 * A map used to map the border width keywords to a pixel width.
    1715 * @type {Object}
    1716 * @private
    1717 */
    1718goog.style.ieBorderWidthKeywords_ = {
    1719 'thin': 2,
    1720 'medium': 4,
    1721 'thick': 6
    1722};
    1723
    1724
    1725/**
    1726 * Helper function for IE to get the pixel border.
    1727 * @param {Element} element The element to get the pixel border for.
    1728 * @param {string} prop The part of the property name.
    1729 * @return {number} The value in pixels.
    1730 * @private
    1731 */
    1732goog.style.getIePixelBorder_ = function(element, prop) {
    1733 if (goog.style.getCascadedStyle(element, prop + 'Style') == 'none') {
    1734 return 0;
    1735 }
    1736 var width = goog.style.getCascadedStyle(element, prop + 'Width');
    1737 if (width in goog.style.ieBorderWidthKeywords_) {
    1738 return goog.style.ieBorderWidthKeywords_[width];
    1739 }
    1740 return goog.style.getIePixelValue_(element, width, 'left', 'pixelLeft');
    1741};
    1742
    1743
    1744/**
    1745 * Gets the computed border widths (on all sides) in pixels
    1746 * @param {Element} element The element to get the border widths for.
    1747 * @return {!goog.math.Box} The computed border widths.
    1748 */
    1749goog.style.getBorderBox = function(element) {
    1750 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
    1751 var left = goog.style.getIePixelBorder_(element, 'borderLeft');
    1752 var right = goog.style.getIePixelBorder_(element, 'borderRight');
    1753 var top = goog.style.getIePixelBorder_(element, 'borderTop');
    1754 var bottom = goog.style.getIePixelBorder_(element, 'borderBottom');
    1755 return new goog.math.Box(top, right, bottom, left);
    1756 } else {
    1757 // On non-IE browsers, getComputedStyle is always non-null.
    1758 var left = /** @type {string} */ (
    1759 goog.style.getComputedStyle(element, 'borderLeftWidth'));
    1760 var right = /** @type {string} */ (
    1761 goog.style.getComputedStyle(element, 'borderRightWidth'));
    1762 var top = /** @type {string} */ (
    1763 goog.style.getComputedStyle(element, 'borderTopWidth'));
    1764 var bottom = /** @type {string} */ (
    1765 goog.style.getComputedStyle(element, 'borderBottomWidth'));
    1766
    1767 return new goog.math.Box(parseFloat(top),
    1768 parseFloat(right),
    1769 parseFloat(bottom),
    1770 parseFloat(left));
    1771 }
    1772};
    1773
    1774
    1775/**
    1776 * Returns the font face applied to a given node. Opera and IE should return
    1777 * the font actually displayed. Firefox returns the author's most-preferred
    1778 * font (whether the browser is capable of displaying it or not.)
    1779 * @param {Element} el The element whose font family is returned.
    1780 * @return {string} The font family applied to el.
    1781 */
    1782goog.style.getFontFamily = function(el) {
    1783 var doc = goog.dom.getOwnerDocument(el);
    1784 var font = '';
    1785 // The moveToElementText method from the TextRange only works if the element
    1786 // is attached to the owner document.
    1787 if (doc.body.createTextRange && goog.dom.contains(doc, el)) {
    1788 var range = doc.body.createTextRange();
    1789 range.moveToElementText(el);
    1790 /** @preserveTry */
    1791 try {
    1792 font = range.queryCommandValue('FontName');
    1793 } catch (e) {
    1794 // This is a workaround for a awkward exception.
    1795 // On some IE, there is an exception coming from it.
    1796 // The error description from this exception is:
    1797 // This window has already been registered as a drop target
    1798 // This is bogus description, likely due to a bug in ie.
    1799 font = '';
    1800 }
    1801 }
    1802 if (!font) {
    1803 // Note if for some reason IE can't derive FontName with a TextRange, we
    1804 // fallback to using currentStyle
    1805 font = goog.style.getStyle_(el, 'fontFamily');
    1806 }
    1807
    1808 // Firefox returns the applied font-family string (author's list of
    1809 // preferred fonts.) We want to return the most-preferred font, in lieu of
    1810 // the *actually* applied font.
    1811 var fontsArray = font.split(',');
    1812 if (fontsArray.length > 1) font = fontsArray[0];
    1813
    1814 // Sanitize for x-browser consistency:
    1815 // Strip quotes because browsers aren't consistent with how they're
    1816 // applied; Opera always encloses, Firefox sometimes, and IE never.
    1817 return goog.string.stripQuotes(font, '"\'');
    1818};
    1819
    1820
    1821/**
    1822 * Regular expression used for getLengthUnits.
    1823 * @type {RegExp}
    1824 * @private
    1825 */
    1826goog.style.lengthUnitRegex_ = /[^\d]+$/;
    1827
    1828
    1829/**
    1830 * Returns the units used for a CSS length measurement.
    1831 * @param {string} value A CSS length quantity.
    1832 * @return {?string} The units of measurement.
    1833 */
    1834goog.style.getLengthUnits = function(value) {
    1835 var units = value.match(goog.style.lengthUnitRegex_);
    1836 return units && units[0] || null;
    1837};
    1838
    1839
    1840/**
    1841 * Map of absolute CSS length units
    1842 * @type {Object}
    1843 * @private
    1844 */
    1845goog.style.ABSOLUTE_CSS_LENGTH_UNITS_ = {
    1846 'cm' : 1,
    1847 'in' : 1,
    1848 'mm' : 1,
    1849 'pc' : 1,
    1850 'pt' : 1
    1851};
    1852
    1853
    1854/**
    1855 * Map of relative CSS length units that can be accurately converted to px
    1856 * font-size values using getIePixelValue_. Only units that are defined in
    1857 * relation to a font size are convertible (%, small, etc. are not).
    1858 * @type {Object}
    1859 * @private
    1860 */
    1861goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_ = {
    1862 'em' : 1,
    1863 'ex' : 1
    1864};
    1865
    1866
    1867/**
    1868 * Returns the font size, in pixels, of text in an element.
    1869 * @param {Element} el The element whose font size is returned.
    1870 * @return {number} The font size (in pixels).
    1871 */
    1872goog.style.getFontSize = function(el) {
    1873 var fontSize = goog.style.getStyle_(el, 'fontSize');
    1874 var sizeUnits = goog.style.getLengthUnits(fontSize);
    1875 if (fontSize && 'px' == sizeUnits) {
    1876 // NOTE(user): This could be parseFloat instead, but IE doesn't return
    1877 // decimal fractions in getStyle_ and Firefox reports the fractions, but
    1878 // ignores them when rendering. Interestingly enough, when we force the
    1879 // issue and size something to e.g., 50% of 25px, the browsers round in
    1880 // opposite directions with Firefox reporting 12px and IE 13px. I punt.
    1881 return parseInt(fontSize, 10);
    1882 }
    1883
    1884 // In IE, we can convert absolute length units to a px value using
    1885 // goog.style.getIePixelValue_. Units defined in relation to a font size
    1886 // (em, ex) are applied relative to the element's parentNode and can also
    1887 // be converted.
    1888 if (goog.userAgent.IE) {
    1889 if (sizeUnits in goog.style.ABSOLUTE_CSS_LENGTH_UNITS_) {
    1890 return goog.style.getIePixelValue_(el,
    1891 fontSize,
    1892 'left',
    1893 'pixelLeft');
    1894 } else if (el.parentNode &&
    1895 el.parentNode.nodeType == goog.dom.NodeType.ELEMENT &&
    1896 sizeUnits in goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_) {
    1897 // Check the parent size - if it is the same it means the relative size
    1898 // value is inherited and we therefore don't want to count it twice. If
    1899 // it is different, this element either has explicit style or has a CSS
    1900 // rule applying to it.
    1901 var parentElement = /** @type {!Element} */ (el.parentNode);
    1902 var parentSize = goog.style.getStyle_(parentElement, 'fontSize');
    1903 return goog.style.getIePixelValue_(parentElement,
    1904 fontSize == parentSize ?
    1905 '1em' : fontSize,
    1906 'left',
    1907 'pixelLeft');
    1908 }
    1909 }
    1910
    1911 // Sometimes we can't cleanly find the font size (some units relative to a
    1912 // node's parent's font size are difficult: %, smaller et al), so we create
    1913 // an invisible, absolutely-positioned span sized to be the height of an 'M'
    1914 // rendered in its parent's (i.e., our target element's) font size. This is
    1915 // the definition of CSS's font size attribute.
    1916 var sizeElement = goog.dom.createDom(
    1917 goog.dom.TagName.SPAN,
    1918 {'style': 'visibility:hidden;position:absolute;' +
    1919 'line-height:0;padding:0;margin:0;border:0;height:1em;'});
    1920 goog.dom.appendChild(el, sizeElement);
    1921 fontSize = sizeElement.offsetHeight;
    1922 goog.dom.removeNode(sizeElement);
    1923
    1924 return fontSize;
    1925};
    1926
    1927
    1928/**
    1929 * Parses a style attribute value. Converts CSS property names to camel case.
    1930 * @param {string} value The style attribute value.
    1931 * @return {!Object} Map of CSS properties to string values.
    1932 */
    1933goog.style.parseStyleAttribute = function(value) {
    1934 var result = {};
    1935 goog.array.forEach(value.split(/\s*;\s*/), function(pair) {
    1936 var keyValue = pair.split(/\s*:\s*/);
    1937 if (keyValue.length == 2) {
    1938 result[goog.string.toCamelCase(keyValue[0].toLowerCase())] = keyValue[1];
    1939 }
    1940 });
    1941 return result;
    1942};
    1943
    1944
    1945/**
    1946 * Reverse of parseStyleAttribute; that is, takes a style object and returns the
    1947 * corresponding attribute value. Converts camel case property names to proper
    1948 * CSS selector names.
    1949 * @param {Object} obj Map of CSS properties to values.
    1950 * @return {string} The style attribute value.
    1951 */
    1952goog.style.toStyleAttribute = function(obj) {
    1953 var buffer = [];
    1954 goog.object.forEach(obj, function(value, key) {
    1955 buffer.push(goog.string.toSelectorCase(key), ':', value, ';');
    1956 });
    1957 return buffer.join('');
    1958};
    1959
    1960
    1961/**
    1962 * JavaScript name of the "float" css property.
    1963 * @private {string}
    1964 * @const
    1965 */
    1966goog.style.FLOAT_CSS_PROPERTY_NAME_ =
    1967 goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(12) ?
    1968 'styleFloat' : 'cssFloat';
    1969
    1970
    1971/**
    1972 * Sets CSS float property on an element.
    1973 * @param {Element} el The element to set float property on.
    1974 * @param {string} value The value of float CSS property to set on this element.
    1975 */
    1976goog.style.setFloat = function(el, value) {
    1977 el.style[goog.style.FLOAT_CSS_PROPERTY_NAME_] = value;
    1978};
    1979
    1980
    1981/**
    1982 * Gets value of explicitly-set float CSS property on an element.
    1983 * @param {Element} el The element to get float property of.
    1984 * @return {string} The value of explicitly-set float CSS property on this
    1985 * element.
    1986 */
    1987goog.style.getFloat = function(el) {
    1988 return el.style[goog.style.FLOAT_CSS_PROPERTY_NAME_] || '';
    1989};
    1990
    1991
    1992/**
    1993 * Returns the scroll bar width (represents the width of both horizontal
    1994 * and vertical scroll).
    1995 *
    1996 * @param {string=} opt_className An optional class name (or names) to apply
    1997 * to the invisible div created to measure the scrollbar. This is necessary
    1998 * if some scrollbars are styled differently than others.
    1999 * @return {number} The scroll bar width in px.
    2000 */
    2001goog.style.getScrollbarWidth = function(opt_className) {
    2002 // Add two hidden divs. The child div is larger than the parent and
    2003 // forces scrollbars to appear on it.
    2004 // Using overflow:scroll does not work consistently with scrollbars that
    2005 // are styled with ::-webkit-scrollbar.
    2006 var outerDiv = goog.dom.createElement(goog.dom.TagName.DIV);
    2007 if (opt_className) {
    2008 outerDiv.className = opt_className;
    2009 }
    2010 outerDiv.style.cssText = 'overflow:auto;' +
    2011 'position:absolute;top:0;width:100px;height:100px';
    2012 var innerDiv = goog.dom.createElement(goog.dom.TagName.DIV);
    2013 goog.style.setSize(innerDiv, '200px', '200px');
    2014 outerDiv.appendChild(innerDiv);
    2015 goog.dom.appendChild(goog.dom.getDocument().body, outerDiv);
    2016 var width = outerDiv.offsetWidth - outerDiv.clientWidth;
    2017 goog.dom.removeNode(outerDiv);
    2018 return width;
    2019};
    2020
    2021
    2022/**
    2023 * Regular expression to extract x and y translation components from a CSS
    2024 * transform Matrix representation.
    2025 *
    2026 * @type {!RegExp}
    2027 * @const
    2028 * @private
    2029 */
    2030goog.style.MATRIX_TRANSLATION_REGEX_ =
    2031 new RegExp('matrix\\([0-9\\.\\-]+, [0-9\\.\\-]+, ' +
    2032 '[0-9\\.\\-]+, [0-9\\.\\-]+, ' +
    2033 '([0-9\\.\\-]+)p?x?, ([0-9\\.\\-]+)p?x?\\)');
    2034
    2035
    2036/**
    2037 * Returns the x,y translation component of any CSS transforms applied to the
    2038 * element, in pixels.
    2039 *
    2040 * @param {!Element} element The element to get the translation of.
    2041 * @return {!goog.math.Coordinate} The CSS translation of the element in px.
    2042 */
    2043goog.style.getCssTranslation = function(element) {
    2044 var transform = goog.style.getComputedTransform(element);
    2045 if (!transform) {
    2046 return new goog.math.Coordinate(0, 0);
    2047 }
    2048 var matches = transform.match(goog.style.MATRIX_TRANSLATION_REGEX_);
    2049 if (!matches) {
    2050 return new goog.math.Coordinate(0, 0);
    2051 }
    2052 return new goog.math.Coordinate(parseFloat(matches[1]),
    2053 parseFloat(matches[2]));
    2054};
    \ 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 index 39df702..702a2de 100644 --- a/docs/source/lib/goog/testing/asserts.js.src.html +++ b/docs/source/lib/goog/testing/asserts.js.src.html @@ -1 +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');
    16
    17goog.require('goog.testing.stacktrace');
    18
    19// TODO(user): Copied from JsUnit with some small modifications, we should
    20// reimplement the asserters.
    21
    22
    23/**
    24 * @typedef {Array|NodeList|Arguments|{length: number}}
    25 */
    26goog.testing.asserts.ArrayLike;
    27
    28var DOUBLE_EQUALITY_PREDICATE = function(var1, var2) {
    29 return var1 == var2;
    30};
    31var JSUNIT_UNDEFINED_VALUE;
    32var TO_STRING_EQUALITY_PREDICATE = function(var1, var2) {
    33 return var1.toString() === var2.toString();
    34};
    35
    36var PRIMITIVE_EQUALITY_PREDICATES = {
    37 'String': DOUBLE_EQUALITY_PREDICATE,
    38 'Number': DOUBLE_EQUALITY_PREDICATE,
    39 'Boolean': DOUBLE_EQUALITY_PREDICATE,
    40 'Date': function(date1, date2) {
    41 return date1.getTime() == date2.getTime();
    42 },
    43 'RegExp': TO_STRING_EQUALITY_PREDICATE,
    44 'Function': TO_STRING_EQUALITY_PREDICATE
    45};
    46
    47
    48/**
    49 * Compares equality of two numbers, allowing them to differ up to a given
    50 * tolerance.
    51 * @param {number} var1 A number.
    52 * @param {number} var2 A number.
    53 * @param {number} tolerance the maximum allowed difference.
    54 * @return {boolean} Whether the two variables are sufficiently close.
    55 * @private
    56 */
    57goog.testing.asserts.numberRoughEqualityPredicate_ = function(
    58 var1, var2, tolerance) {
    59 return Math.abs(var1 - var2) <= tolerance;
    60};
    61
    62
    63/**
    64 * @type {Object.<string, function(*, *, number): boolean>}
    65 * @private
    66 */
    67goog.testing.asserts.primitiveRoughEqualityPredicates_ = {
    68 'Number': goog.testing.asserts.numberRoughEqualityPredicate_
    69};
    70
    71
    72var _trueTypeOf = function(something) {
    73 var result = typeof something;
    74 try {
    75 switch (result) {
    76 case 'string':
    77 break;
    78 case 'boolean':
    79 break;
    80 case 'number':
    81 break;
    82 case 'object':
    83 if (something == null) {
    84 result = 'null';
    85 break;
    86 }
    87 case 'function':
    88 switch (something.constructor) {
    89 case new String('').constructor:
    90 result = 'String';
    91 break;
    92 case new Boolean(true).constructor:
    93 result = 'Boolean';
    94 break;
    95 case new Number(0).constructor:
    96 result = 'Number';
    97 break;
    98 case new Array().constructor:
    99 result = 'Array';
    100 break;
    101 case new RegExp().constructor:
    102 result = 'RegExp';
    103 break;
    104 case new Date().constructor:
    105 result = 'Date';
    106 break;
    107 case Function:
    108 result = 'Function';
    109 break;
    110 default:
    111 var m = something.constructor.toString().match(
    112 /function\s*([^( ]+)\(/);
    113 if (m) {
    114 result = m[1];
    115 } else {
    116 break;
    117 }
    118 }
    119 break;
    120 }
    121 } catch (e) {
    122
    123 } finally {
    124 result = result.substr(0, 1).toUpperCase() + result.substr(1);
    125 }
    126 return result;
    127};
    128
    129var _displayStringForValue = function(aVar) {
    130 var result;
    131 try {
    132 result = '<' + String(aVar) + '>';
    133 } catch (ex) {
    134 result = '<toString failed: ' + ex.message + '>';
    135 // toString does not work on this object :-(
    136 }
    137 if (!(aVar === null || aVar === JSUNIT_UNDEFINED_VALUE)) {
    138 result += ' (' + _trueTypeOf(aVar) + ')';
    139 }
    140 return result;
    141};
    142
    143var fail = function(failureMessage) {
    144 goog.testing.asserts.raiseException('Call to fail()', failureMessage);
    145};
    146
    147var argumentsIncludeComments = function(expectedNumberOfNonCommentArgs, args) {
    148 return args.length == expectedNumberOfNonCommentArgs + 1;
    149};
    150
    151var commentArg = function(expectedNumberOfNonCommentArgs, args) {
    152 if (argumentsIncludeComments(expectedNumberOfNonCommentArgs, args)) {
    153 return args[0];
    154 }
    155
    156 return null;
    157};
    158
    159var nonCommentArg = function(desiredNonCommentArgIndex,
    160 expectedNumberOfNonCommentArgs, args) {
    161 return argumentsIncludeComments(expectedNumberOfNonCommentArgs, args) ?
    162 args[desiredNonCommentArgIndex] :
    163 args[desiredNonCommentArgIndex - 1];
    164};
    165
    166var _validateArguments = function(expectedNumberOfNonCommentArgs, args) {
    167 var valid = args.length == expectedNumberOfNonCommentArgs ||
    168 args.length == expectedNumberOfNonCommentArgs + 1 &&
    169 goog.isString(args[0]);
    170 _assert(null, valid, 'Incorrect arguments passed to assert function');
    171};
    172
    173var _assert = function(comment, booleanValue, failureMessage) {
    174 if (!booleanValue) {
    175 goog.testing.asserts.raiseException(comment, failureMessage);
    176 }
    177};
    178
    179
    180/**
    181 * @param {*} expected The expected value.
    182 * @param {*} actual The actual value.
    183 * @return {string} A failure message of the values don't match.
    184 * @private
    185 */
    186goog.testing.asserts.getDefaultErrorMsg_ = function(expected, actual) {
    187 var msg = 'Expected ' + _displayStringForValue(expected) + ' but was ' +
    188 _displayStringForValue(actual);
    189 if ((typeof expected == 'string') && (typeof actual == 'string')) {
    190 // Try to find a human-readable difference.
    191 var limit = Math.min(expected.length, actual.length);
    192 var commonPrefix = 0;
    193 while (commonPrefix < limit &&
    194 expected.charAt(commonPrefix) == actual.charAt(commonPrefix)) {
    195 commonPrefix++;
    196 }
    197
    198 var commonSuffix = 0;
    199 while (commonSuffix < limit &&
    200 expected.charAt(expected.length - commonSuffix - 1) ==
    201 actual.charAt(actual.length - commonSuffix - 1)) {
    202 commonSuffix++;
    203 }
    204
    205 if (commonPrefix + commonSuffix > limit) {
    206 commonSuffix = 0;
    207 }
    208
    209 if (commonPrefix > 2 || commonSuffix > 2) {
    210 var printString = function(str) {
    211 var startIndex = Math.max(0, commonPrefix - 2);
    212 var endIndex = Math.min(str.length, str.length - (commonSuffix - 2));
    213 return (startIndex > 0 ? '...' : '') +
    214 str.substring(startIndex, endIndex) +
    215 (endIndex < str.length ? '...' : '');
    216 };
    217
    218 msg += '\nDifference was at position ' + commonPrefix +
    219 '. Expected [' + printString(expected) +
    220 '] vs. actual [' + printString(actual) + ']';
    221 }
    222 }
    223 return msg;
    224};
    225
    226
    227/**
    228 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    229 * @param {*=} opt_b The value to assert (2 args only).
    230 */
    231var assert = function(a, opt_b) {
    232 _validateArguments(1, arguments);
    233 var comment = commentArg(1, arguments);
    234 var booleanValue = nonCommentArg(1, 1, arguments);
    235
    236 _assert(comment, goog.isBoolean(booleanValue),
    237 'Bad argument to assert(boolean)');
    238 _assert(comment, booleanValue, 'Call to assert(boolean) with false');
    239};
    240
    241
    242/**
    243 * Asserts that the function throws an error.
    244 *
    245 * @param {!(string|Function)} a The assertion comment or the function to call.
    246 * @param {!Function=} opt_b The function to call (if the first argument of
    247 * {@code assertThrows} was the comment).
    248 * @return {*} The error thrown by the function.
    249 * @throws {goog.testing.JsUnitException} If the assertion failed.
    250 */
    251var assertThrows = function(a, opt_b) {
    252 _validateArguments(1, arguments);
    253 var func = nonCommentArg(1, 1, arguments);
    254 var comment = commentArg(1, arguments);
    255 _assert(comment, typeof func == 'function',
    256 'Argument passed to assertThrows is not a function');
    257
    258 try {
    259 func();
    260 } catch (e) {
    261 if (e && goog.isString(e['stacktrace']) && goog.isString(e['message'])) {
    262 // Remove the stack trace appended to the error message by Opera 10.0
    263 var startIndex = e['message'].length - e['stacktrace'].length;
    264 if (e['message'].indexOf(e['stacktrace'], startIndex) == startIndex) {
    265 e['message'] = e['message'].substr(0, startIndex - 14);
    266 }
    267 }
    268 return e;
    269 }
    270 goog.testing.asserts.raiseException(comment,
    271 'No exception thrown from function passed to assertThrows');
    272};
    273
    274
    275/**
    276 * Asserts that the function does not throw an error.
    277 *
    278 * @param {!(string|Function)} a The assertion comment or the function to call.
    279 * @param {!Function=} opt_b The function to call (if the first argument of
    280 * {@code assertNotThrows} was the comment).
    281 * @return {*} The return value of the function.
    282 * @throws {goog.testing.JsUnitException} If the assertion failed.
    283 */
    284var assertNotThrows = function(a, opt_b) {
    285 _validateArguments(1, arguments);
    286 var comment = commentArg(1, arguments);
    287 var func = nonCommentArg(1, 1, arguments);
    288 _assert(comment, typeof func == 'function',
    289 'Argument passed to assertNotThrows is not a function');
    290
    291 try {
    292 return func();
    293 } catch (e) {
    294 comment = comment ? (comment + '\n') : '';
    295 comment += 'A non expected exception was thrown from function passed to ' +
    296 'assertNotThrows';
    297 // Some browsers don't have a stack trace so at least have the error
    298 // description.
    299 var stackTrace = e['stack'] || e['stacktrace'] || e.toString();
    300 goog.testing.asserts.raiseException(comment, stackTrace);
    301 }
    302};
    303
    304
    305/**
    306 * Asserts that the given callback function results in a JsUnitException when
    307 * called, and that the resulting failure message matches the given expected
    308 * message.
    309 * @param {function() : void} callback Function to be run expected to result
    310 * in a JsUnitException (usually contains a call to an assert).
    311 * @param {string=} opt_expectedMessage Failure message expected to be given
    312 * with the exception.
    313 */
    314var assertThrowsJsUnitException = function(callback, opt_expectedMessage) {
    315 var failed = false;
    316 try {
    317 goog.testing.asserts.callWithoutLogging(callback);
    318 } catch (ex) {
    319 if (!ex.isJsUnitException) {
    320 fail('Expected a JsUnitException');
    321 }
    322 if (typeof opt_expectedMessage != 'undefined' &&
    323 ex.message != opt_expectedMessage) {
    324 fail('Expected message [' + opt_expectedMessage + '] but got [' +
    325 ex.message + ']');
    326 }
    327 failed = true;
    328 }
    329 if (!failed) {
    330 fail('Expected a failure: ' + opt_expectedMessage);
    331 }
    332};
    333
    334
    335/**
    336 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    337 * @param {*=} opt_b The value to assert (2 args only).
    338 */
    339var assertTrue = function(a, opt_b) {
    340 _validateArguments(1, arguments);
    341 var comment = commentArg(1, arguments);
    342 var booleanValue = nonCommentArg(1, 1, arguments);
    343
    344 _assert(comment, goog.isBoolean(booleanValue),
    345 'Bad argument to assertTrue(boolean)');
    346 _assert(comment, booleanValue, 'Call to assertTrue(boolean) with false');
    347};
    348
    349
    350/**
    351 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    352 * @param {*=} opt_b The value to assert (2 args only).
    353 */
    354var assertFalse = function(a, opt_b) {
    355 _validateArguments(1, arguments);
    356 var comment = commentArg(1, arguments);
    357 var booleanValue = nonCommentArg(1, 1, arguments);
    358
    359 _assert(comment, goog.isBoolean(booleanValue),
    360 'Bad argument to assertFalse(boolean)');
    361 _assert(comment, !booleanValue, 'Call to assertFalse(boolean) with true');
    362};
    363
    364
    365/**
    366 * @param {*} a The expected value (2 args) or the debug message (3 args).
    367 * @param {*} b The actual value (2 args) or the expected value (3 args).
    368 * @param {*=} opt_c The actual value (3 args only).
    369 */
    370var assertEquals = function(a, b, opt_c) {
    371 _validateArguments(2, arguments);
    372 var var1 = nonCommentArg(1, 2, arguments);
    373 var var2 = nonCommentArg(2, 2, arguments);
    374 _assert(commentArg(2, arguments), var1 === var2,
    375 goog.testing.asserts.getDefaultErrorMsg_(var1, var2));
    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 assertNotEquals = 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 'Expected not to be ' + _displayStringForValue(var2));
    390};
    391
    392
    393/**
    394 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    395 * @param {*=} opt_b The value to assert (2 args only).
    396 */
    397var assertNull = function(a, opt_b) {
    398 _validateArguments(1, arguments);
    399 var aVar = nonCommentArg(1, 1, arguments);
    400 _assert(commentArg(1, arguments), aVar === null,
    401 goog.testing.asserts.getDefaultErrorMsg_(null, aVar));
    402};
    403
    404
    405/**
    406 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    407 * @param {*=} opt_b The value to assert (2 args only).
    408 */
    409var assertNotNull = function(a, opt_b) {
    410 _validateArguments(1, arguments);
    411 var aVar = nonCommentArg(1, 1, arguments);
    412 _assert(commentArg(1, arguments), aVar !== null,
    413 'Expected not to be ' + _displayStringForValue(null));
    414};
    415
    416
    417/**
    418 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    419 * @param {*=} opt_b The value to assert (2 args only).
    420 */
    421var assertUndefined = function(a, opt_b) {
    422 _validateArguments(1, arguments);
    423 var aVar = nonCommentArg(1, 1, arguments);
    424 _assert(commentArg(1, arguments), aVar === JSUNIT_UNDEFINED_VALUE,
    425 goog.testing.asserts.getDefaultErrorMsg_(JSUNIT_UNDEFINED_VALUE, aVar));
    426};
    427
    428
    429/**
    430 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    431 * @param {*=} opt_b The value to assert (2 args only).
    432 */
    433var assertNotUndefined = function(a, opt_b) {
    434 _validateArguments(1, arguments);
    435 var aVar = nonCommentArg(1, 1, arguments);
    436 _assert(commentArg(1, arguments), aVar !== JSUNIT_UNDEFINED_VALUE,
    437 'Expected not to be ' + _displayStringForValue(JSUNIT_UNDEFINED_VALUE));
    438};
    439
    440
    441/**
    442 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    443 * @param {*=} opt_b The value to assert (2 args only).
    444 */
    445var assertNotNullNorUndefined = function(a, opt_b) {
    446 _validateArguments(1, arguments);
    447 assertNotNull.apply(null, arguments);
    448 assertNotUndefined.apply(null, arguments);
    449};
    450
    451
    452/**
    453 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    454 * @param {*=} opt_b The value to assert (2 args only).
    455 */
    456var assertNonEmptyString = function(a, opt_b) {
    457 _validateArguments(1, arguments);
    458 var aVar = nonCommentArg(1, 1, arguments);
    459 _assert(commentArg(1, arguments),
    460 aVar !== JSUNIT_UNDEFINED_VALUE && aVar !== null &&
    461 typeof aVar == 'string' && aVar !== '',
    462 'Expected non-empty string but was ' + _displayStringForValue(aVar));
    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 assertNaN = function(a, opt_b) {
    471 _validateArguments(1, arguments);
    472 var aVar = nonCommentArg(1, 1, arguments);
    473 _assert(commentArg(1, arguments), isNaN(aVar), 'Expected NaN');
    474};
    475
    476
    477/**
    478 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    479 * @param {*=} opt_b The value to assert (2 args only).
    480 */
    481var assertNotNaN = function(a, opt_b) {
    482 _validateArguments(1, arguments);
    483 var aVar = nonCommentArg(1, 1, arguments);
    484 _assert(commentArg(1, arguments), !isNaN(aVar), 'Expected not NaN');
    485};
    486
    487
    488/**
    489 * Runs a function in an environment where test failures are not logged. This is
    490 * useful for testing test code, where failures can be a normal part of a test.
    491 * @param {function() : void} fn Function to run without logging failures.
    492 */
    493goog.testing.asserts.callWithoutLogging = function(fn) {
    494 var testRunner = goog.global['G_testRunner'];
    495 var oldLogTestFailure = testRunner['logTestFailure'];
    496 try {
    497 // Any failures in the callback shouldn't be recorded.
    498 testRunner['logTestFailure'] = undefined;
    499 fn();
    500 } finally {
    501 testRunner['logTestFailure'] = oldLogTestFailure;
    502 }
    503};
    504
    505
    506/**
    507 * The return value of the equality predicate passed to findDifferences below,
    508 * in cases where the predicate can't test the input variables for equality.
    509 * @type {?string}
    510 */
    511goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS = null;
    512
    513
    514/**
    515 * The return value of the equality predicate passed to findDifferences below,
    516 * in cases where the input vriables are equal.
    517 * @type {?string}
    518 */
    519goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL = '';
    520
    521
    522/**
    523 * Determines if two items of any type match, and formulates an error message
    524 * if not.
    525 * @param {*} expected Expected argument to match.
    526 * @param {*} actual Argument as a result of performing the test.
    527 * @param {(function(string, *, *): ?string)=} opt_equalityPredicate An optional
    528 * function that can be used to check equality of variables. It accepts 3
    529 * arguments: type-of-variables, var1, var2 (in that order) and returns an
    530 * error message if the variables are not equal,
    531 * goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL if the variables
    532 * are equal, or
    533 * goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS if the predicate
    534 * couldn't check the input variables. The function will be called only if
    535 * the types of var1 and var2 are identical.
    536 * @return {?string} Null on success, error message on failure.
    537 */
    538goog.testing.asserts.findDifferences = function(expected, actual,
    539 opt_equalityPredicate) {
    540 var failures = [];
    541 var seen1 = [];
    542 var seen2 = [];
    543
    544 // To avoid infinite recursion when the two parameters are self-referential
    545 // along the same path of properties, keep track of the object pairs already
    546 // seen in this call subtree, and abort when a cycle is detected.
    547 function innerAssert(var1, var2, path) {
    548 // This is used for testing, so we can afford to be slow (but more
    549 // accurate). So we just check whether var1 is in seen1. If we
    550 // found var1 in index i, we simply need to check whether var2 is
    551 // in seen2[i]. If it is, we do not recurse to check var1/var2. If
    552 // it isn't, we know that the structures of the two objects must be
    553 // different.
    554 //
    555 // This is based on the fact that values at index i in seen1 and
    556 // seen2 will be checked for equality eventually (when
    557 // innerAssert_(seen1[i], seen2[i], path) finishes).
    558 for (var i = 0; i < seen1.length; ++i) {
    559 var match1 = seen1[i] === var1;
    560 var match2 = seen2[i] === var2;
    561 if (match1 || match2) {
    562 if (!match1 || !match2) {
    563 // Asymmetric cycles, so the objects have different structure.
    564 failures.push('Asymmetric cycle detected at ' + path);
    565 }
    566 return;
    567 }
    568 }
    569
    570 seen1.push(var1);
    571 seen2.push(var2);
    572 innerAssert_(var1, var2, path);
    573 seen1.pop();
    574 seen2.pop();
    575 }
    576
    577 var equalityPredicate = opt_equalityPredicate || function(type, var1, var2) {
    578 var typedPredicate = PRIMITIVE_EQUALITY_PREDICATES[type];
    579 if (!typedPredicate) {
    580 return goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS;
    581 }
    582 var equal = typedPredicate(var1, var2);
    583 return equal ? goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL :
    584 goog.testing.asserts.getDefaultErrorMsg_(var1, var2);
    585 };
    586
    587 /**
    588 * @param {*} var1 An item in the expected object.
    589 * @param {*} var2 The corresponding item in the actual object.
    590 * @param {string} path Their path in the objects.
    591 * @suppress {missingProperties} The map_ property is unknown to the compiler
    592 * unless goog.structs.Map is loaded.
    593 */
    594 function innerAssert_(var1, var2, path) {
    595 if (var1 === var2) {
    596 return;
    597 }
    598
    599 var typeOfVar1 = _trueTypeOf(var1);
    600 var typeOfVar2 = _trueTypeOf(var2);
    601
    602 if (typeOfVar1 == typeOfVar2) {
    603 var isArray = typeOfVar1 == 'Array';
    604 var errorMessage = equalityPredicate(typeOfVar1, var1, var2);
    605 if (errorMessage !=
    606 goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS) {
    607 if (errorMessage !=
    608 goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL) {
    609 failures.push(path + ': ' + errorMessage);
    610 }
    611 } else if (isArray && var1.length != var2.length) {
    612 failures.push(path + ': Expected ' + var1.length + '-element array ' +
    613 'but got a ' + var2.length + '-element array');
    614 } else {
    615 var childPath = path + (isArray ? '[%s]' : (path ? '.%s' : '%s'));
    616
    617 // if an object has an __iterator__ property, we have no way of
    618 // actually inspecting its raw properties, and JS 1.7 doesn't
    619 // overload [] to make it possible for someone to generically
    620 // use what the iterator returns to compare the object-managed
    621 // properties. This gets us into deep poo with things like
    622 // goog.structs.Map, at least on systems that support iteration.
    623 if (!var1['__iterator__']) {
    624 for (var prop in var1) {
    625 if (isArray && goog.testing.asserts.isArrayIndexProp_(prop)) {
    626 // Skip array indices for now. We'll handle them later.
    627 continue;
    628 }
    629
    630 if (prop in var2) {
    631 innerAssert(var1[prop], var2[prop],
    632 childPath.replace('%s', prop));
    633 } else {
    634 failures.push('property ' + prop +
    635 ' not present in actual ' + (path || typeOfVar2));
    636 }
    637 }
    638 // make sure there aren't properties in var2 that are missing
    639 // from var1. if there are, then by definition they don't
    640 // match.
    641 for (var prop in var2) {
    642 if (isArray && goog.testing.asserts.isArrayIndexProp_(prop)) {
    643 // Skip array indices for now. We'll handle them later.
    644 continue;
    645 }
    646
    647 if (!(prop in var1)) {
    648 failures.push('property ' + prop +
    649 ' not present in expected ' +
    650 (path || typeOfVar1));
    651 }
    652 }
    653
    654 // Handle array indices by iterating from 0 to arr.length.
    655 //
    656 // Although all browsers allow holes in arrays, browsers
    657 // are inconsistent in what they consider a hole. For example,
    658 // "[0,undefined,2]" has a hole on IE but not on Firefox.
    659 //
    660 // Because our style guide bans for...in iteration over arrays,
    661 // we assume that most users don't care about holes in arrays,
    662 // and that it is ok to say that a hole is equivalent to a slot
    663 // populated with 'undefined'.
    664 if (isArray) {
    665 for (prop = 0; prop < var1.length; prop++) {
    666 innerAssert(var1[prop], var2[prop],
    667 childPath.replace('%s', String(prop)));
    668 }
    669 }
    670 } else {
    671 // special-case for closure objects that have iterators
    672 if (goog.isFunction(var1.equals)) {
    673 // use the object's own equals function, assuming it accepts an
    674 // object and returns a boolean
    675 if (!var1.equals(var2)) {
    676 failures.push('equals() returned false for ' +
    677 (path || typeOfVar1));
    678 }
    679 } else if (var1.map_) {
    680 // assume goog.structs.Map or goog.structs.Set, where comparing
    681 // their private map_ field is sufficient
    682 innerAssert(var1.map_, var2.map_, childPath.replace('%s', 'map_'));
    683 } else {
    684 // else die, so user knows we can't do anything
    685 failures.push('unable to check ' + (path || typeOfVar1) +
    686 ' for equality: it has an iterator we do not ' +
    687 'know how to handle. please add an equals method');
    688 }
    689 }
    690 }
    691 } else {
    692 failures.push(path + ' ' +
    693 goog.testing.asserts.getDefaultErrorMsg_(var1, var2));
    694 }
    695 }
    696
    697 innerAssert(expected, actual, '');
    698 return failures.length == 0 ? null :
    699 goog.testing.asserts.getDefaultErrorMsg_(expected, actual) +
    700 '\n ' + failures.join('\n ');
    701};
    702
    703
    704/**
    705 * Notes:
    706 * Object equality has some nasty browser quirks, and this implementation is
    707 * not 100% correct. For example,
    708 *
    709 * <code>
    710 * var a = [0, 1, 2];
    711 * var b = [0, 1, 2];
    712 * delete a[1];
    713 * b[1] = undefined;
    714 * assertObjectEquals(a, b); // should fail, but currently passes
    715 * </code>
    716 *
    717 * See asserts_test.html for more interesting edge cases.
    718 *
    719 * The first comparison object provided is the expected value, the second is
    720 * the actual.
    721 *
    722 * @param {*} a Assertion message or comparison object.
    723 * @param {*} b Comparison object.
    724 * @param {*=} opt_c Comparison object, if an assertion message was provided.
    725 */
    726var assertObjectEquals = function(a, b, opt_c) {
    727 _validateArguments(2, arguments);
    728 var v1 = nonCommentArg(1, 2, arguments);
    729 var v2 = nonCommentArg(2, 2, arguments);
    730 var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
    731 var differences = goog.testing.asserts.findDifferences(v1, v2);
    732
    733 _assert(failureMessage, !differences, differences);
    734};
    735
    736
    737/**
    738 * Similar to assertObjectEquals above, but accepts a tolerance margin.
    739 *
    740 * @param {*} a Assertion message or comparison object.
    741 * @param {*} b Comparison object.
    742 * @param {*} c Comparison object or tolerance.
    743 * @param {*=} opt_d Tolerance, if an assertion message was provided.
    744 */
    745var assertObjectRoughlyEquals = function(a, b, c, opt_d) {
    746 _validateArguments(3, arguments);
    747 var v1 = nonCommentArg(1, 3, arguments);
    748 var v2 = nonCommentArg(2, 3, arguments);
    749 var tolerance = nonCommentArg(3, 3, arguments);
    750 var failureMessage = commentArg(3, arguments) ? commentArg(3, arguments) : '';
    751 var equalityPredicate = function(type, var1, var2) {
    752 var typedPredicate =
    753 goog.testing.asserts.primitiveRoughEqualityPredicates_[type];
    754 if (!typedPredicate) {
    755 return goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS;
    756 }
    757 var equal = typedPredicate(var1, var2, tolerance);
    758 return equal ? goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL :
    759 goog.testing.asserts.getDefaultErrorMsg_(var1, var2) +
    760 ' which was more than ' + tolerance + ' away';
    761 };
    762 var differences = goog.testing.asserts.findDifferences(
    763 v1, v2, equalityPredicate);
    764
    765 _assert(failureMessage, !differences, differences);
    766};
    767
    768
    769/**
    770 * Compares two arbitrary objects for non-equalness.
    771 *
    772 * All the same caveats as for assertObjectEquals apply here:
    773 * Undefined values may be confused for missing values, or vice versa.
    774 *
    775 * @param {*} a Assertion message or comparison object.
    776 * @param {*} b Comparison object.
    777 * @param {*=} opt_c Comparison object, if an assertion message was provided.
    778 */
    779var assertObjectNotEquals = function(a, b, opt_c) {
    780 _validateArguments(2, arguments);
    781 var v1 = nonCommentArg(1, 2, arguments);
    782 var v2 = nonCommentArg(2, 2, arguments);
    783 var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
    784 var differences = goog.testing.asserts.findDifferences(v1, v2);
    785
    786 _assert(failureMessage, differences, 'Objects should not be equal');
    787};
    788
    789
    790/**
    791 * Compares two arrays ignoring negative indexes and extra properties on the
    792 * array objects. Use case: Internet Explorer adds the index, lastIndex and
    793 * input enumerable fields to the result of string.match(/regexp/g), which makes
    794 * assertObjectEquals fail.
    795 * @param {*} a The expected array (2 args) or the debug message (3 args).
    796 * @param {*} b The actual array (2 args) or the expected array (3 args).
    797 * @param {*=} opt_c The actual array (3 args only).
    798 */
    799var assertArrayEquals = function(a, b, opt_c) {
    800 _validateArguments(2, arguments);
    801 var v1 = nonCommentArg(1, 2, arguments);
    802 var v2 = nonCommentArg(2, 2, arguments);
    803 var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
    804
    805 var typeOfVar1 = _trueTypeOf(v1);
    806 _assert(failureMessage,
    807 typeOfVar1 == 'Array',
    808 'Expected an array for assertArrayEquals but found a ' + typeOfVar1);
    809
    810 var typeOfVar2 = _trueTypeOf(v2);
    811 _assert(failureMessage,
    812 typeOfVar2 == 'Array',
    813 'Expected an array for assertArrayEquals but found a ' + typeOfVar2);
    814
    815 assertObjectEquals(failureMessage,
    816 Array.prototype.concat.call(v1), Array.prototype.concat.call(v2));
    817};
    818
    819
    820/**
    821 * Compares two objects that can be accessed like an array and assert that
    822 * each element is equal.
    823 * @param {string|Object} a Failure message (3 arguments)
    824 * or object #1 (2 arguments).
    825 * @param {Object} b Object #1 (2 arguments) or object #2 (3 arguments).
    826 * @param {Object=} opt_c Object #2 (3 arguments).
    827 */
    828var assertElementsEquals = function(a, b, opt_c) {
    829 _validateArguments(2, arguments);
    830
    831 var v1 = nonCommentArg(1, 2, arguments);
    832 var v2 = nonCommentArg(2, 2, arguments);
    833 var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
    834
    835 if (!v1) {
    836 assert(failureMessage, !v2);
    837 } else {
    838 assertEquals('length mismatch: ' + failureMessage, v1.length, v2.length);
    839 for (var i = 0; i < v1.length; ++i) {
    840 assertEquals(
    841 'mismatch at index ' + i + ': ' + failureMessage, v1[i], v2[i]);
    842 }
    843 }
    844};
    845
    846
    847/**
    848 * Compares two objects that can be accessed like an array and assert that
    849 * each element is roughly equal.
    850 * @param {string|Object} a Failure message (4 arguments)
    851 * or object #1 (3 arguments).
    852 * @param {Object} b Object #1 (4 arguments) or object #2 (3 arguments).
    853 * @param {Object|number} c Object #2 (4 arguments) or tolerance (3 arguments).
    854 * @param {number=} opt_d tolerance (4 arguments).
    855 */
    856var assertElementsRoughlyEqual = function(a, b, c, opt_d) {
    857 _validateArguments(3, arguments);
    858
    859 var v1 = nonCommentArg(1, 3, arguments);
    860 var v2 = nonCommentArg(2, 3, arguments);
    861 var tolerance = nonCommentArg(3, 3, arguments);
    862 var failureMessage = commentArg(3, arguments) ? commentArg(3, arguments) : '';
    863
    864 if (!v1) {
    865 assert(failureMessage, !v2);
    866 } else {
    867 assertEquals('length mismatch: ' + failureMessage, v1.length, v2.length);
    868 for (var i = 0; i < v1.length; ++i) {
    869 assertRoughlyEquals(failureMessage, v1[i], v2[i], tolerance);
    870 }
    871 }
    872};
    873
    874
    875/**
    876 * Compares two array-like objects without taking their order into account.
    877 * @param {string|goog.testing.asserts.ArrayLike} a Assertion message or the
    878 * expected elements.
    879 * @param {goog.testing.asserts.ArrayLike} b Expected elements or the actual
    880 * elements.
    881 * @param {goog.testing.asserts.ArrayLike=} opt_c Actual elements.
    882 */
    883var assertSameElements = function(a, b, opt_c) {
    884 _validateArguments(2, arguments);
    885 var expected = nonCommentArg(1, 2, arguments);
    886 var actual = nonCommentArg(2, 2, arguments);
    887 var message = commentArg(2, arguments);
    888
    889 assertTrue('Bad arguments to assertSameElements(opt_message, expected: ' +
    890 'ArrayLike, actual: ArrayLike)',
    891 goog.isArrayLike(expected) && goog.isArrayLike(actual));
    892
    893 // Clones expected and actual and converts them to real arrays.
    894 expected = goog.testing.asserts.toArray_(expected);
    895 actual = goog.testing.asserts.toArray_(actual);
    896 // TODO(user): It would be great to show only the difference
    897 // between the expected and actual elements.
    898 _assert(message, expected.length == actual.length,
    899 'Expected ' + expected.length + ' elements: [' + expected + '], ' +
    900 'got ' + actual.length + ' elements: [' + actual + ']');
    901
    902 var toFind = goog.testing.asserts.toArray_(expected);
    903 for (var i = 0; i < actual.length; i++) {
    904 var index = goog.testing.asserts.indexOf_(toFind, actual[i]);
    905 _assert(message, index != -1, 'Expected [' + expected + '], got [' +
    906 actual + ']');
    907 toFind.splice(index, 1);
    908 }
    909};
    910
    911
    912/**
    913 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    914 * @param {*=} opt_b The value to assert (2 args only).
    915 */
    916var assertEvaluatesToTrue = function(a, opt_b) {
    917 _validateArguments(1, arguments);
    918 var value = nonCommentArg(1, 1, arguments);
    919 if (!value) {
    920 _assert(commentArg(1, arguments), false, 'Expected to evaluate to true');
    921 }
    922};
    923
    924
    925/**
    926 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    927 * @param {*=} opt_b The value to assert (2 args only).
    928 */
    929var assertEvaluatesToFalse = function(a, opt_b) {
    930 _validateArguments(1, arguments);
    931 var value = nonCommentArg(1, 1, arguments);
    932 if (value) {
    933 _assert(commentArg(1, arguments), false, 'Expected to evaluate to false');
    934 }
    935};
    936
    937
    938/**
    939 * Compares two HTML snippets.
    940 *
    941 * Take extra care if attributes are involved. {@code assertHTMLEquals}'s
    942 * implementation isn't prepared for complex cases. For example, the following
    943 * comparisons erroneously fail:
    944 * <pre>
    945 * assertHTMLEquals('<a href="x" target="y">', '<a target="y" href="x">');
    946 * assertHTMLEquals('<div classname="a b">', '<div classname="b a">');
    947 * assertHTMLEquals('<input disabled>', '<input disabled="disabled">');
    948 * </pre>
    949 *
    950 * When in doubt, use {@code goog.testing.dom.assertHtmlMatches}.
    951 *
    952 * @param {*} a The expected value (2 args) or the debug message (3 args).
    953 * @param {*} b The actual value (2 args) or the expected value (3 args).
    954 * @param {*=} opt_c The actual value (3 args only).
    955 */
    956var assertHTMLEquals = function(a, b, opt_c) {
    957 _validateArguments(2, arguments);
    958 var var1 = nonCommentArg(1, 2, arguments);
    959 var var2 = nonCommentArg(2, 2, arguments);
    960 var var1Standardized = standardizeHTML(var1);
    961 var var2Standardized = standardizeHTML(var2);
    962
    963 _assert(commentArg(2, arguments), var1Standardized === var2Standardized,
    964 goog.testing.asserts.getDefaultErrorMsg_(
    965 var1Standardized, var2Standardized));
    966};
    967
    968
    969/**
    970 * Compares two CSS property values to make sure that they represent the same
    971 * things. This will normalize values in the browser. For example, in Firefox,
    972 * this assertion will consider "rgb(0, 0, 255)" and "#0000ff" to be identical
    973 * values for the "color" property. This function won't normalize everything --
    974 * for example, in most browsers, "blue" will not match "#0000ff". It is
    975 * intended only to compensate for unexpected normalizations performed by
    976 * the browser that should also affect your expected value.
    977 * @param {string} a Assertion message, or the CSS property name.
    978 * @param {string} b CSS property name, or the expected value.
    979 * @param {string} c The expected value, or the actual value.
    980 * @param {string=} opt_d The actual value.
    981 */
    982var assertCSSValueEquals = function(a, b, c, opt_d) {
    983 _validateArguments(3, arguments);
    984 var propertyName = nonCommentArg(1, 3, arguments);
    985 var expectedValue = nonCommentArg(2, 3, arguments);
    986 var actualValue = nonCommentArg(3, 3, arguments);
    987 var expectedValueStandardized =
    988 standardizeCSSValue(propertyName, expectedValue);
    989 var actualValueStandardized =
    990 standardizeCSSValue(propertyName, actualValue);
    991
    992 _assert(commentArg(3, arguments),
    993 expectedValueStandardized == actualValueStandardized,
    994 goog.testing.asserts.getDefaultErrorMsg_(
    995 expectedValueStandardized, actualValueStandardized));
    996};
    997
    998
    999/**
    1000 * @param {*} a The expected value (2 args) or the debug message (3 args).
    1001 * @param {*} b The actual value (2 args) or the expected value (3 args).
    1002 * @param {*=} opt_c The actual value (3 args only).
    1003 */
    1004var assertHashEquals = function(a, b, opt_c) {
    1005 _validateArguments(2, arguments);
    1006 var var1 = nonCommentArg(1, 2, arguments);
    1007 var var2 = nonCommentArg(2, 2, arguments);
    1008 var message = commentArg(2, arguments);
    1009 for (var key in var1) {
    1010 _assert(message,
    1011 key in var2, 'Expected hash had key ' + key + ' that was not found');
    1012 _assert(message, var1[key] == var2[key], 'Value for key ' + key +
    1013 ' mismatch - expected = ' + var1[key] + ', actual = ' + var2[key]);
    1014 }
    1015
    1016 for (var key in var2) {
    1017 _assert(message, key in var1, 'Actual hash had key ' + key +
    1018 ' that was not expected');
    1019 }
    1020};
    1021
    1022
    1023/**
    1024 * @param {*} a The expected value (3 args) or the debug message (4 args).
    1025 * @param {*} b The actual value (3 args) or the expected value (4 args).
    1026 * @param {*} c The tolerance (3 args) or the actual value (4 args).
    1027 * @param {*=} opt_d The tolerance (4 args only).
    1028 */
    1029var assertRoughlyEquals = function(a, b, c, opt_d) {
    1030 _validateArguments(3, arguments);
    1031 var expected = nonCommentArg(1, 3, arguments);
    1032 var actual = nonCommentArg(2, 3, arguments);
    1033 var tolerance = nonCommentArg(3, 3, arguments);
    1034 _assert(commentArg(3, arguments),
    1035 goog.testing.asserts.numberRoughEqualityPredicate_(
    1036 expected, actual, tolerance),
    1037 'Expected ' + expected + ', but got ' + actual +
    1038 ' which was more than ' + tolerance + ' away');
    1039};
    1040
    1041
    1042/**
    1043 * Checks if the given element is the member of the given container.
    1044 * @param {*} a Failure message (3 arguments) or the contained element
    1045 * (2 arguments).
    1046 * @param {*} b The contained element (3 arguments) or the container
    1047 * (2 arguments).
    1048 * @param {*=} opt_c The container.
    1049 */
    1050var assertContains = function(a, b, opt_c) {
    1051 _validateArguments(2, arguments);
    1052 var contained = nonCommentArg(1, 2, arguments);
    1053 var container = nonCommentArg(2, 2, arguments);
    1054 _assert(commentArg(2, arguments),
    1055 goog.testing.asserts.contains_(container, contained),
    1056 'Expected \'' + container + '\' to contain \'' + contained + '\'');
    1057};
    1058
    1059
    1060/**
    1061 * Checks if the given element is not the member of the given container.
    1062 * @param {*} a Failure message (3 arguments) or the contained element
    1063 * (2 arguments).
    1064 * @param {*} b The contained element (3 arguments) or the container
    1065 * (2 arguments).
    1066 * @param {*=} opt_c The container.
    1067 */
    1068var assertNotContains = function(a, b, opt_c) {
    1069 _validateArguments(2, arguments);
    1070 var contained = nonCommentArg(1, 2, arguments);
    1071 var container = nonCommentArg(2, 2, arguments);
    1072 _assert(commentArg(2, arguments),
    1073 !goog.testing.asserts.contains_(container, contained),
    1074 'Expected \'' + container + '\' not to contain \'' + contained + '\'');
    1075};
    1076
    1077
    1078/**
    1079 * Checks if the given string matches the given regular expression.
    1080 * @param {*} a Failure message (3 arguments) or the expected regular
    1081 * expression as a string or RegExp (2 arguments).
    1082 * @param {*} b The regular expression (3 arguments) or the string to test
    1083 * (2 arguments).
    1084 * @param {*=} opt_c The string to test.
    1085 */
    1086var assertRegExp = function(a, b, opt_c) {
    1087 _validateArguments(2, arguments);
    1088 var regexp = nonCommentArg(1, 2, arguments);
    1089 var string = nonCommentArg(2, 2, arguments);
    1090 if (typeof(regexp) == 'string') {
    1091 regexp = new RegExp(regexp);
    1092 }
    1093 _assert(commentArg(2, arguments),
    1094 regexp.test(string),
    1095 'Expected \'' + string + '\' to match RegExp ' + regexp.toString());
    1096};
    1097
    1098
    1099/**
    1100 * Converts an array like object to array or clones it if it's already array.
    1101 * @param {goog.testing.asserts.ArrayLike} arrayLike The collection.
    1102 * @return {!Array} Copy of the collection as array.
    1103 * @private
    1104 */
    1105goog.testing.asserts.toArray_ = function(arrayLike) {
    1106 var ret = [];
    1107 for (var i = 0; i < arrayLike.length; i++) {
    1108 ret[i] = arrayLike[i];
    1109 }
    1110 return ret;
    1111};
    1112
    1113
    1114/**
    1115 * Finds the position of the first occurrence of an element in a container.
    1116 * @param {goog.testing.asserts.ArrayLike} container
    1117 * The array to find the element in.
    1118 * @param {*} contained Element to find.
    1119 * @return {number} Index of the first occurrence or -1 if not found.
    1120 * @private
    1121 */
    1122goog.testing.asserts.indexOf_ = function(container, contained) {
    1123 if (container.indexOf) {
    1124 return container.indexOf(contained);
    1125 } else {
    1126 // IE6/7 do not have indexOf so do a search.
    1127 for (var i = 0; i < container.length; i++) {
    1128 if (container[i] === contained) {
    1129 return i;
    1130 }
    1131 }
    1132 return -1;
    1133 }
    1134};
    1135
    1136
    1137/**
    1138 * Tells whether the array contains the given element.
    1139 * @param {goog.testing.asserts.ArrayLike} container The array to
    1140 * find the element in.
    1141 * @param {*} contained Element to find.
    1142 * @return {boolean} Whether the element is in the array.
    1143 * @private
    1144 */
    1145goog.testing.asserts.contains_ = function(container, contained) {
    1146 // TODO(user): Can we check for container.contains as well?
    1147 // That would give us support for most goog.structs (though weird results
    1148 // with anything else with a contains method, like goog.math.Range). Falling
    1149 // back with container.some would catch all iterables, too.
    1150 return goog.testing.asserts.indexOf_(container, contained) != -1;
    1151};
    1152
    1153var standardizeHTML = function(html) {
    1154 var translator = document.createElement('DIV');
    1155 translator.innerHTML = html;
    1156
    1157 // Trim whitespace from result (without relying on goog.string)
    1158 return translator.innerHTML.replace(/^\s+|\s+$/g, '');
    1159};
    1160
    1161
    1162/**
    1163 * Standardizes a CSS value for a given property by applying it to an element
    1164 * and then reading it back.
    1165 * @param {string} propertyName CSS property name.
    1166 * @param {string} value CSS value.
    1167 * @return {string} Normalized CSS value.
    1168 */
    1169var standardizeCSSValue = function(propertyName, value) {
    1170 var styleDeclaration = document.createElement('DIV').style;
    1171 styleDeclaration[propertyName] = value;
    1172 return styleDeclaration[propertyName];
    1173};
    1174
    1175
    1176/**
    1177 * Raises a JsUnit exception with the given comment.
    1178 * @param {string} comment A summary for the exception.
    1179 * @param {string=} opt_message A description of the exception.
    1180 */
    1181goog.testing.asserts.raiseException = function(comment, opt_message) {
    1182 throw new goog.testing.JsUnitException(comment, opt_message);
    1183};
    1184
    1185
    1186/**
    1187 * Helper function for assertObjectEquals.
    1188 * @param {string} prop A property name.
    1189 * @return {boolean} If the property name is an array index.
    1190 * @private
    1191 */
    1192goog.testing.asserts.isArrayIndexProp_ = function(prop) {
    1193 return (prop | 0) == prop;
    1194};
    1195
    1196
    1197
    1198/**
    1199 * @param {string} comment A summary for the exception.
    1200 * @param {?string=} opt_message A description of the exception.
    1201 * @constructor
    1202 * @extends {Error}
    1203 * @final
    1204 */
    1205goog.testing.JsUnitException = function(comment, opt_message) {
    1206 this.isJsUnitException = true;
    1207 this.message = (comment ? comment : '') +
    1208 (comment && opt_message ? '\n' : '') +
    1209 (opt_message ? opt_message : '');
    1210 this.stackTrace = goog.testing.stacktrace.get();
    1211 // These fields are for compatibility with jsUnitTestManager.
    1212 this.comment = comment || null;
    1213 this.jsUnitMessage = opt_message || '';
    1214
    1215 // Ensure there is a stack trace.
    1216 if (Error.captureStackTrace) {
    1217 Error.captureStackTrace(this, goog.testing.JsUnitException);
    1218 } else {
    1219 this.stack = new Error().stack || '';
    1220 }
    1221};
    1222goog.inherits(goog.testing.JsUnitException, Error);
    1223
    1224
    1225/** @override */
    1226goog.testing.JsUnitException.prototype.toString = function() {
    1227 return this.message;
    1228};
    1229
    1230
    1231goog.exportSymbol('fail', fail);
    1232goog.exportSymbol('assert', assert);
    1233goog.exportSymbol('assertThrows', assertThrows);
    1234goog.exportSymbol('assertNotThrows', assertNotThrows);
    1235goog.exportSymbol('assertTrue', assertTrue);
    1236goog.exportSymbol('assertFalse', assertFalse);
    1237goog.exportSymbol('assertEquals', assertEquals);
    1238goog.exportSymbol('assertNotEquals', assertNotEquals);
    1239goog.exportSymbol('assertNull', assertNull);
    1240goog.exportSymbol('assertNotNull', assertNotNull);
    1241goog.exportSymbol('assertUndefined', assertUndefined);
    1242goog.exportSymbol('assertNotUndefined', assertNotUndefined);
    1243goog.exportSymbol('assertNotNullNorUndefined', assertNotNullNorUndefined);
    1244goog.exportSymbol('assertNonEmptyString', assertNonEmptyString);
    1245goog.exportSymbol('assertNaN', assertNaN);
    1246goog.exportSymbol('assertNotNaN', assertNotNaN);
    1247goog.exportSymbol('assertObjectEquals', assertObjectEquals);
    1248goog.exportSymbol('assertObjectRoughlyEquals', assertObjectRoughlyEquals);
    1249goog.exportSymbol('assertObjectNotEquals', assertObjectNotEquals);
    1250goog.exportSymbol('assertArrayEquals', assertArrayEquals);
    1251goog.exportSymbol('assertElementsEquals', assertElementsEquals);
    1252goog.exportSymbol('assertElementsRoughlyEqual', assertElementsRoughlyEqual);
    1253goog.exportSymbol('assertSameElements', assertSameElements);
    1254goog.exportSymbol('assertEvaluatesToTrue', assertEvaluatesToTrue);
    1255goog.exportSymbol('assertEvaluatesToFalse', assertEvaluatesToFalse);
    1256goog.exportSymbol('assertHTMLEquals', assertHTMLEquals);
    1257goog.exportSymbol('assertHashEquals', assertHashEquals);
    1258goog.exportSymbol('assertRoughlyEquals', assertRoughlyEquals);
    1259goog.exportSymbol('assertContains', assertContains);
    1260goog.exportSymbol('assertNotContains', assertNotContains);
    1261goog.exportSymbol('assertRegExp', assertRegExp);
    \ No newline at end of file +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;
    33var TO_STRING_EQUALITY_PREDICATE = function(var1, var2) {
    34 return var1.toString() === var2.toString();
    35};
    36
    37var PRIMITIVE_EQUALITY_PREDICATES = {
    38 'String': DOUBLE_EQUALITY_PREDICATE,
    39 'Number': DOUBLE_EQUALITY_PREDICATE,
    40 'Boolean': DOUBLE_EQUALITY_PREDICATE,
    41 'Date': function(date1, date2) {
    42 return date1.getTime() == date2.getTime();
    43 },
    44 'RegExp': TO_STRING_EQUALITY_PREDICATE,
    45 'Function': TO_STRING_EQUALITY_PREDICATE
    46};
    47
    48
    49/**
    50 * Compares equality of two numbers, allowing them to differ up to a given
    51 * tolerance.
    52 * @param {number} var1 A number.
    53 * @param {number} var2 A number.
    54 * @param {number} tolerance the maximum allowed difference.
    55 * @return {boolean} Whether the two variables are sufficiently close.
    56 * @private
    57 */
    58goog.testing.asserts.numberRoughEqualityPredicate_ = function(
    59 var1, var2, tolerance) {
    60 return Math.abs(var1 - var2) <= tolerance;
    61};
    62
    63
    64/**
    65 * @type {Object<string, function(*, *, number): boolean>}
    66 * @private
    67 */
    68goog.testing.asserts.primitiveRoughEqualityPredicates_ = {
    69 'Number': goog.testing.asserts.numberRoughEqualityPredicate_
    70};
    71
    72
    73var _trueTypeOf = function(something) {
    74 var result = typeof something;
    75 try {
    76 switch (result) {
    77 case 'string':
    78 break;
    79 case 'boolean':
    80 break;
    81 case 'number':
    82 break;
    83 case 'object':
    84 if (something == null) {
    85 result = 'null';
    86 break;
    87 }
    88 case 'function':
    89 switch (something.constructor) {
    90 case new String('').constructor:
    91 result = 'String';
    92 break;
    93 case new Boolean(true).constructor:
    94 result = 'Boolean';
    95 break;
    96 case new Number(0).constructor:
    97 result = 'Number';
    98 break;
    99 case new Array().constructor:
    100 result = 'Array';
    101 break;
    102 case new RegExp().constructor:
    103 result = 'RegExp';
    104 break;
    105 case new Date().constructor:
    106 result = 'Date';
    107 break;
    108 case Function:
    109 result = 'Function';
    110 break;
    111 default:
    112 var m = something.constructor.toString().match(
    113 /function\s*([^( ]+)\(/);
    114 if (m) {
    115 result = m[1];
    116 } else {
    117 break;
    118 }
    119 }
    120 break;
    121 }
    122 } catch (e) {
    123
    124 } finally {
    125 result = result.substr(0, 1).toUpperCase() + result.substr(1);
    126 }
    127 return result;
    128};
    129
    130var _displayStringForValue = function(aVar) {
    131 var result;
    132 try {
    133 result = '<' + String(aVar) + '>';
    134 } catch (ex) {
    135 result = '<toString failed: ' + ex.message + '>';
    136 // toString does not work on this object :-(
    137 }
    138 if (!(aVar === null || aVar === JSUNIT_UNDEFINED_VALUE)) {
    139 result += ' (' + _trueTypeOf(aVar) + ')';
    140 }
    141 return result;
    142};
    143
    144var fail = function(failureMessage) {
    145 goog.testing.asserts.raiseException('Call to fail()', failureMessage);
    146};
    147
    148var argumentsIncludeComments = function(expectedNumberOfNonCommentArgs, args) {
    149 return args.length == expectedNumberOfNonCommentArgs + 1;
    150};
    151
    152var commentArg = function(expectedNumberOfNonCommentArgs, args) {
    153 if (argumentsIncludeComments(expectedNumberOfNonCommentArgs, args)) {
    154 return args[0];
    155 }
    156
    157 return null;
    158};
    159
    160var nonCommentArg = function(desiredNonCommentArgIndex,
    161 expectedNumberOfNonCommentArgs, args) {
    162 return argumentsIncludeComments(expectedNumberOfNonCommentArgs, args) ?
    163 args[desiredNonCommentArgIndex] :
    164 args[desiredNonCommentArgIndex - 1];
    165};
    166
    167var _validateArguments = function(expectedNumberOfNonCommentArgs, args) {
    168 var valid = args.length == expectedNumberOfNonCommentArgs ||
    169 args.length == expectedNumberOfNonCommentArgs + 1 &&
    170 goog.isString(args[0]);
    171 _assert(null, valid, 'Incorrect arguments passed to assert function');
    172};
    173
    174var _assert = function(comment, booleanValue, failureMessage) {
    175 if (!booleanValue) {
    176 goog.testing.asserts.raiseException(comment, failureMessage);
    177 }
    178};
    179
    180
    181/**
    182 * @param {*} expected The expected value.
    183 * @param {*} actual The actual value.
    184 * @return {string} A failure message of the values don't match.
    185 * @private
    186 */
    187goog.testing.asserts.getDefaultErrorMsg_ = function(expected, actual) {
    188 var msg = 'Expected ' + _displayStringForValue(expected) + ' but was ' +
    189 _displayStringForValue(actual);
    190 if ((typeof expected == 'string') && (typeof actual == 'string')) {
    191 // Try to find a human-readable difference.
    192 var limit = Math.min(expected.length, actual.length);
    193 var commonPrefix = 0;
    194 while (commonPrefix < limit &&
    195 expected.charAt(commonPrefix) == actual.charAt(commonPrefix)) {
    196 commonPrefix++;
    197 }
    198
    199 var commonSuffix = 0;
    200 while (commonSuffix < limit &&
    201 expected.charAt(expected.length - commonSuffix - 1) ==
    202 actual.charAt(actual.length - commonSuffix - 1)) {
    203 commonSuffix++;
    204 }
    205
    206 if (commonPrefix + commonSuffix > limit) {
    207 commonSuffix = 0;
    208 }
    209
    210 if (commonPrefix > 2 || commonSuffix > 2) {
    211 var printString = function(str) {
    212 var startIndex = Math.max(0, commonPrefix - 2);
    213 var endIndex = Math.min(str.length, str.length - (commonSuffix - 2));
    214 return (startIndex > 0 ? '...' : '') +
    215 str.substring(startIndex, endIndex) +
    216 (endIndex < str.length ? '...' : '');
    217 };
    218
    219 msg += '\nDifference was at position ' + commonPrefix +
    220 '. Expected [' + printString(expected) +
    221 '] vs. actual [' + printString(actual) + ']';
    222 }
    223 }
    224 return msg;
    225};
    226
    227
    228/**
    229 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    230 * @param {*=} opt_b The value to assert (2 args only).
    231 */
    232var assert = function(a, opt_b) {
    233 _validateArguments(1, arguments);
    234 var comment = commentArg(1, arguments);
    235 var booleanValue = nonCommentArg(1, 1, arguments);
    236
    237 _assert(comment, goog.isBoolean(booleanValue),
    238 'Bad argument to assert(boolean)');
    239 _assert(comment, booleanValue, 'Call to assert(boolean) with false');
    240};
    241
    242
    243/**
    244 * Asserts that the function throws an error.
    245 *
    246 * @param {!(string|Function)} a The assertion comment or the function to call.
    247 * @param {!Function=} opt_b The function to call (if the first argument of
    248 * {@code assertThrows} was the comment).
    249 * @return {*} The error thrown by the function.
    250 * @throws {goog.testing.JsUnitException} If the assertion failed.
    251 */
    252var assertThrows = function(a, opt_b) {
    253 _validateArguments(1, arguments);
    254 var func = nonCommentArg(1, 1, arguments);
    255 var comment = commentArg(1, arguments);
    256 _assert(comment, typeof func == 'function',
    257 'Argument passed to assertThrows is not a function');
    258
    259 try {
    260 func();
    261 } catch (e) {
    262 if (e && goog.isString(e['stacktrace']) && goog.isString(e['message'])) {
    263 // Remove the stack trace appended to the error message by Opera 10.0
    264 var startIndex = e['message'].length - e['stacktrace'].length;
    265 if (e['message'].indexOf(e['stacktrace'], startIndex) == startIndex) {
    266 e['message'] = e['message'].substr(0, startIndex - 14);
    267 }
    268 }
    269 return e;
    270 }
    271 goog.testing.asserts.raiseException(comment,
    272 'No exception thrown from function passed to assertThrows');
    273};
    274
    275
    276/**
    277 * Asserts that the function does not throw an error.
    278 *
    279 * @param {!(string|Function)} a The assertion comment or the function to call.
    280 * @param {!Function=} opt_b The function to call (if the first argument of
    281 * {@code assertNotThrows} was the comment).
    282 * @return {*} The return value of the function.
    283 * @throws {goog.testing.JsUnitException} If the assertion failed.
    284 */
    285var assertNotThrows = function(a, opt_b) {
    286 _validateArguments(1, arguments);
    287 var comment = commentArg(1, arguments);
    288 var func = nonCommentArg(1, 1, arguments);
    289 _assert(comment, typeof func == 'function',
    290 'Argument passed to assertNotThrows is not a function');
    291
    292 try {
    293 return func();
    294 } catch (e) {
    295 comment = comment ? (comment + '\n') : '';
    296 comment += 'A non expected exception was thrown from function passed to ' +
    297 'assertNotThrows';
    298 // Some browsers don't have a stack trace so at least have the error
    299 // description.
    300 var stackTrace = e['stack'] || e['stacktrace'] || e.toString();
    301 goog.testing.asserts.raiseException(comment, stackTrace);
    302 }
    303};
    304
    305
    306/**
    307 * Asserts that the given callback function results in a JsUnitException when
    308 * called, and that the resulting failure message matches the given expected
    309 * message.
    310 * @param {function() : void} callback Function to be run expected to result
    311 * in a JsUnitException (usually contains a call to an assert).
    312 * @param {string=} opt_expectedMessage Failure message expected to be given
    313 * with the exception.
    314 */
    315var assertThrowsJsUnitException = function(callback, opt_expectedMessage) {
    316 var failed = false;
    317 try {
    318 goog.testing.asserts.callWithoutLogging(callback);
    319 } catch (ex) {
    320 if (!ex.isJsUnitException) {
    321 fail('Expected a JsUnitException');
    322 }
    323 if (typeof opt_expectedMessage != 'undefined' &&
    324 ex.message != opt_expectedMessage) {
    325 fail('Expected message [' + opt_expectedMessage + '] but got [' +
    326 ex.message + ']');
    327 }
    328 failed = true;
    329 }
    330 if (!failed) {
    331 fail('Expected a failure: ' + opt_expectedMessage);
    332 }
    333};
    334
    335
    336/**
    337 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    338 * @param {*=} opt_b The value to assert (2 args only).
    339 */
    340var assertTrue = function(a, opt_b) {
    341 _validateArguments(1, arguments);
    342 var comment = commentArg(1, arguments);
    343 var booleanValue = nonCommentArg(1, 1, arguments);
    344
    345 _assert(comment, goog.isBoolean(booleanValue),
    346 'Bad argument to assertTrue(boolean)');
    347 _assert(comment, booleanValue, 'Call to assertTrue(boolean) with false');
    348};
    349
    350
    351/**
    352 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    353 * @param {*=} opt_b The value to assert (2 args only).
    354 */
    355var assertFalse = function(a, opt_b) {
    356 _validateArguments(1, arguments);
    357 var comment = commentArg(1, arguments);
    358 var booleanValue = nonCommentArg(1, 1, arguments);
    359
    360 _assert(comment, goog.isBoolean(booleanValue),
    361 'Bad argument to assertFalse(boolean)');
    362 _assert(comment, !booleanValue, 'Call to assertFalse(boolean) with true');
    363};
    364
    365
    366/**
    367 * @param {*} a The expected value (2 args) or the debug message (3 args).
    368 * @param {*} b The actual value (2 args) or the expected value (3 args).
    369 * @param {*=} opt_c The actual value (3 args only).
    370 */
    371var assertEquals = function(a, b, opt_c) {
    372 _validateArguments(2, arguments);
    373 var var1 = nonCommentArg(1, 2, arguments);
    374 var var2 = nonCommentArg(2, 2, arguments);
    375 _assert(commentArg(2, arguments), var1 === var2,
    376 goog.testing.asserts.getDefaultErrorMsg_(var1, var2));
    377};
    378
    379
    380/**
    381 * @param {*} a The expected value (2 args) or the debug message (3 args).
    382 * @param {*} b The actual value (2 args) or the expected value (3 args).
    383 * @param {*=} opt_c The actual value (3 args only).
    384 */
    385var assertNotEquals = function(a, b, opt_c) {
    386 _validateArguments(2, arguments);
    387 var var1 = nonCommentArg(1, 2, arguments);
    388 var var2 = nonCommentArg(2, 2, arguments);
    389 _assert(commentArg(2, arguments), var1 !== var2,
    390 'Expected not to be ' + _displayStringForValue(var2));
    391};
    392
    393
    394/**
    395 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    396 * @param {*=} opt_b The value to assert (2 args only).
    397 */
    398var assertNull = function(a, opt_b) {
    399 _validateArguments(1, arguments);
    400 var aVar = nonCommentArg(1, 1, arguments);
    401 _assert(commentArg(1, arguments), aVar === null,
    402 goog.testing.asserts.getDefaultErrorMsg_(null, aVar));
    403};
    404
    405
    406/**
    407 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    408 * @param {*=} opt_b The value to assert (2 args only).
    409 */
    410var assertNotNull = function(a, opt_b) {
    411 _validateArguments(1, arguments);
    412 var aVar = nonCommentArg(1, 1, arguments);
    413 _assert(commentArg(1, arguments), aVar !== null,
    414 'Expected not to be ' + _displayStringForValue(null));
    415};
    416
    417
    418/**
    419 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    420 * @param {*=} opt_b The value to assert (2 args only).
    421 */
    422var assertUndefined = function(a, opt_b) {
    423 _validateArguments(1, arguments);
    424 var aVar = nonCommentArg(1, 1, arguments);
    425 _assert(commentArg(1, arguments), aVar === JSUNIT_UNDEFINED_VALUE,
    426 goog.testing.asserts.getDefaultErrorMsg_(JSUNIT_UNDEFINED_VALUE, aVar));
    427};
    428
    429
    430/**
    431 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    432 * @param {*=} opt_b The value to assert (2 args only).
    433 */
    434var assertNotUndefined = function(a, opt_b) {
    435 _validateArguments(1, arguments);
    436 var aVar = nonCommentArg(1, 1, arguments);
    437 _assert(commentArg(1, arguments), aVar !== JSUNIT_UNDEFINED_VALUE,
    438 'Expected not to be ' + _displayStringForValue(JSUNIT_UNDEFINED_VALUE));
    439};
    440
    441
    442/**
    443 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    444 * @param {*=} opt_b The value to assert (2 args only).
    445 */
    446var assertNotNullNorUndefined = function(a, opt_b) {
    447 _validateArguments(1, arguments);
    448 assertNotNull.apply(null, arguments);
    449 assertNotUndefined.apply(null, arguments);
    450};
    451
    452
    453/**
    454 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    455 * @param {*=} opt_b The value to assert (2 args only).
    456 */
    457var assertNonEmptyString = function(a, opt_b) {
    458 _validateArguments(1, arguments);
    459 var aVar = nonCommentArg(1, 1, arguments);
    460 _assert(commentArg(1, arguments),
    461 aVar !== JSUNIT_UNDEFINED_VALUE && aVar !== null &&
    462 typeof aVar == 'string' && aVar !== '',
    463 'Expected non-empty string but was ' + _displayStringForValue(aVar));
    464};
    465
    466
    467/**
    468 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    469 * @param {*=} opt_b The value to assert (2 args only).
    470 */
    471var assertNaN = function(a, opt_b) {
    472 _validateArguments(1, arguments);
    473 var aVar = nonCommentArg(1, 1, arguments);
    474 _assert(commentArg(1, arguments), isNaN(aVar), 'Expected NaN');
    475};
    476
    477
    478/**
    479 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    480 * @param {*=} opt_b The value to assert (2 args only).
    481 */
    482var assertNotNaN = function(a, opt_b) {
    483 _validateArguments(1, arguments);
    484 var aVar = nonCommentArg(1, 1, arguments);
    485 _assert(commentArg(1, arguments), !isNaN(aVar), 'Expected not NaN');
    486};
    487
    488
    489/**
    490 * Runs a function in an environment where test failures are not logged. This is
    491 * useful for testing test code, where failures can be a normal part of a test.
    492 * @param {function() : void} fn Function to run without logging failures.
    493 */
    494goog.testing.asserts.callWithoutLogging = function(fn) {
    495 var testRunner = goog.global['G_testRunner'];
    496 var oldLogTestFailure = testRunner['logTestFailure'];
    497 try {
    498 // Any failures in the callback shouldn't be recorded.
    499 testRunner['logTestFailure'] = undefined;
    500 fn();
    501 } finally {
    502 testRunner['logTestFailure'] = oldLogTestFailure;
    503 }
    504};
    505
    506
    507/**
    508 * The return value of the equality predicate passed to findDifferences below,
    509 * in cases where the predicate can't test the input variables for equality.
    510 * @type {?string}
    511 */
    512goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS = null;
    513
    514
    515/**
    516 * The return value of the equality predicate passed to findDifferences below,
    517 * in cases where the input vriables are equal.
    518 * @type {?string}
    519 */
    520goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL = '';
    521
    522
    523/**
    524 * Determines if two items of any type match, and formulates an error message
    525 * if not.
    526 * @param {*} expected Expected argument to match.
    527 * @param {*} actual Argument as a result of performing the test.
    528 * @param {(function(string, *, *): ?string)=} opt_equalityPredicate An optional
    529 * function that can be used to check equality of variables. It accepts 3
    530 * arguments: type-of-variables, var1, var2 (in that order) and returns an
    531 * error message if the variables are not equal,
    532 * goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL if the variables
    533 * are equal, or
    534 * goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS if the predicate
    535 * couldn't check the input variables. The function will be called only if
    536 * the types of var1 and var2 are identical.
    537 * @return {?string} Null on success, error message on failure.
    538 */
    539goog.testing.asserts.findDifferences = function(expected, actual,
    540 opt_equalityPredicate) {
    541 var failures = [];
    542 var seen1 = [];
    543 var seen2 = [];
    544
    545 // To avoid infinite recursion when the two parameters are self-referential
    546 // along the same path of properties, keep track of the object pairs already
    547 // seen in this call subtree, and abort when a cycle is detected.
    548 function innerAssertWithCycleCheck(var1, var2, path) {
    549 // This is used for testing, so we can afford to be slow (but more
    550 // accurate). So we just check whether var1 is in seen1. If we
    551 // found var1 in index i, we simply need to check whether var2 is
    552 // in seen2[i]. If it is, we do not recurse to check var1/var2. If
    553 // it isn't, we know that the structures of the two objects must be
    554 // different.
    555 //
    556 // This is based on the fact that values at index i in seen1 and
    557 // seen2 will be checked for equality eventually (when
    558 // innerAssertImplementation(seen1[i], seen2[i], path) finishes).
    559 for (var i = 0; i < seen1.length; ++i) {
    560 var match1 = seen1[i] === var1;
    561 var match2 = seen2[i] === var2;
    562 if (match1 || match2) {
    563 if (!match1 || !match2) {
    564 // Asymmetric cycles, so the objects have different structure.
    565 failures.push('Asymmetric cycle detected at ' + path);
    566 }
    567 return;
    568 }
    569 }
    570
    571 seen1.push(var1);
    572 seen2.push(var2);
    573 innerAssertImplementation(var1, var2, path);
    574 seen1.pop();
    575 seen2.pop();
    576 }
    577
    578 var equalityPredicate = opt_equalityPredicate || function(type, var1, var2) {
    579 var typedPredicate = PRIMITIVE_EQUALITY_PREDICATES[type];
    580 if (!typedPredicate) {
    581 return goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS;
    582 }
    583 var equal = typedPredicate(var1, var2);
    584 return equal ? goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL :
    585 goog.testing.asserts.getDefaultErrorMsg_(var1, var2);
    586 };
    587
    588 /**
    589 * @param {*} var1 An item in the expected object.
    590 * @param {*} var2 The corresponding item in the actual object.
    591 * @param {string} path Their path in the objects.
    592 * @suppress {missingProperties} The map_ property is unknown to the compiler
    593 * unless goog.structs.Map is loaded.
    594 */
    595 function innerAssertImplementation(var1, var2, path) {
    596 if (var1 === var2) {
    597 return;
    598 }
    599
    600 var typeOfVar1 = _trueTypeOf(var1);
    601 var typeOfVar2 = _trueTypeOf(var2);
    602
    603 if (typeOfVar1 == typeOfVar2) {
    604 var isArray = typeOfVar1 == 'Array';
    605 var errorMessage = equalityPredicate(typeOfVar1, var1, var2);
    606 if (errorMessage !=
    607 goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS) {
    608 if (errorMessage !=
    609 goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL) {
    610 failures.push(path + ': ' + errorMessage);
    611 }
    612 } else if (isArray && var1.length != var2.length) {
    613 failures.push(path + ': Expected ' + var1.length + '-element array ' +
    614 'but got a ' + var2.length + '-element array');
    615 } else {
    616 var childPath = path + (isArray ? '[%s]' : (path ? '.%s' : '%s'));
    617
    618 // if an object has an __iterator__ property, we have no way of
    619 // actually inspecting its raw properties, and JS 1.7 doesn't
    620 // overload [] to make it possible for someone to generically
    621 // use what the iterator returns to compare the object-managed
    622 // properties. This gets us into deep poo with things like
    623 // goog.structs.Map, at least on systems that support iteration.
    624 if (!var1['__iterator__']) {
    625 for (var prop in var1) {
    626 if (isArray && goog.testing.asserts.isArrayIndexProp_(prop)) {
    627 // Skip array indices for now. We'll handle them later.
    628 continue;
    629 }
    630
    631 if (prop in var2) {
    632 innerAssertWithCycleCheck(var1[prop], var2[prop],
    633 childPath.replace('%s', prop));
    634 } else {
    635 failures.push('property ' + prop +
    636 ' not present in actual ' + (path || typeOfVar2));
    637 }
    638 }
    639 // make sure there aren't properties in var2 that are missing
    640 // from var1. if there are, then by definition they don't
    641 // match.
    642 for (var prop in var2) {
    643 if (isArray && goog.testing.asserts.isArrayIndexProp_(prop)) {
    644 // Skip array indices for now. We'll handle them later.
    645 continue;
    646 }
    647
    648 if (!(prop in var1)) {
    649 failures.push('property ' + prop +
    650 ' not present in expected ' +
    651 (path || typeOfVar1));
    652 }
    653 }
    654
    655 // Handle array indices by iterating from 0 to arr.length.
    656 //
    657 // Although all browsers allow holes in arrays, browsers
    658 // are inconsistent in what they consider a hole. For example,
    659 // "[0,undefined,2]" has a hole on IE but not on Firefox.
    660 //
    661 // Because our style guide bans for...in iteration over arrays,
    662 // we assume that most users don't care about holes in arrays,
    663 // and that it is ok to say that a hole is equivalent to a slot
    664 // populated with 'undefined'.
    665 if (isArray) {
    666 for (prop = 0; prop < var1.length; prop++) {
    667 innerAssertWithCycleCheck(var1[prop], var2[prop],
    668 childPath.replace('%s', String(prop)));
    669 }
    670 }
    671 } else {
    672 // special-case for closure objects that have iterators
    673 if (goog.isFunction(var1.equals)) {
    674 // use the object's own equals function, assuming it accepts an
    675 // object and returns a boolean
    676 if (!var1.equals(var2)) {
    677 failures.push('equals() returned false for ' +
    678 (path || typeOfVar1));
    679 }
    680 } else if (var1.map_) {
    681 // assume goog.structs.Map or goog.structs.Set, where comparing
    682 // their private map_ field is sufficient
    683 innerAssertWithCycleCheck(var1.map_, var2.map_,
    684 childPath.replace('%s', 'map_'));
    685 } else {
    686 // else die, so user knows we can't do anything
    687 failures.push('unable to check ' + (path || typeOfVar1) +
    688 ' for equality: it has an iterator we do not ' +
    689 'know how to handle. please add an equals method');
    690 }
    691 }
    692 }
    693 } else {
    694 failures.push(path + ' ' +
    695 goog.testing.asserts.getDefaultErrorMsg_(var1, var2));
    696 }
    697 }
    698
    699 innerAssertWithCycleCheck(expected, actual, '');
    700 return failures.length == 0 ? null :
    701 goog.testing.asserts.getDefaultErrorMsg_(expected, actual) +
    702 '\n ' + failures.join('\n ');
    703};
    704
    705
    706/**
    707 * Notes:
    708 * Object equality has some nasty browser quirks, and this implementation is
    709 * not 100% correct. For example,
    710 *
    711 * <code>
    712 * var a = [0, 1, 2];
    713 * var b = [0, 1, 2];
    714 * delete a[1];
    715 * b[1] = undefined;
    716 * assertObjectEquals(a, b); // should fail, but currently passes
    717 * </code>
    718 *
    719 * See asserts_test.html for more interesting edge cases.
    720 *
    721 * The first comparison object provided is the expected value, the second is
    722 * the actual.
    723 *
    724 * @param {*} a Assertion message or comparison object.
    725 * @param {*} b Comparison object.
    726 * @param {*=} opt_c Comparison object, if an assertion message was provided.
    727 */
    728var assertObjectEquals = function(a, b, opt_c) {
    729 _validateArguments(2, arguments);
    730 var v1 = nonCommentArg(1, 2, arguments);
    731 var v2 = nonCommentArg(2, 2, arguments);
    732 var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
    733 var differences = goog.testing.asserts.findDifferences(v1, v2);
    734
    735 _assert(failureMessage, !differences, differences);
    736};
    737
    738
    739/**
    740 * Similar to assertObjectEquals above, but accepts a tolerance margin.
    741 *
    742 * @param {*} a Assertion message or comparison object.
    743 * @param {*} b Comparison object.
    744 * @param {*} c Comparison object or tolerance.
    745 * @param {*=} opt_d Tolerance, if an assertion message was provided.
    746 */
    747var assertObjectRoughlyEquals = function(a, b, c, opt_d) {
    748 _validateArguments(3, arguments);
    749 var v1 = nonCommentArg(1, 3, arguments);
    750 var v2 = nonCommentArg(2, 3, arguments);
    751 var tolerance = nonCommentArg(3, 3, arguments);
    752 var failureMessage = commentArg(3, arguments) ? commentArg(3, arguments) : '';
    753 var equalityPredicate = function(type, var1, var2) {
    754 var typedPredicate =
    755 goog.testing.asserts.primitiveRoughEqualityPredicates_[type];
    756 if (!typedPredicate) {
    757 return goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS;
    758 }
    759 var equal = typedPredicate(var1, var2, tolerance);
    760 return equal ? goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL :
    761 goog.testing.asserts.getDefaultErrorMsg_(var1, var2) +
    762 ' which was more than ' + tolerance + ' away';
    763 };
    764 var differences = goog.testing.asserts.findDifferences(
    765 v1, v2, equalityPredicate);
    766
    767 _assert(failureMessage, !differences, differences);
    768};
    769
    770
    771/**
    772 * Compares two arbitrary objects for non-equalness.
    773 *
    774 * All the same caveats as for assertObjectEquals apply here:
    775 * Undefined values may be confused for missing values, or vice versa.
    776 *
    777 * @param {*} a Assertion message or comparison object.
    778 * @param {*} b Comparison object.
    779 * @param {*=} opt_c Comparison object, if an assertion message was provided.
    780 */
    781var assertObjectNotEquals = function(a, b, opt_c) {
    782 _validateArguments(2, arguments);
    783 var v1 = nonCommentArg(1, 2, arguments);
    784 var v2 = nonCommentArg(2, 2, arguments);
    785 var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
    786 var differences = goog.testing.asserts.findDifferences(v1, v2);
    787
    788 _assert(failureMessage, differences, 'Objects should not be equal');
    789};
    790
    791
    792/**
    793 * Compares two arrays ignoring negative indexes and extra properties on the
    794 * array objects. Use case: Internet Explorer adds the index, lastIndex and
    795 * input enumerable fields to the result of string.match(/regexp/g), which makes
    796 * assertObjectEquals fail.
    797 * @param {*} a The expected array (2 args) or the debug message (3 args).
    798 * @param {*} b The actual array (2 args) or the expected array (3 args).
    799 * @param {*=} opt_c The actual array (3 args only).
    800 */
    801var assertArrayEquals = function(a, b, opt_c) {
    802 _validateArguments(2, arguments);
    803 var v1 = nonCommentArg(1, 2, arguments);
    804 var v2 = nonCommentArg(2, 2, arguments);
    805 var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
    806
    807 var typeOfVar1 = _trueTypeOf(v1);
    808 _assert(failureMessage,
    809 typeOfVar1 == 'Array',
    810 'Expected an array for assertArrayEquals but found a ' + typeOfVar1);
    811
    812 var typeOfVar2 = _trueTypeOf(v2);
    813 _assert(failureMessage,
    814 typeOfVar2 == 'Array',
    815 'Expected an array for assertArrayEquals but found a ' + typeOfVar2);
    816
    817 assertObjectEquals(failureMessage,
    818 Array.prototype.concat.call(v1), Array.prototype.concat.call(v2));
    819};
    820
    821
    822/**
    823 * Compares two objects that can be accessed like an array and assert that
    824 * each element is equal.
    825 * @param {string|Object} a Failure message (3 arguments)
    826 * or object #1 (2 arguments).
    827 * @param {Object} b Object #1 (2 arguments) or object #2 (3 arguments).
    828 * @param {Object=} opt_c Object #2 (3 arguments).
    829 */
    830var assertElementsEquals = function(a, b, opt_c) {
    831 _validateArguments(2, arguments);
    832
    833 var v1 = nonCommentArg(1, 2, arguments);
    834 var v2 = nonCommentArg(2, 2, arguments);
    835 var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
    836
    837 if (!v1) {
    838 assert(failureMessage, !v2);
    839 } else {
    840 assertEquals('length mismatch: ' + failureMessage, v1.length, v2.length);
    841 for (var i = 0; i < v1.length; ++i) {
    842 assertEquals(
    843 'mismatch at index ' + i + ': ' + failureMessage, v1[i], v2[i]);
    844 }
    845 }
    846};
    847
    848
    849/**
    850 * Compares two objects that can be accessed like an array and assert that
    851 * each element is roughly equal.
    852 * @param {string|Object} a Failure message (4 arguments)
    853 * or object #1 (3 arguments).
    854 * @param {Object} b Object #1 (4 arguments) or object #2 (3 arguments).
    855 * @param {Object|number} c Object #2 (4 arguments) or tolerance (3 arguments).
    856 * @param {number=} opt_d tolerance (4 arguments).
    857 */
    858var assertElementsRoughlyEqual = function(a, b, c, opt_d) {
    859 _validateArguments(3, arguments);
    860
    861 var v1 = nonCommentArg(1, 3, arguments);
    862 var v2 = nonCommentArg(2, 3, arguments);
    863 var tolerance = nonCommentArg(3, 3, arguments);
    864 var failureMessage = commentArg(3, arguments) ? commentArg(3, arguments) : '';
    865
    866 if (!v1) {
    867 assert(failureMessage, !v2);
    868 } else {
    869 assertEquals('length mismatch: ' + failureMessage, v1.length, v2.length);
    870 for (var i = 0; i < v1.length; ++i) {
    871 assertRoughlyEquals(failureMessage, v1[i], v2[i], tolerance);
    872 }
    873 }
    874};
    875
    876
    877/**
    878 * Compares two array-like objects without taking their order into account.
    879 * @param {string|goog.testing.asserts.ArrayLike} a Assertion message or the
    880 * expected elements.
    881 * @param {goog.testing.asserts.ArrayLike} b Expected elements or the actual
    882 * elements.
    883 * @param {goog.testing.asserts.ArrayLike=} opt_c Actual elements.
    884 */
    885var assertSameElements = function(a, b, opt_c) {
    886 _validateArguments(2, arguments);
    887 var expected = nonCommentArg(1, 2, arguments);
    888 var actual = nonCommentArg(2, 2, arguments);
    889 var message = commentArg(2, arguments);
    890
    891 assertTrue('Bad arguments to assertSameElements(opt_message, expected: ' +
    892 'ArrayLike, actual: ArrayLike)',
    893 goog.isArrayLike(expected) && goog.isArrayLike(actual));
    894
    895 // Clones expected and actual and converts them to real arrays.
    896 expected = goog.testing.asserts.toArray_(expected);
    897 actual = goog.testing.asserts.toArray_(actual);
    898 // TODO(user): It would be great to show only the difference
    899 // between the expected and actual elements.
    900 _assert(message, expected.length == actual.length,
    901 'Expected ' + expected.length + ' elements: [' + expected + '], ' +
    902 'got ' + actual.length + ' elements: [' + actual + ']');
    903
    904 var toFind = goog.testing.asserts.toArray_(expected);
    905 for (var i = 0; i < actual.length; i++) {
    906 var index = goog.testing.asserts.indexOf_(toFind, actual[i]);
    907 _assert(message, index != -1, 'Expected [' + expected + '], got [' +
    908 actual + ']');
    909 toFind.splice(index, 1);
    910 }
    911};
    912
    913
    914/**
    915 * @param {*} a The value to assert (1 arg) or debug message (2 args).
    916 * @param {*=} opt_b The value to assert (2 args only).
    917 */
    918var assertEvaluatesToTrue = function(a, opt_b) {
    919 _validateArguments(1, arguments);
    920 var value = nonCommentArg(1, 1, arguments);
    921 if (!value) {
    922 _assert(commentArg(1, arguments), false, 'Expected to evaluate to true');
    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 assertEvaluatesToFalse = 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 false');
    936 }
    937};
    938
    939
    940/**
    941 * Compares two HTML snippets.
    942 *
    943 * Take extra care if attributes are involved. {@code assertHTMLEquals}'s
    944 * implementation isn't prepared for complex cases. For example, the following
    945 * comparisons erroneously fail:
    946 * <pre>
    947 * assertHTMLEquals('<a href="x" target="y">', '<a target="y" href="x">');
    948 * assertHTMLEquals('<div classname="a b">', '<div classname="b a">');
    949 * assertHTMLEquals('<input disabled>', '<input disabled="disabled">');
    950 * </pre>
    951 *
    952 * When in doubt, use {@code goog.testing.dom.assertHtmlMatches}.
    953 *
    954 * @param {*} a The expected value (2 args) or the debug message (3 args).
    955 * @param {*} b The actual value (2 args) or the expected value (3 args).
    956 * @param {*=} opt_c The actual value (3 args only).
    957 */
    958var assertHTMLEquals = function(a, b, opt_c) {
    959 _validateArguments(2, arguments);
    960 var var1 = nonCommentArg(1, 2, arguments);
    961 var var2 = nonCommentArg(2, 2, arguments);
    962 var var1Standardized = standardizeHTML(var1);
    963 var var2Standardized = standardizeHTML(var2);
    964
    965 _assert(commentArg(2, arguments), var1Standardized === var2Standardized,
    966 goog.testing.asserts.getDefaultErrorMsg_(
    967 var1Standardized, var2Standardized));
    968};
    969
    970
    971/**
    972 * Compares two CSS property values to make sure that they represent the same
    973 * things. This will normalize values in the browser. For example, in Firefox,
    974 * this assertion will consider "rgb(0, 0, 255)" and "#0000ff" to be identical
    975 * values for the "color" property. This function won't normalize everything --
    976 * for example, in most browsers, "blue" will not match "#0000ff". It is
    977 * intended only to compensate for unexpected normalizations performed by
    978 * the browser that should also affect your expected value.
    979 * @param {string} a Assertion message, or the CSS property name.
    980 * @param {string} b CSS property name, or the expected value.
    981 * @param {string} c The expected value, or the actual value.
    982 * @param {string=} opt_d The actual value.
    983 */
    984var assertCSSValueEquals = function(a, b, c, opt_d) {
    985 _validateArguments(3, arguments);
    986 var propertyName = nonCommentArg(1, 3, arguments);
    987 var expectedValue = nonCommentArg(2, 3, arguments);
    988 var actualValue = nonCommentArg(3, 3, arguments);
    989 var expectedValueStandardized =
    990 standardizeCSSValue(propertyName, expectedValue);
    991 var actualValueStandardized =
    992 standardizeCSSValue(propertyName, actualValue);
    993
    994 _assert(commentArg(3, arguments),
    995 expectedValueStandardized == actualValueStandardized,
    996 goog.testing.asserts.getDefaultErrorMsg_(
    997 expectedValueStandardized, actualValueStandardized));
    998};
    999
    1000
    1001/**
    1002 * @param {*} a The expected value (2 args) or the debug message (3 args).
    1003 * @param {*} b The actual value (2 args) or the expected value (3 args).
    1004 * @param {*=} opt_c The actual value (3 args only).
    1005 */
    1006var assertHashEquals = function(a, b, opt_c) {
    1007 _validateArguments(2, arguments);
    1008 var var1 = nonCommentArg(1, 2, arguments);
    1009 var var2 = nonCommentArg(2, 2, arguments);
    1010 var message = commentArg(2, arguments);
    1011 for (var key in var1) {
    1012 _assert(message,
    1013 key in var2, 'Expected hash had key ' + key + ' that was not found');
    1014 _assert(message, var1[key] == var2[key], 'Value for key ' + key +
    1015 ' mismatch - expected = ' + var1[key] + ', actual = ' + var2[key]);
    1016 }
    1017
    1018 for (var key in var2) {
    1019 _assert(message, key in var1, 'Actual hash had key ' + key +
    1020 ' that was not expected');
    1021 }
    1022};
    1023
    1024
    1025/**
    1026 * @param {*} a The expected value (3 args) or the debug message (4 args).
    1027 * @param {*} b The actual value (3 args) or the expected value (4 args).
    1028 * @param {*} c The tolerance (3 args) or the actual value (4 args).
    1029 * @param {*=} opt_d The tolerance (4 args only).
    1030 */
    1031var assertRoughlyEquals = function(a, b, c, opt_d) {
    1032 _validateArguments(3, arguments);
    1033 var expected = nonCommentArg(1, 3, arguments);
    1034 var actual = nonCommentArg(2, 3, arguments);
    1035 var tolerance = nonCommentArg(3, 3, arguments);
    1036 _assert(commentArg(3, arguments),
    1037 goog.testing.asserts.numberRoughEqualityPredicate_(
    1038 expected, actual, tolerance),
    1039 'Expected ' + expected + ', but got ' + actual +
    1040 ' which was more than ' + tolerance + ' away');
    1041};
    1042
    1043
    1044/**
    1045 * Checks if the test value is a member of the given container. Uses
    1046 * container.indexOf as the underlying function, so this works for strings
    1047 * and arrays.
    1048 * @param {*} a Failure message (3 arguments) or the test value
    1049 * (2 arguments).
    1050 * @param {*} b The test value (3 arguments) or the container
    1051 * (2 arguments).
    1052 * @param {*=} opt_c The container.
    1053 */
    1054var assertContains = function(a, b, opt_c) {
    1055 _validateArguments(2, arguments);
    1056 var contained = nonCommentArg(1, 2, arguments);
    1057 var container = nonCommentArg(2, 2, arguments);
    1058 _assert(commentArg(2, arguments),
    1059 goog.testing.asserts.contains_(container, contained),
    1060 'Expected \'' + container + '\' to contain \'' + contained + '\'');
    1061};
    1062
    1063
    1064/**
    1065 * Checks if the given element is not the member of the given container.
    1066 * @param {*} a Failure message (3 arguments) or the contained element
    1067 * (2 arguments).
    1068 * @param {*} b The contained element (3 arguments) or the container
    1069 * (2 arguments).
    1070 * @param {*=} opt_c The container.
    1071 */
    1072var assertNotContains = function(a, b, opt_c) {
    1073 _validateArguments(2, arguments);
    1074 var contained = nonCommentArg(1, 2, arguments);
    1075 var container = nonCommentArg(2, 2, arguments);
    1076 _assert(commentArg(2, arguments),
    1077 !goog.testing.asserts.contains_(container, contained),
    1078 'Expected \'' + container + '\' not to contain \'' + contained + '\'');
    1079};
    1080
    1081
    1082/**
    1083 * Checks if the given string matches the given regular expression.
    1084 * @param {*} a Failure message (3 arguments) or the expected regular
    1085 * expression as a string or RegExp (2 arguments).
    1086 * @param {*} b The regular expression (3 arguments) or the string to test
    1087 * (2 arguments).
    1088 * @param {*=} opt_c The string to test.
    1089 */
    1090var assertRegExp = function(a, b, opt_c) {
    1091 _validateArguments(2, arguments);
    1092 var regexp = nonCommentArg(1, 2, arguments);
    1093 var string = nonCommentArg(2, 2, arguments);
    1094 if (typeof(regexp) == 'string') {
    1095 regexp = new RegExp(regexp);
    1096 }
    1097 _assert(commentArg(2, arguments),
    1098 regexp.test(string),
    1099 'Expected \'' + string + '\' to match RegExp ' + regexp.toString());
    1100};
    1101
    1102
    1103/**
    1104 * Converts an array like object to array or clones it if it's already array.
    1105 * @param {goog.testing.asserts.ArrayLike} arrayLike The collection.
    1106 * @return {!Array<?>} Copy of the collection as array.
    1107 * @private
    1108 */
    1109goog.testing.asserts.toArray_ = function(arrayLike) {
    1110 var ret = [];
    1111 for (var i = 0; i < arrayLike.length; i++) {
    1112 ret[i] = arrayLike[i];
    1113 }
    1114 return ret;
    1115};
    1116
    1117
    1118/**
    1119 * Finds the position of the first occurrence of an element in a container.
    1120 * @param {goog.testing.asserts.ArrayLike} container
    1121 * The array to find the element in.
    1122 * @param {*} contained Element to find.
    1123 * @return {number} Index of the first occurrence or -1 if not found.
    1124 * @private
    1125 */
    1126goog.testing.asserts.indexOf_ = function(container, contained) {
    1127 if (container.indexOf) {
    1128 return container.indexOf(contained);
    1129 } else {
    1130 // IE6/7 do not have indexOf so do a search.
    1131 for (var i = 0; i < container.length; i++) {
    1132 if (container[i] === contained) {
    1133 return i;
    1134 }
    1135 }
    1136 return -1;
    1137 }
    1138};
    1139
    1140
    1141/**
    1142 * Tells whether the array contains the given element.
    1143 * @param {goog.testing.asserts.ArrayLike} container The array to
    1144 * find the element in.
    1145 * @param {*} contained Element to find.
    1146 * @return {boolean} Whether the element is in the array.
    1147 * @private
    1148 */
    1149goog.testing.asserts.contains_ = function(container, contained) {
    1150 // TODO(user): Can we check for container.contains as well?
    1151 // That would give us support for most goog.structs (though weird results
    1152 // with anything else with a contains method, like goog.math.Range). Falling
    1153 // back with container.some would catch all iterables, too.
    1154 return goog.testing.asserts.indexOf_(container, contained) != -1;
    1155};
    1156
    1157var standardizeHTML = function(html) {
    1158 var translator = document.createElement('DIV');
    1159 translator.innerHTML = html;
    1160
    1161 // Trim whitespace from result (without relying on goog.string)
    1162 return translator.innerHTML.replace(/^\s+|\s+$/g, '');
    1163};
    1164
    1165
    1166/**
    1167 * Standardizes a CSS value for a given property by applying it to an element
    1168 * and then reading it back.
    1169 * @param {string} propertyName CSS property name.
    1170 * @param {string} value CSS value.
    1171 * @return {string} Normalized CSS value.
    1172 */
    1173var standardizeCSSValue = function(propertyName, value) {
    1174 var styleDeclaration = document.createElement('DIV').style;
    1175 styleDeclaration[propertyName] = value;
    1176 return styleDeclaration[propertyName];
    1177};
    1178
    1179
    1180/**
    1181 * Raises a JsUnit exception with the given comment.
    1182 * @param {string} comment A summary for the exception.
    1183 * @param {string=} opt_message A description of the exception.
    1184 */
    1185goog.testing.asserts.raiseException = function(comment, opt_message) {
    1186 throw new goog.testing.JsUnitException(comment, opt_message);
    1187};
    1188
    1189
    1190/**
    1191 * Helper function for assertObjectEquals.
    1192 * @param {string} prop A property name.
    1193 * @return {boolean} If the property name is an array index.
    1194 * @private
    1195 */
    1196goog.testing.asserts.isArrayIndexProp_ = function(prop) {
    1197 return (prop | 0) == prop;
    1198};
    1199
    1200
    1201
    1202/**
    1203 * @param {string} comment A summary for the exception.
    1204 * @param {?string=} opt_message A description of the exception.
    1205 * @constructor
    1206 * @extends {Error}
    1207 * @final
    1208 */
    1209goog.testing.JsUnitException = function(comment, opt_message) {
    1210 this.isJsUnitException = true;
    1211 this.message = (comment ? comment : '') +
    1212 (comment && opt_message ? '\n' : '') +
    1213 (opt_message ? opt_message : '');
    1214 this.stackTrace = goog.testing.stacktrace.get();
    1215 // These fields are for compatibility with jsUnitTestManager.
    1216 this.comment = comment || null;
    1217 this.jsUnitMessage = opt_message || '';
    1218
    1219 // Ensure there is a stack trace.
    1220 if (Error.captureStackTrace) {
    1221 Error.captureStackTrace(this, goog.testing.JsUnitException);
    1222 } else {
    1223 this.stack = new Error().stack || '';
    1224 }
    1225};
    1226goog.inherits(goog.testing.JsUnitException, Error);
    1227
    1228
    1229/** @override */
    1230goog.testing.JsUnitException.prototype.toString = function() {
    1231 return this.message;
    1232};
    1233
    1234
    1235goog.exportSymbol('fail', fail);
    1236goog.exportSymbol('assert', assert);
    1237goog.exportSymbol('assertThrows', assertThrows);
    1238goog.exportSymbol('assertNotThrows', assertNotThrows);
    1239goog.exportSymbol('assertTrue', assertTrue);
    1240goog.exportSymbol('assertFalse', assertFalse);
    1241goog.exportSymbol('assertEquals', assertEquals);
    1242goog.exportSymbol('assertNotEquals', assertNotEquals);
    1243goog.exportSymbol('assertNull', assertNull);
    1244goog.exportSymbol('assertNotNull', assertNotNull);
    1245goog.exportSymbol('assertUndefined', assertUndefined);
    1246goog.exportSymbol('assertNotUndefined', assertNotUndefined);
    1247goog.exportSymbol('assertNotNullNorUndefined', assertNotNullNorUndefined);
    1248goog.exportSymbol('assertNonEmptyString', assertNonEmptyString);
    1249goog.exportSymbol('assertNaN', assertNaN);
    1250goog.exportSymbol('assertNotNaN', assertNotNaN);
    1251goog.exportSymbol('assertObjectEquals', assertObjectEquals);
    1252goog.exportSymbol('assertObjectRoughlyEquals', assertObjectRoughlyEquals);
    1253goog.exportSymbol('assertObjectNotEquals', assertObjectNotEquals);
    1254goog.exportSymbol('assertArrayEquals', assertArrayEquals);
    1255goog.exportSymbol('assertElementsEquals', assertElementsEquals);
    1256goog.exportSymbol('assertElementsRoughlyEqual', assertElementsRoughlyEqual);
    1257goog.exportSymbol('assertSameElements', assertSameElements);
    1258goog.exportSymbol('assertEvaluatesToTrue', assertEvaluatesToTrue);
    1259goog.exportSymbol('assertEvaluatesToFalse', assertEvaluatesToFalse);
    1260goog.exportSymbol('assertHTMLEquals', assertHTMLEquals);
    1261goog.exportSymbol('assertHashEquals', assertHashEquals);
    1262goog.exportSymbol('assertRoughlyEquals', assertRoughlyEquals);
    1263goog.exportSymbol('assertContains', assertContains);
    1264goog.exportSymbol('assertNotContains', assertNotContains);
    1265goog.exportSymbol('assertRegExp', assertRegExp);
    \ 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 index f20dea4..2b4c4ab 100644 --- a/docs/source/lib/goog/testing/events/events.js.src.html +++ b/docs/source/lib/goog/testing/events/events.js.src.html @@ -1 +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 mouseover 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 event's
    298 * 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.fireMouseOverEvent = function(target, relatedTarget,
    303 opt_coords) {
    304 var mouseover =
    305 new goog.testing.events.Event(goog.events.EventType.MOUSEOVER, target);
    306 mouseover.relatedTarget = relatedTarget;
    307 goog.testing.events.setEventClientXY_(mouseover, opt_coords);
    308 return goog.testing.events.fireBrowserEvent(mouseover);
    309};
    310
    311
    312/**
    313 * Simulates a mousemove event on the given target.
    314 * @param {EventTarget} target The target for the event.
    315 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    316 * target's position (if available), otherwise (0, 0).
    317 * @return {boolean} The returnValue of the event: false if preventDefault() was
    318 * called on it, true otherwise.
    319 */
    320goog.testing.events.fireMouseMoveEvent = function(target, opt_coords) {
    321 var mousemove =
    322 new goog.testing.events.Event(goog.events.EventType.MOUSEMOVE, target);
    323
    324 goog.testing.events.setEventClientXY_(mousemove, opt_coords);
    325 return goog.testing.events.fireBrowserEvent(mousemove);
    326};
    327
    328
    329/**
    330 * Simulates a mouseout event on the given target.
    331 * @param {EventTarget} target The target for the event.
    332 * @param {EventTarget} relatedTarget The related target for the event (e.g.,
    333 * the node that the mouse is being moved into).
    334 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    335 * target's position (if available), otherwise (0, 0).
    336 * @return {boolean} The returnValue of the event: false if preventDefault() was
    337 * called on it, true otherwise.
    338 */
    339goog.testing.events.fireMouseOutEvent = function(target, relatedTarget,
    340 opt_coords) {
    341 var mouseout =
    342 new goog.testing.events.Event(goog.events.EventType.MOUSEOUT, target);
    343 mouseout.relatedTarget = relatedTarget;
    344 goog.testing.events.setEventClientXY_(mouseout, opt_coords);
    345 return goog.testing.events.fireBrowserEvent(mouseout);
    346};
    347
    348
    349/**
    350 * Simulates a mousedown event on the given target.
    351 * @param {EventTarget} target The target for the event.
    352 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
    353 * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    354 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    355 * target's position (if available), otherwise (0, 0).
    356 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    357 * BrowserEvent.
    358 * @return {boolean} The returnValue of the event: false if preventDefault() was
    359 * called on it, true otherwise.
    360 */
    361goog.testing.events.fireMouseDownEvent =
    362 function(target, opt_button, opt_coords, opt_eventProperties) {
    363
    364 var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
    365 button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
    366 goog.events.BrowserEvent.IEButtonMap[button] : button;
    367 return goog.testing.events.fireMouseButtonEvent_(
    368 goog.events.EventType.MOUSEDOWN, target, button, opt_coords,
    369 opt_eventProperties);
    370};
    371
    372
    373/**
    374 * Simulates a mouseup event on the given target.
    375 * @param {EventTarget} target The target for the event.
    376 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
    377 * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    378 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    379 * target's position (if available), otherwise (0, 0).
    380 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    381 * BrowserEvent.
    382 * @return {boolean} The returnValue of the event: false if preventDefault() was
    383 * called on it, true otherwise.
    384 */
    385goog.testing.events.fireMouseUpEvent =
    386 function(target, opt_button, opt_coords, opt_eventProperties) {
    387 var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
    388 button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
    389 goog.events.BrowserEvent.IEButtonMap[button] : button;
    390 return goog.testing.events.fireMouseButtonEvent_(
    391 goog.events.EventType.MOUSEUP, target, button, opt_coords,
    392 opt_eventProperties);
    393};
    394
    395
    396/**
    397 * Simulates a click event on the given target. IE only supports click with
    398 * the left mouse button.
    399 * @param {EventTarget} target The target for the event.
    400 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
    401 * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    402 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    403 * target's position (if available), otherwise (0, 0).
    404 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    405 * BrowserEvent.
    406 * @return {boolean} The returnValue of the event: false if preventDefault() was
    407 * called on it, true otherwise.
    408 */
    409goog.testing.events.fireClickEvent =
    410 function(target, opt_button, opt_coords, opt_eventProperties) {
    411 return goog.testing.events.fireMouseButtonEvent_(goog.events.EventType.CLICK,
    412 target, opt_button, opt_coords, opt_eventProperties);
    413};
    414
    415
    416/**
    417 * Simulates a double-click event on the given target. Always double-clicks
    418 * with the left mouse button since no browser supports double-clicking with
    419 * any other buttons.
    420 * @param {EventTarget} target The target for the event.
    421 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    422 * target's position (if available), otherwise (0, 0).
    423 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    424 * BrowserEvent.
    425 * @return {boolean} The returnValue of the event: false if preventDefault() was
    426 * called on it, true otherwise.
    427 */
    428goog.testing.events.fireDoubleClickEvent =
    429 function(target, opt_coords, opt_eventProperties) {
    430 return goog.testing.events.fireMouseButtonEvent_(
    431 goog.events.EventType.DBLCLICK, target,
    432 goog.events.BrowserEvent.MouseButton.LEFT, opt_coords,
    433 opt_eventProperties);
    434};
    435
    436
    437/**
    438 * Helper function to fire a mouse event.
    439 * with the left mouse button since no browser supports double-clicking with
    440 * any other buttons.
    441 * @param {string} type The event type.
    442 * @param {EventTarget} target The target for the event.
    443 * @param {number=} opt_button Mouse button; defaults to
    444 * {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    445 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    446 * target's position (if available), otherwise (0, 0).
    447 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    448 * BrowserEvent.
    449 * @return {boolean} The returnValue of the event: false if preventDefault() was
    450 * called on it, true otherwise.
    451 * @private
    452 */
    453goog.testing.events.fireMouseButtonEvent_ =
    454 function(type, target, opt_button, opt_coords, opt_eventProperties) {
    455 var e =
    456 new goog.testing.events.Event(type, target);
    457 e.button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
    458 goog.testing.events.setEventClientXY_(e, opt_coords);
    459 if (opt_eventProperties) {
    460 goog.object.extend(e, opt_eventProperties);
    461 }
    462 return goog.testing.events.fireBrowserEvent(e);
    463};
    464
    465
    466/**
    467 * Simulates a contextmenu event on the given target.
    468 * @param {EventTarget} target The target for the event.
    469 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    470 * target's position (if available), otherwise (0, 0).
    471 * @return {boolean} The returnValue of the event: false if preventDefault() was
    472 * called on it, true otherwise.
    473 */
    474goog.testing.events.fireContextMenuEvent = function(target, opt_coords) {
    475 var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
    476 goog.events.BrowserEvent.MouseButton.LEFT :
    477 goog.events.BrowserEvent.MouseButton.RIGHT;
    478 var contextmenu =
    479 new goog.testing.events.Event(goog.events.EventType.CONTEXTMENU, target);
    480 contextmenu.button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
    481 goog.events.BrowserEvent.IEButtonMap[button] : button;
    482 contextmenu.ctrlKey = goog.userAgent.MAC;
    483 goog.testing.events.setEventClientXY_(contextmenu, opt_coords);
    484 return goog.testing.events.fireBrowserEvent(contextmenu);
    485};
    486
    487
    488/**
    489 * Simulates a mousedown, contextmenu, and the mouseup on the given event
    490 * target, with the right mouse button.
    491 * @param {EventTarget} target The target for the event.
    492 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    493 * target's position (if available), otherwise (0, 0).
    494 * @return {boolean} The returnValue of the sequence: false if preventDefault()
    495 * was called on any of the events, true otherwise.
    496 */
    497goog.testing.events.fireContextMenuSequence = function(target, opt_coords) {
    498 var props = goog.userAgent.MAC ? {ctrlKey: true} : {};
    499 var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
    500 goog.events.BrowserEvent.MouseButton.LEFT :
    501 goog.events.BrowserEvent.MouseButton.RIGHT;
    502
    503 var result = goog.testing.events.fireMouseDownEvent(target,
    504 button, opt_coords, props);
    505 if (goog.userAgent.WINDOWS) {
    506 // All browsers are consistent on Windows.
    507 result &= goog.testing.events.fireMouseUpEvent(target,
    508 button, opt_coords) &
    509 goog.testing.events.fireContextMenuEvent(target, opt_coords);
    510 } else {
    511 result &= goog.testing.events.fireContextMenuEvent(target, opt_coords);
    512
    513 // GECKO on Mac and Linux always fires the mouseup after the contextmenu.
    514
    515 // WEBKIT is really weird.
    516 //
    517 // On Linux, it sometimes fires mouseup, but most of the time doesn't.
    518 // It's really hard to reproduce consistently. I think there's some
    519 // internal race condition. If contextmenu is preventDefaulted, then
    520 // mouseup always fires.
    521 //
    522 // On Mac, it always fires mouseup and then fires a click.
    523 result &= goog.testing.events.fireMouseUpEvent(target,
    524 button, opt_coords, props);
    525
    526 if (goog.userAgent.WEBKIT && goog.userAgent.MAC) {
    527 result &= goog.testing.events.fireClickEvent(
    528 target, button, opt_coords, props);
    529 }
    530 }
    531 return !!result;
    532};
    533
    534
    535/**
    536 * Simulates a popstate event on the given target.
    537 * @param {EventTarget} target The target for the event.
    538 * @param {Object} state History state object.
    539 * @return {boolean} The returnValue of the event: false if preventDefault() was
    540 * called on it, true otherwise.
    541 */
    542goog.testing.events.firePopStateEvent = function(target, state) {
    543 var e = new goog.testing.events.Event(goog.events.EventType.POPSTATE, target);
    544 e.state = state;
    545 return goog.testing.events.fireBrowserEvent(e);
    546};
    547
    548
    549/**
    550 * Simulate a blur event on the given target.
    551 * @param {EventTarget} target The target for the event.
    552 * @return {boolean} The value returned by firing the blur browser event,
    553 * which returns false iff 'preventDefault' was invoked.
    554 */
    555goog.testing.events.fireBlurEvent = function(target) {
    556 var e = new goog.testing.events.Event(
    557 goog.events.EventType.BLUR, target);
    558 return goog.testing.events.fireBrowserEvent(e);
    559};
    560
    561
    562/**
    563 * Simulate a focus event on the given target.
    564 * @param {EventTarget} target The target for the event.
    565 * @return {boolean} The value returned by firing the focus browser event,
    566 * which returns false iff 'preventDefault' was invoked.
    567 */
    568goog.testing.events.fireFocusEvent = function(target) {
    569 var e = new goog.testing.events.Event(
    570 goog.events.EventType.FOCUS, target);
    571 return goog.testing.events.fireBrowserEvent(e);
    572};
    573
    574
    575/**
    576 * Simulates an event's capturing and bubbling phases.
    577 * @param {Event} event A simulated native event. It will be wrapped in a
    578 * normalized BrowserEvent and dispatched to Closure listeners on all
    579 * ancestors of its target (inclusive).
    580 * @return {boolean} The returnValue of the event: false if preventDefault() was
    581 * called on it, true otherwise.
    582 */
    583goog.testing.events.fireBrowserEvent = function(event) {
    584 event.returnValue_ = true;
    585
    586 // generate a list of ancestors
    587 var ancestors = [];
    588 for (var current = event.target; current; current = current.parentNode) {
    589 ancestors.push(current);
    590 }
    591
    592 // dispatch capturing listeners
    593 for (var j = ancestors.length - 1;
    594 j >= 0 && !event.propagationStopped_;
    595 j--) {
    596 goog.events.fireListeners(ancestors[j], event.type, true,
    597 new goog.events.BrowserEvent(event, ancestors[j]));
    598 }
    599
    600 // dispatch bubbling listeners
    601 for (var j = 0;
    602 j < ancestors.length && !event.propagationStopped_;
    603 j++) {
    604 goog.events.fireListeners(ancestors[j], event.type, false,
    605 new goog.events.BrowserEvent(event, ancestors[j]));
    606 }
    607
    608 return event.returnValue_;
    609};
    610
    611
    612/**
    613 * Simulates a touchstart event on the given target.
    614 * @param {EventTarget} target The target for the event.
    615 * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
    616 * target's position (if available), otherwise (0, 0).
    617 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    618 * BrowserEvent.
    619 * @return {boolean} The returnValue of the event: false if preventDefault() was
    620 * called on it, true otherwise.
    621 */
    622goog.testing.events.fireTouchStartEvent = function(
    623 target, opt_coords, opt_eventProperties) {
    624 // TODO: Support multi-touch events with array of coordinates.
    625 var touchstart =
    626 new goog.testing.events.Event(goog.events.EventType.TOUCHSTART, target);
    627 goog.testing.events.setEventClientXY_(touchstart, opt_coords);
    628 if (opt_eventProperties) {
    629 goog.object.extend(touchstart, opt_eventProperties);
    630 }
    631 return goog.testing.events.fireBrowserEvent(touchstart);
    632};
    633
    634
    635/**
    636 * Simulates a touchmove event on the given target.
    637 * @param {EventTarget} target The target for the event.
    638 * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
    639 * target's position (if available), otherwise (0, 0).
    640 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    641 * BrowserEvent.
    642 * @return {boolean} The returnValue of the event: false if preventDefault() was
    643 * called on it, true otherwise.
    644 */
    645goog.testing.events.fireTouchMoveEvent = function(
    646 target, opt_coords, opt_eventProperties) {
    647 // TODO: Support multi-touch events with array of coordinates.
    648 var touchmove =
    649 new goog.testing.events.Event(goog.events.EventType.TOUCHMOVE, target);
    650 goog.testing.events.setEventClientXY_(touchmove, opt_coords);
    651 if (opt_eventProperties) {
    652 goog.object.extend(touchmove, opt_eventProperties);
    653 }
    654 return goog.testing.events.fireBrowserEvent(touchmove);
    655};
    656
    657
    658/**
    659 * Simulates a touchend event on the given target.
    660 * @param {EventTarget} target The target for the event.
    661 * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
    662 * target's position (if available), otherwise (0, 0).
    663 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    664 * BrowserEvent.
    665 * @return {boolean} The returnValue of the event: false if preventDefault() was
    666 * called on it, true otherwise.
    667 */
    668goog.testing.events.fireTouchEndEvent = function(
    669 target, opt_coords, opt_eventProperties) {
    670 // TODO: Support multi-touch events with array of coordinates.
    671 var touchend =
    672 new goog.testing.events.Event(goog.events.EventType.TOUCHEND, target);
    673 goog.testing.events.setEventClientXY_(touchend, opt_coords);
    674 if (opt_eventProperties) {
    675 goog.object.extend(touchend, opt_eventProperties);
    676 }
    677 return goog.testing.events.fireBrowserEvent(touchend);
    678};
    679
    680
    681/**
    682 * Simulates a simple touch sequence on the given target.
    683 * @param {EventTarget} target The target for the event.
    684 * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event
    685 * target's position (if available), otherwise (0, 0).
    686 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    687 * BrowserEvent.
    688 * @return {boolean} The returnValue of the sequence: false if preventDefault()
    689 * was called on any of the events, true otherwise.
    690 */
    691goog.testing.events.fireTouchSequence = function(
    692 target, opt_coords, opt_eventProperties) {
    693 // TODO: Support multi-touch events with array of coordinates.
    694 // Fire touchstart, touchmove, touchend then return the bitwise AND of the 3.
    695 return !!(goog.testing.events.fireTouchStartEvent(
    696 target, opt_coords, opt_eventProperties) &
    697 goog.testing.events.fireTouchEndEvent(
    698 target, opt_coords, opt_eventProperties));
    699};
    700
    701
    702/**
    703 * Mixins a listenable into the given object. This turns the object
    704 * into a goog.events.Listenable. This is useful, for example, when
    705 * you need to mock a implementation of listenable and still want it
    706 * to work with goog.events.
    707 * @param {!Object} obj The object to mixin into.
    708 */
    709goog.testing.events.mixinListenable = function(obj) {
    710 var listenable = new goog.events.EventTarget();
    711
    712 listenable.setTargetForTesting(obj);
    713
    714 var listenablePrototype = goog.events.EventTarget.prototype;
    715 var disposablePrototype = goog.Disposable.prototype;
    716 for (var key in listenablePrototype) {
    717 if (listenablePrototype.hasOwnProperty(key) ||
    718 disposablePrototype.hasOwnProperty(key)) {
    719 var member = listenablePrototype[key];
    720 if (goog.isFunction(member)) {
    721 obj[key] = goog.bind(member, listenable);
    722 } else {
    723 obj[key] = member;
    724 }
    725 }
    726 }
    727};
    \ No newline at end of file +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 mouseover 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 event's
    298 * 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.fireMouseOverEvent = function(target, relatedTarget,
    303 opt_coords) {
    304 var mouseover =
    305 new goog.testing.events.Event(goog.events.EventType.MOUSEOVER, target);
    306 mouseover.relatedTarget = relatedTarget;
    307 goog.testing.events.setEventClientXY_(mouseover, opt_coords);
    308 return goog.testing.events.fireBrowserEvent(mouseover);
    309};
    310
    311
    312/**
    313 * Simulates a mousemove event on the given target.
    314 * @param {EventTarget} target The target for the event.
    315 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    316 * target's position (if available), otherwise (0, 0).
    317 * @return {boolean} The returnValue of the event: false if preventDefault() was
    318 * called on it, true otherwise.
    319 */
    320goog.testing.events.fireMouseMoveEvent = function(target, opt_coords) {
    321 var mousemove =
    322 new goog.testing.events.Event(goog.events.EventType.MOUSEMOVE, target);
    323
    324 goog.testing.events.setEventClientXY_(mousemove, opt_coords);
    325 return goog.testing.events.fireBrowserEvent(mousemove);
    326};
    327
    328
    329/**
    330 * Simulates a mouseout event on the given target.
    331 * @param {EventTarget} target The target for the event.
    332 * @param {EventTarget} relatedTarget The related target for the event (e.g.,
    333 * the node that the mouse is being moved into).
    334 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    335 * target's position (if available), otherwise (0, 0).
    336 * @return {boolean} The returnValue of the event: false if preventDefault() was
    337 * called on it, true otherwise.
    338 */
    339goog.testing.events.fireMouseOutEvent = function(target, relatedTarget,
    340 opt_coords) {
    341 var mouseout =
    342 new goog.testing.events.Event(goog.events.EventType.MOUSEOUT, target);
    343 mouseout.relatedTarget = relatedTarget;
    344 goog.testing.events.setEventClientXY_(mouseout, opt_coords);
    345 return goog.testing.events.fireBrowserEvent(mouseout);
    346};
    347
    348
    349/**
    350 * Simulates a mousedown event on the given target.
    351 * @param {EventTarget} target The target for the event.
    352 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
    353 * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    354 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    355 * target's position (if available), otherwise (0, 0).
    356 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    357 * BrowserEvent.
    358 * @return {boolean} The returnValue of the event: false if preventDefault() was
    359 * called on it, true otherwise.
    360 */
    361goog.testing.events.fireMouseDownEvent =
    362 function(target, opt_button, opt_coords, opt_eventProperties) {
    363
    364 var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
    365 button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
    366 goog.events.BrowserEvent.IEButtonMap[button] : button;
    367 return goog.testing.events.fireMouseButtonEvent_(
    368 goog.events.EventType.MOUSEDOWN, target, button, opt_coords,
    369 opt_eventProperties);
    370};
    371
    372
    373/**
    374 * Simulates a mouseup event on the given target.
    375 * @param {EventTarget} target The target for the event.
    376 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
    377 * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    378 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    379 * target's position (if available), otherwise (0, 0).
    380 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    381 * BrowserEvent.
    382 * @return {boolean} The returnValue of the event: false if preventDefault() was
    383 * called on it, true otherwise.
    384 */
    385goog.testing.events.fireMouseUpEvent =
    386 function(target, opt_button, opt_coords, opt_eventProperties) {
    387 var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
    388 button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
    389 goog.events.BrowserEvent.IEButtonMap[button] : button;
    390 return goog.testing.events.fireMouseButtonEvent_(
    391 goog.events.EventType.MOUSEUP, target, button, opt_coords,
    392 opt_eventProperties);
    393};
    394
    395
    396/**
    397 * Simulates a click event on the given target. IE only supports click with
    398 * the left mouse button.
    399 * @param {EventTarget} target The target for the event.
    400 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
    401 * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    402 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    403 * target's position (if available), otherwise (0, 0).
    404 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    405 * BrowserEvent.
    406 * @return {boolean} The returnValue of the event: false if preventDefault() was
    407 * called on it, true otherwise.
    408 */
    409goog.testing.events.fireClickEvent =
    410 function(target, opt_button, opt_coords, opt_eventProperties) {
    411 return goog.testing.events.fireMouseButtonEvent_(goog.events.EventType.CLICK,
    412 target, opt_button, opt_coords, opt_eventProperties);
    413};
    414
    415
    416/**
    417 * Simulates a double-click event on the given target. Always double-clicks
    418 * with the left mouse button since no browser supports double-clicking with
    419 * any other buttons.
    420 * @param {EventTarget} target The target for the event.
    421 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    422 * target's position (if available), otherwise (0, 0).
    423 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    424 * BrowserEvent.
    425 * @return {boolean} The returnValue of the event: false if preventDefault() was
    426 * called on it, true otherwise.
    427 */
    428goog.testing.events.fireDoubleClickEvent =
    429 function(target, opt_coords, opt_eventProperties) {
    430 return goog.testing.events.fireMouseButtonEvent_(
    431 goog.events.EventType.DBLCLICK, target,
    432 goog.events.BrowserEvent.MouseButton.LEFT, opt_coords,
    433 opt_eventProperties);
    434};
    435
    436
    437/**
    438 * Helper function to fire a mouse event.
    439 * with the left mouse button since no browser supports double-clicking with
    440 * any other buttons.
    441 * @param {string} type The event type.
    442 * @param {EventTarget} target The target for the event.
    443 * @param {number=} opt_button Mouse button; defaults to
    444 * {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    445 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    446 * target's position (if available), otherwise (0, 0).
    447 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    448 * BrowserEvent.
    449 * @return {boolean} The returnValue of the event: false if preventDefault() was
    450 * called on it, true otherwise.
    451 * @private
    452 */
    453goog.testing.events.fireMouseButtonEvent_ =
    454 function(type, target, opt_button, opt_coords, opt_eventProperties) {
    455 var e =
    456 new goog.testing.events.Event(type, target);
    457 e.button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
    458 goog.testing.events.setEventClientXY_(e, opt_coords);
    459 if (opt_eventProperties) {
    460 goog.object.extend(e, opt_eventProperties);
    461 }
    462 return goog.testing.events.fireBrowserEvent(e);
    463};
    464
    465
    466/**
    467 * Simulates a contextmenu event on the given target.
    468 * @param {EventTarget} target The target for the event.
    469 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    470 * target's position (if available), otherwise (0, 0).
    471 * @return {boolean} The returnValue of the event: false if preventDefault() was
    472 * called on it, true otherwise.
    473 */
    474goog.testing.events.fireContextMenuEvent = function(target, opt_coords) {
    475 var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
    476 goog.events.BrowserEvent.MouseButton.LEFT :
    477 goog.events.BrowserEvent.MouseButton.RIGHT;
    478 var contextmenu =
    479 new goog.testing.events.Event(goog.events.EventType.CONTEXTMENU, target);
    480 contextmenu.button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
    481 goog.events.BrowserEvent.IEButtonMap[button] : button;
    482 contextmenu.ctrlKey = goog.userAgent.MAC;
    483 goog.testing.events.setEventClientXY_(contextmenu, opt_coords);
    484 return goog.testing.events.fireBrowserEvent(contextmenu);
    485};
    486
    487
    488/**
    489 * Simulates a mousedown, contextmenu, and the mouseup on the given event
    490 * target, with the right mouse button.
    491 * @param {EventTarget} target The target for the event.
    492 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    493 * target's position (if available), otherwise (0, 0).
    494 * @return {boolean} The returnValue of the sequence: false if preventDefault()
    495 * was called on any of the events, true otherwise.
    496 */
    497goog.testing.events.fireContextMenuSequence = function(target, opt_coords) {
    498 var props = goog.userAgent.MAC ? {ctrlKey: true} : {};
    499 var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
    500 goog.events.BrowserEvent.MouseButton.LEFT :
    501 goog.events.BrowserEvent.MouseButton.RIGHT;
    502
    503 var result = goog.testing.events.fireMouseDownEvent(target,
    504 button, opt_coords, props);
    505 if (goog.userAgent.WINDOWS) {
    506 // All browsers are consistent on Windows.
    507 result &= goog.testing.events.fireMouseUpEvent(target,
    508 button, opt_coords) &
    509 goog.testing.events.fireContextMenuEvent(target, opt_coords);
    510 } else {
    511 result &= goog.testing.events.fireContextMenuEvent(target, opt_coords);
    512
    513 // GECKO on Mac and Linux always fires the mouseup after the contextmenu.
    514
    515 // WEBKIT is really weird.
    516 //
    517 // On Linux, it sometimes fires mouseup, but most of the time doesn't.
    518 // It's really hard to reproduce consistently. I think there's some
    519 // internal race condition. If contextmenu is preventDefaulted, then
    520 // mouseup always fires.
    521 //
    522 // On Mac, it always fires mouseup and then fires a click.
    523 result &= goog.testing.events.fireMouseUpEvent(target,
    524 button, opt_coords, props);
    525
    526 if (goog.userAgent.WEBKIT && goog.userAgent.MAC) {
    527 result &= goog.testing.events.fireClickEvent(
    528 target, button, opt_coords, props);
    529 }
    530 }
    531 return !!result;
    532};
    533
    534
    535/**
    536 * Simulates a popstate event on the given target.
    537 * @param {EventTarget} target The target for the event.
    538 * @param {Object} state History state object.
    539 * @return {boolean} The returnValue of the event: false if preventDefault() was
    540 * called on it, true otherwise.
    541 */
    542goog.testing.events.firePopStateEvent = function(target, state) {
    543 var e = new goog.testing.events.Event(goog.events.EventType.POPSTATE, target);
    544 e.state = state;
    545 return goog.testing.events.fireBrowserEvent(e);
    546};
    547
    548
    549/**
    550 * Simulate a blur event on the given target.
    551 * @param {EventTarget} target The target for the event.
    552 * @return {boolean} The value returned by firing the blur browser event,
    553 * which returns false iff 'preventDefault' was invoked.
    554 */
    555goog.testing.events.fireBlurEvent = function(target) {
    556 var e = new goog.testing.events.Event(
    557 goog.events.EventType.BLUR, target);
    558 return goog.testing.events.fireBrowserEvent(e);
    559};
    560
    561
    562/**
    563 * Simulate a focus event on the given target.
    564 * @param {EventTarget} target The target for the event.
    565 * @return {boolean} The value returned by firing the focus browser event,
    566 * which returns false iff 'preventDefault' was invoked.
    567 */
    568goog.testing.events.fireFocusEvent = function(target) {
    569 var e = new goog.testing.events.Event(
    570 goog.events.EventType.FOCUS, target);
    571 return goog.testing.events.fireBrowserEvent(e);
    572};
    573
    574
    575/**
    576 * Simulates an event's capturing and bubbling phases.
    577 * @param {Event} event A simulated native event. It will be wrapped in a
    578 * normalized BrowserEvent and dispatched to Closure listeners on all
    579 * ancestors of its target (inclusive).
    580 * @return {boolean} The returnValue of the event: false if preventDefault() was
    581 * called on it, true otherwise.
    582 */
    583goog.testing.events.fireBrowserEvent = function(event) {
    584 event.returnValue_ = true;
    585
    586 // generate a list of ancestors
    587 var ancestors = [];
    588 for (var current = event.target; current; current = current.parentNode) {
    589 ancestors.push(current);
    590 }
    591
    592 // dispatch capturing listeners
    593 for (var j = ancestors.length - 1;
    594 j >= 0 && !event.propagationStopped_;
    595 j--) {
    596 goog.events.fireListeners(ancestors[j], event.type, true,
    597 new goog.events.BrowserEvent(event, ancestors[j]));
    598 }
    599
    600 // dispatch bubbling listeners
    601 for (var j = 0;
    602 j < ancestors.length && !event.propagationStopped_;
    603 j++) {
    604 goog.events.fireListeners(ancestors[j], event.type, false,
    605 new goog.events.BrowserEvent(event, ancestors[j]));
    606 }
    607
    608 return event.returnValue_;
    609};
    610
    611
    612/**
    613 * Simulates a touchstart event on the given target.
    614 * @param {EventTarget} target The target for the event.
    615 * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
    616 * target's position (if available), otherwise (0, 0).
    617 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    618 * BrowserEvent.
    619 * @return {boolean} The returnValue of the event: false if preventDefault() was
    620 * called on it, true otherwise.
    621 */
    622goog.testing.events.fireTouchStartEvent = function(
    623 target, opt_coords, opt_eventProperties) {
    624 // TODO: Support multi-touch events with array of coordinates.
    625 var touchstart =
    626 new goog.testing.events.Event(goog.events.EventType.TOUCHSTART, target);
    627 goog.testing.events.setEventClientXY_(touchstart, opt_coords);
    628 if (opt_eventProperties) {
    629 goog.object.extend(touchstart, opt_eventProperties);
    630 }
    631 return goog.testing.events.fireBrowserEvent(touchstart);
    632};
    633
    634
    635/**
    636 * Simulates a touchmove event on the given target.
    637 * @param {EventTarget} target The target for the event.
    638 * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
    639 * target's position (if available), otherwise (0, 0).
    640 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    641 * BrowserEvent.
    642 * @return {boolean} The returnValue of the event: false if preventDefault() was
    643 * called on it, true otherwise.
    644 */
    645goog.testing.events.fireTouchMoveEvent = function(
    646 target, opt_coords, opt_eventProperties) {
    647 // TODO: Support multi-touch events with array of coordinates.
    648 var touchmove =
    649 new goog.testing.events.Event(goog.events.EventType.TOUCHMOVE, target);
    650 goog.testing.events.setEventClientXY_(touchmove, opt_coords);
    651 if (opt_eventProperties) {
    652 goog.object.extend(touchmove, opt_eventProperties);
    653 }
    654 return goog.testing.events.fireBrowserEvent(touchmove);
    655};
    656
    657
    658/**
    659 * Simulates a touchend event on the given target.
    660 * @param {EventTarget} target The target for the event.
    661 * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
    662 * target's position (if available), otherwise (0, 0).
    663 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    664 * BrowserEvent.
    665 * @return {boolean} The returnValue of the event: false if preventDefault() was
    666 * called on it, true otherwise.
    667 */
    668goog.testing.events.fireTouchEndEvent = function(
    669 target, opt_coords, opt_eventProperties) {
    670 // TODO: Support multi-touch events with array of coordinates.
    671 var touchend =
    672 new goog.testing.events.Event(goog.events.EventType.TOUCHEND, target);
    673 goog.testing.events.setEventClientXY_(touchend, opt_coords);
    674 if (opt_eventProperties) {
    675 goog.object.extend(touchend, opt_eventProperties);
    676 }
    677 return goog.testing.events.fireBrowserEvent(touchend);
    678};
    679
    680
    681/**
    682 * Simulates a simple touch sequence on the given target.
    683 * @param {EventTarget} target The target for the event.
    684 * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event
    685 * target's position (if available), otherwise (0, 0).
    686 * @param {Object=} opt_eventProperties Event properties to be mixed into the
    687 * BrowserEvent.
    688 * @return {boolean} The returnValue of the sequence: false if preventDefault()
    689 * was called on any of the events, true otherwise.
    690 */
    691goog.testing.events.fireTouchSequence = function(
    692 target, opt_coords, opt_eventProperties) {
    693 // TODO: Support multi-touch events with array of coordinates.
    694 // Fire touchstart, touchmove, touchend then return the bitwise AND of the 3.
    695 return !!(goog.testing.events.fireTouchStartEvent(
    696 target, opt_coords, opt_eventProperties) &
    697 goog.testing.events.fireTouchEndEvent(
    698 target, opt_coords, opt_eventProperties));
    699};
    700
    701
    702/**
    703 * Mixins a listenable into the given object. This turns the object
    704 * into a goog.events.Listenable. This is useful, for example, when
    705 * you need to mock a implementation of listenable and still want it
    706 * to work with goog.events.
    707 * @param {!Object} obj The object to mixin into.
    708 */
    709goog.testing.events.mixinListenable = function(obj) {
    710 var listenable = new goog.events.EventTarget();
    711
    712 listenable.setTargetForTesting(obj);
    713
    714 var listenablePrototype = goog.events.EventTarget.prototype;
    715 var disposablePrototype = goog.Disposable.prototype;
    716 for (var key in listenablePrototype) {
    717 if (listenablePrototype.hasOwnProperty(key) ||
    718 disposablePrototype.hasOwnProperty(key)) {
    719 var member = listenablePrototype[key];
    720 if (goog.isFunction(member)) {
    721 obj[key] = goog.bind(member, listenable);
    722 } else {
    723 obj[key] = member;
    724 }
    725 }
    726 }
    727};
    \ 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 index 820a40f..4fbf530 100644 --- a/docs/source/lib/goog/testing/functionmock.js.src.html +++ b/docs/source/lib/goog/testing/functionmock.js.src.html @@ -1 +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.MockInterface');
    32goog.require('goog.testing.PropertyReplacer');
    33goog.require('goog.testing.StrictMock');
    34
    35
    36/**
    37 * Class used to mock a function. Useful for mocking closures and anonymous
    38 * callbacks etc. Creates a function object that extends goog.testing.Mock.
    39 * @param {string=} opt_functionName The optional name of the function to mock.
    40 * Set to '[anonymous mocked function]' if not passed in.
    41 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    42 * goog.testing.Mock.STRICT. The default is STRICT.
    43 * @return {goog.testing.MockInterface} The mocked function.
    44 * @suppress {missingProperties} Mocks do not fit in the type system well.
    45 */
    46goog.testing.FunctionMock = function(opt_functionName, opt_strictness) {
    47 var fn = function() {
    48 var args = Array.prototype.slice.call(arguments);
    49 args.splice(0, 0, opt_functionName || '[anonymous mocked function]');
    50 return fn.$mockMethod.apply(fn, args);
    51 };
    52 var base = opt_strictness === goog.testing.Mock.LOOSE ?
    53 goog.testing.LooseMock : goog.testing.StrictMock;
    54 goog.object.extend(fn, new base({}));
    55
    56 return /** @type {goog.testing.MockInterface} */ (fn);
    57};
    58
    59
    60/**
    61 * Mocks an existing function. Creates a goog.testing.FunctionMock
    62 * and registers it in the given scope with the name specified by functionName.
    63 * @param {Object} scope The scope of the method to be mocked out.
    64 * @param {string} functionName The name of the function we're going to mock.
    65 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    66 * goog.testing.Mock.STRICT. The default is STRICT.
    67 * @return {!goog.testing.MockInterface} The mocked method.
    68 */
    69goog.testing.MethodMock = function(scope, functionName, opt_strictness) {
    70 if (!(functionName in scope)) {
    71 throw Error(functionName + ' is not a property of the given scope.');
    72 }
    73
    74 var fn = goog.testing.FunctionMock(functionName, opt_strictness);
    75
    76 fn.$propertyReplacer_ = new goog.testing.PropertyReplacer();
    77 fn.$propertyReplacer_.set(scope, functionName, fn);
    78 fn.$tearDown = goog.testing.MethodMock.$tearDown;
    79
    80 return fn;
    81};
    82
    83
    84/**
    85 * Resets the global function that we mocked back to its original state.
    86 * @this {goog.testing.MockInterface}
    87 */
    88goog.testing.MethodMock.$tearDown = function() {
    89 this.$propertyReplacer_.reset();
    90};
    91
    92
    93/**
    94 * Mocks a global / top-level function. Creates a goog.testing.MethodMock
    95 * in the global scope with the name specified by functionName.
    96 * @param {string} functionName The name of the function we're going to mock.
    97 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    98 * goog.testing.Mock.STRICT. The default is STRICT.
    99 * @return {!goog.testing.MockInterface} The mocked global function.
    100 */
    101goog.testing.GlobalFunctionMock = function(functionName, opt_strictness) {
    102 return goog.testing.MethodMock(goog.global, functionName, opt_strictness);
    103};
    104
    105
    106/**
    107 * Convenience method for creating a mock for a function.
    108 * @param {string=} opt_functionName The optional name of the function to mock
    109 * set to '[anonymous mocked function]' if not passed in.
    110 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    111 * goog.testing.Mock.STRICT. The default is STRICT.
    112 * @return {goog.testing.MockInterface} The mocked function.
    113 */
    114goog.testing.createFunctionMock = function(opt_functionName, opt_strictness) {
    115 return goog.testing.FunctionMock(opt_functionName, opt_strictness);
    116};
    117
    118
    119/**
    120 * Convenience method for creating a mock for a method.
    121 * @param {Object} scope The scope of the method to be mocked out.
    122 * @param {string} functionName The name of the function we're going to mock.
    123 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    124 * goog.testing.Mock.STRICT. The default is STRICT.
    125 * @return {!goog.testing.MockInterface} The mocked global function.
    126 */
    127goog.testing.createMethodMock = function(scope, functionName, opt_strictness) {
    128 return goog.testing.MethodMock(scope, functionName, opt_strictness);
    129};
    130
    131
    132/**
    133 * Convenience method for creating a mock for a constructor. Copies class
    134 * members to the mock.
    135 *
    136 * <p>When mocking a constructor to return a mocked instance, remember to create
    137 * the instance mock before mocking the constructor. If you mock the constructor
    138 * first, then the mock framework will be unable to examine the prototype chain
    139 * when creating the mock instance.
    140 * @param {Object} scope The scope of the constructor to be mocked out.
    141 * @param {string} constructorName The name of the constructor we're going to
    142 * mock.
    143 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    144 * goog.testing.Mock.STRICT. The default is STRICT.
    145 * @return {!goog.testing.MockInterface} The mocked constructor.
    146 */
    147goog.testing.createConstructorMock = function(scope, constructorName,
    148 opt_strictness) {
    149 var realConstructor = scope[constructorName];
    150 var constructorMock = goog.testing.MethodMock(scope, constructorName,
    151 opt_strictness);
    152
    153 // Copy class members from the real constructor to the mock. Do not copy
    154 // the closure superClass_ property (see goog.inherits), the built-in
    155 // prototype property, or properties added to Function.prototype
    156 // (see goog.MODIFY_FUNCTION_PROTOTYPES in closure/base.js).
    157 for (var property in realConstructor) {
    158 if (property != 'superClass_' &&
    159 property != 'prototype' &&
    160 realConstructor.hasOwnProperty(property)) {
    161 constructorMock[property] = realConstructor[property];
    162 }
    163 }
    164 return constructorMock;
    165};
    166
    167
    168/**
    169 * Convenience method for creating a mocks for a global / top-level function.
    170 * @param {string} functionName The name of the function we're going to mock.
    171 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    172 * goog.testing.Mock.STRICT. The default is STRICT.
    173 * @return {goog.testing.MockInterface} The mocked global function.
    174 */
    175goog.testing.createGlobalFunctionMock = function(functionName, opt_strictness) {
    176 return goog.testing.GlobalFunctionMock(functionName, opt_strictness);
    177};
    \ No newline at end of file +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 // (see goog.MODIFY_FUNCTION_PROTOTYPES in closure/base.js).
    156 for (var property in realConstructor) {
    157 if (property != 'superClass_' &&
    158 property != 'prototype' &&
    159 realConstructor.hasOwnProperty(property)) {
    160 constructorMock[property] = realConstructor[property];
    161 }
    162 }
    163 return constructorMock;
    164};
    165
    166
    167/**
    168 * Convenience method for creating a mocks for a global / top-level function.
    169 * @param {string} functionName The name of the function we're going to mock.
    170 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    171 * goog.testing.Mock.STRICT. The default is STRICT.
    172 * @return {!goog.testing.MockInterface} The mocked global function.
    173 */
    174goog.testing.createGlobalFunctionMock = function(functionName, opt_strictness) {
    175 return goog.testing.GlobalFunctionMock(functionName, opt_strictness);
    176};
    \ 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 index a3424ac..d93c3b7 100644 --- a/docs/source/lib/goog/testing/jsunit.js.src.html +++ b/docs/source/lib/goog/testing/jsunit.js.src.html @@ -1 +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.testing.TestCase');
    27goog.require('goog.testing.TestRunner');
    28
    29
    30/**
    31 * Base path for JsUnit app files, relative to Closure's base path.
    32 * @type {string}
    33 */
    34goog.testing.jsunit.BASE_PATH =
    35 '../../third_party/java/jsunit/core/app/';
    36
    37
    38/**
    39 * Filename for the core JS Unit script.
    40 * @type {string}
    41 */
    42goog.testing.jsunit.CORE_SCRIPT =
    43 goog.testing.jsunit.BASE_PATH + 'jsUnitCore.js';
    44
    45
    46/**
    47 * @define {boolean} If this code is being parsed by JsTestC, we let it disable
    48 * the onload handler to avoid running the test in JsTestC.
    49 */
    50goog.define('goog.testing.jsunit.AUTO_RUN_ONLOAD', true);
    51
    52
    53/**
    54 * @define {number} Sets a delay in milliseconds after the window onload event
    55 * and running the tests. Used to prevent interference with Selenium and give
    56 * tests with asynchronous operations time to finish loading.
    57 */
    58goog.define('goog.testing.jsunit.AUTO_RUN_DELAY_IN_MS', 500);
    59
    60
    61(function() {
    62 // Increases the maximum number of stack frames in Google Chrome from the
    63 // default 10 to 50 to get more useful stack traces.
    64 Error.stackTraceLimit = 50;
    65
    66 // Store a reference to the window's timeout so that it can't be overridden
    67 // by tests.
    68 /** @type {!Function} */
    69 var realTimeout = window.setTimeout;
    70
    71 // Check for JsUnit's test runner (need to check for >2.2 and <=2.2)
    72 if (top['JsUnitTestManager'] || top['jsUnitTestManager']) {
    73 // Running inside JsUnit so add support code.
    74 var path = goog.basePath + goog.testing.jsunit.CORE_SCRIPT;
    75 document.write('<script type="text/javascript" src="' +
    76 path + '"></' + 'script>');
    77
    78 } else {
    79
    80 // Create a test runner.
    81 var tr = new goog.testing.TestRunner();
    82
    83 // Export it so that it can be queried by Selenium and tests that use a
    84 // compiled test runner.
    85 goog.exportSymbol('G_testRunner', tr);
    86 goog.exportSymbol('G_testRunner.initialize', tr.initialize);
    87 goog.exportSymbol('G_testRunner.isInitialized', tr.isInitialized);
    88 goog.exportSymbol('G_testRunner.isFinished', tr.isFinished);
    89 goog.exportSymbol('G_testRunner.isSuccess', tr.isSuccess);
    90 goog.exportSymbol('G_testRunner.getReport', tr.getReport);
    91 goog.exportSymbol('G_testRunner.getRunTime', tr.getRunTime);
    92 goog.exportSymbol('G_testRunner.getNumFilesLoaded', tr.getNumFilesLoaded);
    93 goog.exportSymbol('G_testRunner.setStrict', tr.setStrict);
    94 goog.exportSymbol('G_testRunner.logTestFailure', tr.logTestFailure);
    95 goog.exportSymbol('G_testRunner.getTestResults', tr.getTestResults);
    96
    97 // Export debug as a global function for JSUnit compatibility. This just
    98 // calls log on the current test case.
    99 if (!goog.global['debug']) {
    100 goog.exportSymbol('debug', goog.bind(tr.log, tr));
    101 }
    102
    103 // If the application has defined a global error filter, set it now. This
    104 // allows users who use a base test include to set the error filter before
    105 // the testing code is loaded.
    106 if (goog.global['G_errorFilter']) {
    107 tr.setErrorFilter(goog.global['G_errorFilter']);
    108 }
    109
    110 // Add an error handler to report errors that may occur during
    111 // initialization of the page.
    112 var onerror = window.onerror;
    113 window.onerror = function(error, url, line) {
    114 // Call any existing onerror handlers.
    115 if (onerror) {
    116 onerror(error, url, line);
    117 }
    118 if (typeof error == 'object') {
    119 // Webkit started passing an event object as the only argument to
    120 // window.onerror. It doesn't contain an error message, url or line
    121 // number. We therefore log as much info as we can.
    122 if (error.target && error.target.tagName == 'SCRIPT') {
    123 tr.logError('UNKNOWN ERROR: Script ' + error.target.src);
    124 } else {
    125 tr.logError('UNKNOWN ERROR: No error information available.');
    126 }
    127 } else {
    128 tr.logError('JS ERROR: ' + error + '\nURL: ' + url + '\nLine: ' + line);
    129 }
    130 };
    131
    132 // Create an onload handler, if the test runner hasn't been initialized then
    133 // no test has been registered with the test runner by the test file. We
    134 // then create a new test case and auto discover any tests in the global
    135 // scope. If this code is being parsed by JsTestC, we let it disable the
    136 // onload handler to avoid running the test in JsTestC.
    137 if (goog.testing.jsunit.AUTO_RUN_ONLOAD) {
    138 var onload = window.onload;
    139 window.onload = function(e) {
    140 // Call any existing onload handlers.
    141 if (onload) {
    142 onload(e);
    143 }
    144 // Wait so that we don't interfere with WebDriver.
    145 realTimeout(function() {
    146 if (!tr.initialized) {
    147 var test = new goog.testing.TestCase(document.title);
    148 test.autoDiscoverTests();
    149 tr.initialize(test);
    150 }
    151 tr.execute();
    152 }, goog.testing.jsunit.AUTO_RUN_DELAY_IN_MS);
    153 window.onload = null;
    154 };
    155 }
    156 }
    157})();
    \ No newline at end of file +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 index 9aab9bc..1aacbe1 100644 --- a/docs/source/lib/goog/testing/loosemock.js.src.html +++ b/docs/source/lib/goog/testing/loosemock.js.src.html @@ -1 +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 +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 index db8ae1e..e7fef80 100644 --- a/docs/source/lib/goog/testing/mock.js.src.html +++ b/docs/source/lib/goog/testing/mock.js.src.html @@ -1 +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}
    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 verfifier 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 +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 verfifier 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 index e81c49b..01b8354 100644 --- a/docs/source/lib/goog/testing/mockclock.js.src.html +++ b/docs/source/lib/goog/testing/mockclock.js.src.html @@ -1 +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');
    31goog.require('goog.testing.watchers');
    32
    33
    34
    35/**
    36 * Class for unit testing code that uses setTimeout and clearTimeout.
    37 *
    38 * NOTE: If you are using MockClock to test code that makes use of
    39 * goog.fx.Animation, then you must either:
    40 *
    41 * 1. Install and dispose of the MockClock in setUpPage() and tearDownPage()
    42 * respectively (rather than setUp()/tearDown()).
    43 *
    44 * or
    45 *
    46 * 2. Ensure that every test clears the animation queue by calling
    47 * mockClock.tick(x) at the end of each test function (where `x` is large
    48 * enough to complete all animations).
    49 *
    50 * Otherwise, if any animation is left pending at the time that
    51 * MockClock.dispose() is called, that will permanently prevent any future
    52 * animations from playing on the page.
    53 *
    54 * @param {boolean=} opt_autoInstall Install the MockClock at construction time.
    55 * @constructor
    56 * @extends {goog.Disposable}
    57 * @final
    58 */
    59goog.testing.MockClock = function(opt_autoInstall) {
    60 goog.Disposable.call(this);
    61
    62 /**
    63 * Reverse-order queue of timers to fire.
    64 *
    65 * The last item of the queue is popped off. Insertion happens from the
    66 * right. For example, the expiration times for each element of the queue
    67 * might be in the order 300, 200, 200.
    68 *
    69 * @type {Array.<Object>}
    70 * @private
    71 */
    72 this.queue_ = [];
    73
    74 /**
    75 * Set of timeouts that should be treated as cancelled.
    76 *
    77 * Rather than removing cancelled timers directly from the queue, this set
    78 * simply marks them as deleted so that they can be ignored when their
    79 * turn comes up. The keys are the timeout keys that are cancelled, each
    80 * mapping to true.
    81 *
    82 * @type {Object}
    83 * @private
    84 */
    85 this.deletedKeys_ = {};
    86
    87 if (opt_autoInstall) {
    88 this.install();
    89 }
    90};
    91goog.inherits(goog.testing.MockClock, goog.Disposable);
    92
    93
    94/**
    95 * Default wait timeout for mocking requestAnimationFrame (in milliseconds).
    96 *
    97 * @type {number}
    98 * @const
    99 */
    100goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT = 20;
    101
    102
    103/**
    104 * Count of the number of timeouts made.
    105 * @type {number}
    106 * @private
    107 */
    108goog.testing.MockClock.prototype.timeoutsMade_ = 0;
    109
    110
    111/**
    112 * PropertyReplacer instance which overwrites and resets setTimeout,
    113 * setInterval, etc. or null if the MockClock is not installed.
    114 * @type {goog.testing.PropertyReplacer}
    115 * @private
    116 */
    117goog.testing.MockClock.prototype.replacer_ = null;
    118
    119
    120/**
    121 * Map of deleted keys. These keys represents keys that were deleted in a
    122 * clearInterval, timeoutid -> object.
    123 * @type {Object}
    124 * @private
    125 */
    126goog.testing.MockClock.prototype.deletedKeys_ = null;
    127
    128
    129/**
    130 * The current simulated time in milliseconds.
    131 * @type {number}
    132 * @private
    133 */
    134goog.testing.MockClock.prototype.nowMillis_ = 0;
    135
    136
    137/**
    138 * Additional delay between the time a timeout was set to fire, and the time
    139 * it actually fires. Useful for testing workarounds for this Firefox 2 bug:
    140 * https://bugzilla.mozilla.org/show_bug.cgi?id=291386
    141 * May be negative.
    142 * @type {number}
    143 * @private
    144 */
    145goog.testing.MockClock.prototype.timeoutDelay_ = 0;
    146
    147
    148/**
    149 * Installs the MockClock by overriding the global object's implementation of
    150 * setTimeout, setInterval, clearTimeout and clearInterval.
    151 */
    152goog.testing.MockClock.prototype.install = function() {
    153 if (!this.replacer_) {
    154 var r = this.replacer_ = new goog.testing.PropertyReplacer();
    155 r.set(goog.global, 'setTimeout', goog.bind(this.setTimeout_, this));
    156 r.set(goog.global, 'setInterval', goog.bind(this.setInterval_, this));
    157 r.set(goog.global, 'setImmediate', goog.bind(this.setImmediate_, this));
    158 r.set(goog.global, 'clearTimeout', goog.bind(this.clearTimeout_, this));
    159 r.set(goog.global, 'clearInterval', goog.bind(this.clearInterval_, this));
    160 // goog.Promise uses goog.async.run. In order to be able to test
    161 // Promise-based code, we need to make sure that goog.async.run uses
    162 // nextTick instead of native browser Promises. This means that it will
    163 // default to setImmediate, which is replaced above. Note that we test for
    164 // the presence of goog.async.run.forceNextTick to be resilient to the case
    165 // where tests replace goog.async.run directly.
    166 goog.async.run.forceNextTick && goog.async.run.forceNextTick();
    167
    168 // Replace the requestAnimationFrame functions.
    169 this.replaceRequestAnimationFrame_();
    170
    171 // PropertyReplacer#set can't be called with renameable functions.
    172 this.oldGoogNow_ = goog.now;
    173 goog.now = goog.bind(this.getCurrentTime, this);
    174 }
    175};
    176
    177
    178/**
    179 * Installs the mocks for requestAnimationFrame and cancelRequestAnimationFrame.
    180 * @private
    181 */
    182goog.testing.MockClock.prototype.replaceRequestAnimationFrame_ = function() {
    183 var r = this.replacer_;
    184 var requestFuncs = ['requestAnimationFrame',
    185 'webkitRequestAnimationFrame',
    186 'mozRequestAnimationFrame',
    187 'oRequestAnimationFrame',
    188 'msRequestAnimationFrame'];
    189
    190 var cancelFuncs = ['cancelRequestAnimationFrame',
    191 'webkitCancelRequestAnimationFrame',
    192 'mozCancelRequestAnimationFrame',
    193 'oCancelRequestAnimationFrame',
    194 'msCancelRequestAnimationFrame'];
    195
    196 for (var i = 0; i < requestFuncs.length; ++i) {
    197 if (goog.global && goog.global[requestFuncs[i]]) {
    198 r.set(goog.global, requestFuncs[i],
    199 goog.bind(this.requestAnimationFrame_, this));
    200 }
    201 }
    202
    203 for (var i = 0; i < cancelFuncs.length; ++i) {
    204 if (goog.global && goog.global[cancelFuncs[i]]) {
    205 r.set(goog.global, cancelFuncs[i],
    206 goog.bind(this.cancelRequestAnimationFrame_, this));
    207 }
    208 }
    209};
    210
    211
    212/**
    213 * Removes the MockClock's hooks into the global object's functions and revert
    214 * to their original values.
    215 */
    216goog.testing.MockClock.prototype.uninstall = function() {
    217 if (this.replacer_) {
    218 this.replacer_.reset();
    219 this.replacer_ = null;
    220 goog.now = this.oldGoogNow_;
    221 }
    222
    223 this.fireResetEvent();
    224};
    225
    226
    227/** @override */
    228goog.testing.MockClock.prototype.disposeInternal = function() {
    229 this.uninstall();
    230 this.queue_ = null;
    231 this.deletedKeys_ = null;
    232 goog.testing.MockClock.superClass_.disposeInternal.call(this);
    233};
    234
    235
    236/**
    237 * Resets the MockClock, removing all timeouts that are scheduled and resets
    238 * the fake timer count.
    239 */
    240goog.testing.MockClock.prototype.reset = function() {
    241 this.queue_ = [];
    242 this.deletedKeys_ = {};
    243 this.nowMillis_ = 0;
    244 this.timeoutsMade_ = 0;
    245 this.timeoutDelay_ = 0;
    246
    247 this.fireResetEvent();
    248};
    249
    250
    251/**
    252 * Signals that the mock clock has been reset, allowing objects that
    253 * maintain their own internal state to reset.
    254 */
    255goog.testing.MockClock.prototype.fireResetEvent = function() {
    256 goog.testing.watchers.signalClockReset();
    257};
    258
    259
    260/**
    261 * Sets the amount of time between when a timeout is scheduled to fire and when
    262 * it actually fires.
    263 * @param {number} delay The delay in milliseconds. May be negative.
    264 */
    265goog.testing.MockClock.prototype.setTimeoutDelay = function(delay) {
    266 this.timeoutDelay_ = delay;
    267};
    268
    269
    270/**
    271 * @return {number} delay The amount of time between when a timeout is
    272 * scheduled to fire and when it actually fires, in milliseconds. May
    273 * be negative.
    274 */
    275goog.testing.MockClock.prototype.getTimeoutDelay = function() {
    276 return this.timeoutDelay_;
    277};
    278
    279
    280/**
    281 * Increments the MockClock's time by a given number of milliseconds, running
    282 * any functions that are now overdue.
    283 * @param {number=} opt_millis Number of milliseconds to increment the counter.
    284 * If not specified, clock ticks 1 millisecond.
    285 * @return {number} Current mock time in milliseconds.
    286 */
    287goog.testing.MockClock.prototype.tick = function(opt_millis) {
    288 if (typeof opt_millis != 'number') {
    289 opt_millis = 1;
    290 }
    291 var endTime = this.nowMillis_ + opt_millis;
    292 this.runFunctionsWithinRange_(endTime);
    293 this.nowMillis_ = endTime;
    294 return endTime;
    295};
    296
    297
    298/**
    299 * @return {number} The number of timeouts that have been scheduled.
    300 */
    301goog.testing.MockClock.prototype.getTimeoutsMade = function() {
    302 return this.timeoutsMade_;
    303};
    304
    305
    306/**
    307 * @return {number} The MockClock's current time in milliseconds.
    308 */
    309goog.testing.MockClock.prototype.getCurrentTime = function() {
    310 return this.nowMillis_;
    311};
    312
    313
    314/**
    315 * @param {number} timeoutKey The timeout key.
    316 * @return {boolean} Whether the timer has been set and not cleared,
    317 * independent of the timeout's expiration. In other words, the timeout
    318 * could have passed or could be scheduled for the future. Either way,
    319 * this function returns true or false depending only on whether the
    320 * provided timeoutKey represents a timeout that has been set and not
    321 * cleared.
    322 */
    323goog.testing.MockClock.prototype.isTimeoutSet = function(timeoutKey) {
    324 return timeoutKey <= this.timeoutsMade_ && !this.deletedKeys_[timeoutKey];
    325};
    326
    327
    328/**
    329 * Runs any function that is scheduled before a certain time. Timeouts can
    330 * be made to fire early or late if timeoutDelay_ is non-0.
    331 * @param {number} endTime The latest time in the range, in milliseconds.
    332 * @private
    333 */
    334goog.testing.MockClock.prototype.runFunctionsWithinRange_ = function(
    335 endTime) {
    336 var adjustedEndTime = endTime - this.timeoutDelay_;
    337
    338 // Repeatedly pop off the last item since the queue is always sorted.
    339 while (this.queue_ && this.queue_.length &&
    340 this.queue_[this.queue_.length - 1].runAtMillis <= adjustedEndTime) {
    341 var timeout = this.queue_.pop();
    342
    343 if (!(timeout.timeoutKey in this.deletedKeys_)) {
    344 // Only move time forwards.
    345 this.nowMillis_ = Math.max(this.nowMillis_,
    346 timeout.runAtMillis + this.timeoutDelay_);
    347 // Call timeout in global scope and pass the timeout key as the argument.
    348 timeout.funcToCall.call(goog.global, timeout.timeoutKey);
    349 // In case the interval was cleared in the funcToCall
    350 if (timeout.recurring) {
    351 this.scheduleFunction_(
    352 timeout.timeoutKey, timeout.funcToCall, timeout.millis, true);
    353 }
    354 }
    355 }
    356};
    357
    358
    359/**
    360 * Schedules a function to be run at a certain time.
    361 * @param {number} timeoutKey The timeout key.
    362 * @param {Function} funcToCall The function to call.
    363 * @param {number} millis The number of milliseconds to call it in.
    364 * @param {boolean} recurring Whether to function call should recur.
    365 * @private
    366 */
    367goog.testing.MockClock.prototype.scheduleFunction_ = function(
    368 timeoutKey, funcToCall, millis, recurring) {
    369 if (!goog.isFunction(funcToCall)) {
    370 // Early error for debuggability rather than dying in the next .tick()
    371 throw new TypeError('The provided callback must be a function, not a ' +
    372 typeof funcToCall);
    373 }
    374
    375 var timeout = {
    376 runAtMillis: this.nowMillis_ + millis,
    377 funcToCall: funcToCall,
    378 recurring: recurring,
    379 timeoutKey: timeoutKey,
    380 millis: millis
    381 };
    382
    383 goog.testing.MockClock.insert_(timeout, this.queue_);
    384};
    385
    386
    387/**
    388 * Inserts a timer descriptor into a descending-order queue.
    389 *
    390 * Later-inserted duplicates appear at lower indices. For example, the
    391 * asterisk in (5,4,*,3,2,1) would be the insertion point for 3.
    392 *
    393 * @param {Object} timeout The timeout to insert, with numerical runAtMillis
    394 * property.
    395 * @param {Array.<Object>} queue The queue to insert into, with each element
    396 * having a numerical runAtMillis property.
    397 * @private
    398 */
    399goog.testing.MockClock.insert_ = function(timeout, queue) {
    400 // Although insertion of N items is quadratic, requiring goog.structs.Heap
    401 // from a unit test will make tests more prone to breakage. Since unit
    402 // tests are normally small, scalability is not a primary issue.
    403
    404 // Find an insertion point. Since the queue is in reverse order (so we
    405 // can pop rather than unshift), and later timers with the same time stamp
    406 // should be executed later, we look for the element strictly greater than
    407 // the one we are inserting.
    408
    409 for (var i = queue.length; i != 0; i--) {
    410 if (queue[i - 1].runAtMillis > timeout.runAtMillis) {
    411 break;
    412 }
    413 queue[i] = queue[i - 1];
    414 }
    415
    416 queue[i] = timeout;
    417};
    418
    419
    420/**
    421 * Maximum 32-bit signed integer.
    422 *
    423 * Timeouts over this time return immediately in many browsers, due to integer
    424 * overflow. Such known browsers include Firefox, Chrome, and Safari, but not
    425 * IE.
    426 *
    427 * @type {number}
    428 * @private
    429 */
    430goog.testing.MockClock.MAX_INT_ = 2147483647;
    431
    432
    433/**
    434 * Schedules a function to be called after {@code millis} milliseconds.
    435 * Mock implementation for setTimeout.
    436 * @param {Function} funcToCall The function to call.
    437 * @param {number} millis The number of milliseconds to call it after.
    438 * @return {number} The number of timeouts created.
    439 * @private
    440 */
    441goog.testing.MockClock.prototype.setTimeout_ = function(funcToCall, millis) {
    442 if (millis > goog.testing.MockClock.MAX_INT_) {
    443 throw Error(
    444 'Bad timeout value: ' + millis + '. Timeouts over MAX_INT ' +
    445 '(24.8 days) cause timeouts to be fired ' +
    446 'immediately in most browsers, except for IE.');
    447 }
    448 this.timeoutsMade_ = this.timeoutsMade_ + 1;
    449 this.scheduleFunction_(this.timeoutsMade_, funcToCall, millis, false);
    450 return this.timeoutsMade_;
    451};
    452
    453
    454/**
    455 * Schedules a function to be called every {@code millis} milliseconds.
    456 * Mock implementation for setInterval.
    457 * @param {Function} funcToCall The function to call.
    458 * @param {number} millis The number of milliseconds between calls.
    459 * @return {number} The number of timeouts created.
    460 * @private
    461 */
    462goog.testing.MockClock.prototype.setInterval_ = function(funcToCall, millis) {
    463 this.timeoutsMade_ = this.timeoutsMade_ + 1;
    464 this.scheduleFunction_(this.timeoutsMade_, funcToCall, millis, true);
    465 return this.timeoutsMade_;
    466};
    467
    468
    469/**
    470 * Schedules a function to be called when an animation frame is triggered.
    471 * Mock implementation for requestAnimationFrame.
    472 * @param {Function} funcToCall The function to call.
    473 * @return {number} The number of timeouts created.
    474 * @private
    475 */
    476goog.testing.MockClock.prototype.requestAnimationFrame_ = function(funcToCall) {
    477 return this.setTimeout_(goog.bind(function() {
    478 if (funcToCall) {
    479 funcToCall(this.getCurrentTime());
    480 } else if (goog.global.mozRequestAnimationFrame) {
    481 var event = new goog.testing.events.Event('MozBeforePaint', goog.global);
    482 event['timeStamp'] = this.getCurrentTime();
    483 goog.testing.events.fireBrowserEvent(event);
    484 }
    485 }, this), goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT);
    486};
    487
    488
    489/**
    490 * Schedules a function to be called immediately after the current JS
    491 * execution.
    492 * Mock implementation for setImmediate.
    493 * @param {Function} funcToCall The function to call.
    494 * @return {number} The number of timeouts created.
    495 * @private
    496 */
    497goog.testing.MockClock.prototype.setImmediate_ = function(funcToCall) {
    498 return this.setTimeout_(funcToCall, 0);
    499};
    500
    501
    502/**
    503 * Clears a timeout.
    504 * Mock implementation for clearTimeout.
    505 * @param {number} timeoutKey The timeout key to clear.
    506 * @private
    507 */
    508goog.testing.MockClock.prototype.clearTimeout_ = function(timeoutKey) {
    509 // Some common libraries register static state with timers.
    510 // This is bad. It leads to all sorts of crazy test problems where
    511 // 1) Test A sets up a new mock clock and a static timer.
    512 // 2) Test B sets up a new mock clock, but re-uses the static timer
    513 // from Test A.
    514 // 3) A timeout key from test A gets cleared, breaking a timeout in
    515 // Test B.
    516 //
    517 // For now, we just hackily fail silently if someone tries to clear a timeout
    518 // key before we've allocated it.
    519 // Ideally, we should throw an exception if we see this happening.
    520 //
    521 // TODO(user): We might also try allocating timeout ids from a global
    522 // pool rather than a local pool.
    523 if (this.isTimeoutSet(timeoutKey)) {
    524 this.deletedKeys_[timeoutKey] = true;
    525 }
    526};
    527
    528
    529/**
    530 * Clears an interval.
    531 * Mock implementation for clearInterval.
    532 * @param {number} timeoutKey The interval key to clear.
    533 * @private
    534 */
    535goog.testing.MockClock.prototype.clearInterval_ = function(timeoutKey) {
    536 this.clearTimeout_(timeoutKey);
    537};
    538
    539
    540/**
    541 * Clears a requestAnimationFrame.
    542 * Mock implementation for cancelRequestAnimationFrame.
    543 * @param {number} timeoutKey The requestAnimationFrame key to clear.
    544 * @private
    545 */
    546goog.testing.MockClock.prototype.cancelRequestAnimationFrame_ =
    547 function(timeoutKey) {
    548 this.clearTimeout_(timeoutKey);
    549};
    \ No newline at end of file +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');
    31goog.require('goog.testing.watchers');
    32
    33
    34
    35/**
    36 * Class for unit testing code that uses setTimeout and clearTimeout.
    37 *
    38 * NOTE: If you are using MockClock to test code that makes use of
    39 * goog.fx.Animation, then you must either:
    40 *
    41 * 1. Install and dispose of the MockClock in setUpPage() and tearDownPage()
    42 * respectively (rather than setUp()/tearDown()).
    43 *
    44 * or
    45 *
    46 * 2. Ensure that every test clears the animation queue by calling
    47 * mockClock.tick(x) at the end of each test function (where `x` is large
    48 * enough to complete all animations).
    49 *
    50 * Otherwise, if any animation is left pending at the time that
    51 * MockClock.dispose() is called, that will permanently prevent any future
    52 * animations from playing on the page.
    53 *
    54 * @param {boolean=} opt_autoInstall Install the MockClock at construction time.
    55 * @constructor
    56 * @extends {goog.Disposable}
    57 * @final
    58 */
    59goog.testing.MockClock = function(opt_autoInstall) {
    60 goog.Disposable.call(this);
    61
    62 /**
    63 * Reverse-order queue of timers to fire.
    64 *
    65 * The last item of the queue is popped off. Insertion happens from the
    66 * right. For example, the expiration times for each element of the queue
    67 * might be in the order 300, 200, 200.
    68 *
    69 * @type {Array<Object>}
    70 * @private
    71 */
    72 this.queue_ = [];
    73
    74 /**
    75 * Set of timeouts that should be treated as cancelled.
    76 *
    77 * Rather than removing cancelled timers directly from the queue, this set
    78 * simply marks them as deleted so that they can be ignored when their
    79 * turn comes up. The keys are the timeout keys that are cancelled, each
    80 * mapping to true.
    81 *
    82 * @type {Object}
    83 * @private
    84 */
    85 this.deletedKeys_ = {};
    86
    87 if (opt_autoInstall) {
    88 this.install();
    89 }
    90};
    91goog.inherits(goog.testing.MockClock, goog.Disposable);
    92
    93
    94/**
    95 * Default wait timeout for mocking requestAnimationFrame (in milliseconds).
    96 *
    97 * @type {number}
    98 * @const
    99 */
    100goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT = 20;
    101
    102
    103/**
    104 * ID to use for next timeout. Timeout IDs must never be reused, even across
    105 * MockClock instances.
    106 * @public {number}
    107 */
    108goog.testing.MockClock.nextId = Math.round(Math.random() * 10000);
    109
    110
    111/**
    112 * Count of the number of timeouts made by this instance.
    113 * @type {number}
    114 * @private
    115 */
    116goog.testing.MockClock.prototype.timeoutsMade_ = 0;
    117
    118
    119/**
    120 * PropertyReplacer instance which overwrites and resets setTimeout,
    121 * setInterval, etc. or null if the MockClock is not installed.
    122 * @type {goog.testing.PropertyReplacer}
    123 * @private
    124 */
    125goog.testing.MockClock.prototype.replacer_ = null;
    126
    127
    128/**
    129 * Map of deleted keys. These keys represents keys that were deleted in a
    130 * clearInterval, timeoutid -> object.
    131 * @type {Object}
    132 * @private
    133 */
    134goog.testing.MockClock.prototype.deletedKeys_ = null;
    135
    136
    137/**
    138 * The current simulated time in milliseconds.
    139 * @type {number}
    140 * @private
    141 */
    142goog.testing.MockClock.prototype.nowMillis_ = 0;
    143
    144
    145/**
    146 * Additional delay between the time a timeout was set to fire, and the time
    147 * it actually fires. Useful for testing workarounds for this Firefox 2 bug:
    148 * https://bugzilla.mozilla.org/show_bug.cgi?id=291386
    149 * May be negative.
    150 * @type {number}
    151 * @private
    152 */
    153goog.testing.MockClock.prototype.timeoutDelay_ = 0;
    154
    155
    156/**
    157 * The real set timeout for reference.
    158 * @const @private {!Function}
    159 */
    160goog.testing.MockClock.REAL_SETTIMEOUT_ = goog.global.setTimeout;
    161
    162
    163/**
    164 * Installs the MockClock by overriding the global object's implementation of
    165 * setTimeout, setInterval, clearTimeout and clearInterval.
    166 */
    167goog.testing.MockClock.prototype.install = function() {
    168 if (!this.replacer_) {
    169 if (goog.testing.MockClock.REAL_SETTIMEOUT_ !== goog.global.setTimeout) {
    170 if (typeof console !== 'undefined' && console.warn) {
    171 console.warn('Non default setTimeout detected. ' +
    172 'Use of multiple MockClock instances or other clock mocking ' +
    173 'should be avoided due to unspecified behavior and ' +
    174 'the resulting fragility.');
    175 }
    176 }
    177
    178 var r = this.replacer_ = new goog.testing.PropertyReplacer();
    179 r.set(goog.global, 'setTimeout', goog.bind(this.setTimeout_, this));
    180 r.set(goog.global, 'setInterval', goog.bind(this.setInterval_, this));
    181 r.set(goog.global, 'setImmediate', goog.bind(this.setImmediate_, this));
    182 r.set(goog.global, 'clearTimeout', goog.bind(this.clearTimeout_, this));
    183 r.set(goog.global, 'clearInterval', goog.bind(this.clearInterval_, this));
    184 // goog.Promise uses goog.async.run. In order to be able to test
    185 // Promise-based code, we need to make sure that goog.async.run uses
    186 // nextTick instead of native browser Promises. This means that it will
    187 // default to setImmediate, which is replaced above. Note that we test for
    188 // the presence of goog.async.run.forceNextTick to be resilient to the case
    189 // where tests replace goog.async.run directly.
    190 goog.async.run.forceNextTick && goog.async.run.forceNextTick(
    191 goog.testing.MockClock.REAL_SETTIMEOUT_);
    192
    193 // Replace the requestAnimationFrame functions.
    194 this.replaceRequestAnimationFrame_();
    195
    196 // PropertyReplacer#set can't be called with renameable functions.
    197 this.oldGoogNow_ = goog.now;
    198 goog.now = goog.bind(this.getCurrentTime, this);
    199 }
    200};
    201
    202
    203/**
    204 * Installs the mocks for requestAnimationFrame and cancelRequestAnimationFrame.
    205 * @private
    206 */
    207goog.testing.MockClock.prototype.replaceRequestAnimationFrame_ = function() {
    208 var r = this.replacer_;
    209 var requestFuncs = ['requestAnimationFrame',
    210 'webkitRequestAnimationFrame',
    211 'mozRequestAnimationFrame',
    212 'oRequestAnimationFrame',
    213 'msRequestAnimationFrame'];
    214
    215 var cancelFuncs = ['cancelAnimationFrame',
    216 'cancelRequestAnimationFrame',
    217 'webkitCancelRequestAnimationFrame',
    218 'mozCancelRequestAnimationFrame',
    219 'oCancelRequestAnimationFrame',
    220 'msCancelRequestAnimationFrame'];
    221
    222 for (var i = 0; i < requestFuncs.length; ++i) {
    223 if (goog.global && goog.global[requestFuncs[i]]) {
    224 r.set(goog.global, requestFuncs[i],
    225 goog.bind(this.requestAnimationFrame_, this));
    226 }
    227 }
    228
    229 for (var i = 0; i < cancelFuncs.length; ++i) {
    230 if (goog.global && goog.global[cancelFuncs[i]]) {
    231 r.set(goog.global, cancelFuncs[i],
    232 goog.bind(this.cancelRequestAnimationFrame_, this));
    233 }
    234 }
    235};
    236
    237
    238/**
    239 * Removes the MockClock's hooks into the global object's functions and revert
    240 * to their original values.
    241 */
    242goog.testing.MockClock.prototype.uninstall = function() {
    243 if (this.replacer_) {
    244 this.replacer_.reset();
    245 this.replacer_ = null;
    246 goog.now = this.oldGoogNow_;
    247 }
    248
    249 this.fireResetEvent();
    250};
    251
    252
    253/** @override */
    254goog.testing.MockClock.prototype.disposeInternal = function() {
    255 this.uninstall();
    256 this.queue_ = null;
    257 this.deletedKeys_ = null;
    258 goog.testing.MockClock.superClass_.disposeInternal.call(this);
    259};
    260
    261
    262/**
    263 * Resets the MockClock, removing all timeouts that are scheduled and resets
    264 * the fake timer count.
    265 */
    266goog.testing.MockClock.prototype.reset = function() {
    267 this.queue_ = [];
    268 this.deletedKeys_ = {};
    269 this.nowMillis_ = 0;
    270 this.timeoutsMade_ = 0;
    271 this.timeoutDelay_ = 0;
    272
    273 this.fireResetEvent();
    274};
    275
    276
    277/**
    278 * Signals that the mock clock has been reset, allowing objects that
    279 * maintain their own internal state to reset.
    280 */
    281goog.testing.MockClock.prototype.fireResetEvent = function() {
    282 goog.testing.watchers.signalClockReset();
    283};
    284
    285
    286/**
    287 * Sets the amount of time between when a timeout is scheduled to fire and when
    288 * it actually fires.
    289 * @param {number} delay The delay in milliseconds. May be negative.
    290 */
    291goog.testing.MockClock.prototype.setTimeoutDelay = function(delay) {
    292 this.timeoutDelay_ = delay;
    293};
    294
    295
    296/**
    297 * @return {number} delay The amount of time between when a timeout is
    298 * scheduled to fire and when it actually fires, in milliseconds. May
    299 * be negative.
    300 */
    301goog.testing.MockClock.prototype.getTimeoutDelay = function() {
    302 return this.timeoutDelay_;
    303};
    304
    305
    306/**
    307 * Increments the MockClock's time by a given number of milliseconds, running
    308 * any functions that are now overdue.
    309 * @param {number=} opt_millis Number of milliseconds to increment the counter.
    310 * If not specified, clock ticks 1 millisecond.
    311 * @return {number} Current mock time in milliseconds.
    312 */
    313goog.testing.MockClock.prototype.tick = function(opt_millis) {
    314 if (typeof opt_millis != 'number') {
    315 opt_millis = 1;
    316 }
    317 var endTime = this.nowMillis_ + opt_millis;
    318 this.runFunctionsWithinRange_(endTime);
    319 this.nowMillis_ = endTime;
    320 return endTime;
    321};
    322
    323
    324/**
    325 * Takes a promise and then ticks the mock clock. If the promise successfully
    326 * resolves, returns the value produced by the promise. If the promise is
    327 * rejected, it throws the rejection as an exception. If the promise is not
    328 * resolved at all, throws an exception.
    329 * Also ticks the general clock by the specified amount.
    330 *
    331 * @param {!goog.Thenable<T>} promise A promise that should be resolved after
    332 * the mockClock is ticked for the given opt_millis.
    333 * @param {number=} opt_millis Number of milliseconds to increment the counter.
    334 * If not specified, clock ticks 1 millisecond.
    335 * @return {T}
    336 * @template T
    337 */
    338goog.testing.MockClock.prototype.tickPromise = function(promise, opt_millis) {
    339 var value;
    340 var error;
    341 var resolved = false;
    342 promise.then(function(v) {
    343 value = v;
    344 resolved = true;
    345 }, function(e) {
    346 error = e;
    347 resolved = true;
    348 });
    349 this.tick(opt_millis);
    350 if (!resolved) {
    351 throw new Error(
    352 'Promise was expected to be resolved after mock clock tick.');
    353 }
    354 if (error) {
    355 throw error;
    356 }
    357 return value;
    358};
    359
    360
    361/**
    362 * @return {number} The number of timeouts that have been scheduled.
    363 */
    364goog.testing.MockClock.prototype.getTimeoutsMade = function() {
    365 return this.timeoutsMade_;
    366};
    367
    368
    369/**
    370 * @return {number} The MockClock's current time in milliseconds.
    371 */
    372goog.testing.MockClock.prototype.getCurrentTime = function() {
    373 return this.nowMillis_;
    374};
    375
    376
    377/**
    378 * @param {number} timeoutKey The timeout key.
    379 * @return {boolean} Whether the timer has been set and not cleared,
    380 * independent of the timeout's expiration. In other words, the timeout
    381 * could have passed or could be scheduled for the future. Either way,
    382 * this function returns true or false depending only on whether the
    383 * provided timeoutKey represents a timeout that has been set and not
    384 * cleared.
    385 */
    386goog.testing.MockClock.prototype.isTimeoutSet = function(timeoutKey) {
    387 return timeoutKey < goog.testing.MockClock.nextId &&
    388 timeoutKey >= goog.testing.MockClock.nextId - this.timeoutsMade_ &&
    389 !this.deletedKeys_[timeoutKey];
    390};
    391
    392
    393/**
    394 * Runs any function that is scheduled before a certain time. Timeouts can
    395 * be made to fire early or late if timeoutDelay_ is non-0.
    396 * @param {number} endTime The latest time in the range, in milliseconds.
    397 * @private
    398 */
    399goog.testing.MockClock.prototype.runFunctionsWithinRange_ = function(
    400 endTime) {
    401 var adjustedEndTime = endTime - this.timeoutDelay_;
    402
    403 // Repeatedly pop off the last item since the queue is always sorted.
    404 while (this.queue_ && this.queue_.length &&
    405 this.queue_[this.queue_.length - 1].runAtMillis <= adjustedEndTime) {
    406 var timeout = this.queue_.pop();
    407
    408 if (!(timeout.timeoutKey in this.deletedKeys_)) {
    409 // Only move time forwards.
    410 this.nowMillis_ = Math.max(this.nowMillis_,
    411 timeout.runAtMillis + this.timeoutDelay_);
    412 // Call timeout in global scope and pass the timeout key as the argument.
    413 timeout.funcToCall.call(goog.global, timeout.timeoutKey);
    414 // In case the interval was cleared in the funcToCall
    415 if (timeout.recurring) {
    416 this.scheduleFunction_(
    417 timeout.timeoutKey, timeout.funcToCall, timeout.millis, true);
    418 }
    419 }
    420 }
    421};
    422
    423
    424/**
    425 * Schedules a function to be run at a certain time.
    426 * @param {number} timeoutKey The timeout key.
    427 * @param {Function} funcToCall The function to call.
    428 * @param {number} millis The number of milliseconds to call it in.
    429 * @param {boolean} recurring Whether to function call should recur.
    430 * @private
    431 */
    432goog.testing.MockClock.prototype.scheduleFunction_ = function(
    433 timeoutKey, funcToCall, millis, recurring) {
    434 if (!goog.isFunction(funcToCall)) {
    435 // Early error for debuggability rather than dying in the next .tick()
    436 throw new TypeError('The provided callback must be a function, not a ' +
    437 typeof funcToCall);
    438 }
    439
    440 var timeout = {
    441 runAtMillis: this.nowMillis_ + millis,
    442 funcToCall: funcToCall,
    443 recurring: recurring,
    444 timeoutKey: timeoutKey,
    445 millis: millis
    446 };
    447
    448 goog.testing.MockClock.insert_(timeout, this.queue_);
    449};
    450
    451
    452/**
    453 * Inserts a timer descriptor into a descending-order queue.
    454 *
    455 * Later-inserted duplicates appear at lower indices. For example, the
    456 * asterisk in (5,4,*,3,2,1) would be the insertion point for 3.
    457 *
    458 * @param {Object} timeout The timeout to insert, with numerical runAtMillis
    459 * property.
    460 * @param {Array<Object>} queue The queue to insert into, with each element
    461 * having a numerical runAtMillis property.
    462 * @private
    463 */
    464goog.testing.MockClock.insert_ = function(timeout, queue) {
    465 // Although insertion of N items is quadratic, requiring goog.structs.Heap
    466 // from a unit test will make tests more prone to breakage. Since unit
    467 // tests are normally small, scalability is not a primary issue.
    468
    469 // Find an insertion point. Since the queue is in reverse order (so we
    470 // can pop rather than unshift), and later timers with the same time stamp
    471 // should be executed later, we look for the element strictly greater than
    472 // the one we are inserting.
    473
    474 for (var i = queue.length; i != 0; i--) {
    475 if (queue[i - 1].runAtMillis > timeout.runAtMillis) {
    476 break;
    477 }
    478 queue[i] = queue[i - 1];
    479 }
    480
    481 queue[i] = timeout;
    482};
    483
    484
    485/**
    486 * Maximum 32-bit signed integer.
    487 *
    488 * Timeouts over this time return immediately in many browsers, due to integer
    489 * overflow. Such known browsers include Firefox, Chrome, and Safari, but not
    490 * IE.
    491 *
    492 * @type {number}
    493 * @private
    494 */
    495goog.testing.MockClock.MAX_INT_ = 2147483647;
    496
    497
    498/**
    499 * Schedules a function to be called after {@code millis} milliseconds.
    500 * Mock implementation for setTimeout.
    501 * @param {Function} funcToCall The function to call.
    502 * @param {number=} opt_millis The number of milliseconds to call it after.
    503 * @return {number} The number of timeouts created.
    504 * @private
    505 */
    506goog.testing.MockClock.prototype.setTimeout_ = function(
    507 funcToCall, opt_millis) {
    508 var millis = opt_millis || 0;
    509 if (millis > goog.testing.MockClock.MAX_INT_) {
    510 throw Error(
    511 'Bad timeout value: ' + millis + '. Timeouts over MAX_INT ' +
    512 '(24.8 days) cause timeouts to be fired ' +
    513 'immediately in most browsers, except for IE.');
    514 }
    515 this.timeoutsMade_++;
    516 this.scheduleFunction_(goog.testing.MockClock.nextId, funcToCall, millis,
    517 false);
    518 return goog.testing.MockClock.nextId++;
    519};
    520
    521
    522/**
    523 * Schedules a function to be called every {@code millis} milliseconds.
    524 * Mock implementation for setInterval.
    525 * @param {Function} funcToCall The function to call.
    526 * @param {number=} opt_millis The number of milliseconds between calls.
    527 * @return {number} The number of timeouts created.
    528 * @private
    529 */
    530goog.testing.MockClock.prototype.setInterval_ =
    531 function(funcToCall, opt_millis) {
    532 var millis = opt_millis || 0;
    533 this.timeoutsMade_++;
    534 this.scheduleFunction_(goog.testing.MockClock.nextId, funcToCall, millis,
    535 true);
    536 return goog.testing.MockClock.nextId++;
    537};
    538
    539
    540/**
    541 * Schedules a function to be called when an animation frame is triggered.
    542 * Mock implementation for requestAnimationFrame.
    543 * @param {Function} funcToCall The function to call.
    544 * @return {number} The number of timeouts created.
    545 * @private
    546 */
    547goog.testing.MockClock.prototype.requestAnimationFrame_ = function(funcToCall) {
    548 return this.setTimeout_(goog.bind(function() {
    549 if (funcToCall) {
    550 funcToCall(this.getCurrentTime());
    551 } else if (goog.global.mozRequestAnimationFrame) {
    552 var event = new goog.testing.events.Event('MozBeforePaint', goog.global);
    553 event['timeStamp'] = this.getCurrentTime();
    554 goog.testing.events.fireBrowserEvent(event);
    555 }
    556 }, this), goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT);
    557};
    558
    559
    560/**
    561 * Schedules a function to be called immediately after the current JS
    562 * execution.
    563 * Mock implementation for setImmediate.
    564 * @param {Function} funcToCall The function to call.
    565 * @return {number} The number of timeouts created.
    566 * @private
    567 */
    568goog.testing.MockClock.prototype.setImmediate_ = function(funcToCall) {
    569 return this.setTimeout_(funcToCall, 0);
    570};
    571
    572
    573/**
    574 * Clears a timeout.
    575 * Mock implementation for clearTimeout.
    576 * @param {number} timeoutKey The timeout key to clear.
    577 * @private
    578 */
    579goog.testing.MockClock.prototype.clearTimeout_ = function(timeoutKey) {
    580 // Some common libraries register static state with timers.
    581 // This is bad. It leads to all sorts of crazy test problems where
    582 // 1) Test A sets up a new mock clock and a static timer.
    583 // 2) Test B sets up a new mock clock, but re-uses the static timer
    584 // from Test A.
    585 // 3) A timeout key from test A gets cleared, breaking a timeout in
    586 // Test B.
    587 //
    588 // For now, we just hackily fail silently if someone tries to clear a timeout
    589 // key before we've allocated it.
    590 // Ideally, we should throw an exception if we see this happening.
    591 if (this.isTimeoutSet(timeoutKey)) {
    592 this.deletedKeys_[timeoutKey] = true;
    593 }
    594};
    595
    596
    597/**
    598 * Clears an interval.
    599 * Mock implementation for clearInterval.
    600 * @param {number} timeoutKey The interval key to clear.
    601 * @private
    602 */
    603goog.testing.MockClock.prototype.clearInterval_ = function(timeoutKey) {
    604 this.clearTimeout_(timeoutKey);
    605};
    606
    607
    608/**
    609 * Clears a requestAnimationFrame.
    610 * Mock implementation for cancelRequestAnimationFrame.
    611 * @param {number} timeoutKey The requestAnimationFrame key to clear.
    612 * @private
    613 */
    614goog.testing.MockClock.prototype.cancelRequestAnimationFrame_ =
    615 function(timeoutKey) {
    616 this.clearTimeout_(timeoutKey);
    617};
    \ 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 index 966951f..f596062 100644 --- a/docs/source/lib/goog/testing/mockcontrol.js.src.html +++ b/docs/source/lib/goog/testing/mockcontrol.js.src.html @@ -1 +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 +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 index e536030..b644f39 100644 --- a/docs/source/lib/goog/testing/mockinterface.js.src.html +++ b/docs/source/lib/goog/testing/mockinterface.js.src.html @@ -1 +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 +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 index 8d055a5..fda5916 100644 --- a/docs/source/lib/goog/testing/mockmatchers.js.src.html +++ b/docs/source/lib/goog/testing/mockmatchers.js.src.html @@ -1 +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 +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 index 6c949da..df83670 100644 --- a/docs/source/lib/goog/testing/objectpropertystring.js.src.html +++ b/docs/source/lib/goog/testing/objectpropertystring.js.src.html @@ -1 +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 +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 index 36e2f64..db7bce8 100644 --- a/docs/source/lib/goog/testing/propertyreplacer.js.src.html +++ b/docs/source/lib/goog/testing/propertyreplacer.js.src.html @@ -1 +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 * <pre>var stubs = new goog.testing.PropertyReplacer();
    37 *
    38 * function setUp() {
    39 * // Mock functions used in all test cases.
    40 * stubs.set(Math, 'random', function() {
    41 * return 4; // Chosen by fair dice roll. Guaranteed to be random.
    42 * });
    43 * }
    44 *
    45 * function tearDown() {
    46 * stubs.reset();
    47 * }
    48 *
    49 * function testThreeDice() {
    50 * // Mock a constant used only in this test case.
    51 * stubs.set(goog.global, 'DICE_COUNT', 3);
    52 * assertEquals(12, rollAllDice());
    53 * }</pre>
    54 *
    55 * Constraints on altered objects:
    56 * <ul>
    57 * <li>DOM subclasses aren't supported.
    58 * <li>The value of the objects' constructor property must either be equal to
    59 * the real constructor or kept untouched.
    60 * </ul>
    61 *
    62 * @constructor
    63 * @final
    64 */
    65goog.testing.PropertyReplacer = function() {
    66 /**
    67 * Stores the values changed by the set() method in chronological order.
    68 * Its items are objects with 3 fields: 'object', 'key', 'value'. The
    69 * original value for the given key in the given object is stored under the
    70 * 'value' key.
    71 * @type {Array.<Object>}
    72 * @private
    73 */
    74 this.original_ = [];
    75};
    76
    77
    78/**
    79 * Indicates that a key didn't exist before having been set by the set() method.
    80 * @type {Object}
    81 * @private
    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 * @private
    133 */
    134goog.testing.PropertyReplacer.deleteKey_ = function(obj, key) {
    135 try {
    136 delete obj[key];
    137 // Delete has no effect for built-in properties of DOM nodes in FF.
    138 if (!goog.testing.PropertyReplacer.hasKey_(obj, key)) {
    139 return;
    140 }
    141 } catch (e) {
    142 // IE throws TypeError when trying to delete properties of native objects
    143 // (e.g. DOM nodes or window), even if they have been added by JavaScript.
    144 }
    145
    146 obj[key] = undefined;
    147 if (obj[key] == 'undefined') {
    148 // Some properties such as className in IE are always evaluated as string
    149 // so undefined will become 'undefined'.
    150 obj[key] = '';
    151 }
    152};
    153
    154
    155/**
    156 * Adds or changes a value in an object while saving its original state.
    157 * @param {Object|Function} obj The JavaScript or native object or function to
    158 * alter. See the constraints in the class description.
    159 * @param {string} key The key to change the value for.
    160 * @param {*} value The new value to set.
    161 */
    162goog.testing.PropertyReplacer.prototype.set = function(obj, key, value) {
    163 var origValue = goog.testing.PropertyReplacer.hasKey_(obj, key) ? obj[key] :
    164 goog.testing.PropertyReplacer.NO_SUCH_KEY_;
    165 this.original_.push({object: obj, key: key, value: origValue});
    166 obj[key] = value;
    167};
    168
    169
    170/**
    171 * Changes an existing value in an object to another one of the same type while
    172 * saving its original state. The advantage of {@code replace} over {@link #set}
    173 * is that {@code replace} protects against typos and erroneously passing tests
    174 * after some members have been renamed during a refactoring.
    175 * @param {Object|Function} obj The JavaScript or native object or function to
    176 * alter. See the constraints in the class description.
    177 * @param {string} key The key to change the value for. It has to be present
    178 * either in {@code obj} or in its prototype chain.
    179 * @param {*} value The new value to set. It has to have the same type as the
    180 * original value. The types are compared with {@link goog.typeOf}.
    181 * @throws {Error} In case of missing key or type mismatch.
    182 */
    183goog.testing.PropertyReplacer.prototype.replace = function(obj, key, value) {
    184 if (!(key in obj)) {
    185 throw Error('Cannot replace missing property "' + key + '" in ' + obj);
    186 }
    187 if (goog.typeOf(obj[key]) != goog.typeOf(value)) {
    188 throw Error('Cannot replace property "' + key + '" in ' + obj +
    189 ' with a value of different type');
    190 }
    191 this.set(obj, key, value);
    192};
    193
    194
    195/**
    196 * Builds an object structure for the provided namespace path. Doesn't
    197 * overwrite those prefixes of the path that are already objects or functions.
    198 * @param {string} path The path to create or alter, e.g. 'goog.ui.Menu'.
    199 * @param {*} value The value to set.
    200 */
    201goog.testing.PropertyReplacer.prototype.setPath = function(path, value) {
    202 var parts = path.split('.');
    203 var obj = goog.global;
    204 for (var i = 0; i < parts.length - 1; i++) {
    205 var part = parts[i];
    206 if (part == 'prototype' && !obj[part]) {
    207 throw Error('Cannot set the prototype of ' + parts.slice(0, i).join('.'));
    208 }
    209 if (!goog.isObject(obj[part]) && !goog.isFunction(obj[part])) {
    210 this.set(obj, part, {});
    211 }
    212 obj = obj[part];
    213 }
    214 this.set(obj, parts[parts.length - 1], value);
    215};
    216
    217
    218/**
    219 * Deletes the key from the object while saving its original value.
    220 * @param {Object|Function} obj The JavaScript or native object or function to
    221 * alter. See the constraints in the class description.
    222 * @param {string} key The key to delete.
    223 */
    224goog.testing.PropertyReplacer.prototype.remove = function(obj, key) {
    225 if (goog.testing.PropertyReplacer.hasKey_(obj, key)) {
    226 this.original_.push({object: obj, key: key, value: obj[key]});
    227 goog.testing.PropertyReplacer.deleteKey_(obj, key);
    228 }
    229};
    230
    231
    232/**
    233 * Resets all changes made by goog.testing.PropertyReplacer.prototype.set.
    234 */
    235goog.testing.PropertyReplacer.prototype.reset = function() {
    236 for (var i = this.original_.length - 1; i >= 0; i--) {
    237 var original = this.original_[i];
    238 if (original.value == goog.testing.PropertyReplacer.NO_SUCH_KEY_) {
    239 goog.testing.PropertyReplacer.deleteKey_(original.object, original.key);
    240 } else {
    241 original.object[original.key] = original.value;
    242 }
    243 delete this.original_[i];
    244 }
    245 this.original_.length = 0;
    246};
    \ No newline at end of file +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 * <pre>var stubs = new goog.testing.PropertyReplacer();
    37 *
    38 * function setUp() {
    39 * // Mock functions used in all test cases.
    40 * stubs.set(Math, 'random', function() {
    41 * return 4; // Chosen by fair dice roll. Guaranteed to be random.
    42 * });
    43 * }
    44 *
    45 * function tearDown() {
    46 * stubs.reset();
    47 * }
    48 *
    49 * function testThreeDice() {
    50 * // Mock a constant used only in this test case.
    51 * stubs.set(goog.global, 'DICE_COUNT', 3);
    52 * assertEquals(12, rollAllDice());
    53 * }</pre>
    54 *
    55 * Constraints on altered objects:
    56 * <ul>
    57 * <li>DOM subclasses aren't supported.
    58 * <li>The value of the objects' constructor property must either be equal to
    59 * the real constructor or kept untouched.
    60 * </ul>
    61 *
    62 * @constructor
    63 * @final
    64 */
    65goog.testing.PropertyReplacer = function() {
    66 /**
    67 * Stores the values changed by the set() method in chronological order.
    68 * Its items are objects with 3 fields: 'object', 'key', 'value'. The
    69 * original value for the given key in the given object is stored under the
    70 * 'value' key.
    71 * @type {Array<Object>}
    72 * @private
    73 */
    74 this.original_ = [];
    75};
    76
    77
    78/**
    79 * Indicates that a key didn't exist before having been set by the set() method.
    80 * @private @const
    81 */
    82goog.testing.PropertyReplacer.NO_SUCH_KEY_ = {};
    83
    84
    85/**
    86 * Tells if the given key exists in the object. Ignores inherited fields.
    87 * @param {Object|Function} obj The JavaScript or native object or function
    88 * whose key is to be checked.
    89 * @param {string} key The key to check.
    90 * @return {boolean} Whether the object has the key as own key.
    91 * @private
    92 */
    93goog.testing.PropertyReplacer.hasKey_ = function(obj, key) {
    94 if (!(key in obj)) {
    95 return false;
    96 }
    97 // hasOwnProperty is only reliable with JavaScript objects. It returns false
    98 // for built-in DOM attributes.
    99 if (Object.prototype.hasOwnProperty.call(obj, key)) {
    100 return true;
    101 }
    102 // In all browsers except Opera obj.constructor never equals to Object if
    103 // obj is an instance of a native class. In Opera we have to fall back on
    104 // examining obj.toString().
    105 if (obj.constructor == Object &&
    106 (!goog.userAgent.OPERA ||
    107 Object.prototype.toString.call(obj) == '[object Object]')) {
    108 return false;
    109 }
    110 try {
    111 // Firefox hack to consider "className" part of the HTML elements or
    112 // "body" part of document. Although they are defined in the prototype of
    113 // HTMLElement or Document, accessing them this way throws an exception.
    114 // <pre>
    115 // var dummy = document.body.constructor.prototype.className
    116 // [Exception... "Cannot modify properties of a WrappedNative"]
    117 // </pre>
    118 var dummy = obj.constructor.prototype[key];
    119 } catch (e) {
    120 return true;
    121 }
    122 return !(key in obj.constructor.prototype);
    123};
    124
    125
    126/**
    127 * Deletes a key from an object. Sets it to undefined or empty string if the
    128 * delete failed.
    129 * @param {Object|Function} obj The object or function to delete a key from.
    130 * @param {string} key The key to delete.
    131 * @private
    132 */
    133goog.testing.PropertyReplacer.deleteKey_ = function(obj, key) {
    134 try {
    135 delete obj[key];
    136 // Delete has no effect for built-in properties of DOM nodes in FF.
    137 if (!goog.testing.PropertyReplacer.hasKey_(obj, key)) {
    138 return;
    139 }
    140 } catch (e) {
    141 // IE throws TypeError when trying to delete properties of native objects
    142 // (e.g. DOM nodes or window), even if they have been added by JavaScript.
    143 }
    144
    145 obj[key] = undefined;
    146 if (obj[key] == 'undefined') {
    147 // Some properties such as className in IE are always evaluated as string
    148 // so undefined will become 'undefined'.
    149 obj[key] = '';
    150 }
    151};
    152
    153
    154/**
    155 * Adds or changes a value in an object while saving its original state.
    156 * @param {Object|Function} obj The JavaScript or native object or function to
    157 * alter. See the constraints in the class description.
    158 * @param {string} key The key to change the value for.
    159 * @param {*} value The new value to set.
    160 */
    161goog.testing.PropertyReplacer.prototype.set = function(obj, key, value) {
    162 var origValue = goog.testing.PropertyReplacer.hasKey_(obj, key) ? obj[key] :
    163 goog.testing.PropertyReplacer.NO_SUCH_KEY_;
    164 this.original_.push({object: obj, key: key, value: origValue});
    165 obj[key] = value;
    166};
    167
    168
    169/**
    170 * Changes an existing value in an object to another one of the same type while
    171 * saving its original state. The advantage of {@code replace} over {@link #set}
    172 * is that {@code replace} protects against typos and erroneously passing tests
    173 * after some members have been renamed during a refactoring.
    174 * @param {Object|Function} obj The JavaScript or native object or function to
    175 * alter. See the constraints in the class description.
    176 * @param {string} key The key to change the value for. It has to be present
    177 * either in {@code obj} or in its prototype chain.
    178 * @param {*} value The new value to set. It has to have the same type as the
    179 * original value. The types are compared with {@link goog.typeOf}.
    180 * @throws {Error} In case of missing key or type mismatch.
    181 */
    182goog.testing.PropertyReplacer.prototype.replace = function(obj, key, value) {
    183 if (!(key in obj)) {
    184 throw Error('Cannot replace missing property "' + key + '" in ' + obj);
    185 }
    186 if (goog.typeOf(obj[key]) != goog.typeOf(value)) {
    187 throw Error('Cannot replace property "' + key + '" in ' + obj +
    188 ' with a value of different type');
    189 }
    190 this.set(obj, key, value);
    191};
    192
    193
    194/**
    195 * Builds an object structure for the provided namespace path. Doesn't
    196 * overwrite those prefixes of the path that are already objects or functions.
    197 * @param {string} path The path to create or alter, e.g. 'goog.ui.Menu'.
    198 * @param {*} value The value to set.
    199 */
    200goog.testing.PropertyReplacer.prototype.setPath = function(path, value) {
    201 var parts = path.split('.');
    202 var obj = goog.global;
    203 for (var i = 0; i < parts.length - 1; i++) {
    204 var part = parts[i];
    205 if (part == 'prototype' && !obj[part]) {
    206 throw Error('Cannot set the prototype of ' + parts.slice(0, i).join('.'));
    207 }
    208 if (!goog.isObject(obj[part]) && !goog.isFunction(obj[part])) {
    209 this.set(obj, part, {});
    210 }
    211 obj = obj[part];
    212 }
    213 this.set(obj, parts[parts.length - 1], value);
    214};
    215
    216
    217/**
    218 * Deletes the key from the object while saving its original value.
    219 * @param {Object|Function} obj The JavaScript or native object or function to
    220 * alter. See the constraints in the class description.
    221 * @param {string} key The key to delete.
    222 */
    223goog.testing.PropertyReplacer.prototype.remove = function(obj, key) {
    224 if (goog.testing.PropertyReplacer.hasKey_(obj, key)) {
    225 this.original_.push({object: obj, key: key, value: obj[key]});
    226 goog.testing.PropertyReplacer.deleteKey_(obj, key);
    227 }
    228};
    229
    230
    231/**
    232 * Resets all changes made by goog.testing.PropertyReplacer.prototype.set.
    233 */
    234goog.testing.PropertyReplacer.prototype.reset = function() {
    235 for (var i = this.original_.length - 1; i >= 0; i--) {
    236 var original = this.original_[i];
    237 if (original.value == goog.testing.PropertyReplacer.NO_SUCH_KEY_) {
    238 goog.testing.PropertyReplacer.deleteKey_(original.object, original.key);
    239 } else {
    240 original.object[original.key] = original.value;
    241 }
    242 delete this.original_[i];
    243 }
    244 this.original_.length = 0;
    245};
    \ 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 index c2c52c2..2a596df 100644 --- a/docs/source/lib/goog/testing/recordfunction.js.src.html +++ b/docs/source/lib/goog/testing/recordfunction.js.src.html @@ -1 +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 +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 index 73406c7..5fdefd0 100644 --- a/docs/source/lib/goog/testing/stacktrace.js.src.html +++ b/docs/source/lib/goog/testing/stacktrace.js.src.html @@ -1 +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 +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 index f54c965..755a5ca 100644 --- a/docs/source/lib/goog/testing/strictmock.js.src.html +++ b/docs/source/lib/goog/testing/strictmock.js.src.html @@ -1 +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 +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 index 38d876d..aed219f 100644 --- a/docs/source/lib/goog/testing/testcase.js.src.html +++ b/docs/source/lib/goog/testing/testcase.js.src.html @@ -1 +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
    32goog.require('goog.object');
    33goog.require('goog.testing.asserts');
    34goog.require('goog.testing.stacktrace');
    35
    36
    37
    38/**
    39 * A class representing a JsUnit test case. A TestCase is made up of a number
    40 * of test functions which can be run. Individual test cases can override the
    41 * following functions to set up their test environment:
    42 * - runTests - completely override the test's runner
    43 * - setUpPage - called before any of the test functions are run
    44 * - tearDownPage - called after all tests are finished
    45 * - setUp - called before each of the test functions
    46 * - tearDown - called after each of the test functions
    47 * - shouldRunTests - called before a test run, all tests are skipped if it
    48 * returns false. Can be used to disable tests on browsers
    49 * where they aren't expected to pass.
    50 *
    51 * Use {@link #autoDiscoverLifecycle} and {@link #autoDiscoverTests}
    52 *
    53 * @param {string=} opt_name The name of the test case, defaults to
    54 * 'Untitled Test Case'.
    55 * @constructor
    56 */
    57goog.testing.TestCase = function(opt_name) {
    58 /**
    59 * A name for the test case.
    60 * @type {string}
    61 * @private
    62 */
    63 this.name_ = opt_name || 'Untitled Test Case';
    64
    65 /**
    66 * Array of test functions that can be executed.
    67 * @type {!Array.<!goog.testing.TestCase.Test>}
    68 * @private
    69 */
    70 this.tests_ = [];
    71
    72 /**
    73 * Set of test names and/or indices to execute, or null if all tests should
    74 * be executed.
    75 *
    76 * Indices are included to allow automation tools to run a subset of the
    77 * tests without knowing the exact contents of the test file.
    78 *
    79 * Indices should only be used with SORTED ordering.
    80 *
    81 * Example valid values:
    82 * <ul>
    83 * <li>[testName]
    84 * <li>[testName1, testName2]
    85 * <li>[2] - will run the 3rd test in the order specified
    86 * <li>[1,3,5]
    87 * <li>[testName1, testName2, 3, 5] - will work
    88 * <ul>
    89 * @type {Object}
    90 * @private
    91 */
    92 this.testsToRun_ = null;
    93
    94 var search = '';
    95 if (goog.global.location) {
    96 search = goog.global.location.search;
    97 }
    98
    99 // Parse the 'runTests' query parameter into a set of test names and/or
    100 // test indices.
    101 var runTestsMatch = search.match(/(?:\?|&)runTests=([^?&]+)/i);
    102 if (runTestsMatch) {
    103 this.testsToRun_ = {};
    104 var arr = runTestsMatch[1].split(',');
    105 for (var i = 0, len = arr.length; i < len; i++) {
    106 this.testsToRun_[arr[i]] = 1;
    107 }
    108 }
    109
    110 // Checks the URL for a valid order param.
    111 var orderMatch = search.match(/(?:\?|&)order=(natural|random|sorted)/i);
    112 if (orderMatch) {
    113 this.order = orderMatch[1];
    114 }
    115
    116 /**
    117 * Object used to encapsulate the test results.
    118 * @type {goog.testing.TestCase.Result}
    119 * @protected
    120 * @suppress {underscore|visibility}
    121 */
    122 this.result_ = new goog.testing.TestCase.Result(this);
    123
    124 // This silences a compiler warning from the legacy property check, which
    125 // is deprecated. It idly writes to testRunner properties that are used
    126 // in this file.
    127 var testRunnerMethods = {isFinished: true, hasErrors: true};
    128};
    129
    130
    131/**
    132 * The order to run the auto-discovered tests.
    133 * @enum {string}
    134 */
    135goog.testing.TestCase.Order = {
    136 /**
    137 * This is browser dependent and known to be different in FF and Safari
    138 * compared to others.
    139 */
    140 NATURAL: 'natural',
    141
    142 /** Random order. */
    143 RANDOM: 'random',
    144
    145 /** Sorted based on the name. */
    146 SORTED: 'sorted'
    147};
    148
    149
    150/**
    151 * @return {string} The name of the test.
    152 */
    153goog.testing.TestCase.prototype.getName = function() {
    154 return this.name_;
    155};
    156
    157
    158/**
    159 * The maximum amount of time that the test can run before we force it to be
    160 * async. This prevents the test runner from blocking the browser and
    161 * potentially hurting the Selenium test harness.
    162 * @type {number}
    163 */
    164goog.testing.TestCase.maxRunTime = 200;
    165
    166
    167/**
    168 * The order to run the auto-discovered tests in.
    169 * @type {string}
    170 */
    171goog.testing.TestCase.prototype.order = goog.testing.TestCase.Order.SORTED;
    172
    173
    174/**
    175 * Save a reference to {@code window.setTimeout}, so any code that overrides the
    176 * default behavior (the MockClock, for example) doesn't affect our runner.
    177 * @type {function((Function|string), number, *=): number}
    178 * @private
    179 */
    180goog.testing.TestCase.protectedSetTimeout_ = goog.global.setTimeout;
    181
    182
    183/**
    184 * Save a reference to {@code window.clearTimeout}, so any code that overrides
    185 * the default behavior (e.g. MockClock) doesn't affect our runner.
    186 * @type {function((null|number|undefined)): void}
    187 * @private
    188 */
    189goog.testing.TestCase.protectedClearTimeout_ = goog.global.clearTimeout;
    190
    191
    192/**
    193 * Save a reference to {@code window.Date}, so any code that overrides
    194 * the default behavior doesn't affect our runner.
    195 * @type {function(new: Date)}
    196 * @private
    197 */
    198goog.testing.TestCase.protectedDate_ = Date;
    199
    200
    201/**
    202 * Saved string referencing goog.global.setTimeout's string serialization. IE
    203 * sometimes fails to uphold equality for setTimeout, but the string version
    204 * stays the same.
    205 * @type {string}
    206 * @private
    207 */
    208goog.testing.TestCase.setTimeoutAsString_ = String(goog.global.setTimeout);
    209
    210
    211/**
    212 * TODO(user) replace this with prototype.currentTest.
    213 * Name of the current test that is running, or null if none is running.
    214 * @type {?string}
    215 */
    216goog.testing.TestCase.currentTestName = null;
    217
    218
    219/**
    220 * Avoid a dependency on goog.userAgent and keep our own reference of whether
    221 * the browser is IE.
    222 * @type {boolean}
    223 */
    224goog.testing.TestCase.IS_IE = typeof opera == 'undefined' &&
    225 !!goog.global.navigator &&
    226 goog.global.navigator.userAgent.indexOf('MSIE') != -1;
    227
    228
    229/**
    230 * Exception object that was detected before a test runs.
    231 * @type {*}
    232 * @protected
    233 */
    234goog.testing.TestCase.prototype.exceptionBeforeTest;
    235
    236
    237/**
    238 * Whether the test case has ever tried to execute.
    239 * @type {boolean}
    240 */
    241goog.testing.TestCase.prototype.started = false;
    242
    243
    244/**
    245 * Whether the test case is running.
    246 * @type {boolean}
    247 */
    248goog.testing.TestCase.prototype.running = false;
    249
    250
    251/**
    252 * Timestamp for when the test was started.
    253 * @type {number}
    254 * @private
    255 */
    256goog.testing.TestCase.prototype.startTime_ = 0;
    257
    258
    259/**
    260 * Time since the last batch of tests was started, if batchTime exceeds
    261 * {@link #maxRunTime} a timeout will be used to stop the tests blocking the
    262 * browser and a new batch will be started.
    263 * @type {number}
    264 * @private
    265 */
    266goog.testing.TestCase.prototype.batchTime_ = 0;
    267
    268
    269/**
    270 * Pointer to the current test.
    271 * @type {number}
    272 * @private
    273 */
    274goog.testing.TestCase.prototype.currentTestPointer_ = 0;
    275
    276
    277/**
    278 * Optional callback that will be executed when the test has finalized.
    279 * @type {Function}
    280 * @private
    281 */
    282goog.testing.TestCase.prototype.onCompleteCallback_ = null;
    283
    284
    285/**
    286 * Adds a new test to the test case.
    287 * @param {goog.testing.TestCase.Test} test The test to add.
    288 */
    289goog.testing.TestCase.prototype.add = function(test) {
    290 if (this.started) {
    291 throw Error('Tests cannot be added after execute() has been called. ' +
    292 'Test: ' + test.name);
    293 }
    294
    295 this.tests_.push(test);
    296};
    297
    298
    299/**
    300 * Creates and adds a new test.
    301 *
    302 * Convenience function to make syntax less awkward when not using automatic
    303 * test discovery.
    304 *
    305 * @param {string} name The test name.
    306 * @param {!Function} ref Reference to the test function.
    307 * @param {!Object=} opt_scope Optional scope that the test function should be
    308 * called in.
    309 */
    310goog.testing.TestCase.prototype.addNewTest = function(name, ref, opt_scope) {
    311 var test = new goog.testing.TestCase.Test(name, ref, opt_scope || this);
    312 this.add(test);
    313};
    314
    315
    316/**
    317 * Sets the tests.
    318 * @param {!Array.<goog.testing.TestCase.Test>} tests A new test array.
    319 * @protected
    320 */
    321goog.testing.TestCase.prototype.setTests = function(tests) {
    322 this.tests_ = tests;
    323};
    324
    325
    326/**
    327 * Gets the tests.
    328 * @return {!Array.<goog.testing.TestCase.Test>} The test array.
    329 */
    330goog.testing.TestCase.prototype.getTests = function() {
    331 return this.tests_;
    332};
    333
    334
    335/**
    336 * Returns the number of tests contained in the test case.
    337 * @return {number} The number of tests.
    338 */
    339goog.testing.TestCase.prototype.getCount = function() {
    340 return this.tests_.length;
    341};
    342
    343
    344/**
    345 * Returns the number of tests actually run in the test case, i.e. subtracting
    346 * any which are skipped.
    347 * @return {number} The number of un-ignored tests.
    348 */
    349goog.testing.TestCase.prototype.getActuallyRunCount = function() {
    350 return this.testsToRun_ ? goog.object.getCount(this.testsToRun_) : 0;
    351};
    352
    353
    354/**
    355 * Returns the current test and increments the pointer.
    356 * @return {goog.testing.TestCase.Test} The current test case.
    357 */
    358goog.testing.TestCase.prototype.next = function() {
    359 var test;
    360 while ((test = this.tests_[this.currentTestPointer_++])) {
    361 if (!this.testsToRun_ || this.testsToRun_[test.name] ||
    362 this.testsToRun_[this.currentTestPointer_ - 1]) {
    363 return test;
    364 }
    365 }
    366 return null;
    367};
    368
    369
    370/**
    371 * Resets the test case pointer, so that next returns the first test.
    372 */
    373goog.testing.TestCase.prototype.reset = function() {
    374 this.currentTestPointer_ = 0;
    375 this.result_ = new goog.testing.TestCase.Result(this);
    376};
    377
    378
    379/**
    380 * Sets the callback function that should be executed when the tests have
    381 * completed.
    382 * @param {Function} fn The callback function.
    383 */
    384goog.testing.TestCase.prototype.setCompletedCallback = function(fn) {
    385 this.onCompleteCallback_ = fn;
    386};
    387
    388
    389/**
    390 * Can be overridden in test classes to indicate whether the tests in a case
    391 * should be run in that particular situation. For example, this could be used
    392 * to stop tests running in a particular browser, where browser support for
    393 * the class under test was absent.
    394 * @return {boolean} Whether any of the tests in the case should be run.
    395 */
    396goog.testing.TestCase.prototype.shouldRunTests = function() {
    397 return true;
    398};
    399
    400
    401/**
    402 * Executes each of the tests.
    403 */
    404goog.testing.TestCase.prototype.execute = function() {
    405 this.started = true;
    406 this.reset();
    407 this.startTime_ = this.now();
    408 this.running = true;
    409 this.result_.totalCount = this.getCount();
    410
    411 if (!this.shouldRunTests()) {
    412 this.log('shouldRunTests() returned false, skipping these tests.');
    413 this.result_.testSuppressed = true;
    414 this.finalize();
    415 return;
    416 }
    417
    418 this.log('Starting tests: ' + this.name_);
    419 this.cycleTests();
    420};
    421
    422
    423/**
    424 * Finalizes the test case, called when the tests have finished executing.
    425 */
    426goog.testing.TestCase.prototype.finalize = function() {
    427 this.saveMessage('Done');
    428
    429 this.tearDownPage();
    430
    431 var restoredSetTimeout =
    432 goog.testing.TestCase.protectedSetTimeout_ == goog.global.setTimeout &&
    433 goog.testing.TestCase.protectedClearTimeout_ == goog.global.clearTimeout;
    434 if (!restoredSetTimeout && goog.testing.TestCase.IS_IE &&
    435 String(goog.global.setTimeout) ==
    436 goog.testing.TestCase.setTimeoutAsString_) {
    437 // In strange cases, IE's value of setTimeout *appears* to change, but
    438 // the string representation stays stable.
    439 restoredSetTimeout = true;
    440 }
    441
    442 if (!restoredSetTimeout) {
    443 var message = 'ERROR: Test did not restore setTimeout and clearTimeout';
    444 this.saveMessage(message);
    445 var err = new goog.testing.TestCase.Error(this.name_, message);
    446 this.result_.errors.push(err);
    447 }
    448 goog.global.clearTimeout = goog.testing.TestCase.protectedClearTimeout_;
    449 goog.global.setTimeout = goog.testing.TestCase.protectedSetTimeout_;
    450 this.endTime_ = this.now();
    451 this.running = false;
    452 this.result_.runTime = this.endTime_ - this.startTime_;
    453 this.result_.numFilesLoaded = this.countNumFilesLoaded_();
    454 this.result_.complete = true;
    455
    456 this.log(this.result_.getSummary());
    457 if (this.result_.isSuccess()) {
    458 this.log('Tests complete');
    459 } else {
    460 this.log('Tests Failed');
    461 }
    462 if (this.onCompleteCallback_) {
    463 var fn = this.onCompleteCallback_;
    464 // Execute's the completed callback in the context of the global object.
    465 fn();
    466 this.onCompleteCallback_ = null;
    467 }
    468};
    469
    470
    471/**
    472 * Saves a message to the result set.
    473 * @param {string} message The message to save.
    474 */
    475goog.testing.TestCase.prototype.saveMessage = function(message) {
    476 this.result_.messages.push(this.getTimeStamp_() + ' ' + message);
    477};
    478
    479
    480/**
    481 * @return {boolean} Whether the test case is running inside the multi test
    482 * runner.
    483 */
    484goog.testing.TestCase.prototype.isInsideMultiTestRunner = function() {
    485 var top = goog.global['top'];
    486 return top && typeof top['_allTests'] != 'undefined';
    487};
    488
    489
    490/**
    491 * Logs an object to the console, if available.
    492 * @param {*} val The value to log. Will be ToString'd.
    493 */
    494goog.testing.TestCase.prototype.log = function(val) {
    495 if (!this.isInsideMultiTestRunner() && goog.global.console) {
    496 if (typeof val == 'string') {
    497 val = this.getTimeStamp_() + ' : ' + val;
    498 }
    499 if (val instanceof Error && val.stack) {
    500 // Chrome does console.log asynchronously in a different process
    501 // (http://code.google.com/p/chromium/issues/detail?id=50316).
    502 // This is an acute problem for Errors, which almost never survive.
    503 // Grab references to the immutable strings so they survive.
    504 goog.global.console.log(val, val.message, val.stack);
    505 // TODO(gboyer): Consider for Chrome cloning any object if we can ensure
    506 // there are no circular references.
    507 } else {
    508 goog.global.console.log(val);
    509 }
    510 }
    511};
    512
    513
    514/**
    515 * @return {boolean} Whether the test was a success.
    516 */
    517goog.testing.TestCase.prototype.isSuccess = function() {
    518 return !!this.result_ && this.result_.isSuccess();
    519};
    520
    521
    522/**
    523 * Returns a string detailing the results from the test.
    524 * @param {boolean=} opt_verbose If true results will include data about all
    525 * tests, not just what failed.
    526 * @return {string} The results from the test.
    527 */
    528goog.testing.TestCase.prototype.getReport = function(opt_verbose) {
    529 var rv = [];
    530
    531 if (this.running) {
    532 rv.push(this.name_ + ' [RUNNING]');
    533 } else {
    534 var label = this.result_.isSuccess() ? 'PASSED' : 'FAILED';
    535 rv.push(this.name_ + ' [' + label + ']');
    536 }
    537
    538 if (goog.global.location) {
    539 rv.push(this.trimPath_(goog.global.location.href));
    540 }
    541
    542 rv.push(this.result_.getSummary());
    543
    544 if (opt_verbose) {
    545 rv.push('.', this.result_.messages.join('\n'));
    546 } else if (!this.result_.isSuccess()) {
    547 rv.push(this.result_.errors.join('\n'));
    548 }
    549
    550 rv.push(' ');
    551
    552 return rv.join('\n');
    553};
    554
    555
    556/**
    557 * Returns the amount of time it took for the test to run.
    558 * @return {number} The run time, in milliseconds.
    559 */
    560goog.testing.TestCase.prototype.getRunTime = function() {
    561 return this.result_.runTime;
    562};
    563
    564
    565/**
    566 * Returns the number of script files that were loaded in order to run the test.
    567 * @return {number} The number of script files.
    568 */
    569goog.testing.TestCase.prototype.getNumFilesLoaded = function() {
    570 return this.result_.numFilesLoaded;
    571};
    572
    573
    574/**
    575 * Returns the test results object: a map from test names to a list of test
    576 * failures (if any exist).
    577 * @return {!Object.<string, !Array.<string>>} Tests results object.
    578 */
    579goog.testing.TestCase.prototype.getTestResults = function() {
    580 return this.result_.resultsByName;
    581};
    582
    583
    584/**
    585 * Executes each of the tests.
    586 * Overridable by the individual test case. This allows test cases to defer
    587 * when the test is actually started. If overridden, finalize must be called
    588 * by the test to indicate it has finished.
    589 */
    590goog.testing.TestCase.prototype.runTests = function() {
    591 try {
    592 this.setUpPage();
    593 } catch (e) {
    594 this.exceptionBeforeTest = e;
    595 }
    596 this.execute();
    597};
    598
    599
    600/**
    601 * Reorders the tests depending on the {@code order} field.
    602 * @param {Array.<goog.testing.TestCase.Test>} tests An array of tests to
    603 * reorder.
    604 * @private
    605 */
    606goog.testing.TestCase.prototype.orderTests_ = function(tests) {
    607 switch (this.order) {
    608 case goog.testing.TestCase.Order.RANDOM:
    609 // Fisher-Yates shuffle
    610 var i = tests.length;
    611 while (i > 1) {
    612 // goog.math.randomInt is inlined to reduce dependencies.
    613 var j = Math.floor(Math.random() * i); // exclusive
    614 i--;
    615 var tmp = tests[i];
    616 tests[i] = tests[j];
    617 tests[j] = tmp;
    618 }
    619 break;
    620
    621 case goog.testing.TestCase.Order.SORTED:
    622 tests.sort(function(t1, t2) {
    623 if (t1.name == t2.name) {
    624 return 0;
    625 }
    626 return t1.name < t2.name ? -1 : 1;
    627 });
    628 break;
    629
    630 // Do nothing for NATURAL.
    631 }
    632};
    633
    634
    635/**
    636 * Gets list of objects that potentially contain test cases. For IE 8 and below,
    637 * this is the global "this" (for properties set directly on the global this or
    638 * window) and the RuntimeObject (for global variables and functions). For all
    639 * other browsers, the array simply contains the global this.
    640 *
    641 * @param {string=} opt_prefix An optional prefix. If specified, only get things
    642 * under this prefix. Note that the prefix is only honored in IE, since it
    643 * supports the RuntimeObject:
    644 * http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
    645 * TODO: Remove this option.
    646 * @return {!Array.<!Object>} A list of objects that should be inspected.
    647 */
    648goog.testing.TestCase.prototype.getGlobals = function(opt_prefix) {
    649 return goog.testing.TestCase.getGlobals(opt_prefix);
    650};
    651
    652
    653/**
    654 * Gets list of objects that potentially contain test cases. For IE 8 and below,
    655 * this is the global "this" (for properties set directly on the global this or
    656 * window) and the RuntimeObject (for global variables and functions). For all
    657 * other browsers, the array simply contains the global this.
    658 *
    659 * @param {string=} opt_prefix An optional prefix. If specified, only get things
    660 * under this prefix. Note that the prefix is only honored in IE, since it
    661 * supports the RuntimeObject:
    662 * http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
    663 * TODO: Remove this option.
    664 * @return {!Array.<!Object>} A list of objects that should be inspected.
    665 */
    666goog.testing.TestCase.getGlobals = function(opt_prefix) {
    667 // Look in the global scope for most browsers, on IE we use the little known
    668 // RuntimeObject which holds references to all globals. We reference this
    669 // via goog.global so that there isn't an aliasing that throws an exception
    670 // in Firefox.
    671 return typeof goog.global['RuntimeObject'] != 'undefined' ?
    672 [goog.global['RuntimeObject']((opt_prefix || '') + '*'), goog.global] :
    673 [goog.global];
    674};
    675
    676
    677/**
    678 * Gets called before any tests are executed. Can be overridden to set up the
    679 * environment for the whole test case.
    680 */
    681goog.testing.TestCase.prototype.setUpPage = function() {};
    682
    683
    684/**
    685 * Gets called after all tests have been executed. Can be overridden to tear
    686 * down the entire test case.
    687 */
    688goog.testing.TestCase.prototype.tearDownPage = function() {};
    689
    690
    691/**
    692 * Gets called before every goog.testing.TestCase.Test is been executed. Can be
    693 * overridden to add set up functionality to each test.
    694 */
    695goog.testing.TestCase.prototype.setUp = function() {};
    696
    697
    698/**
    699 * Gets called after every goog.testing.TestCase.Test has been executed. Can be
    700 * overriden to add tear down functionality to each test.
    701 */
    702goog.testing.TestCase.prototype.tearDown = function() {};
    703
    704
    705/**
    706 * @return {string} The function name prefix used to auto-discover tests.
    707 * @protected
    708 */
    709goog.testing.TestCase.prototype.getAutoDiscoveryPrefix = function() {
    710 return 'test';
    711};
    712
    713
    714/**
    715 * @return {number} Time since the last batch of tests was started.
    716 * @protected
    717 */
    718goog.testing.TestCase.prototype.getBatchTime = function() {
    719 return this.batchTime_;
    720};
    721
    722
    723/**
    724 * @param {number} batchTime Time since the last batch of tests was started.
    725 * @protected
    726 */
    727goog.testing.TestCase.prototype.setBatchTime = function(batchTime) {
    728 this.batchTime_ = batchTime;
    729};
    730
    731
    732/**
    733 * Creates a {@code goog.testing.TestCase.Test} from an auto-discovered
    734 * function.
    735 * @param {string} name The name of the function.
    736 * @param {function() : void} ref The auto-discovered function.
    737 * @return {!goog.testing.TestCase.Test} The newly created test.
    738 * @protected
    739 */
    740goog.testing.TestCase.prototype.createTestFromAutoDiscoveredFunction =
    741 function(name, ref) {
    742 return new goog.testing.TestCase.Test(name, ref, goog.global);
    743};
    744
    745
    746/**
    747 * Adds any functions defined in the global scope that correspond to
    748 * lifecycle events for the test case. Overrides setUp, tearDown, setUpPage,
    749 * tearDownPage and runTests if they are defined.
    750 */
    751goog.testing.TestCase.prototype.autoDiscoverLifecycle = function() {
    752 if (goog.global['setUp']) {
    753 this.setUp = goog.bind(goog.global['setUp'], goog.global);
    754 }
    755 if (goog.global['tearDown']) {
    756 this.tearDown = goog.bind(goog.global['tearDown'], goog.global);
    757 }
    758 if (goog.global['setUpPage']) {
    759 this.setUpPage = goog.bind(goog.global['setUpPage'], goog.global);
    760 }
    761 if (goog.global['tearDownPage']) {
    762 this.tearDownPage = goog.bind(goog.global['tearDownPage'], goog.global);
    763 }
    764 if (goog.global['runTests']) {
    765 this.runTests = goog.bind(goog.global['runTests'], goog.global);
    766 }
    767 if (goog.global['shouldRunTests']) {
    768 this.shouldRunTests = goog.bind(goog.global['shouldRunTests'], goog.global);
    769 }
    770};
    771
    772
    773/**
    774 * Adds any functions defined in the global scope that are prefixed with "test"
    775 * to the test case.
    776 */
    777goog.testing.TestCase.prototype.autoDiscoverTests = function() {
    778 var prefix = this.getAutoDiscoveryPrefix();
    779 var testSources = this.getGlobals(prefix);
    780
    781 var foundTests = [];
    782
    783 for (var i = 0; i < testSources.length; i++) {
    784 var testSource = testSources[i];
    785 for (var name in testSource) {
    786 if ((new RegExp('^' + prefix)).test(name)) {
    787 var ref;
    788 try {
    789 ref = testSource[name];
    790 } catch (ex) {
    791 // NOTE(brenneman): When running tests from a file:// URL on Firefox
    792 // 3.5 for Windows, any reference to goog.global.sessionStorage raises
    793 // an "Operation is not supported" exception. Ignore any exceptions
    794 // raised by simply accessing global properties.
    795 ref = undefined;
    796 }
    797
    798 if (goog.isFunction(ref)) {
    799 foundTests.push(this.createTestFromAutoDiscoveredFunction(name, ref));
    800 }
    801 }
    802 }
    803 }
    804
    805 this.orderTests_(foundTests);
    806
    807 for (var i = 0; i < foundTests.length; i++) {
    808 this.add(foundTests[i]);
    809 }
    810
    811 this.log(this.getCount() + ' tests auto-discovered');
    812
    813 // TODO(user): Do this as a separate call. Unfortunately, a lot of projects
    814 // currently override autoDiscoverTests and expect lifecycle events to be
    815 // registered as a part of this call.
    816 this.autoDiscoverLifecycle();
    817};
    818
    819
    820/**
    821 * Checks to see if the test should be marked as failed before it is run.
    822 *
    823 * If there was an error in setUpPage, we treat that as a failure for all tests
    824 * and mark them all as having failed.
    825 *
    826 * @param {goog.testing.TestCase.Test} testCase The current test case.
    827 * @return {boolean} Whether the test was marked as failed.
    828 * @protected
    829 */
    830goog.testing.TestCase.prototype.maybeFailTestEarly = function(testCase) {
    831 if (this.exceptionBeforeTest) {
    832 // We just use the first error to report an error on a failed test.
    833 testCase.name = 'setUpPage for ' + testCase.name;
    834 this.doError(testCase, this.exceptionBeforeTest);
    835 return true;
    836 }
    837 return false;
    838};
    839
    840
    841/**
    842 * Cycles through the tests, breaking out using a setTimeout if the execution
    843 * time has execeeded {@link #maxRunTime}.
    844 */
    845goog.testing.TestCase.prototype.cycleTests = function() {
    846 this.saveMessage('Start');
    847 this.batchTime_ = this.now();
    848 var nextTest;
    849 while ((nextTest = this.next()) && this.running) {
    850 this.result_.runCount++;
    851 // Execute the test and handle the error, we execute all tests rather than
    852 // stopping after a single error.
    853 var cleanedUp = false;
    854 try {
    855 this.log('Running test: ' + nextTest.name);
    856
    857 if (this.maybeFailTestEarly(nextTest)) {
    858 cleanedUp = true;
    859 } else {
    860 goog.testing.TestCase.currentTestName = nextTest.name;
    861 this.setUp();
    862 nextTest.execute();
    863 this.tearDown();
    864 goog.testing.TestCase.currentTestName = null;
    865
    866 cleanedUp = true;
    867
    868 this.doSuccess(nextTest);
    869 }
    870 } catch (e) {
    871 this.doError(nextTest, e);
    872
    873 if (!cleanedUp) {
    874 try {
    875 this.tearDown();
    876 } catch (e2) {} // Fail silently if tearDown is throwing the errors.
    877 }
    878 }
    879
    880 // If the max run time is exceeded call this function again async so as not
    881 // to block the browser.
    882 if (this.currentTestPointer_ < this.tests_.length &&
    883 this.now() - this.batchTime_ > goog.testing.TestCase.maxRunTime) {
    884 this.saveMessage('Breaking async');
    885 this.timeout(goog.bind(this.cycleTests, this), 0);
    886 return;
    887 }
    888 }
    889 // Tests are done.
    890 this.finalize();
    891};
    892
    893
    894/**
    895 * Counts the number of files that were loaded for dependencies that are
    896 * required to run the test.
    897 * @return {number} The number of files loaded.
    898 * @private
    899 */
    900goog.testing.TestCase.prototype.countNumFilesLoaded_ = function() {
    901 var scripts = document.getElementsByTagName('script');
    902 var count = 0;
    903 for (var i = 0, n = scripts.length; i < n; i++) {
    904 if (scripts[i].src) {
    905 count++;
    906 }
    907 }
    908 return count;
    909};
    910
    911
    912/**
    913 * Calls a function after a delay, using the protected timeout.
    914 * @param {Function} fn The function to call.
    915 * @param {number} time Delay in milliseconds.
    916 * @return {number} The timeout id.
    917 * @protected
    918 */
    919goog.testing.TestCase.prototype.timeout = function(fn, time) {
    920 // NOTE: invoking protectedSetTimeout_ as a member of goog.testing.TestCase
    921 // would result in an Illegal Invocation error. The method must be executed
    922 // with the global context.
    923 var protectedSetTimeout = goog.testing.TestCase.protectedSetTimeout_;
    924 return protectedSetTimeout(fn, time);
    925};
    926
    927
    928/**
    929 * Clears a timeout created by {@code this.timeout()}.
    930 * @param {number} id A timeout id.
    931 * @protected
    932 */
    933goog.testing.TestCase.prototype.clearTimeout = function(id) {
    934 // NOTE: see execution note for protectedSetTimeout above.
    935 var protectedClearTimeout = goog.testing.TestCase.protectedClearTimeout_;
    936 protectedClearTimeout(id);
    937};
    938
    939
    940/**
    941 * @return {number} The current time in milliseconds, don't use goog.now as some
    942 * tests override it.
    943 * @protected
    944 */
    945goog.testing.TestCase.prototype.now = function() {
    946 // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
    947 var protectedDate = goog.testing.TestCase.protectedDate_;
    948 return new protectedDate().getTime();
    949};
    950
    951
    952/**
    953 * Returns the current time.
    954 * @return {string} HH:MM:SS.
    955 * @private
    956 */
    957goog.testing.TestCase.prototype.getTimeStamp_ = function() {
    958 // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
    959 var protectedDate = goog.testing.TestCase.protectedDate_;
    960 var d = new protectedDate();
    961
    962 // Ensure millis are always 3-digits
    963 var millis = '00' + d.getMilliseconds();
    964 millis = millis.substr(millis.length - 3);
    965
    966 return this.pad_(d.getHours()) + ':' + this.pad_(d.getMinutes()) + ':' +
    967 this.pad_(d.getSeconds()) + '.' + millis;
    968};
    969
    970
    971/**
    972 * Pads a number to make it have a leading zero if it's less than 10.
    973 * @param {number} number The number to pad.
    974 * @return {string} The resulting string.
    975 * @private
    976 */
    977goog.testing.TestCase.prototype.pad_ = function(number) {
    978 return number < 10 ? '0' + number : String(number);
    979};
    980
    981
    982/**
    983 * Trims a path to be only that after google3.
    984 * @param {string} path The path to trim.
    985 * @return {string} The resulting string.
    986 * @private
    987 */
    988goog.testing.TestCase.prototype.trimPath_ = function(path) {
    989 return path.substring(path.indexOf('google3') + 8);
    990};
    991
    992
    993/**
    994 * Handles a test that passed.
    995 * @param {goog.testing.TestCase.Test} test The test that passed.
    996 * @protected
    997 */
    998goog.testing.TestCase.prototype.doSuccess = function(test) {
    999 this.result_.successCount++;
    1000 // An empty list of error messages indicates that the test passed.
    1001 // If we already have a failure for this test, do not set to empty list.
    1002 if (!(test.name in this.result_.resultsByName)) {
    1003 this.result_.resultsByName[test.name] = [];
    1004 }
    1005 var message = test.name + ' : PASSED';
    1006 this.saveMessage(message);
    1007 this.log(message);
    1008};
    1009
    1010
    1011/**
    1012 * Handles a test that failed.
    1013 * @param {goog.testing.TestCase.Test} test The test that failed.
    1014 * @param {*=} opt_e The exception object associated with the
    1015 * failure or a string.
    1016 * @protected
    1017 */
    1018goog.testing.TestCase.prototype.doError = function(test, opt_e) {
    1019 var message = test.name + ' : FAILED';
    1020 this.log(message);
    1021 this.saveMessage(message);
    1022 var err = this.logError(test.name, opt_e);
    1023 this.result_.errors.push(err);
    1024 if (test.name in this.result_.resultsByName) {
    1025 this.result_.resultsByName[test.name].push(err.toString());
    1026 } else {
    1027 this.result_.resultsByName[test.name] = [err.toString()];
    1028 }
    1029};
    1030
    1031
    1032/**
    1033 * @param {string} name Failed test name.
    1034 * @param {*=} opt_e The exception object associated with the
    1035 * failure or a string.
    1036 * @return {!goog.testing.TestCase.Error} Error object.
    1037 */
    1038goog.testing.TestCase.prototype.logError = function(name, opt_e) {
    1039 var errMsg = null;
    1040 var stack = null;
    1041 if (opt_e) {
    1042 this.log(opt_e);
    1043 if (goog.isString(opt_e)) {
    1044 errMsg = opt_e;
    1045 } else {
    1046 errMsg = opt_e.message || opt_e.description || opt_e.toString();
    1047 stack = opt_e.stack ? goog.testing.stacktrace.canonicalize(opt_e.stack) :
    1048 opt_e['stackTrace'];
    1049 }
    1050 } else {
    1051 errMsg = 'An unknown error occurred';
    1052 }
    1053 var err = new goog.testing.TestCase.Error(name, errMsg, stack);
    1054
    1055 // Avoid double logging.
    1056 if (!opt_e || !opt_e['isJsUnitException'] ||
    1057 !opt_e['loggedJsUnitException']) {
    1058 this.saveMessage(err.toString());
    1059 }
    1060 if (opt_e && opt_e['isJsUnitException']) {
    1061 opt_e['loggedJsUnitException'] = true;
    1062 }
    1063
    1064 return err;
    1065};
    1066
    1067
    1068
    1069/**
    1070 * A class representing a single test function.
    1071 * @param {string} name The test name.
    1072 * @param {Function} ref Reference to the test function.
    1073 * @param {Object=} opt_scope Optional scope that the test function should be
    1074 * called in.
    1075 * @constructor
    1076 */
    1077goog.testing.TestCase.Test = function(name, ref, opt_scope) {
    1078 /**
    1079 * The name of the test.
    1080 * @type {string}
    1081 */
    1082 this.name = name;
    1083
    1084 /**
    1085 * Reference to the test function.
    1086 * @type {Function}
    1087 */
    1088 this.ref = ref;
    1089
    1090 /**
    1091 * Scope that the test function should be called in.
    1092 * @type {Object}
    1093 */
    1094 this.scope = opt_scope || null;
    1095};
    1096
    1097
    1098/**
    1099 * Executes the test function.
    1100 */
    1101goog.testing.TestCase.Test.prototype.execute = function() {
    1102 this.ref.call(this.scope);
    1103};
    1104
    1105
    1106
    1107/**
    1108 * A class for representing test results. A bag of public properties.
    1109 * @param {goog.testing.TestCase} testCase The test case that owns this result.
    1110 * @constructor
    1111 * @final
    1112 */
    1113goog.testing.TestCase.Result = function(testCase) {
    1114 /**
    1115 * The test case that owns this result.
    1116 * @type {goog.testing.TestCase}
    1117 * @private
    1118 */
    1119 this.testCase_ = testCase;
    1120
    1121 /**
    1122 * Total number of tests that should have been run.
    1123 * @type {number}
    1124 */
    1125 this.totalCount = 0;
    1126
    1127 /**
    1128 * Total number of tests that were actually run.
    1129 * @type {number}
    1130 */
    1131 this.runCount = 0;
    1132
    1133 /**
    1134 * Number of successful tests.
    1135 * @type {number}
    1136 */
    1137 this.successCount = 0;
    1138
    1139 /**
    1140 * The amount of time the tests took to run.
    1141 * @type {number}
    1142 */
    1143 this.runTime = 0;
    1144
    1145 /**
    1146 * The number of files loaded to run this test.
    1147 * @type {number}
    1148 */
    1149 this.numFilesLoaded = 0;
    1150
    1151 /**
    1152 * Whether this test case was suppressed by shouldRunTests() returning false.
    1153 * @type {boolean}
    1154 */
    1155 this.testSuppressed = false;
    1156
    1157 /**
    1158 * Test results for each test that was run. The test name is always added
    1159 * as the key in the map, and the array of strings is an optional list
    1160 * of failure messages. If the array is empty, the test passed. Otherwise,
    1161 * the test failed.
    1162 * @type {!Object.<string, !Array.<string>>}
    1163 */
    1164 this.resultsByName = {};
    1165
    1166 /**
    1167 * Errors encountered while running the test.
    1168 * @type {!Array.<goog.testing.TestCase.Error>}
    1169 */
    1170 this.errors = [];
    1171
    1172 /**
    1173 * Messages to show the user after running the test.
    1174 * @type {!Array.<string>}
    1175 */
    1176 this.messages = [];
    1177
    1178 /**
    1179 * Whether the tests have completed.
    1180 * @type {boolean}
    1181 */
    1182 this.complete = false;
    1183};
    1184
    1185
    1186/**
    1187 * @return {boolean} Whether the test was successful.
    1188 */
    1189goog.testing.TestCase.Result.prototype.isSuccess = function() {
    1190 return this.complete && this.errors.length == 0;
    1191};
    1192
    1193
    1194/**
    1195 * @return {string} A summary of the tests, including total number of tests that
    1196 * passed, failed, and the time taken.
    1197 */
    1198goog.testing.TestCase.Result.prototype.getSummary = function() {
    1199 var summary = this.runCount + ' of ' + this.totalCount + ' tests run in ' +
    1200 this.runTime + 'ms.\n';
    1201 if (this.testSuppressed) {
    1202 summary += 'Tests not run because shouldRunTests() returned false.';
    1203 } else {
    1204 var failures = this.totalCount - this.successCount;
    1205 var suppressionMessage = '';
    1206
    1207 var countOfRunTests = this.testCase_.getActuallyRunCount();
    1208 if (countOfRunTests) {
    1209 failures = countOfRunTests - this.successCount;
    1210 suppressionMessage = ', ' +
    1211 (this.totalCount - countOfRunTests) + ' suppressed by querystring';
    1212 }
    1213 summary += this.successCount + ' passed, ' +
    1214 failures + ' failed' + suppressionMessage + '.\n' +
    1215 Math.round(this.runTime / this.runCount) + ' ms/test. ' +
    1216 this.numFilesLoaded + ' files loaded.';
    1217 }
    1218
    1219 return summary;
    1220};
    1221
    1222
    1223/**
    1224 * Initializes the given test case with the global test runner 'G_testRunner'.
    1225 * @param {goog.testing.TestCase} testCase The test case to install.
    1226 */
    1227goog.testing.TestCase.initializeTestRunner = function(testCase) {
    1228 testCase.autoDiscoverTests();
    1229 var gTestRunner = goog.global['G_testRunner'];
    1230 if (gTestRunner) {
    1231 gTestRunner['initialize'](testCase);
    1232 } else {
    1233 throw Error('G_testRunner is undefined. Please ensure goog.testing.jsunit' +
    1234 ' is included.');
    1235 }
    1236};
    1237
    1238
    1239
    1240/**
    1241 * A class representing an error thrown by the test
    1242 * @param {string} source The name of the test which threw the error.
    1243 * @param {string} message The error message.
    1244 * @param {string=} opt_stack A string showing the execution stack.
    1245 * @constructor
    1246 * @final
    1247 */
    1248goog.testing.TestCase.Error = function(source, message, opt_stack) {
    1249 /**
    1250 * The name of the test which threw the error.
    1251 * @type {string}
    1252 */
    1253 this.source = source;
    1254
    1255 /**
    1256 * Reference to the test function.
    1257 * @type {string}
    1258 */
    1259 this.message = message;
    1260
    1261 /**
    1262 * Scope that the test function should be called in.
    1263 * @type {?string}
    1264 */
    1265 this.stack = opt_stack || null;
    1266};
    1267
    1268
    1269/**
    1270 * Returns a string representing the error object.
    1271 * @return {string} A string representation of the error.
    1272 * @override
    1273 */
    1274goog.testing.TestCase.Error.prototype.toString = function() {
    1275 return 'ERROR in ' + this.source + '\n' +
    1276 this.message + (this.stack ? '\n' + this.stack : '');
    1277};
    \ No newline at end of file +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
    130/**
    131 * The order to run the auto-discovered tests.
    132 * @enum {string}
    133 */
    134goog.testing.TestCase.Order = {
    135 /**
    136 * This is browser dependent and known to be different in FF and Safari
    137 * compared to others.
    138 */
    139 NATURAL: 'natural',
    140
    141 /** Random order. */
    142 RANDOM: 'random',
    143
    144 /** Sorted based on the name. */
    145 SORTED: 'sorted'
    146};
    147
    148
    149/**
    150 * @return {string} The name of the test.
    151 */
    152goog.testing.TestCase.prototype.getName = function() {
    153 return this.name_;
    154};
    155
    156
    157/**
    158 * The maximum amount of time in milliseconds that the test case can take
    159 * before it is forced to yield and reschedule. This prevents the test runner
    160 * from blocking the browser and potentially hurting the test harness.
    161 * @type {number}
    162 */
    163goog.testing.TestCase.maxRunTime = 200;
    164
    165
    166/**
    167 * The maximum number of {@link runNextTest_} frames that can be on the stack
    168 * before the test case is forced to yield and reschedule. Although modern
    169 * browsers can handle thousands of stack frames, this is set conservatively
    170 * because maximum stack depth has never been standardized, and engine-specific
    171 * techniques like tail cail optimization can affect the exact depth.
    172 * @private @const
    173 */
    174goog.testing.TestCase.MAX_STACK_DEPTH_ = 100;
    175
    176
    177/**
    178 * Save a reference to {@code window.setTimeout}, so any code that overrides the
    179 * default behavior (the MockClock, for example) doesn't affect our runner.
    180 * @type {function((Function|string), number=, *=): number}
    181 * @private
    182 */
    183goog.testing.TestCase.protectedSetTimeout_ = goog.global.setTimeout;
    184
    185
    186/**
    187 * Save a reference to {@code window.clearTimeout}, so any code that overrides
    188 * the default behavior (e.g. MockClock) doesn't affect our runner.
    189 * @type {function((null|number|undefined)): void}
    190 * @private
    191 */
    192goog.testing.TestCase.protectedClearTimeout_ = goog.global.clearTimeout;
    193
    194
    195/**
    196 * Save a reference to {@code window.Date}, so any code that overrides
    197 * the default behavior doesn't affect our runner.
    198 * @type {function(new: Date)}
    199 * @private
    200 */
    201goog.testing.TestCase.protectedDate_ = Date;
    202
    203
    204/**
    205 * Saved string referencing goog.global.setTimeout's string serialization. IE
    206 * sometimes fails to uphold equality for setTimeout, but the string version
    207 * stays the same.
    208 * @type {string}
    209 * @private
    210 */
    211goog.testing.TestCase.setTimeoutAsString_ = String(goog.global.setTimeout);
    212
    213
    214/**
    215 * TODO(user) replace this with prototype.currentTest.
    216 * Name of the current test that is running, or null if none is running.
    217 * @type {?string}
    218 */
    219goog.testing.TestCase.currentTestName = null;
    220
    221
    222/**
    223 * Avoid a dependency on goog.userAgent and keep our own reference of whether
    224 * the browser is IE.
    225 * @type {boolean}
    226 */
    227goog.testing.TestCase.IS_IE = typeof opera == 'undefined' &&
    228 !!goog.global.navigator &&
    229 goog.global.navigator.userAgent.indexOf('MSIE') != -1;
    230
    231
    232/**
    233 * Exception object that was detected before a test runs.
    234 * @type {*}
    235 * @protected
    236 */
    237goog.testing.TestCase.prototype.exceptionBeforeTest;
    238
    239
    240/**
    241 * Whether the test case has ever tried to execute.
    242 * @type {boolean}
    243 */
    244goog.testing.TestCase.prototype.started = false;
    245
    246
    247/**
    248 * Whether the test case is running.
    249 * @type {boolean}
    250 */
    251goog.testing.TestCase.prototype.running = false;
    252
    253
    254/**
    255 * Timestamp for when the test was started.
    256 * @type {number}
    257 * @private
    258 */
    259goog.testing.TestCase.prototype.startTime_ = 0;
    260
    261
    262/**
    263 * Time since the last batch of tests was started, if batchTime exceeds
    264 * {@link #maxRunTime} a timeout will be used to stop the tests blocking the
    265 * browser and a new batch will be started.
    266 * @type {number}
    267 * @private
    268 */
    269goog.testing.TestCase.prototype.batchTime_ = 0;
    270
    271
    272/**
    273 * Pointer to the current test.
    274 * @type {number}
    275 * @private
    276 */
    277goog.testing.TestCase.prototype.currentTestPointer_ = 0;
    278
    279
    280/**
    281 * Optional callback that will be executed when the test has finalized.
    282 * @type {Function}
    283 * @private
    284 */
    285goog.testing.TestCase.prototype.onCompleteCallback_ = null;
    286
    287
    288/**
    289 * Adds a new test to the test case.
    290 * @param {goog.testing.TestCase.Test} test The test to add.
    291 */
    292goog.testing.TestCase.prototype.add = function(test) {
    293 if (this.started) {
    294 throw Error('Tests cannot be added after execute() has been called. ' +
    295 'Test: ' + test.name);
    296 }
    297
    298 this.tests_.push(test);
    299};
    300
    301
    302/**
    303 * Creates and adds a new test.
    304 *
    305 * Convenience function to make syntax less awkward when not using automatic
    306 * test discovery.
    307 *
    308 * @param {string} name The test name.
    309 * @param {!Function} ref Reference to the test function.
    310 * @param {!Object=} opt_scope Optional scope that the test function should be
    311 * called in.
    312 */
    313goog.testing.TestCase.prototype.addNewTest = function(name, ref, opt_scope) {
    314 var test = new goog.testing.TestCase.Test(name, ref, opt_scope || this);
    315 this.add(test);
    316};
    317
    318
    319/**
    320 * Sets the tests.
    321 * @param {!Array<goog.testing.TestCase.Test>} tests A new test array.
    322 * @protected
    323 */
    324goog.testing.TestCase.prototype.setTests = function(tests) {
    325 this.tests_ = tests;
    326};
    327
    328
    329/**
    330 * Gets the tests.
    331 * @return {!Array<goog.testing.TestCase.Test>} The test array.
    332 */
    333goog.testing.TestCase.prototype.getTests = function() {
    334 return this.tests_;
    335};
    336
    337
    338/**
    339 * Returns the number of tests contained in the test case.
    340 * @return {number} The number of tests.
    341 */
    342goog.testing.TestCase.prototype.getCount = function() {
    343 return this.tests_.length;
    344};
    345
    346
    347/**
    348 * Returns the number of tests actually run in the test case, i.e. subtracting
    349 * any which are skipped.
    350 * @return {number} The number of un-ignored tests.
    351 */
    352goog.testing.TestCase.prototype.getActuallyRunCount = function() {
    353 return this.testsToRun_ ? goog.object.getCount(this.testsToRun_) : 0;
    354};
    355
    356
    357/**
    358 * Returns the current test and increments the pointer.
    359 * @return {goog.testing.TestCase.Test} The current test case.
    360 */
    361goog.testing.TestCase.prototype.next = function() {
    362 var test;
    363 while ((test = this.tests_[this.currentTestPointer_++])) {
    364 if (!this.testsToRun_ || this.testsToRun_[test.name] ||
    365 this.testsToRun_[this.currentTestPointer_ - 1]) {
    366 return test;
    367 }
    368 }
    369 return null;
    370};
    371
    372
    373/**
    374 * Resets the test case pointer, so that next returns the first test.
    375 */
    376goog.testing.TestCase.prototype.reset = function() {
    377 this.currentTestPointer_ = 0;
    378 this.result_ = new goog.testing.TestCase.Result(this);
    379};
    380
    381
    382/**
    383 * Sets the callback function that should be executed when the tests have
    384 * completed.
    385 * @param {Function} fn The callback function.
    386 */
    387goog.testing.TestCase.prototype.setCompletedCallback = function(fn) {
    388 this.onCompleteCallback_ = fn;
    389};
    390
    391
    392/**
    393 * @param {goog.testing.TestCase.Order} order The sort order for running tests.
    394 */
    395goog.testing.TestCase.prototype.setOrder = function(order) {
    396 this.order = order;
    397};
    398
    399
    400/**
    401 * @param {Object<string, boolean>} testsToRun Set of tests to run. Entries in
    402 * the set may be test names, like "testFoo", or numeric indicies. Only
    403 * tests identified by name or by index will be executed.
    404 */
    405goog.testing.TestCase.prototype.setTestsToRun = function(testsToRun) {
    406 this.testsToRun_ = testsToRun;
    407};
    408
    409
    410/**
    411 * Can be overridden in test classes to indicate whether the tests in a case
    412 * should be run in that particular situation. For example, this could be used
    413 * to stop tests running in a particular browser, where browser support for
    414 * the class under test was absent.
    415 * @return {boolean} Whether any of the tests in the case should be run.
    416 */
    417goog.testing.TestCase.prototype.shouldRunTests = function() {
    418 return true;
    419};
    420
    421
    422/**
    423 * Executes the tests, yielding asynchronously if execution time exceeds
    424 * {@link maxRunTime}. There is no guarantee that the test case has finished
    425 * once this method has returned. To be notified when the test case
    426 * has finished, use {@link #setCompletedCallback} or
    427 * {@link #runTestsReturningPromise}.
    428 */
    429goog.testing.TestCase.prototype.execute = function() {
    430 if (!this.prepareForRun_()) {
    431 return;
    432 }
    433 this.log('Starting tests: ' + this.name_);
    434 this.cycleTests();
    435};
    436
    437
    438/**
    439 * Sets up the internal state of the test case for a run.
    440 * @return {boolean} If false, preparation failed because the test case
    441 * is not supposed to run in the present environment.
    442 * @private
    443 */
    444goog.testing.TestCase.prototype.prepareForRun_ = function() {
    445 this.started = true;
    446 this.reset();
    447 this.startTime_ = this.now();
    448 this.running = true;
    449 this.result_.totalCount = this.getCount();
    450 if (!this.shouldRunTests()) {
    451 this.log('shouldRunTests() returned false, skipping these tests.');
    452 this.result_.testSuppressed = true;
    453 this.finalize();
    454 return false;
    455 }
    456 return true;
    457};
    458
    459
    460/**
    461 * Finalizes the test case, called when the tests have finished executing.
    462 */
    463goog.testing.TestCase.prototype.finalize = function() {
    464 this.saveMessage('Done');
    465
    466 this.tearDownPage();
    467
    468 var restoredSetTimeout =
    469 goog.testing.TestCase.protectedSetTimeout_ == goog.global.setTimeout &&
    470 goog.testing.TestCase.protectedClearTimeout_ == goog.global.clearTimeout;
    471 if (!restoredSetTimeout && goog.testing.TestCase.IS_IE &&
    472 String(goog.global.setTimeout) ==
    473 goog.testing.TestCase.setTimeoutAsString_) {
    474 // In strange cases, IE's value of setTimeout *appears* to change, but
    475 // the string representation stays stable.
    476 restoredSetTimeout = true;
    477 }
    478
    479 if (!restoredSetTimeout) {
    480 var message = 'ERROR: Test did not restore setTimeout and clearTimeout';
    481 this.saveMessage(message);
    482 var err = new goog.testing.TestCase.Error(this.name_, message);
    483 this.result_.errors.push(err);
    484 }
    485 goog.global.clearTimeout = goog.testing.TestCase.protectedClearTimeout_;
    486 goog.global.setTimeout = goog.testing.TestCase.protectedSetTimeout_;
    487 this.endTime_ = this.now();
    488 this.running = false;
    489 this.result_.runTime = this.endTime_ - this.startTime_;
    490 this.result_.numFilesLoaded = this.countNumFilesLoaded_();
    491 this.result_.complete = true;
    492
    493 this.log(this.result_.getSummary());
    494 if (this.result_.isSuccess()) {
    495 this.log('Tests complete');
    496 } else {
    497 this.log('Tests Failed');
    498 }
    499 if (this.onCompleteCallback_) {
    500 var fn = this.onCompleteCallback_;
    501 // Execute's the completed callback in the context of the global object.
    502 fn();
    503 this.onCompleteCallback_ = null;
    504 }
    505};
    506
    507
    508/**
    509 * Saves a message to the result set.
    510 * @param {string} message The message to save.
    511 */
    512goog.testing.TestCase.prototype.saveMessage = function(message) {
    513 this.result_.messages.push(this.getTimeStamp_() + ' ' + message);
    514};
    515
    516
    517/**
    518 * @return {boolean} Whether the test case is running inside the multi test
    519 * runner.
    520 */
    521goog.testing.TestCase.prototype.isInsideMultiTestRunner = function() {
    522 var top = goog.global['top'];
    523 return top && typeof top['_allTests'] != 'undefined';
    524};
    525
    526
    527/**
    528 * Logs an object to the console, if available.
    529 * @param {*} val The value to log. Will be ToString'd.
    530 */
    531goog.testing.TestCase.prototype.log = function(val) {
    532 if (!this.isInsideMultiTestRunner() && goog.global.console) {
    533 if (typeof val == 'string') {
    534 val = this.getTimeStamp_() + ' : ' + val;
    535 }
    536 if (val instanceof Error && val.stack) {
    537 // Chrome does console.log asynchronously in a different process
    538 // (http://code.google.com/p/chromium/issues/detail?id=50316).
    539 // This is an acute problem for Errors, which almost never survive.
    540 // Grab references to the immutable strings so they survive.
    541 goog.global.console.log(val, val.message, val.stack);
    542 // TODO(gboyer): Consider for Chrome cloning any object if we can ensure
    543 // there are no circular references.
    544 } else {
    545 goog.global.console.log(val);
    546 }
    547 }
    548};
    549
    550
    551/**
    552 * @return {boolean} Whether the test was a success.
    553 */
    554goog.testing.TestCase.prototype.isSuccess = function() {
    555 return !!this.result_ && this.result_.isSuccess();
    556};
    557
    558
    559/**
    560 * Returns a string detailing the results from the test.
    561 * @param {boolean=} opt_verbose If true results will include data about all
    562 * tests, not just what failed.
    563 * @return {string} The results from the test.
    564 */
    565goog.testing.TestCase.prototype.getReport = function(opt_verbose) {
    566 var rv = [];
    567
    568 if (this.running) {
    569 rv.push(this.name_ + ' [RUNNING]');
    570 } else {
    571 var label = this.result_.isSuccess() ? 'PASSED' : 'FAILED';
    572 rv.push(this.name_ + ' [' + label + ']');
    573 }
    574
    575 if (goog.global.location) {
    576 rv.push(this.trimPath_(goog.global.location.href));
    577 }
    578
    579 rv.push(this.result_.getSummary());
    580
    581 if (opt_verbose) {
    582 rv.push('.', this.result_.messages.join('\n'));
    583 } else if (!this.result_.isSuccess()) {
    584 rv.push(this.result_.errors.join('\n'));
    585 }
    586
    587 rv.push(' ');
    588
    589 return rv.join('\n');
    590};
    591
    592
    593/**
    594 * Returns the test results.
    595 * @return {!goog.testing.TestCase.Result}
    596 * @package
    597 */
    598goog.testing.TestCase.prototype.getResult = function() {
    599 return this.result_;
    600};
    601
    602
    603/**
    604 * Returns the amount of time it took for the test to run.
    605 * @return {number} The run time, in milliseconds.
    606 */
    607goog.testing.TestCase.prototype.getRunTime = function() {
    608 return this.result_.runTime;
    609};
    610
    611
    612/**
    613 * Returns the number of script files that were loaded in order to run the test.
    614 * @return {number} The number of script files.
    615 */
    616goog.testing.TestCase.prototype.getNumFilesLoaded = function() {
    617 return this.result_.numFilesLoaded;
    618};
    619
    620
    621/**
    622 * Returns the test results object: a map from test names to a list of test
    623 * failures (if any exist).
    624 * @return {!Object<string, !Array<string>>} Tests results object.
    625 */
    626goog.testing.TestCase.prototype.getTestResults = function() {
    627 return this.result_.resultsByName;
    628};
    629
    630
    631/**
    632 * Executes each of the tests, yielding asynchronously if execution time
    633 * exceeds {@link #maxRunTime}. There is no guarantee that the test case
    634 * has finished execution once this method has returned.
    635 * To be notified when the test case has finished execution, use
    636 * {@link #setCompletedCallback} or {@link #runTestsReturningPromise}.
    637 *
    638 * Overridable by the individual test case. This allows test cases to defer
    639 * when the test is actually started. If overridden, finalize must be called
    640 * by the test to indicate it has finished.
    641 */
    642goog.testing.TestCase.prototype.runTests = function() {
    643 try {
    644 this.setUpPage();
    645 } catch (e) {
    646 this.exceptionBeforeTest = e;
    647 }
    648 this.execute();
    649};
    650
    651
    652/**
    653 * Executes each of the tests, returning a promise that resolves with the
    654 * test results once they are done running.
    655 * @return {!IThenable.<!goog.testing.TestCase.Result>}
    656 * @final
    657 * @package
    658 */
    659goog.testing.TestCase.prototype.runTestsReturningPromise = function() {
    660 try {
    661 this.setUpPage();
    662 } catch (e) {
    663 this.exceptionBeforeTest = e;
    664 }
    665 if (!this.prepareForRun_()) {
    666 return goog.Promise.resolve(this.result_);
    667 }
    668 this.log('Starting tests: ' + this.name_);
    669 this.saveMessage('Start');
    670 this.batchTime_ = this.now();
    671 return new goog.Promise(function(resolve) {
    672 this.runNextTestCallback_ = resolve;
    673 this.runNextTest_();
    674 }, this);
    675};
    676
    677
    678/**
    679 * Executes the next test method synchronously or with promises, depending on
    680 * the test method's return value.
    681 *
    682 * If the test method returns a promise, the next test method will run once
    683 * the promise is resolved or rejected. If the test method does not
    684 * return a promise, it is assumed to be synchronous, and execution proceeds
    685 * immediately to the next test method. This means that test cases can run
    686 * partially synchronously and partially asynchronously, depending on
    687 * the return values of their test methods. In particular, a test case
    688 * executes synchronously until the first promise is returned from a
    689 * test method (or until a resource limit is reached; see
    690 * {@link finishTestInvocation_}).
    691 * @private
    692 */
    693goog.testing.TestCase.prototype.runNextTest_ = function() {
    694 this.curTest_ = this.next();
    695 if (!this.curTest_ || !this.running) {
    696 this.finalize();
    697 this.runNextTestCallback_(this.result_);
    698 return;
    699 }
    700 this.result_.runCount++;
    701 this.log('Running test: ' + this.curTest_.name);
    702 if (this.maybeFailTestEarly(this.curTest_)) {
    703 this.finishTestInvocation_();
    704 return;
    705 }
    706 goog.testing.TestCase.currentTestName = this.curTest_.name;
    707 this.invokeTestFunction_(
    708 this.setUp, this.safeRunTest_, this.safeTearDown_);
    709};
    710
    711
    712/**
    713 * Calls the given test function, handling errors appropriately.
    714 * @private
    715 */
    716goog.testing.TestCase.prototype.safeRunTest_ = function() {
    717 this.invokeTestFunction_(
    718 goog.bind(this.curTest_.ref, this.curTest_.scope),
    719 this.safeTearDown_,
    720 this.safeTearDown_);
    721};
    722
    723
    724/**
    725 * Calls {@link tearDown}, handling errors appropriately.
    726 * @param {*=} opt_error Error associated with the test, if any.
    727 * @private
    728 */
    729goog.testing.TestCase.prototype.safeTearDown_ = function(opt_error) {
    730 if (arguments.length == 1) {
    731 this.doError(this.curTest_, opt_error);
    732 }
    733 this.invokeTestFunction_(
    734 this.tearDown, this.finishTestInvocation_, this.finishTestInvocation_);
    735};
    736
    737
    738/**
    739 * Calls the given {@code fn}, then calls either {@code onSuccess} or
    740 * {@code onFailure}, either synchronously or using promises, depending on
    741 * {@code fn}'s return value.
    742 *
    743 * If {@code fn} throws an exception, {@code onFailure} is called immediately
    744 * with the exception.
    745 *
    746 * If {@code fn} returns a promise, and the promise is eventually resolved,
    747 * {@code onSuccess} is called with no arguments. If the promise is eventually
    748 * rejected, {@code onFailure} is called with the rejection reason.
    749 *
    750 * Otherwise, if {@code fn} neither returns a promise nor throws an exception,
    751 * {@code onSuccess} is called immediately with no arguments.
    752 *
    753 * {@code fn}, {@code onSuccess}, and {@code onFailure} are all called with
    754 * the TestCase instance as the method receiver.
    755 *
    756 * @param {function()} fn The function to call.
    757 * @param {function()} onSuccess Success callback.
    758 * @param {function(*)} onFailure Failure callback.
    759 * @private
    760 */
    761goog.testing.TestCase.prototype.invokeTestFunction_ = function(
    762 fn, onSuccess, onFailure) {
    763 try {
    764 var retval = fn.call(this);
    765 if (goog.Thenable.isImplementedBy(retval) ||
    766 goog.isFunction(retval && retval['then'])) {
    767 var self = this;
    768 retval.then(
    769 function() {
    770 self.resetBatchTimeAfterPromise_();
    771 onSuccess.call(self);
    772 },
    773 function(e) {
    774 self.resetBatchTimeAfterPromise_();
    775 onFailure.call(self, e);
    776 });
    777 } else {
    778 onSuccess.call(this);
    779 }
    780 } catch (e) {
    781 onFailure.call(this, e);
    782 }
    783};
    784
    785
    786/**
    787 * Resets the batch run timer. This should only be called after resolving a
    788 * promise since Promise.then() has an implicit yield.
    789 * @private
    790 */
    791goog.testing.TestCase.prototype.resetBatchTimeAfterPromise_ = function() {
    792 this.batchTime_ = this.now();
    793};
    794
    795
    796/**
    797 * Finishes up bookkeeping for the current test function, and schedules
    798 * the next test function to run, either immediately or asychronously.
    799 * @param {*=} opt_error Optional error resulting from the test invocation.
    800 * @private
    801 */
    802goog.testing.TestCase.prototype.finishTestInvocation_ = function(opt_error) {
    803 if (arguments.length == 1) {
    804 this.doError(this.curTest_, opt_error);
    805 }
    806
    807 // If no errors have been recorded for the test, it is a success.
    808 if (!(this.curTest_.name in this.result_.resultsByName) ||
    809 !this.result_.resultsByName[this.curTest_.name].length) {
    810 this.doSuccess(this.curTest_);
    811 }
    812
    813 goog.testing.TestCase.currentTestName = null;
    814
    815 // If the test case has consumed too much time or stack space,
    816 // yield to avoid blocking the browser. Otherwise, proceed to the next test.
    817 if (this.depth_ > goog.testing.TestCase.MAX_STACK_DEPTH_ ||
    818 this.now() - this.batchTime_ > goog.testing.TestCase.maxRunTime) {
    819 this.saveMessage('Breaking async');
    820 this.timeout(goog.bind(this.startNextBatch_, this), 0);
    821 } else {
    822 ++this.depth_;
    823 this.runNextTest_();
    824 }
    825};
    826
    827
    828/**
    829 * Start a new batch to tests after yielding, resetting batchTime and depth.
    830 * @private
    831 */
    832goog.testing.TestCase.prototype.startNextBatch_ = function() {
    833 this.batchTime_ = this.now();
    834 this.depth_ = 0;
    835 this.runNextTest_();
    836};
    837
    838
    839/**
    840 * Reorders the tests depending on the {@code order} field.
    841 * @private
    842 */
    843goog.testing.TestCase.prototype.orderTests_ = function() {
    844 switch (this.order) {
    845 case goog.testing.TestCase.Order.RANDOM:
    846 // Fisher-Yates shuffle
    847 var i = this.tests_.length;
    848 while (i > 1) {
    849 // goog.math.randomInt is inlined to reduce dependencies.
    850 var j = Math.floor(Math.random() * i); // exclusive
    851 i--;
    852 var tmp = this.tests_[i];
    853 this.tests_[i] = this.tests_[j];
    854 this.tests_[j] = tmp;
    855 }
    856 break;
    857
    858 case goog.testing.TestCase.Order.SORTED:
    859 this.tests_.sort(function(t1, t2) {
    860 if (t1.name == t2.name) {
    861 return 0;
    862 }
    863 return t1.name < t2.name ? -1 : 1;
    864 });
    865 break;
    866
    867 // Do nothing for NATURAL.
    868 }
    869};
    870
    871
    872/**
    873 * Gets list of objects that potentially contain test cases. For IE 8 and below,
    874 * this is the global "this" (for properties set directly on the global this or
    875 * window) and the RuntimeObject (for global variables and functions). For all
    876 * other browsers, the array simply contains the global this.
    877 *
    878 * @param {string=} opt_prefix An optional prefix. If specified, only get things
    879 * under this prefix. Note that the prefix is only honored in IE, since it
    880 * supports the RuntimeObject:
    881 * http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
    882 * TODO: Remove this option.
    883 * @return {!Array<!Object>} A list of objects that should be inspected.
    884 */
    885goog.testing.TestCase.prototype.getGlobals = function(opt_prefix) {
    886 return goog.testing.TestCase.getGlobals(opt_prefix);
    887};
    888
    889
    890/**
    891 * Gets list of objects that potentially contain test cases. For IE 8 and below,
    892 * this is the global "this" (for properties set directly on the global this or
    893 * window) and the RuntimeObject (for global variables and functions). For all
    894 * other browsers, the array simply contains the global this.
    895 *
    896 * @param {string=} opt_prefix An optional prefix. If specified, only get things
    897 * under this prefix. Note that the prefix is only honored in IE, since it
    898 * supports the RuntimeObject:
    899 * http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
    900 * TODO: Remove this option.
    901 * @return {!Array<!Object>} A list of objects that should be inspected.
    902 */
    903goog.testing.TestCase.getGlobals = function(opt_prefix) {
    904 // Look in the global scope for most browsers, on IE we use the little known
    905 // RuntimeObject which holds references to all globals. We reference this
    906 // via goog.global so that there isn't an aliasing that throws an exception
    907 // in Firefox.
    908 return typeof goog.global['RuntimeObject'] != 'undefined' ?
    909 [goog.global['RuntimeObject']((opt_prefix || '') + '*'), goog.global] :
    910 [goog.global];
    911};
    912
    913
    914/**
    915 * Gets called before any tests are executed. Can be overridden to set up the
    916 * environment for the whole test case.
    917 */
    918goog.testing.TestCase.prototype.setUpPage = function() {};
    919
    920
    921/**
    922 * Gets called after all tests have been executed. Can be overridden to tear
    923 * down the entire test case.
    924 */
    925goog.testing.TestCase.prototype.tearDownPage = function() {};
    926
    927
    928/**
    929 * Gets called before every goog.testing.TestCase.Test is been executed. Can be
    930 * overridden to add set up functionality to each test.
    931 */
    932goog.testing.TestCase.prototype.setUp = function() {};
    933
    934
    935/**
    936 * Gets called after every goog.testing.TestCase.Test has been executed. Can be
    937 * overriden to add tear down functionality to each test.
    938 */
    939goog.testing.TestCase.prototype.tearDown = function() {};
    940
    941
    942/**
    943 * @return {string} The function name prefix used to auto-discover tests.
    944 */
    945goog.testing.TestCase.prototype.getAutoDiscoveryPrefix = function() {
    946 return 'test';
    947};
    948
    949
    950/**
    951 * @return {number} Time since the last batch of tests was started.
    952 * @protected
    953 */
    954goog.testing.TestCase.prototype.getBatchTime = function() {
    955 return this.batchTime_;
    956};
    957
    958
    959/**
    960 * @param {number} batchTime Time since the last batch of tests was started.
    961 * @protected
    962 */
    963goog.testing.TestCase.prototype.setBatchTime = function(batchTime) {
    964 this.batchTime_ = batchTime;
    965};
    966
    967
    968/**
    969 * Creates a {@code goog.testing.TestCase.Test} from an auto-discovered
    970 * function.
    971 * @param {string} name The name of the function.
    972 * @param {function() : void} ref The auto-discovered function.
    973 * @return {!goog.testing.TestCase.Test} The newly created test.
    974 * @protected
    975 */
    976goog.testing.TestCase.prototype.createTestFromAutoDiscoveredFunction =
    977 function(name, ref) {
    978 return new goog.testing.TestCase.Test(name, ref, goog.global);
    979};
    980
    981
    982/**
    983 * Adds any functions defined on 'obj' (the global object, by default)
    984 * that correspond to lifecycle events for the test case. Overrides
    985 * setUp, tearDown, setUpPage, tearDownPage, runTests, and shouldRunTests
    986 * if they are defined on 'obj'.
    987 * @param {!Object=} opt_obj Defaults to goog.global.
    988 */
    989goog.testing.TestCase.prototype.autoDiscoverLifecycle = function(opt_obj) {
    990 var obj = opt_obj || goog.global;
    991 if (obj['setUp']) {
    992 this.setUp = goog.bind(obj['setUp'], obj);
    993 }
    994 if (obj['tearDown']) {
    995 this.tearDown = goog.bind(obj['tearDown'], obj);
    996 }
    997 if (obj['setUpPage']) {
    998 this.setUpPage = goog.bind(obj['setUpPage'], obj);
    999 }
    1000 if (obj['tearDownPage']) {
    1001 this.tearDownPage = goog.bind(obj['tearDownPage'], obj);
    1002 }
    1003 if (obj['runTests']) {
    1004 this.runTests = goog.bind(obj['runTests'], obj);
    1005 }
    1006 if (obj['shouldRunTests']) {
    1007 this.shouldRunTests = goog.bind(obj['shouldRunTests'], obj);
    1008 }
    1009};
    1010
    1011
    1012// TODO(johnlenz): make this package private
    1013/**
    1014 * @param {!Object} obj An object from which to extract test and lifecycle
    1015 * methods.
    1016 */
    1017goog.testing.TestCase.prototype.setTestObj = function(obj) {
    1018 // Drop any previously added (likely auto-discovered) tests, only one source
    1019 // of discovered test and life-cycle methods is allowed.
    1020 goog.asserts.assert(this.tests_.length == 0,
    1021 'Test methods have already been configured.');
    1022
    1023 var regex = new RegExp('^' + this.getAutoDiscoveryPrefix());
    1024 for (var name in obj) {
    1025 if (regex.test(name)) {
    1026 var testMethod = obj[name];
    1027 if (goog.isFunction(testMethod)) {
    1028 this.addNewTest(name, testMethod, obj);
    1029 }
    1030 }
    1031 }
    1032
    1033 this.autoDiscoverLifecycle(obj);
    1034};
    1035
    1036
    1037/**
    1038 * Adds any functions defined in the global scope that are prefixed with "test"
    1039 * to the test case.
    1040 */
    1041goog.testing.TestCase.prototype.autoDiscoverTests = function() {
    1042 var prefix = this.getAutoDiscoveryPrefix();
    1043 var testSources = this.getGlobals(prefix);
    1044
    1045 var foundTests = [];
    1046
    1047 for (var i = 0; i < testSources.length; i++) {
    1048 var testSource = testSources[i];
    1049 for (var name in testSource) {
    1050 if ((new RegExp('^' + prefix)).test(name)) {
    1051 var ref;
    1052 try {
    1053 ref = testSource[name];
    1054 } catch (ex) {
    1055 // NOTE(brenneman): When running tests from a file:// URL on Firefox
    1056 // 3.5 for Windows, any reference to goog.global.sessionStorage raises
    1057 // an "Operation is not supported" exception. Ignore any exceptions
    1058 // raised by simply accessing global properties.
    1059 ref = undefined;
    1060 }
    1061
    1062 if (goog.isFunction(ref)) {
    1063 foundTests.push(this.createTestFromAutoDiscoveredFunction(name, ref));
    1064 }
    1065 }
    1066 }
    1067 }
    1068
    1069 for (var i = 0; i < foundTests.length; i++) {
    1070 this.add(foundTests[i]);
    1071 }
    1072 this.orderTests_();
    1073
    1074 this.log(this.getCount() + ' tests auto-discovered');
    1075
    1076 // TODO(user): Do this as a separate call. Unfortunately, a lot of projects
    1077 // currently override autoDiscoverTests and expect lifecycle events to be
    1078 // registered as a part of this call.
    1079 this.autoDiscoverLifecycle();
    1080};
    1081
    1082
    1083/**
    1084 * Checks to see if the test should be marked as failed before it is run.
    1085 *
    1086 * If there was an error in setUpPage, we treat that as a failure for all tests
    1087 * and mark them all as having failed.
    1088 *
    1089 * @param {goog.testing.TestCase.Test} testCase The current test case.
    1090 * @return {boolean} Whether the test was marked as failed.
    1091 * @protected
    1092 */
    1093goog.testing.TestCase.prototype.maybeFailTestEarly = function(testCase) {
    1094 if (this.exceptionBeforeTest) {
    1095 // We just use the first error to report an error on a failed test.
    1096 this.curTest_.name = 'setUpPage for ' + this.curTest_.name;
    1097 this.doError(this.curTest_, this.exceptionBeforeTest);
    1098 return true;
    1099 }
    1100 return false;
    1101};
    1102
    1103
    1104/**
    1105 * Cycles through the tests, yielding asynchronously if the execution time
    1106 * execeeds {@link #maxRunTime}. In particular, there is no guarantee that
    1107 * the test case has finished execution once this method has returned.
    1108 * To be notified when the test case has finished execution, use
    1109 * {@link #setCompletedCallback} or {@link #runTestsReturningPromise}.
    1110 */
    1111goog.testing.TestCase.prototype.cycleTests = function() {
    1112 this.saveMessage('Start');
    1113 this.batchTime_ = this.now();
    1114 if (this.running) {
    1115 this.runNextTestCallback_ = goog.nullFunction;
    1116 // Kick off the tests. runNextTest_ will schedule all of the tests,
    1117 // using a mixture of synchronous and asynchronous strategies.
    1118 this.runNextTest_();
    1119 }
    1120};
    1121
    1122
    1123/**
    1124 * Counts the number of files that were loaded for dependencies that are
    1125 * required to run the test.
    1126 * @return {number} The number of files loaded.
    1127 * @private
    1128 */
    1129goog.testing.TestCase.prototype.countNumFilesLoaded_ = function() {
    1130 var scripts = document.getElementsByTagName(goog.dom.TagName.SCRIPT);
    1131 var count = 0;
    1132 for (var i = 0, n = scripts.length; i < n; i++) {
    1133 if (scripts[i].src) {
    1134 count++;
    1135 }
    1136 }
    1137 return count;
    1138};
    1139
    1140
    1141/**
    1142 * Calls a function after a delay, using the protected timeout.
    1143 * @param {Function} fn The function to call.
    1144 * @param {number} time Delay in milliseconds.
    1145 * @return {number} The timeout id.
    1146 * @protected
    1147 */
    1148goog.testing.TestCase.prototype.timeout = function(fn, time) {
    1149 // NOTE: invoking protectedSetTimeout_ as a member of goog.testing.TestCase
    1150 // would result in an Illegal Invocation error. The method must be executed
    1151 // with the global context.
    1152 var protectedSetTimeout = goog.testing.TestCase.protectedSetTimeout_;
    1153 return protectedSetTimeout(fn, time);
    1154};
    1155
    1156
    1157/**
    1158 * Clears a timeout created by {@code this.timeout()}.
    1159 * @param {number} id A timeout id.
    1160 * @protected
    1161 */
    1162goog.testing.TestCase.prototype.clearTimeout = function(id) {
    1163 // NOTE: see execution note for protectedSetTimeout above.
    1164 var protectedClearTimeout = goog.testing.TestCase.protectedClearTimeout_;
    1165 protectedClearTimeout(id);
    1166};
    1167
    1168
    1169/**
    1170 * @return {number} The current time in milliseconds, don't use goog.now as some
    1171 * tests override it.
    1172 * @protected
    1173 */
    1174goog.testing.TestCase.prototype.now = function() {
    1175 // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
    1176 var protectedDate = goog.testing.TestCase.protectedDate_;
    1177 return new protectedDate().getTime();
    1178};
    1179
    1180
    1181/**
    1182 * Returns the current time.
    1183 * @return {string} HH:MM:SS.
    1184 * @private
    1185 */
    1186goog.testing.TestCase.prototype.getTimeStamp_ = function() {
    1187 // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
    1188 var protectedDate = goog.testing.TestCase.protectedDate_;
    1189 var d = new protectedDate();
    1190
    1191 // Ensure millis are always 3-digits
    1192 var millis = '00' + d.getMilliseconds();
    1193 millis = millis.substr(millis.length - 3);
    1194
    1195 return this.pad_(d.getHours()) + ':' + this.pad_(d.getMinutes()) + ':' +
    1196 this.pad_(d.getSeconds()) + '.' + millis;
    1197};
    1198
    1199
    1200/**
    1201 * Pads a number to make it have a leading zero if it's less than 10.
    1202 * @param {number} number The number to pad.
    1203 * @return {string} The resulting string.
    1204 * @private
    1205 */
    1206goog.testing.TestCase.prototype.pad_ = function(number) {
    1207 return number < 10 ? '0' + number : String(number);
    1208};
    1209
    1210
    1211/**
    1212 * Trims a path to be only that after google3.
    1213 * @param {string} path The path to trim.
    1214 * @return {string} The resulting string.
    1215 * @private
    1216 */
    1217goog.testing.TestCase.prototype.trimPath_ = function(path) {
    1218 return path.substring(path.indexOf('google3') + 8);
    1219};
    1220
    1221
    1222/**
    1223 * Handles a test that passed.
    1224 * @param {goog.testing.TestCase.Test} test The test that passed.
    1225 * @protected
    1226 */
    1227goog.testing.TestCase.prototype.doSuccess = function(test) {
    1228 this.result_.successCount++;
    1229 // An empty list of error messages indicates that the test passed.
    1230 // If we already have a failure for this test, do not set to empty list.
    1231 if (!(test.name in this.result_.resultsByName)) {
    1232 this.result_.resultsByName[test.name] = [];
    1233 }
    1234 var message = test.name + ' : PASSED';
    1235 this.saveMessage(message);
    1236 this.log(message);
    1237};
    1238
    1239
    1240/**
    1241 * Handles a test that failed.
    1242 * @param {goog.testing.TestCase.Test} test The test that failed.
    1243 * @param {*=} opt_e The exception object associated with the
    1244 * failure or a string.
    1245 * @protected
    1246 */
    1247goog.testing.TestCase.prototype.doError = function(test, opt_e) {
    1248 var message = test.name + ' : FAILED';
    1249 this.log(message);
    1250 this.saveMessage(message);
    1251 var err = this.logError(test.name, opt_e);
    1252 this.result_.errors.push(err);
    1253 if (test.name in this.result_.resultsByName) {
    1254 this.result_.resultsByName[test.name].push(err.toString());
    1255 } else {
    1256 this.result_.resultsByName[test.name] = [err.toString()];
    1257 }
    1258};
    1259
    1260
    1261/**
    1262 * @param {string} name Failed test name.
    1263 * @param {*=} opt_e The exception object associated with the
    1264 * failure or a string.
    1265 * @return {!goog.testing.TestCase.Error} Error object.
    1266 */
    1267goog.testing.TestCase.prototype.logError = function(name, opt_e) {
    1268 var errMsg = null;
    1269 var stack = null;
    1270 if (opt_e) {
    1271 this.log(opt_e);
    1272 if (goog.isString(opt_e)) {
    1273 errMsg = opt_e;
    1274 } else {
    1275 errMsg = opt_e.message || opt_e.description || opt_e.toString();
    1276 stack = opt_e.stack ? goog.testing.stacktrace.canonicalize(opt_e.stack) :
    1277 opt_e['stackTrace'];
    1278 }
    1279 } else {
    1280 errMsg = 'An unknown error occurred';
    1281 }
    1282 var err = new goog.testing.TestCase.Error(name, errMsg, stack);
    1283
    1284 // Avoid double logging.
    1285 if (!opt_e || !opt_e['isJsUnitException'] ||
    1286 !opt_e['loggedJsUnitException']) {
    1287 this.saveMessage(err.toString());
    1288 }
    1289 if (opt_e && opt_e['isJsUnitException']) {
    1290 opt_e['loggedJsUnitException'] = true;
    1291 }
    1292
    1293 return err;
    1294};
    1295
    1296
    1297
    1298/**
    1299 * A class representing a single test function.
    1300 * @param {string} name The test name.
    1301 * @param {Function} ref Reference to the test function.
    1302 * @param {Object=} opt_scope Optional scope that the test function should be
    1303 * called in.
    1304 * @constructor
    1305 */
    1306goog.testing.TestCase.Test = function(name, ref, opt_scope) {
    1307 /**
    1308 * The name of the test.
    1309 * @type {string}
    1310 */
    1311 this.name = name;
    1312
    1313 /**
    1314 * Reference to the test function.
    1315 * @type {Function}
    1316 */
    1317 this.ref = ref;
    1318
    1319 /**
    1320 * Scope that the test function should be called in.
    1321 * @type {Object}
    1322 */
    1323 this.scope = opt_scope || null;
    1324};
    1325
    1326
    1327/**
    1328 * Executes the test function.
    1329 * @package
    1330 */
    1331goog.testing.TestCase.Test.prototype.execute = function() {
    1332 this.ref.call(this.scope);
    1333};
    1334
    1335
    1336
    1337/**
    1338 * A class for representing test results. A bag of public properties.
    1339 * @param {goog.testing.TestCase} testCase The test case that owns this result.
    1340 * @constructor
    1341 * @final
    1342 */
    1343goog.testing.TestCase.Result = function(testCase) {
    1344 /**
    1345 * The test case that owns this result.
    1346 * @type {goog.testing.TestCase}
    1347 * @private
    1348 */
    1349 this.testCase_ = testCase;
    1350
    1351 /**
    1352 * Total number of tests that should have been run.
    1353 * @type {number}
    1354 */
    1355 this.totalCount = 0;
    1356
    1357 /**
    1358 * Total number of tests that were actually run.
    1359 * @type {number}
    1360 */
    1361 this.runCount = 0;
    1362
    1363 /**
    1364 * Number of successful tests.
    1365 * @type {number}
    1366 */
    1367 this.successCount = 0;
    1368
    1369 /**
    1370 * The amount of time the tests took to run.
    1371 * @type {number}
    1372 */
    1373 this.runTime = 0;
    1374
    1375 /**
    1376 * The number of files loaded to run this test.
    1377 * @type {number}
    1378 */
    1379 this.numFilesLoaded = 0;
    1380
    1381 /**
    1382 * Whether this test case was suppressed by shouldRunTests() returning false.
    1383 * @type {boolean}
    1384 */
    1385 this.testSuppressed = false;
    1386
    1387 /**
    1388 * Test results for each test that was run. The test name is always added
    1389 * as the key in the map, and the array of strings is an optional list
    1390 * of failure messages. If the array is empty, the test passed. Otherwise,
    1391 * the test failed.
    1392 * @type {!Object<string, !Array<string>>}
    1393 */
    1394 this.resultsByName = {};
    1395
    1396 /**
    1397 * Errors encountered while running the test.
    1398 * @type {!Array<goog.testing.TestCase.Error>}
    1399 */
    1400 this.errors = [];
    1401
    1402 /**
    1403 * Messages to show the user after running the test.
    1404 * @type {!Array<string>}
    1405 */
    1406 this.messages = [];
    1407
    1408 /**
    1409 * Whether the tests have completed.
    1410 * @type {boolean}
    1411 */
    1412 this.complete = false;
    1413};
    1414
    1415
    1416/**
    1417 * @return {boolean} Whether the test was successful.
    1418 */
    1419goog.testing.TestCase.Result.prototype.isSuccess = function() {
    1420 return this.complete && this.errors.length == 0;
    1421};
    1422
    1423
    1424/**
    1425 * @return {string} A summary of the tests, including total number of tests that
    1426 * passed, failed, and the time taken.
    1427 */
    1428goog.testing.TestCase.Result.prototype.getSummary = function() {
    1429 var summary = this.runCount + ' of ' + this.totalCount + ' tests run in ' +
    1430 this.runTime + 'ms.\n';
    1431 if (this.testSuppressed) {
    1432 summary += 'Tests not run because shouldRunTests() returned false.';
    1433 } else {
    1434 var failures = this.totalCount - this.successCount;
    1435 var suppressionMessage = '';
    1436
    1437 var countOfRunTests = this.testCase_.getActuallyRunCount();
    1438 if (countOfRunTests) {
    1439 failures = countOfRunTests - this.successCount;
    1440 suppressionMessage = ', ' +
    1441 (this.totalCount - countOfRunTests) + ' suppressed by querystring';
    1442 }
    1443 summary += this.successCount + ' passed, ' +
    1444 failures + ' failed' + suppressionMessage + '.\n' +
    1445 Math.round(this.runTime / this.runCount) + ' ms/test. ' +
    1446 this.numFilesLoaded + ' files loaded.';
    1447 }
    1448
    1449 return summary;
    1450};
    1451
    1452
    1453/**
    1454 * Initializes the given test case with the global test runner 'G_testRunner'.
    1455 * @param {goog.testing.TestCase} testCase The test case to install.
    1456 */
    1457goog.testing.TestCase.initializeTestRunner = function(testCase) {
    1458 testCase.autoDiscoverTests();
    1459
    1460 if (goog.global.location) {
    1461 var search = goog.global.location.search;
    1462 testCase.setOrder(goog.testing.TestCase.parseOrder_(search) ||
    1463 goog.testing.TestCase.Order.SORTED);
    1464 testCase.setTestsToRun(goog.testing.TestCase.parseRunTests_(search));
    1465 }
    1466
    1467 var gTestRunner = goog.global['G_testRunner'];
    1468 if (gTestRunner) {
    1469 gTestRunner['initialize'](testCase);
    1470 } else {
    1471 throw Error('G_testRunner is undefined. Please ensure goog.testing.jsunit' +
    1472 ' is included.');
    1473 }
    1474};
    1475
    1476
    1477/**
    1478 * Parses URL query parameters for the 'order' parameter.
    1479 * @param {string} search The URL query string.
    1480 * @return {?goog.testing.TestCase.Order} The sort order for running tests.
    1481 * @private
    1482 */
    1483goog.testing.TestCase.parseOrder_ = function(search) {
    1484 var order = null;
    1485 var orderMatch = search.match(
    1486 /(?:\?|&)order=(natural|random|sorted)/i);
    1487 if (orderMatch) {
    1488 order = /** @type {goog.testing.TestCase.Order} */ (
    1489 orderMatch[1].toLowerCase());
    1490 }
    1491 return order;
    1492};
    1493
    1494
    1495/**
    1496 * Parses URL query parameters for the 'runTests' parameter.
    1497 * @param {string} search The URL query string.
    1498 * @return {Object<string, boolean>} A set of test names or test indices to be
    1499 * run by the test runner.
    1500 * @private
    1501 */
    1502goog.testing.TestCase.parseRunTests_ = function(search) {
    1503 var testsToRun = null;
    1504 var runTestsMatch = search.match(/(?:\?|&)runTests=([^?&]+)/i);
    1505 if (runTestsMatch) {
    1506 testsToRun = {};
    1507 var arr = runTestsMatch[1].split(',');
    1508 for (var i = 0, len = arr.length; i < len; i++) {
    1509 testsToRun[arr[i]] = true;
    1510 }
    1511 }
    1512 return testsToRun;
    1513};
    1514
    1515
    1516
    1517/**
    1518 * A class representing an error thrown by the test
    1519 * @param {string} source The name of the test which threw the error.
    1520 * @param {string} message The error message.
    1521 * @param {string=} opt_stack A string showing the execution stack.
    1522 * @constructor
    1523 * @final
    1524 */
    1525goog.testing.TestCase.Error = function(source, message, opt_stack) {
    1526 /**
    1527 * The name of the test which threw the error.
    1528 * @type {string}
    1529 */
    1530 this.source = source;
    1531
    1532 /**
    1533 * Reference to the test function.
    1534 * @type {string}
    1535 */
    1536 this.message = message;
    1537
    1538 /**
    1539 * The stack.
    1540 * @type {?string}
    1541 */
    1542 this.stack = null;
    1543
    1544 if (opt_stack) {
    1545 this.stack = opt_stack;
    1546 } else {
    1547 // Attempt to capture a stack trace.
    1548 if (Error.captureStackTrace) {
    1549 // See https://code.google.com/p/v8-wiki/wiki/JavaScriptStackTraceApi
    1550 Error.captureStackTrace(this, goog.testing.TestCase.Error);
    1551 } else {
    1552 var stack = new Error().stack;
    1553 if (stack) {
    1554 this.stack = stack;
    1555 }
    1556 }
    1557 }
    1558};
    1559
    1560
    1561/**
    1562 * Returns a string representing the error object.
    1563 * @return {string} A string representation of the error.
    1564 * @override
    1565 */
    1566goog.testing.TestCase.Error.prototype.toString = function() {
    1567 return 'ERROR in ' + this.source + '\n' +
    1568 this.message + (this.stack ? '\n' + this.stack : '');
    1569};
    \ 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 index 023ab7f..8edf546 100644 --- a/docs/source/lib/goog/testing/testrunner.js.src.html +++ b/docs/source/lib/goog/testing/testrunner.js.src.html @@ -1 +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.testing.TestCase');
    37
    38
    39
    40/**
    41 * Construct a test runner.
    42 *
    43 * NOTE(user): This is currently pretty weird, I'm essentially trying to
    44 * create a wrapper that the Selenium test can hook into to query the state of
    45 * the running test case, while making goog.testing.TestCase general.
    46 *
    47 * @constructor
    48 */
    49goog.testing.TestRunner = function() {
    50 /**
    51 * Errors that occurred in the window.
    52 * @type {Array.<string>}
    53 */
    54 this.errors = [];
    55};
    56
    57
    58/**
    59 * Reference to the active test case.
    60 * @type {goog.testing.TestCase?}
    61 */
    62goog.testing.TestRunner.prototype.testCase = null;
    63
    64
    65/**
    66 * Whether the test runner has been initialized yet.
    67 * @type {boolean}
    68 */
    69goog.testing.TestRunner.prototype.initialized = false;
    70
    71
    72/**
    73 * Element created in the document to add test results to.
    74 * @type {Element}
    75 * @private
    76 */
    77goog.testing.TestRunner.prototype.logEl_ = null;
    78
    79
    80/**
    81 * Function to use when filtering errors.
    82 * @type {(function(string))?}
    83 * @private
    84 */
    85goog.testing.TestRunner.prototype.errorFilter_ = null;
    86
    87
    88/**
    89 * Whether an empty test case counts as an error.
    90 * @type {boolean}
    91 * @private
    92 */
    93goog.testing.TestRunner.prototype.strict_ = true;
    94
    95
    96/**
    97 * Initializes the test runner.
    98 * @param {goog.testing.TestCase} testCase The test case to initialize with.
    99 */
    100goog.testing.TestRunner.prototype.initialize = function(testCase) {
    101 if (this.testCase && this.testCase.running) {
    102 throw Error('The test runner is already waiting for a test to complete');
    103 }
    104 this.testCase = testCase;
    105 this.initialized = true;
    106};
    107
    108
    109/**
    110 * By default, the test runner is strict, and fails if it runs an empty
    111 * test case.
    112 * @param {boolean} strict Whether the test runner should fail on an empty
    113 * test case.
    114 */
    115goog.testing.TestRunner.prototype.setStrict = function(strict) {
    116 this.strict_ = strict;
    117};
    118
    119
    120/**
    121 * @return {boolean} Whether the test runner should fail on an empty
    122 * test case.
    123 */
    124goog.testing.TestRunner.prototype.isStrict = function() {
    125 return this.strict_;
    126};
    127
    128
    129/**
    130 * Returns true if the test runner is initialized.
    131 * Used by Selenium Hooks.
    132 * @return {boolean} Whether the test runner is active.
    133 */
    134goog.testing.TestRunner.prototype.isInitialized = function() {
    135 return this.initialized;
    136};
    137
    138
    139/**
    140 * Returns true if the test runner is finished.
    141 * Used by Selenium Hooks.
    142 * @return {boolean} Whether the test runner is active.
    143 */
    144goog.testing.TestRunner.prototype.isFinished = function() {
    145 return this.errors.length > 0 ||
    146 this.initialized && !!this.testCase && this.testCase.started &&
    147 !this.testCase.running;
    148};
    149
    150
    151/**
    152 * Returns true if the test case didn't fail.
    153 * Used by Selenium Hooks.
    154 * @return {boolean} Whether the current test returned successfully.
    155 */
    156goog.testing.TestRunner.prototype.isSuccess = function() {
    157 return !this.hasErrors() && !!this.testCase && this.testCase.isSuccess();
    158};
    159
    160
    161/**
    162 * Returns true if the test case runner has errors that were caught outside of
    163 * the test case.
    164 * @return {boolean} Whether there were JS errors.
    165 */
    166goog.testing.TestRunner.prototype.hasErrors = function() {
    167 return this.errors.length > 0;
    168};
    169
    170
    171/**
    172 * Logs an error that occurred. Used in the case of environment setting up
    173 * an onerror handler.
    174 * @param {string} msg Error message.
    175 */
    176goog.testing.TestRunner.prototype.logError = function(msg) {
    177 if (!this.errorFilter_ || this.errorFilter_.call(null, msg)) {
    178 this.errors.push(msg);
    179 }
    180};
    181
    182
    183/**
    184 * Log failure in current running test.
    185 * @param {Error} ex Exception.
    186 */
    187goog.testing.TestRunner.prototype.logTestFailure = function(ex) {
    188 var testName = /** @type {string} */ (goog.testing.TestCase.currentTestName);
    189 if (this.testCase) {
    190 this.testCase.logError(testName, ex);
    191 } else {
    192 // NOTE: Do not forget to log the original exception raised.
    193 throw new Error('Test runner not initialized with a test case. Original ' +
    194 'exception: ' + ex.message);
    195 }
    196};
    197
    198
    199/**
    200 * Sets a function to use as a filter for errors.
    201 * @param {function(string)} fn Filter function.
    202 */
    203goog.testing.TestRunner.prototype.setErrorFilter = function(fn) {
    204 this.errorFilter_ = fn;
    205};
    206
    207
    208/**
    209 * Returns a report of the test case that ran.
    210 * Used by Selenium Hooks.
    211 * @param {boolean=} opt_verbose If true results will include data about all
    212 * tests, not just what failed.
    213 * @return {string} A report summary of the test.
    214 */
    215goog.testing.TestRunner.prototype.getReport = function(opt_verbose) {
    216 var report = [];
    217 if (this.testCase) {
    218 report.push(this.testCase.getReport(opt_verbose));
    219 }
    220 if (this.errors.length > 0) {
    221 report.push('JavaScript errors detected by test runner:');
    222 report.push.apply(report, this.errors);
    223 report.push('\n');
    224 }
    225 return report.join('\n');
    226};
    227
    228
    229/**
    230 * Returns the amount of time it took for the test to run.
    231 * Used by Selenium Hooks.
    232 * @return {number} The run time, in milliseconds.
    233 */
    234goog.testing.TestRunner.prototype.getRunTime = function() {
    235 return this.testCase ? this.testCase.getRunTime() : 0;
    236};
    237
    238
    239/**
    240 * Returns the number of script files that were loaded in order to run the test.
    241 * @return {number} The number of script files.
    242 */
    243goog.testing.TestRunner.prototype.getNumFilesLoaded = function() {
    244 return this.testCase ? this.testCase.getNumFilesLoaded() : 0;
    245};
    246
    247
    248/**
    249 * Executes a test case and prints the results to the window.
    250 */
    251goog.testing.TestRunner.prototype.execute = function() {
    252 if (!this.testCase) {
    253 throw Error('The test runner must be initialized with a test case ' +
    254 'before execute can be called.');
    255 }
    256
    257 if (this.strict_ && this.testCase.getCount() == 0) {
    258 throw Error(
    259 'No tests found in given test case: ' +
    260 this.testCase.getName() + ' ' +
    261 'By default, the test runner fails if a test case has no tests. ' +
    262 'To modify this behavior, see goog.testing.TestRunner\'s ' +
    263 'setStrict() method, or G_testRunner.setStrict()');
    264 }
    265
    266 this.testCase.setCompletedCallback(goog.bind(this.onComplete_, this));
    267 this.testCase.runTests();
    268};
    269
    270
    271/**
    272 * Writes the results to the document when the test case completes.
    273 * @private
    274 */
    275goog.testing.TestRunner.prototype.onComplete_ = function() {
    276 var log = this.testCase.getReport(true);
    277 if (this.errors.length > 0) {
    278 log += '\n' + this.errors.join('\n');
    279 }
    280
    281 if (!this.logEl_) {
    282 var el = document.getElementById('closureTestRunnerLog');
    283 if (el == null) {
    284 el = document.createElement('div');
    285 document.body.appendChild(el);
    286 }
    287 this.logEl_ = el;
    288 }
    289
    290 // Highlight the page to indicate the overall outcome.
    291 this.writeLog(log);
    292
    293 // TODO(user): Make this work with multiple test cases (b/8603638).
    294 var runAgainLink = document.createElement('a');
    295 runAgainLink.style.display = 'inline-block';
    296 runAgainLink.style.fontSize = 'small';
    297 runAgainLink.style.marginBottom = '16px';
    298 runAgainLink.href = '';
    299 runAgainLink.onclick = goog.bind(function() {
    300 this.execute();
    301 return false;
    302 }, this);
    303 runAgainLink.innerHTML = 'Run again without reloading';
    304 this.logEl_.appendChild(runAgainLink);
    305};
    306
    307
    308/**
    309 * Writes a nicely formatted log out to the document.
    310 * @param {string} log The string to write.
    311 */
    312goog.testing.TestRunner.prototype.writeLog = function(log) {
    313 var lines = log.split('\n');
    314 for (var i = 0; i < lines.length; i++) {
    315 var line = lines[i];
    316 var color;
    317 var isFailOrError = /FAILED/.test(line) || /ERROR/.test(line);
    318 if (/PASSED/.test(line)) {
    319 color = 'darkgreen';
    320 } else if (isFailOrError) {
    321 color = 'darkred';
    322 } else {
    323 color = '#333';
    324 }
    325 var div = document.createElement('div');
    326 if (line.substr(0, 2) == '> ') {
    327 // The stack trace may contain links so it has to be interpreted as HTML.
    328 div.innerHTML = line;
    329 } else {
    330 div.appendChild(document.createTextNode(line));
    331 }
    332
    333 var testNameMatch =
    334 /(\S+) (\[[^\]]*] )?: (FAILED|ERROR|PASSED)/.exec(line);
    335 if (testNameMatch) {
    336 // Build a URL to run the test individually. If this test was already
    337 // part of another subset test, we need to overwrite the old runTests
    338 // query parameter. We also need to do this without bringing in any
    339 // extra dependencies, otherwise we could mask missing dependency bugs.
    340 var newSearch = 'runTests=' + testNameMatch[1];
    341 var search = window.location.search;
    342 if (search) {
    343 var oldTests = /runTests=([^&]*)/.exec(search);
    344 if (oldTests) {
    345 newSearch = search.substr(0, oldTests.index) +
    346 newSearch +
    347 search.substr(oldTests.index + oldTests[0].length);
    348 } else {
    349 newSearch = search + '&' + newSearch;
    350 }
    351 } else {
    352 newSearch = '?' + newSearch;
    353 }
    354 var href = window.location.href;
    355 var hash = window.location.hash;
    356 if (hash && hash.charAt(0) != '#') {
    357 hash = '#' + hash;
    358 }
    359 href = href.split('#')[0].split('?')[0] + newSearch + hash;
    360
    361 // Add the link.
    362 var a = document.createElement('A');
    363 a.innerHTML = '(run individually)';
    364 a.style.fontSize = '0.8em';
    365 a.style.color = '#888';
    366 a.href = href;
    367 div.appendChild(document.createTextNode(' '));
    368 div.appendChild(a);
    369 }
    370
    371 div.style.color = color;
    372 div.style.font = 'normal 100% monospace';
    373 div.style.wordWrap = 'break-word';
    374 if (i == 0) {
    375 // Highlight the first line as a header that indicates the test outcome.
    376 div.style.padding = '20px';
    377 div.style.marginBottom = '10px';
    378 if (isFailOrError) {
    379 div.style.border = '5px solid ' + color;
    380 div.style.backgroundColor = '#ffeeee';
    381 } else {
    382 div.style.border = '1px solid black';
    383 div.style.backgroundColor = '#eeffee';
    384 }
    385 }
    386
    387 try {
    388 div.style.whiteSpace = 'pre-wrap';
    389 } catch (e) {
    390 // NOTE(brenneman): IE raises an exception when assigning to pre-wrap.
    391 // Thankfully, it doesn't collapse whitespace when using monospace fonts,
    392 // so it will display correctly if we ignore the exception.
    393 }
    394
    395 if (i < 2) {
    396 div.style.fontWeight = 'bold';
    397 }
    398 this.logEl_.appendChild(div);
    399 }
    400};
    401
    402
    403/**
    404 * Logs a message to the current test case.
    405 * @param {string} s The text to output to the log.
    406 */
    407goog.testing.TestRunner.prototype.log = function(s) {
    408 if (this.testCase) {
    409 this.testCase.log(s);
    410 }
    411};
    412
    413
    414// TODO(nnaze): Properly handle serving test results when multiple test cases
    415// are run.
    416/**
    417 * @return {Object.<string, !Array.<string>>} A map of test names to a list of
    418 * test failures (if any) to provide formatted data for the test runner.
    419 */
    420goog.testing.TestRunner.prototype.getTestResults = function() {
    421 if (this.testCase) {
    422 return this.testCase.getTestResults();
    423 }
    424 return null;
    425};
    \ No newline at end of file +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 index af097a8..036b3a5 100644 --- a/docs/source/lib/goog/testing/watchers.js.src.html +++ b/docs/source/lib/goog/testing/watchers.js.src.html @@ -1 +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 +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 108deea..01cc1d6 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 * 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 * @constructor
    71 */
    72goog.Uri = function(opt_uri, opt_ignoreCase) {
    73 // Parse in the uri string
    74 var m;
    75 if (opt_uri instanceof goog.Uri) {
    76 this.ignoreCase_ = goog.isDef(opt_ignoreCase) ?
    77 opt_ignoreCase : opt_uri.getIgnoreCase();
    78 this.setScheme(opt_uri.getScheme());
    79 this.setUserInfo(opt_uri.getUserInfo());
    80 this.setDomain(opt_uri.getDomain());
    81 this.setPort(opt_uri.getPort());
    82 this.setPath(opt_uri.getPath());
    83 this.setQueryData(opt_uri.getQueryData().clone());
    84 this.setFragment(opt_uri.getFragment());
    85 } else if (opt_uri && (m = goog.uri.utils.split(String(opt_uri)))) {
    86 this.ignoreCase_ = !!opt_ignoreCase;
    87
    88 // Set the parts -- decoding as we do so.
    89 // COMPATABILITY NOTE - In IE, unmatched fields may be empty strings,
    90 // whereas in other browsers they will be undefined.
    91 this.setScheme(m[goog.uri.utils.ComponentIndex.SCHEME] || '', true);
    92 this.setUserInfo(m[goog.uri.utils.ComponentIndex.USER_INFO] || '', true);
    93 this.setDomain(m[goog.uri.utils.ComponentIndex.DOMAIN] || '', true);
    94 this.setPort(m[goog.uri.utils.ComponentIndex.PORT]);
    95 this.setPath(m[goog.uri.utils.ComponentIndex.PATH] || '', true);
    96 this.setQueryData(m[goog.uri.utils.ComponentIndex.QUERY_DATA] || '', true);
    97 this.setFragment(m[goog.uri.utils.ComponentIndex.FRAGMENT] || '', true);
    98
    99 } else {
    100 this.ignoreCase_ = !!opt_ignoreCase;
    101 this.queryData_ = new goog.Uri.QueryData(null, null, this.ignoreCase_);
    102 }
    103};
    104
    105
    106/**
    107 * If true, we preserve the type of query parameters set programmatically.
    108 *
    109 * This means that if you set a parameter to a boolean, and then call
    110 * getParameterValue, you will get a boolean back.
    111 *
    112 * If false, we will coerce parameters to strings, just as they would
    113 * appear in real URIs.
    114 *
    115 * TODO(nicksantos): Remove this once people have time to fix all tests.
    116 *
    117 * @type {boolean}
    118 */
    119goog.Uri.preserveParameterTypesCompatibilityFlag = false;
    120
    121
    122/**
    123 * Parameter name added to stop caching.
    124 * @type {string}
    125 */
    126goog.Uri.RANDOM_PARAM = goog.uri.utils.StandardQueryParam.RANDOM;
    127
    128
    129/**
    130 * Scheme such as "http".
    131 * @type {string}
    132 * @private
    133 */
    134goog.Uri.prototype.scheme_ = '';
    135
    136
    137/**
    138 * User credentials in the form "username:password".
    139 * @type {string}
    140 * @private
    141 */
    142goog.Uri.prototype.userInfo_ = '';
    143
    144
    145/**
    146 * Domain part, e.g. "www.google.com".
    147 * @type {string}
    148 * @private
    149 */
    150goog.Uri.prototype.domain_ = '';
    151
    152
    153/**
    154 * Port, e.g. 8080.
    155 * @type {?number}
    156 * @private
    157 */
    158goog.Uri.prototype.port_ = null;
    159
    160
    161/**
    162 * Path, e.g. "/tests/img.png".
    163 * @type {string}
    164 * @private
    165 */
    166goog.Uri.prototype.path_ = '';
    167
    168
    169/**
    170 * Object representing query data.
    171 * @type {!goog.Uri.QueryData}
    172 * @private
    173 */
    174goog.Uri.prototype.queryData_;
    175
    176
    177/**
    178 * The fragment without the #.
    179 * @type {string}
    180 * @private
    181 */
    182goog.Uri.prototype.fragment_ = '';
    183
    184
    185/**
    186 * Whether or not this Uri should be treated as Read Only.
    187 * @type {boolean}
    188 * @private
    189 */
    190goog.Uri.prototype.isReadOnly_ = false;
    191
    192
    193/**
    194 * Whether or not to ignore case when comparing query params.
    195 * @type {boolean}
    196 * @private
    197 */
    198goog.Uri.prototype.ignoreCase_ = false;
    199
    200
    201/**
    202 * @return {string} The string form of the url.
    203 * @override
    204 */
    205goog.Uri.prototype.toString = function() {
    206 var out = [];
    207
    208 var scheme = this.getScheme();
    209 if (scheme) {
    210 out.push(goog.Uri.encodeSpecialChars_(
    211 scheme, goog.Uri.reDisallowedInSchemeOrUserInfo_, true), ':');
    212 }
    213
    214 var domain = this.getDomain();
    215 if (domain) {
    216 out.push('//');
    217
    218 var userInfo = this.getUserInfo();
    219 if (userInfo) {
    220 out.push(goog.Uri.encodeSpecialChars_(
    221 userInfo, goog.Uri.reDisallowedInSchemeOrUserInfo_, true), '@');
    222 }
    223
    224 out.push(goog.Uri.removeDoubleEncoding_(goog.string.urlEncode(domain)));
    225
    226 var port = this.getPort();
    227 if (port != null) {
    228 out.push(':', String(port));
    229 }
    230 }
    231
    232 var path = this.getPath();
    233 if (path) {
    234 if (this.hasDomain() && path.charAt(0) != '/') {
    235 out.push('/');
    236 }
    237 out.push(goog.Uri.encodeSpecialChars_(
    238 path,
    239 path.charAt(0) == '/' ?
    240 goog.Uri.reDisallowedInAbsolutePath_ :
    241 goog.Uri.reDisallowedInRelativePath_,
    242 true));
    243 }
    244
    245 var query = this.getEncodedQuery();
    246 if (query) {
    247 out.push('?', query);
    248 }
    249
    250 var fragment = this.getFragment();
    251 if (fragment) {
    252 out.push('#', goog.Uri.encodeSpecialChars_(
    253 fragment, goog.Uri.reDisallowedInFragment_));
    254 }
    255 return out.join('');
    256};
    257
    258
    259/**
    260 * Resolves the given relative URI (a goog.Uri object), using the URI
    261 * represented by this instance as the base URI.
    262 *
    263 * There are several kinds of relative URIs:<br>
    264 * 1. foo - replaces the last part of the path, the whole query and fragment<br>
    265 * 2. /foo - replaces the the path, the query and fragment<br>
    266 * 3. //foo - replaces everything from the domain on. foo is a domain name<br>
    267 * 4. ?foo - replace the query and fragment<br>
    268 * 5. #foo - replace the fragment only
    269 *
    270 * Additionally, if relative URI has a non-empty path, all ".." and "."
    271 * segments will be resolved, as described in RFC 3986.
    272 *
    273 * @param {goog.Uri} relativeUri The relative URI to resolve.
    274 * @return {!goog.Uri} The resolved URI.
    275 */
    276goog.Uri.prototype.resolve = function(relativeUri) {
    277
    278 var absoluteUri = this.clone();
    279
    280 // we satisfy these conditions by looking for the first part of relativeUri
    281 // that is not blank and applying defaults to the rest
    282
    283 var overridden = relativeUri.hasScheme();
    284
    285 if (overridden) {
    286 absoluteUri.setScheme(relativeUri.getScheme());
    287 } else {
    288 overridden = relativeUri.hasUserInfo();
    289 }
    290
    291 if (overridden) {
    292 absoluteUri.setUserInfo(relativeUri.getUserInfo());
    293 } else {
    294 overridden = relativeUri.hasDomain();
    295 }
    296
    297 if (overridden) {
    298 absoluteUri.setDomain(relativeUri.getDomain());
    299 } else {
    300 overridden = relativeUri.hasPort();
    301 }
    302
    303 var path = relativeUri.getPath();
    304 if (overridden) {
    305 absoluteUri.setPort(relativeUri.getPort());
    306 } else {
    307 overridden = relativeUri.hasPath();
    308 if (overridden) {
    309 // resolve path properly
    310 if (path.charAt(0) != '/') {
    311 // path is relative
    312 if (this.hasDomain() && !this.hasPath()) {
    313 // RFC 3986, section 5.2.3, case 1
    314 path = '/' + path;
    315 } else {
    316 // RFC 3986, section 5.2.3, case 2
    317 var lastSlashIndex = absoluteUri.getPath().lastIndexOf('/');
    318 if (lastSlashIndex != -1) {
    319 path = absoluteUri.getPath().substr(0, lastSlashIndex + 1) + path;
    320 }
    321 }
    322 }
    323 path = goog.Uri.removeDotSegments(path);
    324 }
    325 }
    326
    327 if (overridden) {
    328 absoluteUri.setPath(path);
    329 } else {
    330 overridden = relativeUri.hasQuery();
    331 }
    332
    333 if (overridden) {
    334 absoluteUri.setQueryData(relativeUri.getDecodedQuery());
    335 } else {
    336 overridden = relativeUri.hasFragment();
    337 }
    338
    339 if (overridden) {
    340 absoluteUri.setFragment(relativeUri.getFragment());
    341 }
    342
    343 return absoluteUri;
    344};
    345
    346
    347/**
    348 * Clones the URI instance.
    349 * @return {!goog.Uri} New instance of the URI object.
    350 */
    351goog.Uri.prototype.clone = function() {
    352 return new goog.Uri(this);
    353};
    354
    355
    356/**
    357 * @return {string} The encoded scheme/protocol for the URI.
    358 */
    359goog.Uri.prototype.getScheme = function() {
    360 return this.scheme_;
    361};
    362
    363
    364/**
    365 * Sets the scheme/protocol.
    366 * @param {string} newScheme New scheme value.
    367 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    368 * @return {!goog.Uri} Reference to this URI object.
    369 */
    370goog.Uri.prototype.setScheme = function(newScheme, opt_decode) {
    371 this.enforceReadOnly();
    372 this.scheme_ = opt_decode ? goog.Uri.decodeOrEmpty_(newScheme, true) :
    373 newScheme;
    374
    375 // remove an : at the end of the scheme so somebody can pass in
    376 // window.location.protocol
    377 if (this.scheme_) {
    378 this.scheme_ = this.scheme_.replace(/:$/, '');
    379 }
    380 return this;
    381};
    382
    383
    384/**
    385 * @return {boolean} Whether the scheme has been set.
    386 */
    387goog.Uri.prototype.hasScheme = function() {
    388 return !!this.scheme_;
    389};
    390
    391
    392/**
    393 * @return {string} The decoded user info.
    394 */
    395goog.Uri.prototype.getUserInfo = function() {
    396 return this.userInfo_;
    397};
    398
    399
    400/**
    401 * Sets the userInfo.
    402 * @param {string} newUserInfo New userInfo value.
    403 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    404 * @return {!goog.Uri} Reference to this URI object.
    405 */
    406goog.Uri.prototype.setUserInfo = function(newUserInfo, opt_decode) {
    407 this.enforceReadOnly();
    408 this.userInfo_ = opt_decode ? goog.Uri.decodeOrEmpty_(newUserInfo) :
    409 newUserInfo;
    410 return this;
    411};
    412
    413
    414/**
    415 * @return {boolean} Whether the user info has been set.
    416 */
    417goog.Uri.prototype.hasUserInfo = function() {
    418 return !!this.userInfo_;
    419};
    420
    421
    422/**
    423 * @return {string} The decoded domain.
    424 */
    425goog.Uri.prototype.getDomain = function() {
    426 return this.domain_;
    427};
    428
    429
    430/**
    431 * Sets the domain.
    432 * @param {string} newDomain New domain value.
    433 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    434 * @return {!goog.Uri} Reference to this URI object.
    435 */
    436goog.Uri.prototype.setDomain = function(newDomain, opt_decode) {
    437 this.enforceReadOnly();
    438 this.domain_ = opt_decode ? goog.Uri.decodeOrEmpty_(newDomain, true) :
    439 newDomain;
    440 return this;
    441};
    442
    443
    444/**
    445 * @return {boolean} Whether the domain has been set.
    446 */
    447goog.Uri.prototype.hasDomain = function() {
    448 return !!this.domain_;
    449};
    450
    451
    452/**
    453 * @return {?number} The port number.
    454 */
    455goog.Uri.prototype.getPort = function() {
    456 return this.port_;
    457};
    458
    459
    460/**
    461 * Sets the port number.
    462 * @param {*} newPort Port number. Will be explicitly casted to a number.
    463 * @return {!goog.Uri} Reference to this URI object.
    464 */
    465goog.Uri.prototype.setPort = function(newPort) {
    466 this.enforceReadOnly();
    467
    468 if (newPort) {
    469 newPort = Number(newPort);
    470 if (isNaN(newPort) || newPort < 0) {
    471 throw Error('Bad port number ' + newPort);
    472 }
    473 this.port_ = newPort;
    474 } else {
    475 this.port_ = null;
    476 }
    477
    478 return this;
    479};
    480
    481
    482/**
    483 * @return {boolean} Whether the port has been set.
    484 */
    485goog.Uri.prototype.hasPort = function() {
    486 return this.port_ != null;
    487};
    488
    489
    490/**
    491 * @return {string} The decoded path.
    492 */
    493goog.Uri.prototype.getPath = function() {
    494 return this.path_;
    495};
    496
    497
    498/**
    499 * Sets the path.
    500 * @param {string} newPath New path value.
    501 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    502 * @return {!goog.Uri} Reference to this URI object.
    503 */
    504goog.Uri.prototype.setPath = function(newPath, opt_decode) {
    505 this.enforceReadOnly();
    506 this.path_ = opt_decode ? goog.Uri.decodeOrEmpty_(newPath, true) : newPath;
    507 return this;
    508};
    509
    510
    511/**
    512 * @return {boolean} Whether the path has been set.
    513 */
    514goog.Uri.prototype.hasPath = function() {
    515 return !!this.path_;
    516};
    517
    518
    519/**
    520 * @return {boolean} Whether the query string has been set.
    521 */
    522goog.Uri.prototype.hasQuery = function() {
    523 return this.queryData_.toString() !== '';
    524};
    525
    526
    527/**
    528 * Sets the query data.
    529 * @param {goog.Uri.QueryData|string|undefined} queryData QueryData object.
    530 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    531 * Applies only if queryData is a string.
    532 * @return {!goog.Uri} Reference to this URI object.
    533 */
    534goog.Uri.prototype.setQueryData = function(queryData, opt_decode) {
    535 this.enforceReadOnly();
    536
    537 if (queryData instanceof goog.Uri.QueryData) {
    538 this.queryData_ = queryData;
    539 this.queryData_.setIgnoreCase(this.ignoreCase_);
    540 } else {
    541 if (!opt_decode) {
    542 // QueryData accepts encoded query string, so encode it if
    543 // opt_decode flag is not true.
    544 queryData = goog.Uri.encodeSpecialChars_(queryData,
    545 goog.Uri.reDisallowedInQuery_);
    546 }
    547 this.queryData_ = new goog.Uri.QueryData(queryData, null, this.ignoreCase_);
    548 }
    549
    550 return this;
    551};
    552
    553
    554/**
    555 * Sets the URI query.
    556 * @param {string} newQuery New query value.
    557 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    558 * @return {!goog.Uri} Reference to this URI object.
    559 */
    560goog.Uri.prototype.setQuery = function(newQuery, opt_decode) {
    561 return this.setQueryData(newQuery, opt_decode);
    562};
    563
    564
    565/**
    566 * @return {string} The encoded URI query, not including the ?.
    567 */
    568goog.Uri.prototype.getEncodedQuery = function() {
    569 return this.queryData_.toString();
    570};
    571
    572
    573/**
    574 * @return {string} The decoded URI query, not including the ?.
    575 */
    576goog.Uri.prototype.getDecodedQuery = function() {
    577 return this.queryData_.toDecodedString();
    578};
    579
    580
    581/**
    582 * Returns the query data.
    583 * @return {!goog.Uri.QueryData} QueryData object.
    584 */
    585goog.Uri.prototype.getQueryData = function() {
    586 return this.queryData_;
    587};
    588
    589
    590/**
    591 * @return {string} The encoded URI query, not including the ?.
    592 *
    593 * Warning: This method, unlike other getter methods, returns encoded
    594 * value, instead of decoded one.
    595 */
    596goog.Uri.prototype.getQuery = function() {
    597 return this.getEncodedQuery();
    598};
    599
    600
    601/**
    602 * Sets the value of the named query parameters, clearing previous values for
    603 * that key.
    604 *
    605 * @param {string} key The parameter to set.
    606 * @param {*} value The new value.
    607 * @return {!goog.Uri} Reference to this URI object.
    608 */
    609goog.Uri.prototype.setParameterValue = function(key, value) {
    610 this.enforceReadOnly();
    611 this.queryData_.set(key, value);
    612 return this;
    613};
    614
    615
    616/**
    617 * Sets the values of the named query parameters, clearing previous values for
    618 * that key. Not new values will currently be moved to the end of the query
    619 * string.
    620 *
    621 * So, <code>goog.Uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new'])
    622 * </code> yields <tt>foo?a=b&e=f&c=new</tt>.</p>
    623 *
    624 * @param {string} key The parameter to set.
    625 * @param {*} values The new values. If values is a single
    626 * string then it will be treated as the sole value.
    627 * @return {!goog.Uri} Reference to this URI object.
    628 */
    629goog.Uri.prototype.setParameterValues = function(key, values) {
    630 this.enforceReadOnly();
    631
    632 if (!goog.isArray(values)) {
    633 values = [String(values)];
    634 }
    635
    636 // TODO(nicksantos): This cast shouldn't be necessary.
    637 this.queryData_.setValues(key, /** @type {Array} */ (values));
    638
    639 return this;
    640};
    641
    642
    643/**
    644 * Returns the value<b>s</b> for a given cgi parameter as a list of decoded
    645 * query parameter values.
    646 * @param {string} name The parameter to get values for.
    647 * @return {!Array} The values for a given cgi parameter as a list of
    648 * decoded query parameter values.
    649 */
    650goog.Uri.prototype.getParameterValues = function(name) {
    651 return this.queryData_.getValues(name);
    652};
    653
    654
    655/**
    656 * Returns the first value for a given cgi parameter or undefined if the given
    657 * parameter name does not appear in the query string.
    658 * @param {string} paramName Unescaped parameter name.
    659 * @return {string|undefined} The first value for a given cgi parameter or
    660 * undefined if the given parameter name does not appear in the query
    661 * string.
    662 */
    663goog.Uri.prototype.getParameterValue = function(paramName) {
    664 // NOTE(nicksantos): This type-cast is a lie when
    665 // preserveParameterTypesCompatibilityFlag is set to true.
    666 // But this should only be set to true in tests.
    667 return /** @type {string|undefined} */ (this.queryData_.get(paramName));
    668};
    669
    670
    671/**
    672 * @return {string} The URI fragment, not including the #.
    673 */
    674goog.Uri.prototype.getFragment = function() {
    675 return this.fragment_;
    676};
    677
    678
    679/**
    680 * Sets the URI fragment.
    681 * @param {string} newFragment New fragment value.
    682 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    683 * @return {!goog.Uri} Reference to this URI object.
    684 */
    685goog.Uri.prototype.setFragment = function(newFragment, opt_decode) {
    686 this.enforceReadOnly();
    687 this.fragment_ = opt_decode ? goog.Uri.decodeOrEmpty_(newFragment) :
    688 newFragment;
    689 return this;
    690};
    691
    692
    693/**
    694 * @return {boolean} Whether the URI has a fragment set.
    695 */
    696goog.Uri.prototype.hasFragment = function() {
    697 return !!this.fragment_;
    698};
    699
    700
    701/**
    702 * Returns true if this has the same domain as that of uri2.
    703 * @param {goog.Uri} uri2 The URI object to compare to.
    704 * @return {boolean} true if same domain; false otherwise.
    705 */
    706goog.Uri.prototype.hasSameDomainAs = function(uri2) {
    707 return ((!this.hasDomain() && !uri2.hasDomain()) ||
    708 this.getDomain() == uri2.getDomain()) &&
    709 ((!this.hasPort() && !uri2.hasPort()) ||
    710 this.getPort() == uri2.getPort());
    711};
    712
    713
    714/**
    715 * Adds a random parameter to the Uri.
    716 * @return {!goog.Uri} Reference to this Uri object.
    717 */
    718goog.Uri.prototype.makeUnique = function() {
    719 this.enforceReadOnly();
    720 this.setParameterValue(goog.Uri.RANDOM_PARAM, goog.string.getRandomString());
    721
    722 return this;
    723};
    724
    725
    726/**
    727 * Removes the named query parameter.
    728 *
    729 * @param {string} key The parameter to remove.
    730 * @return {!goog.Uri} Reference to this URI object.
    731 */
    732goog.Uri.prototype.removeParameter = function(key) {
    733 this.enforceReadOnly();
    734 this.queryData_.remove(key);
    735 return this;
    736};
    737
    738
    739/**
    740 * Sets whether Uri is read only. If this goog.Uri is read-only,
    741 * enforceReadOnly_ will be called at the start of any function that may modify
    742 * this Uri.
    743 * @param {boolean} isReadOnly whether this goog.Uri should be read only.
    744 * @return {!goog.Uri} Reference to this Uri object.
    745 */
    746goog.Uri.prototype.setReadOnly = function(isReadOnly) {
    747 this.isReadOnly_ = isReadOnly;
    748 return this;
    749};
    750
    751
    752/**
    753 * @return {boolean} Whether the URI is read only.
    754 */
    755goog.Uri.prototype.isReadOnly = function() {
    756 return this.isReadOnly_;
    757};
    758
    759
    760/**
    761 * Checks if this Uri has been marked as read only, and if so, throws an error.
    762 * This should be called whenever any modifying function is called.
    763 */
    764goog.Uri.prototype.enforceReadOnly = function() {
    765 if (this.isReadOnly_) {
    766 throw Error('Tried to modify a read-only Uri');
    767 }
    768};
    769
    770
    771/**
    772 * Sets whether to ignore case.
    773 * NOTE: If there are already key/value pairs in the QueryData, and
    774 * ignoreCase_ is set to false, the keys will all be lower-cased.
    775 * @param {boolean} ignoreCase whether this goog.Uri should ignore case.
    776 * @return {!goog.Uri} Reference to this Uri object.
    777 */
    778goog.Uri.prototype.setIgnoreCase = function(ignoreCase) {
    779 this.ignoreCase_ = ignoreCase;
    780 if (this.queryData_) {
    781 this.queryData_.setIgnoreCase(ignoreCase);
    782 }
    783 return this;
    784};
    785
    786
    787/**
    788 * @return {boolean} Whether to ignore case.
    789 */
    790goog.Uri.prototype.getIgnoreCase = function() {
    791 return this.ignoreCase_;
    792};
    793
    794
    795//==============================================================================
    796// Static members
    797//==============================================================================
    798
    799
    800/**
    801 * Creates a uri from the string form. Basically an alias of new goog.Uri().
    802 * If a Uri object is passed to parse then it will return a clone of the object.
    803 *
    804 * @param {*} uri Raw URI string or instance of Uri
    805 * object.
    806 * @param {boolean=} opt_ignoreCase Whether to ignore the case of parameter
    807 * names in #getParameterValue.
    808 * @return {!goog.Uri} The new URI object.
    809 */
    810goog.Uri.parse = function(uri, opt_ignoreCase) {
    811 return uri instanceof goog.Uri ?
    812 uri.clone() : new goog.Uri(uri, opt_ignoreCase);
    813};
    814
    815
    816/**
    817 * Creates a new goog.Uri object from unencoded parts.
    818 *
    819 * @param {?string=} opt_scheme Scheme/protocol or full URI to parse.
    820 * @param {?string=} opt_userInfo username:password.
    821 * @param {?string=} opt_domain www.google.com.
    822 * @param {?number=} opt_port 9830.
    823 * @param {?string=} opt_path /some/path/to/a/file.html.
    824 * @param {string|goog.Uri.QueryData=} opt_query a=1&b=2.
    825 * @param {?string=} opt_fragment The fragment without the #.
    826 * @param {boolean=} opt_ignoreCase Whether to ignore parameter name case in
    827 * #getParameterValue.
    828 *
    829 * @return {!goog.Uri} The new URI object.
    830 */
    831goog.Uri.create = function(opt_scheme, opt_userInfo, opt_domain, opt_port,
    832 opt_path, opt_query, opt_fragment, opt_ignoreCase) {
    833
    834 var uri = new goog.Uri(null, opt_ignoreCase);
    835
    836 // Only set the parts if they are defined and not empty strings.
    837 opt_scheme && uri.setScheme(opt_scheme);
    838 opt_userInfo && uri.setUserInfo(opt_userInfo);
    839 opt_domain && uri.setDomain(opt_domain);
    840 opt_port && uri.setPort(opt_port);
    841 opt_path && uri.setPath(opt_path);
    842 opt_query && uri.setQueryData(opt_query);
    843 opt_fragment && uri.setFragment(opt_fragment);
    844
    845 return uri;
    846};
    847
    848
    849/**
    850 * Resolves a relative Uri against a base Uri, accepting both strings and
    851 * Uri objects.
    852 *
    853 * @param {*} base Base Uri.
    854 * @param {*} rel Relative Uri.
    855 * @return {!goog.Uri} Resolved uri.
    856 */
    857goog.Uri.resolve = function(base, rel) {
    858 if (!(base instanceof goog.Uri)) {
    859 base = goog.Uri.parse(base);
    860 }
    861
    862 if (!(rel instanceof goog.Uri)) {
    863 rel = goog.Uri.parse(rel);
    864 }
    865
    866 return base.resolve(rel);
    867};
    868
    869
    870/**
    871 * Removes dot segments in given path component, as described in
    872 * RFC 3986, section 5.2.4.
    873 *
    874 * @param {string} path A non-empty path component.
    875 * @return {string} Path component with removed dot segments.
    876 */
    877goog.Uri.removeDotSegments = function(path) {
    878 if (path == '..' || path == '.') {
    879 return '';
    880
    881 } else if (!goog.string.contains(path, './') &&
    882 !goog.string.contains(path, '/.')) {
    883 // This optimization detects uris which do not contain dot-segments,
    884 // and as a consequence do not require any processing.
    885 return path;
    886
    887 } else {
    888 var leadingSlash = goog.string.startsWith(path, '/');
    889 var segments = path.split('/');
    890 var out = [];
    891
    892 for (var pos = 0; pos < segments.length; ) {
    893 var segment = segments[pos++];
    894
    895 if (segment == '.') {
    896 if (leadingSlash && pos == segments.length) {
    897 out.push('');
    898 }
    899 } else if (segment == '..') {
    900 if (out.length > 1 || out.length == 1 && out[0] != '') {
    901 out.pop();
    902 }
    903 if (leadingSlash && pos == segments.length) {
    904 out.push('');
    905 }
    906 } else {
    907 out.push(segment);
    908 leadingSlash = true;
    909 }
    910 }
    911
    912 return out.join('/');
    913 }
    914};
    915
    916
    917/**
    918 * Decodes a value or returns the empty string if it isn't defined or empty.
    919 * @param {string|undefined} val Value to decode.
    920 * @param {boolean=} opt_preserveReserved If true, restricted characters will
    921 * not be decoded.
    922 * @return {string} Decoded value.
    923 * @private
    924 */
    925goog.Uri.decodeOrEmpty_ = function(val, opt_preserveReserved) {
    926 // Don't use UrlDecode() here because val is not a query parameter.
    927 if (!val) {
    928 return '';
    929 }
    930
    931 return opt_preserveReserved ? decodeURI(val) : decodeURIComponent(val);
    932};
    933
    934
    935/**
    936 * If unescapedPart is non null, then escapes any characters in it that aren't
    937 * valid characters in a url and also escapes any special characters that
    938 * appear in extra.
    939 *
    940 * @param {*} unescapedPart The string to encode.
    941 * @param {RegExp} extra A character set of characters in [\01-\177].
    942 * @param {boolean=} opt_removeDoubleEncoding If true, remove double percent
    943 * encoding.
    944 * @return {?string} null iff unescapedPart == null.
    945 * @private
    946 */
    947goog.Uri.encodeSpecialChars_ = function(unescapedPart, extra,
    948 opt_removeDoubleEncoding) {
    949 if (goog.isString(unescapedPart)) {
    950 var encoded = encodeURI(unescapedPart).
    951 replace(extra, goog.Uri.encodeChar_);
    952 if (opt_removeDoubleEncoding) {
    953 // encodeURI double-escapes %XX sequences used to represent restricted
    954 // characters in some URI components, remove the double escaping here.
    955 encoded = goog.Uri.removeDoubleEncoding_(encoded);
    956 }
    957 return encoded;
    958 }
    959 return null;
    960};
    961
    962
    963/**
    964 * Converts a character in [\01-\177] to its unicode character equivalent.
    965 * @param {string} ch One character string.
    966 * @return {string} Encoded string.
    967 * @private
    968 */
    969goog.Uri.encodeChar_ = function(ch) {
    970 var n = ch.charCodeAt(0);
    971 return '%' + ((n >> 4) & 0xf).toString(16) + (n & 0xf).toString(16);
    972};
    973
    974
    975/**
    976 * Removes double percent-encoding from a string.
    977 * @param {string} doubleEncodedString String
    978 * @return {string} String with double encoding removed.
    979 * @private
    980 */
    981goog.Uri.removeDoubleEncoding_ = function(doubleEncodedString) {
    982 return doubleEncodedString.replace(/%25([0-9a-fA-F]{2})/g, '%$1');
    983};
    984
    985
    986/**
    987 * Regular expression for characters that are disallowed in the scheme or
    988 * userInfo part of the URI.
    989 * @type {RegExp}
    990 * @private
    991 */
    992goog.Uri.reDisallowedInSchemeOrUserInfo_ = /[#\/\?@]/g;
    993
    994
    995/**
    996 * Regular expression for characters that are disallowed in a relative path.
    997 * Colon is included due to RFC 3986 3.3.
    998 * @type {RegExp}
    999 * @private
    1000 */
    1001goog.Uri.reDisallowedInRelativePath_ = /[\#\?:]/g;
    1002
    1003
    1004/**
    1005 * Regular expression for characters that are disallowed in an absolute path.
    1006 * @type {RegExp}
    1007 * @private
    1008 */
    1009goog.Uri.reDisallowedInAbsolutePath_ = /[\#\?]/g;
    1010
    1011
    1012/**
    1013 * Regular expression for characters that are disallowed in the query.
    1014 * @type {RegExp}
    1015 * @private
    1016 */
    1017goog.Uri.reDisallowedInQuery_ = /[\#\?@]/g;
    1018
    1019
    1020/**
    1021 * Regular expression for characters that are disallowed in the fragment.
    1022 * @type {RegExp}
    1023 * @private
    1024 */
    1025goog.Uri.reDisallowedInFragment_ = /#/g;
    1026
    1027
    1028/**
    1029 * Checks whether two URIs have the same domain.
    1030 * @param {string} uri1String First URI string.
    1031 * @param {string} uri2String Second URI string.
    1032 * @return {boolean} true if the two URIs have the same domain; false otherwise.
    1033 */
    1034goog.Uri.haveSameDomain = function(uri1String, uri2String) {
    1035 // Differs from goog.uri.utils.haveSameDomain, since this ignores scheme.
    1036 // TODO(gboyer): Have this just call goog.uri.util.haveSameDomain.
    1037 var pieces1 = goog.uri.utils.split(uri1String);
    1038 var pieces2 = goog.uri.utils.split(uri2String);
    1039 return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==
    1040 pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&
    1041 pieces1[goog.uri.utils.ComponentIndex.PORT] ==
    1042 pieces2[goog.uri.utils.ComponentIndex.PORT];
    1043};
    1044
    1045
    1046
    1047/**
    1048 * Class used to represent URI query parameters. It is essentially a hash of
    1049 * name-value pairs, though a name can be present more than once.
    1050 *
    1051 * Has the same interface as the collections in goog.structs.
    1052 *
    1053 * @param {?string=} opt_query Optional encoded query string to parse into
    1054 * the object.
    1055 * @param {goog.Uri=} opt_uri Optional uri object that should have its
    1056 * cache invalidated when this object updates. Deprecated -- this
    1057 * is no longer required.
    1058 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
    1059 * name in #get.
    1060 * @constructor
    1061 * @final
    1062 */
    1063goog.Uri.QueryData = function(opt_query, opt_uri, opt_ignoreCase) {
    1064 /**
    1065 * Encoded query string, or null if it requires computing from the key map.
    1066 * @type {?string}
    1067 * @private
    1068 */
    1069 this.encodedQuery_ = opt_query || null;
    1070
    1071 /**
    1072 * If true, ignore the case of the parameter name in #get.
    1073 * @type {boolean}
    1074 * @private
    1075 */
    1076 this.ignoreCase_ = !!opt_ignoreCase;
    1077};
    1078
    1079
    1080/**
    1081 * If the underlying key map is not yet initialized, it parses the
    1082 * query string and fills the map with parsed data.
    1083 * @private
    1084 */
    1085goog.Uri.QueryData.prototype.ensureKeyMapInitialized_ = function() {
    1086 if (!this.keyMap_) {
    1087 this.keyMap_ = new goog.structs.Map();
    1088 this.count_ = 0;
    1089
    1090 if (this.encodedQuery_) {
    1091 var pairs = this.encodedQuery_.split('&');
    1092 for (var i = 0; i < pairs.length; i++) {
    1093 var indexOfEquals = pairs[i].indexOf('=');
    1094 var name = null;
    1095 var value = null;
    1096 if (indexOfEquals >= 0) {
    1097 name = pairs[i].substring(0, indexOfEquals);
    1098 value = pairs[i].substring(indexOfEquals + 1);
    1099 } else {
    1100 name = pairs[i];
    1101 }
    1102 name = goog.string.urlDecode(name);
    1103 name = this.getKeyName_(name);
    1104 this.add(name, value ? goog.string.urlDecode(value) : '');
    1105 }
    1106 }
    1107 }
    1108};
    1109
    1110
    1111/**
    1112 * Creates a new query data instance from a map of names and values.
    1113 *
    1114 * @param {!goog.structs.Map|!Object} map Map of string parameter
    1115 * names to parameter value. If parameter value is an array, it is
    1116 * treated as if the key maps to each individual value in the
    1117 * array.
    1118 * @param {goog.Uri=} opt_uri URI object that should have its cache
    1119 * invalidated when this object updates.
    1120 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
    1121 * name in #get.
    1122 * @return {!goog.Uri.QueryData} The populated query data instance.
    1123 */
    1124goog.Uri.QueryData.createFromMap = function(map, opt_uri, opt_ignoreCase) {
    1125 var keys = goog.structs.getKeys(map);
    1126 if (typeof keys == 'undefined') {
    1127 throw Error('Keys are undefined');
    1128 }
    1129
    1130 var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase);
    1131 var values = goog.structs.getValues(map);
    1132 for (var i = 0; i < keys.length; i++) {
    1133 var key = keys[i];
    1134 var value = values[i];
    1135 if (!goog.isArray(value)) {
    1136 queryData.add(key, value);
    1137 } else {
    1138 queryData.setValues(key, value);
    1139 }
    1140 }
    1141 return queryData;
    1142};
    1143
    1144
    1145/**
    1146 * Creates a new query data instance from parallel arrays of parameter names
    1147 * and values. Allows for duplicate parameter names. Throws an error if the
    1148 * lengths of the arrays differ.
    1149 *
    1150 * @param {Array.<string>} keys Parameter names.
    1151 * @param {Array} values Parameter values.
    1152 * @param {goog.Uri=} opt_uri URI object that should have its cache
    1153 * invalidated when this object updates.
    1154 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
    1155 * name in #get.
    1156 * @return {!goog.Uri.QueryData} The populated query data instance.
    1157 */
    1158goog.Uri.QueryData.createFromKeysValues = function(
    1159 keys, values, opt_uri, opt_ignoreCase) {
    1160 if (keys.length != values.length) {
    1161 throw Error('Mismatched lengths for keys/values');
    1162 }
    1163 var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase);
    1164 for (var i = 0; i < keys.length; i++) {
    1165 queryData.add(keys[i], values[i]);
    1166 }
    1167 return queryData;
    1168};
    1169
    1170
    1171/**
    1172 * The map containing name/value or name/array-of-values pairs.
    1173 * May be null if it requires parsing from the query string.
    1174 *
    1175 * We need to use a Map because we cannot guarantee that the key names will
    1176 * not be problematic for IE.
    1177 *
    1178 * @type {goog.structs.Map.<string, Array>}
    1179 * @private
    1180 */
    1181goog.Uri.QueryData.prototype.keyMap_ = null;
    1182
    1183
    1184/**
    1185 * The number of params, or null if it requires computing.
    1186 * @type {?number}
    1187 * @private
    1188 */
    1189goog.Uri.QueryData.prototype.count_ = null;
    1190
    1191
    1192/**
    1193 * @return {?number} The number of parameters.
    1194 */
    1195goog.Uri.QueryData.prototype.getCount = function() {
    1196 this.ensureKeyMapInitialized_();
    1197 return this.count_;
    1198};
    1199
    1200
    1201/**
    1202 * Adds a key value pair.
    1203 * @param {string} key Name.
    1204 * @param {*} value Value.
    1205 * @return {!goog.Uri.QueryData} Instance of this object.
    1206 */
    1207goog.Uri.QueryData.prototype.add = function(key, value) {
    1208 this.ensureKeyMapInitialized_();
    1209 this.invalidateCache_();
    1210
    1211 key = this.getKeyName_(key);
    1212 var values = this.keyMap_.get(key);
    1213 if (!values) {
    1214 this.keyMap_.set(key, (values = []));
    1215 }
    1216 values.push(value);
    1217 this.count_++;
    1218 return this;
    1219};
    1220
    1221
    1222/**
    1223 * Removes all the params with the given key.
    1224 * @param {string} key Name.
    1225 * @return {boolean} Whether any parameter was removed.
    1226 */
    1227goog.Uri.QueryData.prototype.remove = function(key) {
    1228 this.ensureKeyMapInitialized_();
    1229
    1230 key = this.getKeyName_(key);
    1231 if (this.keyMap_.containsKey(key)) {
    1232 this.invalidateCache_();
    1233
    1234 // Decrement parameter count.
    1235 this.count_ -= this.keyMap_.get(key).length;
    1236 return this.keyMap_.remove(key);
    1237 }
    1238 return false;
    1239};
    1240
    1241
    1242/**
    1243 * Clears the parameters.
    1244 */
    1245goog.Uri.QueryData.prototype.clear = function() {
    1246 this.invalidateCache_();
    1247 this.keyMap_ = null;
    1248 this.count_ = 0;
    1249};
    1250
    1251
    1252/**
    1253 * @return {boolean} Whether we have any parameters.
    1254 */
    1255goog.Uri.QueryData.prototype.isEmpty = function() {
    1256 this.ensureKeyMapInitialized_();
    1257 return this.count_ == 0;
    1258};
    1259
    1260
    1261/**
    1262 * Whether there is a parameter with the given name
    1263 * @param {string} key The parameter name to check for.
    1264 * @return {boolean} Whether there is a parameter with the given name.
    1265 */
    1266goog.Uri.QueryData.prototype.containsKey = function(key) {
    1267 this.ensureKeyMapInitialized_();
    1268 key = this.getKeyName_(key);
    1269 return this.keyMap_.containsKey(key);
    1270};
    1271
    1272
    1273/**
    1274 * Whether there is a parameter with the given value.
    1275 * @param {*} value The value to check for.
    1276 * @return {boolean} Whether there is a parameter with the given value.
    1277 */
    1278goog.Uri.QueryData.prototype.containsValue = function(value) {
    1279 // NOTE(arv): This solution goes through all the params even if it was the
    1280 // first param. We can get around this by not reusing code or by switching to
    1281 // iterators.
    1282 var vals = this.getValues();
    1283 return goog.array.contains(vals, value);
    1284};
    1285
    1286
    1287/**
    1288 * Returns all the keys of the parameters. If a key is used multiple times
    1289 * it will be included multiple times in the returned array
    1290 * @return {!Array.<string>} All the keys of the parameters.
    1291 */
    1292goog.Uri.QueryData.prototype.getKeys = function() {
    1293 this.ensureKeyMapInitialized_();
    1294 // We need to get the values to know how many keys to add.
    1295 var vals = /** @type {Array.<Array|*>} */ (this.keyMap_.getValues());
    1296 var keys = this.keyMap_.getKeys();
    1297 var rv = [];
    1298 for (var i = 0; i < keys.length; i++) {
    1299 var val = vals[i];
    1300 for (var j = 0; j < val.length; j++) {
    1301 rv.push(keys[i]);
    1302 }
    1303 }
    1304 return rv;
    1305};
    1306
    1307
    1308/**
    1309 * Returns all the values of the parameters with the given name. If the query
    1310 * data has no such key this will return an empty array. If no key is given
    1311 * all values wil be returned.
    1312 * @param {string=} opt_key The name of the parameter to get the values for.
    1313 * @return {!Array} All the values of the parameters with the given name.
    1314 */
    1315goog.Uri.QueryData.prototype.getValues = function(opt_key) {
    1316 this.ensureKeyMapInitialized_();
    1317 var rv = [];
    1318 if (goog.isString(opt_key)) {
    1319 if (this.containsKey(opt_key)) {
    1320 rv = goog.array.concat(rv, this.keyMap_.get(this.getKeyName_(opt_key)));
    1321 }
    1322 } else {
    1323 // Return all values.
    1324 var values = /** @type {Array.<Array|*>} */ (this.keyMap_.getValues());
    1325 for (var i = 0; i < values.length; i++) {
    1326 rv = goog.array.concat(rv, values[i]);
    1327 }
    1328 }
    1329 return rv;
    1330};
    1331
    1332
    1333/**
    1334 * Sets a key value pair and removes all other keys with the same value.
    1335 *
    1336 * @param {string} key Name.
    1337 * @param {*} value Value.
    1338 * @return {!goog.Uri.QueryData} Instance of this object.
    1339 */
    1340goog.Uri.QueryData.prototype.set = function(key, value) {
    1341 this.ensureKeyMapInitialized_();
    1342 this.invalidateCache_();
    1343
    1344 // TODO(user): This could be better written as
    1345 // this.remove(key), this.add(key, value), but that would reorder
    1346 // the key (since the key is first removed and then added at the
    1347 // end) and we would have to fix unit tests that depend on key
    1348 // ordering.
    1349 key = this.getKeyName_(key);
    1350 if (this.containsKey(key)) {
    1351 this.count_ -= this.keyMap_.get(key).length;
    1352 }
    1353 this.keyMap_.set(key, [value]);
    1354 this.count_++;
    1355 return this;
    1356};
    1357
    1358
    1359/**
    1360 * Returns the first value associated with the key. If the query data has no
    1361 * such key this will return undefined or the optional default.
    1362 * @param {string} key The name of the parameter to get the value for.
    1363 * @param {*=} opt_default The default value to return if the query data
    1364 * has no such key.
    1365 * @return {*} The first string value associated with the key, or opt_default
    1366 * if there's no value.
    1367 */
    1368goog.Uri.QueryData.prototype.get = function(key, opt_default) {
    1369 var values = key ? this.getValues(key) : [];
    1370 if (goog.Uri.preserveParameterTypesCompatibilityFlag) {
    1371 return values.length > 0 ? values[0] : opt_default;
    1372 } else {
    1373 return values.length > 0 ? String(values[0]) : opt_default;
    1374 }
    1375};
    1376
    1377
    1378/**
    1379 * Sets the values for a key. If the key already exists, this will
    1380 * override all of the existing values that correspond to the key.
    1381 * @param {string} key The key to set values for.
    1382 * @param {Array} values The values to set.
    1383 */
    1384goog.Uri.QueryData.prototype.setValues = function(key, values) {
    1385 this.remove(key);
    1386
    1387 if (values.length > 0) {
    1388 this.invalidateCache_();
    1389 this.keyMap_.set(this.getKeyName_(key), goog.array.clone(values));
    1390 this.count_ += values.length;
    1391 }
    1392};
    1393
    1394
    1395/**
    1396 * @return {string} Encoded query string.
    1397 * @override
    1398 */
    1399goog.Uri.QueryData.prototype.toString = function() {
    1400 if (this.encodedQuery_) {
    1401 return this.encodedQuery_;
    1402 }
    1403
    1404 if (!this.keyMap_) {
    1405 return '';
    1406 }
    1407
    1408 var sb = [];
    1409
    1410 // In the past, we use this.getKeys() and this.getVals(), but that
    1411 // generates a lot of allocations as compared to simply iterating
    1412 // over the keys.
    1413 var keys = this.keyMap_.getKeys();
    1414 for (var i = 0; i < keys.length; i++) {
    1415 var key = keys[i];
    1416 var encodedKey = goog.string.urlEncode(key);
    1417 var val = this.getValues(key);
    1418 for (var j = 0; j < val.length; j++) {
    1419 var param = encodedKey;
    1420 // Ensure that null and undefined are encoded into the url as
    1421 // literal strings.
    1422 if (val[j] !== '') {
    1423 param += '=' + goog.string.urlEncode(val[j]);
    1424 }
    1425 sb.push(param);
    1426 }
    1427 }
    1428
    1429 return this.encodedQuery_ = sb.join('&');
    1430};
    1431
    1432
    1433/**
    1434 * @return {string} Decoded query string.
    1435 */
    1436goog.Uri.QueryData.prototype.toDecodedString = function() {
    1437 return goog.Uri.decodeOrEmpty_(this.toString());
    1438};
    1439
    1440
    1441/**
    1442 * Invalidate the cache.
    1443 * @private
    1444 */
    1445goog.Uri.QueryData.prototype.invalidateCache_ = function() {
    1446 this.encodedQuery_ = null;
    1447};
    1448
    1449
    1450/**
    1451 * Removes all keys that are not in the provided list. (Modifies this object.)
    1452 * @param {Array.<string>} keys The desired keys.
    1453 * @return {!goog.Uri.QueryData} a reference to this object.
    1454 */
    1455goog.Uri.QueryData.prototype.filterKeys = function(keys) {
    1456 this.ensureKeyMapInitialized_();
    1457 this.keyMap_.forEach(
    1458 function(value, key) {
    1459 if (!goog.array.contains(keys, key)) {
    1460 this.remove(key);
    1461 }
    1462 }, this);
    1463 return this;
    1464};
    1465
    1466
    1467/**
    1468 * Clone the query data instance.
    1469 * @return {!goog.Uri.QueryData} New instance of the QueryData object.
    1470 */
    1471goog.Uri.QueryData.prototype.clone = function() {
    1472 var rv = new goog.Uri.QueryData();
    1473 rv.encodedQuery_ = this.encodedQuery_;
    1474 if (this.keyMap_) {
    1475 rv.keyMap_ = this.keyMap_.clone();
    1476 rv.count_ = this.count_;
    1477 }
    1478 return rv;
    1479};
    1480
    1481
    1482/**
    1483 * Helper function to get the key name from a JavaScript object. Converts
    1484 * the object to a string, and to lower case if necessary.
    1485 * @private
    1486 * @param {*} arg The object to get a key name from.
    1487 * @return {string} valid key name which can be looked up in #keyMap_.
    1488 */
    1489goog.Uri.QueryData.prototype.getKeyName_ = function(arg) {
    1490 var keyName = String(arg);
    1491 if (this.ignoreCase_) {
    1492 keyName = keyName.toLowerCase();
    1493 }
    1494 return keyName;
    1495};
    1496
    1497
    1498/**
    1499 * Ignore case in parameter names.
    1500 * NOTE: If there are already key/value pairs in the QueryData, and
    1501 * ignoreCase_ is set to false, the keys will all be lower-cased.
    1502 * @param {boolean} ignoreCase whether this goog.Uri should ignore case.
    1503 */
    1504goog.Uri.QueryData.prototype.setIgnoreCase = function(ignoreCase) {
    1505 var resetKeys = ignoreCase && !this.ignoreCase_;
    1506 if (resetKeys) {
    1507 this.ensureKeyMapInitialized_();
    1508 this.invalidateCache_();
    1509 this.keyMap_.forEach(
    1510 function(value, key) {
    1511 var lowerCase = key.toLowerCase();
    1512 if (key != lowerCase) {
    1513 this.remove(key);
    1514 this.setValues(lowerCase, value);
    1515 }
    1516 }, this);
    1517 }
    1518 this.ignoreCase_ = ignoreCase;
    1519};
    1520
    1521
    1522/**
    1523 * Extends a query data object with another query data or map like object. This
    1524 * operates 'in-place', it does not create a new QueryData object.
    1525 *
    1526 * @param {...(goog.Uri.QueryData|goog.structs.Map|Object)} var_args The object
    1527 * from which key value pairs will be copied.
    1528 */
    1529goog.Uri.QueryData.prototype.extend = function(var_args) {
    1530 for (var i = 0; i < arguments.length; i++) {
    1531 var data = arguments[i];
    1532 goog.structs.forEach(data,
    1533 /** @this {goog.Uri.QueryData} */
    1534 function(value, key) {
    1535 this.add(key, value);
    1536 }, this);
    1537 }
    1538};
    \ 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 * @constructor
    71 * @struct
    72 */
    73goog.Uri = function(opt_uri, opt_ignoreCase) {
    74 /**
    75 * Scheme such as "http".
    76 * @private {string}
    77 */
    78 this.scheme_ = '';
    79
    80 /**
    81 * User credentials in the form "username:password".
    82 * @private {string}
    83 */
    84 this.userInfo_ = '';
    85
    86 /**
    87 * Domain part, e.g. "www.google.com".
    88 * @private {string}
    89 */
    90 this.domain_ = '';
    91
    92 /**
    93 * Port, e.g. 8080.
    94 * @private {?number}
    95 */
    96 this.port_ = null;
    97
    98 /**
    99 * Path, e.g. "/tests/img.png".
    100 * @private {string}
    101 */
    102 this.path_ = '';
    103
    104 /**
    105 * The fragment without the #.
    106 * @private {string}
    107 */
    108 this.fragment_ = '';
    109
    110 /**
    111 * Whether or not this Uri should be treated as Read Only.
    112 * @private {boolean}
    113 */
    114 this.isReadOnly_ = false;
    115
    116 /**
    117 * Whether or not to ignore case when comparing query params.
    118 * @private {boolean}
    119 */
    120 this.ignoreCase_ = false;
    121
    122 /**
    123 * Object representing query data.
    124 * @private {!goog.Uri.QueryData}
    125 */
    126 this.queryData_;
    127
    128 // Parse in the uri string
    129 var m;
    130 if (opt_uri instanceof goog.Uri) {
    131 this.ignoreCase_ = goog.isDef(opt_ignoreCase) ?
    132 opt_ignoreCase : opt_uri.getIgnoreCase();
    133 this.setScheme(opt_uri.getScheme());
    134 this.setUserInfo(opt_uri.getUserInfo());
    135 this.setDomain(opt_uri.getDomain());
    136 this.setPort(opt_uri.getPort());
    137 this.setPath(opt_uri.getPath());
    138 this.setQueryData(opt_uri.getQueryData().clone());
    139 this.setFragment(opt_uri.getFragment());
    140 } else if (opt_uri && (m = goog.uri.utils.split(String(opt_uri)))) {
    141 this.ignoreCase_ = !!opt_ignoreCase;
    142
    143 // Set the parts -- decoding as we do so.
    144 // COMPATABILITY NOTE - In IE, unmatched fields may be empty strings,
    145 // whereas in other browsers they will be undefined.
    146 this.setScheme(m[goog.uri.utils.ComponentIndex.SCHEME] || '', true);
    147 this.setUserInfo(m[goog.uri.utils.ComponentIndex.USER_INFO] || '', true);
    148 this.setDomain(m[goog.uri.utils.ComponentIndex.DOMAIN] || '', true);
    149 this.setPort(m[goog.uri.utils.ComponentIndex.PORT]);
    150 this.setPath(m[goog.uri.utils.ComponentIndex.PATH] || '', true);
    151 this.setQueryData(m[goog.uri.utils.ComponentIndex.QUERY_DATA] || '', true);
    152 this.setFragment(m[goog.uri.utils.ComponentIndex.FRAGMENT] || '', true);
    153
    154 } else {
    155 this.ignoreCase_ = !!opt_ignoreCase;
    156 this.queryData_ = new goog.Uri.QueryData(null, null, this.ignoreCase_);
    157 }
    158};
    159
    160
    161/**
    162 * If true, we preserve the type of query parameters set programmatically.
    163 *
    164 * This means that if you set a parameter to a boolean, and then call
    165 * getParameterValue, you will get a boolean back.
    166 *
    167 * If false, we will coerce parameters to strings, just as they would
    168 * appear in real URIs.
    169 *
    170 * TODO(nicksantos): Remove this once people have time to fix all tests.
    171 *
    172 * @type {boolean}
    173 */
    174goog.Uri.preserveParameterTypesCompatibilityFlag = false;
    175
    176
    177/**
    178 * Parameter name added to stop caching.
    179 * @type {string}
    180 */
    181goog.Uri.RANDOM_PARAM = goog.uri.utils.StandardQueryParam.RANDOM;
    182
    183
    184/**
    185 * @return {string} The string form of the url.
    186 * @override
    187 */
    188goog.Uri.prototype.toString = function() {
    189 var out = [];
    190
    191 var scheme = this.getScheme();
    192 if (scheme) {
    193 out.push(goog.Uri.encodeSpecialChars_(
    194 scheme, goog.Uri.reDisallowedInSchemeOrUserInfo_, true), ':');
    195 }
    196
    197 var domain = this.getDomain();
    198 if (domain) {
    199 out.push('//');
    200
    201 var userInfo = this.getUserInfo();
    202 if (userInfo) {
    203 out.push(goog.Uri.encodeSpecialChars_(
    204 userInfo, goog.Uri.reDisallowedInSchemeOrUserInfo_, true), '@');
    205 }
    206
    207 out.push(goog.Uri.removeDoubleEncoding_(goog.string.urlEncode(domain)));
    208
    209 var port = this.getPort();
    210 if (port != null) {
    211 out.push(':', String(port));
    212 }
    213 }
    214
    215 var path = this.getPath();
    216 if (path) {
    217 if (this.hasDomain() && path.charAt(0) != '/') {
    218 out.push('/');
    219 }
    220 out.push(goog.Uri.encodeSpecialChars_(
    221 path,
    222 path.charAt(0) == '/' ?
    223 goog.Uri.reDisallowedInAbsolutePath_ :
    224 goog.Uri.reDisallowedInRelativePath_,
    225 true));
    226 }
    227
    228 var query = this.getEncodedQuery();
    229 if (query) {
    230 out.push('?', query);
    231 }
    232
    233 var fragment = this.getFragment();
    234 if (fragment) {
    235 out.push('#', goog.Uri.encodeSpecialChars_(
    236 fragment, goog.Uri.reDisallowedInFragment_));
    237 }
    238 return out.join('');
    239};
    240
    241
    242/**
    243 * Resolves the given relative URI (a goog.Uri object), using the URI
    244 * represented by this instance as the base URI.
    245 *
    246 * There are several kinds of relative URIs:<br>
    247 * 1. foo - replaces the last part of the path, the whole query and fragment<br>
    248 * 2. /foo - replaces the the path, the query and fragment<br>
    249 * 3. //foo - replaces everything from the domain on. foo is a domain name<br>
    250 * 4. ?foo - replace the query and fragment<br>
    251 * 5. #foo - replace the fragment only
    252 *
    253 * Additionally, if relative URI has a non-empty path, all ".." and "."
    254 * segments will be resolved, as described in RFC 3986.
    255 *
    256 * @param {!goog.Uri} relativeUri The relative URI to resolve.
    257 * @return {!goog.Uri} The resolved URI.
    258 */
    259goog.Uri.prototype.resolve = function(relativeUri) {
    260
    261 var absoluteUri = this.clone();
    262
    263 // we satisfy these conditions by looking for the first part of relativeUri
    264 // that is not blank and applying defaults to the rest
    265
    266 var overridden = relativeUri.hasScheme();
    267
    268 if (overridden) {
    269 absoluteUri.setScheme(relativeUri.getScheme());
    270 } else {
    271 overridden = relativeUri.hasUserInfo();
    272 }
    273
    274 if (overridden) {
    275 absoluteUri.setUserInfo(relativeUri.getUserInfo());
    276 } else {
    277 overridden = relativeUri.hasDomain();
    278 }
    279
    280 if (overridden) {
    281 absoluteUri.setDomain(relativeUri.getDomain());
    282 } else {
    283 overridden = relativeUri.hasPort();
    284 }
    285
    286 var path = relativeUri.getPath();
    287 if (overridden) {
    288 absoluteUri.setPort(relativeUri.getPort());
    289 } else {
    290 overridden = relativeUri.hasPath();
    291 if (overridden) {
    292 // resolve path properly
    293 if (path.charAt(0) != '/') {
    294 // path is relative
    295 if (this.hasDomain() && !this.hasPath()) {
    296 // RFC 3986, section 5.2.3, case 1
    297 path = '/' + path;
    298 } else {
    299 // RFC 3986, section 5.2.3, case 2
    300 var lastSlashIndex = absoluteUri.getPath().lastIndexOf('/');
    301 if (lastSlashIndex != -1) {
    302 path = absoluteUri.getPath().substr(0, lastSlashIndex + 1) + path;
    303 }
    304 }
    305 }
    306 path = goog.Uri.removeDotSegments(path);
    307 }
    308 }
    309
    310 if (overridden) {
    311 absoluteUri.setPath(path);
    312 } else {
    313 overridden = relativeUri.hasQuery();
    314 }
    315
    316 if (overridden) {
    317 absoluteUri.setQueryData(relativeUri.getDecodedQuery());
    318 } else {
    319 overridden = relativeUri.hasFragment();
    320 }
    321
    322 if (overridden) {
    323 absoluteUri.setFragment(relativeUri.getFragment());
    324 }
    325
    326 return absoluteUri;
    327};
    328
    329
    330/**
    331 * Clones the URI instance.
    332 * @return {!goog.Uri} New instance of the URI object.
    333 */
    334goog.Uri.prototype.clone = function() {
    335 return new goog.Uri(this);
    336};
    337
    338
    339/**
    340 * @return {string} The encoded scheme/protocol for the URI.
    341 */
    342goog.Uri.prototype.getScheme = function() {
    343 return this.scheme_;
    344};
    345
    346
    347/**
    348 * Sets the scheme/protocol.
    349 * @param {string} newScheme New scheme value.
    350 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    351 * @return {!goog.Uri} Reference to this URI object.
    352 */
    353goog.Uri.prototype.setScheme = function(newScheme, opt_decode) {
    354 this.enforceReadOnly();
    355 this.scheme_ = opt_decode ? goog.Uri.decodeOrEmpty_(newScheme, true) :
    356 newScheme;
    357
    358 // remove an : at the end of the scheme so somebody can pass in
    359 // window.location.protocol
    360 if (this.scheme_) {
    361 this.scheme_ = this.scheme_.replace(/:$/, '');
    362 }
    363 return this;
    364};
    365
    366
    367/**
    368 * @return {boolean} Whether the scheme has been set.
    369 */
    370goog.Uri.prototype.hasScheme = function() {
    371 return !!this.scheme_;
    372};
    373
    374
    375/**
    376 * @return {string} The decoded user info.
    377 */
    378goog.Uri.prototype.getUserInfo = function() {
    379 return this.userInfo_;
    380};
    381
    382
    383/**
    384 * Sets the userInfo.
    385 * @param {string} newUserInfo New userInfo value.
    386 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    387 * @return {!goog.Uri} Reference to this URI object.
    388 */
    389goog.Uri.prototype.setUserInfo = function(newUserInfo, opt_decode) {
    390 this.enforceReadOnly();
    391 this.userInfo_ = opt_decode ? goog.Uri.decodeOrEmpty_(newUserInfo) :
    392 newUserInfo;
    393 return this;
    394};
    395
    396
    397/**
    398 * @return {boolean} Whether the user info has been set.
    399 */
    400goog.Uri.prototype.hasUserInfo = function() {
    401 return !!this.userInfo_;
    402};
    403
    404
    405/**
    406 * @return {string} The decoded domain.
    407 */
    408goog.Uri.prototype.getDomain = function() {
    409 return this.domain_;
    410};
    411
    412
    413/**
    414 * Sets the domain.
    415 * @param {string} newDomain New domain value.
    416 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    417 * @return {!goog.Uri} Reference to this URI object.
    418 */
    419goog.Uri.prototype.setDomain = function(newDomain, opt_decode) {
    420 this.enforceReadOnly();
    421 this.domain_ = opt_decode ? goog.Uri.decodeOrEmpty_(newDomain, true) :
    422 newDomain;
    423 return this;
    424};
    425
    426
    427/**
    428 * @return {boolean} Whether the domain has been set.
    429 */
    430goog.Uri.prototype.hasDomain = function() {
    431 return !!this.domain_;
    432};
    433
    434
    435/**
    436 * @return {?number} The port number.
    437 */
    438goog.Uri.prototype.getPort = function() {
    439 return this.port_;
    440};
    441
    442
    443/**
    444 * Sets the port number.
    445 * @param {*} newPort Port number. Will be explicitly casted to a number.
    446 * @return {!goog.Uri} Reference to this URI object.
    447 */
    448goog.Uri.prototype.setPort = function(newPort) {
    449 this.enforceReadOnly();
    450
    451 if (newPort) {
    452 newPort = Number(newPort);
    453 if (isNaN(newPort) || newPort < 0) {
    454 throw Error('Bad port number ' + newPort);
    455 }
    456 this.port_ = newPort;
    457 } else {
    458 this.port_ = null;
    459 }
    460
    461 return this;
    462};
    463
    464
    465/**
    466 * @return {boolean} Whether the port has been set.
    467 */
    468goog.Uri.prototype.hasPort = function() {
    469 return this.port_ != null;
    470};
    471
    472
    473/**
    474 * @return {string} The decoded path.
    475 */
    476goog.Uri.prototype.getPath = function() {
    477 return this.path_;
    478};
    479
    480
    481/**
    482 * Sets the path.
    483 * @param {string} newPath New path value.
    484 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    485 * @return {!goog.Uri} Reference to this URI object.
    486 */
    487goog.Uri.prototype.setPath = function(newPath, opt_decode) {
    488 this.enforceReadOnly();
    489 this.path_ = opt_decode ? goog.Uri.decodeOrEmpty_(newPath, true) : newPath;
    490 return this;
    491};
    492
    493
    494/**
    495 * @return {boolean} Whether the path has been set.
    496 */
    497goog.Uri.prototype.hasPath = function() {
    498 return !!this.path_;
    499};
    500
    501
    502/**
    503 * @return {boolean} Whether the query string has been set.
    504 */
    505goog.Uri.prototype.hasQuery = function() {
    506 return this.queryData_.toString() !== '';
    507};
    508
    509
    510/**
    511 * Sets the query data.
    512 * @param {goog.Uri.QueryData|string|undefined} queryData QueryData object.
    513 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    514 * Applies only if queryData is a string.
    515 * @return {!goog.Uri} Reference to this URI object.
    516 */
    517goog.Uri.prototype.setQueryData = function(queryData, opt_decode) {
    518 this.enforceReadOnly();
    519
    520 if (queryData instanceof goog.Uri.QueryData) {
    521 this.queryData_ = queryData;
    522 this.queryData_.setIgnoreCase(this.ignoreCase_);
    523 } else {
    524 if (!opt_decode) {
    525 // QueryData accepts encoded query string, so encode it if
    526 // opt_decode flag is not true.
    527 queryData = goog.Uri.encodeSpecialChars_(queryData,
    528 goog.Uri.reDisallowedInQuery_);
    529 }
    530 this.queryData_ = new goog.Uri.QueryData(queryData, null, this.ignoreCase_);
    531 }
    532
    533 return this;
    534};
    535
    536
    537/**
    538 * Sets the URI query.
    539 * @param {string} newQuery New query value.
    540 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    541 * @return {!goog.Uri} Reference to this URI object.
    542 */
    543goog.Uri.prototype.setQuery = function(newQuery, opt_decode) {
    544 return this.setQueryData(newQuery, opt_decode);
    545};
    546
    547
    548/**
    549 * @return {string} The encoded URI query, not including the ?.
    550 */
    551goog.Uri.prototype.getEncodedQuery = function() {
    552 return this.queryData_.toString();
    553};
    554
    555
    556/**
    557 * @return {string} The decoded URI query, not including the ?.
    558 */
    559goog.Uri.prototype.getDecodedQuery = function() {
    560 return this.queryData_.toDecodedString();
    561};
    562
    563
    564/**
    565 * Returns the query data.
    566 * @return {!goog.Uri.QueryData} QueryData object.
    567 */
    568goog.Uri.prototype.getQueryData = function() {
    569 return this.queryData_;
    570};
    571
    572
    573/**
    574 * @return {string} The encoded URI query, not including the ?.
    575 *
    576 * Warning: This method, unlike other getter methods, returns encoded
    577 * value, instead of decoded one.
    578 */
    579goog.Uri.prototype.getQuery = function() {
    580 return this.getEncodedQuery();
    581};
    582
    583
    584/**
    585 * Sets the value of the named query parameters, clearing previous values for
    586 * that key.
    587 *
    588 * @param {string} key The parameter to set.
    589 * @param {*} value The new value.
    590 * @return {!goog.Uri} Reference to this URI object.
    591 */
    592goog.Uri.prototype.setParameterValue = function(key, value) {
    593 this.enforceReadOnly();
    594 this.queryData_.set(key, value);
    595 return this;
    596};
    597
    598
    599/**
    600 * Sets the values of the named query parameters, clearing previous values for
    601 * that key. Not new values will currently be moved to the end of the query
    602 * string.
    603 *
    604 * So, <code>goog.Uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new'])
    605 * </code> yields <tt>foo?a=b&e=f&c=new</tt>.</p>
    606 *
    607 * @param {string} key The parameter to set.
    608 * @param {*} values The new values. If values is a single
    609 * string then it will be treated as the sole value.
    610 * @return {!goog.Uri} Reference to this URI object.
    611 */
    612goog.Uri.prototype.setParameterValues = function(key, values) {
    613 this.enforceReadOnly();
    614
    615 if (!goog.isArray(values)) {
    616 values = [String(values)];
    617 }
    618
    619 this.queryData_.setValues(key, values);
    620
    621 return this;
    622};
    623
    624
    625/**
    626 * Returns the value<b>s</b> for a given cgi parameter as a list of decoded
    627 * query parameter values.
    628 * @param {string} name The parameter to get values for.
    629 * @return {!Array<?>} The values for a given cgi parameter as a list of
    630 * decoded query parameter values.
    631 */
    632goog.Uri.prototype.getParameterValues = function(name) {
    633 return this.queryData_.getValues(name);
    634};
    635
    636
    637/**
    638 * Returns the first value for a given cgi parameter or undefined if the given
    639 * parameter name does not appear in the query string.
    640 * @param {string} paramName Unescaped parameter name.
    641 * @return {string|undefined} The first value for a given cgi parameter or
    642 * undefined if the given parameter name does not appear in the query
    643 * string.
    644 */
    645goog.Uri.prototype.getParameterValue = function(paramName) {
    646 // NOTE(nicksantos): This type-cast is a lie when
    647 // preserveParameterTypesCompatibilityFlag is set to true.
    648 // But this should only be set to true in tests.
    649 return /** @type {string|undefined} */ (this.queryData_.get(paramName));
    650};
    651
    652
    653/**
    654 * @return {string} The URI fragment, not including the #.
    655 */
    656goog.Uri.prototype.getFragment = function() {
    657 return this.fragment_;
    658};
    659
    660
    661/**
    662 * Sets the URI fragment.
    663 * @param {string} newFragment New fragment value.
    664 * @param {boolean=} opt_decode Optional param for whether to decode new value.
    665 * @return {!goog.Uri} Reference to this URI object.
    666 */
    667goog.Uri.prototype.setFragment = function(newFragment, opt_decode) {
    668 this.enforceReadOnly();
    669 this.fragment_ = opt_decode ? goog.Uri.decodeOrEmpty_(newFragment) :
    670 newFragment;
    671 return this;
    672};
    673
    674
    675/**
    676 * @return {boolean} Whether the URI has a fragment set.
    677 */
    678goog.Uri.prototype.hasFragment = function() {
    679 return !!this.fragment_;
    680};
    681
    682
    683/**
    684 * Returns true if this has the same domain as that of uri2.
    685 * @param {!goog.Uri} uri2 The URI object to compare to.
    686 * @return {boolean} true if same domain; false otherwise.
    687 */
    688goog.Uri.prototype.hasSameDomainAs = function(uri2) {
    689 return ((!this.hasDomain() && !uri2.hasDomain()) ||
    690 this.getDomain() == uri2.getDomain()) &&
    691 ((!this.hasPort() && !uri2.hasPort()) ||
    692 this.getPort() == uri2.getPort());
    693};
    694
    695
    696/**
    697 * Adds a random parameter to the Uri.
    698 * @return {!goog.Uri} Reference to this Uri object.
    699 */
    700goog.Uri.prototype.makeUnique = function() {
    701 this.enforceReadOnly();
    702 this.setParameterValue(goog.Uri.RANDOM_PARAM, goog.string.getRandomString());
    703
    704 return this;
    705};
    706
    707
    708/**
    709 * Removes the named query parameter.
    710 *
    711 * @param {string} key The parameter to remove.
    712 * @return {!goog.Uri} Reference to this URI object.
    713 */
    714goog.Uri.prototype.removeParameter = function(key) {
    715 this.enforceReadOnly();
    716 this.queryData_.remove(key);
    717 return this;
    718};
    719
    720
    721/**
    722 * Sets whether Uri is read only. If this goog.Uri is read-only,
    723 * enforceReadOnly_ will be called at the start of any function that may modify
    724 * this Uri.
    725 * @param {boolean} isReadOnly whether this goog.Uri should be read only.
    726 * @return {!goog.Uri} Reference to this Uri object.
    727 */
    728goog.Uri.prototype.setReadOnly = function(isReadOnly) {
    729 this.isReadOnly_ = isReadOnly;
    730 return this;
    731};
    732
    733
    734/**
    735 * @return {boolean} Whether the URI is read only.
    736 */
    737goog.Uri.prototype.isReadOnly = function() {
    738 return this.isReadOnly_;
    739};
    740
    741
    742/**
    743 * Checks if this Uri has been marked as read only, and if so, throws an error.
    744 * This should be called whenever any modifying function is called.
    745 */
    746goog.Uri.prototype.enforceReadOnly = function() {
    747 if (this.isReadOnly_) {
    748 throw Error('Tried to modify a read-only Uri');
    749 }
    750};
    751
    752
    753/**
    754 * Sets whether to ignore case.
    755 * NOTE: If there are already key/value pairs in the QueryData, and
    756 * ignoreCase_ is set to false, the keys will all be lower-cased.
    757 * @param {boolean} ignoreCase whether this goog.Uri should ignore case.
    758 * @return {!goog.Uri} Reference to this Uri object.
    759 */
    760goog.Uri.prototype.setIgnoreCase = function(ignoreCase) {
    761 this.ignoreCase_ = ignoreCase;
    762 if (this.queryData_) {
    763 this.queryData_.setIgnoreCase(ignoreCase);
    764 }
    765 return this;
    766};
    767
    768
    769/**
    770 * @return {boolean} Whether to ignore case.
    771 */
    772goog.Uri.prototype.getIgnoreCase = function() {
    773 return this.ignoreCase_;
    774};
    775
    776
    777//==============================================================================
    778// Static members
    779//==============================================================================
    780
    781
    782/**
    783 * Creates a uri from the string form. Basically an alias of new goog.Uri().
    784 * If a Uri object is passed to parse then it will return a clone of the object.
    785 *
    786 * @param {*} uri Raw URI string or instance of Uri
    787 * object.
    788 * @param {boolean=} opt_ignoreCase Whether to ignore the case of parameter
    789 * names in #getParameterValue.
    790 * @return {!goog.Uri} The new URI object.
    791 */
    792goog.Uri.parse = function(uri, opt_ignoreCase) {
    793 return uri instanceof goog.Uri ?
    794 uri.clone() : new goog.Uri(uri, opt_ignoreCase);
    795};
    796
    797
    798/**
    799 * Creates a new goog.Uri object from unencoded parts.
    800 *
    801 * @param {?string=} opt_scheme Scheme/protocol or full URI to parse.
    802 * @param {?string=} opt_userInfo username:password.
    803 * @param {?string=} opt_domain www.google.com.
    804 * @param {?number=} opt_port 9830.
    805 * @param {?string=} opt_path /some/path/to/a/file.html.
    806 * @param {string|goog.Uri.QueryData=} opt_query a=1&b=2.
    807 * @param {?string=} opt_fragment The fragment without the #.
    808 * @param {boolean=} opt_ignoreCase Whether to ignore parameter name case in
    809 * #getParameterValue.
    810 *
    811 * @return {!goog.Uri} The new URI object.
    812 */
    813goog.Uri.create = function(opt_scheme, opt_userInfo, opt_domain, opt_port,
    814 opt_path, opt_query, opt_fragment, opt_ignoreCase) {
    815
    816 var uri = new goog.Uri(null, opt_ignoreCase);
    817
    818 // Only set the parts if they are defined and not empty strings.
    819 opt_scheme && uri.setScheme(opt_scheme);
    820 opt_userInfo && uri.setUserInfo(opt_userInfo);
    821 opt_domain && uri.setDomain(opt_domain);
    822 opt_port && uri.setPort(opt_port);
    823 opt_path && uri.setPath(opt_path);
    824 opt_query && uri.setQueryData(opt_query);
    825 opt_fragment && uri.setFragment(opt_fragment);
    826
    827 return uri;
    828};
    829
    830
    831/**
    832 * Resolves a relative Uri against a base Uri, accepting both strings and
    833 * Uri objects.
    834 *
    835 * @param {*} base Base Uri.
    836 * @param {*} rel Relative Uri.
    837 * @return {!goog.Uri} Resolved uri.
    838 */
    839goog.Uri.resolve = function(base, rel) {
    840 if (!(base instanceof goog.Uri)) {
    841 base = goog.Uri.parse(base);
    842 }
    843
    844 if (!(rel instanceof goog.Uri)) {
    845 rel = goog.Uri.parse(rel);
    846 }
    847
    848 return base.resolve(rel);
    849};
    850
    851
    852/**
    853 * Removes dot segments in given path component, as described in
    854 * RFC 3986, section 5.2.4.
    855 *
    856 * @param {string} path A non-empty path component.
    857 * @return {string} Path component with removed dot segments.
    858 */
    859goog.Uri.removeDotSegments = function(path) {
    860 if (path == '..' || path == '.') {
    861 return '';
    862
    863 } else if (!goog.string.contains(path, './') &&
    864 !goog.string.contains(path, '/.')) {
    865 // This optimization detects uris which do not contain dot-segments,
    866 // and as a consequence do not require any processing.
    867 return path;
    868
    869 } else {
    870 var leadingSlash = goog.string.startsWith(path, '/');
    871 var segments = path.split('/');
    872 var out = [];
    873
    874 for (var pos = 0; pos < segments.length; ) {
    875 var segment = segments[pos++];
    876
    877 if (segment == '.') {
    878 if (leadingSlash && pos == segments.length) {
    879 out.push('');
    880 }
    881 } else if (segment == '..') {
    882 if (out.length > 1 || out.length == 1 && out[0] != '') {
    883 out.pop();
    884 }
    885 if (leadingSlash && pos == segments.length) {
    886 out.push('');
    887 }
    888 } else {
    889 out.push(segment);
    890 leadingSlash = true;
    891 }
    892 }
    893
    894 return out.join('/');
    895 }
    896};
    897
    898
    899/**
    900 * Decodes a value or returns the empty string if it isn't defined or empty.
    901 * @param {string|undefined} val Value to decode.
    902 * @param {boolean=} opt_preserveReserved If true, restricted characters will
    903 * not be decoded.
    904 * @return {string} Decoded value.
    905 * @private
    906 */
    907goog.Uri.decodeOrEmpty_ = function(val, opt_preserveReserved) {
    908 // Don't use UrlDecode() here because val is not a query parameter.
    909 if (!val) {
    910 return '';
    911 }
    912
    913 // decodeURI has the same output for '%2f' and '%252f'. We double encode %25
    914 // so that we can distinguish between the 2 inputs. This is later undone by
    915 // removeDoubleEncoding_.
    916 return opt_preserveReserved ?
    917 decodeURI(val.replace(/%25/g, '%2525')) : decodeURIComponent(val);
    918};
    919
    920
    921/**
    922 * If unescapedPart is non null, then escapes any characters in it that aren't
    923 * valid characters in a url and also escapes any special characters that
    924 * appear in extra.
    925 *
    926 * @param {*} unescapedPart The string to encode.
    927 * @param {RegExp} extra A character set of characters in [\01-\177].
    928 * @param {boolean=} opt_removeDoubleEncoding If true, remove double percent
    929 * encoding.
    930 * @return {?string} null iff unescapedPart == null.
    931 * @private
    932 */
    933goog.Uri.encodeSpecialChars_ = function(unescapedPart, extra,
    934 opt_removeDoubleEncoding) {
    935 if (goog.isString(unescapedPart)) {
    936 var encoded = encodeURI(unescapedPart).
    937 replace(extra, goog.Uri.encodeChar_);
    938 if (opt_removeDoubleEncoding) {
    939 // encodeURI double-escapes %XX sequences used to represent restricted
    940 // characters in some URI components, remove the double escaping here.
    941 encoded = goog.Uri.removeDoubleEncoding_(encoded);
    942 }
    943 return encoded;
    944 }
    945 return null;
    946};
    947
    948
    949/**
    950 * Converts a character in [\01-\177] to its unicode character equivalent.
    951 * @param {string} ch One character string.
    952 * @return {string} Encoded string.
    953 * @private
    954 */
    955goog.Uri.encodeChar_ = function(ch) {
    956 var n = ch.charCodeAt(0);
    957 return '%' + ((n >> 4) & 0xf).toString(16) + (n & 0xf).toString(16);
    958};
    959
    960
    961/**
    962 * Removes double percent-encoding from a string.
    963 * @param {string} doubleEncodedString String
    964 * @return {string} String with double encoding removed.
    965 * @private
    966 */
    967goog.Uri.removeDoubleEncoding_ = function(doubleEncodedString) {
    968 return doubleEncodedString.replace(/%25([0-9a-fA-F]{2})/g, '%$1');
    969};
    970
    971
    972/**
    973 * Regular expression for characters that are disallowed in the scheme or
    974 * userInfo part of the URI.
    975 * @type {RegExp}
    976 * @private
    977 */
    978goog.Uri.reDisallowedInSchemeOrUserInfo_ = /[#\/\?@]/g;
    979
    980
    981/**
    982 * Regular expression for characters that are disallowed in a relative path.
    983 * Colon is included due to RFC 3986 3.3.
    984 * @type {RegExp}
    985 * @private
    986 */
    987goog.Uri.reDisallowedInRelativePath_ = /[\#\?:]/g;
    988
    989
    990/**
    991 * Regular expression for characters that are disallowed in an absolute path.
    992 * @type {RegExp}
    993 * @private
    994 */
    995goog.Uri.reDisallowedInAbsolutePath_ = /[\#\?]/g;
    996
    997
    998/**
    999 * Regular expression for characters that are disallowed in the query.
    1000 * @type {RegExp}
    1001 * @private
    1002 */
    1003goog.Uri.reDisallowedInQuery_ = /[\#\?@]/g;
    1004
    1005
    1006/**
    1007 * Regular expression for characters that are disallowed in the fragment.
    1008 * @type {RegExp}
    1009 * @private
    1010 */
    1011goog.Uri.reDisallowedInFragment_ = /#/g;
    1012
    1013
    1014/**
    1015 * Checks whether two URIs have the same domain.
    1016 * @param {string} uri1String First URI string.
    1017 * @param {string} uri2String Second URI string.
    1018 * @return {boolean} true if the two URIs have the same domain; false otherwise.
    1019 */
    1020goog.Uri.haveSameDomain = function(uri1String, uri2String) {
    1021 // Differs from goog.uri.utils.haveSameDomain, since this ignores scheme.
    1022 // TODO(gboyer): Have this just call goog.uri.util.haveSameDomain.
    1023 var pieces1 = goog.uri.utils.split(uri1String);
    1024 var pieces2 = goog.uri.utils.split(uri2String);
    1025 return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==
    1026 pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&
    1027 pieces1[goog.uri.utils.ComponentIndex.PORT] ==
    1028 pieces2[goog.uri.utils.ComponentIndex.PORT];
    1029};
    1030
    1031
    1032
    1033/**
    1034 * Class used to represent URI query parameters. It is essentially a hash of
    1035 * name-value pairs, though a name can be present more than once.
    1036 *
    1037 * Has the same interface as the collections in goog.structs.
    1038 *
    1039 * @param {?string=} opt_query Optional encoded query string to parse into
    1040 * the object.
    1041 * @param {goog.Uri=} opt_uri Optional uri object that should have its
    1042 * cache invalidated when this object updates. Deprecated -- this
    1043 * is no longer required.
    1044 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
    1045 * name in #get.
    1046 * @constructor
    1047 * @struct
    1048 * @final
    1049 */
    1050goog.Uri.QueryData = function(opt_query, opt_uri, opt_ignoreCase) {
    1051 /**
    1052 * The map containing name/value or name/array-of-values pairs.
    1053 * May be null if it requires parsing from the query string.
    1054 *
    1055 * We need to use a Map because we cannot guarantee that the key names will
    1056 * not be problematic for IE.
    1057 *
    1058 * @private {goog.structs.Map<string, !Array<*>>}
    1059 */
    1060 this.keyMap_ = null;
    1061
    1062 /**
    1063 * The number of params, or null if it requires computing.
    1064 * @private {?number}
    1065 */
    1066 this.count_ = null;
    1067
    1068 /**
    1069 * Encoded query string, or null if it requires computing from the key map.
    1070 * @private {?string}
    1071 */
    1072 this.encodedQuery_ = opt_query || null;
    1073
    1074 /**
    1075 * If true, ignore the case of the parameter name in #get.
    1076 * @private {boolean}
    1077 */
    1078 this.ignoreCase_ = !!opt_ignoreCase;
    1079};
    1080
    1081
    1082/**
    1083 * If the underlying key map is not yet initialized, it parses the
    1084 * query string and fills the map with parsed data.
    1085 * @private
    1086 */
    1087goog.Uri.QueryData.prototype.ensureKeyMapInitialized_ = function() {
    1088 if (!this.keyMap_) {
    1089 this.keyMap_ = new goog.structs.Map();
    1090 this.count_ = 0;
    1091 if (this.encodedQuery_) {
    1092 var self = this;
    1093 goog.uri.utils.parseQueryData(this.encodedQuery_, function(name, value) {
    1094 self.add(goog.string.urlDecode(name), value);
    1095 });
    1096 }
    1097 }
    1098};
    1099
    1100
    1101/**
    1102 * Creates a new query data instance from a map of names and values.
    1103 *
    1104 * @param {!goog.structs.Map<string, ?>|!Object} map Map of string parameter
    1105 * names to parameter value. If parameter value is an array, it is
    1106 * treated as if the key maps to each individual value in the
    1107 * array.
    1108 * @param {goog.Uri=} opt_uri URI object that should have its cache
    1109 * invalidated when this object updates.
    1110 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
    1111 * name in #get.
    1112 * @return {!goog.Uri.QueryData} The populated query data instance.
    1113 */
    1114goog.Uri.QueryData.createFromMap = function(map, opt_uri, opt_ignoreCase) {
    1115 var keys = goog.structs.getKeys(map);
    1116 if (typeof keys == 'undefined') {
    1117 throw Error('Keys are undefined');
    1118 }
    1119
    1120 var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase);
    1121 var values = goog.structs.getValues(map);
    1122 for (var i = 0; i < keys.length; i++) {
    1123 var key = keys[i];
    1124 var value = values[i];
    1125 if (!goog.isArray(value)) {
    1126 queryData.add(key, value);
    1127 } else {
    1128 queryData.setValues(key, value);
    1129 }
    1130 }
    1131 return queryData;
    1132};
    1133
    1134
    1135/**
    1136 * Creates a new query data instance from parallel arrays of parameter names
    1137 * and values. Allows for duplicate parameter names. Throws an error if the
    1138 * lengths of the arrays differ.
    1139 *
    1140 * @param {!Array<string>} keys Parameter names.
    1141 * @param {!Array<?>} values Parameter values.
    1142 * @param {goog.Uri=} opt_uri URI object that should have its cache
    1143 * invalidated when this object updates.
    1144 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
    1145 * name in #get.
    1146 * @return {!goog.Uri.QueryData} The populated query data instance.
    1147 */
    1148goog.Uri.QueryData.createFromKeysValues = function(
    1149 keys, values, opt_uri, opt_ignoreCase) {
    1150 if (keys.length != values.length) {
    1151 throw Error('Mismatched lengths for keys/values');
    1152 }
    1153 var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase);
    1154 for (var i = 0; i < keys.length; i++) {
    1155 queryData.add(keys[i], values[i]);
    1156 }
    1157 return queryData;
    1158};
    1159
    1160
    1161/**
    1162 * @return {?number} The number of parameters.
    1163 */
    1164goog.Uri.QueryData.prototype.getCount = function() {
    1165 this.ensureKeyMapInitialized_();
    1166 return this.count_;
    1167};
    1168
    1169
    1170/**
    1171 * Adds a key value pair.
    1172 * @param {string} key Name.
    1173 * @param {*} value Value.
    1174 * @return {!goog.Uri.QueryData} Instance of this object.
    1175 */
    1176goog.Uri.QueryData.prototype.add = function(key, value) {
    1177 this.ensureKeyMapInitialized_();
    1178 this.invalidateCache_();
    1179
    1180 key = this.getKeyName_(key);
    1181 var values = this.keyMap_.get(key);
    1182 if (!values) {
    1183 this.keyMap_.set(key, (values = []));
    1184 }
    1185 values.push(value);
    1186 this.count_++;
    1187 return this;
    1188};
    1189
    1190
    1191/**
    1192 * Removes all the params with the given key.
    1193 * @param {string} key Name.
    1194 * @return {boolean} Whether any parameter was removed.
    1195 */
    1196goog.Uri.QueryData.prototype.remove = function(key) {
    1197 this.ensureKeyMapInitialized_();
    1198
    1199 key = this.getKeyName_(key);
    1200 if (this.keyMap_.containsKey(key)) {
    1201 this.invalidateCache_();
    1202
    1203 // Decrement parameter count.
    1204 this.count_ -= this.keyMap_.get(key).length;
    1205 return this.keyMap_.remove(key);
    1206 }
    1207 return false;
    1208};
    1209
    1210
    1211/**
    1212 * Clears the parameters.
    1213 */
    1214goog.Uri.QueryData.prototype.clear = function() {
    1215 this.invalidateCache_();
    1216 this.keyMap_ = null;
    1217 this.count_ = 0;
    1218};
    1219
    1220
    1221/**
    1222 * @return {boolean} Whether we have any parameters.
    1223 */
    1224goog.Uri.QueryData.prototype.isEmpty = function() {
    1225 this.ensureKeyMapInitialized_();
    1226 return this.count_ == 0;
    1227};
    1228
    1229
    1230/**
    1231 * Whether there is a parameter with the given name
    1232 * @param {string} key The parameter name to check for.
    1233 * @return {boolean} Whether there is a parameter with the given name.
    1234 */
    1235goog.Uri.QueryData.prototype.containsKey = function(key) {
    1236 this.ensureKeyMapInitialized_();
    1237 key = this.getKeyName_(key);
    1238 return this.keyMap_.containsKey(key);
    1239};
    1240
    1241
    1242/**
    1243 * Whether there is a parameter with the given value.
    1244 * @param {*} value The value to check for.
    1245 * @return {boolean} Whether there is a parameter with the given value.
    1246 */
    1247goog.Uri.QueryData.prototype.containsValue = function(value) {
    1248 // NOTE(arv): This solution goes through all the params even if it was the
    1249 // first param. We can get around this by not reusing code or by switching to
    1250 // iterators.
    1251 var vals = this.getValues();
    1252 return goog.array.contains(vals, value);
    1253};
    1254
    1255
    1256/**
    1257 * Returns all the keys of the parameters. If a key is used multiple times
    1258 * it will be included multiple times in the returned array
    1259 * @return {!Array<string>} All the keys of the parameters.
    1260 */
    1261goog.Uri.QueryData.prototype.getKeys = function() {
    1262 this.ensureKeyMapInitialized_();
    1263 // We need to get the values to know how many keys to add.
    1264 var vals = /** @type {!Array<*>} */ (this.keyMap_.getValues());
    1265 var keys = this.keyMap_.getKeys();
    1266 var rv = [];
    1267 for (var i = 0; i < keys.length; i++) {
    1268 var val = vals[i];
    1269 for (var j = 0; j < val.length; j++) {
    1270 rv.push(keys[i]);
    1271 }
    1272 }
    1273 return rv;
    1274};
    1275
    1276
    1277/**
    1278 * Returns all the values of the parameters with the given name. If the query
    1279 * data has no such key this will return an empty array. If no key is given
    1280 * all values wil be returned.
    1281 * @param {string=} opt_key The name of the parameter to get the values for.
    1282 * @return {!Array<?>} All the values of the parameters with the given name.
    1283 */
    1284goog.Uri.QueryData.prototype.getValues = function(opt_key) {
    1285 this.ensureKeyMapInitialized_();
    1286 var rv = [];
    1287 if (goog.isString(opt_key)) {
    1288 if (this.containsKey(opt_key)) {
    1289 rv = goog.array.concat(rv, this.keyMap_.get(this.getKeyName_(opt_key)));
    1290 }
    1291 } else {
    1292 // Return all values.
    1293 var values = this.keyMap_.getValues();
    1294 for (var i = 0; i < values.length; i++) {
    1295 rv = goog.array.concat(rv, values[i]);
    1296 }
    1297 }
    1298 return rv;
    1299};
    1300
    1301
    1302/**
    1303 * Sets a key value pair and removes all other keys with the same value.
    1304 *
    1305 * @param {string} key Name.
    1306 * @param {*} value Value.
    1307 * @return {!goog.Uri.QueryData} Instance of this object.
    1308 */
    1309goog.Uri.QueryData.prototype.set = function(key, value) {
    1310 this.ensureKeyMapInitialized_();
    1311 this.invalidateCache_();
    1312
    1313 // TODO(chrishenry): This could be better written as
    1314 // this.remove(key), this.add(key, value), but that would reorder
    1315 // the key (since the key is first removed and then added at the
    1316 // end) and we would have to fix unit tests that depend on key
    1317 // ordering.
    1318 key = this.getKeyName_(key);
    1319 if (this.containsKey(key)) {
    1320 this.count_ -= this.keyMap_.get(key).length;
    1321 }
    1322 this.keyMap_.set(key, [value]);
    1323 this.count_++;
    1324 return this;
    1325};
    1326
    1327
    1328/**
    1329 * Returns the first value associated with the key. If the query data has no
    1330 * such key this will return undefined or the optional default.
    1331 * @param {string} key The name of the parameter to get the value for.
    1332 * @param {*=} opt_default The default value to return if the query data
    1333 * has no such key.
    1334 * @return {*} The first string value associated with the key, or opt_default
    1335 * if there's no value.
    1336 */
    1337goog.Uri.QueryData.prototype.get = function(key, opt_default) {
    1338 var values = key ? this.getValues(key) : [];
    1339 if (goog.Uri.preserveParameterTypesCompatibilityFlag) {
    1340 return values.length > 0 ? values[0] : opt_default;
    1341 } else {
    1342 return values.length > 0 ? String(values[0]) : opt_default;
    1343 }
    1344};
    1345
    1346
    1347/**
    1348 * Sets the values for a key. If the key already exists, this will
    1349 * override all of the existing values that correspond to the key.
    1350 * @param {string} key The key to set values for.
    1351 * @param {!Array<?>} values The values to set.
    1352 */
    1353goog.Uri.QueryData.prototype.setValues = function(key, values) {
    1354 this.remove(key);
    1355
    1356 if (values.length > 0) {
    1357 this.invalidateCache_();
    1358 this.keyMap_.set(this.getKeyName_(key), goog.array.clone(values));
    1359 this.count_ += values.length;
    1360 }
    1361};
    1362
    1363
    1364/**
    1365 * @return {string} Encoded query string.
    1366 * @override
    1367 */
    1368goog.Uri.QueryData.prototype.toString = function() {
    1369 if (this.encodedQuery_) {
    1370 return this.encodedQuery_;
    1371 }
    1372
    1373 if (!this.keyMap_) {
    1374 return '';
    1375 }
    1376
    1377 var sb = [];
    1378
    1379 // In the past, we use this.getKeys() and this.getVals(), but that
    1380 // generates a lot of allocations as compared to simply iterating
    1381 // over the keys.
    1382 var keys = this.keyMap_.getKeys();
    1383 for (var i = 0; i < keys.length; i++) {
    1384 var key = keys[i];
    1385 var encodedKey = goog.string.urlEncode(key);
    1386 var val = this.getValues(key);
    1387 for (var j = 0; j < val.length; j++) {
    1388 var param = encodedKey;
    1389 // Ensure that null and undefined are encoded into the url as
    1390 // literal strings.
    1391 if (val[j] !== '') {
    1392 param += '=' + goog.string.urlEncode(val[j]);
    1393 }
    1394 sb.push(param);
    1395 }
    1396 }
    1397
    1398 return this.encodedQuery_ = sb.join('&');
    1399};
    1400
    1401
    1402/**
    1403 * @return {string} Decoded query string.
    1404 */
    1405goog.Uri.QueryData.prototype.toDecodedString = function() {
    1406 return goog.Uri.decodeOrEmpty_(this.toString());
    1407};
    1408
    1409
    1410/**
    1411 * Invalidate the cache.
    1412 * @private
    1413 */
    1414goog.Uri.QueryData.prototype.invalidateCache_ = function() {
    1415 this.encodedQuery_ = null;
    1416};
    1417
    1418
    1419/**
    1420 * Removes all keys that are not in the provided list. (Modifies this object.)
    1421 * @param {Array<string>} keys The desired keys.
    1422 * @return {!goog.Uri.QueryData} a reference to this object.
    1423 */
    1424goog.Uri.QueryData.prototype.filterKeys = function(keys) {
    1425 this.ensureKeyMapInitialized_();
    1426 this.keyMap_.forEach(
    1427 function(value, key) {
    1428 if (!goog.array.contains(keys, key)) {
    1429 this.remove(key);
    1430 }
    1431 }, this);
    1432 return this;
    1433};
    1434
    1435
    1436/**
    1437 * Clone the query data instance.
    1438 * @return {!goog.Uri.QueryData} New instance of the QueryData object.
    1439 */
    1440goog.Uri.QueryData.prototype.clone = function() {
    1441 var rv = new goog.Uri.QueryData();
    1442 rv.encodedQuery_ = this.encodedQuery_;
    1443 if (this.keyMap_) {
    1444 rv.keyMap_ = this.keyMap_.clone();
    1445 rv.count_ = this.count_;
    1446 }
    1447 return rv;
    1448};
    1449
    1450
    1451/**
    1452 * Helper function to get the key name from a JavaScript object. Converts
    1453 * the object to a string, and to lower case if necessary.
    1454 * @private
    1455 * @param {*} arg The object to get a key name from.
    1456 * @return {string} valid key name which can be looked up in #keyMap_.
    1457 */
    1458goog.Uri.QueryData.prototype.getKeyName_ = function(arg) {
    1459 var keyName = String(arg);
    1460 if (this.ignoreCase_) {
    1461 keyName = keyName.toLowerCase();
    1462 }
    1463 return keyName;
    1464};
    1465
    1466
    1467/**
    1468 * Ignore case in parameter names.
    1469 * NOTE: If there are already key/value pairs in the QueryData, and
    1470 * ignoreCase_ is set to false, the keys will all be lower-cased.
    1471 * @param {boolean} ignoreCase whether this goog.Uri should ignore case.
    1472 */
    1473goog.Uri.QueryData.prototype.setIgnoreCase = function(ignoreCase) {
    1474 var resetKeys = ignoreCase && !this.ignoreCase_;
    1475 if (resetKeys) {
    1476 this.ensureKeyMapInitialized_();
    1477 this.invalidateCache_();
    1478 this.keyMap_.forEach(
    1479 function(value, key) {
    1480 var lowerCase = key.toLowerCase();
    1481 if (key != lowerCase) {
    1482 this.remove(key);
    1483 this.setValues(lowerCase, value);
    1484 }
    1485 }, this);
    1486 }
    1487 this.ignoreCase_ = ignoreCase;
    1488};
    1489
    1490
    1491/**
    1492 * Extends a query data object with another query data or map like object. This
    1493 * operates 'in-place', it does not create a new QueryData object.
    1494 *
    1495 * @param {...(goog.Uri.QueryData|goog.structs.Map<?, ?>|Object)} var_args
    1496 * The object from which key value pairs will be copied.
    1497 */
    1498goog.Uri.QueryData.prototype.extend = function(var_args) {
    1499 for (var i = 0; i < arguments.length; i++) {
    1500 var data = arguments[i];
    1501 goog.structs.forEach(data,
    1502 /** @this {goog.Uri.QueryData} */
    1503 function(value, key) {
    1504 this.add(key, value);
    1505 }, this);
    1506 }
    1507};
    \ 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 15b1739..5328e76 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 * @param {boolean=} opt_preserveReserved If true, percent-encoding of RFC-3986
    302 * reserved characters will not be removed.
    303 * @return {?string} The string URI-decoded, or null if uri is null.
    304 * @private
    305 */
    306goog.uri.utils.decodeIfPossible_ = function(uri, opt_preserveReserved) {
    307 if (!uri) {
    308 return uri;
    309 }
    310
    311 return opt_preserveReserved ? decodeURI(uri) : decodeURIComponent(uri);
    312};
    313
    314
    315/**
    316 * Gets a URI component by index.
    317 *
    318 * It is preferred to use the getPathEncoded() variety of functions ahead,
    319 * since they are more readable.
    320 *
    321 * @param {goog.uri.utils.ComponentIndex} componentIndex The component index.
    322 * @param {string} uri The URI to examine.
    323 * @return {?string} The still-encoded component, or null if the component
    324 * is not present.
    325 * @private
    326 */
    327goog.uri.utils.getComponentByIndex_ = function(componentIndex, uri) {
    328 // Convert undefined, null, and empty string into null.
    329 return goog.uri.utils.split(uri)[componentIndex] || null;
    330};
    331
    332
    333/**
    334 * @param {string} uri The URI to examine.
    335 * @return {?string} The protocol or scheme, or null if none. Does not
    336 * include trailing colons or slashes.
    337 */
    338goog.uri.utils.getScheme = function(uri) {
    339 return goog.uri.utils.getComponentByIndex_(
    340 goog.uri.utils.ComponentIndex.SCHEME, uri);
    341};
    342
    343
    344/**
    345 * Gets the effective scheme for the URL. If the URL is relative then the
    346 * scheme is derived from the page's location.
    347 * @param {string} uri The URI to examine.
    348 * @return {string} The protocol or scheme, always lower case.
    349 */
    350goog.uri.utils.getEffectiveScheme = function(uri) {
    351 var scheme = goog.uri.utils.getScheme(uri);
    352 if (!scheme && self.location) {
    353 var protocol = self.location.protocol;
    354 scheme = protocol.substr(0, protocol.length - 1);
    355 }
    356 // NOTE: When called from a web worker in Firefox 3.5, location maybe null.
    357 // All other browsers with web workers support self.location from the worker.
    358 return scheme ? scheme.toLowerCase() : '';
    359};
    360
    361
    362/**
    363 * @param {string} uri The URI to examine.
    364 * @return {?string} The user name still encoded, or null if none.
    365 */
    366goog.uri.utils.getUserInfoEncoded = function(uri) {
    367 return goog.uri.utils.getComponentByIndex_(
    368 goog.uri.utils.ComponentIndex.USER_INFO, uri);
    369};
    370
    371
    372/**
    373 * @param {string} uri The URI to examine.
    374 * @return {?string} The decoded user info, or null if none.
    375 */
    376goog.uri.utils.getUserInfo = function(uri) {
    377 return goog.uri.utils.decodeIfPossible_(
    378 goog.uri.utils.getUserInfoEncoded(uri));
    379};
    380
    381
    382/**
    383 * @param {string} uri The URI to examine.
    384 * @return {?string} The domain name still encoded, or null if none.
    385 */
    386goog.uri.utils.getDomainEncoded = function(uri) {
    387 return goog.uri.utils.getComponentByIndex_(
    388 goog.uri.utils.ComponentIndex.DOMAIN, uri);
    389};
    390
    391
    392/**
    393 * @param {string} uri The URI to examine.
    394 * @return {?string} The decoded domain, or null if none.
    395 */
    396goog.uri.utils.getDomain = function(uri) {
    397 return goog.uri.utils.decodeIfPossible_(
    398 goog.uri.utils.getDomainEncoded(uri), true /* opt_preserveReserved */);
    399};
    400
    401
    402/**
    403 * @param {string} uri The URI to examine.
    404 * @return {?number} The port number, or null if none.
    405 */
    406goog.uri.utils.getPort = function(uri) {
    407 // Coerce to a number. If the result of getComponentByIndex_ is null or
    408 // non-numeric, the number coersion yields NaN. This will then return
    409 // null for all non-numeric cases (though also zero, which isn't a relevant
    410 // port number).
    411 return Number(goog.uri.utils.getComponentByIndex_(
    412 goog.uri.utils.ComponentIndex.PORT, uri)) || null;
    413};
    414
    415
    416/**
    417 * @param {string} uri The URI to examine.
    418 * @return {?string} The path still encoded, or null if none. Includes the
    419 * leading slash, if any.
    420 */
    421goog.uri.utils.getPathEncoded = function(uri) {
    422 return goog.uri.utils.getComponentByIndex_(
    423 goog.uri.utils.ComponentIndex.PATH, uri);
    424};
    425
    426
    427/**
    428 * @param {string} uri The URI to examine.
    429 * @return {?string} The decoded path, or null if none. Includes the leading
    430 * slash, if any.
    431 */
    432goog.uri.utils.getPath = function(uri) {
    433 return goog.uri.utils.decodeIfPossible_(
    434 goog.uri.utils.getPathEncoded(uri), true /* opt_preserveReserved */);
    435};
    436
    437
    438/**
    439 * @param {string} uri The URI to examine.
    440 * @return {?string} The query data still encoded, or null if none. Does not
    441 * include the question mark itself.
    442 */
    443goog.uri.utils.getQueryData = function(uri) {
    444 return goog.uri.utils.getComponentByIndex_(
    445 goog.uri.utils.ComponentIndex.QUERY_DATA, uri);
    446};
    447
    448
    449/**
    450 * @param {string} uri The URI to examine.
    451 * @return {?string} The fragment identifier, or null if none. Does not
    452 * include the hash mark itself.
    453 */
    454goog.uri.utils.getFragmentEncoded = function(uri) {
    455 // The hash mark may not appear in any other part of the URL.
    456 var hashIndex = uri.indexOf('#');
    457 return hashIndex < 0 ? null : uri.substr(hashIndex + 1);
    458};
    459
    460
    461/**
    462 * @param {string} uri The URI to examine.
    463 * @param {?string} fragment The encoded fragment identifier, or null if none.
    464 * Does not include the hash mark itself.
    465 * @return {string} The URI with the fragment set.
    466 */
    467goog.uri.utils.setFragmentEncoded = function(uri, fragment) {
    468 return goog.uri.utils.removeFragment(uri) + (fragment ? '#' + fragment : '');
    469};
    470
    471
    472/**
    473 * @param {string} uri The URI to examine.
    474 * @return {?string} The decoded fragment identifier, or null if none. Does
    475 * not include the hash mark.
    476 */
    477goog.uri.utils.getFragment = function(uri) {
    478 return goog.uri.utils.decodeIfPossible_(
    479 goog.uri.utils.getFragmentEncoded(uri));
    480};
    481
    482
    483/**
    484 * Extracts everything up to the port of the URI.
    485 * @param {string} uri The URI string.
    486 * @return {string} Everything up to and including the port.
    487 */
    488goog.uri.utils.getHost = function(uri) {
    489 var pieces = goog.uri.utils.split(uri);
    490 return goog.uri.utils.buildFromEncodedParts(
    491 pieces[goog.uri.utils.ComponentIndex.SCHEME],
    492 pieces[goog.uri.utils.ComponentIndex.USER_INFO],
    493 pieces[goog.uri.utils.ComponentIndex.DOMAIN],
    494 pieces[goog.uri.utils.ComponentIndex.PORT]);
    495};
    496
    497
    498/**
    499 * Extracts the path of the URL and everything after.
    500 * @param {string} uri The URI string.
    501 * @return {string} The URI, starting at the path and including the query
    502 * parameters and fragment identifier.
    503 */
    504goog.uri.utils.getPathAndAfter = function(uri) {
    505 var pieces = goog.uri.utils.split(uri);
    506 return goog.uri.utils.buildFromEncodedParts(null, null, null, null,
    507 pieces[goog.uri.utils.ComponentIndex.PATH],
    508 pieces[goog.uri.utils.ComponentIndex.QUERY_DATA],
    509 pieces[goog.uri.utils.ComponentIndex.FRAGMENT]);
    510};
    511
    512
    513/**
    514 * Gets the URI with the fragment identifier removed.
    515 * @param {string} uri The URI to examine.
    516 * @return {string} Everything preceding the hash mark.
    517 */
    518goog.uri.utils.removeFragment = function(uri) {
    519 // The hash mark may not appear in any other part of the URL.
    520 var hashIndex = uri.indexOf('#');
    521 return hashIndex < 0 ? uri : uri.substr(0, hashIndex);
    522};
    523
    524
    525/**
    526 * Ensures that two URI's have the exact same domain, scheme, and port.
    527 *
    528 * Unlike the version in goog.Uri, this checks protocol, and therefore is
    529 * suitable for checking against the browser's same-origin policy.
    530 *
    531 * @param {string} uri1 The first URI.
    532 * @param {string} uri2 The second URI.
    533 * @return {boolean} Whether they have the same scheme, domain and port.
    534 */
    535goog.uri.utils.haveSameDomain = function(uri1, uri2) {
    536 var pieces1 = goog.uri.utils.split(uri1);
    537 var pieces2 = goog.uri.utils.split(uri2);
    538 return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==
    539 pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&
    540 pieces1[goog.uri.utils.ComponentIndex.SCHEME] ==
    541 pieces2[goog.uri.utils.ComponentIndex.SCHEME] &&
    542 pieces1[goog.uri.utils.ComponentIndex.PORT] ==
    543 pieces2[goog.uri.utils.ComponentIndex.PORT];
    544};
    545
    546
    547/**
    548 * Asserts that there are no fragment or query identifiers, only in uncompiled
    549 * mode.
    550 * @param {string} uri The URI to examine.
    551 * @private
    552 */
    553goog.uri.utils.assertNoFragmentsOrQueries_ = function(uri) {
    554 // NOTE: would use goog.asserts here, but jscompiler doesn't know that
    555 // indexOf has no side effects.
    556 if (goog.DEBUG && (uri.indexOf('#') >= 0 || uri.indexOf('?') >= 0)) {
    557 throw Error('goog.uri.utils: Fragment or query identifiers are not ' +
    558 'supported: [' + uri + ']');
    559 }
    560};
    561
    562
    563/**
    564 * Supported query parameter values by the parameter serializing utilities.
    565 *
    566 * If a value is null or undefined, the key-value pair is skipped, as an easy
    567 * way to omit parameters conditionally. Non-array parameters are converted
    568 * to a string and URI encoded. Array values are expanded into multiple
    569 * &key=value pairs, with each element stringized and URI-encoded.
    570 *
    571 * @typedef {*}
    572 */
    573goog.uri.utils.QueryValue;
    574
    575
    576/**
    577 * An array representing a set of query parameters with alternating keys
    578 * and values.
    579 *
    580 * Keys are assumed to be URI encoded already and live at even indices. See
    581 * goog.uri.utils.QueryValue for details on how parameter values are encoded.
    582 *
    583 * Example:
    584 * <pre>
    585 * var data = [
    586 * // Simple param: ?name=BobBarker
    587 * 'name', 'BobBarker',
    588 * // Conditional param -- may be omitted entirely.
    589 * 'specialDietaryNeeds', hasDietaryNeeds() ? getDietaryNeeds() : null,
    590 * // Multi-valued param: &house=LosAngeles&house=NewYork&house=null
    591 * 'house', ['LosAngeles', 'NewYork', null]
    592 * ];
    593 * </pre>
    594 *
    595 * @typedef {!Array.<string|goog.uri.utils.QueryValue>}
    596 */
    597goog.uri.utils.QueryArray;
    598
    599
    600/**
    601 * Appends a URI and query data in a string buffer with special preconditions.
    602 *
    603 * Internal implementation utility, performing very few object allocations.
    604 *
    605 * @param {!Array.<string|undefined>} buffer A string buffer. The first element
    606 * must be the base URI, and may have a fragment identifier. If the array
    607 * contains more than one element, the second element must be an ampersand,
    608 * and may be overwritten, depending on the base URI. Undefined elements
    609 * are treated as empty-string.
    610 * @return {string} The concatenated URI and query data.
    611 * @private
    612 */
    613goog.uri.utils.appendQueryData_ = function(buffer) {
    614 if (buffer[1]) {
    615 // At least one query parameter was added. We need to check the
    616 // punctuation mark, which is currently an ampersand, and also make sure
    617 // there aren't any interfering fragment identifiers.
    618 var baseUri = /** @type {string} */ (buffer[0]);
    619 var hashIndex = baseUri.indexOf('#');
    620 if (hashIndex >= 0) {
    621 // Move the fragment off the base part of the URI into the end.
    622 buffer.push(baseUri.substr(hashIndex));
    623 buffer[0] = baseUri = baseUri.substr(0, hashIndex);
    624 }
    625 var questionIndex = baseUri.indexOf('?');
    626 if (questionIndex < 0) {
    627 // No question mark, so we need a question mark instead of an ampersand.
    628 buffer[1] = '?';
    629 } else if (questionIndex == baseUri.length - 1) {
    630 // Question mark is the very last character of the existing URI, so don't
    631 // append an additional delimiter.
    632 buffer[1] = undefined;
    633 }
    634 }
    635
    636 return buffer.join('');
    637};
    638
    639
    640/**
    641 * Appends key=value pairs to an array, supporting multi-valued objects.
    642 * @param {string} key The key prefix.
    643 * @param {goog.uri.utils.QueryValue} value The value to serialize.
    644 * @param {!Array.<string>} pairs The array to which the 'key=value' strings
    645 * should be appended.
    646 * @private
    647 */
    648goog.uri.utils.appendKeyValuePairs_ = function(key, value, pairs) {
    649 if (goog.isArray(value)) {
    650 // Convince the compiler it's an array.
    651 goog.asserts.assertArray(value);
    652 for (var j = 0; j < value.length; j++) {
    653 // Convert to string explicitly, to short circuit the null and array
    654 // logic in this function -- this ensures that null and undefined get
    655 // written as literal 'null' and 'undefined', and arrays don't get
    656 // expanded out but instead encoded in the default way.
    657 goog.uri.utils.appendKeyValuePairs_(key, String(value[j]), pairs);
    658 }
    659 } else if (value != null) {
    660 // Skip a top-level null or undefined entirely.
    661 pairs.push('&', key,
    662 // Check for empty string. Zero gets encoded into the url as literal
    663 // strings. For empty string, skip the equal sign, to be consistent
    664 // with UriBuilder.java.
    665 value === '' ? '' : '=',
    666 goog.string.urlEncode(value));
    667 }
    668};
    669
    670
    671/**
    672 * Builds a buffer of query data from a sequence of alternating keys and values.
    673 *
    674 * @param {!Array.<string|undefined>} buffer A string buffer to append to. The
    675 * first element appended will be an '&', and may be replaced by the caller.
    676 * @param {goog.uri.utils.QueryArray|Arguments} keysAndValues An array with
    677 * alternating keys and values -- see the typedef.
    678 * @param {number=} opt_startIndex A start offset into the arary, defaults to 0.
    679 * @return {!Array.<string|undefined>} The buffer argument.
    680 * @private
    681 */
    682goog.uri.utils.buildQueryDataBuffer_ = function(
    683 buffer, keysAndValues, opt_startIndex) {
    684 goog.asserts.assert(Math.max(keysAndValues.length - (opt_startIndex || 0),
    685 0) % 2 == 0, 'goog.uri.utils: Key/value lists must be even in length.');
    686
    687 for (var i = opt_startIndex || 0; i < keysAndValues.length; i += 2) {
    688 goog.uri.utils.appendKeyValuePairs_(
    689 keysAndValues[i], keysAndValues[i + 1], buffer);
    690 }
    691
    692 return buffer;
    693};
    694
    695
    696/**
    697 * Builds a query data string from a sequence of alternating keys and values.
    698 * Currently generates "&key&" for empty args.
    699 *
    700 * @param {goog.uri.utils.QueryArray} keysAndValues Alternating keys and
    701 * values. See the typedef.
    702 * @param {number=} opt_startIndex A start offset into the arary, defaults to 0.
    703 * @return {string} The encoded query string, in the form 'a=1&b=2'.
    704 */
    705goog.uri.utils.buildQueryData = function(keysAndValues, opt_startIndex) {
    706 var buffer = goog.uri.utils.buildQueryDataBuffer_(
    707 [], keysAndValues, opt_startIndex);
    708 buffer[0] = ''; // Remove the leading ampersand.
    709 return buffer.join('');
    710};
    711
    712
    713/**
    714 * Builds a buffer of query data from a map.
    715 *
    716 * @param {!Array.<string|undefined>} buffer A string buffer to append to. The
    717 * first element appended will be an '&', and may be replaced by the caller.
    718 * @param {Object.<goog.uri.utils.QueryValue>} map An object where keys are
    719 * URI-encoded parameter keys, and the values conform to the contract
    720 * specified in the goog.uri.utils.QueryValue typedef.
    721 * @return {!Array.<string|undefined>} The buffer argument.
    722 * @private
    723 */
    724goog.uri.utils.buildQueryDataBufferFromMap_ = function(buffer, map) {
    725 for (var key in map) {
    726 goog.uri.utils.appendKeyValuePairs_(key, map[key], buffer);
    727 }
    728
    729 return buffer;
    730};
    731
    732
    733/**
    734 * Builds a query data string from a map.
    735 * Currently generates "&key&" for empty args.
    736 *
    737 * @param {Object} map An object where keys are URI-encoded parameter keys,
    738 * and the values are arbitrary types or arrays. Keys with a null value
    739 * are dropped.
    740 * @return {string} The encoded query string, in the form 'a=1&b=2'.
    741 */
    742goog.uri.utils.buildQueryDataFromMap = function(map) {
    743 var buffer = goog.uri.utils.buildQueryDataBufferFromMap_([], map);
    744 buffer[0] = '';
    745 return buffer.join('');
    746};
    747
    748
    749/**
    750 * Appends URI parameters to an existing URI.
    751 *
    752 * The variable arguments may contain alternating keys and values. Keys are
    753 * assumed to be already URI encoded. The values should not be URI-encoded,
    754 * and will instead be encoded by this function.
    755 * <pre>
    756 * appendParams('http://www.foo.com?existing=true',
    757 * 'key1', 'value1',
    758 * 'key2', 'value?willBeEncoded',
    759 * 'key3', ['valueA', 'valueB', 'valueC'],
    760 * 'key4', null);
    761 * result: 'http://www.foo.com?existing=true&' +
    762 * 'key1=value1&' +
    763 * 'key2=value%3FwillBeEncoded&' +
    764 * 'key3=valueA&key3=valueB&key3=valueC'
    765 * </pre>
    766 *
    767 * A single call to this function will not exhibit quadratic behavior in IE,
    768 * whereas multiple repeated calls may, although the effect is limited by
    769 * fact that URL's generally can't exceed 2kb.
    770 *
    771 * @param {string} uri The original URI, which may already have query data.
    772 * @param {...(goog.uri.utils.QueryArray|string|goog.uri.utils.QueryValue)} var_args
    773 * An array or argument list conforming to goog.uri.utils.QueryArray.
    774 * @return {string} The URI with all query parameters added.
    775 */
    776goog.uri.utils.appendParams = function(uri, var_args) {
    777 return goog.uri.utils.appendQueryData_(
    778 arguments.length == 2 ?
    779 goog.uri.utils.buildQueryDataBuffer_([uri], arguments[1], 0) :
    780 goog.uri.utils.buildQueryDataBuffer_([uri], arguments, 1));
    781};
    782
    783
    784/**
    785 * Appends query parameters from a map.
    786 *
    787 * @param {string} uri The original URI, which may already have query data.
    788 * @param {Object} map An object where keys are URI-encoded parameter keys,
    789 * and the values are arbitrary types or arrays. Keys with a null value
    790 * are dropped.
    791 * @return {string} The new parameters.
    792 */
    793goog.uri.utils.appendParamsFromMap = function(uri, map) {
    794 return goog.uri.utils.appendQueryData_(
    795 goog.uri.utils.buildQueryDataBufferFromMap_([uri], map));
    796};
    797
    798
    799/**
    800 * Appends a single URI parameter.
    801 *
    802 * Repeated calls to this can exhibit quadratic behavior in IE6 due to the
    803 * way string append works, though it should be limited given the 2kb limit.
    804 *
    805 * @param {string} uri The original URI, which may already have query data.
    806 * @param {string} key The key, which must already be URI encoded.
    807 * @param {*=} opt_value The value, which will be stringized and encoded
    808 * (assumed not already to be encoded). If omitted, undefined, or null, the
    809 * key will be added as a valueless parameter.
    810 * @return {string} The URI with the query parameter added.
    811 */
    812goog.uri.utils.appendParam = function(uri, key, opt_value) {
    813 var paramArr = [uri, '&', key];
    814 if (goog.isDefAndNotNull(opt_value)) {
    815 paramArr.push('=', goog.string.urlEncode(opt_value));
    816 }
    817 return goog.uri.utils.appendQueryData_(paramArr);
    818};
    819
    820
    821/**
    822 * Finds the next instance of a query parameter with the specified name.
    823 *
    824 * Does not instantiate any objects.
    825 *
    826 * @param {string} uri The URI to search. May contain a fragment identifier
    827 * if opt_hashIndex is specified.
    828 * @param {number} startIndex The index to begin searching for the key at. A
    829 * match may be found even if this is one character after the ampersand.
    830 * @param {string} keyEncoded The URI-encoded key.
    831 * @param {number} hashOrEndIndex Index to stop looking at. If a hash
    832 * mark is present, it should be its index, otherwise it should be the
    833 * length of the string.
    834 * @return {number} The position of the first character in the key's name,
    835 * immediately after either a question mark or a dot.
    836 * @private
    837 */
    838goog.uri.utils.findParam_ = function(
    839 uri, startIndex, keyEncoded, hashOrEndIndex) {
    840 var index = startIndex;
    841 var keyLength = keyEncoded.length;
    842
    843 // Search for the key itself and post-filter for surronuding punctuation,
    844 // rather than expensively building a regexp.
    845 while ((index = uri.indexOf(keyEncoded, index)) >= 0 &&
    846 index < hashOrEndIndex) {
    847 var precedingChar = uri.charCodeAt(index - 1);
    848 // Ensure that the preceding character is '&' or '?'.
    849 if (precedingChar == goog.uri.utils.CharCode_.AMPERSAND ||
    850 precedingChar == goog.uri.utils.CharCode_.QUESTION) {
    851 // Ensure the following character is '&', '=', '#', or NaN
    852 // (end of string).
    853 var followingChar = uri.charCodeAt(index + keyLength);
    854 if (!followingChar ||
    855 followingChar == goog.uri.utils.CharCode_.EQUAL ||
    856 followingChar == goog.uri.utils.CharCode_.AMPERSAND ||
    857 followingChar == goog.uri.utils.CharCode_.HASH) {
    858 return index;
    859 }
    860 }
    861 index += keyLength + 1;
    862 }
    863
    864 return -1;
    865};
    866
    867
    868/**
    869 * Regular expression for finding a hash mark or end of string.
    870 * @type {RegExp}
    871 * @private
    872 */
    873goog.uri.utils.hashOrEndRe_ = /#|$/;
    874
    875
    876/**
    877 * Determines if the URI contains a specific key.
    878 *
    879 * Performs no object instantiations.
    880 *
    881 * @param {string} uri The URI to process. May contain a fragment
    882 * identifier.
    883 * @param {string} keyEncoded The URI-encoded key. Case-sensitive.
    884 * @return {boolean} Whether the key is present.
    885 */
    886goog.uri.utils.hasParam = function(uri, keyEncoded) {
    887 return goog.uri.utils.findParam_(uri, 0, keyEncoded,
    888 uri.search(goog.uri.utils.hashOrEndRe_)) >= 0;
    889};
    890
    891
    892/**
    893 * Gets the first value of a query parameter.
    894 * @param {string} uri The URI to process. May contain a fragment.
    895 * @param {string} keyEncoded The URI-encoded key. Case-sensitive.
    896 * @return {?string} The first value of the parameter (URI-decoded), or null
    897 * if the parameter is not found.
    898 */
    899goog.uri.utils.getParamValue = function(uri, keyEncoded) {
    900 var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
    901 var foundIndex = goog.uri.utils.findParam_(
    902 uri, 0, keyEncoded, hashOrEndIndex);
    903
    904 if (foundIndex < 0) {
    905 return null;
    906 } else {
    907 var endPosition = uri.indexOf('&', foundIndex);
    908 if (endPosition < 0 || endPosition > hashOrEndIndex) {
    909 endPosition = hashOrEndIndex;
    910 }
    911 // Progress forth to the end of the "key=" or "key&" substring.
    912 foundIndex += keyEncoded.length + 1;
    913 // Use substr, because it (unlike substring) will return empty string
    914 // if foundIndex > endPosition.
    915 return goog.string.urlDecode(
    916 uri.substr(foundIndex, endPosition - foundIndex));
    917 }
    918};
    919
    920
    921/**
    922 * Gets all values of a query parameter.
    923 * @param {string} uri The URI to process. May contain a framgnet.
    924 * @param {string} keyEncoded The URI-encoded key. Case-snsitive.
    925 * @return {!Array.<string>} All URI-decoded values with the given key.
    926 * If the key is not found, this will have length 0, but never be null.
    927 */
    928goog.uri.utils.getParamValues = function(uri, keyEncoded) {
    929 var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
    930 var position = 0;
    931 var foundIndex;
    932 var result = [];
    933
    934 while ((foundIndex = goog.uri.utils.findParam_(
    935 uri, position, keyEncoded, hashOrEndIndex)) >= 0) {
    936 // Find where this parameter ends, either the '&' or the end of the
    937 // query parameters.
    938 position = uri.indexOf('&', foundIndex);
    939 if (position < 0 || position > hashOrEndIndex) {
    940 position = hashOrEndIndex;
    941 }
    942
    943 // Progress forth to the end of the "key=" or "key&" substring.
    944 foundIndex += keyEncoded.length + 1;
    945 // Use substr, because it (unlike substring) will return empty string
    946 // if foundIndex > position.
    947 result.push(goog.string.urlDecode(uri.substr(
    948 foundIndex, position - foundIndex)));
    949 }
    950
    951 return result;
    952};
    953
    954
    955/**
    956 * Regexp to find trailing question marks and ampersands.
    957 * @type {RegExp}
    958 * @private
    959 */
    960goog.uri.utils.trailingQueryPunctuationRe_ = /[?&]($|#)/;
    961
    962
    963/**
    964 * Removes all instances of a query parameter.
    965 * @param {string} uri The URI to process. Must not contain a fragment.
    966 * @param {string} keyEncoded The URI-encoded key.
    967 * @return {string} The URI with all instances of the parameter removed.
    968 */
    969goog.uri.utils.removeParam = function(uri, keyEncoded) {
    970 var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
    971 var position = 0;
    972 var foundIndex;
    973 var buffer = [];
    974
    975 // Look for a query parameter.
    976 while ((foundIndex = goog.uri.utils.findParam_(
    977 uri, position, keyEncoded, hashOrEndIndex)) >= 0) {
    978 // Get the portion of the query string up to, but not including, the ?
    979 // or & starting the parameter.
    980 buffer.push(uri.substring(position, foundIndex));
    981 // Progress to immediately after the '&'. If not found, go to the end.
    982 // Avoid including the hash mark.
    983 position = Math.min((uri.indexOf('&', foundIndex) + 1) || hashOrEndIndex,
    984 hashOrEndIndex);
    985 }
    986
    987 // Append everything that is remaining.
    988 buffer.push(uri.substr(position));
    989
    990 // Join the buffer, and remove trailing punctuation that remains.
    991 return buffer.join('').replace(
    992 goog.uri.utils.trailingQueryPunctuationRe_, '$1');
    993};
    994
    995
    996/**
    997 * Replaces all existing definitions of a parameter with a single definition.
    998 *
    999 * Repeated calls to this can exhibit quadratic behavior due to the need to
    1000 * find existing instances and reconstruct the string, though it should be
    1001 * limited given the 2kb limit. Consider using appendParams to append multiple
    1002 * parameters in bulk.
    1003 *
    1004 * @param {string} uri The original URI, which may already have query data.
    1005 * @param {string} keyEncoded The key, which must already be URI encoded.
    1006 * @param {*} value The value, which will be stringized and encoded (assumed
    1007 * not already to be encoded).
    1008 * @return {string} The URI with the query parameter added.
    1009 */
    1010goog.uri.utils.setParam = function(uri, keyEncoded, value) {
    1011 return goog.uri.utils.appendParam(
    1012 goog.uri.utils.removeParam(uri, keyEncoded), keyEncoded, value);
    1013};
    1014
    1015
    1016/**
    1017 * Generates a URI path using a given URI and a path with checks to
    1018 * prevent consecutive "//". The baseUri passed in must not contain
    1019 * query or fragment identifiers. The path to append may not contain query or
    1020 * fragment identifiers.
    1021 *
    1022 * @param {string} baseUri URI to use as the base.
    1023 * @param {string} path Path to append.
    1024 * @return {string} Updated URI.
    1025 */
    1026goog.uri.utils.appendPath = function(baseUri, path) {
    1027 goog.uri.utils.assertNoFragmentsOrQueries_(baseUri);
    1028
    1029 // Remove any trailing '/'
    1030 if (goog.string.endsWith(baseUri, '/')) {
    1031 baseUri = baseUri.substr(0, baseUri.length - 1);
    1032 }
    1033 // Remove any leading '/'
    1034 if (goog.string.startsWith(path, '/')) {
    1035 path = path.substr(1);
    1036 }
    1037 return goog.string.buildString(baseUri, '/', path);
    1038};
    1039
    1040
    1041/**
    1042 * Replaces the path.
    1043 * @param {string} uri URI to use as the base.
    1044 * @param {string} path New path.
    1045 * @return {string} Updated URI.
    1046 */
    1047goog.uri.utils.setPath = function(uri, path) {
    1048 // Add any missing '/'.
    1049 if (!goog.string.startsWith(path, '/')) {
    1050 path = '/' + path;
    1051 }
    1052 var parts = goog.uri.utils.split(uri);
    1053 return goog.uri.utils.buildFromEncodedParts(
    1054 parts[goog.uri.utils.ComponentIndex.SCHEME],
    1055 parts[goog.uri.utils.ComponentIndex.USER_INFO],
    1056 parts[goog.uri.utils.ComponentIndex.DOMAIN],
    1057 parts[goog.uri.utils.ComponentIndex.PORT],
    1058 path,
    1059 parts[goog.uri.utils.ComponentIndex.QUERY_DATA],
    1060 parts[goog.uri.utils.ComponentIndex.FRAGMENT]);
    1061};
    1062
    1063
    1064/**
    1065 * Standard supported query parameters.
    1066 * @enum {string}
    1067 */
    1068goog.uri.utils.StandardQueryParam = {
    1069
    1070 /** Unused parameter for unique-ifying. */
    1071 RANDOM: 'zx'
    1072};
    1073
    1074
    1075/**
    1076 * Sets the zx parameter of a URI to a random value.
    1077 * @param {string} uri Any URI.
    1078 * @return {string} That URI with the "zx" parameter added or replaced to
    1079 * contain a random string.
    1080 */
    1081goog.uri.utils.makeUnique = function(uri) {
    1082 return goog.uri.utils.setParam(uri,
    1083 goog.uri.utils.StandardQueryParam.RANDOM, goog.string.getRandomString());
    1084};
    \ 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');
    55goog.require('goog.userAgent');
    56
    57
    58/**
    59 * Character codes inlined to avoid object allocations due to charCode.
    60 * @enum {number}
    61 * @private
    62 */
    63goog.uri.utils.CharCode_ = {
    64 AMPERSAND: 38,
    65 EQUAL: 61,
    66 HASH: 35,
    67 QUESTION: 63
    68};
    69
    70
    71/**
    72 * Builds a URI string from already-encoded parts.
    73 *
    74 * No encoding is performed. Any component may be omitted as either null or
    75 * undefined.
    76 *
    77 * @param {?string=} opt_scheme The scheme such as 'http'.
    78 * @param {?string=} opt_userInfo The user name before the '@'.
    79 * @param {?string=} opt_domain The domain such as 'www.google.com', already
    80 * URI-encoded.
    81 * @param {(string|number|null)=} opt_port The port number.
    82 * @param {?string=} opt_path The path, already URI-encoded. If it is not
    83 * empty, it must begin with a slash.
    84 * @param {?string=} opt_queryData The URI-encoded query data.
    85 * @param {?string=} opt_fragment The URI-encoded fragment identifier.
    86 * @return {string} The fully combined URI.
    87 */
    88goog.uri.utils.buildFromEncodedParts = function(opt_scheme, opt_userInfo,
    89 opt_domain, opt_port, opt_path, opt_queryData, opt_fragment) {
    90 var out = '';
    91
    92 if (opt_scheme) {
    93 out += opt_scheme + ':';
    94 }
    95
    96 if (opt_domain) {
    97 out += '//';
    98
    99 if (opt_userInfo) {
    100 out += opt_userInfo + '@';
    101 }
    102
    103 out += opt_domain;
    104
    105 if (opt_port) {
    106 out += ':' + opt_port;
    107 }
    108 }
    109
    110 if (opt_path) {
    111 out += opt_path;
    112 }
    113
    114 if (opt_queryData) {
    115 out += '?' + opt_queryData;
    116 }
    117
    118 if (opt_fragment) {
    119 out += '#' + opt_fragment;
    120 }
    121
    122 return out;
    123};
    124
    125
    126/**
    127 * A regular expression for breaking a URI into its component parts.
    128 *
    129 * {@link http://www.ietf.org/rfc/rfc3986.txt} says in Appendix B
    130 * As the "first-match-wins" algorithm is identical to the "greedy"
    131 * disambiguation method used by POSIX regular expressions, it is natural and
    132 * commonplace to use a regular expression for parsing the potential five
    133 * components of a URI reference.
    134 *
    135 * The following line is the regular expression for breaking-down a
    136 * well-formed URI reference into its components.
    137 *
    138 * <pre>
    139 * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
    140 * 12 3 4 5 6 7 8 9
    141 * </pre>
    142 *
    143 * The numbers in the second line above are only to assist readability; they
    144 * indicate the reference points for each subexpression (i.e., each paired
    145 * parenthesis). We refer to the value matched for subexpression <n> as $<n>.
    146 * For example, matching the above expression to
    147 * <pre>
    148 * http://www.ics.uci.edu/pub/ietf/uri/#Related
    149 * </pre>
    150 * results in the following subexpression matches:
    151 * <pre>
    152 * $1 = http:
    153 * $2 = http
    154 * $3 = //www.ics.uci.edu
    155 * $4 = www.ics.uci.edu
    156 * $5 = /pub/ietf/uri/
    157 * $6 = <undefined>
    158 * $7 = <undefined>
    159 * $8 = #Related
    160 * $9 = Related
    161 * </pre>
    162 * where <undefined> indicates that the component is not present, as is the
    163 * case for the query component in the above example. Therefore, we can
    164 * determine the value of the five components as
    165 * <pre>
    166 * scheme = $2
    167 * authority = $4
    168 * path = $5
    169 * query = $7
    170 * fragment = $9
    171 * </pre>
    172 *
    173 * The regular expression has been modified slightly to expose the
    174 * userInfo, domain, and port separately from the authority.
    175 * The modified version yields
    176 * <pre>
    177 * $1 = http scheme
    178 * $2 = <undefined> userInfo -\
    179 * $3 = www.ics.uci.edu domain | authority
    180 * $4 = <undefined> port -/
    181 * $5 = /pub/ietf/uri/ path
    182 * $6 = <undefined> query without ?
    183 * $7 = Related fragment without #
    184 * </pre>
    185 * @type {!RegExp}
    186 * @private
    187 */
    188goog.uri.utils.splitRe_ = new RegExp(
    189 '^' +
    190 '(?:' +
    191 '([^:/?#.]+)' + // scheme - ignore special characters
    192 // used by other URL parts such as :,
    193 // ?, /, #, and .
    194 ':)?' +
    195 '(?://' +
    196 '(?:([^/?#]*)@)?' + // userInfo
    197 '([^/#?]*?)' + // domain
    198 '(?::([0-9]+))?' + // port
    199 '(?=[/#?]|$)' + // authority-terminating character
    200 ')?' +
    201 '([^?#]+)?' + // path
    202 '(?:\\?([^#]*))?' + // query
    203 '(?:#(.*))?' + // fragment
    204 '$');
    205
    206
    207/**
    208 * The index of each URI component in the return value of goog.uri.utils.split.
    209 * @enum {number}
    210 */
    211goog.uri.utils.ComponentIndex = {
    212 SCHEME: 1,
    213 USER_INFO: 2,
    214 DOMAIN: 3,
    215 PORT: 4,
    216 PATH: 5,
    217 QUERY_DATA: 6,
    218 FRAGMENT: 7
    219};
    220
    221
    222/**
    223 * Splits a URI into its component parts.
    224 *
    225 * Each component can be accessed via the component indices; for example:
    226 * <pre>
    227 * goog.uri.utils.split(someStr)[goog.uri.utils.CompontentIndex.QUERY_DATA];
    228 * </pre>
    229 *
    230 * @param {string} uri The URI string to examine.
    231 * @return {!Array<string|undefined>} Each component still URI-encoded.
    232 * Each component that is present will contain the encoded value, whereas
    233 * components that are not present will be undefined or empty, depending
    234 * on the browser's regular expression implementation. Never null, since
    235 * arbitrary strings may still look like path names.
    236 */
    237goog.uri.utils.split = function(uri) {
    238 goog.uri.utils.phishingProtection_();
    239
    240 // See @return comment -- never null.
    241 return /** @type {!Array<string|undefined>} */ (
    242 uri.match(goog.uri.utils.splitRe_));
    243};
    244
    245
    246/**
    247 * Safari has a nasty bug where if you have an http URL with a username, e.g.,
    248 * http://evil.com%2F@google.com/
    249 * Safari will report that window.location.href is
    250 * http://evil.com/google.com/
    251 * so that anyone who tries to parse the domain of that URL will get
    252 * the wrong domain. We've seen exploits where people use this to trick
    253 * Safari into loading resources from evil domains.
    254 *
    255 * To work around this, we run a little "Safari phishing check", and throw
    256 * an exception if we see this happening.
    257 *
    258 * There is no convenient place to put this check. We apply it to
    259 * anyone doing URI parsing on Webkit. We're not happy about this, but
    260 * it fixes the problem.
    261 *
    262 * This should be removed once Safari fixes their bug.
    263 *
    264 * Exploit reported by Masato Kinugawa.
    265 *
    266 * @type {boolean}
    267 * @private
    268 */
    269goog.uri.utils.needsPhishingProtection_ = goog.userAgent.WEBKIT;
    270
    271
    272/**
    273 * Check to see if the user is being phished.
    274 * @private
    275 */
    276goog.uri.utils.phishingProtection_ = function() {
    277 if (goog.uri.utils.needsPhishingProtection_) {
    278 // Turn protection off, so that we don't recurse.
    279 goog.uri.utils.needsPhishingProtection_ = false;
    280
    281 // Use quoted access, just in case the user isn't using location externs.
    282 var location = goog.global['location'];
    283 if (location) {
    284 var href = location['href'];
    285 if (href) {
    286 var domain = goog.uri.utils.getDomain(href);
    287 if (domain && domain != location['hostname']) {
    288 // Phishing attack
    289 goog.uri.utils.needsPhishingProtection_ = true;
    290 throw Error();
    291 }
    292 }
    293 }
    294 }
    295};
    296
    297
    298/**
    299 * @param {?string} uri A possibly null string.
    300 * @param {boolean=} opt_preserveReserved If true, percent-encoding of RFC-3986
    301 * reserved characters will not be removed.
    302 * @return {?string} The string URI-decoded, or null if uri is null.
    303 * @private
    304 */
    305goog.uri.utils.decodeIfPossible_ = function(uri, opt_preserveReserved) {
    306 if (!uri) {
    307 return uri;
    308 }
    309
    310 return opt_preserveReserved ? decodeURI(uri) : decodeURIComponent(uri);
    311};
    312
    313
    314/**
    315 * Gets a URI component by index.
    316 *
    317 * It is preferred to use the getPathEncoded() variety of functions ahead,
    318 * since they are more readable.
    319 *
    320 * @param {goog.uri.utils.ComponentIndex} componentIndex The component index.
    321 * @param {string} uri The URI to examine.
    322 * @return {?string} The still-encoded component, or null if the component
    323 * is not present.
    324 * @private
    325 */
    326goog.uri.utils.getComponentByIndex_ = function(componentIndex, uri) {
    327 // Convert undefined, null, and empty string into null.
    328 return goog.uri.utils.split(uri)[componentIndex] || null;
    329};
    330
    331
    332/**
    333 * @param {string} uri The URI to examine.
    334 * @return {?string} The protocol or scheme, or null if none. Does not
    335 * include trailing colons or slashes.
    336 */
    337goog.uri.utils.getScheme = function(uri) {
    338 return goog.uri.utils.getComponentByIndex_(
    339 goog.uri.utils.ComponentIndex.SCHEME, uri);
    340};
    341
    342
    343/**
    344 * Gets the effective scheme for the URL. If the URL is relative then the
    345 * scheme is derived from the page's location.
    346 * @param {string} uri The URI to examine.
    347 * @return {string} The protocol or scheme, always lower case.
    348 */
    349goog.uri.utils.getEffectiveScheme = function(uri) {
    350 var scheme = goog.uri.utils.getScheme(uri);
    351 if (!scheme && self.location) {
    352 var protocol = self.location.protocol;
    353 scheme = protocol.substr(0, protocol.length - 1);
    354 }
    355 // NOTE: When called from a web worker in Firefox 3.5, location maybe null.
    356 // All other browsers with web workers support self.location from the worker.
    357 return scheme ? scheme.toLowerCase() : '';
    358};
    359
    360
    361/**
    362 * @param {string} uri The URI to examine.
    363 * @return {?string} The user name still encoded, or null if none.
    364 */
    365goog.uri.utils.getUserInfoEncoded = function(uri) {
    366 return goog.uri.utils.getComponentByIndex_(
    367 goog.uri.utils.ComponentIndex.USER_INFO, uri);
    368};
    369
    370
    371/**
    372 * @param {string} uri The URI to examine.
    373 * @return {?string} The decoded user info, or null if none.
    374 */
    375goog.uri.utils.getUserInfo = function(uri) {
    376 return goog.uri.utils.decodeIfPossible_(
    377 goog.uri.utils.getUserInfoEncoded(uri));
    378};
    379
    380
    381/**
    382 * @param {string} uri The URI to examine.
    383 * @return {?string} The domain name still encoded, or null if none.
    384 */
    385goog.uri.utils.getDomainEncoded = function(uri) {
    386 return goog.uri.utils.getComponentByIndex_(
    387 goog.uri.utils.ComponentIndex.DOMAIN, uri);
    388};
    389
    390
    391/**
    392 * @param {string} uri The URI to examine.
    393 * @return {?string} The decoded domain, or null if none.
    394 */
    395goog.uri.utils.getDomain = function(uri) {
    396 return goog.uri.utils.decodeIfPossible_(
    397 goog.uri.utils.getDomainEncoded(uri), true /* opt_preserveReserved */);
    398};
    399
    400
    401/**
    402 * @param {string} uri The URI to examine.
    403 * @return {?number} The port number, or null if none.
    404 */
    405goog.uri.utils.getPort = function(uri) {
    406 // Coerce to a number. If the result of getComponentByIndex_ is null or
    407 // non-numeric, the number coersion yields NaN. This will then return
    408 // null for all non-numeric cases (though also zero, which isn't a relevant
    409 // port number).
    410 return Number(goog.uri.utils.getComponentByIndex_(
    411 goog.uri.utils.ComponentIndex.PORT, uri)) || null;
    412};
    413
    414
    415/**
    416 * @param {string} uri The URI to examine.
    417 * @return {?string} The path still encoded, or null if none. Includes the
    418 * leading slash, if any.
    419 */
    420goog.uri.utils.getPathEncoded = function(uri) {
    421 return goog.uri.utils.getComponentByIndex_(
    422 goog.uri.utils.ComponentIndex.PATH, uri);
    423};
    424
    425
    426/**
    427 * @param {string} uri The URI to examine.
    428 * @return {?string} The decoded path, or null if none. Includes the leading
    429 * slash, if any.
    430 */
    431goog.uri.utils.getPath = function(uri) {
    432 return goog.uri.utils.decodeIfPossible_(
    433 goog.uri.utils.getPathEncoded(uri), true /* opt_preserveReserved */);
    434};
    435
    436
    437/**
    438 * @param {string} uri The URI to examine.
    439 * @return {?string} The query data still encoded, or null if none. Does not
    440 * include the question mark itself.
    441 */
    442goog.uri.utils.getQueryData = function(uri) {
    443 return goog.uri.utils.getComponentByIndex_(
    444 goog.uri.utils.ComponentIndex.QUERY_DATA, uri);
    445};
    446
    447
    448/**
    449 * @param {string} uri The URI to examine.
    450 * @return {?string} The fragment identifier, or null if none. Does not
    451 * include the hash mark itself.
    452 */
    453goog.uri.utils.getFragmentEncoded = function(uri) {
    454 // The hash mark may not appear in any other part of the URL.
    455 var hashIndex = uri.indexOf('#');
    456 return hashIndex < 0 ? null : uri.substr(hashIndex + 1);
    457};
    458
    459
    460/**
    461 * @param {string} uri The URI to examine.
    462 * @param {?string} fragment The encoded fragment identifier, or null if none.
    463 * Does not include the hash mark itself.
    464 * @return {string} The URI with the fragment set.
    465 */
    466goog.uri.utils.setFragmentEncoded = function(uri, fragment) {
    467 return goog.uri.utils.removeFragment(uri) + (fragment ? '#' + fragment : '');
    468};
    469
    470
    471/**
    472 * @param {string} uri The URI to examine.
    473 * @return {?string} The decoded fragment identifier, or null if none. Does
    474 * not include the hash mark.
    475 */
    476goog.uri.utils.getFragment = function(uri) {
    477 return goog.uri.utils.decodeIfPossible_(
    478 goog.uri.utils.getFragmentEncoded(uri));
    479};
    480
    481
    482/**
    483 * Extracts everything up to the port of the URI.
    484 * @param {string} uri The URI string.
    485 * @return {string} Everything up to and including the port.
    486 */
    487goog.uri.utils.getHost = function(uri) {
    488 var pieces = goog.uri.utils.split(uri);
    489 return goog.uri.utils.buildFromEncodedParts(
    490 pieces[goog.uri.utils.ComponentIndex.SCHEME],
    491 pieces[goog.uri.utils.ComponentIndex.USER_INFO],
    492 pieces[goog.uri.utils.ComponentIndex.DOMAIN],
    493 pieces[goog.uri.utils.ComponentIndex.PORT]);
    494};
    495
    496
    497/**
    498 * Extracts the path of the URL and everything after.
    499 * @param {string} uri The URI string.
    500 * @return {string} The URI, starting at the path and including the query
    501 * parameters and fragment identifier.
    502 */
    503goog.uri.utils.getPathAndAfter = function(uri) {
    504 var pieces = goog.uri.utils.split(uri);
    505 return goog.uri.utils.buildFromEncodedParts(null, null, null, null,
    506 pieces[goog.uri.utils.ComponentIndex.PATH],
    507 pieces[goog.uri.utils.ComponentIndex.QUERY_DATA],
    508 pieces[goog.uri.utils.ComponentIndex.FRAGMENT]);
    509};
    510
    511
    512/**
    513 * Gets the URI with the fragment identifier removed.
    514 * @param {string} uri The URI to examine.
    515 * @return {string} Everything preceding the hash mark.
    516 */
    517goog.uri.utils.removeFragment = function(uri) {
    518 // The hash mark may not appear in any other part of the URL.
    519 var hashIndex = uri.indexOf('#');
    520 return hashIndex < 0 ? uri : uri.substr(0, hashIndex);
    521};
    522
    523
    524/**
    525 * Ensures that two URI's have the exact same domain, scheme, and port.
    526 *
    527 * Unlike the version in goog.Uri, this checks protocol, and therefore is
    528 * suitable for checking against the browser's same-origin policy.
    529 *
    530 * @param {string} uri1 The first URI.
    531 * @param {string} uri2 The second URI.
    532 * @return {boolean} Whether they have the same scheme, domain and port.
    533 */
    534goog.uri.utils.haveSameDomain = function(uri1, uri2) {
    535 var pieces1 = goog.uri.utils.split(uri1);
    536 var pieces2 = goog.uri.utils.split(uri2);
    537 return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==
    538 pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&
    539 pieces1[goog.uri.utils.ComponentIndex.SCHEME] ==
    540 pieces2[goog.uri.utils.ComponentIndex.SCHEME] &&
    541 pieces1[goog.uri.utils.ComponentIndex.PORT] ==
    542 pieces2[goog.uri.utils.ComponentIndex.PORT];
    543};
    544
    545
    546/**
    547 * Asserts that there are no fragment or query identifiers, only in uncompiled
    548 * mode.
    549 * @param {string} uri The URI to examine.
    550 * @private
    551 */
    552goog.uri.utils.assertNoFragmentsOrQueries_ = function(uri) {
    553 // NOTE: would use goog.asserts here, but jscompiler doesn't know that
    554 // indexOf has no side effects.
    555 if (goog.DEBUG && (uri.indexOf('#') >= 0 || uri.indexOf('?') >= 0)) {
    556 throw Error('goog.uri.utils: Fragment or query identifiers are not ' +
    557 'supported: [' + uri + ']');
    558 }
    559};
    560
    561
    562/**
    563 * Supported query parameter values by the parameter serializing utilities.
    564 *
    565 * If a value is null or undefined, the key-value pair is skipped, as an easy
    566 * way to omit parameters conditionally. Non-array parameters are converted
    567 * to a string and URI encoded. Array values are expanded into multiple
    568 * &key=value pairs, with each element stringized and URI-encoded.
    569 *
    570 * @typedef {*}
    571 */
    572goog.uri.utils.QueryValue;
    573
    574
    575/**
    576 * An array representing a set of query parameters with alternating keys
    577 * and values.
    578 *
    579 * Keys are assumed to be URI encoded already and live at even indices. See
    580 * goog.uri.utils.QueryValue for details on how parameter values are encoded.
    581 *
    582 * Example:
    583 * <pre>
    584 * var data = [
    585 * // Simple param: ?name=BobBarker
    586 * 'name', 'BobBarker',
    587 * // Conditional param -- may be omitted entirely.
    588 * 'specialDietaryNeeds', hasDietaryNeeds() ? getDietaryNeeds() : null,
    589 * // Multi-valued param: &house=LosAngeles&house=NewYork&house=null
    590 * 'house', ['LosAngeles', 'NewYork', null]
    591 * ];
    592 * </pre>
    593 *
    594 * @typedef {!Array<string|goog.uri.utils.QueryValue>}
    595 */
    596goog.uri.utils.QueryArray;
    597
    598
    599/**
    600 * Parses encoded query parameters and calls callback function for every
    601 * parameter found in the string.
    602 *
    603 * Missing value of parameter (e.g. “…&key&…”) is treated as if the value was an
    604 * empty string. Keys may be empty strings (e.g. “…&=value&…”) which also means
    605 * that “…&=&…” and “…&&…” will result in an empty key and value.
    606 *
    607 * @param {string} encodedQuery Encoded query string excluding question mark at
    608 * the beginning.
    609 * @param {function(string, string)} callback Function called for every
    610 * parameter found in query string. The first argument (name) will not be
    611 * urldecoded (so the function is consistent with buildQueryData), but the
    612 * second will. If the parameter has no value (i.e. “=” was not present)
    613 * the second argument (value) will be an empty string.
    614 */
    615goog.uri.utils.parseQueryData = function(encodedQuery, callback) {
    616 var pairs = encodedQuery.split('&');
    617 for (var i = 0; i < pairs.length; i++) {
    618 var indexOfEquals = pairs[i].indexOf('=');
    619 var name = null;
    620 var value = null;
    621 if (indexOfEquals >= 0) {
    622 name = pairs[i].substring(0, indexOfEquals);
    623 value = pairs[i].substring(indexOfEquals + 1);
    624 } else {
    625 name = pairs[i];
    626 }
    627 callback(name, value ? goog.string.urlDecode(value) : '');
    628 }
    629};
    630
    631
    632/**
    633 * Appends a URI and query data in a string buffer with special preconditions.
    634 *
    635 * Internal implementation utility, performing very few object allocations.
    636 *
    637 * @param {!Array<string|undefined>} buffer A string buffer. The first element
    638 * must be the base URI, and may have a fragment identifier. If the array
    639 * contains more than one element, the second element must be an ampersand,
    640 * and may be overwritten, depending on the base URI. Undefined elements
    641 * are treated as empty-string.
    642 * @return {string} The concatenated URI and query data.
    643 * @private
    644 */
    645goog.uri.utils.appendQueryData_ = function(buffer) {
    646 if (buffer[1]) {
    647 // At least one query parameter was added. We need to check the
    648 // punctuation mark, which is currently an ampersand, and also make sure
    649 // there aren't any interfering fragment identifiers.
    650 var baseUri = /** @type {string} */ (buffer[0]);
    651 var hashIndex = baseUri.indexOf('#');
    652 if (hashIndex >= 0) {
    653 // Move the fragment off the base part of the URI into the end.
    654 buffer.push(baseUri.substr(hashIndex));
    655 buffer[0] = baseUri = baseUri.substr(0, hashIndex);
    656 }
    657 var questionIndex = baseUri.indexOf('?');
    658 if (questionIndex < 0) {
    659 // No question mark, so we need a question mark instead of an ampersand.
    660 buffer[1] = '?';
    661 } else if (questionIndex == baseUri.length - 1) {
    662 // Question mark is the very last character of the existing URI, so don't
    663 // append an additional delimiter.
    664 buffer[1] = undefined;
    665 }
    666 }
    667
    668 return buffer.join('');
    669};
    670
    671
    672/**
    673 * Appends key=value pairs to an array, supporting multi-valued objects.
    674 * @param {string} key The key prefix.
    675 * @param {goog.uri.utils.QueryValue} value The value to serialize.
    676 * @param {!Array<string>} pairs The array to which the 'key=value' strings
    677 * should be appended.
    678 * @private
    679 */
    680goog.uri.utils.appendKeyValuePairs_ = function(key, value, pairs) {
    681 if (goog.isArray(value)) {
    682 // Convince the compiler it's an array.
    683 goog.asserts.assertArray(value);
    684 for (var j = 0; j < value.length; j++) {
    685 // Convert to string explicitly, to short circuit the null and array
    686 // logic in this function -- this ensures that null and undefined get
    687 // written as literal 'null' and 'undefined', and arrays don't get
    688 // expanded out but instead encoded in the default way.
    689 goog.uri.utils.appendKeyValuePairs_(key, String(value[j]), pairs);
    690 }
    691 } else if (value != null) {
    692 // Skip a top-level null or undefined entirely.
    693 pairs.push('&', key,
    694 // Check for empty string. Zero gets encoded into the url as literal
    695 // strings. For empty string, skip the equal sign, to be consistent
    696 // with UriBuilder.java.
    697 value === '' ? '' : '=',
    698 goog.string.urlEncode(value));
    699 }
    700};
    701
    702
    703/**
    704 * Builds a buffer of query data from a sequence of alternating keys and values.
    705 *
    706 * @param {!Array<string|undefined>} buffer A string buffer to append to. The
    707 * first element appended will be an '&', and may be replaced by the caller.
    708 * @param {!goog.uri.utils.QueryArray|!Arguments} keysAndValues An array with
    709 * alternating keys and values -- see the typedef.
    710 * @param {number=} opt_startIndex A start offset into the arary, defaults to 0.
    711 * @return {!Array<string|undefined>} The buffer argument.
    712 * @private
    713 */
    714goog.uri.utils.buildQueryDataBuffer_ = function(
    715 buffer, keysAndValues, opt_startIndex) {
    716 goog.asserts.assert(Math.max(keysAndValues.length - (opt_startIndex || 0),
    717 0) % 2 == 0, 'goog.uri.utils: Key/value lists must be even in length.');
    718
    719 for (var i = opt_startIndex || 0; i < keysAndValues.length; i += 2) {
    720 goog.uri.utils.appendKeyValuePairs_(
    721 keysAndValues[i], keysAndValues[i + 1], buffer);
    722 }
    723
    724 return buffer;
    725};
    726
    727
    728/**
    729 * Builds a query data string from a sequence of alternating keys and values.
    730 * Currently generates "&key&" for empty args.
    731 *
    732 * @param {goog.uri.utils.QueryArray} keysAndValues Alternating keys and
    733 * values. See the typedef.
    734 * @param {number=} opt_startIndex A start offset into the arary, defaults to 0.
    735 * @return {string} The encoded query string, in the form 'a=1&b=2'.
    736 */
    737goog.uri.utils.buildQueryData = function(keysAndValues, opt_startIndex) {
    738 var buffer = goog.uri.utils.buildQueryDataBuffer_(
    739 [], keysAndValues, opt_startIndex);
    740 buffer[0] = ''; // Remove the leading ampersand.
    741 return buffer.join('');
    742};
    743
    744
    745/**
    746 * Builds a buffer of query data from a map.
    747 *
    748 * @param {!Array<string|undefined>} buffer A string buffer to append to. The
    749 * first element appended will be an '&', and may be replaced by the caller.
    750 * @param {!Object<string, goog.uri.utils.QueryValue>} map An object where keys
    751 * are URI-encoded parameter keys, and the values conform to the contract
    752 * specified in the goog.uri.utils.QueryValue typedef.
    753 * @return {!Array<string|undefined>} The buffer argument.
    754 * @private
    755 */
    756goog.uri.utils.buildQueryDataBufferFromMap_ = function(buffer, map) {
    757 for (var key in map) {
    758 goog.uri.utils.appendKeyValuePairs_(key, map[key], buffer);
    759 }
    760
    761 return buffer;
    762};
    763
    764
    765/**
    766 * Builds a query data string from a map.
    767 * Currently generates "&key&" for empty args.
    768 *
    769 * @param {!Object<string, goog.uri.utils.QueryValue>} map An object where keys
    770 * are URI-encoded parameter keys, and the values are arbitrary types
    771 * or arrays. Keys with a null value are dropped.
    772 * @return {string} The encoded query string, in the form 'a=1&b=2'.
    773 */
    774goog.uri.utils.buildQueryDataFromMap = function(map) {
    775 var buffer = goog.uri.utils.buildQueryDataBufferFromMap_([], map);
    776 buffer[0] = '';
    777 return buffer.join('');
    778};
    779
    780
    781/**
    782 * Appends URI parameters to an existing URI.
    783 *
    784 * The variable arguments may contain alternating keys and values. Keys are
    785 * assumed to be already URI encoded. The values should not be URI-encoded,
    786 * and will instead be encoded by this function.
    787 * <pre>
    788 * appendParams('http://www.foo.com?existing=true',
    789 * 'key1', 'value1',
    790 * 'key2', 'value?willBeEncoded',
    791 * 'key3', ['valueA', 'valueB', 'valueC'],
    792 * 'key4', null);
    793 * result: 'http://www.foo.com?existing=true&' +
    794 * 'key1=value1&' +
    795 * 'key2=value%3FwillBeEncoded&' +
    796 * 'key3=valueA&key3=valueB&key3=valueC'
    797 * </pre>
    798 *
    799 * A single call to this function will not exhibit quadratic behavior in IE,
    800 * whereas multiple repeated calls may, although the effect is limited by
    801 * fact that URL's generally can't exceed 2kb.
    802 *
    803 * @param {string} uri The original URI, which may already have query data.
    804 * @param {...(goog.uri.utils.QueryArray|string|goog.uri.utils.QueryValue)} var_args
    805 * An array or argument list conforming to goog.uri.utils.QueryArray.
    806 * @return {string} The URI with all query parameters added.
    807 */
    808goog.uri.utils.appendParams = function(uri, var_args) {
    809 return goog.uri.utils.appendQueryData_(
    810 arguments.length == 2 ?
    811 goog.uri.utils.buildQueryDataBuffer_([uri], arguments[1], 0) :
    812 goog.uri.utils.buildQueryDataBuffer_([uri], arguments, 1));
    813};
    814
    815
    816/**
    817 * Appends query parameters from a map.
    818 *
    819 * @param {string} uri The original URI, which may already have query data.
    820 * @param {!Object<goog.uri.utils.QueryValue>} map An object where keys are
    821 * URI-encoded parameter keys, and the values are arbitrary types or arrays.
    822 * Keys with a null value are dropped.
    823 * @return {string} The new parameters.
    824 */
    825goog.uri.utils.appendParamsFromMap = function(uri, map) {
    826 return goog.uri.utils.appendQueryData_(
    827 goog.uri.utils.buildQueryDataBufferFromMap_([uri], map));
    828};
    829
    830
    831/**
    832 * Appends a single URI parameter.
    833 *
    834 * Repeated calls to this can exhibit quadratic behavior in IE6 due to the
    835 * way string append works, though it should be limited given the 2kb limit.
    836 *
    837 * @param {string} uri The original URI, which may already have query data.
    838 * @param {string} key The key, which must already be URI encoded.
    839 * @param {*=} opt_value The value, which will be stringized and encoded
    840 * (assumed not already to be encoded). If omitted, undefined, or null, the
    841 * key will be added as a valueless parameter.
    842 * @return {string} The URI with the query parameter added.
    843 */
    844goog.uri.utils.appendParam = function(uri, key, opt_value) {
    845 var paramArr = [uri, '&', key];
    846 if (goog.isDefAndNotNull(opt_value)) {
    847 paramArr.push('=', goog.string.urlEncode(opt_value));
    848 }
    849 return goog.uri.utils.appendQueryData_(paramArr);
    850};
    851
    852
    853/**
    854 * Finds the next instance of a query parameter with the specified name.
    855 *
    856 * Does not instantiate any objects.
    857 *
    858 * @param {string} uri The URI to search. May contain a fragment identifier
    859 * if opt_hashIndex is specified.
    860 * @param {number} startIndex The index to begin searching for the key at. A
    861 * match may be found even if this is one character after the ampersand.
    862 * @param {string} keyEncoded The URI-encoded key.
    863 * @param {number} hashOrEndIndex Index to stop looking at. If a hash
    864 * mark is present, it should be its index, otherwise it should be the
    865 * length of the string.
    866 * @return {number} The position of the first character in the key's name,
    867 * immediately after either a question mark or a dot.
    868 * @private
    869 */
    870goog.uri.utils.findParam_ = function(
    871 uri, startIndex, keyEncoded, hashOrEndIndex) {
    872 var index = startIndex;
    873 var keyLength = keyEncoded.length;
    874
    875 // Search for the key itself and post-filter for surronuding punctuation,
    876 // rather than expensively building a regexp.
    877 while ((index = uri.indexOf(keyEncoded, index)) >= 0 &&
    878 index < hashOrEndIndex) {
    879 var precedingChar = uri.charCodeAt(index - 1);
    880 // Ensure that the preceding character is '&' or '?'.
    881 if (precedingChar == goog.uri.utils.CharCode_.AMPERSAND ||
    882 precedingChar == goog.uri.utils.CharCode_.QUESTION) {
    883 // Ensure the following character is '&', '=', '#', or NaN
    884 // (end of string).
    885 var followingChar = uri.charCodeAt(index + keyLength);
    886 if (!followingChar ||
    887 followingChar == goog.uri.utils.CharCode_.EQUAL ||
    888 followingChar == goog.uri.utils.CharCode_.AMPERSAND ||
    889 followingChar == goog.uri.utils.CharCode_.HASH) {
    890 return index;
    891 }
    892 }
    893 index += keyLength + 1;
    894 }
    895
    896 return -1;
    897};
    898
    899
    900/**
    901 * Regular expression for finding a hash mark or end of string.
    902 * @type {RegExp}
    903 * @private
    904 */
    905goog.uri.utils.hashOrEndRe_ = /#|$/;
    906
    907
    908/**
    909 * Determines if the URI contains a specific key.
    910 *
    911 * Performs no object instantiations.
    912 *
    913 * @param {string} uri The URI to process. May contain a fragment
    914 * identifier.
    915 * @param {string} keyEncoded The URI-encoded key. Case-sensitive.
    916 * @return {boolean} Whether the key is present.
    917 */
    918goog.uri.utils.hasParam = function(uri, keyEncoded) {
    919 return goog.uri.utils.findParam_(uri, 0, keyEncoded,
    920 uri.search(goog.uri.utils.hashOrEndRe_)) >= 0;
    921};
    922
    923
    924/**
    925 * Gets the first value of a query parameter.
    926 * @param {string} uri The URI to process. May contain a fragment.
    927 * @param {string} keyEncoded The URI-encoded key. Case-sensitive.
    928 * @return {?string} The first value of the parameter (URI-decoded), or null
    929 * if the parameter is not found.
    930 */
    931goog.uri.utils.getParamValue = function(uri, keyEncoded) {
    932 var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
    933 var foundIndex = goog.uri.utils.findParam_(
    934 uri, 0, keyEncoded, hashOrEndIndex);
    935
    936 if (foundIndex < 0) {
    937 return null;
    938 } else {
    939 var endPosition = uri.indexOf('&', foundIndex);
    940 if (endPosition < 0 || endPosition > hashOrEndIndex) {
    941 endPosition = hashOrEndIndex;
    942 }
    943 // Progress forth to the end of the "key=" or "key&" substring.
    944 foundIndex += keyEncoded.length + 1;
    945 // Use substr, because it (unlike substring) will return empty string
    946 // if foundIndex > endPosition.
    947 return goog.string.urlDecode(
    948 uri.substr(foundIndex, endPosition - foundIndex));
    949 }
    950};
    951
    952
    953/**
    954 * Gets all values of a query parameter.
    955 * @param {string} uri The URI to process. May contain a fragment.
    956 * @param {string} keyEncoded The URI-encoded key. Case-sensitive.
    957 * @return {!Array<string>} All URI-decoded values with the given key.
    958 * If the key is not found, this will have length 0, but never be null.
    959 */
    960goog.uri.utils.getParamValues = function(uri, keyEncoded) {
    961 var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
    962 var position = 0;
    963 var foundIndex;
    964 var result = [];
    965
    966 while ((foundIndex = goog.uri.utils.findParam_(
    967 uri, position, keyEncoded, hashOrEndIndex)) >= 0) {
    968 // Find where this parameter ends, either the '&' or the end of the
    969 // query parameters.
    970 position = uri.indexOf('&', foundIndex);
    971 if (position < 0 || position > hashOrEndIndex) {
    972 position = hashOrEndIndex;
    973 }
    974
    975 // Progress forth to the end of the "key=" or "key&" substring.
    976 foundIndex += keyEncoded.length + 1;
    977 // Use substr, because it (unlike substring) will return empty string
    978 // if foundIndex > position.
    979 result.push(goog.string.urlDecode(uri.substr(
    980 foundIndex, position - foundIndex)));
    981 }
    982
    983 return result;
    984};
    985
    986
    987/**
    988 * Regexp to find trailing question marks and ampersands.
    989 * @type {RegExp}
    990 * @private
    991 */
    992goog.uri.utils.trailingQueryPunctuationRe_ = /[?&]($|#)/;
    993
    994
    995/**
    996 * Removes all instances of a query parameter.
    997 * @param {string} uri The URI to process. Must not contain a fragment.
    998 * @param {string} keyEncoded The URI-encoded key.
    999 * @return {string} The URI with all instances of the parameter removed.
    1000 */
    1001goog.uri.utils.removeParam = function(uri, keyEncoded) {
    1002 var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
    1003 var position = 0;
    1004 var foundIndex;
    1005 var buffer = [];
    1006
    1007 // Look for a query parameter.
    1008 while ((foundIndex = goog.uri.utils.findParam_(
    1009 uri, position, keyEncoded, hashOrEndIndex)) >= 0) {
    1010 // Get the portion of the query string up to, but not including, the ?
    1011 // or & starting the parameter.
    1012 buffer.push(uri.substring(position, foundIndex));
    1013 // Progress to immediately after the '&'. If not found, go to the end.
    1014 // Avoid including the hash mark.
    1015 position = Math.min((uri.indexOf('&', foundIndex) + 1) || hashOrEndIndex,
    1016 hashOrEndIndex);
    1017 }
    1018
    1019 // Append everything that is remaining.
    1020 buffer.push(uri.substr(position));
    1021
    1022 // Join the buffer, and remove trailing punctuation that remains.
    1023 return buffer.join('').replace(
    1024 goog.uri.utils.trailingQueryPunctuationRe_, '$1');
    1025};
    1026
    1027
    1028/**
    1029 * Replaces all existing definitions of a parameter with a single definition.
    1030 *
    1031 * Repeated calls to this can exhibit quadratic behavior due to the need to
    1032 * find existing instances and reconstruct the string, though it should be
    1033 * limited given the 2kb limit. Consider using appendParams to append multiple
    1034 * parameters in bulk.
    1035 *
    1036 * @param {string} uri The original URI, which may already have query data.
    1037 * @param {string} keyEncoded The key, which must already be URI encoded.
    1038 * @param {*} value The value, which will be stringized and encoded (assumed
    1039 * not already to be encoded).
    1040 * @return {string} The URI with the query parameter added.
    1041 */
    1042goog.uri.utils.setParam = function(uri, keyEncoded, value) {
    1043 return goog.uri.utils.appendParam(
    1044 goog.uri.utils.removeParam(uri, keyEncoded), keyEncoded, value);
    1045};
    1046
    1047
    1048/**
    1049 * Generates a URI path using a given URI and a path with checks to
    1050 * prevent consecutive "//". The baseUri passed in must not contain
    1051 * query or fragment identifiers. The path to append may not contain query or
    1052 * fragment identifiers.
    1053 *
    1054 * @param {string} baseUri URI to use as the base.
    1055 * @param {string} path Path to append.
    1056 * @return {string} Updated URI.
    1057 */
    1058goog.uri.utils.appendPath = function(baseUri, path) {
    1059 goog.uri.utils.assertNoFragmentsOrQueries_(baseUri);
    1060
    1061 // Remove any trailing '/'
    1062 if (goog.string.endsWith(baseUri, '/')) {
    1063 baseUri = baseUri.substr(0, baseUri.length - 1);
    1064 }
    1065 // Remove any leading '/'
    1066 if (goog.string.startsWith(path, '/')) {
    1067 path = path.substr(1);
    1068 }
    1069 return goog.string.buildString(baseUri, '/', path);
    1070};
    1071
    1072
    1073/**
    1074 * Replaces the path.
    1075 * @param {string} uri URI to use as the base.
    1076 * @param {string} path New path.
    1077 * @return {string} Updated URI.
    1078 */
    1079goog.uri.utils.setPath = function(uri, path) {
    1080 // Add any missing '/'.
    1081 if (!goog.string.startsWith(path, '/')) {
    1082 path = '/' + path;
    1083 }
    1084 var parts = goog.uri.utils.split(uri);
    1085 return goog.uri.utils.buildFromEncodedParts(
    1086 parts[goog.uri.utils.ComponentIndex.SCHEME],
    1087 parts[goog.uri.utils.ComponentIndex.USER_INFO],
    1088 parts[goog.uri.utils.ComponentIndex.DOMAIN],
    1089 parts[goog.uri.utils.ComponentIndex.PORT],
    1090 path,
    1091 parts[goog.uri.utils.ComponentIndex.QUERY_DATA],
    1092 parts[goog.uri.utils.ComponentIndex.FRAGMENT]);
    1093};
    1094
    1095
    1096/**
    1097 * Standard supported query parameters.
    1098 * @enum {string}
    1099 */
    1100goog.uri.utils.StandardQueryParam = {
    1101
    1102 /** Unused parameter for unique-ifying. */
    1103 RANDOM: 'zx'
    1104};
    1105
    1106
    1107/**
    1108 * Sets the zx parameter of a URI to a random value.
    1109 * @param {string} uri Any URI.
    1110 * @return {string} That URI with the "zx" parameter added or replaced to
    1111 * contain a random string.
    1112 */
    1113goog.uri.utils.makeUnique = function(uri) {
    1114 return goog.uri.utils.setParam(uri,
    1115 goog.uri.utils.StandardQueryParam.RANDOM, goog.string.getRandomString());
    1116};
    \ 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 67b3fd6..b7b12f0 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('Chrome') != -1) {
    166 goog.userAgent.product.detectedChrome_ = true;
    167 } else if (ua.indexOf('Android') != -1) {
    168 goog.userAgent.product.detectedAndroid_ = true;
    169 } else if (ua.indexOf('Safari') != -1) {
    170 goog.userAgent.product.detectedSafari_ = true;
    171 }
    172};
    173
    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_OPERA ||
    75 goog.userAgent.product.ASSUME_FIREFOX ||
    76 goog.userAgent.product.ASSUME_IPHONE ||
    77 goog.userAgent.product.ASSUME_IPAD ||
    78 goog.userAgent.product.ASSUME_ANDROID ||
    79 goog.userAgent.product.ASSUME_CHROME ||
    80 goog.userAgent.product.ASSUME_SAFARI;
    81
    82
    83/**
    84 * Whether the code is running on the Opera web browser.
    85 * @type {boolean}
    86 */
    87goog.userAgent.product.OPERA = goog.userAgent.OPERA;
    88
    89
    90/**
    91 * Whether the code is running on an IE web browser.
    92 * @type {boolean}
    93 */
    94goog.userAgent.product.IE = goog.userAgent.IE;
    95
    96
    97/**
    98 * Whether the code is running on the Firefox web browser.
    99 * @type {boolean}
    100 */
    101goog.userAgent.product.FIREFOX = goog.userAgent.product.PRODUCT_KNOWN_ ?
    102 goog.userAgent.product.ASSUME_FIREFOX :
    103 goog.labs.userAgent.browser.isFirefox();
    104
    105
    106/**
    107 * Whether the user agent is an iPhone or iPod (as in iPod touch).
    108 * @return {boolean}
    109 * @private
    110 */
    111goog.userAgent.product.isIphoneOrIpod_ = function() {
    112 return goog.labs.userAgent.platform.isIphone() ||
    113 goog.labs.userAgent.platform.isIpod();
    114};
    115
    116
    117/**
    118 * Whether the code is running on an iPhone or iPod touch.
    119 *
    120 * iPod touch is considered an iPhone for legacy reasons.
    121 * @type {boolean}
    122 */
    123goog.userAgent.product.IPHONE = goog.userAgent.product.PRODUCT_KNOWN_ ?
    124 goog.userAgent.product.ASSUME_IPHONE :
    125 goog.userAgent.product.isIphoneOrIpod_();
    126
    127
    128/**
    129 * Whether the code is running on an iPad.
    130 * @type {boolean}
    131 */
    132goog.userAgent.product.IPAD = goog.userAgent.product.PRODUCT_KNOWN_ ?
    133 goog.userAgent.product.ASSUME_IPAD :
    134 goog.labs.userAgent.platform.isIpad();
    135
    136
    137/**
    138 * Whether the code is running on AOSP browser or WebView inside
    139 * a pre KitKat Android phone or tablet.
    140 * @type {boolean}
    141 */
    142goog.userAgent.product.ANDROID = goog.userAgent.product.PRODUCT_KNOWN_ ?
    143 goog.userAgent.product.ASSUME_ANDROID :
    144 goog.labs.userAgent.browser.isAndroidBrowser();
    145
    146
    147/**
    148 * Whether the code is running on the Chrome web browser on any platform
    149 * or AOSP browser or WebView in a KitKat+ Android phone or tablet.
    150 * @type {boolean}
    151 */
    152goog.userAgent.product.CHROME = goog.userAgent.product.PRODUCT_KNOWN_ ?
    153 goog.userAgent.product.ASSUME_CHROME :
    154 goog.labs.userAgent.browser.isChrome();
    155
    156
    157/**
    158 * @return {boolean} Whether the browser is Safari on desktop.
    159 * @private
    160 */
    161goog.userAgent.product.isSafariDesktop_ = function() {
    162 return goog.labs.userAgent.browser.isSafari() &&
    163 !goog.labs.userAgent.platform.isIos();
    164};
    165
    166
    167/**
    168 * Whether the code is running on the desktop Safari web browser.
    169 * Note: the legacy behavior here is only true for Safari not running
    170 * on iOS.
    171 * @type {boolean}
    172 */
    173goog.userAgent.product.SAFARI = goog.userAgent.product.PRODUCT_KNOWN_ ?
    174 goog.userAgent.product.ASSUME_SAFARI :
    175 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 18025d8..adcfceb 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.OPERA) {
    51 return goog.userAgent.VERSION;
    52 }
    53
    54 if (goog.userAgent.product.CHROME) {
    55 // Chrome/4.0.223.1
    56 return goog.userAgent.product.getFirstRegExpGroup_(/Chrome\/([0-9.]+)/);
    57 }
    58
    59 // This replicates legacy logic, which considered Safari and iOS to be
    60 // different products.
    61 if (goog.userAgent.product.SAFARI && !goog.labs.userAgent.platform.isIos()) {
    62 // Version/5.0.3
    63 //
    64 // NOTE: Before version 3, Safari did not report a product version number.
    65 // The product version number for these browsers will be the empty string.
    66 // They may be differentiated by WebKit version number in goog.userAgent.
    67 return goog.userAgent.product.getFirstRegExpGroup_(/Version\/([0-9.]+)/);
    68 }
    69
    70 if (goog.userAgent.product.IPHONE || goog.userAgent.product.IPAD) {
    71 // Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1
    72 // (KHTML, like Gecko) Version/3.0 Mobile/3A100a Safari/419.3
    73 // Version is the browser version, Mobile is the build number. We combine
    74 // the version string with the build number: 3.0.3A100a for the example.
    75 var arr = goog.userAgent.product.execRegExp_(
    76 /Version\/(\S+).*Mobile\/(\S+)/);
    77 if (arr) {
    78 return arr[1] + '.' + arr[2];
    79 }
    80 } else if (goog.userAgent.product.ANDROID) {
    81 // Mozilla/5.0 (Linux; U; Android 0.5; en-us) AppleWebKit/522+
    82 // (KHTML, like Gecko) Safari/419.3
    83 //
    84 // Mozilla/5.0 (Linux; U; Android 1.0; en-us; dream) AppleWebKit/525.10+
    85 // (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2
    86 //
    87 // Prefer Version number if present, else make do with the OS number
    88 var version = goog.userAgent.product.getFirstRegExpGroup_(
    89 /Android\s+([0-9.]+)/);
    90 if (version) {
    91 return version;
    92 }
    93
    94 return goog.userAgent.product.getFirstRegExpGroup_(/Version\/([0-9.]+)/);
    95 }
    96
    97 return '';
    98};
    99
    100
    101/**
    102 * Return the first group of the given regex.
    103 * @param {!RegExp} re Regular expression with at least one group.
    104 * @return {string} Contents of the first group or an empty string if no match.
    105 * @private
    106 */
    107goog.userAgent.product.getFirstRegExpGroup_ = function(re) {
    108 var arr = goog.userAgent.product.execRegExp_(re);
    109 return arr ? arr[1] : '';
    110};
    111
    112
    113/**
    114 * Run regexp's exec() on the userAgent string.
    115 * @param {!RegExp} re Regular expression.
    116 * @return {Array<?>} A result array, or null for no match.
    117 * @private
    118 */
    119goog.userAgent.product.execRegExp_ = function(re) {
    120 return re.exec(goog.userAgent.getUserAgentString());
    121};
    122
    123
    124/**
    125 * The version of the user agent. This is a string because it might contain
    126 * 'b' (as in beta) as well as multiple dots.
    127 * @type {string}
    128 */
    129goog.userAgent.product.VERSION = goog.userAgent.product.determineVersion_();
    130
    131
    132/**
    133 * Whether the user agent product version is higher or the same as the given
    134 * version.
    135 *
    136 * @param {string|number} version The version to check.
    137 * @return {boolean} Whether the user agent product version is higher or the
    138 * same as the given version.
    139 */
    140goog.userAgent.product.isVersion = function(version) {
    141 return goog.string.compareVersions(
    142 goog.userAgent.product.VERSION, version) >= 0;
    143};
    \ 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 09d123f..f01d9d4 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.labs.userAgent.browser');
    26goog.require('goog.labs.userAgent.engine');
    27goog.require('goog.labs.userAgent.util');
    28goog.require('goog.string');
    29
    30
    31/**
    32 * @define {boolean} Whether we know at compile-time that the browser is IE.
    33 */
    34goog.define('goog.userAgent.ASSUME_IE', false);
    35
    36
    37/**
    38 * @define {boolean} Whether we know at compile-time that the browser is GECKO.
    39 */
    40goog.define('goog.userAgent.ASSUME_GECKO', false);
    41
    42
    43/**
    44 * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.
    45 */
    46goog.define('goog.userAgent.ASSUME_WEBKIT', false);
    47
    48
    49/**
    50 * @define {boolean} Whether we know at compile-time that the browser is a
    51 * mobile device running WebKit e.g. iPhone or Android.
    52 */
    53goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);
    54
    55
    56/**
    57 * @define {boolean} Whether we know at compile-time that the browser is OPERA.
    58 */
    59goog.define('goog.userAgent.ASSUME_OPERA', false);
    60
    61
    62/**
    63 * @define {boolean} Whether the
    64 * {@code goog.userAgent.isVersionOrHigher}
    65 * function will return true for any version.
    66 */
    67goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);
    68
    69
    70/**
    71 * Whether we know the browser engine at compile-time.
    72 * @type {boolean}
    73 * @private
    74 */
    75goog.userAgent.BROWSER_KNOWN_ =
    76 goog.userAgent.ASSUME_IE ||
    77 goog.userAgent.ASSUME_GECKO ||
    78 goog.userAgent.ASSUME_MOBILE_WEBKIT ||
    79 goog.userAgent.ASSUME_WEBKIT ||
    80 goog.userAgent.ASSUME_OPERA;
    81
    82
    83/**
    84 * Returns the userAgent string for the current browser.
    85 *
    86 * @return {string} The userAgent string.
    87 */
    88goog.userAgent.getUserAgentString = function() {
    89 return goog.labs.userAgent.util.getUserAgent();
    90};
    91
    92
    93/**
    94 * TODO(nnaze): Change type to "Navigator" and update compilation targets.
    95 * @return {Object} The native navigator object.
    96 */
    97goog.userAgent.getNavigator = function() {
    98 // Need a local navigator reference instead of using the global one,
    99 // to avoid the rare case where they reference different objects.
    100 // (in a WorkerPool, for example).
    101 return goog.global['navigator'] || null;
    102};
    103
    104
    105/**
    106 * Whether the user agent is Opera.
    107 * @type {boolean}
    108 */
    109goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?
    110 goog.userAgent.ASSUME_OPERA :
    111 goog.labs.userAgent.browser.isOpera();
    112
    113
    114/**
    115 * Whether the user agent is Internet Explorer.
    116 * @type {boolean}
    117 */
    118goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?
    119 goog.userAgent.ASSUME_IE :
    120 goog.labs.userAgent.browser.isIE();
    121
    122
    123/**
    124 * Whether the user agent is Gecko. Gecko is the rendering engine used by
    125 * Mozilla, Firefox, and others.
    126 * @type {boolean}
    127 */
    128goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?
    129 goog.userAgent.ASSUME_GECKO :
    130 goog.labs.userAgent.engine.isGecko();
    131
    132
    133/**
    134 * Whether the user agent is WebKit. WebKit is the rendering engine that
    135 * Safari, Android and others use.
    136 * @type {boolean}
    137 */
    138goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?
    139 goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :
    140 goog.labs.userAgent.engine.isWebKit();
    141
    142
    143/**
    144 * Whether the user agent is running on a mobile device.
    145 *
    146 * This is a separate function so that the logic can be tested.
    147 *
    148 * TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile().
    149 *
    150 * @return {boolean} Whether the user agent is running on a mobile device.
    151 * @private
    152 */
    153goog.userAgent.isMobile_ = function() {
    154 return goog.userAgent.WEBKIT &&
    155 goog.labs.userAgent.util.matchUserAgent('Mobile');
    156};
    157
    158
    159/**
    160 * Whether the user agent is running on a mobile device.
    161 *
    162 * TODO(nnaze): Consider deprecating MOBILE when labs.userAgent
    163 * is promoted as the gecko/webkit logic is likely inaccurate.
    164 *
    165 * @type {boolean}
    166 */
    167goog.userAgent.MOBILE = goog.userAgent.ASSUME_MOBILE_WEBKIT ||
    168 goog.userAgent.isMobile_();
    169
    170
    171/**
    172 * Used while transitioning code to use WEBKIT instead.
    173 * @type {boolean}
    174 * @deprecated Use {@link goog.userAgent.product.SAFARI} instead.
    175 * TODO(nicksantos): Delete this from goog.userAgent.
    176 */
    177goog.userAgent.SAFARI = goog.userAgent.WEBKIT;
    178
    179
    180/**
    181 * @return {string} the platform (operating system) the user agent is running
    182 * on. Default to empty string because navigator.platform may not be defined
    183 * (on Rhino, for example).
    184 * @private
    185 */
    186goog.userAgent.determinePlatform_ = function() {
    187 var navigator = goog.userAgent.getNavigator();
    188 return navigator && navigator.platform || '';
    189};
    190
    191
    192/**
    193 * The platform (operating system) the user agent is running on. Default to
    194 * empty string because navigator.platform may not be defined (on Rhino, for
    195 * example).
    196 * @type {string}
    197 */
    198goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();
    199
    200
    201/**
    202 * @define {boolean} Whether the user agent is running on a Macintosh operating
    203 * system.
    204 */
    205goog.define('goog.userAgent.ASSUME_MAC', false);
    206
    207
    208/**
    209 * @define {boolean} Whether the user agent is running on a Windows operating
    210 * system.
    211 */
    212goog.define('goog.userAgent.ASSUME_WINDOWS', false);
    213
    214
    215/**
    216 * @define {boolean} Whether the user agent is running on a Linux operating
    217 * system.
    218 */
    219goog.define('goog.userAgent.ASSUME_LINUX', false);
    220
    221
    222/**
    223 * @define {boolean} Whether the user agent is running on a X11 windowing
    224 * system.
    225 */
    226goog.define('goog.userAgent.ASSUME_X11', false);
    227
    228
    229/**
    230 * @define {boolean} Whether the user agent is running on Android.
    231 */
    232goog.define('goog.userAgent.ASSUME_ANDROID', false);
    233
    234
    235/**
    236 * @define {boolean} Whether the user agent is running on an iPhone.
    237 */
    238goog.define('goog.userAgent.ASSUME_IPHONE', false);
    239
    240
    241/**
    242 * @define {boolean} Whether the user agent is running on an iPad.
    243 */
    244goog.define('goog.userAgent.ASSUME_IPAD', false);
    245
    246
    247/**
    248 * @type {boolean}
    249 * @private
    250 */
    251goog.userAgent.PLATFORM_KNOWN_ =
    252 goog.userAgent.ASSUME_MAC ||
    253 goog.userAgent.ASSUME_WINDOWS ||
    254 goog.userAgent.ASSUME_LINUX ||
    255 goog.userAgent.ASSUME_X11 ||
    256 goog.userAgent.ASSUME_ANDROID ||
    257 goog.userAgent.ASSUME_IPHONE ||
    258 goog.userAgent.ASSUME_IPAD;
    259
    260
    261/**
    262 * Initialize the goog.userAgent constants that define which platform the user
    263 * agent is running on.
    264 * @private
    265 */
    266goog.userAgent.initPlatform_ = function() {
    267 /**
    268 * Whether the user agent is running on a Macintosh operating system.
    269 * @type {boolean}
    270 * @private
    271 */
    272 goog.userAgent.detectedMac_ = goog.string.contains(goog.userAgent.PLATFORM,
    273 'Mac');
    274
    275 /**
    276 * Whether the user agent is running on a Windows operating system.
    277 * @type {boolean}
    278 * @private
    279 */
    280 goog.userAgent.detectedWindows_ = goog.string.contains(
    281 goog.userAgent.PLATFORM, 'Win');
    282
    283 /**
    284 * Whether the user agent is running on a Linux operating system.
    285 * @type {boolean}
    286 * @private
    287 */
    288 goog.userAgent.detectedLinux_ = goog.string.contains(goog.userAgent.PLATFORM,
    289 'Linux');
    290
    291 /**
    292 * Whether the user agent is running on a X11 windowing system.
    293 * @type {boolean}
    294 * @private
    295 */
    296 goog.userAgent.detectedX11_ = !!goog.userAgent.getNavigator() &&
    297 goog.string.contains(goog.userAgent.getNavigator()['appVersion'] || '',
    298 'X11');
    299
    300 // Need user agent string for Android/IOS detection
    301 var ua = goog.userAgent.getUserAgentString();
    302
    303 /**
    304 * Whether the user agent is running on Android.
    305 * @type {boolean}
    306 * @private
    307 */
    308 goog.userAgent.detectedAndroid_ = !!ua &&
    309 goog.string.contains(ua, 'Android');
    310
    311 /**
    312 * Whether the user agent is running on an iPhone.
    313 * @type {boolean}
    314 * @private
    315 */
    316 goog.userAgent.detectedIPhone_ = !!ua && goog.string.contains(ua, 'iPhone');
    317
    318 /**
    319 * Whether the user agent is running on an iPad.
    320 * @type {boolean}
    321 * @private
    322 */
    323 goog.userAgent.detectedIPad_ = !!ua && goog.string.contains(ua, 'iPad');
    324};
    325
    326
    327if (!goog.userAgent.PLATFORM_KNOWN_) {
    328 goog.userAgent.initPlatform_();
    329}
    330
    331
    332/**
    333 * Whether the user agent is running on a Macintosh operating system.
    334 * @type {boolean}
    335 */
    336goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?
    337 goog.userAgent.ASSUME_MAC : goog.userAgent.detectedMac_;
    338
    339
    340/**
    341 * Whether the user agent is running on a Windows operating system.
    342 * @type {boolean}
    343 */
    344goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?
    345 goog.userAgent.ASSUME_WINDOWS : goog.userAgent.detectedWindows_;
    346
    347
    348/**
    349 * Whether the user agent is running on a Linux operating system.
    350 * @type {boolean}
    351 */
    352goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?
    353 goog.userAgent.ASSUME_LINUX : goog.userAgent.detectedLinux_;
    354
    355
    356/**
    357 * Whether the user agent is running on a X11 windowing system.
    358 * @type {boolean}
    359 */
    360goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?
    361 goog.userAgent.ASSUME_X11 : goog.userAgent.detectedX11_;
    362
    363
    364/**
    365 * Whether the user agent is running on Android.
    366 * @type {boolean}
    367 */
    368goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?
    369 goog.userAgent.ASSUME_ANDROID : goog.userAgent.detectedAndroid_;
    370
    371
    372/**
    373 * Whether the user agent is running on an iPhone.
    374 * @type {boolean}
    375 */
    376goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?
    377 goog.userAgent.ASSUME_IPHONE : goog.userAgent.detectedIPhone_;
    378
    379
    380/**
    381 * Whether the user agent is running on an iPad.
    382 * @type {boolean}
    383 */
    384goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?
    385 goog.userAgent.ASSUME_IPAD : goog.userAgent.detectedIPad_;
    386
    387
    388/**
    389 * @return {string} The string that describes the version number of the user
    390 * agent.
    391 * @private
    392 */
    393goog.userAgent.determineVersion_ = function() {
    394 // All browsers have different ways to detect the version and they all have
    395 // different naming schemes.
    396
    397 // version is a string rather than a number because it may contain 'b', 'a',
    398 // and so on.
    399 var version = '', re;
    400
    401 if (goog.userAgent.OPERA && goog.global['opera']) {
    402 var operaVersion = goog.global['opera'].version;
    403 return goog.isFunction(operaVersion) ? operaVersion() : operaVersion;
    404 }
    405
    406 if (goog.userAgent.GECKO) {
    407 re = /rv\:([^\);]+)(\)|;)/;
    408 } else if (goog.userAgent.IE) {
    409 re = /\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/;
    410 } else if (goog.userAgent.WEBKIT) {
    411 // WebKit/125.4
    412 re = /WebKit\/(\S+)/;
    413 }
    414
    415 if (re) {
    416 var arr = re.exec(goog.userAgent.getUserAgentString());
    417 version = arr ? arr[1] : '';
    418 }
    419
    420 if (goog.userAgent.IE) {
    421 // IE9 can be in document mode 9 but be reporting an inconsistent user agent
    422 // version. If it is identifying as a version lower than 9 we take the
    423 // documentMode as the version instead. IE8 has similar behavior.
    424 // It is recommended to set the X-UA-Compatible header to ensure that IE9
    425 // uses documentMode 9.
    426 var docMode = goog.userAgent.getDocumentMode_();
    427 if (docMode > parseFloat(version)) {
    428 return String(docMode);
    429 }
    430 }
    431
    432 return version;
    433};
    434
    435
    436/**
    437 * @return {number|undefined} Returns the document mode (for testing).
    438 * @private
    439 */
    440goog.userAgent.getDocumentMode_ = function() {
    441 // NOTE(user): goog.userAgent may be used in context where there is no DOM.
    442 var doc = goog.global['document'];
    443 return doc ? doc['documentMode'] : undefined;
    444};
    445
    446
    447/**
    448 * The version of the user agent. This is a string because it might contain
    449 * 'b' (as in beta) as well as multiple dots.
    450 * @type {string}
    451 */
    452goog.userAgent.VERSION = goog.userAgent.determineVersion_();
    453
    454
    455/**
    456 * Compares two version numbers.
    457 *
    458 * @param {string} v1 Version of first item.
    459 * @param {string} v2 Version of second item.
    460 *
    461 * @return {number} 1 if first argument is higher
    462 * 0 if arguments are equal
    463 * -1 if second argument is higher.
    464 * @deprecated Use goog.string.compareVersions.
    465 */
    466goog.userAgent.compare = function(v1, v2) {
    467 return goog.string.compareVersions(v1, v2);
    468};
    469
    470
    471/**
    472 * Cache for {@link goog.userAgent.isVersionOrHigher}.
    473 * Calls to compareVersions are surprisingly expensive and, as a browser's
    474 * version number is unlikely to change during a session, we cache the results.
    475 * @const
    476 * @private
    477 */
    478goog.userAgent.isVersionOrHigherCache_ = {};
    479
    480
    481/**
    482 * Whether the user agent version is higher or the same as the given version.
    483 * NOTE: When checking the version numbers for Firefox and Safari, be sure to
    484 * use the engine's version, not the browser's version number. For example,
    485 * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.
    486 * Opera and Internet Explorer versions match the product release number.<br>
    487 * @see <a href="http://en.wikipedia.org/wiki/Safari_version_history">
    488 * Webkit</a>
    489 * @see <a href="http://en.wikipedia.org/wiki/Gecko_engine">Gecko</a>
    490 *
    491 * @param {string|number} version The version to check.
    492 * @return {boolean} Whether the user agent version is higher or the same as
    493 * the given version.
    494 */
    495goog.userAgent.isVersionOrHigher = function(version) {
    496 return goog.userAgent.ASSUME_ANY_VERSION ||
    497 goog.userAgent.isVersionOrHigherCache_[version] ||
    498 (goog.userAgent.isVersionOrHigherCache_[version] =
    499 goog.string.compareVersions(goog.userAgent.VERSION, version) >= 0);
    500};
    501
    502
    503/**
    504 * Deprecated alias to {@code goog.userAgent.isVersionOrHigher}.
    505 * @param {string|number} version The version to check.
    506 * @return {boolean} Whether the user agent version is higher or the same as
    507 * the given version.
    508 * @deprecated Use goog.userAgent.isVersionOrHigher().
    509 */
    510goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;
    511
    512
    513/**
    514 * Whether the IE effective document mode is higher or the same as the given
    515 * document mode version.
    516 * NOTE: Only for IE, return false for another browser.
    517 *
    518 * @param {number} documentMode The document mode version to check.
    519 * @return {boolean} Whether the IE effective document mode is higher or the
    520 * same as the given version.
    521 */
    522goog.userAgent.isDocumentModeOrHigher = function(documentMode) {
    523 return goog.userAgent.IE && goog.userAgent.DOCUMENT_MODE >= documentMode;
    524};
    525
    526
    527/**
    528 * Deprecated alias to {@code goog.userAgent.isDocumentModeOrHigher}.
    529 * @param {number} version The version to check.
    530 * @return {boolean} Whether the IE effective document mode is higher or the
    531 * same as the given version.
    532 * @deprecated Use goog.userAgent.isDocumentModeOrHigher().
    533 */
    534goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;
    535
    536
    537/**
    538 * For IE version < 7, documentMode is undefined, so attempt to use the
    539 * CSS1Compat property to see if we are in standards mode. If we are in
    540 * standards mode, treat the browser version as the document mode. Otherwise,
    541 * IE is emulating version 5.
    542 * @type {number|undefined}
    543 * @const
    544 */
    545goog.userAgent.DOCUMENT_MODE = (function() {
    546 var doc = goog.global['document'];
    547 if (!doc || !goog.userAgent.IE) {
    548 return undefined;
    549 }
    550 var mode = goog.userAgent.getDocumentMode_();
    551 return mode || (doc['compatMode'] == 'CSS1Compat' ?
    552 parseInt(goog.userAgent.VERSION, 10) : 5);
    553})();
    \ 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 GECKO.
    41 */
    42goog.define('goog.userAgent.ASSUME_GECKO', false);
    43
    44
    45/**
    46 * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.
    47 */
    48goog.define('goog.userAgent.ASSUME_WEBKIT', false);
    49
    50
    51/**
    52 * @define {boolean} Whether we know at compile-time that the browser is a
    53 * mobile device running WebKit e.g. iPhone or Android.
    54 */
    55goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);
    56
    57
    58/**
    59 * @define {boolean} Whether we know at compile-time that the browser is OPERA.
    60 */
    61goog.define('goog.userAgent.ASSUME_OPERA', false);
    62
    63
    64/**
    65 * @define {boolean} Whether the
    66 * {@code goog.userAgent.isVersionOrHigher}
    67 * function will return true for any version.
    68 */
    69goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);
    70
    71
    72/**
    73 * Whether we know the browser engine at compile-time.
    74 * @type {boolean}
    75 * @private
    76 */
    77goog.userAgent.BROWSER_KNOWN_ =
    78 goog.userAgent.ASSUME_IE ||
    79 goog.userAgent.ASSUME_GECKO ||
    80 goog.userAgent.ASSUME_MOBILE_WEBKIT ||
    81 goog.userAgent.ASSUME_WEBKIT ||
    82 goog.userAgent.ASSUME_OPERA;
    83
    84
    85/**
    86 * Returns the userAgent string for the current browser.
    87 *
    88 * @return {string} The userAgent string.
    89 */
    90goog.userAgent.getUserAgentString = function() {
    91 return goog.labs.userAgent.util.getUserAgent();
    92};
    93
    94
    95/**
    96 * TODO(nnaze): Change type to "Navigator" and update compilation targets.
    97 * @return {Object} The native navigator object.
    98 */
    99goog.userAgent.getNavigator = function() {
    100 // Need a local navigator reference instead of using the global one,
    101 // to avoid the rare case where they reference different objects.
    102 // (in a WorkerPool, for example).
    103 return goog.global['navigator'] || null;
    104};
    105
    106
    107/**
    108 * Whether the user agent is Opera.
    109 * @type {boolean}
    110 */
    111goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?
    112 goog.userAgent.ASSUME_OPERA :
    113 goog.labs.userAgent.browser.isOpera();
    114
    115
    116/**
    117 * Whether the user agent is Internet Explorer.
    118 * @type {boolean}
    119 */
    120goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?
    121 goog.userAgent.ASSUME_IE :
    122 goog.labs.userAgent.browser.isIE();
    123
    124
    125/**
    126 * Whether the user agent is Gecko. Gecko is the rendering engine used by
    127 * Mozilla, Firefox, and others.
    128 * @type {boolean}
    129 */
    130goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?
    131 goog.userAgent.ASSUME_GECKO :
    132 goog.labs.userAgent.engine.isGecko();
    133
    134
    135/**
    136 * Whether the user agent is WebKit. WebKit is the rendering engine that
    137 * Safari, Android and others use.
    138 * @type {boolean}
    139 */
    140goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?
    141 goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :
    142 goog.labs.userAgent.engine.isWebKit();
    143
    144
    145/**
    146 * Whether the user agent is running on a mobile device.
    147 *
    148 * This is a separate function so that the logic can be tested.
    149 *
    150 * TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile().
    151 *
    152 * @return {boolean} Whether the user agent is running on a mobile device.
    153 * @private
    154 */
    155goog.userAgent.isMobile_ = function() {
    156 return goog.userAgent.WEBKIT &&
    157 goog.labs.userAgent.util.matchUserAgent('Mobile');
    158};
    159
    160
    161/**
    162 * Whether the user agent is running on a mobile device.
    163 *
    164 * TODO(nnaze): Consider deprecating MOBILE when labs.userAgent
    165 * is promoted as the gecko/webkit logic is likely inaccurate.
    166 *
    167 * @type {boolean}
    168 */
    169goog.userAgent.MOBILE = goog.userAgent.ASSUME_MOBILE_WEBKIT ||
    170 goog.userAgent.isMobile_();
    171
    172
    173/**
    174 * Used while transitioning code to use WEBKIT instead.
    175 * @type {boolean}
    176 * @deprecated Use {@link goog.userAgent.product.SAFARI} instead.
    177 * TODO(nicksantos): Delete this from goog.userAgent.
    178 */
    179goog.userAgent.SAFARI = goog.userAgent.WEBKIT;
    180
    181
    182/**
    183 * @return {string} the platform (operating system) the user agent is running
    184 * on. Default to empty string because navigator.platform may not be defined
    185 * (on Rhino, for example).
    186 * @private
    187 */
    188goog.userAgent.determinePlatform_ = function() {
    189 var navigator = goog.userAgent.getNavigator();
    190 return navigator && navigator.platform || '';
    191};
    192
    193
    194/**
    195 * The platform (operating system) the user agent is running on. Default to
    196 * empty string because navigator.platform may not be defined (on Rhino, for
    197 * example).
    198 * @type {string}
    199 */
    200goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();
    201
    202
    203/**
    204 * @define {boolean} Whether the user agent is running on a Macintosh operating
    205 * system.
    206 */
    207goog.define('goog.userAgent.ASSUME_MAC', false);
    208
    209
    210/**
    211 * @define {boolean} Whether the user agent is running on a Windows operating
    212 * system.
    213 */
    214goog.define('goog.userAgent.ASSUME_WINDOWS', false);
    215
    216
    217/**
    218 * @define {boolean} Whether the user agent is running on a Linux operating
    219 * system.
    220 */
    221goog.define('goog.userAgent.ASSUME_LINUX', false);
    222
    223
    224/**
    225 * @define {boolean} Whether the user agent is running on a X11 windowing
    226 * system.
    227 */
    228goog.define('goog.userAgent.ASSUME_X11', false);
    229
    230
    231/**
    232 * @define {boolean} Whether the user agent is running on Android.
    233 */
    234goog.define('goog.userAgent.ASSUME_ANDROID', false);
    235
    236
    237/**
    238 * @define {boolean} Whether the user agent is running on an iPhone.
    239 */
    240goog.define('goog.userAgent.ASSUME_IPHONE', false);
    241
    242
    243/**
    244 * @define {boolean} Whether the user agent is running on an iPad.
    245 */
    246goog.define('goog.userAgent.ASSUME_IPAD', false);
    247
    248
    249/**
    250 * @type {boolean}
    251 * @private
    252 */
    253goog.userAgent.PLATFORM_KNOWN_ =
    254 goog.userAgent.ASSUME_MAC ||
    255 goog.userAgent.ASSUME_WINDOWS ||
    256 goog.userAgent.ASSUME_LINUX ||
    257 goog.userAgent.ASSUME_X11 ||
    258 goog.userAgent.ASSUME_ANDROID ||
    259 goog.userAgent.ASSUME_IPHONE ||
    260 goog.userAgent.ASSUME_IPAD;
    261
    262
    263/**
    264 * Whether the user agent is running on a Macintosh operating system.
    265 * @type {boolean}
    266 */
    267goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?
    268 goog.userAgent.ASSUME_MAC : goog.labs.userAgent.platform.isMacintosh();
    269
    270
    271/**
    272 * Whether the user agent is running on a Windows operating system.
    273 * @type {boolean}
    274 */
    275goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?
    276 goog.userAgent.ASSUME_WINDOWS :
    277 goog.labs.userAgent.platform.isWindows();
    278
    279
    280/**
    281 * Whether the user agent is Linux per the legacy behavior of
    282 * goog.userAgent.LINUX, which considered ChromeOS to also be
    283 * Linux.
    284 * @return {boolean}
    285 * @private
    286 */
    287goog.userAgent.isLegacyLinux_ = function() {
    288 return goog.labs.userAgent.platform.isLinux() ||
    289 goog.labs.userAgent.platform.isChromeOS();
    290};
    291
    292
    293/**
    294 * Whether the user agent is running on a Linux operating system.
    295 *
    296 * Note that goog.userAgent.LINUX considers ChromeOS to be Linux,
    297 * while goog.labs.userAgent.platform considers ChromeOS and
    298 * Linux to be different OSes.
    299 *
    300 * @type {boolean}
    301 */
    302goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?
    303 goog.userAgent.ASSUME_LINUX :
    304 goog.userAgent.isLegacyLinux_();
    305
    306
    307/**
    308 * @return {boolean} Whether the user agent is an X11 windowing system.
    309 * @private
    310 */
    311goog.userAgent.isX11_ = function() {
    312 var navigator = goog.userAgent.getNavigator();
    313 return !!navigator &&
    314 goog.string.contains(navigator['appVersion'] || '', 'X11');
    315};
    316
    317
    318/**
    319 * Whether the user agent is running on a X11 windowing system.
    320 * @type {boolean}
    321 */
    322goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?
    323 goog.userAgent.ASSUME_X11 :
    324 goog.userAgent.isX11_();
    325
    326
    327/**
    328 * Whether the user agent is running on Android.
    329 * @type {boolean}
    330 */
    331goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?
    332 goog.userAgent.ASSUME_ANDROID :
    333 goog.labs.userAgent.platform.isAndroid();
    334
    335
    336/**
    337 * Whether the user agent is running on an iPhone.
    338 * @type {boolean}
    339 */
    340goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?
    341 goog.userAgent.ASSUME_IPHONE :
    342 goog.labs.userAgent.platform.isIphone();
    343
    344
    345/**
    346 * Whether the user agent is running on an iPad.
    347 * @type {boolean}
    348 */
    349goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?
    350 goog.userAgent.ASSUME_IPAD :
    351 goog.labs.userAgent.platform.isIpad();
    352
    353
    354/**
    355 * @return {string} The string that describes the version number of the user
    356 * agent.
    357 * @private
    358 */
    359goog.userAgent.determineVersion_ = function() {
    360 // All browsers have different ways to detect the version and they all have
    361 // different naming schemes.
    362
    363 if (goog.userAgent.OPERA && goog.global['opera']) {
    364 var operaVersion = goog.global['opera'].version;
    365 return goog.isFunction(operaVersion) ? operaVersion() : operaVersion;
    366 }
    367
    368 // version is a string rather than a number because it may contain 'b', 'a',
    369 // and so on.
    370 var version = '';
    371 var arr = goog.userAgent.getVersionRegexResult_();
    372 if (arr) {
    373 version = arr ? arr[1] : '';
    374 }
    375
    376 if (goog.userAgent.IE && !goog.labs.userAgent.engine.isEdge()) {
    377 // IE9 can be in document mode 9 but be reporting an inconsistent user agent
    378 // version. If it is identifying as a version lower than 9 we take the
    379 // documentMode as the version instead. IE8 has similar behavior.
    380 // It is recommended to set the X-UA-Compatible header to ensure that IE9
    381 // uses documentMode 9.
    382 var docMode = goog.userAgent.getDocumentMode_();
    383 if (docMode > parseFloat(version)) {
    384 return String(docMode);
    385 }
    386 }
    387
    388 return version;
    389};
    390
    391
    392/**
    393 * @return {Array|undefined} The version regex matches from parsing the user
    394 * agent string. These regex statements must be executed inline so they can
    395 * be compiled out by the closure compiler with the rest of the useragent
    396 * detection logic when ASSUME_* is specified.
    397 * @private
    398 */
    399goog.userAgent.getVersionRegexResult_ = function() {
    400 var userAgent = goog.userAgent.getUserAgentString();
    401 if (goog.userAgent.GECKO) {
    402 return /rv\:([^\);]+)(\)|;)/.exec(userAgent);
    403 }
    404 if (goog.userAgent.IE && goog.labs.userAgent.engine.isEdge()) {
    405 return /Edge\/([\d\.]+)/.exec(userAgent);
    406 }
    407 if (goog.userAgent.IE) {
    408 return /\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(userAgent);
    409 }
    410 if (goog.userAgent.WEBKIT) {
    411 // WebKit/125.4
    412 return /WebKit\/(\S+)/.exec(userAgent);
    413 }
    414};
    415
    416
    417/**
    418 * @return {number|undefined} Returns the document mode (for testing).
    419 * @private
    420 */
    421goog.userAgent.getDocumentMode_ = function() {
    422 // NOTE(user): goog.userAgent may be used in context where there is no DOM.
    423 var doc = goog.global['document'];
    424 return doc ? doc['documentMode'] : undefined;
    425};
    426
    427
    428/**
    429 * The version of the user agent. This is a string because it might contain
    430 * 'b' (as in beta) as well as multiple dots.
    431 * @type {string}
    432 */
    433goog.userAgent.VERSION = goog.userAgent.determineVersion_();
    434
    435
    436/**
    437 * Compares two version numbers.
    438 *
    439 * @param {string} v1 Version of first item.
    440 * @param {string} v2 Version of second item.
    441 *
    442 * @return {number} 1 if first argument is higher
    443 * 0 if arguments are equal
    444 * -1 if second argument is higher.
    445 * @deprecated Use goog.string.compareVersions.
    446 */
    447goog.userAgent.compare = function(v1, v2) {
    448 return goog.string.compareVersions(v1, v2);
    449};
    450
    451
    452/**
    453 * Cache for {@link goog.userAgent.isVersionOrHigher}.
    454 * Calls to compareVersions are surprisingly expensive and, as a browser's
    455 * version number is unlikely to change during a session, we cache the results.
    456 * @const
    457 * @private
    458 */
    459goog.userAgent.isVersionOrHigherCache_ = {};
    460
    461
    462/**
    463 * Whether the user agent version is higher or the same as the given version.
    464 * NOTE: When checking the version numbers for Firefox and Safari, be sure to
    465 * use the engine's version, not the browser's version number. For example,
    466 * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.
    467 * Opera and Internet Explorer versions match the product release number.<br>
    468 * @see <a href="http://en.wikipedia.org/wiki/Safari_version_history">
    469 * Webkit</a>
    470 * @see <a href="http://en.wikipedia.org/wiki/Gecko_engine">Gecko</a>
    471 *
    472 * @param {string|number} version The version to check.
    473 * @return {boolean} Whether the user agent version is higher or the same as
    474 * the given version.
    475 */
    476goog.userAgent.isVersionOrHigher = function(version) {
    477 return goog.userAgent.ASSUME_ANY_VERSION ||
    478 goog.userAgent.isVersionOrHigherCache_[version] ||
    479 (goog.userAgent.isVersionOrHigherCache_[version] =
    480 goog.string.compareVersions(goog.userAgent.VERSION, version) >= 0);
    481};
    482
    483
    484/**
    485 * Deprecated alias to {@code goog.userAgent.isVersionOrHigher}.
    486 * @param {string|number} version The version to check.
    487 * @return {boolean} Whether the user agent version is higher or the same as
    488 * the given version.
    489 * @deprecated Use goog.userAgent.isVersionOrHigher().
    490 */
    491goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;
    492
    493
    494/**
    495 * Whether the IE effective document mode is higher or the same as the given
    496 * document mode version. Because document modes were deprecated with the launch
    497 * of IE's new Edge engine, Edge browsers will always return true for this
    498 * function.
    499 * NOTE: Only for IE, return false for another browser.
    500 *
    501 * @param {number} documentMode The document mode version to check.
    502 * @return {boolean} Whether the IE effective document mode is higher or the
    503 * same as the given version.
    504 */
    505goog.userAgent.isDocumentModeOrHigher = function(documentMode) {
    506 return goog.userAgent.IE && (goog.labs.userAgent.engine.isEdge() ||
    507 goog.userAgent.DOCUMENT_MODE >= documentMode);
    508};
    509
    510
    511/**
    512 * Deprecated alias to {@code goog.userAgent.isDocumentModeOrHigher}.
    513 * @param {number} version The version to check.
    514 * @return {boolean} Whether the IE effective document mode is higher or the
    515 * same as the given version.
    516 * @deprecated Use goog.userAgent.isDocumentModeOrHigher().
    517 */
    518goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;
    519
    520
    521/**
    522 * For IE version < 7 and IE Edge browsers, documentMode is undefined. For
    523 * non-Edge browsers attempt to use the CSS1Compat property to see if we are in
    524 * standards mode. If we are in standards mode, treat the browser version as the
    525 * document mode. Otherwise, IE is emulating version 5.
    526 * @type {number|undefined}
    527 * @const
    528 */
    529goog.userAgent.DOCUMENT_MODE = (function() {
    530 var doc = goog.global['document'];
    531 var mode = goog.userAgent.getDocumentMode_();
    532 if (!doc || !goog.userAgent.IE ||
    533 (!mode && goog.labs.userAgent.engine.isEdge())) {
    534 return undefined;
    535 }
    536 return mode || (doc['compatMode'] == 'CSS1Compat' ?
    537 parseInt(goog.userAgent.VERSION, 10) : 5);
    538})();
    \ 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 4ae23ef..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).getId().
    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/button.js.src.html b/docs/source/lib/webdriver/button.js.src.html index 6bf866c..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 dd5cdf0..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');
    22goog.provide('webdriver.ProxyConfig');
    23
    24goog.require('webdriver.logging.Preferences');
    25
    26
    27
    28/**
    29 * Recognized browser names.
    30 * @enum {string}
    31 */
    32webdriver.Browser = {
    33 ANDROID: 'android',
    34 CHROME: 'chrome',
    35 FIREFOX: 'firefox',
    36 INTERNET_EXPLORER: 'internet explorer',
    37 IPAD: 'iPad',
    38 IPHONE: 'iPhone',
    39 OPERA: 'opera',
    40 PHANTOM_JS: 'phantomjs',
    41 SAFARI: 'safari',
    42 HTMLUNIT: 'htmlunit'
    43};
    44
    45
    46
    47/**
    48 * Describes how a proxy should be configured for a WebDriver session.
    49 * Proxy configuration object, as defined by the WebDriver wire protocol.
    50 * @typedef {(
    51 * {proxyType: string}|
    52 * {proxyType: string,
    53 * proxyAutoconfigUrl: string}|
    54 * {proxyType: string,
    55 * ftpProxy: string,
    56 * httpProxy: string,
    57 * sslProxy: string,
    58 * noProxy: string})}
    59 */
    60webdriver.ProxyConfig;
    61
    62
    63
    64/**
    65 * Common webdriver capability keys.
    66 * @enum {string}
    67 */
    68webdriver.Capability = {
    69
    70 /**
    71 * Indicates whether a driver should accept all SSL certs by default. This
    72 * capability only applies when requesting a new session. To query whether
    73 * a driver can handle insecure SSL certs, see
    74 * {@link webdriver.Capability.SECURE_SSL}.
    75 */
    76 ACCEPT_SSL_CERTS: 'acceptSslCerts',
    77
    78
    79 /**
    80 * The browser name. Common browser names are defined in the
    81 * {@link webdriver.Browser} enum.
    82 */
    83 BROWSER_NAME: 'browserName',
    84
    85 /**
    86 * Defines how elements should be scrolled into the viewport for interaction.
    87 * This capability will be set to zero (0) if elements are aligned with the
    88 * top of the viewport, or one (1) if aligned with the bottom. The default
    89 * behavior is to align with the top of the viewport.
    90 */
    91 ELEMENT_SCROLL_BEHAVIOR: 'elementScrollBehavior',
    92
    93 /**
    94 * Whether the driver is capable of handling modal alerts (e.g. alert,
    95 * confirm, prompt). To define how a driver <i>should</i> handle alerts,
    96 * use {@link webdriver.Capability.UNEXPECTED_ALERT_BEHAVIOR}.
    97 */
    98 HANDLES_ALERTS: 'handlesAlerts',
    99
    100 /**
    101 * Key for the logging driver logging preferences.
    102 */
    103 LOGGING_PREFS: 'loggingPrefs',
    104
    105 /**
    106 * Whether this session generates native events when simulating user input.
    107 */
    108 NATIVE_EVENTS: 'nativeEvents',
    109
    110 /**
    111 * Describes the platform the browser is running on. Will be one of
    112 * ANDROID, IOS, LINUX, MAC, UNIX, or WINDOWS. When <i>requesting</i> a
    113 * session, ANY may be used to indicate no platform preference (this is
    114 * semantically equivalent to omitting the platform capability).
    115 */
    116 PLATFORM: 'platform',
    117
    118 /**
    119 * Describes the proxy configuration to use for a new WebDriver session.
    120 */
    121 PROXY: 'proxy',
    122
    123 /** Whether the driver supports changing the brower's orientation. */
    124 ROTATABLE: 'rotatable',
    125
    126 /**
    127 * Whether a driver is only capable of handling secure SSL certs. To request
    128 * that a driver accept insecure SSL certs by default, use
    129 * {@link webdriver.Capability.ACCEPT_SSL_CERTS}.
    130 */
    131 SECURE_SSL: 'secureSsl',
    132
    133 /** Whether the driver supports manipulating the app cache. */
    134 SUPPORTS_APPLICATION_CACHE: 'applicationCacheEnabled',
    135
    136 /** Whether the driver supports locating elements with CSS selectors. */
    137 SUPPORTS_CSS_SELECTORS: 'cssSelectorsEnabled',
    138
    139 /** Whether the browser supports JavaScript. */
    140 SUPPORTS_JAVASCRIPT: 'javascriptEnabled',
    141
    142 /** Whether the driver supports controlling the browser's location info. */
    143 SUPPORTS_LOCATION_CONTEXT: 'locationContextEnabled',
    144
    145 /** Whether the driver supports taking screenshots. */
    146 TAKES_SCREENSHOT: 'takesScreenshot',
    147
    148 /**
    149 * Defines how the driver should handle unexpected alerts. The value should
    150 * be one of "accept", "dismiss", or "ignore.
    151 */
    152 UNEXPECTED_ALERT_BEHAVIOR: 'unexpectedAlertBehavior',
    153
    154 /** Defines the browser version. */
    155 VERSION: 'version'
    156};
    157
    158
    159
    160/**
    161 * @param {(webdriver.Capabilities|Object)=} opt_other Another set of
    162 * capabilities to merge into this instance.
    163 * @constructor
    164 */
    165webdriver.Capabilities = function(opt_other) {
    166
    167 /** @private {!Object} */
    168 this.caps_ = {};
    169
    170 if (opt_other) {
    171 this.merge(opt_other);
    172 }
    173};
    174
    175
    176/**
    177 * @return {!webdriver.Capabilities} A basic set of capabilities for Android.
    178 */
    179webdriver.Capabilities.android = function() {
    180 return new webdriver.Capabilities().
    181 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.ANDROID).
    182 set(webdriver.Capability.PLATFORM, 'ANDROID');
    183};
    184
    185
    186/**
    187 * @return {!webdriver.Capabilities} A basic set of capabilities for Chrome.
    188 */
    189webdriver.Capabilities.chrome = function() {
    190 return new webdriver.Capabilities().
    191 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.CHROME);
    192};
    193
    194
    195/**
    196 * @return {!webdriver.Capabilities} A basic set of capabilities for Firefox.
    197 */
    198webdriver.Capabilities.firefox = function() {
    199 return new webdriver.Capabilities().
    200 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.FIREFOX);
    201};
    202
    203
    204/**
    205 * @return {!webdriver.Capabilities} A basic set of capabilities for
    206 * Internet Explorer.
    207 */
    208webdriver.Capabilities.ie = function() {
    209 return new webdriver.Capabilities().
    210 set(webdriver.Capability.BROWSER_NAME,
    211 webdriver.Browser.INTERNET_EXPLORER).
    212 set(webdriver.Capability.PLATFORM, 'WINDOWS');
    213};
    214
    215
    216/**
    217 * @return {!webdriver.Capabilities} A basic set of capabilities for iPad.
    218 */
    219webdriver.Capabilities.ipad = function() {
    220 return new webdriver.Capabilities().
    221 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.IPAD).
    222 set(webdriver.Capability.PLATFORM, 'MAC');
    223};
    224
    225
    226/**
    227 * @return {!webdriver.Capabilities} A basic set of capabilities for iPhone.
    228 */
    229webdriver.Capabilities.iphone = function() {
    230 return new webdriver.Capabilities().
    231 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.IPHONE).
    232 set(webdriver.Capability.PLATFORM, 'MAC');
    233};
    234
    235
    236/**
    237 * @return {!webdriver.Capabilities} A basic set of capabilities for Opera.
    238 */
    239webdriver.Capabilities.opera = function() {
    240 return new webdriver.Capabilities().
    241 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.OPERA);
    242};
    243
    244
    245/**
    246 * @return {!webdriver.Capabilities} A basic set of capabilities for
    247 * PhantomJS.
    248 */
    249webdriver.Capabilities.phantomjs = function() {
    250 return new webdriver.Capabilities().
    251 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.PHANTOM_JS);
    252};
    253
    254
    255/**
    256 * @return {!webdriver.Capabilities} A basic set of capabilities for Safari.
    257 */
    258webdriver.Capabilities.safari = function() {
    259 return new webdriver.Capabilities().
    260 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.SAFARI);
    261};
    262
    263
    264/**
    265 * @return {!webdriver.Capabilities} A basic set of capabilities for HTMLUnit.
    266 */
    267webdriver.Capabilities.htmlunit = function() {
    268 return new webdriver.Capabilities().
    269 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.HTMLUNIT);
    270};
    271
    272
    273/**
    274 * @return {!webdriver.Capabilities} A basic set of capabilities for HTMLUnit
    275 * with enabled Javascript.
    276 */
    277webdriver.Capabilities.htmlunitwithjs = function() {
    278 return new webdriver.Capabilities().
    279 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.HTMLUNIT).
    280 set(webdriver.Capability.SUPPORTS_JAVASCRIPT, true);
    281};
    282
    283
    284/** @return {!Object} The JSON representation of this instance. */
    285webdriver.Capabilities.prototype.toJSON = function() {
    286 return this.caps_;
    287};
    288
    289
    290/**
    291 * Merges another set of capabilities into this instance. Any duplicates in
    292 * the provided set will override those already set on this instance.
    293 * @param {!(webdriver.Capabilities|Object)} other The capabilities to
    294 * merge into this instance.
    295 * @return {!webdriver.Capabilities} A self reference.
    296 */
    297webdriver.Capabilities.prototype.merge = function(other) {
    298 var caps = other instanceof webdriver.Capabilities ?
    299 other.caps_ : other;
    300 for (var key in caps) {
    301 if (caps.hasOwnProperty(key)) {
    302 this.set(key, caps[key]);
    303 }
    304 }
    305 return this;
    306};
    307
    308
    309/**
    310 * @param {string} key The capability to set.
    311 * @param {*} value The capability value. Capability values must be JSON
    312 * serializable. Pass {@code null} to unset the capability.
    313 * @return {!webdriver.Capabilities} A self reference.
    314 */
    315webdriver.Capabilities.prototype.set = function(key, value) {
    316 if (goog.isDefAndNotNull(value)) {
    317 this.caps_[key] = value;
    318 } else {
    319 delete this.caps_[key];
    320 }
    321 return this;
    322};
    323
    324
    325/**
    326 * @param {string} key The capability to return.
    327 * @return {*} The capability with the given key, or {@code null} if it has
    328 * not been set.
    329 */
    330webdriver.Capabilities.prototype.get = function(key) {
    331 var val = null;
    332 if (this.caps_.hasOwnProperty(key)) {
    333 val = this.caps_[key];
    334 }
    335 return goog.isDefAndNotNull(val) ? val : null;
    336};
    337
    338
    339/**
    340 * @param {string} key The capability to check.
    341 * @return {boolean} Whether the specified capability is set.
    342 */
    343webdriver.Capabilities.prototype.has = function(key) {
    344 return !!this.get(key);
    345};
    346
    347
    348/**
    349 * Sets the logging preferences. Preferences may be specified as a
    350 * {@link webdriver.logging.Preferences} instance, or a as a map of log-type to
    351 * log-level.
    352 * @param {!(webdriver.logging.Preferences|Object.<string, string>)} prefs The
    353 * logging preferences.
    354 * @return {!webdriver.Capabilities} A self reference.
    355 */
    356webdriver.Capabilities.prototype.setLoggingPrefs = function(prefs) {
    357 return this.set(webdriver.Capability.LOGGING_PREFS, prefs);
    358};
    359
    360
    361/**
    362 * Sets the proxy configuration for this instance.
    363 * @param {webdriver.ProxyConfig} proxy The desired proxy configuration.
    364 * @return {!webdriver.Capabilities} A self reference.
    365 */
    366webdriver.Capabilities.prototype.setProxy = function(proxy) {
    367 return this.set(webdriver.Capability.PROXY, proxy);
    368};
    369
    370
    371/**
    372 * Sets whether native events should be used.
    373 * @param {boolean} enabled Whether to enable native events.
    374 * @return {!webdriver.Capabilities} A self reference.
    375 */
    376webdriver.Capabilities.prototype.setEnableNativeEvents = function(enabled) {
    377 return this.set(webdriver.Capability.NATIVE_EVENTS, enabled);
    378};
    379
    380
    381/**
    382 * Sets how elements should be scrolled into view for interaction.
    383 * @param {number} behavior The desired scroll behavior: either 0 to align with
    384 * the top of the viewport or 1 to align with the bottom.
    385 * @return {!webdriver.Capabilities} A self reference.
    386 */
    387webdriver.Capabilities.prototype.setScrollBehavior = function(behavior) {
    388 return this.set(webdriver.Capability.ELEMENT_SCROLL_BEHAVIOR, behavior);
    389};
    390
    391
    392/**
    393 * Sets the default action to take with an unexpected alert before returning
    394 * an error.
    395 * @param {string} behavior The desired behavior; should be "accept", "dismiss",
    396 * or "ignore". Defaults to "dismiss".
    397 * @return {!webdriver.Capabilities} A self reference.
    398 */
    399webdriver.Capabilities.prototype.setAlertBehavior = function(behavior) {
    400 return this.set(webdriver.Capability.UNEXPECTED_ALERT_BEHAVIOR, behavior);
    401};
    \ 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 34c531d..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: 'mouseButtonDown',
    203 MOUSE_UP: 'mouseButtonUp',
    204 MOVE_TO: 'mouseMoveTo',
    205 SEND_KEYS_TO_ACTIVE_ELEMENT: 'sendKeysToActiveElement',
    206
    207 // These belong to the Advanced Touch API
    208 TOUCH_SINGLE_TAP: 'touchSingleTap',
    209 TOUCH_DOWN: 'touchDown',
    210 TOUCH_UP: 'touchUp',
    211 TOUCH_MOVE: 'touchMove',
    212 TOUCH_SCROLL: 'touchScroll',
    213 TOUCH_DOUBLE_TAP: 'touchDoubleTap',
    214 TOUCH_LONG_PRESS: 'touchLongPress',
    215 TOUCH_FLICK: 'touchFlick',
    216
    217 GET_AVAILABLE_LOG_TYPES: 'getAvailableLogTypes',
    218 GET_LOG: 'getLog',
    219 GET_SESSION_LOGS: 'getSessionLogs'
    220};
    221
    222
    223
    224/**
    225 * Handles the execution of {@code webdriver.Command} objects.
    226 * @interface
    227 */
    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 adc622f..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/http/http.js.src.html b/docs/source/lib/webdriver/http/http.js.src.html index 2222847..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/key.js.src.html b/docs/source/lib/webdriver/key.js.src.html index d494624..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 28134d4..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 fa077a9..5948e57 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 * Logging levels.
    24 * @enum {{value: number, name: string}}
    25 */
    26webdriver.logging.Level = {
    27 ALL: {value: Number.MIN_VALUE, name: 'ALL'},
    28 DEBUG: {value: 700, name: 'DEBUG'},
    29 INFO: {value: 800, name: 'INFO'},
    30 WARNING: {value: 900, name: 'WARNING'},
    31 SEVERE: {value: 1000, name: 'SEVERE'},
    32 OFF: {value: Number.MAX_VALUE, name: 'OFF'}
    33};
    34
    35
    36/**
    37 * Converts a level name or value to a {@link webdriver.logging.Level} value.
    38 * If the name/value is not recognized, {@link webdriver.logging.Level.ALL}
    39 * will be returned.
    40 * @param {(number|string)} nameOrValue The log level name, or value, to
    41 * convert .
    42 * @return {!webdriver.logging.Level} The converted level.
    43 */
    44webdriver.logging.getLevel = function(nameOrValue) {
    45 var predicate = goog.isString(nameOrValue) ?
    46 function(val) { return val.name === nameOrValue; } :
    47 function(val) { return val.value === nameOrValue; };
    48
    49 return goog.object.findValue(webdriver.logging.Level, predicate) ||
    50 webdriver.logging.Level.ALL;
    51};
    52
    53
    54/**
    55 * Common log types.
    56 * @enum {string}
    57 */
    58webdriver.logging.Type = {
    59 /** Logs originating from the browser. */
    60 BROWSER: 'browser',
    61 /** Logs from a WebDriver client. */
    62 CLIENT: 'client',
    63 /** Logs from a WebDriver implementation. */
    64 DRIVER: 'driver',
    65 /** Logs related to performance. */
    66 PERFORMANCE: 'performance',
    67 /** Logs from the remote server. */
    68 SERVER: 'server'
    69};
    70
    71
    72/**
    73 * Describes the log preferences for a WebDriver session.
    74 * @constructor
    75 */
    76webdriver.logging.Preferences = function() {
    77 /** @private {!Object.<string, webdriver.logging.Level>} */
    78 this.prefs_ = {};
    79};
    80
    81
    82/**
    83 * Sets the desired logging level for a particular log type.
    84 * @param {(string|webdriver.logging.Type)} type The log type.
    85 * @param {!webdriver.logging.Level} level The desired log level.
    86 */
    87webdriver.logging.Preferences.prototype.setLevel = function(type, level) {
    88 this.prefs_[type] = level;
    89};
    90
    91
    92/**
    93 * Converts this instance to its JSON representation.
    94 * @return {!Object.<string, string>} The JSON representation of this set of
    95 * preferences.
    96 */
    97webdriver.logging.Preferences.prototype.toJSON = function() {
    98 var obj = {};
    99 for (var type in this.prefs_) {
    100 if (this.prefs_.hasOwnProperty(type)) {
    101 obj[type] = this.prefs_[type].name;
    102 }
    103 }
    104 return obj;
    105};
    106
    107
    108/**
    109 * A single log entry.
    110 * @param {(!webdriver.logging.Level|string)} level The entry level.
    111 * @param {string} message The log message.
    112 * @param {number=} opt_timestamp The time this entry was generated, in
    113 * milliseconds since 0:00:00, January 1, 1970 UTC. If omitted, the
    114 * current time will be used.
    115 * @param {string=} opt_type The log type, if known.
    116 * @constructor
    117 */
    118webdriver.logging.Entry = function(level, message, opt_timestamp, opt_type) {
    119
    120 /** @type {!webdriver.logging.Level} */
    121 this.level =
    122 goog.isString(level) ? webdriver.logging.getLevel(level) : level;
    123
    124 /** @type {string} */
    125 this.message = message;
    126
    127 /** @type {number} */
    128 this.timestamp = goog.isNumber(opt_timestamp) ? opt_timestamp : goog.now();
    129
    130 /** @type {string} */
    131 this.type = opt_type || '';
    132};
    133
    134
    135/**
    136 * @return {{level: string, message: string, timestamp: number,
    137 * type: string}} The JSON representation of this entry.
    138 */
    139webdriver.logging.Entry.prototype.toJSON = function() {
    140 return {
    141 'level': this.level.name,
    142 'message': this.message,
    143 'timestamp': this.timestamp,
    144 'type': this.type
    145 };
    146};
    147
    148
    149/**
    150 * Converts a {@link goog.debug.LogRecord} into a
    151 * {@link webdriver.logging.Entry}.
    152 * @param {!goog.debug.LogRecord} logRecord The record to convert.
    153 * @param {string=} opt_type The log type.
    154 * @return {!webdriver.logging.Entry} The converted entry.
    155 */
    156webdriver.logging.Entry.fromClosureLogRecord = function(logRecord, opt_type) {
    157 var closureLevel = logRecord.getLevel();
    158 var level = webdriver.logging.Level.SEVERE;
    159
    160 if (closureLevel.value <= webdriver.logging.Level.DEBUG.value) {
    161 level = webdriver.logging.Level.DEBUG;
    162 } else if (closureLevel.value <= webdriver.logging.Level.INFO.value) {
    163 level = webdriver.logging.Level.INFO;
    164 } else if (closureLevel.value <= webdriver.logging.Level.WARNING.value) {
    165 level = webdriver.logging.Level.WARNING;
    166 }
    167
    168 return new webdriver.logging.Entry(
    169 level,
    170 '[' + logRecord.getLoggerName() + '] ' + logRecord.getMessage(),
    171 logRecord.getMillis(),
    172 opt_type);
    173};
    \ 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') {
    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/promise.js.src.html b/docs/source/lib/webdriver/promise.js.src.html index c596651..dee8fdb 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');
    54goog.provide('webdriver.promise.Thenable');
    55
    56goog.require('goog.array');
    57goog.require('goog.debug.Error');
    58goog.require('goog.object');
    59goog.require('webdriver.EventEmitter');
    60goog.require('webdriver.stacktrace.Snapshot');
    61
    62
    63
    64/**
    65 * Thenable is a promise-like object with a {@code then} method which may be
    66 * used to schedule callbacks on a promised value.
    67 *
    68 * @interface
    69 * @template T
    70 */
    71webdriver.promise.Thenable = function() {};
    72
    73
    74/**
    75 * Cancels the computation of this promise's value, rejecting the promise in the
    76 * process. This method is a no-op if the promise has alreayd been resolved.
    77 *
    78 * @param {*=} opt_reason The reason this promise is being cancelled. If not an
    79 * {@code Error}, one will be created using the value's string
    80 * representation.
    81 */
    82webdriver.promise.Thenable.prototype.cancel = function(opt_reason) {};
    83
    84
    85/** @return {boolean} Whether this promise's value is still being computed. */
    86webdriver.promise.Thenable.prototype.isPending = function() {};
    87
    88
    89/**
    90 * Registers listeners for when this instance is resolved.
    91 *
    92 * @param {?(function(T): (R|webdriver.promise.Promise.<R>))=} opt_callback The
    93 * function to call if this promise is successfully resolved. The function
    94 * should expect a single argument: the promise's resolved value.
    95 * @param {?(function(*): (R|webdriver.promise.Promise.<R>))=} opt_errback The
    96 * function to call if this promise is rejected. The function should expect
    97 * a single argument: the rejection reason.
    98 * @return {!webdriver.promise.Promise.<R>} A new promise which will be
    99 * resolved with the result of the invoked callback.
    100 * @template R
    101 */
    102webdriver.promise.Thenable.prototype.then = function(
    103 opt_callback, opt_errback) {};
    104
    105
    106/**
    107 * Registers a listener for when this promise is rejected. This is synonymous
    108 * with the {@code catch} clause in a synchronous API:
    109 * <pre><code>
    110 * // Synchronous API:
    111 * try {
    112 * doSynchronousWork();
    113 * } catch (ex) {
    114 * console.error(ex);
    115 * }
    116 *
    117 * // Asynchronous promise API:
    118 * doAsynchronousWork().thenCatch(function(ex) {
    119 * console.error(ex);
    120 * });
    121 * </code></pre>
    122 *
    123 * @param {function(*): (R|webdriver.promise.Promise.<R>)} errback The function
    124 * to call if this promise is rejected. The function should expect a single
    125 * argument: the rejection reason.
    126 * @return {!webdriver.promise.Promise.<R>} A new promise which will be
    127 * resolved with the result of the invoked callback.
    128 * @template R
    129 */
    130webdriver.promise.Thenable.prototype.thenCatch = function(errback) {};
    131
    132
    133/**
    134 * Registers a listener to invoke when this promise is resolved, regardless
    135 * of whether the promise's value was successfully computed. This function
    136 * is synonymous with the {@code finally} clause in a synchronous API:
    137 * <pre><code>
    138 * // Synchronous API:
    139 * try {
    140 * doSynchronousWork();
    141 * } finally {
    142 * cleanUp();
    143 * }
    144 *
    145 * // Asynchronous promise API:
    146 * doAsynchronousWork().thenFinally(cleanUp);
    147 * </code></pre>
    148 *
    149 * <b>Note:</b> similar to the {@code finally} clause, if the registered
    150 * callback returns a rejected promise or throws an error, it will silently
    151 * replace the rejection error (if any) from this promise:
    152 * <pre><code>
    153 * try {
    154 * throw Error('one');
    155 * } finally {
    156 * throw Error('two'); // Hides Error: one
    157 * }
    158 *
    159 * webdriver.promise.rejected(Error('one'))
    160 * .thenFinally(function() {
    161 * throw Error('two'); // Hides Error: one
    162 * });
    163 * </code></pre>
    164 *
    165 *
    166 * @param {function(): (R|webdriver.promise.Promise.<R>)} callback The function
    167 * to call when this promise is resolved.
    168 * @return {!webdriver.promise.Promise.<R>} A promise that will be fulfilled
    169 * with the callback result.
    170 * @template R
    171 */
    172webdriver.promise.Thenable.prototype.thenFinally = function(callback) {};
    173
    174
    175/**
    176 * Property used to flag constructor's as implementing the Thenable interface
    177 * for runtime type checking.
    178 * @private {string}
    179 * @const
    180 */
    181webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_ = '$webdriver_Thenable';
    182
    183
    184/**
    185 * Adds a property to a class prototype to allow runtime checks of whether
    186 * instances of that class implement the Thenable interface. This function will
    187 * also ensure the prototype's {@code then} function is exported from compiled
    188 * code.
    189 * @param {function(new: webdriver.promise.Thenable, ...[?])} ctor The
    190 * constructor whose prototype to modify.
    191 */
    192webdriver.promise.Thenable.addImplementation = function(ctor) {
    193 // Based on goog.promise.Thenable.isImplementation.
    194 ctor.prototype['then'] = ctor.prototype.then;
    195 try {
    196 // Old IE7 does not support defineProperty; IE8 only supports it for
    197 // DOM elements.
    198 Object.defineProperty(
    199 ctor.prototype,
    200 webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_,
    201 {'value': true, 'enumerable': false});
    202 } catch (ex) {
    203 ctor.prototype[webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_] = true;
    204 }
    205};
    206
    207
    208/**
    209 * Checks if an object has been tagged for implementing the Thenable interface
    210 * as defined by {@link webdriver.promise.Thenable.addImplementation}.
    211 * @param {*} object The object to test.
    212 * @return {boolean} Whether the object is an implementation of the Thenable
    213 * interface.
    214 */
    215webdriver.promise.Thenable.isImplementation = function(object) {
    216 // Based on goog.promise.Thenable.isImplementation.
    217 if (!object) {
    218 return false;
    219 }
    220 try {
    221 return !!object[webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_];
    222 } catch (e) {
    223 return false; // Property access seems to be forbidden.
    224 }
    225};
    226
    227
    228
    229/**
    230 * Represents the eventual value of a completed operation. Each promise may be
    231 * in one of three states: pending, resolved, or rejected. Each promise starts
    232 * in the pending state and may make a single transition to either a
    233 * fulfilled or rejected state, at which point the promise is considered
    234 * resolved.
    235 *
    236 * @constructor
    237 * @implements {webdriver.promise.Thenable.<T>}
    238 * @template T
    239 * @see http://promises-aplus.github.io/promises-spec/
    240 */
    241webdriver.promise.Promise = function() {};
    242webdriver.promise.Thenable.addImplementation(webdriver.promise.Promise);
    243
    244
    245/** @override */
    246webdriver.promise.Promise.prototype.cancel = function(reason) {
    247 throw new TypeError('Unimplemented function: "cancel"');
    248};
    249
    250
    251/** @override */
    252webdriver.promise.Promise.prototype.isPending = function() {
    253 throw new TypeError('Unimplemented function: "isPending"');
    254};
    255
    256
    257/** @override */
    258webdriver.promise.Promise.prototype.then = function(
    259 opt_callback, opt_errback) {
    260 throw new TypeError('Unimplemented function: "then"');
    261};
    262
    263
    264/** @override */
    265webdriver.promise.Promise.prototype.thenCatch = function(errback) {
    266 return this.then(null, errback);
    267};
    268
    269
    270/** @override */
    271webdriver.promise.Promise.prototype.thenFinally = function(callback) {
    272 return this.then(callback, function(err) {
    273 var value = callback();
    274 if (webdriver.promise.isPromise(value)) {
    275 return value.then(function() {
    276 throw err;
    277 });
    278 }
    279 throw err;
    280 });
    281};
    282
    283
    284
    285/**
    286 * Represents a value that will be resolved at some point in the future. This
    287 * class represents the protected "producer" half of a Promise - each Deferred
    288 * has a {@code promise} property that may be returned to consumers for
    289 * registering callbacks, reserving the ability to resolve the deferred to the
    290 * producer.
    291 *
    292 * <p>If this Deferred is rejected and there are no listeners registered before
    293 * the next turn of the event loop, the rejection will be passed to the
    294 * {@link webdriver.promise.ControlFlow} as an unhandled failure.
    295 *
    296 * <p>If this Deferred is cancelled, the cancellation reason will be forward to
    297 * the Deferred's canceller function (if provided). The canceller may return a
    298 * truth-y value to override the reason provided for rejection.
    299 *
    300 * @param {Function=} opt_canceller Function to call when cancelling the
    301 * computation of this instance's value.
    302 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow
    303 * this instance was created under. This should only be provided during
    304 * unit tests.
    305 * @constructor
    306 * @extends {webdriver.promise.Promise.<T>}
    307 * @template T
    308 */
    309webdriver.promise.Deferred = function(opt_canceller, opt_flow) {
    310 /* NOTE: This class's implementation diverges from the prototypical style
    311 * used in the rest of the atoms library. This was done intentionally to
    312 * protect the internal Deferred state from consumers, as outlined by
    313 * http://wiki.commonjs.org/wiki/Promises
    314 */
    315 goog.base(this);
    316
    317 var flow = opt_flow || webdriver.promise.controlFlow();
    318
    319 /**
    320 * The listeners registered with this Deferred. Each element in the list will
    321 * be a 3-tuple of the callback function, errback function, and the
    322 * corresponding deferred object.
    323 * @type {!Array.<!webdriver.promise.Deferred.Listener_>}
    324 */
    325 var listeners = [];
    326
    327 /**
    328 * Whether this Deferred's resolution was ever handled by a listener.
    329 * If the Deferred is rejected and its value is not handled by a listener
    330 * before the next turn of the event loop, the error will be passed to the
    331 * global error handler.
    332 * @type {boolean}
    333 */
    334 var handled = false;
    335
    336 /**
    337 * Key for the timeout used to delay reproting an unhandled rejection to the
    338 * parent {@link webdriver.promise.ControlFlow}.
    339 * @type {?number}
    340 */
    341 var pendingRejectionKey = null;
    342
    343 /**
    344 * This Deferred's current state.
    345 * @type {!webdriver.promise.Deferred.State_}
    346 */
    347 var state = webdriver.promise.Deferred.State_.PENDING;
    348
    349 /**
    350 * This Deferred's resolved value; set when the state transitions from
    351 * {@code webdriver.promise.Deferred.State_.PENDING}.
    352 * @type {*}
    353 */
    354 var value;
    355
    356 /** @return {boolean} Whether this promise's value is still pending. */
    357 function isPending() {
    358 return state == webdriver.promise.Deferred.State_.PENDING;
    359 }
    360
    361 /**
    362 * Removes all of the listeners previously registered on this deferred.
    363 * @throws {Error} If this deferred has already been resolved.
    364 */
    365 function removeAll() {
    366 listeners = [];
    367 }
    368
    369 /**
    370 * Resolves this deferred. If the new value is a promise, this function will
    371 * wait for it to be resolved before notifying the registered listeners.
    372 * @param {!webdriver.promise.Deferred.State_} newState The deferred's new
    373 * state.
    374 * @param {*} newValue The deferred's new value.
    375 */
    376 function resolve(newState, newValue) {
    377 if (webdriver.promise.Deferred.State_.PENDING !== state) {
    378 return;
    379 }
    380
    381 if (newValue === self) {
    382 // See promise a+, 2.3.1
    383 // http://promises-aplus.github.io/promises-spec/#point-48
    384 throw TypeError('A promise may not resolve to itself');
    385 }
    386
    387 state = webdriver.promise.Deferred.State_.BLOCKED;
    388
    389 if (webdriver.promise.isPromise(newValue)) {
    390 var onFulfill = goog.partial(notifyAll, newState);
    391 var onReject = goog.partial(
    392 notifyAll, webdriver.promise.Deferred.State_.REJECTED);
    393 if (newValue instanceof webdriver.promise.Deferred) {
    394 newValue.then(onFulfill, onReject);
    395 } else {
    396 webdriver.promise.asap(newValue, onFulfill, onReject);
    397 }
    398
    399 } else {
    400 notifyAll(newState, newValue);
    401 }
    402 }
    403
    404 /**
    405 * Notifies all of the listeners registered with this Deferred that its state
    406 * has changed.
    407 * @param {!webdriver.promise.Deferred.State_} newState The deferred's new
    408 * state.
    409 * @param {*} newValue The deferred's new value.
    410 */
    411 function notifyAll(newState, newValue) {
    412 if (newState === webdriver.promise.Deferred.State_.REJECTED &&
    413 // We cannot check instanceof Error since the object may have been
    414 // created in a different JS context.
    415 goog.isObject(newValue) && goog.isString(newValue.message)) {
    416 newValue = flow.annotateError(/** @type {!Error} */(newValue));
    417 }
    418
    419 state = newState;
    420 value = newValue;
    421 while (listeners.length) {
    422 notify(listeners.shift());
    423 }
    424
    425 if (!handled && state == webdriver.promise.Deferred.State_.REJECTED) {
    426 flow.pendingRejections_ += 1;
    427 pendingRejectionKey = flow.timer.setTimeout(function() {
    428 pendingRejectionKey = null;
    429 flow.pendingRejections_ -= 1;
    430 flow.abortFrame_(value);
    431 }, 0);
    432 }
    433 }
    434
    435 /**
    436 * Notifies a single listener of this Deferred's change in state.
    437 * @param {!webdriver.promise.Deferred.Listener_} listener The listener to
    438 * notify.
    439 */
    440 function notify(listener) {
    441 var func = state == webdriver.promise.Deferred.State_.RESOLVED ?
    442 listener.callback : listener.errback;
    443 if (func) {
    444 flow.runInNewFrame_(goog.partial(func, value),
    445 listener.fulfill, listener.reject);
    446 } else if (state == webdriver.promise.Deferred.State_.REJECTED) {
    447 listener.reject(value);
    448 } else {
    449 listener.fulfill(value);
    450 }
    451 }
    452
    453 /**
    454 * The consumer promise for this instance. Provides protected access to the
    455 * callback registering functions.
    456 * @type {!webdriver.promise.Promise.<T>}
    457 */
    458 var promise = new webdriver.promise.Promise();
    459
    460 /**
    461 * Registers a callback on this Deferred.
    462 *
    463 * @param {?(function(T): (R|webdriver.promise.Promise.<R>))=} opt_callback .
    464 * @param {?(function(*): (R|webdriver.promise.Promise.<R>))=} opt_errback .
    465 * @return {!webdriver.promise.Promise.<R>} A new promise representing the
    466 * result of the callback.
    467 * @template R
    468 * @see webdriver.promise.Promise#then
    469 */
    470 function then(opt_callback, opt_errback) {
    471 // Avoid unnecessary allocations if we weren't given any callback functions.
    472 if (!opt_callback && !opt_errback) {
    473 return promise;
    474 }
    475
    476 // The moment a listener is registered, we consider this deferred to be
    477 // handled; the callback must handle any rejection errors.
    478 handled = true;
    479 if (pendingRejectionKey !== null) {
    480 flow.pendingRejections_ -= 1;
    481 flow.timer.clearTimeout(pendingRejectionKey);
    482 pendingRejectionKey = null;
    483 }
    484
    485 var deferred = new webdriver.promise.Deferred(cancel, flow);
    486 var listener = {
    487 callback: opt_callback,
    488 errback: opt_errback,
    489 fulfill: deferred.fulfill,
    490 reject: deferred.reject
    491 };
    492
    493 if (state == webdriver.promise.Deferred.State_.PENDING ||
    494 state == webdriver.promise.Deferred.State_.BLOCKED) {
    495 listeners.push(listener);
    496 } else {
    497 notify(listener);
    498 }
    499
    500 return deferred.promise;
    501 }
    502
    503 var self = this;
    504
    505 /**
    506 * Resolves this promise with the given value. If the value is itself a
    507 * promise and not a reference to this deferred, this instance will wait for
    508 * it before resolving.
    509 * @param {T=} opt_value The fulfilled value.
    510 */
    511 function fulfill(opt_value) {
    512 resolve(webdriver.promise.Deferred.State_.RESOLVED, opt_value);
    513 }
    514
    515 /**
    516 * Rejects this promise. If the error is itself a promise, this instance will
    517 * be chained to it and be rejected with the error's resolved value.
    518 * @param {*=} opt_error The rejection reason, typically either a
    519 * {@code Error} or a {@code string}.
    520 */
    521 function reject(opt_error) {
    522 resolve(webdriver.promise.Deferred.State_.REJECTED, opt_error);
    523 }
    524
    525 /**
    526 * Attempts to cancel the computation of this instance's value. This attempt
    527 * will silently fail if this instance has already resolved.
    528 * @param {*=} opt_reason The reason for cancelling this promise.
    529 */
    530 function cancel(opt_reason) {
    531 if (!isPending()) {
    532 return;
    533 }
    534
    535 if (opt_canceller) {
    536 opt_reason = opt_canceller(opt_reason) || opt_reason;
    537 }
    538
    539 reject(opt_reason);
    540 }
    541
    542 this.promise = promise;
    543 this.promise.then = this.then = then;
    544 this.promise.cancel = this.cancel = cancel;
    545 this.promise.isPending = this.isPending = isPending;
    546 this.fulfill = fulfill;
    547 this.reject = this.errback = reject;
    548
    549 // Only expose this function to our internal classes.
    550 // TODO: find a cleaner way of handling this.
    551 if (this instanceof webdriver.promise.Task_) {
    552 this.removeAll = removeAll;
    553 }
    554
    555 // Export symbols necessary for the contract on this object to work in
    556 // compiled mode.
    557 goog.exportProperty(this, 'then', this.then);
    558 goog.exportProperty(this, 'cancel', cancel);
    559 goog.exportProperty(this, 'fulfill', fulfill);
    560 goog.exportProperty(this, 'reject', reject);
    561 goog.exportProperty(this, 'isPending', isPending);
    562 goog.exportProperty(this, 'promise', this.promise);
    563 goog.exportProperty(this.promise, 'then', this.then);
    564 goog.exportProperty(this.promise, 'cancel', cancel);
    565 goog.exportProperty(this.promise, 'isPending', isPending);
    566};
    567goog.inherits(webdriver.promise.Deferred, webdriver.promise.Promise);
    568
    569
    570/**
    571 * Type definition for a listener registered on a Deferred object.
    572 * @typedef {{callback:(Function|undefined),
    573 * errback:(Function|undefined),
    574 * fulfill: function(*), reject: function(*)}}
    575 * @private
    576 */
    577webdriver.promise.Deferred.Listener_;
    578
    579
    580/**
    581 * The three states a {@link webdriver.promise.Deferred} object may be in.
    582 * @enum {number}
    583 * @private
    584 */
    585webdriver.promise.Deferred.State_ = {
    586 REJECTED: -1,
    587 PENDING: 0,
    588 BLOCKED: 1,
    589 RESOLVED: 2
    590};
    591
    592
    593/**
    594 * Tests if a value is an Error-like object. This is more than an straight
    595 * instanceof check since the value may originate from another context.
    596 * @param {*} value The value to test.
    597 * @return {boolean} Whether the value is an error.
    598 * @private
    599 */
    600webdriver.promise.isError_ = function(value) {
    601 return value instanceof Error ||
    602 goog.isObject(value) &&
    603 (Object.prototype.toString.call(value) === '[object Error]' ||
    604 // A special test for goog.testing.JsUnitException.
    605 value.isJsUnitException);
    606
    607};
    608
    609
    610/**
    611 * Determines whether a {@code value} should be treated as a promise.
    612 * Any object whose "then" property is a function will be considered a promise.
    613 *
    614 * @param {*} value The value to test.
    615 * @return {boolean} Whether the value is a promise.
    616 */
    617webdriver.promise.isPromise = function(value) {
    618 return !!value && goog.isObject(value) &&
    619 // Use array notation so the Closure compiler does not obfuscate away our
    620 // contract.
    621 goog.isFunction(value['then']);
    622};
    623
    624
    625/**
    626 * Creates a promise that will be resolved at a set time in the future.
    627 * @param {number} ms The amount of time, in milliseconds, to wait before
    628 * resolving the promise.
    629 * @return {!webdriver.promise.Promise} The promise.
    630 */
    631webdriver.promise.delayed = function(ms) {
    632 var timer = webdriver.promise.controlFlow().timer;
    633 var key;
    634 var deferred = new webdriver.promise.Deferred(function() {
    635 timer.clearTimeout(key);
    636 });
    637 key = timer.setTimeout(deferred.fulfill, ms);
    638 return deferred.promise;
    639};
    640
    641
    642/**
    643 * Creates a new deferred object.
    644 * @param {Function=} opt_canceller Function to call when cancelling the
    645 * computation of this instance's value.
    646 * @return {!webdriver.promise.Deferred.<T>} The new deferred object.
    647 * @template T
    648 */
    649webdriver.promise.defer = function(opt_canceller) {
    650 return new webdriver.promise.Deferred(opt_canceller);
    651};
    652
    653
    654/**
    655 * Creates a promise that has been resolved with the given value.
    656 * @param {T=} opt_value The resolved value.
    657 * @return {!webdriver.promise.Promise.<T>} The resolved promise.
    658 * @template T
    659 */
    660webdriver.promise.fulfilled = function(opt_value) {
    661 if (opt_value instanceof webdriver.promise.Promise) {
    662 return opt_value;
    663 }
    664 var deferred = new webdriver.promise.Deferred();
    665 deferred.fulfill(opt_value);
    666 return deferred.promise;
    667};
    668
    669
    670/**
    671 * Creates a promise that has been rejected with the given reason.
    672 * @param {*=} opt_reason The rejection reason; may be any value, but is
    673 * usually an Error or a string.
    674 * @return {!webdriver.promise.Promise.<T>} The rejected promise.
    675 * @template T
    676 */
    677webdriver.promise.rejected = function(opt_reason) {
    678 var deferred = new webdriver.promise.Deferred();
    679 deferred.reject(opt_reason);
    680 return deferred.promise;
    681};
    682
    683
    684/**
    685 * Wraps a function that is assumed to be a node-style callback as its final
    686 * argument. This callback takes two arguments: an error value (which will be
    687 * null if the call succeeded), and the success value as the second argument.
    688 * If the call fails, the returned promise will be rejected, otherwise it will
    689 * be resolved with the result.
    690 * @param {!Function} fn The function to wrap.
    691 * @param {...?} var_args The arguments to apply to the function, excluding the
    692 * final callback.
    693 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
    694 * result of the provided function's callback.
    695 */
    696webdriver.promise.checkedNodeCall = function(fn, var_args) {
    697 var deferred = new webdriver.promise.Deferred(function() {
    698 throw Error('This Deferred may not be cancelled');
    699 });
    700 try {
    701 var args = goog.array.slice(arguments, 1);
    702 args.push(function(error, value) {
    703 error ? deferred.reject(error) : deferred.fulfill(value);
    704 });
    705 fn.apply(null, args);
    706 } catch (ex) {
    707 deferred.reject(ex);
    708 }
    709 return deferred.promise;
    710};
    711
    712
    713/**
    714 * Registers an observer on a promised {@code value}, returning a new promise
    715 * that will be resolved when the value is. If {@code value} is not a promise,
    716 * then the return promise will be immediately resolved.
    717 * @param {*} value The value to observe.
    718 * @param {Function=} opt_callback The function to call when the value is
    719 * resolved successfully.
    720 * @param {Function=} opt_errback The function to call when the value is
    721 * rejected.
    722 * @return {!webdriver.promise.Promise} A new promise.
    723 */
    724webdriver.promise.when = function(value, opt_callback, opt_errback) {
    725 if (webdriver.promise.Thenable.isImplementation(value)) {
    726 return value.then(opt_callback, opt_errback);
    727 }
    728
    729 var deferred = new webdriver.promise.Deferred();
    730
    731 webdriver.promise.asap(value, deferred.fulfill, deferred.reject);
    732
    733 return deferred.then(opt_callback, opt_errback);
    734};
    735
    736
    737/**
    738 * Invokes the appropriate callback function as soon as a promised
    739 * {@code value} is resolved. This function is similar to
    740 * {@link webdriver.promise.when}, except it does not return a new promise.
    741 * @param {*} value The value to observe.
    742 * @param {Function} callback The function to call when the value is
    743 * resolved successfully.
    744 * @param {Function=} opt_errback The function to call when the value is
    745 * rejected.
    746 */
    747webdriver.promise.asap = function(value, callback, opt_errback) {
    748 if (webdriver.promise.isPromise(value)) {
    749 value.then(callback, opt_errback);
    750
    751 // Maybe a Dojo-like deferred object?
    752 } else if (!!value && goog.isObject(value) &&
    753 goog.isFunction(value.addCallbacks)) {
    754 value.addCallbacks(callback, opt_errback);
    755
    756 // A raw value, return a resolved promise.
    757 } else if (callback) {
    758 callback(value);
    759 }
    760};
    761
    762
    763/**
    764 * Given an array of promises, will return a promise that will be fulfilled
    765 * with the fulfillment values of the input array's values. If any of the
    766 * input array's promises are rejected, the returned promise will be rejected
    767 * with the same reason.
    768 *
    769 * @param {!Array.<(T|!webdriver.promise.Promise.<T>)>} arr An array of
    770 * promises to wait on.
    771 * @return {!webdriver.promise.Promise.<!Array.<T>>} A promise that is
    772 * fulfilled with an array containing the fulfilled values of the
    773 * input array, or rejected with the same reason as the first
    774 * rejected value.
    775 * @template T
    776 */
    777webdriver.promise.all = function(arr) {
    778 var n = arr.length;
    779 if (!n) {
    780 return webdriver.promise.fulfilled([]);
    781 }
    782
    783 var toFulfill = n;
    784 var result = webdriver.promise.defer();
    785 var values = [];
    786
    787 var onFulfill = function(index, value) {
    788 values[index] = value;
    789 toFulfill--;
    790 if (toFulfill == 0) {
    791 result.fulfill(values);
    792 }
    793 };
    794
    795 for (var i = 0; i < n; ++i) {
    796 webdriver.promise.asap(
    797 arr[i], goog.partial(onFulfill, i), result.reject);
    798 }
    799
    800 return result.promise;
    801};
    802
    803
    804/**
    805 * Calls a function for each element in an array and inserts the result into a
    806 * new array, which is used as the fulfillment value of the promise returned
    807 * by this function.
    808 *
    809 * <p>If the return value of the mapping function is a promise, this function
    810 * will wait for it to be fulfilled before inserting it into the new array.
    811 *
    812 * <p>If the mapping function throws or returns a rejected promise, the
    813 * promise returned by this function will be rejected with the same reason.
    814 * Only the first failure will be reported; all subsequent errors will be
    815 * silently ignored.
    816 *
    817 * @param {!(Array.<TYPE>|webdriver.promise.Promise.<!Array.<TYPE>>)} arr The
    818 * array to iterator over, or a promise that will resolve to said array.
    819 * @param {function(this: SELF, TYPE, number, !Array.<TYPE>): ?} fn The
    820 * function to call for each element in the array. This function should
    821 * expect three arguments (the element, the index, and the array itself.
    822 * @param {SELF=} opt_self The object to be used as the value of 'this' within
    823 * {@code fn}.
    824 * @template TYPE, SELF
    825 */
    826webdriver.promise.map = function(arr, fn, opt_self) {
    827 return webdriver.promise.when(arr, function(arr) {
    828 var result = goog.array.map(arr, fn, opt_self);
    829 return webdriver.promise.all(result);
    830 });
    831};
    832
    833
    834/**
    835 * Calls a function for each element in an array, and if the function returns
    836 * true adds the element to a new array.
    837 *
    838 * <p>If the return value of the filter function is a promise, this function
    839 * will wait for it to be fulfilled before determining whether to insert the
    840 * element into the new array.
    841 *
    842 * <p>If the filter function throws or returns a rejected promise, the promise
    843 * returned by this function will be rejected with the same reason. Only the
    844 * first failure will be reported; all subsequent errors will be silently
    845 * ignored.
    846 *
    847 * @param {!(Array.<TYPE>|webdriver.promise.Promise.<!Array.<TYPE>>)} arr The
    848 * array to iterator over, or a promise that will resolve to said array.
    849 * @param {function(this: SELF, TYPE, number, !Array.<TYPE>): (
    850 * boolean|webdriver.promise.Promise.<boolean>)} fn The function
    851 * to call for each element in the array.
    852 * @param {SELF=} opt_self The object to be used as the value of 'this' within
    853 * {@code fn}.
    854 * @template TYPE, SELF
    855 */
    856webdriver.promise.filter = function(arr, fn, opt_self) {
    857 return webdriver.promise.when(arr, function(arr) {
    858 var originalValues = goog.array.clone(arr);
    859 return webdriver.promise.map(arr, fn, opt_self).then(function(include) {
    860 return goog.array.filter(originalValues, function(value, index) {
    861 return include[index];
    862 });
    863 });
    864 });
    865};
    866
    867
    868/**
    869 * Returns a promise that will be resolved with the input value in a
    870 * fully-resolved state. If the value is an array, each element will be fully
    871 * resolved. Likewise, if the value is an object, all keys will be fully
    872 * resolved. In both cases, all nested arrays and objects will also be
    873 * fully resolved. All fields are resolved in place; the returned promise will
    874 * resolve on {@code value} and not a copy.
    875 *
    876 * Warning: This function makes no checks against objects that contain
    877 * cyclical references:
    878 * <pre><code>
    879 * var value = {};
    880 * value['self'] = value;
    881 * webdriver.promise.fullyResolved(value); // Stack overflow.
    882 * </code></pre>
    883 *
    884 * @param {*} value The value to fully resolve.
    885 * @return {!webdriver.promise.Promise} A promise for a fully resolved version
    886 * of the input value.
    887 */
    888webdriver.promise.fullyResolved = function(value) {
    889 if (webdriver.promise.isPromise(value)) {
    890 return webdriver.promise.when(value, webdriver.promise.fullyResolveValue_);
    891 }
    892 return webdriver.promise.fullyResolveValue_(value);
    893};
    894
    895
    896/**
    897 * @param {*} value The value to fully resolve. If a promise, assumed to
    898 * already be resolved.
    899 * @return {!webdriver.promise.Promise} A promise for a fully resolved version
    900 * of the input value.
    901 * @private
    902 */
    903webdriver.promise.fullyResolveValue_ = function(value) {
    904 switch (goog.typeOf(value)) {
    905 case 'array':
    906 return webdriver.promise.fullyResolveKeys_(
    907 /** @type {!Array} */ (value));
    908
    909 case 'object':
    910 if (webdriver.promise.isPromise(value)) {
    911 // We get here when the original input value is a promise that
    912 // resolves to itself. When the user provides us with such a promise,
    913 // trust that it counts as a "fully resolved" value and return it.
    914 // Of course, since it's already a promise, we can just return it
    915 // to the user instead of wrapping it in another promise.
    916 return /** @type {!webdriver.promise.Promise} */ (value);
    917 }
    918
    919 if (goog.isNumber(value.nodeType) &&
    920 goog.isObject(value.ownerDocument) &&
    921 goog.isNumber(value.ownerDocument.nodeType)) {
    922 // DOM node; return early to avoid infinite recursion. Should we
    923 // only support objects with a certain level of nesting?
    924 return webdriver.promise.fulfilled(value);
    925 }
    926
    927 return webdriver.promise.fullyResolveKeys_(
    928 /** @type {!Object} */ (value));
    929
    930 default: // boolean, function, null, number, string, undefined
    931 return webdriver.promise.fulfilled(value);
    932 }
    933};
    934
    935
    936/**
    937 * @param {!(Array|Object)} obj the object to resolve.
    938 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
    939 * input object once all of its values have been fully resolved.
    940 * @private
    941 */
    942webdriver.promise.fullyResolveKeys_ = function(obj) {
    943 var isArray = goog.isArray(obj);
    944 var numKeys = isArray ? obj.length : goog.object.getCount(obj);
    945 if (!numKeys) {
    946 return webdriver.promise.fulfilled(obj);
    947 }
    948
    949 var numResolved = 0;
    950 var deferred = new webdriver.promise.Deferred();
    951
    952 // In pre-IE9, goog.array.forEach will not iterate properly over arrays
    953 // containing undefined values because "index in array" returns false
    954 // when array[index] === undefined (even for x = [undefined, 1]). To get
    955 // around this, we need to use our own forEach implementation.
    956 // DO NOT REMOVE THIS UNTIL WE NO LONGER SUPPORT IE8. This cannot be
    957 // reproduced in IE9 by changing the browser/document modes, it requires an
    958 // actual pre-IE9 browser. Yay, IE!
    959 var forEachKey = !isArray ? goog.object.forEach : function(arr, fn) {
    960 var n = arr.length;
    961 for (var i = 0; i < n; ++i) {
    962 fn.call(null, arr[i], i, arr);
    963 }
    964 };
    965
    966 forEachKey(obj, function(partialValue, key) {
    967 var type = goog.typeOf(partialValue);
    968 if (type != 'array' && type != 'object') {
    969 maybeResolveValue();
    970 return;
    971 }
    972
    973 webdriver.promise.fullyResolved(partialValue).then(
    974 function(resolvedValue) {
    975 obj[key] = resolvedValue;
    976 maybeResolveValue();
    977 },
    978 deferred.reject);
    979 });
    980
    981 return deferred.promise;
    982
    983 function maybeResolveValue() {
    984 if (++numResolved == numKeys) {
    985 deferred.fulfill(obj);
    986 }
    987 }
    988};
    989
    990
    991//////////////////////////////////////////////////////////////////////////////
    992//
    993// webdriver.promise.ControlFlow
    994//
    995//////////////////////////////////////////////////////////////////////////////
    996
    997
    998
    999/**
    1000 * Handles the execution of scheduled tasks, each of which may be an
    1001 * asynchronous operation. The control flow will ensure tasks are executed in
    1002 * the ordered scheduled, starting each task only once those before it have
    1003 * completed.
    1004 *
    1005 * <p>Each task scheduled within this flow may return a
    1006 * {@link webdriver.promise.Promise} to indicate it is an asynchronous
    1007 * operation. The ControlFlow will wait for such promises to be resolved before
    1008 * marking the task as completed.
    1009 *
    1010 * <p>Tasks and each callback registered on a {@link webdriver.promise.Deferred}
    1011 * will be run in their own ControlFlow frame. Any tasks scheduled within a
    1012 * frame will have priority over previously scheduled tasks. Furthermore, if
    1013 * any of the tasks in the frame fails, the remainder of the tasks in that frame
    1014 * will be discarded and the failure will be propagated to the user through the
    1015 * callback/task's promised result.
    1016 *
    1017 * <p>Each time a ControlFlow empties its task queue, it will fire an
    1018 * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Conversely,
    1019 * whenever the flow terminates due to an unhandled error, it will remove all
    1020 * remaining tasks in its queue and fire an
    1021 * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION} event. If
    1022 * there are no listeners registered with the flow, the error will be
    1023 * rethrown to the global error handler.
    1024 *
    1025 * @param {webdriver.promise.ControlFlow.Timer=} opt_timer The timer object
    1026 * to use. Should only be set for testing.
    1027 * @constructor
    1028 * @extends {webdriver.EventEmitter}
    1029 */
    1030webdriver.promise.ControlFlow = function(opt_timer) {
    1031 webdriver.EventEmitter.call(this);
    1032
    1033 /**
    1034 * The timer used by this instance.
    1035 * @type {webdriver.promise.ControlFlow.Timer}
    1036 */
    1037 this.timer = opt_timer || webdriver.promise.ControlFlow.defaultTimer;
    1038
    1039 /**
    1040 * A list of recent tasks. Each time a new task is started, or a frame is
    1041 * completed, the previously recorded task is removed from this list. If
    1042 * there are multiple tasks, task N+1 is considered a sub-task of task
    1043 * N.
    1044 * @private {!Array.<!webdriver.promise.Task_>}
    1045 */
    1046 this.history_ = [];
    1047};
    1048goog.inherits(webdriver.promise.ControlFlow, webdriver.EventEmitter);
    1049
    1050
    1051/**
    1052 * @typedef {{clearInterval: function(number),
    1053 * clearTimeout: function(number),
    1054 * setInterval: function(!Function, number): number,
    1055 * setTimeout: function(!Function, number): number}}
    1056 */
    1057webdriver.promise.ControlFlow.Timer;
    1058
    1059
    1060/**
    1061 * The default timer object, which uses the global timer functions.
    1062 * @type {webdriver.promise.ControlFlow.Timer}
    1063 */
    1064webdriver.promise.ControlFlow.defaultTimer = (function() {
    1065 // The default timer functions may be defined as free variables for the
    1066 // current context, so do not reference them using "window" or
    1067 // "goog.global". Also, we must invoke them in a closure, and not using
    1068 // bind(), so we do not get "TypeError: Illegal invocation" (WebKit) or
    1069 // "Invalid calling object" (IE) errors.
    1070 return {
    1071 clearInterval: wrap(clearInterval),
    1072 clearTimeout: wrap(clearTimeout),
    1073 setInterval: wrap(setInterval),
    1074 setTimeout: wrap(setTimeout)
    1075 };
    1076
    1077 function wrap(fn) {
    1078 return function() {
    1079 // Cannot use .call() or .apply() since we do not know which variable
    1080 // the function is bound to, and using the wrong one will generate
    1081 // an error.
    1082 return fn(arguments[0], arguments[1]);
    1083 };
    1084 }
    1085})();
    1086
    1087
    1088/**
    1089 * Events that may be emitted by an {@link webdriver.promise.ControlFlow}.
    1090 * @enum {string}
    1091 */
    1092webdriver.promise.ControlFlow.EventType = {
    1093
    1094 /** Emitted when all tasks have been successfully executed. */
    1095 IDLE: 'idle',
    1096
    1097 /** Emitted when a ControlFlow has been reset. */
    1098 RESET: 'reset',
    1099
    1100 /** Emitted whenever a new task has been scheduled. */
    1101 SCHEDULE_TASK: 'scheduleTask',
    1102
    1103 /**
    1104 * Emitted whenever a control flow aborts due to an unhandled promise
    1105 * rejection. This event will be emitted along with the offending rejection
    1106 * reason. Upon emitting this event, the control flow will empty its task
    1107 * queue and revert to its initial state.
    1108 */
    1109 UNCAUGHT_EXCEPTION: 'uncaughtException'
    1110};
    1111
    1112
    1113/**
    1114 * How often, in milliseconds, the event loop should run.
    1115 * @type {number}
    1116 * @const
    1117 */
    1118webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY = 10;
    1119
    1120
    1121/**
    1122 * Tracks the active execution frame for this instance. Lazily initialized
    1123 * when the first task is scheduled.
    1124 * @private {webdriver.promise.Frame_}
    1125 */
    1126webdriver.promise.ControlFlow.prototype.activeFrame_ = null;
    1127
    1128
    1129/**
    1130 * A reference to the frame in which new tasks should be scheduled. If
    1131 * {@code null}, tasks will be scheduled within the active frame. When forcing
    1132 * a function to run in the context of a new frame, this pointer is used to
    1133 * ensure tasks are scheduled within the newly created frame, even though it
    1134 * won't be active yet.
    1135 * @private {webdriver.promise.Frame_}
    1136 * @see {#runInNewFrame_}
    1137 */
    1138webdriver.promise.ControlFlow.prototype.schedulingFrame_ = null;
    1139
    1140
    1141/**
    1142 * Timeout ID set when the flow is about to shutdown without any errors
    1143 * being detected. Upon shutting down, the flow will emit an
    1144 * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Idle events
    1145 * always follow a brief timeout in order to catch latent errors from the last
    1146 * completed task. If this task had a callback registered, but no errback, and
    1147 * the task fails, the unhandled failure would not be reported by the promise
    1148 * system until the next turn of the event loop:
    1149 *
    1150 * // Schedule 1 task that fails.
    1151 * var result = webriver.promise.controlFlow().schedule('example',
    1152 * function() { return webdriver.promise.rejected('failed'); });
    1153 * // Set a callback on the result. This delays reporting the unhandled
    1154 * // failure for 1 turn of the event loop.
    1155 * result.then(goog.nullFunction);
    1156 *
    1157 * @private {?number}
    1158 */
    1159webdriver.promise.ControlFlow.prototype.shutdownId_ = null;
    1160
    1161
    1162/**
    1163 * Interval ID for this instance's event loop.
    1164 * @private {?number}
    1165 */
    1166webdriver.promise.ControlFlow.prototype.eventLoopId_ = null;
    1167
    1168
    1169/**
    1170 * The number of "pending" promise rejections.
    1171 *
    1172 * <p>Each time a promise is rejected and is not handled by a listener, it will
    1173 * schedule a 0-based timeout to check if it is still unrejected in the next
    1174 * turn of the JS-event loop. This allows listeners to attach to, and handle,
    1175 * the rejected promise at any point in same turn of the event loop that the
    1176 * promise was rejected.
    1177 *
    1178 * <p>When this flow's own event loop triggers, it will not run if there
    1179 * are any outstanding promise rejections. This allows unhandled promises to
    1180 * be reported before a new task is started, ensuring the error is reported to
    1181 * the current task queue.
    1182 *
    1183 * @private {number}
    1184 */
    1185webdriver.promise.ControlFlow.prototype.pendingRejections_ = 0;
    1186
    1187
    1188/**
    1189 * The number of aborted frames since the last time a task was executed or a
    1190 * frame completed successfully.
    1191 * @private {number}
    1192 */
    1193webdriver.promise.ControlFlow.prototype.numAbortedFrames_ = 0;
    1194
    1195
    1196/**
    1197 * Resets this instance, clearing its queue and removing all event listeners.
    1198 */
    1199webdriver.promise.ControlFlow.prototype.reset = function() {
    1200 this.activeFrame_ = null;
    1201 this.clearHistory();
    1202 this.emit(webdriver.promise.ControlFlow.EventType.RESET);
    1203 this.removeAllListeners();
    1204 this.cancelShutdown_();
    1205 this.cancelEventLoop_();
    1206};
    1207
    1208
    1209/**
    1210 * Returns a summary of the recent task activity for this instance. This
    1211 * includes the most recently completed task, as well as any parent tasks. In
    1212 * the returned summary, the task at index N is considered a sub-task of the
    1213 * task at index N+1.
    1214 * @return {!Array.<string>} A summary of this instance's recent task
    1215 * activity.
    1216 */
    1217webdriver.promise.ControlFlow.prototype.getHistory = function() {
    1218 var pendingTasks = [];
    1219 var currentFrame = this.activeFrame_;
    1220 while (currentFrame) {
    1221 var task = currentFrame.getPendingTask();
    1222 if (task) {
    1223 pendingTasks.push(task);
    1224 }
    1225 // A frame's parent node will always be another frame.
    1226 currentFrame =
    1227 /** @type {webdriver.promise.Frame_} */ (currentFrame.getParent());
    1228 }
    1229
    1230 var fullHistory = goog.array.concat(this.history_, pendingTasks);
    1231 return goog.array.map(fullHistory, function(task) {
    1232 return task.toString();
    1233 });
    1234};
    1235
    1236
    1237/** Clears this instance's task history. */
    1238webdriver.promise.ControlFlow.prototype.clearHistory = function() {
    1239 this.history_ = [];
    1240};
    1241
    1242
    1243/**
    1244 * Removes a completed task from this instance's history record. If any
    1245 * tasks remain from aborted frames, those will be removed as well.
    1246 * @private
    1247 */
    1248webdriver.promise.ControlFlow.prototype.trimHistory_ = function() {
    1249 if (this.numAbortedFrames_) {
    1250 goog.array.splice(this.history_,
    1251 this.history_.length - this.numAbortedFrames_,
    1252 this.numAbortedFrames_);
    1253 this.numAbortedFrames_ = 0;
    1254 }
    1255 this.history_.pop();
    1256};
    1257
    1258
    1259/**
    1260 * Property used to track whether an error has been annotated by
    1261 * {@link webdriver.promise.ControlFlow#annotateError}.
    1262 * @private {string}
    1263 * @const
    1264 */
    1265webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_ =
    1266 'webdriver_promise_error_';
    1267
    1268
    1269/**
    1270 * Appends a summary of this instance's recent task history to the given
    1271 * error's stack trace. This function will also ensure the error's stack trace
    1272 * is in canonical form.
    1273 * @param {!(Error|goog.testing.JsUnitException)} e The error to annotate.
    1274 * @return {!(Error|goog.testing.JsUnitException)} The annotated error.
    1275 */
    1276webdriver.promise.ControlFlow.prototype.annotateError = function(e) {
    1277 if (!!e[webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_]) {
    1278 return e;
    1279 }
    1280
    1281 var history = this.getHistory();
    1282 if (history.length) {
    1283 e = webdriver.stacktrace.format(e);
    1284
    1285 /** @type {!Error} */(e).stack += [
    1286 '\n==== async task ====\n',
    1287 history.join('\n==== async task ====\n')
    1288 ].join('');
    1289
    1290 e[webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_] = true;
    1291 }
    1292
    1293 return e;
    1294};
    1295
    1296
    1297/**
    1298 * @return {string} The scheduled tasks still pending with this instance.
    1299 */
    1300webdriver.promise.ControlFlow.prototype.getSchedule = function() {
    1301 return this.activeFrame_ ? this.activeFrame_.getRoot().toString() : '[]';
    1302};
    1303
    1304
    1305/**
    1306 * Schedules a task for execution. If there is nothing currently in the
    1307 * queue, the task will be executed in the next turn of the event loop. If
    1308 * the task function is a generator, the task will be executed using
    1309 * {@link webdriver.promise.consume}.
    1310 *
    1311 * @param {function(): (T|webdriver.promise.Promise.<T>)} fn The function to
    1312 * call to start the task. If the function returns a
    1313 * {@link webdriver.promise.Promise}, this instance will wait for it to be
    1314 * resolved before starting the next task.
    1315 * @param {string=} opt_description A description of the task.
    1316 * @return {!webdriver.promise.Promise.<T>} A promise that will be resolved
    1317 * with the result of the action.
    1318 * @template T
    1319 */
    1320webdriver.promise.ControlFlow.prototype.execute = function(
    1321 fn, opt_description) {
    1322 if (webdriver.promise.isGenerator(fn)) {
    1323 fn = goog.partial(webdriver.promise.consume, fn);
    1324 }
    1325
    1326 this.cancelShutdown_();
    1327
    1328 if (!this.activeFrame_) {
    1329 this.activeFrame_ = new webdriver.promise.Frame_(this);
    1330 }
    1331
    1332 // Trim an extra frame off the generated stack trace for the call to this
    1333 // function.
    1334 var snapshot = new webdriver.stacktrace.Snapshot(1);
    1335 var task = new webdriver.promise.Task_(
    1336 this, fn, opt_description || '', snapshot);
    1337 var scheduleIn = this.schedulingFrame_ || this.activeFrame_;
    1338 scheduleIn.addChild(task);
    1339
    1340 this.emit(webdriver.promise.ControlFlow.EventType.SCHEDULE_TASK, opt_description);
    1341
    1342 this.scheduleEventLoopStart_();
    1343 return task.promise;
    1344};
    1345
    1346
    1347/**
    1348 * Inserts a {@code setTimeout} into the command queue. This is equivalent to
    1349 * a thread sleep in a synchronous programming language.
    1350 *
    1351 * @param {number} ms The timeout delay, in milliseconds.
    1352 * @param {string=} opt_description A description to accompany the timeout.
    1353 * @return {!webdriver.promise.Promise} A promise that will be resolved with
    1354 * the result of the action.
    1355 */
    1356webdriver.promise.ControlFlow.prototype.timeout = function(
    1357 ms, opt_description) {
    1358 return this.execute(function() {
    1359 return webdriver.promise.delayed(ms);
    1360 }, opt_description);
    1361};
    1362
    1363
    1364/**
    1365 * Schedules a task that shall wait for a condition to hold. Each condition
    1366 * function may return any value, but it will always be evaluated as a boolean.
    1367 *
    1368 * <p>Condition functions may schedule sub-tasks with this instance, however,
    1369 * their execution time will be factored into whether a wait has timed out.
    1370 *
    1371 * <p>In the event a condition returns a Promise, the polling loop will wait for
    1372 * it to be resolved before evaluating whether the condition has been satisfied.
    1373 * The resolution time for a promise is factored into whether a wait has timed
    1374 * out.
    1375 *
    1376 * <p>If the condition function throws, or returns a rejected promise, the
    1377 * wait task will fail.
    1378 *
    1379 * @param {!Function} condition The condition function to poll.
    1380 * @param {number} timeout How long to wait, in milliseconds, for the condition
    1381 * to hold before timing out.
    1382 * @param {string=} opt_message An optional error message to include if the
    1383 * wait times out; defaults to the empty string.
    1384 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
    1385 * condition has been satisified. The promise shall be rejected if the wait
    1386 * times out waiting for the condition.
    1387 */
    1388webdriver.promise.ControlFlow.prototype.wait = function(
    1389 condition, timeout, opt_message) {
    1390 var sleep = Math.min(timeout, 100);
    1391 var self = this;
    1392
    1393 if (webdriver.promise.isGenerator(condition)) {
    1394 condition = goog.partial(webdriver.promise.consume, condition);
    1395 }
    1396
    1397 return this.execute(function() {
    1398 var startTime = goog.now();
    1399 var waitResult = new webdriver.promise.Deferred();
    1400 var waitFrame = self.activeFrame_;
    1401 waitFrame.isWaiting = true;
    1402 pollCondition();
    1403 return waitResult.promise;
    1404
    1405 function pollCondition() {
    1406 self.runInNewFrame_(condition, function(value) {
    1407 var elapsed = goog.now() - startTime;
    1408 if (!!value) {
    1409 waitFrame.isWaiting = false;
    1410 waitResult.fulfill(value);
    1411 } else if (elapsed >= timeout) {
    1412 waitResult.reject(new Error((opt_message ? opt_message + '\n' : '') +
    1413 'Wait timed out after ' + elapsed + 'ms'));
    1414 } else {
    1415 self.timer.setTimeout(pollCondition, sleep);
    1416 }
    1417 }, waitResult.reject, true);
    1418 }
    1419 }, opt_message);
    1420};
    1421
    1422
    1423/**
    1424 * Schedules a task that will wait for another promise to resolve. The resolved
    1425 * promise's value will be returned as the task result.
    1426 * @param {!webdriver.promise.Promise} promise The promise to wait on.
    1427 * @return {!webdriver.promise.Promise} A promise that will resolve when the
    1428 * task has completed.
    1429 */
    1430webdriver.promise.ControlFlow.prototype.await = function(promise) {
    1431 return this.execute(function() {
    1432 return promise;
    1433 });
    1434};
    1435
    1436
    1437/**
    1438 * Schedules the interval for this instance's event loop, if necessary.
    1439 * @private
    1440 */
    1441webdriver.promise.ControlFlow.prototype.scheduleEventLoopStart_ = function() {
    1442 if (!this.eventLoopId_) {
    1443 this.eventLoopId_ = this.timer.setInterval(
    1444 goog.bind(this.runEventLoop_, this),
    1445 webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY);
    1446 }
    1447};
    1448
    1449
    1450/**
    1451 * Cancels the event loop, if necessary.
    1452 * @private
    1453 */
    1454webdriver.promise.ControlFlow.prototype.cancelEventLoop_ = function() {
    1455 if (this.eventLoopId_) {
    1456 this.timer.clearInterval(this.eventLoopId_);
    1457 this.eventLoopId_ = null;
    1458 }
    1459};
    1460
    1461
    1462/**
    1463 * Executes the next task for the current frame. If the current frame has no
    1464 * more tasks, the frame's result will be resolved, returning control to the
    1465 * frame's creator. This will terminate the flow if the completed frame was at
    1466 * the top of the stack.
    1467 * @private
    1468 */
    1469webdriver.promise.ControlFlow.prototype.runEventLoop_ = function() {
    1470 // If we get here and there are pending promise rejections, then those
    1471 // promises are queued up to run as soon as this (JS) event loop terminates.
    1472 // Short-circuit our loop to give those promises a chance to run. Otherwise,
    1473 // we might start a new task only to have it fail because of one of these
    1474 // pending rejections.
    1475 if (this.pendingRejections_) {
    1476 return;
    1477 }
    1478
    1479 // If the flow aborts due to an unhandled exception after we've scheduled
    1480 // another turn of the execution loop, we can end up in here with no tasks
    1481 // left. This is OK, just quietly return.
    1482 if (!this.activeFrame_) {
    1483 this.commenceShutdown_();
    1484 return;
    1485 }
    1486
    1487 var task;
    1488 if (this.activeFrame_.getPendingTask() || !(task = this.getNextTask_())) {
    1489 // Either the current frame is blocked on a pending task, or we don't have
    1490 // a task to finish because we've completed a frame. When completing a
    1491 // frame, we must abort the event loop to allow the frame's promise's
    1492 // callbacks to execute.
    1493 return;
    1494 }
    1495
    1496 var activeFrame = this.activeFrame_;
    1497 activeFrame.setPendingTask(task);
    1498 var markTaskComplete = goog.bind(function() {
    1499 this.history_.push(/** @type {!webdriver.promise.Task_} */ (task));
    1500 activeFrame.setPendingTask(null);
    1501 }, this);
    1502
    1503 this.trimHistory_();
    1504 var self = this;
    1505 this.runInNewFrame_(task.execute, function(result) {
    1506 markTaskComplete();
    1507 task.fulfill(result);
    1508 }, function(error) {
    1509 markTaskComplete();
    1510
    1511 if (!webdriver.promise.isError_(error) &&
    1512 !webdriver.promise.isPromise(error)) {
    1513 error = Error(error);
    1514 }
    1515
    1516 task.reject(self.annotateError(/** @type {!Error} */ (error)));
    1517 }, true);
    1518};
    1519
    1520
    1521/**
    1522 * @return {webdriver.promise.Task_} The next task to execute, or
    1523 * {@code null} if a frame was resolved.
    1524 * @private
    1525 */
    1526webdriver.promise.ControlFlow.prototype.getNextTask_ = function() {
    1527 var firstChild = this.activeFrame_.getFirstChild();
    1528 if (!firstChild) {
    1529 if (!this.activeFrame_.isWaiting) {
    1530 this.resolveFrame_(this.activeFrame_);
    1531 }
    1532 return null;
    1533 }
    1534
    1535 if (firstChild instanceof webdriver.promise.Frame_) {
    1536 this.activeFrame_ = firstChild;
    1537 return this.getNextTask_();
    1538 }
    1539
    1540 firstChild.getParent().removeChild(firstChild);
    1541 return firstChild;
    1542};
    1543
    1544
    1545/**
    1546 * @param {!webdriver.promise.Frame_} frame The frame to resolve.
    1547 * @private
    1548 */
    1549webdriver.promise.ControlFlow.prototype.resolveFrame_ = function(frame) {
    1550 if (this.activeFrame_ === frame) {
    1551 // Frame parent is always another frame, but the compiler is not smart
    1552 // enough to recognize this.
    1553 this.activeFrame_ =
    1554 /** @type {webdriver.promise.Frame_} */ (frame.getParent());
    1555 }
    1556
    1557 if (frame.getParent()) {
    1558 frame.getParent().removeChild(frame);
    1559 }
    1560 this.trimHistory_();
    1561 frame.fulfill();
    1562
    1563 if (!this.activeFrame_) {
    1564 this.commenceShutdown_();
    1565 }
    1566};
    1567
    1568
    1569/**
    1570 * Aborts the current frame. The frame, and all of the tasks scheduled within it
    1571 * will be discarded. If this instance does not have an active frame, it will
    1572 * immediately terminate all execution.
    1573 * @param {*} error The reason the frame is being aborted; typically either
    1574 * an Error or string.
    1575 * @private
    1576 */
    1577webdriver.promise.ControlFlow.prototype.abortFrame_ = function(error) {
    1578 // Annotate the error value if it is Error-like.
    1579 if (webdriver.promise.isError_(error)) {
    1580 this.annotateError(/** @type {!Error} */ (error));
    1581 }
    1582 this.numAbortedFrames_++;
    1583
    1584 if (!this.activeFrame_) {
    1585 this.abortNow_(error);
    1586 return;
    1587 }
    1588
    1589 // Frame parent is always another frame, but the compiler is not smart
    1590 // enough to recognize this.
    1591 var parent = /** @type {webdriver.promise.Frame_} */ (
    1592 this.activeFrame_.getParent());
    1593 if (parent) {
    1594 parent.removeChild(this.activeFrame_);
    1595 }
    1596
    1597 var frame = this.activeFrame_;
    1598 this.activeFrame_ = parent;
    1599 frame.reject(error);
    1600};
    1601
    1602
    1603/**
    1604 * Executes a function in a new frame. If the function does not schedule any new
    1605 * tasks, the frame will be discarded and the function's result returned
    1606 * immediately. Otherwise, a promise will be returned. This promise will be
    1607 * resolved with the function's result once all of the tasks scheduled within
    1608 * the function have been completed. If the function's frame is aborted, the
    1609 * returned promise will be rejected.
    1610 *
    1611 * @param {!Function} fn The function to execute.
    1612 * @param {function(*)} callback The function to call with a successful result.
    1613 * @param {function(*)} errback The function to call if there is an error.
    1614 * @param {boolean=} opt_activate Whether the active frame should be updated to
    1615 * the newly created frame so tasks are treated as sub-tasks.
    1616 * @private
    1617 */
    1618webdriver.promise.ControlFlow.prototype.runInNewFrame_ = function(
    1619 fn, callback, errback, opt_activate) {
    1620 var newFrame = new webdriver.promise.Frame_(this),
    1621 self = this,
    1622 oldFrame = this.activeFrame_;
    1623
    1624 try {
    1625 if (!this.activeFrame_) {
    1626 this.activeFrame_ = newFrame;
    1627 } else {
    1628 this.activeFrame_.addChild(newFrame);
    1629 }
    1630
    1631 // Activate the new frame to force tasks to be treated as sub-tasks of
    1632 // the parent frame.
    1633 if (opt_activate) {
    1634 this.activeFrame_ = newFrame;
    1635 }
    1636
    1637 try {
    1638 this.schedulingFrame_ = newFrame;
    1639 webdriver.promise.pushFlow_(this);
    1640 var result = fn();
    1641 } finally {
    1642 webdriver.promise.popFlow_();
    1643 this.schedulingFrame_ = null;
    1644 }
    1645 newFrame.lockFrame();
    1646
    1647 // If there was nothing scheduled in the new frame we can discard the
    1648 // frame and return immediately.
    1649 if (!newFrame.children_.length) {
    1650 removeNewFrame();
    1651 webdriver.promise.asap(result, callback, errback);
    1652 return;
    1653 }
    1654
    1655 newFrame.then(function() {
    1656 webdriver.promise.asap(result, callback, errback);
    1657 }, function(e) {
    1658 if (webdriver.promise.Thenable.isImplementation(result) &&
    1659 result.isPending()) {
    1660 result.cancel(e);
    1661 e = result;
    1662 }
    1663 errback(e);
    1664 });
    1665 } catch (ex) {
    1666 removeNewFrame(new webdriver.promise.CanceledTaskError_(ex));
    1667 errback(ex);
    1668 }
    1669
    1670 /**
    1671 * @param {webdriver.promise.CanceledTaskError_=} opt_err If provided, the
    1672 * error that triggered the removal of this frame.
    1673 */
    1674 function removeNewFrame(opt_err) {
    1675 var parent = newFrame.getParent();
    1676 if (parent) {
    1677 parent.removeChild(newFrame);
    1678 }
    1679
    1680 if (opt_err) {
    1681 newFrame.cancelRemainingTasks(opt_err);
    1682 }
    1683 self.activeFrame_ = oldFrame;
    1684 }
    1685};
    1686
    1687
    1688/**
    1689 * Commences the shutdown sequence for this instance. After one turn of the
    1690 * event loop, this object will emit the
    1691 * {@link webdriver.promise.ControlFlow.EventType.IDLE} event to signal
    1692 * listeners that it has completed. During this wait, if another task is
    1693 * scheduled, the shutdown will be aborted.
    1694 * @private
    1695 */
    1696webdriver.promise.ControlFlow.prototype.commenceShutdown_ = function() {
    1697 if (!this.shutdownId_) {
    1698 // Go ahead and stop the event loop now. If we're in here, then there are
    1699 // no more frames with tasks to execute. If we waited to cancel the event
    1700 // loop in our timeout below, the event loop could trigger *before* the
    1701 // timeout, generating an error from there being no frames.
    1702 // If #execute is called before the timeout below fires, it will cancel
    1703 // the timeout and restart the event loop.
    1704 this.cancelEventLoop_();
    1705
    1706 var self = this;
    1707 self.shutdownId_ = self.timer.setTimeout(function() {
    1708 self.shutdownId_ = null;
    1709 self.emit(webdriver.promise.ControlFlow.EventType.IDLE);
    1710 }, 0);
    1711 }
    1712};
    1713
    1714
    1715/**
    1716 * Cancels the shutdown sequence if it is currently scheduled.
    1717 * @private
    1718 */
    1719webdriver.promise.ControlFlow.prototype.cancelShutdown_ = function() {
    1720 if (this.shutdownId_) {
    1721 this.timer.clearTimeout(this.shutdownId_);
    1722 this.shutdownId_ = null;
    1723 }
    1724};
    1725
    1726
    1727/**
    1728 * Aborts this flow, abandoning all remaining tasks. If there are
    1729 * listeners registered, an {@code UNCAUGHT_EXCEPTION} will be emitted with the
    1730 * offending {@code error}, otherwise, the {@code error} will be rethrown to the
    1731 * global error handler.
    1732 * @param {*} error Object describing the error that caused the flow to
    1733 * abort; usually either an Error or string value.
    1734 * @private
    1735 */
    1736webdriver.promise.ControlFlow.prototype.abortNow_ = function(error) {
    1737 this.activeFrame_ = null;
    1738 this.cancelShutdown_();
    1739 this.cancelEventLoop_();
    1740
    1741 var listeners = this.listeners(
    1742 webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
    1743 if (!listeners.length) {
    1744 this.timer.setTimeout(function() {
    1745 throw error;
    1746 }, 0);
    1747 } else {
    1748 this.emit(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
    1749 error);
    1750 }
    1751};
    1752
    1753
    1754
    1755/**
    1756 * A single node in an {@link webdriver.promise.ControlFlow}'s task tree.
    1757 * @param {!webdriver.promise.ControlFlow} flow The flow this instance belongs
    1758 * to.
    1759 * @constructor
    1760 * @extends {webdriver.promise.Deferred}
    1761 * @private
    1762 */
    1763webdriver.promise.Node_ = function(flow) {
    1764 webdriver.promise.Deferred.call(this, null, flow);
    1765};
    1766goog.inherits(webdriver.promise.Node_, webdriver.promise.Deferred);
    1767
    1768
    1769/**
    1770 * This node's parent.
    1771 * @private {webdriver.promise.Node_}
    1772 */
    1773webdriver.promise.Node_.prototype.parent_ = null;
    1774
    1775
    1776/** @return {webdriver.promise.Node_} This node's parent. */
    1777webdriver.promise.Node_.prototype.getParent = function() {
    1778 return this.parent_;
    1779};
    1780
    1781
    1782/**
    1783 * @param {webdriver.promise.Node_} parent This node's new parent.
    1784 */
    1785webdriver.promise.Node_.prototype.setParent = function(parent) {
    1786 this.parent_ = parent;
    1787};
    1788
    1789
    1790/**
    1791 * @return {!webdriver.promise.Node_} The root of this node's tree.
    1792 */
    1793webdriver.promise.Node_.prototype.getRoot = function() {
    1794 var root = this;
    1795 while (root.parent_) {
    1796 root = root.parent_;
    1797 }
    1798 return root;
    1799};
    1800
    1801
    1802
    1803/**
    1804 * An execution frame within a {@link webdriver.promise.ControlFlow}. Each
    1805 * frame represents the execution context for either a
    1806 * {@link webdriver.promise.Task_} or a callback on a
    1807 * {@link webdriver.promise.Deferred}.
    1808 *
    1809 * <p>Each frame may contain sub-frames. If child N is a sub-frame, then the
    1810 * items queued within it are given priority over child N+1.
    1811 *
    1812 * @param {!webdriver.promise.ControlFlow} flow The flow this instance belongs
    1813 * to.
    1814 * @constructor
    1815 * @extends {webdriver.promise.Node_}
    1816 * @private
    1817 */
    1818webdriver.promise.Frame_ = function(flow) {
    1819 webdriver.promise.Node_.call(this, flow);
    1820
    1821 var reject = goog.bind(this.reject, this);
    1822 var cancelRemainingTasks = goog.bind(this.cancelRemainingTasks, this);
    1823
    1824 /** @override */
    1825 this.reject = function(e) {
    1826 cancelRemainingTasks(new webdriver.promise.CanceledTaskError_(e));
    1827 reject(e);
    1828 };
    1829
    1830 /**
    1831 * @private {!Array.<!(webdriver.promise.Frame_|webdriver.promise.Task_)>}
    1832 */
    1833 this.children_ = [];
    1834};
    1835goog.inherits(webdriver.promise.Frame_, webdriver.promise.Node_);
    1836
    1837
    1838/**
    1839 * The task currently being executed within this frame.
    1840 * @private {webdriver.promise.Task_}
    1841 */
    1842webdriver.promise.Frame_.prototype.pendingTask_ = null;
    1843
    1844
    1845/**
    1846 * Whether this frame is active. A frame is considered active once one of its
    1847 * descendants has been removed for execution.
    1848 *
    1849 * Adding a sub-frame as a child to an active frame is an indication that
    1850 * a callback to a {@link webdriver.promise.Deferred} is being invoked and any
    1851 * tasks scheduled within it should have priority over previously scheduled
    1852 * tasks:
    1853 * <code><pre>
    1854 * var flow = webdriver.promise.controlFlow();
    1855 * flow.execute('start here', goog.nullFunction).then(function() {
    1856 * flow.execute('this should execute 2nd', goog.nullFunction);
    1857 * });
    1858 * flow.execute('this should execute last', goog.nullFunction);
    1859 * </pre></code>
    1860 *
    1861 * @private {boolean}
    1862 */
    1863webdriver.promise.Frame_.prototype.isActive_ = false;
    1864
    1865
    1866/**
    1867 * Whether this frame is currently locked. A locked frame represents a callback
    1868 * or task function which has run to completion and scheduled all of its tasks.
    1869 *
    1870 * <p>Once a frame becomes {@link #isActive_ active}, any new frames which are
    1871 * added represent callbacks on a {@link webdriver.promise.Deferred}, whose
    1872 * tasks must be given priority over previously scheduled tasks.
    1873 *
    1874 * @private {boolean}
    1875 */
    1876webdriver.promise.Frame_.prototype.isLocked_ = false;
    1877
    1878
    1879/**
    1880 * A reference to the last node inserted in this frame.
    1881 * @private {webdriver.promise.Node_}
    1882 */
    1883webdriver.promise.Frame_.prototype.lastInsertedChild_ = null;
    1884
    1885
    1886/**
    1887 * Marks all of the tasks that are descendants of this frame in the execution
    1888 * tree as cancelled. This is necessary for callbacks scheduled asynchronous.
    1889 * For example:
    1890 *
    1891 * var someResult;
    1892 * webdriver.promise.createFlow(function(flow) {
    1893 * someResult = flow.execute(function() {});
    1894 * throw Error();
    1895 * }).addErrback(function(err) {
    1896 * console.log('flow failed: ' + err);
    1897 * someResult.then(function() {
    1898 * console.log('task succeeded!');
    1899 * }, function(err) {
    1900 * console.log('task failed! ' + err);
    1901 * });
    1902 * });
    1903 * // flow failed: Error: boom
    1904 * // task failed! CanceledTaskError: Task discarded due to a previous
    1905 * // task failure: Error: boom
    1906 *
    1907 * @param {!webdriver.promise.CanceledTaskError_} error The cancellation
    1908 * error.
    1909 */
    1910webdriver.promise.Frame_.prototype.cancelRemainingTasks = function(error) {
    1911 goog.array.forEach(this.children_, function(child) {
    1912 if (child instanceof webdriver.promise.Frame_) {
    1913 child.cancelRemainingTasks(error);
    1914 } else {
    1915 // None of the previously registered listeners should be notified that
    1916 // the task is being canceled, however, we need at least one errback
    1917 // to prevent the cancellation from bubbling up.
    1918 child.removeAll();
    1919 child.thenCatch(goog.nullFunction);
    1920 child.cancel(error);
    1921 }
    1922 });
    1923};
    1924
    1925
    1926/**
    1927 * @return {webdriver.promise.Task_} The task currently executing
    1928 * within this frame, if any.
    1929 */
    1930webdriver.promise.Frame_.prototype.getPendingTask = function() {
    1931 return this.pendingTask_;
    1932};
    1933
    1934
    1935/**
    1936 * @param {webdriver.promise.Task_} task The task currently
    1937 * executing within this frame, if any.
    1938 */
    1939webdriver.promise.Frame_.prototype.setPendingTask = function(task) {
    1940 this.pendingTask_ = task;
    1941};
    1942
    1943
    1944/** Locks this frame. */
    1945webdriver.promise.Frame_.prototype.lockFrame = function() {
    1946 this.isLocked_ = true;
    1947};
    1948
    1949
    1950/**
    1951 * Adds a new node to this frame.
    1952 * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} node
    1953 * The node to insert.
    1954 */
    1955webdriver.promise.Frame_.prototype.addChild = function(node) {
    1956 if (this.lastInsertedChild_ &&
    1957 this.lastInsertedChild_ instanceof webdriver.promise.Frame_ &&
    1958 !this.lastInsertedChild_.isLocked_) {
    1959 this.lastInsertedChild_.addChild(node);
    1960 return;
    1961 }
    1962
    1963 node.setParent(this);
    1964
    1965 if (this.isActive_ && node instanceof webdriver.promise.Frame_) {
    1966 var index = 0;
    1967 if (this.lastInsertedChild_ instanceof
    1968 webdriver.promise.Frame_) {
    1969 index = goog.array.indexOf(this.children_, this.lastInsertedChild_) + 1;
    1970 }
    1971 goog.array.insertAt(this.children_, node, index);
    1972 this.lastInsertedChild_ = node;
    1973 return;
    1974 }
    1975
    1976 this.lastInsertedChild_ = node;
    1977 this.children_.push(node);
    1978};
    1979
    1980
    1981/**
    1982 * @return {(webdriver.promise.Frame_|webdriver.promise.Task_)} This frame's
    1983 * fist child.
    1984 */
    1985webdriver.promise.Frame_.prototype.getFirstChild = function() {
    1986 this.isActive_ = true;
    1987 this.lastInsertedChild_ = null;
    1988 return this.children_[0];
    1989};
    1990
    1991
    1992/**
    1993 * Removes a child from this frame.
    1994 * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} child
    1995 * The child to remove.
    1996 */
    1997webdriver.promise.Frame_.prototype.removeChild = function(child) {
    1998 var index = goog.array.indexOf(this.children_, child);
    1999 child.setParent(null);
    2000 goog.array.removeAt(this.children_, index);
    2001 if (this.lastInsertedChild_ === child) {
    2002 this.lastInsertedChild_ = null;
    2003 }
    2004};
    2005
    2006
    2007/** @override */
    2008webdriver.promise.Frame_.prototype.toString = function() {
    2009 return '[' + goog.array.map(this.children_, function(child) {
    2010 return child.toString();
    2011 }).join(', ') + ']';
    2012};
    2013
    2014
    2015
    2016/**
    2017 * A task to be executed by a {@link webdriver.promise.ControlFlow}.
    2018 *
    2019 * @param {!webdriver.promise.ControlFlow} flow The flow this instances belongs
    2020 * to.
    2021 * @param {!Function} fn The function to call when the task executes. If it
    2022 * returns a {@code webdriver.promise.Promise}, the flow will wait
    2023 * for it to be resolved before starting the next task.
    2024 * @param {string} description A description of the task for debugging.
    2025 * @param {!webdriver.stacktrace.Snapshot} snapshot A snapshot of the stack
    2026 * when this task was scheduled.
    2027 * @constructor
    2028 * @extends {webdriver.promise.Node_}
    2029 * @private
    2030 */
    2031webdriver.promise.Task_ = function(flow, fn, description, snapshot) {
    2032 webdriver.promise.Node_.call(this, flow);
    2033
    2034 /**
    2035 * Executes this task.
    2036 * @type {!Function}
    2037 */
    2038 this.execute = fn;
    2039
    2040 /** @private {string} */
    2041 this.description_ = description;
    2042
    2043 /** @private {!webdriver.stacktrace.Snapshot} */
    2044 this.snapshot_ = snapshot;
    2045};
    2046goog.inherits(webdriver.promise.Task_, webdriver.promise.Node_);
    2047
    2048
    2049/** @return {string} This task's description. */
    2050webdriver.promise.Task_.prototype.getDescription = function() {
    2051 return this.description_;
    2052};
    2053
    2054
    2055/** @override */
    2056webdriver.promise.Task_.prototype.toString = function() {
    2057 var stack = this.snapshot_.getStacktrace();
    2058 var ret = this.description_;
    2059 if (stack.length) {
    2060 if (this.description_) {
    2061 ret += '\n';
    2062 }
    2063 ret += stack.join('\n');
    2064 }
    2065 return ret;
    2066};
    2067
    2068
    2069
    2070/**
    2071 * Special error used to signal when a task is canceled because a previous
    2072 * task in the same frame failed.
    2073 * @param {*} err The error that caused the task cancellation.
    2074 * @constructor
    2075 * @extends {goog.debug.Error}
    2076 * @private
    2077 */
    2078webdriver.promise.CanceledTaskError_ = function(err) {
    2079 goog.base(this, 'Task discarded due to a previous task failure: ' + err);
    2080};
    2081goog.inherits(webdriver.promise.CanceledTaskError_, goog.debug.Error);
    2082
    2083
    2084/** @override */
    2085webdriver.promise.CanceledTaskError_.prototype.name = 'CanceledTaskError';
    2086
    2087
    2088
    2089/**
    2090 * The default flow to use if no others are active.
    2091 * @private {!webdriver.promise.ControlFlow}
    2092 */
    2093webdriver.promise.defaultFlow_ = new webdriver.promise.ControlFlow();
    2094
    2095
    2096/**
    2097 * A stack of active control flows, with the top of the stack used to schedule
    2098 * commands. When there are multiple flows on the stack, the flow at index N
    2099 * represents a callback triggered within a task owned by the flow at index
    2100 * N-1.
    2101 * @private {!Array.<!webdriver.promise.ControlFlow>}
    2102 */
    2103webdriver.promise.activeFlows_ = [];
    2104
    2105
    2106/**
    2107 * Changes the default flow to use when no others are active.
    2108 * @param {!webdriver.promise.ControlFlow} flow The new default flow.
    2109 * @throws {Error} If the default flow is not currently active.
    2110 */
    2111webdriver.promise.setDefaultFlow = function(flow) {
    2112 if (webdriver.promise.activeFlows_.length) {
    2113 throw Error('You may only change the default flow while it is active');
    2114 }
    2115 webdriver.promise.defaultFlow_ = flow;
    2116};
    2117
    2118
    2119/**
    2120 * @return {!webdriver.promise.ControlFlow} The currently active control flow.
    2121 */
    2122webdriver.promise.controlFlow = function() {
    2123 return /** @type {!webdriver.promise.ControlFlow} */ (
    2124 goog.array.peek(webdriver.promise.activeFlows_) ||
    2125 webdriver.promise.defaultFlow_);
    2126};
    2127
    2128
    2129/**
    2130 * @param {!webdriver.promise.ControlFlow} flow The new flow.
    2131 * @private
    2132 */
    2133webdriver.promise.pushFlow_ = function(flow) {
    2134 webdriver.promise.activeFlows_.push(flow);
    2135};
    2136
    2137
    2138/** @private */
    2139webdriver.promise.popFlow_ = function() {
    2140 webdriver.promise.activeFlows_.pop();
    2141};
    2142
    2143
    2144/**
    2145 * Creates a new control flow. The provided callback will be invoked as the
    2146 * first task within the new flow, with the flow as its sole argument. Returns
    2147 * a promise that resolves to the callback result.
    2148 * @param {function(!webdriver.promise.ControlFlow)} callback The entry point
    2149 * to the newly created flow.
    2150 * @return {!webdriver.promise.Promise} A promise that resolves to the callback
    2151 * result.
    2152 */
    2153webdriver.promise.createFlow = function(callback) {
    2154 var flow = new webdriver.promise.ControlFlow(
    2155 webdriver.promise.defaultFlow_.timer);
    2156 return flow.execute(function() {
    2157 return callback(flow);
    2158 });
    2159};
    2160
    2161
    2162/**
    2163 * Tests is a function is a generator.
    2164 * @param {!Function} fn The function to test.
    2165 * @return {boolean} Whether the function is a generator.
    2166 */
    2167webdriver.promise.isGenerator = function(fn) {
    2168 return fn.constructor.name === 'GeneratorFunction';
    2169};
    2170
    2171
    2172/**
    2173 * Consumes a {@code GeneratorFunction}. Each time the generator yields a
    2174 * promise, this function will wait for it to be fulfilled before feeding the
    2175 * fulfilled value back into {@code next}. Likewise, if a yielded promise is
    2176 * rejected, the rejection error will be passed to {@code throw}.
    2177 *
    2178 * <p>Example 1: the Fibonacci Sequence.
    2179 * <pre><code>
    2180 * webdriver.promise.consume(function* fibonacci() {
    2181 * var n1 = 1, n2 = 1;
    2182 * for (var i = 0; i < 4; ++i) {
    2183 * var tmp = yield n1 + n2;
    2184 * n1 = n2;
    2185 * n2 = tmp;
    2186 * }
    2187 * return n1 + n2;
    2188 * }).then(function(result) {
    2189 * console.log(result); // 13
    2190 * });
    2191 * </code></pre>
    2192 *
    2193 * <p>Example 2: a generator that throws.
    2194 * <pre><code>
    2195 * webdriver.promise.consume(function* () {
    2196 * yield webdriver.promise.delayed(250).then(function() {
    2197 * throw Error('boom');
    2198 * });
    2199 * }).thenCatch(function(e) {
    2200 * console.log(e.toString()); // Error: boom
    2201 * });
    2202 * </code></pre>
    2203 *
    2204 * @param {!Function} generatorFn The generator function to execute.
    2205 * @param {Object=} opt_self The object to use as "this" when invoking the
    2206 * initial generator.
    2207 * @param {...*} var_args Any arguments to pass to the initial generator.
    2208 * @return {!webdriver.promise.Promise.<?>} A promise that will resolve to the
    2209 * generator's final result.
    2210 * @throws {TypeError} If the given function is not a generator.
    2211 */
    2212webdriver.promise.consume = function(generatorFn, opt_self, var_args) {
    2213 if (!webdriver.promise.isGenerator(generatorFn)) {
    2214 throw TypeError('Input is not a GeneratorFunction: ' +
    2215 generatorFn.constructor.name);
    2216 }
    2217
    2218 var deferred = webdriver.promise.defer();
    2219 var generator = generatorFn.apply(opt_self, goog.array.slice(arguments, 2));
    2220 callNext();
    2221 return deferred.promise;
    2222
    2223 /** @param {*=} opt_value . */
    2224 function callNext(opt_value) {
    2225 pump(generator.next, opt_value);
    2226 }
    2227
    2228 /** @param {*=} opt_error . */
    2229 function callThrow(opt_error) {
    2230 // Dictionary lookup required because Closure compiler's built-in
    2231 // externs does not include GeneratorFunction.prototype.throw.
    2232 pump(generator['throw'], opt_error);
    2233 }
    2234
    2235 function pump(fn, opt_arg) {
    2236 if (!deferred.isPending()) {
    2237 return; // Defererd was cancelled; silently abort.
    2238 }
    2239
    2240 try {
    2241 var result = fn.call(generator, opt_arg);
    2242 } catch (ex) {
    2243 deferred.reject(ex);
    2244 return;
    2245 }
    2246
    2247 if (result.done) {
    2248 deferred.fulfill(result.value);
    2249 return;
    2250 }
    2251
    2252 webdriver.promise.asap(result.value, callNext, callThrow);
    2253 }
    2254};
    \ 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 A promise implementation based on the CommonJS promise/A and
    48 * promise/B proposals. For more information, see
    49 * http://wiki.commonjs.org/wiki/Promises.
    50 */
    51
    52goog.module('webdriver.promise');
    53goog.module.declareLegacyNamespace();
    54
    55var Arrays = goog.require('goog.array');
    56var asserts = goog.require('goog.asserts');
    57var asyncRun = goog.require('goog.async.run');
    58var throwException = goog.require('goog.async.throwException');
    59var DebugError = goog.require('goog.debug.Error');
    60var Objects = goog.require('goog.object');
    61var EventEmitter = goog.require('webdriver.EventEmitter');
    62var stacktrace = goog.require('webdriver.stacktrace');
    63
    64
    65
    66/**
    67 * @define {boolean} Whether to append traces of {@code then} to rejection
    68 * errors.
    69 */
    70goog.define('webdriver.promise.LONG_STACK_TRACES', false);
    71
    72/** @const */
    73var promise = exports;
    74
    75
    76/**
    77 * Generates an error to capture the current stack trace.
    78 * @param {string} name Error name for this stack trace.
    79 * @param {string} msg Message to record.
    80 * @param {!Function} topFn The function that should appear at the top of the
    81 * stack; only applicable in V8.
    82 * @return {!Error} The generated error.
    83 */
    84promise.captureStackTrace = function(name, msg, topFn) {
    85 var e = Error(msg);
    86 e.name = name;
    87 if (Error.captureStackTrace) {
    88 Error.captureStackTrace(e, topFn);
    89 } else {
    90 var stack = stacktrace.getStack(e);
    91 e.stack = e.toString();
    92 if (stack) {
    93 e.stack += '\n' + stack;
    94 }
    95 }
    96 return e;
    97};
    98
    99
    100/**
    101 * Error used when the computation of a promise is cancelled.
    102 *
    103 * @param {string=} opt_msg The cancellation message.
    104 * @constructor
    105 * @extends {DebugError}
    106 * @final
    107 */
    108promise.CancellationError = function(opt_msg) {
    109 DebugError.call(this, opt_msg);
    110
    111 /** @override */
    112 this.name = 'CancellationError';
    113};
    114goog.inherits(promise.CancellationError, DebugError);
    115
    116
    117/**
    118 * Wraps the given error in a CancellationError. Will trivially return
    119 * the error itself if it is an instanceof CancellationError.
    120 *
    121 * @param {*} error The error to wrap.
    122 * @param {string=} opt_msg The prefix message to use.
    123 * @return {!promise.CancellationError} A cancellation error.
    124 */
    125promise.CancellationError.wrap = function(error, opt_msg) {
    126 if (error instanceof promise.CancellationError) {
    127 return /** @type {!promise.CancellationError} */(error);
    128 } else if (opt_msg) {
    129 var message = opt_msg;
    130 if (error) {
    131 message += ': ' + error;
    132 }
    133 return new promise.CancellationError(message);
    134 }
    135 var message;
    136 if (error) {
    137 message = error + '';
    138 }
    139 return new promise.CancellationError(message);
    140};
    141
    142
    143
    144/**
    145 * Thenable is a promise-like object with a {@code then} method which may be
    146 * used to schedule callbacks on a promised value.
    147 *
    148 * @interface
    149 * @extends {IThenable<T>}
    150 * @template T
    151 */
    152promise.Thenable = function() {};
    153
    154
    155/**
    156 * Cancels the computation of this promise's value, rejecting the promise in the
    157 * process. This method is a no-op if the promise has already been resolved.
    158 *
    159 * @param {(string|promise.CancellationError)=} opt_reason The reason this
    160 * promise is being cancelled.
    161 */
    162promise.Thenable.prototype.cancel = function(opt_reason) {};
    163
    164
    165/** @return {boolean} Whether this promise's value is still being computed. */
    166promise.Thenable.prototype.isPending = function() {};
    167
    168
    169/**
    170 * Registers listeners for when this instance is resolved.
    171 *
    172 * @param {?(function(T): (R|IThenable<R>))=} opt_callback The
    173 * function to call if this promise is successfully resolved. The function
    174 * should expect a single argument: the promise's resolved value.
    175 * @param {?(function(*): (R|IThenable<R>))=} opt_errback
    176 * The function to call if this promise is rejected. The function should
    177 * expect a single argument: the rejection reason.
    178 * @return {!promise.Promise<R>} A new promise which will be
    179 * resolved with the result of the invoked callback.
    180 * @template R
    181 */
    182promise.Thenable.prototype.then = function(opt_callback, opt_errback) {};
    183
    184
    185/**
    186 * Registers a listener for when this promise is rejected. This is synonymous
    187 * with the {@code catch} clause in a synchronous API:
    188 *
    189 * // Synchronous API:
    190 * try {
    191 * doSynchronousWork();
    192 * } catch (ex) {
    193 * console.error(ex);
    194 * }
    195 *
    196 * // Asynchronous promise API:
    197 * doAsynchronousWork().thenCatch(function(ex) {
    198 * console.error(ex);
    199 * });
    200 *
    201 * @param {function(*): (R|IThenable<R>)} errback The
    202 * function to call if this promise is rejected. The function should
    203 * expect a single argument: the rejection reason.
    204 * @return {!promise.Promise<R>} A new promise which will be
    205 * resolved with the result of the invoked callback.
    206 * @template R
    207 */
    208promise.Thenable.prototype.thenCatch = function(errback) {};
    209
    210
    211/**
    212 * Registers a listener to invoke when this promise is resolved, regardless
    213 * of whether the promise's value was successfully computed. This function
    214 * is synonymous with the {@code finally} clause in a synchronous API:
    215 *
    216 * // Synchronous API:
    217 * try {
    218 * doSynchronousWork();
    219 * } finally {
    220 * cleanUp();
    221 * }
    222 *
    223 * // Asynchronous promise API:
    224 * doAsynchronousWork().thenFinally(cleanUp);
    225 *
    226 * __Note:__ similar to the {@code finally} clause, if the registered
    227 * callback returns a rejected promise or throws an error, it will silently
    228 * replace the rejection error (if any) from this promise:
    229 *
    230 * try {
    231 * throw Error('one');
    232 * } finally {
    233 * throw Error('two'); // Hides Error: one
    234 * }
    235 *
    236 * promise.rejected(Error('one'))
    237 * .thenFinally(function() {
    238 * throw Error('two'); // Hides Error: one
    239 * });
    240 *
    241 * @param {function(): (R|IThenable<R>)} callback The function
    242 * to call when this promise is resolved.
    243 * @return {!promise.Promise<R>} A promise that will be fulfilled
    244 * with the callback result.
    245 * @template R
    246 */
    247promise.Thenable.prototype.thenFinally = function(callback) {};
    248
    249
    250/**
    251 * Property used to flag constructor's as implementing the Thenable interface
    252 * for runtime type checking.
    253 * @type {string}
    254 * @const
    255 */
    256var IMPLEMENTED_BY_PROP = '$webdriver_Thenable';
    257
    258
    259/**
    260 * Adds a property to a class prototype to allow runtime checks of whether
    261 * instances of that class implement the Thenable interface. This function will
    262 * also ensure the prototype's {@code then} function is exported from compiled
    263 * code.
    264 * @param {function(new: promise.Thenable, ...?)} ctor The
    265 * constructor whose prototype to modify.
    266 */
    267promise.Thenable.addImplementation = function(ctor) {
    268 // Based on goog.promise.Thenable.isImplementation.
    269 ctor.prototype['then'] = ctor.prototype.then;
    270 try {
    271 // Old IE7 does not support defineProperty; IE8 only supports it for
    272 // DOM elements.
    273 Object.defineProperty(
    274 ctor.prototype,
    275 IMPLEMENTED_BY_PROP,
    276 {'value': true, 'enumerable': false});
    277 } catch (ex) {
    278 ctor.prototype[IMPLEMENTED_BY_PROP] = true;
    279 }
    280};
    281
    282
    283/**
    284 * Checks if an object has been tagged for implementing the Thenable interface
    285 * as defined by {@link webdriver.promise.Thenable.addImplementation}.
    286 * @param {*} object The object to test.
    287 * @return {boolean} Whether the object is an implementation of the Thenable
    288 * interface.
    289 */
    290promise.Thenable.isImplementation = function(object) {
    291 // Based on goog.promise.Thenable.isImplementation.
    292 if (!object) {
    293 return false;
    294 }
    295 try {
    296 return !!object[IMPLEMENTED_BY_PROP];
    297 } catch (e) {
    298 return false; // Property access seems to be forbidden.
    299 }
    300};
    301
    302
    303
    304/**
    305 * @enum {string}
    306 */
    307var PromiseState = {
    308 PENDING: 'pending',
    309 BLOCKED: 'blocked',
    310 REJECTED: 'rejected',
    311 FULFILLED: 'fulfilled'
    312};
    313
    314
    315
    316/**
    317 * Represents the eventual value of a completed operation. Each promise may be
    318 * in one of three states: pending, fulfilled, or rejected. Each promise starts
    319 * in the pending state and may make a single transition to either a
    320 * fulfilled or rejected state, at which point the promise is considered
    321 * resolved.
    322 *
    323 * @param {function(
    324 * function((T|IThenable<T>|Thenable)=),
    325 * function(*=))} resolver
    326 * Function that is invoked immediately to begin computation of this
    327 * promise's value. The function should accept a pair of callback functions,
    328 * one for fulfilling the promise and another for rejecting it.
    329 * @param {promise.ControlFlow=} opt_flow The control flow
    330 * this instance was created under. Defaults to the currently active flow.
    331 * @constructor
    332 * @implements {promise.Thenable<T>}
    333 * @template T
    334 * @see http://promises-aplus.github.io/promises-spec/
    335 */
    336promise.Promise = function(resolver, opt_flow) {
    337 goog.getUid(this);
    338
    339 /** @private {!promise.ControlFlow} */
    340 this.flow_ = opt_flow || promise.controlFlow();
    341
    342 /** @private {Error} */
    343 this.stack_ = null;
    344 if (promise.LONG_STACK_TRACES) {
    345 this.stack_ = promise.captureStackTrace('Promise', 'new', promise.Promise);
    346 }
    347
    348 /** @private {promise.Promise<?>} */
    349 this.parent_ = null;
    350
    351 /** @private {Array<!Callback>} */
    352 this.callbacks_ = null;
    353
    354 /** @private {PromiseState} */
    355 this.state_ = PromiseState.PENDING;
    356
    357 /** @private {boolean} */
    358 this.handled_ = false;
    359
    360 /** @private {boolean} */
    361 this.pendingNotifications_ = false;
    362
    363 /** @private {*} */
    364 this.value_ = undefined;
    365
    366 try {
    367 var self = this;
    368 resolver(function(value) {
    369 self.resolve_(PromiseState.FULFILLED, value);
    370 }, function(reason) {
    371 self.resolve_(PromiseState.REJECTED, reason);
    372 });
    373 } catch (ex) {
    374 this.resolve_(PromiseState.REJECTED, ex);
    375 }
    376};
    377promise.Thenable.addImplementation(promise.Promise);
    378
    379
    380/** @override */
    381promise.Promise.prototype.toString = function() {
    382 return 'Promise::' + goog.getUid(this) +
    383 ' {[[PromiseStatus]]: "' + this.state_ + '"}';
    384};
    385
    386
    387/**
    388 * Resolves this promise. If the new value is itself a promise, this function
    389 * will wait for it to be resolved before notifying the registered listeners.
    390 * @param {PromiseState} newState The promise's new state.
    391 * @param {*} newValue The promise's new value.
    392 * @throws {TypeError} If {@code newValue === this}.
    393 * @private
    394 */
    395promise.Promise.prototype.resolve_ = function(newState, newValue) {
    396 if (PromiseState.PENDING !== this.state_) {
    397 return;
    398 }
    399
    400 if (newValue === this) {
    401 // See promise a+, 2.3.1
    402 // http://promises-aplus.github.io/promises-spec/#point-48
    403 throw new TypeError('A promise may not resolve to itself');
    404 }
    405
    406 this.parent_ = null;
    407 this.state_ = PromiseState.BLOCKED;
    408
    409 if (promise.Thenable.isImplementation(newValue)) {
    410 // 2.3.2
    411 newValue = /** @type {!promise.Thenable} */(newValue);
    412 newValue.then(
    413 this.unblockAndResolve_.bind(this, PromiseState.FULFILLED),
    414 this.unblockAndResolve_.bind(this, PromiseState.REJECTED));
    415 return;
    416
    417 } else if (goog.isObject(newValue)) {
    418 // 2.3.3
    419
    420 try {
    421 // 2.3.3.1
    422 var then = newValue['then'];
    423 } catch (e) {
    424 // 2.3.3.2
    425 this.state_ = PromiseState.REJECTED;
    426 this.value_ = e;
    427 this.scheduleNotifications_();
    428 return;
    429 }
    430
    431 // NB: goog.isFunction is loose and will accept instanceof Function.
    432 if (typeof then === 'function') {
    433 // 2.3.3.3
    434 this.invokeThen_(newValue, then);
    435 return;
    436 }
    437 }
    438
    439 if (newState === PromiseState.REJECTED &&
    440 isError(newValue) && newValue.stack && this.stack_) {
    441 newValue.stack += '\nFrom: ' + (this.stack_.stack || this.stack_);
    442 }
    443
    444 // 2.3.3.4 and 2.3.4
    445 this.state_ = newState;
    446 this.value_ = newValue;
    447 this.scheduleNotifications_();
    448};
    449
    450
    451/**
    452 * Invokes a thenable's "then" method according to 2.3.3.3 of the promise
    453 * A+ spec.
    454 * @param {!Object} x The thenable object.
    455 * @param {!Function} then The "then" function to invoke.
    456 * @private
    457 */
    458promise.Promise.prototype.invokeThen_ = function(x, then) {
    459 var called = false;
    460 var self = this;
    461
    462 var resolvePromise = function(value) {
    463 if (!called) { // 2.3.3.3.3
    464 called = true;
    465 // 2.3.3.3.1
    466 self.unblockAndResolve_(PromiseState.FULFILLED, value);
    467 }
    468 };
    469
    470 var rejectPromise = function(reason) {
    471 if (!called) { // 2.3.3.3.3
    472 called = true;
    473 // 2.3.3.3.2
    474 self.unblockAndResolve_(PromiseState.REJECTED, reason);
    475 }
    476 };
    477
    478 try {
    479 // 2.3.3.3
    480 then.call(x, resolvePromise, rejectPromise);
    481 } catch (e) {
    482 // 2.3.3.3.4.2
    483 rejectPromise(e);
    484 }
    485};
    486
    487
    488/**
    489 * @param {PromiseState} newState The promise's new state.
    490 * @param {*} newValue The promise's new value.
    491 * @private
    492 */
    493promise.Promise.prototype.unblockAndResolve_ = function(newState, newValue) {
    494 if (this.state_ === PromiseState.BLOCKED) {
    495 this.state_ = PromiseState.PENDING;
    496 this.resolve_(newState, newValue);
    497 }
    498};
    499
    500
    501/**
    502 * @private
    503 */
    504promise.Promise.prototype.scheduleNotifications_ = function() {
    505 if (!this.pendingNotifications_) {
    506 this.pendingNotifications_ = true;
    507 this.flow_.suspend_();
    508
    509 var activeFrame;
    510
    511 if (!this.handled_ &&
    512 this.state_ === PromiseState.REJECTED &&
    513 !(this.value_ instanceof promise.CancellationError)) {
    514 activeFrame = this.flow_.getActiveFrame_();
    515 activeFrame.pendingRejection = true;
    516 }
    517
    518 if (this.callbacks_ && this.callbacks_.length) {
    519 activeFrame = this.flow_.getRunningFrame_();
    520 var self = this;
    521 this.callbacks_.forEach(function(callback) {
    522 if (!callback.frame_.getParent()) {
    523 activeFrame.addChild(callback.frame_);
    524 }
    525 });
    526 }
    527
    528 asyncRun(goog.bind(this.notifyAll_, this, activeFrame));
    529 }
    530};
    531
    532
    533/**
    534 * Notifies all of the listeners registered with this promise that its state
    535 * has changed.
    536 * @param {Frame} frame The active frame from when this round of
    537 * notifications were scheduled.
    538 * @private
    539 */
    540promise.Promise.prototype.notifyAll_ = function(frame) {
    541 this.flow_.resume_();
    542 this.pendingNotifications_ = false;
    543
    544 if (!this.handled_ &&
    545 this.state_ === PromiseState.REJECTED &&
    546 !(this.value_ instanceof promise.CancellationError)) {
    547 this.flow_.abortFrame_(this.value_, frame);
    548 }
    549
    550 if (this.callbacks_) {
    551 var callbacks = this.callbacks_;
    552 this.callbacks_ = null;
    553 callbacks.forEach(this.notify_, this);
    554 }
    555};
    556
    557
    558/**
    559 * Notifies a single callback of this promise's change ins tate.
    560 * @param {Callback} callback The callback to notify.
    561 * @private
    562 */
    563promise.Promise.prototype.notify_ = function(callback) {
    564 callback.notify(this.state_, this.value_);
    565};
    566
    567
    568/** @override */
    569promise.Promise.prototype.cancel = function(opt_reason) {
    570 if (!this.isPending()) {
    571 return;
    572 }
    573
    574 if (this.parent_) {
    575 this.parent_.cancel(opt_reason);
    576 } else {
    577 this.resolve_(
    578 PromiseState.REJECTED,
    579 promise.CancellationError.wrap(opt_reason));
    580 }
    581};
    582
    583
    584/** @override */
    585promise.Promise.prototype.isPending = function() {
    586 return this.state_ === PromiseState.PENDING;
    587};
    588
    589
    590/** @override */
    591promise.Promise.prototype.then = function(opt_callback, opt_errback) {
    592 return this.addCallback_(
    593 opt_callback, opt_errback, 'then', promise.Promise.prototype.then);
    594};
    595
    596
    597/** @override */
    598promise.Promise.prototype.thenCatch = function(errback) {
    599 return this.addCallback_(
    600 null, errback, 'thenCatch', promise.Promise.prototype.thenCatch);
    601};
    602
    603
    604/** @override */
    605promise.Promise.prototype.thenFinally = function(callback) {
    606 var error;
    607 var mustThrow = false;
    608 return this.then(function() {
    609 return callback();
    610 }, function(err) {
    611 error = err;
    612 mustThrow = true;
    613 return callback();
    614 }).then(function() {
    615 if (mustThrow) {
    616 throw error;
    617 }
    618 });
    619};
    620
    621
    622/**
    623 * Registers a new callback with this promise
    624 * @param {(function(T): (R|IThenable<R>)|null|undefined)} callback The
    625 * fulfillment callback.
    626 * @param {(function(*): (R|IThenable<R>)|null|undefined)} errback The
    627 * rejection callback.
    628 * @param {string} name The callback name.
    629 * @param {!Function} fn The function to use as the top of the stack when
    630 * recording the callback's creation point.
    631 * @return {!promise.Promise<R>} A new promise which will be resolved with the
    632 * esult of the invoked callback.
    633 * @template R
    634 * @private
    635 */
    636promise.Promise.prototype.addCallback_ = function(callback, errback, name, fn) {
    637 if (!goog.isFunction(callback) && !goog.isFunction(errback)) {
    638 return this;
    639 }
    640
    641 this.handled_ = true;
    642 var cb = new Callback(this, callback, errback, name, fn);
    643
    644 if (!this.callbacks_) {
    645 this.callbacks_ = [];
    646 }
    647 this.callbacks_.push(cb);
    648
    649 if (this.state_ !== PromiseState.PENDING &&
    650 this.state_ !== PromiseState.BLOCKED) {
    651 this.flow_.getSchedulingFrame_().addChild(cb.frame_);
    652 this.scheduleNotifications_();
    653 }
    654 return cb.promise;
    655};
    656
    657
    658/**
    659 * Represents a value that will be resolved at some point in the future. This
    660 * class represents the protected "producer" half of a Promise - each Deferred
    661 * has a {@code promise} property that may be returned to consumers for
    662 * registering callbacks, reserving the ability to resolve the deferred to the
    663 * producer.
    664 *
    665 * If this Deferred is rejected and there are no listeners registered before
    666 * the next turn of the event loop, the rejection will be passed to the
    667 * {@link webdriver.promise.ControlFlow} as an unhandled failure.
    668 *
    669 * @param {promise.ControlFlow=} opt_flow The control flow
    670 * this instance was created under. This should only be provided during
    671 * unit tests.
    672 * @constructor
    673 * @implements {promise.Thenable<T>}
    674 * @template T
    675 */
    676promise.Deferred = function(opt_flow) {
    677 var fulfill, reject;
    678
    679 /** @type {!promise.Promise<T>} */
    680 this.promise = new promise.Promise(function(f, r) {
    681 fulfill = f;
    682 reject = r;
    683 }, opt_flow);
    684
    685 var self = this;
    686 var checkNotSelf = function(value) {
    687 if (value === self) {
    688 throw new TypeError('May not resolve a Deferred with itself');
    689 }
    690 };
    691
    692 /**
    693 * Resolves this deferred with the given value. It is safe to call this as a
    694 * normal function (with no bound "this").
    695 * @param {(T|IThenable<T>|Thenable)=} opt_value The fulfilled value.
    696 */
    697 this.fulfill = function(opt_value) {
    698 checkNotSelf(opt_value);
    699 fulfill(opt_value);
    700 };
    701
    702 /**
    703 * Rejects this promise with the given reason. It is safe to call this as a
    704 * normal function (with no bound "this").
    705 * @param {*=} opt_reason The rejection reason.
    706 */
    707 this.reject = function(opt_reason) {
    708 checkNotSelf(opt_reason);
    709 reject(opt_reason);
    710 };
    711};
    712promise.Thenable.addImplementation(promise.Deferred);
    713
    714
    715/** @override */
    716promise.Deferred.prototype.isPending = function() {
    717 return this.promise.isPending();
    718};
    719
    720
    721/** @override */
    722promise.Deferred.prototype.cancel = function(opt_reason) {
    723 this.promise.cancel(opt_reason);
    724};
    725
    726
    727/**
    728 * @override
    729 * @deprecated Use {@code then} from the promise property directly.
    730 */
    731promise.Deferred.prototype.then = function(opt_cb, opt_eb) {
    732 return this.promise.then(opt_cb, opt_eb);
    733};
    734
    735
    736/**
    737 * @override
    738 * @deprecated Use {@code thenCatch} from the promise property directly.
    739 */
    740promise.Deferred.prototype.thenCatch = function(opt_eb) {
    741 return this.promise.thenCatch(opt_eb);
    742};
    743
    744
    745/**
    746 * @override
    747 * @deprecated Use {@code thenFinally} from the promise property directly.
    748 */
    749promise.Deferred.prototype.thenFinally = function(opt_cb) {
    750 return this.promise.thenFinally(opt_cb);
    751};
    752
    753
    754/**
    755 * Tests if a value is an Error-like object. This is more than an straight
    756 * instanceof check since the value may originate from another context.
    757 * @param {*} value The value to test.
    758 * @return {boolean} Whether the value is an error.
    759 */
    760function isError(value) {
    761 return value instanceof Error ||
    762 goog.isObject(value) &&
    763 (goog.isString(value.message) ||
    764 // A special test for goog.testing.JsUnitException.
    765 value.isJsUnitException);
    766
    767};
    768
    769
    770/**
    771 * Determines whether a {@code value} should be treated as a promise.
    772 * Any object whose "then" property is a function will be considered a promise.
    773 *
    774 * @param {*} value The value to test.
    775 * @return {boolean} Whether the value is a promise.
    776 */
    777promise.isPromise = function(value) {
    778 return !!value && goog.isObject(value) &&
    779 // Use array notation so the Closure compiler does not obfuscate away our
    780 // contract. Use typeof rather than goog.isFunction because
    781 // goog.isFunction accepts instanceof Function, which the promise spec
    782 // does not.
    783 typeof value['then'] === 'function';
    784};
    785
    786
    787/**
    788 * Creates a promise that will be resolved at a set time in the future.
    789 * @param {number} ms The amount of time, in milliseconds, to wait before
    790 * resolving the promise.
    791 * @return {!promise.Promise} The promise.
    792 */
    793promise.delayed = function(ms) {
    794 var key;
    795 return new promise.Promise(function(fulfill) {
    796 key = setTimeout(function() {
    797 key = null;
    798 fulfill();
    799 }, ms);
    800 }).thenCatch(function(e) {
    801 clearTimeout(key);
    802 key = null;
    803 throw e;
    804 });
    805};
    806
    807
    808/**
    809 * Creates a new deferred object.
    810 * @return {!promise.Deferred<T>} The new deferred object.
    811 * @template T
    812 */
    813promise.defer = function() {
    814 return new promise.Deferred();
    815};
    816
    817
    818/**
    819 * Creates a promise that has been resolved with the given value.
    820 * @param {T=} opt_value The resolved value.
    821 * @return {!promise.Promise<T>} The resolved promise.
    822 * @template T
    823 */
    824promise.fulfilled = function(opt_value) {
    825 if (opt_value instanceof promise.Promise) {
    826 return opt_value;
    827 }
    828 return new promise.Promise(function(fulfill) {
    829 fulfill(opt_value);
    830 });
    831};
    832
    833
    834/**
    835 * Creates a promise that has been rejected with the given reason.
    836 * @param {*=} opt_reason The rejection reason; may be any value, but is
    837 * usually an Error or a string.
    838 * @return {!promise.Promise<T>} The rejected promise.
    839 * @template T
    840 */
    841promise.rejected = function(opt_reason) {
    842 if (opt_reason instanceof promise.Promise) {
    843 return opt_reason;
    844 }
    845 return new promise.Promise(function(_, reject) {
    846 reject(opt_reason);
    847 });
    848};
    849
    850
    851/**
    852 * Wraps a function that expects a node-style callback as its final
    853 * argument. This callback expects two arguments: an error value (which will be
    854 * null if the call succeeded), and the success value as the second argument.
    855 * The callback will the resolve or reject the returned promise, based on its arguments.
    856 * @param {!Function} fn The function to wrap.
    857 * @param {...?} var_args The arguments to apply to the function, excluding the
    858 * final callback.
    859 * @return {!promise.Promise} A promise that will be resolved with the
    860 * result of the provided function's callback.
    861 */
    862promise.checkedNodeCall = function(fn, var_args) {
    863 var args = Arrays.slice(arguments, 1);
    864 return new promise.Promise(function(fulfill, reject) {
    865 try {
    866 args.push(function(error, value) {
    867 error ? reject(error) : fulfill(value);
    868 });
    869 fn.apply(undefined, args);
    870 } catch (ex) {
    871 reject(ex);
    872 }
    873 });
    874};
    875
    876
    877/**
    878 * Registers an observer on a promised {@code value}, returning a new promise
    879 * that will be resolved when the value is. If {@code value} is not a promise,
    880 * then the return promise will be immediately resolved.
    881 * @param {*} value The value to observe.
    882 * @param {Function=} opt_callback The function to call when the value is
    883 * resolved successfully.
    884 * @param {Function=} opt_errback The function to call when the value is
    885 * rejected.
    886 * @return {!promise.Promise} A new promise.
    887 */
    888promise.when = function(value, opt_callback, opt_errback) {
    889 if (promise.Thenable.isImplementation(value)) {
    890 return value.then(opt_callback, opt_errback);
    891 }
    892
    893 return new promise.Promise(function(fulfill, reject) {
    894 promise.asap(value, fulfill, reject);
    895 }).then(opt_callback, opt_errback);
    896};
    897
    898
    899/**
    900 * Invokes the appropriate callback function as soon as a promised
    901 * {@code value} is resolved. This function is similar to
    902 * {@link webdriver.promise.when}, except it does not return a new promise.
    903 * @param {*} value The value to observe.
    904 * @param {Function} callback The function to call when the value is
    905 * resolved successfully.
    906 * @param {Function=} opt_errback The function to call when the value is
    907 * rejected.
    908 */
    909promise.asap = function(value, callback, opt_errback) {
    910 if (promise.isPromise(value)) {
    911 value.then(callback, opt_errback);
    912
    913 // Maybe a Dojo-like deferred object?
    914 } else if (!!value && goog.isObject(value) &&
    915 goog.isFunction(value.addCallbacks)) {
    916 value.addCallbacks(callback, opt_errback);
    917
    918 // A raw value, return a resolved promise.
    919 } else if (callback) {
    920 callback(value);
    921 }
    922};
    923
    924
    925/**
    926 * Given an array of promises, will return a promise that will be fulfilled
    927 * with the fulfillment values of the input array's values. If any of the
    928 * input array's promises are rejected, the returned promise will be rejected
    929 * with the same reason.
    930 *
    931 * @param {!Array<(T|!promise.Promise<T>)>} arr An array of
    932 * promises to wait on.
    933 * @return {!promise.Promise<!Array<T>>} A promise that is
    934 * fulfilled with an array containing the fulfilled values of the
    935 * input array, or rejected with the same reason as the first
    936 * rejected value.
    937 * @template T
    938 */
    939promise.all = function(arr) {
    940 return new promise.Promise(function(fulfill, reject) {
    941 var n = arr.length;
    942 var values = [];
    943
    944 if (!n) {
    945 fulfill(values);
    946 return;
    947 }
    948
    949 var toFulfill = n;
    950 var onFulfilled = function(index, value) {
    951 values[index] = value;
    952 toFulfill--;
    953 if (toFulfill == 0) {
    954 fulfill(values);
    955 }
    956 };
    957
    958 for (var i = 0; i < n; ++i) {
    959 promise.asap(arr[i], goog.partial(onFulfilled, i), reject);
    960 }
    961 });
    962};
    963
    964
    965/**
    966 * Calls a function for each element in an array and inserts the result into a
    967 * new array, which is used as the fulfillment value of the promise returned
    968 * by this function.
    969 *
    970 * If the return value of the mapping function is a promise, this function
    971 * will wait for it to be fulfilled before inserting it into the new array.
    972 *
    973 * If the mapping function throws or returns a rejected promise, the
    974 * promise returned by this function will be rejected with the same reason.
    975 * Only the first failure will be reported; all subsequent errors will be
    976 * silently ignored.
    977 *
    978 * @param {!(Array<TYPE>|promise.Promise<!Array<TYPE>>)} arr The
    979 * array to iterator over, or a promise that will resolve to said array.
    980 * @param {function(this: SELF, TYPE, number, !Array<TYPE>): ?} fn The
    981 * function to call for each element in the array. This function should
    982 * expect three arguments (the element, the index, and the array itself.
    983 * @param {SELF=} opt_self The object to be used as the value of 'this' within
    984 * {@code fn}.
    985 * @template TYPE, SELF
    986 */
    987promise.map = function(arr, fn, opt_self) {
    988 return promise.fulfilled(arr).then(function(arr) {
    989 goog.asserts.assertNumber(arr.length, 'not an array like value');
    990 return new promise.Promise(function(fulfill, reject) {
    991 var n = arr.length;
    992 var values = new Array(n);
    993 (function processNext(i) {
    994 for (; i < n; i++) {
    995 if (i in arr) {
    996 break;
    997 }
    998 }
    999 if (i >= n) {
    1000 fulfill(values);
    1001 return;
    1002 }
    1003 try {
    1004 promise.asap(
    1005 fn.call(opt_self, arr[i], i, /** @type {!Array} */(arr)),
    1006 function(value) {
    1007 values[i] = value;
    1008 processNext(i + 1);
    1009 },
    1010 reject);
    1011 } catch (ex) {
    1012 reject(ex);
    1013 }
    1014 })(0);
    1015 });
    1016 });
    1017};
    1018
    1019
    1020/**
    1021 * Calls a function for each element in an array, and if the function returns
    1022 * true adds the element to a new array.
    1023 *
    1024 * If the return value of the filter function is a promise, this function
    1025 * will wait for it to be fulfilled before determining whether to insert the
    1026 * element into the new array.
    1027 *
    1028 * If the filter function throws or returns a rejected promise, the promise
    1029 * returned by this function will be rejected with the same reason. Only the
    1030 * first failure will be reported; all subsequent errors will be silently
    1031 * ignored.
    1032 *
    1033 * @param {!(Array<TYPE>|promise.Promise<!Array<TYPE>>)} arr The
    1034 * array to iterator over, or a promise that will resolve to said array.
    1035 * @param {function(this: SELF, TYPE, number, !Array<TYPE>): (
    1036 * boolean|promise.Promise<boolean>)} fn The function
    1037 * to call for each element in the array.
    1038 * @param {SELF=} opt_self The object to be used as the value of 'this' within
    1039 * {@code fn}.
    1040 * @template TYPE, SELF
    1041 */
    1042promise.filter = function(arr, fn, opt_self) {
    1043 return promise.fulfilled(arr).then(function(arr) {
    1044 goog.asserts.assertNumber(arr.length, 'not an array like value');
    1045 return new promise.Promise(function(fulfill, reject) {
    1046 var n = arr.length;
    1047 var values = [];
    1048 var valuesLength = 0;
    1049 (function processNext(i) {
    1050 for (; i < n; i++) {
    1051 if (i in arr) {
    1052 break;
    1053 }
    1054 }
    1055 if (i >= n) {
    1056 fulfill(values);
    1057 return;
    1058 }
    1059 try {
    1060 var value = arr[i];
    1061 var include = fn.call(opt_self, value, i, /** @type {!Array} */(arr));
    1062 promise.asap(include, function(include) {
    1063 if (include) {
    1064 values[valuesLength++] = value;
    1065 }
    1066 processNext(i + 1);
    1067 }, reject);
    1068 } catch (ex) {
    1069 reject(ex);
    1070 }
    1071 })(0);
    1072 });
    1073 });
    1074};
    1075
    1076
    1077/**
    1078 * Returns a promise that will be resolved with the input value in a
    1079 * fully-resolved state. If the value is an array, each element will be fully
    1080 * resolved. Likewise, if the value is an object, all keys will be fully
    1081 * resolved. In both cases, all nested arrays and objects will also be
    1082 * fully resolved. All fields are resolved in place; the returned promise will
    1083 * resolve on {@code value} and not a copy.
    1084 *
    1085 * Warning: This function makes no checks against objects that contain
    1086 * cyclical references:
    1087 *
    1088 * var value = {};
    1089 * value['self'] = value;
    1090 * promise.fullyResolved(value); // Stack overflow.
    1091 *
    1092 * @param {*} value The value to fully resolve.
    1093 * @return {!promise.Promise} A promise for a fully resolved version
    1094 * of the input value.
    1095 */
    1096promise.fullyResolved = function(value) {
    1097 if (promise.isPromise(value)) {
    1098 return promise.when(value, fullyResolveValue);
    1099 }
    1100 return fullyResolveValue(value);
    1101};
    1102
    1103
    1104/**
    1105 * @param {*} value The value to fully resolve. If a promise, assumed to
    1106 * already be resolved.
    1107 * @return {!promise.Promise} A promise for a fully resolved version
    1108 * of the input value.
    1109 */
    1110 function fullyResolveValue(value) {
    1111 switch (goog.typeOf(value)) {
    1112 case 'array':
    1113 return fullyResolveKeys(/** @type {!Array} */ (value));
    1114
    1115 case 'object':
    1116 if (promise.isPromise(value)) {
    1117 // We get here when the original input value is a promise that
    1118 // resolves to itself. When the user provides us with such a promise,
    1119 // trust that it counts as a "fully resolved" value and return it.
    1120 // Of course, since it's already a promise, we can just return it
    1121 // to the user instead of wrapping it in another promise.
    1122 return /** @type {!promise.Promise} */ (value);
    1123 }
    1124
    1125 if (goog.isNumber(value.nodeType) &&
    1126 goog.isObject(value.ownerDocument) &&
    1127 goog.isNumber(value.ownerDocument.nodeType)) {
    1128 // DOM node; return early to avoid infinite recursion. Should we
    1129 // only support objects with a certain level of nesting?
    1130 return promise.fulfilled(value);
    1131 }
    1132
    1133 return fullyResolveKeys(/** @type {!Object} */ (value));
    1134
    1135 default: // boolean, function, null, number, string, undefined
    1136 return promise.fulfilled(value);
    1137 }
    1138};
    1139
    1140
    1141/**
    1142 * @param {!(Array|Object)} obj the object to resolve.
    1143 * @return {!promise.Promise} A promise that will be resolved with the
    1144 * input object once all of its values have been fully resolved.
    1145 */
    1146 function fullyResolveKeys(obj) {
    1147 var isArray = goog.isArray(obj);
    1148 var numKeys = isArray ? obj.length : Objects.getCount(obj);
    1149 if (!numKeys) {
    1150 return promise.fulfilled(obj);
    1151 }
    1152
    1153 var numResolved = 0;
    1154 return new promise.Promise(function(fulfill, reject) {
    1155 // In pre-IE9, goog.array.forEach will not iterate properly over arrays
    1156 // containing undefined values because "index in array" returns false
    1157 // when array[index] === undefined (even for x = [undefined, 1]). To get
    1158 // around this, we need to use our own forEach implementation.
    1159 // DO NOT REMOVE THIS UNTIL WE NO LONGER SUPPORT IE8. This cannot be
    1160 // reproduced in IE9 by changing the browser/document modes, it requires an
    1161 // actual pre-IE9 browser. Yay, IE!
    1162 var forEachKey = !isArray ? Objects.forEach : function(arr, fn) {
    1163 var n = arr.length;
    1164 for (var i = 0; i < n; ++i) {
    1165 fn.call(null, arr[i], i, arr);
    1166 }
    1167 };
    1168
    1169 forEachKey(obj, function(partialValue, key) {
    1170 var type = goog.typeOf(partialValue);
    1171 if (type != 'array' && type != 'object') {
    1172 maybeResolveValue();
    1173 return;
    1174 }
    1175
    1176 promise.fullyResolved(partialValue).then(
    1177 function(resolvedValue) {
    1178 obj[key] = resolvedValue;
    1179 maybeResolveValue();
    1180 },
    1181 reject);
    1182 });
    1183
    1184 function maybeResolveValue() {
    1185 if (++numResolved == numKeys) {
    1186 fulfill(obj);
    1187 }
    1188 }
    1189 });
    1190};
    1191
    1192
    1193//////////////////////////////////////////////////////////////////////////////
    1194//
    1195// promise.ControlFlow
    1196//
    1197//////////////////////////////////////////////////////////////////////////////
    1198
    1199
    1200
    1201/**
    1202 * Handles the execution of scheduled tasks, each of which may be an
    1203 * asynchronous operation. The control flow will ensure tasks are executed in
    1204 * the ordered scheduled, starting each task only once those before it have
    1205 * completed.
    1206 *
    1207 * Each task scheduled within this flow may return a
    1208 * {@link webdriver.promise.Promise} to indicate it is an asynchronous
    1209 * operation. The ControlFlow will wait for such promises to be resolved before
    1210 * marking the task as completed.
    1211 *
    1212 * Tasks and each callback registered on a {@link webdriver.promise.Promise}
    1213 * will be run in their own ControlFlow frame. Any tasks scheduled within a
    1214 * frame will take priority over previously scheduled tasks. Furthermore, if any
    1215 * of the tasks in the frame fail, the remainder of the tasks in that frame will
    1216 * be discarded and the failure will be propagated to the user through the
    1217 * callback/task's promised result.
    1218 *
    1219 * Each time a ControlFlow empties its task queue, it will fire an
    1220 * {@link webdriver.promise.ControlFlow.EventType.IDLE IDLE} event. Conversely,
    1221 * whenever the flow terminates due to an unhandled error, it will remove all
    1222 * remaining tasks in its queue and fire an
    1223 * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION
    1224 * UNCAUGHT_EXCEPTION} event. If there are no listeners registered with the
    1225 * flow, the error will be rethrown to the global error handler.
    1226 *
    1227 * @constructor
    1228 * @extends {EventEmitter}
    1229 * @final
    1230 */
    1231promise.ControlFlow = function() {
    1232 EventEmitter.call(this);
    1233 goog.getUid(this);
    1234
    1235 /**
    1236 * Tracks the active execution frame for this instance. Lazily initialized
    1237 * when the first task is scheduled.
    1238 * @private {Frame}
    1239 */
    1240 this.activeFrame_ = null;
    1241
    1242 /**
    1243 * A reference to the frame which is currently top of the stack in
    1244 * {@link #runInFrame_}. The {@link #activeFrame_} will always be an ancestor
    1245 * of the {@link #runningFrame_}, but the two will often not be the same. The
    1246 * active frame represents which frame is currently executing a task while the
    1247 * running frame represents either the task itself or a promise callback which
    1248 * has fired asynchronously.
    1249 * @private {Frame}
    1250 */
    1251 this.runningFrame_ = null;
    1252
    1253 /**
    1254 * A reference to the frame in which new tasks should be scheduled. If
    1255 * {@code null}, tasks will be scheduled within the active frame. When forcing
    1256 * a function to run in the context of a new frame, this pointer is used to
    1257 * ensure tasks are scheduled within the newly created frame, even though it
    1258 * won't be active yet.
    1259 * @private {Frame}
    1260 * @see {#runInFrame_}
    1261 */
    1262 this.schedulingFrame_ = null;
    1263
    1264 /**
    1265 * Micro task that controls shutting down the control flow. Upon shut down,
    1266 * the flow will emit an {@link webdriver.promise.ControlFlow.EventType.IDLE}
    1267 * event. Idle events always follow a brief timeout in order to catch latent
    1268 * errors from the last completed task. If this task had a callback
    1269 * registered, but no errback, and the task fails, the unhandled failure would
    1270 * not be reported by the promise system until the next turn of the event
    1271 * loop:
    1272 *
    1273 * // Schedule 1 task that fails.
    1274 * var result = promise.controlFlow().schedule('example',
    1275 * function() { return promise.rejected('failed'); });
    1276 * // Set a callback on the result. This delays reporting the unhandled
    1277 * // failure for 1 turn of the event loop.
    1278 * result.then(goog.nullFunction);
    1279 *
    1280 * @private {MicroTask}
    1281 */
    1282 this.shutdownTask_ = null;
    1283
    1284 /**
    1285 * Micro task used to trigger execution of this instance's event loop.
    1286 * @private {MicroTask}
    1287 */
    1288 this.eventLoopTask_ = null;
    1289
    1290 /**
    1291 * ID for a long running interval used to keep a Node.js process running
    1292 * while a control flow's event loop has yielded. This is a cheap hack
    1293 * required since the {@link #runEventLoop_} is only scheduled to run when
    1294 * there is _actually_ something to run. When a control flow is waiting on
    1295 * a task, there will be nothing in the JS event loop and the process would
    1296 * terminate without this.
    1297 *
    1298 * An alternative solution would be to change {@link #runEventLoop_} to run
    1299 * as an interval rather than as on-demand micro-tasks. While this approach
    1300 * (which was previously used) requires fewer micro-task allocations, it
    1301 * results in many unnecessary invocations of {@link #runEventLoop_}.
    1302 *
    1303 * @private {?number}
    1304 */
    1305 this.hold_ = null;
    1306
    1307 /**
    1308 * The number of holds placed on this flow. These represent points where the
    1309 * flow must not execute any further actions so an asynchronous action may
    1310 * run first. One such example are notifications fired by a
    1311 * {@link webdriver.promise.Promise}: the Promise spec requires that callbacks
    1312 * are invoked in a turn of the event loop after they are scheduled. To ensure
    1313 * tasks within a callback are scheduled in the correct frame, a promise will
    1314 * make the parent flow yield before its notifications are fired.
    1315 * @private {number}
    1316 */
    1317 this.yieldCount_ = 0;
    1318};
    1319goog.inherits(promise.ControlFlow, EventEmitter);
    1320
    1321
    1322/**
    1323 * Events that may be emitted by an {@link webdriver.promise.ControlFlow}.
    1324 * @enum {string}
    1325 */
    1326promise.ControlFlow.EventType = {
    1327
    1328 /** Emitted when all tasks have been successfully executed. */
    1329 IDLE: 'idle',
    1330
    1331 /** Emitted when a ControlFlow has been reset. */
    1332 RESET: 'reset',
    1333
    1334 /** Emitted whenever a new task has been scheduled. */
    1335 SCHEDULE_TASK: 'scheduleTask',
    1336
    1337 /**
    1338 * Emitted whenever a control flow aborts due to an unhandled promise
    1339 * rejection. This event will be emitted along with the offending rejection
    1340 * reason. Upon emitting this event, the control flow will empty its task
    1341 * queue and revert to its initial state.
    1342 */
    1343 UNCAUGHT_EXCEPTION: 'uncaughtException'
    1344};
    1345
    1346
    1347/**
    1348 * Returns a string representation of this control flow, which is its current
    1349 * {@link #getSchedule() schedule}, sans task stack traces.
    1350 * @return {string} The string representation of this contorl flow.
    1351 * @override
    1352 */
    1353promise.ControlFlow.prototype.toString = function() {
    1354 return this.getSchedule();
    1355};
    1356
    1357
    1358/**
    1359 * Resets this instance, clearing its queue and removing all event listeners.
    1360 */
    1361promise.ControlFlow.prototype.reset = function() {
    1362 this.activeFrame_ = null;
    1363 this.schedulingFrame_ = null;
    1364 this.emit(promise.ControlFlow.EventType.RESET);
    1365 this.removeAllListeners();
    1366 this.cancelShutdown_();
    1367 this.cancelEventLoop_();
    1368};
    1369
    1370
    1371/**
    1372 * Generates an annotated string describing the internal state of this control
    1373 * flow, including the currently executing as well as pending tasks. If
    1374 * {@code opt_includeStackTraces === true}, the string will include the
    1375 * stack trace from when each task was scheduled.
    1376 * @param {string=} opt_includeStackTraces Whether to include the stack traces
    1377 * from when each task was scheduled. Defaults to false.
    1378 * @return {string} String representation of this flow's internal state.
    1379 */
    1380promise.ControlFlow.prototype.getSchedule = function(opt_includeStackTraces) {
    1381 var ret = 'ControlFlow::' + goog.getUid(this);
    1382 var activeFrame = this.activeFrame_;
    1383 var runningFrame = this.runningFrame_;
    1384 if (!activeFrame) {
    1385 return ret;
    1386 }
    1387 var childIndent = '| ';
    1388 return ret + '\n' + toStringHelper(activeFrame.getRoot(), childIndent);
    1389
    1390 /**
    1391 * @param {!(Frame|Task)} node .
    1392 * @param {string} indent .
    1393 * @param {boolean=} opt_isPending .
    1394 * @return {string} .
    1395 */
    1396 function toStringHelper(node, indent, opt_isPending) {
    1397 var ret = node.toString();
    1398 if (opt_isPending) {
    1399 ret = '(pending) ' + ret;
    1400 }
    1401 if (node === activeFrame) {
    1402 ret = '(active) ' + ret;
    1403 }
    1404 if (node === runningFrame) {
    1405 ret = '(running) ' + ret;
    1406 }
    1407 if (node instanceof Frame) {
    1408 if (node.getPendingTask()) {
    1409 ret += '\n' + toStringHelper(
    1410 /** @type {!Task} */(node.getPendingTask()),
    1411 childIndent,
    1412 true);
    1413 }
    1414 if (node.children_) {
    1415 node.children_.forEach(function(child) {
    1416 if (!node.getPendingTask() ||
    1417 node.getPendingTask().getFrame() !== child) {
    1418 ret += '\n' + toStringHelper(child, childIndent);
    1419 }
    1420 });
    1421 }
    1422 } else {
    1423 var task = /** @type {!Task} */(node);
    1424 if (opt_includeStackTraces && task.promise.stack_) {
    1425 ret += '\n' + childIndent +
    1426 (task.promise.stack_.stack || task.promise.stack_).
    1427 replace(/\n/g, '\n' + childIndent);
    1428 }
    1429 if (task.getFrame()) {
    1430 ret += '\n' + toStringHelper(
    1431 /** @type {!Frame} */(task.getFrame()),
    1432 childIndent);
    1433 }
    1434 }
    1435 return indent + ret.replace(/\n/g, '\n' + indent);
    1436 }
    1437};
    1438
    1439
    1440/**
    1441 * @return {!Frame} The active frame for this flow.
    1442 * @private
    1443 */
    1444promise.ControlFlow.prototype.getActiveFrame_ = function() {
    1445 this.cancelShutdown_();
    1446 if (!this.activeFrame_) {
    1447 this.activeFrame_ = new Frame(this);
    1448 this.activeFrame_.once(Frame.ERROR_EVENT, this.abortNow_, this);
    1449 this.scheduleEventLoopStart_();
    1450 }
    1451 return this.activeFrame_;
    1452};
    1453
    1454
    1455/**
    1456 * @return {!Frame} The frame that new items should be added to.
    1457 * @private
    1458 */
    1459promise.ControlFlow.prototype.getSchedulingFrame_ = function() {
    1460 return this.schedulingFrame_ || this.getActiveFrame_();
    1461};
    1462
    1463
    1464/**
    1465 * @return {!Frame} The frame that is current executing.
    1466 * @private
    1467 */
    1468promise.ControlFlow.prototype.getRunningFrame_ = function() {
    1469 return this.runningFrame_ || this.getActiveFrame_();
    1470};
    1471
    1472
    1473/**
    1474 * Schedules a task for execution. If there is nothing currently in the
    1475 * queue, the task will be executed in the next turn of the event loop. If
    1476 * the task function is a generator, the task will be executed using
    1477 * {@link webdriver.promise.consume}.
    1478 *
    1479 * @param {function(): (T|promise.Promise<T>)} fn The function to
    1480 * call to start the task. If the function returns a
    1481 * {@link webdriver.promise.Promise}, this instance will wait for it to be
    1482 * resolved before starting the next task.
    1483 * @param {string=} opt_description A description of the task.
    1484 * @return {!promise.Promise<T>} A promise that will be resolved
    1485 * with the result of the action.
    1486 * @template T
    1487 */
    1488promise.ControlFlow.prototype.execute = function(fn, opt_description) {
    1489 if (promise.isGenerator(fn)) {
    1490 fn = goog.partial(promise.consume, fn);
    1491 }
    1492
    1493 if (!this.hold_) {
    1494 var holdIntervalMs = 2147483647; // 2^31-1; max timer length for Node.js
    1495 this.hold_ = setInterval(goog.nullFunction, holdIntervalMs);
    1496 }
    1497
    1498 var description = opt_description || '<anonymous>';
    1499 var task = new Task(this, fn, description);
    1500 task.promise.stack_ = promise.captureStackTrace('Task', description,
    1501 promise.ControlFlow.prototype.execute);
    1502
    1503 this.getSchedulingFrame_().addChild(task);
    1504 this.emit(promise.ControlFlow.EventType.SCHEDULE_TASK, opt_description);
    1505 this.scheduleEventLoopStart_();
    1506 return task.promise;
    1507};
    1508
    1509
    1510/**
    1511 * Inserts a {@code setTimeout} into the command queue. This is equivalent to
    1512 * a thread sleep in a synchronous programming language.
    1513 *
    1514 * @param {number} ms The timeout delay, in milliseconds.
    1515 * @param {string=} opt_description A description to accompany the timeout.
    1516 * @return {!promise.Promise} A promise that will be resolved with
    1517 * the result of the action.
    1518 */
    1519promise.ControlFlow.prototype.timeout = function(ms, opt_description) {
    1520 return this.execute(function() {
    1521 return promise.delayed(ms);
    1522 }, opt_description);
    1523};
    1524
    1525
    1526/**
    1527 * Schedules a task that shall wait for a condition to hold. Each condition
    1528 * function may return any value, but it will always be evaluated as a boolean.
    1529 *
    1530 * Condition functions may schedule sub-tasks with this instance, however,
    1531 * their execution time will be factored into whether a wait has timed out.
    1532 *
    1533 * In the event a condition returns a Promise, the polling loop will wait for
    1534 * it to be resolved before evaluating whether the condition has been satisfied.
    1535 * The resolution time for a promise is factored into whether a wait has timed
    1536 * out.
    1537 *
    1538 * If the condition function throws, or returns a rejected promise, the
    1539 * wait task will fail.
    1540 *
    1541 * If the condition is defined as a promise, the flow will wait for it to
    1542 * settle. If the timeout expires before the promise settles, the promise
    1543 * returned by this function will be rejected.
    1544 *
    1545 * If this function is invoked with `timeout === 0`, or the timeout is omitted,
    1546 * the flow will wait indefinitely for the condition to be satisfied.
    1547 *
    1548 * @param {(!promise.Promise<T>|function())} condition The condition to poll,
    1549 * or a promise to wait on.
    1550 * @param {number=} opt_timeout How long to wait, in milliseconds, for the
    1551 * condition to hold before timing out. If omitted, the flow will wait
    1552 * indefinitely.
    1553 * @param {string=} opt_message An optional error message to include if the
    1554 * wait times out; defaults to the empty string.
    1555 * @return {!promise.Promise<T>} A promise that will be fulfilled
    1556 * when the condition has been satisified. The promise shall be rejected if
    1557 * the wait times out waiting for the condition.
    1558 * @throws {TypeError} If condition is not a function or promise or if timeout
    1559 * is not a number >= 0.
    1560 * @template T
    1561 */
    1562promise.ControlFlow.prototype.wait = function(
    1563 condition, opt_timeout, opt_message) {
    1564 var timeout = opt_timeout || 0;
    1565 if (!goog.isNumber(timeout) || timeout < 0) {
    1566 throw TypeError('timeout must be a number >= 0: ' + timeout);
    1567 }
    1568
    1569 if (promise.isPromise(condition)) {
    1570 return this.execute(function() {
    1571 if (!timeout) {
    1572 return condition;
    1573 }
    1574 return new promise.Promise(function(fulfill, reject) {
    1575 var start = goog.now();
    1576 var timer = setTimeout(function() {
    1577 timer = null;
    1578 reject(Error((opt_message ? opt_message + '\n' : '') +
    1579 'Timed out waiting for promise to resolve after ' +
    1580 (goog.now() - start) + 'ms'));
    1581 }, timeout);
    1582
    1583 /** @type {Thenable} */(condition).then(
    1584 function(value) {
    1585 timer && clearTimeout(timer);
    1586 fulfill(value);
    1587 },
    1588 function(error) {
    1589 timer && clearTimeout(timer);
    1590 reject(error);
    1591 });
    1592 });
    1593 }, opt_message || '<anonymous wait: promise resolution>');
    1594 }
    1595
    1596 if (!goog.isFunction(condition)) {
    1597 throw TypeError('Invalid condition; must be a function or promise: ' +
    1598 goog.typeOf(condition));
    1599 }
    1600
    1601 if (promise.isGenerator(condition)) {
    1602 condition = goog.partial(promise.consume, condition);
    1603 }
    1604
    1605 var self = this;
    1606 return this.execute(function() {
    1607 var startTime = goog.now();
    1608 return new promise.Promise(function(fulfill, reject) {
    1609 self.suspend_();
    1610 pollCondition();
    1611
    1612 function pollCondition() {
    1613 self.resume_();
    1614 self.execute(/**@type {function()}*/(condition)).then(function(value) {
    1615 var elapsed = goog.now() - startTime;
    1616 if (!!value) {
    1617 fulfill(value);
    1618 } else if (timeout && elapsed >= timeout) {
    1619 reject(new Error((opt_message ? opt_message + '\n' : '') +
    1620 'Wait timed out after ' + elapsed + 'ms'));
    1621 } else {
    1622 self.suspend_();
    1623 // Do not use asyncRun here because we need a non-micro yield
    1624 // here so the UI thread is given a chance when running in a
    1625 // browser.
    1626 setTimeout(pollCondition, 0);
    1627 }
    1628 }, reject);
    1629 }
    1630 });
    1631 }, opt_message || '<anonymous wait>');
    1632};
    1633
    1634
    1635/**
    1636 * Schedules the interval for this instance's event loop, if necessary.
    1637 * @private
    1638 */
    1639promise.ControlFlow.prototype.scheduleEventLoopStart_ = function() {
    1640 if (!this.eventLoopTask_ && !this.yieldCount_ && this.activeFrame_ &&
    1641 !this.activeFrame_.getPendingTask()) {
    1642 this.eventLoopTask_ = new MicroTask(this.runEventLoop_, this);
    1643 }
    1644};
    1645
    1646
    1647/**
    1648 * Cancels the event loop, if necessary.
    1649 * @private
    1650 */
    1651promise.ControlFlow.prototype.cancelEventLoop_ = function() {
    1652 if (this.eventLoopTask_) {
    1653 this.eventLoopTask_.cancel();
    1654 this.eventLoopTask_ = null;
    1655 }
    1656};
    1657
    1658
    1659/**
    1660 * Suspends this control flow, preventing it from executing any more tasks.
    1661 * @private
    1662 */
    1663promise.ControlFlow.prototype.suspend_ = function() {
    1664 this.yieldCount_ += 1;
    1665 this.cancelEventLoop_();
    1666};
    1667
    1668
    1669/**
    1670 * Resumes execution of tasks scheduled within this control flow.
    1671 * @private
    1672 */
    1673promise.ControlFlow.prototype.resume_ = function() {
    1674 this.yieldCount_ -= 1;
    1675 if (!this.yieldCount_ && this.activeFrame_) {
    1676 this.scheduleEventLoopStart_();
    1677 }
    1678};
    1679
    1680
    1681/**
    1682 * Executes the next task for the current frame. If the current frame has no
    1683 * more tasks, the frame's result will be resolved, returning control to the
    1684 * frame's creator. This will terminate the flow if the completed frame was at
    1685 * the top of the stack.
    1686 * @private
    1687 */
    1688promise.ControlFlow.prototype.runEventLoop_ = function() {
    1689 this.eventLoopTask_ = null;
    1690
    1691 if (this.yieldCount_) {
    1692 return;
    1693 }
    1694
    1695 if (!this.activeFrame_) {
    1696 this.commenceShutdown_();
    1697 return;
    1698 }
    1699
    1700 if (this.activeFrame_.getPendingTask()) {
    1701 return;
    1702 }
    1703
    1704 var task = this.getNextTask_();
    1705 if (!task) {
    1706 return;
    1707 }
    1708
    1709 var activeFrame = this.activeFrame_;
    1710 var scheduleEventLoop = goog.bind(this.scheduleEventLoopStart_, this);
    1711
    1712 var onSuccess = function(value) {
    1713 activeFrame.setPendingTask(null);
    1714 task.setFrame(null);
    1715 task.fulfill(value);
    1716 scheduleEventLoop();
    1717 };
    1718
    1719 var onFailure = function(reason) {
    1720 activeFrame.setPendingTask(null);
    1721 task.setFrame(null);
    1722 task.reject(reason);
    1723 scheduleEventLoop();
    1724 };
    1725
    1726 activeFrame.setPendingTask(task);
    1727 var frame = new Frame(this);
    1728 task.setFrame(frame);
    1729 this.runInFrame_(frame, task.execute, function(result) {
    1730 promise.asap(result, onSuccess, onFailure);
    1731 }, onFailure, true);
    1732};
    1733
    1734
    1735/**
    1736 * @return {Task} The next task to execute, or
    1737 * {@code null} if a frame was resolved.
    1738 * @private
    1739 */
    1740promise.ControlFlow.prototype.getNextTask_ = function() {
    1741 var frame = this.activeFrame_;
    1742 var firstChild = frame.getFirstChild();
    1743 if (!firstChild) {
    1744 if (!frame.pendingCallback && !frame.isBlocked_) {
    1745 this.resolveFrame_(frame);
    1746 }
    1747 return null;
    1748 }
    1749
    1750 if (firstChild instanceof Frame) {
    1751 this.activeFrame_ = firstChild;
    1752 return this.getNextTask_();
    1753 }
    1754
    1755 frame.removeChild(firstChild);
    1756 if (!firstChild.isPending()) {
    1757 return this.getNextTask_();
    1758 }
    1759 return firstChild;
    1760};
    1761
    1762
    1763/**
    1764 * @param {!Frame} frame The frame to resolve.
    1765 * @private
    1766 */
    1767promise.ControlFlow.prototype.resolveFrame_ = function(frame) {
    1768 if (this.activeFrame_ === frame) {
    1769 this.activeFrame_ = frame.getParent();
    1770 }
    1771
    1772 if (frame.getParent()) {
    1773 frame.getParent().removeChild(frame);
    1774 }
    1775 frame.emit(Frame.CLOSE_EVENT);
    1776
    1777 if (!this.activeFrame_) {
    1778 this.commenceShutdown_();
    1779 } else {
    1780 this.scheduleEventLoopStart_();
    1781 }
    1782};
    1783
    1784
    1785/**
    1786 * Aborts the current frame. The frame, and all of the tasks scheduled within it
    1787 * will be discarded. If this instance does not have an active frame, it will
    1788 * immediately terminate all execution.
    1789 * @param {*} error The reason the frame is being aborted; typically either
    1790 * an Error or string.
    1791 * @param {Frame=} opt_frame The frame to abort; will use the
    1792 * currently active frame if not specified.
    1793 * @private
    1794 */
    1795promise.ControlFlow.prototype.abortFrame_ = function(error, opt_frame) {
    1796 if (!this.activeFrame_) {
    1797 this.abortNow_(error);
    1798 return;
    1799 }
    1800
    1801 // Frame parent is always another frame, but the compiler is not smart
    1802 // enough to recognize this.
    1803 var parent = /** @type {Frame} */ (
    1804 this.activeFrame_.getParent());
    1805 if (parent) {
    1806 parent.removeChild(this.activeFrame_);
    1807 }
    1808
    1809 var frame = this.activeFrame_;
    1810 this.activeFrame_ = parent;
    1811 frame.abort(error);
    1812};
    1813
    1814
    1815/**
    1816 * Executes a function within a specific frame. If the function does not
    1817 * schedule any new tasks, the frame will be discarded and the function's result
    1818 * returned passed to the given callback immediately. Otherwise, the callback
    1819 * will be invoked once all of the tasks scheduled within the function have been
    1820 * completed. If the frame is aborted, the `errback` will be invoked with the
    1821 * offending error.
    1822 *
    1823 * @param {!Frame} newFrame The frame to use.
    1824 * @param {!Function} fn The function to execute.
    1825 * @param {function(T)} callback The function to call with a successful result.
    1826 * @param {function(*)} errback The function to call if there is an error.
    1827 * @param {boolean=} opt_isTask Whether the function is a task and the frame
    1828 * should be immediately activated to capture subtasks and errors.
    1829 * @throws {Error} If this function is invoked while another call to this
    1830 * function is present on the stack.
    1831 * @template T
    1832 * @private
    1833 */
    1834promise.ControlFlow.prototype.runInFrame_ = function(
    1835 newFrame, fn, callback, errback, opt_isTask) {
    1836 asserts.assert(
    1837 !this.runningFrame_, 'unexpected recursive call to runInFrame');
    1838
    1839 var self = this,
    1840 oldFrame = this.activeFrame_;
    1841
    1842 try {
    1843 if (this.activeFrame_ !== newFrame && !newFrame.getParent()) {
    1844 this.activeFrame_.addChild(newFrame);
    1845 }
    1846
    1847 // Activate the new frame to force tasks to be treated as sub-tasks of
    1848 // the parent frame.
    1849 if (opt_isTask) {
    1850 this.activeFrame_ = newFrame;
    1851 }
    1852
    1853 try {
    1854 this.runningFrame_ = newFrame;
    1855 this.schedulingFrame_ = newFrame;
    1856 activeFlows.push(this);
    1857 var result = fn();
    1858 } finally {
    1859 activeFlows.pop();
    1860 this.schedulingFrame_ = null;
    1861
    1862 // `newFrame` should only be considered the running frame when it is
    1863 // actually executing. After it finishes, set top of stack to its parent
    1864 // so any failures/interrupts that occur while processing newFrame's
    1865 // result are handled there.
    1866 this.runningFrame_ = newFrame.parent_;
    1867 }
    1868 newFrame.isLocked_ = true;
    1869
    1870 // If there was nothing scheduled in the new frame we can discard the
    1871 // frame and return immediately.
    1872 if (isCloseable(newFrame) && (!opt_isTask || !promise.isPromise(result))) {
    1873 removeNewFrame();
    1874 callback(result);
    1875 return;
    1876 }
    1877
    1878 // If the executed function returned a promise, wait for it to resolve. If
    1879 // there is nothing scheduled in the frame, go ahead and discard it.
    1880 // Otherwise, we wait for the frame to be closed out by the event loop.
    1881 var shortCircuitTask;
    1882 if (promise.isPromise(result)) {
    1883 newFrame.isBlocked_ = true;
    1884 var onResolve = function() {
    1885 newFrame.isBlocked_ = false;
    1886 shortCircuitTask = new MicroTask(function() {
    1887 if (isCloseable(newFrame)) {
    1888 removeNewFrame();
    1889 callback(result);
    1890 }
    1891 });
    1892 };
    1893 /** @type {Thenable} */(result).then(onResolve, onResolve);
    1894
    1895 // If the result is a thenable, attach a listener to silence any unhandled
    1896 // rejection warnings. This is safe because we *will* handle it once the
    1897 // frame has completed.
    1898 } else if (promise.Thenable.isImplementation(result)) {
    1899 /** @type {!promise.Thenable} */(result).thenCatch(goog.nullFunction);
    1900 }
    1901
    1902 newFrame.once(Frame.CLOSE_EVENT, function() {
    1903 shortCircuitTask && shortCircuitTask.cancel();
    1904 if (isCloseable(newFrame)) {
    1905 removeNewFrame();
    1906 }
    1907 callback(result);
    1908 }).once(Frame.ERROR_EVENT, function(reason) {
    1909 shortCircuitTask && shortCircuitTask.cancel();
    1910 if (promise.Thenable.isImplementation(result) && result.isPending()) {
    1911 result.cancel(reason);
    1912 }
    1913 errback(reason);
    1914 });
    1915 } catch (ex) {
    1916 removeNewFrame(ex);
    1917 errback(ex);
    1918 } finally {
    1919 // No longer running anything, clear the reference.
    1920 this.runningFrame_ = null;
    1921 }
    1922
    1923 function isCloseable(frame) {
    1924 return (!frame.children_ || !frame.children_.length)
    1925 && !frame.pendingRejection;
    1926 }
    1927
    1928 /**
    1929 * @param {*=} opt_err If provided, the reason that the frame was removed.
    1930 */
    1931 function removeNewFrame(opt_err) {
    1932 var parent = newFrame.getParent();
    1933 if (parent) {
    1934 parent.removeChild(newFrame);
    1935 asyncRun(function() {
    1936 if (isCloseable(parent) && parent !== self.activeFrame_) {
    1937 parent.emit(Frame.CLOSE_EVENT);
    1938 }
    1939 });
    1940 self.scheduleEventLoopStart_();
    1941 }
    1942
    1943 if (opt_err) {
    1944 newFrame.cancelRemainingTasks(promise.CancellationError.wrap(
    1945 opt_err, 'Tasks cancelled due to uncaught error'));
    1946 }
    1947 self.activeFrame_ = oldFrame;
    1948 }
    1949};
    1950
    1951
    1952/**
    1953 * Commences the shutdown sequence for this instance. After one turn of the
    1954 * event loop, this object will emit the
    1955 * {@link webdriver.promise.ControlFlow.EventType.IDLE IDLE} event to signal
    1956 * listeners that it has completed. During this wait, if another task is
    1957 * scheduled, the shutdown will be aborted.
    1958 * @private
    1959 */
    1960promise.ControlFlow.prototype.commenceShutdown_ = function() {
    1961 if (!this.shutdownTask_) {
    1962 // Go ahead and stop the event loop now. If we're in here, then there are
    1963 // no more frames with tasks to execute. If we waited to cancel the event
    1964 // loop in our timeout below, the event loop could trigger *before* the
    1965 // timeout, generating an error from there being no frames.
    1966 // If #execute is called before the timeout below fires, it will cancel
    1967 // the timeout and restart the event loop.
    1968 this.cancelEventLoop_();
    1969 this.shutdownTask_ = new MicroTask(this.shutdown_, this);
    1970 }
    1971};
    1972
    1973
    1974/** @private */
    1975promise.ControlFlow.prototype.cancelHold_ = function() {
    1976 if (this.hold_) {
    1977 clearInterval(this.hold_);
    1978 this.hold_ = null;
    1979 }
    1980};
    1981
    1982
    1983/** @private */
    1984promise.ControlFlow.prototype.shutdown_ = function() {
    1985 this.cancelHold_();
    1986 this.shutdownTask_ = null;
    1987 this.emit(promise.ControlFlow.EventType.IDLE);
    1988};
    1989
    1990
    1991/**
    1992 * Cancels the shutdown sequence if it is currently scheduled.
    1993 * @private
    1994 */
    1995promise.ControlFlow.prototype.cancelShutdown_ = function() {
    1996 if (this.shutdownTask_) {
    1997 this.shutdownTask_.cancel();
    1998 this.shutdownTask_ = null;
    1999 }
    2000};
    2001
    2002
    2003/**
    2004 * Aborts this flow, abandoning all remaining tasks. If there are
    2005 * listeners registered, an {@code UNCAUGHT_EXCEPTION} will be emitted with the
    2006 * offending {@code error}, otherwise, the {@code error} will be rethrown to the
    2007 * global error handler.
    2008 * @param {*} error Object describing the error that caused the flow to
    2009 * abort; usually either an Error or string value.
    2010 * @private
    2011 */
    2012promise.ControlFlow.prototype.abortNow_ = function(error) {
    2013 this.activeFrame_ = null;
    2014 this.cancelShutdown_();
    2015 this.cancelEventLoop_();
    2016 this.cancelHold_();
    2017
    2018 var listeners = this.listeners(
    2019 promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
    2020 if (!listeners.length) {
    2021 throwException(error);
    2022 } else {
    2023 this.emit(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, error);
    2024 }
    2025};
    2026
    2027
    2028/**
    2029 * Wraps a function to execute as a cancellable micro task.
    2030 * @final
    2031 */
    2032var MicroTask = goog.defineClass(null, {
    2033 /**
    2034 * @param {function(this: THIS)} fn The function to run as a micro task.
    2035 * @param {THIS=} opt_scope The scope to run the function in.
    2036 * @template THIS
    2037 */
    2038 constructor: function(fn, opt_scope) {
    2039 /** @private {boolean} */
    2040 this.cancelled_ = false;
    2041 asyncRun(function() {
    2042 if (!this.cancelled_) {
    2043 fn.call(opt_scope);
    2044 }
    2045 }, this);
    2046 },
    2047
    2048 /**
    2049 * Cancels the execution of this task. Note: this will not prevent the task
    2050 * timer from firing, just the invocation of the wrapped function.
    2051 */
    2052 cancel: function() {
    2053 this.cancelled_ = true;
    2054 }
    2055});
    2056
    2057
    2058/**
    2059 * An execution frame within a {@link webdriver.promise.ControlFlow}. Each
    2060 * frame represents the execution context for either a
    2061 * {@link webdriver.Task} or a callback on a
    2062 * {@link webdriver.promise.Promise}.
    2063 *
    2064 * Each frame may contain sub-frames. If child N is a sub-frame, then the
    2065 * items queued within it are given priority over child N+1.
    2066 *
    2067 * @unrestricted
    2068 * @final
    2069 * @private
    2070 */
    2071var Frame = goog.defineClass(EventEmitter, {
    2072 /**
    2073 * @param {!promise.ControlFlow} flow The flow this instance belongs to.
    2074 */
    2075 constructor: function(flow) {
    2076 EventEmitter.call(this);
    2077 goog.getUid(this);
    2078
    2079 /** @private {!promise.ControlFlow} */
    2080 this.flow_ = flow;
    2081
    2082 /** @private {Frame} */
    2083 this.parent_ = null;
    2084
    2085 /** @private {Array<!(Frame|Task)>} */
    2086 this.children_ = null;
    2087
    2088 /** @private {(Frame|Task)} */
    2089 this.lastInsertedChild_ = null;
    2090
    2091 /**
    2092 * The task currently being executed within this frame.
    2093 * @private {Task}
    2094 */
    2095 this.pendingTask_ = null;
    2096
    2097 /**
    2098 * Whether this frame is currently locked. A locked frame represents an
    2099 * executed function that has scheduled all of its tasks.
    2100 *
    2101 * Once a frame becomes locked, any new frames which are added as children
    2102 * represent interrupts (such as a {@link webdriver.promise.Promise}
    2103 * callback) whose tasks must be given priority over those already scheduled
    2104 * within this frame. For example:
    2105 *
    2106 * var flow = promise.controlFlow();
    2107 * flow.execute('start here', goog.nullFunction).then(function() {
    2108 * flow.execute('this should execute 2nd', goog.nullFunction);
    2109 * });
    2110 * flow.execute('this should execute last', goog.nullFunction);
    2111 *
    2112 * @private {boolean}
    2113 */
    2114 this.isLocked_ = false;
    2115
    2116 /**
    2117 * Whether this frame's completion is blocked on the resolution of a promise
    2118 * returned by its main function.
    2119 * @private
    2120 */
    2121 this.isBlocked_ = false;
    2122
    2123 /**
    2124 * Whether this frame represents a pending callback attached to a
    2125 * {@link webdriver.promise.Promise}.
    2126 * @private {boolean}
    2127 */
    2128 this.pendingCallback = false;
    2129
    2130 /**
    2131 * Whether there are pending unhandled rejections detected within this frame.
    2132 * @private {boolean}
    2133 */
    2134 this.pendingRejection = false;
    2135
    2136 /** @private {promise.CancellationError} */
    2137 this.cancellationError_ = null;
    2138 },
    2139
    2140 statics: {
    2141 /** @const */
    2142 CLOSE_EVENT: 'close',
    2143
    2144 /** @const */
    2145 ERROR_EVENT: 'error',
    2146
    2147 /**
    2148 * @param {!promise.CancellationError} error The cancellation error.
    2149 * @param {!(Frame|Task)} child The child to cancel.
    2150 * @private
    2151 */
    2152 cancelChild_: function(error, child) {
    2153 if (child instanceof Frame) {
    2154 child.cancelRemainingTasks(error);
    2155 } else {
    2156 child.promise.callbacks_ = null;
    2157 child.cancel(error);
    2158 }
    2159 }
    2160 },
    2161
    2162 /** @return {Frame} This frame's parent, if any. */
    2163 getParent: function() {
    2164 return this.parent_;
    2165 },
    2166
    2167 /** @param {Frame} parent This frame's new parent. */
    2168 setParent: function(parent) {
    2169 this.parent_ = parent;
    2170 },
    2171
    2172 /** @return {!Frame} The root of this frame's tree. */
    2173 getRoot: function() {
    2174 var root = this;
    2175 while (root.parent_) {
    2176 root = root.parent_;
    2177 }
    2178 return root;
    2179 },
    2180
    2181 /**
    2182 * Aborts the execution of this frame, cancelling all outstanding tasks
    2183 * scheduled within this frame.
    2184 *
    2185 * @param {*} error The error that triggered this abortion.
    2186 */
    2187 abort: function(error) {
    2188 this.cancellationError_ = promise.CancellationError.wrap(
    2189 error, 'Task discarded due to a previous task failure');
    2190 this.cancelRemainingTasks(this.cancellationError_);
    2191 if (!this.pendingCallback) {
    2192 this.emit(Frame.ERROR_EVENT, error);
    2193 }
    2194 },
    2195
    2196 /**
    2197 * Marks all of the tasks that are descendants of this frame in the execution
    2198 * tree as cancelled. This is necessary for callbacks scheduled asynchronous.
    2199 * For example:
    2200 *
    2201 * var someResult;
    2202 * promise.createFlow(function(flow) {
    2203 * someResult = flow.execute(function() {});
    2204 * throw Error();
    2205 * }).thenCatch(function(err) {
    2206 * console.log('flow failed: ' + err);
    2207 * someResult.then(function() {
    2208 * console.log('task succeeded!');
    2209 * }, function(err) {
    2210 * console.log('task failed! ' + err);
    2211 * });
    2212 * });
    2213 * // flow failed: Error: boom
    2214 * // task failed! CancelledTaskError: Task discarded due to a previous
    2215 * // task failure: Error: boom
    2216 *
    2217 * @param {!promise.CancellationError} reason The cancellation reason.
    2218 */
    2219 cancelRemainingTasks: function(reason) {
    2220 if (this.children_) {
    2221 this.children_.forEach(function(child) {
    2222 Frame.cancelChild_(reason, child);
    2223 });
    2224 }
    2225 },
    2226
    2227 /**
    2228 * @return {Task} The task currently executing
    2229 * within this frame, if any.
    2230 */
    2231 getPendingTask: function() {
    2232 return this.pendingTask_;
    2233 },
    2234
    2235 /**
    2236 * @param {Task} task The task currently
    2237 * executing within this frame, if any.
    2238 */
    2239 setPendingTask: function(task) {
    2240 this.pendingTask_ = task;
    2241 },
    2242
    2243 /**
    2244 * @return {boolean} Whether this frame is empty (has no scheduled tasks or
    2245 * pending callback frames).
    2246 */
    2247 isEmpty: function() {
    2248 return !this.children_ || !this.children_.length;
    2249 },
    2250
    2251 /**
    2252 * Adds a new node to this frame.
    2253 * @param {!(Frame|Task)} node The node to insert.
    2254 */
    2255 addChild: function(node) {
    2256 if (this.cancellationError_) {
    2257 Frame.cancelChild_(this.cancellationError_, node);
    2258 return; // Child will never run, no point keeping a reference.
    2259 }
    2260
    2261 if (!this.children_) {
    2262 this.children_ = [];
    2263 }
    2264
    2265 node.setParent(this);
    2266 if (this.isLocked_ && node instanceof Frame) {
    2267 var index = 0;
    2268 if (this.lastInsertedChild_ instanceof Frame) {
    2269 index = this.children_.indexOf(this.lastInsertedChild_);
    2270 // If the last inserted child into a locked frame is a pending callback,
    2271 // it is an interrupt and the new interrupt must come after it. Otherwise,
    2272 // we have our first interrupt for this frame and it shoudl go before the
    2273 // last inserted child.
    2274 index += (this.lastInsertedChild_.pendingCallback) ? 1 : -1;
    2275 }
    2276 this.children_.splice(Math.max(index, 0), 0, node);
    2277 this.lastInsertedChild_ = node;
    2278 return;
    2279 }
    2280
    2281 this.lastInsertedChild_ = node;
    2282 this.children_.push(node);
    2283 },
    2284
    2285 /**
    2286 * @return {(Frame|Task)} This frame's fist child.
    2287 */
    2288 getFirstChild: function() {
    2289 this.isLocked_ = true;
    2290 return this.children_ && this.children_[0];
    2291 },
    2292
    2293 /**
    2294 * Removes a child from this frame.
    2295 * @param {!(Frame|Task)} child The child to remove.
    2296 */
    2297 removeChild: function(child) {
    2298 goog.asserts.assert(child.parent_ === this, 'not a child of this frame');
    2299 goog.asserts.assert(this.children_ !== null, 'frame has no children!');
    2300 var index = this.children_.indexOf(child);
    2301 child.setParent(null);
    2302 this.children_.splice(index, 1);
    2303 if (this.lastInsertedChild_ === child) {
    2304 this.lastInsertedChild_ = this.children_[index - 1] || null;
    2305 }
    2306 if (!this.children_.length) {
    2307 this.children_ = null;
    2308 }
    2309 },
    2310
    2311 /** @override */
    2312 toString: function() {
    2313 return 'Frame::' + goog.getUid(this);
    2314 }
    2315});
    2316
    2317
    2318/**
    2319 * A task to be executed by a {@link webdriver.promise.ControlFlow}.
    2320 *
    2321 * @unrestricted
    2322 * @final
    2323 */
    2324var Task = goog.defineClass(promise.Deferred, {
    2325 /**
    2326 * @param {!promise.ControlFlow} flow The flow this instances belongs
    2327 * to.
    2328 * @param {function(): (T|!promise.Promise<T>)} fn The function to
    2329 * call when the task executes. If it returns a
    2330 * {@link webdriver.promise.Promise}, the flow will wait for it to be
    2331 * resolved before starting the next task.
    2332 * @param {string} description A description of the task for debugging.
    2333 * @constructor
    2334 * @extends {promise.Deferred<T>}
    2335 * @template T
    2336 */
    2337 constructor: function(flow, fn, description) {
    2338 Task.base(this, 'constructor', flow);
    2339 goog.getUid(this);
    2340
    2341 /**
    2342 * @type {function(): (T|!promise.Promise<T>)}
    2343 */
    2344 this.execute = fn;
    2345
    2346 /** @private {string} */
    2347 this.description_ = description;
    2348
    2349 /** @private {Frame} */
    2350 this.parent_ = null;
    2351
    2352 /** @private {Frame} */
    2353 this.frame_ = null;
    2354 },
    2355
    2356 /**
    2357 * @return {Frame} frame The frame used to run this task's
    2358 * {@link #execute} method.
    2359 */
    2360 getFrame: function() {
    2361 return this.frame_;
    2362 },
    2363
    2364 /**
    2365 * @param {Frame} frame The frame used to run this task's
    2366 * {@link #execute} method.
    2367 */
    2368 setFrame: function(frame) {
    2369 this.frame_ = frame;
    2370 },
    2371
    2372 /**
    2373 * @param {Frame} frame The frame this task is scheduled in.
    2374 */
    2375 setParent: function(frame) {
    2376 goog.asserts.assert(goog.isNull(this.parent_) || goog.isNull(frame),
    2377 'parent already set');
    2378 this.parent_ = frame;
    2379 },
    2380
    2381 /** @return {string} This task's description. */
    2382 getDescription: function() {
    2383 return this.description_;
    2384 },
    2385
    2386 /** @override */
    2387 toString: function() {
    2388 return 'Task::' + goog.getUid(this) + '<' + this.description_ + '>';
    2389 }
    2390});
    2391
    2392
    2393/**
    2394 * Manages a callback attached to a {@link webdriver.promise.Promise}. When the
    2395 * promise is resolved, this callback will invoke the appropriate callback
    2396 * function based on the promise's resolved value.
    2397 *
    2398 * @unrestricted
    2399 * @final
    2400 */
    2401var Callback = goog.defineClass(promise.Deferred, {
    2402 /**
    2403 * @param {!promise.Promise} parent The promise this callback is attached to.
    2404 * @param {(function(T): (IThenable<R>|R)|null|undefined)} callback
    2405 * The fulfillment callback.
    2406 * @param {(function(*): (IThenable<R>|R)|null|undefined)} errback
    2407 * The rejection callback.
    2408 * @param {string} name The callback name.
    2409 * @param {!Function} fn The function to use as the top of the stack when
    2410 * recording the callback's creation point.
    2411 * @extends {promise.Deferred<R>}
    2412 * @template T, R
    2413 */
    2414 constructor: function(parent, callback, errback, name, fn) {
    2415 Callback.base(this, 'constructor', parent.flow_);
    2416
    2417 /** @private {(function(T): (IThenable<R>|R)|null|undefined)} */
    2418 this.callback_ = callback;
    2419
    2420 /** @private {(function(*): (IThenable<R>|R)|null|undefined)} */
    2421 this.errback_ = errback;
    2422
    2423 /** @private {!Frame} */
    2424 this.frame_ = new Frame(parent.flow_);
    2425 this.frame_.pendingCallback = true;
    2426
    2427 this.promise.parent_ = parent;
    2428 if (promise.LONG_STACK_TRACES) {
    2429 this.promise.stack_ = promise.captureStackTrace('Promise', name, fn);
    2430 }
    2431 },
    2432
    2433 /**
    2434 * Called by the parent promise when it has been resolved.
    2435 * @param {!PromiseState} state The parent's new state.
    2436 * @param {*} value The parent's new value.
    2437 */
    2438 notify: function(state, value) {
    2439 var callback = this.callback_;
    2440 var fallback = this.fulfill;
    2441 if (state === PromiseState.REJECTED) {
    2442 callback = this.errback_;
    2443 fallback = this.reject;
    2444 }
    2445
    2446 this.frame_.pendingCallback = false;
    2447 if (goog.isFunction(callback)) {
    2448 this.frame_.flow_.runInFrame_(
    2449 this.frame_,
    2450 goog.bind(callback, undefined, value),
    2451 this.fulfill, this.reject);
    2452 } else {
    2453 if (this.frame_.getParent()) {
    2454 this.frame_.getParent().removeChild(this.frame_);
    2455 }
    2456 fallback(value);
    2457 }
    2458 }
    2459});
    2460
    2461
    2462
    2463/**
    2464 * The default flow to use if no others are active.
    2465 * @type {!promise.ControlFlow}
    2466 */
    2467var defaultFlow = new promise.ControlFlow();
    2468
    2469
    2470/**
    2471 * A stack of active control flows, with the top of the stack used to schedule
    2472 * commands. When there are multiple flows on the stack, the flow at index N
    2473 * represents a callback triggered within a task owned by the flow at index
    2474 * N-1.
    2475 * @type {!Array<!promise.ControlFlow>}
    2476 */
    2477var activeFlows = [];
    2478
    2479
    2480/**
    2481 * Changes the default flow to use when no others are active.
    2482 * @param {!promise.ControlFlow} flow The new default flow.
    2483 * @throws {Error} If the default flow is not currently active.
    2484 */
    2485promise.setDefaultFlow = function(flow) {
    2486 if (activeFlows.length) {
    2487 throw Error('You may only change the default flow while it is active');
    2488 }
    2489 defaultFlow = flow;
    2490};
    2491
    2492
    2493/**
    2494 * @return {!promise.ControlFlow} The currently active control flow.
    2495 */
    2496promise.controlFlow = function() {
    2497 return /** @type {!promise.ControlFlow} */ (
    2498 Arrays.peek(activeFlows) || defaultFlow);
    2499};
    2500
    2501
    2502/**
    2503 * Creates a new control flow. The provided callback will be invoked as the
    2504 * first task within the new flow, with the flow as its sole argument. Returns
    2505 * a promise that resolves to the callback result.
    2506 * @param {function(!promise.ControlFlow)} callback The entry point
    2507 * to the newly created flow.
    2508 * @return {!promise.Promise} A promise that resolves to the callback
    2509 * result.
    2510 */
    2511promise.createFlow = function(callback) {
    2512 var flow = new promise.ControlFlow;
    2513 return flow.execute(function() {
    2514 return callback(flow);
    2515 });
    2516};
    2517
    2518
    2519/**
    2520 * Tests is a function is a generator.
    2521 * @param {!Function} fn The function to test.
    2522 * @return {boolean} Whether the function is a generator.
    2523 */
    2524promise.isGenerator = function(fn) {
    2525 return fn.constructor.name === 'GeneratorFunction';
    2526};
    2527
    2528
    2529/**
    2530 * Consumes a {@code GeneratorFunction}. Each time the generator yields a
    2531 * promise, this function will wait for it to be fulfilled before feeding the
    2532 * fulfilled value back into {@code next}. Likewise, if a yielded promise is
    2533 * rejected, the rejection error will be passed to {@code throw}.
    2534 *
    2535 * __Example 1:__ the Fibonacci Sequence.
    2536 *
    2537 * promise.consume(function* fibonacci() {
    2538 * var n1 = 1, n2 = 1;
    2539 * for (var i = 0; i < 4; ++i) {
    2540 * var tmp = yield n1 + n2;
    2541 * n1 = n2;
    2542 * n2 = tmp;
    2543 * }
    2544 * return n1 + n2;
    2545 * }).then(function(result) {
    2546 * console.log(result); // 13
    2547 * });
    2548 *
    2549 * __Example 2:__ a generator that throws.
    2550 *
    2551 * promise.consume(function* () {
    2552 * yield promise.delayed(250).then(function() {
    2553 * throw Error('boom');
    2554 * });
    2555 * }).thenCatch(function(e) {
    2556 * console.log(e.toString()); // Error: boom
    2557 * });
    2558 *
    2559 * @param {!Function} generatorFn The generator function to execute.
    2560 * @param {Object=} opt_self The object to use as "this" when invoking the
    2561 * initial generator.
    2562 * @param {...*} var_args Any arguments to pass to the initial generator.
    2563 * @return {!promise.Promise<?>} A promise that will resolve to the
    2564 * generator's final result.
    2565 * @throws {TypeError} If the given function is not a generator.
    2566 */
    2567promise.consume = function(generatorFn, opt_self, var_args) {
    2568 if (!promise.isGenerator(generatorFn)) {
    2569 throw new TypeError('Input is not a GeneratorFunction: ' +
    2570 generatorFn.constructor.name);
    2571 }
    2572
    2573 var deferred = promise.defer();
    2574 var generator = generatorFn.apply(opt_self, Arrays.slice(arguments, 2));
    2575 callNext();
    2576 return deferred.promise;
    2577
    2578 /** @param {*=} opt_value . */
    2579 function callNext(opt_value) {
    2580 pump(generator.next, opt_value);
    2581 }
    2582
    2583 /** @param {*=} opt_error . */
    2584 function callThrow(opt_error) {
    2585 // Dictionary lookup required because Closure compiler's built-in
    2586 // externs does not include GeneratorFunction.prototype.throw.
    2587 pump(generator['throw'], opt_error);
    2588 }
    2589
    2590 function pump(fn, opt_arg) {
    2591 if (!deferred.isPending()) {
    2592 return; // Defererd was cancelled; silently abort.
    2593 }
    2594
    2595 try {
    2596 var result = fn.call(generator, opt_arg);
    2597 } catch (ex) {
    2598 deferred.reject(ex);
    2599 return;
    2600 }
    2601
    2602 if (result.done) {
    2603 deferred.fulfill(result.value);
    2604 return;
    2605 }
    2606
    2607 promise.asap(result.value, callNext, callThrow);
    2608 }
    2609};
    \ 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 467bd5e..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 0315722..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 creates 2 submatches on the (optional) context and function name,
    430 * matching identifiers like 'foo.Bar.prototype.baz', 'Anonymous function',
    431 * 'eval code', and 'Global code'.
    432 * @private {string}
    433 * @const
    434 */
    435webdriver.stacktrace.CHAKRA_FUNCTION_CALL_PATTERN_ =
    436 '(?:(' + webdriver.stacktrace.IDENTIFIER_PATTERN_ +
    437 '(?:\\.' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + ')*)\\.)?' +
    438 '(' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + '(?:\\s+\\w+)*)';
    439
    440
    441/**
    442 * Regular expression for parsing on stack frame in Chakra (IE).
    443 * @private {!RegExp}
    444 * @const
    445 */
    446webdriver.stacktrace.CHAKRA_STACK_FRAME_REGEXP_ = new RegExp('^ at ' +
    447 webdriver.stacktrace.CHAKRA_FUNCTION_CALL_PATTERN_ +
    448 '\\s*(?:\\((.*)\\))$');
    449
    450
    451/**
    452 * Placeholder for an unparsable frame in a stack trace generated by
    453 * {@link goog.testing.stacktrace}.
    454 * @private {string}
    455 * @const
    456 */
    457webdriver.stacktrace.UNKNOWN_CLOSURE_FRAME_ = '> (unknown)';
    458
    459
    460/**
    461 * Representation of an anonymous frame in a stack trace generated by
    462 * {@link goog.testing.stacktrace}.
    463 * @private {string}
    464 * @const
    465 */
    466webdriver.stacktrace.ANONYMOUS_CLOSURE_FRAME_ = '> anonymous';
    467
    468
    469/**
    470 * Pattern for a function call in a Closure stack trace. Creates three optional
    471 * submatches: the context, function name, and alias.
    472 * @private {string}
    473 * @const
    474 */
    475webdriver.stacktrace.CLOSURE_FUNCTION_CALL_PATTERN_ =
    476 webdriver.stacktrace.QUALIFIED_NAME_PATTERN_ +
    477 '(?:\\(.*\\))?' + // Ignore arguments if present.
    478 webdriver.stacktrace.V8_ALIAS_PATTERN_;
    479
    480
    481/**
    482 * Regular expression for parsing a stack frame generated by Closure's
    483 * {@link goog.testing.stacktrace}.
    484 * @private {!RegExp}
    485 * @const
    486 */
    487webdriver.stacktrace.CLOSURE_STACK_FRAME_REGEXP_ = new RegExp('^> ' +
    488 '(?:' + webdriver.stacktrace.CLOSURE_FUNCTION_CALL_PATTERN_ +
    489 '(?: at )?)?' +
    490 '(?:(.*:\\d+:\\d+)|' + webdriver.stacktrace.URL_PATTERN_ + ')?$');
    491
    492
    493/**
    494 * Parses one stack frame.
    495 * @param {string} frameStr The stack frame as string.
    496 * @return {webdriver.stacktrace.Frame} Stack frame object or null if the
    497 * parsing failed.
    498 * @private
    499 */
    500webdriver.stacktrace.parseStackFrame_ = function(frameStr) {
    501 var m = frameStr.match(webdriver.stacktrace.V8_STACK_FRAME_REGEXP_);
    502 if (m) {
    503 return new webdriver.stacktrace.Frame(
    504 m[1] || m[2], m[3], m[4], m[5] || m[6]);
    505 }
    506
    507 if (frameStr.length >
    508 webdriver.stacktrace.MAX_FIREFOX_FRAMESTRING_LENGTH_) {
    509 return webdriver.stacktrace.parseLongFirefoxFrame_(frameStr);
    510 }
    511
    512 m = frameStr.match(webdriver.stacktrace.FIREFOX_STACK_FRAME_REGEXP_);
    513 if (m) {
    514 return new webdriver.stacktrace.Frame('', m[1], '', m[2]);
    515 }
    516
    517 m = frameStr.match(webdriver.stacktrace.OPERA_STACK_FRAME_REGEXP_);
    518 if (m) {
    519 return new webdriver.stacktrace.Frame(m[2], m[1] || m[3], '', m[4]);
    520 }
    521
    522 m = frameStr.match(webdriver.stacktrace.CHAKRA_STACK_FRAME_REGEXP_);
    523 if (m) {
    524 return new webdriver.stacktrace.Frame(m[1], m[2], '', m[3]);
    525 }
    526
    527 if (frameStr == webdriver.stacktrace.UNKNOWN_CLOSURE_FRAME_ ||
    528 frameStr == webdriver.stacktrace.ANONYMOUS_CLOSURE_FRAME_) {
    529 return webdriver.stacktrace.ANONYMOUS_FRAME_;
    530 }
    531
    532 m = frameStr.match(webdriver.stacktrace.CLOSURE_STACK_FRAME_REGEXP_);
    533 if (m) {
    534 return new webdriver.stacktrace.Frame(m[1], m[2], m[3], m[4] || m[5]);
    535 }
    536
    537 return null;
    538};
    539
    540
    541/**
    542 * Parses a long firefox stack frame.
    543 * @param {string} frameStr The stack frame as string.
    544 * @return {!webdriver.stacktrace.Frame} Stack frame object.
    545 * @private
    546 */
    547webdriver.stacktrace.parseLongFirefoxFrame_ = function(frameStr) {
    548 var firstParen = frameStr.indexOf('(');
    549 var lastAmpersand = frameStr.lastIndexOf('@');
    550 var lastColon = frameStr.lastIndexOf(':');
    551 var functionName = '';
    552 if ((firstParen >= 0) && (firstParen < lastAmpersand)) {
    553 functionName = frameStr.substring(0, firstParen);
    554 }
    555 var loc = '';
    556 if ((lastAmpersand >= 0) && (lastAmpersand + 1 < lastColon)) {
    557 loc = frameStr.substring(lastAmpersand + 1);
    558 }
    559 return new webdriver.stacktrace.Frame('', functionName, '', loc);
    560};
    561
    562
    563/**
    564 * Get an error's stack trace with the error string trimmed.
    565 * V8 prepends the string representation of an error to its stack trace.
    566 * This function trims the string so that the stack trace can be parsed
    567 * consistently with the other JS engines.
    568 * @param {(Error|goog.testing.JsUnitException)} error The error.
    569 * @return {string} The stack trace string.
    570 * @private
    571 */
    572webdriver.stacktrace.getStack_ = function(error) {
    573 if (!error) {
    574 return '';
    575 }
    576 var stack = error.stack || error.stackTrace || '';
    577 var errorStr = error + '\n';
    578 if (goog.string.startsWith(stack, errorStr)) {
    579 stack = stack.substring(errorStr.length);
    580 }
    581 return stack;
    582};
    583
    584
    585/**
    586 * Formats an error's stack trace.
    587 * @param {!(Error|goog.testing.JsUnitException)} error The error to format.
    588 * @return {!(Error|goog.testing.JsUnitException)} The formatted error.
    589 */
    590webdriver.stacktrace.format = function(error) {
    591 var stack = webdriver.stacktrace.getStack_(error);
    592 var frames = webdriver.stacktrace.parse_(stack);
    593
    594 // Older versions of IE simply return [object Error] for toString(), so
    595 // only use that as a last resort.
    596 var errorStr = '';
    597 if (error.message) {
    598 errorStr = (error.name ? error.name + ': ' : '') + error.message;
    599 } else {
    600 errorStr = error.toString();
    601 }
    602
    603 // Ensure the error is in the V8 style with the error's string representation
    604 // prepended to the stack.
    605 error.stack = errorStr + '\n' + frames.join('\n');
    606 return error;
    607};
    608
    609
    610/**
    611 * Parses an Error object's stack trace.
    612 * @param {string} stack The stack trace.
    613 * @return {!Array.<!webdriver.stacktrace.Frame>} Stack frames. The
    614 * unrecognized frames will be nulled out.
    615 * @private
    616 */
    617webdriver.stacktrace.parse_ = function(stack) {
    618 if (!stack) {
    619 return [];
    620 }
    621
    622 var lines = stack.
    623 replace(/\s*$/, '').
    624 split('\n');
    625 var frames = [];
    626 for (var i = 0; i < lines.length; i++) {
    627 var frame = webdriver.stacktrace.parseStackFrame_(lines[i]);
    628 // The first two frames will be:
    629 // webdriver.stacktrace.Snapshot()
    630 // webdriver.stacktrace.get()
    631 // In the case of Opera, sometimes an extra frame is injected in the next
    632 // frame with a reported line number of zero. The next line detects that
    633 // case and skips that frame.
    634 if (!(goog.userAgent.OPERA && i == 2 && frame.getLine() == 0)) {
    635 frames.push(frame || webdriver.stacktrace.ANONYMOUS_FRAME_);
    636 }
    637 }
    638 return frames;
    639};
    640
    641
    642/**
    643 * Gets the native stack trace if available otherwise follows the call chain.
    644 * The generated trace will exclude all frames up to and including the call to
    645 * this function.
    646 * @return {!Array.<!webdriver.stacktrace.Frame>} The frames of the stack trace.
    647 */
    648webdriver.stacktrace.get = function() {
    649 return new webdriver.stacktrace.Snapshot(1).getStacktrace();
    650};
    \ 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 e1a80e2..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/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 db568a6..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.AlertPromise');
    21goog.provide('webdriver.UnhandledAlertError');
    22goog.provide('webdriver.WebDriver');
    23goog.provide('webdriver.WebElement');
    24goog.provide('webdriver.WebElementPromise');
    25
    26goog.require('bot.Error');
    27goog.require('bot.ErrorCode');
    28goog.require('bot.response');
    29goog.require('goog.array');
    30goog.require('goog.object');
    31goog.require('webdriver.ActionSequence');
    32goog.require('webdriver.Command');
    33goog.require('webdriver.CommandName');
    34goog.require('webdriver.Key');
    35goog.require('webdriver.Locator');
    36goog.require('webdriver.Session');
    37goog.require('webdriver.logging');
    38goog.require('webdriver.promise');
    39
    40
    41//////////////////////////////////////////////////////////////////////////////
    42//
    43// webdriver.WebDriver
    44//
    45//////////////////////////////////////////////////////////////////////////////
    46
    47
    48
    49/**
    50 * Creates a new WebDriver client, which provides control over a browser.
    51 *
    52 * Every WebDriver command returns a {@code webdriver.promise.Promise} that
    53 * represents the result of that command. Callbacks may be registered on this
    54 * object to manipulate the command result or catch an expected error. Any
    55 * commands scheduled with a callback are considered sub-commands and will
    56 * execute before the next command in the current frame. For example:
    57 * <pre><code>
    58 * var message = [];
    59 * driver.call(message.push, message, 'a').then(function() {
    60 * driver.call(message.push, message, 'b');
    61 * });
    62 * driver.call(message.push, message, 'c');
    63 * driver.call(function() {
    64 * alert('message is abc? ' + (message.join('') == 'abc'));
    65 * });
    66 * </code></pre>
    67 *
    68 * @param {!(webdriver.Session|webdriver.promise.Promise)} session Either a
    69 * known session or a promise that will be resolved to a session.
    70 * @param {!webdriver.CommandExecutor} executor The executor to use when
    71 * sending commands to the browser.
    72 * @param {webdriver.promise.ControlFlow=} opt_flow The flow to
    73 * schedule commands through. Defaults to the active flow object.
    74 * @constructor
    75 */
    76webdriver.WebDriver = function(session, executor, opt_flow) {
    77
    78 /** @private {!(webdriver.Session|webdriver.promise.Promise)} */
    79 this.session_ = session;
    80
    81 /** @private {!webdriver.CommandExecutor} */
    82 this.executor_ = executor;
    83
    84 /** @private {!webdriver.promise.ControlFlow} */
    85 this.flow_ = opt_flow || webdriver.promise.controlFlow();
    86};
    87
    88
    89/**
    90 * Creates a new WebDriver client for an existing session.
    91 * @param {!webdriver.CommandExecutor} executor Command executor to use when
    92 * querying for session details.
    93 * @param {string} sessionId ID of the session to attach to.
    94 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow all driver
    95 * commands should execute under. Defaults to the
    96 * {@link webdriver.promise.controlFlow() currently active} control flow.
    97 * @return {!webdriver.WebDriver} A new client for the specified session.
    98 */
    99webdriver.WebDriver.attachToSession = function(executor, sessionId, opt_flow) {
    100 return webdriver.WebDriver.acquireSession_(executor,
    101 new webdriver.Command(webdriver.CommandName.DESCRIBE_SESSION).
    102 setParameter('sessionId', sessionId),
    103 'WebDriver.attachToSession()',
    104 opt_flow);
    105};
    106
    107
    108/**
    109 * Creates a new WebDriver session.
    110 * @param {!webdriver.CommandExecutor} executor The executor to create the new
    111 * session with.
    112 * @param {!webdriver.Capabilities} desiredCapabilities The desired
    113 * capabilities for the new session.
    114 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow all driver
    115 * commands should execute under, including the initial session creation.
    116 * Defaults to the {@link webdriver.promise.controlFlow() currently active}
    117 * control flow.
    118 * @return {!webdriver.WebDriver} The driver for the newly created session.
    119 */
    120webdriver.WebDriver.createSession = function(
    121 executor, desiredCapabilities, opt_flow) {
    122 return webdriver.WebDriver.acquireSession_(executor,
    123 new webdriver.Command(webdriver.CommandName.NEW_SESSION).
    124 setParameter('desiredCapabilities', desiredCapabilities),
    125 'WebDriver.createSession()',
    126 opt_flow);
    127};
    128
    129
    130/**
    131 * Sends a command to the server that is expected to return the details for a
    132 * {@link webdriver.Session}. This may either be an existing session, or a
    133 * newly created one.
    134 * @param {!webdriver.CommandExecutor} executor Command executor to use when
    135 * querying for session details.
    136 * @param {!webdriver.Command} command The command to send to fetch the session
    137 * details.
    138 * @param {string} description A descriptive debug label for this action.
    139 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow all driver
    140 * commands should execute under. Defaults to the
    141 * {@link webdriver.promise.controlFlow() currently active} control flow.
    142 * @return {!webdriver.WebDriver} A new WebDriver client for the session.
    143 * @private
    144 */
    145webdriver.WebDriver.acquireSession_ = function(
    146 executor, command, description, opt_flow) {
    147 var flow = opt_flow || webdriver.promise.controlFlow();
    148 var session = flow.execute(function() {
    149 return webdriver.WebDriver.executeCommand_(executor, command).
    150 then(function(response) {
    151 bot.response.checkResponse(response);
    152 return new webdriver.Session(response['sessionId'],
    153 response['value']);
    154 });
    155 }, description);
    156 return new webdriver.WebDriver(session, executor, flow);
    157};
    158
    159
    160/**
    161 * Converts an object to its JSON representation in the WebDriver wire protocol.
    162 * When converting values of type object, the following steps will be taken:
    163 * <ol>
    164 * <li>if the object is a WebElement, the return value will be the element's
    165 * server ID</li>
    166 * <li>if the object provides a "toJSON" function, the return value of this
    167 * function will be returned</li>
    168 * <li>otherwise, the value of each key will be recursively converted according
    169 * to the rules above.</li>
    170 * </ol>
    171 *
    172 * @param {*} obj The object to convert.
    173 * @return {!webdriver.promise.Promise.<?>} A promise that will resolve to the
    174 * input value's JSON representation.
    175 * @private
    176 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol
    177 */
    178webdriver.WebDriver.toWireValue_ = function(obj) {
    179 if (webdriver.promise.isPromise(obj)) {
    180 return obj.then(webdriver.WebDriver.toWireValue_);
    181 }
    182 switch (goog.typeOf(obj)) {
    183 case 'array':
    184 return webdriver.promise.all(
    185 goog.array.map(/** @type {!Array} */ (obj),
    186 webdriver.WebDriver.toWireValue_));
    187 case 'object':
    188 if (obj instanceof webdriver.WebElement) {
    189 return obj.getId();
    190 }
    191 if (goog.isFunction(obj.toJSON)) {
    192 return webdriver.promise.fulfilled(obj.toJSON());
    193 }
    194 if (goog.isNumber(obj.nodeType) && goog.isString(obj.nodeName)) {
    195 throw Error([
    196 'Invalid argument type: ', obj.nodeName, '(', obj.nodeType, ')'
    197 ].join(''));
    198 }
    199 return webdriver.promise.fullyResolved(
    200 goog.object.map(/** @type {!Object} */ (obj),
    201 webdriver.WebDriver.toWireValue_));
    202 case 'function':
    203 return webdriver.promise.fulfilled('' + obj);
    204 case 'undefined':
    205 return webdriver.promise.fulfilled(null);
    206 default:
    207 return webdriver.promise.fulfilled(obj);
    208 }
    209};
    210
    211
    212/**
    213 * Converts a value from its JSON representation according to the WebDriver wire
    214 * protocol. Any JSON object containing a
    215 * {@code webdriver.WebElement.ELEMENT_KEY} key will be decoded to a
    216 * {@code webdriver.WebElement} object. All other values will be passed through
    217 * as is.
    218 * @param {!webdriver.WebDriver} driver The driver instance to use as the
    219 * parent of any unwrapped {@code webdriver.WebElement} values.
    220 * @param {*} value The value to convert.
    221 * @return {*} The converted value.
    222 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol
    223 * @private
    224 */
    225webdriver.WebDriver.fromWireValue_ = function(driver, value) {
    226 if (goog.isArray(value)) {
    227 value = goog.array.map(/**@type {goog.array.ArrayLike}*/ (value),
    228 goog.partial(webdriver.WebDriver.fromWireValue_, driver));
    229 } else if (value && goog.isObject(value) && !goog.isFunction(value)) {
    230 if (webdriver.WebElement.ELEMENT_KEY in value) {
    231 value = new webdriver.WebElement(driver, value);
    232 } else {
    233 value = goog.object.map(/**@type {!Object}*/ (value),
    234 goog.partial(webdriver.WebDriver.fromWireValue_, driver));
    235 }
    236 }
    237 return value;
    238};
    239
    240
    241/**
    242 * Translates a command to its wire-protocol representation before passing it
    243 * to the given {@code executor} for execution.
    244 * @param {!webdriver.CommandExecutor} executor The executor to use.
    245 * @param {!webdriver.Command} command The command to execute.
    246 * @return {!webdriver.promise.Promise} A promise that will resolve with the
    247 * command response.
    248 * @private
    249 */
    250webdriver.WebDriver.executeCommand_ = function(executor, command) {
    251 return webdriver.WebDriver.toWireValue_(command.getParameters()).
    252 then(function(parameters) {
    253 command.setParameters(parameters);
    254 return webdriver.promise.checkedNodeCall(
    255 goog.bind(executor.execute, executor, command));
    256 });
    257};
    258
    259
    260/**
    261 * @return {!webdriver.promise.ControlFlow} The control flow used by this
    262 * instance.
    263 */
    264webdriver.WebDriver.prototype.controlFlow = function() {
    265 return this.flow_;
    266};
    267
    268
    269/**
    270 * Schedules a {@code webdriver.Command} to be executed by this driver's
    271 * {@code webdriver.CommandExecutor}.
    272 * @param {!webdriver.Command} command The command to schedule.
    273 * @param {string} description A description of the command for debugging.
    274 * @return {!webdriver.promise.Promise.<T>} A promise that will be resolved
    275 * with the command result.
    276 * @template T
    277 */
    278webdriver.WebDriver.prototype.schedule = function(command, description) {
    279 var self = this;
    280
    281 checkHasNotQuit();
    282 command.setParameter('sessionId', this.session_);
    283
    284 // If any of the command parameters are rejected promises, those
    285 // rejections may be reported as unhandled before the control flow
    286 // attempts to execute the command. To ensure parameters errors
    287 // propagate through the command itself, we resolve all of the
    288 // command parameters now, but suppress any errors until the ControlFlow
    289 // actually executes the command. This addresses scenarios like catching
    290 // an element not found error in:
    291 //
    292 // driver.findElement(By.id('foo')).click().thenCatch(function(e) {
    293 // if (e.code === bot.ErrorCode.NO_SUCH_ELEMENT) {
    294 // // Do something.
    295 // }
    296 // });
    297 var prepCommand = webdriver.WebDriver.toWireValue_(command.getParameters());
    298 prepCommand.thenCatch(goog.nullFunction);
    299
    300 var flow = this.flow_;
    301 var executor = this.executor_;
    302 return flow.execute(function() {
    303 // A call to WebDriver.quit() may have been scheduled in the same event
    304 // loop as this |command|, which would prevent us from detecting that the
    305 // driver has quit above. Therefore, we need to make another quick check.
    306 // We still check above so we can fail as early as possible.
    307 checkHasNotQuit();
    308
    309 // Retrieve resolved command parameters; any previously suppressed errors
    310 // will now propagate up through the control flow as part of the command
    311 // execution.
    312 return prepCommand.then(function(parameters) {
    313 command.setParameters(parameters);
    314 return webdriver.promise.checkedNodeCall(
    315 goog.bind(executor.execute, executor, command));
    316 });
    317 }, description).then(function(response) {
    318 try {
    319 bot.response.checkResponse(response);
    320 } catch (ex) {
    321 var value = response['value'];
    322 if (ex.code === bot.ErrorCode.UNEXPECTED_ALERT_OPEN) {
    323 var text = value && value['alert'] ? value['alert']['text'] : '';
    324 throw new webdriver.UnhandledAlertError(ex.message, text,
    325 new webdriver.Alert(self, text));
    326 }
    327 throw ex;
    328 }
    329 return webdriver.WebDriver.fromWireValue_(self, response['value']);
    330 });
    331
    332 function checkHasNotQuit() {
    333 if (!self.session_) {
    334 throw new Error('This driver instance does not have a valid session ID ' +
    335 '(did you call WebDriver.quit()?) and may no longer be ' +
    336 'used.');
    337 }
    338 }
    339};
    340
    341
    342// ----------------------------------------------------------------------------
    343// Client command functions:
    344// ----------------------------------------------------------------------------
    345
    346
    347/**
    348 * @return {!webdriver.promise.Promise.<!webdriver.Session>} A promise for this
    349 * client's session.
    350 */
    351webdriver.WebDriver.prototype.getSession = function() {
    352 return webdriver.promise.when(this.session_);
    353};
    354
    355
    356/**
    357 * @return {!webdriver.promise.Promise.<!webdriver.Capabilities>} A promise
    358 * that will resolve with the this instance's capabilities.
    359 */
    360webdriver.WebDriver.prototype.getCapabilities = function() {
    361 return webdriver.promise.when(this.session_, function(session) {
    362 return session.getCapabilities();
    363 });
    364};
    365
    366
    367/**
    368 * Schedules a command to quit the current session. After calling quit, this
    369 * instance will be invalidated and may no longer be used to issue commands
    370 * against the browser.
    371 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    372 * when the command has completed.
    373 */
    374webdriver.WebDriver.prototype.quit = function() {
    375 var result = this.schedule(
    376 new webdriver.Command(webdriver.CommandName.QUIT),
    377 'WebDriver.quit()');
    378 // Delete our session ID when the quit command finishes; this will allow us to
    379 // throw an error when attemnpting to use a driver post-quit.
    380 return result.thenFinally(goog.bind(function() {
    381 delete this.session_;
    382 }, this));
    383};
    384
    385
    386/**
    387 * Creates a new action sequence using this driver. The sequence will not be
    388 * scheduled for execution until {@link webdriver.ActionSequence#perform} is
    389 * called. Example:
    390 * <pre><code>
    391 * driver.actions().
    392 * mouseDown(element1).
    393 * mouseMove(element2).
    394 * mouseUp().
    395 * perform();
    396 * </code></pre>
    397 * @return {!webdriver.ActionSequence} A new action sequence for this instance.
    398 */
    399webdriver.WebDriver.prototype.actions = function() {
    400 return new webdriver.ActionSequence(this);
    401};
    402
    403
    404/**
    405 * Schedules a command to execute JavaScript in the context of the currently
    406 * selected frame or window. The script fragment will be executed as the body
    407 * of an anonymous function. If the script is provided as a function object,
    408 * that function will be converted to a string for injection into the target
    409 * window.
    410 *
    411 * Any arguments provided in addition to the script will be included as script
    412 * arguments and may be referenced using the {@code arguments} object.
    413 * Arguments may be a boolean, number, string, or {@code webdriver.WebElement}.
    414 * Arrays and objects may also be used as script arguments as long as each item
    415 * adheres to the types previously mentioned.
    416 *
    417 * The script may refer to any variables accessible from the current window.
    418 * Furthermore, the script will execute in the window's context, thus
    419 * {@code document} may be used to refer to the current document. Any local
    420 * variables will not be available once the script has finished executing,
    421 * though global variables will persist.
    422 *
    423 * If the script has a return value (i.e. if the script contains a return
    424 * statement), then the following steps will be taken for resolving this
    425 * functions return value:
    426 * <ul>
    427 * <li>For a HTML element, the value will resolve to a
    428 * {@code webdriver.WebElement}</li>
    429 * <li>Null and undefined return values will resolve to null</li>
    430 * <li>Booleans, numbers, and strings will resolve as is</li>
    431 * <li>Functions will resolve to their string representation</li>
    432 * <li>For arrays and objects, each member item will be converted according to
    433 * the rules above</li>
    434 * </ul>
    435 *
    436 * @param {!(string|Function)} script The script to execute.
    437 * @param {...*} var_args The arguments to pass to the script.
    438 * @return {!webdriver.promise.Promise.<T>} A promise that will resolve to the
    439 * scripts return value.
    440 * @template T
    441 */
    442webdriver.WebDriver.prototype.executeScript = function(script, var_args) {
    443 if (goog.isFunction(script)) {
    444 script = 'return (' + script + ').apply(null, arguments);';
    445 }
    446 return this.schedule(
    447 new webdriver.Command(webdriver.CommandName.EXECUTE_SCRIPT).
    448 setParameter('script', script).
    449 setParameter('args', goog.array.slice(arguments, 1)),
    450 'WebDriver.executeScript()');
    451};
    452
    453
    454/**
    455 * Schedules a command to execute asynchronous JavaScript in the context of the
    456 * currently selected frame or window. The script fragment will be executed as
    457 * the body of an anonymous function. If the script is provided as a function
    458 * object, that function will be converted to a string for injection into the
    459 * target window.
    460 *
    461 * Any arguments provided in addition to the script will be included as script
    462 * arguments and may be referenced using the {@code arguments} object.
    463 * Arguments may be a boolean, number, string, or {@code webdriver.WebElement}.
    464 * Arrays and objects may also be used as script arguments as long as each item
    465 * adheres to the types previously mentioned.
    466 *
    467 * Unlike executing synchronous JavaScript with
    468 * {@code webdriver.WebDriver.prototype.executeScript}, scripts executed with
    469 * this function must explicitly signal they are finished by invoking the
    470 * provided callback. This callback will always be injected into the
    471 * executed function as the last argument, and thus may be referenced with
    472 * {@code arguments[arguments.length - 1]}. The following steps will be taken
    473 * for resolving this functions return value against the first argument to the
    474 * script's callback function:
    475 * <ul>
    476 * <li>For a HTML element, the value will resolve to a
    477 * {@code webdriver.WebElement}</li>
    478 * <li>Null and undefined return values will resolve to null</li>
    479 * <li>Booleans, numbers, and strings will resolve as is</li>
    480 * <li>Functions will resolve to their string representation</li>
    481 * <li>For arrays and objects, each member item will be converted according to
    482 * the rules above</li>
    483 * </ul>
    484 *
    485 * Example #1: Performing a sleep that is synchronized with the currently
    486 * selected window:
    487 * <code><pre>
    488 * var start = new Date().getTime();
    489 * driver.executeAsyncScript(
    490 * 'window.setTimeout(arguments[arguments.length - 1], 500);').
    491 * then(function() {
    492 * console.log('Elapsed time: ' + (new Date().getTime() - start) + ' ms');
    493 * });
    494 * </pre></code>
    495 *
    496 * Example #2: Synchronizing a test with an AJAX application:
    497 * <code><pre>
    498 * var button = driver.findElement(By.id('compose-button'));
    499 * button.click();
    500 * driver.executeAsyncScript(
    501 * 'var callback = arguments[arguments.length - 1];' +
    502 * 'mailClient.getComposeWindowWidget().onload(callback);');
    503 * driver.switchTo().frame('composeWidget');
    504 * driver.findElement(By.id('to')).sendKeys('dog@example.com');
    505 * </pre></code>
    506 *
    507 * Example #3: Injecting a XMLHttpRequest and waiting for the result. In this
    508 * example, the inject script is specified with a function literal. When using
    509 * this format, the function is converted to a string for injection, so it
    510 * should not reference any symbols not defined in the scope of the page under
    511 * test.
    512 * <code><pre>
    513 * driver.executeAsyncScript(function() {
    514 * var callback = arguments[arguments.length - 1];
    515 * var xhr = new XMLHttpRequest();
    516 * xhr.open("GET", "/resource/data.json", true);
    517 * xhr.onreadystatechange = function() {
    518 * if (xhr.readyState == 4) {
    519 * callback(xhr.responseText);
    520 * }
    521 * }
    522 * xhr.send('');
    523 * }).then(function(str) {
    524 * console.log(JSON.parse(str)['food']);
    525 * });
    526 * </pre></code>
    527 *
    528 * @param {!(string|Function)} script The script to execute.
    529 * @param {...*} var_args The arguments to pass to the script.
    530 * @return {!webdriver.promise.Promise.<T>} A promise that will resolve to the
    531 * scripts return value.
    532 * @template T
    533 */
    534webdriver.WebDriver.prototype.executeAsyncScript = function(script, var_args) {
    535 if (goog.isFunction(script)) {
    536 script = 'return (' + script + ').apply(null, arguments);';
    537 }
    538 return this.schedule(
    539 new webdriver.Command(webdriver.CommandName.EXECUTE_ASYNC_SCRIPT).
    540 setParameter('script', script).
    541 setParameter('args', goog.array.slice(arguments, 1)),
    542 'WebDriver.executeScript()');
    543};
    544
    545
    546/**
    547 * Schedules a command to execute a custom function.
    548 * @param {function(...): (T|webdriver.promise.Promise.<T>)} fn The function to
    549 * execute.
    550 * @param {Object=} opt_scope The object in whose scope to execute the function.
    551 * @param {...*} var_args Any arguments to pass to the function.
    552 * @return {!webdriver.promise.Promise.<T>} A promise that will be resolved'
    553 * with the function's result.
    554 * @template T
    555 */
    556webdriver.WebDriver.prototype.call = function(fn, opt_scope, var_args) {
    557 var args = goog.array.slice(arguments, 2);
    558 var flow = this.flow_;
    559 return flow.execute(function() {
    560 return webdriver.promise.fullyResolved(args).then(function(args) {
    561 if (webdriver.promise.isGenerator(fn)) {
    562 args.unshift(fn, opt_scope);
    563 return webdriver.promise.consume.apply(null, args);
    564 }
    565 return fn.apply(opt_scope, args);
    566 });
    567 }, 'WebDriver.call(' + (fn.name || 'function') + ')');
    568};
    569
    570
    571/**
    572 * Schedules a command to wait for a condition to hold, as defined by some
    573 * user supplied function. If any errors occur while evaluating the wait, they
    574 * will be allowed to propagate.
    575 *
    576 * <p>In the event a condition returns a {@link webdriver.promise.Promise}, the
    577 * polling loop will wait for it to be resolved and use the resolved value for
    578 * evaluating whether the condition has been satisfied. The resolution time for
    579 * a promise is factored into whether a wait has timed out.
    580 *
    581 * @param {function():boolean} fn The function to evaluate as a wait condition.
    582 * @param {number} timeout How long to wait for the condition to be true.
    583 * @param {string=} opt_message An optional message to use if the wait times
    584 * out.
    585 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
    586 * wait condition has been satisfied.
    587 */
    588webdriver.WebDriver.prototype.wait = function(fn, timeout, opt_message) {
    589 return this.flow_.wait(fn, timeout, opt_message);
    590};
    591
    592
    593/**
    594 * Schedules a command to make the driver sleep for the given amount of time.
    595 * @param {number} ms The amount of time, in milliseconds, to sleep.
    596 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    597 * when the sleep has finished.
    598 */
    599webdriver.WebDriver.prototype.sleep = function(ms) {
    600 return this.flow_.timeout(ms, 'WebDriver.sleep(' + ms + ')');
    601};
    602
    603
    604/**
    605 * Schedules a command to retrieve they current window handle.
    606 * @return {!webdriver.promise.Promise.<string>} A promise that will be
    607 * resolved with the current window handle.
    608 */
    609webdriver.WebDriver.prototype.getWindowHandle = function() {
    610 return this.schedule(
    611 new webdriver.Command(webdriver.CommandName.GET_CURRENT_WINDOW_HANDLE),
    612 'WebDriver.getWindowHandle()');
    613};
    614
    615
    616/**
    617 * Schedules a command to retrieve the current list of available window handles.
    618 * @return {!webdriver.promise.Promise.<!Array.<string>>} A promise that will
    619 * be resolved with an array of window handles.
    620 */
    621webdriver.WebDriver.prototype.getAllWindowHandles = function() {
    622 return this.schedule(
    623 new webdriver.Command(webdriver.CommandName.GET_WINDOW_HANDLES),
    624 'WebDriver.getAllWindowHandles()');
    625};
    626
    627
    628/**
    629 * Schedules a command to retrieve the current page's source. The page source
    630 * returned is a representation of the underlying DOM: do not expect it to be
    631 * formatted or escaped in the same way as the response sent from the web
    632 * server.
    633 * @return {!webdriver.promise.Promise.<string>} A promise that will be
    634 * resolved with the current page source.
    635 */
    636webdriver.WebDriver.prototype.getPageSource = function() {
    637 return this.schedule(
    638 new webdriver.Command(webdriver.CommandName.GET_PAGE_SOURCE),
    639 'WebDriver.getAllWindowHandles()');
    640};
    641
    642
    643/**
    644 * Schedules a command to close the current window.
    645 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    646 * when this command has completed.
    647 */
    648webdriver.WebDriver.prototype.close = function() {
    649 return this.schedule(new webdriver.Command(webdriver.CommandName.CLOSE),
    650 'WebDriver.close()');
    651};
    652
    653
    654/**
    655 * Schedules a command to navigate to the given URL.
    656 * @param {string} url The fully qualified URL to open.
    657 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    658 * when the document has finished loading.
    659 */
    660webdriver.WebDriver.prototype.get = function(url) {
    661 return this.navigate().to(url);
    662};
    663
    664
    665/**
    666 * Schedules a command to retrieve the URL of the current page.
    667 * @return {!webdriver.promise.Promise.<string>} A promise that will be
    668 * resolved with the current URL.
    669 */
    670webdriver.WebDriver.prototype.getCurrentUrl = function() {
    671 return this.schedule(
    672 new webdriver.Command(webdriver.CommandName.GET_CURRENT_URL),
    673 'WebDriver.getCurrentUrl()');
    674};
    675
    676
    677/**
    678 * Schedules a command to retrieve the current page's title.
    679 * @return {!webdriver.promise.Promise.<string>} A promise that will be
    680 * resolved with the current page's title.
    681 */
    682webdriver.WebDriver.prototype.getTitle = function() {
    683 return this.schedule(new webdriver.Command(webdriver.CommandName.GET_TITLE),
    684 'WebDriver.getTitle()');
    685};
    686
    687
    688/**
    689 * Schedule a command to find an element on the page. If the element cannot be
    690 * found, a {@link bot.ErrorCode.NO_SUCH_ELEMENT} result will be returned
    691 * by the driver. Unlike other commands, this error cannot be suppressed. In
    692 * other words, scheduling a command to find an element doubles as an assert
    693 * that the element is present on the page. To test whether an element is
    694 * present on the page, use {@link #isElementPresent} instead.
    695 *
    696 * <p>The search criteria for an element may be defined using one of the
    697 * factories in the {@link webdriver.By} namespace, or as a short-hand
    698 * {@link webdriver.By.Hash} object. For example, the following two statements
    699 * are equivalent:
    700 * <code><pre>
    701 * var e1 = driver.findElement(By.id('foo'));
    702 * var e2 = driver.findElement({id:'foo'});
    703 * </pre></code>
    704 *
    705 * <p>You may also provide a custom locator function, which takes as input
    706 * this WebDriver instance and returns a {@link webdriver.WebElement}, or a
    707 * promise that will resolve to a WebElement. For example, to find the first
    708 * visible link on a page, you could write:
    709 * <code><pre>
    710 * var link = driver.findElement(firstVisibleLink);
    711 *
    712 * function firstVisibleLink(driver) {
    713 * var links = driver.findElements(By.tagName('a'));
    714 * return webdriver.promise.filter(links, function(link) {
    715 * return links.isDisplayed();
    716 * }).then(function(visibleLinks) {
    717 * return visibleLinks[0];
    718 * });
    719 * }
    720 * </pre></code>
    721 *
    722 * <p>When running in the browser, a WebDriver cannot manipulate DOM elements
    723 * directly; it may do so only through a {@link webdriver.WebElement} reference.
    724 * This function may be used to generate a WebElement from a DOM element. A
    725 * reference to the DOM element will be stored in a known location and this
    726 * driver will attempt to retrieve it through {@link #executeScript}. If the
    727 * element cannot be found (eg, it belongs to a different document than the
    728 * one this instance is currently focused on), a
    729 * {@link bot.ErrorCode.NO_SUCH_ELEMENT} error will be returned.
    730 *
    731 * @param {!(webdriver.Locator|webdriver.By.Hash|Element|Function)} locator The
    732 * locator to use.
    733 * @return {!webdriver.WebElement} A WebElement that can be used to issue
    734 * commands against the located element. If the element is not found, the
    735 * element will be invalidated and all scheduled commands aborted.
    736 */
    737webdriver.WebDriver.prototype.findElement = function(locator) {
    738 var id;
    739 if ('nodeType' in locator && 'ownerDocument' in locator) {
    740 var element = /** @type {!Element} */ (locator);
    741 id = this.findDomElement_(element).then(function(element) {
    742 if (!element) {
    743 throw new bot.Error(bot.ErrorCode.NO_SUCH_ELEMENT,
    744 'Unable to locate element. Is WebDriver focused on its ' +
    745 'ownerDocument\'s frame?');
    746 }
    747 return element;
    748 });
    749 } else {
    750 locator = webdriver.Locator.checkLocator(locator);
    751 if (goog.isFunction(locator)) {
    752 id = this.findElementInternal_(locator, this);
    753 } else {
    754 var command = new webdriver.Command(webdriver.CommandName.FIND_ELEMENT).
    755 setParameter('using', locator.using).
    756 setParameter('value', locator.value);
    757 id = this.schedule(command, 'WebDriver.findElement(' + locator + ')');
    758 }
    759 }
    760 return new webdriver.WebElementPromise(this, id);
    761};
    762
    763
    764/**
    765 * @param {!Function} locatorFn The locator function to use.
    766 * @param {!(webdriver.WebDriver|webdriver.WebElement)} context The search
    767 * context.
    768 * @return {!webdriver.promise.Promise.<!webdriver.WebElement>} A
    769 * promise that will resolve to a list of WebElements.
    770 * @private
    771 */
    772webdriver.WebDriver.prototype.findElementInternal_ = function(
    773 locatorFn, context) {
    774 return this.call(goog.partial(locatorFn, context)).then(function(result) {
    775 if (goog.isArray(result)) {
    776 result = result[0];
    777 }
    778 if (!(result instanceof webdriver.WebElement)) {
    779 throw new TypeError('Custom locator did not return a WebElement');
    780 }
    781 return result;
    782 });
    783};
    784
    785
    786/**
    787 * Locates a DOM element so that commands may be issued against it using the
    788 * {@link webdriver.WebElement} class. This is accomplished by storing a
    789 * reference to the element in an object on the element's ownerDocument.
    790 * {@link #executeScript} will then be used to create a WebElement from this
    791 * reference. This requires this driver to currently be focused on the
    792 * ownerDocument's window+frame.
    793
    794 * @param {!Element} element The element to locate.
    795 * @return {!webdriver.promise.Promise.<webdriver.WebElement>} A promise that
    796 * will be fulfilled with the located element, or null if the element
    797 * could not be found.
    798 * @private
    799 */
    800webdriver.WebDriver.prototype.findDomElement_ = function(element) {
    801 var doc = element.ownerDocument;
    802 var store = doc['$webdriver$'] = doc['$webdriver$'] || {};
    803 var id = Math.floor(Math.random() * goog.now()).toString(36);
    804 store[id] = element;
    805 element[id] = id;
    806
    807 function cleanUp() {
    808 delete store[id];
    809 }
    810
    811 function lookupElement(id) {
    812 var store = document['$webdriver$'];
    813 if (!store) {
    814 return null;
    815 }
    816
    817 var element = store[id];
    818 if (!element || element[id] !== id) {
    819 return null;
    820 }
    821 return element;
    822 }
    823
    824 /** @type {!webdriver.promise.Promise.<webdriver.WebElement>} */
    825 var foundElement = this.executeScript(lookupElement, id);
    826 foundElement.thenFinally(cleanUp);
    827 return foundElement;
    828};
    829
    830
    831/**
    832 * Schedules a command to test if an element is present on the page.
    833 *
    834 * <p>If given a DOM element, this function will check if it belongs to the
    835 * document the driver is currently focused on. Otherwise, the function will
    836 * test if at least one element can be found with the given search criteria.
    837 *
    838 * @param {!(webdriver.Locator|webdriver.By.Hash|Element|
    839 * Function)} locatorOrElement The locator to use, or the actual
    840 * DOM element to be located by the server.
    841 * @return {!webdriver.promise.Promise.<boolean>} A promise that will resolve
    842 * with whether the element is present on the page.
    843 */
    844webdriver.WebDriver.prototype.isElementPresent = function(locatorOrElement) {
    845 if ('nodeType' in locatorOrElement && 'ownerDocument' in locatorOrElement) {
    846 return this.findDomElement_(/** @type {!Element} */ (locatorOrElement)).
    847 then(function(result) { return !!result; });
    848 } else {
    849 return this.findElements.apply(this, arguments).then(function(result) {
    850 return !!result.length;
    851 });
    852 }
    853};
    854
    855
    856/**
    857 * Schedule a command to search for multiple elements on the page.
    858 *
    859 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The locator
    860 * strategy to use when searching for the element.
    861 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.WebElement>>} A
    862 * promise that will resolve to an array of WebElements.
    863 */
    864webdriver.WebDriver.prototype.findElements = function(locator) {
    865 locator = webdriver.Locator.checkLocator(locator);
    866 if (goog.isFunction(locator)) {
    867 return this.findElementsInternal_(locator, this);
    868 } else {
    869 var command = new webdriver.Command(webdriver.CommandName.FIND_ELEMENTS).
    870 setParameter('using', locator.using).
    871 setParameter('value', locator.value);
    872 return this.schedule(command, 'WebDriver.findElements(' + locator + ')');
    873 }
    874};
    875
    876
    877/**
    878 * @param {!Function} locatorFn The locator function to use.
    879 * @param {!(webdriver.WebDriver|webdriver.WebElement)} context The search
    880 * context.
    881 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.WebElement>>} A
    882 * promise that will resolve to an array of WebElements.
    883 * @private
    884 */
    885webdriver.WebDriver.prototype.findElementsInternal_ = function(
    886 locatorFn, context) {
    887 return this.call(goog.partial(locatorFn, context)).then(function(result) {
    888 if (result instanceof webdriver.WebElement) {
    889 return [result];
    890 }
    891
    892 if (!goog.isArray(result)) {
    893 return [];
    894 }
    895
    896 return goog.array.filter(result, function(item) {
    897 return item instanceof webdriver.WebElement;
    898 });
    899 });
    900};
    901
    902
    903/**
    904 * Schedule a command to take a screenshot. The driver makes a best effort to
    905 * return a screenshot of the following, in order of preference:
    906 * <ol>
    907 * <li>Entire page
    908 * <li>Current window
    909 * <li>Visible portion of the current frame
    910 * <li>The screenshot of the entire display containing the browser
    911 * </ol>
    912 *
    913 * @return {!webdriver.promise.Promise.<string>} A promise that will be
    914 * resolved to the screenshot as a base-64 encoded PNG.
    915 */
    916webdriver.WebDriver.prototype.takeScreenshot = function() {
    917 return this.schedule(new webdriver.Command(webdriver.CommandName.SCREENSHOT),
    918 'WebDriver.takeScreenshot()');
    919};
    920
    921
    922/**
    923 * @return {!webdriver.WebDriver.Options} The options interface for this
    924 * instance.
    925 */
    926webdriver.WebDriver.prototype.manage = function() {
    927 return new webdriver.WebDriver.Options(this);
    928};
    929
    930
    931/**
    932 * @return {!webdriver.WebDriver.Navigation} The navigation interface for this
    933 * instance.
    934 */
    935webdriver.WebDriver.prototype.navigate = function() {
    936 return new webdriver.WebDriver.Navigation(this);
    937};
    938
    939
    940/**
    941 * @return {!webdriver.WebDriver.TargetLocator} The target locator interface for
    942 * this instance.
    943 */
    944webdriver.WebDriver.prototype.switchTo = function() {
    945 return new webdriver.WebDriver.TargetLocator(this);
    946};
    947
    948
    949
    950/**
    951 * Interface for navigating back and forth in the browser history.
    952 * @param {!webdriver.WebDriver} driver The parent driver.
    953 * @constructor
    954 */
    955webdriver.WebDriver.Navigation = function(driver) {
    956
    957 /** @private {!webdriver.WebDriver} */
    958 this.driver_ = driver;
    959};
    960
    961
    962/**
    963 * Schedules a command to navigate to a new URL.
    964 * @param {string} url The URL to navigate to.
    965 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    966 * when the URL has been loaded.
    967 */
    968webdriver.WebDriver.Navigation.prototype.to = function(url) {
    969 return this.driver_.schedule(
    970 new webdriver.Command(webdriver.CommandName.GET).
    971 setParameter('url', url),
    972 'WebDriver.navigate().to(' + url + ')');
    973};
    974
    975
    976/**
    977 * Schedules a command to move backwards in the browser history.
    978 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    979 * when the navigation event has completed.
    980 */
    981webdriver.WebDriver.Navigation.prototype.back = function() {
    982 return this.driver_.schedule(
    983 new webdriver.Command(webdriver.CommandName.GO_BACK),
    984 'WebDriver.navigate().back()');
    985};
    986
    987
    988/**
    989 * Schedules a command to move forwards in the browser history.
    990 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    991 * when the navigation event has completed.
    992 */
    993webdriver.WebDriver.Navigation.prototype.forward = function() {
    994 return this.driver_.schedule(
    995 new webdriver.Command(webdriver.CommandName.GO_FORWARD),
    996 'WebDriver.navigate().forward()');
    997};
    998
    999
    1000/**
    1001 * Schedules a command to refresh the current page.
    1002 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1003 * when the navigation event has completed.
    1004 */
    1005webdriver.WebDriver.Navigation.prototype.refresh = function() {
    1006 return this.driver_.schedule(
    1007 new webdriver.Command(webdriver.CommandName.REFRESH),
    1008 'WebDriver.navigate().refresh()');
    1009};
    1010
    1011
    1012
    1013/**
    1014 * Provides methods for managing browser and driver state.
    1015 * @param {!webdriver.WebDriver} driver The parent driver.
    1016 * @constructor
    1017 */
    1018webdriver.WebDriver.Options = function(driver) {
    1019
    1020 /** @private {!webdriver.WebDriver} */
    1021 this.driver_ = driver;
    1022};
    1023
    1024
    1025/**
    1026 * A JSON description of a browser cookie.
    1027 * @typedef {{
    1028 * name: string,
    1029 * value: string,
    1030 * path: (string|undefined),
    1031 * domain: (string|undefined),
    1032 * secure: (boolean|undefined),
    1033 * expiry: (number|undefined)
    1034 * }}
    1035 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object
    1036 */
    1037webdriver.WebDriver.Options.Cookie;
    1038
    1039
    1040/**
    1041 * Schedules a command to add a cookie.
    1042 * @param {string} name The cookie name.
    1043 * @param {string} value The cookie value.
    1044 * @param {string=} opt_path The cookie path.
    1045 * @param {string=} opt_domain The cookie domain.
    1046 * @param {boolean=} opt_isSecure Whether the cookie is secure.
    1047 * @param {(number|!Date)=} opt_expiry When the cookie expires. If specified as
    1048 * a number, should be in milliseconds since midnight, January 1, 1970 UTC.
    1049 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1050 * when the cookie has been added to the page.
    1051 */
    1052webdriver.WebDriver.Options.prototype.addCookie = function(
    1053 name, value, opt_path, opt_domain, opt_isSecure, opt_expiry) {
    1054 // We do not allow '=' or ';' in the name.
    1055 if (/[;=]/.test(name)) {
    1056 throw Error('Invalid cookie name "' + name + '"');
    1057 }
    1058
    1059 // We do not allow ';' in value.
    1060 if (/;/.test(value)) {
    1061 throw Error('Invalid cookie value "' + value + '"');
    1062 }
    1063
    1064 var cookieString = name + '=' + value +
    1065 (opt_domain ? ';domain=' + opt_domain : '') +
    1066 (opt_path ? ';path=' + opt_path : '') +
    1067 (opt_isSecure ? ';secure' : '');
    1068
    1069 var expiry;
    1070 if (goog.isDef(opt_expiry)) {
    1071 var expiryDate;
    1072 if (goog.isNumber(opt_expiry)) {
    1073 expiryDate = new Date(opt_expiry);
    1074 } else {
    1075 expiryDate = /** @type {!Date} */ (opt_expiry);
    1076 opt_expiry = expiryDate.getTime();
    1077 }
    1078 cookieString += ';expires=' + expiryDate.toUTCString();
    1079 // Convert from milliseconds to seconds.
    1080 expiry = Math.floor(/** @type {number} */ (opt_expiry) / 1000);
    1081 }
    1082
    1083 return this.driver_.schedule(
    1084 new webdriver.Command(webdriver.CommandName.ADD_COOKIE).
    1085 setParameter('cookie', {
    1086 'name': name,
    1087 'value': value,
    1088 'path': opt_path,
    1089 'domain': opt_domain,
    1090 'secure': !!opt_isSecure,
    1091 'expiry': expiry
    1092 }),
    1093 'WebDriver.manage().addCookie(' + cookieString + ')');
    1094};
    1095
    1096
    1097/**
    1098 * Schedules a command to delete all cookies visible to the current page.
    1099 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1100 * when all cookies have been deleted.
    1101 */
    1102webdriver.WebDriver.Options.prototype.deleteAllCookies = function() {
    1103 return this.driver_.schedule(
    1104 new webdriver.Command(webdriver.CommandName.DELETE_ALL_COOKIES),
    1105 'WebDriver.manage().deleteAllCookies()');
    1106};
    1107
    1108
    1109/**
    1110 * Schedules a command to delete the cookie with the given name. This command is
    1111 * a no-op if there is no cookie with the given name visible to the current
    1112 * page.
    1113 * @param {string} name The name of the cookie to delete.
    1114 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1115 * when the cookie has been deleted.
    1116 */
    1117webdriver.WebDriver.Options.prototype.deleteCookie = function(name) {
    1118 return this.driver_.schedule(
    1119 new webdriver.Command(webdriver.CommandName.DELETE_COOKIE).
    1120 setParameter('name', name),
    1121 'WebDriver.manage().deleteCookie(' + name + ')');
    1122};
    1123
    1124
    1125/**
    1126 * Schedules a command to retrieve all cookies visible to the current page.
    1127 * Each cookie will be returned as a JSON object as described by the WebDriver
    1128 * wire protocol.
    1129 * @return {!webdriver.promise.Promise.<
    1130 * !Array.<webdriver.WebDriver.Options.Cookie>>} A promise that will be
    1131 * resolved with the cookies visible to the current page.
    1132 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object
    1133 */
    1134webdriver.WebDriver.Options.prototype.getCookies = function() {
    1135 return this.driver_.schedule(
    1136 new webdriver.Command(webdriver.CommandName.GET_ALL_COOKIES),
    1137 'WebDriver.manage().getCookies()');
    1138};
    1139
    1140
    1141/**
    1142 * Schedules a command to retrieve the cookie with the given name. Returns null
    1143 * if there is no such cookie. The cookie will be returned as a JSON object as
    1144 * described by the WebDriver wire protocol.
    1145 * @param {string} name The name of the cookie to retrieve.
    1146 * @return {!webdriver.promise.Promise.<?webdriver.WebDriver.Options.Cookie>} A
    1147 * promise that will be resolved with the named cookie, or {@code null}
    1148 * if there is no such cookie.
    1149 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object
    1150 */
    1151webdriver.WebDriver.Options.prototype.getCookie = function(name) {
    1152 return this.getCookies().then(function(cookies) {
    1153 return goog.array.find(cookies, function(cookie) {
    1154 return cookie && cookie['name'] == name;
    1155 });
    1156 });
    1157};
    1158
    1159
    1160/**
    1161 * @return {!webdriver.WebDriver.Logs} The interface for managing driver
    1162 * logs.
    1163 */
    1164webdriver.WebDriver.Options.prototype.logs = function() {
    1165 return new webdriver.WebDriver.Logs(this.driver_);
    1166};
    1167
    1168
    1169/**
    1170 * @return {!webdriver.WebDriver.Timeouts} The interface for managing driver
    1171 * timeouts.
    1172 */
    1173webdriver.WebDriver.Options.prototype.timeouts = function() {
    1174 return new webdriver.WebDriver.Timeouts(this.driver_);
    1175};
    1176
    1177
    1178/**
    1179 * @return {!webdriver.WebDriver.Window} The interface for managing the
    1180 * current window.
    1181 */
    1182webdriver.WebDriver.Options.prototype.window = function() {
    1183 return new webdriver.WebDriver.Window(this.driver_);
    1184};
    1185
    1186
    1187
    1188/**
    1189 * An interface for managing timeout behavior for WebDriver instances.
    1190 * @param {!webdriver.WebDriver} driver The parent driver.
    1191 * @constructor
    1192 */
    1193webdriver.WebDriver.Timeouts = function(driver) {
    1194
    1195 /** @private {!webdriver.WebDriver} */
    1196 this.driver_ = driver;
    1197};
    1198
    1199
    1200/**
    1201 * Specifies the amount of time the driver should wait when searching for an
    1202 * element if it is not immediately present.
    1203 * <p/>
    1204 * When searching for a single element, the driver should poll the page
    1205 * until the element has been found, or this timeout expires before failing
    1206 * with a {@code bot.ErrorCode.NO_SUCH_ELEMENT} error. When searching
    1207 * for multiple elements, the driver should poll the page until at least one
    1208 * element has been found or this timeout has expired.
    1209 * <p/>
    1210 * Setting the wait timeout to 0 (its default value), disables implicit
    1211 * waiting.
    1212 * <p/>
    1213 * Increasing the implicit wait timeout should be used judiciously as it
    1214 * will have an adverse effect on test run time, especially when used with
    1215 * slower location strategies like XPath.
    1216 *
    1217 * @param {number} ms The amount of time to wait, in milliseconds.
    1218 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1219 * when the implicit wait timeout has been set.
    1220 */
    1221webdriver.WebDriver.Timeouts.prototype.implicitlyWait = function(ms) {
    1222 return this.driver_.schedule(
    1223 new webdriver.Command(webdriver.CommandName.IMPLICITLY_WAIT).
    1224 setParameter('ms', ms < 0 ? 0 : ms),
    1225 'WebDriver.manage().timeouts().implicitlyWait(' + ms + ')');
    1226};
    1227
    1228
    1229/**
    1230 * Sets the amount of time to wait, in milliseconds, for an asynchronous script
    1231 * to finish execution before returning an error. If the timeout is less than or
    1232 * equal to 0, the script will be allowed to run indefinitely.
    1233 *
    1234 * @param {number} ms The amount of time to wait, in milliseconds.
    1235 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1236 * when the script timeout has been set.
    1237 */
    1238webdriver.WebDriver.Timeouts.prototype.setScriptTimeout = function(ms) {
    1239 return this.driver_.schedule(
    1240 new webdriver.Command(webdriver.CommandName.SET_SCRIPT_TIMEOUT).
    1241 setParameter('ms', ms < 0 ? 0 : ms),
    1242 'WebDriver.manage().timeouts().setScriptTimeout(' + ms + ')');
    1243};
    1244
    1245
    1246/**
    1247 * Sets the amount of time to wait for a page load to complete before returning
    1248 * an error. If the timeout is negative, page loads may be indefinite.
    1249 * @param {number} ms The amount of time to wait, in milliseconds.
    1250 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1251 * when the timeout has been set.
    1252 */
    1253webdriver.WebDriver.Timeouts.prototype.pageLoadTimeout = function(ms) {
    1254 return this.driver_.schedule(
    1255 new webdriver.Command(webdriver.CommandName.SET_TIMEOUT).
    1256 setParameter('type', 'page load').
    1257 setParameter('ms', ms),
    1258 'WebDriver.manage().timeouts().pageLoadTimeout(' + ms + ')');
    1259};
    1260
    1261
    1262
    1263/**
    1264 * An interface for managing the current window.
    1265 * @param {!webdriver.WebDriver} driver The parent driver.
    1266 * @constructor
    1267 */
    1268webdriver.WebDriver.Window = function(driver) {
    1269
    1270 /** @private {!webdriver.WebDriver} */
    1271 this.driver_ = driver;
    1272};
    1273
    1274
    1275/**
    1276 * Retrieves the window's current position, relative to the top left corner of
    1277 * the screen.
    1278 * @return {!webdriver.promise.Promise.<{x: number, y: number}>} A promise that
    1279 * will be resolved with the window's position in the form of a
    1280 * {x:number, y:number} object literal.
    1281 */
    1282webdriver.WebDriver.Window.prototype.getPosition = function() {
    1283 return this.driver_.schedule(
    1284 new webdriver.Command(webdriver.CommandName.GET_WINDOW_POSITION).
    1285 setParameter('windowHandle', 'current'),
    1286 'WebDriver.manage().window().getPosition()');
    1287};
    1288
    1289
    1290/**
    1291 * Repositions the current window.
    1292 * @param {number} x The desired horizontal position, relative to the left side
    1293 * of the screen.
    1294 * @param {number} y The desired vertical position, relative to the top of the
    1295 * of the screen.
    1296 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1297 * when the command has completed.
    1298 */
    1299webdriver.WebDriver.Window.prototype.setPosition = function(x, y) {
    1300 return this.driver_.schedule(
    1301 new webdriver.Command(webdriver.CommandName.SET_WINDOW_POSITION).
    1302 setParameter('windowHandle', 'current').
    1303 setParameter('x', x).
    1304 setParameter('y', y),
    1305 'WebDriver.manage().window().setPosition(' + x + ', ' + y + ')');
    1306};
    1307
    1308
    1309/**
    1310 * Retrieves the window's current size.
    1311 * @return {!webdriver.promise.Promise.<{width: number, height: number}>} A
    1312 * promise that will be resolved with the window's size in the form of a
    1313 * {width:number, height:number} object literal.
    1314 */
    1315webdriver.WebDriver.Window.prototype.getSize = function() {
    1316 return this.driver_.schedule(
    1317 new webdriver.Command(webdriver.CommandName.GET_WINDOW_SIZE).
    1318 setParameter('windowHandle', 'current'),
    1319 'WebDriver.manage().window().getSize()');
    1320};
    1321
    1322
    1323/**
    1324 * Resizes the current window.
    1325 * @param {number} width The desired window width.
    1326 * @param {number} height The desired window height.
    1327 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1328 * when the command has completed.
    1329 */
    1330webdriver.WebDriver.Window.prototype.setSize = function(width, height) {
    1331 return this.driver_.schedule(
    1332 new webdriver.Command(webdriver.CommandName.SET_WINDOW_SIZE).
    1333 setParameter('windowHandle', 'current').
    1334 setParameter('width', width).
    1335 setParameter('height', height),
    1336 'WebDriver.manage().window().setSize(' + width + ', ' + height + ')');
    1337};
    1338
    1339
    1340/**
    1341 * Maximizes the current window.
    1342 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1343 * when the command has completed.
    1344 */
    1345webdriver.WebDriver.Window.prototype.maximize = function() {
    1346 return this.driver_.schedule(
    1347 new webdriver.Command(webdriver.CommandName.MAXIMIZE_WINDOW).
    1348 setParameter('windowHandle', 'current'),
    1349 'WebDriver.manage().window().maximize()');
    1350};
    1351
    1352
    1353/**
    1354 * Interface for managing WebDriver log records.
    1355 * @param {!webdriver.WebDriver} driver The parent driver.
    1356 * @constructor
    1357 */
    1358webdriver.WebDriver.Logs = function(driver) {
    1359
    1360 /** @private {!webdriver.WebDriver} */
    1361 this.driver_ = driver;
    1362};
    1363
    1364
    1365/**
    1366 * Fetches available log entries for the given type.
    1367 *
    1368 * <p/>Note that log buffers are reset after each call, meaning that
    1369 * available log entries correspond to those entries not yet returned for a
    1370 * given log type. In practice, this means that this call will return the
    1371 * available log entries since the last call, or from the start of the
    1372 * session.
    1373 *
    1374 * @param {!webdriver.logging.Type} type The desired log type.
    1375 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.logging.Entry>>} A
    1376 * promise that will resolve to a list of log entries for the specified
    1377 * type.
    1378 */
    1379webdriver.WebDriver.Logs.prototype.get = function(type) {
    1380 return this.driver_.schedule(
    1381 new webdriver.Command(webdriver.CommandName.GET_LOG).
    1382 setParameter('type', type),
    1383 'WebDriver.manage().logs().get(' + type + ')').
    1384 then(function(entries) {
    1385 return goog.array.map(entries, function(entry) {
    1386 if (!(entry instanceof webdriver.logging.Entry)) {
    1387 return new webdriver.logging.Entry(
    1388 entry['level'], entry['message'], entry['timestamp'],
    1389 entry['type']);
    1390 }
    1391 return entry;
    1392 });
    1393 });
    1394};
    1395
    1396
    1397/**
    1398 * Retrieves the log types available to this driver.
    1399 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.logging.Type>>} A
    1400 * promise that will resolve to a list of available log types.
    1401 */
    1402webdriver.WebDriver.Logs.prototype.getAvailableLogTypes = function() {
    1403 return this.driver_.schedule(
    1404 new webdriver.Command(webdriver.CommandName.GET_AVAILABLE_LOG_TYPES),
    1405 'WebDriver.manage().logs().getAvailableLogTypes()');
    1406};
    1407
    1408
    1409
    1410/**
    1411 * An interface for changing the focus of the driver to another frame or window.
    1412 * @param {!webdriver.WebDriver} driver The parent driver.
    1413 * @constructor
    1414 */
    1415webdriver.WebDriver.TargetLocator = function(driver) {
    1416
    1417 /** @private {!webdriver.WebDriver} */
    1418 this.driver_ = driver;
    1419};
    1420
    1421
    1422/**
    1423 * Schedules a command retrieve the {@code document.activeElement} element on
    1424 * the current document, or {@code document.body} if activeElement is not
    1425 * available.
    1426 * @return {!webdriver.WebElementPromise} The active element.
    1427 */
    1428webdriver.WebDriver.TargetLocator.prototype.activeElement = function() {
    1429 var id = this.driver_.schedule(
    1430 new webdriver.Command(webdriver.CommandName.GET_ACTIVE_ELEMENT),
    1431 'WebDriver.switchTo().activeElement()');
    1432 return new webdriver.WebElementPromise(this.driver_, id);
    1433};
    1434
    1435
    1436/**
    1437 * Schedules a command to switch focus of all future commands to the first frame
    1438 * on the page.
    1439 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1440 * when the driver has changed focus to the default content.
    1441 */
    1442webdriver.WebDriver.TargetLocator.prototype.defaultContent = function() {
    1443 return this.driver_.schedule(
    1444 new webdriver.Command(webdriver.CommandName.SWITCH_TO_FRAME).
    1445 setParameter('id', null),
    1446 'WebDriver.switchTo().defaultContent()');
    1447};
    1448
    1449
    1450/**
    1451 * Schedules a command to switch the focus of all future commands to another
    1452 * frame on the page.
    1453 * <p/>
    1454 * If the frame is specified by a number, the command will switch to the frame
    1455 * by its (zero-based) index into the {@code window.frames} collection.
    1456 * <p/>
    1457 * If the frame is specified by a string, the command will select the frame by
    1458 * its name or ID. To select sub-frames, simply separate the frame names/IDs by
    1459 * dots. As an example, "main.child" will select the frame with the name "main"
    1460 * and then its child "child".
    1461 * <p/>
    1462 * If the specified frame can not be found, the deferred result will errback
    1463 * with a {@code bot.ErrorCode.NO_SUCH_FRAME} error.
    1464 * @param {string|number} nameOrIndex The frame locator.
    1465 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1466 * when the driver has changed focus to the specified frame.
    1467 */
    1468webdriver.WebDriver.TargetLocator.prototype.frame = function(nameOrIndex) {
    1469 return this.driver_.schedule(
    1470 new webdriver.Command(webdriver.CommandName.SWITCH_TO_FRAME).
    1471 setParameter('id', nameOrIndex),
    1472 'WebDriver.switchTo().frame(' + nameOrIndex + ')');
    1473};
    1474
    1475
    1476/**
    1477 * Schedules a command to switch the focus of all future commands to another
    1478 * window. Windows may be specified by their {@code window.name} attribute or
    1479 * by its handle (as returned by {@code webdriver.WebDriver#getWindowHandles}).
    1480 * <p/>
    1481 * If the specificed window can not be found, the deferred result will errback
    1482 * with a {@code bot.ErrorCode.NO_SUCH_WINDOW} error.
    1483 * @param {string} nameOrHandle The name or window handle of the window to
    1484 * switch focus to.
    1485 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1486 * when the driver has changed focus to the specified window.
    1487 */
    1488webdriver.WebDriver.TargetLocator.prototype.window = function(nameOrHandle) {
    1489 return this.driver_.schedule(
    1490 new webdriver.Command(webdriver.CommandName.SWITCH_TO_WINDOW).
    1491 setParameter('name', nameOrHandle),
    1492 'WebDriver.switchTo().window(' + nameOrHandle + ')');
    1493};
    1494
    1495
    1496/**
    1497 * Schedules a command to change focus to the active alert dialog. This command
    1498 * will return a {@link bot.ErrorCode.NO_SUCH_ALERT} error if an alert dialog
    1499 * is not currently open.
    1500 * @return {!webdriver.AlertPromise} The open alert.
    1501 */
    1502webdriver.WebDriver.TargetLocator.prototype.alert = function() {
    1503 var text = this.driver_.schedule(
    1504 new webdriver.Command(webdriver.CommandName.GET_ALERT_TEXT),
    1505 'WebDriver.switchTo().alert()');
    1506 var driver = this.driver_;
    1507 return new webdriver.AlertPromise(driver, text.then(function(text) {
    1508 return new webdriver.Alert(driver, text);
    1509 }));
    1510};
    1511
    1512
    1513/**
    1514 * Simulate pressing many keys at once in a "chord". Takes a sequence of
    1515 * {@link webdriver.Key}s or strings, appends each of the values to a string,
    1516 * and adds the chord termination key ({@link webdriver.Key.NULL}) and returns
    1517 * the resultant string.
    1518 *
    1519 * Note: when the low-level webdriver key handlers see Keys.NULL, active
    1520 * modifier keys (CTRL/ALT/SHIFT/etc) release via a keyup event.
    1521 *
    1522 * @param {...string} var_args The key sequence to concatenate.
    1523 * @return {string} The null-terminated key sequence.
    1524 * @see http://code.google.com/p/webdriver/issues/detail?id=79
    1525 */
    1526webdriver.Key.chord = function(var_args) {
    1527 var sequence = goog.array.reduce(
    1528 goog.array.slice(arguments, 0),
    1529 function(str, key) {
    1530 return str + key;
    1531 }, '');
    1532 sequence += webdriver.Key.NULL;
    1533 return sequence;
    1534};
    1535
    1536
    1537//////////////////////////////////////////////////////////////////////////////
    1538//
    1539// webdriver.WebElement
    1540//
    1541//////////////////////////////////////////////////////////////////////////////
    1542
    1543
    1544
    1545/**
    1546 * Represents a DOM element. WebElements can be found by searching from the
    1547 * document root using a {@code webdriver.WebDriver} instance, or by searching
    1548 * under another {@code webdriver.WebElement}:
    1549 * <pre><code>
    1550 * driver.get('http://www.google.com');
    1551 * var searchForm = driver.findElement(By.tagName('form'));
    1552 * var searchBox = searchForm.findElement(By.name('q'));
    1553 * searchBox.sendKeys('webdriver');
    1554 * </code></pre>
    1555 *
    1556 * The WebElement is implemented as a promise for compatibility with the promise
    1557 * API. It will always resolve itself when its internal state has been fully
    1558 * resolved and commands may be issued against the element. This can be used to
    1559 * catch errors when an element cannot be located on the page:
    1560 * <pre><code>
    1561 * driver.findElement(By.id('not-there')).then(function(element) {
    1562 * alert('Found an element that was not expected to be there!');
    1563 * }, function(error) {
    1564 * alert('The element was not found, as expected');
    1565 * });
    1566 * </code></pre>
    1567 *
    1568 * @param {!webdriver.WebDriver} driver The parent WebDriver instance for this
    1569 * element.
    1570 * @param {!(webdriver.promise.Promise.<webdriver.WebElement.Id>|
    1571 * webdriver.WebElement.Id)} id The server-assigned opaque ID for the
    1572 * underlying DOM element.
    1573 * @constructor
    1574 */
    1575webdriver.WebElement = function(driver, id) {
    1576
    1577 /** @private {!webdriver.WebDriver} */
    1578 this.driver_ = driver;
    1579
    1580 /** @private {!webdriver.promise.Promise.<webdriver.WebElement.Id>} */
    1581 this.id_ = id instanceof webdriver.promise.Promise ?
    1582 id : webdriver.promise.fulfilled(id);
    1583};
    1584
    1585
    1586/**
    1587 * Wire protocol definition of a WebElement ID.
    1588 * @typedef {{ELEMENT: string}}
    1589 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol
    1590 */
    1591webdriver.WebElement.Id;
    1592
    1593
    1594/**
    1595 * The property key used in the wire protocol to indicate that a JSON object
    1596 * contains the ID of a WebElement.
    1597 * @type {string}
    1598 * @const
    1599 */
    1600webdriver.WebElement.ELEMENT_KEY = 'ELEMENT';
    1601
    1602
    1603/**
    1604 * Compares to WebElements for equality.
    1605 * @param {!webdriver.WebElement} a A WebElement.
    1606 * @param {!webdriver.WebElement} b A WebElement.
    1607 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be
    1608 * resolved to whether the two WebElements are equal.
    1609 */
    1610webdriver.WebElement.equals = function(a, b) {
    1611 if (a == b) {
    1612 return webdriver.promise.fulfilled(true);
    1613 }
    1614 var ids = [a.getId(), b.getId()];
    1615 return webdriver.promise.all(ids).then(function(ids) {
    1616 // If the two element's have the same ID, they should be considered
    1617 // equal. Otherwise, they may still be equivalent, but we'll need to
    1618 // ask the server to check for us.
    1619 if (ids[0][webdriver.WebElement.ELEMENT_KEY] ==
    1620 ids[1][webdriver.WebElement.ELEMENT_KEY]) {
    1621 return true;
    1622 }
    1623
    1624 var command = new webdriver.Command(webdriver.CommandName.ELEMENT_EQUALS);
    1625 command.setParameter('id', ids[0]);
    1626 command.setParameter('other', ids[1]);
    1627 return a.driver_.schedule(command, 'webdriver.WebElement.equals()');
    1628 });
    1629};
    1630
    1631
    1632/**
    1633 * @return {!webdriver.WebDriver} The parent driver for this instance.
    1634 */
    1635webdriver.WebElement.prototype.getDriver = function() {
    1636 return this.driver_;
    1637};
    1638
    1639
    1640/**
    1641 * @return {!webdriver.promise.Promise.<webdriver.WebElement.Id>} A promise
    1642 * that resolves to this element's JSON representation as defined by the
    1643 * WebDriver wire protocol.
    1644 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol
    1645 */
    1646webdriver.WebElement.prototype.getId = function() {
    1647 return this.id_;
    1648};
    1649
    1650
    1651/**
    1652 * Schedules a command that targets this element with the parent WebDriver
    1653 * instance. Will ensure this element's ID is included in the command parameters
    1654 * under the "id" key.
    1655 * @param {!webdriver.Command} command The command to schedule.
    1656 * @param {string} description A description of the command for debugging.
    1657 * @return {!webdriver.promise.Promise.<T>} A promise that will be resolved
    1658 * with the command result.
    1659 * @template T
    1660 * @see webdriver.WebDriver.prototype.schedule
    1661 * @private
    1662 */
    1663webdriver.WebElement.prototype.schedule_ = function(command, description) {
    1664 command.setParameter('id', this.getId());
    1665 return this.driver_.schedule(command, description);
    1666};
    1667
    1668
    1669/**
    1670 * Schedule a command to find a descendant of this element. If the element
    1671 * cannot be found, a {@code bot.ErrorCode.NO_SUCH_ELEMENT} result will
    1672 * be returned by the driver. Unlike other commands, this error cannot be
    1673 * suppressed. In other words, scheduling a command to find an element doubles
    1674 * as an assert that the element is present on the page. To test whether an
    1675 * element is present on the page, use {@code #isElementPresent} instead.
    1676 *
    1677 * <p>The search criteria for an element may be defined using one of the
    1678 * factories in the {@link webdriver.By} namespace, or as a short-hand
    1679 * {@link webdriver.By.Hash} object. For example, the following two statements
    1680 * are equivalent:
    1681 * <code><pre>
    1682 * var e1 = element.findElement(By.id('foo'));
    1683 * var e2 = element.findElement({id:'foo'});
    1684 * </pre></code>
    1685 *
    1686 * <p>You may also provide a custom locator function, which takes as input
    1687 * this WebDriver instance and returns a {@link webdriver.WebElement}, or a
    1688 * promise that will resolve to a WebElement. For example, to find the first
    1689 * visible link on a page, you could write:
    1690 * <code><pre>
    1691 * var link = element.findElement(firstVisibleLink);
    1692 *
    1693 * function firstVisibleLink(element) {
    1694 * var links = element.findElements(By.tagName('a'));
    1695 * return webdriver.promise.filter(links, function(link) {
    1696 * return links.isDisplayed();
    1697 * }).then(function(visibleLinks) {
    1698 * return visibleLinks[0];
    1699 * });
    1700 * }
    1701 * </pre></code>
    1702 *
    1703 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The
    1704 * locator strategy to use when searching for the element.
    1705 * @return {!webdriver.WebElement} A WebElement that can be used to issue
    1706 * commands against the located element. If the element is not found, the
    1707 * element will be invalidated and all scheduled commands aborted.
    1708 */
    1709webdriver.WebElement.prototype.findElement = function(locator) {
    1710 locator = webdriver.Locator.checkLocator(locator);
    1711 var id;
    1712 if (goog.isFunction(locator)) {
    1713 id = this.driver_.findElementInternal_(locator, this);
    1714 } else {
    1715 var command = new webdriver.Command(
    1716 webdriver.CommandName.FIND_CHILD_ELEMENT).
    1717 setParameter('using', locator.using).
    1718 setParameter('value', locator.value);
    1719 id = this.schedule_(command, 'WebElement.findElement(' + locator + ')');
    1720 }
    1721 return new webdriver.WebElementPromise(this.driver_, id);
    1722};
    1723
    1724
    1725/**
    1726 * Schedules a command to test if there is at least one descendant of this
    1727 * element that matches the given search criteria.
    1728 *
    1729 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The
    1730 * locator strategy to use when searching for the element.
    1731 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be
    1732 * resolved with whether an element could be located on the page.
    1733 */
    1734webdriver.WebElement.prototype.isElementPresent = function(locator) {
    1735 return this.findElements(locator).then(function(result) {
    1736 return !!result.length;
    1737 });
    1738};
    1739
    1740
    1741/**
    1742 * Schedules a command to find all of the descendants of this element that
    1743 * match the given search criteria.
    1744 *
    1745 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The
    1746 * locator strategy to use when searching for the elements.
    1747 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.WebElement>>} A
    1748 * promise that will resolve to an array of WebElements.
    1749 */
    1750webdriver.WebElement.prototype.findElements = function(locator) {
    1751 locator = webdriver.Locator.checkLocator(locator);
    1752 if (goog.isFunction(locator)) {
    1753 return this.driver_.findElementsInternal_(locator, this);
    1754 } else {
    1755 var command = new webdriver.Command(
    1756 webdriver.CommandName.FIND_CHILD_ELEMENTS).
    1757 setParameter('using', locator.using).
    1758 setParameter('value', locator.value);
    1759 return this.schedule_(command, 'WebElement.findElements(' + locator + ')');
    1760 }
    1761};
    1762
    1763
    1764/**
    1765 * Schedules a command to click on this element.
    1766 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1767 * when the click command has completed.
    1768 */
    1769webdriver.WebElement.prototype.click = function() {
    1770 return this.schedule_(
    1771 new webdriver.Command(webdriver.CommandName.CLICK_ELEMENT),
    1772 'WebElement.click()');
    1773};
    1774
    1775
    1776/**
    1777 * Schedules a command to type a sequence on the DOM element represented by this
    1778 * instance.
    1779 * <p/>
    1780 * Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is
    1781 * processed in the keysequence, that key state is toggled until one of the
    1782 * following occurs:
    1783 * <ul>
    1784 * <li>The modifier key is encountered again in the sequence. At this point the
    1785 * state of the key is toggled (along with the appropriate keyup/down events).
    1786 * </li>
    1787 * <li>The {@code webdriver.Key.NULL} key is encountered in the sequence. When
    1788 * this key is encountered, all modifier keys current in the down state are
    1789 * released (with accompanying keyup events). The NULL key can be used to
    1790 * simulate common keyboard shortcuts:
    1791 * <code><pre>
    1792 * element.sendKeys("text was",
    1793 * webdriver.Key.CONTROL, "a", webdriver.Key.NULL,
    1794 * "now text is");
    1795 * // Alternatively:
    1796 * element.sendKeys("text was",
    1797 * webdriver.Key.chord(webdriver.Key.CONTROL, "a"),
    1798 * "now text is");
    1799 * </pre></code></li>
    1800 * <li>The end of the keysequence is encountered. When there are no more keys
    1801 * to type, all depressed modifier keys are released (with accompanying keyup
    1802 * events).
    1803 * </li>
    1804 * </ul>
    1805 * <strong>Note:</strong> On browsers where native keyboard events are not yet
    1806 * supported (e.g. Firefox on OS X), key events will be synthesized. Special
    1807 * punctionation keys will be synthesized according to a standard QWERTY en-us
    1808 * keyboard layout.
    1809 *
    1810 * @param {...string} var_args The sequence of keys to
    1811 * type. All arguments will be joined into a single sequence (var_args is
    1812 * permitted for convenience).
    1813 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1814 * when all keys have been typed.
    1815 */
    1816webdriver.WebElement.prototype.sendKeys = function(var_args) {
    1817 // Coerce every argument to a string. This protects us from users that
    1818 // ignore the jsdoc and give us a number (which ends up causing problems on
    1819 // the server, which requires strings).
    1820 var keys = webdriver.promise.fullyResolved(goog.array.slice(arguments, 0)).
    1821 then(function(args) {
    1822 return goog.array.map(goog.array.slice(args, 0), function(key) {
    1823 return key + '';
    1824 });
    1825 });
    1826 return this.schedule_(
    1827 new webdriver.Command(webdriver.CommandName.SEND_KEYS_TO_ELEMENT).
    1828 setParameter('value', keys),
    1829 'WebElement.sendKeys(' + keys + ')');
    1830};
    1831
    1832
    1833/**
    1834 * Schedules a command to query for the tag/node name of this element.
    1835 * @return {!webdriver.promise.Promise.<string>} A promise that will be
    1836 * resolved with the element's tag name.
    1837 */
    1838webdriver.WebElement.prototype.getTagName = function() {
    1839 return this.schedule_(
    1840 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_TAG_NAME),
    1841 'WebElement.getTagName()');
    1842};
    1843
    1844
    1845/**
    1846 * Schedules a command to query for the computed style of the element
    1847 * represented by this instance. If the element inherits the named style from
    1848 * its parent, the parent will be queried for its value. Where possible, color
    1849 * values will be converted to their hex representation (e.g. #00ff00 instead of
    1850 * rgb(0, 255, 0)).
    1851 * <p/>
    1852 * <em>Warning:</em> the value returned will be as the browser interprets it, so
    1853 * it may be tricky to form a proper assertion.
    1854 *
    1855 * @param {string} cssStyleProperty The name of the CSS style property to look
    1856 * up.
    1857 * @return {!webdriver.promise.Promise.<string>} A promise that will be
    1858 * resolved with the requested CSS value.
    1859 */
    1860webdriver.WebElement.prototype.getCssValue = function(cssStyleProperty) {
    1861 var name = webdriver.CommandName.GET_ELEMENT_VALUE_OF_CSS_PROPERTY;
    1862 return this.schedule_(
    1863 new webdriver.Command(name).
    1864 setParameter('propertyName', cssStyleProperty),
    1865 'WebElement.getCssValue(' + cssStyleProperty + ')');
    1866};
    1867
    1868
    1869/**
    1870 * Schedules a command to query for the value of the given attribute of the
    1871 * element. Will return the current value, even if it has been modified after
    1872 * the page has been loaded. More exactly, this method will return the value of
    1873 * the given attribute, unless that attribute is not present, in which case the
    1874 * value of the property with the same name is returned. If neither value is
    1875 * set, null is returned (for example, the "value" property of a textarea
    1876 * element). The "style" attribute is converted as best can be to a
    1877 * text representation with a trailing semi-colon. The following are deemed to
    1878 * be "boolean" attributes and will return either "true" or null:
    1879 *
    1880 * <p>async, autofocus, autoplay, checked, compact, complete, controls, declare,
    1881 * defaultchecked, defaultselected, defer, disabled, draggable, ended,
    1882 * formnovalidate, hidden, indeterminate, iscontenteditable, ismap, itemscope,
    1883 * loop, multiple, muted, nohref, noresize, noshade, novalidate, nowrap, open,
    1884 * paused, pubdate, readonly, required, reversed, scoped, seamless, seeking,
    1885 * selected, spellcheck, truespeed, willvalidate
    1886 *
    1887 * <p>Finally, the following commonly mis-capitalized attribute/property names
    1888 * are evaluated as expected:
    1889 * <ul>
    1890 * <li>"class"
    1891 * <li>"readonly"
    1892 * </ul>
    1893 * @param {string} attributeName The name of the attribute to query.
    1894 * @return {!webdriver.promise.Promise.<?string>} A promise that will be
    1895 * resolved with the attribute's value. The returned value will always be
    1896 * either a string or null.
    1897 */
    1898webdriver.WebElement.prototype.getAttribute = function(attributeName) {
    1899 return this.schedule_(
    1900 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_ATTRIBUTE).
    1901 setParameter('name', attributeName),
    1902 'WebElement.getAttribute(' + attributeName + ')');
    1903};
    1904
    1905
    1906/**
    1907 * Get the visible (i.e. not hidden by CSS) innerText of this element, including
    1908 * sub-elements, without any leading or trailing whitespace.
    1909 * @return {!webdriver.promise.Promise.<string>} A promise that will be
    1910 * resolved with the element's visible text.
    1911 */
    1912webdriver.WebElement.prototype.getText = function() {
    1913 return this.schedule_(
    1914 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_TEXT),
    1915 'WebElement.getText()');
    1916};
    1917
    1918
    1919/**
    1920 * Schedules a command to compute the size of this element's bounding box, in
    1921 * pixels.
    1922 * @return {!webdriver.promise.Promise.<{width: number, height: number}>} A
    1923 * promise that will be resolved with the element's size as a
    1924 * {@code {width:number, height:number}} object.
    1925 */
    1926webdriver.WebElement.prototype.getSize = function() {
    1927 return this.schedule_(
    1928 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_SIZE),
    1929 'WebElement.getSize()');
    1930};
    1931
    1932
    1933/**
    1934 * Schedules a command to compute the location of this element in page space.
    1935 * @return {!webdriver.promise.Promise.<{x: number, y: number}>} A promise that
    1936 * will be resolved to the element's location as a
    1937 * {@code {x:number, y:number}} object.
    1938 */
    1939webdriver.WebElement.prototype.getLocation = function() {
    1940 return this.schedule_(
    1941 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_LOCATION),
    1942 'WebElement.getLocation()');
    1943};
    1944
    1945
    1946/**
    1947 * Schedules a command to query whether the DOM element represented by this
    1948 * instance is enabled, as dicted by the {@code disabled} attribute.
    1949 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be
    1950 * resolved with whether this element is currently enabled.
    1951 */
    1952webdriver.WebElement.prototype.isEnabled = function() {
    1953 return this.schedule_(
    1954 new webdriver.Command(webdriver.CommandName.IS_ELEMENT_ENABLED),
    1955 'WebElement.isEnabled()');
    1956};
    1957
    1958
    1959/**
    1960 * Schedules a command to query whether this element is selected.
    1961 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be
    1962 * resolved with whether this element is currently selected.
    1963 */
    1964webdriver.WebElement.prototype.isSelected = function() {
    1965 return this.schedule_(
    1966 new webdriver.Command(webdriver.CommandName.IS_ELEMENT_SELECTED),
    1967 'WebElement.isSelected()');
    1968};
    1969
    1970
    1971/**
    1972 * Schedules a command to submit the form containing this element (or this
    1973 * element if it is a FORM element). This command is a no-op if the element is
    1974 * not contained in a form.
    1975 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1976 * when the form has been submitted.
    1977 */
    1978webdriver.WebElement.prototype.submit = function() {
    1979 return this.schedule_(
    1980 new webdriver.Command(webdriver.CommandName.SUBMIT_ELEMENT),
    1981 'WebElement.submit()');
    1982};
    1983
    1984
    1985/**
    1986 * Schedules a command to clear the {@code value} of this element. This command
    1987 * has no effect if the underlying DOM element is neither a text INPUT element
    1988 * nor a TEXTAREA element.
    1989 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    1990 * when the element has been cleared.
    1991 */
    1992webdriver.WebElement.prototype.clear = function() {
    1993 return this.schedule_(
    1994 new webdriver.Command(webdriver.CommandName.CLEAR_ELEMENT),
    1995 'WebElement.clear()');
    1996};
    1997
    1998
    1999/**
    2000 * Schedules a command to test whether this element is currently displayed.
    2001 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be
    2002 * resolved with whether this element is currently visible on the page.
    2003 */
    2004webdriver.WebElement.prototype.isDisplayed = function() {
    2005 return this.schedule_(
    2006 new webdriver.Command(webdriver.CommandName.IS_ELEMENT_DISPLAYED),
    2007 'WebElement.isDisplayed()');
    2008};
    2009
    2010
    2011/**
    2012 * Schedules a command to retrieve the outer HTML of this element.
    2013 * @return {!webdriver.promise.Promise.<string>} A promise that will be
    2014 * resolved with the element's outer HTML.
    2015 */
    2016webdriver.WebElement.prototype.getOuterHtml = function() {
    2017 return this.driver_.executeScript(function() {
    2018 var element = arguments[0];
    2019 if ('outerHTML' in element) {
    2020 return element.outerHTML;
    2021 } else {
    2022 var div = element.ownerDocument.createElement('div');
    2023 div.appendChild(element.cloneNode(true));
    2024 return div.innerHTML;
    2025 }
    2026 }, this);
    2027};
    2028
    2029
    2030/**
    2031 * Schedules a command to retrieve the inner HTML of this element.
    2032 * @return {!webdriver.promise.Promise.<string>} A promise that will be
    2033 * resolved with the element's inner HTML.
    2034 */
    2035webdriver.WebElement.prototype.getInnerHtml = function() {
    2036 return this.driver_.executeScript('return arguments[0].innerHTML', this);
    2037};
    2038
    2039
    2040
    2041/**
    2042 * WebElementPromise is a promise that will be fulfilled with a WebElement.
    2043 * This serves as a forward proxy on WebElement, allowing calls to be
    2044 * scheduled without directly on this instance before the underlying
    2045 * WebElement has been fulfilled. In other words, the following two statements
    2046 * are equivalent:
    2047 * <pre><code>
    2048 * driver.findElement({id: 'my-button'}).click();
    2049 * driver.findElement({id: 'my-button'}).then(function(el) {
    2050 * return el.click();
    2051 * });
    2052 * </code></pre>
    2053 *
    2054 * @param {!webdriver.WebDriver} driver The parent WebDriver instance for this
    2055 * element.
    2056 * @param {!webdriver.promise.Promise.<!webdriver.WebElement>} el A promise
    2057 * that will resolve to the promised element.
    2058 * @constructor
    2059 * @extends {webdriver.WebElement}
    2060 * @implements {webdriver.promise.Thenable.<!webdriver.WebElement>}
    2061 * @final
    2062 */
    2063webdriver.WebElementPromise = function(driver, el) {
    2064 webdriver.WebElement.call(this, driver, {'ELEMENT': 'unused'});
    2065
    2066 /** @override */
    2067 this.cancel = goog.bind(el.cancel, el);
    2068
    2069 /** @override */
    2070 this.isPending = goog.bind(el.isPending, el);
    2071
    2072 /** @override */
    2073 this.then = goog.bind(el.then, el);
    2074
    2075 /** @override */
    2076 this.thenCatch = goog.bind(el.thenCatch, el);
    2077
    2078 /** @override */
    2079 this.thenFinally = goog.bind(el.thenFinally, el);
    2080
    2081 /**
    2082 * Defers returning the element ID until the wrapped WebElement has been
    2083 * resolved.
    2084 * @override
    2085 */
    2086 this.getId = function() {
    2087 return el.then(function(el) {
    2088 return el.getId();
    2089 });
    2090 };
    2091};
    2092goog.inherits(webdriver.WebElementPromise, webdriver.WebElement);
    2093
    2094
    2095/**
    2096 * Represents a modal dialog such as {@code alert}, {@code confirm}, or
    2097 * {@code prompt}. Provides functions to retrieve the message displayed with
    2098 * the alert, accept or dismiss the alert, and set the response text (in the
    2099 * case of {@code prompt}).
    2100 * @param {!webdriver.WebDriver} driver The driver controlling the browser this
    2101 * alert is attached to.
    2102 * @param {string} text The message text displayed with this alert.
    2103 * @constructor
    2104 */
    2105webdriver.Alert = function(driver, text) {
    2106 /** @private {!webdriver.WebDriver} */
    2107 this.driver_ = driver;
    2108
    2109 /** @private {!webdriver.promise.Promise.<string>} */
    2110 this.text_ = webdriver.promise.when(text);
    2111};
    2112
    2113
    2114/**
    2115 * Retrieves the message text displayed with this alert. For instance, if the
    2116 * alert were opened with alert("hello"), then this would return "hello".
    2117 * @return {!webdriver.promise.Promise.<string>} A promise that will be
    2118 * resolved to the text displayed with this alert.
    2119 */
    2120webdriver.Alert.prototype.getText = function() {
    2121 return this.text_;
    2122};
    2123
    2124
    2125/**
    2126 * Accepts this alert.
    2127 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    2128 * when this command has completed.
    2129 */
    2130webdriver.Alert.prototype.accept = function() {
    2131 return this.driver_.schedule(
    2132 new webdriver.Command(webdriver.CommandName.ACCEPT_ALERT),
    2133 'WebDriver.switchTo().alert().accept()');
    2134};
    2135
    2136
    2137/**
    2138 * Dismisses this alert.
    2139 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    2140 * when this command has completed.
    2141 */
    2142webdriver.Alert.prototype.dismiss = function() {
    2143 return this.driver_.schedule(
    2144 new webdriver.Command(webdriver.CommandName.DISMISS_ALERT),
    2145 'WebDriver.switchTo().alert().dismiss()');
    2146};
    2147
    2148
    2149/**
    2150 * Sets the response text on this alert. This command will return an error if
    2151 * the underlying alert does not support response text (e.g. window.alert and
    2152 * window.confirm).
    2153 * @param {string} text The text to set.
    2154 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
    2155 * when this command has completed.
    2156 */
    2157webdriver.Alert.prototype.sendKeys = function(text) {
    2158 return this.driver_.schedule(
    2159 new webdriver.Command(webdriver.CommandName.SET_ALERT_TEXT).
    2160 setParameter('text', text),
    2161 'WebDriver.switchTo().alert().sendKeys(' + text + ')');
    2162};
    2163
    2164
    2165
    2166/**
    2167 * AlertPromise is a promise that will be fulfilled with an Alert. This promise
    2168 * serves as a forward proxy on an Alert, allowing calls to be scheduled
    2169 * directly on this instance before the underlying Alert has been fulfilled. In
    2170 * other words, the following two statements are equivalent:
    2171 * <pre><code>
    2172 * driver.switchTo().alert().dismiss();
    2173 * driver.switchTo().alert().then(function(alert) {
    2174 * return alert.dismiss();
    2175 * });
    2176 * </code></pre>
    2177 *
    2178 * @param {!webdriver.WebDriver} driver The driver controlling the browser this
    2179 * alert is attached to.
    2180 * @param {!webdriver.promise.Thenable.<!webdriver.Alert>} alert A thenable
    2181 * that will be fulfilled with the promised alert.
    2182 * @constructor
    2183 * @extends {webdriver.Alert}
    2184 * @implements {webdriver.promise.Thenable.<!webdriver.Alert>}
    2185 * @final
    2186 */
    2187webdriver.AlertPromise = function(driver, alert) {
    2188 webdriver.Alert.call(this, driver, 'unused');
    2189
    2190 /** @override */
    2191 this.cancel = goog.bind(alert.cancel, alert);
    2192
    2193 /** @override */
    2194 this.isPending = goog.bind(alert.isPending, alert);
    2195
    2196 /** @override */
    2197 this.then = goog.bind(alert.then, alert);
    2198
    2199 /** @override */
    2200 this.thenCatch = goog.bind(alert.thenCatch, alert);
    2201
    2202 /** @override */
    2203 this.thenFinally = goog.bind(alert.thenFinally, alert);
    2204
    2205 /**
    2206 * Defer returning text until the promised alert has been resolved.
    2207 * @override
    2208 */
    2209 this.getText = function() {
    2210 return alert.then(function(alert) {
    2211 return alert.getText();
    2212 });
    2213 };
    2214
    2215 /**
    2216 * Defers action until the alert has been located.
    2217 * @override
    2218 */
    2219 this.accept = function() {
    2220 return alert.then(function(alert) {
    2221 return alert.accept();
    2222 });
    2223 };
    2224
    2225 /**
    2226 * Defers action until the alert has been located.
    2227 * @override
    2228 */
    2229 this.dismiss = function() {
    2230 return alert.then(function(alert) {
    2231 return alert.dismiss();
    2232 });
    2233 };
    2234
    2235 /**
    2236 * Defers action until the alert has been located.
    2237 * @override
    2238 */
    2239 this.sendKeys = function(text) {
    2240 return alert.then(function(alert) {
    2241 return alert.sendKeys(text);
    2242 });
    2243 };
    2244};
    2245goog.inherits(webdriver.AlertPromise, webdriver.Alert);
    2246
    2247
    2248
    2249/**
    2250 * An error returned to indicate that there is an unhandled modal dialog on the
    2251 * current page.
    2252 * @param {string} message The error message.
    2253 * @param {string} text The text displayed with the unhandled alert.
    2254 * @param {!webdriver.Alert} alert The alert handle.
    2255 * @constructor
    2256 * @extends {bot.Error}
    2257 */
    2258webdriver.UnhandledAlertError = function(message, text, alert) {
    2259 goog.base(this, bot.ErrorCode.UNEXPECTED_ALERT_OPEN, message);
    2260
    2261 /** @private {string} */
    2262 this.text_ = text;
    2263
    2264 /** @private {!webdriver.Alert} */
    2265 this.alert_ = alert;
    2266};
    2267goog.inherits(webdriver.UnhandledAlertError, bot.Error);
    2268
    2269
    2270/**
    2271 * @return {string} The text displayed with the unhandled alert.
    2272 */
    2273webdriver.UnhandledAlertError.prototype.getAlertText = function() {
    2274 return this.text_;
    2275};
    2276
    2277
    2278/**
    2279 * @return {!webdriver.Alert} The open alert.
    2280 * @deprecated Use {@link #getAlertText}. This method will be removed in
    2281 * 2.45.0.
    2282 */
    2283webdriver.UnhandledAlertError.prototype.getAlert = function() {
    2284 return this.alert_;
    2285};
    \ 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 db63c9a..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 var lo = getLoInterface();
    45 interfaces = lo ? [lo] : null;
    46 }
    47 interfaces = interfaces || os.networkInterfaces();
    48 for (var key in interfaces) {
    49 interfaces[key].forEach(function(ipAddress) {
    50 if (ipAddress.family === family &&
    51 ipAddress.internal === loopback) {
    52 addresses.push(ipAddress.address);
    53 }
    54 });
    55 }
    56 return addresses[0];
    57}
    58
    59
    60// PUBLIC API
    61
    62
    63/**
    64 * Retrieves the external IP address for this host.
    65 * @param {string=} opt_family The IP family to retrieve. Defaults to "IPv4".
    66 * @return {string} The IP address or undefined if not available.
    67 */
    68exports.getAddress = function(opt_family) {
    69 return getAddress(false, opt_family);
    70};
    71
    72
    73/**
    74 * Retrieves a loopback address for this machine.
    75 * @param {string=} opt_family The IP family to retrieve. Defaults to "IPv4".
    76 * @return {string} The IP address or undefined if not available.
    77 */
    78exports.getLoopbackAddress = function(opt_family) {
    79 return getAddress(true, opt_family);
    80};
    \ 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 c4667da..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 4fbff7a..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 executors = require('./executors'),
    23 io = require('./io'),
    24 portprober = require('./net/portprober'),
    25 remote = require('./remote');
    26
    27
    28/**
    29 * Name of the PhantomJS executable.
    30 * @type {string}
    31 * @const
    32 */
    33var PHANTOMJS_EXE =
    34 process.platform === 'win32' ? 'phantomjs.exe' : 'phantomjs';
    35
    36
    37/**
    38 * Capability that designates the location of the PhantomJS executable to use.
    39 * @type {string}
    40 * @const
    41 */
    42var BINARY_PATH_CAPABILITY = 'phantomjs.binary.path';
    43
    44
    45/**
    46 * Capability that designates the CLI arguments to pass to PhantomJS.
    47 * @type {string}
    48 * @const
    49 */
    50var CLI_ARGS_CAPABILITY = 'phantomjs.cli.args';
    51
    52
    53/**
    54 * Default log file to use if one is not specified through CLI args.
    55 * @type {string}
    56 * @const
    57 */
    58var DEFAULT_LOG_FILE = 'phantomjsdriver.log';
    59
    60
    61/**
    62 * Finds the PhantomJS executable.
    63 * @param {string=} opt_exe Path to the executable to use.
    64 * @return {string} The located executable.
    65 * @throws {Error} If the executable cannot be found on the PATH, or if the
    66 * provided executable path does not exist.
    67 */
    68function findExecutable(opt_exe) {
    69 var exe = opt_exe || io.findInPath(PHANTOMJS_EXE, true);
    70 if (!exe) {
    71 throw Error(
    72 'The PhantomJS executable could not be found on the current PATH. ' +
    73 'Please download the latest version from ' +
    74 'http://phantomjs.org/download.html and ensure it can be found on ' +
    75 'your PATH. For more information, see ' +
    76 'https://github.com/ariya/phantomjs/wiki');
    77 }
    78 if (!fs.existsSync(exe)) {
    79 throw Error('File does not exist: ' + exe);
    80 }
    81 return exe;
    82}
    83
    84
    85/**
    86 * Maps WebDriver logging level name to those recognised by PhantomJS.
    87 * @type {!Object.<string, string>}
    88 * @const
    89 */
    90var WEBDRIVER_TO_PHANTOMJS_LEVEL = (function() {
    91 var map = {};
    92 map[webdriver.logging.Level.ALL.name] = 'DEBUG';
    93 map[webdriver.logging.Level.DEBUG.name] = 'DEBUG';
    94 map[webdriver.logging.Level.INFO.name] = 'INFO';
    95 map[webdriver.logging.Level.WARNING.name] = 'WARN';
    96 map[webdriver.logging.Level.SEVERE.name] = '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 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or
    105 * {@code null} to use the currently active flow.
    106 * @return {!webdriver.WebDriver} A new WebDriver instance.
    107 * @deprecated Use {@link Driver}.
    108 */
    109function createDriver(opt_capabilities, opt_flow) {
    110 return new Driver(opt_capabilities, opt_flow);
    111}
    112
    113
    114/**
    115 * Creates a new WebDriver client for PhantomJS.
    116 *
    117 * @param {webdriver.Capabilities=} opt_capabilities The desired capabilities.
    118 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or
    119 * {@code null} to use the currently active flow.
    120 * @constructor
    121 * @extends {webdriver.WebDriver}
    122 */
    123var Driver = function(opt_capabilities, opt_flow) {
    124 var capabilities = opt_capabilities || webdriver.Capabilities.phantomjs();
    125 var exe = findExecutable(capabilities.get(BINARY_PATH_CAPABILITY));
    126 var args = ['--webdriver-logfile=' + DEFAULT_LOG_FILE];
    127
    128 var logPrefs = capabilities.get(webdriver.Capability.LOGGING_PREFS);
    129 if (logPrefs instanceof webdriver.logging.Preferences) {
    130 logPrefs = logPrefs.toJSON();
    131 }
    132
    133 if (logPrefs && logPrefs[webdriver.logging.Type.DRIVER]) {
    134 var level = WEBDRIVER_TO_PHANTOMJS_LEVEL[
    135 logPrefs[webdriver.logging.Type.DRIVER]];
    136 if (level) {
    137 args.push('--webdriver-loglevel=' + level);
    138 }
    139 }
    140
    141 var proxy = capabilities.get(webdriver.Capability.PROXY);
    142 if (proxy) {
    143 switch (proxy.proxyType) {
    144 case 'manual':
    145 if (proxy.httpProxy) {
    146 args.push(
    147 '--proxy-type=http',
    148 '--proxy=http://' + proxy.httpProxy);
    149 }
    150 break;
    151 case 'pac':
    152 throw Error('PhantomJS does not support Proxy PAC files');
    153 case 'system':
    154 args.push('--proxy-type=system');
    155 break;
    156 case 'direct':
    157 args.push('--proxy-type=none');
    158 break;
    159 }
    160 }
    161 args = args.concat(capabilities.get(CLI_ARGS_CAPABILITY) || []);
    162
    163 var port = portprober.findFreePort();
    164 var service = new remote.DriverService(exe, {
    165 port: port,
    166 args: webdriver.promise.when(port, function(port) {
    167 args.push('--webdriver=' + port);
    168 return args;
    169 })
    170 });
    171
    172 var executor = executors.createExecutor(service.start());
    173 var driver = webdriver.WebDriver.createSession(
    174 executor, capabilities, opt_flow);
    175
    176 webdriver.WebDriver.call(
    177 this, driver.getSession(), executor, driver.controlFlow());
    178
    179 var boundQuit = this.quit.bind(this);
    180
    181 /** @override */
    182 this.quit = function() {
    183 return boundQuit().thenFinally(service.kill.bind(service));
    184 };
    185 return driver;
    186};
    187util.inherits(Driver, webdriver.WebDriver);
    188
    189
    190// PUBLIC API
    191
    192exports.Driver = Driver;
    193exports.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 90edf0d..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// PUBLIC API
    35
    36
    37/**
    38 * Configures WebDriver to bypass all browser proxies.
    39 * @return {!webdriver.ProxyConfig} A new proxy configuration object.
    40 */
    41exports.direct = function() {
    42 return {proxyType: 'direct'};
    43};
    44
    45
    46/**
    47 * Manually configures the browser proxy. The following options are
    48 * supported:
    49 * <ul>
    50 * <li>{@code ftp}: Proxy host to use for FTP requests
    51 * <li>{@code http}: Proxy host to use for HTTP requests
    52 * <li>{@code https}: Proxy host to use for HTTPS requests
    53 * <li>{@code bypass}: A list of hosts requests should directly connect to,
    54 * bypassing any other proxies for that request. May be specified as a
    55 * comma separated string, or a list of strings.
    56 * </ul>
    57 *
    58 * Behavior is undefined for FTP, HTTP, and HTTPS requests if the
    59 * corresponding key is omitted from the configuration options.
    60 *
    61 * @param {{ftp: (string|undefined),
    62 * http: (string|undefined),
    63 * https: (string|undefined),
    64 * bypass: (string|!Array.<string>|undefined)}} options Proxy
    65 * configuration options.
    66 * @return {!webdriver.ProxyConfig} A new proxy configuration object.
    67 */
    68exports.manual = function(options) {
    69 return {
    70 proxyType: 'manual',
    71 ftpProxy: options.ftp,
    72 httpProxy: options.http,
    73 sslProxy: options.https,
    74 noProxy: util.isArray(options.bypass) ?
    75 options.bypass.join(',') : options.bypass
    76 };
    77};
    78
    79
    80/**
    81 * Configures WebDriver to configure the browser proxy using the PAC file at
    82 * the given URL.
    83 * @param {string} url URL for the PAC proxy to use.
    84 * @return {!webdriver.ProxyConfig} A new proxy configuration object.
    85 */
    86exports.pac = function(url) {
    87 return {
    88 proxyType: 'pac',
    89 proxyAutoconfigUrl: url
    90 };
    91};
    92
    93
    94/**
    95 * Configures WebDriver to use the current system's proxy.
    96 * @return {!webdriver.ProxyConfig} A new proxy configuration object.
    97 */
    98exports.system = function() {
    99 return {proxyType: 'system'};
    100};
    \ 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 870271a..dabee06 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 path = require('path'),
    18 url = require('url'),
    19 util = require('util');
    20
    21var promise = require('../').promise,
    22 httpUtil = require('../http/util'),
    23 exec = require('../io/exec'),
    24 net = require('../net'),
    25 portprober = require('../net/portprober');
    26
    27
    28
    29/**
    30 * Configuration options for a DriverService instance.
    31 * <ul>
    32 * <li>
    33 * <li>{@code loopback} - Whether the service should only be accessed on this
    34 * host's loopback address.
    35 * <li>{@code port} - The port to start the server on (must be > 0). If the
    36 * port is provided as a promise, the service will wait for the promise to
    37 * resolve before starting.
    38 * <li>{@code args} - The arguments to pass to the service. If a promise is
    39 * provided, the service will wait for it to resolve before starting.
    40 * <li>{@code path} - The base path on the server for the WebDriver wire
    41 * protocol (e.g. '/wd/hub'). Defaults to '/'.
    42 * <li>{@code env} - The environment variables that should be visible to the
    43 * server process. Defaults to inheriting the current process's
    44 * environment.
    45 * <li>{@code stdio} - IO configuration for the spawned server process. For
    46 * more information, refer to the documentation of
    47 * {@code child_process.spawn}.
    48 * </ul>
    49 *
    50 * @typedef {{
    51 * port: (number|!webdriver.promise.Promise.<number>),
    52 * args: !(Array.<string>|webdriver.promise.Promise.<!Array.<string>>),
    53 * path: (string|undefined),
    54 * env: (!Object.<string, string>|undefined),
    55 * stdio: (string|!Array.<string|number|!Stream|null|undefined>|undefined)
    56 * }}
    57 */
    58var ServiceOptions;
    59
    60
    61/**
    62 * Manages the life and death of a native executable WebDriver server.
    63 *
    64 * <p>It is expected that the driver server implements the
    65 * <a href="http://code.google.com/p/selenium/wiki/JsonWireProtocol">WebDriver
    66 * Wire Protocol</a>. Furthermore, the managed server should support multiple
    67 * concurrent sessions, so that this class may be reused for multiple clients.
    68 *
    69 * @param {string} executable Path to the executable to run.
    70 * @param {!ServiceOptions} options Configuration options for the service.
    71 * @constructor
    72 */
    73function DriverService(executable, options) {
    74
    75 /** @private {string} */
    76 this.executable_ = executable;
    77
    78 /** @private {boolean} */
    79 this.loopbackOnly_ = !!options.loopback;
    80
    81 /** @private {(number|!webdriver.promise.Promise.<number>)} */
    82 this.port_ = options.port;
    83
    84 /**
    85 * @private {!(Array.<string>|webdriver.promise.Promise.<!Array.<string>>)}
    86 */
    87 this.args_ = options.args;
    88
    89 /** @private {string} */
    90 this.path_ = options.path || '/';
    91
    92 /** @private {!Object.<string, string>} */
    93 this.env_ = options.env || process.env;
    94
    95 /** @private {(string|!Array.<string|number|!Stream|null|undefined>)} */
    96 this.stdio_ = options.stdio || 'ignore';
    97
    98 /**
    99 * A promise for the managed subprocess, or null if the server has not been
    100 * started yet. This promise will never be rejected.
    101 * @private {promise.Promise.<!exec.Command>}
    102 */
    103 this.command_ = null;
    104
    105 /**
    106 * Promise that resolves to the server's address or null if the server has
    107 * not been started. This promise will be rejected if the server terminates
    108 * before it starts accepting WebDriver requests.
    109 * @private {promise.Promise.<string>}
    110 */
    111 this.address_ = null;
    112}
    113
    114
    115/**
    116 * The default amount of time, in milliseconds, to wait for the server to
    117 * start.
    118 * @type {number}
    119 */
    120DriverService.DEFAULT_START_TIMEOUT_MS = 30 * 1000;
    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 * Returns whether the underlying process is still running. This does not take
    138 * into account whether the process is in the process of shutting down.
    139 * @return {boolean} Whether the underlying service process is running.
    140 */
    141DriverService.prototype.isRunning = function() {
    142 return !!this.address_;
    143};
    144
    145
    146/**
    147 * Starts the server if it is not already running.
    148 * @param {number=} opt_timeoutMs How long to wait, in milliseconds, for the
    149 * server to start accepting requests. Defaults to 30 seconds.
    150 * @return {!promise.Promise.<string>} A promise that will resolve
    151 * to the server's base URL when it has started accepting requests. If the
    152 * timeout expires before the server has started, the promise will be
    153 * rejected.
    154 */
    155DriverService.prototype.start = function(opt_timeoutMs) {
    156 if (this.address_) {
    157 return this.address_;
    158 }
    159
    160 var timeout = opt_timeoutMs || DriverService.DEFAULT_START_TIMEOUT_MS;
    161
    162 var self = this;
    163 this.command_ = promise.defer();
    164 this.address_ = promise.defer();
    165 this.address_.fulfill(promise.when(this.port_, function(port) {
    166 if (port <= 0) {
    167 throw Error('Port must be > 0: ' + port);
    168 }
    169 return promise.when(self.args_, function(args) {
    170 var command = exec(self.executable_, {
    171 args: args,
    172 env: self.env_,
    173 stdio: self.stdio_
    174 });
    175
    176 self.command_.fulfill(command);
    177
    178 command.result().then(function(result) {
    179 self.address_.reject(result.code == null ?
    180 Error('Server was killed with ' + result.signal) :
    181 Error('Server exited with ' + result.code));
    182 self.address_ = null;
    183 self.command_ = null;
    184 });
    185
    186 var serverUrl = url.format({
    187 protocol: 'http',
    188 hostname: !self.loopbackOnly_ && net.getAddress() ||
    189 net.getLoopbackAddress(),
    190 port: port,
    191 pathname: self.path_
    192 });
    193
    194 return httpUtil.waitForServer(serverUrl, timeout).then(function() {
    195 return serverUrl;
    196 });
    197 });
    198 }));
    199
    200 return this.address_;
    201};
    202
    203
    204/**
    205 * Stops the service if it is not currently running. This function will kill
    206 * the server immediately. To synchronize with the active control flow, use
    207 * {@link #stop()}.
    208 * @return {!webdriver.promise.Promise} A promise that will be resolved when
    209 * the server has been stopped.
    210 */
    211DriverService.prototype.kill = function() {
    212 if (!this.address_ || !this.command_) {
    213 return promise.fulfilled(); // Not currently running.
    214 }
    215 return this.command_.then(function(command) {
    216 command.kill('SIGTERM');
    217 });
    218};
    219
    220
    221/**
    222 * Schedules a task in the current control flow to stop the server if it is
    223 * currently running.
    224 * @return {!webdriver.promise.Promise} A promise that will be resolved when
    225 * the server has been stopped.
    226 */
    227DriverService.prototype.stop = function() {
    228 return promise.controlFlow().execute(this.kill.bind(this));
    229};
    230
    231
    232
    233/**
    234 * Manages the life and death of the Selenium standalone server. The server
    235 * may be obtained from http://selenium-release.storage.googleapis.com/index.html.
    236 * @param {string} jar Path to the Selenium server jar.
    237 * @param {!SeleniumServer.Options} options Configuration options for the
    238 * server.
    239 * @throws {Error} If an invalid port is specified.
    240 * @constructor
    241 * @extends {DriverService}
    242 */
    243function SeleniumServer(jar, options) {
    244 if (options.port < 0)
    245 throw Error('Port must be >= 0: ' + options.port);
    246
    247 var port = options.port || portprober.findFreePort();
    248 var args = promise.when(options.jvmArgs || [], function(jvmArgs) {
    249 return promise.when(options.args || [], function(args) {
    250 return promise.when(port, function(port) {
    251 return jvmArgs.concat(['-jar', jar, '-port', port]).concat(args);
    252 });
    253 });
    254 });
    255
    256 DriverService.call(this, 'java', {
    257 port: port,
    258 args: args,
    259 path: '/wd/hub',
    260 env: options.env,
    261 stdio: options.stdio
    262 });
    263}
    264util.inherits(SeleniumServer, DriverService);
    265
    266
    267/**
    268 * Options for the Selenium server:
    269 * <ul>
    270 * <li>{@code port} - The port to start the server on (must be > 0). If the
    271 * port is provided as a promise, the service will wait for the promise to
    272 * resolve before starting.
    273 * <li>{@code args} - The arguments to pass to the service. If a promise is
    274 * provided, the service will wait for it to resolve before starting.
    275 * <li>{@code jvmArgs} - The arguments to pass to the JVM. If a promise is
    276 * provided, the service will wait for it to resolve before starting.
    277 * <li>{@code env} - The environment variables that should be visible to the
    278 * server process. Defaults to inheriting the current process's
    279 * environment.
    280 * <li>{@code stdio} - IO configuration for the spawned server process. For
    281 * more information, refer to the documentation of
    282 * {@code child_process.spawn}.
    283 * </ul>
    284 *
    285 * @typedef {{
    286 * port: (number|!webdriver.promise.Promise.<number>),
    287 * args: !(Array.<string>|webdriver.promise.Promise.<!Array.<string>>),
    288 * jvmArgs: (!Array.<string>|
    289 * !webdriver.promise.Promise.<!Array.<string>>|
    290 * undefined),
    291 * env: (!Object.<string, string>|undefined),
    292 * stdio: (string|!Array.<string|number|!Stream|null|undefined>|undefined)
    293 * }}
    294 */
    295SeleniumServer.Options;
    296
    297
    298// PUBLIC API
    299
    300exports.DriverService = DriverService;
    301exports.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 fs = require('fs'),
    22 path = require('path'),
    23 url = require('url'),
    24 util = require('util');
    25
    26var _base = require('../_base'),
    27 webdriver = require('../'),
    28 promise = require('../').promise,
    29 httpUtil = require('../http/util'),
    30 exec = require('../io/exec'),
    31 net = require('../net'),
    32 portprober = require('../net/portprober');
    33
    34
    35
    36/**
    37 * Configuration options for a DriverService instance.
    38 *
    39 * - `loopback` - Whether the service should only be accessed on this host's
    40 * loopback address.
    41 * - `port` - The port to start the server on (must be > 0). If the port is
    42 * provided as a promise, the service will wait for the promise to resolve
    43 * before starting.
    44 * - `args` - The arguments to pass to the service. If a promise is provided,
    45 * the service will wait for it to resolve before starting.
    46 * - `path` - The base path on the server for the WebDriver wire protocol
    47 * (e.g. '/wd/hub'). Defaults to '/'.
    48 * - `env` - The environment variables that should be visible to the server
    49 * process. Defaults to inheriting the current process's environment.
    50 * - `stdio` - IO configuration for the spawned server process. For more
    51 * information, refer to the documentation of `child_process.spawn`.
    52 *
    53 * @typedef {{
    54 * port: (number|!webdriver.promise.Promise.<number>),
    55 * args: !(Array.<string>|webdriver.promise.Promise.<!Array.<string>>),
    56 * path: (string|undefined),
    57 * env: (!Object.<string, string>|undefined),
    58 * stdio: (string|!Array.<string|number|!Stream|null|undefined>|undefined)
    59 * }}
    60 */
    61var ServiceOptions;
    62
    63
    64/**
    65 * Manages the life and death of a native executable WebDriver server.
    66 *
    67 * It is expected that the driver server implements the
    68 * https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol.
    69 * Furthermore, the managed server should support multiple concurrent sessions,
    70 * so that this class may be reused for multiple clients.
    71 *
    72 * @param {string} executable Path to the executable to run.
    73 * @param {!ServiceOptions} options Configuration options for the service.
    74 * @constructor
    75 */
    76function DriverService(executable, options) {
    77
    78 /** @private {string} */
    79 this.executable_ = executable;
    80
    81 /** @private {boolean} */
    82 this.loopbackOnly_ = !!options.loopback;
    83
    84 /** @private {(number|!webdriver.promise.Promise.<number>)} */
    85 this.port_ = options.port;
    86
    87 /**
    88 * @private {!(Array.<string>|webdriver.promise.Promise.<!Array.<string>>)}
    89 */
    90 this.args_ = options.args;
    91
    92 /** @private {string} */
    93 this.path_ = options.path || '/';
    94
    95 /** @private {!Object.<string, string>} */
    96 this.env_ = options.env || process.env;
    97
    98 /** @private {(string|!Array.<string|number|!Stream|null|undefined>)} */
    99 this.stdio_ = options.stdio || 'ignore';
    100
    101 /**
    102 * A promise for the managed subprocess, or null if the server has not been
    103 * started yet. This promise will never be rejected.
    104 * @private {promise.Promise.<!exec.Command>}
    105 */
    106 this.command_ = null;
    107
    108 /**
    109 * Promise that resolves to the server's address or null if the server has
    110 * not been started. This promise will be rejected if the server terminates
    111 * before it starts accepting WebDriver requests.
    112 * @private {promise.Promise.<string>}
    113 */
    114 this.address_ = null;
    115}
    116
    117
    118/**
    119 * The default amount of time, in milliseconds, to wait for the server to
    120 * start.
    121 * @type {number}
    122 */
    123DriverService.DEFAULT_START_TIMEOUT_MS = 30 * 1000;
    124
    125
    126/**
    127 * @return {!webdriver.promise.Promise.<string>} A promise that resolves to
    128 * the server's address.
    129 * @throws {Error} If the server has not been started.
    130 */
    131DriverService.prototype.address = function() {
    132 if (this.address_) {
    133 return this.address_;
    134 }
    135 throw Error('Server has not been started.');
    136};
    137
    138
    139/**
    140 * Returns whether the underlying process is still running. This does not take
    141 * into account whether the process is in the process of shutting down.
    142 * @return {boolean} Whether the underlying service process is running.
    143 */
    144DriverService.prototype.isRunning = function() {
    145 return !!this.address_;
    146};
    147
    148
    149/**
    150 * Starts the server if it is not already running.
    151 * @param {number=} opt_timeoutMs How long to wait, in milliseconds, for the
    152 * server to start accepting requests. Defaults to 30 seconds.
    153 * @return {!promise.Promise.<string>} A promise that will resolve
    154 * to the server's base URL when it has started accepting requests. If the
    155 * timeout expires before the server has started, the promise will be
    156 * rejected.
    157 */
    158DriverService.prototype.start = function(opt_timeoutMs) {
    159 if (this.address_) {
    160 return this.address_;
    161 }
    162
    163 var timeout = opt_timeoutMs || DriverService.DEFAULT_START_TIMEOUT_MS;
    164
    165 var self = this;
    166 this.command_ = promise.defer();
    167 this.address_ = promise.defer();
    168 this.address_.fulfill(promise.when(this.port_, function(port) {
    169 if (port <= 0) {
    170 throw Error('Port must be > 0: ' + port);
    171 }
    172 return promise.when(self.args_, function(args) {
    173 var command = exec(self.executable_, {
    174 args: args,
    175 env: self.env_,
    176 stdio: self.stdio_
    177 });
    178
    179 self.command_.fulfill(command);
    180
    181 var earlyTermination = command.result().then(function(result) {
    182 var error = result.code == null ?
    183 Error('Server was killed with ' + result.signal) :
    184 Error('Server terminated early with status ' + result.code);
    185 self.address_.reject(error);
    186 self.address_ = null;
    187 self.command_ = null;
    188 throw error;
    189 });
    190
    191 var serverUrl = url.format({
    192 protocol: 'http',
    193 hostname: !self.loopbackOnly_ && net.getAddress() ||
    194 net.getLoopbackAddress(),
    195 port: port,
    196 pathname: self.path_
    197 });
    198
    199 return new promise.Promise(function(fulfill, reject) {
    200 var ready = httpUtil.waitForServer(serverUrl, timeout)
    201 .then(fulfill, reject);
    202 earlyTermination.thenCatch(function(e) {
    203 ready.cancel(e);
    204 reject(Error(e.message));
    205 });
    206 }).then(function() {
    207 return serverUrl;
    208 });
    209 });
    210 }));
    211
    212 return this.address_;
    213};
    214
    215
    216/**
    217 * Stops the service if it is not currently running. This function will kill
    218 * the server immediately. To synchronize with the active control flow, use
    219 * {@link #stop()}.
    220 * @return {!webdriver.promise.Promise} A promise that will be resolved when
    221 * the server has been stopped.
    222 */
    223DriverService.prototype.kill = function() {
    224 if (!this.address_ || !this.command_) {
    225 return promise.fulfilled(); // Not currently running.
    226 }
    227 return this.command_.then(function(command) {
    228 command.kill('SIGTERM');
    229 });
    230};
    231
    232
    233/**
    234 * Schedules a task in the current control flow to stop the server if it is
    235 * currently running.
    236 * @return {!webdriver.promise.Promise} A promise that will be resolved when
    237 * the server has been stopped.
    238 */
    239DriverService.prototype.stop = function() {
    240 return promise.controlFlow().execute(this.kill.bind(this));
    241};
    242
    243
    244
    245/**
    246 * Manages the life and death of the
    247 * <a href="http://selenium-release.storage.googleapis.com/index.html">
    248 * standalone Selenium server</a>.
    249 *
    250 * @param {string} jar Path to the Selenium server jar.
    251 * @param {SeleniumServer.Options=} opt_options Configuration options for the
    252 * server.
    253 * @throws {Error} If the path to the Selenium jar is not specified or if an
    254 * invalid port is specified.
    255 * @constructor
    256 * @extends {DriverService}
    257 */
    258function SeleniumServer(jar, opt_options) {
    259 if (!jar) {
    260 throw Error('Path to the Selenium jar not specified');
    261 }
    262
    263 var options = opt_options || {};
    264
    265 if (options.port < 0) {
    266 throw Error('Port must be >= 0: ' + options.port);
    267 }
    268
    269 var port = options.port || portprober.findFreePort();
    270 var args = promise.when(options.jvmArgs || [], function(jvmArgs) {
    271 return promise.when(options.args || [], function(args) {
    272 return promise.when(port, function(port) {
    273 return jvmArgs.concat(['-jar', jar, '-port', port]).concat(args);
    274 });
    275 });
    276 });
    277
    278 DriverService.call(this, 'java', {
    279 port: port,
    280 args: args,
    281 path: '/wd/hub',
    282 env: options.env,
    283 stdio: options.stdio
    284 });
    285}
    286util.inherits(SeleniumServer, DriverService);
    287
    288
    289/**
    290 * Options for the Selenium server:
    291 *
    292 * - `port` - The port to start the server on (must be > 0). If the port is
    293 * provided as a promise, the service will wait for the promise to resolve
    294 * before starting.
    295 * - `args` - The arguments to pass to the service. If a promise is provided,
    296 * the service will wait for it to resolve before starting.
    297 * - `jvmArgs` - The arguments to pass to the JVM. If a promise is provided,
    298 * the service will wait for it to resolve before starting.
    299 * - `env` - The environment variables that should be visible to the server
    300 * process. Defaults to inheriting the current process's environment.
    301 * - `stdio` - IO configuration for the spawned server process. For more
    302 * information, refer to the documentation of `child_process.spawn`.
    303 *
    304 * @typedef {{
    305 * port: (number|!webdriver.promise.Promise.<number>),
    306 * args: !(Array.<string>|webdriver.promise.Promise.<!Array.<string>>),
    307 * jvmArgs: (!Array.<string>|
    308 * !webdriver.promise.Promise.<!Array.<string>>|
    309 * undefined),
    310 * env: (!Object.<string, string>|undefined),
    311 * stdio: (string|!Array.<string|number|!Stream|null|undefined>|undefined)
    312 * }}
    313 */
    314SeleniumServer.Options;
    315
    316
    317
    318/**
    319 * A {@link webdriver.FileDetector} that may be used when running
    320 * against a remote
    321 * [Selenium server](http://selenium-release.storage.googleapis.com/index.html).
    322 *
    323 * When a file path on the local machine running this script is entered with
    324 * {@link webdriver.WebElement#sendKeys WebElement#sendKeys}, this file detector
    325 * will transfer the specified file to the Selenium server's host; the sendKeys
    326 * command will be updated to use the transfered file's path.
    327 *
    328 * __Note:__ This class depends on a non-standard command supported on the
    329 * Java Selenium server. The file detector will fail if used with a server that
    330 * only supports standard WebDriver commands (such as the ChromeDriver).
    331 *
    332 * @constructor
    333 * @extends {webdriver.FileDetector}
    334 * @final
    335 */
    336var FileDetector = function() {};
    337util.inherits(webdriver.FileDetector, FileDetector);
    338
    339
    340/** @override */
    341FileDetector.prototype.handleFile = function(driver, filePath) {
    342 return promise.checkedNodeCall(fs.stat, filePath).then(function(stats) {
    343 if (stats.isDirectory()) {
    344 throw TypeError('Uploading directories is not supported: ' + filePath);
    345 }
    346
    347 var zip = new AdmZip();
    348 zip.addLocalFile(filePath);
    349
    350 var command = new webdriver.Command(webdriver.CommandName.UPLOAD_FILE)
    351 .setParameter('file', zip.toBuffer().toString('base64'));
    352 return driver.schedule(command,
    353 'remote.FileDetector.handleFile(' + filePath + ')');
    354 }, function(err) {
    355 if (err.code === 'ENOENT') {
    356 return filePath; // Not a file; return original input.
    357 }
    358 throw err;
    359 });
    360};
    361
    362// PUBLIC API
    363
    364exports.DriverService = DriverService;
    365exports.FileDetector = FileDetector;
    366exports.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 9869371..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 328152b..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 {@link webdriver.promise.ControlFlow}
    31 * to simplify writing asynchronous tests:
    32 * <pre><code>
    33 * var webdriver = require('selenium-webdriver'),
    34 * portprober = require('selenium-webdriver/net/portprober'),
    35 * remote = require('selenium-webdriver/remote'),
    36 * test = require('selenium-webdriver/testing');
    37 *
    38 * test.describe('Google Search', function() {
    39 * var driver, server;
    40 *
    41 * test.before(function() {
    42 * server = new remote.SeleniumServer(
    43 * 'path/to/selenium-server-standalone.jar',
    44 * {port: portprober.findFreePort()});
    45 * server.start();
    46 *
    47 * driver = new webdriver.Builder().
    48 * withCapabilities({'browserName': 'firefox'}).
    49 * usingServer(server.address()).
    50 * build();
    51 * });
    52 *
    53 * test.after(function() {
    54 * driver.quit();
    55 * server.stop();
    56 * });
    57 *
    58 * test.it('should append query to title', function() {
    59 * driver.get('http://www.google.com');
    60 * driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');
    61 * driver.findElement(webdriver.By.name('btnG')).click();
    62 * driver.wait(function() {
    63 * return driver.getTitle().then(function(title) {
    64 * return 'webdriver - Google Search' === title;
    65 * });
    66 * }, 1000, 'Waiting for title to update');
    67 * });
    68 * });
    69 * </code></pre>
    70 *
    71 * <p>You may conditionally suppress a test function using the exported
    72 * "ignore" function. If the provided predicate returns true, the attached
    73 * test case will be skipped:
    74 * <pre><code>
    75 * test.ignore(maybe()).it('is flaky', function() {
    76 * if (Math.random() < 0.5) throw Error();
    77 * });
    78 *
    79 * function maybe() { return Math.random() < 0.5; }
    80 * </code></pre>
    81 */
    82
    83var promise = require('..').promise;
    84var flow = promise.controlFlow();
    85
    86
    87/**
    88 * Wraps a function so that all passed arguments are ignored.
    89 * @param {!Function} fn The function to wrap.
    90 * @return {!Function} The wrapped function.
    91 */
    92function seal(fn) {
    93 return function() {
    94 fn();
    95 };
    96}
    97
    98
    99/**
    100 * Wraps a function on Mocha's BDD interface so it runs inside a
    101 * webdriver.promise.ControlFlow and waits for the flow to complete before
    102 * continuing.
    103 * @param {!Function} globalFn The function to wrap.
    104 * @return {!Function} The new function.
    105 */
    106function wrapped(globalFn) {
    107 return function() {
    108 if (arguments.length === 1) {
    109 return globalFn(asyncTestFn(arguments[0]));
    110 }
    111 else if (arguments.length === 2) {
    112 return globalFn(arguments[0], asyncTestFn(arguments[1]));
    113 }
    114 else {
    115 throw Error('Invalid # arguments: ' + arguments.length);
    116 }
    117 };
    118
    119 function asyncTestFn(fn) {
    120 var ret = function(done) {
    121 function cleanupBeforeCallback() {
    122 flow.reset();
    123 return cleanupBeforeCallback.mochaCallback.apply(this, arguments);
    124 }
    125 // We set this as an attribute of the callback function to allow us to
    126 // test this properly.
    127 cleanupBeforeCallback.mochaCallback = this.runnable().callback;
    128
    129 this.runnable().callback = cleanupBeforeCallback;
    130
    131 var testFn = fn.bind(this);
    132 flow.execute(function() {
    133 var done = promise.defer();
    134 promise.asap(testFn(done.reject), done.fulfill, done.reject);
    135 return done.promise;
    136 }).then(seal(done), done);
    137 };
    138
    139 ret.toString = function() {
    140 return fn.toString();
    141 };
    142
    143 return ret;
    144 }
    145}
    146
    147
    148/**
    149 * Ignores the test chained to this function if the provided predicate returns
    150 * true.
    151 * @param {function(): boolean} predicateFn A predicate to call to determine
    152 * if the test should be suppressed. This function MUST be synchronous.
    153 * @return {!Object} An object with wrapped versions of {@link #it()} and
    154 * {@link #describe()} that ignore tests as indicated by the predicate.
    155 */
    156function ignore(predicateFn) {
    157 var describe = wrap(exports.xdescribe, exports.describe);
    158 describe.only = wrap(exports.xdescribe, exports.describe.only);
    159
    160 var it = wrap(exports.xit, exports.it);
    161 it.only = wrap(exports.xit, exports.it.only);
    162
    163 return {
    164 describe: describe,
    165 it: it
    166 };
    167
    168 function wrap(onSkip, onRun) {
    169 return function(title, fn) {
    170 if (predicateFn()) {
    171 onSkip(title, fn);
    172 } else {
    173 onRun(title, fn);
    174 }
    175 };
    176 }
    177}
    178
    179
    180// PUBLIC API
    181
    182/**
    183 * Registers a new test suite.
    184 * @param {string} name The suite name.
    185 * @param {function()=} fn The suite function, or {@code undefined} to define
    186 * a pending test suite.
    187 */
    188exports.describe = global.describe;
    189
    190/**
    191 * Defines a suppressed test suite.
    192 * @param {string} name The suite name.
    193 * @param {function()=} fn The suite function, or {@code undefined} to define
    194 * a pending test suite.
    195 */
    196exports.xdescribe = global.xdescribe;
    197exports.describe.skip = global.describe.skip;
    198
    199/**
    200 * Register a function to call after the current suite finishes.
    201 * @param {function()} fn .
    202 */
    203exports.after = wrapped(global.after);
    204
    205/**
    206 * Register a function to call after each test in a suite.
    207 * @param {function()} fn .
    208 */
    209exports.afterEach = wrapped(global.afterEach);
    210
    211/**
    212 * Register a function to call before the current suite starts.
    213 * @param {function()} fn .
    214 */
    215exports.before = wrapped(global.before);
    216
    217/**
    218 * Register a function to call before each test in a suite.
    219 * @param {function()} fn .
    220 */
    221exports.beforeEach = wrapped(global.beforeEach);
    222
    223/**
    224 * Add a test to the current suite.
    225 * @param {string} name The test name.
    226 * @param {function()=} fn The test function, or {@code undefined} to define
    227 * a pending test case.
    228 */
    229exports.it = wrapped(global.it);
    230
    231/**
    232 * An alias for {@link #it()} that flags the test as the only one that should
    233 * be run within the current suite.
    234 * @param {string} name The test name.
    235 * @param {function()=} fn The test function, or {@code undefined} to define
    236 * a pending test case.
    237 */
    238exports.iit = exports.it.only = wrapped(global.it.only);
    239
    240/**
    241 * Adds a test to the current suite while suppressing it so it is not run.
    242 * @param {string} name The test name.
    243 * @param {function()=} fn The test function, or {@code undefined} to define
    244 * a pending test case.
    245 */
    246exports.xit = exports.it.skip = wrapped(global.xit);
    247
    248exports.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 af318cc..2432b6c 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":"firefox/binary.js","href":"source/firefox/binary.js.src.html"},{"name":"firefox/extension.js","href":"source/firefox/extension.js.src.html"},{"name":"firefox/index.js","href":"source/firefox/index.js.src.html"},{"name":"firefox/profile.js","href":"source/firefox/profile.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/exec.js","href":"source/io/exec.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/async/nexttick.js","href":"source/lib/goog/async/nexttick.js.src.html"},{"name":"lib/goog/async/run.js","href":"source/lib/goog/async/run.js.src.html"},{"name":"lib/goog/base.js","href":"source/lib/goog/base.js.src.html"},{"name":"lib/goog/debug/debug.js","href":"source/lib/goog/debug/debug.js.src.html"},{"name":"lib/goog/debug/entrypointregistry.js","href":"source/lib/goog/debug/entrypointregistry.js.src.html"},{"name":"lib/goog/debug/error.js","href":"source/lib/goog/debug/error.js.src.html"},{"name":"lib/goog/debug/logbuffer.js","href":"source/lib/goog/debug/logbuffer.js.src.html"},{"name":"lib/goog/debug/logger.js","href":"source/lib/goog/debug/logger.js.src.html"},{"name":"lib/goog/debug/logrecord.js","href":"source/lib/goog/debug/logrecord.js.src.html"},{"name":"lib/goog/deps.js","href":"source/lib/goog/deps.js.src.html"},{"name":"lib/goog/disposable/disposable.js","href":"source/lib/goog/disposable/disposable.js.src.html"},{"name":"lib/goog/disposable/idisposable.js","href":"source/lib/goog/disposable/idisposable.js.src.html"},{"name":"lib/goog/dom/browserfeature.js","href":"source/lib/goog/dom/browserfeature.js.src.html"},{"name":"lib/goog/dom/dom.js","href":"source/lib/goog/dom/dom.js.src.html"},{"name":"lib/goog/dom/nodetype.js","href":"source/lib/goog/dom/nodetype.js.src.html"},{"name":"lib/goog/dom/tagname.js","href":"source/lib/goog/dom/tagname.js.src.html"},{"name":"lib/goog/dom/vendor.js","href":"source/lib/goog/dom/vendor.js.src.html"},{"name":"lib/goog/events/browserevent.js","href":"source/lib/goog/events/browserevent.js.src.html"},{"name":"lib/goog/events/browserfeature.js","href":"source/lib/goog/events/browserfeature.js.src.html"},{"name":"lib/goog/events/event.js","href":"source/lib/goog/events/event.js.src.html"},{"name":"lib/goog/events/eventid.js","href":"source/lib/goog/events/eventid.js.src.html"},{"name":"lib/goog/events/events.js","href":"source/lib/goog/events/events.js.src.html"},{"name":"lib/goog/events/eventtarget.js","href":"source/lib/goog/events/eventtarget.js.src.html"},{"name":"lib/goog/events/eventtype.js","href":"source/lib/goog/events/eventtype.js.src.html"},{"name":"lib/goog/events/keycodes.js","href":"source/lib/goog/events/keycodes.js.src.html"},{"name":"lib/goog/events/listenable.js","href":"source/lib/goog/events/listenable.js.src.html"},{"name":"lib/goog/events/listener.js","href":"source/lib/goog/events/listener.js.src.html"},{"name":"lib/goog/events/listenermap.js","href":"source/lib/goog/events/listenermap.js.src.html"},{"name":"lib/goog/functions/functions.js","href":"source/lib/goog/functions/functions.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/labs/useragent/browser.js","href":"source/lib/goog/labs/useragent/browser.js.src.html"},{"name":"lib/goog/labs/useragent/engine.js","href":"source/lib/goog/labs/useragent/engine.js.src.html"},{"name":"lib/goog/labs/useragent/util.js","href":"source/lib/goog/labs/useragent/util.js.src.html"},{"name":"lib/goog/math/box.js","href":"source/lib/goog/math/box.js.src.html"},{"name":"lib/goog/math/coordinate.js","href":"source/lib/goog/math/coordinate.js.src.html"},{"name":"lib/goog/math/math.js","href":"source/lib/goog/math/math.js.src.html"},{"name":"lib/goog/math/rect.js","href":"source/lib/goog/math/rect.js.src.html"},{"name":"lib/goog/math/size.js","href":"source/lib/goog/math/size.js.src.html"},{"name":"lib/goog/net/wrapperxmlhttpfactory.js","href":"source/lib/goog/net/wrapperxmlhttpfactory.js.src.html"},{"name":"lib/goog/net/xhrlike.js","href":"source/lib/goog/net/xhrlike.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/reflect/reflect.js","href":"source/lib/goog/reflect/reflect.js.src.html"},{"name":"lib/goog/string/string.js","href":"source/lib/goog/string/string.js.src.html"},{"name":"lib/goog/structs/collection.js","href":"source/lib/goog/structs/collection.js.src.html"},{"name":"lib/goog/structs/map.js","href":"source/lib/goog/structs/map.js.src.html"},{"name":"lib/goog/structs/set.js","href":"source/lib/goog/structs/set.js.src.html"},{"name":"lib/goog/structs/structs.js","href":"source/lib/goog/structs/structs.js.src.html"},{"name":"lib/goog/style/style.js","href":"source/lib/goog/style/style.js.src.html"},{"name":"lib/goog/testing/asserts.js","href":"source/lib/goog/testing/asserts.js.src.html"},{"name":"lib/goog/testing/asynctestcase.js","href":"source/lib/goog/testing/asynctestcase.js.src.html"},{"name":"lib/goog/testing/events/events.js","href":"source/lib/goog/testing/events/events.js.src.html"},{"name":"lib/goog/testing/functionmock.js","href":"source/lib/goog/testing/functionmock.js.src.html"},{"name":"lib/goog/testing/jsunit.js","href":"source/lib/goog/testing/jsunit.js.src.html"},{"name":"lib/goog/testing/loosemock.js","href":"source/lib/goog/testing/loosemock.js.src.html"},{"name":"lib/goog/testing/mock.js","href":"source/lib/goog/testing/mock.js.src.html"},{"name":"lib/goog/testing/mockclock.js","href":"source/lib/goog/testing/mockclock.js.src.html"},{"name":"lib/goog/testing/mockcontrol.js","href":"source/lib/goog/testing/mockcontrol.js.src.html"},{"name":"lib/goog/testing/mockinterface.js","href":"source/lib/goog/testing/mockinterface.js.src.html"},{"name":"lib/goog/testing/mockmatchers.js","href":"source/lib/goog/testing/mockmatchers.js.src.html"},{"name":"lib/goog/testing/objectpropertystring.js","href":"source/lib/goog/testing/objectpropertystring.js.src.html"},{"name":"lib/goog/testing/propertyreplacer.js","href":"source/lib/goog/testing/propertyreplacer.js.src.html"},{"name":"lib/goog/testing/recordfunction.js","href":"source/lib/goog/testing/recordfunction.js.src.html"},{"name":"lib/goog/testing/stacktrace.js","href":"source/lib/goog/testing/stacktrace.js.src.html"},{"name":"lib/goog/testing/strictmock.js","href":"source/lib/goog/testing/strictmock.js.src.html"},{"name":"lib/goog/testing/testcase.js","href":"source/lib/goog/testing/testcase.js.src.html"},{"name":"lib/goog/testing/testrunner.js","href":"source/lib/goog/testing/testrunner.js.src.html"},{"name":"lib/goog/testing/watchers.js","href":"source/lib/goog/testing/watchers.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/testing/testcase.js","href":"source/lib/webdriver/testing/testcase.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":"PRIMITIVE_EQUALITY_PREDICATES","isTypedef":false,"href":"namespace_PRIMITIVE_EQUALITY_PREDICATES.html"},{"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.Disposable","isTypedef":false,"href":"class_goog_Disposable.html"},{"isInterface":false,"name":"goog.Disposable.MonitoringMode","isTypedef":false,"href":"enum_goog_Disposable_MonitoringMode.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.async","isTypedef":false,"href":"namespace_goog_async.html"},{"isInterface":false,"name":"goog.async.nextTick","isTypedef":false,"href":"namespace_goog_async_nextTick.html"},{"isInterface":false,"name":"goog.async.run","isTypedef":false,"href":"namespace_goog_async_run.html"},{"isInterface":false,"name":"goog.async.run.WorkItem_","isTypedef":false,"href":"class_goog_async_run_WorkItem_.html"},{"isInterface":false,"name":"goog.debug","isTypedef":false,"href":"namespace_goog_debug.html"},{"name":"goog.debug.Loggable","isTypedef":true,"href":"namespace_goog_debug.html#goog.debug.Loggable"},{"isInterface":true,"name":"goog.debug.EntryPointMonitor","isTypedef":false,"href":"interface_goog_debug_EntryPointMonitor.html"},{"isInterface":false,"name":"goog.debug.Error","isTypedef":false,"href":"class_goog_debug_Error.html"},{"isInterface":false,"name":"goog.debug.LogBuffer","isTypedef":false,"href":"class_goog_debug_LogBuffer.html"},{"isInterface":false,"name":"goog.debug.LogManager","isTypedef":false,"href":"namespace_goog_debug_LogManager.html"},{"isInterface":false,"name":"goog.debug.LogRecord","isTypedef":false,"href":"class_goog_debug_LogRecord.html"},{"isInterface":false,"name":"goog.debug.Logger","isTypedef":false,"href":"class_goog_debug_Logger.html"},{"isInterface":false,"name":"goog.debug.Logger.Level","isTypedef":false,"href":"class_goog_debug_Logger_Level.html"},{"isInterface":false,"name":"goog.debug.entryPointRegistry","isTypedef":false,"href":"namespace_goog_debug_entryPointRegistry.html"},{"isInterface":false,"name":"goog.defineClass","isTypedef":false,"href":"namespace_goog_defineClass.html"},{"name":"goog.defineClass.ClassDescriptor","isTypedef":true,"href":"namespace_goog_defineClass.html#goog.defineClass.ClassDescriptor"},{"isInterface":false,"name":"goog.disposable","isTypedef":false,"href":"namespace_goog_disposable.html"},{"isInterface":true,"name":"goog.disposable.IDisposable","isTypedef":false,"href":"interface_goog_disposable_IDisposable.html"},{"isInterface":false,"name":"goog.dom","isTypedef":false,"href":"namespace_goog_dom.html"},{"name":"goog.dom.Appendable","isTypedef":true,"href":"namespace_goog_dom.html#goog.dom.Appendable"},{"isInterface":false,"name":"goog.dom.BrowserFeature","isTypedef":false,"href":"enum_goog_dom_BrowserFeature.html"},{"isInterface":false,"name":"goog.dom.DomHelper","isTypedef":false,"href":"class_goog_dom_DomHelper.html"},{"isInterface":false,"name":"goog.dom.NodeType","isTypedef":false,"href":"enum_goog_dom_NodeType.html"},{"isInterface":false,"name":"goog.dom.TagName","isTypedef":false,"href":"enum_goog_dom_TagName.html"},{"isInterface":false,"name":"goog.dom.vendor","isTypedef":false,"href":"namespace_goog_dom_vendor.html"},{"isInterface":false,"name":"goog.events","isTypedef":false,"href":"namespace_goog_events.html"},{"name":"goog.events.EventLike","isTypedef":true,"href":"namespace_goog_events.html#goog.events.EventLike"},{"name":"goog.events.Key","isTypedef":true,"href":"namespace_goog_events.html#goog.events.Key"},{"name":"goog.events.ListenableType","isTypedef":true,"href":"namespace_goog_events.html#goog.events.ListenableType"},{"isInterface":false,"name":"goog.events.BrowserEvent","isTypedef":false,"href":"class_goog_events_BrowserEvent.html"},{"isInterface":false,"name":"goog.events.BrowserEvent.MouseButton","isTypedef":false,"href":"enum_goog_events_BrowserEvent_MouseButton.html"},{"isInterface":false,"name":"goog.events.BrowserFeature","isTypedef":false,"href":"enum_goog_events_BrowserFeature.html"},{"isInterface":false,"name":"goog.events.CaptureSimulationMode","isTypedef":false,"href":"enum_goog_events_CaptureSimulationMode.html"},{"isInterface":false,"name":"goog.events.Event","isTypedef":false,"href":"class_goog_events_Event.html"},{"isInterface":false,"name":"goog.events.EventId","isTypedef":false,"href":"class_goog_events_EventId.html"},{"isInterface":false,"name":"goog.events.EventTarget","isTypedef":false,"href":"class_goog_events_EventTarget.html"},{"isInterface":false,"name":"goog.events.EventType","isTypedef":false,"href":"enum_goog_events_EventType.html"},{"isInterface":false,"name":"goog.events.KeyCodes","isTypedef":false,"href":"enum_goog_events_KeyCodes.html"},{"isInterface":true,"name":"goog.events.Listenable","isTypedef":false,"href":"interface_goog_events_Listenable.html"},{"isInterface":true,"name":"goog.events.ListenableKey","isTypedef":false,"href":"interface_goog_events_ListenableKey.html"},{"isInterface":false,"name":"goog.events.Listener","isTypedef":false,"href":"class_goog_events_Listener.html"},{"isInterface":false,"name":"goog.events.ListenerMap","isTypedef":false,"href":"class_goog_events_ListenerMap.html"},{"isInterface":false,"name":"goog.functions","isTypedef":false,"href":"namespace_goog_functions.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.GroupByIterator_","isTypedef":false,"href":"class_goog_iter_GroupByIterator_.html"},{"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.labs.userAgent","isTypedef":false,"href":"namespace_goog_labs_userAgent.html"},{"isInterface":false,"name":"goog.labs.userAgent.browser","isTypedef":false,"href":"namespace_goog_labs_userAgent_browser.html"},{"isInterface":false,"name":"goog.labs.userAgent.engine","isTypedef":false,"href":"namespace_goog_labs_userAgent_engine.html"},{"isInterface":false,"name":"goog.labs.userAgent.util","isTypedef":false,"href":"namespace_goog_labs_userAgent_util.html"},{"isInterface":false,"name":"goog.math","isTypedef":false,"href":"namespace_goog_math.html"},{"isInterface":false,"name":"goog.math.Box","isTypedef":false,"href":"class_goog_math_Box.html"},{"isInterface":false,"name":"goog.math.Coordinate","isTypedef":false,"href":"class_goog_math_Coordinate.html"},{"isInterface":false,"name":"goog.math.Rect","isTypedef":false,"href":"class_goog_math_Rect.html"},{"isInterface":false,"name":"goog.math.Size","isTypedef":false,"href":"class_goog_math_Size.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":true,"name":"goog.net.XhrLike","isTypedef":false,"href":"interface_goog_net_XhrLike.html"},{"name":"goog.net.XhrLike.OrNative","isTypedef":true,"href":"interface_goog_net_XhrLike.html#goog.net.XhrLike.OrNative"},{"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.XmlHttpDefines","isTypedef":false,"href":"namespace_goog_net_XmlHttpDefines.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.reflect","isTypedef":false,"href":"namespace_goog_reflect.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":true,"name":"goog.structs.Collection","isTypedef":false,"href":"interface_goog_structs_Collection.html"},{"isInterface":false,"name":"goog.structs.Map","isTypedef":false,"href":"class_goog_structs_Map.html"},{"isInterface":false,"name":"goog.structs.Set","isTypedef":false,"href":"class_goog_structs_Set.html"},{"isInterface":false,"name":"goog.style","isTypedef":false,"href":"namespace_goog_style.html"},{"isInterface":false,"name":"goog.testing","isTypedef":false,"href":"namespace_goog_testing.html"},{"isInterface":false,"name":"goog.testing.AsyncTestCase","isTypedef":false,"href":"class_goog_testing_AsyncTestCase.html"},{"name":"goog.testing.AsyncTestCase.TopStackFuncResult_","isTypedef":true,"href":"class_goog_testing_AsyncTestCase.html#goog.testing.AsyncTestCase.TopStackFuncResult_"},{"isInterface":false,"name":"goog.testing.AsyncTestCase.ControlBreakingException","isTypedef":false,"href":"class_goog_testing_AsyncTestCase_ControlBreakingException.html"},{"isInterface":false,"name":"goog.testing.FunctionCall","isTypedef":false,"href":"class_goog_testing_FunctionCall.html"},{"isInterface":false,"name":"goog.testing.JsUnitException","isTypedef":false,"href":"class_goog_testing_JsUnitException.html"},{"isInterface":false,"name":"goog.testing.LooseExpectationCollection","isTypedef":false,"href":"class_goog_testing_LooseExpectationCollection.html"},{"isInterface":false,"name":"goog.testing.LooseMock","isTypedef":false,"href":"class_goog_testing_LooseMock.html"},{"isInterface":false,"name":"goog.testing.MethodMock","isTypedef":false,"href":"namespace_goog_testing_MethodMock.html"},{"isInterface":false,"name":"goog.testing.Mock","isTypedef":false,"href":"class_goog_testing_Mock.html"},{"isInterface":false,"name":"goog.testing.MockClock","isTypedef":false,"href":"class_goog_testing_MockClock.html"},{"isInterface":false,"name":"goog.testing.MockControl","isTypedef":false,"href":"class_goog_testing_MockControl.html"},{"isInterface":false,"name":"goog.testing.MockExpectation","isTypedef":false,"href":"class_goog_testing_MockExpectation.html"},{"isInterface":true,"name":"goog.testing.MockInterface","isTypedef":false,"href":"interface_goog_testing_MockInterface.html"},{"isInterface":false,"name":"goog.testing.ObjectPropertyString","isTypedef":false,"href":"class_goog_testing_ObjectPropertyString.html"},{"isInterface":false,"name":"goog.testing.PropertyReplacer","isTypedef":false,"href":"class_goog_testing_PropertyReplacer.html"},{"isInterface":false,"name":"goog.testing.StrictMock","isTypedef":false,"href":"class_goog_testing_StrictMock.html"},{"isInterface":false,"name":"goog.testing.TestCase","isTypedef":false,"href":"class_goog_testing_TestCase.html"},{"isInterface":false,"name":"goog.testing.TestCase.Error","isTypedef":false,"href":"class_goog_testing_TestCase_Error.html"},{"isInterface":false,"name":"goog.testing.TestCase.Order","isTypedef":false,"href":"enum_goog_testing_TestCase_Order.html"},{"isInterface":false,"name":"goog.testing.TestCase.Result","isTypedef":false,"href":"class_goog_testing_TestCase_Result.html"},{"isInterface":false,"name":"goog.testing.TestCase.Test","isTypedef":false,"href":"class_goog_testing_TestCase_Test.html"},{"isInterface":false,"name":"goog.testing.TestCase.protectedDate_","isTypedef":false,"href":"class_Date.html"},{"isInterface":false,"name":"goog.testing.TestRunner","isTypedef":false,"href":"class_goog_testing_TestRunner.html"},{"isInterface":false,"name":"goog.testing.asserts","isTypedef":false,"href":"namespace_goog_testing_asserts.html"},{"name":"goog.testing.asserts.ArrayLike","isTypedef":true,"href":"namespace_goog_testing_asserts.html#goog.testing.asserts.ArrayLike"},{"isInterface":false,"name":"goog.testing.events","isTypedef":false,"href":"namespace_goog_testing_events.html"},{"isInterface":false,"name":"goog.testing.events.Event","isTypedef":false,"href":"class_goog_testing_events_Event.html"},{"isInterface":false,"name":"goog.testing.jsunit","isTypedef":false,"href":"namespace_goog_testing_jsunit.html"},{"isInterface":false,"name":"goog.testing.mockmatchers","isTypedef":false,"href":"namespace_goog_testing_mockmatchers.html"},{"isInterface":false,"name":"goog.testing.mockmatchers.ArgumentMatcher","isTypedef":false,"href":"class_goog_testing_mockmatchers_ArgumentMatcher.html"},{"isInterface":false,"name":"goog.testing.mockmatchers.IgnoreArgument","isTypedef":false,"href":"class_goog_testing_mockmatchers_IgnoreArgument.html"},{"isInterface":false,"name":"goog.testing.mockmatchers.InstanceOf","isTypedef":false,"href":"class_goog_testing_mockmatchers_InstanceOf.html"},{"isInterface":false,"name":"goog.testing.mockmatchers.ObjectEquals","isTypedef":false,"href":"class_goog_testing_mockmatchers_ObjectEquals.html"},{"isInterface":false,"name":"goog.testing.mockmatchers.RegexpMatch","isTypedef":false,"href":"class_goog_testing_mockmatchers_RegexpMatch.html"},{"isInterface":false,"name":"goog.testing.mockmatchers.SaveArgument","isTypedef":false,"href":"class_goog_testing_mockmatchers_SaveArgument.html"},{"isInterface":false,"name":"goog.testing.mockmatchers.TypeOf","isTypedef":false,"href":"class_goog_testing_mockmatchers_TypeOf.html"},{"isInterface":false,"name":"goog.testing.stacktrace","isTypedef":false,"href":"namespace_goog_testing_stacktrace.html"},{"isInterface":false,"name":"goog.testing.stacktrace.Frame","isTypedef":false,"href":"class_goog_testing_stacktrace_Frame.html"},{"isInterface":false,"name":"goog.testing.watchers","isTypedef":false,"href":"namespace_goog_testing_watchers.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"},{"name":"webdriver.ProxyConfig","isTypedef":true,"href":"namespace_webdriver.html#webdriver.ProxyConfig"},{"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.AlertPromise","isTypedef":false,"href":"class_webdriver_AlertPromise.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"},{"name":"webdriver.WebDriver.Options.Cookie","isTypedef":true,"href":"class_webdriver_WebDriver_Options.html#webdriver.WebDriver.Options.Cookie"},{"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"},{"name":"webdriver.WebElement.Id","isTypedef":true,"href":"class_webdriver_WebElement.html#webdriver.WebElement.Id"},{"isInterface":false,"name":"webdriver.WebElementPromise","isTypedef":false,"href":"class_webdriver_WebElementPromise.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"},{"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.Preferences","isTypedef":false,"href":"class_webdriver_logging_Preferences.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":true,"name":"webdriver.promise.Thenable","isTypedef":false,"href":"interface_webdriver_promise_Thenable.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.TestCase","isTypedef":false,"href":"class_webdriver_testing_TestCase.html"},{"isInterface":false,"name":"webdriver.testing.assert","isTypedef":false,"href":"module_selenium-webdriver_testing_assert_namespace_dossier$$module__$Users$jleyba$Development$selenium2$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":"WebElement","isTypedef":false,"href":"class_webdriver_WebElement.html"},{"name":"webdriver.WebElement.Id","isTypedef":true,"href":"class_webdriver_WebElement.html#webdriver.WebElement.Id"},{"isInterface":false,"name":"CommandName","isTypedef":false,"href":"enum_webdriver_CommandName.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":"WebElementPromise","isTypedef":false,"href":"class_webdriver_WebElementPromise.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":[{"isInterface":false,"name":"Context","isTypedef":false,"href":"module_selenium-webdriver__base_class_Context.html"}],"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":"Driver","isTypedef":false,"href":"module_selenium-webdriver_chrome_class_Driver.html"},{"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/firefox","types":[{"isInterface":false,"name":"Driver","isTypedef":false,"href":"module_selenium-webdriver_firefox_class_Driver.html"},{"isInterface":false,"name":"Binary","isTypedef":false,"href":"module_selenium-webdriver_firefox_class_Binary.html"},{"isInterface":false,"name":"Profile","isTypedef":false,"href":"module_selenium-webdriver_firefox_class_Profile.html"},{"isInterface":false,"name":"Options","isTypedef":false,"href":"module_selenium-webdriver_firefox_class_Options.html"}],"href":"module_selenium-webdriver_firefox.html"},{"name":"selenium-webdriver/firefox/binary","types":[{"isInterface":false,"name":"Binary","isTypedef":false,"href":"module_selenium-webdriver_firefox_binary_class_Binary.html"}],"href":"module_selenium-webdriver_firefox_binary.html"},{"name":"selenium-webdriver/firefox/extension","types":[{"isInterface":false,"name":"AddonDetails","isTypedef":true,"href":"module_selenium-webdriver_firefox_extension_namespace_AddonDetails.html"}],"href":"module_selenium-webdriver_firefox_extension.html"},{"name":"selenium-webdriver/firefox/profile","types":[{"isInterface":false,"name":"Profile","isTypedef":false,"href":"module_selenium-webdriver_firefox_profile_class_Profile.html"}],"href":"module_selenium-webdriver_firefox_profile.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/io/exec","types":[{"isInterface":false,"name":"Options","isTypedef":true,"href":"module_selenium-webdriver_io_exec_namespace_Options.html"}],"href":"module_selenium-webdriver_io_exec.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":[{"isInterface":false,"name":"Driver","isTypedef":false,"href":"module_selenium-webdriver_phantomjs_class_Driver.html"}],"href":"module_selenium-webdriver_phantomjs.html"},{"name":"selenium-webdriver/proxy","types":[],"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":["execute","getSchedule","reset","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.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","kill","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 4b90f87..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 browserstack-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 index 5e60576..90173bc 100644 --- a/example/google_search_generator.js +++ b/example/google_search_generator.js @@ -1,38 +1,41 @@ -// 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 using Harmony generator functions. * This requires node v0.11 or newer. * * Usage: node --harmony-generators \ - * browserstack-webdriver/example/google_search_generator.js + * selenium-webdriver/example/google_search_generator.js */ -var webdriver = require('..'); +var webdriver = require('..'), + By = webdriver.By; -var driver = new webdriver.Builder(). - withCapabilities(webdriver.Capabilities.chrome()). - build(); +var driver = new webdriver.Builder() + .forBrowser('firefox') + .build(); -driver.get('http://www.google.com'); +driver.get('http://www.google.com/ncr'); driver.call(function* () { - var query = yield driver.findElement(webdriver.By.name('q')); + var query = yield driver.findElement(By.name('q')); query.sendKeys('webdriver'); - var submit = yield driver.findElement(webdriver.By.name('btnG')); + var submit = yield driver.findElement(By.name('btnG')); submit.click(); }); 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 index 29ebe10..f416922 100644 --- a/example/parallel_flows.js +++ b/example/parallel_flows.js @@ -1,24 +1,28 @@ -// 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. /** * @fileoverview An example of starting multiple WebDriver clients that run * in parallel in separate control flows. */ -var webdriver = require('..'); +var webdriver = require('..'), + By = webdriver.By, + until = webdriver.until; for (var i = 0; i < 3; i++) { (function(n) { @@ -28,7 +32,7 @@ for (var i = 0; i < 3; i++) { }); var driver = new webdriver.Builder(). - withCapabilities(webdriver.Capabilities.chrome()). + forBrowser('firefox'). setControlFlow(flow). // Comment out this line to see the difference. build(); @@ -37,13 +41,9 @@ for (var i = 0; i < 3; i++) { driver.manage().window().setPosition(300 * i, 400 * i); 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); 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 index 23f44ac..843d9e8 100644 --- a/firefox/binary.js +++ b/firefox/binary.js @@ -1,17 +1,24 @@ -// 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. + +/** + * @fileoverview Manages Firefox binaries. This module is considered internal; + * users should use {@link selenium-webdriver/firefox}. + */ 'use strict'; @@ -20,7 +27,8 @@ var child = require('child_process'), path = require('path'), util = require('util'); -var promise = require('..').promise, +var Serializable = require('..').Serializable, + promise = require('..').promise, _base = require('../_base'), io = require('../io'), exec = require('../io/exec'); @@ -124,35 +132,17 @@ function installNoFocusLibs(profileDir) { } -/** - * Silently runs Firefox to install a profile directory (which is assumed to be - * defined in the given environment variables). - * @param {string} firefox Path to the Firefox executable. - * @param {!Object.} env The environment variables to use. - * @return {!promise.Promise} A promise for when the profile has been installed. - */ -function installProfile(firefox, env) { - var installed = promise.defer(); - child.exec(firefox + ' -silent', {env: env, timeout: 180 * 1000}, - function(err) { - if (err) { - installed.reject(new Error( - 'Failed to install Firefox profile: ' + err)); - return; - } - installed.fulfill(); - }); - return installed.promise; -} - - /** * Manages a Firefox subprocess configured 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; @@ -171,6 +161,7 @@ var Binary = function(opt_exe) { /** @private {promise.Promise.} */ this.command_ = null; }; +util.inherits(Binary, Serializable); /** @@ -209,25 +200,13 @@ Binary.prototype.launch = function(profile) { var args = ['-foreground'].concat(this.args_); - var self = this; - this.command_ = promise.when(this.exe_ || findFirefox(), function(firefox) { if (process.platform === 'win32' || process.platform === 'darwin') { - return firefox; + 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 firefox; - }); - }).then(function(firefox) { - var install = exec(firefox, {args: ['-silent'], env: env}); - return install.result().then(function(result) { - if (result.code !== 0) { - throw Error( - 'Failed to install profile; firefox terminated with ' + result); - } - return exec(firefox, {args: args, env: env}); }); }); @@ -254,6 +233,21 @@ Binary.prototype.kill = function() { }; +/** + * 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 diff --git a/firefox/extension.js b/firefox/extension.js index c224421..9feb05b 100644 --- a/firefox/extension.js +++ b/firefox/extension.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. /** @fileoverview Utilities for working with Firefox extensions. */ diff --git a/firefox/index.js b/firefox/index.js index b780b5a..82db486 100644 --- a/firefox/index.js +++ b/firefox/index.js @@ -1,17 +1,96 @@ -// 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. + +/** + * @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'; @@ -24,6 +103,7 @@ var Binary = require('./binary').Binary, webdriver = require('..'), executors = require('../executors'), httpUtil = require('../http/util'), + io = require('../io'), net = require('../net'), portprober = require('../net/portprober'); @@ -156,6 +236,13 @@ var Driver = function(opt_config, opt_flow) { caps.set('firefox_binary', null); caps.set('firefox_profile', null); + /** @private {?string} */ + this.profilePath_ = null; + + /** @private {!Binary} */ + this.binary_ = binary; + + var self = this; var serverUrl = portprober.findFreePort().then(function(port) { var prepareProfile; if (typeof profile === 'string') { @@ -170,6 +257,7 @@ var Driver = function(opt_config, opt_flow) { } return prepareProfile.then(function(dir) { + self.profilePath_ = dir; return binary.launch(dir); }).then(function() { var serverUrl = url.format({ @@ -193,6 +281,32 @@ var Driver = function(opt_config, 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.binary_.kill(); + }) + .thenFinally(function() { + if (self.profilePath_) { + return io.rmDir(self.profilePath_); + } + }); + }, this); +}; + + // PUBLIC API diff --git a/firefox/profile.js b/firefox/profile.js index 32cacb8..ca5b6f7 100644 --- a/firefox/profile.js +++ b/firefox/profile.js @@ -1,26 +1,35 @@ -// 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. + +/** + * @fileoverview Profile management module. This module is considered internal; + * users should use {@link selenium-webdriver/firefox}. + */ 'use strict'; var AdmZip = require('adm-zip'), fs = require('fs'), path = require('path'), + util = require('util'), vm = require('vm'); -var promise = require('..').promise, +var Serializable = require('..').Serializable, + promise = require('..').promise, _base = require('../_base'), io = require('../io'), extension = require('./extension'); @@ -213,8 +222,11 @@ function decode(data) { * 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_ = {}; @@ -233,6 +245,7 @@ var Profile = function(opt_dir) { /** @private {!Array.} */ this.extensions_ = []; }; +util.inherits(Profile, Serializable); /** @@ -400,6 +413,16 @@ Profile.prototype.encode = function() { }; +/** + * 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 diff --git a/http/index.js b/http/index.js index 9a2c35d..4a99a92 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. */ @@ -32,10 +35,12 @@ var KeepAliveAgent = require('keep-alive-agent'), * @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, opt_agent) { +var HttpClient = function(serverUrl, opt_agent, opt_proxy) { var parsedUrl = url.parse(serverUrl); if (!parsedUrl.hostname) { throw new Error('Invalid server URL: ' + serverUrl); @@ -44,6 +49,9 @@ var HttpClient = function(serverUrl, opt_agent) { /** @private {http.Agent} */ this.agent_ = opt_agent || agent; + /** @private {string} */ + this.proxy_ = opt_proxy; + /** * Base options for each request. * @private {!Object} @@ -80,10 +88,12 @@ HttpClient.prototype.send = function(httpRequest, callback) { path: path, headers: httpRequest.headers, }; + if (this.agent_) { options.agent = this.agent_; } - sendRequest(options, callback, data); + + sendRequest(options, callback, data, this.proxy_); }; @@ -93,8 +103,25 @@ 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) { try { @@ -108,8 +135,8 @@ var sendRequest = function(options, callback, opt_data) { } if (!location.hostname) { - location.hostname = options.host; - location.port = options.port; + location.hostname = host; + location.port = port; } request.abort(); @@ -121,7 +148,7 @@ var sendRequest = function(options, callback, opt_data) { headers: { 'Accept': 'application/json; charset=utf-8' } - }, callback); + }, callback, undefined, opt_proxy); return; } @@ -137,7 +164,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 e498164..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'); @@ -130,3 +140,9 @@ exports.WebElementPromise = base.require('webdriver.WebElementPromise'); (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 index df0b189..187ea78 100644 --- a/io/exec.js +++ b/io/exec.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'; @@ -22,15 +24,12 @@ var promise = require('..').promise; /** * A hash with configuration options for an executed command. - *
      - *
    • - *
    • {@code args} - Command line arguments. - *
    • {@code env} - Command environment; will inherit from the current process - * if missing. - *
    • {@code stdio} - IO configuration for the spawned server process. For - * more information, refer to the documentation of - * {@code child_process.spawn}. - *
    + * + * - `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), diff --git a/io/index.js b/io/index.js index a356ef6..3077c46 100644 --- a/io/index.js +++ b/io/index.js @@ -1,32 +1,62 @@ -// 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'), + 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. @@ -116,6 +146,27 @@ exports.exists = function(path) { }; +/** + * 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. @@ -127,12 +178,17 @@ exports.tmpDir = function() { /** + * @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() { - return promise.checkedNodeCall(tmp.file); +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); }; @@ -153,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/README b/lib/README index 355a0af..bdf25b0 100644 --- a/lib/README +++ b/lib/README @@ -1,2 +1,2 @@ -This directory contains modules internal to browserstack-webdriver that are not +This directory contains modules internal to selenium-webdriver that are not intended for general consumption. They may change at any time. diff --git a/lib/atoms/error.js b/lib/atoms/error.js index 1e5ec48..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,12 +48,8 @@ bot.ErrorCode = { NO_SUCH_WINDOW: 23, INVALID_COOKIE_DOMAIN: 24, UNABLE_TO_SET_COOKIE: 25, - /** @deprecated */ - MODAL_DIALOG_OPENED: 26, UNEXPECTED_ALERT_OPEN: 26, NO_SUCH_ALERT: 27, - /** @deprecated */ - NO_MODAL_DIALOG_OPEN: 27, SCRIPT_TIMEOUT: 28, INVALID_ELEMENT_COORDINATES: 29, IME_NOT_AVAILABLE: 30, @@ -67,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. @@ -120,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' }; @@ -165,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; @@ -176,7 +170,6 @@ 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; @@ -184,10 +177,8 @@ goog.scope(function() { 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; 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 index 916e530..991e642 100644 Binary files a/lib/firefox/amd64/libnoblur64.so and b/lib/firefox/amd64/libnoblur64.so differ diff --git a/lib/firefox/i386/libnoblur.so b/lib/firefox/i386/libnoblur.so index 8e7db8d..716a8e0 100644 Binary files a/lib/firefox/i386/libnoblur.so and b/lib/firefox/i386/libnoblur.so differ diff --git a/lib/firefox/webdriver.json b/lib/firefox/webdriver.json index 7ed7717..14ba421 100644 --- a/lib/firefox/webdriver.json +++ b/lib/firefox/webdriver.json @@ -2,19 +2,28 @@ "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, @@ -22,6 +31,7 @@ "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, @@ -42,7 +52,8 @@ "toolkit.networkmanager.disable": true, "toolkit.telemetry.prompted": 2, "toolkit.telemetry.enabled": false, - "toolkit.telemetry.rejected": true + "toolkit.telemetry.rejected": true, + "xpinstall.signatures.required": false }, "mutable": { "browser.dom.window.dump.enabled": true, diff --git a/lib/firefox/webdriver.xpi b/lib/firefox/webdriver.xpi index 3c2d1bd..16c8579 100644 Binary files a/lib/firefox/webdriver.xpi and b/lib/firefox/webdriver.xpi differ diff --git a/lib/goog/array/array.js b/lib/goog/array/array.js index 9c940ac..bd99626 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) */ @@ -59,7 +60,7 @@ goog.array.ArrayLike; /** * Returns the last element in an array without removing it. * Same as goog.array.last. - * @param {Array.|goog.array.ArrayLike} array The array. + * @param {Array|goog.array.ArrayLike} array The array. * @return {T} Last item in array. * @template T */ @@ -71,7 +72,7 @@ goog.array.peek = function(array) { /** * Returns the last element in an array without removing it. * Same as goog.array.peek. - * @param {Array.|goog.array.ArrayLike} array The array. + * @param {Array|goog.array.ArrayLike} array The array. * @return {T} Last item in array. * @template T */ @@ -98,7 +99,7 @@ goog.array.ARRAY_PROTOTYPE_ = Array.prototype; * * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof} * - * @param {Array.|goog.array.ArrayLike} arr The array to be searched. + * @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. @@ -140,7 +141,7 @@ goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES && * * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof} * - * @param {!Array.|!goog.array.ArrayLike} arr The array to be searched. + * @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. @@ -185,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 @@ -216,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 @@ -243,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 @@ -252,7 +253,7 @@ 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 + * @return {!Array} a new array in which only elements that passed the test * are present. * @template T,S */ @@ -287,14 +288,14 @@ 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 + * @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. + * @return {!Array} a new array with the results from f. * @template THIS, VALUE, RESULT */ goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES && @@ -328,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 @@ -372,7 +373,7 @@ goog.array.reduce = goog.NATIVE_ARRAY_PROTOTYPES && * 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 @@ -413,7 +414,7 @@ goog.array.reduceRight = goog.NATIVE_ARRAY_PROTOTYPES && * * 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 @@ -450,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 @@ -484,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). @@ -506,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 */ @@ -525,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 @@ -551,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 */ @@ -571,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 @@ -634,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 */ @@ -671,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. @@ -689,7 +690,7 @@ goog.array.insertBefore = function(arr, obj, opt_obj2) { /** * Removes the first occurrence of a particular value from an array. - * @param {Array.|goog.array.ArrayLike} arr Array from which 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. @@ -724,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 @@ -746,7 +747,7 @@ 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 + * @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 @@ -794,7 +795,7 @@ goog.array.removeAllIf = 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( @@ -804,8 +805,8 @@ 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.} + * @param {...!Array} var_args + * @return {!Array} * @template T */ goog.array.join = function(var_args) { @@ -816,9 +817,9 @@ goog.array.join = function(var_args) { /** * Converts an object to an array. - * @param {Array.|goog.array.ArrayLike} object The object to convert to an + * @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 + * @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. @@ -843,9 +844,9 @@ goog.array.toArray = function(object) { /** * Does a shallow copy of an array. - * @param {Array.|goog.array.ArrayLike} arr Array or array-like object to + * @param {Array|goog.array.ArrayLike} arr Array or array-like object to * clone. - * @return {!Array.} Clone of the input array. + * @return {!Array} Clone of the input array. * @template T */ goog.array.clone = goog.array.toArray; @@ -862,30 +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 {...(Array.|VALUE)} var_args The elements or arrays of elements + * @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]; } @@ -901,7 +890,7 @@ 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 {Array.|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 @@ -909,7 +898,7 @@ goog.array.extend = function(arr1, var_args) { * are floored). * @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) { @@ -925,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 */ @@ -962,7 +951,7 @@ goog.array.slice = function(arr, start, opt_end) { * Runtime: N, * Worstcase space: 2N (no dupes) * - * @param {Array.|goog.array.ArrayLike} arr The array from which to remove + * @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 @@ -1008,7 +997,7 @@ goog.array.removeDuplicates = function(arr, opt_rv, opt_hashFn) { * * Runtime: O(log n) * - * @param {Array.|goog.array.ArrayLike} arr The array to be searched. + * @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 @@ -1037,7 +1026,7 @@ goog.array.binarySearch = function(arr, target, opt_compareFn) { * * Runtime: O(log n) * - * @param {Array.|goog.array.ArrayLike} arr The array to be searched. + * @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 @@ -1070,7 +1059,7 @@ goog.array.binarySelect = function(arr, evaluator, opt_obj) { * * Runtime: O(log n) * - * @param {Array.|goog.array.ArrayLike} arr The array to be searched. + * @param {Array|goog.array.ArrayLike} arr The array to be searched. * @param {function(TARGET, VALUE): number| * function(this:THIS, VALUE, number, ?): number} compareFn Either an * evaluator or a comparison function, as defined by binarySearch @@ -1128,7 +1117,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 @@ -1152,7 +1141,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 @@ -1175,28 +1164,55 @@ 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 + * @template 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, @@ -1249,9 +1265,9 @@ goog.array.equals = function(arr1, arr2, opt_equalsFn) { /** * 3-way array compare function. - * @param {!Array.|!goog.array.ArrayLike} arr1 The first array to + * @param {!Array|!goog.array.ArrayLike} arr1 The first array to * compare. - * @param {!Array.|!goog.array.ArrayLike} arr2 The second array to + * @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 @@ -1282,7 +1298,8 @@ goog.array.compare3 = function(arr1, arr2, opt_compareFn) { * @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) { @@ -1290,6 +1307,21 @@ goog.array.defaultCompare = function(a, b) { }; +/** + * 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. @@ -1304,7 +1336,7 @@ 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.|goog.array.ArrayLike} array The array to modify. + * @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 @@ -1326,7 +1358,7 @@ goog.array.binaryInsert = function(array, value, opt_compareFn) { /** * Removes a value from a sorted array. - * @param {!Array.|!goog.array.ArrayLike} array The array to modify. + * @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 @@ -1344,8 +1376,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. @@ -1376,7 +1408,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 @@ -1386,7 +1418,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) { @@ -1414,7 +1446,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. */ @@ -1451,7 +1483,7 @@ goog.array.range = function(startOrEnd, opt_end, opt_step) { * * @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) { @@ -1468,14 +1500,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); } @@ -1493,9 +1533,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) { @@ -1545,7 +1585,8 @@ goog.array.moveItem = function(arr, fromIndex, toIndex) { * 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) { @@ -1575,7 +1616,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). @@ -1593,3 +1634,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 2bb985f..95513d1 100644 --- a/lib/goog/asserts/asserts.js +++ b/lib/goog/asserts/asserts.js @@ -31,6 +31,7 @@ * 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'); @@ -51,7 +52,7 @@ 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 @@ -95,9 +96,9 @@ 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 */ @@ -124,7 +125,7 @@ goog.asserts.doAssertFailure_ = * 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 + * @param {function(!goog.asserts.AssertionError)} errorHandler */ goog.asserts.setErrorHandler = function(errorHandler) { if (goog.asserts.ENABLE_ASSERTS) { @@ -257,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) { @@ -266,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); }; @@ -296,7 +297,7 @@ goog.asserts.assertBoolean = function(value, opt_message, var_args) { * @param {...*} var_args The items to substitute into the failure message. * @return {!Element} The value, likely to be a DOM Element when asserts are * enabled. - * @throws {goog.asserts.AssertionError} When the value is not a boolean. + * @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) || @@ -321,12 +322,13 @@ goog.asserts.assertElement = function(value, opt_message, var_args) { * @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; @@ -342,3 +344,22 @@ goog.asserts.assertObjectPrototypeIsIntact = function() { 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 index f836bd3..bdbad6b 100644 --- a/lib/goog/async/nexttick.js +++ b/lib/goog/async/nexttick.js @@ -23,8 +23,10 @@ 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'); /** @@ -53,9 +55,11 @@ goog.async.throwException = function(exception) { * @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) { +goog.async.nextTick = function(callback, opt_context, opt_useSetImmediate) { var cb = callback; if (opt_context) { cb = goog.bind(callback, opt_context); @@ -70,11 +74,22 @@ goog.async.nextTick = function(callback, opt_context) { // which we do want to support. // See // http://connect.microsoft.com/IE/feedback/details/801823/setimmediate-and-messagechannel-are-broken-in-ie10 - if (goog.isFunction(goog.global.setImmediate) && (!goog.global.Window || + // + // 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_ = @@ -107,11 +122,14 @@ goog.async.nextTick.getSetImmediateEmulator_ = function() { // document.addEventListener. The latter excludes IE8 because it has a // synchronous postMessage implementation. if (typeof Channel === 'undefined' && typeof window !== 'undefined' && - window.postMessage && window.addEventListener) { + 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('iframe'); + var iframe = document.createElement(goog.dom.TagName.IFRAME); iframe.style.display = 'none'; iframe.src = ''; document.documentElement.appendChild(iframe); @@ -131,8 +149,10 @@ goog.async.nextTick.getSetImmediateEmulator_ = function() { '*' : 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 (e.origin != origin && e.data != message) { + // 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(); @@ -147,21 +167,23 @@ goog.async.nextTick.getSetImmediateEmulator_ = function() { }; } if (typeof Channel !== 'undefined' && - // 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. - !goog.labs.userAgent.browser.isIE()) { + (!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() { - head = head.next; - var cb = head.cb; - head.cb = null; - cb(); + if (goog.isDef(head.next)) { + head = head.next; + var cb = head.cb; + head.cb = null; + cb(); + } }; return function(cb) { tail.next = { @@ -174,9 +196,9 @@ goog.async.nextTick.getSetImmediateEmulator_ = function() { // Implementation for IE6+: Script elements fire an asynchronous // onreadystatechange event when inserted into the DOM. if (typeof document !== 'undefined' && 'onreadystatechange' in - document.createElement('script')) { + document.createElement(goog.dom.TagName.SCRIPT)) { return function(cb) { - var script = document.createElement('script'); + var script = document.createElement(goog.dom.TagName.SCRIPT); script.onreadystatechange = function() { // Clean up and call the callback. script.onreadystatechange = null; diff --git a/lib/goog/async/run.js b/lib/goog/async/run.js index 0010752..d818683 100644 --- a/lib/goog/async/run.js +++ b/lib/goog/async/run.js @@ -14,6 +14,7 @@ goog.provide('goog.async.run'); +goog.require('goog.async.WorkQueue'); goog.require('goog.async.nextTick'); goog.require('goog.async.throwException'); goog.require('goog.testing.watchers'); @@ -37,8 +38,7 @@ goog.async.run = function(callback, opt_context) { goog.async.run.workQueueScheduled_ = true; } - goog.async.run.workQueue_.push( - new goog.async.run.WorkItem_(callback, opt_context)); + goog.async.run.workQueue_.add(callback, opt_context); }; @@ -69,10 +69,19 @@ goog.async.run.initializeRunner_ = function() { * 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() { +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); + } }; }; @@ -88,18 +97,18 @@ goog.async.run.schedule_; goog.async.run.workQueueScheduled_ = false; -/** @private {!Array.} */ -goog.async.run.workQueue_ = []; +/** @private {!goog.async.WorkQueue} */ +goog.async.run.workQueue_ = new goog.async.WorkQueue(); if (goog.DEBUG) { /** - * Reset the event queue. + * Reset the work queue. * @private */ goog.async.run.resetQueue_ = function() { goog.async.run.workQueueScheduled_ = false; - goog.async.run.workQueue_ = []; + goog.async.run.workQueue_ = new goog.async.WorkQueue(); }; // If there is a clock implemenation in use for testing @@ -114,37 +123,17 @@ if (goog.DEBUG) { * goog.async.nextTick. */ goog.async.run.processWorkQueue = function() { - // NOTE: additional work queue items may be pushed while processing. - while (goog.async.run.workQueue_.length) { - // Don't let the work queue grow indefinitely. - var workItems = goog.async.run.workQueue_; - goog.async.run.workQueue_ = []; - for (var i = 0; i < workItems.length; i++) { - var workItem = workItems[i]; - try { - workItem.fn.call(workItem.scope); - } catch (e) { - goog.async.throwException(e); - } + // 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, reset the work queue. + // There are no more work items, allow processing to be scheduled again. goog.async.run.workQueueScheduled_ = false; }; - - - -/** - * @constructor - * @final - * @struct - * @private - * - * @param {function()} fn - * @param {Object|null|undefined} scope - */ -goog.async.run.WorkItem_ = function(fn, scope) { - /** @const */ this.fn = fn; - /** @const */ this.scope = scope; -}; 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 775e519..bb232f8 100644 --- a/lib/goog/base.js +++ b/lib/goog/base.js @@ -19,6 +19,7 @@ * 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 */ @@ -61,7 +62,7 @@ goog.global = this; * var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false}; * * - * @type {Object.|undefined} + * @type {Object|undefined} */ goog.global.CLOSURE_UNCOMPILED_DEFINES; @@ -79,10 +80,10 @@ goog.global.CLOSURE_UNCOMPILED_DEFINES; * * Example: *
    - *   var CLOSURE_DEFINES = {'goog.DEBUG': false};
    + *   var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
      * 
    * - * @type {Object.|undefined} + * @type {Object|undefined} */ goog.global.CLOSURE_DEFINES; @@ -144,11 +145,11 @@ goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { /** - * Defines a named value. In uncompiled mode, the value is retreived from + * 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 compiler command-line - * options. + * 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 @@ -178,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); /** @@ -222,18 +223,45 @@ goog.define('goog.TRUSTED_SITE', true); * * 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 + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode + * */ goog.define('goog.STRICT_MODE_COMPATIBLE', false); /** - * Creates object stubs for a namespace. The presence of one or more - * goog.provide() calls indicate that the file defines the given - * objects/namespaces. Provided objects must not be null or undefined. - * Build tools also scan for provide/require statements + * @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". */ @@ -244,6 +272,20 @@ goog.provide = function(name) { 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; @@ -255,33 +297,55 @@ goog.provide = function(name) { } } - goog.exportPath_(name); + goog.exportPath_(name, opt_obj); }; /** - * 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: + * 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 loaded enclosed in + * + * When a goog.module annotated file is loaded, it is 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. + * - 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. + * 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) { + if (!goog.isString(name) || + !name || + name.search(goog.VALID_MODULE_RE_) == -1) { throw Error('Invalid module identifier'); } if (!goog.isInModuleLoader_()) { @@ -304,9 +368,45 @@ goog.module = function(name) { }; -/** @private {{ - * moduleName:(string|undefined), - * exportTestMethods:boolean}|null}} +/** + * @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), + * declareTestMethods: boolean + * }} */ goog.moduleLoaderState_ = null; @@ -328,13 +428,35 @@ goog.isInModuleLoader_ = function() { * TODO(johnlenz): Make the test framework aware of goog.module so * that this isn't necessary. Alternately combine this with goog.setTestOnly * to minimize boiler plate. + * @suppress {missingProvide} + * @deprecated This approach does not translate to ES6 module syntax, instead + * use goog.testing.testSuite to declare the test methods. */ -goog.module.exportTestMethods = function() { +goog.module.declareTestMethods = function() { if (!goog.isInModuleLoader_()) { - throw new Error('goog.module.exportTestMethods must be called from ' + + throw new Error('goog.module.declareTestMethods must be called from ' + + 'within a goog.module'); + } + goog.moduleLoaderState_.declareTestMethods = true; +}; + + +/** + * 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'); } - goog.moduleLoaderState_.exportTestMethods = true; + if (!COMPILED && !goog.moduleLoaderState_.moduleName) { + throw Error('goog.module must be called prior to ' + + 'goog.module.declareLegacyNamespace.'); + } + goog.moduleLoaderState_.declareLegacyNamespace = true; }; @@ -350,7 +472,7 @@ goog.module.exportTestMethods = function() { * 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 : '.')); @@ -397,7 +519,7 @@ if (!COMPILED) { * goog.provide('goog.events.Event') implicitly declares that 'goog' and * 'goog.events' must be namespaces. * - * @type {Object.} + * @type {!Object} * @private */ goog.implicitNamespaces_ = {'goog.module': true}; @@ -437,7 +559,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. @@ -453,10 +575,10 @@ 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. + * @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. */ @@ -537,12 +659,13 @@ goog.require = function(name) { // 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)) { if (goog.isInModuleLoader_()) { - // goog.require only return a value with-in goog.module files. - return name in goog.loadedModules_ ? - goog.loadedModules_[name] : - goog.getObjectByName(name); + return goog.module.getInternal_(name); } else { return null; } @@ -606,19 +729,6 @@ 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: @@ -662,7 +772,7 @@ 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_ = []; @@ -671,17 +781,24 @@ 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 - * similiar. However in some environments the use of {@code eval} is banned + * 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.} + * @private @const {!Object} */ goog.loadedModules_ = {}; @@ -697,8 +814,7 @@ 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 + * @private {!Object} */ goog.included_ = {}; @@ -707,15 +823,28 @@ if (goog.DEPENDENCIES_ENABLED) { * This object is used to keep track of dependencies and other data that is * used for loading scripts. * @private - * @type {Object} + * @type {{ + * pathIsModule: !Object, + * nameToPath: !Object, + * requires: !Object>, + * visited: !Object, + * written: !Object, + * deferred: !Object + * }} */ goog.dependencies_ = { pathIsModule: {}, // 1 to 1 - nameToPath: {}, // many to 1 + + nameToPath: {}, // 1 to 1 + requires: {}, // 1 to many + // 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 }; @@ -743,11 +872,12 @@ if (goog.DEPENDENCIES_ENABLED) { return; } 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') { @@ -775,8 +905,8 @@ if (goog.DEPENDENCIES_ENABLED) { /** @const @private {boolean} */ - goog.IS_OLD_IE_ = goog.global.document && - goog.global.document.all && !goog.global.atob; + goog.IS_OLD_IE_ = !goog.global.atob && goog.global.document && + goog.global.document.all; /** @@ -795,47 +925,10 @@ if (goog.DEPENDENCIES_ENABLED) { }; - /** @private {Array.} */ + /** @private {!Array} */ goog.queuedModules_ = []; - /** - * Retrieve and execute a module. - * @param {string} src Script source URL. - * @private - */ - goog.retrieveAndExecModule_ = function(src) { - var importScript = goog.global.CLOSURE_IMPORT_SCRIPT || - goog.writeScriptTag_; - - var scriptText = null; - - var xhr = new goog.global['XMLHttpRequest'](); - - /** @this {Object} */ - xhr.onload = function() { - scriptText = this.responseText; - }; - xhr.open('get', src, false); - xhr.send(); - - scriptText = xhr.responseText; - - if (scriptText != null) { - var execModuleScript = goog.wrapModule_(src, scriptText); - var isOldIE = goog.IS_OLD_IE_; - if (isOldIE) { - goog.queuedModules_.push(execModuleScript); - } else { - importScript(src, execModuleScript); - } - goog.dependencies_.written[src] = true; - } else { - throw new Error('load of ' + src + 'failed'); - } - }; - - /** * Return an appropriate module text. Suitable to insert into * a script tag (that is unescaped). @@ -863,9 +956,30 @@ if (goog.DEPENDENCIES_ENABLED) { } }; + // 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. /** - * Load any deferred goog.module loads. + * Handle any remaining deferred goog.module evals. * @private */ goog.loadQueuedModules_ = function() { @@ -874,13 +988,76 @@ if (goog.DEPENDENCIES_ENABLED) { var queue = goog.queuedModules_; goog.queuedModules_ = []; for (var i = 0; i < count; i++) { - var entry = queue[i]; - goog.globalEval(entry); + 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); + } + }; + + /** * @param {function(?):?|string} moduleDef The module definition. */ @@ -890,9 +1067,10 @@ if (goog.DEPENDENCIES_ENABLED) { // 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, exportTestMethods: false}; + moduleName: undefined, declareTestMethods: false}; var exports; if (goog.isFunction(moduleDef)) { exports = moduleDef.call(goog.global, {}); @@ -902,34 +1080,43 @@ if (goog.DEPENDENCIES_ENABLED) { throw Error('Invalid module definition'); } - if (Object.seal) { - Object.seal(exports); - } 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; - if (goog.moduleLoaderState_.exportTestMethods) { + if (goog.moduleLoaderState_.declareTestMethods) { for (var entry in exports) { if (entry.indexOf('test', 0) === 0 || entry == 'tearDown' || - entry == 'setup') { + entry == 'setUp' || + entry == 'setUpPage' || + entry == 'tearDownPage') { goog.global[entry] = exports[entry]; } } } } finally { - goog.moduleLoaderState_ = null; + goog.moduleLoaderState_ = previousState; } }; /** - * @private @const {function(string):?} + * @param {string} source + * @return {!Object} + * @private */ - goog.loadModuleFromSource_ = function() { + goog.loadModuleFromSource_ = function(source) { // NOTE: we avoid declaring parameters or local variables here to avoid // masking globals or leaking values into the module definition. 'use strict'; @@ -939,6 +1126,50 @@ if (goog.DEPENDENCIES_ENABLED) { }; + /** + * 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( + ' + + \ No newline at end of file diff --git a/lib/test/data/click_tests/overlapping_elements.html b/lib/test/data/click_tests/overlapping_elements.html new file mode 100644 index 0000000..16c4bd9 --- /dev/null +++ b/lib/test/data/click_tests/overlapping_elements.html @@ -0,0 +1,59 @@ + + + + An element that disappears on click + + + +

    Hello

    +
    +
    +

    Log:

    +

    + + + \ No newline at end of file diff --git a/lib/test/data/coordinates_tests/page_with_fixed_element.html b/lib/test/data/coordinates_tests/page_with_fixed_element.html index b815891..6cbb273 100644 --- a/lib/test/data/coordinates_tests/page_with_fixed_element.html +++ b/lib/test/data/coordinates_tests/page_with_fixed_element.html @@ -5,7 +5,7 @@
    fixed red box
    -
    Placeholder
    +
    Placeholder
    Element at the bottom
    Tex after box
    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/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 86dea38..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'); @@ -103,6 +109,8 @@ var Pages = (function() { var Path = { 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', @@ -110,85 +118,31 @@ var Path = { 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 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 1a435a0..f6ccc11 100644 --- a/lib/test/index.js +++ b/lib/test/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'; @@ -21,58 +23,76 @@ 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_FIREFOX: 'remote.firefox', - REMOTE_PHANTOMJS: 'remote.phantomjs' -}; + fileserver = require('./fileserver'); /** * Browsers with native support. - * @type {!Array.} + * @type {!Array.} */ var NATIVE_BROWSERS = [ - Browser.CHROME, - Browser.FIREFOX, - 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 startServer = !!serverJar && !remoteUrl; +var nativeRun = !serverJar && !remoteUrl; + + var browsersToTest = (function() { - var browsers = process.env['SELENIUM_BROWSERS'] || Browser.FIREFOX; - 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); + } + return browsers; })(); @@ -85,10 +105,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; }; } @@ -100,85 +117,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.__defineSetter__('driver', function(d) { - if (driver) throw Error('Driver already created'); - driver = d; - }); + 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) { - var d = driver; - driver = null; - return d.quit(); - } - }; - - this.waitForTitleToBe = function(expected) { - driver.wait(function() { - return driver.getTitle().then(function(title) { - return title === expected; - }); - }, 5000, 'Waiting for title to be ' + expected); - }; } @@ -202,47 +172,51 @@ 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() { + + 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(); + }); + } + } + var serverToUse = null; - if (NATIVE_BROWSERS.indexOf(browser) == -1) { - serverToUse = seleniumServer; - if (!serverToUse) { - serverToUse = seleniumServer = new seleniumserver.Server(); + if (!!serverJar && !remoteUrl) { + if (!(serverToUse = seleniumServer)) { + serverToUse = seleniumServer = new remote.SeleniumServer(serverJar); } + testing.before(function() { - // Starting the server may require a build, so disable timeouts. this.timeout(0); return seleniumServer.start(60 * 1000); }); } - - var env = new TestEnvironment(browser, serverToUse); - - testing.beforeEach(function() { - if (env.autoCreateDriver) { - return env.createDriver().getSession(); // Catch start-up failures. - } - }); - - testing.after(function() { - return env.dispose(); - }); - - fn(env); + fn(new TestEnvironment(browser, serverToUse)); }); }); } finally { @@ -253,24 +227,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); - -if (_base.isDevMode() && browsersToTest.indexOf(Browser.FIREFOX) != -1) { - testing.before(function() { - return build.of('//javascript/firefox-driver:webdriver').onlyOnce().go(); - }); -} - -// Server is only started if required for a specific config. testing.after(function() { - if (seleniumServer) { - seleniumServer.stop(); - } + return fileserver.stop(); }); - // PUBLIC API @@ -282,6 +249,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/webdriver/actionsequence.js b/lib/webdriver/actionsequence.js index 8aac13d..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).getId(). - 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..27e7eaf 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,8 +198,8 @@ 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.'); 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 3265864..101d54a 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. @@ -21,7 +24,8 @@ goog.provide('webdriver.Capabilities'); goog.provide('webdriver.Capability'); goog.provide('webdriver.ProxyConfig'); -goog.require('webdriver.logging.Preferences'); +goog.require('webdriver.Serializable'); +goog.require('webdriver.logging'); @@ -33,6 +37,7 @@ webdriver.Browser = { ANDROID: 'android', CHROME: 'chrome', FIREFOX: 'firefox', + IE: 'internet explorer', INTERNET_EXPLORER: 'internet explorer', IPAD: 'iPad', IPHONE: 'iPhone', @@ -70,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', @@ -83,8 +87,8 @@ webdriver.Capability = { BROWSER_NAME: 'browserName', /** - * Another capability for browser name, to support BrowserStack - * + * Additional capability, which contains browser name, to support webdriver + * tests on BrowserStack */ BROWSER: 'browser', @@ -99,7 +103,7 @@ webdriver.Capability = { /** * 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', @@ -132,7 +136,7 @@ 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', @@ -167,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); /** @@ -247,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. @@ -287,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_; }; diff --git a/lib/webdriver/command.js b/lib/webdriver/command.js index 3db8443..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. @@ -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 5b200cc..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,7 +116,7 @@ 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': parameters['sessionId'], 'parameters': parameters @@ -152,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 3712948..ac0909d 100644 --- a/lib/webdriver/logging.js +++ b/lib/webdriver/logging.js @@ -1,35 +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; + + +/** @const */ +exports.LogRecord = LogRecord; + -goog.provide('webdriver.logging'); -goog.provide('webdriver.logging.Preferences'); +/** @const */ +exports.Logger = Logger; -goog.require('goog.object'); + +/** @const */ +exports.Level = Logger.Level; + + +/** + * 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') { + 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 + */ +exports.addConsoleHandler = function(opt_logger) { + var logger = opt_logger || getLogger(); + logger.addHandler(consoleHandler); +}; + + +/** + * Installs the console log handler on the root logger. + * @see exports.addConsoleHandler + */ +exports.installConsoleHandler = function() { + exports.addConsoleHandler(); +}; /** - * Logging levels. - * @enum {{value: number, name: string}} + * 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 */ -webdriver.logging.Level = { - ALL: {value: Number.MIN_VALUE, name: 'ALL'}, - DEBUG: {value: 700, name: 'DEBUG'}, - INFO: {value: 800, name: 'INFO'}, - WARNING: {value: 900, name: 'WARNING'}, - SEVERE: {value: 1000, name: 'SEVERE'}, - OFF: {value: Number.MAX_VALUE, name: 'OFF'} +exports.removeConsoleHandler = function(opt_logger) { + var logger = opt_logger || getLogger(); + logger.removeHandler(consoleHandler); }; @@ -39,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. */ @@ -67,107 +243,105 @@ webdriver.logging.Type = { /** Logs from the remote server. */ SERVER: 'server' }; +exports.Type = Type; /** * Describes the log preferences for a WebDriver session. - * @constructor + * @final */ -webdriver.logging.Preferences = function() { - /** @private {!Object.} */ - this.prefs_ = {}; -}; +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); + }, -/** - * Sets the desired logging level for a particular log type. - * @param {(string|webdriver.logging.Type)} type The log type. - * @param {!webdriver.logging.Level} level The desired log level. - */ -webdriver.logging.Preferences.prototype.setLevel = function(type, level) { - this.prefs_[type] = level; -}; - - -/** - * Converts this instance to its JSON representation. - * @return {!Object.} The JSON representation of this set of - * preferences. - */ -webdriver.logging.Preferences.prototype.toJSON = function() { - var obj = {}; - for (var type in this.prefs_) { - if (this.prefs_.hasOwnProperty(type)) { - obj[type] = this.prefs_[type].name; + /** + * 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; } - 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) { - - /** @type {!webdriver.logging.Level} */ - this.level = - goog.isString(level) ? webdriver.logging.getLevel(level) : level; +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 {string} */ - this.message = message; + /** @type {!Logger.Level} */ + this.level = goog.isString(level) ? getLevel(level) : level; - /** @type {number} */ - this.timestamp = goog.isNumber(opt_timestamp) ? opt_timestamp : goog.now(); - - /** @type {string} */ - this.type = opt_type || ''; -}; + /** @type {string} */ + this.message = message; + /** @type {number} */ + this.timestamp = goog.isNumber(opt_timestamp) ? opt_timestamp : goog.now(); -/** - * @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 - }; -}; + /** @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); + } + }, -/** - * 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 7debe44..5756429 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 @@ -46,18 +49,95 @@ * http://wiki.commonjs.org/wiki/Promises. */ -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.provide('webdriver.promise.Thenable'); +goog.module('webdriver.promise'); +goog.module.declareLegacyNamespace(); + +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 Objects = goog.require('goog.object'); +var EventEmitter = goog.require('webdriver.EventEmitter'); +var stacktrace = goog.require('webdriver.stacktrace'); + + + +/** + * @define {boolean} Whether to append traces of {@code then} to rejection + * errors. + */ +goog.define('webdriver.promise.LONG_STACK_TRACES', false); + +/** @const */ +var promise = exports; + + +/** + * 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. + */ +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; +}; + + +/** + * Error used when the computation of a promise is cancelled. + * + * @param {string=} opt_msg The cancellation message. + * @constructor + * @extends {DebugError} + * @final + */ +promise.CancellationError = function(opt_msg) { + DebugError.call(this, opt_msg); + + /** @override */ + this.name = 'CancellationError'; +}; +goog.inherits(promise.CancellationError, DebugError); + -goog.require('goog.array'); -goog.require('goog.debug.Error'); -goog.require('goog.object'); -goog.require('webdriver.EventEmitter'); -goog.require('webdriver.stacktrace.Snapshot'); +/** + * Wraps the given error in a CancellationError. Will trivially return + * the error itself if it is an instanceof CancellationError. + * + * @param {*} error The error to wrap. + * @param {string=} opt_msg The prefix message to use. + * @return {!promise.CancellationError} A cancellation error. + */ +promise.CancellationError.wrap = function(error, opt_msg) { + if (error instanceof promise.CancellationError) { + return /** @type {!promise.CancellationError} */(error); + } else if (opt_msg) { + var message = opt_msg; + if (error) { + message += ': ' + error; + } + return new promise.CancellationError(message); + } + var message; + if (error) { + message = error + ''; + } + return new promise.CancellationError(message); +}; @@ -66,119 +146,114 @@ goog.require('webdriver.stacktrace.Snapshot'); * used to schedule callbacks on a promised value. * * @interface + * @extends {IThenable} * @template T */ -webdriver.promise.Thenable = function() {}; +promise.Thenable = function() {}; /** * Cancels the computation of this promise's value, rejecting the promise in the - * process. This method is a no-op if the promise has alreayd been resolved. + * process. This method is a no-op if the promise has already been resolved. * - * @param {*=} opt_reason The reason this promise is being cancelled. If not an - * {@code Error}, one will be created using the value's string - * representation. + * @param {(string|promise.CancellationError)=} opt_reason The reason this + * promise is being cancelled. */ -webdriver.promise.Thenable.prototype.cancel = function(opt_reason) {}; +promise.Thenable.prototype.cancel = function(opt_reason) {}; /** @return {boolean} Whether this promise's value is still being computed. */ -webdriver.promise.Thenable.prototype.isPending = function() {}; +promise.Thenable.prototype.isPending = function() {}; /** * Registers listeners for when this instance is resolved. * - * @param {?(function(T): (R|webdriver.promise.Promise.))=} opt_callback The + * @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|webdriver.promise.Promise.))=} 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 + * @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 */ -webdriver.promise.Thenable.prototype.then = function( - opt_callback, opt_errback) {}; +promise.Thenable.prototype.then = function(opt_callback, opt_errback) {}; /** * 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);
    - *   });
    - * 
    + * // Synchronous API: + * try { + * doSynchronousWork(); + * } catch (ex) { + * console.error(ex); + * } * - * @param {function(*): (R|webdriver.promise.Promise.)} 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 + * // 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 */ -webdriver.promise.Thenable.prototype.thenCatch = function(errback) {}; +promise.Thenable.prototype.thenCatch = function(errback) {}; /** * 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);
    - * 
    + * // Synchronous API: + * try { + * doSynchronousWork(); + * } finally { + * cleanUp(); + * } + * + * // Asynchronous promise API: + * doAsynchronousWork().thenFinally(cleanUp); * - * Note: similar to the {@code finally} clause, if the registered + * __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
    - *       });
    - * 
    + * 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|webdriver.promise.Promise.)} callback The function + * @param {function(): (R|IThenable)} callback The function * to call when this promise is resolved. - * @return {!webdriver.promise.Promise.} A promise that will be fulfilled + * @return {!promise.Promise} A promise that will be fulfilled * with the callback result. * @template R */ -webdriver.promise.Thenable.prototype.thenFinally = function(callback) {}; +promise.Thenable.prototype.thenFinally = function(callback) {}; /** * Property used to flag constructor's as implementing the Thenable interface * for runtime type checking. - * @private {string} + * @type {string} * @const */ -webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_ = '$webdriver_Thenable'; +var IMPLEMENTED_BY_PROP = '$webdriver_Thenable'; /** @@ -186,10 +261,10 @@ webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_ = '$webdriver_Thenable'; * 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: webdriver.promise.Thenable, ...[?])} ctor The + * @param {function(new: promise.Thenable, ...?)} ctor The * constructor whose prototype to modify. */ -webdriver.promise.Thenable.addImplementation = function(ctor) { +promise.Thenable.addImplementation = function(ctor) { // Based on goog.promise.Thenable.isImplementation. ctor.prototype['then'] = ctor.prototype.then; try { @@ -197,10 +272,10 @@ webdriver.promise.Thenable.addImplementation = function(ctor) { // DOM elements. Object.defineProperty( ctor.prototype, - webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_, + IMPLEMENTED_BY_PROP, {'value': true, 'enumerable': false}); } catch (ex) { - ctor.prototype[webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_] = true; + ctor.prototype[IMPLEMENTED_BY_PROP] = true; } }; @@ -212,13 +287,13 @@ webdriver.promise.Thenable.addImplementation = function(ctor) { * @return {boolean} Whether the object is an implementation of the Thenable * interface. */ -webdriver.promise.Thenable.isImplementation = function(object) { +promise.Thenable.isImplementation = function(object) { // Based on goog.promise.Thenable.isImplementation. if (!object) { return false; } try { - return !!object[webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_]; + return !!object[IMPLEMENTED_BY_PROP]; } catch (e) { return false; // Property access seems to be forbidden. } @@ -226,367 +301,453 @@ webdriver.promise.Thenable.isImplementation = function(object) { +/** + * @enum {string} + */ +var PromiseState = { + PENDING: 'pending', + BLOCKED: 'blocked', + REJECTED: 'rejected', + FULFILLED: 'fulfilled' +}; + + + /** * 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 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. * + * @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. * @constructor - * @implements {webdriver.promise.Thenable.} + * @implements {promise.Thenable} * @template T * @see http://promises-aplus.github.io/promises-spec/ */ -webdriver.promise.Promise = function() {}; -webdriver.promise.Thenable.addImplementation(webdriver.promise.Promise); +promise.Promise = function(resolver, opt_flow) { + goog.getUid(this); + /** @private {!promise.ControlFlow} */ + this.flow_ = opt_flow || promise.controlFlow(); -/** @override */ -webdriver.promise.Promise.prototype.cancel = function(reason) { - throw new TypeError('Unimplemented function: "cancel"'); -}; + /** @private {Error} */ + this.stack_ = null; + if (promise.LONG_STACK_TRACES) { + this.stack_ = promise.captureStackTrace('Promise', 'new', promise.Promise); + } + /** @private {promise.Promise} */ + this.parent_ = null; -/** @override */ -webdriver.promise.Promise.prototype.isPending = function() { - throw new TypeError('Unimplemented function: "isPending"'); -}; + /** @private {Array} */ + this.callbacks_ = null; + /** @private {PromiseState} */ + this.state_ = PromiseState.PENDING; -/** @override */ -webdriver.promise.Promise.prototype.then = function( - opt_callback, opt_errback) { - throw new TypeError('Unimplemented function: "then"'); -}; + /** @private {boolean} */ + this.handled_ = false; + /** @private {boolean} */ + this.pendingNotifications_ = false; -/** @override */ -webdriver.promise.Promise.prototype.thenCatch = function(errback) { - return this.then(null, errback); + /** @private {*} */ + this.value_ = undefined; + + 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); + } }; +promise.Thenable.addImplementation(promise.Promise); /** @override */ -webdriver.promise.Promise.prototype.thenFinally = function(callback) { - return this.then(callback, function(err) { - var value = callback(); - if (webdriver.promise.isPromise(value)) { - return value.then(function() { - throw err; - }); - } - throw err; - }); +promise.Promise.prototype.toString = function() { + return 'Promise::' + goog.getUid(this) + + ' {[[PromiseStatus]]: "' + this.state_ + '"}'; }; - /** - * 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. - * - *

    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.} - * @template T + * 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 */ -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.Promise.prototype.resolve_ = function(newState, newValue) { + if (PromiseState.PENDING !== this.state_) { + return; + } - /** - * 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.} - */ - var listeners = []; + if (newValue === this) { + // See promise a+, 2.3.1 + // http://promises-aplus.github.io/promises-spec/#point-48 + throw new TypeError('A promise may not resolve to itself'); + } - /** - * 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; + this.parent_ = null; + this.state_ = PromiseState.BLOCKED; - /** - * Key for the timeout used to delay reproting an unhandled rejection to the - * parent {@link webdriver.promise.ControlFlow}. - * @type {?number} - */ - var pendingRejectionKey = null; + 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; - /** - * This Deferred's current state. - * @type {!webdriver.promise.Deferred.State_} - */ - var state = webdriver.promise.Deferred.State_.PENDING; + } else if (goog.isObject(newValue)) { + // 2.3.3 - /** - * This Deferred's resolved value; set when the state transitions from - * {@code webdriver.promise.Deferred.State_.PENDING}. - * @type {*} - */ - var value; + 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; + } - /** @return {boolean} Whether this promise's value is still pending. */ - function isPending() { - return state == webdriver.promise.Deferred.State_.PENDING; + // NB: goog.isFunction is loose and will accept instanceof Function. + if (typeof then === 'function') { + // 2.3.3.3 + this.invokeThen_(newValue, then); + return; + } } - /** - * Removes all of the listeners previously registered on this deferred. - * @throws {Error} If this deferred has already been resolved. - */ - function removeAll() { - listeners = []; + if (newState === PromiseState.REJECTED && + isError(newValue) && newValue.stack && this.stack_) { + newValue.stack += '\nFrom: ' + (this.stack_.stack || this.stack_); } - /** - * 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. - */ - function resolve(newState, newValue) { - if (webdriver.promise.Deferred.State_.PENDING !== state) { - return; + // 2.3.3.4 and 2.3.4 + this.state_ = newState; + this.value_ = newValue; + this.scheduleNotifications_(); +}; + + +/** + * 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 + */ +promise.Promise.prototype.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); } + }; - if (newValue === self) { - // See promise a+, 2.3.1 - // http://promises-aplus.github.io/promises-spec/#point-48 - throw TypeError('A promise may not resolve to itself'); + var rejectPromise = function(reason) { + if (!called) { // 2.3.3.3.3 + called = true; + // 2.3.3.3.2 + self.unblockAndResolve_(PromiseState.REJECTED, reason); } + }; - state = webdriver.promise.Deferred.State_.BLOCKED; + try { + // 2.3.3.3 + then.call(x, resolvePromise, rejectPromise); + } catch (e) { + // 2.3.3.3.4.2 + rejectPromise(e); + } +}; - if (webdriver.promise.isPromise(newValue)) { - 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); - } - } else { - notifyAll(newState, newValue); - } +/** + * @param {PromiseState} newState The promise's new state. + * @param {*} newValue The promise's new value. + * @private + */ +promise.Promise.prototype.unblockAndResolve_ = function(newState, newValue) { + if (this.state_ === PromiseState.BLOCKED) { + this.state_ = PromiseState.PENDING; + this.resolve_(newState, newValue); } +}; - /** - * 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. - */ - 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)); - } - state = newState; - value = newValue; - while (listeners.length) { - notify(listeners.shift()); +/** + * @private + */ +promise.Promise.prototype.scheduleNotifications_ = function() { + if (!this.pendingNotifications_) { + this.pendingNotifications_ = true; + this.flow_.suspend_(); + + var activeFrame; + + if (!this.handled_ && + this.state_ === PromiseState.REJECTED && + !(this.value_ instanceof promise.CancellationError)) { + activeFrame = this.flow_.getActiveFrame_(); + activeFrame.pendingRejection = true; } - if (!handled && state == webdriver.promise.Deferred.State_.REJECTED) { - flow.pendingRejections_ += 1; - pendingRejectionKey = flow.timer.setTimeout(function() { - pendingRejectionKey = null; - flow.pendingRejections_ -= 1; - flow.abortFrame_(value); - }, 0); + if (this.callbacks_ && this.callbacks_.length) { + activeFrame = this.flow_.getRunningFrame_(); + var self = this; + this.callbacks_.forEach(function(callback) { + if (!callback.frame_.getParent()) { + activeFrame.addChild(callback.frame_); + } + }); } + + asyncRun(goog.bind(this.notifyAll_, this, activeFrame)); } +}; - /** - * Notifies a single listener of this Deferred's change in state. - * @param {!webdriver.promise.Deferred.Listener_} listener The listener to - * notify. - */ - 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); - } + +/** + * Notifies all of the listeners registered with this promise that its state + * has changed. + * @param {Frame} frame The active frame from when this round of + * notifications were scheduled. + * @private + */ +promise.Promise.prototype.notifyAll_ = function(frame) { + this.flow_.resume_(); + this.pendingNotifications_ = false; + + if (!this.handled_ && + this.state_ === PromiseState.REJECTED && + !(this.value_ instanceof promise.CancellationError)) { + this.flow_.abortFrame_(this.value_, frame); } - /** - * The consumer promise for this instance. Provides protected access to the - * callback registering functions. - * @type {!webdriver.promise.Promise.} - */ - var promise = new webdriver.promise.Promise(); + if (this.callbacks_) { + var callbacks = this.callbacks_; + this.callbacks_ = null; + callbacks.forEach(this.notify_, this); + } +}; - /** - * Registers a callback on this Deferred. - * - * @param {?(function(T): (R|webdriver.promise.Promise.))=} opt_callback . - * @param {?(function(*): (R|webdriver.promise.Promise.))=} opt_errback . - * @return {!webdriver.promise.Promise.} A new promise representing the - * result of the callback. - * @template R - * @see webdriver.promise.Promise#then - */ - 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 !== null) { - flow.pendingRejections_ -= 1; - flow.timer.clearTimeout(pendingRejectionKey); - pendingRejectionKey = null; - } +/** + * Notifies a single callback of this promise's change ins tate. + * @param {Callback} callback The callback to notify. + * @private + */ +promise.Promise.prototype.notify_ = function(callback) { + callback.notify(this.state_, this.value_); +}; - var deferred = new webdriver.promise.Deferred(cancel, flow); - var listener = { - callback: opt_callback, - errback: opt_errback, - fulfill: deferred.fulfill, - reject: deferred.reject - }; - if (state == webdriver.promise.Deferred.State_.PENDING || - state == webdriver.promise.Deferred.State_.BLOCKED) { - listeners.push(listener); - } else { - notify(listener); +/** @override */ +promise.Promise.prototype.cancel = function(opt_reason) { + if (!this.isPending()) { + return; + } + + if (this.parent_) { + this.parent_.cancel(opt_reason); + } else { + this.resolve_( + PromiseState.REJECTED, + promise.CancellationError.wrap(opt_reason)); + } +}; + + +/** @override */ +promise.Promise.prototype.isPending = function() { + return this.state_ === PromiseState.PENDING; +}; + + +/** @override */ +promise.Promise.prototype.then = function(opt_callback, opt_errback) { + return this.addCallback_( + opt_callback, opt_errback, 'then', promise.Promise.prototype.then); +}; + + +/** @override */ +promise.Promise.prototype.thenCatch = function(errback) { + return this.addCallback_( + null, errback, 'thenCatch', promise.Promise.prototype.thenCatch); +}; + + +/** @override */ +promise.Promise.prototype.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; } + }); +}; - return deferred.promise; + +/** + * 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 + */ +promise.Promise.prototype.addCallback_ = function(callback, errback, name, fn) { + if (!goog.isFunction(callback) && !goog.isFunction(errback)) { + return this; } - var self = this; + this.handled_ = true; + var cb = new Callback(this, callback, errback, name, fn); - /** - * 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 {T=} opt_value The fulfilled value. - */ - function fulfill(opt_value) { - resolve(webdriver.promise.Deferred.State_.RESOLVED, opt_value); + if (!this.callbacks_) { + this.callbacks_ = []; } + this.callbacks_.push(cb); + + if (this.state_ !== PromiseState.PENDING && + this.state_ !== PromiseState.BLOCKED) { + this.flow_.getSchedulingFrame_().addChild(cb.frame_); + this.scheduleNotifications_(); + } + return cb.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. + * + * @param {promise.ControlFlow=} opt_flow The control flow + * this instance was created under. This should only be provided during + * unit tests. + * @constructor + * @implements {promise.Thenable} + * @template T + */ +promise.Deferred = function(opt_flow) { + var fulfill, reject; + + /** @type {!promise.Promise} */ + this.promise = new promise.Promise(function(f, r) { + fulfill = f; + reject = r; + }, opt_flow); + + var self = this; + var checkNotSelf = function(value) { + if (value === self) { + throw new TypeError('May not resolve a Deferred with itself'); + } + }; /** - * 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}. + * 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. */ - function reject(opt_error) { - resolve(webdriver.promise.Deferred.State_.REJECTED, opt_error); - } + this.fulfill = function(opt_value) { + checkNotSelf(opt_value); + fulfill(opt_value); + }; /** - * 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. + * 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. */ - function cancel(opt_reason) { - if (!isPending()) { - return; - } + this.reject = function(opt_reason) { + checkNotSelf(opt_reason); + reject(opt_reason); + }; +}; +promise.Thenable.addImplementation(promise.Deferred); - if (opt_canceller) { - opt_reason = opt_canceller(opt_reason) || opt_reason; - } - reject(opt_reason); - } +/** @override */ +promise.Deferred.prototype.isPending = function() { + return this.promise.isPending(); +}; - 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; - } +/** @override */ +promise.Deferred.prototype.cancel = function(opt_reason) { + this.promise.cancel(opt_reason); +}; + - // 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); +/** + * @override + * @deprecated Use {@code then} from the promise property directly. + */ +promise.Deferred.prototype.then = function(opt_cb, opt_eb) { + return this.promise.then(opt_cb, opt_eb); }; -goog.inherits(webdriver.promise.Deferred, webdriver.promise.Promise); /** - * Type definition for a listener registered on a Deferred object. - * @typedef {{callback:(Function|undefined), - * errback:(Function|undefined), - * fulfill: function(*), reject: function(*)}} - * @private + * @override + * @deprecated Use {@code thenCatch} from the promise property directly. */ -webdriver.promise.Deferred.Listener_; +promise.Deferred.prototype.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 + * @override + * @deprecated Use {@code thenFinally} from the promise property directly. */ -webdriver.promise.Deferred.State_ = { - REJECTED: -1, - PENDING: 0, - BLOCKED: 1, - RESOLVED: 2 +promise.Deferred.prototype.thenFinally = function(opt_cb) { + return this.promise.thenFinally(opt_cb); }; @@ -595,12 +756,11 @@ 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); @@ -614,11 +774,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'; }; @@ -626,44 +788,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 {T=} opt_value The resolved value. - * @return {!webdriver.promise.Promise.} The resolved promise. + * @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); + }); }; @@ -671,42 +835,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. * @param {...?} var_args The arguments to apply to the function, excluding the * final callback. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the + * @return {!promise.Promise} A promise that will be resolved with the * result of the provided function's callback. */ -webdriver.promise.checkedNodeCall = function(fn, var_args) { - 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 { - var args = goog.array.slice(arguments, 1); - args.push(function(error, value) { - error ? deferred.reject(error) : deferred.fulfill(value); - }); - fn.apply(null, args); - } catch (ex) { - deferred.reject(ex); - } - return deferred.promise; }; @@ -719,18 +883,16 @@ webdriver.promise.checkedNodeCall = function(fn, var_args) { * 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 (webdriver.promise.Thenable.isImplementation(value)) { +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); }; @@ -744,8 +906,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? @@ -766,38 +928,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); + } + }); }; @@ -806,27 +967,52 @@ 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(arr) { + goog.asserts.assertNumber(arr.length, 'not an array like value'); + 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); + }); }); }; @@ -835,31 +1021,54 @@ 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(arr) { + goog.asserts.assertNumber(arr.length, 'not an array like value'); + 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); }); }); }; @@ -875,45 +1084,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) && @@ -921,76 +1127,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 // ////////////////////////////////////////////////////////////////////////////// @@ -1002,94 +1204,126 @@ 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. + * {@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. * - * @param {webdriver.promise.ControlFlow.Timer=} opt_timer The timer object - * to use. Should only be set for testing. * @constructor - * @extends {webdriver.EventEmitter} + * @extends {EventEmitter} + * @final */ -webdriver.promise.ControlFlow = function(opt_timer) { - webdriver.EventEmitter.call(this); +promise.ControlFlow = function() { + EventEmitter.call(this); + goog.getUid(this); /** - * The timer used by this instance. - * @type {webdriver.promise.ControlFlow.Timer} + * Tracks the active execution frame for this instance. Lazily initialized + * when the first task is scheduled. + * @private {Frame} */ - this.timer = opt_timer || webdriver.promise.ControlFlow.defaultTimer; + this.activeFrame_ = null; /** - * 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.} + * A reference to the frame which is currently top of the stack in + * {@link #runInFrame_}. The {@link #activeFrame_} will always be an ancestor + * of the {@link #runningFrame_}, but the two will often not be the same. The + * active frame represents which frame is currently executing a task while the + * running frame represents either the task itself or a promise callback which + * has fired asynchronously. + * @private {Frame} */ - this.history_ = []; -}; -goog.inherits(webdriver.promise.ControlFlow, webdriver.EventEmitter); + this.runningFrame_ = 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 {Frame} + * @see {#runInFrame_} + */ + this.schedulingFrame_ = null; -/** - * @typedef {{clearInterval: function(number), - * clearTimeout: function(number), - * setInterval: function(!Function, number): number, - * setTimeout: function(!Function, number): number}} - */ -webdriver.promise.ControlFlow.Timer; + /** + * 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; + /** + * Micro task used to trigger execution of this instance's event loop. + * @private {MicroTask} + */ + this.eventLoopTask_ = null; -/** - * 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) - }; + /** + * ID for a long running interval used to keep a Node.js process running + * while a control flow's event loop has yielded. This is a cheap hack + * required since the {@link #runEventLoop_} is 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. + * + * An alternative solution would be to change {@link #runEventLoop_} to run + * as an interval rather than as on-demand micro-tasks. While this approach + * (which was previously used) requires fewer micro-task allocations, it + * results in many unnecessary invocations of {@link #runEventLoop_}. + * + * @private {?number} + */ + this.hold_ = null; - 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]); - }; - } -})(); + /** + * The number of holds placed on this flow. These represent points where the + * flow must not execute any further actions so an asynchronous action may + * run first. One such example are notifications fired by a + * {@link webdriver.promise.Promise}: the Promise spec requires that callbacks + * are invoked in a turn of the event loop after they are scheduled. To ensure + * tasks within a callback are scheduled in the correct frame, a promise will + * make the parent flow yield before its notifications are fired. + * @private {number} + */ + this.yieldCount_ = 0; +}; +goog.inherits(promise.ControlFlow, EventEmitter); /** * Events that may be emitted by an {@link webdriver.promise.ControlFlow}. * @enum {string} */ -webdriver.promise.ControlFlow.EventType = { +promise.ControlFlow.EventType = { /** Emitted when all tasks have been successfully executed. */ IDLE: 'idle', @@ -1111,95 +1345,23 @@ webdriver.promise.ControlFlow.EventType = { /** - * 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_} + * 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 */ -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; +promise.ControlFlow.prototype.toString = function() { + return this.getSchedule(); +}; /** * Resets this instance, clearing its queue and removing all event listeners. */ -webdriver.promise.ControlFlow.prototype.reset = function() { +promise.ControlFlow.prototype.reset = function() { this.activeFrame_ = null; - this.clearHistory(); - this.emit(webdriver.promise.ControlFlow.EventType.RESET); + this.schedulingFrame_ = null; + this.emit(promise.ControlFlow.EventType.RESET); this.removeAllListeners(); this.cancelShutdown_(); this.cancelEventLoop_(); @@ -1207,98 +1369,104 @@ webdriver.promise.ControlFlow.prototype.reset = function() { /** - * 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. + * 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. */ -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()); +promise.ControlFlow.prototype.getSchedule = function(opt_includeStackTraces) { + var ret = 'ControlFlow::' + goog.getUid(this); + var activeFrame = this.activeFrame_; + var runningFrame = this.runningFrame_; + if (!activeFrame) { + return ret; } + var childIndent = '| '; + return ret + '\n' + toStringHelper(activeFrame.getRoot(), childIndent); - 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; + /** + * @param {!(Frame|Task)} node . + * @param {string} indent . + * @param {boolean=} opt_isPending . + * @return {string} . + */ + function toStringHelper(node, indent, opt_isPending) { + var ret = node.toString(); + if (opt_isPending) { + ret = '(pending) ' + ret; + } + if (node === activeFrame) { + ret = '(active) ' + ret; + } + if (node === runningFrame) { + ret = '(running) ' + ret; + } + if (node instanceof Frame) { + if (node.getPendingTask()) { + ret += '\n' + toStringHelper( + /** @type {!Task} */(node.getPendingTask()), + childIndent, + true); + } + if (node.children_) { + node.children_.forEach(function(child) { + if (!node.getPendingTask() || + node.getPendingTask().getFrame() !== child) { + ret += '\n' + toStringHelper(child, childIndent); + } + }); + } + } else { + var task = /** @type {!Task} */(node); + if (opt_includeStackTraces && task.promise.stack_) { + ret += '\n' + childIndent + + (task.promise.stack_.stack || task.promise.stack_). + replace(/\n/g, '\n' + childIndent); + } + if (task.getFrame()) { + ret += '\n' + toStringHelper( + /** @type {!Frame} */(task.getFrame()), + childIndent); + } + } + return indent + ret.replace(/\n/g, '\n' + indent); } - 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. + * @return {!Frame} The active frame for this flow. + * @private */ -webdriver.promise.ControlFlow.prototype.annotateError = function(e) { - if (!!e[webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_]) { - return e; +promise.ControlFlow.prototype.getActiveFrame_ = function() { + this.cancelShutdown_(); + if (!this.activeFrame_) { + this.activeFrame_ = new Frame(this); + this.activeFrame_.once(Frame.ERROR_EVENT, this.abortNow_, this); + this.scheduleEventLoopStart_(); } + return this.activeFrame_; +}; - 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 {!Frame} The frame that new items should be added to. + * @private + */ +promise.ControlFlow.prototype.getSchedulingFrame_ = function() { + return this.schedulingFrame_ || this.getActiveFrame_(); }; /** - * @return {string} The scheduled tasks still pending with this instance. + * @return {!Frame} The frame that is current executing. + * @private */ -webdriver.promise.ControlFlow.prototype.getSchedule = function() { - return this.activeFrame_ ? this.activeFrame_.getRoot().toString() : '[]'; +promise.ControlFlow.prototype.getRunningFrame_ = function() { + return this.runningFrame_ || this.getActiveFrame_(); }; @@ -1308,37 +1476,32 @@ webdriver.promise.ControlFlow.prototype.getSchedule = function() { * the task function is a generator, the task will be executed using * {@link webdriver.promise.consume}. * - * @param {function(): (T|webdriver.promise.Promise.)} fn The function to + * @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 {!webdriver.promise.Promise.} A promise that will be resolved + * @return {!promise.Promise} A promise that will be resolved * with the result of the action. * @template T */ -webdriver.promise.ControlFlow.prototype.execute = function( - fn, opt_description) { - if (webdriver.promise.isGenerator(fn)) { - fn = goog.partial(webdriver.promise.consume, fn); +promise.ControlFlow.prototype.execute = function(fn, opt_description) { + if (promise.isGenerator(fn)) { + fn = goog.partial(promise.consume, fn); } - this.cancelShutdown_(); - - if (!this.activeFrame_) { - this.activeFrame_ = new webdriver.promise.Frame_(this); + if (!this.hold_) { + var holdIntervalMs = 2147483647; // 2^31-1; max timer length for Node.js + this.hold_ = setInterval(goog.nullFunction, holdIntervalMs); } - // 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, opt_description); + var description = opt_description || ''; + var task = new Task(this, fn, description); + task.promise.stack_ = promise.captureStackTrace('Task', description, + promise.ControlFlow.prototype.execute); + this.getSchedulingFrame_().addChild(task); + this.emit(promise.ControlFlow.EventType.SCHEDULE_TASK, opt_description); this.scheduleEventLoopStart_(); return task.promise; }; @@ -1350,13 +1513,12 @@ webdriver.promise.ControlFlow.prototype.execute = function( * * @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 + * @return {!promise.Promise} A promise that will be resolved with * the result of the action. */ -webdriver.promise.ControlFlow.prototype.timeout = function( - ms, opt_description) { +promise.ControlFlow.prototype.timeout = function(ms, opt_description) { return this.execute(function() { - return webdriver.promise.delayed(ms); + return promise.delayed(ms); }, opt_description); }; @@ -1365,96 +1527,153 @@ webdriver.promise.ControlFlow.prototype.timeout = function( * 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, + * 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 + * 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 + * 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. + * 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 {!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. + * @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 */ -webdriver.promise.ControlFlow.prototype.wait = function( - condition, timeout, opt_message) { - var sleep = Math.min(timeout, 100); - var self = this; +promise.ControlFlow.prototype.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); + } + + 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 (!goog.isFunction(condition)) { + throw TypeError('Invalid condition; must be a function or promise: ' + + goog.typeOf(condition)); + } - if (webdriver.promise.isGenerator(condition)) { - condition = goog.partial(webdriver.promise.consume, condition); + if (promise.isGenerator(condition)) { + condition = goog.partial(promise.consume, condition); } + 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')); - } else { - self.timer.setTimeout(pollCondition, sleep); - } - }, waitResult.reject, true); - } - }, opt_message); + return new promise.Promise(function(fulfill, reject) { + self.suspend_(); + pollCondition(); + + function pollCondition() { + self.resume_(); + self.execute(/**@type {function()}*/(condition)).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 { + self.suspend_(); + // 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 || ''); }; /** - * 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. + * Schedules the interval for this instance's event loop, if necessary. + * @private */ -webdriver.promise.ControlFlow.prototype.await = function(promise) { - return this.execute(function() { - return promise; - }); +promise.ControlFlow.prototype.scheduleEventLoopStart_ = function() { + if (!this.eventLoopTask_ && !this.yieldCount_ && this.activeFrame_ && + !this.activeFrame_.getPendingTask()) { + this.eventLoopTask_ = new MicroTask(this.runEventLoop_, this); + } }; /** - * Schedules the interval for this instance's event loop, if necessary. + * Cancels the 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); +promise.ControlFlow.prototype.cancelEventLoop_ = function() { + if (this.eventLoopTask_) { + this.eventLoopTask_.cancel(); + this.eventLoopTask_ = null; } }; /** - * Cancels the event loop, if necessary. + * Suspends this control flow, preventing it from executing any more tasks. + * @private + */ +promise.ControlFlow.prototype.suspend_ = function() { + this.yieldCount_ += 1; + this.cancelEventLoop_(); +}; + + +/** + * Resumes execution of tasks scheduled within this control flow. * @private */ -webdriver.promise.ControlFlow.prototype.cancelEventLoop_ = function() { - if (this.eventLoopId_) { - this.timer.clearInterval(this.eventLoopId_); - this.eventLoopId_ = null; +promise.ControlFlow.prototype.resume_ = function() { + this.yieldCount_ -= 1; + if (!this.yieldCount_ && this.activeFrame_) { + this.scheduleEventLoopStart_(); } }; @@ -1466,102 +1685,99 @@ webdriver.promise.ControlFlow.prototype.cancelEventLoop_ = function() { * 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_) { +promise.ControlFlow.prototype.runEventLoop_ = function() { + this.eventLoopTask_ = null; + + if (this.yieldCount_) { 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. + if (this.activeFrame_.getPendingTask()) { + return; + } + + var task = this.getNextTask_(); + if (!task) { return; } var activeFrame = this.activeFrame_; - activeFrame.setPendingTask(task); - var markTaskComplete = goog.bind(function() { - this.history_.push(/** @type {!webdriver.promise.Task_} */ (task)); + var scheduleEventLoop = goog.bind(this.scheduleEventLoopStart_, this); + + var onSuccess = function(value) { activeFrame.setPendingTask(null); - }, this); + task.setFrame(null); + task.fulfill(value); + scheduleEventLoop(); + }; - 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); - } + var onFailure = function(reason) { + activeFrame.setPendingTask(null); + task.setFrame(null); + task.reject(reason); + scheduleEventLoop(); + }; - task.reject(self.annotateError(/** @type {!Error} */ (error))); - }, true); + activeFrame.setPendingTask(task); + var frame = new Frame(this); + task.setFrame(frame); + this.runInFrame_(frame, task.execute, function(result) { + promise.asap(result, onSuccess, onFailure); + }, onFailure, true); }; /** - * @return {webdriver.promise.Task_} The next task to execute, or + * @return {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(); +promise.ControlFlow.prototype.getNextTask_ = function() { + var frame = this.activeFrame_; + var firstChild = frame.getFirstChild(); if (!firstChild) { - if (!this.activeFrame_.isWaiting) { - this.resolveFrame_(this.activeFrame_); + if (!frame.pendingCallback && !frame.isBlocked_) { + this.resolveFrame_(frame); } return null; } - if (firstChild instanceof webdriver.promise.Frame_) { + if (firstChild instanceof Frame) { this.activeFrame_ = firstChild; return this.getNextTask_(); } - firstChild.getParent().removeChild(firstChild); + frame.removeChild(firstChild); + if (!firstChild.isPending()) { + return this.getNextTask_(); + } return firstChild; }; /** - * @param {!webdriver.promise.Frame_} frame The frame to resolve. + * @param {!Frame} frame The frame to resolve. * @private */ -webdriver.promise.ControlFlow.prototype.resolveFrame_ = function(frame) { +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()); + this.activeFrame_ = frame.getParent(); } if (frame.getParent()) { frame.getParent().removeChild(frame); } - this.trimHistory_(); - frame.fulfill(); + frame.emit(Frame.CLOSE_EVENT); if (!this.activeFrame_) { this.commenceShutdown_(); + } else { + this.scheduleEventLoopStart_(); } }; @@ -1572,15 +1788,11 @@ webdriver.promise.ControlFlow.prototype.resolveFrame_ = function(frame) { * immediately terminate all execution. * @param {*} error The reason the frame is being aborted; typically either * an Error or string. + * @param {Frame=} opt_frame The frame to abort; will use the + * currently active frame if not specified. * @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_++; - +promise.ControlFlow.prototype.abortFrame_ = function(error, opt_frame) { if (!this.activeFrame_) { this.abortNow_(error); return; @@ -1588,7 +1800,7 @@ webdriver.promise.ControlFlow.prototype.abortFrame_ = function(error) { // Frame parent is always another frame, but the compiler is not smart // enough to recognize this. - var parent = /** @type {webdriver.promise.Frame_} */ ( + var parent = /** @type {Frame} */ ( this.activeFrame_.getParent()); if (parent) { parent.removeChild(this.activeFrame_); @@ -1596,89 +1808,141 @@ webdriver.promise.ControlFlow.prototype.abortFrame_ = function(error) { var frame = this.activeFrame_; this.activeFrame_ = parent; - frame.reject(error); + frame.abort(error); }; /** - * 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. + * Executes a function within a specific frame. If the function does not + * schedule any new tasks, the frame will be discarded and the function's result + * returned passed to the given callback immediately. Otherwise, the callback + * will be invoked once all of the tasks scheduled within the function have been + * completed. If the frame is aborted, the `errback` will be invoked with the + * offending error. * + * @param {!Frame} newFrame The frame to use. * @param {!Function} fn The function to execute. - * @param {function(*)} callback The function to call with a successful result. + * @param {function(T)} 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. + * @param {boolean=} opt_isTask Whether the function is a task and the frame + * should be immediately activated to capture subtasks and errors. + * @throws {Error} If this function is invoked while another call to this + * function is present on the stack. + * @template T * @private */ -webdriver.promise.ControlFlow.prototype.runInNewFrame_ = function( - fn, callback, errback, opt_activate) { - var newFrame = new webdriver.promise.Frame_(this), - self = this, +promise.ControlFlow.prototype.runInFrame_ = function( + newFrame, fn, callback, errback, opt_isTask) { + asserts.assert( + !this.runningFrame_, 'unexpected recursive call to runInFrame'); + + var self = this, oldFrame = this.activeFrame_; try { - if (!this.activeFrame_) { - this.activeFrame_ = newFrame; - } else { + if (this.activeFrame_ !== newFrame && !newFrame.getParent()) { this.activeFrame_.addChild(newFrame); } // Activate the new frame to force tasks to be treated as sub-tasks of // the parent frame. - if (opt_activate) { + if (opt_isTask) { this.activeFrame_ = newFrame; } try { + this.runningFrame_ = newFrame; this.schedulingFrame_ = newFrame; - webdriver.promise.pushFlow_(this); + activeFlows.push(this); var result = fn(); } finally { - webdriver.promise.popFlow_(); + activeFlows.pop(); this.schedulingFrame_ = null; + + // `newFrame` should only be considered the running frame when it is + // actually executing. After it finishes, set top of stack to its parent + // so any failures/interrupts that occur while processing newFrame's + // result are handled there. + this.runningFrame_ = newFrame.parent_; } - newFrame.lockFrame(); + newFrame.isLocked_ = true; // If there was nothing scheduled in the new frame we can discard the // frame and return immediately. - if (!newFrame.children_.length) { + if (isCloseable(newFrame) && (!opt_isTask || !promise.isPromise(result))) { removeNewFrame(); - webdriver.promise.asap(result, callback, errback); + callback(result); return; } - newFrame.then(function() { - webdriver.promise.asap(result, callback, errback); - }, function(e) { - if (webdriver.promise.Thenable.isImplementation(result) && - result.isPending()) { - result.cancel(e); - e = result; + // If the executed function returned a promise, wait for it to resolve. If + // there is nothing scheduled in the frame, go ahead and discard it. + // Otherwise, we wait for the frame to be closed out by the event loop. + var shortCircuitTask; + if (promise.isPromise(result)) { + newFrame.isBlocked_ = true; + var onResolve = function() { + newFrame.isBlocked_ = false; + shortCircuitTask = new MicroTask(function() { + if (isCloseable(newFrame)) { + removeNewFrame(); + callback(result); + } + }); + }; + /** @type {Thenable} */(result).then(onResolve, onResolve); + + // If the result is a thenable, attach a listener to silence any unhandled + // rejection warnings. This is safe because we *will* handle it once the + // frame has completed. + } else if (promise.Thenable.isImplementation(result)) { + /** @type {!promise.Thenable} */(result).thenCatch(goog.nullFunction); + } + + newFrame.once(Frame.CLOSE_EVENT, function() { + shortCircuitTask && shortCircuitTask.cancel(); + if (isCloseable(newFrame)) { + removeNewFrame(); + } + callback(result); + }).once(Frame.ERROR_EVENT, function(reason) { + shortCircuitTask && shortCircuitTask.cancel(); + if (promise.Thenable.isImplementation(result) && result.isPending()) { + result.cancel(reason); } - errback(e); + errback(reason); }); } catch (ex) { - removeNewFrame(new webdriver.promise.CanceledTaskError_(ex)); + removeNewFrame(ex); errback(ex); + } finally { + // No longer running anything, clear the reference. + this.runningFrame_ = null; + } + + function isCloseable(frame) { + return (!frame.children_ || !frame.children_.length) + && !frame.pendingRejection; } /** - * @param {webdriver.promise.CanceledTaskError_=} opt_err If provided, the - * error that triggered the removal of this frame. + * @param {*=} opt_err If provided, the reason that the frame was removed. */ function removeNewFrame(opt_err) { var parent = newFrame.getParent(); if (parent) { parent.removeChild(newFrame); + asyncRun(function() { + if (isCloseable(parent) && parent !== self.activeFrame_) { + parent.emit(Frame.CLOSE_EVENT); + } + }); + self.scheduleEventLoopStart_(); } if (opt_err) { - newFrame.cancelRemainingTasks(opt_err); + newFrame.cancelRemainingTasks(promise.CancellationError.wrap( + opt_err, 'Tasks cancelled due to uncaught error')); } self.activeFrame_ = oldFrame; } @@ -1688,13 +1952,13 @@ webdriver.promise.ControlFlow.prototype.runInNewFrame_ = function( /** * 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 + * {@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. * @private */ -webdriver.promise.ControlFlow.prototype.commenceShutdown_ = function() { - if (!this.shutdownId_) { +promise.ControlFlow.prototype.commenceShutdown_ = function() { + if (!this.shutdownTask_) { // 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 @@ -1702,24 +1966,36 @@ webdriver.promise.ControlFlow.prototype.commenceShutdown_ = function() { // If #execute is called before the timeout below fires, it will cancel // the timeout and restart the event loop. this.cancelEventLoop_(); + this.shutdownTask_ = new MicroTask(this.shutdown_, this); + } +}; - var self = this; - self.shutdownId_ = self.timer.setTimeout(function() { - self.shutdownId_ = null; - self.emit(webdriver.promise.ControlFlow.EventType.IDLE); - }, 0); + +/** @private */ +promise.ControlFlow.prototype.cancelHold_ = function() { + if (this.hold_) { + clearInterval(this.hold_); + this.hold_ = null; } }; +/** @private */ +promise.ControlFlow.prototype.shutdown_ = function() { + this.cancelHold_(); + this.shutdownTask_ = null; + this.emit(promise.ControlFlow.EventType.IDLE); +}; + + /** * 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; +promise.ControlFlow.prototype.cancelShutdown_ = function() { + if (this.shutdownTask_) { + this.shutdownTask_.cancel(); + this.shutdownTask_ = null; } }; @@ -1733,364 +2009,462 @@ webdriver.promise.ControlFlow.prototype.cancelShutdown_ = function() { * abort; usually either an Error or string value. * @private */ -webdriver.promise.ControlFlow.prototype.abortNow_ = function(error) { +promise.ControlFlow.prototype.abortNow_ = function(error) { this.activeFrame_ = null; this.cancelShutdown_(); this.cancelEventLoop_(); + this.cancelHold_(); var listeners = this.listeners( - webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); + promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); if (!listeners.length) { - this.timer.setTimeout(function() { - throw error; - }, 0); + throwException(error); } else { - this.emit(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, - error); + this.emit(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, error); } }; - -/** - * 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 - */ -webdriver.promise.Node_ = function(flow) { - webdriver.promise.Deferred.call(this, null, flow); -}; -goog.inherits(webdriver.promise.Node_, webdriver.promise.Deferred); - - /** - * This node's parent. - * @private {webdriver.promise.Node_} + * Wraps a function to execute as a cancellable micro task. + * @final */ -webdriver.promise.Node_.prototype.parent_ = null; - - -/** @return {webdriver.promise.Node_} This node's parent. */ -webdriver.promise.Node_.prototype.getParent = function() { - return this.parent_; -}; - - -/** - * @param {webdriver.promise.Node_} parent This node's new parent. - */ -webdriver.promise.Node_.prototype.setParent = function(parent) { - this.parent_ = parent; -}; - +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); + }, -/** - * @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_; + /** + * 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; } - return root; -}; - +}); /** * 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}. + * {@link webdriver.Task} or a callback on a + * {@link webdriver.promise.Promise}. * - *

    Each frame may contain sub-frames. If child N is a sub-frame, then the + * 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_} + * @unrestricted + * @final * @private */ -webdriver.promise.Frame_ = function(flow) { - webdriver.promise.Node_.call(this, flow); - - var reject = goog.bind(this.reject, this); - var cancelRemainingTasks = goog.bind(this.cancelRemainingTasks, this); - - /** @override */ - this.reject = function(e) { - cancelRemainingTasks(new webdriver.promise.CanceledTaskError_(e)); - reject(e); - }; - +var Frame = goog.defineClass(EventEmitter, { /** - * @private {!Array.} + * @param {!promise.ControlFlow} flow The flow this instance belongs to. */ - this.children_ = []; -}; -goog.inherits(webdriver.promise.Frame_, webdriver.promise.Node_); + constructor: function(flow) { + EventEmitter.call(this); + goog.getUid(this); + /** @private {!promise.ControlFlow} */ + this.flow_ = flow; -/** - * The task currently being executed within this frame. - * @private {webdriver.promise.Task_} - */ -webdriver.promise.Frame_.prototype.pendingTask_ = null; + /** @private {Frame} */ + this.parent_ = null; + /** @private {Array} */ + this.children_ = 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 {(Frame|Task)} */ + this.lastInsertedChild_ = null; + /** + * The task currently being executed within this frame. + * @private {Task} + */ + this.pendingTask_ = null; + + /** + * Whether this frame is currently locked. A locked frame represents an + * executed function that has scheduled all of its tasks. + * + * Once a frame becomes locked, any new frames which are added as children + * represent interrupts (such as a {@link webdriver.promise.Promise} + * callback) whose tasks must be given priority over those already scheduled + * within this frame. For example: + * + * var flow = 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} + */ + this.isLocked_ = false; + + /** + * Whether this frame's completion is blocked on the resolution of a promise + * returned by its main function. + * @private + */ + this.isBlocked_ = false; + + /** + * Whether this frame represents a pending callback attached to a + * {@link webdriver.promise.Promise}. + * @private {boolean} + */ + this.pendingCallback = false; + + /** + * Whether there are pending unhandled rejections detected within this frame. + * @private {boolean} + */ + this.pendingRejection = false; + + /** @private {promise.CancellationError} */ + this.cancellationError_ = null; + }, + + statics: { + /** @const */ + CLOSE_EVENT: 'close', + + /** @const */ + ERROR_EVENT: 'error', + + /** + * @param {!promise.CancellationError} error The cancellation error. + * @param {!(Frame|Task)} child The child to cancel. + * @private + */ + cancelChild_: function(error, child) { + if (child instanceof Frame) { + child.cancelRemainingTasks(error); + } else { + child.promise.callbacks_ = null; + child.cancel(error); + } + } + }, + + /** @return {Frame} This frame's parent, if any. */ + getParent: function() { + return this.parent_; + }, + + /** @param {Frame} parent This frame's new parent. */ + setParent: function(parent) { + this.parent_ = parent; + }, + + /** @return {!Frame} The root of this frame's tree. */ + getRoot: function() { + var root = this; + while (root.parent_) { + root = root.parent_; + } + return root; + }, -/** - * 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; + /** + * Aborts the execution of this frame, cancelling all outstanding tasks + * scheduled within this frame. + * + * @param {*} error The error that triggered this abortion. + */ + abort: function(error) { + this.cancellationError_ = promise.CancellationError.wrap( + error, 'Task discarded due to a previous task failure'); + this.cancelRemainingTasks(this.cancellationError_); + if (!this.pendingCallback) { + this.emit(Frame.ERROR_EVENT, error); + } + }, + /** + * 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; + * promise.createFlow(function(flow) { + * someResult = flow.execute(function() {}); + * throw Error(); + * }).thenCatch(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! CancelledTaskError: Task discarded due to a previous + * // task failure: Error: boom + * + * @param {!promise.CancellationError} reason The cancellation reason. + */ + cancelRemainingTasks: function(reason) { + if (this.children_) { + this.children_.forEach(function(child) { + Frame.cancelChild_(reason, child); + }); + } + }, -/** - * A reference to the last node inserted in this frame. - * @private {webdriver.promise.Node_} - */ -webdriver.promise.Frame_.prototype.lastInsertedChild_ = null; + /** + * @return {Task} The task currently executing + * within this frame, if any. + */ + getPendingTask: function() { + return this.pendingTask_; + }, + /** + * @param {Task} task The task currently + * executing within this frame, if any. + */ + setPendingTask: function(task) { + this.pendingTask_ = task; + }, -/** - * 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); + /** + * @return {boolean} Whether this frame is empty (has no scheduled tasks or + * pending callback frames). + */ + isEmpty: function() { + return !this.children_ || !this.children_.length; + }, + + /** + * Adds a new node to this frame. + * @param {!(Frame|Task)} node The node to insert. + */ + addChild: function(node) { + if (this.cancellationError_) { + Frame.cancelChild_(this.cancellationError_, node); + return; // Child will never run, no point keeping a reference. } - }); -}; + if (!this.children_) { + this.children_ = []; + } -/** - * @return {webdriver.promise.Task_} The task currently executing - * within this frame, if any. - */ -webdriver.promise.Frame_.prototype.getPendingTask = function() { - return this.pendingTask_; -}; + node.setParent(this); + if (this.isLocked_ && node instanceof Frame) { + var index = 0; + if (this.lastInsertedChild_ instanceof Frame) { + index = this.children_.indexOf(this.lastInsertedChild_); + // If the last inserted child into a locked frame is a pending callback, + // it is an interrupt and the new interrupt must come after it. Otherwise, + // we have our first interrupt for this frame and it shoudl go before the + // last inserted child. + index += (this.lastInsertedChild_.pendingCallback) ? 1 : -1; + } + this.children_.splice(Math.max(index, 0), 0, node); + this.lastInsertedChild_ = node; + return; + } + this.lastInsertedChild_ = node; + this.children_.push(node); + }, -/** - * @param {webdriver.promise.Task_} task The task currently - * executing within this frame, if any. - */ -webdriver.promise.Frame_.prototype.setPendingTask = function(task) { - this.pendingTask_ = task; -}; + /** + * @return {(Frame|Task)} This frame's fist child. + */ + getFirstChild: function() { + this.isLocked_ = true; + return this.children_ && this.children_[0]; + }, + /** + * Removes a child from this frame. + * @param {!(Frame|Task)} child The child to remove. + */ + removeChild: function(child) { + goog.asserts.assert(child.parent_ === this, 'not a child of this frame'); + goog.asserts.assert(this.children_ !== null, 'frame has no children!'); + var index = this.children_.indexOf(child); + child.setParent(null); + this.children_.splice(index, 1); + if (this.lastInsertedChild_ === child) { + this.lastInsertedChild_ = this.children_[index - 1] || null; + } + if (!this.children_.length) { + this.children_ = null; + } + }, -/** Locks this frame. */ -webdriver.promise.Frame_.prototype.lockFrame = function() { - this.isLocked_ = true; -}; + /** @override */ + toString: function() { + return 'Frame::' + goog.getUid(this); + } +}); /** - * Adds a new node to this frame. - * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} node - * The node to insert. + * A task to be executed by a {@link webdriver.promise.ControlFlow}. + * + * @unrestricted + * @final */ -webdriver.promise.Frame_.prototype.addChild = function(node) { - if (this.lastInsertedChild_ && - this.lastInsertedChild_ instanceof webdriver.promise.Frame_ && - !this.lastInsertedChild_.isLocked_) { - this.lastInsertedChild_.addChild(node); - return; - } - - node.setParent(this); +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. + * @constructor + * @extends {promise.Deferred} + * @template T + */ + constructor: function(flow, fn, description) { + Task.base(this, 'constructor', flow); + goog.getUid(this); - 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; - } - goog.array.insertAt(this.children_, node, index); - this.lastInsertedChild_ = node; - return; - } + /** + * @type {function(): (T|!promise.Promise)} + */ + this.execute = fn; - this.lastInsertedChild_ = node; - this.children_.push(node); -}; + /** @private {string} */ + this.description_ = description; + /** @private {Frame} */ + this.parent_ = null; -/** - * @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]; -}; + /** @private {Frame} */ + this.frame_ = null; + }, + /** + * @return {Frame} frame The frame used to run this task's + * {@link #execute} method. + */ + getFrame: function() { + return this.frame_; + }, -/** - * 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; - } -}; + /** + * @param {Frame} frame The frame used to run this task's + * {@link #execute} method. + */ + setFrame: function(frame) { + this.frame_ = frame; + }, + /** + * @param {Frame} frame The frame this task is scheduled in. + */ + setParent: function(frame) { + goog.asserts.assert(goog.isNull(this.parent_) || goog.isNull(frame), + 'parent already set'); + this.parent_ = frame; + }, -/** @override */ -webdriver.promise.Frame_.prototype.toString = function() { - return '[' + goog.array.map(this.children_, function(child) { - return child.toString(); - }).join(', ') + ']'; -}; + /** @return {string} This task's description. */ + getDescription: function() { + return this.description_; + }, + /** @override */ + toString: function() { + return 'Task::' + goog.getUid(this) + '<' + this.description_ + '>'; + } +}); /** - * A task to be executed by a {@link webdriver.promise.ControlFlow}. + * Manages a callback attached to a {@link webdriver.promise.Promise}. When the + * promise is resolved, this callback will invoke the appropriate callback + * function based on the promise's resolved value. * - * @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 + * @unrestricted + * @final */ -webdriver.promise.Task_ = function(flow, fn, description, snapshot) { - webdriver.promise.Node_.call(this, flow); - +var Callback = goog.defineClass(promise.Deferred, { /** - * Executes this task. - * @type {!Function} + * @param {!promise.Promise} parent The promise this callback is attached to. + * @param {(function(T): (IThenable|R)|null|undefined)} callback + * The fulfillment callback. + * @param {(function(*): (IThenable|R)|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. + * @extends {promise.Deferred} + * @template T, R */ - this.execute = fn; + constructor: function(parent, callback, errback, name, fn) { + Callback.base(this, 'constructor', parent.flow_); - /** @private {string} */ - this.description_ = description; + /** @private {(function(T): (IThenable|R)|null|undefined)} */ + this.callback_ = callback; - /** @private {!webdriver.stacktrace.Snapshot} */ - this.snapshot_ = snapshot; -}; -goog.inherits(webdriver.promise.Task_, webdriver.promise.Node_); + /** @private {(function(*): (IThenable|R)|null|undefined)} */ + this.errback_ = errback; + /** @private {!Frame} */ + this.frame_ = new Frame(parent.flow_); + this.frame_.pendingCallback = true; -/** @return {string} This task's description. */ -webdriver.promise.Task_.prototype.getDescription = function() { - return this.description_; -}; + this.promise.parent_ = parent; + if (promise.LONG_STACK_TRACES) { + this.promise.stack_ = promise.captureStackTrace('Promise', name, fn); + } + }, + /** + * Called by the parent promise when it has been resolved. + * @param {!PromiseState} state The parent's new state. + * @param {*} value The parent's new value. + */ + notify: function(state, value) { + var callback = this.callback_; + var fallback = this.fulfill; + if (state === PromiseState.REJECTED) { + callback = this.errback_; + fallback = this.reject; + } -/** @override */ -webdriver.promise.Task_.prototype.toString = function() { - var stack = this.snapshot_.getStacktrace(); - var ret = this.description_; - if (stack.length) { - if (this.description_) { - ret += '\n'; + this.frame_.pendingCallback = false; + if (goog.isFunction(callback)) { + this.frame_.flow_.runInFrame_( + this.frame_, + goog.bind(callback, undefined, value), + this.fulfill, this.reject); + } else { + if (this.frame_.getParent()) { + this.frame_.getParent().removeChild(this.frame_); + } + fallback(value); } - ret += stack.join('\n'); } - return ret; -}; - - - -/** - * 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); - - -/** @override */ -webdriver.promise.CanceledTaskError_.prototype.name = 'CanceledTaskError'; +}); /** * 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(); /** @@ -2098,46 +2472,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); }; @@ -2145,14 +2503,13 @@ 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); }); @@ -2164,7 +2521,7 @@ webdriver.promise.createFlow = function(callback) { * @param {!Function} fn The function to test. * @return {boolean} Whether the function is a generator. */ -webdriver.promise.isGenerator = function(fn) { +promise.isGenerator = function(fn) { return fn.constructor.name === 'GeneratorFunction'; }; @@ -2175,48 +2532,46 @@ webdriver.promise.isGenerator = function(fn) { * 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. - *

    
    - * webdriver.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 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. * - *

    Example 2: a generator that throws. - *

    
    - * webdriver.promise.consume(function* () {
    - *   yield webdriver.promise.delayed(250).then(function() {
    - *     throw Error('boom');
    - *   });
    - * }).thenCatch(function(e) {
    - *   console.log(e.toString());  // Error: boom
    - * });
    - * 
    + * 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 {!webdriver.promise.Promise.} A promise that will resolve to the + * @return {!promise.Promise} A promise that will resolve to the * generator's final result. * @throws {TypeError} If the given function is not a generator. */ -webdriver.promise.consume = function(generatorFn, opt_self, var_args) { - if (!webdriver.promise.isGenerator(generatorFn)) { - throw TypeError('Input is not a GeneratorFunction: ' + +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 = webdriver.promise.defer(); - var generator = generatorFn.apply(opt_self, goog.array.slice(arguments, 2)); + var deferred = promise.defer(); + var generator = generatorFn.apply(opt_self, Arrays.slice(arguments, 2)); callNext(); return deferred.promise; @@ -2249,6 +2604,6 @@ webdriver.promise.consume = function(generatorFn, opt_self, var_args) { return; } - webdriver.promise.asap(result.value, callNext, callThrow); + 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 92e61a0..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,40 +392,6 @@ 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 creates 2 submatches on the (optional) context and function name, @@ -514,11 +482,6 @@ 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], '', m[3]); @@ -567,9 +530,8 @@ webdriver.stacktrace.parseLongFirefoxFrame_ = function(frameStr) { * consistently with the other JS engines. * @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 ''; } @@ -588,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 = ''; @@ -628,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 index e9cc5fb..2278f1f 100644 --- a/lib/webdriver/test/capabilities_test.js +++ b/lib/webdriver/test/capabilities_test.js @@ -1,16 +1,19 @@ -// Copyright 2014 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.require('goog.testing.jsunit'); goog.require('webdriver.Capabilities'); diff --git a/lib/webdriver/test/events_test.js b/lib/webdriver/test/events_test.js index ae65067..41a86de 100644 --- a/lib/webdriver/test/events_test.js +++ b/lib/webdriver/test/events_test.js @@ -1,16 +1,19 @@ -// Copyright 2014 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.require('goog.testing.jsunit'); goog.require('webdriver.EventEmitter'); diff --git a/lib/webdriver/test/http/corsclient_test.js b/lib/webdriver/test/http/corsclient_test.js index 7d4d35e..6289b24 100644 --- a/lib/webdriver/test/http/corsclient_test.js +++ b/lib/webdriver/test/http/corsclient_test.js @@ -1,21 +1,24 @@ -// Copyright 2014 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.require('goog.json'); 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'); @@ -38,6 +41,10 @@ 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); @@ -65,7 +72,7 @@ function setXdr(opt_value) { function expectRequest(mockXhr) { mockXhr.open('POST', URL + '/xdrpc', true); - return mockXhr.send(goog.json.serialize({ + return mockXhr.send(JSON.stringify({ 'method': REQUEST.method, 'path': REQUEST.path, 'data': REQUEST.data diff --git a/lib/webdriver/test/http/http_test.js b/lib/webdriver/test/http/http_test.js index 507856c..1b37ca3 100644 --- a/lib/webdriver/test/http/http_test.js +++ b/lib/webdriver/test/http/http_test.js @@ -1,22 +1,25 @@ -// Copyright 2014 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.require('bot.ErrorCode'); goog.require('goog.Uri'); -goog.require('goog.json'); 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'); @@ -29,6 +32,10 @@ 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); @@ -59,7 +66,7 @@ function headersToString(headers) { function expectRequest(method, path, data, headers) { var description = method + ' ' + path + '\n' + headersToString(headers) + - '\n' + goog.json.serialize(data); + '\n' + JSON.stringify(data); return mockClient.send(new goog.testing.mockmatchers.ArgumentMatcher( function(request) { @@ -165,8 +172,7 @@ function assertFailsToSend(command, opt_onError) { function testExecute_clientFailsToSendRequest() { var error = new Error('boom'); expectRequest('POST', '/session', {}, { - 'Accept': 'application/json; charset=utf-8', - 'connection': 'keep-alive' + 'Accept': 'application/json; charset=utf-8' }). $does(respondsWith(error)); control.$replayAll(); @@ -179,8 +185,7 @@ function testExecute_clientFailsToSendRequest() { function testExecute_commandWithNoUrlParameters() { expectRequest('POST', '/session', {}, { - 'Accept': 'application/json; charset=utf-8', - 'connection': 'keep-alive' + 'Accept': 'application/json; charset=utf-8' }). $does(respondsWith(null, response(200, {}, ''))); control.$replayAll(); @@ -210,9 +215,7 @@ function testExecute_replacesUrlParametersWithCommandParameters() { expectRequest('POST', '/session/s123/url', {'url': 'http://www.google.com'}, - {'Accept': 'application/json; charset=utf-8', - 'connection': 'keep-alive' - }). + {'Accept': 'application/json; charset=utf-8'}). $does(respondsWith(null, response(200, {}, ''))); control.$replayAll(); @@ -228,11 +231,10 @@ function testExecute_returnsParsedJsonResponse() { setParameter('sessionId', 's123'); expectRequest('GET', '/session/s123/url', {}, { - 'Accept': 'application/json; charset=utf-8', - 'connection': 'keep-alive' + 'Accept': 'application/json; charset=utf-8' }).$does(respondsWith(null, response(200, {'Content-Type': 'application/json'}, - goog.json.serialize(responseObj)))); + JSON.stringify(responseObj)))); control.$replayAll(); assertSendsSuccessfully(command, function(response) { @@ -245,8 +247,7 @@ function testExecute_returnsSuccessFor2xxWithBodyAsValueWhenNotJson() { setParameter('sessionId', 's123'); expectRequest('GET', '/session/s123/url', {}, { - 'Accept': 'application/json; charset=utf-8', - 'connection': 'keep-alive' + 'Accept': 'application/json; charset=utf-8' }).$does(respondsWith(null, response(200, {}, 'hello, world\r\ngoodbye, world!'))); control.$replayAll(); @@ -262,8 +263,7 @@ function testExecute_returnsSuccessFor2xxWithBodyAsValueWhenNotJson() { function testExecute_returnsSuccessFor2xxInvalidJsonBody() { var invalidJson = '['; expectRequest('POST', '/session', {}, { - 'Accept': 'application/json; charset=utf-8', - 'connection': 'keep-alive' + 'Accept': 'application/json; charset=utf-8' }). $does(respondsWith(null, response(200, { 'Content-Type': 'application/json' @@ -285,8 +285,7 @@ function testExecute_returnsUnknownCommandFor404WithBodyAsValueWhenNotJson() { setParameter('sessionId', 's123'); expectRequest('GET', '/session/s123/url', {}, { - 'Accept': 'application/json; charset=utf-8', - 'connection': 'keep-alive' + 'Accept': 'application/json; charset=utf-8' }).$does(respondsWith(null, response(404, {}, 'hello, world\r\ngoodbye, world!'))); control.$replayAll(); @@ -304,8 +303,7 @@ function testExecute_returnsUnknownErrorForGenericErrorCodeWithBodyAsValueWhenNo setParameter('sessionId', 's123'); expectRequest('GET', '/session/s123/url', {}, { - 'Accept': 'application/json; charset=utf-8', - 'connection': 'keep-alive' + 'Accept': 'application/json; charset=utf-8' }).$does(respondsWith(null, response(500, {}, 'hello, world\r\ngoodbye, world!'))); control.$replayAll(); @@ -327,10 +325,9 @@ function testExecute_attemptsToParseBodyWhenNoContentTypeSpecified() { setParameter('sessionId', 's123'); expectRequest('GET', '/session/s123/url', {}, { - 'Accept': 'application/json; charset=utf-8', - 'connection': 'keep-alive' + 'Accept': 'application/json; charset=utf-8' }).$does(respondsWith(null, - response(200, {}, goog.json.serialize(responseObj)))); + response(200, {}, JSON.stringify(responseObj)))); control.$replayAll(); assertSendsSuccessfully(command, function(response) { @@ -338,6 +335,36 @@ function testExecute_attemptsToParseBodyWhenNoContentTypeSpecified() { }); } +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; }, diff --git a/lib/webdriver/test/http/xhrclient_test.js b/lib/webdriver/test/http/xhrclient_test.js index 2a2231f..9f2c3db 100644 --- a/lib/webdriver/test/http/xhrclient_test.js +++ b/lib/webdriver/test/http/xhrclient_test.js @@ -1,26 +1,33 @@ -// Copyright 2014 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.require('goog.json'); 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; @@ -61,7 +68,7 @@ function expectRequest(mockXhr) { for (var header in REQUEST.headers) { mockXhr.setRequestHeader(header, REQUEST.headers[header]); } - return mockXhr.send(goog.json.serialize(REQUEST.data)); + return mockXhr.send(JSON.stringify(REQUEST.data)); } function testXhrClient_whenUnableToSendARequest() { diff --git a/lib/webdriver/test/locators_test.js b/lib/webdriver/test/locators_test.js index a8fa881..21e6479 100644 --- a/lib/webdriver/test/locators_test.js +++ b/lib/webdriver/test/locators_test.js @@ -1,18 +1,20 @@ -// Copyright 2014 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.require('goog.json'); goog.require('goog.testing.jsunit'); goog.require('webdriver.By'); goog.require('webdriver.Locator'); diff --git a/lib/webdriver/test/logging_test.js b/lib/webdriver/test/logging_test.js index 2f12d0f..bb542f9 100644 --- a/lib/webdriver/test/logging_test.js +++ b/lib/webdriver/test/logging_test.js @@ -1,16 +1,19 @@ -// Copyright 2014 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.require('goog.debug.LogRecord'); goog.require('goog.debug.Logger'); @@ -18,7 +21,7 @@ goog.require('goog.testing.jsunit'); goog.require('webdriver.logging'); function convert(level, msg, name, time) { - var recordIn = new goog.debug.LogRecord(level, msg, name, time); + var recordIn = new webdriver.logging.LogRecord(level, msg, name, time); return webdriver.logging.Entry.fromClosureLogRecord(recordIn); } @@ -40,6 +43,18 @@ function testPreferencesToJSON() { 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() { diff --git a/lib/webdriver/test/promise_error_test.js b/lib/webdriver/test/promise_error_test.js new file mode 100644 index 0000000..879045f --- /dev/null +++ b/lib/webdriver/test/promise_error_test.js @@ -0,0 +1,719 @@ +// 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.activeFrame_ && !theFlow.yieldCount_) { + 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, 2).then(function(errors) { + 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 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 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 testThrownPromiseIsHandledSameAsReturningPromise_promiseIsRejected() { + return webdriver.promise.fulfilled().then(function() { + throw webdriver.promise.rejected(new StubError); + }).then(fail, assertIsStubError); +} + + +function testTaskThrowsPromise_taskSucceedsIfPromiseIsFulfilled() { + flow.execute(function() { + throw webdriver.promise.fulfilled(1234); + }).then(function(value) { + assertEquals(1234, value); + }); + return waitForIdle(); +} + + +function testTaskThrowsPromise_taskFailsIfPromiseIsRejected() { + flow.execute(function() { + throw webdriver.promise.rejected(new StubError); + }).then(fail, assertIsStubError); + return waitForIdle(); +} + + +function testFailsTaskIfThereIsAnUnhandledErrorWhileWaitingOnTaskResult() { + var d = webdriver.promise.defer(); + flow.execute(function() { + setTimeout(function() { + webdriver.promise.rejected(new StubError); + }, 10); + 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() { + setTimeout(function() { + flow.execute(throwStubError); + }, 10); + 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 testTasksWithinQueuedCallbackInAFrameAreDroppedIfFrameAborts() { + var seen = []; + return flow.execute(function() { + flow.execute(throwStubError); + webdriver.promise.fulfilled().then(function() { + seen.push(1); + + return flow.execute(function() { + seen.push(2); + }); + + // This callback depends on the result of a cancelled task, so it will never + // be invoked. + }).thenFinally(function() { + seen.push(3); + }); + }).then(fail, function(e) { + assertIsStubError(e); + assertArrayEquals([1], seen); + }); +} + + +function testTaskIsCancelledAfterWaitTimeout() { + var seen = []; + return flow.execute(function() { + flow.wait(function() { + webdriver.promies.delayed(100).then(goog.nullFunction); + }, 5); + + return flow.execute(function() { + seen.push(1); + }).then(function() { + seen.push(2); + }, function() { + seen.push(3); + }); + }).then(fail, function(e) { + assertArrayEquals([], seen); + }); +} + + +function testTaskCallbacksGetCancellationErrorIfRegisteredAfterTaskIsCancelled() { + 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); + }); + }); +} diff --git a/lib/webdriver/test/promise_flow_test.js b/lib/webdriver/test/promise_flow_test.js index 69fb263..9c00a61 100644 --- a/lib/webdriver/test/promise_flow_test.js +++ b/lib/webdriver/test/promise_flow_test.js @@ -1,37 +1,38 @@ -// Copyright 2014 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.require('goog.array'); -goog.require('goog.functions'); goog.require('goog.string'); -goog.require('goog.testing.FunctionMock'); goog.require('goog.testing.jsunit'); goog.require('goog.userAgent'); -goog.require('webdriver.promise.ControlFlow'); +goog.require('webdriver.promise'); goog.require('webdriver.stacktrace.Snapshot'); +goog.require('webdriver.stacktrace'); goog.require('webdriver.test.testutil'); -goog.require('webdriver.testing.promise.FlowTester'); // Aliases for readability. -var STUB_ERROR = webdriver.test.testutil.STUB_ERROR, +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 clock, flow, flowHistory, flowTester; +var flow, flowHistory, uncaughtExceptions; function shouldRunTests() { return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); @@ -39,19 +40,63 @@ function shouldRunTests() { function setUp() { - clock = webdriver.test.testutil.createMockClock(); - flowTester = new webdriver.testing.promise.FlowTester(clock, goog.global); - flow = webdriver.promise.controlFlow(); + 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() { - flowTester.dispose(); - clock.dispose(); + 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; @@ -101,30 +146,6 @@ function scheduleWait(condition, timeout, opt_message) { } -/** @see {@link webdriver.testing.promise.FlowTester#turnEventLoop}. */ -function turnEventLoop() { - flowTester.turnEventLoop(); -} - - -function runAndExpectSuccess(opt_callback) { - flowTester.run(); - flowTester.verifySuccess(); - if (opt_callback) { - opt_callback(); - } -} - - -function runAndExpectFailure(opt_errback) { - flowTester.run(); - flowTester.verifyFailure(); - if (opt_errback) { - opt_errback(flowTester.getFailure()); - } -} - - function assertFlowHistory(var_args) { var expected = goog.array.slice(arguments, 0); assertArrayEquals(expected, flowHistory); @@ -151,562 +172,21 @@ function createFrame() { } -function testAddChild_toEmptyFrame() { - var frame = createFrame(); - - var task1 = createTask(), - task2 = createTask(), - task3 = createTask(); - - frame.addChild(task1); - frame.addChild(task2); - frame.addChild(task3); - - assertArrayEquals([task1, task2, task3], frame.children_); -} - - -function testAddChild_withSubframes() { - var root = createFrame(); - - var task1 = createTask('task1'); - root.addChild(task1); - assertArrayEquals([task1], root.children_); - - var frame1 = createFrame(); - root.addChild(frame1); - assertArrayEquals([task1, frame1], root.children_); - - var task2 = createTask('task2'), task3 = createTask('task3'); - root.addChild(task2); - root.addChild(task3); - assertArrayEquals([task1, frame1], root.children_); - assertArrayEquals([task2, task3], frame1.children_); - - frame1.lockFrame(); - var task4 = createTask('task4'), task5 = createTask('task5'); - root.addChild(task4); - root.addChild(task5); - assertArrayEquals([task1, frame1, task4, task5], root.children_); - assertArrayEquals([task2, task3], frame1.children_); - - var frame2 = createFrame(), - frame3 = createFrame(), - task6 = createTask('task6'), - task7 = createTask('task7'), - task8 = createTask('task8'); - - root.addChild(frame2); - root.addChild(frame3); - root.addChild(task6); - frame3.lockFrame(); - root.addChild(task7); - frame2.lockFrame(); - root.addChild(task8); - - assertArrayEquals([task1, frame1, task4, task5, frame2, task8], - root.children_); - assertArrayEquals([task2, task3], frame1.children_); - assertArrayEquals([frame3, task7], frame2.children_); - assertArrayEquals([task6], frame3.children_); -} - -function testAddChild_insertingFramesIntoAnActiveFrame() { - var root = createFrame(), - frame2 = createFrame(), - frame3 = createFrame(), - task1 = createTask('task1'); - - root.addChild(task1); - root.isActive_ = true; - root.addChild(frame2); - frame2.lockFrame(); - root.addChild(frame3); - frame3.lockFrame(); - - assertArrayEquals([frame2, frame3, task1], root.children_); -} - -function testRemoveChild() { - var frame1 = createFrame(), - frame2 = createFrame(); - - frame1.addChild(frame2); - assertArrayEquals([frame2], frame1.children_); - frame1.removeChild(frame2); - assertArrayEquals([], frame1.children_); -} - - -function testResolveFrame() { - var frame1 = createFrame(), - frame2 = createFrame(), - frame3 = createFrame(); - - frame2.addChild(frame3); - frame1.addChild(frame2); - assertArrayEquals([frame3], frame2.children_); - assertArrayEquals([frame2], frame1.children_); - - frame1.fulfill = callbackHelper(); - frame2.fulfill = callbackHelper(); - frame3.fulfill = callbackHelper(); - - var obj = { - activeFrame_: frame2, - commenceShutdown_: callbackHelper(), - trimHistory_: callbackHelper(), - history_: [] - }; - webdriver.promise.ControlFlow.prototype.resolveFrame_.call(obj, frame3); - assertEquals(1, obj.trimHistory_.getCallCount()); - frame3.fulfill.assertCalled('frame 3 not resolved'); - frame2.fulfill.assertNotCalled('frame 2 should not be resolved yet'); - frame1.fulfill.assertNotCalled('frame 1 should not be resolved yet'); - assertNull(frame3.getParent()); - assertArrayEquals([], frame2.children_); - assertArrayEquals([frame2], frame1.children_); - assertEquals(frame2, obj.activeFrame_); - - webdriver.promise.ControlFlow.prototype.resolveFrame_.call(obj, frame2); - assertEquals(2, obj.trimHistory_.getCallCount()); - frame2.fulfill.assertCalled('frame 2 not resolved'); - frame1.fulfill.assertNotCalled('frame 1 should not be resolved yet'); - assertNull(frame2.getParent()); - assertArrayEquals([], frame1.children_); - assertEquals(frame1, obj.activeFrame_); - - obj.commenceShutdown_.assertNotCalled(); - webdriver.promise.ControlFlow.prototype.resolveFrame_.call(obj, frame1); - assertEquals(3, obj.trimHistory_.getCallCount()); - frame1.fulfill.assertCalled('frame 1 not resolved'); - obj.commenceShutdown_.assertCalled(); - assertNull(frame1.getParent()); - assertNull(obj.activeFrame_); -} - - -function testGetNextTask() { - var root = flow.activeFrame_ = createFrame(); - - var frame1 = createFrame(), - frame2 = createFrame(), - frame3 = createFrame(), - task1 = createTask('task1'), - task2 = createTask('task2'), - task3 = createTask('task3'), - task4 = createTask('task4'), - task5 = createTask('task5'), - task6 = createTask('task6'), - task7 = createTask('task7'), - task8 = createTask('task8'); - - flow.commenceShutdown_ = callbackHelper(); - root.fulfill = callbackHelper(); - frame1.fulfill = callbackHelper(); - frame2.fulfill = callbackHelper(); - frame3.fulfill = callbackHelper(); - - root.addChild(task1); - root.addChild(frame1); - root.addChild(task2); - root.addChild(task3); - assertArrayEquals([task1, frame1], root.children_); - assertArrayEquals([task2, task3], frame1.children_); - - frame1.lockFrame(); - root.addChild(task4); - root.addChild(task5); - assertArrayEquals([task1, frame1, task4, task5], root.children_); - assertArrayEquals([task2, task3], frame1.children_); - - - root.addChild(frame2); - root.addChild(frame3); - root.addChild(task6); - frame3.lockFrame(); - root.addChild(task7); - frame2.lockFrame(); - root.addChild(task8); - - assertArrayEquals([task1, frame1, task4, task5, frame2, task8], - root.children_); - assertArrayEquals([task2, task3], frame1.children_); - assertArrayEquals([frame3, task7], frame2.children_); - assertArrayEquals([task6], frame3.children_); - - assertEquals(root, task1.getParent()); - assertEquals(task1, flow.getNextTask_()); - assertNull(task1.getParent()); - assertEquals(root, flow.activeFrame_); - root.fulfill.assertNotCalled(); - frame1.fulfill.assertNotCalled(); - frame2.fulfill.assertNotCalled(); - frame3.fulfill.assertNotCalled(); - - assertEquals(task2, flow.getNextTask_()); - assertNull(task2.getParent()); - assertEquals(frame1, flow.activeFrame_); - root.fulfill.assertNotCalled(); - frame1.fulfill.assertNotCalled(); - frame2.fulfill.assertNotCalled(); - frame3.fulfill.assertNotCalled(); - - assertEquals(task3, flow.getNextTask_()); - assertNull(task3.getParent()); - assertEquals(frame1, flow.activeFrame_); - root.fulfill.assertNotCalled(); - frame1.fulfill.assertNotCalled(); - frame2.fulfill.assertNotCalled(); - frame3.fulfill.assertNotCalled(); - - assertNull(flow.getNextTask_()); - assertNull(frame1.getParent()); - assertEquals(root, flow.activeFrame_); - root.fulfill.assertNotCalled(); - frame1.fulfill.assertCalled(); - frame2.fulfill.assertNotCalled(); - frame3.fulfill.assertNotCalled(); - - assertEquals(task4, flow.getNextTask_()); - assertNull(task4.getParent()); - assertEquals(root, flow.activeFrame_); - root.fulfill.assertNotCalled(); - frame2.fulfill.assertNotCalled(); - frame3.fulfill.assertNotCalled(); - - assertEquals(task5, flow.getNextTask_()); - assertNull(task5.getParent()); - assertEquals(root, flow.activeFrame_); - root.fulfill.assertNotCalled(); - frame2.fulfill.assertNotCalled(); - frame3.fulfill.assertNotCalled(); - - assertEquals(task6, flow.getNextTask_()); - assertNull(task6.getParent()); - assertEquals(frame3, flow.activeFrame_); - root.fulfill.assertNotCalled(); - frame2.fulfill.assertNotCalled(); - frame3.fulfill.assertNotCalled(); - - assertNull(flow.getNextTask_()); - assertNull(frame3.getParent()); - assertEquals(frame2, flow.activeFrame_); - root.fulfill.assertNotCalled(); - frame2.fulfill.assertNotCalled(); - frame3.fulfill.assertCalled('frame3 should have been resolved'); - - assertEquals(task7, flow.getNextTask_()); - assertNull(task7.getParent()); - assertEquals(frame2, flow.activeFrame_); - root.fulfill.assertNotCalled(); - frame2.fulfill.assertNotCalled(); - - assertNull(flow.getNextTask_()); - assertNull(frame2.getParent()); - assertEquals(root, flow.activeFrame_); - root.fulfill.assertNotCalled(); - frame2.fulfill.assertCalled('frame2 should have been resolved'); - - assertEquals(task8, flow.getNextTask_()); - assertNull(task8.getParent()); - assertEquals(root, flow.activeFrame_); - root.fulfill.assertNotCalled(); - - flow.commenceShutdown_.assertNotCalled(); - assertNull(flow.getNextTask_()); - assertNull(flow.activeFrame_); - root.fulfill.assertCalled('Root should have been resolved'); - flow.commenceShutdown_.assertCalled(); -} - - -function testAbortFrame_noActiveFrame() { - flow.abortFrame_(STUB_ERROR); - assertIsStubError(flowTester.getFailure()); - assertNull(flow.activeFrame_); -} - - -function testAbortFrame_activeIsOnlyFrame() { - // Make the ControlFlow think the flow is not-idle. - flow.emit(webdriver.promise.ControlFlow.EventType.SCHEDULE_TASK); - - flow.activeFrame_ = createFrame(); - flow.abortFrame_(STUB_ERROR); - assertNull(flow.activeFrame_); - flowTester.assertStillRunning(); - - clock.tick(); - assertIsStubError(flowTester.getFailure()); -} - - -function testAbortFrame_unhandledAbortionsBubbleUp() { - var root = flow.activeFrame_ = createFrame(), - frame1 = createFrame(), - frame2 = createFrame(), - frame3 = createFrame(), - task = createTask(); - - var rootHelper = installResolveHelper(root), - frame1Helper = installResolveHelper(frame1), - frame2Helper = installResolveHelper(frame2), - frame3Helper = installResolveHelper(frame3); - - flow.abortNow_ = callbackHelper(assertIsStubError); - - root.addChild(frame1); - root.addChild(frame2); - root.addChild(frame3); - root.addChild(task); - - assertArrayEquals([task], frame3.children_); - assertArrayEquals([frame3], frame2.children_); - assertArrayEquals([frame2], frame1.children_); - assertArrayEquals([frame1], root.children_); - - assertEquals(task, flow.getNextTask_()); - assertEquals(frame3, flow.activeFrame_); - flow.abortNow_.assertNotCalled(); - rootHelper.assertNeither(); - frame1Helper.assertNeither(); - frame2Helper.assertNeither(); - frame3Helper.assertNeither(); - - flow.abortFrame_(STUB_ERROR); - assertEquals(frame2, flow.activeFrame_); - flow.abortNow_.assertNotCalled(); - rootHelper.assertNeither(); - frame1Helper.assertNeither(); - frame2Helper.assertNeither(); - frame3Helper.assertErrback(); - - clock.tick(); - assertEquals(frame1, flow.activeFrame_); - flow.abortNow_.assertNotCalled(); - rootHelper.assertNeither(); - frame1Helper.assertNeither(); - frame2Helper.assertErrback(); - - clock.tick(); - assertEquals(root, flow.activeFrame_); - flow.abortNow_.assertNotCalled(); - rootHelper.assertNeither(); - frame1Helper.assertErrback(); - - clock.tick(); - assertNull(flow.activeFrame_); - flow.abortNow_.assertNotCalled(); - rootHelper.assertErrback(); - - clock.tick(); - assertNull(flow.activeFrame_); - flow.abortNow_.assertCalled(); - - function installResolveHelper(promise) { - var reject = promise.reject; - var pair = callbackPair(promise.fulfill, function(e) { - assertIsStubError(e); - reject(e); - }); - promise.fulfill = pair.callback; - promise.reject = pair.errback; - return pair; - } -} - - -function testRunInNewFrame_nothingScheduledInFunction() { - var root = flow.activeFrame_ = createFrame(), - task1 = createTask(), - task2 = createTask(); - - root.addChild(task1); - root.addChild(task2); - assertArrayEquals([task1, task2], root.children_); - - assertEquals(task1, flow.getNextTask_()); - assertEquals(root, flow.activeFrame_); - assertArrayEquals([task2], root.children_); - - var pair = callbackPair(assertUndefined); - flow.runInNewFrame_(goog.nullFunction, pair.callback, pair.errback); - pair.assertCallback(); - assertEquals(root, flow.activeFrame_); - assertArrayEquals([task2], root.children_); -} - - -function testRunInNewFrame_functionThrows() { - var root = flow.activeFrame_ = createFrame(), - task1 = createTask(), - task2 = createTask(); - - root.addChild(task1); - root.addChild(task2); - assertArrayEquals([task1, task2], root.children_); - - assertEquals(task1, flow.getNextTask_()); - assertEquals(root, flow.activeFrame_); - assertArrayEquals([task2], root.children_); - - var pair = callbackPair(null, assertIsStubError); - flow.runInNewFrame_(throwStubError, pair.callback, pair.errback); - pair.assertErrback(); - assertEquals(root, flow.activeFrame_); - assertArrayEquals([task2], root.children_); -} - - -function testRunInNewFrame_functionThrowsAfterSchedulingTasks() { - var root = flow.activeFrame_ = createFrame(), - task1 = createTask('task1'), - task2 = createTask('task2'); - - root.addChild(task1); - root.addChild(task2); - assertArrayEquals([task1, task2], root.children_); - - assertEquals(task1, flow.getNextTask_()); - assertEquals(root, flow.activeFrame_); - assertArrayEquals([task2], root.children_); - - var pair = callbackPair(null, assertIsStubError); - flow.runInNewFrame_(function() { - flow.execute(goog.nullFunction); - throw STUB_ERROR; - }, pair.callback, pair.errback); - pair.assertErrback(); - assertEquals(root, flow.activeFrame_); - assertArrayEquals([task2], root.children_); -} - - -function testRunInNewFrame_whenThereIsNoCurrentActiveFrame_noopFunc() { - var pair = callbackPair(assertUndefined); - flow.runInNewFrame_(goog.nullFunction, pair.callback, pair.errback); - pair.assertCallback(); - assertNull(flow.activeFrame_); - assertEquals('[]', flow.getSchedule()); -} - - -function testRunInNewFrame_whenThereIsNoCurrentActiveFrame_funcThrows() { - var pair = callbackPair(null, assertIsStubError); - flow.runInNewFrame_(throwStubError, pair.callback, pair.errback); - pair.assertErrback(); - assertNull(flow.activeFrame_); - assertEquals('[]', flow.getSchedule()); -} - - -function - testRunInNewFrame_whenThereIsNoCurrentActiveFrame_throwsAfterSchedule() { - var pair = callbackPair(null, assertIsStubError); - flow.runInNewFrame_(function() { - flow.execute('task3', goog.nullFunction); - throwStubError(); - }, pair.callback, pair.errback); - pair.assertErrback(); - assertNull(flow.activeFrame_); - assertEquals('[]', flow.getSchedule()); -} - - -function testRunInNewFrame_returnsPrimitiveFunctionResultImmediately() { - var pair = callbackPair(goog.partial(assertEquals, 23)); - flow.runInNewFrame_(function() { - return 23; - }, pair.callback, pair.errback); - pair.assertCallback(); -} - - -function testRunInNewFrame_updatesSchedulingFrameForContextOfFunction() { - var root = flow.activeFrame_ = createFrame(); - - var pair = callbackPair(); - flow.runInNewFrame_(function() { - assertNotNull(flow.activeFrame_); - assertNotNull(flow.schedulingFrame_); - assertNotEquals(root, flow.schedulingFrame_); - assertArrayEquals([flow.schedulingFrame_], root.children_); - assertEquals(root, flow.schedulingFrame_.getParent()); - }, pair.callback, pair.errback); - pair.assertCallback(); - - assertEquals('Did not restore active frame', root, flow.activeFrame_); -} - - -function testRunInNewFrame_doesNotReturnUntilScheduledFrameResolved() { - var root = flow.activeFrame_ = createFrame(), - task1 = createTask('task1'), - task2 = createTask('task2'); - - root.addChild(task1); - root.addChild(task2); - assertArrayEquals([task1, task2], root.children_); - - assertEquals(task1, flow.getNextTask_()); - assertEquals(root, flow.activeFrame_); - assertArrayEquals([task2], root.children_); - - var pair = callbackPair(); - flow.runInNewFrame_(function() { - schedule('task3'); - }, pair.callback, pair.errback); - - pair.assertNeither('active frame not resolved yet'); - assertEquals(root, flow.activeFrame_); - - var task = flow.getNextTask_(); - assertEquals('task3', task.getDescription()); - assertEquals(root.children_[0], flow.activeFrame_); - pair.assertNeither('active frame still not resolved yet'); - - assertNull(flow.getNextTask_()); - pair.assertCallback(); - assertEquals(root, flow.activeFrame_); - assertEquals(task2, flow.getNextTask_()); -} - - -function testRunInNewFrame_doesNotReturnUntilScheduledFrameResolved_nested() { - var root = flow.activeFrame_ = createFrame(); - - schedule('task1'); - schedule('task2'); - assertEquals('task1', flow.getNextTask_().getDescription()); - - var pair1 = callbackPair(), pair2 = callbackPair(); - flow.runInNewFrame_(function() { - schedule('task3'); - flow.runInNewFrame_(function() { - schedule('task4'); - }, pair2.callback, pair2.errback); - }, pair1.callback, pair1.errback); - - pair1.assertNeither(); - pair2.assertNeither(); - assertEquals('task3', flow.getNextTask_().getDescription()); - assertEquals('task4', flow.getNextTask_().getDescription()); - assertNull(flow.getNextTask_()); - pair1.assertNeither(); - pair2.assertCallback(); - assertNull(flow.getNextTask_()); - pair1.assertCallback(); - - assertEquals(root, flow.activeFrame_); - assertEquals('task2', flow.getNextTask_().getDescription()); +function testScheduling_aSimpleFunction() { + schedule('go'); + return waitForIdle().then(function() { + assertFlowHistory('go'); + }); } -function testScheduling_aSimpleFunction() { - schedule('go'); - runAndExpectSuccess(); - assertFlowHistory('go'); +function testScheduling_aSimpleFunctionWithANonPromiseReturnValue() { + schedule('go', 123).then(function(value) { + assertEquals(123, value); + }); + return waitForIdle().then(function() { + assertFlowHistory('go'); + }); } @@ -714,42 +194,40 @@ function testScheduling_aSimpleSequence() { schedule('a'); schedule('b'); schedule('c'); - runAndExpectSuccess(); - assertFlowHistory('a', 'b', 'c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); } function testScheduling_invokesCallbacksWhenTaskIsDone() { - var callback; var d = new webdriver.promise.Deferred(); - schedule('a', d.promise).then(callback = callbackHelper(function(value) { + var called = false; + var done = schedule('a', d.promise).then(function(value) { + called = true; assertEquals(123, value); - })); - callback.assertNotCalled('Callback should not have been called yet'); - - turnEventLoop(); - callback.assertNotCalled('Task has not completed yet!'); - - d.fulfill(123); - callback.assertCalled('Callback should have been called!'); - runAndExpectSuccess(); - assertFlowHistory('a'); + }); + return timeout(5).then(function() { + assertFalse(called); + d.fulfill(123); + return done; + }). + then(waitForIdle). + then(function() { + assertFlowHistory('a'); + }); } function testScheduling_blocksUntilPromiseReturnedByTaskIsResolved() { - var d = new webdriver.promise.Deferred(); - schedule('a', d.promise); - schedule('b'); - - assertFlowHistory(); - turnEventLoop(); assertFlowHistory('a'); - turnEventLoop(); assertFlowHistory('a'); // Task 'a' is still running. - turnEventLoop(); assertFlowHistory('a'); // Task 'a' is still running. - - d.fulfill(123); - runAndExpectSuccess(); - assertFlowHistory('a', 'b'); + 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')); } @@ -762,16 +240,18 @@ function testScheduling_waitsForReturnedPromisesToResolve() { assertEquals('fluffy bunny', value); })); - callback.assertNotCalled('d1 not resolved yet'); - - d1.fulfill(d2); - callback.assertNotCalled('Should not be called yet; blocked on d2'); - - d2.fulfill('fluffy bunny'); - - runAndExpectSuccess(); - callback.assertCalled('d2 has been resolved'); - assertFlowHistory('a'); + 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'); + }); } @@ -780,30 +260,27 @@ function testScheduling_executesTasksInAFutureTurnAfterTheyAreScheduled() { function incr() { count++; } scheduleAction('', incr); - assertEquals(0, count); - - turnEventLoop(); - assertEquals(1, count); - - runAndExpectSuccess(); + return waitForIdle().then(function() { + assertEquals(1, count); + }); } function testScheduling_executesOneTaskPerTurnOfTheEventLoop() { - var count = 0; - function incr() { count++; } - - scheduleAction('', incr); - scheduleAction('', incr); - - assertEquals(0, count); - turnEventLoop(); - assertEquals(1, count); - turnEventLoop(); - assertEquals(2, count); + var order = []; + function go() { + order.push(order.length / 2); + goog.async.run(function() { + order.push('-'); + }); + } - runAndExpectSuccess(); + scheduleAction('', go); + scheduleAction('', go); + return waitForIdle().then(function() { + assertArrayEquals([0, '-', 1, '-'], order); + }) } @@ -812,9 +289,26 @@ function testScheduling_firstScheduledTaskIsWithinACallback() { schedule('a'); schedule('b'); schedule('c'); + }).then(function() { + assertFlowHistory('a', 'b', 'c'); + }); + return waitForIdle(); +} + + +function testScheduling_newTasksAddedWhileWaitingOnTaskReturnedPromise() { + 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', 'c', 'b'); }); - runAndExpectSuccess(); - assertFlowHistory('a', 'b', 'c'); } @@ -823,9 +317,9 @@ function testFraming_callbacksRunInANewFrame() { schedule('c'); }); schedule('b'); - - runAndExpectSuccess(); - assertFlowHistory('a', 'c', 'b'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'c', 'b'); + }); } @@ -841,8 +335,41 @@ function testFraming_lotsOfNesting() { }); schedule('b'); - runAndExpectSuccess(); - assertFlowHistory('a', 'c', 'e', 'g', 'f', 'd', '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'); + }); } @@ -857,8 +384,9 @@ function testFraming_eachCallbackWaitsForAllScheduledTasksToComplete() { }); schedule('e'); - runAndExpectSuccess(); - assertFlowHistory('a', 'b', 'c', 'd', 'e'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e'); + }); } @@ -873,19 +401,33 @@ function testFraming_eachCallbackWaitsForReturnTasksToComplete() { }); schedule('e'); - runAndExpectSuccess(); - assertFlowHistory('a', 'b', 'c', 'd', 'e'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e'); + }); } -function testFraming_promiseCallbacks() { +function testFraming_callbacksOnAResolvedPromiseInsertIntoTheCurrentFlow() { webdriver.promise.fulfilled().then(function() { schedule('b'); }); schedule('a'); - runAndExpectSuccess(); - assertFlowHistory('b', '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'); + }); } @@ -896,21 +438,21 @@ function testFraming_allCallbacksInAFrameAreScheduledWhenPromiseIsResolved() { a.then(function() { schedule('d'); }); schedule('e'); - runAndExpectSuccess(); - assertFlowHistory('a', 'b', 'd', 'c', 'e'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'd', 'c', 'e'); + }); } function testFraming_tasksScheduledInInActiveFrameDoNotGetPrecedence() { - var d = new webdriver.promise.Deferred(); - + var d = webdriver.promise.fulfilled(); schedule('a'); schedule('b'); d.then(function() { schedule('c'); }); - d.fulfill(); - runAndExpectSuccess(); - assertFlowHistory('a', 'b', 'c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); } @@ -937,61 +479,53 @@ function testFraming_tasksScheduledInAFrameGetPrecedence_1() { }); schedule('j'); - runAndExpectSuccess(); - assertFlowHistory('a', 'b', 'c', 'd', 'e', 'g', 'f', 'h', 'i', 'j'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e', 'g', 'f', 'h', 'i', 'j'); + }); } function testErrorHandling_thrownErrorsArePassedToTaskErrback() { - var callbacks = callbackPair(null, assertIsStubError); scheduleAction('function that throws', throwStubError). - then(callbacks.callback, callbacks.errback); - runAndExpectSuccess(callbacks.assertErrback); + then(fail, assertIsStubError); + return waitForIdle(); } function testErrorHandling_thrownErrorsPropagateThroughPromiseChain() { - var callbacks = callbackPair(null, assertIsStubError); scheduleAction('function that throws', throwStubError). - then(callbacks.callback). - then(null, callbacks.errback); - runAndExpectSuccess(callbacks.assertErrback); + then(fail). + then(fail, assertIsStubError); + return waitForIdle(); } function testErrorHandling_catchesErrorsFromFailedTasksInAFrame() { - var errback; - - schedule('a'). - then(function() { - schedule('b'); - scheduleAction('function that throws', throwStubError); - }). - then(null, errback = callbackHelper(assertIsStubError)); - - runAndExpectSuccess(); - errback.assertCalled(); -} - - -function testErrorHandling_abortsIfOnlyTaskThrowsAnError() { - scheduleAction('function that throws', throwStubError); - runAndExpectFailure(assertIsStubError); + schedule('a').then(function() { + schedule('b'); + scheduleAction('function that throws', throwStubError); + }). + then(fail, assertIsStubError); + return waitForIdle(); } function testErrorHandling_abortsIfOnlyTaskReturnsAnUnhandledRejection() { - var rejected = webdriver.promise.rejected(STUB_ERROR); - scheduleAction('function that throws', function() { return rejected; }); - runAndExpectFailure(assertIsStubError); + scheduleAction('function that returns rejected promise', function() { + return webdriver.promise.rejected(new StubError); + }); + return waitForAbort().then(assertIsStubError); } function testErrorHandling_abortsIfThereIsAnUnhandledRejection() { - webdriver.promise.rejected(STUB_ERROR); + webdriver.promise.rejected(new StubError); schedule('this should not run'); - runAndExpectFailure(assertIsStubError); - assertFlowHistory(); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory(/* none */); + }); } @@ -1001,8 +535,11 @@ function testErrorHandling_abortsSequenceIfATaskFails() { scheduleAction('c', throwStubError); schedule('d'); // Should never execute. - runAndExpectFailure(assertIsStubError); - assertFlowHistory('a', 'b', 'c'); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'b', 'c'); + }); } @@ -1011,8 +548,11 @@ function testErrorHandling_abortsFromUnhandledFramedTaskFailures_1() { scheduleAction('inner task', throwStubError); }); schedule('this should not run'); - runAndExpectFailure(assertIsStubError); - assertFlowHistory('outer task', 'inner task'); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('outer task', 'inner task'); + }); } @@ -1025,37 +565,45 @@ function testErrorHandling_abortsFromUnhandledFramedTaskFailures_2() { }); }); - runAndExpectFailure(assertIsStubError); - assertFlowHistory('a', 'b', 'c'); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'b', 'c'); + }); } function testErrorHandling_abortsWhenErrorBubblesUpFromFullyResolvingAnObject() { - var obj = {'foo': webdriver.promise.rejected(STUB_ERROR)}; + var callback = callbackHelper(function() { + return webdriver.promise.rejected('rejected 2'); + }); + scheduleAction('', function() { - return webdriver.promise.fullyResolved(obj). - then(function() { - // Should never get here; STUB_ERROR should abort the flow above. - return webdriver.promise.rejected('rejected 2'); - }); + var obj = {'foo': webdriver.promise.rejected(new StubError)}; + return webdriver.promise.fullyResolved(obj).then(callback); }); - runAndExpectFailure(assertIsStubError); + + return waitForAbort(). + then(assertIsStubError). + then(callback.assertNotCalled); } function testErrorHandling_abortsWhenErrorBubblesUpFromFullyResolvingAnObject_withCallback() { - var obj = {'foo': webdriver.promise.rejected(STUB_ERROR)}; - var callback; + var callback1 = callbackHelper(function() { + return webdriver.promise.rejected('rejected 2'); + }); + var callback2 = callbackHelper(); + scheduleAction('', function() { - return webdriver.promise.fullyResolved(obj). - then(function() { - // Should never get here; STUB_ERROR should abort the flow above. - return webdriver.promise.rejected('rejected 2'); - }); - }).then(callback = callbackHelper()); + var obj = {'foo': webdriver.promise.rejected(new StubError)}; + return webdriver.promise.fullyResolved(obj).then(callback1); + }).then(callback2); - callback.assertNotCalled(); - runAndExpectFailure(assertIsStubError); + return waitForAbort(). + then(assertIsStubError). + then(callback1.assertNotCalled). + then(callback2.assertNotCalled); } @@ -1066,8 +614,7 @@ function testErrorHandling_canCatchErrorsFromNestedTasks() { return scheduleAction('b', throwStubError); }). thenCatch(errback = callbackHelper(assertIsStubError)); - runAndExpectSuccess(); - errback.assertCalled(); + return waitForIdle().then(errback.assertCalled); } @@ -1076,37 +623,45 @@ function testErrorHandling_nestedCommandFailuresCanBeCaughtAndSuppressed() { schedule('a').then(function() { return schedule('b').then(function() { return schedule('c').then(function() { - throw STUB_ERROR; + throw new StubError; }); }); }).thenCatch(errback = callbackHelper(assertIsStubError)); schedule('d'); - runAndExpectSuccess(); - assertFlowHistory('a', 'b', 'c', 'd'); - errback.assertCalled(); + return waitForIdle(). + then(errback.assertCalled). + then(function() { + assertFlowHistory('a', 'b', 'c', 'd'); + }); } function testErrorHandling_aTaskWithAnUnhandledPromiseRejection() { schedule('a'); scheduleAction('sub-tasks', function() { - webdriver.promise.rejected(STUB_ERROR); + webdriver.promise.rejected(new StubError); }); schedule('should never run'); - runAndExpectFailure(assertIsStubError); - assertFlowHistory('a', 'sub-tasks'); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'sub-tasks'); + }); } function testErrorHandling_aTaskThatReutrnsARejectedPromise() { schedule('a'); scheduleAction('sub-tasks', function() { - return webdriver.promise.rejected(STUB_ERROR); + return webdriver.promise.rejected(new StubError); }); schedule('should never run'); - runAndExpectFailure(assertIsStubError); - assertFlowHistory('a', 'sub-tasks') + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'sub-tasks'); + }); } @@ -1119,9 +674,11 @@ function testErrorHandling_discardsSubtasksIfTaskThrows() { }).then(pair.callback, pair.errback); schedule('d'); - runAndExpectSuccess(); - pair.assertErrback(); - assertFlowHistory('a', 'd'); + return waitForIdle(). + then(pair.assertErrback). + then(function() { + assertFlowHistory('a', 'd'); + }); } @@ -1134,9 +691,11 @@ function testErrorHandling_discardsRemainingSubtasksIfASubtaskFails() { }).then(pair.callback, pair.errback); schedule('e'); - runAndExpectSuccess(); - pair.assertErrback(); - assertFlowHistory('a', 'b', 'c', 'e'); + return waitForIdle(). + then(pair.assertErrback). + then(function() { + assertFlowHistory('a', 'b', 'c', 'e'); + }); } @@ -1152,8 +711,7 @@ function testTryFinally_happyPath() { schedulePush('foo'). then(goog.partial(schedulePush, 'bar')). thenFinally(goog.partial(schedulePush, 'baz')); - runAndExpectSuccess(assertingMessages('foo', 'bar', 'baz')); - assertFlowHistory('foo', 'bar', 'baz'); + return waitForIdle().then(assertingMessages('foo', 'bar', 'baz')); } @@ -1169,13 +727,14 @@ function testTryFinally_firstTryFails() { scheduleAction('doFoo and throw', function() { webdriver.test.testutil.messages.push('foo'); - throw STUB_ERROR; - }).then(goog.partial(schedulePush, 'bar')). - thenFinally(goog.partial(schedulePush, 'baz')); - runAndExpectFailure(function(e) { - assertIsStubError(e); - webdriver.test.testutil.assertMessages('foo', 'baz'); - }); + throw new StubError; + }). + then(function() { schedulePush('bar'); }). + thenFinally(function() { schedulePush('baz'); }); + + return waitForAbort(). + then(assertIsStubError). + then(assertingMessages('foo', 'baz')); } @@ -1193,56 +752,139 @@ function testTryFinally_secondTryFails() { then(function() { return scheduleAction('doBar and throw', function() { webdriver.test.testutil.messages.push('bar'); - throw STUB_ERROR; + throw new StubError; }); }). thenFinally(function() { return schedulePush('baz'); }); - runAndExpectFailure(function(e) { - assertIsStubError(e); - webdriver.test.testutil.assertMessages('foo', 'bar' , 'baz'); - }); + return waitForAbort(). + then(assertIsStubError). + then(assertingMessages('foo', 'bar', 'baz')); } -function testDelayedNesting_1() { - var a = schedule('a'); - schedule('b').then(function() { - a.then(function() { schedule('c'); }); - schedule('d'); +function testTaskCallbacksInterruptFlow() { + schedule('a').then(function() { + schedule('b'); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); }); - schedule('e'); - - runAndExpectSuccess(); - 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'); }); +function +testTaskCallbacksInterruptFlow_taskDependsOnImmediatelyFulfilledPromise() { + scheduleAction('a', function() { + return webdriver.promise.fulfilled(); + }).then(function() { + schedule('b'); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); }); - schedule('f'); - - runAndExpectSuccess(); - assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f'); } -function testDelayedNesting_3() { - var a = schedule('a'); +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'); - runAndExpectSuccess(); - assertFlowHistory('a', 'b', 'c', 'd', 'e'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e'); + }); } @@ -1256,8 +898,9 @@ function testDelayedNesting_4() { }); schedule('f'); - runAndExpectSuccess(); - assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f'); + }); } @@ -1275,27 +918,17 @@ function testDelayedNesting_5() { }); schedule('i'); - runAndExpectSuccess(); - assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'); + }); } -function testCancelsTerminationEventIfNewCommandIsScheduled() { - schedule('a'); - turnEventLoop(); - assertFlowHistory('a'); - flowTester.assertStillRunning(); - turnEventLoop(); - schedule('b'); - - runAndExpectSuccess(); - assertFlowHistory('a', 'b'); -} - function testWaiting_onAConditionThatIsAlwaysTrue() { scheduleWait(function() { return true;}, 0, 'waiting on true'); - runAndExpectSuccess(); - assertFlowHistory('0: waiting on true'); + return waitForIdle().then(function() { + assertFlowHistory('0: waiting on true'); + }); } @@ -1305,33 +938,26 @@ function testWaiting_aSimpleCountingCondition() { return ++count == 3; }, 200, 'counting to 3'); - turnEventLoop(); // Start the flow; triggers first condition poll. - assertEquals(1, count); - clock.tick(100); // Poll 2 more times. - clock.tick(100); - assertEquals(3, count); - - runAndExpectSuccess(); + 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'); - turnEventLoop(); - flowTester.assertStillRunning(); - - // Should be able to turn the event loop a few times since we're blocked - // on our wait condition. - turnEventLoop(); - turnEventLoop(); - - d.fulfill(123); - runAndExpectSuccess(); + return timeout(50).then(function() { + assertEquals(1, count); + d.fulfill(123); + return waitForIdle(); + }); } @@ -1341,12 +967,9 @@ function testWaiting_aConditionThatReturnsAPromise_2() { return webdriver.promise.fulfilled(++count == 3); }, 200, 'waiting for promise'); - turnEventLoop(); // Start the flow; triggers first condition poll. - clock.tick(100); // Poll 2 more times. - clock.tick(100); - assertEquals(3, count); - - runAndExpectSuccess(); + return waitForIdle().then(function() { + assertEquals(3, count); + }); } @@ -1359,42 +982,14 @@ function testWaiting_aConditionThatReturnsATaskResult() { }, 200, 'counting to 3'); schedule('post wait'); - turnEventLoop(); - assertEquals(0, count); - assertFlowHistory('0: counting to 3'); - - turnEventLoop(); // Runs scheduled task. - turnEventLoop(); - assertFlowHistory( - '0: counting to 3', 'increment count'); - assertEquals(1, count); - - clock.tick(100); // Advance clock for next polling pass. - assertEquals(1, count); - turnEventLoop(); - assertEquals(2, count); - turnEventLoop(); - assertFlowHistory( - '0: counting to 3', 'increment count', - '1: counting to 3', 'increment count'); - - clock.tick(100); // Advance clock for next polling pass. - assertEquals(2, count); - turnEventLoop(); - assertEquals(3, count); - turnEventLoop(); - assertFlowHistory( - '0: counting to 3', 'increment count', - '1: counting to 3', 'increment count', - '2: counting to 3', 'increment count'); - - runAndExpectSuccess(); - assertEquals(3, count); - assertFlowHistory( - '0: counting to 3', 'increment count', - '1: counting to 3', 'increment count', - '2: counting to 3', 'increment count', - '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'); + }); } @@ -1406,13 +1001,14 @@ function testWaiting_conditionContainsASubtask() { }, 200, 'counting to 3'); schedule('post wait'); - runAndExpectSuccess(); - assertEquals(3, count); - assertFlowHistory( - '0: counting to 3', 'sub task', - '1: counting to 3', 'sub task', - '2: counting to 3', 'sub task', - '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'); + }); } @@ -1425,10 +1021,13 @@ function testWaiting_cancelsWaitIfScheduledTaskFails() { }, 200, 'waiting to go boom').then(pair.callback, pair.errback); schedule('post wait'); - runAndExpectSuccess(); - assertFlowHistory( - '0: waiting to go boom', 'boom', - 'post wait'); + return waitForIdle(). + then(pair.assertErrback). + then(function() { + assertFlowHistory( + '0: waiting to go boom', 'boom', + 'post wait'); + }); } @@ -1437,21 +1036,27 @@ function testWaiting_failsIfConditionThrows() { scheduleWait(throwStubError, 0, 'goes boom'). then(callbacks.callback, callbacks.errback); schedule('post wait'); - runAndExpectSuccess(); - assertFlowHistory('0: goes boom', 'post wait'); - callbacks.assertErrback(); + + 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(STUB_ERROR); + return webdriver.promise.rejected(new StubError); }, 0, 'goes boom').then(callbacks.callback, callbacks.errback); schedule('post wait'); - runAndExpectSuccess(); - assertFlowHistory('0: goes boom', 'post wait'); - callbacks.assertErrback(); + + return waitForIdle(). + then(callbacks.assertErrback). + then(function() { + assertFlowHistory('0: goes boom', 'post wait'); + }); } @@ -1461,9 +1066,12 @@ function testWaiting_failsIfConditionHasUnhandledRejection() { webdriver.promise.controlFlow().execute(throwStubError); }, 0, 'goes boom').then(callbacks.callback, callbacks.errback); schedule('post wait'); - runAndExpectSuccess(); - assertFlowHistory('0: goes boom', 'post wait'); - callbacks.assertErrback(); + + return waitForIdle(). + then(callbacks.assertErrback). + then(function() { + assertFlowHistory('0: goes boom', 'post wait'); + }); } @@ -1473,27 +1081,19 @@ function testWaiting_failsIfConditionHasAFailedSubtask() { scheduleWait(function() { scheduleAction('maybe throw', function() { if (++count == 2) { - throw STUB_ERROR; + throw new StubError; } }); }, 200, 'waiting').then(callbacks.callback, callbacks.errback); schedule('post wait'); - turnEventLoop(); - assertEquals(0, count); - - turnEventLoop(); // Runs scheduled task. - assertEquals(1, count); - - clock.tick(100); // Advance clock for next polling pass. - assertEquals(1, count); - - runAndExpectSuccess(); - assertEquals(2, count); - assertFlowHistory( - '0: waiting', 'maybe throw', - '1: waiting', 'maybe throw', - 'post wait'); + return waitForIdle().then(function() { + assertEquals(2, count); + assertFlowHistory( + '0: waiting', 'maybe throw', + '1: waiting', 'maybe throw', + 'post wait'); + }); } @@ -1502,83 +1102,81 @@ function testWaiting_pollingLoopWaitsForAllScheduledTasksInCondition() { scheduleWait(function() { scheduleAction('increment count', function() { ++count; }); return count >= 3; - }, 300, 'counting to 3'); + }, 350, 'counting to 3'); schedule('post wait'); - turnEventLoop(); - assertEquals(0, count); - - turnEventLoop(); // Runs scheduled task. - turnEventLoop(); - assertEquals(1, count); + 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'); + }); +} - clock.tick(100); // Advance clock for next polling pass. - assertEquals(1, count); - turnEventLoop(); - assertEquals(2, count); - clock.tick(100); // Advance clock for next polling pass. - assertEquals(2, count); +function testWaiting_waitsForeverOnAZeroTimeout() { + var done = false; + setTimeout(function() { + done = true; + }, 500); + var waitResult = scheduleWait(function() { + return done; + }, 0); - runAndExpectSuccess(); - 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'); + return timeout(250).then(function() { + assertFalse(done); + return timeout(300); + }).then(function() { + assertTrue(done); + return waitResult; + }); } -function testWaiting_blocksNextTaskOnWait() { - var count = 0; - scheduleWait(function() { - return ++count == 3; - }, 200, 'counting to 3'); - schedule('post wait'); - - turnEventLoop(); // Start the flow; triggers first condition poll. - assertFlowHistory('0: counting to 3'); - assertEquals(1, count); - clock.tick(100); // Poll 2 more times. - assertFlowHistory( - '0: counting to 3', - '1: counting to 3'); - clock.tick(100); - assertFlowHistory( - '0: counting to 3', - '1: counting to 3', - '2: counting to 3'); - assertEquals(3, count); +function testWaiting_waitsForeverIfTimeoutOmitted() { + var done = false; + setTimeout(function() { + done = true; + }, 500); + var waitResult = scheduleWait(function() { + return done; + }); - runAndExpectSuccess(); - assertFlowHistory( - '0: counting to 3', - '1: counting to 3', - '2: counting to 3', - 'post wait'); + return timeout(250).then(function() { + assertFalse(done); + return timeout(300); + }).then(function() { + assertTrue(done); + return waitResult; + }); } -function testWaiting_timesOut_zeroTimeout() { - scheduleWait(function() { return false; }, 0, 'always false'); - runAndExpectFailure(goog.nullFunction); -} - function testWaiting_timesOut_nonZeroTimeout() { var count = 0; scheduleWait(function() { - return ++count == 3; - }, 100, 'counting to 3'); - - turnEventLoop(); // Start the flow; triggers first condition poll. - clock.tick(100); // Poll 2 more times. - assertEquals(2, count); - - runAndExpectFailure(function() { - assertFlowHistory('0: counting to 3', '1: counting to 3'); - assertEquals(2, count); + count += 1; + var ms = count === 2 ? 65 : 5; + var start = goog.now(); + 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); }); } @@ -1586,29 +1184,9 @@ function testWaiting_timesOut_nonZeroTimeout() { function testWaiting_shouldFailIfConditionReturnsARejectedPromise() { var count = 0; scheduleWait(function() { - return webdriver.promise.rejected(STUB_ERROR); + return webdriver.promise.rejected(new StubError); }, 100, 'counting to 3'); - - runAndExpectFailure(assertIsStubError); -} - - -function testWaiting_callbacks() { - var pair = callbackPair(); - - scheduleWait(function() { return true;}, 0, 'waiting on true'). - then(pair.callback, pair.errback); - pair.assertNeither('Wait not expected to be done yet'); - turnEventLoop(); - pair.assertCallback('Wait callback not called!'); - runAndExpectSuccess(); -} - - -function testWaiting_errbacks() { - scheduleWait(function() { return false; }, 0, 'always false'); - - runAndExpectFailure(); + return waitForAbort().then(assertIsStubError); } @@ -1620,8 +1198,9 @@ function testWaiting_scheduleWithIntermittentWaits() { schedule('c'); scheduleWait(function() { return true; }, 0, 'wait 3'); - runAndExpectSuccess(); - assertFlowHistory('a', '0: wait 1', 'b', '0: wait 2', 'c', '0: wait 3'); + return waitForIdle().then(function() { + assertFlowHistory('a', '0: wait 1', 'b', '0: wait 2', 'c', '0: wait 3'); + }); } @@ -1638,10 +1217,57 @@ function testWaiting_scheduleWithIntermittentAndNestedWaits() { schedule('c'); scheduleWait(function() { return true; }, 0, 'wait 4'); - runAndExpectSuccess(); - assertFlowHistory( - 'a', '0: wait 1', 'd', '0: wait 2', 'e', 'b', '0: wait 3', 'c', - '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(value) { + assertArrayEquals(['a', 'b'], messages); + }); + + return waitForIdle(); } @@ -1653,8 +1279,9 @@ function testSubtasks() { }); schedule('b'); - runAndExpectSuccess(); - assertFlowHistory('a', 'sub-tasks', 'c', 'd', 'b'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'sub-tasks', 'c', 'd', 'b'); + }); } @@ -1670,9 +1297,10 @@ function testSubtasks_nesting() { }); schedule('f'); - runAndExpectSuccess(); - assertFlowHistory( - 'a', 'sub-tasks', 'b', 'sub-sub-tasks', 'c', 'd', 'e', 'f'); + return waitForIdle().then(function() { + assertFlowHistory( + 'a', 'sub-tasks', 'b', 'sub-sub-tasks', 'c', 'd', 'e', 'f'); + }); } @@ -1683,8 +1311,9 @@ function testSubtasks_taskReturnsSubTaskResult_1() { }); schedule('b'); - runAndExpectSuccess(); - assertFlowHistory('a', 'sub-tasks', 'c', 'b'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'sub-tasks', 'c', 'b'); + }); } @@ -1697,12 +1326,100 @@ function testSubtasks_taskReturnsSubTaskResult_2() { })); schedule('b'); - runAndExpectSuccess(); - assertFlowHistory('a', 'sub-tasks','b'); - callback.assertCalled(); + 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() { @@ -1710,20 +1427,26 @@ function testSubtasks_subTaskFails_1() { }); schedule('should never execute'); - runAndExpectFailure(assertIsStubError); - assertFlowHistory('a', 'sub-tasks', 'sub-task that fails'); + 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(STUB_ERROR); + return webdriver.promise.rejected(new StubError); }); schedule('should never execute'); - runAndExpectFailure(assertIsStubError); - assertFlowHistory('a', 'sub-tasks'); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'sub-tasks'); + }); } @@ -1732,13 +1455,15 @@ function testSubtasks_subTaskFails_3() { schedule('a'); scheduleAction('sub-tasks', function() { - return webdriver.promise.rejected(STUB_ERROR); + return webdriver.promise.rejected(new StubError); }).then(callbacks.callback, callbacks.errback); schedule('b'); - runAndExpectSuccess(); - assertFlowHistory('a', 'sub-tasks', 'b'); - callbacks.assertErrback(); + return waitForIdle(). + then(function() { + assertFlowHistory('a', 'sub-tasks', 'b'); + callbacks.assertErrback(); + }); } @@ -1749,58 +1474,58 @@ function testEventLoopWaitsOnPendingPromiseRejections_oneRejection() { }); scheduleAction('two', goog.nullFunction); - turn(); - assertFlowHistory('one'); - turn(-1); - d.reject(STUB_ERROR); - clock.tick(1); - assertFlowHistory('one'); - runAndExpectFailure(assertIsStubError); - assertFlowHistory('one'); - - function turn(opt_minusN) { - var n = webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY; - if (opt_minusN) n -= Math.abs(opt_minusN); - clock.tick(n); - } + 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'); - var onError = new goog.testing.FunctionMock('onError', - goog.testing.Mock.LOOSE); - onError(once); - onError(twice); - onError.$replay(); - - flow.on( - webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, onError); + var seen = []; - scheduleAction('one', goog.nullFunction); - scheduleAction('two', goog.nullFunction); - - turn(); - assertFlowHistory('one'); - turn(-1); - webdriver.promise.rejected(once); - webdriver.promise.rejected(twice); - clock.tick(1); - assertFlowHistory('one'); - turn(); - onError.$verify(); - - function turn(opt_minusN) { - var n = webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY; - if (opt_minusN) n -= Math.abs(opt_minusN); - clock.tick(n); - } + 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: ' + seen)); + }, 500); + flow.on( + webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, + function(e) { + seen.push(e); + if (seen.length === 2) { + fulfill(); + } + }); + }).then(function() { + seen.sort(); + assertArrayEquals([once, twice], seen); + assertFlowHistory('one'); + assertFalse('Did not cancel the second task', twoResult.isPending()); + }); } function testCancelsPromiseReturnedByCallbackIfFrameFails_promiseCallback() { var chainPair = callbackPair(null, assertIsStubError); - var deferredPair = 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); @@ -1813,15 +1538,20 @@ function testCancelsPromiseReturnedByCallbackIfFrameFails_promiseCallback() { }). then(chainPair.callback, chainPair.errback); - runAndExpectSuccess(); - assertFlowHistory('boom'); - chainPair.assertErrback('chain errback not invoked'); - deferredPair.assertErrback('deferred errback not invoked'); + 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, 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); @@ -1834,10 +1564,11 @@ function testCancelsPromiseReturnedByCallbackIfFrameFails_taskCallback() { }). then(chainPair.callback, chainPair.errback); - runAndExpectSuccess(); - assertFlowHistory('a', 'boom'); - chainPair.assertErrback('chain errback not invoked'); - deferredPair.assertErrback('deferred errback not invoked'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'boom'); + chainPair.assertErrback('chain errback not invoked'); + deferredPair.assertErrback('deferred errback not invoked'); + }); } function testMaintainsOrderInCallbacksWhenATaskReturnsAPromise() { @@ -1852,93 +1583,12 @@ function testMaintainsOrderInCallbacksWhenATaskReturnsAPromise() { }); schedulePush('e'); - runAndExpectSuccess(); - assertFlowHistory('__start__', 'b', 'e'); - webdriver.test.testutil.assertMessages('a', 'c', 'b', 'd', 'e'); -} - -function assertFrame(description, frame) { - var regexp = new RegExp('^' + description + '(\\n at .*)*$'); - assertTrue( - 'Frame did not match expected regex:' + - '\n expected: ' + regexp + - '\n was: ' + frame, - regexp.test(frame)); -} - -function testHistory_removesLastTaskEachTimeANewTaskIsStarted() { - schedule('one').then(function() { - var flowHistory = webdriver.promise.controlFlow().getHistory(); - assertEquals(1, flowHistory.length); - assertFrame('one', flowHistory[0]); - }); - schedule('two').then(function() { - var flowHistory = webdriver.promise.controlFlow().getHistory(); - assertEquals(1, flowHistory.length); - assertFrame('two', flowHistory[0]); + return waitForIdle().then(function() { + assertFlowHistory('__start__', 'b', 'e'); + webdriver.test.testutil.assertMessages('a', 'c', 'b', 'd', 'e'); }); - schedule('three').then(function() { - var flowHistory = webdriver.promise.controlFlow().getHistory(); - assertEquals(1, flowHistory.length); - assertFrame('three', flowHistory[0]); - }); - runAndExpectSuccess(); - assertEquals(0, webdriver.promise.controlFlow().getHistory().length); } -function testHistory_clearsSubtaskHistoryWhenParentTaskCompletes() { - scheduleAction('one', function() { - schedule('two').then(function() { - var flowHistory = webdriver.promise.controlFlow().getHistory(); - assertEquals(2, flowHistory.length); - assertFrame('two', flowHistory[0]); - assertFrame('one', flowHistory[1]); - }); - }).then(function() { - var flowHistory = webdriver.promise.controlFlow().getHistory(); - assertEquals(1, flowHistory.length); - assertFrame('one', flowHistory[0]); - }); - runAndExpectSuccess(); - assertFlowHistory('one', 'two'); - assertEquals(0, webdriver.promise.controlFlow().getHistory().length); -} - -function testHistory_preservesHistoryWhenChildTaskFails() { - scheduleAction('one', function() { - scheduleAction('two', function() { - scheduleAction('three', throwStubError); - }); - }).then(fail, function() { - var flowHistory = webdriver.promise.controlFlow().getHistory(); - assertEquals(3, flowHistory.length); - assertFrame('three', flowHistory[0]); - assertFrame('two', flowHistory[1]); - assertFrame('one', flowHistory[2]); - }); - runAndExpectSuccess(); - assertFlowHistory('one', 'two', 'three'); - assertEquals(0, webdriver.promise.controlFlow().getHistory().length); -} - -function testHistory_subtaskFailureIsIgnoredByErrback() { - scheduleAction('one', function() { - - scheduleAction('two', function() { - scheduleAction('three', throwStubError); - }).thenCatch(goog.nullFunction); - - schedule('post error').then(function() { - var flowHistory = webdriver.promise.controlFlow().getHistory(); - assertEquals(2, flowHistory.length); - assertFrame('post error', flowHistory[0]); - assertFrame('one', flowHistory[1]); - }); - }); - runAndExpectSuccess(); - assertFlowHistory('one', 'two', 'three', 'post error'); - assertEquals(0, webdriver.promise.controlFlow().getHistory().length); -} function assertFlowIs(flow) { assertEquals(flow, webdriver.promise.controlFlow()); @@ -1946,34 +1596,38 @@ function assertFlowIs(flow) { 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); }); }); - runAndExpectSuccess(); - assertFlowIs(defaultFlow); + return waitForIdle().then(function() { + assertFlowIs(defaultFlow); + assertArrayEquals([0, 1], order); + }); } function testCreateFlowReturnsPromisePairedWithCreatedFlow() { - var defaultFlow = webdriver.promise.controlFlow(); - - var newFlow; - webdriver.promise.createFlow(function(flow) { - newFlow = flow; - assertFlowIs(newFlow); - }).then(function() { - assertFlowIs(newFlow); + 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); + }); }); - - runAndExpectSuccess(); } -function testDeferredFactoriesCreateForActiveFlow() { +function testDeferredFactoriesCreateForActiveFlow_defaultFlow() { var e = Error(); var defaultFlow = webdriver.promise.controlFlow(); webdriver.promise.fulfilled().then(function() { @@ -1987,24 +1641,31 @@ function testDeferredFactoriesCreateForActiveFlow() { assertFlowIs(defaultFlow); }); - var newFlow; - webdriver.promise.createFlow(function(flow) { - newFlow = flow; + return waitForIdle(); +} + + +function testDeferredFactoriesCreateForActiveFlow_newFlow() { + var e = Error(); + var newFlow = new webdriver.promise.ControlFlow; + newFlow.execute(function() { webdriver.promise.fulfilled().then(function() { - assertFlowIs(flow); + assertFlowIs(newFlow); }); + webdriver.promise.rejected(e).then(null, function(err) { assertEquals(e, err); - assertFlowIs(flow); + assertFlowIs(newFlow); }); + webdriver.promise.defer().then(function() { - assertFlowIs(flow); + assertFlowIs(newFlow); }); }).then(function() { assertFlowIs(newFlow); }); - runAndExpectSuccess(); + return waitForIdle(newFlow); } function testFlowsSynchronizeWithThemselvesNotEachOther() { @@ -2018,92 +1679,125 @@ function testFlowsSynchronizeWithThemselvesNotEachOther() { schedulePush('d', 'd'); }); - runAndExpectSuccess(); - webdriver.test.testutil.assertMessages('a', 'c', 'd', 'b'); + return waitForIdle().then(function() { + webdriver.test.testutil.assertMessages('a', 'c', 'd', 'b'); + }); } function testUnhandledErrorsAreReportedToTheOwningFlow() { - var error1 = Error(); - var error2 = Error(); + 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); + }); - var newFlow; webdriver.promise.createFlow(function(flow) { - newFlow = flow; + flow.once('uncaughtException', flow2Error.resolve); webdriver.promise.rejected(error1); + defaultFlow.once('uncaughtException', flow1Error.resolve); defaultFlow.execute(function() { webdriver.promise.rejected(error2); }); }); - flowTester.run(); - assertEquals(error2, flowTester.getFailure(defaultFlow)); - assertEquals(error1, flowTester.getFailure(newFlow)); + return goog.Promise.all([flow1Error.promise, flow2Error.promise]); } function testCanSynchronizeFlowsByReturningPromiseFromOneToAnother() { - var defaultFlow = webdriver.promise.controlFlow(); - schedulePush('a', 'a'); - webdriver.promise.controlFlow().timeout(250); - schedulePush('b', 'b'); - - webdriver.promise.createFlow(function() { + 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'); - scheduleAction('', function() { - return defaultFlow.execute(function() { - assertFlowIs(defaultFlow); - return schedulePush('e', 'e'); + 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'); }); - }); - schedulePush('d', 'd'); - }); - - runAndExpectSuccess(); - webdriver.test.testutil.assertMessages('a', 'c', 'b', 'e', 'd'); } function testFramesWaitToCompleteForPendingRejections() { - webdriver.promise.controlFlow().execute(function() { - webdriver.promise.rejected(STUB_ERROR); - }); + return new goog.Promise(function(fulfill, reject) { + + webdriver.promise.controlFlow().execute(function() { + webdriver.promise.rejected(new StubError); + }).then(fulfill, reject); - runAndExpectFailure(assertIsStubError); + }). + then(goog.partial(fail, 'expected to fail'), assertIsStubError). + then(waitForIdle); } function testSynchronizeErrorsPropagateToOuterFlow() { - var defaultFlow = webdriver.promise.controlFlow(); + var outerFlow = new webdriver.promise.ControlFlow; + var innerFlow = new webdriver.promise.ControlFlow; - var newFlow; - webdriver.promise.createFlow(function(flow) { - newFlow = flow; - return defaultFlow.execute(function() { - webdriver.promise.rejected(STUB_ERROR); - }); - }); + 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'); - flowTester.run(); - assertIsStubError(flowTester.getFailure(defaultFlow)); - flowTester.verifySuccess(newFlow); // Error was transferred to new flow. + return goog.Promise.all([ + waitForIdle(innerFlow), + waitForAbort(outerFlow).then(assertIsStubError) + ]); } function testFailsIfErrbackThrows() { webdriver.promise.rejected('').then(null, throwStubError); - runAndExpectFailure(assertIsStubError); + return waitForAbort().then(assertIsStubError); } function testFailsIfCallbackReturnsRejectedPromise() { webdriver.promise.fulfilled().then(function() { - return webdriver.promise.rejected(STUB_ERROR); + return webdriver.promise.rejected(new StubError); }); - runAndExpectFailure(assertIsStubError); + return waitForAbort().then(assertIsStubError); } function testAbortsFrameIfTaskFails() { webdriver.promise.fulfilled().then(function() { webdriver.promise.controlFlow().execute(throwStubError); }); - runAndExpectFailure(assertIsStubError); + return waitForAbort().then(assertIsStubError); } function testAbortsFramePromisedChainedFromTaskIsNotHandled() { @@ -2111,7 +1805,7 @@ function testAbortsFramePromisedChainedFromTaskIsNotHandled() { webdriver.promise.controlFlow().execute(goog.nullFunction). then(throwStubError); }); - runAndExpectFailure(assertIsStubError); + return waitForAbort().then(assertIsStubError); } function testTrapsChainedUnhandledRejectionsWithinAFrame() { @@ -2121,8 +1815,7 @@ function testTrapsChainedUnhandledRejectionsWithinAFrame() { then(throwStubError); }).then(pair.callback, pair.errback); - runAndExpectSuccess(); - pair.assertErrback(); + return waitForIdle().then(pair.assertErrback); } @@ -2133,21 +1826,25 @@ function testCancelsRemainingTasksIfFrameThrowsDuringScheduling() { flow.execute(function() { task1 = flow.execute(goog.nullFunction); task2 = flow.execute(goog.nullFunction); - throw STUB_ERROR; + throw new StubError; }).then(pair.callback, pair.errback); - runAndExpectSuccess(); - pair.assertErrback(); - - assertFalse(task1.isPending()); - pair = callbackPair(); - task1.then(pair.callback, pair.errback); - pair.assertErrback(); - - assertFalse(task2.isPending()); - pair = callbackPair(); - task2.then(pair.callback, pair.errback); - pair.assertErrback(); + 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() { @@ -2159,122 +1856,102 @@ function testCancelsRemainingTasksInFrameIfATaskFails() { task = flow.execute(goog.nullFunction); }).then(pair.callback, pair.errback); - runAndExpectSuccess(); - pair.assertErrback(); - - assertFalse(task.isPending()); - pair = callbackPair(); - task.then(pair.callback, pair.errback); - pair.assertErrback(); -} - -function testAnnotatesRejectedPromiseErrorsWithFlowState() { - var error = Error('original message'); - var originalStack = webdriver.stacktrace.format(error).stack; - - var pair = callbackPair(null, function(e) { - assertEquals(error, e); - assertEquals('original message', e.message); - assertTrue( - 'Expected to start with: ' + originalStack, - goog.string.startsWith(e.stack, originalStack)); - - var parts = e.stack.split('\n==== async task ====\n'); - assertEquals(2, parts.length); - assertEquals(originalStack, parts[0]); - }); - - webdriver.promise.createFlow(function(flow) { - var d = webdriver.promise.defer(); - d.reject(error); - d.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(); }); - - runAndExpectSuccess(); - pair.assertErrback(); } -function testAnnotatesChainedErrors() { +function testDoesNotModifyRejectionErrorIfPromiseNotInsideAFlow() { var error = Error('original message'); - var originalStack = webdriver.stacktrace.format(error).stack; + var originalStack = error.stack; + var originalStr = error.toString(); var pair = callbackPair(null, function(e) { assertEquals(error, e); assertEquals('original message', e.message); - assertTrue( - 'Expected to start with: ' + originalStack, - goog.string.startsWith(e.stack, originalStack)); - - var parts = e.stack.split('\n==== async task ====\n'); - assertEquals(2, parts.length); - assertEquals(originalStack, parts[0]); - }); - - webdriver.promise.createFlow(function(flow) { - var rejected = webdriver.promise.rejected(error); - webdriver.promise.fulfilled(rejected). - then(pair.callback, pair.errback); + assertEquals(originalStack, e.stack); + assertEquals(originalStr, e.toString()); }); - runAndExpectSuccess(); - pair.assertErrback(); + webdriver.promise.rejected(error).then(pair.callback, pair.errback); + return waitForIdle().then(pair.assertErrback); } -function testAnnotatesRejectedPromiseErrorsWithFlowState_taskErrorBubblesUp() { - var error = Error('original message'); - var originalStack = webdriver.stacktrace.format(error).stack; - var pair = callbackPair(null, function(e) { - assertEquals(error, e); - assertEquals('original message', e.message); - assertTrue( - 'Expected to start with: ' + originalStack, - goog.string.startsWith(e.stack, originalStack)); - var parts = e.stack.split('\n==== async task ====\n'); - assertEquals(3, parts.length); - assertEquals(originalStack, parts[0]); +/** 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', 'b.1', 'c.1', 'd.1', 'a.2', 'b.2', 'c.2', 'd.2'], + messages); }); - - webdriver.promise.createFlow(function(flow) { - flow.execute(function() { throw error; }); - }).then(pair.callback, pair.errback); - - runAndExpectSuccess(); - pair.assertErrback(); } -function testDoesNotAnnotatedRejectedPromisesIfGivenNonErrorValue() { - var error = {}; - var pair = callbackPair(null, function(e) { - assertEquals(error, e); - for (var val in error) { - fail('Did not expect error to be modified'); - } +/** 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(text) { + messages.push(step + '.2'); + }); + }); + }) }); - - webdriver.promise.createFlow(function(flow) { - var d = webdriver.promise.defer(); - d.reject(error); - d.then(pair.callback, pair.errback); + return waitForIdle().then(function() { + assertArrayEquals( + ['a.1', 'b.1', 'c.1', 'd.1', 'a.2', 'b.2', 'c.2', 'd.2'], + messages); }); - - runAndExpectSuccess(); - 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()); +/** 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', 'b.1', 'c.1', 'd.1', 'a.2', 'b.2', 'c.2', 'd.2'], + messages); }); - - webdriver.promise.rejected(error).then(pair.callback, pair.errback); - pair.assertErrback(); } diff --git a/lib/webdriver/test/promise_generator_test.js b/lib/webdriver/test/promise_generator_test.js index fcece45..b98ccd5 100644 --- a/lib/webdriver/test/promise_generator_test.js +++ b/lib/webdriver/test/promise_generator_test.js @@ -1,30 +1,27 @@ -// 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. goog.provide('webdriver.test.promise.generator.test'); goog.setTestOnly('webdriver.test.promise.generator.test'); -goog.require('goog.testing.AsyncTestCase'); goog.require('goog.testing.jsunit'); goog.require('webdriver.promise'); -var test = goog.testing.AsyncTestCase.createAndInstall( - 'promise_generator_test'); - - function testRequiresInputsToBeGeneratorFunctions() { var thrown = assertThrows(function() { webdriver.promise.consume(function() {}); @@ -35,15 +32,13 @@ function testRequiresInputsToBeGeneratorFunctions() { function testBasicGenerator() { var values = []; - test.waitForAsync(); - webdriver.promise.consume(function* () { + return webdriver.promise.consume(function* () { var i = 0; while (i < 4) { i = yield i + 1; values.push(i); } }).then(function() { - test.continueTesting(); assertArrayEquals([1, 2, 3, 4], values); }); } @@ -51,7 +46,7 @@ function testBasicGenerator() { function testPromiseYieldingGenerator() { var values = []; - webdriver.promise.consume(function* () { + return webdriver.promise.consume(function* () { var i = 0; while (i < 4) { // Test that things are actually async here. @@ -64,21 +59,16 @@ function testPromiseYieldingGenerator() { }); } }).then(function() { - test.continueTesting(); assertArrayEquals([0, 0, 2, 1, 4, 2, 6, 3], values); }); - test.waitForAsync(); } function testAssignmentsToYieldedPromisesGetFulfilledValue() { - test.waitForAsync(); - webdriver.promise.consume(function* () { + return webdriver.promise.consume(function* () { var p = webdriver.promise.fulfilled(2); var x = yield p; assertEquals(2, x); - }).then(function() { - test.continueTesting(); }); } @@ -93,37 +83,31 @@ function testCanCancelPromiseGenerator() { }); } }); - setTimeout(function() { - test.waitForAsync('cancelled; verifying it took'); + return webdriver.promise.delayed(75).then(function() { p.cancel(); - p.thenCatch(function() { - setTimeout(function() { - assertArrayEquals([0], values); - test.continueTesting(); - }, 300); + return p.thenCatch(function() { + return webdriver.promise.delayed(300); }); - }, 75); - test.waitForAsync(); + }).then(function() { + assertArrayEquals([0], values); + }); } function testFinalReturnValueIsUsedAsFulfillmentValue() { - test.waitForAsync(); - webdriver.promise.consume(function* () { + return webdriver.promise.consume(function* () { yield 1; yield 2; return 3; }).then(function(value) { assertEquals(3, value); - test.continueTesting(); }); } function testRejectionsAreThrownWithinGenerator() { - test.waitForAsync(); var values = []; - webdriver.promise.consume(function* () { + return webdriver.promise.consume(function* () { values.push('a'); var e = Error('stub error'); try { @@ -136,66 +120,55 @@ function testRejectionsAreThrownWithinGenerator() { values.push('d'); }).then(function() { assertArrayEquals(['a', 'c', 'd'], values); - test.continueTesting(); }); } function testUnhandledRejectionsAbortGenerator() { - test.waitForAsync(); - var values = []; var e = Error('stub error'); - webdriver.promise.consume(function* () { + return webdriver.promise.consume(function* () { values.push(1); yield webdriver.promise.rejected(e); values.push(2); }).thenCatch(function() { assertArrayEquals([1], values); - test.continueTesting(); }); } function testYieldsWaitForPromises() { - test.waitForAsync(); - var values = []; var d = webdriver.promise.defer(); - webdriver.promise.consume(function* () { - values.push(1); - values.push((yield d.promise), 3); - }).then(function() { - assertArrayEquals([1, 2, 3], values); - test.continueTesting(); - }); 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() { - test.waitForAsync(); - webdriver.promise.consume(function* () { + return webdriver.promise.consume(function* () { return this.name; }, {name: 'Bob'}).then(function(value) { assertEquals('Bob', value); - test.continueTesting(); }); } function testCanSpecifyGeneratorArgs() { - test.waitForAsync(); - webdriver.promise.consume(function* (a, b) { + return webdriver.promise.consume(function* (a, b) { assertEquals('red', a); assertEquals('apples', b); - }, null, 'red', 'apples').then(function() { - test.continueTesting(); - }); + }, null, 'red', 'apples'); } @@ -205,30 +178,30 @@ function testExecuteGeneratorInAFlow() { webdriver.promise.defer() ]; var values = []; - 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); - test.continueTesting(); - }); - test.waitForAsync(); 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(); - flow.execute(function* () { + return flow.execute(function* () { var x = yield flow.execute(function() { return webdriver.promise.delayed(10).then(function() { return 1; @@ -242,15 +215,13 @@ function testNestedGeneratorsInAFlow() { return x + y; }).then(function(value) { assertEquals(3, value); - test.continueTesting(); }); - test.waitForAsync(); } function testFlowWaitOnGenerator() { var values = []; - webdriver.promise.controlFlow().wait(function* () { + return webdriver.promise.controlFlow().wait(function* () { yield values.push(1); values.push(yield webdriver.promise.delayed(10).then(function() { return 2; @@ -259,15 +230,13 @@ function testFlowWaitOnGenerator() { return values.length === 6; }, 250).then(function() { assertArrayEquals([1, 2, 3, 1, 2, 3], values); - test.continueTesting(); }); - test.waitForAsync(); } function testFlowWaitingOnGeneratorTimesOut() { var values = []; - webdriver.promise.controlFlow().wait(function* () { + return webdriver.promise.controlFlow().wait(function* () { var i = 0; while (i < 3) { yield webdriver.promise.delayed(100).then(function() { @@ -277,8 +246,6 @@ function testFlowWaitingOnGeneratorTimesOut() { }, 75).thenCatch(function() { assertArrayEquals('Should complete one loop of wait condition', [0, 1, 2], values); - test.continueTesting(); }); - test.waitForAsync(); } diff --git a/lib/webdriver/test/promise_test.js b/lib/webdriver/test/promise_test.js index 1703d39..a7e2f1f 100644 --- a/lib/webdriver/test/promise_test.js +++ b/lib/webdriver/test/promise_test.js @@ -1,22 +1,28 @@ -// Copyright 2014 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.require('goog.testing.MockClock'); goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); goog.require('webdriver.promise'); -goog.require('webdriver.promise.Deferred'); +goog.require('webdriver.stacktrace'); goog.require('webdriver.test.testutil'); + // Aliases for readability. var assertIsPromise = webdriver.test.testutil.assertIsPromise, assertNotPromise = webdriver.test.testutil.assertNotPromise, @@ -24,36 +30,48 @@ var assertIsPromise = webdriver.test.testutil.assertIsPromise, callbackPair = webdriver.test.testutil.callbackPair, assertIsStubError = webdriver.test.testutil.assertIsStubError, throwStubError = webdriver.test.testutil.throwStubError, - STUB_ERROR = webdriver.test.testutil.STUB_ERROR; + StubError = webdriver.test.testutil.StubError; var app, clock, uncaughtExceptions; +function shouldRunTests() { + return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); +} + + function setUp() { - clock = webdriver.test.testutil.createMockClock(); + 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)); - - // The application timer defaults to a protected copies of the native - // timing functions. Reset it to use |window|, which has been modified - // by the MockClock above. - app.timer = window; } function tearDown() { - webdriver.test.testutil.consumeTimeouts(); + 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()); + assertIsPromise(new webdriver.promise.Promise(function(fulfill) { + fulfill(); + })); assertIsPromise(new webdriver.promise.Deferred()); assertIsPromise(new webdriver.promise.Deferred().promise); assertIsPromise({then:function() {}}); @@ -68,7 +86,6 @@ function testCanDetectPromiseLikeObjects() { assertNotPromise({then:1}); assertNotPromise({then:true}); assertNotPromise({then:''}); - } @@ -77,11 +94,12 @@ function testSimpleResolveScenario() { assertEquals(123, value); }); - var deferred = new webdriver.promise.Deferred(); - deferred.then(callback); + var deferred = webdriver.promise.defer(); + deferred.promise.then(callback); callback.assertNotCalled(); deferred.fulfill(123); + clock.tick(); callback.assertCalled(); } @@ -93,11 +111,14 @@ function testRegisteringACallbackPostResolution() { 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(); } @@ -109,11 +130,13 @@ function testRegisterACallbackViaDeferredPromise() { assertEquals(123, value); }))); deferred.fulfill(123); + clock.tick(); callback.assertCalled(); deferred.promise.then((callback = callbackHelper(function(value) { assertEquals(123, value); }))); + clock.tick(); callback.assertCalled(); } @@ -130,11 +153,13 @@ function testTwoStepResolvedChain() { callback.assertNotCalled(); start.fulfill(123); + clock.tick(); callback.assertCalled(); next.then((callback = callbackHelper(function(value) { assertEquals(124, value); }))); + clock.tick(); callback.assertCalled(); } @@ -149,18 +174,20 @@ function testCanResolveOnlyOnce_resolved() { deferred.then(callback = callbackHelper(function(value) { assertEquals(1, value); })); + clock.tick(); callback.assertCalled(); } function testCanResolveOnlyOnce_rejected() { var deferred = new webdriver.promise.Deferred(); - deferred.reject(STUB_ERROR); + deferred.reject(new StubError); deferred.fulfill(1); deferred.reject(2); var callback; deferred.then(null, callback = callbackHelper(assertIsStubError)); + clock.tick(); callback.assertCalled(); } @@ -169,7 +196,6 @@ function testIfFulfilledWithOtherPromiseCannotChangeValueWhileWaiting() { var deferred = webdriver.promise.defer(); var other = webdriver.promise.defer(); - debugger; deferred.fulfill(other.promise); deferred.fulfill('different value'); @@ -181,6 +207,7 @@ function testIfFulfilledWithOtherPromiseCannotChangeValueWhileWaiting() { callback.assertNotCalled(); other.fulfill(123); + clock.tick(); callback.assertCalled(); } @@ -190,6 +217,7 @@ function testOnlyGoesDownListenerPath_resolved() { var errback = callbackHelper(); webdriver.promise.fulfilled().then(callback, errback); + clock.tick(); callback.assertCalled(); errback.assertNotCalled(); } @@ -200,6 +228,7 @@ function testOnlyGoesDownListenerPath_rejected() { var errback = callbackHelper(); webdriver.promise.rejected().then(callback, errback); + clock.tick(); callback.assertNotCalled(); errback.assertCalled(); } @@ -211,9 +240,10 @@ function testCatchingAndSuppressingRejectionErrors() { assertUndefined(arguments[0]); }); - webdriver.promise.rejected(STUB_ERROR). + webdriver.promise.rejected(new StubError). thenCatch(errback). then(callback); + clock.tick(); errback.assertCalled(); callback.assertCalled(); } @@ -226,12 +256,13 @@ function testThrowingNewRejectionErrors() { assertEquals(error2, error); }); - webdriver.promise.rejected(STUB_ERROR). + webdriver.promise.rejected(new StubError). thenCatch(function(error) { errback1(error); throw error2; }). thenCatch(errback2); + clock.tick(); errback1.assertCalled(); errback2.assertCalled(); } @@ -239,9 +270,10 @@ function testThrowingNewRejectionErrors() { function testThenFinally_nonFailingCallbackDoesNotSuppressOriginalError() { var done = callbackHelper(assertIsStubError); - webdriver.promise.rejected(STUB_ERROR). + webdriver.promise.rejected(new StubError). thenFinally(goog.nullFunction). thenCatch(done); + clock.tick(); done.assertCalled(); } @@ -251,6 +283,7 @@ function testThenFinally_failingCallbackSuppressesOriginalError() { webdriver.promise.rejected(new Error('original')). thenFinally(throwStubError). thenCatch(done); + clock.tick(); done.assertCalled(); } @@ -260,6 +293,7 @@ function testThenFinally_callbackThrowsAfterFulfilledPromise() { webdriver.promise.fulfilled(). thenFinally(throwStubError). thenCatch(done); + clock.tick(); done.assertCalled(); } @@ -268,9 +302,10 @@ function testThenFinally_callbackReturnsRejectedPromise() { var done = callbackHelper(assertIsStubError); webdriver.promise.fulfilled(). thenFinally(function() { - return webdriver.promise.rejected(STUB_ERROR); + return webdriver.promise.rejected(new StubError); }). thenCatch(done); + clock.tick(); done.assertCalled(); } @@ -302,6 +337,7 @@ function testChainingThen_AllResolved() { deferred.fulfill(128); + clock.tick(); callbacks[0].assertCalled(); callbacks[1].assertCalled(); callbacks[2].assertCalled(); @@ -316,6 +352,7 @@ function testWhen_ReturnsAResolvedPromiseIfGivenANonPromiseValue() { ret.then(callback = callbackHelper(function (value) { assertEquals('abc', value); })); + clock.tick(); callback.assertCalled(); } @@ -325,6 +362,7 @@ function testWhen_PassesRawErrorsToCallbacks() { webdriver.promise.when(error, callback = callbackHelper(function(value) { assertEquals(error, value); })); + clock.tick(); callback.assertCalled(); } @@ -336,12 +374,16 @@ function testWhen_WaitsForValueToBeResolvedBeforeInvokingCallback() { })); callback.assertNotCalled(); d.fulfill('hi'); + clock.tick(); callback.assertCalled(); } function testWhen_canCancelReturnedPromise() { - var callbacks = callbackPair(null, assertIsStubError); + var callbacks = callbackPair(null, function(e) { + assertTrue(e instanceof webdriver.promise.CancellationError); + assertEquals('just because', e.message); + }); var promiseLike = { then: function(cb, eb) { @@ -354,7 +396,8 @@ function testWhen_canCancelReturnedPromise() { callbacks.callback, callbacks.errback); assertTrue(promise.isPending()); - promise.cancel(STUB_ERROR); + promise.cancel('just because'); + clock.tick(); callbacks.assertErrback(); // The following should have no effect. @@ -364,7 +407,7 @@ function testWhen_canCancelReturnedPromise() { function testFiresUncaughtExceptionEventIfRejectionNeverHandled() { - webdriver.promise.rejected(STUB_ERROR); + webdriver.promise.rejected(new StubError); var handler = callbackHelper(assertIsStubError); // so tearDown() doesn't throw @@ -388,9 +431,11 @@ function testWaitsIfCallbackReturnsAPromiseObject() { assertEquals('bye', value); })); + clock.tick(); callback1.assertCalled(); callback2.assertNotCalled(); callback1Return.fulfill('bye'); + clock.tick(); callback2.assertCalled(); } @@ -415,9 +460,11 @@ function testWaitsIfCallbackReturnsAPromiseLikeObject() { assertEquals('bye', value); })); + clock.tick(); callback1.assertCalled(); callback2.assertNotCalled(); callback1Return.fulfill('bye'); + clock.tick(); callback2.assertCalled(); } @@ -440,10 +487,12 @@ function testResolvingAPromiseWithAnotherPromiseCreatesAChain_ourPromise() { callback2.assertNotCalled(); d2.fulfill(2); + clock.tick(); callback1.assertNotCalled(); callback2.assertCalled(); d1.fulfill(d2promise); + clock.tick(); callback1.assertCalled(); callback2.assertCalled(); } @@ -467,6 +516,60 @@ function testResolvingAPromiseWithAnotherPromiseCreatesAChain_otherPromise() { 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(); } @@ -474,36 +577,33 @@ function testResolvingAPromiseWithAnotherPromiseCreatesAChain_otherPromise() { function testRejectingAPromiseWithAnotherPromiseCreatesAChain_ourPromise() { var d1 = new webdriver.promise.Deferred(); var d2 = new webdriver.promise.Deferred(); - var callback1, errback1, callback2; - - d1.then(callback1 = callbackHelper(), - errback1 = callbackHelper(assertIsStubError)); - - var d2promise = d2.then(callback2 = callbackHelper(function(value) { + var pair1 = callbackPair(assertIsStubError, null); + var pair2 = callbackPair(function(value) { assertEquals(2, value); - return STUB_ERROR; - })); + return new StubError; + }); - callback1.assertNotCalled(); - errback1.assertNotCalled(); - callback2.assertNotCalled(); + d1.then(pair1.callback, pair1.errback); + var d2promise = d2.then(pair2.callback, pair2.errback); + + pair1.assertNeither(); + pair2.assertNeither(); d2.fulfill(2); - callback1.assertNotCalled(); - errback1.assertNotCalled(); - callback2.assertCalled(); + clock.tick(); + pair1.assertNeither(); + pair2.assertCallback(); d1.reject(d2promise); - callback1.assertNotCalled(); - errback1.assertCalled(); - callback2.assertCalled(); + clock.tick(); + pair1.assertCallback(); } function testRejectingAPromiseWithAnotherPromiseCreatesAChain_otherPromise() { var d = new webdriver.promise.Deferred(), callback, errback; - d.then(callback = callbackHelper(), - errback = callbackHelper(assertIsStubError)); + d.then(callback = callbackHelper(assertIsStubError), + errback = callbackHelper()); var otherPromise = { then: function(callback) { @@ -515,12 +615,14 @@ function testRejectingAPromiseWithAnotherPromiseCreatesAChain_otherPromise() { }; d.reject(otherPromise); + clock.tick(); callback.assertNotCalled(); errback.assertNotCalled(); - otherPromise.fulfill(STUB_ERROR); - callback.assertNotCalled(); - errback.assertCalled(); + otherPromise.fulfill(new StubError); + clock.tick(); + callback.assertCalled(); + errback.assertNotCalled(); } @@ -539,18 +641,38 @@ function testResolvingADeferredWithAnotherCopiesTheResolvedValue() { })); d1.fulfill(d2); + clock.tick(); callback1.assertNotCalled(); callback2.assertNotCalled(); d2.fulfill(2); + clock.tick(); callback1.assertCalled(); callback2.assertCalled(); } +function testCannotResolveAPromiseWithItself() { + assertThrows(function() { + var f, p = new webdriver.promise.Promise(function(fulfill) { + f = fulfill; + }); + f(p); + }); + + assertThrows(function() { + var r, p = new webdriver.promise.Promise(function(_, reject) { + r = reject; + }); + r(p); + }); +} + + function testCannotResolveADeferredWithItself() { var deferred = new webdriver.promise.Deferred(); assertThrows(goog.bind(deferred.fulfill, deferred, deferred)); + assertThrows(goog.bind(deferred.reject, deferred, deferred)); } @@ -563,6 +685,7 @@ function testSkipsNullPointsInPromiseChain_callbacks() { assertEquals('hi', value); })); + clock.tick(); errback1.assertNotCalled(); errback2.assertNotCalled(); callback.assertCalled(); @@ -578,6 +701,7 @@ function testSkipsNullPointsInPromiseChain_errbacks() { assertEquals('hi', value); })); + clock.tick(); errback1.assertNotCalled(); errback2.assertNotCalled(); callback.assertCalled(); @@ -593,6 +717,7 @@ function testFullyResolved_primitives() { }), errback = callbackHelper()); + clock.tick(); errback.assertNotCalled( 'Did not expect errback to be called for: ' + value); callback.assertCalled('Expected callback to be called for: ' + value); @@ -618,6 +743,7 @@ function testFullyResolved_arrayOfPrimitives() { webdriver.promise.fullyResolved(array).then( callbacks.callback, callbacks.errback); + clock.tick(); callbacks.assertCallback(); } @@ -633,6 +759,7 @@ function testFullyResolved_nestedArrayOfPrimitives() { }), errback = callbackHelper()); + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } @@ -645,6 +772,8 @@ function testFullyResolved_arrayWithPromisedPrimitive() { assertArrayEquals([123], resolved); }), errback = callbackHelper()); + + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } @@ -659,6 +788,7 @@ function testFullyResolved_promiseResolvesToPrimitive() { }), errback = callbackHelper()); + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } @@ -668,7 +798,9 @@ function testFullyResolved_promiseResolvesToArray() { var array = [true, [goog.nullFunction, null, 123], '', undefined]; var promise = webdriver.promise.fulfilled(array); var callback, errback; - webdriver.promise.fullyResolved(promise).then( + + var result = webdriver.promise.fullyResolved(promise); + result.then( callback = callbackHelper(function(resolved) { assertEquals(array, resolved); assertArrayEquals([true, [goog.nullFunction, null, 123], '', undefined], @@ -677,6 +809,7 @@ function testFullyResolved_promiseResolvesToArray() { }), errback = callbackHelper()); + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } @@ -693,19 +826,17 @@ function testFullyResolved_promiseResolvesToArrayWithPromises() { }), errback = callbackHelper()); + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } function testFullyResolved_rejectsIfArrayPromiseRejects() { - var e = new Error('foo'); - var nestedPromise = webdriver.promise.rejected(e); + var nestedPromise = createRejectedPromise(new StubError); var promise = webdriver.promise.fulfilled([true, nestedPromise]); - var pair = callbackPair(null, function(error) { - assertEquals(e, error); - }); + var pair = callbackPair(null, assertIsStubError); webdriver.promise.fullyResolved(promise).then(pair.callback, pair.errback); clock.tick(); @@ -717,8 +848,8 @@ function testFullyResolved_rejectsOnFirstArrayRejection() { var e1 = new Error('foo'); var e2 = new Error('bar'); var promise = webdriver.promise.fulfilled([ - webdriver.promise.rejected(e1), - webdriver.promise.rejected(e2) + createRejectedPromise(e1), + createRejectedPromise(e2) ]); var pair = callbackPair(null, function(error) { @@ -732,20 +863,15 @@ function testFullyResolved_rejectsOnFirstArrayRejection() { function testFullyResolved_rejectsIfNestedArrayPromiseRejects() { - var e = new Error('foo'); var promise = webdriver.promise.fulfilled([ webdriver.promise.fulfilled([ - webdriver.promise.rejected(e) + createRejectedPromise(new StubError) ]) ]); - var pair = callbackPair(null, function(error) { - assertEquals(e, error); - }); + var pair = callbackPair(null, assertIsStubError); webdriver.promise.fullyResolved(promise).then(pair.callback, pair.errback); - clock.tick(); - clock.tick(); clock.tick(); pair.assertErrback(); } @@ -762,6 +888,7 @@ function testFullyResolved_simpleHash() { }), errback = callbackHelper()); + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } @@ -781,6 +908,7 @@ function testFullyResolved_nestedHash() { }), errback = callbackHelper()); + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } @@ -797,6 +925,7 @@ function testFullyResolved_promiseResolvesToSimpleHash() { }), errback = callbackHelper()); + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } @@ -815,6 +944,7 @@ function testFullyResolved_promiseResolvesToNestedHash() { }), errback = callbackHelper()); + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } @@ -832,6 +962,7 @@ function testFullyResolved_promiseResolvesToHashWithPromises() { }), errback = callbackHelper()); + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } @@ -840,13 +971,12 @@ function testFullyResolved_promiseResolvesToHashWithPromises() { function testFullyResolved_rejectsIfHashPromiseRejects() { var e = new Error('foo'); var promise = webdriver.promise.fulfilled({ - 'a': webdriver.promise.rejected(e) + 'a': createRejectedPromise(new StubError) }); - var pair = callbackPair(null, function(error) { - assertEquals(e, error); - }); - webdriver.promise.fullyResolved(promise).then(pair.callback, pair.errback); + var pair = callbackPair(null, assertIsStubError); + webdriver.promise.fullyResolved(promise).then( + pair.callback, pair.errback); clock.tick(); pair.assertErrback(); @@ -855,12 +985,10 @@ function testFullyResolved_rejectsIfHashPromiseRejects() { function testFullyResolved_rejectsIfNestedHashPromiseRejects() { var e = new Error('foo'); var promise = webdriver.promise.fulfilled({ - 'a': {'b': webdriver.promise.rejected(e)} + 'a': {'b': createRejectedPromise(new StubError)} }); - var pair = callbackPair(null, function(error) { - assertEquals(e, error); - }); + var pair = callbackPair(null, assertIsStubError); webdriver.promise.fullyResolved(promise).then(pair.callback, pair.errback); clock.tick(); @@ -882,6 +1010,8 @@ function testFullyResolved_instantiatedObject() { webdriver.test.testutil.assertObjectEquals(new Foo, resolvedFoo); }), errback = callbackHelper()); + + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } @@ -894,6 +1024,8 @@ function testFullyResolved_withEmptyArray() { assertArrayEquals([], resolved); }), errback = callbackHelper()); + + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } @@ -906,6 +1038,8 @@ function testFullyResolved_withEmptyHash() { webdriver.test.testutil.assertObjectEquals({}, resolved); }), errback = callbackHelper()); + + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } @@ -923,6 +1057,8 @@ function testFullyResolved_arrayWithPromisedHash() { }), errback = callbackHelper()); + + clock.tick(); errback.assertNotCalled(); callback.assertCalled(); } @@ -936,6 +1072,8 @@ function testFullyResolved_aDomElement() { webdriver.promise.fullyResolved(e). then(callbacks.callback, callbacks.errback); + + clock.tick(); callbacks.assertCallback(); } @@ -950,6 +1088,7 @@ function testCallbackChain_nonSplit() { 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'); @@ -968,6 +1107,7 @@ function testCallbackChain_split() { 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'); @@ -984,6 +1124,7 @@ function testCheckedNodeCall_functionThrows() { throw error; }).then(pair.callback, pair.errback); + clock.tick(); pair.assertErrback(); } @@ -996,6 +1137,7 @@ function testCheckedNodeCall_functionReturnsAnError() { webdriver.promise.checkedNodeCall(function(callback) { callback(error); }).then(pair.callback, pair.errback); + clock.tick(); pair.assertErrback(); } @@ -1008,6 +1150,7 @@ function testCheckedNodeCall_functionReturnsSuccess() { webdriver.promise.checkedNodeCall(function(callback) { callback(null, success); }).then(pair.callback, pair.errback); + clock.tick(); pair.assertCallback(); } @@ -1022,6 +1165,7 @@ function testCheckedNodeCall_functionReturnsAndThrows() { callback(error); throw error2; }).then(pair.callback, pair.errback); + clock.tick(); pair.assertErrback(); } @@ -1033,111 +1177,80 @@ function testCheckedNodeCall_functionThrowsAndReturns() { assertEquals(error2, e); }); webdriver.promise.checkedNodeCall(function(callback) { - setTimeout(goog.partial(callback, error), 0); + setTimeout(goog.partial(callback, error), 10); throw error2; }).then(pair.callback, pair.errback); + clock.tick(); pair.assertErrback(); pair.reset(); - webdriver.test.testutil.consumeTimeouts(); + clock.tick(Infinity); pair.assertNeither(); } function testCancel_passesTheCancellationReasonToReject() { - var pair = callbackPair(null, assertIsStubError); - var d = new webdriver.promise.Deferred(); - d.then(pair.callback, pair.errback); - d.cancel(STUB_ERROR); - pair.assertErrback(); -} - - -function testCancel_invokesTheCancellerFunctionIfOneWasProvided() { - var pair = callbackPair(null, assertIsStubError); - var callback = callbackHelper(); - var d = new webdriver.promise.Deferred(callback); - d.then(pair.callback, pair.errback); - d.cancel(STUB_ERROR); - pair.assertErrback(); - callback.assertCalled(); -} - - -function testCancel_canChangeRejectionReasonWithTruthyCancellerReturnValue() { - var pair = callbackPair(null, assertIsStubError); - var callback = callbackHelper(function() { - return STUB_ERROR; + 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(callback); + var d = new webdriver.promise.Deferred(); d.then(pair.callback, pair.errback); - d.cancel(); + d.cancel('because i said so'); + clock.tick(); pair.assertErrback(); - callback.assertCalled(); } function testCancel_canCancelADeferredFromAChainedPromise() { - var pair1 = callbackPair(null, assertIsStubError), - pair2 = callbackPair(); + 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(STUB_ERROR); + 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, assertIsStubError); + 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(STUB_ERROR); + p.cancel(); + clock.tick(); pair.assertErrback(); clock.tick(250); // Just to make sure nothing happens. pair.assertErrback(); } -function testCancel_cannotCancelACheckedNodeCall() { - var d = webdriver.promise.checkedNodeCall(goog.nullFunction); - assertThrows(d.cancel); -} - - -function testCancel_cancellerFunctionExplicityResolvesPromise() { - var pair = callbackPair(goog.partial(assertEquals, 123)); - var canceller; - var d = new webdriver.promise.Deferred( - canceller = callbackHelper(function(e) { - assertIsStubError(e); - d.fulfill(123); - })); - d.then(pair.callback, pair.errback); - d.cancel(STUB_ERROR); - canceller.assertCalled(); - pair.assertCallback(); -} - - 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(STUB_ERROR); + var p = webdriver.promise.rejected(new StubError); p.cancel(); var pair = callbackPair(null, assertIsStubError); p.then(pair.callback, pair.errback); + clock.tick(); pair.assertErrback(); } @@ -1151,46 +1264,63 @@ function testCancel_noopCancelTriggeredOnCallbackOfResolvedPromise() { } -function testCancel_cancellerCanForbidCancellation() { - var d = new webdriver.promise.Deferred(throwStubError); - assertThrows(d.cancel); -} - - function testCallbackRegistersAnotherListener_callbacksConfiguredPreResolve() { var messages = []; var d = new webdriver.promise.Deferred(); - d.then(function() { + d.promise.then(function() { messages.push('a'); - d.then(function() { + d.promise.then(function() { messages.push('c'); }); }); - d.then(function() { + d.promise.then(function() { messages.push('b'); }); d.fulfill(); - assertArrayEquals(['a', 'c', 'b'], messages); + clock.tick(); + assertArrayEquals(['a', 'b', 'c'], messages); } function testCallbackRegistersAnotherListener_callbacksConfiguredPostResolve() { var messages = []; - var d = webdriver.promise.fulfilled(); - d.then(function() { + var p = webdriver.promise.fulfilled(); + p.then(function() { messages.push('a'); - d.then(function() { + p.then(function() { messages.push('c'); }); }); - d.then(function() { + p.then(function() { messages.push('b'); }); - assertArrayEquals(['a', 'c', 'b'], messages); + clock.tick(); + assertArrayEquals(['a', 'b', 'c'], 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() { +function testCallbackRegistersAnotherListener_recursiveCallbacks_many() { var messages = []; var start = 97; // 'a' @@ -1206,21 +1336,20 @@ function testCallbackRegistersAnotherListener_recursiveCallbacks() { } } + clock.tick(); assertArrayEquals(['a', 'b', 'c', 'd', 'done'], messages); } function testThenReturnsOwnPromiseIfNoCallbacksWereGiven() { var deferred = new webdriver.promise.Deferred(); - assertEquals(deferred.promise, deferred.then()); assertEquals(deferred.promise, deferred.promise.then()); - assertEquals(deferred.promise, webdriver.promise.when(deferred)); assertEquals(deferred.promise, webdriver.promise.when(deferred.promise)); } function testIsStillConsideredUnHandledIfNoCallbacksWereGivenOnCallsToThen() { - webdriver.promise.rejected(STUB_ERROR).then(); + webdriver.promise.rejected(new StubError).then(); var handler = callbackHelper(assertIsStubError); // so tearDown() doesn't throw @@ -1238,27 +1367,30 @@ function testResolvedReturnsInputValueIfItIsAPromise() { function testADeferredsParentControlFlowIsActiveForCallbacks() { - var flow = new webdriver.promise.ControlFlow(); - var d = new webdriver.promise.Deferred(null, flow); + 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(null, flow2); + var d2 = new webdriver.promise.Deferred(flow2); d2.fulfill(); - assertIsFlow(webdriver.promise.defaultFlow_)(); + assertIsFlow(defaultFlow); var callbacks = callbackPair(); - d.then(assertIsFlow(flow)). - then(assertIsFlow(flow)). + d.promise.then(assertIsFlow(flow1)). + then(assertIsFlow(flow1)). then(function() { - return d2.then(assertIsFlow(flow2)); + return d2.promise.then(assertIsFlow(flow2)); }). - then(assertIsFlow(flow)). + then(assertIsFlow(flow1)). then(callbacks.callback, callbacks.errback); + clock.tick(); callbacks.assertCallback(); - assertIsFlow(webdriver.promise.defaultFlow_)(); + assertIsFlow(defaultFlow); function assertIsFlow(flow) { return function() { @@ -1274,6 +1406,7 @@ function testPromiseAll_emptyArray() { }); webdriver.promise.all([]).then(pair.callback, pair.errback); + clock.tick(); pair.assertCallback(); } @@ -1300,6 +1433,7 @@ function testPromiseAll() { pair.assertNeither(); a[3].fulfill(3); + clock.tick(); pair.assertCallback(); } @@ -1315,10 +1449,12 @@ function testPromiseAll_usesFirstRejection() { webdriver.promise.all(a).then(pair.callback, pair.errback); pair.assertNeither(); - a[1].reject(STUB_ERROR); + a[1].reject(new StubError); + clock.tick(); pair.assertErrback(); a[0].reject(Error('ignored')); + clock.tick(Infinity); } @@ -1335,6 +1471,7 @@ function testMappingAnArray() { }); result.then(pair.callback, pair.errback); + clock.tick(); pair.assertCallback(); } @@ -1361,6 +1498,7 @@ function testMappingAnArray_omitsDeleted() { }); result.then(pair.callback, pair.errback); + clock.tick(); pair.assertCallback(); } @@ -1375,6 +1513,7 @@ function testMappingAnArray_emptyArray() { }); result.then(pair.callback, pair.errback); + clock.tick(); pair.assertCallback(); } @@ -1392,6 +1531,7 @@ function testMappingAnArray_inputIsPromise() { result.then(pair.callback, pair.errback); pair.assertNeither(); input.fulfill([1, 2, 3]); + clock.tick(); pair.assertCallback(); } @@ -1414,9 +1554,11 @@ function testMappingAnArray_waitsForFunctionResultToResolve() { pair.assertNeither(); innerResults[0].fulfill('a'); + clock.tick(); pair.assertNeither(); innerResults[1].fulfill('b'); + clock.tick(); pair.assertCallback(); } @@ -1425,17 +1567,19 @@ 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(STUB_ERROR); + return webdriver.promise.rejected(new StubError); }); var pair = callbackPair(null, assertIsStubError); result.then(pair.callback, pair.errback); + clock.tick(); pair.assertErrback(); } @@ -1445,12 +1589,13 @@ function testMappingAnArray_stopsCallingFunctionIfPreviousIterationFailed() { var result = webdriver.promise.map([1, 2, 3, 4], function() { count++; if (count == 3) { - throw STUB_ERROR; + throw new StubError; } }); var pair = callbackPair(null, assertIsStubError); result.then(pair.callback, pair.errback); + clock.tick(); pair.assertErrback(); assertEquals(3, count); } @@ -1458,25 +1603,22 @@ function testMappingAnArray_stopsCallingFunctionIfPreviousIterationFailed() { function testMappingAnArray_rejectsWithFirstRejectedPromise() { var innerResult = [ - webdriver.promise.defer(), - webdriver.promise.defer(), - webdriver.promise.defer(), - webdriver.promise.defer() + 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) { - return innerResult[index].promise; + count += 1; + return innerResult[index]; }); var pair = callbackPair(null, assertIsStubError); result.then(pair.callback, pair.errback); - pair.assertNeither(); - - innerResult[2].reject(STUB_ERROR); - innerResult[1].reject(Error('other error')); // Should be ignored. + clock.tick(); pair.assertErrback(); - - innerResult[0].reject('should also be ignored'); + assertEquals(2, count); } @@ -1495,11 +1637,13 @@ function testMappingAnArray_preservesOrderWhenMapReturnsPromise() { 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(); } @@ -1516,6 +1660,7 @@ function testFilteringAnArray() { assertArrayEquals([2, 3], val); }); result.then(pair.callback, pair.errback); + clock.tick(); pair.assertCallback(); } @@ -1533,6 +1678,7 @@ function testFilteringAnArray_omitsDeleted() { assertArrayEquals([2, 5], val); }); result.then(pair.callback, pair.errback); + clock.tick(); pair.assertCallback(); } @@ -1552,6 +1698,7 @@ function testFilteringAnArray_preservesInputs() { assertArrayEquals([2, 3], val); }); result.then(pair.callback, pair.errback); + clock.tick(); pair.assertCallback(); } @@ -1569,6 +1716,7 @@ function testFilteringAnArray_inputIsPromise() { result.then(pair.callback, pair.errback); pair.assertNeither(); input.fulfill([1, 2, 3]); + clock.tick(); pair.assertCallback(); } @@ -1591,20 +1739,23 @@ function testFilteringAnArray_waitsForFunctionResultToResolve() { 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(STUB_ERROR); + return webdriver.promise.rejected(new StubError); }); var pair = callbackPair(null, assertIsStubError); result.then(pair.callback, pair.errback); + clock.tick(); pair.assertErrback(); } @@ -1614,12 +1765,13 @@ function testFilteringAnArray_stopsCallingFunctionIfPreviousIterationFailed() { var result = webdriver.promise.filter([1, 2, 3, 4], function() { count++; if (count == 3) { - throw STUB_ERROR; + throw new StubError; } }); var pair = callbackPair(null, assertIsStubError); result.then(pair.callback, pair.errback); + clock.tick(); pair.assertErrback(); assertEquals(3, count); } @@ -1627,25 +1779,21 @@ function testFilteringAnArray_stopsCallingFunctionIfPreviousIterationFailed() { function testFilteringAnArray_rejectsWithFirstRejectedPromise() { var innerResult = [ - webdriver.promise.defer(), - webdriver.promise.defer(), - webdriver.promise.defer(), - webdriver.promise.defer() + webdriver.promise.fulfilled(), + createRejectedPromise(new StubError), + createRejectedPromise(Error('should be ignored')) ]; var result = webdriver.promise.filter([1, 2, 3, 4], function(value, index) { - return innerResult[index].promise; + assertTrue(index < innerResult.length); + return innerResult[index]; }); var pair = callbackPair(null, assertIsStubError); result.then(pair.callback, pair.errback); pair.assertNeither(); - innerResult[2].reject(STUB_ERROR); - innerResult[1].reject(Error('other error')); // Should be ignored. - + clock.tick(); pair.assertErrback(); - - innerResult[0].reject('should also be ignored'); } @@ -1664,11 +1812,13 @@ function testFilteringAnArray_preservesOrderWhenFilterReturnsPromise() { 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(); } @@ -1679,3 +1829,153 @@ function testAddThenableImplementation() { 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 = 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() { + throw error; + }).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 = 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', + 'From: Promise: then', + 'From: Promise: thenCatch', + 'From: Promise: then', + 'From: Promise: thenCatch', + ], getStackMessages(e)); + }); + + new webdriver.promise.Promise(function() { + throw error; + }). + 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 = 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: then', + 'From: Promise: thenCatch', + ], getStackMessages(e)); + }); + + webdriver.promise.fulfilled(). + then(goog.nullFunction). + then(goog.nullFunction). + then(function() { + throw error; + }). + 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 index f1e794b..8dd4d92 100644 --- a/lib/webdriver/test/stacktrace_test.js +++ b/lib/webdriver/test/stacktrace_test.js @@ -1,16 +1,19 @@ -// Copyright 2014 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.require('bot.Error'); goog.require('bot.ErrorCode'); @@ -61,7 +64,9 @@ function assertStackFrame(message, frameOrFrameString, expectedFrame) { } function assertFrame(frame, file, line) { - assertContains('/' + file, frame.getUrl()); + // Normalize path for when run through Node.js on Windows. + var url = frame.getUrl().replace(/\\/g, '/'); + assertContains('/' + file, url); assertEquals(line, frame.getLine()); } @@ -71,8 +76,8 @@ function testGetStacktraceFromFile() { } var stacktrace = webdriver.test.testutil.getStackTrace(); - assertFrame(stacktrace[0], 'testutil.js', 36); - assertFrame(stacktrace[1], 'stacktrace_test.js', 73); + assertFrame(stacktrace[0], 'testutil.js', 50); + assertFrame(stacktrace[1], 'stacktrace_test.js', 78); } function testGetStacktraceWithUrlOnLine() { @@ -86,10 +91,10 @@ function testGetStacktraceWithUrlOnLine() { } var stacktrace = getStacktraceWithUrlArgument('http://www.google.com'); - assertFrame(stacktrace[0], 'stacktrace_test.js', 85); + assertFrame(stacktrace[0], 'stacktrace_test.js', 90); stacktrace = getStacktraceWithUrlArgument('http://www.google.com/search'); - assertFrame(stacktrace[0], 'stacktrace_test.js', 85); + assertFrame(stacktrace[0], 'stacktrace_test.js', 90); } function testParseStackFrameInV8() { @@ -165,53 +170,11 @@ function testParseStackFrameInV8() { ' 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')); -} - -function testParseStackFrameInOpera() { - assertStackFrame('empty frame', '@', webdriver.stacktrace.ANONYMOUS_FRAME_); - - assertStackFrame('javascript path only', - '@javascript:console.log(Error().stack):1', - new webdriver.stacktrace.Frame('', '', '', - 'javascript:console.log(Error().stack):1')); - - assertStackFrame('path only', - '@file:///foo:42', - new webdriver.stacktrace.Frame('', '', '', 'file:///foo:42')); - - // (function go() { throw Error() })() - // var c = go; c() - assertStackFrame('name and empty path', - 'go([arguments not available])@', - new webdriver.stacktrace.Frame('', 'go', '', '')); - assertStackFrame('name and path', - 'go([arguments not available])@file:///foo:42', - new webdriver.stacktrace.Frame('', 'go', '', 'file:///foo:42')); - - // (function() { throw Error() })() - assertStackFrame('anonymous function', - '([arguments not available])@file:///foo:42', - new webdriver.stacktrace.Frame('', '', '', 'file:///foo:42')); - - // var b = {foo: function() { throw Error() }} - assertStackFrame('object literal function', - '()@file:///foo:42', - new webdriver.stacktrace.Frame('', 'foo', '', 'file:///foo:42')); - - // var c = {}; c.foo = function() { throw Error() } - assertStackFrame('named object literal function', - '()@file:///foo:42', - new webdriver.stacktrace.Frame('c', 'foo', '', 'file:///foo:42')); - - assertStackFrame('prototype function', - '()@', - new webdriver.stacktrace.Frame('Foo.prototype', 'bar', '', '')); - - assertStackFrame('namespaced prototype function', - '()@', - new webdriver.stacktrace.Frame( - 'goog.Foo.prototype', 'bar', '', '')); + 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() { @@ -468,6 +431,19 @@ function testFormatsUsingNameAndMessageIfAvailable() { 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)', diff --git a/lib/webdriver/test/test_bootstrap.js b/lib/webdriver/test/test_bootstrap.js index f77bcdf..e429377 100644 --- a/lib/webdriver/test/test_bootstrap.js +++ b/lib/webdriver/test/test_bootstrap.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 Bootstrap file that loads all of the WebDriver scripts in the diff --git a/lib/webdriver/test/testing/asserts_test.js b/lib/webdriver/test/testing/asserts_test.js index 658f28e..c5e0681 100644 --- a/lib/webdriver/test/testing/asserts_test.js +++ b/lib/webdriver/test/testing/asserts_test.js @@ -1,18 +1,22 @@ -// Copyright 2014 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.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); goog.require('webdriver.test.testutil'); goog.require('webdriver.testing.assert'); goog.require('webdriver.testing.asserts'); @@ -20,6 +24,11 @@ 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(); } @@ -38,29 +47,23 @@ function testAssertion_nonPromiseValue_notValueMatches() { function testAssertion_promiseValue_valueMatches() { - var d = new webdriver.promise.Deferred(); - assert(d).equalTo('foo').then(result.callback, result.errback); - result.assertNeither(); - d.fulfill('foo'); - result.assertCallback(); + return assert(webdriver.promise.fulfilled('foo')).equalTo('foo'); } function testAssertion_promiseValue_notValueMatches() { var d = new webdriver.promise.Deferred(); - assert(d).equalTo('foo').then(result.callback, result.errback); - result.assertNeither(); - d.fulfill('bar'); - result.assertErrback(); + return assert(webdriver.promise.fulfilled('bar')).equalTo('foo'). + then(fail, goog.nullFunction); } function testAssertion_promiseValue_promiseRejected() { - var d = new webdriver.promise.Deferred(); - assert(d).equalTo('foo').then(result.callback, result.errback); - result.assertNeither(); - d.reject(); - result.assertErrback(); + var err = Error(); + return assert(webdriver.promise.rejected(err)).equalTo('foo'). + then(fail, function(e) { + assertEquals(err, e); + }); } @@ -71,7 +74,7 @@ function testAssertion_decoration() { function testAssertion_negation() { - var a = assert('foo'); + 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. @@ -85,44 +88,30 @@ function testAssertion_negation() { function testApplyMatcher_nonPromiseValue_valueMatches() { - assertThat('foo', equals('foo')). - then(result.callback, result.errback); - result.assertCallback(); + return assertThat('foo', equals('foo')); } function testApplyMatcher_nonPromiseValue_notValueMatches() { - assertThat('foo', equals('bar')). - then(result.callback, result.errback); - result.assertErrback(); + return assertThat('foo', equals('bar')).then(fail, goog.nullFunction); } function testApplyMatcher_promiseValue_valueMatches() { - var d = new webdriver.promise.Deferred(); - assertThat(d, equals('foo')). - then(result.callback, result.errback); - result.assertNeither(); - d.fulfill('foo'); - result.assertCallback(); + return assertThat(webdriver.promise.fulfilled('foo'), equals('foo')); } function testApplyMatcher_promiseValue_notValueMatches() { - var d = new webdriver.promise.Deferred(); - assertThat(d, equals('foo')). - then(result.callback, result.errback); - result.assertNeither(); - d.fulfill('bar'); - result.assertErrback(); + return assertThat(webdriver.promise.fulfilled('bar'), equals('foo')). + then(fail, goog.nullFunction); } function testApplyMatcher_promiseValue_promiseRejected() { - var d = new webdriver.promise.Deferred(); - assertThat(d, equals('foo')). - then(result.callback, result.errback); - result.assertNeither(); - d.reject(); - result.assertErrback(); + 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 index ebdb013..f1179ba 100644 --- a/lib/webdriver/test/testing/client_test.js +++ b/lib/webdriver/test/testing/client_test.js @@ -1,20 +1,24 @@ -// Copyright 2014 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.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 = { @@ -32,6 +36,10 @@ 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); @@ -47,7 +55,7 @@ function tearDown() { function expectToSendEvent(type, data) { mockXhr.open('POST', '/testevent', true); - mockXhr.send(goog.json.serialize({ + mockXhr.send(JSON.stringify({ 'id': '/foo/bar/baz', 'type': type, 'data': data diff --git a/lib/webdriver/test/testing/testcase_test.js b/lib/webdriver/test/testing/testcase_test.js index 8b00850..51b54d1 100644 --- a/lib/webdriver/test/testing/testcase_test.js +++ b/lib/webdriver/test/testing/testcase_test.js @@ -1,41 +1,47 @@ -// Copyright 2014 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.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'); -goog.require('webdriver.testing.promise.FlowTester'); // Aliases for readability. var IGNORE_ARGUMENT = goog.testing.mockmatchers.ignoreArgument, IS_ARRAY_ARGUMENT = goog.testing.mockmatchers.isArray, - STUB_ERROR = webdriver.test.testutil.STUB_ERROR, + StubError = webdriver.test.testutil.StubError, throwStubError = webdriver.test.testutil.throwStubError, assertIsStubError = webdriver.test.testutil.assertIsStubError; var control = new goog.testing.MockControl(); -var flowTester, clock, mockTestCase, testStub, mockOnComplete, mockOnError; +var mockTestCase, testStub, mockOnComplete, mockOnError, uncaughtExceptions; -function setUp() { - clock = webdriver.test.testutil.createMockClock(); - flowTester = new webdriver.testing.promise.FlowTester(clock, goog.global); +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, @@ -55,13 +61,27 @@ function setUp() { }; webdriver.test.testutil.messages = []; + uncaughtExceptions = []; + + webdriver.promise.controlFlow(). + on('uncaughtExceptions', onUncaughtException); } function tearDown() { - flowTester.verifySuccess(); - flowTester.dispose(); - control.$tearDown(); - clock.dispose(); + 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) { @@ -70,11 +90,9 @@ function schedule(msg, opt_fn) { } function runTest() { - webdriver.testing.TestCase.prototype.runSingleTest_. + return webdriver.testing.TestCase.prototype.runSingleTest_. call(mockTestCase, testStub, mockOnError). then(mockOnComplete); - flowTester.run(); - control.$verifyAll(); } function testExecutesTheBasicTestFlow() { @@ -84,7 +102,7 @@ function testExecutesTheBasicTestFlow() { mockOnComplete(IGNORE_ARGUMENT); control.$replayAll(); - runTest(); + return runTest(); } function testExecutingAHappyTestWithScheduledActions() { @@ -94,55 +112,59 @@ function testExecutingAHappyTestWithScheduledActions() { mockOnComplete(IGNORE_ARGUMENT); control.$replayAll(); - runTest(); + return runTest(); } function testShouldSkipTestFnIfSetupThrows() { - mockTestCase.setUp().$does(throwStubError); - mockOnError(STUB_ERROR); + var e = Error(); + mockTestCase.setUp().$does(function() { throw e; }); + mockOnError(e); mockTestCase.tearDown(); mockOnComplete(IGNORE_ARGUMENT); control.$replayAll(); - runTest(); + return runTest(); } function testShouldSkipTestFnIfSetupActionFails_1() { + var e = Error(); mockTestCase.setUp().$does(function() { - schedule('an explosion', throwStubError); + schedule('an explosion', function() { throw e; }); }); - mockOnError(STUB_ERROR); + mockOnError(e); mockTestCase.tearDown(); mockOnComplete(IGNORE_ARGUMENT); control.$replayAll(); - runTest(); + return runTest(); } function testShouldSkipTestFnIfSetupActionFails_2() { + var e = Error(); mockTestCase.setUp().$does(function() { - schedule('an explosion', throwStubError); + schedule('an explosion', function() { throw e; }); }); - mockOnError(STUB_ERROR); + mockOnError(e); mockTestCase.tearDown(); mockOnComplete(IGNORE_ARGUMENT); control.$replayAll(); - runTest(); + return runTest(); } function testShouldSkipTestFnIfNestedSetupActionFails() { + var e = Error(); mockTestCase.setUp().$does(function() { schedule('a', goog.nullFunction).then(function() { - schedule('b', throwStubError); + schedule('b', function() { throw e; }); }); }); - mockOnError(STUB_ERROR); + mockOnError(e); mockTestCase.tearDown(); mockOnComplete(IGNORE_ARGUMENT); control.$replayAll(); - runTest(); + return runTest(); } function testRunsAllTasksForEachPhaseBeforeTheNextPhase() { @@ -153,40 +175,44 @@ function testRunsAllTasksForEachPhaseBeforeTheNextPhase() { mockOnComplete(IGNORE_ARGUMENT); control.$replayAll(); - runTest(); + return runTest(); } function testRecordsErrorsFromTestFnBeforeTearDown() { + var e = Error(); mockTestCase.setUp(); - mockTestCase.testFn().$does(throwStubError); - mockOnError(STUB_ERROR); + mockTestCase.testFn().$does(function() { throw e; }); + mockOnError(e); mockTestCase.tearDown(); mockOnComplete(IGNORE_ARGUMENT); control.$replayAll(); - runTest(); + return runTest(); } function testRecordsErrorsFromTearDown() { + var e = Error(); mockTestCase.setUp(); mockTestCase.testFn(); - mockTestCase.tearDown().$does(throwStubError); - mockOnError(STUB_ERROR); + mockTestCase.tearDown().$does(function() { throw e; }); + mockOnError(e); mockOnComplete(IGNORE_ARGUMENT); control.$replayAll(); - runTest(); + return runTest(); } function testErrorFromSetUpAndTearDown() { - mockTestCase.setUp().$does(throwStubError); - mockOnError(STUB_ERROR); - mockTestCase.tearDown().$does(throwStubError); - mockOnError(STUB_ERROR); + 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(); - runTest(); + return runTest(); } function testErrorFromTestFnAndTearDown() { @@ -199,5 +225,5 @@ function testErrorFromTestFnAndTearDown() { mockOnComplete(IGNORE_ARGUMENT); control.$replayAll(); - runTest(); + return runTest(); } diff --git a/lib/webdriver/test/testutil.js b/lib/webdriver/test/testutil.js index a090063..2cf02a5 100644 --- a/lib/webdriver/test/testutil.js +++ b/lib/webdriver/test/testutil.js @@ -1,137 +1,62 @@ -// 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.test.testutil'); +goog.provide('webdriver.test.testutil.StubError'); goog.require('goog.array'); -goog.require('goog.json'); +goog.require('goog.debug.Error'); goog.require('goog.string'); -goog.require('goog.testing.MockClock'); goog.require('goog.testing.recordFunction'); goog.require('webdriver.stacktrace'); -/** @type {?goog.testing.MockClock} */ -webdriver.test.testutil.clock = null; + +/** + * 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 = []; -/** @type {!Error} */ -webdriver.test.testutil.STUB_ERROR = new Error('ouch'); -webdriver.test.testutil.STUB_ERROR.stack = '(stub error; stack irrelevant)'; - webdriver.test.testutil.getStackTrace = function() { return webdriver.stacktrace.get(); }; webdriver.test.testutil.throwStubError = function() { - throw webdriver.test.testutil.STUB_ERROR; + throw new webdriver.test.testutil.StubError; }; webdriver.test.testutil.assertIsStubError = function(error) { - assertEquals(webdriver.test.testutil.STUB_ERROR, error); -}; - -webdriver.test.testutil.createMockClock = function() { - webdriver.test.testutil.clock = new goog.testing.MockClock(true); - - /* Patch to work around the following bug with mock clock: - * function testNewZeroBasedTimeoutsRunInNextEventLoopAfterExistingTasks() { - * var events = []; - * setInterval(function() { events.push('a'); }, 1); - * setTimeout(function() { events.push('b'); }, 0); - * clock.tick(); - * assertEquals('ab', events.join('')); - * } - */ - goog.testing.MockClock.insert_ = function(timeout, queue) { - if (timeout.runAtMillis === goog.now() && timeout.millis === 0) { - timeout.runAtMillis += 1; - } - // Original goog.testing.MockClock.insert_ follows. - for (var i = queue.length; i != 0; i--) { - if (queue[i - 1].runAtMillis > timeout.runAtMillis) { - break; - } - queue[i] = queue[i - 1]; - } - queue[i] = timeout; - }; - - /* Patch to work around the following bug with mock clock: - * function testZeroBasedTimeoutsRunInNextEventLoop() { - * var count = 0; - * setTimeout(function() { - * count += 1; - * setTimeout(function() { count += 1; }, 0); - * setTimeout(function() { count += 1; }, 0); - * }, 0); - * clock.tick(); - * assertEquals(1, count); // Fails; count == 3 - * clock.tick(); - * assertEquals(3, count); - * } - */ - webdriver.test.testutil.clock.runFunctionsWithinRange_ = function(endTime) { - var adjustedEndTime = endTime - this.timeoutDelay_; - - // Repeatedly pop off the last item since the queue is always sorted. - // Stop once we've collected all timeouts that should run. - var timeouts = []; - while (this.queue_.length && - this.queue_[this.queue_.length - 1].runAtMillis <= adjustedEndTime) { - timeouts.push(this.queue_.pop()); - } - - // Now run all timeouts that are within range. - while (timeouts.length) { - var timeout = timeouts.shift(); - - if (!(timeout.timeoutKey in this.deletedKeys_)) { - // Only move time forwards. - this.nowMillis_ = Math.max(this.nowMillis_, - timeout.runAtMillis + this.timeoutDelay_); - // Call timeout in global scope and pass the timeout key as - // the argument. - timeout.funcToCall.call(goog.global, timeout.timeoutKey); - // In case the interval was cleared in the funcToCall - if (timeout.recurring) { - this.scheduleFunction_( - timeout.timeoutKey, timeout.funcToCall, timeout.millis, true); - } - } - } - }; - - return webdriver.test.testutil.clock; -}; - - -/** - * Advances the clock by one tick. - * @param {number=} opt_n The number of ticks to advance the clock. If not - * specified, will advance the clock once for every timeout made. - * Assumes all timeouts are 0-based. - */ -webdriver.test.testutil.consumeTimeouts = function(opt_n) { - // webdriver.promise and webdriver.application only schedule 0 timeouts to - // yield until the next available event loop. - for (var i = 0; - i < (opt_n || webdriver.test.testutil.clock.getTimeoutsMade()); i++) { - webdriver.test.testutil.clock.tick(); - } + assertTrue(error + ' is not an instanceof StubError', + error instanceof webdriver.test.testutil.StubError); }; @@ -278,7 +203,7 @@ webdriver.test.testutil.callbackPair = function(opt_callback, opt_errback) { webdriver.test.testutil.assertObjectEquals = function(expected, actual) { assertObjectEquals( - 'Expected: ' + goog.json.serialize(expected) + '\n' + - 'Actual: ' + goog.json.serialize(actual), + '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 index 183a869..af55b32 100644 --- a/lib/webdriver/test/testutil_test.js +++ b/lib/webdriver/test/testutil_test.js @@ -1,32 +1,26 @@ -// Copyright 2014 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.require('goog.testing.jsunit'); goog.require('webdriver.test.testutil'); // Aliases for readability. var callbackHelper = webdriver.test.testutil.callbackHelper, - callbackPair = webdriver.test.testutil.callbackPair, - clock; - -function setUp() { - clock = webdriver.test.testutil.createMockClock(); -} - -function tearDown() { - clock.dispose(); -} + callbackPair = webdriver.test.testutil.callbackPair; function testCallbackHelper_functionCalled() { var callback = callbackHelper(); @@ -108,24 +102,3 @@ function testCallbackPair_neitherExpected() { pair.errback(); assertThrows(pair.assertNeither); } - -function testZeroBasedTimeoutsRunInNextEventLoop() { - var count = 0; - setTimeout(function() { - count += 1; - setTimeout(function() { count += 1; }, 0); - setTimeout(function() { count += 1; }, 0); - }, 0); - clock.tick(); - assertEquals(1, count); // Fails; count == 3 - clock.tick(); - assertEquals(3, count); -} - -function testNewZeroBasedTimeoutsRunInNextEventLoopAfterExistingTasks() { - var events = []; - setInterval(function() { events.push('a'); }, 1); - setTimeout(function() { events.push('b'); }, 0); - clock.tick(); - assertEquals('ab', events.join('')); -} 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 index c3a8b3c..7b04ea4 100644 --- a/lib/webdriver/test/webdriver_generator_test.js +++ b/lib/webdriver/test/webdriver_generator_test.js @@ -1,30 +1,28 @@ -// 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. goog.provide('webdriver.test.WebDriver.generator.test'); goog.setTestOnly('webdriver.test.WebDriver.generator.test'); -goog.require('goog.testing.AsyncTestCase'); goog.require('goog.testing.jsunit'); goog.require('webdriver.Session'); goog.require('webdriver.WebDriver'); -var test = goog.testing.AsyncTestCase.createAndInstall( - 'webdriver_generator_test'); - var driver; function setUp() { @@ -35,49 +33,40 @@ function setUp() { function testCanUseGeneratorsWithWebDriverCall() { - test.waitForAsync(); - - driver.call(function* () { + 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); - test.continueTesting(); }); } function testCanDefineScopeOnGeneratorCall() { - test.waitForAsync(); - - driver.call(function* () { + return driver.call(function* () { var x = yield webdriver.promise.fulfilled(1); return this.name + x; }, {name: 'Bob'}).then(function(value) { assertEquals('Bob1', value); - test.continueTesting(); }); } function testCanSpecifyArgsOnGeneratorCall() { - test.waitForAsync(); - - driver.call(function* (a, b) { + 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); - test.continueTesting(); }); } function testCanUseGeneratorWithWebDriverWait() { var values = []; - driver.wait(function* () { + return driver.wait(function* () { yield values.push(1); values.push(yield webdriver.promise.delayed(10).then(function() { return 2; @@ -86,9 +75,7 @@ function testCanUseGeneratorWithWebDriverWait() { return values.length === 6; }, 250).then(function() { assertArrayEquals([1, 2, 3, 1, 2, 3], values); - test.continueTesting(); }); - test.waitForAsync(); } diff --git a/lib/webdriver/test/webdriver_test.js b/lib/webdriver/test/webdriver_test.js index 4bc765b..5558c36 100644 --- a/lib/webdriver/test/webdriver_test.js +++ b/lib/webdriver/test/webdriver_test.js @@ -1,36 +1,38 @@ -// Copyright 2014 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.require('bot.ErrorCode'); +goog.require('goog.Promise'); goog.require('goog.functions'); -goog.require('goog.json'); 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.promise.ControlFlow'); -goog.require('webdriver.promise.Deferred'); -goog.require('webdriver.promise.Promise'); goog.require('webdriver.test.testutil'); -goog.require('webdriver.testing.promise.FlowTester'); var SESSION_ID = 'test_session_id'; @@ -41,36 +43,83 @@ var STUB_DRIVER = { // Alias some long names that interfere with test readability. var CName = webdriver.CommandName, ECode = bot.ErrorCode, - STUB_ERROR = webdriver.test.testutil.STUB_ERROR, + StubError = webdriver.test.testutil.StubError, throwStubError = webdriver.test.testutil.throwStubError, - assertIsStubError = webdriver.test.testutil.assertIsStubError, - callbackHelper = webdriver.test.testutil.callbackHelper, - callbackPair = webdriver.test.testutil.callbackPair; + 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 clock; var driver; -var flowTester; +var flow; var mockControl; -var verifyAll; +var uncaughtExceptions; + +function shouldRunTests() { + return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); +} + function setUp() { - clock = webdriver.test.testutil.createMockClock(); - flowTester = new webdriver.testing.promise.FlowTester(clock, goog.global); mockControl = new goog.testing.MockControl(); - verifyAll = callbackHelper(goog.bind(mockControl.$verifyAll, mockControl)); + flow = webdriver.promise.controlFlow(); + uncaughtExceptions = []; + flow.on('uncaughtException', onUncaughtException); } function tearDown() { - flowTester.dispose(); - verifyAll.assertCalled('Never verified mocks'); - clock.uninstall(); - mockControl.$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.activeFrame_ && !theFlow.yieldCount_) { + 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); + } + } + }); } @@ -89,51 +138,16 @@ function createCommandMatcher(commandName, parameters) { parameters, actual.getParameters()); assertNull( 'Wrong parameters for "' + commandName + '"' + - '\n Expected: ' + goog.json.serialize(parameters) + - '\n Actual: ' + goog.json.serialize(actual.getParameters()), + '\n Expected: ' + JSON.stringify(parameters) + + '\n Actual: ' + JSON.stringify(actual.getParameters()), differences); return true; - }, commandName + '(' + goog.json.serialize(parameters) + ')'); + }, commandName + '(' + JSON.stringify(parameters) + ')'); } TestHelper = function() { this.executor = mockControl.createStrictMock(webdriver.CommandExecutor); - this.execute = function() { - fail('Expectations not set!'); - }; -}; - - -TestHelper.expectingFailure = function(opt_errback) { - var helper = new TestHelper(); - - helper.execute = function() { - flowTester.run(); - flowTester.verifyFailure(); - verifyAll(); - if (opt_errback) { - opt_errback(flowTester.getFailure()); - } - }; - - return helper; -}; - - -TestHelper.expectingSuccess = function(opt_callback) { - var helper = new TestHelper(); - - helper.execute = function() { - flowTester.run(); - flowTester.verifySuccess(); - verifyAll(); - if (opt_callback) { - opt_callback(); - } - }; - - return helper; }; @@ -152,6 +166,8 @@ 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 || {}); }; @@ -169,9 +185,17 @@ TestHelper.Command.prototype.withParameters = function(parameters) { TestHelper.Command.prototype.buildExpectation_ = function() { var commandMatcher = createCommandMatcher(this.name_, this.parameters_); assertNotNull(this.toDo_); - this.helper_.executor. + 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_); + } }; @@ -189,6 +213,18 @@ TestHelper.Command.prototype.andReturn = function(code, opt_value) { }; +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); }; @@ -235,47 +271,41 @@ TestHelper.prototype.createDriver = function(opt_session) { ////////////////////////////////////////////////////////////////////////////// function testAttachToSession_sessionIsAvailable() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.DESCRIBE_SESSION). withParameters({'sessionId': SESSION_ID}). andReturnSuccess({'browserName': 'firefox'}). replayAll(); - var callback; var driver = webdriver.WebDriver.attachToSession(testHelper.executor, SESSION_ID); - driver.getSession().then(callback = callbackHelper(function(session) { + return driver.getSession().then(function(session) { webdriver.test.testutil.assertObjectEquals({ 'value':'test_session_id' }, session.getId()); assertEquals('firefox', session.getCapability('browserName')); - })); - testHelper.execute(); - callback.assertCalled(); + }); } function testAttachToSession_failsToGetSessionInfo() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.DESCRIBE_SESSION). withParameters({'sessionId': SESSION_ID}). andReturnError(ECode.UNKNOWN_ERROR, {'message': 'boom'}). replayAll(); - var errback; var driver = webdriver.WebDriver.attachToSession(testHelper.executor, SESSION_ID); - driver.getSession().then(null, errback = callbackHelper(function(e) { + return driver.getSession().then(fail, function(e) { assertEquals(bot.ErrorCode.UNKNOWN_ERROR, e.code); assertEquals('boom', e.message); - })); - testHelper.execute(); - errback.assertCalled(); + }); } function testAttachToSession_usesActiveFlowByDefault() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.DESCRIBE_SESSION). withParameters({'sessionId': SESSION_ID}). andReturnSuccess({}). @@ -284,28 +314,30 @@ function testAttachToSession_usesActiveFlowByDefault() { var driver = webdriver.WebDriver.attachToSession(testHelper.executor, SESSION_ID); assertEquals(driver.controlFlow(), webdriver.promise.controlFlow()); - testHelper.execute(); + + return waitForIdle(driver.controlFlow()); } function testAttachToSession_canAttachInCustomFlow() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.DESCRIBE_SESSION). withParameters({'sessionId': SESSION_ID}). andReturnSuccess({}). replayAll(); - var otherFlow = new webdriver.promise.ControlFlow(goog.global); - var driver = webdriver.WebDriver.attachToSession(testHelper.executor, - SESSION_ID, otherFlow); + 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()); - testHelper.execute(); + + return waitForIdle(otherFlow); } function testCreateSession_happyPathWithCapabilitiesHashObject() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.NEW_SESSION). withParameters({ 'desiredCapabilities': {'browserName': 'firefox'} @@ -313,23 +345,20 @@ function testCreateSession_happyPathWithCapabilitiesHashObject() { andReturnSuccess({'browserName': 'firefox'}). replayAll(); - var callback; var driver = webdriver.WebDriver.createSession(testHelper.executor, { 'browserName': 'firefox' }); - driver.getSession().then(callback = callbackHelper(function(session) { + return driver.getSession().then(function(session) { webdriver.test.testutil.assertObjectEquals({ 'value':'test_session_id' }, session.getId()); assertEquals('firefox', session.getCapability('browserName')); - })); - testHelper.execute(); - callback.assertCalled(); + }); } function testCreateSession_happyPathWithCapabilitiesInstance() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.NEW_SESSION). withParameters({ 'desiredCapabilities': {'browserName': 'firefox'} @@ -337,22 +366,19 @@ function testCreateSession_happyPathWithCapabilitiesInstance() { andReturnSuccess({'browserName': 'firefox'}). replayAll(); - var callback; var driver = webdriver.WebDriver.createSession( testHelper.executor, webdriver.Capabilities.firefox()); - driver.getSession().then(callback = callbackHelper(function(session) { + return driver.getSession().then(function(session) { webdriver.test.testutil.assertObjectEquals({ 'value':'test_session_id' }, session.getId()); assertEquals('firefox', session.getCapability('browserName')); - })); - testHelper.execute(); - callback.assertCalled(); + }); } function testCreateSession_failsToCreateSession() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.NEW_SESSION). withParameters({ 'desiredCapabilities': {'browserName': 'firefox'} @@ -360,21 +386,18 @@ function testCreateSession_failsToCreateSession() { andReturnError(ECode.UNKNOWN_ERROR, {'message': 'boom'}). replayAll(); - var errback; var driver = webdriver.WebDriver.createSession(testHelper.executor, { 'browserName': 'firefox' }); - driver.getSession().then(null, errback = callbackHelper(function(e) { + return driver.getSession().then(fail, function(e) { assertEquals(bot.ErrorCode.UNKNOWN_ERROR, e.code); assertEquals('boom', e.message); - })); - testHelper.execute(); - errback.assertCalled(); + }); } function testCreateSession_usesActiveFlowByDefault() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.NEW_SESSION). withParameters({'desiredCapabilities': {}}). andReturnSuccess({}). @@ -382,12 +405,13 @@ function testCreateSession_usesActiveFlowByDefault() { var driver = webdriver.WebDriver.createSession(testHelper.executor, {}); assertEquals(webdriver.promise.controlFlow(), driver.controlFlow()); - testHelper.execute(); + + return waitForIdle(driver.controlFlow()); } function testCreateSession_canCreateInCustomFlow() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.NEW_SESSION). withParameters({'desiredCapabilities': {}}). andReturnSuccess({}). @@ -398,47 +422,68 @@ function testCreateSession_canCreateInCustomFlow() { testHelper.executor, {}, otherFlow); assertEquals(otherFlow, driver.controlFlow()); assertNotEquals(otherFlow, webdriver.promise.controlFlow()); - testHelper.execute(); + + return waitForIdle(otherFlow); } function testToWireValue_function() { var fn = function() { return 'foo'; }; - var callback; - webdriver.WebDriver.toWireValue_(fn). - then(callback = callbackHelper(function(value) { - assertEquals(fn + '', value); - })); - callback.assertCalled(); - verifyAll(); // Expected by tear down. + 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'}; - var callback; - webdriver.WebDriver.toWireValue_({ + return webdriver.WebDriver.toWireValue_({ 'sessionId': new webdriver.Session('foo', {}) - }).then(callback = callbackHelper(function(actual) { + }).then(function(actual) { webdriver.test.testutil.assertObjectEquals(expected, actual); - })); - callback.assertCalled(); - verifyAll(); // Expected by tear down. + }); } function testToWireValue_nestedObject() { var expected = {'sessionId': {'value': 'foo'}}; - var callback; - webdriver.WebDriver.toWireValue_({ + return webdriver.WebDriver.toWireValue_({ 'sessionId': { 'value': new webdriver.Session('foo', {}) } - }).then(callback = callbackHelper(function(actual) { + }).then(function(actual) { webdriver.test.testutil.assertObjectEquals(expected, actual); - })); - callback.assertCalled(); - verifyAll(); // Expected by tear down. + }); +} + + +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); + }); } @@ -447,13 +492,9 @@ function testToWireValue_webElement() { expected[webdriver.WebElement.ELEMENT_KEY] = 'fefifofum'; var element = new webdriver.WebElement(STUB_DRIVER, expected); - var callback; - webdriver.WebDriver.toWireValue_(element). - then(callback = callbackHelper(function(actual) { - webdriver.test.testutil.assertObjectEquals(expected, actual); - })); - callback.assertCalled(); - verifyAll(); // Expected by tear down. + return webdriver.WebDriver.toWireValue_(element).then(function(actual) { + webdriver.test.testutil.assertObjectEquals(expected, actual); + }); } @@ -464,32 +505,52 @@ function testToWireValue_webElementPromise() { var element = new webdriver.WebElement(STUB_DRIVER, expected); var elementPromise = new webdriver.WebElementPromise(STUB_DRIVER, webdriver.promise.fulfilled(element)); - var callback; - webdriver.WebDriver.toWireValue_(elementPromise). - then(callback = callbackHelper(function(actual) { + return webdriver.WebDriver.toWireValue_(elementPromise). + then(function(actual) { webdriver.test.testutil.assertObjectEquals(expected, actual); - })); - callback.assertCalled(); - verifyAll(); // Expected by tear down. + }); } function testToWireValue_domElement() { assertThrows( goog.partial(webdriver.WebDriver.toWireValue_, document.body)); - verifyAll(); // Expected by tear down. +} + + +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']; - var callback; - webdriver.WebDriver.toWireValue_([new webdriver.Session('foo', {})]).then( - callback = callbackHelper(function(actual) { + return webdriver.WebDriver.toWireValue_([new webdriver.Session('foo', {})]). + then(function(actual) { assertArrayEquals(expected, actual); - })); - callback.assertCalled(); - verifyAll(); // Expected by tear down. + }); } @@ -498,15 +559,29 @@ function testToWireValue_arrayWithWebElement() { elementJson[webdriver.WebElement.ELEMENT_KEY] = 'fefifofum'; var element = new webdriver.WebElement(STUB_DRIVER, elementJson); - var callback; - webdriver.WebDriver.toWireValue_([element]). - then(callback = callbackHelper(function(actual) { + return webdriver.WebDriver.toWireValue_([element]). + then(function(actual) { assertTrue(goog.isArray(actual)); assertEquals(1, actual.length); webdriver.test.testutil.assertObjectEquals(elementJson, actual[0]); - })); - callback.assertCalled(); - verifyAll(); // Expected by tear down. + }); +} + + +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]); + }); } @@ -517,31 +592,25 @@ function testToWireValue_complexArray() { var element = new webdriver.WebElement(STUB_DRIVER, elementJson); var input = ['abc', 123, true, element, [123, {'foo': 'bar'}]]; - var callback; - webdriver.WebDriver.toWireValue_(input). - then(callback = callbackHelper(function(actual) { + return webdriver.WebDriver.toWireValue_(input). + then(function(actual) { webdriver.test.testutil.assertObjectEquals(expected, actual); - })); - callback.assertCalled(); - verifyAll(); // Expected by tear down. + }); } function testToWireValue_arrayWithNestedPromises() { - var callback; - webdriver.WebDriver.toWireValue_([ + return webdriver.WebDriver.toWireValue_([ 'abc', webdriver.promise.fulfilled([ 123, webdriver.promise.fulfilled(true) ]) - ]).then(callback = callbackHelper(function(actual) { + ]).then(function(actual) { assertEquals(2, actual.length); assertEquals('abc', actual[0]); assertArrayEquals([123, true], actual[1]); - })); - callback.assertCalled(); - verifyAll(); // Expected by tear down. + }); } @@ -561,13 +630,10 @@ function testToWireValue_complexHash() { 'sessionId': new webdriver.Session('foo', {}) }; - var callback; - webdriver.WebDriver.toWireValue_(parameters). - then(callback = callbackHelper(function(actual) { + return webdriver.WebDriver.toWireValue_(parameters). + then(function(actual) { webdriver.test.testutil.assertObjectEquals(expected, actual); - })); - callback.assertCalled(); - verifyAll(); // Expected by tear down. + }); } @@ -578,8 +644,6 @@ function testFromWireValue_primitives() { assertUndefined(webdriver.WebDriver.fromWireValue_({}, undefined)); assertNull(webdriver.WebDriver.fromWireValue_({}, null)); - - verifyAll(); // Expected by tear down. } @@ -590,13 +654,9 @@ function testFromWireValue_webElements() { var element = webdriver.WebDriver.fromWireValue_(STUB_DRIVER, json); assertEquals(STUB_DRIVER, element.getDriver()); - var callback; - webdriver.promise.when(element.id_, callback = callbackHelper(function(id) { + return element.getId().then(function(id) { webdriver.test.testutil.assertObjectEquals(json, id); - })); - callback.assertCalled(); - - verifyAll(); // Expected by tear down. + }); } @@ -604,7 +664,6 @@ function testFromWireValue_simpleObject() { var json = {'sessionId': 'foo'}; var out = webdriver.WebDriver.fromWireValue_({}, json); webdriver.test.testutil.assertObjectEquals(json, out); - verifyAll(); // Expected by tear down. } @@ -612,7 +671,6 @@ function testFromWireValue_nestedObject() { var json = {'foo': {'bar': 123}}; var out = webdriver.WebDriver.fromWireValue_({}, json); webdriver.test.testutil.assertObjectEquals(json, out); - verifyAll(); // Expected by tear down. } @@ -620,7 +678,6 @@ function testFromWireValue_array() { var json = [{'foo': {'bar': 123}}]; var out = webdriver.WebDriver.fromWireValue_({}, json); webdriver.test.testutil.assertObjectEquals(json, out); - verifyAll(); // Expected by tear down. } @@ -628,40 +685,31 @@ function testFromWireValue_passesThroughFunctionProperties() { var json = [{'foo': {'bar': 123}, 'func': goog.nullFunction}]; var out = webdriver.WebDriver.fromWireValue_({}, json); webdriver.test.testutil.assertObjectEquals(json, out); - verifyAll(); // Expected by tear down. } function testDoesNotExecuteCommandIfSessionDoesNotResolve() { - var session = webdriver.promise.rejected(STUB_ERROR); - var testHelper = TestHelper. - expectingFailure(assertIsStubError). - replayAll(); + var session = webdriver.promise.rejected(new StubError); + var testHelper = new TestHelper().replayAll(); testHelper.createDriver(session).getTitle(); - testHelper.execute(); + return waitForAbort().then(assertIsStubError); } function testCommandReturnValuesArePassedToFirstCallback() { - var testHelper = TestHelper.expectingSuccess(). - expect(CName.GET_TITLE). - andReturnSuccess('Google Search'). + var testHelper = new TestHelper(). + expect(CName.GET_TITLE).andReturnSuccess('Google Search'). replayAll(); var driver = testHelper.createDriver(); - var callback; - driver.getTitle().then(callback = callbackHelper(function(title) { + return driver.getTitle().then(function(title) { assertEquals('Google Search', title); - })); - - testHelper.execute(); - callback.assertCalled(); + }); } function testStopsCommandExecutionWhenAnErrorOccurs() { - var testHelper = TestHelper. - expectingFailure(expectedError(ECode.NO_SUCH_WINDOW, 'window not found')). + var testHelper = new TestHelper(). expect(CName.SWITCH_TO_WINDOW). withParameters({'name': 'foo'}). andReturnError(ECode.NO_SUCH_WINDOW, {'message': 'window not found'}). @@ -671,74 +719,59 @@ function testStopsCommandExecutionWhenAnErrorOccurs() { driver.switchTo().window('foo'); driver.getTitle(); // mock should blow if this gets executed - testHelper.execute(); + return waitForAbort(). + then(expectedError(ECode.NO_SUCH_WINDOW, 'window not found')); } function testCanSuppressCommandFailures() { - var callback; - var testHelper = TestHelper. - expectingSuccess(function() { - callback.assertCalled(); - }). + var testHelper = new TestHelper(). expect(CName.SWITCH_TO_WINDOW). - withParameters({'name': 'foo'}). - andReturnError(ECode.NO_SUCH_WINDOW, {'message': 'window not found'}). + withParameters({'name': 'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message': 'window not found'}). expect(CName.GET_TITLE). - andReturnSuccess('Google Search'). + andReturnSuccess('Google Search'). replayAll(); var driver = testHelper.createDriver(); - driver.switchTo().window('foo'). - thenCatch(callback = callbackHelper(function(e) { - assertEquals(ECode.NO_SUCH_WINDOW, e.code); - assertEquals('window not found', e.message); - return true; // suppress expected failure - })); + driver.switchTo().window('foo').thenCatch(function(e) { + assertEquals(ECode.NO_SUCH_WINDOW, e.code); + assertEquals('window not found', e.message); + }); driver.getTitle(); - - // The mock will verify that getTitle was executed, which is what we want. - testHelper.execute(); + return waitForIdle(); } function testErrorsPropagateUpToTheRunningApplication() { - var testHelper = TestHelper. - expectingFailure(expectedError(ECode.NO_SUCH_WINDOW, 'window not found')). + var testHelper = new TestHelper(). expect(CName.SWITCH_TO_WINDOW). - withParameters({'name':'foo'}). - andReturnError(ECode.NO_SUCH_WINDOW, {'message': 'window not found'}). + withParameters({'name':'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message': 'window not found'}). replayAll(); testHelper.createDriver().switchTo().window('foo'); - - testHelper.execute(); + return waitForAbort(). + then(expectedError(ECode.NO_SUCH_WINDOW, 'window not found')); } function testErrbacksThatReturnErrorsStillSwitchToCallbackChain() { - var callback; - var testHelper = TestHelper. - expectingSuccess(function() { - callback.assertCalled(); - }). + var testHelper = new TestHelper(). expect(CName.SWITCH_TO_WINDOW). - withParameters({'name':'foo'}). - andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + withParameters({'name':'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). replayAll(); var driver = testHelper.createDriver(); - driver.switchTo().window('foo'). - thenCatch(function() { return STUB_ERROR; }). - then(callback = callbackHelper(assertIsStubError)); - - testHelper.execute(); + return driver.switchTo().window('foo'). + thenCatch(function() { return new StubError; }); + then(assertIsStubError); } function testErrbacksThrownCanOverrideOriginalError() { - var testHelper = TestHelper. - expectingFailure(assertIsStubError). + var testHelper = new TestHelper(). expect(CName.SWITCH_TO_WINDOW, {'name': 'foo'}). andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). replayAll(); @@ -746,51 +779,48 @@ function testErrbacksThrownCanOverrideOriginalError() { var driver = testHelper.createDriver(); driver.switchTo().window('foo').thenCatch(throwStubError); - testHelper.execute(); + return waitForAbort().then(assertIsStubError); } function testCannotScheduleCommandsIfTheSessionIdHasBeenDeleted() { - var testHelper = TestHelper.expectingSuccess().replayAll(); + var testHelper = new TestHelper().replayAll(); var driver = testHelper.createDriver(); delete driver.session_; assertThrows(goog.bind(driver.get, driver, 'http://www.google.com')); - verifyAll(); } function testDeletesSessionIdAfterQuitting() { var driver; - var testHelper = TestHelper. - expectingSuccess(function() { - assertUndefined('Session ID should have been deleted', driver.session_); - }). + var testHelper = new TestHelper(). expect(CName.QUIT). replayAll(); driver = testHelper.createDriver(); - driver.quit(); - testHelper.execute(); + return driver.quit().then(function() { + assertUndefined('Session ID should have been deleted', driver.session_); + }); } function testReportsErrorWhenExecutingCommandsAfterExecutingAQuit() { - var testHelper = TestHelper. - expectingFailure(expectedError(undefined, - 'This driver instance does not have a valid session ID ' + - '(did you call WebDriver.quit()?) and may no longer be used.')). + var testHelper = new TestHelper(). expect(CName.QUIT). replayAll(); var driver = testHelper.createDriver(); driver.quit(); driver.get('http://www.google.com'); - testHelper.execute(); + 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 = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.GET_CURRENT_URL). expect(CName.GET, {'url': 'http://www.google.com'}). expect(CName.CLOSE). @@ -805,12 +835,12 @@ function testCallbackCommandsExecuteBeforeNextCommand() { }); driver.getTitle(); - testHelper.execute(); + return waitForIdle(); } function testEachCallbackFrameRunsToCompletionBeforeTheNext() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.GET_TITLE). expect(CName.GET_CURRENT_URL). expect(CName.GET_CURRENT_WINDOW_HANDLE). @@ -832,79 +862,72 @@ function testEachCallbackFrameRunsToCompletionBeforeTheNext() { // This should execute after everything above driver.quit(); - testHelper.execute(); + return waitForIdle(); } function testNestedCommandFailuresBubbleUpToGlobalHandlerIfUnsuppressed() { - var testHelper = TestHelper. - expectingFailure(expectedError(ECode.NO_SUCH_WINDOW, 'window not found')). + var testHelper = new TestHelper(). expect(CName.GET_TITLE). expect(CName.SWITCH_TO_WINDOW, {'name': 'foo'}). - andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). replayAll(); var driver = testHelper.createDriver(); - driver.getTitle(). - then(function(){ - driver.switchTo().window('foo'); - }); + driver.getTitle().then(function() { + driver.switchTo().window('foo'); + }); - testHelper.execute(); + return waitForAbort(). + then(expectedError(ECode.NO_SUCH_WINDOW, 'window not found')); } function testNestedCommandFailuresCanBeSuppressWhenTheyOccur() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.GET_TITLE). expect(CName.SWITCH_TO_WINDOW, {'name':'foo'}). - andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + 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.functions.TRUE); - }); + driver.getTitle().then(function() { + driver.switchTo().window('foo').thenCatch(goog.nullFunction); + }); driver.close(); - testHelper.execute(); + + return waitForIdle(); } function testNestedCommandFailuresBubbleUpThroughTheFrameStack() { - var callback; - var testHelper = TestHelper. - expectingSuccess(function() { - callback.assertCalled('Error did not bubble up'); - }). + var testHelper = new TestHelper(). expect(CName.GET_TITLE). expect(CName.SWITCH_TO_WINDOW, {'name':'foo'}). - andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). replayAll(); var driver = testHelper.createDriver(); driver.getTitle(). - then(function(){ + then(function() { return driver.switchTo().window('foo'); }). - thenCatch(callback = callbackHelper(function(e) { + thenCatch(function(e) { assertEquals(ECode.NO_SUCH_WINDOW, e.code); assertEquals('window not found', e.message); - return true; // Suppress the error. - })); + }); - testHelper.execute(); + return waitForIdle(); } function testNestedCommandFailuresCanBeCaughtAndSuppressed() { - var testHelper = TestHelper.expectingSuccess(). + 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'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). expect(CName.CLOSE). replayAll(); @@ -914,20 +937,19 @@ function testNestedCommandFailuresCanBeCaughtAndSuppressed() { then(function() { return driver.switchTo().window('foo'); }). - thenCatch(goog.functions.TRUE); + thenCatch(goog.nullFunction); driver.close(); }); - // Let the mock verify everything. - testHelper.execute(); + return waitForIdle(); } function testReturningADeferredResultFromACallback() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.GET_TITLE). expect(CName.GET_CURRENT_URL). - andReturnSuccess('http://www.google.com'). + andReturnSuccess('http://www.google.com'). replayAll(); var driver = testHelper.createDriver(); @@ -938,19 +960,17 @@ function testReturningADeferredResultFromACallback() { then(function(value) { assertEquals('http://www.google.com', value); }); - testHelper.execute(); + return waitForIdle(); } function testReturningADeferredResultFromAnErrbackSuppressesTheError() { var count = 0; - var testHelper = TestHelper.expectingSuccess(function() { - assertEquals(2, count); - }). + var testHelper = new TestHelper(). expect(CName.SWITCH_TO_WINDOW, {'name':'foo'}). - andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). expect(CName.GET_CURRENT_URL). - andReturnSuccess('http://www.google.com'). + andReturnSuccess('http://www.google.com'). replayAll(); var driver = testHelper.createDriver(); @@ -965,31 +985,25 @@ function testReturningADeferredResultFromAnErrbackSuppressesTheError() { count += 1; assertEquals('http://www.google.com', url); }); - testHelper.execute(); + return waitForIdle().then(function() { + assertEquals(2, count); + }); } function testExecutingACustomFunctionThatReturnsANonDeferred() { - var called = false; - var testHelper = TestHelper.expectingSuccess(function() { - assertTrue('Callback not called', called); - }). - replayAll(); + var testHelper = new TestHelper().replayAll(); var driver = testHelper.createDriver(); - driver.call(goog.functions.constant('abc123')).then(function(value) { - called = true; + return driver.call(goog.functions.constant('abc123')).then(function(value) { assertEquals('abc123', value); }); - testHelper.execute(); } function testExecutionOrderwithCustomFunctions() { var msg = []; - var testHelper = TestHelper.expectingSuccess(function() { - assertEquals('cheese is tasty!', msg.join('')); - }). + var testHelper = new TestHelper(). expect(CName.GET_TITLE).andReturnSuccess('cheese '). expect(CName.GET_CURRENT_URL).andReturnSuccess('tasty'). replayAll(); @@ -1002,44 +1016,35 @@ function testExecutionOrderwithCustomFunctions() { driver.getCurrentUrl().then(pushMsg); driver.call(goog.functions.constant('!')).then(pushMsg); - testHelper.execute(); + return waitForIdle().then(function() { + assertEquals('cheese is tasty!', msg.join('')); + }); } function testPassingArgumentsToACustomFunction() { - var testHelper = TestHelper.expectingSuccess().replayAll(); + var testHelper = new TestHelper().replayAll(); - var add = callbackHelper(function(a, b) { + var add = function(a, b) { return a + b; - }); + }; var driver = testHelper.createDriver(); - driver.call(add, null, 1, 2). - then(function(value) { - assertEquals(3, value); - }); - testHelper.execute(); - add.assertCalled(); + return driver.call(add, null, 1, 2).then(function(value) { + assertEquals(3, value); + }); } function testPassingPromisedArgumentsToACustomFunction() { - var testHelper = TestHelper.expectingSuccess().replayAll(); + var testHelper = new TestHelper().replayAll(); - var promisedArg = new webdriver.promise.Deferred; - var add = callbackHelper(function(a, b) { + var promisedArg = webdriver.promise.fulfilled(2); + var add = function(a, b) { return a + b; - }); + }; var driver = testHelper.createDriver(); - driver.call(add, null, 1, promisedArg). - then(function(value) { - assertEquals(3, value); - }); - - flowTester.turnEventLoop(); - add.assertNotCalled(); - - promisedArg.fulfill(2); - add.assertCalled(); - testHelper.execute(); + return driver.call(add, null, 1, promisedArg).then(function(value) { + assertEquals(3, value); + }); } function testPassingArgumentsAndScopeToACustomFunction() { @@ -1051,39 +1056,26 @@ function testPassingArgumentsAndScopeToACustomFunction() { }; var foo = new Foo('foo'); - var called = false; - var testHelper = TestHelper.expectingSuccess(function() { - assertTrue('Callback not called', called); - }). - replayAll(); + var testHelper = new TestHelper().replayAll(); var driver = testHelper.createDriver(); - driver.call(foo.getName, foo).then(function(value) { + return driver.call(foo.getName, foo).then(function(value) { assertEquals('foo', value); - called = true; }); - testHelper.execute(); } function testExecutingACustomFunctionThatThrowsAnError() { - var called = false; - var testHelper = TestHelper.expectingSuccess(function() { - assertTrue('Callback not called', called); - }). - replayAll(); + var testHelper = new TestHelper().replayAll(); var driver = testHelper.createDriver(); - driver.call(goog.functions.error('bam!')).thenCatch(function(e) { + return driver.call(goog.functions.error('bam!')).then(fail, function(e) { assertTrue(e instanceof Error); assertEquals('bam!', e.message); - called = true; - return true; // suppress the error. }); - testHelper.execute(); } function testExecutingACustomFunctionThatSchedulesCommands() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.GET_TITLE). expect(CName.CLOSE). expect(CName.QUIT). @@ -1095,92 +1087,78 @@ function testExecutingACustomFunctionThatSchedulesCommands() { driver.close(); }); driver.quit(); - testHelper.execute(); + return waitForIdle(); } function testExecutingAFunctionThatReturnsATaskResultAfterSchedulingAnother() { - var called = false; - var testHelper = TestHelper.expectingSuccess(function() { - assertTrue(called); - }). + var testHelper = new TestHelper(). expect(CName.GET_TITLE). andReturnSuccess('Google Search'). expect(CName.CLOSE). replayAll(); var driver = testHelper.createDriver(); - var result = driver.call(function() { + return driver.call(function() { var title = driver.getTitle(); driver.close(); return title; - }); - - result.then(function(title) { - called = true; + }).then(function(title) { assertEquals('Google Search', title); }); - - testHelper.execute(); } function testExecutingACustomFunctionWhoseNestedCommandFails() { - var called = false; - var testHelper = TestHelper.expectingSuccess(function() { - assertTrue('Callback not called', called); - }). + var testHelper = new TestHelper(). expect(CName.SWITCH_TO_WINDOW, {'name': 'foo'}). - andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). replayAll(); var driver = testHelper.createDriver(); - var result = driver.call(function() { + return driver.call(function() { return driver.switchTo().window('foo'); - }); - - result.thenCatch(function(e) { + }).then(fail, function(e) { assertEquals(ECode.NO_SUCH_WINDOW, e.code); assertEquals('window not found', e.message); - called = true; - return true; // suppress the error. }); - - testHelper.execute(); } function testCustomFunctionDoesNotCompleteUntilReturnedPromiseIsResolved() { - var testHelper = TestHelper.expectingSuccess().replayAll(); - - var d = new webdriver.promise.Deferred(), - stepOne = callbackHelper(function() { return d.promise; }), - stepTwo = callbackHelper(); + var testHelper = new TestHelper().replayAll(); + var order = []; var driver = testHelper.createDriver(); - driver.call(stepOne); - driver.call(stepTwo); - flowTester.turnEventLoop(); - stepOne.assertCalled(); - stepTwo.assertNotCalled(); + var d = webdriver.promise.defer(); + d.promise.then(function() { + order.push('b'); + }); - flowTester.turnEventLoop(); - stepOne.assertCalled(); - stepTwo.assertNotCalled(); + driver.call(function() { + order.push('a'); + return d.promise; + }); + driver.call(function() { + order.push('c'); + }); - d.fulfill(); - testHelper.execute(); - stepTwo.assertCalled(); + // timeout to ensure the first function starts its execution before we + // trigger d's callbacks. + return 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 = TestHelper.expectingSuccess(function() { - assertEquals('acefdb', msg.join('')); - }). - replayAll(); + var testHelper = new TestHelper().replayAll(); var driver = testHelper.createDriver(); driver.call(msg.push, msg, 'a'); @@ -1193,16 +1171,15 @@ function testNestedFunctionCommandExecutionOrder() { driver.call(msg.push, msg, 'd'); }); driver.call(msg.push, msg, 'b'); - testHelper.execute(); + return waitForIdle().then(function() { + assertEquals('acefdb', msg.join('')); + }); } function testExecutingNestedFunctionCommands() { var msg = []; - var testHelper = TestHelper.expectingSuccess(function() { - assertEquals('cheese is tasty!', msg.join('')); - }). - replayAll(); + var testHelper = new TestHelper().replayAll(); var driver = testHelper.createDriver(); var pushMsg = goog.bind(msg.push, msg); driver.call(goog.functions.constant('cheese ')).then(pushMsg); @@ -1211,35 +1188,28 @@ function testExecutingNestedFunctionCommands() { driver.call(goog.functions.constant('tasty')).then(pushMsg); }); driver.call(goog.functions.constant('!')).then(pushMsg); - testHelper.execute(); + return waitForIdle().then(function() { + assertEquals('cheese is tasty!', msg.join('')); + }); } function testReturnValuesFromNestedFunctionCommands() { - var count = 0; - var testHelper = TestHelper.expectingSuccess(function() { - assertEquals('not called', 1, count); - }). - replayAll(); + var testHelper = new TestHelper().replayAll(); var driver = testHelper.createDriver(); - var result = driver.call(function() { + return driver.call(function() { return driver.call(function() { return driver.call(goog.functions.constant('foobar')); }); - }); - result.then(function(value) { + }).then(function(value) { assertEquals('foobar', value); - count += 1; }); - testHelper.execute(); } function testExecutingANormalCommandAfterNestedCommandsThatReturnsAnAction() { var msg = []; - var testHelper = TestHelper.expectingSuccess(function() { - assertEquals('ab', msg.join('')); - }). + var testHelper = new TestHelper(). expect(CName.CLOSE). replayAll(); var driver = testHelper.createDriver(); @@ -1252,27 +1222,38 @@ function testExecutingANormalCommandAfterNestedCommandsThatReturnsAnAction() { driver.close().then(function() { msg.push('b'); }); + return waitForIdle().then(function() { + assertEquals('ab', msg.join('')); + }); +} + - testHelper.execute(); +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() { - var testHelper = TestHelper. - expectingFailure(expectedError(undefined, 'bam!')). - replayAll(); +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!')); }); }); - testHelper.execute(); + return waitForAbort().then(expectedError(undefined, 'bam!')); } function testExecutingNestedCustomFunctionsThatSchedulesCommands() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.GET_TITLE). expect(CName.CLOSE). replayAll(); @@ -1284,12 +1265,12 @@ function testExecutingNestedCustomFunctionsThatSchedulesCommands() { }); driver.close(); }); - testHelper.execute(); + return waitForIdle(); } function testExecutingACustomFunctionThatReturnsADeferredAction() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.GET_TITLE).andReturnSuccess('Google'). replayAll(); @@ -1299,71 +1280,50 @@ function testExecutingACustomFunctionThatReturnsADeferredAction() { }).then(function(title) { assertEquals('Google', title); }); - - testHelper.execute(); + return waitForIdle(); } function testWebElementPromise_resolvesWhenUnderlyingElementDoes() { var el = new webdriver.WebElement(STUB_DRIVER, {'ELEMENT': 'foo'}); - var d = webdriver.promise.defer(); - var callback; - new webdriver.WebElementPromise(STUB_DRIVER, d.promise).then( - callback = callbackHelper(function(e) { + var promise = webdriver.promise.fulfilled(el); + return new webdriver.WebElementPromise(STUB_DRIVER, promise). + then(function(e) { assertEquals(e, el); - })); - callback.assertNotCalled(); - d.fulfill(el); - callback.assertCalled(); - verifyAll(); // Make tearDown happy. + }); } function testWebElement_resolvesBeforeCallbacksOnWireValueTrigger() { var el = new webdriver.promise.Deferred(); - var callback, idCallback; var element = new webdriver.WebElementPromise(STUB_DRIVER, el.promise); var messages = []; - webdriver.promise.when(element, function() { + element.then(function() { messages.push('element resolved'); }); - - webdriver.promise.when(element.getId(), function() { + element.getId().then(function() { messages.push('wire value resolved'); }); assertArrayEquals([], messages); el.fulfill(new webdriver.WebElement(STUB_DRIVER, {'ELEMENT': 'foo'})); - assertArrayEquals([ - 'element resolved', - 'wire value resolved' - ], messages); - verifyAll(); // Make tearDown happy. + return waitForIdle().then(function() { + assertArrayEquals([ + 'element resolved', + 'wire value resolved' + ], messages); + }); } function testWebElement_isRejectedIfUnderlyingIdIsRejected() { - var id = new webdriver.promise.Deferred(); - - var callback, errback; - var element = new webdriver.WebElementPromise(STUB_DRIVER, id.promise); - - webdriver.promise.when(element, - callback = callbackHelper(), - errback = callbackHelper(assertIsStubError)); - - callback.assertNotCalled(); - errback.assertNotCalled(); - - id.reject(STUB_ERROR); - - callback.assertNotCalled(); - errback.assertCalled(); - verifyAll(); // Make tearDown happy. + var element = new webdriver.WebElementPromise( + STUB_DRIVER, webdriver.promise.rejected(new StubError)); + return element.then(fail, assertIsStubError); } function testExecuteScript_nullReturnValue() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT). withParameters({ 'script': 'return document.body;', @@ -1373,18 +1333,14 @@ function testExecuteScript_nullReturnValue() { replayAll(); var driver = testHelper.createDriver(); - var callback; - driver.executeScript('return document.body;'). - then(callback = callbackHelper(function(result) { - assertNull(result); - })); - testHelper.execute(); - callback.assertCalled(); + return driver.executeScript('return document.body;').then(function(result) { + assertNull(result); + }); } function testExecuteScript_primitiveReturnValue() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT). withParameters({ 'script': 'return document.body;', @@ -1394,13 +1350,9 @@ function testExecuteScript_primitiveReturnValue() { replayAll(); var driver = testHelper.createDriver(); - var callback; - driver.executeScript('return document.body;'). - then(callback = callbackHelper(function(result) { - assertEquals(123, result); - })); - testHelper.execute(); - callback.assertCalled(); + return driver.executeScript('return document.body;').then(function(result) { + assertEquals(123, result); + }); } @@ -1408,7 +1360,7 @@ function testExecuteScript_webElementReturnValue() { var json = {}; json[webdriver.WebElement.ELEMENT_KEY] = 'foo'; - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT). withParameters({ 'script': 'return document.body;', @@ -1418,16 +1370,12 @@ function testExecuteScript_webElementReturnValue() { replayAll(); var driver = testHelper.createDriver(); - var callback; - driver.executeScript('return document.body;'). + return driver.executeScript('return document.body;'). then(function(webelement) { - webdriver.promise.when(webelement.id_, - callback = callbackHelper(function(id) { - webdriver.test.testutil.assertObjectEquals(id, json); - })); + return webdriver.promise.when(webelement.id_, function(id) { + webdriver.test.testutil.assertObjectEquals(id, json); + }); }); - testHelper.execute(); - callback.assertCalled(); } @@ -1435,7 +1383,7 @@ function testExecuteScript_arrayReturnValue() { var json = [{}]; json[0][webdriver.WebElement.ELEMENT_KEY] = 'foo'; - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT). withParameters({ 'script': 'return document.body;', @@ -1445,16 +1393,12 @@ function testExecuteScript_arrayReturnValue() { replayAll(); var driver = testHelper.createDriver(); - var callback; - driver.executeScript('return document.body;'). + return driver.executeScript('return document.body;'). then(function(array) { - webdriver.promise.when(array[0].id_, - callback = callbackHelper(function(id) { - webdriver.test.testutil.assertObjectEquals(id, json[0]); - })); + return webdriver.promise.when(array[0].id_, function(id) { + webdriver.test.testutil.assertObjectEquals(id, json[0]); + }); }); - testHelper.execute(); - callback.assertCalled(); } @@ -1462,7 +1406,7 @@ function testExecuteScript_objectReturnValue() { var json = {'foo':{}}; json['foo'][webdriver.WebElement.ELEMENT_KEY] = 'foo'; - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT). withParameters({ 'script': 'return document.body;', @@ -1473,20 +1417,17 @@ function testExecuteScript_objectReturnValue() { var driver = testHelper.createDriver(); var callback; - driver.executeScript('return document.body;'). + return driver.executeScript('return document.body;'). then(function(obj) { - webdriver.promise.when(obj['foo'].id_, - callback = callbackHelper(function(id) { - webdriver.test.testutil.assertObjectEquals(id, json['foo']); - })); + return webdriver.promise.when(obj['foo'].id_, function(id) { + webdriver.test.testutil.assertObjectEquals(id, json['foo']); + }); }); - testHelper.execute(); - callback.assertCalled(); } function testExecuteScript_scriptAsFunction() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT). withParameters({ 'script': 'return (' + goog.nullFunction + @@ -1497,13 +1438,12 @@ function testExecuteScript_scriptAsFunction() { replayAll(); var driver = testHelper.createDriver(); - driver.executeScript(goog.nullFunction); - testHelper.execute(); + return driver.executeScript(goog.nullFunction); } function testExecuteScript_simpleArgumentConversion() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT). withParameters({ 'script': 'return 1;', @@ -1513,8 +1453,8 @@ function testExecuteScript_simpleArgumentConversion() { replayAll(); var driver = testHelper.createDriver(); - driver.executeScript('return 1;', 'abc', 123, true, [123, {'foo': 'bar'}]); - testHelper.execute(); + return driver.executeScript( + 'return 1;', 'abc', 123, true, [123, {'foo': 'bar'}]); } @@ -1522,7 +1462,7 @@ function testExecuteScript_webElementArgumentConversion() { var elementJson = {}; elementJson[webdriver.WebElement.ELEMENT_KEY] = 'fefifofum'; - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT). withParameters({ 'script': 'return 1;', @@ -1532,9 +1472,28 @@ function testExecuteScript_webElementArgumentConversion() { replayAll(); var driver = testHelper.createDriver(); - driver.executeScript('return 1;', + return driver.executeScript('return 1;', new webdriver.WebElement(driver, elementJson)); - testHelper.execute(); +} + + +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); } @@ -1542,7 +1501,7 @@ function testExecuteScript_argumentConversion() { var elementJson = {}; elementJson[webdriver.WebElement.ELEMENT_KEY] = 'fefifofum'; - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT). withParameters({ 'script': 'return 1;', @@ -1553,15 +1512,13 @@ function testExecuteScript_argumentConversion() { var driver = testHelper.createDriver(); var element = new webdriver.WebElement(driver, elementJson); - driver.executeScript('return 1;', + return driver.executeScript('return 1;', 'abc', 123, true, element, [123, {'foo': 'bar'}]); - testHelper.execute(); } function testExecuteScript_scriptReturnsAnError() { - var testHelper = TestHelper. - expectingFailure(expectedError(ECode.UNKNOWN_ERROR, 'bam')). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT). withParameters({ 'script': 'throw Error(arguments[0]);', @@ -1570,44 +1527,37 @@ function testExecuteScript_scriptReturnsAnError() { andReturnError(ECode.UNKNOWN_ERROR, {'message':'bam'}). replayAll(); var driver = testHelper.createDriver(); - driver.executeScript('throw Error(arguments[0]);', 'bam'); - testHelper.execute(); + return driver.executeScript('throw Error(arguments[0]);', 'bam'). + then(fail, expectedError(ECode.UNKNOWN_ERROR, 'bam')); } function testExecuteScript_failsIfArgumentIsARejectedPromise() { - var testHelper = TestHelper.expectingSuccess().replayAll(); - - var callback = callbackHelper(assertIsStubError); + var testHelper = new TestHelper().replayAll(); - var arg = webdriver.promise.rejected(STUB_ERROR); + var arg = webdriver.promise.rejected(new StubError); arg.thenCatch(goog.nullFunction); // Suppress default handler. var driver = testHelper.createDriver(); - driver.executeScript(goog.nullFunction, arg).thenCatch(callback); - testHelper.execute(); - callback.assertCalled(); + return driver.executeScript(goog.nullFunction, arg). + then(fail, assertIsStubError); } function testExecuteAsyncScript_failsIfArgumentIsARejectedPromise() { - var testHelper = TestHelper.expectingSuccess().replayAll(); + var testHelper = new TestHelper().replayAll(); - var callback = callbackHelper(assertIsStubError); - - var arg = webdriver.promise.rejected(STUB_ERROR); + var arg = webdriver.promise.rejected(new StubError); arg.thenCatch(goog.nullFunction); // Suppress default handler. var driver = testHelper.createDriver(); - driver.executeAsyncScript(goog.nullFunction, arg).thenCatch(callback); - testHelper.execute(); - callback.assertCalled(); + return driver.executeAsyncScript(goog.nullFunction, arg). + then(fail, assertIsStubError); } function testFindElement_elementNotFound() { - var testHelper = TestHelper. - expectingFailure(expectedError(ECode.NO_SUCH_ELEMENT, 'Unable to find element')). + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). andReturnError(ECode.NO_SUCH_ELEMENT, { 'message':'Unable to find element' @@ -1617,14 +1567,13 @@ function testFindElement_elementNotFound() { var driver = testHelper.createDriver(); var element = driver.findElement(By.id('foo')); element.click(); // This should never execute. - testHelper.execute(); + return waitForAbort().then( + expectedError(ECode.NO_SUCH_ELEMENT, 'Unable to find element')); } function testFindElement_elementNotFoundInACallback() { - var testHelper = TestHelper. - expectingFailure( - expectedError(ECode.NO_SUCH_ELEMENT, 'Unable to find element')). + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). andReturnError( ECode.NO_SUCH_ELEMENT, {'message':'Unable to find element'}). @@ -1635,12 +1584,13 @@ function testFindElement_elementNotFoundInACallback() { var element = driver.findElement(By.id('foo')); return element.click(); // Should not execute. }); - testHelper.execute(); + return waitForAbort().then( + expectedError(ECode.NO_SUCH_ELEMENT, 'Unable to find element')); } function testFindElement_elementFound() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). andReturnSuccess({'ELEMENT':'bar'}). expect(CName.CLICK_ELEMENT, {'id':{'ELEMENT':'bar'}}). @@ -1650,12 +1600,12 @@ function testFindElement_elementFound() { var driver = testHelper.createDriver(); var element = driver.findElement(By.id('foo')); element.click(); - testHelper.execute(); + return waitForIdle(); } function testFindElement_canUseElementInCallback() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). andReturnSuccess({'ELEMENT':'bar'}). expect(CName.CLICK_ELEMENT, {'id':{'ELEMENT':'bar'}}). @@ -1666,12 +1616,12 @@ function testFindElement_canUseElementInCallback() { driver.findElement(By.id('foo')).then(function(element) { element.click(); }); - testHelper.execute(); + return waitForIdle(); } function testFindElement_byJs() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT, { 'script': 'return document.body', 'args': [] @@ -1683,17 +1633,12 @@ function testFindElement_byJs() { var driver = testHelper.createDriver(); var element = driver.findElement(By.js('return document.body')); element.click(); // just to make sure - testHelper.execute(); + return waitForIdle(); } function testFindElement_byJs_returnsNonWebElementValue() { - var testHelper = TestHelper. - expectingFailure(function(e) { - assertEquals( - 'Not the expected error message', - 'Custom locator did not return a WebElement', e.message); - }). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT, {'script': 'return 123', 'args': []}). andReturnSuccess(123). replayAll(); @@ -1701,13 +1646,17 @@ function testFindElement_byJs_returnsNonWebElementValue() { var driver = testHelper.createDriver(); var element = driver.findElement(By.js('return 123')); element.click(); // Should not execute. - testHelper.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 = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT, { 'script': script, 'args': ['div'] @@ -1716,12 +1665,12 @@ function testFindElement_byJs_canPassArguments() { replayAll(); var driver = testHelper.createDriver(); driver.findElement(By.js(script, 'div')); - testHelper.execute(); + return waitForIdle(); } function testFindElement_customLocator() { - var testHelper = TestHelper.expectingSuccess(). + 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'}}). @@ -1734,92 +1683,74 @@ function testFindElement_customLocator() { return d.findElements(By.tagName('a')); }); element.click(); - testHelper.execute(); + return waitForIdle(); } function testFindElement_customLocatorThrowsIfResultIsNotAWebElement() { - var testHelper = TestHelper. - expectingFailure(function(e) { - assertEquals( - 'Not the expected error message', - 'Custom locator did not return a WebElement', e.message); - }). - replayAll(); + var testHelper = new TestHelper().replayAll(); var driver = testHelper.createDriver(); driver.findElement(function() { return 1; }); - testHelper.execute(); + 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 = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). andReturnSuccess([]). replayAll(); var driver = testHelper.createDriver(); - var callback; - driver.isElementPresent(By.id('foo')). - then(callback = callbackHelper(function(result) { - assertFalse(result); - })); - testHelper.execute(); - callback.assertCalled(); + return driver.isElementPresent(By.id('foo')).then(assertFalse); } function testIsElementPresent_elementFound() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). andReturnSuccess([{'ELEMENT':'bar'}]). replayAll(); var driver = testHelper.createDriver(); - var callback; - driver.isElementPresent(By.id('foo')). - then(callback = callbackHelper(assertTrue)); - testHelper.execute(); - callback.assertCalled(); + return driver.isElementPresent(By.id('foo')).then(assertTrue); } function testIsElementPresent_letsErrorsPropagate() { - var testHelper = TestHelper. - expectingFailure(expectedError(ECode.UNKNOWN_ERROR, 'There is no spoon')). + 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')); - testHelper.execute(); + return waitForAbort().then( + expectedError(ECode.UNKNOWN_ERROR, 'There is no spoon')); } function testIsElementPresent_byJs() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT, {'script': 'return 123', 'args': []}). andReturnSuccess([{'ELEMENT':'bar'}]). replayAll(); var driver = testHelper.createDriver(); - var callback; - driver.isElementPresent(By.js('return 123')). - then(callback = callbackHelper(function(result) { - assertTrue(result); - })); - testHelper.execute(); - callback.assertCalled(); + return driver.isElementPresent(By.js('return 123')).then(assertTrue); } function testIsElementPresent_byJs_canPassScriptArguments() { var script = 'return document.getElementsByTagName(arguments[0]);'; - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT, { 'script': script, 'args': ['div'] @@ -1829,7 +1760,7 @@ function testIsElementPresent_byJs_canPassScriptArguments() { var driver = testHelper.createDriver(); driver.isElementPresent(By.js(script, 'div')); - testHelper.execute(); + return waitForIdle(); } @@ -1839,35 +1770,30 @@ function testFindElements() { {'ELEMENT':'bar'}, {'ELEMENT':'baz'} ]; - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENTS, {'using':'tag name', 'value':'a'}). andReturnSuccess(json). replayAll(); var driver = testHelper.createDriver(); - var callbacks = []; 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); - elements[index].getId(). - then(callbacks[index] = callbackHelper(function(id) { - webdriver.test.testutil.assertObjectEquals(json[index], id); - })); + callbacks[index] = elements[index].getId().then(function(id) { + webdriver.test.testutil.assertObjectEquals(json[index], id); + }); } - - assertTypeAndId(0); - assertTypeAndId(1); - assertTypeAndId(2); }); - - testHelper.execute(); - assertEquals(3, callbacks.length); - callbacks[0].assertCalled(); - callbacks[1].assertCalled(); - callbacks[2].assertCalled(); } @@ -1877,7 +1803,7 @@ function testFindElements_byJs() { {'ELEMENT':'bar'}, {'ELEMENT':'baz'} ]; - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT, { 'script': 'return document.getElementsByTagName("div");', 'args': [] @@ -1886,30 +1812,26 @@ function testFindElements_byJs() { replayAll(); var driver = testHelper.createDriver(); - var callbacks = []; - driver.findElements(By.js('return document.getElementsByTagName("div");')). + + 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); - elements[index].getId(). - then(callbacks[index] = callbackHelper(function(id) { - webdriver.test.testutil.assertObjectEquals(json[index], id); - })); + callbacks[index] = elements[index].getId().then(function(id) { + webdriver.test.testutil.assertObjectEquals(json[index], id); + }); } - - assertTypeAndId(0); - assertTypeAndId(1); - assertTypeAndId(2); }); - - testHelper.execute(); - assertEquals(3, callbacks.length); - callbacks[0].assertCalled(); - callbacks[1].assertCalled(); - callbacks[2].assertCalled(); } @@ -1923,7 +1845,7 @@ function testFindElements_byJs_filtersOutNonWebElementResponses() { {'not a web element': 1}, {'ELEMENT':'baz'} ]; - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT, { 'script': 'return document.getElementsByTagName("div");', 'args': [] @@ -1932,36 +1854,30 @@ function testFindElements_byJs_filtersOutNonWebElementResponses() { replayAll(); var driver = testHelper.createDriver(); - var callbacks = []; 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); - elements[index].getId(). - then(callbacks[index] = callbackHelper(function(id) { - webdriver.test.testutil.assertObjectEquals(json[jsonIndex], id); - })); + callbacks[index] = elements[index].getId().then(function(id) { + webdriver.test.testutil.assertObjectEquals(json[jsonIndex], id); + }); } - - assertTypeAndId(0, 0); - assertTypeAndId(1, 4); - assertTypeAndId(2, 6); }); - - testHelper.execute(); - assertEquals(3, callbacks.length); - callbacks[0].assertCalled(); - callbacks[1].assertCalled(); - callbacks[2].assertCalled(); + return waitForIdle(); } function testFindElements_byJs_convertsSingleWebElementResponseToArray() { var json = {'ELEMENT':'foo'}; - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT, { 'script': 'return document.getElementsByTagName("div");', 'args': [] @@ -1970,26 +1886,21 @@ function testFindElements_byJs_convertsSingleWebElementResponseToArray() { replayAll(); var driver = testHelper.createDriver(); - var callback1, callback2; - driver.findElements(By.js('return document.getElementsByTagName("div");')). - then(callback1 = callbackHelper(function(elements) { + return driver. + findElements(By.js('return document.getElementsByTagName("div");')). + then(function(elements) { assertEquals(1, elements.length); assertTrue(elements[0] instanceof webdriver.WebElement); - elements[0].getId(). - then(callback2 = callbackHelper(function(id) { - webdriver.test.testutil.assertObjectEquals(json, id); - })); - })); - - testHelper.execute(); - callback1.assertCalled(); - callback2.assertCalled(); + 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 = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.EXECUTE_SCRIPT, { 'script': script, 'args': ['div'] @@ -1999,13 +1910,12 @@ function testFindElements_byJs_canPassScriptArguments() { var driver = testHelper.createDriver(); driver.findElements(By.js(script, 'div')); - testHelper.execute(); + return waitForIdle(); } function testSendKeysConvertsVarArgsIntoStrings_simpleArgs() { - var testHelper = TestHelper. - expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.SEND_KEYS_TO_ELEMENT, {'id':{'ELEMENT':'one'}, 'value':['1','2','abc','3']}). andReturnSuccess(). @@ -2014,13 +1924,12 @@ function testSendKeysConvertsVarArgsIntoStrings_simpleArgs() { var driver = testHelper.createDriver(); var element = new webdriver.WebElement(driver, {'ELEMENT': 'one'}); element.sendKeys(1, 2, 'abc', 3); - testHelper.execute(); + return waitForIdle(); } function testSendKeysConvertsVarArgsIntoStrings_promisedArgs() { - var testHelper = TestHelper. - expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). andReturnSuccess({'ELEMENT':'one'}). expect(CName.SEND_KEYS_TO_ELEMENT, {'id':{'ELEMENT':'one'}, @@ -2033,120 +1942,135 @@ function testSendKeysConvertsVarArgsIntoStrings_promisedArgs() { element.sendKeys( webdriver.promise.fulfilled('abc'), 123, webdriver.promise.fulfilled('def')); - testHelper.execute(); + 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'); - var callback; - webdriver.WebElement.equals(a, a).then( - callback = callbackHelper(assertTrue)); - callback.assertCalled(); - verifyAll(); // for tearDown() + 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'), - callback; - - webdriver.WebElement.equals(a, b).then( - callback = callbackHelper(assertTrue)); - callback.assertCalled('a should == b!'); - webdriver.WebElement.equals(b, a).then( - callback = callbackHelper(assertTrue)); - callback.assertCalled('symmetry check failed'); - webdriver.WebElement.equals(a, c).then( - callback = callbackHelper(assertTrue)); - callback.assertCalled('a should == c!'); - webdriver.WebElement.equals(b, c).then( - callback = callbackHelper(assertTrue)); - callback.assertCalled('transitive check failed'); - - verifyAll(); // for tearDown() + 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 = TestHelper. - expectingSuccess(). + 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), - callback; - - webdriver.WebElement.equals(a, b).then( - callback = callbackHelper(assertTrue)); - - testHelper.execute(); - callback.assertCalled(); + b = new webdriver.WebElement(driver, id2); + return webdriver.WebElement.equals(a, b).then(assertTrue); } function testElementEquals_failsIfAnInputElementCouldNotBeFound() { - var testHelper = TestHelper.expectingSuccess().replayAll(); + var testHelper = new TestHelper().replayAll(); - var callback = callbackHelper(assertIsStubError); - var id = webdriver.promise.rejected(STUB_ERROR); + 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); - webdriver.WebElement.equals(a, b).thenCatch(callback); - testHelper.execute(); - callback.assertCalled(); + return webdriver.WebElement.equals(a, b).then(fail, assertIsStubError); } + function testWaiting_waitSucceeds() { - var testHelper = TestHelper.expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). - andReturnSuccess([]). - expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). - andReturnSuccess([]). + andReturnSuccess([]). + times(2). expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). - andReturnSuccess([{'ELEMENT':'bar'}]). + andReturnSuccess([{'ELEMENT':'bar'}]). replayAll(); var driver = testHelper.createDriver(); driver.wait(function() { return driver.isElementPresent(By.id('foo')); }, 200); - testHelper.execute(); + return waitForIdle(); } -function testWaiting_waitTimesout() { - var testHelper = TestHelper. - expectingFailure(function(e) { - assertEquals('Wait timed out after ', - e.message.substring(0, 'Wait timed out after '.length)); - }). +function testWaiting_waitTimesout_timeoutCaught() { + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). - andReturnSuccess([]). - expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). - andReturnSuccess([]). + 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([]). + andReturnSuccess([]). + anyTimes(). replayAll(); var driver = testHelper.createDriver(); driver.wait(function() { return driver.isElementPresent(By.id('foo')); - }, 200); - testHelper.execute(); + }, 25); + return waitForAbort().then(function(e) { + assertEquals('Wait timed out after ', + e.message.substring(0, 'Wait timed out after '.length)); + }); } function testInterceptsAndTransformsUnhandledAlertErrors() { - var testHelper = TestHelper. - expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). andReturnError(ECode.UNEXPECTED_ALERT_OPEN, { 'message': 'boom', @@ -2154,39 +2078,25 @@ function testInterceptsAndTransformsUnhandledAlertErrors() { }). replayAll(); - var pair = callbackPair(null, function(e) { + var driver = testHelper.createDriver(); + return driver.findElement(By.id('foo')).then(fail, function(e) { assertTrue(e instanceof webdriver.UnhandledAlertError); - - var pair = callbackPair(goog.partial(assertEquals, 'hello')); - e.getAlert().getText().then(pair.callback, pair.errback); - pair.assertCallback(); + assertEquals('hello', e.getAlertText()); }); - - var driver = testHelper.createDriver(); - driver.findElement(By.id('foo')).then(pair.callback, pair.errback); - testHelper.execute(); - pair.assertErrback(); } -function testUnhandledAlertErrors_usesEmptyStringIfAlertTextOmittedFromResponse() { - var testHelper = TestHelper. - expectingSuccess(). +function +testUnhandledAlertErrors_usesEmptyStringIfAlertTextOmittedFromResponse() { + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). andReturnError(ECode.UNEXPECTED_ALERT_OPEN, {'message': 'boom'}). replayAll(); - var pair = callbackPair(null, function(e) { + var driver = testHelper.createDriver(); + return driver.findElement(By.id('foo')).then(fail, function(e) { assertTrue(e instanceof webdriver.UnhandledAlertError); - - var pair = callbackPair(goog.partial(assertEquals, '')); - e.getAlert().getText().then(pair.callback, pair.errback); - pair.assertCallback(); + assertEquals('', e.getAlertText()); }); - - var driver = testHelper.createDriver(); - driver.findElement(By.id('foo')).then(pair.callback, pair.errback); - testHelper.execute(); - pair.assertErrback(); } function testAlertHandleResolvesWhenPromisedTextResolves() { @@ -2195,30 +2105,22 @@ function testAlertHandleResolvesWhenPromisedTextResolves() { var alert = new webdriver.AlertPromise(STUB_DRIVER, promise); assertTrue(alert.isPending()); - var callback; - webdriver.promise.when(alert.getText(), - callback = callbackHelper(function(text) { - assertEquals('foo', text); - })); - - callback.assertNotCalled(); - promise.fulfill(new webdriver.Alert(STUB_DRIVER, 'foo')); - - callback.assertCalled(); - verifyAll(); // Make tearDown happy. + return alert.getText().then(function(text) { + assertEquals('foo', text); + }); } function testWebElementsBelongToSameFlowAsParentDriver() { - var testHelper = TestHelper - .expectingSuccess() + var testHelper = new TestHelper() .expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}) .andReturnSuccess({'ELEMENT': 'abc123'}) .replayAll(); var driver = testHelper.createDriver(); - webdriver.promise.createFlow(function() { + 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', @@ -2226,13 +2128,16 @@ function testWebElementsBelongToSameFlowAsParentDriver() { }); }); - testHelper.execute(); + assertNotEquals(otherFlow, driver.controlFlow); + return goog.Promise.all([ + waitForIdle(otherFlow), + waitForIdle(driver.controlFlow()) + ]); } function testSwitchToAlertThatIsNotPresent() { - var testHelper = TestHelper - .expectingFailure(expectedError(ECode.NO_SUCH_ALERT, 'no alert')) + var testHelper = new TestHelper() .expect(CName.GET_ALERT_TEXT) .andReturnError(ECode.NO_SUCH_ALERT, {'message': 'no alert'}) .replayAll(); @@ -2240,18 +2145,18 @@ function testSwitchToAlertThatIsNotPresent() { var driver = testHelper.createDriver(); var alert = driver.switchTo().alert(); alert.dismiss(); // Should never execute. - testHelper.execute(); + return waitForAbort().then(expectedError(ECode.NO_SUCH_ALERT, 'no alert')); } function testAlertsBelongToSameFlowAsParentDriver() { - var testHelper = TestHelper - .expectingSuccess() + var testHelper = new TestHelper() .expect(CName.GET_ALERT_TEXT).andReturnSuccess('hello') .replayAll(); var driver = testHelper.createDriver(); - webdriver.promise.createFlow(function() { + 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', @@ -2259,12 +2164,15 @@ function testAlertsBelongToSameFlowAsParentDriver() { }); }); - testHelper.execute(); + assertNotEquals(otherFlow, driver.controlFlow); + return goog.Promise.all([ + waitForIdle(otherFlow), + waitForIdle(driver.controlFlow()) + ]); } function testFetchingLogs() { - var testHelper = TestHelper. - expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.GET_LOG, {'type': 'browser'}). andReturnSuccess([ new webdriver.logging.Entry( @@ -2273,7 +2181,8 @@ function testFetchingLogs() { ]). replayAll(); - var pair = callbackPair(function(entries) { + var driver = testHelper.createDriver(); + return driver.manage().logs().get('browser').then(function(entries) { assertEquals(2, entries.length); assertTrue(entries[0] instanceof webdriver.logging.Entry); @@ -2286,110 +2195,161 @@ function testFetchingLogs() { assertEquals('abc123', entries[1].message); assertEquals(5678, entries[1].timestamp); }); - - var driver = testHelper.createDriver(); - driver.manage().logs().get('browser').then(pair.callback, pair.errback); - testHelper.execute(); - pair.assertCallback(); } function testCommandsFailIfInitialSessionCreationFailed() { - var testHelper = TestHelper.expectingSuccess().replayAll(); - var navigateResult = callbackPair(null, assertIsStubError); - var quitResult = callbackPair(null, assertIsStubError); + var testHelper = new TestHelper().replayAll(); - var session = webdriver.promise.rejected(STUB_ERROR); + var session = webdriver.promise.rejected(new StubError); var driver = testHelper.createDriver(session); - driver.get('some-url').then(navigateResult.callback, navigateResult.errback); - driver.quit().then(quitResult.callback, quitResult.errback); + var navigateResult = driver.get('some-url').then(fail, assertIsStubError); + var quitResult = driver.quit().then(fail, assertIsStubError); - testHelper.execute(); - navigateResult.assertErrback(); - quitResult.assertErrback(); + return waitForIdle().then(function() { + return webdriver.promise.all(navigateResult, quitResult); + }); } function testWebElementCommandsFailIfInitialDriverCreationFailed() { - var testHelper = TestHelper.expectingSuccess().replayAll(); + var testHelper = new TestHelper().replayAll(); - var session = webdriver.promise.rejected(STUB_ERROR); - var callback = callbackHelper(assertIsStubError); + var session = webdriver.promise.rejected(new StubError); var driver = testHelper.createDriver(session); - driver.findElement(By.id('foo')).click().thenCatch(callback); - testHelper.execute(); - callback.assertCalled(); + return driver.findElement(By.id('foo')).click(). + then(fail, assertIsStubError); } function testWebElementCommansFailIfElementCouldNotBeFound() { - var testHelper = TestHelper. - expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). andReturnError(ECode.NO_SUCH_ELEMENT, {'message':'Unable to find element'}). replayAll(); - var callback = callbackHelper( - expectedError(ECode.NO_SUCH_ELEMENT, 'Unable to find element')); - var driver = testHelper.createDriver(); - driver.findElement(By.id('foo')).click().thenCatch(callback); - testHelper.execute(); - callback.assertCalled(); + return driver.findElement(By.id('foo')).click(). + then(fail, + expectedError(ECode.NO_SUCH_ELEMENT, 'Unable to find element')); } function testCannotFindChildElementsIfParentCouldNotBeFound() { - var testHelper = TestHelper. - expectingSuccess(). + var testHelper = new TestHelper(). expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). andReturnError(ECode.NO_SUCH_ELEMENT, {'message':'Unable to find element'}). replayAll(); - var callback = callbackHelper( - expectedError(ECode.NO_SUCH_ELEMENT, 'Unable to find element')); - var driver = testHelper.createDriver(); - driver.findElement(By.id('foo')) + return driver.findElement(By.id('foo')) .findElement(By.id('bar')) .findElement(By.id('baz')) - .thenCatch(callback); - testHelper.execute(); - callback.assertCalled(); + .then(fail, + expectedError(ECode.NO_SUCH_ELEMENT, 'Unable to find element')); } function testActionSequenceFailsIfInitialDriverCreationFailed() { - var testHelper = TestHelper.expectingSuccess().replayAll(); + var testHelper = new TestHelper().replayAll(); - var session = webdriver.promise.rejected(STUB_ERROR); + 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); - var callback = callbackHelper(assertIsStubError); - - var driver = testHelper.createDriver(session); - driver.actions(). + return testHelper.createDriver(session). + actions(). mouseDown(). mouseUp(). perform(). - thenCatch(callback); - testHelper.execute(); - callback.assertCalled(); + 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 = TestHelper - .expectingSuccess() + var testHelper = new TestHelper() .expect(CName.GET_ALERT_TEXT) - .andReturnError(ECode.NO_SUCH_ALERT, {'message': 'no alert'}) + .andReturnError(ECode.NO_SUCH_ALERT, {'message': 'no alert'}) .replayAll(); var driver = testHelper.createDriver(); @@ -2399,15 +2359,11 @@ function testAlertCommandsFailIfAlertNotPresent() { var callbacks = []; for (var key in webdriver.Alert.prototype) { if (webdriver.Alert.prototype.hasOwnProperty(key)) { - var helper = callbackHelper(expectError); - callbacks.push(key, helper); - alert[key].call(alert).thenCatch(helper); + callbacks.push(key, alert[key].call(alert).thenCatch(expectError)); } } - testHelper.execute(); - for (var i = 0; i < callbacks.length - 1; i += 2) { - callbacks[i + 1].assertCalled( - 'Error did not propagate for ' + callbacks[i]); - } + 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 index f95e400..ff13842 100644 --- a/lib/webdriver/testing/client.js +++ b/lib/webdriver/testing/client.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.testing.Client'); diff --git a/lib/webdriver/testing/jsunit.js b/lib/webdriver/testing/jsunit.js index 42a2f2c..bfb1365 100644 --- a/lib/webdriver/testing/jsunit.js +++ b/lib/webdriver/testing/jsunit.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 File to include for turning any HTML file page into a WebDriver diff --git a/lib/webdriver/testing/testcase.js b/lib/webdriver/testing/testcase.js index 7d5c8c5..8c2bc32 100644 --- a/lib/webdriver/testing/testcase.js +++ b/lib/webdriver/testing/testcase.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 special test case that runs each test inside of a @@ -28,7 +31,7 @@ goog.provide('webdriver.testing.TestCase'); goog.require('goog.testing.TestCase'); -goog.require('webdriver.promise.ControlFlow'); +goog.require('webdriver.promise'); /** @suppress {extraRequire} Imported for user convenience. */ goog.require('webdriver.testing.asserts'); @@ -83,7 +86,7 @@ webdriver.testing.TestCase.prototype.cycleTests = function() { function onError(e) { hadError = true; - self.doError(test, app.annotateError(e)); + 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]; @@ -101,9 +104,6 @@ webdriver.testing.TestCase.prototype.logError = function(name, opt_e) { if (goog.isString(opt_e)) { errMsg = opt_e; } else { - // In case someone calls this function directly, make sure we have a - // properly annotated error. - webdriver.promise.controlFlow().annotateError(opt_e); errMsg = opt_e.toString(); stack = opt_e.stack.substring(errMsg.length + 1); } @@ -153,7 +153,6 @@ webdriver.testing.TestCase.prototype.logError = function(name, opt_e) { */ webdriver.testing.TestCase.prototype.runSingleTest_ = function(test, onError) { var flow = webdriver.promise.controlFlow(); - flow.clearHistory(); return execute(test.name + '.setUp()', this.setUp)(). then(execute(test.name + '()', test.ref)). diff --git a/lib/webdriver/testing/window.js b/lib/webdriver/testing/window.js index 4711c3d..dd56733 100644 --- a/lib/webdriver/testing/window.js +++ b/lib/webdriver/testing/window.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. /** * @fileoverview A utility class for working with test windows. @@ -19,24 +22,23 @@ goog.provide('webdriver.testing.Window'); goog.require('goog.string'); -goog.require('webdriver.promise.Promise'); +goog.require('webdriver.promise'); /** * Class for managing a window. * - *

    This class is implemented as a promise so consumers may register + * 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);
    - *   });
    - * 
    + * + * 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 @@ -47,7 +49,9 @@ goog.require('webdriver.promise.Promise'); * @extends {webdriver.promise.Promise} */ webdriver.testing.Window = function(driver, handle, opt_window) { - webdriver.promise.Promise.call(this); + webdriver.promise.Promise.call(this, function(fulfill, reject) { + handle.then(fulfill, reject); + }); /** @private {!webdriver.WebDriver} */ this.driver_ = driver; @@ -153,12 +157,6 @@ webdriver.testing.Window.prototype.cancel = function() { }; -/** @override */ -webdriver.testing.Window.prototype.then = function(callback, errback) { - return this.handle_.then(callback, errback); -}; - - /** * Focuses the wrapped driver on the window managed by this class. * @return {!webdriver.promise.Promise} A promise that will be resolved 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 bec9b44..fd78032 100644 --- a/lib/webdriver/webdriver.js +++ b/lib/webdriver/webdriver.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 The heart of the WebDriver JavaScript API. @@ -18,6 +21,7 @@ goog.provide('webdriver.Alert'); goog.provide('webdriver.AlertPromise'); +goog.provide('webdriver.FileDetector'); goog.provide('webdriver.UnhandledAlertError'); goog.provide('webdriver.WebDriver'); goog.provide('webdriver.WebElement'); @@ -33,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'); ////////////////////////////////////////////////////////////////////////////// @@ -54,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. @@ -83,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; }; @@ -113,7 +122,7 @@ webdriver.WebDriver.attachToSession = function(executor, sessionId, opt_flow) { * 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} + * Defaults to the {@link webdriver.promise.controlFlow() currently active} * control flow. * @return {!webdriver.WebDriver} The driver for the newly created session. */ @@ -162,49 +171,100 @@ webdriver.WebDriver.acquireSession_ = function( * When converting values of type object, the following steps will be taken: *
      *
    1. if the object is a WebElement, the return value will be the element's - * server ID
    2. - *
    3. if the object provides a "toJSON" function, the return value of this - * function will be returned
    4. + * server ID + *
    5. 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 + *
    6. if the object provides a "toJSON" function, this algorithm will + * recursively be applied to the result of that function *
    7. otherwise, the value of each key will be recursively converted according - * to the rules above.
    8. + * to the rules above. *
    * * @param {*} obj The object to convert. * @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) { if (webdriver.promise.isPromise(obj)) { return obj.then(webdriver.WebDriver.toWireValue_); } - switch (goog.typeOf(obj)) { - case 'array': - return webdriver.promise.all( - goog.array.map(/** @type {!Array} */ (obj), - webdriver.WebDriver.toWireValue_)); - case 'object': - if (obj instanceof webdriver.WebElement) { - return obj.getId(); + 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); + } } }; @@ -219,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) { @@ -339,6 +399,16 @@ 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: // ---------------------------------------------------------------------------- @@ -387,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() { @@ -401,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 @@ -423,15 +510,14 @@ 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. @@ -443,10 +529,11 @@ 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()'); }; @@ -464,66 +551,62 @@ 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.responseText);
    - *     }
    - *   }
    - *   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. @@ -569,24 +652,73 @@ 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. + * 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}. * - *

    In the event a condition returns a {@link webdriver.promise.Promise}, the + * 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 - * evaluating whether the condition has been satisfied. The resolution time for + * whether the condition has been satisified. Note the resolution time for * a promise is factored into whether a wait has timed out. * - * @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. + * *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. + * @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(fn, timeout, opt_message) { - return this.flow_.wait(fn, timeout, opt_message); +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); }; @@ -693,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 @@ -831,7 +961,7 @@ webdriver.WebDriver.prototype.findDomElement_ = function(element) { /** * 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. * @@ -1032,7 +1162,7 @@ webdriver.WebDriver.Options = function(driver) { * secure: (boolean|undefined), * expiry: (number|undefined) * }} - * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#cookie-json-object */ webdriver.WebDriver.Options.Cookie; @@ -1129,7 +1259,7 @@ webdriver.WebDriver.Options.prototype.deleteCookie = function(name) { * @return {!webdriver.promise.Promise.< * !Array.>} 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 + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#cookie-json-object */ webdriver.WebDriver.Options.prototype.getCookies = function() { return this.driver_.schedule( @@ -1146,7 +1276,7 @@ webdriver.WebDriver.Options.prototype.getCookies = function() { * @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 + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#cookie-json-object */ webdriver.WebDriver.Options.prototype.getCookie = function(name) { return this.getCookies().then(function(cookies) { @@ -1200,16 +1330,16 @@ 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. @@ -1365,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 @@ -1450,17 +1579,19 @@ 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. @@ -1476,10 +1607,11 @@ 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 @@ -1544,26 +1676,24 @@ 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. @@ -1571,8 +1701,10 @@ webdriver.Key.chord = function(var_args) { * webdriver.WebElement.Id)} id The server-assigned opaque ID for the * underlying DOM element. * @constructor + * @extends {webdriver.Serializable.} */ webdriver.WebElement = function(driver, id) { + webdriver.Serializable.call(this); /** @private {!webdriver.WebDriver} */ this.driver_ = driver; @@ -1581,12 +1713,13 @@ webdriver.WebElement = function(driver, id) { this.id_ = id instanceof webdriver.promise.Promise ? id : webdriver.promise.fulfilled(id); }; +goog.inherits(webdriver.WebElement, webdriver.Serializable); /** * Wire protocol definition of a WebElement ID. * @typedef {{ELEMENT: string}} - * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol */ webdriver.WebElement.Id; @@ -1641,13 +1774,32 @@ 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 + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol */ 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 @@ -1668,37 +1820,35 @@ webdriver.WebElement.prototype.schedule_ = function(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. @@ -1776,40 +1926,53 @@ 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). + * @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. */ @@ -1817,16 +1980,32 @@ 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()'); }; @@ -1848,8 +2027,8 @@ 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 @@ -1877,19 +2056,19 @@ 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 @@ -2044,12 +2223,11 @@ webdriver.WebElement.prototype.getInnerHtml = function() { * 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. @@ -2168,12 +2346,11 @@ webdriver.Alert.prototype.sendKeys = function(text) { * 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. @@ -2256,7 +2433,8 @@ goog.inherits(webdriver.AlertPromise, webdriver.Alert); * @extends {bot.Error} */ webdriver.UnhandledAlertError = function(message, text, alert) { - goog.base(this, bot.ErrorCode.UNEXPECTED_ALERT_OPEN, message); + webdriver.UnhandledAlertError.base( + this, 'constructor', bot.ErrorCode.UNEXPECTED_ALERT_OPEN, message); /** @private {string} */ this.text_ = text; @@ -2275,11 +2453,40 @@ webdriver.UnhandledAlertError.prototype.getAlertText = function() { }; + /** - * @return {!webdriver.Alert} The open alert. - * @deprecated Use {@link #getAlertText}. This method will be removed in - * 2.45.0. - */ -webdriver.UnhandledAlertError.prototype.getAlert = function() { - return this.alert_; -}; + * 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 94c4114..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'; diff --git a/net/portprober.js b/net/portprober.js index 852b49d..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'; diff --git a/node_modules/.bin/_mocha b/node_modules/.bin/_mocha new file mode 120000 index 0000000..f2a54ff --- /dev/null +++ b/node_modules/.bin/_mocha @@ -0,0 +1 @@ +../mocha/bin/_mocha \ No newline at end of file diff --git a/node_modules/.bin/mocha b/node_modules/.bin/mocha new file mode 120000 index 0000000..43c668d --- /dev/null +++ b/node_modules/.bin/mocha @@ -0,0 +1 @@ +../mocha/bin/mocha \ No newline at end of file diff --git a/node_modules/.bin/promises-aplus-tests b/node_modules/.bin/promises-aplus-tests new file mode 120000 index 0000000..5759030 --- /dev/null +++ b/node_modules/.bin/promises-aplus-tests @@ -0,0 +1 @@ +../promises-aplus-tests/lib/cli.js \ No newline at end of file diff --git a/node_modules/.bin/rimraf b/node_modules/.bin/rimraf new file mode 120000 index 0000000..4cd49a4 --- /dev/null +++ b/node_modules/.bin/rimraf @@ -0,0 +1 @@ +../rimraf/bin.js \ No newline at end of file diff --git a/node_modules/adm-zip/.idea/scopes/scope_settings.xml b/node_modules/adm-zip/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..0d5175c --- /dev/null +++ b/node_modules/adm-zip/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/node_modules/adm-zip/MIT-LICENSE.txt b/node_modules/adm-zip/MIT-LICENSE.txt new file mode 100644 index 0000000..14c3ee5 --- /dev/null +++ b/node_modules/adm-zip/MIT-LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2012 Another-D-Mention Software and other contributors, +http://www.another-d-mention.ro/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/adm-zip/README.md b/node_modules/adm-zip/README.md new file mode 100644 index 0000000..030fab8 --- /dev/null +++ b/node_modules/adm-zip/README.md @@ -0,0 +1,64 @@ +# ADM-ZIP for NodeJS + +ADM-ZIP is a pure JavaScript implementation for zip data compression for [NodeJS](http://nodejs.org/). + +# Installation + +With [npm](http://npmjs.org) do: + + $ npm install adm-zip + +## What is it good for? +The library allows you to: + +* decompress zip files directly to disk or in memory buffers +* compress files and store them to disk in .zip format or in compressed buffers +* update content of/add new/delete files from an existing .zip + +# Dependencies +There are no other nodeJS libraries that ADM-ZIP is dependent of + +# Examples + +## Basic usage +```javascript + + var AdmZip = require('adm-zip'); + + // reading archives + var zip = new AdmZip("./my_file.zip"); + var zipEntries = zip.getEntries(); // an array of ZipEntry records + + zipEntries.forEach(function(zipEntry) { + console.log(zipEntry.toString()); // outputs zip entries information + if (zipEntry.entryName == "my_file.txt") { + console.log(zipEntry.data.toString('utf8')); + } + }); + // outputs the content of some_folder/my_file.txt + console.log(zip.readAsText("some_folder/my_file.txt")); + // extracts the specified file to the specified location + zip.extractEntryTo(/*entry name*/"some_folder/my_file.txt", /*target path*/"/home/me/tempfolder", /*maintainEntryPath*/false, /*overwrite*/true); + // extracts everything + zip.extractAllTo(/*target path*/"/home/me/zipcontent/", /*overwrite*/true); + + + // creating archives + var zip = new AdmZip(); + + // add file directly + zip.addFile("test.txt", new Buffer("inner content of the file"), "entry comment goes here"); + // add local file + zip.addLocalFile("/home/me/some_picture.png"); + // get everything as a buffer + var willSendthis = zip.toBuffer(); + // or write everything to disk + zip.writeZip(/*target file name*/"/home/me/files.zip"); + + + // ... more examples in the wiki +``` + +For more detailed information please check out the [wiki](https://github.com/cthackers/adm-zip/wiki). + +[![build status](https://secure.travis-ci.org/cthackers/adm-zip.png)](http://travis-ci.org/cthackers/adm-zip) diff --git a/node_modules/adm-zip/adm-zip.js b/node_modules/adm-zip/adm-zip.js new file mode 100644 index 0000000..46595fc --- /dev/null +++ b/node_modules/adm-zip/adm-zip.js @@ -0,0 +1,404 @@ +var fs = require("fs"), + pth = require("path"); + +fs.existsSync = fs.existsSync || pth.existsSync; + +var ZipEntry = require("./zipEntry"), + ZipFile = require("./zipFile"), + Utils = require("./util"); + +module.exports = function(/*String*/input) { + var _zip = undefined, + _filename = ""; + + if (input && typeof input === "string") { // load zip file + if (fs.existsSync(input)) { + _filename = input; + _zip = new ZipFile(input, Utils.Constants.FILE); + } else { + throw Utils.Errors.INVALID_FILENAME; + } + } else if(input && Buffer.isBuffer(input)) { // load buffer + _zip = new ZipFile(input, Utils.Constants.BUFFER); + } else { // create new zip file + _zip = new ZipFile(null, Utils.Constants.NONE); + } + + function getEntry(/*Object*/entry) { + if (entry && _zip) { + var item; + // If entry was given as a file name + if (typeof entry === "string") + item = _zip.getEntry(entry); + // if entry was given as a ZipEntry object + if (typeof entry === "object" && entry.entryName != undefined && entry.header != undefined) + item = _zip.getEntry(entry.entryName); + + if (item) { + return item; + } + } + return null; + } + + return { + /** + * Extracts the given entry from the archive and returns the content as a Buffer object + * @param entry ZipEntry object or String with the full path of the entry + * + * @return Buffer or Null in case of error + */ + readFile : function(/*Object*/entry) { + var item = getEntry(entry); + return item && item.getData() || null; + }, + + /** + * Asynchronous readFile + * @param entry ZipEntry object or String with the full path of the entry + * @param callback + * + * @return Buffer or Null in case of error + */ + readFileAsync : function(/*Object*/entry, /*Function*/callback) { + var item = getEntry(entry); + if (item) { + item.getDataAsync(callback); + } else { + callback(null,"getEntry failed for:" + entry) + } + }, + + /** + * Extracts the given entry from the archive and returns the content as plain text in the given encoding + * @param entry ZipEntry object or String with the full path of the entry + * @param encoding Optional. If no encoding is specified utf8 is used + * + * @return String + */ + readAsText : function(/*Object*/entry, /*String - Optional*/encoding) { + var item = getEntry(entry); + if (item) { + var data = item.getData(); + if (data && data.length) { + return data.toString(encoding || "utf8"); + } + } + return ""; + }, + + /** + * Asynchronous readAsText + * @param entry ZipEntry object or String with the full path of the entry + * @param callback + * @param encoding Optional. If no encoding is specified utf8 is used + * + * @return String + */ + readAsTextAsync : function(/*Object*/entry, /*Function*/callback, /*String - Optional*/encoding) { + var item = getEntry(entry); + if (item) { + item.getDataAsync(function(data) { + if (data && data.length) { + callback(data.toString(encoding || "utf8")); + } else { + callback(""); + } + }) + } else { + callback(""); + } + }, + + /** + * Remove the entry from the file or the entry and all it's nested directories and files if the given entry is a directory + * + * @param entry + */ + deleteFile : function(/*Object*/entry) { // @TODO: test deleteFile + var item = getEntry(entry); + if (item) { + _zip.deleteEntry(item.entryName); + } + }, + + /** + * Adds a comment to the zip. The zip must be rewritten after adding the comment. + * + * @param comment + */ + addZipComment : function(/*String*/comment) { // @TODO: test addZipComment + _zip.comment = comment; + }, + + /** + * Returns the zip comment + * + * @return String + */ + getZipComment : function() { + return _zip.comment || ''; + }, + + /** + * Adds a comment to a specified zipEntry. The zip must be rewritten after adding the comment + * The comment cannot exceed 65535 characters in length + * + * @param entry + * @param comment + */ + addZipEntryComment : function(/*Object*/entry,/*String*/comment) { + var item = getEntry(entry); + if (item) { + item.comment = comment; + } + }, + + /** + * Returns the comment of the specified entry + * + * @param entry + * @return String + */ + getZipEntryComment : function(/*Object*/entry) { + var item = getEntry(entry); + if (item) { + return item.comment || ''; + } + return '' + }, + + /** + * Updates the content of an existing entry inside the archive. The zip must be rewritten after updating the content + * + * @param entry + * @param content + */ + updateFile : function(/*Object*/entry, /*Buffer*/content) { + var item = getEntry(entry); + if (item) { + item.setData(content); + } + }, + + /** + * Adds a file from the disk to the archive + * + * @param localPath + */ + addLocalFile : function(/*String*/localPath, /*String*/zipPath) { + if (fs.existsSync(localPath)) { + if(zipPath){ + zipPath=zipPath.split("\\").join("/"); + if(zipPath.charAt(zipPath.length - 1) != "/"){ + zipPath += "/"; + } + }else{ + zipPath=""; + } + var p = localPath.split("\\").join("/").split("/").pop(); + + this.addFile(zipPath+p, fs.readFileSync(localPath), "", 0) + } else { + throw Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath); + } + }, + + /** + * Adds a local directory and all its nested files and directories to the archive + * + * @param localPath + */ + addLocalFolder : function(/*String*/localPath, /*String*/zipPath) { + if(zipPath){ + zipPath=zipPath.split("\\").join("/"); + if(zipPath.charAt(zipPath.length - 1) != "/"){ + zipPath += "/"; + } + }else{ + zipPath=""; + } + localPath = localPath.split("\\").join("/"); //windows fix + if (localPath.charAt(localPath.length - 1) != "/") + localPath += "/"; + + if (fs.existsSync(localPath)) { + + var items = Utils.findFiles(localPath), + self = this; + + if (items.length) { + items.forEach(function(path) { + var p = path.split("\\").join("/").replace(localPath, ""); //windows fix + if (p.charAt(p.length - 1) !== "/") { + self.addFile(zipPath+p, fs.readFileSync(path), "", 0) + } else { + self.addFile(zipPath+p, new Buffer(0), "", 0) + } + }); + } + } else { + throw Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath); + } + }, + + /** + * Allows you to create a entry (file or directory) in the zip file. + * If you want to create a directory the entryName must end in / and a null buffer should be provided. + * Comment and attributes are optional + * + * @param entryName + * @param content + * @param comment + * @param attr + */ + addFile : function(/*String*/entryName, /*Buffer*/content, /*String*/comment, /*Number*/attr) { + var entry = new ZipEntry(); + entry.entryName = entryName; + entry.comment = comment || ""; + entry.attr = attr || 438; //0666; + if (entry.isDirectory && content.length) { + // throw Utils.Errors.DIRECTORY_CONTENT_ERROR; + } + entry.setData(content); + _zip.setEntry(entry); + }, + + /** + * Returns an array of ZipEntry objects representing the files and folders inside the archive + * + * @return Array + */ + getEntries : function() { + if (_zip) { + return _zip.entries; + } else { + return []; + } + }, + + /** + * Returns a ZipEntry object representing the file or folder specified by ``name``. + * + * @param name + * @return ZipEntry + */ + getEntry : function(/*String*/name) { + return getEntry(name); + }, + + /** + * Extracts the given entry to the given targetPath + * If the entry is a directory inside the archive, the entire directory and it's subdirectories will be extracted + * + * @param entry ZipEntry object or String with the full path of the entry + * @param targetPath Target folder where to write the file + * @param maintainEntryPath If maintainEntryPath is true and the entry is inside a folder, the entry folder + * will be created in targetPath as well. Default is TRUE + * @param overwrite If the file already exists at the target path, the file will be overwriten if this is true. + * Default is FALSE + * + * @return Boolean + */ + extractEntryTo : function(/*Object*/entry, /*String*/targetPath, /*Boolean*/maintainEntryPath, /*Boolean*/overwrite) { + overwrite = overwrite || false; + maintainEntryPath = typeof maintainEntryPath == "undefined" ? true : maintainEntryPath; + + var item = getEntry(entry); + if (!item) { + throw Utils.Errors.NO_ENTRY; + } + + var target = pth.resolve(targetPath, maintainEntryPath ? item.entryName : pth.basename(item.entryName)); + + if (item.isDirectory) { + target = pth.resolve(target, ".."); + var children = _zip.getEntryChildren(item); + children.forEach(function(child) { + if (child.isDirectory) return; + var content = child.getData(); + if (!content) { + throw Utils.Errors.CANT_EXTRACT_FILE; + } + Utils.writeFileTo(pth.resolve(targetPath, maintainEntryPath ? child.entryName : child.entryName.substr(item.entryName.length)), content, overwrite); + }); + return true; + } + + var content = item.getData(); + if (!content) throw Utils.Errors.CANT_EXTRACT_FILE; + + if (fs.existsSync(targetPath) && !overwrite) { + throw Utils.Errors.CANT_OVERRIDE; + } + Utils.writeFileTo(target, content, overwrite); + + return true; + }, + + /** + * Extracts the entire archive to the given location + * + * @param targetPath Target location + * @param overwrite If the file already exists at the target path, the file will be overwriten if this is true. + * Default is FALSE + */ + extractAllTo : function(/*String*/targetPath, /*Boolean*/overwrite) { + overwrite = overwrite || false; + if (!_zip) { + throw Utils.Errors.NO_ZIP; + } + + _zip.entries.forEach(function(entry) { + if (entry.isDirectory) { + Utils.makeDir(pth.resolve(targetPath, entry.entryName.toString())); + return; + } + var content = entry.getData(); + if (!content) { + throw Utils.Errors.CANT_EXTRACT_FILE + "2"; + } + Utils.writeFileTo(pth.resolve(targetPath, entry.entryName.toString()), content, overwrite); + }) + }, + + /** + * Writes the newly created zip file to disk at the specified location or if a zip was opened and no ``targetFileName`` is provided, it will overwrite the opened zip + * + * @param targetFileName + * @param callback + */ + writeZip : function(/*String*/targetFileName, /*Function*/callback) { + if (arguments.length == 1) { + if (typeof targetFileName == "function") { + callback = targetFileName; + targetFileName = ""; + } + } + + if (!targetFileName && _filename) { + targetFileName = _filename; + } + if (!targetFileName) return; + + var zipData = _zip.compressToBuffer(); + if (zipData) { + Utils.writeFileTo(targetFileName, zipData, true); + } + }, + + /** + * Returns the content of the entire zip file as a Buffer object + * + * @return Buffer + */ + toBuffer : function(/*Function*/onSuccess,/*Function*/onFail,/*Function*/onItemStart,/*Function*/onItemEnd) { + this.valueOf = 2; + if (typeof onSuccess == "function") { + _zip.toAsyncBuffer(onSuccess,onFail,onItemStart,onItemEnd); + return null; + } + return _zip.compressToBuffer() + } + } +}; diff --git a/node_modules/adm-zip/headers/entryHeader.js b/node_modules/adm-zip/headers/entryHeader.js new file mode 100644 index 0000000..a29c70f --- /dev/null +++ b/node_modules/adm-zip/headers/entryHeader.js @@ -0,0 +1,261 @@ +var Utils = require("../util"), + Constants = Utils.Constants; + +/* The central directory file header */ +module.exports = function () { + var _verMade = 0x0A, + _version = 0x0A, + _flags = 0, + _method = 0, + _time = 0, + _crc = 0, + _compressedSize = 0, + _size = 0, + _fnameLen = 0, + _extraLen = 0, + + _comLen = 0, + _diskStart = 0, + _inattr = 0, + _attr = 0, + _offset = 0; + + var _dataHeader = {}; + + function setTime(val) { + var val = new Date(val); + _time = (val.getFullYear() - 1980 & 0x7f) << 25 // b09-16 years from 1980 + | (val.getMonth() + 1) << 21 // b05-08 month + | val.getDay() << 16 // b00-04 hour + + // 2 bytes time + | val.getHours() << 11 // b11-15 hour + | val.getMinutes() << 5 // b05-10 minute + | val.getSeconds() >> 1; // b00-04 seconds divided by 2 + } + + setTime(+new Date()); + + return { + get made () { return _verMade; }, + set made (val) { _verMade = val; }, + + get version () { return _version; }, + set version (val) { _version = val }, + + get flags () { return _flags }, + set flags (val) { _flags = val; }, + + get method () { return _method; }, + set method (val) { _method = val; }, + + get time () { return new Date( + ((_time >> 25) & 0x7f) + 1980, + ((_time >> 21) & 0x0f) - 1, + (_time >> 16) & 0x1f, + (_time >> 11) & 0x1f, + (_time >> 5) & 0x3f, + (_time & 0x1f) << 1 + ); + }, + set time (val) { + setTime(val); + }, + + get crc () { return _crc; }, + set crc (val) { _crc = val; }, + + get compressedSize () { return _compressedSize; }, + set compressedSize (val) { _compressedSize = val; }, + + get size () { return _size; }, + set size (val) { _size = val; }, + + get fileNameLength () { return _fnameLen; }, + set fileNameLength (val) { _fnameLen = val; }, + + get extraLength () { return _extraLen }, + set extraLength (val) { _extraLen = val; }, + + get commentLength () { return _comLen }, + set commentLength (val) { _comLen = val }, + + get diskNumStart () { return _diskStart }, + set diskNumStart (val) { _diskStart = val }, + + get inAttr () { return _inattr }, + set inAttr (val) { _inattr = val }, + + get attr () { return _attr }, + set attr (val) { _attr = val }, + + get offset () { return _offset }, + set offset (val) { _offset = val }, + + get encripted () { return (_flags & 1) == 1 }, + + get entryHeaderSize () { + return Constants.CENHDR + _fnameLen + _extraLen + _comLen; + }, + + get realDataOffset () { + return _offset + Constants.LOCHDR + _dataHeader.fnameLen + _dataHeader.extraLen; + }, + + get dataHeader () { + return _dataHeader; + }, + + loadDataHeaderFromBinary : function(/*Buffer*/input) { + var data = input.slice(_offset, _offset + Constants.LOCHDR); + // 30 bytes and should start with "PK\003\004" + if (data.readUInt32LE(0) != Constants.LOCSIG) { + throw Utils.Errors.INVALID_LOC; + } + _dataHeader = { + // version needed to extract + version : data.readUInt16LE(Constants.LOCVER), + // general purpose bit flag + flags : data.readUInt16LE(Constants.LOCFLG), + // compression method + method : data.readUInt16LE(Constants.LOCHOW), + // modification time (2 bytes time, 2 bytes date) + time : data.readUInt32LE(Constants.LOCTIM), + // uncompressed file crc-32 value + crc : data.readUInt32LE(Constants.LOCCRC), + // compressed size + compressedSize : data.readUInt32LE(Constants.LOCSIZ), + // uncompressed size + size : data.readUInt32LE(Constants.LOCLEN), + // filename length + fnameLen : data.readUInt16LE(Constants.LOCNAM), + // extra field length + extraLen : data.readUInt16LE(Constants.LOCEXT) + } + }, + + loadFromBinary : function(/*Buffer*/data) { + // data should be 46 bytes and start with "PK 01 02" + if (data.length != Constants.CENHDR || data.readUInt32LE(0) != Constants.CENSIG) { + throw Utils.Errors.INVALID_CEN; + } + // version made by + _verMade = data.readUInt16LE(Constants.CENVEM); + // version needed to extract + _version = data.readUInt16LE(Constants.CENVER); + // encrypt, decrypt flags + _flags = data.readUInt16LE(Constants.CENFLG); + // compression method + _method = data.readUInt16LE(Constants.CENHOW); + // modification time (2 bytes time, 2 bytes date) + _time = data.readUInt32LE(Constants.CENTIM); + // uncompressed file crc-32 value + _crc = data.readUInt32LE(Constants.CENCRC); + // compressed size + _compressedSize = data.readUInt32LE(Constants.CENSIZ); + // uncompressed size + _size = data.readUInt32LE(Constants.CENLEN); + // filename length + _fnameLen = data.readUInt16LE(Constants.CENNAM); + // extra field length + _extraLen = data.readUInt16LE(Constants.CENEXT); + // file comment length + _comLen = data.readUInt16LE(Constants.CENCOM); + // volume number start + _diskStart = data.readUInt16LE(Constants.CENDSK); + // internal file attributes + _inattr = data.readUInt16LE(Constants.CENATT); + // external file attributes + _attr = data.readUInt32LE(Constants.CENATX); + // LOC header offset + _offset = data.readUInt32LE(Constants.CENOFF); + }, + + dataHeaderToBinary : function() { + // LOC header size (30 bytes) + var data = new Buffer(Constants.LOCHDR); + // "PK\003\004" + data.writeUInt32LE(Constants.LOCSIG, 0); + // version needed to extract + data.writeUInt16LE(_version, Constants.LOCVER); + // general purpose bit flag + data.writeUInt16LE(_flags, Constants.LOCFLG); + // compression method + data.writeUInt16LE(_method, Constants.LOCHOW); + // modification time (2 bytes time, 2 bytes date) + data.writeUInt32LE(_time, Constants.LOCTIM); + // uncompressed file crc-32 value + data.writeUInt32LE(_crc, Constants.LOCCRC); + // compressed size + data.writeUInt32LE(_compressedSize, Constants.LOCSIZ); + // uncompressed size + data.writeUInt32LE(_size, Constants.LOCLEN); + // filename length + data.writeUInt16LE(_fnameLen, Constants.LOCNAM); + // extra field length + data.writeUInt16LE(_extraLen, Constants.LOCEXT); + return data; + }, + + entryHeaderToBinary : function() { + // CEN header size (46 bytes) + var data = new Buffer(Constants.CENHDR + _fnameLen + _extraLen + _comLen); + // "PK\001\002" + data.writeUInt32LE(Constants.CENSIG, 0); + // version made by + data.writeUInt16LE(_verMade, Constants.CENVEM); + // version needed to extract + data.writeUInt16LE(_version, Constants.CENVER); + // encrypt, decrypt flags + data.writeUInt16LE(_flags, Constants.CENFLG); + // compression method + data.writeUInt16LE(_method, Constants.CENHOW); + // modification time (2 bytes time, 2 bytes date) + data.writeUInt32LE(_time, Constants.CENTIM); + // uncompressed file crc-32 value + data.writeInt32LE(_crc, Constants.CENCRC, true); + // compressed size + data.writeUInt32LE(_compressedSize, Constants.CENSIZ); + // uncompressed size + data.writeUInt32LE(_size, Constants.CENLEN); + // filename length + data.writeUInt16LE(_fnameLen, Constants.CENNAM); + // extra field length + data.writeUInt16LE(_extraLen, Constants.CENEXT); + // file comment length + data.writeUInt16LE(_comLen, Constants.CENCOM); + // volume number start + data.writeUInt16LE(_diskStart, Constants.CENDSK); + // internal file attributes + data.writeUInt16LE(_inattr, Constants.CENATT); + // external file attributes + data.writeUInt32LE(_attr, Constants.CENATX); + // LOC header offset + data.writeUInt32LE(_offset, Constants.CENOFF); + // fill all with + data.fill(0x00, Constants.CENHDR); + return data; + }, + + toString : function() { + return '{\n' + + '\t"made" : ' + _verMade + ",\n" + + '\t"version" : ' + _version + ",\n" + + '\t"flags" : ' + _flags + ",\n" + + '\t"method" : ' + Utils.methodToString(_method) + ",\n" + + '\t"time" : ' + _time + ",\n" + + '\t"crc" : 0x' + _crc.toString(16).toUpperCase() + ",\n" + + '\t"compressedSize" : ' + _compressedSize + " bytes,\n" + + '\t"size" : ' + _size + " bytes,\n" + + '\t"fileNameLength" : ' + _fnameLen + ",\n" + + '\t"extraLength" : ' + _extraLen + " bytes,\n" + + '\t"commentLength" : ' + _comLen + " bytes,\n" + + '\t"diskNumStart" : ' + _diskStart + ",\n" + + '\t"inAttr" : ' + _inattr + ",\n" + + '\t"attr" : ' + _attr + ",\n" + + '\t"offset" : ' + _offset + ",\n" + + '\t"entryHeaderSize" : ' + (Constants.CENHDR + _fnameLen + _extraLen + _comLen) + " bytes\n" + + '}'; + } + } +}; diff --git a/node_modules/adm-zip/headers/index.js b/node_modules/adm-zip/headers/index.js new file mode 100644 index 0000000..b8c67b9 --- /dev/null +++ b/node_modules/adm-zip/headers/index.js @@ -0,0 +1,2 @@ +exports.EntryHeader = require("./entryHeader"); +exports.MainHeader = require("./mainHeader"); diff --git a/node_modules/adm-zip/headers/mainHeader.js b/node_modules/adm-zip/headers/mainHeader.js new file mode 100644 index 0000000..002bc8a --- /dev/null +++ b/node_modules/adm-zip/headers/mainHeader.js @@ -0,0 +1,80 @@ +var Utils = require("../util"), + Constants = Utils.Constants; + +/* The entries in the end of central directory */ +module.exports = function () { + var _volumeEntries = 0, + _totalEntries = 0, + _size = 0, + _offset = 0, + _commentLength = 0; + + return { + get diskEntries () { return _volumeEntries }, + set diskEntries (/*Number*/val) { _volumeEntries = _totalEntries = val; }, + + get totalEntries () { return _totalEntries }, + set totalEntries (/*Number*/val) { _totalEntries = _volumeEntries = val; }, + + get size () { return _size }, + set size (/*Number*/val) { _size = val; }, + + get offset () { return _offset }, + set offset (/*Number*/val) { _offset = val; }, + + get commentLength () { return _commentLength }, + set commentLength (/*Number*/val) { _commentLength = val; }, + + get mainHeaderSize () { + return Constants.ENDHDR + _commentLength; + }, + + loadFromBinary : function(/*Buffer*/data) { + // data should be 22 bytes and start with "PK 05 06" + if (data.length != Constants.ENDHDR || data.readUInt32LE(0) != Constants.ENDSIG) + throw Utils.Errors.INVALID_END; + + // number of entries on this volume + _volumeEntries = data.readUInt16LE(Constants.ENDSUB); + // total number of entries + _totalEntries = data.readUInt16LE(Constants.ENDTOT); + // central directory size in bytes + _size = data.readUInt32LE(Constants.ENDSIZ); + // offset of first CEN header + _offset = data.readUInt32LE(Constants.ENDOFF); + // zip file comment length + _commentLength = data.readUInt16LE(Constants.ENDCOM); + }, + + toBinary : function() { + var b = new Buffer(Constants.ENDHDR + _commentLength); + // "PK 05 06" signature + b.writeUInt32LE(Constants.ENDSIG, 0); + b.writeUInt32LE(0, 4); + // number of entries on this volume + b.writeUInt16LE(_volumeEntries, Constants.ENDSUB); + // total number of entries + b.writeUInt16LE(_totalEntries, Constants.ENDTOT); + // central directory size in bytes + b.writeUInt32LE(_size, Constants.ENDSIZ); + // offset of first CEN header + b.writeUInt32LE(_offset, Constants.ENDOFF); + // zip file comment length + b.writeUInt16LE(_commentLength, Constants.ENDCOM); + // fill comment memory with spaces so no garbage is left there + b.fill(" ", Constants.ENDHDR); + + return b; + }, + + toString : function() { + return '{\n' + + '\t"diskEntries" : ' + _volumeEntries + ",\n" + + '\t"totalEntries" : ' + _totalEntries + ",\n" + + '\t"size" : ' + _size + " bytes,\n" + + '\t"offset" : 0x' + _offset.toString(16).toUpperCase() + ",\n" + + '\t"commentLength" : 0x' + _commentLength + "\n" + + '}'; + } + } +}; \ No newline at end of file diff --git a/node_modules/adm-zip/methods/deflater.js b/node_modules/adm-zip/methods/deflater.js new file mode 100644 index 0000000..3267943 --- /dev/null +++ b/node_modules/adm-zip/methods/deflater.js @@ -0,0 +1,1578 @@ +/* + * $Id: rawdeflate.js,v 0.5 2013/04/09 14:25:38 dankogai Exp dankogai $ + * + * GNU General Public License, version 2 (GPL-2.0) + * http://opensource.org/licenses/GPL-2.0 + * Original: + * http://www.onicos.com/staff/iz/amuse/javascript/expert/deflate.txt + */ +function JSDeflater(/*inbuff*/inbuf) { + + /* Copyright (C) 1999 Masanao Izumo + * Version: 1.0.1 + * LastModified: Dec 25 1999 + */ + + var WSIZE = 32768, // Sliding Window size + zip_STORED_BLOCK = 0, + zip_STATIC_TREES = 1, + zip_DYN_TREES = 2, + zip_DEFAULT_LEVEL = 6, + zip_FULL_SEARCH = true, + zip_INBUFSIZ = 32768, // Input buffer size + zip_INBUF_EXTRA = 64, // Extra buffer + zip_OUTBUFSIZ = 1024 * 8, + zip_window_size = 2 * WSIZE, + MIN_MATCH = 3, + MAX_MATCH = 258, + zip_BITS = 16, + LIT_BUFSIZE = 0x2000, + zip_HASH_BITS = 13, + zip_DIST_BUFSIZE = LIT_BUFSIZE, + zip_HASH_SIZE = 1 << zip_HASH_BITS, + zip_HASH_MASK = zip_HASH_SIZE - 1, + zip_WMASK = WSIZE - 1, + zip_NIL = 0, // Tail of hash chains + zip_TOO_FAR = 4096, + zip_MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1, + zip_MAX_DIST = WSIZE - zip_MIN_LOOKAHEAD, + zip_SMALLEST = 1, + zip_MAX_BITS = 15, + zip_MAX_BL_BITS = 7, + zip_LENGTH_CODES = 29, + zip_LITERALS = 256, + zip_END_BLOCK = 256, + zip_L_CODES = zip_LITERALS + 1 + zip_LENGTH_CODES, + zip_D_CODES = 30, + zip_BL_CODES = 19, + zip_REP_3_6 = 16, + zip_REPZ_3_10 = 17, + zip_REPZ_11_138 = 18, + zip_HEAP_SIZE = 2 * zip_L_CODES + 1, + zip_H_SHIFT = parseInt((zip_HASH_BITS + MIN_MATCH - 1) / MIN_MATCH); + + var zip_free_queue, zip_qhead, zip_qtail, zip_initflag, zip_outbuf = null, zip_outcnt, zip_outoff, zip_complete, + zip_window, zip_d_buf, zip_l_buf, zip_prev, zip_bi_buf, zip_bi_valid, zip_block_start, zip_ins_h, zip_hash_head, + zip_prev_match, zip_match_available, zip_match_length, zip_prev_length, zip_strstart, zip_match_start, zip_eofile, + zip_lookahead, zip_max_chain_length, zip_max_lazy_match, zip_compr_level, zip_good_match, zip_nice_match, + zip_dyn_ltree, zip_dyn_dtree, zip_static_ltree, zip_static_dtree, zip_bl_tree, zip_l_desc, zip_d_desc, zip_bl_desc, + zip_bl_count, zip_heap, zip_heap_len, zip_heap_max, zip_depth, zip_length_code, zip_dist_code, zip_base_length, + zip_base_dist, zip_flag_buf, zip_last_lit, zip_last_dist, zip_last_flags, zip_flags, zip_flag_bit, zip_opt_len, + zip_static_len, zip_deflate_data, zip_deflate_pos; + + var zip_DeflateCT = function () { + this.fc = 0; // frequency count or bit string + this.dl = 0; // father node in Huffman tree or length of bit string + }; + + var zip_DeflateTreeDesc = function () { + this.dyn_tree = null; // the dynamic tree + this.static_tree = null; // corresponding static tree or NULL + this.extra_bits = null; // extra bits for each code or NULL + this.extra_base = 0; // base index for extra_bits + this.elems = 0; // max number of elements in the tree + this.max_length = 0; // max bit length for the codes + this.max_code = 0; // largest code with non zero frequency + }; + + /* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ + var zip_DeflateConfiguration = function (a, b, c, d) { + this.good_length = a; // reduce lazy search above this match length + this.max_lazy = b; // do not perform lazy search above this match length + this.nice_length = c; // quit search above this match length + this.max_chain = d; + }; + + var zip_DeflateBuffer = function () { + this.next = null; + this.len = 0; + this.ptr = new Array(zip_OUTBUFSIZ); + this.off = 0; + }; + + /* constant tables */ + var zip_extra_lbits = new Array( + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0); + var zip_extra_dbits = new Array( + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13); + var zip_extra_blbits = new Array( + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7); + var zip_bl_order = new Array( + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15); + var zip_configuration_table = new Array( + new zip_DeflateConfiguration(0, 0, 0, 0), + new zip_DeflateConfiguration(4, 4, 8, 4), + new zip_DeflateConfiguration(4, 5, 16, 8), + new zip_DeflateConfiguration(4, 6, 32, 32), + new zip_DeflateConfiguration(4, 4, 16, 16), + new zip_DeflateConfiguration(8, 16, 32, 32), + new zip_DeflateConfiguration(8, 16, 128, 128), + new zip_DeflateConfiguration(8, 32, 128, 256), + new zip_DeflateConfiguration(32, 128, 258, 1024), + new zip_DeflateConfiguration(32, 258, 258, 4096)); + + + /* routines (deflate) */ + + var zip_deflate_start = function (level) { + var i; + + if (!level) + level = zip_DEFAULT_LEVEL; + else if (level < 1) + level = 1; + else if (level > 9) + level = 9; + + zip_compr_level = level; + zip_initflag = false; + zip_eofile = false; + if (zip_outbuf != null) + return; + + zip_free_queue = zip_qhead = zip_qtail = null; + zip_outbuf = new Array(zip_OUTBUFSIZ); + zip_window = new Array(zip_window_size); + zip_d_buf = new Array(zip_DIST_BUFSIZE); + zip_l_buf = new Array(zip_INBUFSIZ + zip_INBUF_EXTRA); + zip_prev = new Array(1 << zip_BITS); + zip_dyn_ltree = new Array(zip_HEAP_SIZE); + for (i = 0; i < zip_HEAP_SIZE; i++) zip_dyn_ltree[i] = new zip_DeflateCT(); + zip_dyn_dtree = new Array(2 * zip_D_CODES + 1); + for (i = 0; i < 2 * zip_D_CODES + 1; i++) zip_dyn_dtree[i] = new zip_DeflateCT(); + zip_static_ltree = new Array(zip_L_CODES + 2); + for (i = 0; i < zip_L_CODES + 2; i++) zip_static_ltree[i] = new zip_DeflateCT(); + zip_static_dtree = new Array(zip_D_CODES); + for (i = 0; i < zip_D_CODES; i++) zip_static_dtree[i] = new zip_DeflateCT(); + zip_bl_tree = new Array(2 * zip_BL_CODES + 1); + for (i = 0; i < 2 * zip_BL_CODES + 1; i++) zip_bl_tree[i] = new zip_DeflateCT(); + zip_l_desc = new zip_DeflateTreeDesc(); + zip_d_desc = new zip_DeflateTreeDesc(); + zip_bl_desc = new zip_DeflateTreeDesc(); + zip_bl_count = new Array(zip_MAX_BITS + 1); + zip_heap = new Array(2 * zip_L_CODES + 1); + zip_depth = new Array(2 * zip_L_CODES + 1); + zip_length_code = new Array(MAX_MATCH - MIN_MATCH + 1); + zip_dist_code = new Array(512); + zip_base_length = new Array(zip_LENGTH_CODES); + zip_base_dist = new Array(zip_D_CODES); + zip_flag_buf = new Array(parseInt(LIT_BUFSIZE / 8)); + }; + + var zip_deflate_end = function () { + zip_free_queue = zip_qhead = zip_qtail = null; + zip_outbuf = null; + zip_window = null; + zip_d_buf = null; + zip_l_buf = null; + zip_prev = null; + zip_dyn_ltree = null; + zip_dyn_dtree = null; + zip_static_ltree = null; + zip_static_dtree = null; + zip_bl_tree = null; + zip_l_desc = null; + zip_d_desc = null; + zip_bl_desc = null; + zip_bl_count = null; + zip_heap = null; + zip_depth = null; + zip_length_code = null; + zip_dist_code = null; + zip_base_length = null; + zip_base_dist = null; + zip_flag_buf = null; + }; + + var zip_reuse_queue = function (p) { + p.next = zip_free_queue; + zip_free_queue = p; + }; + + var zip_new_queue = function () { + var p; + + if (zip_free_queue != null) { + p = zip_free_queue; + zip_free_queue = zip_free_queue.next; + } + else + p = new zip_DeflateBuffer(); + p.next = null; + p.len = p.off = 0; + + return p; + }; + + var zip_head1 = function (i) { + return zip_prev[WSIZE + i]; + }; + + var zip_head2 = function (i, val) { + return zip_prev[WSIZE + i] = val; + }; + + /* put_byte is used for the compressed output, put_ubyte for the + * uncompressed output. However unlzw() uses window for its + * suffix table instead of its output buffer, so it does not use put_ubyte + * (to be cleaned up). + */ + var zip_put_byte = function (c) { + zip_outbuf[zip_outoff + zip_outcnt++] = c; + if (zip_outoff + zip_outcnt == zip_OUTBUFSIZ) + zip_qoutbuf(); + }; + + /* Output a 16 bit value, lsb first */ + var zip_put_short = function (w) { + w &= 0xffff; + if (zip_outoff + zip_outcnt < zip_OUTBUFSIZ - 2) { + zip_outbuf[zip_outoff + zip_outcnt++] = (w & 0xff); + zip_outbuf[zip_outoff + zip_outcnt++] = (w >>> 8); + } else { + zip_put_byte(w & 0xff); + zip_put_byte(w >>> 8); + } + }; + + /* ========================================================================== + * Insert string s in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of s are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ + var zip_INSERT_STRING = function () { + zip_ins_h = ((zip_ins_h << zip_H_SHIFT) + ^ (zip_window[zip_strstart + MIN_MATCH - 1] & 0xff)) + & zip_HASH_MASK; + zip_hash_head = zip_head1(zip_ins_h); + zip_prev[zip_strstart & zip_WMASK] = zip_hash_head; + zip_head2(zip_ins_h, zip_strstart); + }; + + /* Send a code of the given tree. c and tree must not have side effects */ + var zip_SEND_CODE = function (c, tree) { + zip_send_bits(tree[c].fc, tree[c].dl); + }; + + /* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. dist_code[256] and dist_code[257] are never + * used. + */ + var zip_D_CODE = function (dist) { + return (dist < 256 ? zip_dist_code[dist] + : zip_dist_code[256 + (dist >> 7)]) & 0xff; + }; + + /* ========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ + var zip_SMALLER = function (tree, n, m) { + return tree[n].fc < tree[m].fc || + (tree[n].fc == tree[m].fc && zip_depth[n] <= zip_depth[m]); + }; + + /* ========================================================================== + * read string data + */ + var zip_read_buff = function (buff, offset, n) { + var i; + for (i = 0; i < n && zip_deflate_pos < zip_deflate_data.length; i++) + buff[offset + i] = + zip_deflate_data[zip_deflate_pos++] & 0xff; + return i; + }; + + /* ========================================================================== + * Initialize the "longest match" routines for a new file + */ + var zip_lm_init = function () { + var j; + + /* Initialize the hash table. */ + for (j = 0; j < zip_HASH_SIZE; j++) + zip_prev[WSIZE + j] = 0; + zip_max_lazy_match = zip_configuration_table[zip_compr_level].max_lazy; + zip_good_match = zip_configuration_table[zip_compr_level].good_length; + if (!zip_FULL_SEARCH) + zip_nice_match = zip_configuration_table[zip_compr_level].nice_length; + zip_max_chain_length = zip_configuration_table[zip_compr_level].max_chain; + + zip_strstart = 0; + zip_block_start = 0; + + zip_lookahead = zip_read_buff(zip_window, 0, 2 * WSIZE); + if (zip_lookahead <= 0) { + zip_eofile = true; + zip_lookahead = 0; + return; + } + zip_eofile = false; + /* Make sure that we always have enough lookahead. This is important + * if input comes from a device such as a tty. + */ + while (zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) + zip_fill_window(); + + /* If lookahead < MIN_MATCH, ins_h is garbage, but this is + * not important since only literal bytes will be emitted. + */ + zip_ins_h = 0; + for (j = 0; j < MIN_MATCH - 1; j++) { + zip_ins_h = ((zip_ins_h << zip_H_SHIFT) ^ (zip_window[j] & 0xff)) & zip_HASH_MASK; + } + }; + + /* ========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + */ + var zip_longest_match = function (cur_match) { + var chain_length = zip_max_chain_length; // max hash chain length + var scanp = zip_strstart; // current string + var matchp; // matched string + var len; // length of current match + var best_len = zip_prev_length; // best match length so far + + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + var limit = (zip_strstart > zip_MAX_DIST ? zip_strstart - zip_MAX_DIST : zip_NIL); + + var strendp = zip_strstart + MAX_MATCH; + var scan_end1 = zip_window[scanp + best_len - 1]; + var scan_end = zip_window[scanp + best_len]; + + /* Do not waste too much time if we already have a good match: */ + if (zip_prev_length >= zip_good_match) + chain_length >>= 2; + + do { + matchp = cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ + if (zip_window[matchp + best_len] != scan_end || + zip_window[matchp + best_len - 1] != scan_end1 || + zip_window[matchp] != zip_window[scanp] || + zip_window[++matchp] != zip_window[scanp + 1]) { + continue; + } + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scanp += 2; + matchp++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + scanp < strendp); + + len = MAX_MATCH - (strendp - scanp); + scanp = strendp - MAX_MATCH; + + if (len > best_len) { + zip_match_start = cur_match; + best_len = len; + if (zip_FULL_SEARCH) { + if (len >= MAX_MATCH) break; + } else { + if (len >= zip_nice_match) break; + } + + scan_end1 = zip_window[scanp + best_len - 1]; + scan_end = zip_window[scanp + best_len]; + } + } while ((cur_match = zip_prev[cur_match & zip_WMASK]) > limit + && --chain_length != 0); + + return best_len; + }; + + /* ========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: at least one byte has been read, or eofile is set; + * file reads are performed for at least two bytes (required for the + * translate_eol option). + */ + var zip_fill_window = function () { + var n, m; + + // Amount of free space at the end of the window. + var more = zip_window_size - zip_lookahead - zip_strstart; + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == -1) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + } else if (zip_strstart >= WSIZE + zip_MAX_DIST) { + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + for (n = 0; n < WSIZE; n++) + zip_window[n] = zip_window[n + WSIZE]; + + zip_match_start -= WSIZE; + zip_strstart -= WSIZE; + /* we now have strstart >= MAX_DIST: */ + zip_block_start -= WSIZE; + + for (n = 0; n < zip_HASH_SIZE; n++) { + m = zip_head1(n); + zip_head2(n, m >= WSIZE ? m - WSIZE : zip_NIL); + } + for (n = 0; n < WSIZE; n++) { + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + m = zip_prev[n]; + zip_prev[n] = (m >= WSIZE ? m - WSIZE : zip_NIL); + } + more += WSIZE; + } + // At this point, more >= 2 + if (!zip_eofile) { + n = zip_read_buff(zip_window, zip_strstart + zip_lookahead, more); + if (n <= 0) + zip_eofile = true; + else + zip_lookahead += n; + } + }; + + /* ========================================================================== + * Processes a new input file and return its compressed length. This + * function does not perform lazy evaluationof matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ + var zip_deflate_fast = function () { + while (zip_lookahead != 0 && zip_qhead == null) { + var flush; // set if current block must be flushed + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + zip_INSERT_STRING(); + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (zip_hash_head != zip_NIL && + zip_strstart - zip_hash_head <= zip_MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + zip_match_length = zip_longest_match(zip_hash_head); + /* longest_match() sets match_start */ + if (zip_match_length > zip_lookahead) + zip_match_length = zip_lookahead; + } + if (zip_match_length >= MIN_MATCH) { + flush = zip_ct_tally(zip_strstart - zip_match_start, + zip_match_length - MIN_MATCH); + zip_lookahead -= zip_match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (zip_match_length <= zip_max_lazy_match) { + zip_match_length--; // string at strstart already in hash table + do { + zip_strstart++; + zip_INSERT_STRING(); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since + * the next lookahead bytes will be emitted as literals. + */ + } while (--zip_match_length != 0); + zip_strstart++; + } else { + zip_strstart += zip_match_length; + zip_match_length = 0; + zip_ins_h = zip_window[zip_strstart] & 0xff; + zip_ins_h = ((zip_ins_h << zip_H_SHIFT) ^ (zip_window[zip_strstart + 1] & 0xff)) & zip_HASH_MASK; + } + } else { + /* No match, output a literal byte */ + flush = zip_ct_tally(0, zip_window[zip_strstart] & 0xff); + zip_lookahead--; + zip_strstart++; + } + if (flush) { + zip_flush_block(0); + zip_block_start = zip_strstart; + } + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + while (zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) + zip_fill_window(); + } + }; + + var zip_deflate_better = function () { + /* Process the input block. */ + while (zip_lookahead != 0 && zip_qhead == null) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + zip_INSERT_STRING(); + + /* Find the longest match, discarding those <= prev_length. + */ + zip_prev_length = zip_match_length; + zip_prev_match = zip_match_start; + zip_match_length = MIN_MATCH - 1; + + if (zip_hash_head != zip_NIL && + zip_prev_length < zip_max_lazy_match && + zip_strstart - zip_hash_head <= zip_MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + zip_match_length = zip_longest_match(zip_hash_head); + /* longest_match() sets match_start */ + if (zip_match_length > zip_lookahead) + zip_match_length = zip_lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (zip_match_length == MIN_MATCH && + zip_strstart - zip_match_start > zip_TOO_FAR) { + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + zip_match_length--; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (zip_prev_length >= MIN_MATCH && + zip_match_length <= zip_prev_length) { + var flush; // set if current block must be flushed + flush = zip_ct_tally(zip_strstart - 1 - zip_prev_match, + zip_prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + zip_lookahead -= zip_prev_length - 1; + zip_prev_length -= 2; + do { + zip_strstart++; + zip_INSERT_STRING(); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since the + * next lookahead bytes will always be emitted as literals. + */ + } while (--zip_prev_length != 0); + zip_match_available = 0; + zip_match_length = MIN_MATCH - 1; + zip_strstart++; + if (flush) { + zip_flush_block(0); + zip_block_start = zip_strstart; + } + } else if (zip_match_available != 0) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + if (zip_ct_tally(0, zip_window[zip_strstart - 1] & 0xff)) { + zip_flush_block(0); + zip_block_start = zip_strstart; + } + zip_strstart++; + zip_lookahead--; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + zip_match_available = 1; + zip_strstart++; + zip_lookahead--; + } + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + while (zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) + zip_fill_window(); + } + }; + + var zip_init_deflate = function () { + if (zip_eofile) + return; + zip_bi_buf = 0; + zip_bi_valid = 0; + zip_ct_init(); + zip_lm_init(); + + zip_qhead = null; + zip_outcnt = 0; + zip_outoff = 0; + zip_match_available = 0; + + if (zip_compr_level <= 3) { + zip_prev_length = MIN_MATCH - 1; + zip_match_length = 0; + } + else { + zip_match_length = MIN_MATCH - 1; + zip_match_available = 0; + zip_match_available = 0; + } + + zip_complete = false; + }; + + /* ========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ + var zip_deflate_internal = function (buff, off, buff_size) { + var n; + + if (!zip_initflag) { + zip_init_deflate(); + zip_initflag = true; + if (zip_lookahead == 0) { // empty + zip_complete = true; + return 0; + } + } + + if ((n = zip_qcopy(buff, off, buff_size)) == buff_size) + return buff_size; + + if (zip_complete) + return n; + + if (zip_compr_level <= 3) // optimized for speed + zip_deflate_fast(); + else + zip_deflate_better(); + if (zip_lookahead == 0) { + if (zip_match_available != 0) + zip_ct_tally(0, zip_window[zip_strstart - 1] & 0xff); + zip_flush_block(1); + zip_complete = true; + } + return n + zip_qcopy(buff, n + off, buff_size - n); + }; + + var zip_qcopy = function (buff, off, buff_size) { + var n, i, j; + + n = 0; + while (zip_qhead != null && n < buff_size) { + i = buff_size - n; + if (i > zip_qhead.len) + i = zip_qhead.len; + for (j = 0; j < i; j++) + buff[off + n + j] = zip_qhead.ptr[zip_qhead.off + j]; + + zip_qhead.off += i; + zip_qhead.len -= i; + n += i; + if (zip_qhead.len == 0) { + var p; + p = zip_qhead; + zip_qhead = zip_qhead.next; + zip_reuse_queue(p); + } + } + + if (n == buff_size) + return n; + + if (zip_outoff < zip_outcnt) { + i = buff_size - n; + if (i > zip_outcnt - zip_outoff) + i = zip_outcnt - zip_outoff; + // System.arraycopy(outbuf, outoff, buff, off + n, i); + for (j = 0; j < i; j++) + buff[off + n + j] = zip_outbuf[zip_outoff + j]; + zip_outoff += i; + n += i; + if (zip_outcnt == zip_outoff) + zip_outcnt = zip_outoff = 0; + } + return n; + }; + + /* ========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + */ + var zip_ct_init = function () { + var n; // iterates over tree elements + var bits; // bit counter + var length; // length value + var code; // code value + var dist; // distance index + + if (zip_static_dtree[0].dl != 0) return; // ct_init already called + + zip_l_desc.dyn_tree = zip_dyn_ltree; + zip_l_desc.static_tree = zip_static_ltree; + zip_l_desc.extra_bits = zip_extra_lbits; + zip_l_desc.extra_base = zip_LITERALS + 1; + zip_l_desc.elems = zip_L_CODES; + zip_l_desc.max_length = zip_MAX_BITS; + zip_l_desc.max_code = 0; + + zip_d_desc.dyn_tree = zip_dyn_dtree; + zip_d_desc.static_tree = zip_static_dtree; + zip_d_desc.extra_bits = zip_extra_dbits; + zip_d_desc.extra_base = 0; + zip_d_desc.elems = zip_D_CODES; + zip_d_desc.max_length = zip_MAX_BITS; + zip_d_desc.max_code = 0; + + zip_bl_desc.dyn_tree = zip_bl_tree; + zip_bl_desc.static_tree = null; + zip_bl_desc.extra_bits = zip_extra_blbits; + zip_bl_desc.extra_base = 0; + zip_bl_desc.elems = zip_BL_CODES; + zip_bl_desc.max_length = zip_MAX_BL_BITS; + zip_bl_desc.max_code = 0; + + // Initialize the mapping length (0..255) -> length code (0..28) + length = 0; + for (code = 0; code < zip_LENGTH_CODES - 1; code++) { + zip_base_length[code] = length; + for (n = 0; n < (1 << zip_extra_lbits[code]); n++) + zip_length_code[length++] = code; + } + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + zip_length_code[length - 1] = code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0; code < 16; code++) { + zip_base_dist[code] = dist; + for (n = 0; n < (1 << zip_extra_dbits[code]); n++) { + zip_dist_code[dist++] = code; + } + } + dist >>= 7; // from now on, all distances are divided by 128 + for (; code < zip_D_CODES; code++) { + zip_base_dist[code] = dist << 7; + for (n = 0; n < (1 << (zip_extra_dbits[code] - 7)); n++) + zip_dist_code[256 + dist++] = code; + } + // Construct the codes of the static literal tree + for (bits = 0; bits <= zip_MAX_BITS; bits++) + zip_bl_count[bits] = 0; + n = 0; + while (n <= 143) { + zip_static_ltree[n++].dl = 8; + zip_bl_count[8]++; + } + while (n <= 255) { + zip_static_ltree[n++].dl = 9; + zip_bl_count[9]++; + } + while (n <= 279) { + zip_static_ltree[n++].dl = 7; + zip_bl_count[7]++; + } + while (n <= 287) { + zip_static_ltree[n++].dl = 8; + zip_bl_count[8]++; + } + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + zip_gen_codes(zip_static_ltree, zip_L_CODES + 1); + + /* The static distance tree is trivial: */ + for (n = 0; n < zip_D_CODES; n++) { + zip_static_dtree[n].dl = 5; + zip_static_dtree[n].fc = zip_bi_reverse(n, 5); + } + + // Initialize the first block of the first file: + zip_init_block(); + }; + + /* ========================================================================== + * Initialize a new block. + */ + var zip_init_block = function () { + var n; // iterates over tree elements + + // Initialize the trees. + for (n = 0; n < zip_L_CODES; n++) zip_dyn_ltree[n].fc = 0; + for (n = 0; n < zip_D_CODES; n++) zip_dyn_dtree[n].fc = 0; + for (n = 0; n < zip_BL_CODES; n++) zip_bl_tree[n].fc = 0; + + zip_dyn_ltree[zip_END_BLOCK].fc = 1; + zip_opt_len = zip_static_len = 0; + zip_last_lit = zip_last_dist = zip_last_flags = 0; + zip_flags = 0; + zip_flag_bit = 1; + }; + + /* ========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ + var zip_pqdownheap = function (tree, // the tree to restore + k) { // node to move down + var v = zip_heap[k]; + var j = k << 1; // left son of k + + while (j <= zip_heap_len) { + // Set j to the smallest of the two sons: + if (j < zip_heap_len && + zip_SMALLER(tree, zip_heap[j + 1], zip_heap[j])) + j++; + + // Exit if v is smaller than both sons + if (zip_SMALLER(tree, v, zip_heap[j])) + break; + + // Exchange v with the smallest son + zip_heap[k] = zip_heap[j]; + k = j; + + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + zip_heap[k] = v; + }; + + /* ========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ + var zip_gen_bitlen = function (desc) { // the tree descriptor + var tree = desc.dyn_tree; + var extra = desc.extra_bits; + var base = desc.extra_base; + var max_code = desc.max_code; + var max_length = desc.max_length; + var stree = desc.static_tree; + var h; // heap index + var n, m; // iterate over the tree elements + var bits; // bit length + var xbits; // extra bits + var f; // frequency + var overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= zip_MAX_BITS; bits++) + zip_bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[zip_heap[zip_heap_max]].dl = 0; // root of the heap + + for (h = zip_heap_max + 1; h < zip_HEAP_SIZE; h++) { + n = zip_heap[h]; + bits = tree[tree[n].dl].dl + 1; + if (bits > max_length) { + bits = max_length; + overflow++; + } + tree[n].dl = bits; + // We overwrite tree[n].dl which is no longer needed + + if (n > max_code) + continue; // not a leaf node + + zip_bl_count[bits]++; + xbits = 0; + if (n >= base) + xbits = extra[n - base]; + f = tree[n].fc; + zip_opt_len += f * (bits + xbits); + if (stree != null) + zip_static_len += f * (stree[n].dl + xbits); + } + if (overflow == 0) + return; + + // This happens for example on obj2 and pic of the Calgary corpus + + // Find the first bit length which could increase: + do { + bits = max_length - 1; + while (zip_bl_count[bits] == 0) + bits--; + zip_bl_count[bits]--; // move one leaf down the tree + zip_bl_count[bits + 1] += 2; // move one overflow item as its brother + zip_bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = zip_bl_count[bits]; + while (n != 0) { + m = zip_heap[--h]; + if (m > max_code) + continue; + if (tree[m].dl != bits) { + zip_opt_len += (bits - tree[m].dl) * tree[m].fc; + tree[m].fc = bits; + } + n--; + } + } + }; + + /* ========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ + var zip_gen_codes = function (tree, // the tree to decorate + max_code) { // largest code with non zero frequency + var next_code = new Array(zip_MAX_BITS + 1); // next code value for each bit length + var code = 0; // running code value + var bits; // bit index + var n; // code index + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= zip_MAX_BITS; bits++) { + code = ((code + zip_bl_count[bits - 1]) << 1); + next_code[bits] = code; + } + + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + for (n = 0; n <= max_code; n++) { + var len = tree[n].dl; + if (len == 0) + continue; + // Now reverse the bits + tree[n].fc = zip_bi_reverse(next_code[len]++, len); + } + }; + + /* ========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ + var zip_build_tree = function (desc) { // the tree descriptor + var tree = desc.dyn_tree; + var stree = desc.static_tree; + var elems = desc.elems; + var n, m; // iterate over heap elements + var max_code = -1; // largest code with non zero frequency + var node = elems; // next internal node of the tree + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + zip_heap_len = 0; + zip_heap_max = zip_HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].fc != 0) { + zip_heap[++zip_heap_len] = max_code = n; + zip_depth[n] = 0; + } else + tree[n].dl = 0; + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (zip_heap_len < 2) { + var xnew = zip_heap[++zip_heap_len] = (max_code < 2 ? ++max_code : 0); + tree[xnew].fc = 1; + zip_depth[xnew] = 0; + zip_opt_len--; + if (stree != null) + zip_static_len -= stree[xnew].dl; + // new is 0 or 1 so it does not have extra bits + } + desc.max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = zip_heap_len >> 1; n >= 1; n--) + zip_pqdownheap(tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + n = zip_heap[zip_SMALLEST]; + zip_heap[zip_SMALLEST] = zip_heap[zip_heap_len--]; + zip_pqdownheap(tree, zip_SMALLEST); + + m = zip_heap[zip_SMALLEST]; // m = node of next least frequency + + // keep the nodes sorted by frequency + zip_heap[--zip_heap_max] = n; + zip_heap[--zip_heap_max] = m; + + // Create a new node father of n and m + tree[node].fc = tree[n].fc + tree[m].fc; + if (zip_depth[n] > zip_depth[m] + 1) + zip_depth[node] = zip_depth[n]; + else + zip_depth[node] = zip_depth[m] + 1; + tree[n].dl = tree[m].dl = node; + + // and insert the new node in the heap + zip_heap[zip_SMALLEST] = node++; + zip_pqdownheap(tree, zip_SMALLEST); + + } while (zip_heap_len >= 2); + + zip_heap[--zip_heap_max] = zip_heap[zip_SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + zip_gen_bitlen(desc); + + // The field len is now set, we can generate the bit codes + zip_gen_codes(tree, max_code); + }; + + /* ========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ + var zip_scan_tree = function (tree,// the tree to be scanned + max_code) { // and its largest code of non zero frequency + var n; // iterates over all tree elements + var prevlen = -1; // last emitted length + var curlen; // length of current code + var nextlen = tree[0].dl; // length of next code + var count = 0; // repeat count of the current code + var max_count = 7; // max repeat count + var min_count = 4; // min repeat count + + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } + tree[max_code + 1].dl = 0xffff; // guard + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[n + 1].dl; + if (++count < max_count && curlen == nextlen) + continue; + else if (count < min_count) + zip_bl_tree[curlen].fc += count; + else if (curlen != 0) { + if (curlen != prevlen) + zip_bl_tree[curlen].fc++; + zip_bl_tree[zip_REP_3_6].fc++; + } else if (count <= 10) + zip_bl_tree[zip_REPZ_3_10].fc++; + else + zip_bl_tree[zip_REPZ_11_138].fc++; + count = 0; + prevlen = curlen; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + } else { + max_count = 7; + min_count = 4; + } + } + }; + + /* ========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ + var zip_send_tree = function (tree, // the tree to be scanned + max_code) { // and its largest code of non zero frequency + var n; // iterates over all tree elements + var prevlen = -1; // last emitted length + var curlen; // length of current code + var nextlen = tree[0].dl; // length of next code + var count = 0; // repeat count of the current code + var max_count = 7; // max repeat count + var min_count = 4; // min repeat count + + /* tree[max_code+1].dl = -1; */ + /* guard already set */ + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[n + 1].dl; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { + zip_SEND_CODE(curlen, zip_bl_tree); + } while (--count != 0); + } else if (curlen != 0) { + if (curlen != prevlen) { + zip_SEND_CODE(curlen, zip_bl_tree); + count--; + } + // Assert(count >= 3 && count <= 6, " 3_6?"); + zip_SEND_CODE(zip_REP_3_6, zip_bl_tree); + zip_send_bits(count - 3, 2); + } else if (count <= 10) { + zip_SEND_CODE(zip_REPZ_3_10, zip_bl_tree); + zip_send_bits(count - 3, 3); + } else { + zip_SEND_CODE(zip_REPZ_11_138, zip_bl_tree); + zip_send_bits(count - 11, 7); + } + count = 0; + prevlen = curlen; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + } else { + max_count = 7; + min_count = 4; + } + } + }; + + /* ========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ + var zip_build_bl_tree = function () { + var max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + zip_scan_tree(zip_dyn_ltree, zip_l_desc.max_code); + zip_scan_tree(zip_dyn_dtree, zip_d_desc.max_code); + + // Build the bit length tree: + zip_build_tree(zip_bl_desc); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = zip_BL_CODES - 1; max_blindex >= 3; max_blindex--) { + if (zip_bl_tree[zip_bl_order[max_blindex]].dl != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + zip_opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + return max_blindex; + }; + + /* ========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ + var zip_send_all_trees = function (lcodes, dcodes, blcodes) { // number of codes for each tree + var rank; // index in bl_order + zip_send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + zip_send_bits(dcodes - 1, 5); + zip_send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) { + zip_send_bits(zip_bl_tree[zip_bl_order[rank]].dl, 3); + } + + // send the literal tree + zip_send_tree(zip_dyn_ltree, lcodes - 1); + + // send the distance tree + zip_send_tree(zip_dyn_dtree, dcodes - 1); + }; + + /* ========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ + var zip_flush_block = function (eof) { // true if this is the last block for a file + var opt_lenb, static_lenb; // opt_len and static_len in bytes + var max_blindex; // index of last bit length code of non zero freq + var stored_len; // length of input block + + stored_len = zip_strstart - zip_block_start; + zip_flag_buf[zip_last_flags] = zip_flags; // Save the flags for the last 8 items + + // Construct the literal and distance trees + zip_build_tree(zip_l_desc); + zip_build_tree(zip_d_desc); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = zip_build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (zip_opt_len + 3 + 7) >> 3; + static_lenb = (zip_static_len + 3 + 7) >> 3; + if (static_lenb <= opt_lenb) + opt_lenb = static_lenb; + if (stored_len + 4 <= opt_lenb // 4: two words for the lengths + && zip_block_start >= 0) { + var i; + + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + zip_send_bits((zip_STORED_BLOCK << 1) + eof, 3); + /* send block type */ + zip_bi_windup(); + /* align on byte boundary */ + zip_put_short(stored_len); + zip_put_short(~stored_len); + + // copy block + for (i = 0; i < stored_len; i++) + zip_put_byte(zip_window[zip_block_start + i]); + + } else if (static_lenb == opt_lenb) { + zip_send_bits((zip_STATIC_TREES << 1) + eof, 3); + zip_compress_block(zip_static_ltree, zip_static_dtree); + } else { + zip_send_bits((zip_DYN_TREES << 1) + eof, 3); + zip_send_all_trees(zip_l_desc.max_code + 1, + zip_d_desc.max_code + 1, + max_blindex + 1); + zip_compress_block(zip_dyn_ltree, zip_dyn_dtree); + } + + zip_init_block(); + + if (eof != 0) + zip_bi_windup(); + }; + + /* ========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ + var zip_ct_tally = function (dist, // distance of matched string + lc) { // match length-MIN_MATCH or unmatched char (if dist==0) + zip_l_buf[zip_last_lit++] = lc; + if (dist == 0) { + // lc is the unmatched char + zip_dyn_ltree[lc].fc++; + } else { + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + zip_dyn_ltree[zip_length_code[lc] + zip_LITERALS + 1].fc++; + zip_dyn_dtree[zip_D_CODE(dist)].fc++; + + zip_d_buf[zip_last_dist++] = dist; + zip_flags |= zip_flag_bit; + } + zip_flag_bit <<= 1; + + // Output the flags if they fill a byte + if ((zip_last_lit & 7) == 0) { + zip_flag_buf[zip_last_flags++] = zip_flags; + zip_flags = 0; + zip_flag_bit = 1; + } + // Try to guess if it is profitable to stop the current block here + if (zip_compr_level > 2 && (zip_last_lit & 0xfff) == 0) { + // Compute an upper bound for the compressed length + var out_length = zip_last_lit * 8; + var in_length = zip_strstart - zip_block_start; + var dcode; + + for (dcode = 0; dcode < zip_D_CODES; dcode++) { + out_length += zip_dyn_dtree[dcode].fc * (5 + zip_extra_dbits[dcode]); + } + out_length >>= 3; + if (zip_last_dist < parseInt(zip_last_lit / 2) && + out_length < parseInt(in_length / 2)) + return true; + } + return (zip_last_lit == LIT_BUFSIZE - 1 || + zip_last_dist == zip_DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ + }; + + /* ========================================================================== + * Send the block data compressed using the given Huffman trees + */ + var zip_compress_block = function (ltree, // literal tree + dtree) { // distance tree + var dist; // distance of matched string + var lc; // match length or unmatched char (if dist == 0) + var lx = 0; // running index in l_buf + var dx = 0; // running index in d_buf + var fx = 0; // running index in flag_buf + var flag = 0; // current flags + var code; // the code to send + var extra; // number of extra bits to send + + if (zip_last_lit != 0) do { + if ((lx & 7) == 0) + flag = zip_flag_buf[fx++]; + lc = zip_l_buf[lx++] & 0xff; + if ((flag & 1) == 0) { + zip_SEND_CODE(lc, ltree); + /* send a literal byte */ + } else { + // Here, lc is the match length - MIN_MATCH + code = zip_length_code[lc]; + zip_SEND_CODE(code + zip_LITERALS + 1, ltree); // send the length code + extra = zip_extra_lbits[code]; + if (extra != 0) { + lc -= zip_base_length[code]; + zip_send_bits(lc, extra); // send the extra length bits + } + dist = zip_d_buf[dx++]; + // Here, dist is the match distance - 1 + code = zip_D_CODE(dist); + zip_SEND_CODE(code, dtree); // send the distance code + extra = zip_extra_dbits[code]; + if (extra != 0) { + dist -= zip_base_dist[code]; + zip_send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + flag >>= 1; + } while (lx < zip_last_lit); + + zip_SEND_CODE(zip_END_BLOCK, ltree); + }; + + /* ========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ + var zip_Buf_size = 16; // bit size of bi_buf + var zip_send_bits = function (value, // value to send + length) { // number of bits + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (zip_bi_valid > zip_Buf_size - length) { + zip_bi_buf |= (value << zip_bi_valid); + zip_put_short(zip_bi_buf); + zip_bi_buf = (value >> (zip_Buf_size - zip_bi_valid)); + zip_bi_valid += length - zip_Buf_size; + } else { + zip_bi_buf |= value << zip_bi_valid; + zip_bi_valid += length; + } + }; + + /* ========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ + var zip_bi_reverse = function (code, // the value to invert + len) { // its bit length + var res = 0; + do { + res |= code & 1; + code >>= 1; + res <<= 1; + } while (--len > 0); + return res >> 1; + }; + + /* ========================================================================== + * Write out any remaining bits in an incomplete byte. + */ + var zip_bi_windup = function () { + if (zip_bi_valid > 8) { + zip_put_short(zip_bi_buf); + } else if (zip_bi_valid > 0) { + zip_put_byte(zip_bi_buf); + } + zip_bi_buf = 0; + zip_bi_valid = 0; + }; + + var zip_qoutbuf = function () { + if (zip_outcnt != 0) { + var q, i; + q = zip_new_queue(); + if (zip_qhead == null) + zip_qhead = zip_qtail = q; + else + zip_qtail = zip_qtail.next = q; + q.len = zip_outcnt - zip_outoff; + for (i = 0; i < q.len; i++) + q.ptr[i] = zip_outbuf[zip_outoff + i]; + zip_outcnt = zip_outoff = 0; + } + }; + + function deflate(buffData, level) { + zip_deflate_data = buffData; + zip_deflate_pos = 0; + zip_deflate_start(level); + + var buff = new Array(1024), + pages = [], + totalSize = 0, + i; + + for (i = 0; i < 1024; i++) buff[i] = 0; + while ((i = zip_deflate_internal(buff, 0, buff.length)) > 0) { + var buf = new Buffer(buff.slice(0, i)); + pages.push(buf); + totalSize += buf.length; + } + + if (pages.length == 1) { + return pages[0]; + } + + var result = new Buffer(totalSize), + index = 0; + + for (i = 0; i < pages.length; i++) { + pages[i].copy(result, index); + index = index + pages[i].length + } + + return result; + } + + return { + deflate: function () { + return deflate(inbuf, 8); + } + } +} + +module.exports = function (/*Buffer*/inbuf) { + + var zlib = require("zlib"); + + return { + deflate: function () { + return new JSDeflater(inbuf).deflate(); + }, + + deflateAsync: function (/*Function*/callback) { + var tmp = zlib.createDeflateRaw({chunkSize:(parseInt(inbuf.length / 1024) + 1)*1024}), + parts = [], total = 0; + tmp.on('data', function(data) { + parts.push(data); + total += data.length; + }); + tmp.on('end', function() { + var buf = new Buffer(total), written = 0; + buf.fill(0); + + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + part.copy(buf, written); + written += part.length; + } + callback && callback(buf); + }); + tmp.end(inbuf); + } + } +}; diff --git a/node_modules/adm-zip/methods/index.js b/node_modules/adm-zip/methods/index.js new file mode 100644 index 0000000..ddcbba6 --- /dev/null +++ b/node_modules/adm-zip/methods/index.js @@ -0,0 +1,2 @@ +exports.Deflater = require("./deflater"); +exports.Inflater = require("./inflater"); \ No newline at end of file diff --git a/node_modules/adm-zip/methods/inflater.js b/node_modules/adm-zip/methods/inflater.js new file mode 100644 index 0000000..59536c9 --- /dev/null +++ b/node_modules/adm-zip/methods/inflater.js @@ -0,0 +1,448 @@ +var Buffer = require("buffer").Buffer; + +function JSInflater(/*Buffer*/input) { + + var WSIZE = 0x8000, + slide = new Buffer(0x10000), + windowPos = 0, + fixedTableList = null, + fixedTableDist, + fixedLookup, + bitBuf = 0, + bitLen = 0, + method = -1, + eof = false, + copyLen = 0, + copyDist = 0, + tblList, tblDist, bitList, bitdist, + + inputPosition = 0, + + MASK_BITS = [0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff], + LENS = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0], + LEXT = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99], + DISTS = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577], + DEXT = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13], + BITORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; + + function HuffTable(clen, cnum, cval, blist, elist, lookupm) { + + this.status = 0; + this.root = null; + this.maxbit = 0; + + var el, f, tail, + offsets = [], + countTbl = [], + sTbl = [], + values = [], + tentry = {extra: 0, bitcnt: 0, lbase: 0, next: null}; + + tail = this.root = null; + for(var i = 0; i < 0x11; i++) { countTbl[i] = 0; sTbl[i] = 0; offsets[i] = 0; } + for(i = 0; i < 0x120; i++) values[i] = 0; + + el = cnum > 256 ? clen[256] : 16; + + var pidx = -1; + while (++pidx < cnum) countTbl[clen[pidx]]++; + + if(countTbl[0] == cnum) return; + + for(var j = 1; j <= 16; j++) if(countTbl[j] != 0) break; + var bitLen = j; + for(i = 16; i != 0; i--) if(countTbl[i] != 0) break; + var maxLen = i; + + lookupm < j && (lookupm = j); + + var dCodes = 1 << j; + for(; j < i; j++, dCodes <<= 1) + if((dCodes -= countTbl[j]) < 0) { + this.status = 2; + this.maxbit = lookupm; + return; + } + + if((dCodes -= countTbl[i]) < 0) { + this.status = 2; + this.maxbit = lookupm; + return; + } + + countTbl[i] += dCodes; + offsets[1] = j = 0; + pidx = 1; + var xp = 2; + while(--i > 0) offsets[xp++] = (j += countTbl[pidx++]); + pidx = 0; + i = 0; + do { + (j = clen[pidx++]) && (values[offsets[j]++] = i); + } while(++i < cnum); + cnum = offsets[maxLen]; + offsets[0] = i = 0; + pidx = 0; + + var level = -1, + w = sTbl[0] = 0, + cnode = null, + tblCnt = 0, + tblStack = []; + + for(; bitLen <= maxLen; bitLen++) { + var kccnt = countTbl[bitLen]; + while(kccnt-- > 0) { + while(bitLen > w + sTbl[1 + level]) { + w += sTbl[1 + level]; + level++; + tblCnt = (tblCnt = maxLen - w) > lookupm ? lookupm : tblCnt; + if((f = 1 << (j = bitLen - w)) > kccnt + 1) { + f -= kccnt + 1; + xp = bitLen; + while(++j < tblCnt) { + if((f <<= 1) <= countTbl[++xp]) break; + f -= countTbl[xp]; + } + } + if(w + j > el && w < el) j = el - w; + tblCnt = 1 << j; + sTbl[1 + level] = j; + cnode = []; + while (cnode.length < tblCnt) cnode.push({extra: 0, bitcnt: 0, lbase: 0, next: null}); + if (tail == null) { + tail = this.root = {next:null, list:null}; + } else { + tail = tail.next = {next:null, list:null} + } + tail.next = null; + tail.list = cnode; + + tblStack[level] = cnode; + + if(level > 0) { + offsets[level] = i; + tentry.bitcnt = sTbl[level]; + tentry.extra = 16 + j; + tentry.next = cnode; + j = (i & ((1 << w) - 1)) >> (w - sTbl[level]); + + tblStack[level-1][j].extra = tentry.extra; + tblStack[level-1][j].bitcnt = tentry.bitcnt; + tblStack[level-1][j].lbase = tentry.lbase; + tblStack[level-1][j].next = tentry.next; + } + } + tentry.bitcnt = bitLen - w; + if(pidx >= cnum) + tentry.extra = 99; + else if(values[pidx] < cval) { + tentry.extra = (values[pidx] < 256 ? 16 : 15); + tentry.lbase = values[pidx++]; + } else { + tentry.extra = elist[values[pidx] - cval]; + tentry.lbase = blist[values[pidx++] - cval]; + } + + f = 1 << (bitLen - w); + for(j = i >> w; j < tblCnt; j += f) { + cnode[j].extra = tentry.extra; + cnode[j].bitcnt = tentry.bitcnt; + cnode[j].lbase = tentry.lbase; + cnode[j].next = tentry.next; + } + for(j = 1 << (bitLen - 1); (i & j) != 0; j >>= 1) + i ^= j; + i ^= j; + while((i & ((1 << w) - 1)) != offsets[level]) { + w -= sTbl[level]; + level--; + } + } + } + + this.maxbit = sTbl[1]; + this.status = ((dCodes != 0 && maxLen != 1) ? 1 : 0); + } + + function addBits(n) { + while(bitLen < n) { + bitBuf |= input[inputPosition++] << bitLen; + bitLen += 8; + } + return bitBuf; + } + + function cutBits(n) { + bitLen -= n; + return bitBuf >>= n; + } + + function maskBits(n) { + while(bitLen < n) { + bitBuf |= input[inputPosition++] << bitLen; + bitLen += 8; + } + var res = bitBuf & MASK_BITS[n]; + bitBuf >>= n; + bitLen -= n; + return res; + } + + function codes(buff, off, size) { + var e, t; + if(size == 0) return 0; + + var n = 0; + for(;;) { + t = tblList.list[addBits(bitList) & MASK_BITS[bitList]]; + e = t.extra; + while(e > 16) { + if(e == 99) return -1; + cutBits(t.bitcnt); + e -= 16; + t = t.next[addBits(e) & MASK_BITS[e]]; + e = t.extra; + } + cutBits(t.bitcnt); + if(e == 16) { + windowPos &= WSIZE - 1; + buff[off + n++] = slide[windowPos++] = t.lbase; + if(n == size) return size; + continue; + } + if(e == 15) break; + + copyLen = t.lbase + maskBits(e); + t = tblDist.list[addBits(bitdist) & MASK_BITS[bitdist]]; + e = t.extra; + + while(e > 16) { + if(e == 99) return -1; + cutBits(t.bitcnt); + e -= 16; + t = t.next[addBits(e) & MASK_BITS[e]]; + e = t.extra + } + cutBits(t.bitcnt); + copyDist = windowPos - t.lbase - maskBits(e); + + while(copyLen > 0 && n < size) { + copyLen--; + copyDist &= WSIZE - 1; + windowPos &= WSIZE - 1; + buff[off + n++] = slide[windowPos++] = slide[copyDist++]; + } + + if(n == size) return size; + } + + method = -1; // done + return n; + } + + function stored(buff, off, size) { + cutBits(bitLen & 7); + var n = maskBits(0x10); + if(n != ((~maskBits(0x10)) & 0xffff)) return -1; + copyLen = n; + + n = 0; + while(copyLen > 0 && n < size) { + copyLen--; + windowPos &= WSIZE - 1; + buff[off + n++] = slide[windowPos++] = maskBits(8); + } + + if(copyLen == 0) method = -1; + return n; + } + + function fixed(buff, off, size) { + var fixed_bd = 0; + if(fixedTableList == null) { + var lengths = []; + + for(var symbol = 0; symbol < 144; symbol++) lengths[symbol] = 8; + for(; symbol < 256; symbol++) lengths[symbol] = 9; + for(; symbol < 280; symbol++) lengths[symbol] = 7; + for(; symbol < 288; symbol++) lengths[symbol] = 8; + + fixedLookup = 7; + + var htbl = new HuffTable(lengths, 288, 257, LENS, LEXT, fixedLookup); + + if(htbl.status != 0) return -1; + + fixedTableList = htbl.root; + fixedLookup = htbl.maxbit; + + for(symbol = 0; symbol < 30; symbol++) lengths[symbol] = 5; + fixed_bd = 5; + + htbl = new HuffTable(lengths, 30, 0, DISTS, DEXT, fixed_bd); + if(htbl.status > 1) { + fixedTableList = null; + return -1; + } + fixedTableDist = htbl.root; + fixed_bd = htbl.maxbit; + } + + tblList = fixedTableList; + tblDist = fixedTableDist; + bitList = fixedLookup; + bitdist = fixed_bd; + return codes(buff, off, size); + } + + function dynamic(buff, off, size) { + var ll = new Array(0x023C); + + for (var m = 0; m < 0x023C; m++) ll[m] = 0; + + var llencnt = 257 + maskBits(5), + dcodescnt = 1 + maskBits(5), + bitlencnt = 4 + maskBits(4); + + if(llencnt > 286 || dcodescnt > 30) return -1; + + for(var j = 0; j < bitlencnt; j++) ll[BITORDER[j]] = maskBits(3); + for(; j < 19; j++) ll[BITORDER[j]] = 0; + + // build decoding table for trees--single level, 7 bit lookup + bitList = 7; + var hufTable = new HuffTable(ll, 19, 19, null, null, bitList); + if(hufTable.status != 0) + return -1; // incomplete code set + + tblList = hufTable.root; + bitList = hufTable.maxbit; + var lencnt = llencnt + dcodescnt, + i = 0, + lastLen = 0; + while(i < lencnt) { + var hufLcode = tblList.list[addBits(bitList) & MASK_BITS[bitList]]; + j = hufLcode.bitcnt; + cutBits(j); + j = hufLcode.lbase; + if(j < 16) + ll[i++] = lastLen = j; + else if(j == 16) { + j = 3 + maskBits(2); + if(i + j > lencnt) return -1; + while(j-- > 0) ll[i++] = lastLen; + } else if(j == 17) { + j = 3 + maskBits(3); + if(i + j > lencnt) return -1; + while(j-- > 0) ll[i++] = 0; + lastLen = 0; + } else { + j = 11 + maskBits(7); + if(i + j > lencnt) return -1; + while(j-- > 0) ll[i++] = 0; + lastLen = 0; + } + } + bitList = 9; + hufTable = new HuffTable(ll, llencnt, 257, LENS, LEXT, bitList); + bitList == 0 && (hufTable.status = 1); + + if (hufTable.status != 0) return -1; + + tblList = hufTable.root; + bitList = hufTable.maxbit; + + for(i = 0; i < dcodescnt; i++) ll[i] = ll[i + llencnt]; + bitdist = 6; + hufTable = new HuffTable(ll, dcodescnt, 0, DISTS, DEXT, bitdist); + tblDist = hufTable.root; + bitdist = hufTable.maxbit; + + if((bitdist == 0 && llencnt > 257) || hufTable.status != 0) return -1; + + return codes(buff, off, size); + } + + return { + inflate : function(/*Buffer*/outputBuffer) { + tblList = null; + + var size = outputBuffer.length, + offset = 0, i; + + while(offset < size) { + if(eof && method == -1) return; + if(copyLen > 0) { + if(method != 0) { + while(copyLen > 0 && offset < size) { + copyLen--; + copyDist &= WSIZE - 1; + windowPos &= WSIZE - 1; + outputBuffer[offset++] = (slide[windowPos++] = slide[copyDist++]); + } + } else { + while(copyLen > 0 && offset < size) { + copyLen--; + windowPos &= WSIZE - 1; + outputBuffer[offset++] = (slide[windowPos++] = maskBits(8)); + } + copyLen == 0 && (method = -1); // done + } + if (offset == size) return; + } + + if(method == -1) { + if(eof) break; + eof = maskBits(1) != 0; + method = maskBits(2); + tblList = null; + copyLen = 0; + } + switch(method) { + case 0: i = stored(outputBuffer, offset, size - offset); break; + case 1: i = tblList != null ? codes(outputBuffer, offset, size - offset) : fixed(outputBuffer, offset, size - offset); break; + case 2: i = tblList != null ? codes(outputBuffer, offset, size - offset) : dynamic(outputBuffer, offset, size - offset); break; + default: i = -1; break; + } + + if(i == -1) return; + offset += i; + } + } + }; +} + +module.exports = function(/*Buffer*/inbuf) { + var zlib = require("zlib"); + return { + inflateAsync : function(/*Function*/callback) { + var tmp = zlib.createInflateRaw(), + parts = [], total = 0; + tmp.on('data', function(data) { + parts.push(data); + total += data.length; + }); + tmp.on('end', function() { + var buf = new Buffer(total), written = 0; + buf.fill(0); + + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + part.copy(buf, written); + written += part.length; + } + callback && callback(buf); + }); + tmp.end(inbuf) + }, + + inflate : function(/*Buffer*/outputBuffer) { + var x = { + x: new JSInflater(inbuf) + }; + x.x.inflate(outputBuffer); + delete(x.x); + } + } +}; diff --git a/node_modules/adm-zip/package.json b/node_modules/adm-zip/package.json new file mode 100644 index 0000000..21a0abf --- /dev/null +++ b/node_modules/adm-zip/package.json @@ -0,0 +1,39 @@ +{ + "name": "adm-zip", + "version": "0.4.4", + "description": "A Javascript implementation of zip for nodejs. Allows user to create or extract zip files both in memory or to/from disk", + "keywords": [ + "zip", + "methods", + "archive", + "unzip" + ], + "homepage": "http://github.com/cthackers/adm-zip", + "author": { + "name": "Nasca Iacob", + "email": "sy@another-d-mention.ro", + "url": "https://github.com/cthackers" + }, + "bugs": { + "url": "https://github.com/cthackers/adm-zip/issues", + "email": "sy@another-d-mention.ro" + }, + "licenses": [ + { + "type": "MIT", + "url": "https://raw.github.com/cthackers/adm-zip/master/MIT-LICENSE.txt" + } + ], + "main": "adm-zip.js", + "repository": { + "type": "git", + "url": "https://github.com/cthackers/adm-zip.git" + }, + "engines": { + "node": ">=0.3.0" + }, + "readme": "# ADM-ZIP for NodeJS\r\n\r\nADM-ZIP is a pure JavaScript implementation for zip data compression for [NodeJS](http://nodejs.org/). \r\n\r\n# Installation\r\n\r\nWith [npm](http://npmjs.org) do:\r\n\r\n $ npm install adm-zip\r\n\t\r\n## What is it good for?\r\nThe library allows you to:\r\n\r\n* decompress zip files directly to disk or in memory buffers\r\n* compress files and store them to disk in .zip format or in compressed buffers\r\n* update content of/add new/delete files from an existing .zip\r\n\r\n# Dependencies\r\nThere are no other nodeJS libraries that ADM-ZIP is dependent of\r\n\r\n# Examples\r\n\r\n## Basic usage\r\n```javascript\r\n\r\n\tvar AdmZip = require('adm-zip');\r\n\r\n\t// reading archives\r\n\tvar zip = new AdmZip(\"./my_file.zip\");\r\n\tvar zipEntries = zip.getEntries(); // an array of ZipEntry records\r\n\r\n\tzipEntries.forEach(function(zipEntry) {\r\n\t console.log(zipEntry.toString()); // outputs zip entries information\r\n\t\tif (zipEntry.entryName == \"my_file.txt\") {\r\n\t\t console.log(zipEntry.data.toString('utf8')); \r\n\t\t}\r\n\t});\r\n\t// outputs the content of some_folder/my_file.txt\r\n\tconsole.log(zip.readAsText(\"some_folder/my_file.txt\")); \r\n\t// extracts the specified file to the specified location\r\n\tzip.extractEntryTo(/*entry name*/\"some_folder/my_file.txt\", /*target path*/\"/home/me/tempfolder\", /*maintainEntryPath*/false, /*overwrite*/true);\r\n\t// extracts everything\r\n\tzip.extractAllTo(/*target path*/\"/home/me/zipcontent/\", /*overwrite*/true);\r\n\t\r\n\t\r\n\t// creating archives\r\n\tvar zip = new AdmZip();\r\n\t\r\n\t// add file directly\r\n\tzip.addFile(\"test.txt\", new Buffer(\"inner content of the file\"), \"entry comment goes here\");\r\n\t// add local file\r\n\tzip.addLocalFile(\"/home/me/some_picture.png\");\r\n\t// get everything as a buffer\r\n\tvar willSendthis = zip.toBuffer();\r\n\t// or write everything to disk\r\n\tzip.writeZip(/*target file name*/\"/home/me/files.zip\");\r\n\t\r\n\t\r\n\t// ... more examples in the wiki\r\n```\r\n\r\nFor more detailed information please check out the [wiki](https://github.com/cthackers/adm-zip/wiki).\r\n\r\n[![build status](https://secure.travis-ci.org/cthackers/adm-zip.png)](http://travis-ci.org/cthackers/adm-zip)\r\n", + "readmeFilename": "README.md", + "_id": "adm-zip@0.4.4", + "_from": "adm-zip@0.4.4" +} diff --git a/node_modules/adm-zip/test/assets/attributes_test.zip b/node_modules/adm-zip/test/assets/attributes_test.zip new file mode 100644 index 0000000..d57bfc0 Binary files /dev/null and b/node_modules/adm-zip/test/assets/attributes_test.zip differ diff --git a/node_modules/adm-zip/test/assets/attributes_test/New folder/hidden.txt b/node_modules/adm-zip/test/assets/attributes_test/New folder/hidden.txt new file mode 100644 index 0000000..3c3ca55 --- /dev/null +++ b/node_modules/adm-zip/test/assets/attributes_test/New folder/hidden.txt @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/adm-zip/test/assets/attributes_test/New folder/hidden_readonly.txt b/node_modules/adm-zip/test/assets/attributes_test/New folder/hidden_readonly.txt new file mode 100644 index 0000000..3c3ca55 --- /dev/null +++ b/node_modules/adm-zip/test/assets/attributes_test/New folder/hidden_readonly.txt @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/adm-zip/test/assets/attributes_test/New folder/readonly.txt b/node_modules/adm-zip/test/assets/attributes_test/New folder/readonly.txt new file mode 100644 index 0000000..3c3ca55 --- /dev/null +++ b/node_modules/adm-zip/test/assets/attributes_test/New folder/readonly.txt @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/adm-zip/test/assets/attributes_test/New folder/somefile.txt b/node_modules/adm-zip/test/assets/attributes_test/New folder/somefile.txt new file mode 100644 index 0000000..3c3ca55 --- /dev/null +++ b/node_modules/adm-zip/test/assets/attributes_test/New folder/somefile.txt @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/adm-zip/test/assets/attributes_test/asd/New Text Document.txt b/node_modules/adm-zip/test/assets/attributes_test/asd/New Text Document.txt new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/adm-zip/test/assets/attributes_test/blank file.txt b/node_modules/adm-zip/test/assets/attributes_test/blank file.txt new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/adm-zip/test/assets/fast.zip b/node_modules/adm-zip/test/assets/fast.zip new file mode 100644 index 0000000..f4ed17b Binary files /dev/null and b/node_modules/adm-zip/test/assets/fast.zip differ diff --git a/node_modules/adm-zip/test/assets/fastest.zip b/node_modules/adm-zip/test/assets/fastest.zip new file mode 100644 index 0000000..f4ed17b Binary files /dev/null and b/node_modules/adm-zip/test/assets/fastest.zip differ diff --git a/node_modules/adm-zip/test/assets/linux_arc.zip b/node_modules/adm-zip/test/assets/linux_arc.zip new file mode 100644 index 0000000..188eccb Binary files /dev/null and b/node_modules/adm-zip/test/assets/linux_arc.zip differ diff --git a/node_modules/adm-zip/test/assets/maximum.zip b/node_modules/adm-zip/test/assets/maximum.zip new file mode 100644 index 0000000..86a8ec7 Binary files /dev/null and b/node_modules/adm-zip/test/assets/maximum.zip differ diff --git a/node_modules/adm-zip/test/assets/normal.zip b/node_modules/adm-zip/test/assets/normal.zip new file mode 100644 index 0000000..b4602c9 Binary files /dev/null and b/node_modules/adm-zip/test/assets/normal.zip differ diff --git a/node_modules/adm-zip/test/assets/store.zip b/node_modules/adm-zip/test/assets/store.zip new file mode 100644 index 0000000..e2add30 Binary files /dev/null and b/node_modules/adm-zip/test/assets/store.zip differ diff --git a/node_modules/adm-zip/test/assets/ultra.zip b/node_modules/adm-zip/test/assets/ultra.zip new file mode 100644 index 0000000..86a8ec7 Binary files /dev/null and b/node_modules/adm-zip/test/assets/ultra.zip differ diff --git a/node_modules/adm-zip/test/index.js b/node_modules/adm-zip/test/index.js new file mode 100644 index 0000000..70acb51 --- /dev/null +++ b/node_modules/adm-zip/test/index.js @@ -0,0 +1,5 @@ +var Attr = require("../util").FileAttr, + Zip = require("../adm-zip"), + fs = require("fs"); + +//zip.addLocalFile("./test/readonly.txt"); diff --git a/node_modules/adm-zip/util/constants.js b/node_modules/adm-zip/util/constants.js new file mode 100644 index 0000000..0548054 --- /dev/null +++ b/node_modules/adm-zip/util/constants.js @@ -0,0 +1,84 @@ +module.exports = { + /* The local file header */ + LOCHDR : 30, // LOC header size + LOCSIG : 0x04034b50, // "PK\003\004" + LOCVER : 4, // version needed to extract + LOCFLG : 6, // general purpose bit flag + LOCHOW : 8, // compression method + LOCTIM : 10, // modification time (2 bytes time, 2 bytes date) + LOCCRC : 14, // uncompressed file crc-32 value + LOCSIZ : 18, // compressed size + LOCLEN : 22, // uncompressed size + LOCNAM : 26, // filename length + LOCEXT : 28, // extra field length + + /* The Data descriptor */ + EXTSIG : 0x08074b50, // "PK\007\008" + EXTHDR : 16, // EXT header size + EXTCRC : 4, // uncompressed file crc-32 value + EXTSIZ : 8, // compressed size + EXTLEN : 12, // uncompressed size + + /* The central directory file header */ + CENHDR : 46, // CEN header size + CENSIG : 0x02014b50, // "PK\001\002" + CENVEM : 4, // version made by + CENVER : 6, // version needed to extract + CENFLG : 8, // encrypt, decrypt flags + CENHOW : 10, // compression method + CENTIM : 12, // modification time (2 bytes time, 2 bytes date) + CENCRC : 16, // uncompressed file crc-32 value + CENSIZ : 20, // compressed size + CENLEN : 24, // uncompressed size + CENNAM : 28, // filename length + CENEXT : 30, // extra field length + CENCOM : 32, // file comment length + CENDSK : 34, // volume number start + CENATT : 36, // internal file attributes + CENATX : 38, // external file attributes (host system dependent) + CENOFF : 42, // LOC header offset + + /* The entries in the end of central directory */ + ENDHDR : 22, // END header size + ENDSIG : 0x06054b50, // "PK\005\006" + ENDSUB : 8, // number of entries on this disk + ENDTOT : 10, // total number of entries + ENDSIZ : 12, // central directory size in bytes + ENDOFF : 16, // offset of first CEN header + ENDCOM : 20, // zip file comment length + + /* Compression methods */ + STORED : 0, // no compression + SHRUNK : 1, // shrunk + REDUCED1 : 2, // reduced with compression factor 1 + REDUCED2 : 3, // reduced with compression factor 2 + REDUCED3 : 4, // reduced with compression factor 3 + REDUCED4 : 5, // reduced with compression factor 4 + IMPLODED : 6, // imploded + // 7 reserved + DEFLATED : 8, // deflated + ENHANCED_DEFLATED: 9, // enhanced deflated + PKWARE : 10,// PKWare DCL imploded + // 11 reserved + BZIP2 : 12, // compressed using BZIP2 + // 13 reserved + LZMA : 14, // LZMA + // 15-17 reserved + IBM_TERSE : 18, // compressed using IBM TERSE + IBM_LZ77 : 19, //IBM LZ77 z + + /* General purpose bit flag */ + FLG_ENC : 0, // encripted file + FLG_COMP1 : 1, // compression option + FLG_COMP2 : 2, // compression option + FLG_DESC : 4, // data descriptor + FLG_ENH : 8, // enhanced deflation + FLG_STR : 16, // strong encryption + FLG_LNG : 1024, // language encoding + FLG_MSK : 4096, // mask header values + + /* Load type */ + FILE : 0, + BUFFER : 1, + NONE : 2 +}; diff --git a/node_modules/adm-zip/util/errors.js b/node_modules/adm-zip/util/errors.js new file mode 100644 index 0000000..db5d69e --- /dev/null +++ b/node_modules/adm-zip/util/errors.js @@ -0,0 +1,35 @@ +module.exports = { + /* Header error messages */ + "INVALID_LOC" : "Invalid LOC header (bad signature)", + "INVALID_CEN" : "Invalid CEN header (bad signature)", + "INVALID_END" : "Invalid END header (bad signature)", + + /* ZipEntry error messages*/ + "NO_DATA" : "Nothing to decompress", + "BAD_CRC" : "CRC32 checksum failed", + "FILE_IN_THE_WAY" : "There is a file in the way: %s", + "UNKNOWN_METHOD" : "Invalid/unsupported compression method", + + /* Inflater error messages */ + "AVAIL_DATA" : "inflate::Available inflate data did not terminate", + "INVALID_DISTANCE" : "inflate::Invalid literal/length or distance code in fixed or dynamic block", + "TO_MANY_CODES" : "inflate::Dynamic block code description: too many length or distance codes", + "INVALID_REPEAT_LEN" : "inflate::Dynamic block code description: repeat more than specified lengths", + "INVALID_REPEAT_FIRST" : "inflate::Dynamic block code description: repeat lengths with no first length", + "INCOMPLETE_CODES" : "inflate::Dynamic block code description: code lengths codes incomplete", + "INVALID_DYN_DISTANCE": "inflate::Dynamic block code description: invalid distance code lengths", + "INVALID_CODES_LEN": "inflate::Dynamic block code description: invalid literal/length code lengths", + "INVALID_STORE_BLOCK" : "inflate::Stored block length did not match one's complement", + "INVALID_BLOCK_TYPE" : "inflate::Invalid block type (type == 3)", + + /* ADM-ZIP error messages */ + "CANT_EXTRACT_FILE" : "Could not extract the file", + "CANT_OVERRIDE" : "Target file already exists", + "NO_ZIP" : "No zip file was loaded", + "NO_ENTRY" : "Entry doesn't exist", + "DIRECTORY_CONTENT_ERROR" : "A directory cannot have content", + "FILE_NOT_FOUND" : "File not found: %s", + "NOT_IMPLEMENTED" : "Not implemented", + "INVALID_FILENAME" : "Invalid filename", + "INVALID_FORMAT" : "Invalid or unsupported zip format. No END header found" +}; \ No newline at end of file diff --git a/node_modules/adm-zip/util/fattr.js b/node_modules/adm-zip/util/fattr.js new file mode 100644 index 0000000..2191ec1 --- /dev/null +++ b/node_modules/adm-zip/util/fattr.js @@ -0,0 +1,84 @@ +var fs = require("fs"), + pth = require("path"); + +fs.existsSync = fs.existsSync || pth.existsSync; + +module.exports = function(/*String*/path) { + + var _path = path || "", + _permissions = 0, + _obj = newAttr(), + _stat = null; + + function newAttr() { + return { + directory : false, + readonly : false, + hidden : false, + executable : false, + mtime : 0, + atime : 0 + } + } + + if (_path && fs.existsSync(_path)) { + _stat = fs.statSync(_path); + _obj.directory = _stat.isDirectory(); + _obj.mtime = _stat.mtime; + _obj.atime = _stat.atime; + _obj.executable = !!(1 & parseInt ((_stat.mode & parseInt ("777", 8)).toString (8)[0])); + _obj.readonly = !!(2 & parseInt ((_stat.mode & parseInt ("777", 8)).toString (8)[0])); + _obj.hidden = pth.basename(_path)[0] === "."; + } else { + console.warn("Invalid path: " + _path) + } + + return { + + get directory () { + return _obj.directory; + }, + + get readOnly () { + return _obj.readonly; + }, + + get hidden () { + return _obj.hidden; + }, + + get mtime () { + return _obj.mtime; + }, + + get atime () { + return _obj.atime; + }, + + + get executable () { + return _obj.executable; + }, + + decodeAttributes : function(val) { + + }, + + encodeAttributes : function (val) { + + }, + + toString : function() { + return '{\n' + + '\t"path" : "' + _path + ",\n" + + '\t"isDirectory" : ' + _obj.directory + ",\n" + + '\t"isReadOnly" : ' + _obj.readonly + ",\n" + + '\t"isHidden" : ' + _obj.hidden + ",\n" + + '\t"isExecutable" : ' + _obj.executable + ",\n" + + '\t"mTime" : ' + _obj.mtime + "\n" + + '\t"aTime" : ' + _obj.atime + "\n" + + '}'; + } + } + +}; diff --git a/node_modules/adm-zip/util/index.js b/node_modules/adm-zip/util/index.js new file mode 100644 index 0000000..935fc1a --- /dev/null +++ b/node_modules/adm-zip/util/index.js @@ -0,0 +1,4 @@ +module.exports = require("./utils"); +module.exports.Constants = require("./constants"); +module.exports.Errors = require("./errors"); +module.exports.FileAttr = require("./fattr"); \ No newline at end of file diff --git a/node_modules/adm-zip/util/utils.js b/node_modules/adm-zip/util/utils.js new file mode 100644 index 0000000..b14db8c --- /dev/null +++ b/node_modules/adm-zip/util/utils.js @@ -0,0 +1,145 @@ +var fs = require("fs"), + pth = require('path'); + +fs.existsSync = fs.existsSync || pth.existsSync; + +module.exports = (function() { + + var crcTable = [], + Constants = require('./constants'), + Errors = require('./errors'), + + PATH_SEPARATOR = pth.normalize("/"); + + + function mkdirSync(/*String*/path) { + var resolvedPath = path.split(PATH_SEPARATOR)[0]; + path.split(PATH_SEPARATOR).forEach(function(name) { + if (!name || name.substr(-1,1) == ":") return; + resolvedPath += PATH_SEPARATOR + name; + var stat; + try { + stat = fs.statSync(resolvedPath); + } catch (e) { + fs.mkdirSync(resolvedPath); + } + if (stat && stat.isFile()) + throw Errors.FILE_IN_THE_WAY.replace("%s", resolvedPath); + }); + } + + function findSync(/*String*/root, /*RegExp*/pattern, /*Boolean*/recoursive) { + if (typeof pattern === 'boolean') { + recoursive = pattern; + pattern = undefined; + } + var files = []; + fs.readdirSync(root).forEach(function(file) { + var path = pth.join(root, file); + + if (fs.statSync(path).isDirectory() && recoursive) + files = files.concat(findSync(path, pattern, recoursive)); + + if (!pattern || pattern.test(path)) { + files.push(pth.normalize(path) + (fs.statSync(path).isDirectory() ? PATH_SEPARATOR : "")); + } + + }); + return files; + } + + return { + makeDir : function(/*String*/path) { + mkdirSync(path); + }, + + crc32 : function(buf) { + var b = new Buffer(4); + if (!crcTable.length) { + for (var n = 0; n < 256; n++) { + var c = n; + for (var k = 8; --k >= 0;) // + if ((c & 1) != 0) { c = 0xedb88320 ^ (c >>> 1); } else { c = c >>> 1; } + if (c < 0) { + b.writeInt32LE(c, 0); + c = b.readUInt32LE(0); + } + crcTable[n] = c; + } + } + var crc = 0, off = 0, len = buf.length, c1 = ~crc; + while(--len >= 0) c1 = crcTable[(c1 ^ buf[off++]) & 0xff] ^ (c1 >>> 8); + crc = ~c1; + b.writeInt32LE(crc & 0xffffffff, 0); + return b.readUInt32LE(0); + }, + + methodToString : function(/*Number*/method) { + switch (method) { + case Constants.STORED: + return 'STORED (' + method + ')'; + case Constants.DEFLATED: + return 'DEFLATED (' + method + ')'; + default: + return 'UNSUPPORTED (' + method + ')' + } + + }, + + writeFileTo : function(/*String*/path, /*Buffer*/content, /*Boolean*/overwrite, /*Number*/attr) { + if (fs.existsSync(path)) { + if (!overwrite) + return false; // cannot overwite + + var stat = fs.statSync(path); + if (stat.isDirectory()) { + return false; + } + } + var folder = pth.dirname(path); + if (!fs.existsSync(folder)) { + mkdirSync(folder); + } + + var fd; + try { + fd = fs.openSync(path, 'w', 438); // 0666 + } catch(e) { + fs.chmodSync(path, 438); + fd = fs.openSync(path, 'w', 438); + } + if (fd) { + fs.writeSync(fd, content, 0, content.length, 0); + fs.closeSync(fd); + } + fs.chmodSync(path, attr || 438); + return true; + }, + + findFiles : function(/*String*/path) { + return findSync(path, true); + }, + + getAttributes : function(/*String*/path) { + + }, + + setAttributes : function(/*String*/path) { + + }, + + toBuffer : function(input) { + if (Buffer.isBuffer(input)) { + return input; + } else { + if (input.length == 0) { + return new Buffer(0) + } + return new Buffer(input, 'utf8'); + } + }, + + Constants : Constants, + Errors : Errors + } +})(); diff --git a/node_modules/adm-zip/zipEntry.js b/node_modules/adm-zip/zipEntry.js new file mode 100644 index 0000000..02c3172 --- /dev/null +++ b/node_modules/adm-zip/zipEntry.js @@ -0,0 +1,224 @@ +var Utils = require("./util"), + Headers = require("./headers"), + Constants = Utils.Constants, + Methods = require("./methods"); + +module.exports = function (/*Buffer*/input) { + + var _entryHeader = new Headers.EntryHeader(), + _entryName = new Buffer(0), + _comment = new Buffer(0), + _isDirectory = false, + uncompressedData = null, + _extra = new Buffer(0); + + function getCompressedDataFromZip() { + if (!input || !Buffer.isBuffer(input)) { + return new Buffer(0); + } + _entryHeader.loadDataHeaderFromBinary(input); + return input.slice(_entryHeader.realDataOffset, _entryHeader.realDataOffset + _entryHeader.compressedSize) + } + + function crc32OK(data) { + // if bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written + if (_entryHeader.flags & 0x8 != 0x8) { + if (Utils.crc32(data) != _entryHeader.crc) { + return false; + } + } else { + // @TODO: load and check data descriptor header + // The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure + // (optionally preceded by a 4-byte signature) immediately after the compressed data: + } + return true; + } + + function decompress(/*Boolean*/async, /*Function*/callback) { + if (_isDirectory) { + if (async && callback) { + callback(new Buffer(0), Utils.Errors.DIRECTORY_CONTENT_ERROR); //si added error. + } + return new Buffer(0); + } + + var compressedData = getCompressedDataFromZip(); + if (compressedData.length == 0) { + if (async && callback) callback(compressedData, Utils.Errors.NO_DATA);//si added error. + return compressedData; + } + + var data = new Buffer(_entryHeader.size); + data.fill(0); + + switch (_entryHeader.method) { + case Utils.Constants.STORED: + compressedData.copy(data); + if (!crc32OK(data)) { + if (async && callback) callback(data, Utils.Errors.BAD_CRC);//si added error + return Utils.Errors.BAD_CRC; + } else {//si added otherwise did not seem to return data. + if (async && callback) callback(data); + return data; + } + break; + case Utils.Constants.DEFLATED: + var inflater = new Methods.Inflater(compressedData); + if (!async) { + inflater.inflate(data); + if (!crc32OK(data)) { + console.warn(Utils.Errors.BAD_CRC + " " + _entryName.toString()) + } + return data; + } else { + inflater.inflateAsync(function(result) { + result.copy(data, 0); + if (crc32OK(data)) { + if (callback) callback(data, Utils.Errors.BAD_CRC); //si added error + } else { //si added otherwise did not seem to return data. + if (callback) callback(data); + } + }) + } + break; + default: + if (async && callback) callback(new Buffer(0), Utils.Errors.UNKNOWN_METHOD); + return Utils.Errors.UNKNOWN_METHOD; + } + } + + function compress(/*Boolean*/async, /*Function*/callback) { + if ((!uncompressedData || !uncompressedData.length) && Buffer.isBuffer(input)) { + // no data set or the data wasn't changed to require recompression + if (async && callback) callback(getCompressedDataFromZip()); + return getCompressedDataFromZip(); + } + + if (uncompressedData.length && !_isDirectory) { + var compressedData; + // Local file header + switch (_entryHeader.method) { + case Utils.Constants.STORED: + _entryHeader.compressedSize = _entryHeader.size; + + compressedData = new Buffer(uncompressedData.length); + uncompressedData.copy(compressedData); + + if (async && callback) callback(compressedData); + return compressedData; + + break; + default: + case Utils.Constants.DEFLATED: + + var deflater = new Methods.Deflater(uncompressedData); + if (!async) { + var deflated = deflater.deflate(); + _entryHeader.compressedSize = deflated.length; + return deflated; + } else { + deflater.deflateAsync(function(data) { + compressedData = new Buffer(data.length); + _entryHeader.compressedSize = data.length; + data.copy(compressedData); + callback && callback(compressedData); + }) + } + deflater = null; + break; + } + } else { + if (async && callback) { + callback(new Buffer(0)); + } else { + return new Buffer(0); + } + } + } + + return { + get entryName () { return _entryName.toString(); }, + get rawEntryName() { return _entryName; }, + set entryName (val) { + _entryName = Utils.toBuffer(val); + var lastChar = _entryName[_entryName.length - 1]; + _isDirectory = (lastChar == 47) || (lastChar == 92); + _entryHeader.fileNameLength = _entryName.length; + }, + + get extra () { return _extra; }, + set extra (val) { + _extra = val; + _entryHeader.extraLength = val.length; + }, + + get comment () { return _comment.toString(); }, + set comment (val) { + _comment = Utils.toBuffer(val); + _entryHeader.commentLength = _comment.length; + }, + + get name () { var n = _entryName.toString(); return _isDirectory ? n.substr(n.length - 1).split("/").pop() : n.split("/").pop(); }, + get isDirectory () { return _isDirectory }, + + getCompressedData : function() { + return compress(false, null) + }, + + getCompressedDataAsync : function(/*Function*/callback) { + compress(true, callback) + }, + + setData : function(value) { + uncompressedData = Utils.toBuffer(value); + if (!_isDirectory && uncompressedData.length) { + _entryHeader.size = uncompressedData.length; + _entryHeader.method = Utils.Constants.DEFLATED; + _entryHeader.crc = Utils.crc32(value); + } else { // folders and blank files should be stored + _entryHeader.method = Utils.Constants.STORED; + } + }, + + getData : function() { + return decompress(false, null); + }, + + getDataAsync : function(/*Function*/callback) { + decompress(true, callback) + }, + + set header(/*Buffer*/data) { + _entryHeader.loadFromBinary(data); + }, + + get header() { + return _entryHeader; + }, + + packHeader : function() { + var header = _entryHeader.entryHeaderToBinary(); + // add + _entryName.copy(header, Utils.Constants.CENHDR); + if (_entryHeader.extraLength) { + _extra.copy(header, Utils.Constants.CENHDR + _entryName.length) + } + if (_entryHeader.commentLength) { + _comment.copy(header, Utils.Constants.CENHDR + _entryName.length + _entryHeader.extraLength, _comment.length); + } + return header; + }, + + toString : function() { + return '{\n' + + '\t"entryName" : "' + _entryName.toString() + "\",\n" + + '\t"name" : "' + _entryName.toString().split("/").pop() + "\",\n" + + '\t"comment" : "' + _comment.toString() + "\",\n" + + '\t"isDirectory" : ' + _isDirectory + ",\n" + + '\t"header" : ' + _entryHeader.toString().replace(/\t/mg, "\t\t") + ",\n" + + '\t"compressedData" : <' + (input && input.length + " bytes buffer" || "null") + ">\n" + + '\t"data" : <' + (uncompressedData && uncompressedData.length + " bytes buffer" || "null") + ">\n" + + '}'; + } + } +}; diff --git a/node_modules/adm-zip/zipFile.js b/node_modules/adm-zip/zipFile.js new file mode 100644 index 0000000..f066d7e --- /dev/null +++ b/node_modules/adm-zip/zipFile.js @@ -0,0 +1,311 @@ +var ZipEntry = require("./zipEntry"), + Headers = require("./headers"), + Utils = require("./util"); + +module.exports = function(/*String|Buffer*/input, /*Number*/inputType) { + var entryList = [], + entryTable = {}, + _comment = new Buffer(0), + filename = "", + fs = require("fs"), + inBuffer = null, + mainHeader = new Headers.MainHeader(); + + if (inputType == Utils.Constants.FILE) { + // is a filename + filename = input; + inBuffer = fs.readFileSync(filename); + readMainHeader(); + } else if (inputType == Utils.Constants.BUFFER) { + // is a memory buffer + inBuffer = input; + readMainHeader(); + } else { + // none. is a new file + } + + function readEntries() { + entryTable = {}; + entryList = new Array(mainHeader.diskEntries); // total number of entries + var index = mainHeader.offset; // offset of first CEN header + for(var i = 0; i < entryList.length; i++) { + + var tmp = index, + entry = new ZipEntry(inBuffer); + entry.header = inBuffer.slice(tmp, tmp += Utils.Constants.CENHDR); + + entry.entryName = inBuffer.slice(tmp, tmp += entry.header.fileNameLength); + + if (entry.header.extraLength) { + entry.extra = inBuffer.slice(tmp, tmp += entry.header.extraLength); + } + + if (entry.header.commentLength) + entry.comment = inBuffer.slice(tmp, tmp + entry.header.commentLength); + + index += entry.header.entryHeaderSize; + + entryList[i] = entry; + entryTable[entry.entryName] = entry; + } + } + + function readMainHeader() { + var i = inBuffer.length - Utils.Constants.ENDHDR, // END header size + n = Math.max(0, i - 0xFFFF), // 0xFFFF is the max zip file comment length + endOffset = 0; // Start offset of the END header + + for (i; i >= n; i--) { + if (inBuffer[i] != 0x50) continue; // quick check that the byte is 'P' + if (inBuffer.readUInt32LE(i) == Utils.Constants.ENDSIG) { // "PK\005\006" + endOffset = i; + break; + } + } + if (!endOffset) + throw Utils.Errors.INVALID_FORMAT; + + mainHeader.loadFromBinary(inBuffer.slice(endOffset, endOffset + Utils.Constants.ENDHDR)); + if (mainHeader.commentLength) { + _comment = inBuffer.slice(endOffset + Utils.Constants.ENDHDR); + } + readEntries(); + } + + return { + /** + * Returns an array of ZipEntry objects existent in the current opened archive + * @return Array + */ + get entries () { + return entryList; + }, + + /** + * Archive comment + * @return {String} + */ + get comment () { return _comment.toString(); }, + set comment(val) { + mainHeader.commentLength = val.length; + _comment = val; + }, + + /** + * Returns a reference to the entry with the given name or null if entry is inexistent + * + * @param entryName + * @return ZipEntry + */ + getEntry : function(/*String*/entryName) { + return entryTable[entryName] || null; + }, + + /** + * Adds the given entry to the entry list + * + * @param entry + */ + setEntry : function(/*ZipEntry*/entry) { + entryList.push(entry); + entryTable[entry.entryName] = entry; + mainHeader.totalEntries = entryList.length; + }, + + /** + * Removes the entry with the given name from the entry list. + * + * If the entry is a directory, then all nested files and directories will be removed + * @param entryName + */ + deleteEntry : function(/*String*/entryName) { + var entry = entryTable[entryName]; + if (entry && entry.isDirectory) { + var _self = this; + this.getEntryChildren(entry).forEach(function(child) { + if (child.entryName != entryName) { + _self.deleteEntry(child.entryName) + } + }) + } + entryList.splice(entryList.indexOf(entry), 1); + delete(entryTable[entryName]); + mainHeader.totalEntries = entryList.length; + }, + + /** + * Iterates and returns all nested files and directories of the given entry + * + * @param entry + * @return Array + */ + getEntryChildren : function(/*ZipEntry*/entry) { + if (entry.isDirectory) { + var list = [], + name = entry.entryName, + len = name.length; + + entryList.forEach(function(zipEntry) { + if (zipEntry.entryName.substr(0, len) == name) { + list.push(zipEntry); + } + }); + return list; + } + return [] + }, + + /** + * Returns the zip file + * + * @return Buffer + */ + compressToBuffer : function() { + if (entryList.length > 1) { + entryList.sort(function(a, b) { + var nameA = a.entryName.toLowerCase(); + var nameB = b.entryName.toLowerCase(); + if (nameA < nameB) {return -1} + if (nameA > nameB) {return 1} + return 0; + }); + } + + var totalSize = 0, + dataBlock = [], + entryHeaders = [], + dindex = 0; + + mainHeader.size = 0; + mainHeader.offset = 0; + + entryList.forEach(function(entry) { + entry.header.offset = dindex; + + // compress data and set local and entry header accordingly. Reason why is called first + var compressedData = entry.getCompressedData(); + // data header + var dataHeader = entry.header.dataHeaderToBinary(); + var postHeader = new Buffer(entry.entryName + entry.extra.toString()); + var dataLength = dataHeader.length + postHeader.length + compressedData.length; + + dindex += dataLength; + + dataBlock.push(dataHeader); + dataBlock.push(postHeader); + dataBlock.push(compressedData); + + var entryHeader = entry.packHeader(); + entryHeaders.push(entryHeader); + mainHeader.size += entryHeader.length; + totalSize += (dataLength + entryHeader.length); + }); + + totalSize += mainHeader.mainHeaderSize; // also includes zip file comment length + // point to end of data and begining of central directory first record + mainHeader.offset = dindex; + + dindex = 0; + var outBuffer = new Buffer(totalSize); + dataBlock.forEach(function(content) { + content.copy(outBuffer, dindex); // write data blocks + dindex += content.length; + }); + entryHeaders.forEach(function(content) { + content.copy(outBuffer, dindex); // write central directory entries + dindex += content.length; + }); + + var mh = mainHeader.toBinary(); + if (_comment) { + _comment.copy(mh, Utils.Constants.ENDHDR); // add zip file comment + } + + mh.copy(outBuffer, dindex); // write main header + + return outBuffer + }, + + toAsyncBuffer : function(/*Function*/onSuccess,/*Function*/onFail,/*Function*/onItemStart,/*Function*/onItemEnd) { + if (entryList.length > 1) { + entryList.sort(function(a, b) { + var nameA = a.entryName.toLowerCase(); + var nameB = b.entryName.toLowerCase(); + if (nameA > nameB) {return -1} + if (nameA < nameB) {return 1} + return 0; + }); + } + + var totalSize = 0, + dataBlock = [], + entryHeaders = [], + dindex = 0; + + mainHeader.size = 0; + mainHeader.offset = 0; + + var compress=function(entryList){ + var self=arguments.callee; + var entry; + if(entryList.length){ + var entry=entryList.pop(); + var name=entry.entryName + entry.extra.toString(); + if(onItemStart)onItemStart(name); + entry.getCompressedDataAsync(function(compressedData){ + if(onItemEnd)onItemEnd(name); + + entry.header.offset = dindex; + // data header + var dataHeader = entry.header.dataHeaderToBinary(); + var postHeader = new Buffer(name); + var dataLength = dataHeader.length + postHeader.length + compressedData.length; + + dindex += dataLength; + + dataBlock.push(dataHeader); + dataBlock.push(postHeader); + dataBlock.push(compressedData); + + var entryHeader = entry.packHeader(); + entryHeaders.push(entryHeader); + mainHeader.size += entryHeader.length; + totalSize += (dataLength + entryHeader.length); + + if(entryList.length){ + self(entryList); + }else{ + + + totalSize += mainHeader.mainHeaderSize; // also includes zip file comment length + // point to end of data and begining of central directory first record + mainHeader.offset = dindex; + + dindex = 0; + var outBuffer = new Buffer(totalSize); + dataBlock.forEach(function(content) { + content.copy(outBuffer, dindex); // write data blocks + dindex += content.length; + }); + entryHeaders.forEach(function(content) { + content.copy(outBuffer, dindex); // write central directory entries + dindex += content.length; + }); + + var mh = mainHeader.toBinary(); + if (_comment) { + _comment.copy(mh, Utils.Constants.ENDHDR); // add zip file comment + } + + mh.copy(outBuffer, dindex); // write main header + + onSuccess(outBuffer); + } + }); + } + }; + + compress(entryList); + } + } +}; diff --git a/node_modules/express/History.md b/node_modules/express/History.md new file mode 100644 index 0000000..be89c8e --- /dev/null +++ b/node_modules/express/History.md @@ -0,0 +1,3025 @@ +4.13.3 / 2015-08-02 +=================== + + * Fix infinite loop condition using `mergeParams: true` + * Fix inner numeric indices incorrectly altering parent `req.params` + +4.13.2 / 2015-07-31 +=================== + + * deps: accepts@~1.2.12 + - deps: mime-types@~2.1.4 + * deps: array-flatten@1.1.1 + - perf: enable strict mode + * deps: path-to-regexp@0.1.7 + - Fix regression with escaped round brackets and matching groups + * deps: type-is@~1.6.6 + - deps: mime-types@~2.1.4 + +4.13.1 / 2015-07-05 +=================== + + * deps: accepts@~1.2.10 + - deps: mime-types@~2.1.2 + * deps: qs@4.0.0 + - Fix dropping parameters like `hasOwnProperty` + - Fix various parsing edge cases + * deps: type-is@~1.6.4 + - deps: mime-types@~2.1.2 + - perf: enable strict mode + - perf: remove argument reassignment + +4.13.0 / 2015-06-20 +=================== + + * Add settings to debug output + * Fix `res.format` error when only `default` provided + * Fix issue where `next('route')` in `app.param` would incorrectly skip values + * Fix hiding platform issues with `decodeURIComponent` + - Only `URIError`s are a 400 + * Fix using `*` before params in routes + * Fix using capture groups before params in routes + * Simplify `res.cookie` to call `res.append` + * Use `array-flatten` module for flattening arrays + * deps: accepts@~1.2.9 + - deps: mime-types@~2.1.1 + - perf: avoid argument reassignment & argument slice + - perf: avoid negotiator recursive construction + - perf: enable strict mode + - perf: remove unnecessary bitwise operator + * deps: cookie@0.1.3 + - perf: deduce the scope of try-catch deopt + - perf: remove argument reassignments + * deps: escape-html@1.0.2 + * deps: etag@~1.7.0 + - Always include entity length in ETags for hash length extensions + - Generate non-Stats ETags using MD5 only (no longer CRC32) + - Improve stat performance by removing hashing + - Improve support for JXcore + - Remove base64 padding in ETags to shorten + - Support "fake" stats objects in environments without fs + - Use MD5 instead of MD4 in weak ETags over 1KB + * deps: finalhandler@0.4.0 + - Fix a false-positive when unpiping in Node.js 0.8 + - Support `statusCode` property on `Error` objects + - Use `unpipe` module for unpiping requests + - deps: escape-html@1.0.2 + - deps: on-finished@~2.3.0 + - perf: enable strict mode + - perf: remove argument reassignment + * deps: fresh@0.3.0 + - Add weak `ETag` matching support + * deps: on-finished@~2.3.0 + - Add defined behavior for HTTP `CONNECT` requests + - Add defined behavior for HTTP `Upgrade` requests + - deps: ee-first@1.1.1 + * deps: path-to-regexp@0.1.6 + * deps: send@0.13.0 + - Allow Node.js HTTP server to set `Date` response header + - Fix incorrectly removing `Content-Location` on 304 response + - Improve the default redirect response headers + - Send appropriate headers on default error response + - Use `http-errors` for standard emitted errors + - Use `statuses` instead of `http` module for status messages + - deps: escape-html@1.0.2 + - deps: etag@~1.7.0 + - deps: fresh@0.3.0 + - deps: on-finished@~2.3.0 + - perf: enable strict mode + - perf: remove unnecessary array allocations + * deps: serve-static@~1.10.0 + - Add `fallthrough` option + - Fix reading options from options prototype + - Improve the default redirect response headers + - Malformed URLs now `next()` instead of 400 + - deps: escape-html@1.0.2 + - deps: send@0.13.0 + - perf: enable strict mode + - perf: remove argument reassignment + * deps: type-is@~1.6.3 + - deps: mime-types@~2.1.1 + - perf: reduce try block size + - perf: remove bitwise operations + * perf: enable strict mode + * perf: isolate `app.render` try block + * perf: remove argument reassignments in application + * perf: remove argument reassignments in request prototype + * perf: remove argument reassignments in response prototype + * perf: remove argument reassignments in routing + * perf: remove argument reassignments in `View` + * perf: skip attempting to decode zero length string + * perf: use saved reference to `http.STATUS_CODES` + +4.12.4 / 2015-05-17 +=================== + + * deps: accepts@~1.2.7 + - deps: mime-types@~2.0.11 + - deps: negotiator@0.5.3 + * deps: debug@~2.2.0 + - deps: ms@0.7.1 + * deps: depd@~1.0.1 + * deps: etag@~1.6.0 + - Improve support for JXcore + - Support "fake" stats objects in environments without `fs` + * deps: finalhandler@0.3.6 + - deps: debug@~2.2.0 + - deps: on-finished@~2.2.1 + * deps: on-finished@~2.2.1 + - Fix `isFinished(req)` when data buffered + * deps: proxy-addr@~1.0.8 + - deps: ipaddr.js@1.0.1 + * deps: qs@2.4.2 + - Fix allowing parameters like `constructor` + * deps: send@0.12.3 + - deps: debug@~2.2.0 + - deps: depd@~1.0.1 + - deps: etag@~1.6.0 + - deps: ms@0.7.1 + - deps: on-finished@~2.2.1 + * deps: serve-static@~1.9.3 + - deps: send@0.12.3 + * deps: type-is@~1.6.2 + - deps: mime-types@~2.0.11 + +4.12.3 / 2015-03-17 +=================== + + * deps: accepts@~1.2.5 + - deps: mime-types@~2.0.10 + * deps: debug@~2.1.3 + - Fix high intensity foreground color for bold + - deps: ms@0.7.0 + * deps: finalhandler@0.3.4 + - deps: debug@~2.1.3 + * deps: proxy-addr@~1.0.7 + - deps: ipaddr.js@0.1.9 + * deps: qs@2.4.1 + - Fix error when parameter `hasOwnProperty` is present + * deps: send@0.12.2 + - Throw errors early for invalid `extensions` or `index` options + - deps: debug@~2.1.3 + * deps: serve-static@~1.9.2 + - deps: send@0.12.2 + * deps: type-is@~1.6.1 + - deps: mime-types@~2.0.10 + +4.12.2 / 2015-03-02 +=================== + + * Fix regression where `"Request aborted"` is logged using `res.sendFile` + +4.12.1 / 2015-03-01 +=================== + + * Fix constructing application with non-configurable prototype properties + * Fix `ECONNRESET` errors from `res.sendFile` usage + * Fix `req.host` when using "trust proxy" hops count + * Fix `req.protocol`/`req.secure` when using "trust proxy" hops count + * Fix wrong `code` on aborted connections from `res.sendFile` + * deps: merge-descriptors@1.0.0 + +4.12.0 / 2015-02-23 +=================== + + * Fix `"trust proxy"` setting to inherit when app is mounted + * Generate `ETag`s for all request responses + - No longer restricted to only responses for `GET` and `HEAD` requests + * Use `content-type` to parse `Content-Type` headers + * deps: accepts@~1.2.4 + - Fix preference sorting to be stable for long acceptable lists + - deps: mime-types@~2.0.9 + - deps: negotiator@0.5.1 + * deps: cookie-signature@1.0.6 + * deps: send@0.12.1 + - Always read the stat size from the file + - Fix mutating passed-in `options` + - deps: mime@1.3.4 + * deps: serve-static@~1.9.1 + - deps: send@0.12.1 + * deps: type-is@~1.6.0 + - fix argument reassignment + - fix false-positives in `hasBody` `Transfer-Encoding` check + - support wildcard for both type and subtype (`*/*`) + - deps: mime-types@~2.0.9 + +4.11.2 / 2015-02-01 +=================== + + * Fix `res.redirect` double-calling `res.end` for `HEAD` requests + * deps: accepts@~1.2.3 + - deps: mime-types@~2.0.8 + * deps: proxy-addr@~1.0.6 + - deps: ipaddr.js@0.1.8 + * deps: type-is@~1.5.6 + - deps: mime-types@~2.0.8 + +4.11.1 / 2015-01-20 +=================== + + * deps: send@0.11.1 + - Fix root path disclosure + * deps: serve-static@~1.8.1 + - Fix redirect loop in Node.js 0.11.14 + - Fix root path disclosure + - deps: send@0.11.1 + +4.11.0 / 2015-01-13 +=================== + + * Add `res.append(field, val)` to append headers + * Deprecate leading `:` in `name` for `app.param(name, fn)` + * Deprecate `req.param()` -- use `req.params`, `req.body`, or `req.query` instead + * Deprecate `app.param(fn)` + * Fix `OPTIONS` responses to include the `HEAD` method properly + * Fix `res.sendFile` not always detecting aborted connection + * Match routes iteratively to prevent stack overflows + * deps: accepts@~1.2.2 + - deps: mime-types@~2.0.7 + - deps: negotiator@0.5.0 + * deps: send@0.11.0 + - deps: debug@~2.1.1 + - deps: etag@~1.5.1 + - deps: ms@0.7.0 + - deps: on-finished@~2.2.0 + * deps: serve-static@~1.8.0 + - deps: send@0.11.0 + +4.10.8 / 2015-01-13 +=================== + + * Fix crash from error within `OPTIONS` response handler + * deps: proxy-addr@~1.0.5 + - deps: ipaddr.js@0.1.6 + +4.10.7 / 2015-01-04 +=================== + + * Fix `Allow` header for `OPTIONS` to not contain duplicate methods + * Fix incorrect "Request aborted" for `res.sendFile` when `HEAD` or 304 + * deps: debug@~2.1.1 + * deps: finalhandler@0.3.3 + - deps: debug@~2.1.1 + - deps: on-finished@~2.2.0 + * deps: methods@~1.1.1 + * deps: on-finished@~2.2.0 + * deps: serve-static@~1.7.2 + - Fix potential open redirect when mounted at root + * deps: type-is@~1.5.5 + - deps: mime-types@~2.0.7 + +4.10.6 / 2014-12-12 +=================== + + * Fix exception in `req.fresh`/`req.stale` without response headers + +4.10.5 / 2014-12-10 +=================== + + * Fix `res.send` double-calling `res.end` for `HEAD` requests + * deps: accepts@~1.1.4 + - deps: mime-types@~2.0.4 + * deps: type-is@~1.5.4 + - deps: mime-types@~2.0.4 + +4.10.4 / 2014-11-24 +=================== + + * Fix `res.sendfile` logging standard write errors + +4.10.3 / 2014-11-23 +=================== + + * Fix `res.sendFile` logging standard write errors + * deps: etag@~1.5.1 + * deps: proxy-addr@~1.0.4 + - deps: ipaddr.js@0.1.5 + * deps: qs@2.3.3 + - Fix `arrayLimit` behavior + +4.10.2 / 2014-11-09 +=================== + + * Correctly invoke async router callback asynchronously + * deps: accepts@~1.1.3 + - deps: mime-types@~2.0.3 + * deps: type-is@~1.5.3 + - deps: mime-types@~2.0.3 + +4.10.1 / 2014-10-28 +=================== + + * Fix handling of URLs containing `://` in the path + * deps: qs@2.3.2 + - Fix parsing of mixed objects and values + +4.10.0 / 2014-10-23 +=================== + + * Add support for `app.set('views', array)` + - Views are looked up in sequence in array of directories + * Fix `res.send(status)` to mention `res.sendStatus(status)` + * Fix handling of invalid empty URLs + * Use `content-disposition` module for `res.attachment`/`res.download` + - Sends standards-compliant `Content-Disposition` header + - Full Unicode support + * Use `path.resolve` in view lookup + * deps: debug@~2.1.0 + - Implement `DEBUG_FD` env variable support + * deps: depd@~1.0.0 + * deps: etag@~1.5.0 + - Improve string performance + - Slightly improve speed for weak ETags over 1KB + * deps: finalhandler@0.3.2 + - Terminate in progress response only on error + - Use `on-finished` to determine request status + - deps: debug@~2.1.0 + - deps: on-finished@~2.1.1 + * deps: on-finished@~2.1.1 + - Fix handling of pipelined requests + * deps: qs@2.3.0 + - Fix parsing of mixed implicit and explicit arrays + * deps: send@0.10.1 + - deps: debug@~2.1.0 + - deps: depd@~1.0.0 + - deps: etag@~1.5.0 + - deps: on-finished@~2.1.1 + * deps: serve-static@~1.7.1 + - deps: send@0.10.1 + +4.9.8 / 2014-10-17 +================== + + * Fix `res.redirect` body when redirect status specified + * deps: accepts@~1.1.2 + - Fix error when media type has invalid parameter + - deps: negotiator@0.4.9 + +4.9.7 / 2014-10-10 +================== + + * Fix using same param name in array of paths + +4.9.6 / 2014-10-08 +================== + + * deps: accepts@~1.1.1 + - deps: mime-types@~2.0.2 + - deps: negotiator@0.4.8 + * deps: serve-static@~1.6.4 + - Fix redirect loop when index file serving disabled + * deps: type-is@~1.5.2 + - deps: mime-types@~2.0.2 + +4.9.5 / 2014-09-24 +================== + + * deps: etag@~1.4.0 + * deps: proxy-addr@~1.0.3 + - Use `forwarded` npm module + * deps: send@0.9.3 + - deps: etag@~1.4.0 + * deps: serve-static@~1.6.3 + - deps: send@0.9.3 + +4.9.4 / 2014-09-19 +================== + + * deps: qs@2.2.4 + - Fix issue with object keys starting with numbers truncated + +4.9.3 / 2014-09-18 +================== + + * deps: proxy-addr@~1.0.2 + - Fix a global leak when multiple subnets are trusted + - deps: ipaddr.js@0.1.3 + +4.9.2 / 2014-09-17 +================== + + * Fix regression for empty string `path` in `app.use` + * Fix `router.use` to accept array of middleware without path + * Improve error message for bad `app.use` arguments + +4.9.1 / 2014-09-16 +================== + + * Fix `app.use` to accept array of middleware without path + * deps: depd@0.4.5 + * deps: etag@~1.3.1 + * deps: send@0.9.2 + - deps: depd@0.4.5 + - deps: etag@~1.3.1 + - deps: range-parser@~1.0.2 + * deps: serve-static@~1.6.2 + - deps: send@0.9.2 + +4.9.0 / 2014-09-08 +================== + + * Add `res.sendStatus` + * Invoke callback for sendfile when client aborts + - Applies to `res.sendFile`, `res.sendfile`, and `res.download` + - `err` will be populated with request aborted error + * Support IP address host in `req.subdomains` + * Use `etag` to generate `ETag` headers + * deps: accepts@~1.1.0 + - update `mime-types` + * deps: cookie-signature@1.0.5 + * deps: debug@~2.0.0 + * deps: finalhandler@0.2.0 + - Set `X-Content-Type-Options: nosniff` header + - deps: debug@~2.0.0 + * deps: fresh@0.2.4 + * deps: media-typer@0.3.0 + - Throw error when parameter format invalid on parse + * deps: qs@2.2.3 + - Fix issue where first empty value in array is discarded + * deps: range-parser@~1.0.2 + * deps: send@0.9.1 + - Add `lastModified` option + - Use `etag` to generate `ETag` header + - deps: debug@~2.0.0 + - deps: fresh@0.2.4 + * deps: serve-static@~1.6.1 + - Add `lastModified` option + - deps: send@0.9.1 + * deps: type-is@~1.5.1 + - fix `hasbody` to be true for `content-length: 0` + - deps: media-typer@0.3.0 + - deps: mime-types@~2.0.1 + * deps: vary@~1.0.0 + - Accept valid `Vary` header string as `field` + +4.8.8 / 2014-09-04 +================== + + * deps: send@0.8.5 + - Fix a path traversal issue when using `root` + - Fix malicious path detection for empty string path + * deps: serve-static@~1.5.4 + - deps: send@0.8.5 + +4.8.7 / 2014-08-29 +================== + + * deps: qs@2.2.2 + - Remove unnecessary cloning + +4.8.6 / 2014-08-27 +================== + + * deps: qs@2.2.0 + - Array parsing fix + - Performance improvements + +4.8.5 / 2014-08-18 +================== + + * deps: send@0.8.3 + - deps: destroy@1.0.3 + - deps: on-finished@2.1.0 + * deps: serve-static@~1.5.3 + - deps: send@0.8.3 + +4.8.4 / 2014-08-14 +================== + + * deps: qs@1.2.2 + * deps: send@0.8.2 + - Work around `fd` leak in Node.js 0.10 for `fs.ReadStream` + * deps: serve-static@~1.5.2 + - deps: send@0.8.2 + +4.8.3 / 2014-08-10 +================== + + * deps: parseurl@~1.3.0 + * deps: qs@1.2.1 + * deps: serve-static@~1.5.1 + - Fix parsing of weird `req.originalUrl` values + - deps: parseurl@~1.3.0 + - deps: utils-merge@1.0.0 + +4.8.2 / 2014-08-07 +================== + + * deps: qs@1.2.0 + - Fix parsing array of objects + +4.8.1 / 2014-08-06 +================== + + * fix incorrect deprecation warnings on `res.download` + * deps: qs@1.1.0 + - Accept urlencoded square brackets + - Accept empty values in implicit array notation + +4.8.0 / 2014-08-05 +================== + + * add `res.sendFile` + - accepts a file system path instead of a URL + - requires an absolute path or `root` option specified + * deprecate `res.sendfile` -- use `res.sendFile` instead + * support mounted app as any argument to `app.use()` + * deps: qs@1.0.2 + - Complete rewrite + - Limits array length to 20 + - Limits object depth to 5 + - Limits parameters to 1,000 + * deps: send@0.8.1 + - Add `extensions` option + * deps: serve-static@~1.5.0 + - Add `extensions` option + - deps: send@0.8.1 + +4.7.4 / 2014-08-04 +================== + + * fix `res.sendfile` regression for serving directory index files + * deps: send@0.7.4 + - Fix incorrect 403 on Windows and Node.js 0.11 + - Fix serving index files without root dir + * deps: serve-static@~1.4.4 + - deps: send@0.7.4 + +4.7.3 / 2014-08-04 +================== + + * deps: send@0.7.3 + - Fix incorrect 403 on Windows and Node.js 0.11 + * deps: serve-static@~1.4.3 + - Fix incorrect 403 on Windows and Node.js 0.11 + - deps: send@0.7.3 + +4.7.2 / 2014-07-27 +================== + + * deps: depd@0.4.4 + - Work-around v8 generating empty stack traces + * deps: send@0.7.2 + - deps: depd@0.4.4 + * deps: serve-static@~1.4.2 + +4.7.1 / 2014-07-26 +================== + + * deps: depd@0.4.3 + - Fix exception when global `Error.stackTraceLimit` is too low + * deps: send@0.7.1 + - deps: depd@0.4.3 + * deps: serve-static@~1.4.1 + +4.7.0 / 2014-07-25 +================== + + * fix `req.protocol` for proxy-direct connections + * configurable query parser with `app.set('query parser', parser)` + - `app.set('query parser', 'extended')` parse with "qs" module + - `app.set('query parser', 'simple')` parse with "querystring" core module + - `app.set('query parser', false)` disable query string parsing + - `app.set('query parser', true)` enable simple parsing + * deprecate `res.json(status, obj)` -- use `res.status(status).json(obj)` instead + * deprecate `res.jsonp(status, obj)` -- use `res.status(status).jsonp(obj)` instead + * deprecate `res.send(status, body)` -- use `res.status(status).send(body)` instead + * deps: debug@1.0.4 + * deps: depd@0.4.2 + - Add `TRACE_DEPRECATION` environment variable + - Remove non-standard grey color from color output + - Support `--no-deprecation` argument + - Support `--trace-deprecation` argument + * deps: finalhandler@0.1.0 + - Respond after request fully read + - deps: debug@1.0.4 + * deps: parseurl@~1.2.0 + - Cache URLs based on original value + - Remove no-longer-needed URL mis-parse work-around + - Simplify the "fast-path" `RegExp` + * deps: send@0.7.0 + - Add `dotfiles` option + - Cap `maxAge` value to 1 year + - deps: debug@1.0.4 + - deps: depd@0.4.2 + * deps: serve-static@~1.4.0 + - deps: parseurl@~1.2.0 + - deps: send@0.7.0 + * perf: prevent multiple `Buffer` creation in `res.send` + +4.6.1 / 2014-07-12 +================== + + * fix `subapp.mountpath` regression for `app.use(subapp)` + +4.6.0 / 2014-07-11 +================== + + * accept multiple callbacks to `app.use()` + * add explicit "Rosetta Flash JSONP abuse" protection + - previous versions are not vulnerable; this is just explicit protection + * catch errors in multiple `req.param(name, fn)` handlers + * deprecate `res.redirect(url, status)` -- use `res.redirect(status, url)` instead + * fix `res.send(status, num)` to send `num` as json (not error) + * remove unnecessary escaping when `res.jsonp` returns JSON response + * support non-string `path` in `app.use(path, fn)` + - supports array of paths + - supports `RegExp` + * router: fix optimization on router exit + * router: refactor location of `try` blocks + * router: speed up standard `app.use(fn)` + * deps: debug@1.0.3 + - Add support for multiple wildcards in namespaces + * deps: finalhandler@0.0.3 + - deps: debug@1.0.3 + * deps: methods@1.1.0 + - add `CONNECT` + * deps: parseurl@~1.1.3 + - faster parsing of href-only URLs + * deps: path-to-regexp@0.1.3 + * deps: send@0.6.0 + - deps: debug@1.0.3 + * deps: serve-static@~1.3.2 + - deps: parseurl@~1.1.3 + - deps: send@0.6.0 + * perf: fix arguments reassign deopt in some `res` methods + +4.5.1 / 2014-07-06 +================== + + * fix routing regression when altering `req.method` + +4.5.0 / 2014-07-04 +================== + + * add deprecation message to non-plural `req.accepts*` + * add deprecation message to `res.send(body, status)` + * add deprecation message to `res.vary()` + * add `headers` option to `res.sendfile` + - use to set headers on successful file transfer + * add `mergeParams` option to `Router` + - merges `req.params` from parent routes + * add `req.hostname` -- correct name for what `req.host` returns + * deprecate things with `depd` module + * deprecate `req.host` -- use `req.hostname` instead + * fix behavior when handling request without routes + * fix handling when `route.all` is only route + * invoke `router.param()` only when route matches + * restore `req.params` after invoking router + * use `finalhandler` for final response handling + * use `media-typer` to alter content-type charset + * deps: accepts@~1.0.7 + * deps: send@0.5.0 + - Accept string for `maxage` (converted by `ms`) + - Include link in default redirect response + * deps: serve-static@~1.3.0 + - Accept string for `maxAge` (converted by `ms`) + - Add `setHeaders` option + - Include HTML link in redirect response + - deps: send@0.5.0 + * deps: type-is@~1.3.2 + +4.4.5 / 2014-06-26 +================== + + * deps: cookie-signature@1.0.4 + - fix for timing attacks + +4.4.4 / 2014-06-20 +================== + + * fix `res.attachment` Unicode filenames in Safari + * fix "trim prefix" debug message in `express:router` + * deps: accepts@~1.0.5 + * deps: buffer-crc32@0.2.3 + +4.4.3 / 2014-06-11 +================== + + * fix persistence of modified `req.params[name]` from `app.param()` + * deps: accepts@1.0.3 + - deps: negotiator@0.4.6 + * deps: debug@1.0.2 + * deps: send@0.4.3 + - Do not throw un-catchable error on file open race condition + - Use `escape-html` for HTML escaping + - deps: debug@1.0.2 + - deps: finished@1.2.2 + - deps: fresh@0.2.2 + * deps: serve-static@1.2.3 + - Do not throw un-catchable error on file open race condition + - deps: send@0.4.3 + +4.4.2 / 2014-06-09 +================== + + * fix catching errors from top-level handlers + * use `vary` module for `res.vary` + * deps: debug@1.0.1 + * deps: proxy-addr@1.0.1 + * deps: send@0.4.2 + - fix "event emitter leak" warnings + - deps: debug@1.0.1 + - deps: finished@1.2.1 + * deps: serve-static@1.2.2 + - fix "event emitter leak" warnings + - deps: send@0.4.2 + * deps: type-is@1.2.1 + +4.4.1 / 2014-06-02 +================== + + * deps: methods@1.0.1 + * deps: send@0.4.1 + - Send `max-age` in `Cache-Control` in correct format + * deps: serve-static@1.2.1 + - use `escape-html` for escaping + - deps: send@0.4.1 + +4.4.0 / 2014-05-30 +================== + + * custom etag control with `app.set('etag', val)` + - `app.set('etag', function(body, encoding){ return '"etag"' })` custom etag generation + - `app.set('etag', 'weak')` weak tag + - `app.set('etag', 'strong')` strong etag + - `app.set('etag', false)` turn off + - `app.set('etag', true)` standard etag + * mark `res.send` ETag as weak and reduce collisions + * update accepts to 1.0.2 + - Fix interpretation when header not in request + * update send to 0.4.0 + - Calculate ETag with md5 for reduced collisions + - Ignore stream errors after request ends + - deps: debug@0.8.1 + * update serve-static to 1.2.0 + - Calculate ETag with md5 for reduced collisions + - Ignore stream errors after request ends + - deps: send@0.4.0 + +4.3.2 / 2014-05-28 +================== + + * fix handling of errors from `router.param()` callbacks + +4.3.1 / 2014-05-23 +================== + + * revert "fix behavior of multiple `app.VERB` for the same path" + - this caused a regression in the order of route execution + +4.3.0 / 2014-05-21 +================== + + * add `req.baseUrl` to access the path stripped from `req.url` in routes + * fix behavior of multiple `app.VERB` for the same path + * fix issue routing requests among sub routers + * invoke `router.param()` only when necessary instead of every match + * proper proxy trust with `app.set('trust proxy', trust)` + - `app.set('trust proxy', 1)` trust first hop + - `app.set('trust proxy', 'loopback')` trust loopback addresses + - `app.set('trust proxy', '10.0.0.1')` trust single IP + - `app.set('trust proxy', '10.0.0.1/16')` trust subnet + - `app.set('trust proxy', '10.0.0.1, 10.0.0.2')` trust list + - `app.set('trust proxy', false)` turn off + - `app.set('trust proxy', true)` trust everything + * set proper `charset` in `Content-Type` for `res.send` + * update type-is to 1.2.0 + - support suffix matching + +4.2.0 / 2014-05-11 +================== + + * deprecate `app.del()` -- use `app.delete()` instead + * deprecate `res.json(obj, status)` -- use `res.json(status, obj)` instead + - the edge-case `res.json(status, num)` requires `res.status(status).json(num)` + * deprecate `res.jsonp(obj, status)` -- use `res.jsonp(status, obj)` instead + - the edge-case `res.jsonp(status, num)` requires `res.status(status).jsonp(num)` + * fix `req.next` when inside router instance + * include `ETag` header in `HEAD` requests + * keep previous `Content-Type` for `res.jsonp` + * support PURGE method + - add `app.purge` + - add `router.purge` + - include PURGE in `app.all` + * update debug to 0.8.0 + - add `enable()` method + - change from stderr to stdout + * update methods to 1.0.0 + - add PURGE + +4.1.2 / 2014-05-08 +================== + + * fix `req.host` for IPv6 literals + * fix `res.jsonp` error if callback param is object + +4.1.1 / 2014-04-27 +================== + + * fix package.json to reflect supported node version + +4.1.0 / 2014-04-24 +================== + + * pass options from `res.sendfile` to `send` + * preserve casing of headers in `res.header` and `res.set` + * support unicode file names in `res.attachment` and `res.download` + * update accepts to 1.0.1 + - deps: negotiator@0.4.0 + * update cookie to 0.1.2 + - Fix for maxAge == 0 + - made compat with expires field + * update send to 0.3.0 + - Accept API options in options object + - Coerce option types + - Control whether to generate etags + - Default directory access to 403 when index disabled + - Fix sending files with dots without root set + - Include file path in etag + - Make "Can't set headers after they are sent." catchable + - Send full entity-body for multi range requests + - Set etags to "weak" + - Support "If-Range" header + - Support multiple index paths + - deps: mime@1.2.11 + * update serve-static to 1.1.0 + - Accept options directly to `send` module + - Resolve relative paths at middleware setup + - Use parseurl to parse the URL from request + - deps: send@0.3.0 + * update type-is to 1.1.0 + - add non-array values support + - add `multipart` as a shorthand + +4.0.0 / 2014-04-09 +================== + + * remove: + - node 0.8 support + - connect and connect's patches except for charset handling + - express(1) - moved to [express-generator](https://github.com/expressjs/generator) + - `express.createServer()` - it has been deprecated for a long time. Use `express()` + - `app.configure` - use logic in your own app code + - `app.router` - is removed + - `req.auth` - use `basic-auth` instead + - `req.accepted*` - use `req.accepts*()` instead + - `res.location` - relative URL resolution is removed + - `res.charset` - include the charset in the content type when using `res.set()` + - all bundled middleware except `static` + * change: + - `app.route` -> `app.mountpath` when mounting an express app in another express app + - `json spaces` no longer enabled by default in development + - `req.accepts*` -> `req.accepts*s` - i.e. `req.acceptsEncoding` -> `req.acceptsEncodings` + - `req.params` is now an object instead of an array + - `res.locals` is no longer a function. It is a plain js object. Treat it as such. + - `res.headerSent` -> `res.headersSent` to match node.js ServerResponse object + * refactor: + - `req.accepts*` with [accepts](https://github.com/expressjs/accepts) + - `req.is` with [type-is](https://github.com/expressjs/type-is) + - [path-to-regexp](https://github.com/component/path-to-regexp) + * add: + - `app.router()` - returns the app Router instance + - `app.route()` - Proxy to the app's `Router#route()` method to create a new route + - Router & Route - public API + +3.21.2 / 2015-07-31 +=================== + + * deps: connect@2.30.2 + - deps: body-parser@~1.13.3 + - deps: compression@~1.5.2 + - deps: errorhandler@~1.4.2 + - deps: method-override@~2.3.5 + - deps: serve-index@~1.7.2 + - deps: type-is@~1.6.6 + - deps: vhost@~3.0.1 + * deps: vary@~1.0.1 + - Fix setting empty header from empty `field` + - perf: enable strict mode + - perf: remove argument reassignments + +3.21.1 / 2015-07-05 +=================== + + * deps: basic-auth@~1.0.3 + * deps: connect@2.30.1 + - deps: body-parser@~1.13.2 + - deps: compression@~1.5.1 + - deps: errorhandler@~1.4.1 + - deps: morgan@~1.6.1 + - deps: pause@0.1.0 + - deps: qs@4.0.0 + - deps: serve-index@~1.7.1 + - deps: type-is@~1.6.4 + +3.21.0 / 2015-06-18 +=================== + + * deps: basic-auth@1.0.2 + - perf: enable strict mode + - perf: hoist regular expression + - perf: parse with regular expressions + - perf: remove argument reassignment + * deps: connect@2.30.0 + - deps: body-parser@~1.13.1 + - deps: bytes@2.1.0 + - deps: compression@~1.5.0 + - deps: cookie@0.1.3 + - deps: cookie-parser@~1.3.5 + - deps: csurf@~1.8.3 + - deps: errorhandler@~1.4.0 + - deps: express-session@~1.11.3 + - deps: finalhandler@0.4.0 + - deps: fresh@0.3.0 + - deps: morgan@~1.6.0 + - deps: serve-favicon@~2.3.0 + - deps: serve-index@~1.7.0 + - deps: serve-static@~1.10.0 + - deps: type-is@~1.6.3 + * deps: cookie@0.1.3 + - perf: deduce the scope of try-catch deopt + - perf: remove argument reassignments + * deps: escape-html@1.0.2 + * deps: etag@~1.7.0 + - Always include entity length in ETags for hash length extensions + - Generate non-Stats ETags using MD5 only (no longer CRC32) + - Improve stat performance by removing hashing + - Improve support for JXcore + - Remove base64 padding in ETags to shorten + - Support "fake" stats objects in environments without fs + - Use MD5 instead of MD4 in weak ETags over 1KB + * deps: fresh@0.3.0 + - Add weak `ETag` matching support + * deps: mkdirp@0.5.1 + - Work in global strict mode + * deps: send@0.13.0 + - Allow Node.js HTTP server to set `Date` response header + - Fix incorrectly removing `Content-Location` on 304 response + - Improve the default redirect response headers + - Send appropriate headers on default error response + - Use `http-errors` for standard emitted errors + - Use `statuses` instead of `http` module for status messages + - deps: escape-html@1.0.2 + - deps: etag@~1.7.0 + - deps: fresh@0.3.0 + - deps: on-finished@~2.3.0 + - perf: enable strict mode + - perf: remove unnecessary array allocations + +3.20.3 / 2015-05-17 +=================== + + * deps: connect@2.29.2 + - deps: body-parser@~1.12.4 + - deps: compression@~1.4.4 + - deps: connect-timeout@~1.6.2 + - deps: debug@~2.2.0 + - deps: depd@~1.0.1 + - deps: errorhandler@~1.3.6 + - deps: finalhandler@0.3.6 + - deps: method-override@~2.3.3 + - deps: morgan@~1.5.3 + - deps: qs@2.4.2 + - deps: response-time@~2.3.1 + - deps: serve-favicon@~2.2.1 + - deps: serve-index@~1.6.4 + - deps: serve-static@~1.9.3 + - deps: type-is@~1.6.2 + * deps: debug@~2.2.0 + - deps: ms@0.7.1 + * deps: depd@~1.0.1 + * deps: proxy-addr@~1.0.8 + - deps: ipaddr.js@1.0.1 + * deps: send@0.12.3 + - deps: debug@~2.2.0 + - deps: depd@~1.0.1 + - deps: etag@~1.6.0 + - deps: ms@0.7.1 + - deps: on-finished@~2.2.1 + +3.20.2 / 2015-03-16 +=================== + + * deps: connect@2.29.1 + - deps: body-parser@~1.12.2 + - deps: compression@~1.4.3 + - deps: connect-timeout@~1.6.1 + - deps: debug@~2.1.3 + - deps: errorhandler@~1.3.5 + - deps: express-session@~1.10.4 + - deps: finalhandler@0.3.4 + - deps: method-override@~2.3.2 + - deps: morgan@~1.5.2 + - deps: qs@2.4.1 + - deps: serve-index@~1.6.3 + - deps: serve-static@~1.9.2 + - deps: type-is@~1.6.1 + * deps: debug@~2.1.3 + - Fix high intensity foreground color for bold + - deps: ms@0.7.0 + * deps: merge-descriptors@1.0.0 + * deps: proxy-addr@~1.0.7 + - deps: ipaddr.js@0.1.9 + * deps: send@0.12.2 + - Throw errors early for invalid `extensions` or `index` options + - deps: debug@~2.1.3 + +3.20.1 / 2015-02-28 +=================== + + * Fix `req.host` when using "trust proxy" hops count + * Fix `req.protocol`/`req.secure` when using "trust proxy" hops count + +3.20.0 / 2015-02-18 +=================== + + * Fix `"trust proxy"` setting to inherit when app is mounted + * Generate `ETag`s for all request responses + - No longer restricted to only responses for `GET` and `HEAD` requests + * Use `content-type` to parse `Content-Type` headers + * deps: connect@2.29.0 + - Use `content-type` to parse `Content-Type` headers + - deps: body-parser@~1.12.0 + - deps: compression@~1.4.1 + - deps: connect-timeout@~1.6.0 + - deps: cookie-parser@~1.3.4 + - deps: cookie-signature@1.0.6 + - deps: csurf@~1.7.0 + - deps: errorhandler@~1.3.4 + - deps: express-session@~1.10.3 + - deps: http-errors@~1.3.1 + - deps: response-time@~2.3.0 + - deps: serve-index@~1.6.2 + - deps: serve-static@~1.9.1 + - deps: type-is@~1.6.0 + * deps: cookie-signature@1.0.6 + * deps: send@0.12.1 + - Always read the stat size from the file + - Fix mutating passed-in `options` + - deps: mime@1.3.4 + +3.19.2 / 2015-02-01 +=================== + + * deps: connect@2.28.3 + - deps: compression@~1.3.1 + - deps: csurf@~1.6.6 + - deps: errorhandler@~1.3.3 + - deps: express-session@~1.10.2 + - deps: serve-index@~1.6.1 + - deps: type-is@~1.5.6 + * deps: proxy-addr@~1.0.6 + - deps: ipaddr.js@0.1.8 + +3.19.1 / 2015-01-20 +=================== + + * deps: connect@2.28.2 + - deps: body-parser@~1.10.2 + - deps: serve-static@~1.8.1 + * deps: send@0.11.1 + - Fix root path disclosure + +3.19.0 / 2015-01-09 +=================== + + * Fix `OPTIONS` responses to include the `HEAD` method property + * Use `readline` for prompt in `express(1)` + * deps: commander@2.6.0 + * deps: connect@2.28.1 + - deps: body-parser@~1.10.1 + - deps: compression@~1.3.0 + - deps: connect-timeout@~1.5.0 + - deps: csurf@~1.6.4 + - deps: debug@~2.1.1 + - deps: errorhandler@~1.3.2 + - deps: express-session@~1.10.1 + - deps: finalhandler@0.3.3 + - deps: method-override@~2.3.1 + - deps: morgan@~1.5.1 + - deps: serve-favicon@~2.2.0 + - deps: serve-index@~1.6.0 + - deps: serve-static@~1.8.0 + - deps: type-is@~1.5.5 + * deps: debug@~2.1.1 + * deps: methods@~1.1.1 + * deps: proxy-addr@~1.0.5 + - deps: ipaddr.js@0.1.6 + * deps: send@0.11.0 + - deps: debug@~2.1.1 + - deps: etag@~1.5.1 + - deps: ms@0.7.0 + - deps: on-finished@~2.2.0 + +3.18.6 / 2014-12-12 +=================== + + * Fix exception in `req.fresh`/`req.stale` without response headers + +3.18.5 / 2014-12-11 +=================== + + * deps: connect@2.27.6 + - deps: compression@~1.2.2 + - deps: express-session@~1.9.3 + - deps: http-errors@~1.2.8 + - deps: serve-index@~1.5.3 + - deps: type-is@~1.5.4 + +3.18.4 / 2014-11-23 +=================== + + * deps: connect@2.27.4 + - deps: body-parser@~1.9.3 + - deps: compression@~1.2.1 + - deps: errorhandler@~1.2.3 + - deps: express-session@~1.9.2 + - deps: qs@2.3.3 + - deps: serve-favicon@~2.1.7 + - deps: serve-static@~1.5.1 + - deps: type-is@~1.5.3 + * deps: etag@~1.5.1 + * deps: proxy-addr@~1.0.4 + - deps: ipaddr.js@0.1.5 + +3.18.3 / 2014-11-09 +=================== + + * deps: connect@2.27.3 + - Correctly invoke async callback asynchronously + - deps: csurf@~1.6.3 + +3.18.2 / 2014-10-28 +=================== + + * deps: connect@2.27.2 + - Fix handling of URLs containing `://` in the path + - deps: body-parser@~1.9.2 + - deps: qs@2.3.2 + +3.18.1 / 2014-10-22 +=================== + + * Fix internal `utils.merge` deprecation warnings + * deps: connect@2.27.1 + - deps: body-parser@~1.9.1 + - deps: express-session@~1.9.1 + - deps: finalhandler@0.3.2 + - deps: morgan@~1.4.1 + - deps: qs@2.3.0 + - deps: serve-static@~1.7.1 + * deps: send@0.10.1 + - deps: on-finished@~2.1.1 + +3.18.0 / 2014-10-17 +=================== + + * Use `content-disposition` module for `res.attachment`/`res.download` + - Sends standards-compliant `Content-Disposition` header + - Full Unicode support + * Use `etag` module to generate `ETag` headers + * deps: connect@2.27.0 + - Use `http-errors` module for creating errors + - Use `utils-merge` module for merging objects + - deps: body-parser@~1.9.0 + - deps: compression@~1.2.0 + - deps: connect-timeout@~1.4.0 + - deps: debug@~2.1.0 + - deps: depd@~1.0.0 + - deps: express-session@~1.9.0 + - deps: finalhandler@0.3.1 + - deps: method-override@~2.3.0 + - deps: morgan@~1.4.0 + - deps: response-time@~2.2.0 + - deps: serve-favicon@~2.1.6 + - deps: serve-index@~1.5.0 + - deps: serve-static@~1.7.0 + * deps: debug@~2.1.0 + - Implement `DEBUG_FD` env variable support + * deps: depd@~1.0.0 + * deps: send@0.10.0 + - deps: debug@~2.1.0 + - deps: depd@~1.0.0 + - deps: etag@~1.5.0 + +3.17.8 / 2014-10-15 +=================== + + * deps: connect@2.26.6 + - deps: compression@~1.1.2 + - deps: csurf@~1.6.2 + - deps: errorhandler@~1.2.2 + +3.17.7 / 2014-10-08 +=================== + + * deps: connect@2.26.5 + - Fix accepting non-object arguments to `logger` + - deps: serve-static@~1.6.4 + +3.17.6 / 2014-10-02 +=================== + + * deps: connect@2.26.4 + - deps: morgan@~1.3.2 + - deps: type-is@~1.5.2 + +3.17.5 / 2014-09-24 +=================== + + * deps: connect@2.26.3 + - deps: body-parser@~1.8.4 + - deps: serve-favicon@~2.1.5 + - deps: serve-static@~1.6.3 + * deps: proxy-addr@~1.0.3 + - Use `forwarded` npm module + * deps: send@0.9.3 + - deps: etag@~1.4.0 + +3.17.4 / 2014-09-19 +=================== + + * deps: connect@2.26.2 + - deps: body-parser@~1.8.3 + - deps: qs@2.2.4 + +3.17.3 / 2014-09-18 +=================== + + * deps: proxy-addr@~1.0.2 + - Fix a global leak when multiple subnets are trusted + - deps: ipaddr.js@0.1.3 + +3.17.2 / 2014-09-15 +=================== + + * Use `crc` instead of `buffer-crc32` for speed + * deps: connect@2.26.1 + - deps: body-parser@~1.8.2 + - deps: depd@0.4.5 + - deps: express-session@~1.8.2 + - deps: morgan@~1.3.1 + - deps: serve-favicon@~2.1.3 + - deps: serve-static@~1.6.2 + * deps: depd@0.4.5 + * deps: send@0.9.2 + - deps: depd@0.4.5 + - deps: etag@~1.3.1 + - deps: range-parser@~1.0.2 + +3.17.1 / 2014-09-08 +=================== + + * Fix error in `req.subdomains` on empty host + +3.17.0 / 2014-09-08 +=================== + + * Support `X-Forwarded-Host` in `req.subdomains` + * Support IP address host in `req.subdomains` + * deps: connect@2.26.0 + - deps: body-parser@~1.8.1 + - deps: compression@~1.1.0 + - deps: connect-timeout@~1.3.0 + - deps: cookie-parser@~1.3.3 + - deps: cookie-signature@1.0.5 + - deps: csurf@~1.6.1 + - deps: debug@~2.0.0 + - deps: errorhandler@~1.2.0 + - deps: express-session@~1.8.1 + - deps: finalhandler@0.2.0 + - deps: fresh@0.2.4 + - deps: media-typer@0.3.0 + - deps: method-override@~2.2.0 + - deps: morgan@~1.3.0 + - deps: qs@2.2.3 + - deps: serve-favicon@~2.1.3 + - deps: serve-index@~1.2.1 + - deps: serve-static@~1.6.1 + - deps: type-is@~1.5.1 + - deps: vhost@~3.0.0 + * deps: cookie-signature@1.0.5 + * deps: debug@~2.0.0 + * deps: fresh@0.2.4 + * deps: media-typer@0.3.0 + - Throw error when parameter format invalid on parse + * deps: range-parser@~1.0.2 + * deps: send@0.9.1 + - Add `lastModified` option + - Use `etag` to generate `ETag` header + - deps: debug@~2.0.0 + - deps: fresh@0.2.4 + * deps: vary@~1.0.0 + - Accept valid `Vary` header string as `field` + +3.16.10 / 2014-09-04 +==================== + + * deps: connect@2.25.10 + - deps: serve-static@~1.5.4 + * deps: send@0.8.5 + - Fix a path traversal issue when using `root` + - Fix malicious path detection for empty string path + +3.16.9 / 2014-08-29 +=================== + + * deps: connect@2.25.9 + - deps: body-parser@~1.6.7 + - deps: qs@2.2.2 + +3.16.8 / 2014-08-27 +=================== + + * deps: connect@2.25.8 + - deps: body-parser@~1.6.6 + - deps: csurf@~1.4.1 + - deps: qs@2.2.0 + +3.16.7 / 2014-08-18 +=================== + + * deps: connect@2.25.7 + - deps: body-parser@~1.6.5 + - deps: express-session@~1.7.6 + - deps: morgan@~1.2.3 + - deps: serve-static@~1.5.3 + * deps: send@0.8.3 + - deps: destroy@1.0.3 + - deps: on-finished@2.1.0 + +3.16.6 / 2014-08-14 +=================== + + * deps: connect@2.25.6 + - deps: body-parser@~1.6.4 + - deps: qs@1.2.2 + - deps: serve-static@~1.5.2 + * deps: send@0.8.2 + - Work around `fd` leak in Node.js 0.10 for `fs.ReadStream` + +3.16.5 / 2014-08-11 +=================== + + * deps: connect@2.25.5 + - Fix backwards compatibility in `logger` + +3.16.4 / 2014-08-10 +=================== + + * Fix original URL parsing in `res.location` + * deps: connect@2.25.4 + - Fix `query` middleware breaking with argument + - deps: body-parser@~1.6.3 + - deps: compression@~1.0.11 + - deps: connect-timeout@~1.2.2 + - deps: express-session@~1.7.5 + - deps: method-override@~2.1.3 + - deps: on-headers@~1.0.0 + - deps: parseurl@~1.3.0 + - deps: qs@1.2.1 + - deps: response-time@~2.0.1 + - deps: serve-index@~1.1.6 + - deps: serve-static@~1.5.1 + * deps: parseurl@~1.3.0 + +3.16.3 / 2014-08-07 +=================== + + * deps: connect@2.25.3 + - deps: multiparty@3.3.2 + +3.16.2 / 2014-08-07 +=================== + + * deps: connect@2.25.2 + - deps: body-parser@~1.6.2 + - deps: qs@1.2.0 + +3.16.1 / 2014-08-06 +=================== + + * deps: connect@2.25.1 + - deps: body-parser@~1.6.1 + - deps: qs@1.1.0 + +3.16.0 / 2014-08-05 +=================== + + * deps: connect@2.25.0 + - deps: body-parser@~1.6.0 + - deps: compression@~1.0.10 + - deps: csurf@~1.4.0 + - deps: express-session@~1.7.4 + - deps: qs@1.0.2 + - deps: serve-static@~1.5.0 + * deps: send@0.8.1 + - Add `extensions` option + +3.15.3 / 2014-08-04 +=================== + + * fix `res.sendfile` regression for serving directory index files + * deps: connect@2.24.3 + - deps: serve-index@~1.1.5 + - deps: serve-static@~1.4.4 + * deps: send@0.7.4 + - Fix incorrect 403 on Windows and Node.js 0.11 + - Fix serving index files without root dir + +3.15.2 / 2014-07-27 +=================== + + * deps: connect@2.24.2 + - deps: body-parser@~1.5.2 + - deps: depd@0.4.4 + - deps: express-session@~1.7.2 + - deps: morgan@~1.2.2 + - deps: serve-static@~1.4.2 + * deps: depd@0.4.4 + - Work-around v8 generating empty stack traces + * deps: send@0.7.2 + - deps: depd@0.4.4 + +3.15.1 / 2014-07-26 +=================== + + * deps: connect@2.24.1 + - deps: body-parser@~1.5.1 + - deps: depd@0.4.3 + - deps: express-session@~1.7.1 + - deps: morgan@~1.2.1 + - deps: serve-index@~1.1.4 + - deps: serve-static@~1.4.1 + * deps: depd@0.4.3 + - Fix exception when global `Error.stackTraceLimit` is too low + * deps: send@0.7.1 + - deps: depd@0.4.3 + +3.15.0 / 2014-07-22 +=================== + + * Fix `req.protocol` for proxy-direct connections + * Pass options from `res.sendfile` to `send` + * deps: connect@2.24.0 + - deps: body-parser@~1.5.0 + - deps: compression@~1.0.9 + - deps: connect-timeout@~1.2.1 + - deps: debug@1.0.4 + - deps: depd@0.4.2 + - deps: express-session@~1.7.0 + - deps: finalhandler@0.1.0 + - deps: method-override@~2.1.2 + - deps: morgan@~1.2.0 + - deps: multiparty@3.3.1 + - deps: parseurl@~1.2.0 + - deps: serve-static@~1.4.0 + * deps: debug@1.0.4 + * deps: depd@0.4.2 + - Add `TRACE_DEPRECATION` environment variable + - Remove non-standard grey color from color output + - Support `--no-deprecation` argument + - Support `--trace-deprecation` argument + * deps: parseurl@~1.2.0 + - Cache URLs based on original value + - Remove no-longer-needed URL mis-parse work-around + - Simplify the "fast-path" `RegExp` + * deps: send@0.7.0 + - Add `dotfiles` option + - Cap `maxAge` value to 1 year + - deps: debug@1.0.4 + - deps: depd@0.4.2 + +3.14.0 / 2014-07-11 +=================== + + * add explicit "Rosetta Flash JSONP abuse" protection + - previous versions are not vulnerable; this is just explicit protection + * deprecate `res.redirect(url, status)` -- use `res.redirect(status, url)` instead + * fix `res.send(status, num)` to send `num` as json (not error) + * remove unnecessary escaping when `res.jsonp` returns JSON response + * deps: basic-auth@1.0.0 + - support empty password + - support empty username + * deps: connect@2.23.0 + - deps: debug@1.0.3 + - deps: express-session@~1.6.4 + - deps: method-override@~2.1.0 + - deps: parseurl@~1.1.3 + - deps: serve-static@~1.3.1 + * deps: debug@1.0.3 + - Add support for multiple wildcards in namespaces + * deps: methods@1.1.0 + - add `CONNECT` + * deps: parseurl@~1.1.3 + - faster parsing of href-only URLs + +3.13.0 / 2014-07-03 +=================== + + * add deprecation message to `app.configure` + * add deprecation message to `req.auth` + * use `basic-auth` to parse `Authorization` header + * deps: connect@2.22.0 + - deps: csurf@~1.3.0 + - deps: express-session@~1.6.1 + - deps: multiparty@3.3.0 + - deps: serve-static@~1.3.0 + * deps: send@0.5.0 + - Accept string for `maxage` (converted by `ms`) + - Include link in default redirect response + +3.12.1 / 2014-06-26 +=================== + + * deps: connect@2.21.1 + - deps: cookie-parser@1.3.2 + - deps: cookie-signature@1.0.4 + - deps: express-session@~1.5.2 + - deps: type-is@~1.3.2 + * deps: cookie-signature@1.0.4 + - fix for timing attacks + +3.12.0 / 2014-06-21 +=================== + + * use `media-typer` to alter content-type charset + * deps: connect@2.21.0 + - deprecate `connect(middleware)` -- use `app.use(middleware)` instead + - deprecate `connect.createServer()` -- use `connect()` instead + - fix `res.setHeader()` patch to work with with get -> append -> set pattern + - deps: compression@~1.0.8 + - deps: errorhandler@~1.1.1 + - deps: express-session@~1.5.0 + - deps: serve-index@~1.1.3 + +3.11.0 / 2014-06-19 +=================== + + * deprecate things with `depd` module + * deps: buffer-crc32@0.2.3 + * deps: connect@2.20.2 + - deprecate `verify` option to `json` -- use `body-parser` npm module instead + - deprecate `verify` option to `urlencoded` -- use `body-parser` npm module instead + - deprecate things with `depd` module + - use `finalhandler` for final response handling + - use `media-typer` to parse `content-type` for charset + - deps: body-parser@1.4.3 + - deps: connect-timeout@1.1.1 + - deps: cookie-parser@1.3.1 + - deps: csurf@1.2.2 + - deps: errorhandler@1.1.0 + - deps: express-session@1.4.0 + - deps: multiparty@3.2.9 + - deps: serve-index@1.1.2 + - deps: type-is@1.3.1 + - deps: vhost@2.0.0 + +3.10.5 / 2014-06-11 +=================== + + * deps: connect@2.19.6 + - deps: body-parser@1.3.1 + - deps: compression@1.0.7 + - deps: debug@1.0.2 + - deps: serve-index@1.1.1 + - deps: serve-static@1.2.3 + * deps: debug@1.0.2 + * deps: send@0.4.3 + - Do not throw un-catchable error on file open race condition + - Use `escape-html` for HTML escaping + - deps: debug@1.0.2 + - deps: finished@1.2.2 + - deps: fresh@0.2.2 + +3.10.4 / 2014-06-09 +=================== + + * deps: connect@2.19.5 + - fix "event emitter leak" warnings + - deps: csurf@1.2.1 + - deps: debug@1.0.1 + - deps: serve-static@1.2.2 + - deps: type-is@1.2.1 + * deps: debug@1.0.1 + * deps: send@0.4.2 + - fix "event emitter leak" warnings + - deps: finished@1.2.1 + - deps: debug@1.0.1 + +3.10.3 / 2014-06-05 +=================== + + * use `vary` module for `res.vary` + * deps: connect@2.19.4 + - deps: errorhandler@1.0.2 + - deps: method-override@2.0.2 + - deps: serve-favicon@2.0.1 + * deps: debug@1.0.0 + +3.10.2 / 2014-06-03 +=================== + + * deps: connect@2.19.3 + - deps: compression@1.0.6 + +3.10.1 / 2014-06-03 +=================== + + * deps: connect@2.19.2 + - deps: compression@1.0.4 + * deps: proxy-addr@1.0.1 + +3.10.0 / 2014-06-02 +=================== + + * deps: connect@2.19.1 + - deprecate `methodOverride()` -- use `method-override` npm module instead + - deps: body-parser@1.3.0 + - deps: method-override@2.0.1 + - deps: multiparty@3.2.8 + - deps: response-time@2.0.0 + - deps: serve-static@1.2.1 + * deps: methods@1.0.1 + * deps: send@0.4.1 + - Send `max-age` in `Cache-Control` in correct format + +3.9.0 / 2014-05-30 +================== + + * custom etag control with `app.set('etag', val)` + - `app.set('etag', function(body, encoding){ return '"etag"' })` custom etag generation + - `app.set('etag', 'weak')` weak tag + - `app.set('etag', 'strong')` strong etag + - `app.set('etag', false)` turn off + - `app.set('etag', true)` standard etag + * Include ETag in HEAD requests + * mark `res.send` ETag as weak and reduce collisions + * update connect to 2.18.0 + - deps: compression@1.0.3 + - deps: serve-index@1.1.0 + - deps: serve-static@1.2.0 + * update send to 0.4.0 + - Calculate ETag with md5 for reduced collisions + - Ignore stream errors after request ends + - deps: debug@0.8.1 + +3.8.1 / 2014-05-27 +================== + + * update connect to 2.17.3 + - deps: body-parser@1.2.2 + - deps: express-session@1.2.1 + - deps: method-override@1.0.2 + +3.8.0 / 2014-05-21 +================== + + * keep previous `Content-Type` for `res.jsonp` + * set proper `charset` in `Content-Type` for `res.send` + * update connect to 2.17.1 + - fix `res.charset` appending charset when `content-type` has one + - deps: express-session@1.2.0 + - deps: morgan@1.1.1 + - deps: serve-index@1.0.3 + +3.7.0 / 2014-05-18 +================== + + * proper proxy trust with `app.set('trust proxy', trust)` + - `app.set('trust proxy', 1)` trust first hop + - `app.set('trust proxy', 'loopback')` trust loopback addresses + - `app.set('trust proxy', '10.0.0.1')` trust single IP + - `app.set('trust proxy', '10.0.0.1/16')` trust subnet + - `app.set('trust proxy', '10.0.0.1, 10.0.0.2')` trust list + - `app.set('trust proxy', false)` turn off + - `app.set('trust proxy', true)` trust everything + * update connect to 2.16.2 + - deprecate `res.headerSent` -- use `res.headersSent` + - deprecate `res.on("header")` -- use on-headers module instead + - fix edge-case in `res.appendHeader` that would append in wrong order + - json: use body-parser + - urlencoded: use body-parser + - dep: bytes@1.0.0 + - dep: cookie-parser@1.1.0 + - dep: csurf@1.2.0 + - dep: express-session@1.1.0 + - dep: method-override@1.0.1 + +3.6.0 / 2014-05-09 +================== + + * deprecate `app.del()` -- use `app.delete()` instead + * deprecate `res.json(obj, status)` -- use `res.json(status, obj)` instead + - the edge-case `res.json(status, num)` requires `res.status(status).json(num)` + * deprecate `res.jsonp(obj, status)` -- use `res.jsonp(status, obj)` instead + - the edge-case `res.jsonp(status, num)` requires `res.status(status).jsonp(num)` + * support PURGE method + - add `app.purge` + - add `router.purge` + - include PURGE in `app.all` + * update connect to 2.15.0 + * Add `res.appendHeader` + * Call error stack even when response has been sent + * Patch `res.headerSent` to return Boolean + * Patch `res.headersSent` for node.js 0.8 + * Prevent default 404 handler after response sent + * dep: compression@1.0.2 + * dep: connect-timeout@1.1.0 + * dep: debug@^0.8.0 + * dep: errorhandler@1.0.1 + * dep: express-session@1.0.4 + * dep: morgan@1.0.1 + * dep: serve-favicon@2.0.0 + * dep: serve-index@1.0.2 + * update debug to 0.8.0 + * add `enable()` method + * change from stderr to stdout + * update methods to 1.0.0 + - add PURGE + * update mkdirp to 0.5.0 + +3.5.3 / 2014-05-08 +================== + + * fix `req.host` for IPv6 literals + * fix `res.jsonp` error if callback param is object + +3.5.2 / 2014-04-24 +================== + + * update connect to 2.14.5 + * update cookie to 0.1.2 + * update mkdirp to 0.4.0 + * update send to 0.3.0 + +3.5.1 / 2014-03-25 +================== + + * pin less-middleware in generated app + +3.5.0 / 2014-03-06 +================== + + * bump deps + +3.4.8 / 2014-01-13 +================== + + * prevent incorrect automatic OPTIONS responses #1868 @dpatti + * update binary and examples for jade 1.0 #1876 @yossi, #1877 @reqshark, #1892 @matheusazzi + * throw 400 in case of malformed paths @rlidwka + +3.4.7 / 2013-12-10 +================== + + * update connect + +3.4.6 / 2013-12-01 +================== + + * update connect (raw-body) + +3.4.5 / 2013-11-27 +================== + + * update connect + * res.location: remove leading ./ #1802 @kapouer + * res.redirect: fix `res.redirect('toString') #1829 @michaelficarra + * res.send: always send ETag when content-length > 0 + * router: add Router.all() method + +3.4.4 / 2013-10-29 +================== + + * update connect + * update supertest + * update methods + * express(1): replace bodyParser() with urlencoded() and json() #1795 @chirag04 + +3.4.3 / 2013-10-23 +================== + + * update connect + +3.4.2 / 2013-10-18 +================== + + * update connect + * downgrade commander + +3.4.1 / 2013-10-15 +================== + + * update connect + * update commander + * jsonp: check if callback is a function + * router: wrap encodeURIComponent in a try/catch #1735 (@lxe) + * res.format: now includes charset @1747 (@sorribas) + * res.links: allow multiple calls @1746 (@sorribas) + +3.4.0 / 2013-09-07 +================== + + * add res.vary(). Closes #1682 + * update connect + +3.3.8 / 2013-09-02 +================== + + * update connect + +3.3.7 / 2013-08-28 +================== + + * update connect + +3.3.6 / 2013-08-27 +================== + + * Revert "remove charset from json responses. Closes #1631" (causes issues in some clients) + * add: req.accepts take an argument list + +3.3.4 / 2013-07-08 +================== + + * update send and connect + +3.3.3 / 2013-07-04 +================== + + * update connect + +3.3.2 / 2013-07-03 +================== + + * update connect + * update send + * remove .version export + +3.3.1 / 2013-06-27 +================== + + * update connect + +3.3.0 / 2013-06-26 +================== + + * update connect + * add support for multiple X-Forwarded-Proto values. Closes #1646 + * change: remove charset from json responses. Closes #1631 + * change: return actual booleans from req.accept* functions + * fix jsonp callback array throw + +3.2.6 / 2013-06-02 +================== + + * update connect + +3.2.5 / 2013-05-21 +================== + + * update connect + * update node-cookie + * add: throw a meaningful error when there is no default engine + * change generation of ETags with res.send() to GET requests only. Closes #1619 + +3.2.4 / 2013-05-09 +================== + + * fix `req.subdomains` when no Host is present + * fix `req.host` when no Host is present, return undefined + +3.2.3 / 2013-05-07 +================== + + * update connect / qs + +3.2.2 / 2013-05-03 +================== + + * update qs + +3.2.1 / 2013-04-29 +================== + + * add app.VERB() paths array deprecation warning + * update connect + * update qs and remove all ~ semver crap + * fix: accept number as value of Signed Cookie + +3.2.0 / 2013-04-15 +================== + + * add "view" constructor setting to override view behaviour + * add req.acceptsEncoding(name) + * add req.acceptedEncodings + * revert cookie signature change causing session race conditions + * fix sorting of Accept values of the same quality + +3.1.2 / 2013-04-12 +================== + + * add support for custom Accept parameters + * update cookie-signature + +3.1.1 / 2013-04-01 +================== + + * add X-Forwarded-Host support to `req.host` + * fix relative redirects + * update mkdirp + * update buffer-crc32 + * remove legacy app.configure() method from app template. + +3.1.0 / 2013-01-25 +================== + + * add support for leading "." in "view engine" setting + * add array support to `res.set()` + * add node 0.8.x to travis.yml + * add "subdomain offset" setting for tweaking `req.subdomains` + * add `res.location(url)` implementing `res.redirect()`-like setting of Location + * use app.get() for x-powered-by setting for inheritance + * fix colons in passwords for `req.auth` + +3.0.6 / 2013-01-04 +================== + + * add http verb methods to Router + * update connect + * fix mangling of the `res.cookie()` options object + * fix jsonp whitespace escape. Closes #1132 + +3.0.5 / 2012-12-19 +================== + + * add throwing when a non-function is passed to a route + * fix: explicitly remove Transfer-Encoding header from 204 and 304 responses + * revert "add 'etag' option" + +3.0.4 / 2012-12-05 +================== + + * add 'etag' option to disable `res.send()` Etags + * add escaping of urls in text/plain in `res.redirect()` + for old browsers interpreting as html + * change crc32 module for a more liberal license + * update connect + +3.0.3 / 2012-11-13 +================== + + * update connect + * update cookie module + * fix cookie max-age + +3.0.2 / 2012-11-08 +================== + + * add OPTIONS to cors example. Closes #1398 + * fix route chaining regression. Closes #1397 + +3.0.1 / 2012-11-01 +================== + + * update connect + +3.0.0 / 2012-10-23 +================== + + * add `make clean` + * add "Basic" check to req.auth + * add `req.auth` test coverage + * add cb && cb(payload) to `res.jsonp()`. Closes #1374 + * add backwards compat for `res.redirect()` status. Closes #1336 + * add support for `res.json()` to retain previously defined Content-Types. Closes #1349 + * update connect + * change `res.redirect()` to utilize a pathname-relative Location again. Closes #1382 + * remove non-primitive string support for `res.send()` + * fix view-locals example. Closes #1370 + * fix route-separation example + +3.0.0rc5 / 2012-09-18 +================== + + * update connect + * add redis search example + * add static-files example + * add "x-powered-by" setting (`app.disable('x-powered-by')`) + * add "application/octet-stream" redirect Accept test case. Closes #1317 + +3.0.0rc4 / 2012-08-30 +================== + + * add `res.jsonp()`. Closes #1307 + * add "verbose errors" option to error-pages example + * add another route example to express(1) so people are not so confused + * add redis online user activity tracking example + * update connect dep + * fix etag quoting. Closes #1310 + * fix error-pages 404 status + * fix jsonp callback char restrictions + * remove old OPTIONS default response + +3.0.0rc3 / 2012-08-13 +================== + + * update connect dep + * fix signed cookies to work with `connect.cookieParser()` ("s:" prefix was missing) [tnydwrds] + * fix `res.render()` clobbering of "locals" + +3.0.0rc2 / 2012-08-03 +================== + + * add CORS example + * update connect dep + * deprecate `.createServer()` & remove old stale examples + * fix: escape `res.redirect()` link + * fix vhost example + +3.0.0rc1 / 2012-07-24 +================== + + * add more examples to view-locals + * add scheme-relative redirects (`res.redirect("//foo.com")`) support + * update cookie dep + * update connect dep + * update send dep + * fix `express(1)` -h flag, use -H for hogan. Closes #1245 + * fix `res.sendfile()` socket error handling regression + +3.0.0beta7 / 2012-07-16 +================== + + * update connect dep for `send()` root normalization regression + +3.0.0beta6 / 2012-07-13 +================== + + * add `err.view` property for view errors. Closes #1226 + * add "jsonp callback name" setting + * add support for "/foo/:bar*" non-greedy matches + * change `res.sendfile()` to use `send()` module + * change `res.send` to use "response-send" module + * remove `app.locals.use` and `res.locals.use`, use regular middleware + +3.0.0beta5 / 2012-07-03 +================== + + * add "make check" support + * add route-map example + * add `res.json(obj, status)` support back for BC + * add "methods" dep, remove internal methods module + * update connect dep + * update auth example to utilize cores pbkdf2 + * updated tests to use "supertest" + +3.0.0beta4 / 2012-06-25 +================== + + * Added `req.auth` + * Added `req.range(size)` + * Added `res.links(obj)` + * Added `res.send(body, status)` support back for backwards compat + * Added `.default()` support to `res.format()` + * Added 2xx / 304 check to `req.fresh` + * Revert "Added + support to the router" + * Fixed `res.send()` freshness check, respect res.statusCode + +3.0.0beta3 / 2012-06-15 +================== + + * Added hogan `--hjs` to express(1) [nullfirm] + * Added another example to content-negotiation + * Added `fresh` dep + * Changed: `res.send()` always checks freshness + * Fixed: expose connects mime module. Closes #1165 + +3.0.0beta2 / 2012-06-06 +================== + + * Added `+` support to the router + * Added `req.host` + * Changed `req.param()` to check route first + * Update connect dep + +3.0.0beta1 / 2012-06-01 +================== + + * Added `res.format()` callback to override default 406 behaviour + * Fixed `res.redirect()` 406. Closes #1154 + +3.0.0alpha5 / 2012-05-30 +================== + + * Added `req.ip` + * Added `{ signed: true }` option to `res.cookie()` + * Removed `res.signedCookie()` + * Changed: dont reverse `req.ips` + * Fixed "trust proxy" setting check for `req.ips` + +3.0.0alpha4 / 2012-05-09 +================== + + * Added: allow `[]` in jsonp callback. Closes #1128 + * Added `PORT` env var support in generated template. Closes #1118 [benatkin] + * Updated: connect 2.2.2 + +3.0.0alpha3 / 2012-05-04 +================== + + * Added public `app.routes`. Closes #887 + * Added _view-locals_ example + * Added _mvc_ example + * Added `res.locals.use()`. Closes #1120 + * Added conditional-GET support to `res.send()` + * Added: coerce `res.set()` values to strings + * Changed: moved `static()` in generated apps below router + * Changed: `res.send()` only set ETag when not previously set + * Changed connect 2.2.1 dep + * Changed: `make test` now runs unit / acceptance tests + * Fixed req/res proto inheritance + +3.0.0alpha2 / 2012-04-26 +================== + + * Added `make benchmark` back + * Added `res.send()` support for `String` objects + * Added client-side data exposing example + * Added `res.header()` and `req.header()` aliases for BC + * Added `express.createServer()` for BC + * Perf: memoize parsed urls + * Perf: connect 2.2.0 dep + * Changed: make `expressInit()` middleware self-aware + * Fixed: use app.get() for all core settings + * Fixed redis session example + * Fixed session example. Closes #1105 + * Fixed generated express dep. Closes #1078 + +3.0.0alpha1 / 2012-04-15 +================== + + * Added `app.locals.use(callback)` + * Added `app.locals` object + * Added `app.locals(obj)` + * Added `res.locals` object + * Added `res.locals(obj)` + * Added `res.format()` for content-negotiation + * Added `app.engine()` + * Added `res.cookie()` JSON cookie support + * Added "trust proxy" setting + * Added `req.subdomains` + * Added `req.protocol` + * Added `req.secure` + * Added `req.path` + * Added `req.ips` + * Added `req.fresh` + * Added `req.stale` + * Added comma-delimited / array support for `req.accepts()` + * Added debug instrumentation + * Added `res.set(obj)` + * Added `res.set(field, value)` + * Added `res.get(field)` + * Added `app.get(setting)`. Closes #842 + * Added `req.acceptsLanguage()` + * Added `req.acceptsCharset()` + * Added `req.accepted` + * Added `req.acceptedLanguages` + * Added `req.acceptedCharsets` + * Added "json replacer" setting + * Added "json spaces" setting + * Added X-Forwarded-Proto support to `res.redirect()`. Closes #92 + * Added `--less` support to express(1) + * Added `express.response` prototype + * Added `express.request` prototype + * Added `express.application` prototype + * Added `app.path()` + * Added `app.render()` + * Added `res.type()` to replace `res.contentType()` + * Changed: `res.redirect()` to add relative support + * Changed: enable "jsonp callback" by default + * Changed: renamed "case sensitive routes" to "case sensitive routing" + * Rewrite of all tests with mocha + * Removed "root" setting + * Removed `res.redirect('home')` support + * Removed `req.notify()` + * Removed `app.register()` + * Removed `app.redirect()` + * Removed `app.is()` + * Removed `app.helpers()` + * Removed `app.dynamicHelpers()` + * Fixed `res.sendfile()` with non-GET. Closes #723 + * Fixed express(1) public dir for windows. Closes #866 + +2.5.9/ 2012-04-02 +================== + + * Added support for PURGE request method [pbuyle] + * Fixed `express(1)` generated app `app.address()` before `listening` [mmalecki] + +2.5.8 / 2012-02-08 +================== + + * Update mkdirp dep. Closes #991 + +2.5.7 / 2012-02-06 +================== + + * Fixed `app.all` duplicate DELETE requests [mscdex] + +2.5.6 / 2012-01-13 +================== + + * Updated hamljs dev dep. Closes #953 + +2.5.5 / 2012-01-08 +================== + + * Fixed: set `filename` on cached templates [matthewleon] + +2.5.4 / 2012-01-02 +================== + + * Fixed `express(1)` eol on 0.4.x. Closes #947 + +2.5.3 / 2011-12-30 +================== + + * Fixed `req.is()` when a charset is present + +2.5.2 / 2011-12-10 +================== + + * Fixed: express(1) LF -> CRLF for windows + +2.5.1 / 2011-11-17 +================== + + * Changed: updated connect to 1.8.x + * Removed sass.js support from express(1) + +2.5.0 / 2011-10-24 +================== + + * Added ./routes dir for generated app by default + * Added npm install reminder to express(1) app gen + * Added 0.5.x support + * Removed `make test-cov` since it wont work with node 0.5.x + * Fixed express(1) public dir for windows. Closes #866 + +2.4.7 / 2011-10-05 +================== + + * Added mkdirp to express(1). Closes #795 + * Added simple _json-config_ example + * Added shorthand for the parsed request's pathname via `req.path` + * Changed connect dep to 1.7.x to fix npm issue... + * Fixed `res.redirect()` __HEAD__ support. [reported by xerox] + * Fixed `req.flash()`, only escape args + * Fixed absolute path checking on windows. Closes #829 [reported by andrewpmckenzie] + +2.4.6 / 2011-08-22 +================== + + * Fixed multiple param callback regression. Closes #824 [reported by TroyGoode] + +2.4.5 / 2011-08-19 +================== + + * Added support for routes to handle errors. Closes #809 + * Added `app.routes.all()`. Closes #803 + * Added "basepath" setting to work in conjunction with reverse proxies etc. + * Refactored `Route` to use a single array of callbacks + * Added support for multiple callbacks for `app.param()`. Closes #801 +Closes #805 + * Changed: removed .call(self) for route callbacks + * Dependency: `qs >= 0.3.1` + * Fixed `res.redirect()` on windows due to `join()` usage. Closes #808 + +2.4.4 / 2011-08-05 +================== + + * Fixed `res.header()` intention of a set, even when `undefined` + * Fixed `*`, value no longer required + * Fixed `res.send(204)` support. Closes #771 + +2.4.3 / 2011-07-14 +================== + + * Added docs for `status` option special-case. Closes #739 + * Fixed `options.filename`, exposing the view path to template engines + +2.4.2. / 2011-07-06 +================== + + * Revert "removed jsonp stripping" for XSS + +2.4.1 / 2011-07-06 +================== + + * Added `res.json()` JSONP support. Closes #737 + * Added _extending-templates_ example. Closes #730 + * Added "strict routing" setting for trailing slashes + * Added support for multiple envs in `app.configure()` calls. Closes #735 + * Changed: `res.send()` using `res.json()` + * Changed: when cookie `path === null` don't default it + * Changed; default cookie path to "home" setting. Closes #731 + * Removed _pids/logs_ creation from express(1) + +2.4.0 / 2011-06-28 +================== + + * Added chainable `res.status(code)` + * Added `res.json()`, an explicit version of `res.send(obj)` + * Added simple web-service example + +2.3.12 / 2011-06-22 +================== + + * \#express is now on freenode! come join! + * Added `req.get(field, param)` + * Added links to Japanese documentation, thanks @hideyukisaito! + * Added; the `express(1)` generated app outputs the env + * Added `content-negotiation` example + * Dependency: connect >= 1.5.1 < 2.0.0 + * Fixed view layout bug. Closes #720 + * Fixed; ignore body on 304. Closes #701 + +2.3.11 / 2011-06-04 +================== + + * Added `npm test` + * Removed generation of dummy test file from `express(1)` + * Fixed; `express(1)` adds express as a dep + * Fixed; prune on `prepublish` + +2.3.10 / 2011-05-27 +================== + + * Added `req.route`, exposing the current route + * Added _package.json_ generation support to `express(1)` + * Fixed call to `app.param()` function for optional params. Closes #682 + +2.3.9 / 2011-05-25 +================== + + * Fixed bug-ish with `../' in `res.partial()` calls + +2.3.8 / 2011-05-24 +================== + + * Fixed `app.options()` + +2.3.7 / 2011-05-23 +================== + + * Added route `Collection`, ex: `app.get('/user/:id').remove();` + * Added support for `app.param(fn)` to define param logic + * Removed `app.param()` support for callback with return value + * Removed module.parent check from express(1) generated app. Closes #670 + * Refactored router. Closes #639 + +2.3.6 / 2011-05-20 +================== + + * Changed; using devDependencies instead of git submodules + * Fixed redis session example + * Fixed markdown example + * Fixed view caching, should not be enabled in development + +2.3.5 / 2011-05-20 +================== + + * Added export `.view` as alias for `.View` + +2.3.4 / 2011-05-08 +================== + + * Added `./examples/say` + * Fixed `res.sendfile()` bug preventing the transfer of files with spaces + +2.3.3 / 2011-05-03 +================== + + * Added "case sensitive routes" option. + * Changed; split methods supported per rfc [slaskis] + * Fixed route-specific middleware when using the same callback function several times + +2.3.2 / 2011-04-27 +================== + + * Fixed view hints + +2.3.1 / 2011-04-26 +================== + + * Added `app.match()` as `app.match.all()` + * Added `app.lookup()` as `app.lookup.all()` + * Added `app.remove()` for `app.remove.all()` + * Added `app.remove.VERB()` + * Fixed template caching collision issue. Closes #644 + * Moved router over from connect and started refactor + +2.3.0 / 2011-04-25 +================== + + * Added options support to `res.clearCookie()` + * Added `res.helpers()` as alias of `res.locals()` + * Added; json defaults to UTF-8 with `res.send()`. Closes #632. [Daniel * Dependency `connect >= 1.4.0` + * Changed; auto set Content-Type in res.attachement [Aaron Heckmann] + * Renamed "cache views" to "view cache". Closes #628 + * Fixed caching of views when using several apps. Closes #637 + * Fixed gotcha invoking `app.param()` callbacks once per route middleware. +Closes #638 + * Fixed partial lookup precedence. Closes #631 +Shaw] + +2.2.2 / 2011-04-12 +================== + + * Added second callback support for `res.download()` connection errors + * Fixed `filename` option passing to template engine + +2.2.1 / 2011-04-04 +================== + + * Added `layout(path)` helper to change the layout within a view. Closes #610 + * Fixed `partial()` collection object support. + Previously only anything with `.length` would work. + When `.length` is present one must still be aware of holes, + however now `{ collection: {foo: 'bar'}}` is valid, exposes + `keyInCollection` and `keysInCollection`. + + * Performance improved with better view caching + * Removed `request` and `response` locals + * Changed; errorHandler page title is now `Express` instead of `Connect` + +2.2.0 / 2011-03-30 +================== + + * Added `app.lookup.VERB()`, ex `app.lookup.put('/user/:id')`. Closes #606 + * Added `app.match.VERB()`, ex `app.match.put('/user/12')`. Closes #606 + * Added `app.VERB(path)` as alias of `app.lookup.VERB()`. + * Dependency `connect >= 1.2.0` + +2.1.1 / 2011-03-29 +================== + + * Added; expose `err.view` object when failing to locate a view + * Fixed `res.partial()` call `next(err)` when no callback is given [reported by aheckmann] + * Fixed; `res.send(undefined)` responds with 204 [aheckmann] + +2.1.0 / 2011-03-24 +================== + + * Added `/_?` partial lookup support. Closes #447 + * Added `request`, `response`, and `app` local variables + * Added `settings` local variable, containing the app's settings + * Added `req.flash()` exception if `req.session` is not available + * Added `res.send(bool)` support (json response) + * Fixed stylus example for latest version + * Fixed; wrap try/catch around `res.render()` + +2.0.0 / 2011-03-17 +================== + + * Fixed up index view path alternative. + * Changed; `res.locals()` without object returns the locals + +2.0.0rc3 / 2011-03-17 +================== + + * Added `res.locals(obj)` to compliment `res.local(key, val)` + * Added `res.partial()` callback support + * Fixed recursive error reporting issue in `res.render()` + +2.0.0rc2 / 2011-03-17 +================== + + * Changed; `partial()` "locals" are now optional + * Fixed `SlowBuffer` support. Closes #584 [reported by tyrda01] + * Fixed .filename view engine option [reported by drudge] + * Fixed blog example + * Fixed `{req,res}.app` reference when mounting [Ben Weaver] + +2.0.0rc / 2011-03-14 +================== + + * Fixed; expose `HTTPSServer` constructor + * Fixed express(1) default test charset. Closes #579 [reported by secoif] + * Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP] + +2.0.0beta3 / 2011-03-09 +================== + + * Added support for `res.contentType()` literal + The original `res.contentType('.json')`, + `res.contentType('application/json')`, and `res.contentType('json')` + will work now. + * Added `res.render()` status option support back + * Added charset option for `res.render()` + * Added `.charset` support (via connect 1.0.4) + * Added view resolution hints when in development and a lookup fails + * Added layout lookup support relative to the page view. + For example while rendering `./views/user/index.jade` if you create + `./views/user/layout.jade` it will be used in favour of the root layout. + * Fixed `res.redirect()`. RFC states absolute url [reported by unlink] + * Fixed; default `res.send()` string charset to utf8 + * Removed `Partial` constructor (not currently used) + +2.0.0beta2 / 2011-03-07 +================== + + * Added res.render() `.locals` support back to aid in migration process + * Fixed flash example + +2.0.0beta / 2011-03-03 +================== + + * Added HTTPS support + * Added `res.cookie()` maxAge support + * Added `req.header()` _Referrer_ / _Referer_ special-case, either works + * Added mount support for `res.redirect()`, now respects the mount-point + * Added `union()` util, taking place of `merge(clone())` combo + * Added stylus support to express(1) generated app + * Added secret to session middleware used in examples and generated app + * Added `res.local(name, val)` for progressive view locals + * Added default param support to `req.param(name, default)` + * Added `app.disabled()` and `app.enabled()` + * Added `app.register()` support for omitting leading ".", either works + * Added `res.partial()`, using the same interface as `partial()` within a view. Closes #539 + * Added `app.param()` to map route params to async/sync logic + * Added; aliased `app.helpers()` as `app.locals()`. Closes #481 + * Added extname with no leading "." support to `res.contentType()` + * Added `cache views` setting, defaulting to enabled in "production" env + * Added index file partial resolution, eg: partial('user') may try _views/user/index.jade_. + * Added `req.accepts()` support for extensions + * Changed; `res.download()` and `res.sendfile()` now utilize Connect's + static file server `connect.static.send()`. + * Changed; replaced `connect.utils.mime()` with npm _mime_ module + * Changed; allow `req.query` to be pre-defined (via middleware or other parent + * Changed view partial resolution, now relative to parent view + * Changed view engine signature. no longer `engine.render(str, options, callback)`, now `engine.compile(str, options) -> Function`, the returned function accepts `fn(locals)`. + * Fixed `req.param()` bug returning Array.prototype methods. Closes #552 + * Fixed; using `Stream#pipe()` instead of `sys.pump()` in `res.sendfile()` + * Fixed; using _qs_ module instead of _querystring_ + * Fixed; strip unsafe chars from jsonp callbacks + * Removed "stream threshold" setting + +1.0.8 / 2011-03-01 +================== + + * Allow `req.query` to be pre-defined (via middleware or other parent app) + * "connect": ">= 0.5.0 < 1.0.0". Closes #547 + * Removed the long deprecated __EXPRESS_ENV__ support + +1.0.7 / 2011-02-07 +================== + + * Fixed `render()` setting inheritance. + Mounted apps would not inherit "view engine" + +1.0.6 / 2011-02-07 +================== + + * Fixed `view engine` setting bug when period is in dirname + +1.0.5 / 2011-02-05 +================== + + * Added secret to generated app `session()` call + +1.0.4 / 2011-02-05 +================== + + * Added `qs` dependency to _package.json_ + * Fixed namespaced `require()`s for latest connect support + +1.0.3 / 2011-01-13 +================== + + * Remove unsafe characters from JSONP callback names [Ryan Grove] + +1.0.2 / 2011-01-10 +================== + + * Removed nested require, using `connect.router` + +1.0.1 / 2010-12-29 +================== + + * Fixed for middleware stacked via `createServer()` + previously the `foo` middleware passed to `createServer(foo)` + would not have access to Express methods such as `res.send()` + or props like `req.query` etc. + +1.0.0 / 2010-11-16 +================== + + * Added; deduce partial object names from the last segment. + For example by default `partial('forum/post', postObject)` will + give you the _post_ object, providing a meaningful default. + * Added http status code string representation to `res.redirect()` body + * Added; `res.redirect()` supporting _text/plain_ and _text/html_ via __Accept__. + * Added `req.is()` to aid in content negotiation + * Added partial local inheritance [suggested by masylum]. Closes #102 + providing access to parent template locals. + * Added _-s, --session[s]_ flag to express(1) to add session related middleware + * Added _--template_ flag to express(1) to specify the + template engine to use. + * Added _--css_ flag to express(1) to specify the + stylesheet engine to use (or just plain css by default). + * Added `app.all()` support [thanks aheckmann] + * Added partial direct object support. + You may now `partial('user', user)` providing the "user" local, + vs previously `partial('user', { object: user })`. + * Added _route-separation_ example since many people question ways + to do this with CommonJS modules. Also view the _blog_ example for + an alternative. + * Performance; caching view path derived partial object names + * Fixed partial local inheritance precedence. [reported by Nick Poulden] Closes #454 + * Fixed jsonp support; _text/javascript_ as per mailinglist discussion + +1.0.0rc4 / 2010-10-14 +================== + + * Added _NODE_ENV_ support, _EXPRESS_ENV_ is deprecated and will be removed in 1.0.0 + * Added route-middleware support (very helpful, see the [docs](http://expressjs.com/guide.html#Route-Middleware)) + * Added _jsonp callback_ setting to enable/disable jsonp autowrapping [Dav Glass] + * Added callback query check on response.send to autowrap JSON objects for simple webservice implementations [Dav Glass] + * Added `partial()` support for array-like collections. Closes #434 + * Added support for swappable querystring parsers + * Added session usage docs. Closes #443 + * Added dynamic helper caching. Closes #439 [suggested by maritz] + * Added authentication example + * Added basic Range support to `res.sendfile()` (and `res.download()` etc) + * Changed; `express(1)` generated app using 2 spaces instead of 4 + * Default env to "development" again [aheckmann] + * Removed _context_ option is no more, use "scope" + * Fixed; exposing _./support_ libs to examples so they can run without installs + * Fixed mvc example + +1.0.0rc3 / 2010-09-20 +================== + + * Added confirmation for `express(1)` app generation. Closes #391 + * Added extending of flash formatters via `app.flashFormatters` + * Added flash formatter support. Closes #411 + * Added streaming support to `res.sendfile()` using `sys.pump()` when >= "stream threshold" + * Added _stream threshold_ setting for `res.sendfile()` + * Added `res.send()` __HEAD__ support + * Added `res.clearCookie()` + * Added `res.cookie()` + * Added `res.render()` headers option + * Added `res.redirect()` response bodies + * Added `res.render()` status option support. Closes #425 [thanks aheckmann] + * Fixed `res.sendfile()` responding with 403 on malicious path + * Fixed `res.download()` bug; when an error occurs remove _Content-Disposition_ + * Fixed; mounted apps settings now inherit from parent app [aheckmann] + * Fixed; stripping Content-Length / Content-Type when 204 + * Fixed `res.send()` 204. Closes #419 + * Fixed multiple _Set-Cookie_ headers via `res.header()`. Closes #402 + * Fixed bug messing with error handlers when `listenFD()` is called instead of `listen()`. [thanks guillermo] + + +1.0.0rc2 / 2010-08-17 +================== + + * Added `app.register()` for template engine mapping. Closes #390 + * Added `res.render()` callback support as second argument (no options) + * Added callback support to `res.download()` + * Added callback support for `res.sendfile()` + * Added support for middleware access via `express.middlewareName()` vs `connect.middlewareName()` + * Added "partials" setting to docs + * Added default expresso tests to `express(1)` generated app. Closes #384 + * Fixed `res.sendfile()` error handling, defer via `next()` + * Fixed `res.render()` callback when a layout is used [thanks guillermo] + * Fixed; `make install` creating ~/.node_libraries when not present + * Fixed issue preventing error handlers from being defined anywhere. Closes #387 + +1.0.0rc / 2010-07-28 +================== + + * Added mounted hook. Closes #369 + * Added connect dependency to _package.json_ + + * Removed "reload views" setting and support code + development env never caches, production always caches. + + * Removed _param_ in route callbacks, signature is now + simply (req, res, next), previously (req, res, params, next). + Use _req.params_ for path captures, _req.query_ for GET params. + + * Fixed "home" setting + * Fixed middleware/router precedence issue. Closes #366 + * Fixed; _configure()_ callbacks called immediately. Closes #368 + +1.0.0beta2 / 2010-07-23 +================== + + * Added more examples + * Added; exporting `Server` constructor + * Added `Server#helpers()` for view locals + * Added `Server#dynamicHelpers()` for dynamic view locals. Closes #349 + * Added support for absolute view paths + * Added; _home_ setting defaults to `Server#route` for mounted apps. Closes #363 + * Added Guillermo Rauch to the contributor list + * Added support for "as" for non-collection partials. Closes #341 + * Fixed _install.sh_, ensuring _~/.node_libraries_ exists. Closes #362 [thanks jf] + * Fixed `res.render()` exceptions, now passed to `next()` when no callback is given [thanks guillermo] + * Fixed instanceof `Array` checks, now `Array.isArray()` + * Fixed express(1) expansion of public dirs. Closes #348 + * Fixed middleware precedence. Closes #345 + * Fixed view watcher, now async [thanks aheckmann] + +1.0.0beta / 2010-07-15 +================== + + * Re-write + - much faster + - much lighter + - Check [ExpressJS.com](http://expressjs.com) for migration guide and updated docs + +0.14.0 / 2010-06-15 +================== + + * Utilize relative requires + * Added Static bufferSize option [aheckmann] + * Fixed caching of view and partial subdirectories [aheckmann] + * Fixed mime.type() comments now that ".ext" is not supported + * Updated haml submodule + * Updated class submodule + * Removed bin/express + +0.13.0 / 2010-06-01 +================== + + * Added node v0.1.97 compatibility + * Added support for deleting cookies via Request#cookie('key', null) + * Updated haml submodule + * Fixed not-found page, now using using charset utf-8 + * Fixed show-exceptions page, now using using charset utf-8 + * Fixed view support due to fs.readFile Buffers + * Changed; mime.type() no longer accepts ".type" due to node extname() changes + +0.12.0 / 2010-05-22 +================== + + * Added node v0.1.96 compatibility + * Added view `helpers` export which act as additional local variables + * Updated haml submodule + * Changed ETag; removed inode, modified time only + * Fixed LF to CRLF for setting multiple cookies + * Fixed cookie complation; values are now urlencoded + * Fixed cookies parsing; accepts quoted values and url escaped cookies + +0.11.0 / 2010-05-06 +================== + + * Added support for layouts using different engines + - this.render('page.html.haml', { layout: 'super-cool-layout.html.ejs' }) + - this.render('page.html.haml', { layout: 'foo' }) // assumes 'foo.html.haml' + - this.render('page.html.haml', { layout: false }) // no layout + * Updated ext submodule + * Updated haml submodule + * Fixed EJS partial support by passing along the context. Issue #307 + +0.10.1 / 2010-05-03 +================== + + * Fixed binary uploads. + +0.10.0 / 2010-04-30 +================== + + * Added charset support via Request#charset (automatically assigned to 'UTF-8' when respond()'s + encoding is set to 'utf8' or 'utf-8'. + * Added "encoding" option to Request#render(). Closes #299 + * Added "dump exceptions" setting, which is enabled by default. + * Added simple ejs template engine support + * Added error response support for text/plain, application/json. Closes #297 + * Added callback function param to Request#error() + * Added Request#sendHead() + * Added Request#stream() + * Added support for Request#respond(304, null) for empty response bodies + * Added ETag support to Request#sendfile() + * Added options to Request#sendfile(), passed to fs.createReadStream() + * Added filename arg to Request#download() + * Performance enhanced due to pre-reversing plugins so that plugins.reverse() is not called on each request + * Performance enhanced by preventing several calls to toLowerCase() in Router#match() + * Changed; Request#sendfile() now streams + * Changed; Renamed Request#halt() to Request#respond(). Closes #289 + * Changed; Using sys.inspect() instead of JSON.encode() for error output + * Changed; run() returns the http.Server instance. Closes #298 + * Changed; Defaulting Server#host to null (INADDR_ANY) + * Changed; Logger "common" format scale of 0.4f + * Removed Logger "request" format + * Fixed; Catching ENOENT in view caching, preventing error when "views/partials" is not found + * Fixed several issues with http client + * Fixed Logger Content-Length output + * Fixed bug preventing Opera from retaining the generated session id. Closes #292 + +0.9.0 / 2010-04-14 +================== + + * Added DSL level error() route support + * Added DSL level notFound() route support + * Added Request#error() + * Added Request#notFound() + * Added Request#render() callback function. Closes #258 + * Added "max upload size" setting + * Added "magic" variables to collection partials (\_\_index\_\_, \_\_length\_\_, \_\_isFirst\_\_, \_\_isLast\_\_). Closes #254 + * Added [haml.js](http://github.com/visionmedia/haml.js) submodule; removed haml-js + * Added callback function support to Request#halt() as 3rd/4th arg + * Added preprocessing of route param wildcards using param(). Closes #251 + * Added view partial support (with collections etc) + * Fixed bug preventing falsey params (such as ?page=0). Closes #286 + * Fixed setting of multiple cookies. Closes #199 + * Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml) + * Changed; session cookie is now httpOnly + * Changed; Request is no longer global + * Changed; Event is no longer global + * Changed; "sys" module is no longer global + * Changed; moved Request#download to Static plugin where it belongs + * Changed; Request instance created before body parsing. Closes #262 + * Changed; Pre-caching views in memory when "cache view contents" is enabled. Closes #253 + * Changed; Pre-caching view partials in memory when "cache view partials" is enabled + * Updated support to node --version 0.1.90 + * Updated dependencies + * Removed set("session cookie") in favour of use(Session, { cookie: { ... }}) + * Removed utils.mixin(); use Object#mergeDeep() + +0.8.0 / 2010-03-19 +================== + + * Added coffeescript example app. Closes #242 + * Changed; cache api now async friendly. Closes #240 + * Removed deprecated 'express/static' support. Use 'express/plugins/static' + +0.7.6 / 2010-03-19 +================== + + * Added Request#isXHR. Closes #229 + * Added `make install` (for the executable) + * Added `express` executable for setting up simple app templates + * Added "GET /public/*" to Static plugin, defaulting to /public + * Added Static plugin + * Fixed; Request#render() only calls cache.get() once + * Fixed; Namespacing View caches with "view:" + * Fixed; Namespacing Static caches with "static:" + * Fixed; Both example apps now use the Static plugin + * Fixed set("views"). Closes #239 + * Fixed missing space for combined log format + * Deprecated Request#sendfile() and 'express/static' + * Removed Server#running + +0.7.5 / 2010-03-16 +================== + + * Added Request#flash() support without args, now returns all flashes + * Updated ext submodule + +0.7.4 / 2010-03-16 +================== + + * Fixed session reaper + * Changed; class.js replacing js-oo Class implementation (quite a bit faster, no browser cruft) + +0.7.3 / 2010-03-16 +================== + + * Added package.json + * Fixed requiring of haml / sass due to kiwi removal + +0.7.2 / 2010-03-16 +================== + + * Fixed GIT submodules (HAH!) + +0.7.1 / 2010-03-16 +================== + + * Changed; Express now using submodules again until a PM is adopted + * Changed; chat example using millisecond conversions from ext + +0.7.0 / 2010-03-15 +================== + + * Added Request#pass() support (finds the next matching route, or the given path) + * Added Logger plugin (default "common" format replaces CommonLogger) + * Removed Profiler plugin + * Removed CommonLogger plugin + +0.6.0 / 2010-03-11 +================== + + * Added seed.yml for kiwi package management support + * Added HTTP client query string support when method is GET. Closes #205 + + * Added support for arbitrary view engines. + For example "foo.engine.html" will now require('engine'), + the exports from this module are cached after the first require(). + + * Added async plugin support + + * Removed usage of RESTful route funcs as http client + get() etc, use http.get() and friends + + * Removed custom exceptions + +0.5.0 / 2010-03-10 +================== + + * Added ext dependency (library of js extensions) + * Removed extname() / basename() utils. Use path module + * Removed toArray() util. Use arguments.values + * Removed escapeRegexp() util. Use RegExp.escape() + * Removed process.mixin() dependency. Use utils.mixin() + * Removed Collection + * Removed ElementCollection + * Shameless self promotion of ebook "Advanced JavaScript" (http://dev-mag.com) ;) + +0.4.0 / 2010-02-11 +================== + + * Added flash() example to sample upload app + * Added high level restful http client module (express/http) + * Changed; RESTful route functions double as HTTP clients. Closes #69 + * Changed; throwing error when routes are added at runtime + * Changed; defaulting render() context to the current Request. Closes #197 + * Updated haml submodule + +0.3.0 / 2010-02-11 +================== + + * Updated haml / sass submodules. Closes #200 + * Added flash message support. Closes #64 + * Added accepts() now allows multiple args. fixes #117 + * Added support for plugins to halt. Closes #189 + * Added alternate layout support. Closes #119 + * Removed Route#run(). Closes #188 + * Fixed broken specs due to use(Cookie) missing + +0.2.1 / 2010-02-05 +================== + + * Added "plot" format option for Profiler (for gnuplot processing) + * Added request number to Profiler plugin + * Fixed binary encoding for multi-part file uploads, was previously defaulting to UTF8 + * Fixed issue with routes not firing when not files are present. Closes #184 + * Fixed process.Promise -> events.Promise + +0.2.0 / 2010-02-03 +================== + + * Added parseParam() support for name[] etc. (allows for file inputs with "multiple" attr) Closes #180 + * Added Both Cache and Session option "reapInterval" may be "reapEvery". Closes #174 + * Added expiration support to cache api with reaper. Closes #133 + * Added cache Store.Memory#reap() + * Added Cache; cache api now uses first class Cache instances + * Added abstract session Store. Closes #172 + * Changed; cache Memory.Store#get() utilizing Collection + * Renamed MemoryStore -> Store.Memory + * Fixed use() of the same plugin several time will always use latest options. Closes #176 + +0.1.0 / 2010-02-03 +================== + + * Changed; Hooks (before / after) pass request as arg as well as evaluated in their context + * Updated node support to 0.1.27 Closes #169 + * Updated dirname(__filename) -> __dirname + * Updated libxmljs support to v0.2.0 + * Added session support with memory store / reaping + * Added quick uid() helper + * Added multi-part upload support + * Added Sass.js support / submodule + * Added production env caching view contents and static files + * Added static file caching. Closes #136 + * Added cache plugin with memory stores + * Added support to StaticFile so that it works with non-textual files. + * Removed dirname() helper + * Removed several globals (now their modules must be required) + +0.0.2 / 2010-01-10 +================== + + * Added view benchmarks; currently haml vs ejs + * Added Request#attachment() specs. Closes #116 + * Added use of node's parseQuery() util. Closes #123 + * Added `make init` for submodules + * Updated Haml + * Updated sample chat app to show messages on load + * Updated libxmljs parseString -> parseHtmlString + * Fixed `make init` to work with older versions of git + * Fixed specs can now run independent specs for those who cant build deps. Closes #127 + * Fixed issues introduced by the node url module changes. Closes 126. + * Fixed two assertions failing due to Collection#keys() returning strings + * Fixed faulty Collection#toArray() spec due to keys() returning strings + * Fixed `make test` now builds libxmljs.node before testing + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/node_modules/express/LICENSE b/node_modules/express/LICENSE new file mode 100644 index 0000000..aa927e4 --- /dev/null +++ b/node_modules/express/LICENSE @@ -0,0 +1,24 @@ +(The MIT License) + +Copyright (c) 2009-2014 TJ Holowaychuk +Copyright (c) 2013-2014 Roman Shtylman +Copyright (c) 2014-2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/Readme.md b/node_modules/express/Readme.md new file mode 100644 index 0000000..8da83a5 --- /dev/null +++ b/node_modules/express/Readme.md @@ -0,0 +1,138 @@ +[![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](http://expressjs.com/) + + Fast, unopinionated, minimalist web framework for [node](http://nodejs.org). + + [![NPM Version][npm-image]][npm-url] + [![NPM Downloads][downloads-image]][downloads-url] + [![Linux Build][travis-image]][travis-url] + [![Windows Build][appveyor-image]][appveyor-url] + [![Test Coverage][coveralls-image]][coveralls-url] + +```js +var express = require('express') +var app = express() + +app.get('/', function (req, res) { + res.send('Hello World') +}) + +app.listen(3000) +``` + +## Installation + +```bash +$ npm install express +``` + +## Features + + * Robust routing + * Focus on high performance + * Super-high test coverage + * HTTP helpers (redirection, caching, etc) + * View system supporting 14+ template engines + * Content negotiation + * Executable for generating applications quickly + +## Docs & Community + + * [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/strongloop/expressjs.com)] + * [#express](https://webchat.freenode.net/?channels=express) on freenode IRC + * [Github Organization](https://github.com/expressjs) for Official Middleware & Modules + * Visit the [Wiki](https://github.com/strongloop/express/wiki) + * [Google Group](https://groups.google.com/group/express-js) for discussion + * [Русскоязычная документация](http://jsman.ru/express/) + * [한국어 문서](http://expressjs.kr) - [[website repo](https://github.com/Hanul/expressjs.kr)] + +**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/strongloop/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/strongloop/express/wiki/New-features-in-4.x). + +## Quick Start + + The quickest way to get started with express is to utilize the executable [`express(1)`](https://github.com/expressjs/generator) to generate an application as shown below: + + Install the executable. The executable's major version will match Express's: + +```bash +$ npm install -g express-generator@4 +``` + + Create the app: + +```bash +$ express /tmp/foo && cd /tmp/foo +``` + + Install dependencies: + +```bash +$ npm install +``` + + Start the server: + +```bash +$ npm start +``` + +## Philosophy + + The Express philosophy is to provide small, robust tooling for HTTP servers, making + it a great solution for single page applications, web sites, hybrids, or public + HTTP APIs. + + Express does not force you to use any specific ORM or template engine. With support for over + 14 template engines via [Consolidate.js](https://github.com/tj/consolidate.js), + you can quickly craft your perfect framework. + +## Examples + + To view the examples, clone the Express repo and install the dependencies: + +```bash +$ git clone git://github.com/strongloop/express.git --depth 1 +$ cd express +$ npm install +``` + + Then run whichever example you want: + +```bash +$ node examples/content-negotiation +``` + +## Tests + + To run the test suite, first install the dependencies, then run `npm test`: + +```bash +$ npm install +$ npm test +``` + +## People + +The original author of Express is [TJ Holowaychuk](https://github.com/tj) [![TJ's Gratipay][gratipay-image-visionmedia]][gratipay-url-visionmedia] + +The current lead maintainer is [Douglas Christopher Wilson](https://github.com/dougwilson) [![Doug's Gratipay][gratipay-image-dougwilson]][gratipay-url-dougwilson] + +[List of all contributors](https://github.com/strongloop/express/graphs/contributors) + +## License + + [MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/express.svg +[npm-url]: https://npmjs.org/package/express +[downloads-image]: https://img.shields.io/npm/dm/express.svg +[downloads-url]: https://npmjs.org/package/express +[travis-image]: https://img.shields.io/travis/strongloop/express/master.svg?label=linux +[travis-url]: https://travis-ci.org/strongloop/express +[appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/express/master.svg?label=windows +[appveyor-url]: https://ci.appveyor.com/project/dougwilson/express +[coveralls-image]: https://img.shields.io/coveralls/strongloop/express/master.svg +[coveralls-url]: https://coveralls.io/r/strongloop/express?branch=master +[gratipay-image-visionmedia]: https://img.shields.io/gratipay/visionmedia.svg +[gratipay-url-visionmedia]: https://gratipay.com/visionmedia/ +[gratipay-image-dougwilson]: https://img.shields.io/gratipay/dougwilson.svg +[gratipay-url-dougwilson]: https://gratipay.com/dougwilson/ diff --git a/node_modules/express/index.js b/node_modules/express/index.js new file mode 100644 index 0000000..d219b0c --- /dev/null +++ b/node_modules/express/index.js @@ -0,0 +1,11 @@ +/*! + * express + * Copyright(c) 2009-2013 TJ Holowaychuk + * Copyright(c) 2013 Roman Shtylman + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +module.exports = require('./lib/express'); diff --git a/node_modules/express/lib/application.js b/node_modules/express/lib/application.js new file mode 100644 index 0000000..a9df910 --- /dev/null +++ b/node_modules/express/lib/application.js @@ -0,0 +1,643 @@ +/*! + * express + * Copyright(c) 2009-2013 TJ Holowaychuk + * Copyright(c) 2013 Roman Shtylman + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Module dependencies. + * @private + */ + +var finalhandler = require('finalhandler'); +var Router = require('./router'); +var methods = require('methods'); +var middleware = require('./middleware/init'); +var query = require('./middleware/query'); +var debug = require('debug')('express:application'); +var View = require('./view'); +var http = require('http'); +var compileETag = require('./utils').compileETag; +var compileQueryParser = require('./utils').compileQueryParser; +var compileTrust = require('./utils').compileTrust; +var deprecate = require('depd')('express'); +var flatten = require('array-flatten'); +var merge = require('utils-merge'); +var resolve = require('path').resolve; +var slice = Array.prototype.slice; + +/** + * Application prototype. + */ + +var app = exports = module.exports = {}; + +/** + * Variable for trust proxy inheritance back-compat + * @private + */ + +var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default'; + +/** + * Initialize the server. + * + * - setup default configuration + * - setup default middleware + * - setup route reflection methods + * + * @private + */ + +app.init = function init() { + this.cache = {}; + this.engines = {}; + this.settings = {}; + + this.defaultConfiguration(); +}; + +/** + * Initialize application configuration. + * @private + */ + +app.defaultConfiguration = function defaultConfiguration() { + var env = process.env.NODE_ENV || 'development'; + + // default settings + this.enable('x-powered-by'); + this.set('etag', 'weak'); + this.set('env', env); + this.set('query parser', 'extended'); + this.set('subdomain offset', 2); + this.set('trust proxy', false); + + // trust proxy inherit back-compat + Object.defineProperty(this.settings, trustProxyDefaultSymbol, { + configurable: true, + value: true + }); + + debug('booting in %s mode', env); + + this.on('mount', function onmount(parent) { + // inherit trust proxy + if (this.settings[trustProxyDefaultSymbol] === true + && typeof parent.settings['trust proxy fn'] === 'function') { + delete this.settings['trust proxy']; + delete this.settings['trust proxy fn']; + } + + // inherit protos + this.request.__proto__ = parent.request; + this.response.__proto__ = parent.response; + this.engines.__proto__ = parent.engines; + this.settings.__proto__ = parent.settings; + }); + + // setup locals + this.locals = Object.create(null); + + // top-most app is mounted at / + this.mountpath = '/'; + + // default locals + this.locals.settings = this.settings; + + // default configuration + this.set('view', View); + this.set('views', resolve('views')); + this.set('jsonp callback name', 'callback'); + + if (env === 'production') { + this.enable('view cache'); + } + + Object.defineProperty(this, 'router', { + get: function() { + throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.'); + } + }); +}; + +/** + * lazily adds the base router if it has not yet been added. + * + * We cannot add the base router in the defaultConfiguration because + * it reads app settings which might be set after that has run. + * + * @private + */ +app.lazyrouter = function lazyrouter() { + if (!this._router) { + this._router = new Router({ + caseSensitive: this.enabled('case sensitive routing'), + strict: this.enabled('strict routing') + }); + + this._router.use(query(this.get('query parser fn'))); + this._router.use(middleware.init(this)); + } +}; + +/** + * Dispatch a req, res pair into the application. Starts pipeline processing. + * + * If no callback is provided, then default error handlers will respond + * in the event of an error bubbling through the stack. + * + * @private + */ + +app.handle = function handle(req, res, callback) { + var router = this._router; + + // final handler + var done = callback || finalhandler(req, res, { + env: this.get('env'), + onerror: logerror.bind(this) + }); + + // no routes + if (!router) { + debug('no routes defined on app'); + done(); + return; + } + + router.handle(req, res, done); +}; + +/** + * Proxy `Router#use()` to add middleware to the app router. + * See Router#use() documentation for details. + * + * If the _fn_ parameter is an express app, then it will be + * mounted at the _route_ specified. + * + * @public + */ + +app.use = function use(fn) { + var offset = 0; + var path = '/'; + + // default path to '/' + // disambiguate app.use([fn]) + if (typeof fn !== 'function') { + var arg = fn; + + while (Array.isArray(arg) && arg.length !== 0) { + arg = arg[0]; + } + + // first arg is the path + if (typeof arg !== 'function') { + offset = 1; + path = fn; + } + } + + var fns = flatten(slice.call(arguments, offset)); + + if (fns.length === 0) { + throw new TypeError('app.use() requires middleware functions'); + } + + // setup router + this.lazyrouter(); + var router = this._router; + + fns.forEach(function (fn) { + // non-express app + if (!fn || !fn.handle || !fn.set) { + return router.use(path, fn); + } + + debug('.use app under %s', path); + fn.mountpath = path; + fn.parent = this; + + // restore .app property on req and res + router.use(path, function mounted_app(req, res, next) { + var orig = req.app; + fn.handle(req, res, function (err) { + req.__proto__ = orig.request; + res.__proto__ = orig.response; + next(err); + }); + }); + + // mounted an app + fn.emit('mount', this); + }, this); + + return this; +}; + +/** + * Proxy to the app `Router#route()` + * Returns a new `Route` instance for the _path_. + * + * Routes are isolated middleware stacks for specific paths. + * See the Route api docs for details. + * + * @public + */ + +app.route = function route(path) { + this.lazyrouter(); + return this._router.route(path); +}; + +/** + * Register the given template engine callback `fn` + * as `ext`. + * + * By default will `require()` the engine based on the + * file extension. For example if you try to render + * a "foo.jade" file Express will invoke the following internally: + * + * app.engine('jade', require('jade').__express); + * + * For engines that do not provide `.__express` out of the box, + * or if you wish to "map" a different extension to the template engine + * you may use this method. For example mapping the EJS template engine to + * ".html" files: + * + * app.engine('html', require('ejs').renderFile); + * + * In this case EJS provides a `.renderFile()` method with + * the same signature that Express expects: `(path, options, callback)`, + * though note that it aliases this method as `ejs.__express` internally + * so if you're using ".ejs" extensions you dont need to do anything. + * + * Some template engines do not follow this convention, the + * [Consolidate.js](https://github.com/tj/consolidate.js) + * library was created to map all of node's popular template + * engines to follow this convention, thus allowing them to + * work seamlessly within Express. + * + * @param {String} ext + * @param {Function} fn + * @return {app} for chaining + * @public + */ + +app.engine = function engine(ext, fn) { + if (typeof fn !== 'function') { + throw new Error('callback function required'); + } + + // get file extension + var extension = ext[0] !== '.' + ? '.' + ext + : ext; + + // store engine + this.engines[extension] = fn; + + return this; +}; + +/** + * Proxy to `Router#param()` with one added api feature. The _name_ parameter + * can be an array of names. + * + * See the Router#param() docs for more details. + * + * @param {String|Array} name + * @param {Function} fn + * @return {app} for chaining + * @public + */ + +app.param = function param(name, fn) { + this.lazyrouter(); + + if (Array.isArray(name)) { + for (var i = 0; i < name.length; i++) { + this.param(name[i], fn); + } + + return this; + } + + this._router.param(name, fn); + + return this; +}; + +/** + * Assign `setting` to `val`, or return `setting`'s value. + * + * app.set('foo', 'bar'); + * app.get('foo'); + * // => "bar" + * + * Mounted servers inherit their parent server's settings. + * + * @param {String} setting + * @param {*} [val] + * @return {Server} for chaining + * @public + */ + +app.set = function set(setting, val) { + if (arguments.length === 1) { + // app.get(setting) + return this.settings[setting]; + } + + debug('set "%s" to %o', setting, val); + + // set value + this.settings[setting] = val; + + // trigger matched settings + switch (setting) { + case 'etag': + this.set('etag fn', compileETag(val)); + break; + case 'query parser': + this.set('query parser fn', compileQueryParser(val)); + break; + case 'trust proxy': + this.set('trust proxy fn', compileTrust(val)); + + // trust proxy inherit back-compat + Object.defineProperty(this.settings, trustProxyDefaultSymbol, { + configurable: true, + value: false + }); + + break; + } + + return this; +}; + +/** + * Return the app's absolute pathname + * based on the parent(s) that have + * mounted it. + * + * For example if the application was + * mounted as "/admin", which itself + * was mounted as "/blog" then the + * return value would be "/blog/admin". + * + * @return {String} + * @private + */ + +app.path = function path() { + return this.parent + ? this.parent.path() + this.mountpath + : ''; +}; + +/** + * Check if `setting` is enabled (truthy). + * + * app.enabled('foo') + * // => false + * + * app.enable('foo') + * app.enabled('foo') + * // => true + * + * @param {String} setting + * @return {Boolean} + * @public + */ + +app.enabled = function enabled(setting) { + return Boolean(this.set(setting)); +}; + +/** + * Check if `setting` is disabled. + * + * app.disabled('foo') + * // => true + * + * app.enable('foo') + * app.disabled('foo') + * // => false + * + * @param {String} setting + * @return {Boolean} + * @public + */ + +app.disabled = function disabled(setting) { + return !this.set(setting); +}; + +/** + * Enable `setting`. + * + * @param {String} setting + * @return {app} for chaining + * @public + */ + +app.enable = function enable(setting) { + return this.set(setting, true); +}; + +/** + * Disable `setting`. + * + * @param {String} setting + * @return {app} for chaining + * @public + */ + +app.disable = function disable(setting) { + return this.set(setting, false); +}; + +/** + * Delegate `.VERB(...)` calls to `router.VERB(...)`. + */ + +methods.forEach(function(method){ + app[method] = function(path){ + if (method === 'get' && arguments.length === 1) { + // app.get(setting) + return this.set(path); + } + + this.lazyrouter(); + + var route = this._router.route(path); + route[method].apply(route, slice.call(arguments, 1)); + return this; + }; +}); + +/** + * Special-cased "all" method, applying the given route `path`, + * middleware, and callback to _every_ HTTP method. + * + * @param {String} path + * @param {Function} ... + * @return {app} for chaining + * @public + */ + +app.all = function all(path) { + this.lazyrouter(); + + var route = this._router.route(path); + var args = slice.call(arguments, 1); + + for (var i = 0; i < methods.length; i++) { + route[methods[i]].apply(route, args); + } + + return this; +}; + +// del -> delete alias + +app.del = deprecate.function(app.delete, 'app.del: Use app.delete instead'); + +/** + * Render the given view `name` name with `options` + * and a callback accepting an error and the + * rendered template string. + * + * Example: + * + * app.render('email', { name: 'Tobi' }, function(err, html){ + * // ... + * }) + * + * @param {String} name + * @param {String|Function} options or fn + * @param {Function} callback + * @public + */ + +app.render = function render(name, options, callback) { + var cache = this.cache; + var done = callback; + var engines = this.engines; + var opts = options; + var renderOptions = {}; + var view; + + // support callback function as second arg + if (typeof options === 'function') { + done = options; + opts = {}; + } + + // merge app.locals + merge(renderOptions, this.locals); + + // merge options._locals + if (opts._locals) { + merge(renderOptions, opts._locals); + } + + // merge options + merge(renderOptions, opts); + + // set .cache unless explicitly provided + if (renderOptions.cache == null) { + renderOptions.cache = this.enabled('view cache'); + } + + // primed cache + if (renderOptions.cache) { + view = cache[name]; + } + + // view + if (!view) { + var View = this.get('view'); + + view = new View(name, { + defaultEngine: this.get('view engine'), + root: this.get('views'), + engines: engines + }); + + if (!view.path) { + var dirs = Array.isArray(view.root) && view.root.length > 1 + ? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"' + : 'directory "' + view.root + '"' + var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs); + err.view = view; + return done(err); + } + + // prime the cache + if (renderOptions.cache) { + cache[name] = view; + } + } + + // render + tryRender(view, renderOptions, done); +}; + +/** + * Listen for connections. + * + * A node `http.Server` is returned, with this + * application (which is a `Function`) as its + * callback. If you wish to create both an HTTP + * and HTTPS server you may do so with the "http" + * and "https" modules as shown here: + * + * var http = require('http') + * , https = require('https') + * , express = require('express') + * , app = express(); + * + * http.createServer(app).listen(80); + * https.createServer({ ... }, app).listen(443); + * + * @return {http.Server} + * @public + */ + +app.listen = function listen() { + var server = http.createServer(this); + return server.listen.apply(server, arguments); +}; + +/** + * Log error using console.error. + * + * @param {Error} err + * @private + */ + +function logerror(err) { + /* istanbul ignore next */ + if (this.get('env') !== 'test') console.error(err.stack || err.toString()); +} + +/** + * Try rendering a view. + * @private + */ + +function tryRender(view, options, callback) { + try { + view.render(options, callback); + } catch (err) { + callback(err); + } +} diff --git a/node_modules/express/lib/express.js b/node_modules/express/lib/express.js new file mode 100644 index 0000000..540c8be --- /dev/null +++ b/node_modules/express/lib/express.js @@ -0,0 +1,103 @@ +/*! + * express + * Copyright(c) 2009-2013 TJ Holowaychuk + * Copyright(c) 2013 Roman Shtylman + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter; +var mixin = require('merge-descriptors'); +var proto = require('./application'); +var Route = require('./router/route'); +var Router = require('./router'); +var req = require('./request'); +var res = require('./response'); + +/** + * Expose `createApplication()`. + */ + +exports = module.exports = createApplication; + +/** + * Create an express application. + * + * @return {Function} + * @api public + */ + +function createApplication() { + var app = function(req, res, next) { + app.handle(req, res, next); + }; + + mixin(app, EventEmitter.prototype, false); + mixin(app, proto, false); + + app.request = { __proto__: req, app: app }; + app.response = { __proto__: res, app: app }; + app.init(); + return app; +} + +/** + * Expose the prototypes. + */ + +exports.application = proto; +exports.request = req; +exports.response = res; + +/** + * Expose constructors. + */ + +exports.Route = Route; +exports.Router = Router; + +/** + * Expose middleware + */ + +exports.query = require('./middleware/query'); +exports.static = require('serve-static'); + +/** + * Replace removed middleware with an appropriate error message. + */ + +[ + 'json', + 'urlencoded', + 'bodyParser', + 'compress', + 'cookieSession', + 'session', + 'logger', + 'cookieParser', + 'favicon', + 'responseTime', + 'errorHandler', + 'timeout', + 'methodOverride', + 'vhost', + 'csrf', + 'directory', + 'limit', + 'multipart', + 'staticCache', +].forEach(function (name) { + Object.defineProperty(exports, name, { + get: function () { + throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.'); + }, + configurable: true + }); +}); diff --git a/node_modules/express/lib/middleware/init.js b/node_modules/express/lib/middleware/init.js new file mode 100644 index 0000000..f3119ed --- /dev/null +++ b/node_modules/express/lib/middleware/init.js @@ -0,0 +1,36 @@ +/*! + * express + * Copyright(c) 2009-2013 TJ Holowaychuk + * Copyright(c) 2013 Roman Shtylman + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Initialization middleware, exposing the + * request and response to each other, as well + * as defaulting the X-Powered-By header field. + * + * @param {Function} app + * @return {Function} + * @api private + */ + +exports.init = function(app){ + return function expressInit(req, res, next){ + if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express'); + req.res = res; + res.req = req; + req.next = next; + + req.__proto__ = app.request; + res.__proto__ = app.response; + + res.locals = res.locals || Object.create(null); + + next(); + }; +}; + diff --git a/node_modules/express/lib/middleware/query.js b/node_modules/express/lib/middleware/query.js new file mode 100644 index 0000000..a665f3f --- /dev/null +++ b/node_modules/express/lib/middleware/query.js @@ -0,0 +1,51 @@ +/*! + * express + * Copyright(c) 2009-2013 TJ Holowaychuk + * Copyright(c) 2013 Roman Shtylman + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Module dependencies. + */ + +var parseUrl = require('parseurl'); +var qs = require('qs'); + +/** + * @param {Object} options + * @return {Function} + * @api public + */ + +module.exports = function query(options) { + var opts = Object.create(options || null); + var queryparse = qs.parse; + + if (typeof options === 'function') { + queryparse = options; + opts = undefined; + } + + if (opts !== undefined) { + if (opts.allowDots === undefined) { + opts.allowDots = false; + } + + if (opts.allowPrototypes === undefined) { + opts.allowPrototypes = true; + } + } + + return function query(req, res, next){ + if (!req.query) { + var val = parseUrl(req).query; + req.query = queryparse(val, opts); + } + + next(); + }; +}; diff --git a/node_modules/express/lib/request.js b/node_modules/express/lib/request.js new file mode 100644 index 0000000..33cac18 --- /dev/null +++ b/node_modules/express/lib/request.js @@ -0,0 +1,489 @@ +/*! + * express + * Copyright(c) 2009-2013 TJ Holowaychuk + * Copyright(c) 2013 Roman Shtylman + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Module dependencies. + * @private + */ + +var accepts = require('accepts'); +var deprecate = require('depd')('express'); +var isIP = require('net').isIP; +var typeis = require('type-is'); +var http = require('http'); +var fresh = require('fresh'); +var parseRange = require('range-parser'); +var parse = require('parseurl'); +var proxyaddr = require('proxy-addr'); + +/** + * Request prototype. + */ + +var req = exports = module.exports = { + __proto__: http.IncomingMessage.prototype +}; + +/** + * Return request header. + * + * The `Referrer` header field is special-cased, + * both `Referrer` and `Referer` are interchangeable. + * + * Examples: + * + * req.get('Content-Type'); + * // => "text/plain" + * + * req.get('content-type'); + * // => "text/plain" + * + * req.get('Something'); + * // => undefined + * + * Aliased as `req.header()`. + * + * @param {String} name + * @return {String} + * @public + */ + +req.get = +req.header = function header(name) { + var lc = name.toLowerCase(); + + switch (lc) { + case 'referer': + case 'referrer': + return this.headers.referrer + || this.headers.referer; + default: + return this.headers[lc]; + } +}; + +/** + * To do: update docs. + * + * Check if the given `type(s)` is acceptable, returning + * the best match when true, otherwise `undefined`, in which + * case you should respond with 406 "Not Acceptable". + * + * The `type` value may be a single MIME type string + * such as "application/json", an extension name + * such as "json", a comma-delimited list such as "json, html, text/plain", + * an argument list such as `"json", "html", "text/plain"`, + * or an array `["json", "html", "text/plain"]`. When a list + * or array is given, the _best_ match, if any is returned. + * + * Examples: + * + * // Accept: text/html + * req.accepts('html'); + * // => "html" + * + * // Accept: text/*, application/json + * req.accepts('html'); + * // => "html" + * req.accepts('text/html'); + * // => "text/html" + * req.accepts('json, text'); + * // => "json" + * req.accepts('application/json'); + * // => "application/json" + * + * // Accept: text/*, application/json + * req.accepts('image/png'); + * req.accepts('png'); + * // => undefined + * + * // Accept: text/*;q=.5, application/json + * req.accepts(['html', 'json']); + * req.accepts('html', 'json'); + * req.accepts('html, json'); + * // => "json" + * + * @param {String|Array} type(s) + * @return {String|Array|Boolean} + * @public + */ + +req.accepts = function(){ + var accept = accepts(this); + return accept.types.apply(accept, arguments); +}; + +/** + * Check if the given `encoding`s are accepted. + * + * @param {String} ...encoding + * @return {String|Array} + * @public + */ + +req.acceptsEncodings = function(){ + var accept = accepts(this); + return accept.encodings.apply(accept, arguments); +}; + +req.acceptsEncoding = deprecate.function(req.acceptsEncodings, + 'req.acceptsEncoding: Use acceptsEncodings instead'); + +/** + * Check if the given `charset`s are acceptable, + * otherwise you should respond with 406 "Not Acceptable". + * + * @param {String} ...charset + * @return {String|Array} + * @public + */ + +req.acceptsCharsets = function(){ + var accept = accepts(this); + return accept.charsets.apply(accept, arguments); +}; + +req.acceptsCharset = deprecate.function(req.acceptsCharsets, + 'req.acceptsCharset: Use acceptsCharsets instead'); + +/** + * Check if the given `lang`s are acceptable, + * otherwise you should respond with 406 "Not Acceptable". + * + * @param {String} ...lang + * @return {String|Array} + * @public + */ + +req.acceptsLanguages = function(){ + var accept = accepts(this); + return accept.languages.apply(accept, arguments); +}; + +req.acceptsLanguage = deprecate.function(req.acceptsLanguages, + 'req.acceptsLanguage: Use acceptsLanguages instead'); + +/** + * Parse Range header field, + * capping to the given `size`. + * + * Unspecified ranges such as "0-" require + * knowledge of your resource length. In + * the case of a byte range this is of course + * the total number of bytes. If the Range + * header field is not given `null` is returned, + * `-1` when unsatisfiable, `-2` when syntactically invalid. + * + * NOTE: remember that ranges are inclusive, so + * for example "Range: users=0-3" should respond + * with 4 users when available, not 3. + * + * @param {Number} size + * @return {Array} + * @public + */ + +req.range = function(size){ + var range = this.get('Range'); + if (!range) return; + return parseRange(size, range); +}; + +/** + * Return the value of param `name` when present or `defaultValue`. + * + * - Checks route placeholders, ex: _/user/:id_ + * - Checks body params, ex: id=12, {"id":12} + * - Checks query string params, ex: ?id=12 + * + * To utilize request bodies, `req.body` + * should be an object. This can be done by using + * the `bodyParser()` middleware. + * + * @param {String} name + * @param {Mixed} [defaultValue] + * @return {String} + * @public + */ + +req.param = function param(name, defaultValue) { + var params = this.params || {}; + var body = this.body || {}; + var query = this.query || {}; + + var args = arguments.length === 1 + ? 'name' + : 'name, default'; + deprecate('req.param(' + args + '): Use req.params, req.body, or req.query instead'); + + if (null != params[name] && params.hasOwnProperty(name)) return params[name]; + if (null != body[name]) return body[name]; + if (null != query[name]) return query[name]; + + return defaultValue; +}; + +/** + * Check if the incoming request contains the "Content-Type" + * header field, and it contains the give mime `type`. + * + * Examples: + * + * // With Content-Type: text/html; charset=utf-8 + * req.is('html'); + * req.is('text/html'); + * req.is('text/*'); + * // => true + * + * // When Content-Type is application/json + * req.is('json'); + * req.is('application/json'); + * req.is('application/*'); + * // => true + * + * req.is('html'); + * // => false + * + * @param {String|Array} types... + * @return {String|false|null} + * @public + */ + +req.is = function is(types) { + var arr = types; + + // support flattened arguments + if (!Array.isArray(types)) { + arr = new Array(arguments.length); + for (var i = 0; i < arr.length; i++) { + arr[i] = arguments[i]; + } + } + + return typeis(this, arr); +}; + +/** + * Return the protocol string "http" or "https" + * when requested with TLS. When the "trust proxy" + * setting trusts the socket address, the + * "X-Forwarded-Proto" header field will be trusted + * and used if present. + * + * If you're running behind a reverse proxy that + * supplies https for you this may be enabled. + * + * @return {String} + * @public + */ + +defineGetter(req, 'protocol', function protocol(){ + var proto = this.connection.encrypted + ? 'https' + : 'http'; + var trust = this.app.get('trust proxy fn'); + + if (!trust(this.connection.remoteAddress, 0)) { + return proto; + } + + // Note: X-Forwarded-Proto is normally only ever a + // single value, but this is to be safe. + proto = this.get('X-Forwarded-Proto') || proto; + return proto.split(/\s*,\s*/)[0]; +}); + +/** + * Short-hand for: + * + * req.protocol == 'https' + * + * @return {Boolean} + * @public + */ + +defineGetter(req, 'secure', function secure(){ + return this.protocol === 'https'; +}); + +/** + * Return the remote address from the trusted proxy. + * + * The is the remote address on the socket unless + * "trust proxy" is set. + * + * @return {String} + * @public + */ + +defineGetter(req, 'ip', function ip(){ + var trust = this.app.get('trust proxy fn'); + return proxyaddr(this, trust); +}); + +/** + * When "trust proxy" is set, trusted proxy addresses + client. + * + * For example if the value were "client, proxy1, proxy2" + * you would receive the array `["client", "proxy1", "proxy2"]` + * where "proxy2" is the furthest down-stream and "proxy1" and + * "proxy2" were trusted. + * + * @return {Array} + * @public + */ + +defineGetter(req, 'ips', function ips() { + var trust = this.app.get('trust proxy fn'); + var addrs = proxyaddr.all(this, trust); + return addrs.slice(1).reverse(); +}); + +/** + * Return subdomains as an array. + * + * Subdomains are the dot-separated parts of the host before the main domain of + * the app. By default, the domain of the app is assumed to be the last two + * parts of the host. This can be changed by setting "subdomain offset". + * + * For example, if the domain is "tobi.ferrets.example.com": + * If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`. + * If "subdomain offset" is 3, req.subdomains is `["tobi"]`. + * + * @return {Array} + * @public + */ + +defineGetter(req, 'subdomains', function subdomains() { + var hostname = this.hostname; + + if (!hostname) return []; + + var offset = this.app.get('subdomain offset'); + var subdomains = !isIP(hostname) + ? hostname.split('.').reverse() + : [hostname]; + + return subdomains.slice(offset); +}); + +/** + * Short-hand for `url.parse(req.url).pathname`. + * + * @return {String} + * @public + */ + +defineGetter(req, 'path', function path() { + return parse(this).pathname; +}); + +/** + * Parse the "Host" header field to a hostname. + * + * When the "trust proxy" setting trusts the socket + * address, the "X-Forwarded-Host" header field will + * be trusted. + * + * @return {String} + * @public + */ + +defineGetter(req, 'hostname', function hostname(){ + var trust = this.app.get('trust proxy fn'); + var host = this.get('X-Forwarded-Host'); + + if (!host || !trust(this.connection.remoteAddress, 0)) { + host = this.get('Host'); + } + + if (!host) return; + + // IPv6 literal support + var offset = host[0] === '[' + ? host.indexOf(']') + 1 + : 0; + var index = host.indexOf(':', offset); + + return index !== -1 + ? host.substring(0, index) + : host; +}); + +// TODO: change req.host to return host in next major + +defineGetter(req, 'host', deprecate.function(function host(){ + return this.hostname; +}, 'req.host: Use req.hostname instead')); + +/** + * Check if the request is fresh, aka + * Last-Modified and/or the ETag + * still match. + * + * @return {Boolean} + * @public + */ + +defineGetter(req, 'fresh', function(){ + var method = this.method; + var s = this.res.statusCode; + + // GET or HEAD for weak freshness validation only + if ('GET' != method && 'HEAD' != method) return false; + + // 2xx or 304 as per rfc2616 14.26 + if ((s >= 200 && s < 300) || 304 == s) { + return fresh(this.headers, (this.res._headers || {})); + } + + return false; +}); + +/** + * Check if the request is stale, aka + * "Last-Modified" and / or the "ETag" for the + * resource has changed. + * + * @return {Boolean} + * @public + */ + +defineGetter(req, 'stale', function stale(){ + return !this.fresh; +}); + +/** + * Check if the request was an _XMLHttpRequest_. + * + * @return {Boolean} + * @public + */ + +defineGetter(req, 'xhr', function xhr(){ + var val = this.get('X-Requested-With') || ''; + return val.toLowerCase() === 'xmlhttprequest'; +}); + +/** + * Helper function for creating a getter on an object. + * + * @param {Object} obj + * @param {String} name + * @param {Function} getter + * @private + */ +function defineGetter(obj, name, getter) { + Object.defineProperty(obj, name, { + configurable: true, + enumerable: true, + get: getter + }); +}; diff --git a/node_modules/express/lib/response.js b/node_modules/express/lib/response.js new file mode 100644 index 0000000..641704b --- /dev/null +++ b/node_modules/express/lib/response.js @@ -0,0 +1,1053 @@ +/*! + * express + * Copyright(c) 2009-2013 TJ Holowaychuk + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Module dependencies. + * @private + */ + +var contentDisposition = require('content-disposition'); +var deprecate = require('depd')('express'); +var escapeHtml = require('escape-html'); +var http = require('http'); +var isAbsolute = require('./utils').isAbsolute; +var onFinished = require('on-finished'); +var path = require('path'); +var merge = require('utils-merge'); +var sign = require('cookie-signature').sign; +var normalizeType = require('./utils').normalizeType; +var normalizeTypes = require('./utils').normalizeTypes; +var setCharset = require('./utils').setCharset; +var statusCodes = http.STATUS_CODES; +var cookie = require('cookie'); +var send = require('send'); +var extname = path.extname; +var mime = send.mime; +var resolve = path.resolve; +var vary = require('vary'); + +/** + * Response prototype. + */ + +var res = module.exports = { + __proto__: http.ServerResponse.prototype +}; + +/** + * Module variables. + * @private + */ + +var charsetRegExp = /;\s*charset\s*=/; + +/** + * Set status `code`. + * + * @param {Number} code + * @return {ServerResponse} + * @public + */ + +res.status = function status(code) { + this.statusCode = code; + return this; +}; + +/** + * Set Link header field with the given `links`. + * + * Examples: + * + * res.links({ + * next: 'http://api.example.com/users?page=2', + * last: 'http://api.example.com/users?page=5' + * }); + * + * @param {Object} links + * @return {ServerResponse} + * @public + */ + +res.links = function(links){ + var link = this.get('Link') || ''; + if (link) link += ', '; + return this.set('Link', link + Object.keys(links).map(function(rel){ + return '<' + links[rel] + '>; rel="' + rel + '"'; + }).join(', ')); +}; + +/** + * Send a response. + * + * Examples: + * + * res.send(new Buffer('wahoo')); + * res.send({ some: 'json' }); + * res.send('

    some html

    '); + * + * @param {string|number|boolean|object|Buffer} body + * @public + */ + +res.send = function send(body) { + var chunk = body; + var encoding; + var len; + var req = this.req; + var type; + + // settings + var app = this.app; + + // allow status / body + if (arguments.length === 2) { + // res.send(body, status) backwards compat + if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') { + deprecate('res.send(body, status): Use res.status(status).send(body) instead'); + this.statusCode = arguments[1]; + } else { + deprecate('res.send(status, body): Use res.status(status).send(body) instead'); + this.statusCode = arguments[0]; + chunk = arguments[1]; + } + } + + // disambiguate res.send(status) and res.send(status, num) + if (typeof chunk === 'number' && arguments.length === 1) { + // res.send(status) will set status message as text string + if (!this.get('Content-Type')) { + this.type('txt'); + } + + deprecate('res.send(status): Use res.sendStatus(status) instead'); + this.statusCode = chunk; + chunk = statusCodes[chunk]; + } + + switch (typeof chunk) { + // string defaulting to html + case 'string': + if (!this.get('Content-Type')) { + this.type('html'); + } + break; + case 'boolean': + case 'number': + case 'object': + if (chunk === null) { + chunk = ''; + } else if (Buffer.isBuffer(chunk)) { + if (!this.get('Content-Type')) { + this.type('bin'); + } + } else { + return this.json(chunk); + } + break; + } + + // write strings in utf-8 + if (typeof chunk === 'string') { + encoding = 'utf8'; + type = this.get('Content-Type'); + + // reflect this in content-type + if (typeof type === 'string') { + this.set('Content-Type', setCharset(type, 'utf-8')); + } + } + + // populate Content-Length + if (chunk !== undefined) { + if (!Buffer.isBuffer(chunk)) { + // convert chunk to Buffer; saves later double conversions + chunk = new Buffer(chunk, encoding); + encoding = undefined; + } + + len = chunk.length; + this.set('Content-Length', len); + } + + // populate ETag + var etag; + var generateETag = len !== undefined && app.get('etag fn'); + if (typeof generateETag === 'function' && !this.get('ETag')) { + if ((etag = generateETag(chunk, encoding))) { + this.set('ETag', etag); + } + } + + // freshness + if (req.fresh) this.statusCode = 304; + + // strip irrelevant headers + if (204 == this.statusCode || 304 == this.statusCode) { + this.removeHeader('Content-Type'); + this.removeHeader('Content-Length'); + this.removeHeader('Transfer-Encoding'); + chunk = ''; + } + + if (req.method === 'HEAD') { + // skip body for HEAD + this.end(); + } else { + // respond + this.end(chunk, encoding); + } + + return this; +}; + +/** + * Send JSON response. + * + * Examples: + * + * res.json(null); + * res.json({ user: 'tj' }); + * + * @param {string|number|boolean|object} obj + * @public + */ + +res.json = function json(obj) { + var val = obj; + + // allow status / body + if (arguments.length === 2) { + // res.json(body, status) backwards compat + if (typeof arguments[1] === 'number') { + deprecate('res.json(obj, status): Use res.status(status).json(obj) instead'); + this.statusCode = arguments[1]; + } else { + deprecate('res.json(status, obj): Use res.status(status).json(obj) instead'); + this.statusCode = arguments[0]; + val = arguments[1]; + } + } + + // settings + var app = this.app; + var replacer = app.get('json replacer'); + var spaces = app.get('json spaces'); + var body = JSON.stringify(val, replacer, spaces); + + // content-type + if (!this.get('Content-Type')) { + this.set('Content-Type', 'application/json'); + } + + return this.send(body); +}; + +/** + * Send JSON response with JSONP callback support. + * + * Examples: + * + * res.jsonp(null); + * res.jsonp({ user: 'tj' }); + * + * @param {string|number|boolean|object} obj + * @public + */ + +res.jsonp = function jsonp(obj) { + var val = obj; + + // allow status / body + if (arguments.length === 2) { + // res.json(body, status) backwards compat + if (typeof arguments[1] === 'number') { + deprecate('res.jsonp(obj, status): Use res.status(status).json(obj) instead'); + this.statusCode = arguments[1]; + } else { + deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead'); + this.statusCode = arguments[0]; + val = arguments[1]; + } + } + + // settings + var app = this.app; + var replacer = app.get('json replacer'); + var spaces = app.get('json spaces'); + var body = JSON.stringify(val, replacer, spaces); + var callback = this.req.query[app.get('jsonp callback name')]; + + // content-type + if (!this.get('Content-Type')) { + this.set('X-Content-Type-Options', 'nosniff'); + this.set('Content-Type', 'application/json'); + } + + // fixup callback + if (Array.isArray(callback)) { + callback = callback[0]; + } + + // jsonp + if (typeof callback === 'string' && callback.length !== 0) { + this.charset = 'utf-8'; + this.set('X-Content-Type-Options', 'nosniff'); + this.set('Content-Type', 'text/javascript'); + + // restrict callback charset + callback = callback.replace(/[^\[\]\w$.]/g, ''); + + // replace chars not allowed in JavaScript that are in JSON + body = body + .replace(/\u2028/g, '\\u2028') + .replace(/\u2029/g, '\\u2029'); + + // the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse" + // the typeof check is just to reduce client error noise + body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');'; + } + + return this.send(body); +}; + +/** + * Send given HTTP status code. + * + * Sets the response status to `statusCode` and the body of the + * response to the standard description from node's http.STATUS_CODES + * or the statusCode number if no description. + * + * Examples: + * + * res.sendStatus(200); + * + * @param {number} statusCode + * @public + */ + +res.sendStatus = function sendStatus(statusCode) { + var body = statusCodes[statusCode] || String(statusCode); + + this.statusCode = statusCode; + this.type('txt'); + + return this.send(body); +}; + +/** + * Transfer the file at the given `path`. + * + * Automatically sets the _Content-Type_ response header field. + * The callback `callback(err)` is invoked when the transfer is complete + * or when an error occurs. Be sure to check `res.sentHeader` + * if you wish to attempt responding, as the header and some data + * may have already been transferred. + * + * Options: + * + * - `maxAge` defaulting to 0 (can be string converted by `ms`) + * - `root` root directory for relative filenames + * - `headers` object of headers to serve with file + * - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them + * + * Other options are passed along to `send`. + * + * Examples: + * + * The following example illustrates how `res.sendFile()` may + * be used as an alternative for the `static()` middleware for + * dynamic situations. The code backing `res.sendFile()` is actually + * the same code, so HTTP cache support etc is identical. + * + * app.get('/user/:uid/photos/:file', function(req, res){ + * var uid = req.params.uid + * , file = req.params.file; + * + * req.user.mayViewFilesFrom(uid, function(yes){ + * if (yes) { + * res.sendFile('/uploads/' + uid + '/' + file); + * } else { + * res.send(403, 'Sorry! you cant see that.'); + * } + * }); + * }); + * + * @public + */ + +res.sendFile = function sendFile(path, options, callback) { + var done = callback; + var req = this.req; + var res = this; + var next = req.next; + var opts = options || {}; + + if (!path) { + throw new TypeError('path argument is required to res.sendFile'); + } + + // support function as second arg + if (typeof options === 'function') { + done = options; + opts = {}; + } + + if (!opts.root && !isAbsolute(path)) { + throw new TypeError('path must be absolute or specify root to res.sendFile'); + } + + // create file stream + var pathname = encodeURI(path); + var file = send(req, pathname, opts); + + // transfer + sendfile(res, file, opts, function (err) { + if (done) return done(err); + if (err && err.code === 'EISDIR') return next(); + + // next() all but write errors + if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') { + next(err); + } + }); +}; + +/** + * Transfer the file at the given `path`. + * + * Automatically sets the _Content-Type_ response header field. + * The callback `callback(err)` is invoked when the transfer is complete + * or when an error occurs. Be sure to check `res.sentHeader` + * if you wish to attempt responding, as the header and some data + * may have already been transferred. + * + * Options: + * + * - `maxAge` defaulting to 0 (can be string converted by `ms`) + * - `root` root directory for relative filenames + * - `headers` object of headers to serve with file + * - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them + * + * Other options are passed along to `send`. + * + * Examples: + * + * The following example illustrates how `res.sendfile()` may + * be used as an alternative for the `static()` middleware for + * dynamic situations. The code backing `res.sendfile()` is actually + * the same code, so HTTP cache support etc is identical. + * + * app.get('/user/:uid/photos/:file', function(req, res){ + * var uid = req.params.uid + * , file = req.params.file; + * + * req.user.mayViewFilesFrom(uid, function(yes){ + * if (yes) { + * res.sendfile('/uploads/' + uid + '/' + file); + * } else { + * res.send(403, 'Sorry! you cant see that.'); + * } + * }); + * }); + * + * @public + */ + +res.sendfile = function (path, options, callback) { + var done = callback; + var req = this.req; + var res = this; + var next = req.next; + var opts = options || {}; + + // support function as second arg + if (typeof options === 'function') { + done = options; + opts = {}; + } + + // create file stream + var file = send(req, path, opts); + + // transfer + sendfile(res, file, opts, function (err) { + if (done) return done(err); + if (err && err.code === 'EISDIR') return next(); + + // next() all but write errors + if (err && err.code !== 'ECONNABORT' && err.syscall !== 'write') { + next(err); + } + }); +}; + +res.sendfile = deprecate.function(res.sendfile, + 'res.sendfile: Use res.sendFile instead'); + +/** + * Transfer the file at the given `path` as an attachment. + * + * Optionally providing an alternate attachment `filename`, + * and optional callback `callback(err)`. The callback is invoked + * when the data transfer is complete, or when an error has + * ocurred. Be sure to check `res.headersSent` if you plan to respond. + * + * This method uses `res.sendfile()`. + * + * @public + */ + +res.download = function download(path, filename, callback) { + var done = callback; + var name = filename; + + // support function as second arg + if (typeof filename === 'function') { + done = filename; + name = null; + } + + // set Content-Disposition when file is sent + var headers = { + 'Content-Disposition': contentDisposition(name || path) + }; + + // Resolve the full path for sendFile + var fullPath = resolve(path); + + return this.sendFile(fullPath, { headers: headers }, done); +}; + +/** + * Set _Content-Type_ response header with `type` through `mime.lookup()` + * when it does not contain "/", or set the Content-Type to `type` otherwise. + * + * Examples: + * + * res.type('.html'); + * res.type('html'); + * res.type('json'); + * res.type('application/json'); + * res.type('png'); + * + * @param {String} type + * @return {ServerResponse} for chaining + * @public + */ + +res.contentType = +res.type = function contentType(type) { + var ct = type.indexOf('/') === -1 + ? mime.lookup(type) + : type; + + return this.set('Content-Type', ct); +}; + +/** + * Respond to the Acceptable formats using an `obj` + * of mime-type callbacks. + * + * This method uses `req.accepted`, an array of + * acceptable types ordered by their quality values. + * When "Accept" is not present the _first_ callback + * is invoked, otherwise the first match is used. When + * no match is performed the server responds with + * 406 "Not Acceptable". + * + * Content-Type is set for you, however if you choose + * you may alter this within the callback using `res.type()` + * or `res.set('Content-Type', ...)`. + * + * res.format({ + * 'text/plain': function(){ + * res.send('hey'); + * }, + * + * 'text/html': function(){ + * res.send('

    hey

    '); + * }, + * + * 'appliation/json': function(){ + * res.send({ message: 'hey' }); + * } + * }); + * + * In addition to canonicalized MIME types you may + * also use extnames mapped to these types: + * + * res.format({ + * text: function(){ + * res.send('hey'); + * }, + * + * html: function(){ + * res.send('

    hey

    '); + * }, + * + * json: function(){ + * res.send({ message: 'hey' }); + * } + * }); + * + * By default Express passes an `Error` + * with a `.status` of 406 to `next(err)` + * if a match is not made. If you provide + * a `.default` callback it will be invoked + * instead. + * + * @param {Object} obj + * @return {ServerResponse} for chaining + * @public + */ + +res.format = function(obj){ + var req = this.req; + var next = req.next; + + var fn = obj.default; + if (fn) delete obj.default; + var keys = Object.keys(obj); + + var key = keys.length > 0 + ? req.accepts(keys) + : false; + + this.vary("Accept"); + + if (key) { + this.set('Content-Type', normalizeType(key).value); + obj[key](req, this, next); + } else if (fn) { + fn(); + } else { + var err = new Error('Not Acceptable'); + err.status = err.statusCode = 406; + err.types = normalizeTypes(keys).map(function(o){ return o.value }); + next(err); + } + + return this; +}; + +/** + * Set _Content-Disposition_ header to _attachment_ with optional `filename`. + * + * @param {String} filename + * @return {ServerResponse} + * @public + */ + +res.attachment = function attachment(filename) { + if (filename) { + this.type(extname(filename)); + } + + this.set('Content-Disposition', contentDisposition(filename)); + + return this; +}; + +/** + * Append additional header `field` with value `val`. + * + * Example: + * + * res.append('Link', ['', '']); + * res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly'); + * res.append('Warning', '199 Miscellaneous warning'); + * + * @param {String} field + * @param {String|Array} val + * @return {ServerResponse} for chaining + * @public + */ + +res.append = function append(field, val) { + var prev = this.get(field); + var value = val; + + if (prev) { + // concat the new and prev vals + value = Array.isArray(prev) ? prev.concat(val) + : Array.isArray(val) ? [prev].concat(val) + : [prev, val]; + } + + return this.set(field, value); +}; + +/** + * Set header `field` to `val`, or pass + * an object of header fields. + * + * Examples: + * + * res.set('Foo', ['bar', 'baz']); + * res.set('Accept', 'application/json'); + * res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' }); + * + * Aliased as `res.header()`. + * + * @param {String|Object} field + * @param {String|Array} val + * @return {ServerResponse} for chaining + * @public + */ + +res.set = +res.header = function header(field, val) { + if (arguments.length === 2) { + var value = Array.isArray(val) + ? val.map(String) + : String(val); + + // add charset to content-type + if (field.toLowerCase() === 'content-type' && !charsetRegExp.test(value)) { + var charset = mime.charsets.lookup(value.split(';')[0]); + if (charset) value += '; charset=' + charset.toLowerCase(); + } + + this.setHeader(field, value); + } else { + for (var key in field) { + this.set(key, field[key]); + } + } + return this; +}; + +/** + * Get value for header `field`. + * + * @param {String} field + * @return {String} + * @public + */ + +res.get = function(field){ + return this.getHeader(field); +}; + +/** + * Clear cookie `name`. + * + * @param {String} name + * @param {Object} options + * @return {ServerResponse} for chaining + * @public + */ + +res.clearCookie = function clearCookie(name, options) { + var opts = merge({ expires: new Date(1), path: '/' }, options); + + return this.cookie(name, '', opts); +}; + +/** + * Set cookie `name` to `value`, with the given `options`. + * + * Options: + * + * - `maxAge` max-age in milliseconds, converted to `expires` + * - `signed` sign the cookie + * - `path` defaults to "/" + * + * Examples: + * + * // "Remember Me" for 15 minutes + * res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true }); + * + * // save as above + * res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }) + * + * @param {String} name + * @param {String|Object} value + * @param {Options} options + * @return {ServerResponse} for chaining + * @public + */ + +res.cookie = function (name, value, options) { + var opts = merge({}, options); + var secret = this.req.secret; + var signed = opts.signed; + + if (signed && !secret) { + throw new Error('cookieParser("secret") required for signed cookies'); + } + + var val = typeof value === 'object' + ? 'j:' + JSON.stringify(value) + : String(value); + + if (signed) { + val = 's:' + sign(val, secret); + } + + if ('maxAge' in opts) { + opts.expires = new Date(Date.now() + opts.maxAge); + opts.maxAge /= 1000; + } + + if (opts.path == null) { + opts.path = '/'; + } + + this.append('Set-Cookie', cookie.serialize(name, String(val), opts)); + + return this; +}; + +/** + * Set the location header to `url`. + * + * The given `url` can also be "back", which redirects + * to the _Referrer_ or _Referer_ headers or "/". + * + * Examples: + * + * res.location('/foo/bar').; + * res.location('http://example.com'); + * res.location('../login'); + * + * @param {String} url + * @return {ServerResponse} for chaining + * @public + */ + +res.location = function location(url) { + var loc = url; + + // "back" is an alias for the referrer + if (url === 'back') { + loc = this.req.get('Referrer') || '/'; + } + + // set location + this.set('Location', loc); + return this; +}; + +/** + * Redirect to the given `url` with optional response `status` + * defaulting to 302. + * + * The resulting `url` is determined by `res.location()`, so + * it will play nicely with mounted apps, relative paths, + * `"back"` etc. + * + * Examples: + * + * res.redirect('/foo/bar'); + * res.redirect('http://example.com'); + * res.redirect(301, 'http://example.com'); + * res.redirect('../login'); // /blog/post/1 -> /blog/login + * + * @public + */ + +res.redirect = function redirect(url) { + var address = url; + var body; + var status = 302; + + // allow status / url + if (arguments.length === 2) { + if (typeof arguments[0] === 'number') { + status = arguments[0]; + address = arguments[1]; + } else { + deprecate('res.redirect(url, status): Use res.redirect(status, url) instead'); + status = arguments[1]; + } + } + + // Set location header + this.location(address); + address = this.get('Location'); + + // Support text/{plain,html} by default + this.format({ + text: function(){ + body = statusCodes[status] + '. Redirecting to ' + encodeURI(address); + }, + + html: function(){ + var u = escapeHtml(address); + body = '

    ' + statusCodes[status] + '. Redirecting to ' + u + '

    '; + }, + + default: function(){ + body = ''; + } + }); + + // Respond + this.statusCode = status; + this.set('Content-Length', Buffer.byteLength(body)); + + if (this.req.method === 'HEAD') { + this.end(); + } else { + this.end(body); + } +}; + +/** + * Add `field` to Vary. If already present in the Vary set, then + * this call is simply ignored. + * + * @param {Array|String} field + * @return {ServerResponse} for chaining + * @public + */ + +res.vary = function(field){ + // checks for back-compat + if (!field || (Array.isArray(field) && !field.length)) { + deprecate('res.vary(): Provide a field name'); + return this; + } + + vary(this, field); + + return this; +}; + +/** + * Render `view` with the given `options` and optional callback `fn`. + * When a callback function is given a response will _not_ be made + * automatically, otherwise a response of _200_ and _text/html_ is given. + * + * Options: + * + * - `cache` boolean hinting to the engine it should cache + * - `filename` filename of the view being rendered + * + * @public + */ + +res.render = function render(view, options, callback) { + var app = this.req.app; + var done = callback; + var opts = options || {}; + var req = this.req; + var self = this; + + // support callback function as second arg + if (typeof options === 'function') { + done = options; + opts = {}; + } + + // merge res.locals + opts._locals = self.locals; + + // default callback to respond + done = done || function (err, str) { + if (err) return req.next(err); + self.send(str); + }; + + // render + app.render(view, opts, done); +}; + +// pipe the send file stream +function sendfile(res, file, options, callback) { + var done = false; + var streaming; + + // request aborted + function onaborted() { + if (done) return; + done = true; + + var err = new Error('Request aborted'); + err.code = 'ECONNABORTED'; + callback(err); + } + + // directory + function ondirectory() { + if (done) return; + done = true; + + var err = new Error('EISDIR, read'); + err.code = 'EISDIR'; + callback(err); + } + + // errors + function onerror(err) { + if (done) return; + done = true; + callback(err); + } + + // ended + function onend() { + if (done) return; + done = true; + callback(); + } + + // file + function onfile() { + streaming = false; + } + + // finished + function onfinish(err) { + if (err && err.code === 'ECONNRESET') return onaborted(); + if (err) return onerror(err); + if (done) return; + + setImmediate(function () { + if (streaming !== false && !done) { + onaborted(); + return; + } + + if (done) return; + done = true; + callback(); + }); + } + + // streaming + function onstream() { + streaming = true; + } + + file.on('directory', ondirectory); + file.on('end', onend); + file.on('error', onerror); + file.on('file', onfile); + file.on('stream', onstream); + onFinished(res, onfinish); + + if (options.headers) { + // set headers on successful transfer + file.on('headers', function headers(res) { + var obj = options.headers; + var keys = Object.keys(obj); + + for (var i = 0; i < keys.length; i++) { + var k = keys[i]; + res.setHeader(k, obj[k]); + } + }); + } + + // pipe + file.pipe(res); +} diff --git a/node_modules/express/lib/router/index.js b/node_modules/express/lib/router/index.js new file mode 100644 index 0000000..504ed9c --- /dev/null +++ b/node_modules/express/lib/router/index.js @@ -0,0 +1,645 @@ +/*! + * express + * Copyright(c) 2009-2013 TJ Holowaychuk + * Copyright(c) 2013 Roman Shtylman + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Module dependencies. + * @private + */ + +var Route = require('./route'); +var Layer = require('./layer'); +var methods = require('methods'); +var mixin = require('utils-merge'); +var debug = require('debug')('express:router'); +var deprecate = require('depd')('express'); +var flatten = require('array-flatten'); +var parseUrl = require('parseurl'); + +/** + * Module variables. + * @private + */ + +var objectRegExp = /^\[object (\S+)\]$/; +var slice = Array.prototype.slice; +var toString = Object.prototype.toString; + +/** + * Initialize a new `Router` with the given `options`. + * + * @param {Object} options + * @return {Router} which is an callable function + * @public + */ + +var proto = module.exports = function(options) { + var opts = options || {}; + + function router(req, res, next) { + router.handle(req, res, next); + } + + // mixin Router class functions + router.__proto__ = proto; + + router.params = {}; + router._params = []; + router.caseSensitive = opts.caseSensitive; + router.mergeParams = opts.mergeParams; + router.strict = opts.strict; + router.stack = []; + + return router; +}; + +/** + * Map the given param placeholder `name`(s) to the given callback. + * + * Parameter mapping is used to provide pre-conditions to routes + * which use normalized placeholders. For example a _:user_id_ parameter + * could automatically load a user's information from the database without + * any additional code, + * + * The callback uses the same signature as middleware, the only difference + * being that the value of the placeholder is passed, in this case the _id_ + * of the user. Once the `next()` function is invoked, just like middleware + * it will continue on to execute the route, or subsequent parameter functions. + * + * Just like in middleware, you must either respond to the request or call next + * to avoid stalling the request. + * + * app.param('user_id', function(req, res, next, id){ + * User.find(id, function(err, user){ + * if (err) { + * return next(err); + * } else if (!user) { + * return next(new Error('failed to load user')); + * } + * req.user = user; + * next(); + * }); + * }); + * + * @param {String} name + * @param {Function} fn + * @return {app} for chaining + * @public + */ + +proto.param = function param(name, fn) { + // param logic + if (typeof name === 'function') { + deprecate('router.param(fn): Refactor to use path params'); + this._params.push(name); + return; + } + + // apply param functions + var params = this._params; + var len = params.length; + var ret; + + if (name[0] === ':') { + deprecate('router.param(' + JSON.stringify(name) + ', fn): Use router.param(' + JSON.stringify(name.substr(1)) + ', fn) instead'); + name = name.substr(1); + } + + for (var i = 0; i < len; ++i) { + if (ret = params[i](name, fn)) { + fn = ret; + } + } + + // ensure we end up with a + // middleware function + if ('function' != typeof fn) { + throw new Error('invalid param() call for ' + name + ', got ' + fn); + } + + (this.params[name] = this.params[name] || []).push(fn); + return this; +}; + +/** + * Dispatch a req, res into the router. + * @private + */ + +proto.handle = function handle(req, res, out) { + var self = this; + + debug('dispatching %s %s', req.method, req.url); + + var search = 1 + req.url.indexOf('?'); + var pathlength = search ? search - 1 : req.url.length; + var fqdn = req.url[0] !== '/' && 1 + req.url.substr(0, pathlength).indexOf('://'); + var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : ''; + var idx = 0; + var removed = ''; + var slashAdded = false; + var paramcalled = {}; + + // store options for OPTIONS request + // only used if OPTIONS request + var options = []; + + // middleware and routes + var stack = self.stack; + + // manage inter-router variables + var parentParams = req.params; + var parentUrl = req.baseUrl || ''; + var done = restore(out, req, 'baseUrl', 'next', 'params'); + + // setup next layer + req.next = next; + + // for options requests, respond with a default if nothing else responds + if (req.method === 'OPTIONS') { + done = wrap(done, function(old, err) { + if (err || options.length === 0) return old(err); + sendOptionsResponse(res, options, old); + }); + } + + // setup basic req values + req.baseUrl = parentUrl; + req.originalUrl = req.originalUrl || req.url; + + next(); + + function next(err) { + var layerError = err === 'route' + ? null + : err; + + // remove added slash + if (slashAdded) { + req.url = req.url.substr(1); + slashAdded = false; + } + + // restore altered req.url + if (removed.length !== 0) { + req.baseUrl = parentUrl; + req.url = protohost + removed + req.url.substr(protohost.length); + removed = ''; + } + + // no more matching layers + if (idx >= stack.length) { + setImmediate(done, layerError); + return; + } + + // get pathname of request + var path = getPathname(req); + + if (path == null) { + return done(layerError); + } + + // find next matching layer + var layer; + var match; + var route; + + while (match !== true && idx < stack.length) { + layer = stack[idx++]; + match = matchLayer(layer, path); + route = layer.route; + + if (typeof match !== 'boolean') { + // hold on to layerError + layerError = layerError || match; + } + + if (match !== true) { + continue; + } + + if (!route) { + // process non-route handlers normally + continue; + } + + if (layerError) { + // routes do not match with a pending error + match = false; + continue; + } + + var method = req.method; + var has_method = route._handles_method(method); + + // build up automatic options response + if (!has_method && method === 'OPTIONS') { + appendMethods(options, route._options()); + } + + // don't even bother matching route + if (!has_method && method !== 'HEAD') { + match = false; + continue; + } + } + + // no match + if (match !== true) { + return done(layerError); + } + + // store route for dispatch on change + if (route) { + req.route = route; + } + + // Capture one-time layer values + req.params = self.mergeParams + ? mergeParams(layer.params, parentParams) + : layer.params; + var layerPath = layer.path; + + // this should be done for the layer + self.process_params(layer, paramcalled, req, res, function (err) { + if (err) { + return next(layerError || err); + } + + if (route) { + return layer.handle_request(req, res, next); + } + + trim_prefix(layer, layerError, layerPath, path); + }); + } + + function trim_prefix(layer, layerError, layerPath, path) { + var c = path[layerPath.length]; + if (c && '/' !== c && '.' !== c) return next(layerError); + + // Trim off the part of the url that matches the route + // middleware (.use stuff) needs to have the path stripped + if (layerPath.length !== 0) { + debug('trim prefix (%s) from url %s', layerPath, req.url); + removed = layerPath; + req.url = protohost + req.url.substr(protohost.length + removed.length); + + // Ensure leading slash + if (!fqdn && req.url[0] !== '/') { + req.url = '/' + req.url; + slashAdded = true; + } + + // Setup base URL (no trailing slash) + req.baseUrl = parentUrl + (removed[removed.length - 1] === '/' + ? removed.substring(0, removed.length - 1) + : removed); + } + + debug('%s %s : %s', layer.name, layerPath, req.originalUrl); + + if (layerError) { + layer.handle_error(layerError, req, res, next); + } else { + layer.handle_request(req, res, next); + } + } +}; + +/** + * Process any parameters for the layer. + * @private + */ + +proto.process_params = function process_params(layer, called, req, res, done) { + var params = this.params; + + // captured parameters from the layer, keys and values + var keys = layer.keys; + + // fast track + if (!keys || keys.length === 0) { + return done(); + } + + var i = 0; + var name; + var paramIndex = 0; + var key; + var paramVal; + var paramCallbacks; + var paramCalled; + + // process params in order + // param callbacks can be async + function param(err) { + if (err) { + return done(err); + } + + if (i >= keys.length ) { + return done(); + } + + paramIndex = 0; + key = keys[i++]; + + if (!key) { + return done(); + } + + name = key.name; + paramVal = req.params[name]; + paramCallbacks = params[name]; + paramCalled = called[name]; + + if (paramVal === undefined || !paramCallbacks) { + return param(); + } + + // param previously called with same value or error occurred + if (paramCalled && (paramCalled.match === paramVal + || (paramCalled.error && paramCalled.error !== 'route'))) { + // restore value + req.params[name] = paramCalled.value; + + // next param + return param(paramCalled.error); + } + + called[name] = paramCalled = { + error: null, + match: paramVal, + value: paramVal + }; + + paramCallback(); + } + + // single param callbacks + function paramCallback(err) { + var fn = paramCallbacks[paramIndex++]; + + // store updated value + paramCalled.value = req.params[key.name]; + + if (err) { + // store error + paramCalled.error = err; + param(err); + return; + } + + if (!fn) return param(); + + try { + fn(req, res, paramCallback, paramVal, key.name); + } catch (e) { + paramCallback(e); + } + } + + param(); +}; + +/** + * Use the given middleware function, with optional path, defaulting to "/". + * + * Use (like `.all`) will run for any http METHOD, but it will not add + * handlers for those methods so OPTIONS requests will not consider `.use` + * functions even if they could respond. + * + * The other difference is that _route_ path is stripped and not visible + * to the handler function. The main effect of this feature is that mounted + * handlers can operate without any code changes regardless of the "prefix" + * pathname. + * + * @public + */ + +proto.use = function use(fn) { + var offset = 0; + var path = '/'; + + // default path to '/' + // disambiguate router.use([fn]) + if (typeof fn !== 'function') { + var arg = fn; + + while (Array.isArray(arg) && arg.length !== 0) { + arg = arg[0]; + } + + // first arg is the path + if (typeof arg !== 'function') { + offset = 1; + path = fn; + } + } + + var callbacks = flatten(slice.call(arguments, offset)); + + if (callbacks.length === 0) { + throw new TypeError('Router.use() requires middleware functions'); + } + + for (var i = 0; i < callbacks.length; i++) { + var fn = callbacks[i]; + + if (typeof fn !== 'function') { + throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn)); + } + + // add the middleware + debug('use %s %s', path, fn.name || ''); + + var layer = new Layer(path, { + sensitive: this.caseSensitive, + strict: false, + end: false + }, fn); + + layer.route = undefined; + + this.stack.push(layer); + } + + return this; +}; + +/** + * Create a new Route for the given path. + * + * Each route contains a separate middleware stack and VERB handlers. + * + * See the Route api documentation for details on adding handlers + * and middleware to routes. + * + * @param {String} path + * @return {Route} + * @public + */ + +proto.route = function route(path) { + var route = new Route(path); + + var layer = new Layer(path, { + sensitive: this.caseSensitive, + strict: this.strict, + end: true + }, route.dispatch.bind(route)); + + layer.route = route; + + this.stack.push(layer); + return route; +}; + +// create Router#VERB functions +methods.concat('all').forEach(function(method){ + proto[method] = function(path){ + var route = this.route(path) + route[method].apply(route, slice.call(arguments, 1)); + return this; + }; +}); + +// append methods to a list of methods +function appendMethods(list, addition) { + for (var i = 0; i < addition.length; i++) { + var method = addition[i]; + if (list.indexOf(method) === -1) { + list.push(method); + } + } +} + +// get pathname of request +function getPathname(req) { + try { + return parseUrl(req).pathname; + } catch (err) { + return undefined; + } +} + +// get type for error message +function gettype(obj) { + var type = typeof obj; + + if (type !== 'object') { + return type; + } + + // inspect [[Class]] for objects + return toString.call(obj) + .replace(objectRegExp, '$1'); +} + +/** + * Match path to a layer. + * + * @param {Layer} layer + * @param {string} path + * @private + */ + +function matchLayer(layer, path) { + try { + return layer.match(path); + } catch (err) { + return err; + } +} + +// merge params with parent params +function mergeParams(params, parent) { + if (typeof parent !== 'object' || !parent) { + return params; + } + + // make copy of parent for base + var obj = mixin({}, parent); + + // simple non-numeric merging + if (!(0 in params) || !(0 in parent)) { + return mixin(obj, params); + } + + var i = 0; + var o = 0; + + // determine numeric gaps + while (i in params) { + i++; + } + + while (o in parent) { + o++; + } + + // offset numeric indices in params before merge + for (i--; i >= 0; i--) { + params[i + o] = params[i]; + + // create holes for the merge when necessary + if (i < o) { + delete params[i]; + } + } + + return mixin(obj, params); +} + +// restore obj props after function +function restore(fn, obj) { + var props = new Array(arguments.length - 2); + var vals = new Array(arguments.length - 2); + + for (var i = 0; i < props.length; i++) { + props[i] = arguments[i + 2]; + vals[i] = obj[props[i]]; + } + + return function(err){ + // restore vals + for (var i = 0; i < props.length; i++) { + obj[props[i]] = vals[i]; + } + + return fn.apply(this, arguments); + }; +} + +// send an OPTIONS response +function sendOptionsResponse(res, options, next) { + try { + var body = options.join(','); + res.set('Allow', body); + res.send(body); + } catch (err) { + next(err); + } +} + +// wrap a function +function wrap(old, fn) { + return function proxy() { + var args = new Array(arguments.length + 1); + + args[0] = old; + for (var i = 0, len = arguments.length; i < len; i++) { + args[i + 1] = arguments[i]; + } + + fn.apply(this, args); + }; +} diff --git a/node_modules/express/lib/router/layer.js b/node_modules/express/lib/router/layer.js new file mode 100644 index 0000000..fe9210c --- /dev/null +++ b/node_modules/express/lib/router/layer.js @@ -0,0 +1,176 @@ +/*! + * express + * Copyright(c) 2009-2013 TJ Holowaychuk + * Copyright(c) 2013 Roman Shtylman + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Module dependencies. + * @private + */ + +var pathRegexp = require('path-to-regexp'); +var debug = require('debug')('express:router:layer'); + +/** + * Module variables. + * @private + */ + +var hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * Module exports. + * @public + */ + +module.exports = Layer; + +function Layer(path, options, fn) { + if (!(this instanceof Layer)) { + return new Layer(path, options, fn); + } + + debug('new %s', path); + var opts = options || {}; + + this.handle = fn; + this.name = fn.name || ''; + this.params = undefined; + this.path = undefined; + this.regexp = pathRegexp(path, this.keys = [], opts); + + if (path === '/' && opts.end === false) { + this.regexp.fast_slash = true; + } +} + +/** + * Handle the error for the layer. + * + * @param {Error} error + * @param {Request} req + * @param {Response} res + * @param {function} next + * @api private + */ + +Layer.prototype.handle_error = function handle_error(error, req, res, next) { + var fn = this.handle; + + if (fn.length !== 4) { + // not a standard error handler + return next(error); + } + + try { + fn(error, req, res, next); + } catch (err) { + next(err); + } +}; + +/** + * Handle the request for the layer. + * + * @param {Request} req + * @param {Response} res + * @param {function} next + * @api private + */ + +Layer.prototype.handle_request = function handle(req, res, next) { + var fn = this.handle; + + if (fn.length > 3) { + // not a standard request handler + return next(); + } + + try { + fn(req, res, next); + } catch (err) { + next(err); + } +}; + +/** + * Check if this route matches `path`, if so + * populate `.params`. + * + * @param {String} path + * @return {Boolean} + * @api private + */ + +Layer.prototype.match = function match(path) { + if (path == null) { + // no path, nothing matches + this.params = undefined; + this.path = undefined; + return false; + } + + if (this.regexp.fast_slash) { + // fast path non-ending match for / (everything matches) + this.params = {}; + this.path = ''; + return true; + } + + var m = this.regexp.exec(path); + + if (!m) { + this.params = undefined; + this.path = undefined; + return false; + } + + // store values + this.params = {}; + this.path = m[0]; + + var keys = this.keys; + var params = this.params; + + for (var i = 1; i < m.length; i++) { + var key = keys[i - 1]; + var prop = key.name; + var val = decode_param(m[i]); + + if (val !== undefined || !(hasOwnProperty.call(params, prop))) { + params[prop] = val; + } + } + + return true; +}; + +/** + * Decode param value. + * + * @param {string} val + * @return {string} + * @private + */ + +function decode_param(val) { + if (typeof val !== 'string' || val.length === 0) { + return val; + } + + try { + return decodeURIComponent(val); + } catch (err) { + if (err instanceof URIError) { + err.message = 'Failed to decode param \'' + val + '\''; + err.status = err.statusCode = 400; + } + + throw err; + } +} diff --git a/node_modules/express/lib/router/route.js b/node_modules/express/lib/router/route.js new file mode 100644 index 0000000..2788d7b --- /dev/null +++ b/node_modules/express/lib/router/route.js @@ -0,0 +1,210 @@ +/*! + * express + * Copyright(c) 2009-2013 TJ Holowaychuk + * Copyright(c) 2013 Roman Shtylman + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Module dependencies. + * @private + */ + +var debug = require('debug')('express:router:route'); +var flatten = require('array-flatten'); +var Layer = require('./layer'); +var methods = require('methods'); + +/** + * Module variables. + * @private + */ + +var slice = Array.prototype.slice; +var toString = Object.prototype.toString; + +/** + * Module exports. + * @public + */ + +module.exports = Route; + +/** + * Initialize `Route` with the given `path`, + * + * @param {String} path + * @public + */ + +function Route(path) { + this.path = path; + this.stack = []; + + debug('new %s', path); + + // route handlers for various http methods + this.methods = {}; +} + +/** + * Determine if the route handles a given method. + * @private + */ + +Route.prototype._handles_method = function _handles_method(method) { + if (this.methods._all) { + return true; + } + + var name = method.toLowerCase(); + + if (name === 'head' && !this.methods['head']) { + name = 'get'; + } + + return Boolean(this.methods[name]); +}; + +/** + * @return {Array} supported HTTP methods + * @private + */ + +Route.prototype._options = function _options() { + var methods = Object.keys(this.methods); + + // append automatic head + if (this.methods.get && !this.methods.head) { + methods.push('head'); + } + + for (var i = 0; i < methods.length; i++) { + // make upper case + methods[i] = methods[i].toUpperCase(); + } + + return methods; +}; + +/** + * dispatch req, res into this route + * @private + */ + +Route.prototype.dispatch = function dispatch(req, res, done) { + var idx = 0; + var stack = this.stack; + if (stack.length === 0) { + return done(); + } + + var method = req.method.toLowerCase(); + if (method === 'head' && !this.methods['head']) { + method = 'get'; + } + + req.route = this; + + next(); + + function next(err) { + if (err && err === 'route') { + return done(); + } + + var layer = stack[idx++]; + if (!layer) { + return done(err); + } + + if (layer.method && layer.method !== method) { + return next(err); + } + + if (err) { + layer.handle_error(err, req, res, next); + } else { + layer.handle_request(req, res, next); + } + } +}; + +/** + * Add a handler for all HTTP verbs to this route. + * + * Behaves just like middleware and can respond or call `next` + * to continue processing. + * + * You can use multiple `.all` call to add multiple handlers. + * + * function check_something(req, res, next){ + * next(); + * }; + * + * function validate_user(req, res, next){ + * next(); + * }; + * + * route + * .all(validate_user) + * .all(check_something) + * .get(function(req, res, next){ + * res.send('hello world'); + * }); + * + * @param {function} handler + * @return {Route} for chaining + * @api public + */ + +Route.prototype.all = function all() { + var handles = flatten(slice.call(arguments)); + + for (var i = 0; i < handles.length; i++) { + var handle = handles[i]; + + if (typeof handle !== 'function') { + var type = toString.call(handle); + var msg = 'Route.all() requires callback functions but got a ' + type; + throw new TypeError(msg); + } + + var layer = Layer('/', {}, handle); + layer.method = undefined; + + this.methods._all = true; + this.stack.push(layer); + } + + return this; +}; + +methods.forEach(function(method){ + Route.prototype[method] = function(){ + var handles = flatten(slice.call(arguments)); + + for (var i = 0; i < handles.length; i++) { + var handle = handles[i]; + + if (typeof handle !== 'function') { + var type = toString.call(handle); + var msg = 'Route.' + method + '() requires callback functions but got a ' + type; + throw new Error(msg); + } + + debug('%s %s', method, this.path); + + var layer = Layer('/', {}, handle); + layer.method = method; + + this.methods[method] = true; + this.stack.push(layer); + } + + return this; + }; +}); diff --git a/node_modules/express/lib/utils.js b/node_modules/express/lib/utils.js new file mode 100644 index 0000000..3d54247 --- /dev/null +++ b/node_modules/express/lib/utils.js @@ -0,0 +1,300 @@ +/*! + * express + * Copyright(c) 2009-2013 TJ Holowaychuk + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Module dependencies. + * @api private + */ + +var contentDisposition = require('content-disposition'); +var contentType = require('content-type'); +var deprecate = require('depd')('express'); +var flatten = require('array-flatten'); +var mime = require('send').mime; +var basename = require('path').basename; +var etag = require('etag'); +var proxyaddr = require('proxy-addr'); +var qs = require('qs'); +var querystring = require('querystring'); + +/** + * Return strong ETag for `body`. + * + * @param {String|Buffer} body + * @param {String} [encoding] + * @return {String} + * @api private + */ + +exports.etag = function (body, encoding) { + var buf = !Buffer.isBuffer(body) + ? new Buffer(body, encoding) + : body; + + return etag(buf, {weak: false}); +}; + +/** + * Return weak ETag for `body`. + * + * @param {String|Buffer} body + * @param {String} [encoding] + * @return {String} + * @api private + */ + +exports.wetag = function wetag(body, encoding){ + var buf = !Buffer.isBuffer(body) + ? new Buffer(body, encoding) + : body; + + return etag(buf, {weak: true}); +}; + +/** + * Check if `path` looks absolute. + * + * @param {String} path + * @return {Boolean} + * @api private + */ + +exports.isAbsolute = function(path){ + if ('/' == path[0]) return true; + if (':' == path[1] && '\\' == path[2]) return true; + if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path +}; + +/** + * Flatten the given `arr`. + * + * @param {Array} arr + * @return {Array} + * @api private + */ + +exports.flatten = deprecate.function(flatten, + 'utils.flatten: use array-flatten npm module instead'); + +/** + * Normalize the given `type`, for example "html" becomes "text/html". + * + * @param {String} type + * @return {Object} + * @api private + */ + +exports.normalizeType = function(type){ + return ~type.indexOf('/') + ? acceptParams(type) + : { value: mime.lookup(type), params: {} }; +}; + +/** + * Normalize `types`, for example "html" becomes "text/html". + * + * @param {Array} types + * @return {Array} + * @api private + */ + +exports.normalizeTypes = function(types){ + var ret = []; + + for (var i = 0; i < types.length; ++i) { + ret.push(exports.normalizeType(types[i])); + } + + return ret; +}; + +/** + * Generate Content-Disposition header appropriate for the filename. + * non-ascii filenames are urlencoded and a filename* parameter is added + * + * @param {String} filename + * @return {String} + * @api private + */ + +exports.contentDisposition = deprecate.function(contentDisposition, + 'utils.contentDisposition: use content-disposition npm module instead'); + +/** + * Parse accept params `str` returning an + * object with `.value`, `.quality` and `.params`. + * also includes `.originalIndex` for stable sorting + * + * @param {String} str + * @return {Object} + * @api private + */ + +function acceptParams(str, index) { + var parts = str.split(/ *; */); + var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index }; + + for (var i = 1; i < parts.length; ++i) { + var pms = parts[i].split(/ *= */); + if ('q' == pms[0]) { + ret.quality = parseFloat(pms[1]); + } else { + ret.params[pms[0]] = pms[1]; + } + } + + return ret; +} + +/** + * Compile "etag" value to function. + * + * @param {Boolean|String|Function} val + * @return {Function} + * @api private + */ + +exports.compileETag = function(val) { + var fn; + + if (typeof val === 'function') { + return val; + } + + switch (val) { + case true: + fn = exports.wetag; + break; + case false: + break; + case 'strong': + fn = exports.etag; + break; + case 'weak': + fn = exports.wetag; + break; + default: + throw new TypeError('unknown value for etag function: ' + val); + } + + return fn; +} + +/** + * Compile "query parser" value to function. + * + * @param {String|Function} val + * @return {Function} + * @api private + */ + +exports.compileQueryParser = function compileQueryParser(val) { + var fn; + + if (typeof val === 'function') { + return val; + } + + switch (val) { + case true: + fn = querystring.parse; + break; + case false: + fn = newObject; + break; + case 'extended': + fn = parseExtendedQueryString; + break; + case 'simple': + fn = querystring.parse; + break; + default: + throw new TypeError('unknown value for query parser function: ' + val); + } + + return fn; +} + +/** + * Compile "proxy trust" value to function. + * + * @param {Boolean|String|Number|Array|Function} val + * @return {Function} + * @api private + */ + +exports.compileTrust = function(val) { + if (typeof val === 'function') return val; + + if (val === true) { + // Support plain true/false + return function(){ return true }; + } + + if (typeof val === 'number') { + // Support trusting hop count + return function(a, i){ return i < val }; + } + + if (typeof val === 'string') { + // Support comma-separated values + val = val.split(/ *, */); + } + + return proxyaddr.compile(val || []); +} + +/** + * Set the charset in a given Content-Type string. + * + * @param {String} type + * @param {String} charset + * @return {String} + * @api private + */ + +exports.setCharset = function setCharset(type, charset) { + if (!type || !charset) { + return type; + } + + // parse type + var parsed = contentType.parse(type); + + // set charset + parsed.parameters.charset = charset; + + // format type + return contentType.format(parsed); +}; + +/** + * Parse an extended query string with qs. + * + * @return {Object} + * @private + */ + +function parseExtendedQueryString(str) { + return qs.parse(str, { + allowDots: false, + allowPrototypes: true + }); +} + +/** + * Return new empty object. + * + * @return {Object} + * @api private + */ + +function newObject() { + return {}; +} diff --git a/node_modules/express/lib/view.js b/node_modules/express/lib/view.js new file mode 100644 index 0000000..52415d4 --- /dev/null +++ b/node_modules/express/lib/view.js @@ -0,0 +1,173 @@ +/*! + * express + * Copyright(c) 2009-2013 TJ Holowaychuk + * Copyright(c) 2013 Roman Shtylman + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Module dependencies. + * @private + */ + +var debug = require('debug')('express:view'); +var path = require('path'); +var fs = require('fs'); +var utils = require('./utils'); + +/** + * Module variables. + * @private + */ + +var dirname = path.dirname; +var basename = path.basename; +var extname = path.extname; +var join = path.join; +var resolve = path.resolve; + +/** + * Module exports. + * @public + */ + +module.exports = View; + +/** + * Initialize a new `View` with the given `name`. + * + * Options: + * + * - `defaultEngine` the default template engine name + * - `engines` template engine require() cache + * - `root` root path for view lookup + * + * @param {string} name + * @param {object} options + * @public + */ + +function View(name, options) { + var opts = options || {}; + + this.defaultEngine = opts.defaultEngine; + this.ext = extname(name); + this.name = name; + this.root = opts.root; + + if (!this.ext && !this.defaultEngine) { + throw new Error('No default engine was specified and no extension was provided.'); + } + + var fileName = name; + + if (!this.ext) { + // get extension from default engine name + this.ext = this.defaultEngine[0] !== '.' + ? '.' + this.defaultEngine + : this.defaultEngine; + + fileName += this.ext; + } + + if (!opts.engines[this.ext]) { + // load engine + opts.engines[this.ext] = require(this.ext.substr(1)).__express; + } + + // store loaded engine + this.engine = opts.engines[this.ext]; + + // lookup path + this.path = this.lookup(fileName); +} + +/** + * Lookup view by the given `name` + * + * @param {string} name + * @private + */ + +View.prototype.lookup = function lookup(name) { + var path; + var roots = [].concat(this.root); + + debug('lookup "%s"', name); + + for (var i = 0; i < roots.length && !path; i++) { + var root = roots[i]; + + // resolve the path + var loc = resolve(root, name); + var dir = dirname(loc); + var file = basename(loc); + + // resolve the file + path = this.resolve(dir, file); + } + + return path; +}; + +/** + * Render with the given options. + * + * @param {object} options + * @param {function} callback + * @private + */ + +View.prototype.render = function render(options, callback) { + debug('render "%s"', this.path); + this.engine(this.path, options, callback); +}; + +/** + * Resolve the file within the given directory. + * + * @param {string} dir + * @param {string} file + * @private + */ + +View.prototype.resolve = function resolve(dir, file) { + var ext = this.ext; + + // . + var path = join(dir, file); + var stat = tryStat(path); + + if (stat && stat.isFile()) { + return path; + } + + // /index. + path = join(dir, basename(file, ext), 'index' + ext); + stat = tryStat(path); + + if (stat && stat.isFile()) { + return path; + } +}; + +/** + * Return a stat, maybe. + * + * @param {string} path + * @return {fs.Stats} + * @private + */ + +function tryStat(path) { + debug('stat "%s"', path); + + try { + return fs.statSync(path); + } catch (e) { + return undefined; + } +} diff --git a/node_modules/express/node_modules/accepts/HISTORY.md b/node_modules/express/node_modules/accepts/HISTORY.md new file mode 100644 index 0000000..397636e --- /dev/null +++ b/node_modules/express/node_modules/accepts/HISTORY.md @@ -0,0 +1,170 @@ +1.2.13 / 2015-09-06 +=================== + + * deps: mime-types@~2.1.6 + - deps: mime-db@~1.18.0 + +1.2.12 / 2015-07-30 +=================== + + * deps: mime-types@~2.1.4 + - deps: mime-db@~1.16.0 + +1.2.11 / 2015-07-16 +=================== + + * deps: mime-types@~2.1.3 + - deps: mime-db@~1.15.0 + +1.2.10 / 2015-07-01 +=================== + + * deps: mime-types@~2.1.2 + - deps: mime-db@~1.14.0 + +1.2.9 / 2015-06-08 +================== + + * deps: mime-types@~2.1.1 + - perf: fix deopt during mapping + +1.2.8 / 2015-06-07 +================== + + * deps: mime-types@~2.1.0 + - deps: mime-db@~1.13.0 + * perf: avoid argument reassignment & argument slice + * perf: avoid negotiator recursive construction + * perf: enable strict mode + * perf: remove unnecessary bitwise operator + +1.2.7 / 2015-05-10 +================== + + * deps: negotiator@0.5.3 + - Fix media type parameter matching to be case-insensitive + +1.2.6 / 2015-05-07 +================== + + * deps: mime-types@~2.0.11 + - deps: mime-db@~1.9.1 + * deps: negotiator@0.5.2 + - Fix comparing media types with quoted values + - Fix splitting media types with quoted commas + +1.2.5 / 2015-03-13 +================== + + * deps: mime-types@~2.0.10 + - deps: mime-db@~1.8.0 + +1.2.4 / 2015-02-14 +================== + + * Support Node.js 0.6 + * deps: mime-types@~2.0.9 + - deps: mime-db@~1.7.0 + * deps: negotiator@0.5.1 + - Fix preference sorting to be stable for long acceptable lists + +1.2.3 / 2015-01-31 +================== + + * deps: mime-types@~2.0.8 + - deps: mime-db@~1.6.0 + +1.2.2 / 2014-12-30 +================== + + * deps: mime-types@~2.0.7 + - deps: mime-db@~1.5.0 + +1.2.1 / 2014-12-30 +================== + + * deps: mime-types@~2.0.5 + - deps: mime-db@~1.3.1 + +1.2.0 / 2014-12-19 +================== + + * deps: negotiator@0.5.0 + - Fix list return order when large accepted list + - Fix missing identity encoding when q=0 exists + - Remove dynamic building of Negotiator class + +1.1.4 / 2014-12-10 +================== + + * deps: mime-types@~2.0.4 + - deps: mime-db@~1.3.0 + +1.1.3 / 2014-11-09 +================== + + * deps: mime-types@~2.0.3 + - deps: mime-db@~1.2.0 + +1.1.2 / 2014-10-14 +================== + + * deps: negotiator@0.4.9 + - Fix error when media type has invalid parameter + +1.1.1 / 2014-09-28 +================== + + * deps: mime-types@~2.0.2 + - deps: mime-db@~1.1.0 + * deps: negotiator@0.4.8 + - Fix all negotiations to be case-insensitive + - Stable sort preferences of same quality according to client order + +1.1.0 / 2014-09-02 +================== + + * update `mime-types` + +1.0.7 / 2014-07-04 +================== + + * Fix wrong type returned from `type` when match after unknown extension + +1.0.6 / 2014-06-24 +================== + + * deps: negotiator@0.4.7 + +1.0.5 / 2014-06-20 +================== + + * fix crash when unknown extension given + +1.0.4 / 2014-06-19 +================== + + * use `mime-types` + +1.0.3 / 2014-06-11 +================== + + * deps: negotiator@0.4.6 + - Order by specificity when quality is the same + +1.0.2 / 2014-05-29 +================== + + * Fix interpretation when header not in request + * deps: pin negotiator@0.4.5 + +1.0.1 / 2014-01-18 +================== + + * Identity encoding isn't always acceptable + * deps: negotiator@~0.4.0 + +1.0.0 / 2013-12-27 +================== + + * Genesis diff --git a/node_modules/express/node_modules/accepts/LICENSE b/node_modules/express/node_modules/accepts/LICENSE new file mode 100644 index 0000000..0616607 --- /dev/null +++ b/node_modules/express/node_modules/accepts/LICENSE @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/accepts/README.md b/node_modules/express/node_modules/accepts/README.md new file mode 100644 index 0000000..ae36676 --- /dev/null +++ b/node_modules/express/node_modules/accepts/README.md @@ -0,0 +1,135 @@ +# accepts + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Higher level content negotiation based on [negotiator](https://www.npmjs.com/package/negotiator). Extracted from [koa](https://www.npmjs.com/package/koa) for general use. + +In addition to negotiator, it allows: + +- Allows types as an array or arguments list, ie `(['text/html', 'application/json'])` as well as `('text/html', 'application/json')`. +- Allows type shorthands such as `json`. +- Returns `false` when no types match +- Treats non-existent headers as `*` + +## Installation + +```sh +npm install accepts +``` + +## API + +```js +var accepts = require('accepts') +``` + +### accepts(req) + +Create a new `Accepts` object for the given `req`. + +#### .charset(charsets) + +Return the first accepted charset. If nothing in `charsets` is accepted, +then `false` is returned. + +#### .charsets() + +Return the charsets that the request accepts, in the order of the client's +preference (most preferred first). + +#### .encoding(encodings) + +Return the first accepted encoding. If nothing in `encodings` is accepted, +then `false` is returned. + +#### .encodings() + +Return the encodings that the request accepts, in the order of the client's +preference (most preferred first). + +#### .language(languages) + +Return the first accepted language. If nothing in `languages` is accepted, +then `false` is returned. + +#### .languages() + +Return the languages that the request accepts, in the order of the client's +preference (most preferred first). + +#### .type(types) + +Return the first accepted type (and it is returned as the same text as what +appears in the `types` array). If nothing in `types` is accepted, then `false` +is returned. + +The `types` array can contain full MIME types or file extensions. Any value +that is not a full MIME types is passed to `require('mime-types').lookup`. + +#### .types() + +Return the types that the request accepts, in the order of the client's +preference (most preferred first). + +## Examples + +### Simple type negotiation + +This simple example shows how to use `accepts` to return a different typed +respond body based on what the client wants to accept. The server lists it's +preferences in order and will get back the best match between the client and +server. + +```js +var accepts = require('accepts') +var http = require('http') + +function app(req, res) { + var accept = accepts(req) + + // the order of this list is significant; should be server preferred order + switch(accept.type(['json', 'html'])) { + case 'json': + res.setHeader('Content-Type', 'application/json') + res.write('{"hello":"world!"}') + break + case 'html': + res.setHeader('Content-Type', 'text/html') + res.write('hello, world!') + break + default: + // the fallback is text/plain, so no need to specify it above + res.setHeader('Content-Type', 'text/plain') + res.write('hello, world!') + break + } + + res.end() +} + +http.createServer(app).listen(3000) +``` + +You can test this out with the cURL program: +```sh +curl -I -H'Accept: text/html' http://localhost:3000/ +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/accepts.svg +[npm-url]: https://npmjs.org/package/accepts +[node-version-image]: https://img.shields.io/node/v/accepts.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/accepts/master.svg +[travis-url]: https://travis-ci.org/jshttp/accepts +[coveralls-image]: https://img.shields.io/coveralls/jshttp/accepts/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/accepts +[downloads-image]: https://img.shields.io/npm/dm/accepts.svg +[downloads-url]: https://npmjs.org/package/accepts diff --git a/node_modules/express/node_modules/accepts/index.js b/node_modules/express/node_modules/accepts/index.js new file mode 100644 index 0000000..e80192a --- /dev/null +++ b/node_modules/express/node_modules/accepts/index.js @@ -0,0 +1,231 @@ +/*! + * accepts + * Copyright(c) 2014 Jonathan Ong + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module dependencies. + * @private + */ + +var Negotiator = require('negotiator') +var mime = require('mime-types') + +/** + * Module exports. + * @public + */ + +module.exports = Accepts + +/** + * Create a new Accepts object for the given req. + * + * @param {object} req + * @public + */ + +function Accepts(req) { + if (!(this instanceof Accepts)) + return new Accepts(req) + + this.headers = req.headers + this.negotiator = new Negotiator(req) +} + +/** + * Check if the given `type(s)` is acceptable, returning + * the best match when true, otherwise `undefined`, in which + * case you should respond with 406 "Not Acceptable". + * + * The `type` value may be a single mime type string + * such as "application/json", the extension name + * such as "json" or an array `["json", "html", "text/plain"]`. When a list + * or array is given the _best_ match, if any is returned. + * + * Examples: + * + * // Accept: text/html + * this.types('html'); + * // => "html" + * + * // Accept: text/*, application/json + * this.types('html'); + * // => "html" + * this.types('text/html'); + * // => "text/html" + * this.types('json', 'text'); + * // => "json" + * this.types('application/json'); + * // => "application/json" + * + * // Accept: text/*, application/json + * this.types('image/png'); + * this.types('png'); + * // => undefined + * + * // Accept: text/*;q=.5, application/json + * this.types(['html', 'json']); + * this.types('html', 'json'); + * // => "json" + * + * @param {String|Array} types... + * @return {String|Array|Boolean} + * @public + */ + +Accepts.prototype.type = +Accepts.prototype.types = function (types_) { + var types = types_ + + // support flattened arguments + if (types && !Array.isArray(types)) { + types = new Array(arguments.length) + for (var i = 0; i < types.length; i++) { + types[i] = arguments[i] + } + } + + // no types, return all requested types + if (!types || types.length === 0) { + return this.negotiator.mediaTypes() + } + + if (!this.headers.accept) return types[0]; + var mimes = types.map(extToMime); + var accepts = this.negotiator.mediaTypes(mimes.filter(validMime)); + var first = accepts[0]; + if (!first) return false; + return types[mimes.indexOf(first)]; +} + +/** + * Return accepted encodings or best fit based on `encodings`. + * + * Given `Accept-Encoding: gzip, deflate` + * an array sorted by quality is returned: + * + * ['gzip', 'deflate'] + * + * @param {String|Array} encodings... + * @return {String|Array} + * @public + */ + +Accepts.prototype.encoding = +Accepts.prototype.encodings = function (encodings_) { + var encodings = encodings_ + + // support flattened arguments + if (encodings && !Array.isArray(encodings)) { + encodings = new Array(arguments.length) + for (var i = 0; i < encodings.length; i++) { + encodings[i] = arguments[i] + } + } + + // no encodings, return all requested encodings + if (!encodings || encodings.length === 0) { + return this.negotiator.encodings() + } + + return this.negotiator.encodings(encodings)[0] || false +} + +/** + * Return accepted charsets or best fit based on `charsets`. + * + * Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5` + * an array sorted by quality is returned: + * + * ['utf-8', 'utf-7', 'iso-8859-1'] + * + * @param {String|Array} charsets... + * @return {String|Array} + * @public + */ + +Accepts.prototype.charset = +Accepts.prototype.charsets = function (charsets_) { + var charsets = charsets_ + + // support flattened arguments + if (charsets && !Array.isArray(charsets)) { + charsets = new Array(arguments.length) + for (var i = 0; i < charsets.length; i++) { + charsets[i] = arguments[i] + } + } + + // no charsets, return all requested charsets + if (!charsets || charsets.length === 0) { + return this.negotiator.charsets() + } + + return this.negotiator.charsets(charsets)[0] || false +} + +/** + * Return accepted languages or best fit based on `langs`. + * + * Given `Accept-Language: en;q=0.8, es, pt` + * an array sorted by quality is returned: + * + * ['es', 'pt', 'en'] + * + * @param {String|Array} langs... + * @return {Array|String} + * @public + */ + +Accepts.prototype.lang = +Accepts.prototype.langs = +Accepts.prototype.language = +Accepts.prototype.languages = function (languages_) { + var languages = languages_ + + // support flattened arguments + if (languages && !Array.isArray(languages)) { + languages = new Array(arguments.length) + for (var i = 0; i < languages.length; i++) { + languages[i] = arguments[i] + } + } + + // no languages, return all requested languages + if (!languages || languages.length === 0) { + return this.negotiator.languages() + } + + return this.negotiator.languages(languages)[0] || false +} + +/** + * Convert extnames to mime. + * + * @param {String} type + * @return {String} + * @private + */ + +function extToMime(type) { + return type.indexOf('/') === -1 + ? mime.lookup(type) + : type +} + +/** + * Check if mime is valid. + * + * @param {String} type + * @return {String} + * @private + */ + +function validMime(type) { + return typeof type === 'string'; +} diff --git a/node_modules/express/node_modules/accepts/node_modules/mime-types/HISTORY.md b/node_modules/express/node_modules/accepts/node_modules/mime-types/HISTORY.md new file mode 100644 index 0000000..64241d9 --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/mime-types/HISTORY.md @@ -0,0 +1,177 @@ +2.1.8 / 2015-11-30 +================== + + * deps: mime-db@~1.20.0 + - Add new mime types + +2.1.7 / 2015-09-20 +================== + + * deps: mime-db@~1.19.0 + - Add new mime types + +2.1.6 / 2015-09-03 +================== + + * deps: mime-db@~1.18.0 + - Add new mime types + +2.1.5 / 2015-08-20 +================== + + * deps: mime-db@~1.17.0 + - Add new mime types + +2.1.4 / 2015-07-30 +================== + + * deps: mime-db@~1.16.0 + - Add new mime types + +2.1.3 / 2015-07-13 +================== + + * deps: mime-db@~1.15.0 + - Add new mime types + +2.1.2 / 2015-06-25 +================== + + * deps: mime-db@~1.14.0 + - Add new mime types + +2.1.1 / 2015-06-08 +================== + + * perf: fix deopt during mapping + +2.1.0 / 2015-06-07 +================== + + * Fix incorrectly treating extension-less file name as extension + - i.e. `'path/to/json'` will no longer return `application/json` + * Fix `.charset(type)` to accept parameters + * Fix `.charset(type)` to match case-insensitive + * Improve generation of extension to MIME mapping + * Refactor internals for readability and no argument reassignment + * Prefer `application/*` MIME types from the same source + * Prefer any type over `application/octet-stream` + * deps: mime-db@~1.13.0 + - Add nginx as a source + - Add new mime types + +2.0.14 / 2015-06-06 +=================== + + * deps: mime-db@~1.12.0 + - Add new mime types + +2.0.13 / 2015-05-31 +=================== + + * deps: mime-db@~1.11.0 + - Add new mime types + +2.0.12 / 2015-05-19 +=================== + + * deps: mime-db@~1.10.0 + - Add new mime types + +2.0.11 / 2015-05-05 +=================== + + * deps: mime-db@~1.9.1 + - Add new mime types + +2.0.10 / 2015-03-13 +=================== + + * deps: mime-db@~1.8.0 + - Add new mime types + +2.0.9 / 2015-02-09 +================== + + * deps: mime-db@~1.7.0 + - Add new mime types + - Community extensions ownership transferred from `node-mime` + +2.0.8 / 2015-01-29 +================== + + * deps: mime-db@~1.6.0 + - Add new mime types + +2.0.7 / 2014-12-30 +================== + + * deps: mime-db@~1.5.0 + - Add new mime types + - Fix various invalid MIME type entries + +2.0.6 / 2014-12-30 +================== + + * deps: mime-db@~1.4.0 + - Add new mime types + - Fix various invalid MIME type entries + - Remove example template MIME types + +2.0.5 / 2014-12-29 +================== + + * deps: mime-db@~1.3.1 + - Fix missing extensions + +2.0.4 / 2014-12-10 +================== + + * deps: mime-db@~1.3.0 + - Add new mime types + +2.0.3 / 2014-11-09 +================== + + * deps: mime-db@~1.2.0 + - Add new mime types + +2.0.2 / 2014-09-28 +================== + + * deps: mime-db@~1.1.0 + - Add new mime types + - Add additional compressible + - Update charsets + +2.0.1 / 2014-09-07 +================== + + * Support Node.js 0.6 + +2.0.0 / 2014-09-02 +================== + + * Use `mime-db` + * Remove `.define()` + +1.0.2 / 2014-08-04 +================== + + * Set charset=utf-8 for `text/javascript` + +1.0.1 / 2014-06-24 +================== + + * Add `text/jsx` type + +1.0.0 / 2014-05-12 +================== + + * Return `false` for unknown types + * Set charset=utf-8 for `application/json` + +0.1.0 / 2014-05-02 +================== + + * Initial release diff --git a/node_modules/express/node_modules/accepts/node_modules/mime-types/LICENSE b/node_modules/express/node_modules/accepts/node_modules/mime-types/LICENSE new file mode 100644 index 0000000..0616607 --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/mime-types/LICENSE @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/accepts/node_modules/mime-types/README.md b/node_modules/express/node_modules/accepts/node_modules/mime-types/README.md new file mode 100644 index 0000000..e26295d --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/mime-types/README.md @@ -0,0 +1,103 @@ +# mime-types + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +The ultimate javascript content-type utility. + +Similar to [node-mime](https://github.com/broofa/node-mime), except: + +- __No fallbacks.__ Instead of naively returning the first available type, `mime-types` simply returns `false`, + so do `var type = mime.lookup('unrecognized') || 'application/octet-stream'`. +- No `new Mime()` business, so you could do `var lookup = require('mime-types').lookup`. +- Additional mime types are added such as jade and stylus via [mime-db](https://github.com/jshttp/mime-db) +- No `.define()` functionality + +Otherwise, the API is compatible. + +## Install + +```sh +$ npm install mime-types +``` + +## Adding Types + +All mime types are based on [mime-db](https://github.com/jshttp/mime-db), +so open a PR there if you'd like to add mime types. + +## API + +```js +var mime = require('mime-types') +``` + +All functions return `false` if input is invalid or not found. + +### mime.lookup(path) + +Lookup the content-type associated with a file. + +```js +mime.lookup('json') // 'application/json' +mime.lookup('.md') // 'text/x-markdown' +mime.lookup('file.html') // 'text/html' +mime.lookup('folder/file.js') // 'application/javascript' +mime.lookup('folder/.htaccess') // false + +mime.lookup('cats') // false +``` + +### mime.contentType(type) + +Create a full content-type header given a content-type or extension. + +```js +mime.contentType('markdown') // 'text/x-markdown; charset=utf-8' +mime.contentType('file.json') // 'application/json; charset=utf-8' + +// from a full path +mime.contentType(path.extname('/path/to/file.json')) // 'application/json; charset=utf-8' +``` + +### mime.extension(type) + +Get the default extension for a content-type. + +```js +mime.extension('application/octet-stream') // 'bin' +``` + +### mime.charset(type) + +Lookup the implied default charset of a content-type. + +```js +mime.charset('text/x-markdown') // 'UTF-8' +``` + +### var type = mime.types[extension] + +A map of content-types by extension. + +### [extensions...] = mime.extensions[type] + +A map of extensions by content-type. + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/mime-types.svg +[npm-url]: https://npmjs.org/package/mime-types +[node-version-image]: https://img.shields.io/node/v/mime-types.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/mime-types/master.svg +[travis-url]: https://travis-ci.org/jshttp/mime-types +[coveralls-image]: https://img.shields.io/coveralls/jshttp/mime-types/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/mime-types +[downloads-image]: https://img.shields.io/npm/dm/mime-types.svg +[downloads-url]: https://npmjs.org/package/mime-types diff --git a/node_modules/express/node_modules/accepts/node_modules/mime-types/index.js b/node_modules/express/node_modules/accepts/node_modules/mime-types/index.js new file mode 100644 index 0000000..f7008b2 --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/mime-types/index.js @@ -0,0 +1,188 @@ +/*! + * mime-types + * Copyright(c) 2014 Jonathan Ong + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module dependencies. + * @private + */ + +var db = require('mime-db') +var extname = require('path').extname + +/** + * Module variables. + * @private + */ + +var extractTypeRegExp = /^\s*([^;\s]*)(?:;|\s|$)/ +var textTypeRegExp = /^text\//i + +/** + * Module exports. + * @public + */ + +exports.charset = charset +exports.charsets = { lookup: charset } +exports.contentType = contentType +exports.extension = extension +exports.extensions = Object.create(null) +exports.lookup = lookup +exports.types = Object.create(null) + +// Populate the extensions/types maps +populateMaps(exports.extensions, exports.types) + +/** + * Get the default charset for a MIME type. + * + * @param {string} type + * @return {boolean|string} + */ + +function charset(type) { + if (!type || typeof type !== 'string') { + return false + } + + // TODO: use media-typer + var match = extractTypeRegExp.exec(type) + var mime = match && db[match[1].toLowerCase()] + + if (mime && mime.charset) { + return mime.charset + } + + // default text/* to utf-8 + if (match && textTypeRegExp.test(match[1])) { + return 'UTF-8' + } + + return false +} + +/** + * Create a full Content-Type header given a MIME type or extension. + * + * @param {string} str + * @return {boolean|string} + */ + +function contentType(str) { + // TODO: should this even be in this module? + if (!str || typeof str !== 'string') { + return false + } + + var mime = str.indexOf('/') === -1 + ? exports.lookup(str) + : str + + if (!mime) { + return false + } + + // TODO: use content-type or other module + if (mime.indexOf('charset') === -1) { + var charset = exports.charset(mime) + if (charset) mime += '; charset=' + charset.toLowerCase() + } + + return mime +} + +/** + * Get the default extension for a MIME type. + * + * @param {string} type + * @return {boolean|string} + */ + +function extension(type) { + if (!type || typeof type !== 'string') { + return false + } + + // TODO: use media-typer + var match = extractTypeRegExp.exec(type) + + // get extensions + var exts = match && exports.extensions[match[1].toLowerCase()] + + if (!exts || !exts.length) { + return false + } + + return exts[0] +} + +/** + * Lookup the MIME type for a file path/extension. + * + * @param {string} path + * @return {boolean|string} + */ + +function lookup(path) { + if (!path || typeof path !== 'string') { + return false + } + + // get the extension ("ext" or ".ext" or full path) + var extension = extname('x.' + path) + .toLowerCase() + .substr(1) + + if (!extension) { + return false + } + + return exports.types[extension] || false +} + +/** + * Populate the extensions and types maps. + * @private + */ + +function populateMaps(extensions, types) { + // source preference (least -> most) + var preference = ['nginx', 'apache', undefined, 'iana'] + + Object.keys(db).forEach(function forEachMimeType(type) { + var mime = db[type] + var exts = mime.extensions + + if (!exts || !exts.length) { + return + } + + // mime -> extensions + extensions[type] = exts + + // extension -> mime + for (var i = 0; i < exts.length; i++) { + var extension = exts[i] + + if (types[extension]) { + var from = preference.indexOf(db[types[extension]].source) + var to = preference.indexOf(mime.source) + + if (types[extension] !== 'application/octet-stream' + && from > to || (from === to && types[extension].substr(0, 12) === 'application/')) { + // skip the remapping + continue + } + } + + // set the extension -> mime + types[extension] = type + } + }) +} diff --git a/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/HISTORY.md b/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/HISTORY.md new file mode 100644 index 0000000..c7f8b5a --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/HISTORY.md @@ -0,0 +1,287 @@ +1.20.0 / 2015-11-10 +=================== + + * Add `application/cdni` + * Add `application/csvm+json` + * Add `application/rfc+xml` + * Add `application/vnd.3gpp.access-transfer-events+xml` + * Add `application/vnd.3gpp.srvcc-ext+xml` + * Add `application/vnd.ms-windows.wsd.oob` + * Add `application/vnd.oxli.countgraph` + * Add `application/vnd.pagerduty+json` + * Add `text/x-suse-ymp` + +1.19.0 / 2015-09-17 +=================== + + * Add `application/vnd.3gpp-prose-pc3ch+xml` + * Add `application/vnd.3gpp.srvcc-info+xml` + * Add `application/vnd.apple.pkpass` + * Add `application/vnd.drive+json` + +1.18.0 / 2015-09-03 +=================== + + * Add `application/pkcs12` + * Add `application/vnd.3gpp-prose+xml` + * Add `application/vnd.3gpp.mid-call+xml` + * Add `application/vnd.3gpp.state-and-event-info+xml` + * Add `application/vnd.anki` + * Add `application/vnd.firemonkeys.cloudcell` + * Add `application/vnd.openblox.game+xml` + * Add `application/vnd.openblox.game-binary` + +1.17.0 / 2015-08-13 +=================== + + * Add `application/x-msdos-program` + * Add `audio/g711-0` + * Add `image/vnd.mozilla.apng` + * Add extension `.exe` to `application/x-msdos-program` + +1.16.0 / 2015-07-29 +=================== + + * Add `application/vnd.uri-map` + +1.15.0 / 2015-07-13 +=================== + + * Add `application/x-httpd-php` + +1.14.0 / 2015-06-25 +=================== + + * Add `application/scim+json` + * Add `application/vnd.3gpp.ussd+xml` + * Add `application/vnd.biopax.rdf+xml` + * Add `text/x-processing` + +1.13.0 / 2015-06-07 +=================== + + * Add nginx as a source + * Add `application/x-cocoa` + * Add `application/x-java-archive-diff` + * Add `application/x-makeself` + * Add `application/x-perl` + * Add `application/x-pilot` + * Add `application/x-redhat-package-manager` + * Add `application/x-sea` + * Add `audio/x-m4a` + * Add `audio/x-realaudio` + * Add `image/x-jng` + * Add `text/mathml` + +1.12.0 / 2015-06-05 +=================== + + * Add `application/bdoc` + * Add `application/vnd.hyperdrive+json` + * Add `application/x-bdoc` + * Add extension `.rtf` to `text/rtf` + +1.11.0 / 2015-05-31 +=================== + + * Add `audio/wav` + * Add `audio/wave` + * Add extension `.litcoffee` to `text/coffeescript` + * Add extension `.sfd-hdstx` to `application/vnd.hydrostatix.sof-data` + * Add extension `.n-gage` to `application/vnd.nokia.n-gage.symbian.install` + +1.10.0 / 2015-05-19 +=================== + + * Add `application/vnd.balsamiq.bmpr` + * Add `application/vnd.microsoft.portable-executable` + * Add `application/x-ns-proxy-autoconfig` + +1.9.1 / 2015-04-19 +================== + + * Remove `.json` extension from `application/manifest+json` + - This is causing bugs downstream + +1.9.0 / 2015-04-19 +================== + + * Add `application/manifest+json` + * Add `application/vnd.micro+json` + * Add `image/vnd.zbrush.pcx` + * Add `image/x-ms-bmp` + +1.8.0 / 2015-03-13 +================== + + * Add `application/vnd.citationstyles.style+xml` + * Add `application/vnd.fastcopy-disk-image` + * Add `application/vnd.gov.sk.xmldatacontainer+xml` + * Add extension `.jsonld` to `application/ld+json` + +1.7.0 / 2015-02-08 +================== + + * Add `application/vnd.gerber` + * Add `application/vnd.msa-disk-image` + +1.6.1 / 2015-02-05 +================== + + * Community extensions ownership transferred from `node-mime` + +1.6.0 / 2015-01-29 +================== + + * Add `application/jose` + * Add `application/jose+json` + * Add `application/json-seq` + * Add `application/jwk+json` + * Add `application/jwk-set+json` + * Add `application/jwt` + * Add `application/rdap+json` + * Add `application/vnd.gov.sk.e-form+xml` + * Add `application/vnd.ims.imsccv1p3` + +1.5.0 / 2014-12-30 +================== + + * Add `application/vnd.oracle.resource+json` + * Fix various invalid MIME type entries + - `application/mbox+xml` + - `application/oscp-response` + - `application/vwg-multiplexed` + - `audio/g721` + +1.4.0 / 2014-12-21 +================== + + * Add `application/vnd.ims.imsccv1p2` + * Fix various invalid MIME type entries + - `application/vnd-acucobol` + - `application/vnd-curl` + - `application/vnd-dart` + - `application/vnd-dxr` + - `application/vnd-fdf` + - `application/vnd-mif` + - `application/vnd-sema` + - `application/vnd-wap-wmlc` + - `application/vnd.adobe.flash-movie` + - `application/vnd.dece-zip` + - `application/vnd.dvb_service` + - `application/vnd.micrografx-igx` + - `application/vnd.sealed-doc` + - `application/vnd.sealed-eml` + - `application/vnd.sealed-mht` + - `application/vnd.sealed-ppt` + - `application/vnd.sealed-tiff` + - `application/vnd.sealed-xls` + - `application/vnd.sealedmedia.softseal-html` + - `application/vnd.sealedmedia.softseal-pdf` + - `application/vnd.wap-slc` + - `application/vnd.wap-wbxml` + - `audio/vnd.sealedmedia.softseal-mpeg` + - `image/vnd-djvu` + - `image/vnd-svf` + - `image/vnd-wap-wbmp` + - `image/vnd.sealed-png` + - `image/vnd.sealedmedia.softseal-gif` + - `image/vnd.sealedmedia.softseal-jpg` + - `model/vnd-dwf` + - `model/vnd.parasolid.transmit-binary` + - `model/vnd.parasolid.transmit-text` + - `text/vnd-a` + - `text/vnd-curl` + - `text/vnd.wap-wml` + * Remove example template MIME types + - `application/example` + - `audio/example` + - `image/example` + - `message/example` + - `model/example` + - `multipart/example` + - `text/example` + - `video/example` + +1.3.1 / 2014-12-16 +================== + + * Fix missing extensions + - `application/json5` + - `text/hjson` + +1.3.0 / 2014-12-07 +================== + + * Add `application/a2l` + * Add `application/aml` + * Add `application/atfx` + * Add `application/atxml` + * Add `application/cdfx+xml` + * Add `application/dii` + * Add `application/json5` + * Add `application/lxf` + * Add `application/mf4` + * Add `application/vnd.apache.thrift.compact` + * Add `application/vnd.apache.thrift.json` + * Add `application/vnd.coffeescript` + * Add `application/vnd.enphase.envoy` + * Add `application/vnd.ims.imsccv1p1` + * Add `text/csv-schema` + * Add `text/hjson` + * Add `text/markdown` + * Add `text/yaml` + +1.2.0 / 2014-11-09 +================== + + * Add `application/cea` + * Add `application/dit` + * Add `application/vnd.gov.sk.e-form+zip` + * Add `application/vnd.tmd.mediaflex.api+xml` + * Type `application/epub+zip` is now IANA-registered + +1.1.2 / 2014-10-23 +================== + + * Rebuild database for `application/x-www-form-urlencoded` change + +1.1.1 / 2014-10-20 +================== + + * Mark `application/x-www-form-urlencoded` as compressible. + +1.1.0 / 2014-09-28 +================== + + * Add `application/font-woff2` + +1.0.3 / 2014-09-25 +================== + + * Fix engine requirement in package + +1.0.2 / 2014-09-25 +================== + + * Add `application/coap-group+json` + * Add `application/dcd` + * Add `application/vnd.apache.thrift.binary` + * Add `image/vnd.tencent.tap` + * Mark all JSON-derived types as compressible + * Update `text/vtt` data + +1.0.1 / 2014-08-30 +================== + + * Fix extension ordering + +1.0.0 / 2014-08-30 +================== + + * Add `application/atf` + * Add `application/merge-patch+json` + * Add `multipart/x-mixed-replace` + * Add `source: 'apache'` metadata + * Add `source: 'iana'` metadata + * Remove badly-assumed charset data diff --git a/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/LICENSE b/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/LICENSE new file mode 100644 index 0000000..a7ae8ee --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/README.md b/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/README.md new file mode 100644 index 0000000..164cca0 --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/README.md @@ -0,0 +1,82 @@ +# mime-db + +[![NPM Version][npm-version-image]][npm-url] +[![NPM Downloads][npm-downloads-image]][npm-url] +[![Node.js Version][node-image]][node-url] +[![Build Status][travis-image]][travis-url] +[![Coverage Status][coveralls-image]][coveralls-url] + +This is a database of all mime types. +It consists of a single, public JSON file and does not include any logic, +allowing it to remain as un-opinionated as possible with an API. +It aggregates data from the following sources: + +- http://www.iana.org/assignments/media-types/media-types.xhtml +- http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types +- http://hg.nginx.org/nginx/raw-file/default/conf/mime.types + +## Installation + +```bash +npm install mime-db +``` + +### Database Download + +If you're crazy enough to use this in the browser, you can just grab the +JSON file using [RawGit](https://rawgit.com/). It is recommended to replace +`master` with [a release tag](https://github.com/jshttp/mime-db/tags) as the +JSON format may change in the future. + +``` +https://cdn.rawgit.com/jshttp/mime-db/master/db.json +``` + +## Usage + +```js +var db = require('mime-db'); + +// grab data on .js files +var data = db['application/javascript']; +``` + +## Data Structure + +The JSON file is a map lookup for lowercased mime types. +Each mime type has the following properties: + +- `.source` - where the mime type is defined. + If not set, it's probably a custom media type. + - `apache` - [Apache common media types](http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types) + - `iana` - [IANA-defined media types](http://www.iana.org/assignments/media-types/media-types.xhtml) + - `nginx` - [nginx media types](http://hg.nginx.org/nginx/raw-file/default/conf/mime.types) +- `.extensions[]` - known extensions associated with this mime type. +- `.compressible` - whether a file of this type is can be gzipped. +- `.charset` - the default charset associated with this type, if any. + +If unknown, every property could be `undefined`. + +## Contributing + +To edit the database, only make PRs against `src/custom.json` or +`src/custom-suffix.json`. + +To update the build, run `npm run build`. + +## Adding Custom Media Types + +The best way to get new media types included in this library is to register +them with the IANA. The community registration procedure is outlined in +[RFC 6838 section 5](http://tools.ietf.org/html/rfc6838#section-5). Types +registered with the IANA are automatically pulled into this library. + +[npm-version-image]: https://img.shields.io/npm/v/mime-db.svg +[npm-downloads-image]: https://img.shields.io/npm/dm/mime-db.svg +[npm-url]: https://npmjs.org/package/mime-db +[travis-image]: https://img.shields.io/travis/jshttp/mime-db/master.svg +[travis-url]: https://travis-ci.org/jshttp/mime-db +[coveralls-image]: https://img.shields.io/coveralls/jshttp/mime-db/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/mime-db?branch=master +[node-image]: https://img.shields.io/node/v/mime-db.svg +[node-url]: http://nodejs.org/download/ diff --git a/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/db.json b/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/db.json new file mode 100644 index 0000000..123e7f9 --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/db.json @@ -0,0 +1,6504 @@ +{ + "application/1d-interleaved-parityfec": { + "source": "iana" + }, + "application/3gpdash-qoe-report+xml": { + "source": "iana" + }, + "application/3gpp-ims+xml": { + "source": "iana" + }, + "application/a2l": { + "source": "iana" + }, + "application/activemessage": { + "source": "iana" + }, + "application/alto-costmap+json": { + "source": "iana", + "compressible": true + }, + "application/alto-costmapfilter+json": { + "source": "iana", + "compressible": true + }, + "application/alto-directory+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointcost+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointcostparams+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointprop+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointpropparams+json": { + "source": "iana", + "compressible": true + }, + "application/alto-error+json": { + "source": "iana", + "compressible": true + }, + "application/alto-networkmap+json": { + "source": "iana", + "compressible": true + }, + "application/alto-networkmapfilter+json": { + "source": "iana", + "compressible": true + }, + "application/aml": { + "source": "iana" + }, + "application/andrew-inset": { + "source": "iana", + "extensions": ["ez"] + }, + "application/applefile": { + "source": "iana" + }, + "application/applixware": { + "source": "apache", + "extensions": ["aw"] + }, + "application/atf": { + "source": "iana" + }, + "application/atfx": { + "source": "iana" + }, + "application/atom+xml": { + "source": "iana", + "compressible": true, + "extensions": ["atom"] + }, + "application/atomcat+xml": { + "source": "iana", + "extensions": ["atomcat"] + }, + "application/atomdeleted+xml": { + "source": "iana" + }, + "application/atomicmail": { + "source": "iana" + }, + "application/atomsvc+xml": { + "source": "iana", + "extensions": ["atomsvc"] + }, + "application/atxml": { + "source": "iana" + }, + "application/auth-policy+xml": { + "source": "iana" + }, + "application/bacnet-xdd+zip": { + "source": "iana" + }, + "application/batch-smtp": { + "source": "iana" + }, + "application/bdoc": { + "compressible": false, + "extensions": ["bdoc"] + }, + "application/beep+xml": { + "source": "iana" + }, + "application/calendar+json": { + "source": "iana", + "compressible": true + }, + "application/calendar+xml": { + "source": "iana" + }, + "application/call-completion": { + "source": "iana" + }, + "application/cals-1840": { + "source": "iana" + }, + "application/cbor": { + "source": "iana" + }, + "application/ccmp+xml": { + "source": "iana" + }, + "application/ccxml+xml": { + "source": "iana", + "extensions": ["ccxml"] + }, + "application/cdfx+xml": { + "source": "iana" + }, + "application/cdmi-capability": { + "source": "iana", + "extensions": ["cdmia"] + }, + "application/cdmi-container": { + "source": "iana", + "extensions": ["cdmic"] + }, + "application/cdmi-domain": { + "source": "iana", + "extensions": ["cdmid"] + }, + "application/cdmi-object": { + "source": "iana", + "extensions": ["cdmio"] + }, + "application/cdmi-queue": { + "source": "iana", + "extensions": ["cdmiq"] + }, + "application/cdni": { + "source": "iana" + }, + "application/cea": { + "source": "iana" + }, + "application/cea-2018+xml": { + "source": "iana" + }, + "application/cellml+xml": { + "source": "iana" + }, + "application/cfw": { + "source": "iana" + }, + "application/cms": { + "source": "iana" + }, + "application/cnrp+xml": { + "source": "iana" + }, + "application/coap-group+json": { + "source": "iana", + "compressible": true + }, + "application/commonground": { + "source": "iana" + }, + "application/conference-info+xml": { + "source": "iana" + }, + "application/cpl+xml": { + "source": "iana" + }, + "application/csrattrs": { + "source": "iana" + }, + "application/csta+xml": { + "source": "iana" + }, + "application/cstadata+xml": { + "source": "iana" + }, + "application/csvm+json": { + "source": "iana", + "compressible": true + }, + "application/cu-seeme": { + "source": "apache", + "extensions": ["cu"] + }, + "application/cybercash": { + "source": "iana" + }, + "application/dart": { + "compressible": true + }, + "application/dash+xml": { + "source": "iana", + "extensions": ["mdp"] + }, + "application/dashdelta": { + "source": "iana" + }, + "application/davmount+xml": { + "source": "iana", + "extensions": ["davmount"] + }, + "application/dca-rft": { + "source": "iana" + }, + "application/dcd": { + "source": "iana" + }, + "application/dec-dx": { + "source": "iana" + }, + "application/dialog-info+xml": { + "source": "iana" + }, + "application/dicom": { + "source": "iana" + }, + "application/dii": { + "source": "iana" + }, + "application/dit": { + "source": "iana" + }, + "application/dns": { + "source": "iana" + }, + "application/docbook+xml": { + "source": "apache", + "extensions": ["dbk"] + }, + "application/dskpp+xml": { + "source": "iana" + }, + "application/dssc+der": { + "source": "iana", + "extensions": ["dssc"] + }, + "application/dssc+xml": { + "source": "iana", + "extensions": ["xdssc"] + }, + "application/dvcs": { + "source": "iana" + }, + "application/ecmascript": { + "source": "iana", + "compressible": true, + "extensions": ["ecma"] + }, + "application/edi-consent": { + "source": "iana" + }, + "application/edi-x12": { + "source": "iana", + "compressible": false + }, + "application/edifact": { + "source": "iana", + "compressible": false + }, + "application/emma+xml": { + "source": "iana", + "extensions": ["emma"] + }, + "application/emotionml+xml": { + "source": "iana" + }, + "application/encaprtp": { + "source": "iana" + }, + "application/epp+xml": { + "source": "iana" + }, + "application/epub+zip": { + "source": "iana", + "extensions": ["epub"] + }, + "application/eshop": { + "source": "iana" + }, + "application/exi": { + "source": "iana", + "extensions": ["exi"] + }, + "application/fastinfoset": { + "source": "iana" + }, + "application/fastsoap": { + "source": "iana" + }, + "application/fdt+xml": { + "source": "iana" + }, + "application/fits": { + "source": "iana" + }, + "application/font-sfnt": { + "source": "iana" + }, + "application/font-tdpfr": { + "source": "iana", + "extensions": ["pfr"] + }, + "application/font-woff": { + "source": "iana", + "compressible": false, + "extensions": ["woff"] + }, + "application/font-woff2": { + "compressible": false, + "extensions": ["woff2"] + }, + "application/framework-attributes+xml": { + "source": "iana" + }, + "application/gml+xml": { + "source": "apache", + "extensions": ["gml"] + }, + "application/gpx+xml": { + "source": "apache", + "extensions": ["gpx"] + }, + "application/gxf": { + "source": "apache", + "extensions": ["gxf"] + }, + "application/gzip": { + "source": "iana", + "compressible": false + }, + "application/h224": { + "source": "iana" + }, + "application/held+xml": { + "source": "iana" + }, + "application/http": { + "source": "iana" + }, + "application/hyperstudio": { + "source": "iana", + "extensions": ["stk"] + }, + "application/ibe-key-request+xml": { + "source": "iana" + }, + "application/ibe-pkg-reply+xml": { + "source": "iana" + }, + "application/ibe-pp-data": { + "source": "iana" + }, + "application/iges": { + "source": "iana" + }, + "application/im-iscomposing+xml": { + "source": "iana" + }, + "application/index": { + "source": "iana" + }, + "application/index.cmd": { + "source": "iana" + }, + "application/index.obj": { + "source": "iana" + }, + "application/index.response": { + "source": "iana" + }, + "application/index.vnd": { + "source": "iana" + }, + "application/inkml+xml": { + "source": "iana", + "extensions": ["ink","inkml"] + }, + "application/iotp": { + "source": "iana" + }, + "application/ipfix": { + "source": "iana", + "extensions": ["ipfix"] + }, + "application/ipp": { + "source": "iana" + }, + "application/isup": { + "source": "iana" + }, + "application/its+xml": { + "source": "iana" + }, + "application/java-archive": { + "source": "apache", + "compressible": false, + "extensions": ["jar","war","ear"] + }, + "application/java-serialized-object": { + "source": "apache", + "compressible": false, + "extensions": ["ser"] + }, + "application/java-vm": { + "source": "apache", + "compressible": false, + "extensions": ["class"] + }, + "application/javascript": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["js"] + }, + "application/jose": { + "source": "iana" + }, + "application/jose+json": { + "source": "iana", + "compressible": true + }, + "application/jrd+json": { + "source": "iana", + "compressible": true + }, + "application/json": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["json","map"] + }, + "application/json-patch+json": { + "source": "iana", + "compressible": true + }, + "application/json-seq": { + "source": "iana" + }, + "application/json5": { + "extensions": ["json5"] + }, + "application/jsonml+json": { + "source": "apache", + "compressible": true, + "extensions": ["jsonml"] + }, + "application/jwk+json": { + "source": "iana", + "compressible": true + }, + "application/jwk-set+json": { + "source": "iana", + "compressible": true + }, + "application/jwt": { + "source": "iana" + }, + "application/kpml-request+xml": { + "source": "iana" + }, + "application/kpml-response+xml": { + "source": "iana" + }, + "application/ld+json": { + "source": "iana", + "compressible": true, + "extensions": ["jsonld"] + }, + "application/link-format": { + "source": "iana" + }, + "application/load-control+xml": { + "source": "iana" + }, + "application/lost+xml": { + "source": "iana", + "extensions": ["lostxml"] + }, + "application/lostsync+xml": { + "source": "iana" + }, + "application/lxf": { + "source": "iana" + }, + "application/mac-binhex40": { + "source": "iana", + "extensions": ["hqx"] + }, + "application/mac-compactpro": { + "source": "apache", + "extensions": ["cpt"] + }, + "application/macwriteii": { + "source": "iana" + }, + "application/mads+xml": { + "source": "iana", + "extensions": ["mads"] + }, + "application/manifest+json": { + "charset": "UTF-8", + "compressible": true, + "extensions": ["webmanifest"] + }, + "application/marc": { + "source": "iana", + "extensions": ["mrc"] + }, + "application/marcxml+xml": { + "source": "iana", + "extensions": ["mrcx"] + }, + "application/mathematica": { + "source": "iana", + "extensions": ["ma","nb","mb"] + }, + "application/mathml+xml": { + "source": "iana", + "extensions": ["mathml"] + }, + "application/mathml-content+xml": { + "source": "iana" + }, + "application/mathml-presentation+xml": { + "source": "iana" + }, + "application/mbms-associated-procedure-description+xml": { + "source": "iana" + }, + "application/mbms-deregister+xml": { + "source": "iana" + }, + "application/mbms-envelope+xml": { + "source": "iana" + }, + "application/mbms-msk+xml": { + "source": "iana" + }, + "application/mbms-msk-response+xml": { + "source": "iana" + }, + "application/mbms-protection-description+xml": { + "source": "iana" + }, + "application/mbms-reception-report+xml": { + "source": "iana" + }, + "application/mbms-register+xml": { + "source": "iana" + }, + "application/mbms-register-response+xml": { + "source": "iana" + }, + "application/mbms-schedule+xml": { + "source": "iana" + }, + "application/mbms-user-service-description+xml": { + "source": "iana" + }, + "application/mbox": { + "source": "iana", + "extensions": ["mbox"] + }, + "application/media-policy-dataset+xml": { + "source": "iana" + }, + "application/media_control+xml": { + "source": "iana" + }, + "application/mediaservercontrol+xml": { + "source": "iana", + "extensions": ["mscml"] + }, + "application/merge-patch+json": { + "source": "iana", + "compressible": true + }, + "application/metalink+xml": { + "source": "apache", + "extensions": ["metalink"] + }, + "application/metalink4+xml": { + "source": "iana", + "extensions": ["meta4"] + }, + "application/mets+xml": { + "source": "iana", + "extensions": ["mets"] + }, + "application/mf4": { + "source": "iana" + }, + "application/mikey": { + "source": "iana" + }, + "application/mods+xml": { + "source": "iana", + "extensions": ["mods"] + }, + "application/moss-keys": { + "source": "iana" + }, + "application/moss-signature": { + "source": "iana" + }, + "application/mosskey-data": { + "source": "iana" + }, + "application/mosskey-request": { + "source": "iana" + }, + "application/mp21": { + "source": "iana", + "extensions": ["m21","mp21"] + }, + "application/mp4": { + "source": "iana", + "extensions": ["mp4s","m4p"] + }, + "application/mpeg4-generic": { + "source": "iana" + }, + "application/mpeg4-iod": { + "source": "iana" + }, + "application/mpeg4-iod-xmt": { + "source": "iana" + }, + "application/mrb-consumer+xml": { + "source": "iana" + }, + "application/mrb-publish+xml": { + "source": "iana" + }, + "application/msc-ivr+xml": { + "source": "iana" + }, + "application/msc-mixer+xml": { + "source": "iana" + }, + "application/msword": { + "source": "iana", + "compressible": false, + "extensions": ["doc","dot"] + }, + "application/mxf": { + "source": "iana", + "extensions": ["mxf"] + }, + "application/nasdata": { + "source": "iana" + }, + "application/news-checkgroups": { + "source": "iana" + }, + "application/news-groupinfo": { + "source": "iana" + }, + "application/news-transmission": { + "source": "iana" + }, + "application/nlsml+xml": { + "source": "iana" + }, + "application/nss": { + "source": "iana" + }, + "application/ocsp-request": { + "source": "iana" + }, + "application/ocsp-response": { + "source": "iana" + }, + "application/octet-stream": { + "source": "iana", + "compressible": false, + "extensions": ["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","exe","dll","deb","dmg","iso","img","msi","msp","msm","buffer"] + }, + "application/oda": { + "source": "iana", + "extensions": ["oda"] + }, + "application/odx": { + "source": "iana" + }, + "application/oebps-package+xml": { + "source": "iana", + "extensions": ["opf"] + }, + "application/ogg": { + "source": "iana", + "compressible": false, + "extensions": ["ogx"] + }, + "application/omdoc+xml": { + "source": "apache", + "extensions": ["omdoc"] + }, + "application/onenote": { + "source": "apache", + "extensions": ["onetoc","onetoc2","onetmp","onepkg"] + }, + "application/oxps": { + "source": "iana", + "extensions": ["oxps"] + }, + "application/p2p-overlay+xml": { + "source": "iana" + }, + "application/parityfec": { + "source": "iana" + }, + "application/patch-ops-error+xml": { + "source": "iana", + "extensions": ["xer"] + }, + "application/pdf": { + "source": "iana", + "compressible": false, + "extensions": ["pdf"] + }, + "application/pdx": { + "source": "iana" + }, + "application/pgp-encrypted": { + "source": "iana", + "compressible": false, + "extensions": ["pgp"] + }, + "application/pgp-keys": { + "source": "iana" + }, + "application/pgp-signature": { + "source": "iana", + "extensions": ["asc","sig"] + }, + "application/pics-rules": { + "source": "apache", + "extensions": ["prf"] + }, + "application/pidf+xml": { + "source": "iana" + }, + "application/pidf-diff+xml": { + "source": "iana" + }, + "application/pkcs10": { + "source": "iana", + "extensions": ["p10"] + }, + "application/pkcs12": { + "source": "iana" + }, + "application/pkcs7-mime": { + "source": "iana", + "extensions": ["p7m","p7c"] + }, + "application/pkcs7-signature": { + "source": "iana", + "extensions": ["p7s"] + }, + "application/pkcs8": { + "source": "iana", + "extensions": ["p8"] + }, + "application/pkix-attr-cert": { + "source": "iana", + "extensions": ["ac"] + }, + "application/pkix-cert": { + "source": "iana", + "extensions": ["cer"] + }, + "application/pkix-crl": { + "source": "iana", + "extensions": ["crl"] + }, + "application/pkix-pkipath": { + "source": "iana", + "extensions": ["pkipath"] + }, + "application/pkixcmp": { + "source": "iana", + "extensions": ["pki"] + }, + "application/pls+xml": { + "source": "iana", + "extensions": ["pls"] + }, + "application/poc-settings+xml": { + "source": "iana" + }, + "application/postscript": { + "source": "iana", + "compressible": true, + "extensions": ["ai","eps","ps"] + }, + "application/provenance+xml": { + "source": "iana" + }, + "application/prs.alvestrand.titrax-sheet": { + "source": "iana" + }, + "application/prs.cww": { + "source": "iana", + "extensions": ["cww"] + }, + "application/prs.hpub+zip": { + "source": "iana" + }, + "application/prs.nprend": { + "source": "iana" + }, + "application/prs.plucker": { + "source": "iana" + }, + "application/prs.rdf-xml-crypt": { + "source": "iana" + }, + "application/prs.xsf+xml": { + "source": "iana" + }, + "application/pskc+xml": { + "source": "iana", + "extensions": ["pskcxml"] + }, + "application/qsig": { + "source": "iana" + }, + "application/raptorfec": { + "source": "iana" + }, + "application/rdap+json": { + "source": "iana", + "compressible": true + }, + "application/rdf+xml": { + "source": "iana", + "compressible": true, + "extensions": ["rdf"] + }, + "application/reginfo+xml": { + "source": "iana", + "extensions": ["rif"] + }, + "application/relax-ng-compact-syntax": { + "source": "iana", + "extensions": ["rnc"] + }, + "application/remote-printing": { + "source": "iana" + }, + "application/reputon+json": { + "source": "iana", + "compressible": true + }, + "application/resource-lists+xml": { + "source": "iana", + "extensions": ["rl"] + }, + "application/resource-lists-diff+xml": { + "source": "iana", + "extensions": ["rld"] + }, + "application/rfc+xml": { + "source": "iana" + }, + "application/riscos": { + "source": "iana" + }, + "application/rlmi+xml": { + "source": "iana" + }, + "application/rls-services+xml": { + "source": "iana", + "extensions": ["rs"] + }, + "application/rpki-ghostbusters": { + "source": "iana", + "extensions": ["gbr"] + }, + "application/rpki-manifest": { + "source": "iana", + "extensions": ["mft"] + }, + "application/rpki-roa": { + "source": "iana", + "extensions": ["roa"] + }, + "application/rpki-updown": { + "source": "iana" + }, + "application/rsd+xml": { + "source": "apache", + "extensions": ["rsd"] + }, + "application/rss+xml": { + "source": "apache", + "compressible": true, + "extensions": ["rss"] + }, + "application/rtf": { + "source": "iana", + "compressible": true, + "extensions": ["rtf"] + }, + "application/rtploopback": { + "source": "iana" + }, + "application/rtx": { + "source": "iana" + }, + "application/samlassertion+xml": { + "source": "iana" + }, + "application/samlmetadata+xml": { + "source": "iana" + }, + "application/sbml+xml": { + "source": "iana", + "extensions": ["sbml"] + }, + "application/scaip+xml": { + "source": "iana" + }, + "application/scim+json": { + "source": "iana", + "compressible": true + }, + "application/scvp-cv-request": { + "source": "iana", + "extensions": ["scq"] + }, + "application/scvp-cv-response": { + "source": "iana", + "extensions": ["scs"] + }, + "application/scvp-vp-request": { + "source": "iana", + "extensions": ["spq"] + }, + "application/scvp-vp-response": { + "source": "iana", + "extensions": ["spp"] + }, + "application/sdp": { + "source": "iana", + "extensions": ["sdp"] + }, + "application/sep+xml": { + "source": "iana" + }, + "application/sep-exi": { + "source": "iana" + }, + "application/session-info": { + "source": "iana" + }, + "application/set-payment": { + "source": "iana" + }, + "application/set-payment-initiation": { + "source": "iana", + "extensions": ["setpay"] + }, + "application/set-registration": { + "source": "iana" + }, + "application/set-registration-initiation": { + "source": "iana", + "extensions": ["setreg"] + }, + "application/sgml": { + "source": "iana" + }, + "application/sgml-open-catalog": { + "source": "iana" + }, + "application/shf+xml": { + "source": "iana", + "extensions": ["shf"] + }, + "application/sieve": { + "source": "iana" + }, + "application/simple-filter+xml": { + "source": "iana" + }, + "application/simple-message-summary": { + "source": "iana" + }, + "application/simplesymbolcontainer": { + "source": "iana" + }, + "application/slate": { + "source": "iana" + }, + "application/smil": { + "source": "iana" + }, + "application/smil+xml": { + "source": "iana", + "extensions": ["smi","smil"] + }, + "application/smpte336m": { + "source": "iana" + }, + "application/soap+fastinfoset": { + "source": "iana" + }, + "application/soap+xml": { + "source": "iana", + "compressible": true + }, + "application/sparql-query": { + "source": "iana", + "extensions": ["rq"] + }, + "application/sparql-results+xml": { + "source": "iana", + "extensions": ["srx"] + }, + "application/spirits-event+xml": { + "source": "iana" + }, + "application/sql": { + "source": "iana" + }, + "application/srgs": { + "source": "iana", + "extensions": ["gram"] + }, + "application/srgs+xml": { + "source": "iana", + "extensions": ["grxml"] + }, + "application/sru+xml": { + "source": "iana", + "extensions": ["sru"] + }, + "application/ssdl+xml": { + "source": "apache", + "extensions": ["ssdl"] + }, + "application/ssml+xml": { + "source": "iana", + "extensions": ["ssml"] + }, + "application/tamp-apex-update": { + "source": "iana" + }, + "application/tamp-apex-update-confirm": { + "source": "iana" + }, + "application/tamp-community-update": { + "source": "iana" + }, + "application/tamp-community-update-confirm": { + "source": "iana" + }, + "application/tamp-error": { + "source": "iana" + }, + "application/tamp-sequence-adjust": { + "source": "iana" + }, + "application/tamp-sequence-adjust-confirm": { + "source": "iana" + }, + "application/tamp-status-query": { + "source": "iana" + }, + "application/tamp-status-response": { + "source": "iana" + }, + "application/tamp-update": { + "source": "iana" + }, + "application/tamp-update-confirm": { + "source": "iana" + }, + "application/tar": { + "compressible": true + }, + "application/tei+xml": { + "source": "iana", + "extensions": ["tei","teicorpus"] + }, + "application/thraud+xml": { + "source": "iana", + "extensions": ["tfi"] + }, + "application/timestamp-query": { + "source": "iana" + }, + "application/timestamp-reply": { + "source": "iana" + }, + "application/timestamped-data": { + "source": "iana", + "extensions": ["tsd"] + }, + "application/ttml+xml": { + "source": "iana" + }, + "application/tve-trigger": { + "source": "iana" + }, + "application/ulpfec": { + "source": "iana" + }, + "application/urc-grpsheet+xml": { + "source": "iana" + }, + "application/urc-ressheet+xml": { + "source": "iana" + }, + "application/urc-targetdesc+xml": { + "source": "iana" + }, + "application/urc-uisocketdesc+xml": { + "source": "iana" + }, + "application/vcard+json": { + "source": "iana", + "compressible": true + }, + "application/vcard+xml": { + "source": "iana" + }, + "application/vemmi": { + "source": "iana" + }, + "application/vividence.scriptfile": { + "source": "apache" + }, + "application/vnd.3gpp-prose+xml": { + "source": "iana" + }, + "application/vnd.3gpp-prose-pc3ch+xml": { + "source": "iana" + }, + "application/vnd.3gpp.access-transfer-events+xml": { + "source": "iana" + }, + "application/vnd.3gpp.bsf+xml": { + "source": "iana" + }, + "application/vnd.3gpp.mid-call+xml": { + "source": "iana" + }, + "application/vnd.3gpp.pic-bw-large": { + "source": "iana", + "extensions": ["plb"] + }, + "application/vnd.3gpp.pic-bw-small": { + "source": "iana", + "extensions": ["psb"] + }, + "application/vnd.3gpp.pic-bw-var": { + "source": "iana", + "extensions": ["pvb"] + }, + "application/vnd.3gpp.sms": { + "source": "iana" + }, + "application/vnd.3gpp.srvcc-ext+xml": { + "source": "iana" + }, + "application/vnd.3gpp.srvcc-info+xml": { + "source": "iana" + }, + "application/vnd.3gpp.state-and-event-info+xml": { + "source": "iana" + }, + "application/vnd.3gpp.ussd+xml": { + "source": "iana" + }, + "application/vnd.3gpp2.bcmcsinfo+xml": { + "source": "iana" + }, + "application/vnd.3gpp2.sms": { + "source": "iana" + }, + "application/vnd.3gpp2.tcap": { + "source": "iana", + "extensions": ["tcap"] + }, + "application/vnd.3m.post-it-notes": { + "source": "iana", + "extensions": ["pwn"] + }, + "application/vnd.accpac.simply.aso": { + "source": "iana", + "extensions": ["aso"] + }, + "application/vnd.accpac.simply.imp": { + "source": "iana", + "extensions": ["imp"] + }, + "application/vnd.acucobol": { + "source": "iana", + "extensions": ["acu"] + }, + "application/vnd.acucorp": { + "source": "iana", + "extensions": ["atc","acutc"] + }, + "application/vnd.adobe.air-application-installer-package+zip": { + "source": "apache", + "extensions": ["air"] + }, + "application/vnd.adobe.flash.movie": { + "source": "iana" + }, + "application/vnd.adobe.formscentral.fcdt": { + "source": "iana", + "extensions": ["fcdt"] + }, + "application/vnd.adobe.fxp": { + "source": "iana", + "extensions": ["fxp","fxpl"] + }, + "application/vnd.adobe.partial-upload": { + "source": "iana" + }, + "application/vnd.adobe.xdp+xml": { + "source": "iana", + "extensions": ["xdp"] + }, + "application/vnd.adobe.xfdf": { + "source": "iana", + "extensions": ["xfdf"] + }, + "application/vnd.aether.imp": { + "source": "iana" + }, + "application/vnd.ah-barcode": { + "source": "iana" + }, + "application/vnd.ahead.space": { + "source": "iana", + "extensions": ["ahead"] + }, + "application/vnd.airzip.filesecure.azf": { + "source": "iana", + "extensions": ["azf"] + }, + "application/vnd.airzip.filesecure.azs": { + "source": "iana", + "extensions": ["azs"] + }, + "application/vnd.amazon.ebook": { + "source": "apache", + "extensions": ["azw"] + }, + "application/vnd.americandynamics.acc": { + "source": "iana", + "extensions": ["acc"] + }, + "application/vnd.amiga.ami": { + "source": "iana", + "extensions": ["ami"] + }, + "application/vnd.amundsen.maze+xml": { + "source": "iana" + }, + "application/vnd.android.package-archive": { + "source": "apache", + "compressible": false, + "extensions": ["apk"] + }, + "application/vnd.anki": { + "source": "iana" + }, + "application/vnd.anser-web-certificate-issue-initiation": { + "source": "iana", + "extensions": ["cii"] + }, + "application/vnd.anser-web-funds-transfer-initiation": { + "source": "apache", + "extensions": ["fti"] + }, + "application/vnd.antix.game-component": { + "source": "iana", + "extensions": ["atx"] + }, + "application/vnd.apache.thrift.binary": { + "source": "iana" + }, + "application/vnd.apache.thrift.compact": { + "source": "iana" + }, + "application/vnd.apache.thrift.json": { + "source": "iana" + }, + "application/vnd.api+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.apple.installer+xml": { + "source": "iana", + "extensions": ["mpkg"] + }, + "application/vnd.apple.mpegurl": { + "source": "iana", + "extensions": ["m3u8"] + }, + "application/vnd.apple.pkpass": { + "compressible": false, + "extensions": ["pkpass"] + }, + "application/vnd.arastra.swi": { + "source": "iana" + }, + "application/vnd.aristanetworks.swi": { + "source": "iana", + "extensions": ["swi"] + }, + "application/vnd.artsquare": { + "source": "iana" + }, + "application/vnd.astraea-software.iota": { + "source": "iana", + "extensions": ["iota"] + }, + "application/vnd.audiograph": { + "source": "iana", + "extensions": ["aep"] + }, + "application/vnd.autopackage": { + "source": "iana" + }, + "application/vnd.avistar+xml": { + "source": "iana" + }, + "application/vnd.balsamiq.bmml+xml": { + "source": "iana" + }, + "application/vnd.balsamiq.bmpr": { + "source": "iana" + }, + "application/vnd.bekitzur-stech+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.biopax.rdf+xml": { + "source": "iana" + }, + "application/vnd.blueice.multipass": { + "source": "iana", + "extensions": ["mpm"] + }, + "application/vnd.bluetooth.ep.oob": { + "source": "iana" + }, + "application/vnd.bluetooth.le.oob": { + "source": "iana" + }, + "application/vnd.bmi": { + "source": "iana", + "extensions": ["bmi"] + }, + "application/vnd.businessobjects": { + "source": "iana", + "extensions": ["rep"] + }, + "application/vnd.cab-jscript": { + "source": "iana" + }, + "application/vnd.canon-cpdl": { + "source": "iana" + }, + "application/vnd.canon-lips": { + "source": "iana" + }, + "application/vnd.cendio.thinlinc.clientconf": { + "source": "iana" + }, + "application/vnd.century-systems.tcp_stream": { + "source": "iana" + }, + "application/vnd.chemdraw+xml": { + "source": "iana", + "extensions": ["cdxml"] + }, + "application/vnd.chipnuts.karaoke-mmd": { + "source": "iana", + "extensions": ["mmd"] + }, + "application/vnd.cinderella": { + "source": "iana", + "extensions": ["cdy"] + }, + "application/vnd.cirpack.isdn-ext": { + "source": "iana" + }, + "application/vnd.citationstyles.style+xml": { + "source": "iana" + }, + "application/vnd.claymore": { + "source": "iana", + "extensions": ["cla"] + }, + "application/vnd.cloanto.rp9": { + "source": "iana", + "extensions": ["rp9"] + }, + "application/vnd.clonk.c4group": { + "source": "iana", + "extensions": ["c4g","c4d","c4f","c4p","c4u"] + }, + "application/vnd.cluetrust.cartomobile-config": { + "source": "iana", + "extensions": ["c11amc"] + }, + "application/vnd.cluetrust.cartomobile-config-pkg": { + "source": "iana", + "extensions": ["c11amz"] + }, + "application/vnd.coffeescript": { + "source": "iana" + }, + "application/vnd.collection+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.collection.doc+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.collection.next+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.commerce-battelle": { + "source": "iana" + }, + "application/vnd.commonspace": { + "source": "iana", + "extensions": ["csp"] + }, + "application/vnd.contact.cmsg": { + "source": "iana", + "extensions": ["cdbcmsg"] + }, + "application/vnd.cosmocaller": { + "source": "iana", + "extensions": ["cmc"] + }, + "application/vnd.crick.clicker": { + "source": "iana", + "extensions": ["clkx"] + }, + "application/vnd.crick.clicker.keyboard": { + "source": "iana", + "extensions": ["clkk"] + }, + "application/vnd.crick.clicker.palette": { + "source": "iana", + "extensions": ["clkp"] + }, + "application/vnd.crick.clicker.template": { + "source": "iana", + "extensions": ["clkt"] + }, + "application/vnd.crick.clicker.wordbank": { + "source": "iana", + "extensions": ["clkw"] + }, + "application/vnd.criticaltools.wbs+xml": { + "source": "iana", + "extensions": ["wbs"] + }, + "application/vnd.ctc-posml": { + "source": "iana", + "extensions": ["pml"] + }, + "application/vnd.ctct.ws+xml": { + "source": "iana" + }, + "application/vnd.cups-pdf": { + "source": "iana" + }, + "application/vnd.cups-postscript": { + "source": "iana" + }, + "application/vnd.cups-ppd": { + "source": "iana", + "extensions": ["ppd"] + }, + "application/vnd.cups-raster": { + "source": "iana" + }, + "application/vnd.cups-raw": { + "source": "iana" + }, + "application/vnd.curl": { + "source": "iana" + }, + "application/vnd.curl.car": { + "source": "apache", + "extensions": ["car"] + }, + "application/vnd.curl.pcurl": { + "source": "apache", + "extensions": ["pcurl"] + }, + "application/vnd.cyan.dean.root+xml": { + "source": "iana" + }, + "application/vnd.cybank": { + "source": "iana" + }, + "application/vnd.dart": { + "source": "iana", + "compressible": true, + "extensions": ["dart"] + }, + "application/vnd.data-vision.rdz": { + "source": "iana", + "extensions": ["rdz"] + }, + "application/vnd.debian.binary-package": { + "source": "iana" + }, + "application/vnd.dece.data": { + "source": "iana", + "extensions": ["uvf","uvvf","uvd","uvvd"] + }, + "application/vnd.dece.ttml+xml": { + "source": "iana", + "extensions": ["uvt","uvvt"] + }, + "application/vnd.dece.unspecified": { + "source": "iana", + "extensions": ["uvx","uvvx"] + }, + "application/vnd.dece.zip": { + "source": "iana", + "extensions": ["uvz","uvvz"] + }, + "application/vnd.denovo.fcselayout-link": { + "source": "iana", + "extensions": ["fe_launch"] + }, + "application/vnd.desmume-movie": { + "source": "iana" + }, + "application/vnd.dir-bi.plate-dl-nosuffix": { + "source": "iana" + }, + "application/vnd.dm.delegation+xml": { + "source": "iana" + }, + "application/vnd.dna": { + "source": "iana", + "extensions": ["dna"] + }, + "application/vnd.document+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.dolby.mlp": { + "source": "apache", + "extensions": ["mlp"] + }, + "application/vnd.dolby.mobile.1": { + "source": "iana" + }, + "application/vnd.dolby.mobile.2": { + "source": "iana" + }, + "application/vnd.doremir.scorecloud-binary-document": { + "source": "iana" + }, + "application/vnd.dpgraph": { + "source": "iana", + "extensions": ["dpg"] + }, + "application/vnd.dreamfactory": { + "source": "iana", + "extensions": ["dfac"] + }, + "application/vnd.drive+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ds-keypoint": { + "source": "apache", + "extensions": ["kpxx"] + }, + "application/vnd.dtg.local": { + "source": "iana" + }, + "application/vnd.dtg.local.flash": { + "source": "iana" + }, + "application/vnd.dtg.local.html": { + "source": "iana" + }, + "application/vnd.dvb.ait": { + "source": "iana", + "extensions": ["ait"] + }, + "application/vnd.dvb.dvbj": { + "source": "iana" + }, + "application/vnd.dvb.esgcontainer": { + "source": "iana" + }, + "application/vnd.dvb.ipdcdftnotifaccess": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgaccess": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgaccess2": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgpdd": { + "source": "iana" + }, + "application/vnd.dvb.ipdcroaming": { + "source": "iana" + }, + "application/vnd.dvb.iptv.alfec-base": { + "source": "iana" + }, + "application/vnd.dvb.iptv.alfec-enhancement": { + "source": "iana" + }, + "application/vnd.dvb.notif-aggregate-root+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-container+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-generic+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-ia-msglist+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-ia-registration-request+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-ia-registration-response+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-init+xml": { + "source": "iana" + }, + "application/vnd.dvb.pfr": { + "source": "iana" + }, + "application/vnd.dvb.service": { + "source": "iana", + "extensions": ["svc"] + }, + "application/vnd.dxr": { + "source": "iana" + }, + "application/vnd.dynageo": { + "source": "iana", + "extensions": ["geo"] + }, + "application/vnd.dzr": { + "source": "iana" + }, + "application/vnd.easykaraoke.cdgdownload": { + "source": "iana" + }, + "application/vnd.ecdis-update": { + "source": "iana" + }, + "application/vnd.ecowin.chart": { + "source": "iana", + "extensions": ["mag"] + }, + "application/vnd.ecowin.filerequest": { + "source": "iana" + }, + "application/vnd.ecowin.fileupdate": { + "source": "iana" + }, + "application/vnd.ecowin.series": { + "source": "iana" + }, + "application/vnd.ecowin.seriesrequest": { + "source": "iana" + }, + "application/vnd.ecowin.seriesupdate": { + "source": "iana" + }, + "application/vnd.emclient.accessrequest+xml": { + "source": "iana" + }, + "application/vnd.enliven": { + "source": "iana", + "extensions": ["nml"] + }, + "application/vnd.enphase.envoy": { + "source": "iana" + }, + "application/vnd.eprints.data+xml": { + "source": "iana" + }, + "application/vnd.epson.esf": { + "source": "iana", + "extensions": ["esf"] + }, + "application/vnd.epson.msf": { + "source": "iana", + "extensions": ["msf"] + }, + "application/vnd.epson.quickanime": { + "source": "iana", + "extensions": ["qam"] + }, + "application/vnd.epson.salt": { + "source": "iana", + "extensions": ["slt"] + }, + "application/vnd.epson.ssf": { + "source": "iana", + "extensions": ["ssf"] + }, + "application/vnd.ericsson.quickcall": { + "source": "iana" + }, + "application/vnd.eszigno3+xml": { + "source": "iana", + "extensions": ["es3","et3"] + }, + "application/vnd.etsi.aoc+xml": { + "source": "iana" + }, + "application/vnd.etsi.asic-e+zip": { + "source": "iana" + }, + "application/vnd.etsi.asic-s+zip": { + "source": "iana" + }, + "application/vnd.etsi.cug+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvcommand+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvdiscovery+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvprofile+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsad-bc+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsad-cod+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsad-npvr+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvservice+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsync+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvueprofile+xml": { + "source": "iana" + }, + "application/vnd.etsi.mcid+xml": { + "source": "iana" + }, + "application/vnd.etsi.mheg5": { + "source": "iana" + }, + "application/vnd.etsi.overload-control-policy-dataset+xml": { + "source": "iana" + }, + "application/vnd.etsi.pstn+xml": { + "source": "iana" + }, + "application/vnd.etsi.sci+xml": { + "source": "iana" + }, + "application/vnd.etsi.simservs+xml": { + "source": "iana" + }, + "application/vnd.etsi.timestamp-token": { + "source": "iana" + }, + "application/vnd.etsi.tsl+xml": { + "source": "iana" + }, + "application/vnd.etsi.tsl.der": { + "source": "iana" + }, + "application/vnd.eudora.data": { + "source": "iana" + }, + "application/vnd.ezpix-album": { + "source": "iana", + "extensions": ["ez2"] + }, + "application/vnd.ezpix-package": { + "source": "iana", + "extensions": ["ez3"] + }, + "application/vnd.f-secure.mobile": { + "source": "iana" + }, + "application/vnd.fastcopy-disk-image": { + "source": "iana" + }, + "application/vnd.fdf": { + "source": "iana", + "extensions": ["fdf"] + }, + "application/vnd.fdsn.mseed": { + "source": "iana", + "extensions": ["mseed"] + }, + "application/vnd.fdsn.seed": { + "source": "iana", + "extensions": ["seed","dataless"] + }, + "application/vnd.ffsns": { + "source": "iana" + }, + "application/vnd.fints": { + "source": "iana" + }, + "application/vnd.firemonkeys.cloudcell": { + "source": "iana" + }, + "application/vnd.flographit": { + "source": "iana", + "extensions": ["gph"] + }, + "application/vnd.fluxtime.clip": { + "source": "iana", + "extensions": ["ftc"] + }, + "application/vnd.font-fontforge-sfd": { + "source": "iana" + }, + "application/vnd.framemaker": { + "source": "iana", + "extensions": ["fm","frame","maker","book"] + }, + "application/vnd.frogans.fnc": { + "source": "iana", + "extensions": ["fnc"] + }, + "application/vnd.frogans.ltf": { + "source": "iana", + "extensions": ["ltf"] + }, + "application/vnd.fsc.weblaunch": { + "source": "iana", + "extensions": ["fsc"] + }, + "application/vnd.fujitsu.oasys": { + "source": "iana", + "extensions": ["oas"] + }, + "application/vnd.fujitsu.oasys2": { + "source": "iana", + "extensions": ["oa2"] + }, + "application/vnd.fujitsu.oasys3": { + "source": "iana", + "extensions": ["oa3"] + }, + "application/vnd.fujitsu.oasysgp": { + "source": "iana", + "extensions": ["fg5"] + }, + "application/vnd.fujitsu.oasysprs": { + "source": "iana", + "extensions": ["bh2"] + }, + "application/vnd.fujixerox.art-ex": { + "source": "iana" + }, + "application/vnd.fujixerox.art4": { + "source": "iana" + }, + "application/vnd.fujixerox.ddd": { + "source": "iana", + "extensions": ["ddd"] + }, + "application/vnd.fujixerox.docuworks": { + "source": "iana", + "extensions": ["xdw"] + }, + "application/vnd.fujixerox.docuworks.binder": { + "source": "iana", + "extensions": ["xbd"] + }, + "application/vnd.fujixerox.docuworks.container": { + "source": "iana" + }, + "application/vnd.fujixerox.hbpl": { + "source": "iana" + }, + "application/vnd.fut-misnet": { + "source": "iana" + }, + "application/vnd.fuzzysheet": { + "source": "iana", + "extensions": ["fzs"] + }, + "application/vnd.genomatix.tuxedo": { + "source": "iana", + "extensions": ["txd"] + }, + "application/vnd.geo+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.geocube+xml": { + "source": "iana" + }, + "application/vnd.geogebra.file": { + "source": "iana", + "extensions": ["ggb"] + }, + "application/vnd.geogebra.tool": { + "source": "iana", + "extensions": ["ggt"] + }, + "application/vnd.geometry-explorer": { + "source": "iana", + "extensions": ["gex","gre"] + }, + "application/vnd.geonext": { + "source": "iana", + "extensions": ["gxt"] + }, + "application/vnd.geoplan": { + "source": "iana", + "extensions": ["g2w"] + }, + "application/vnd.geospace": { + "source": "iana", + "extensions": ["g3w"] + }, + "application/vnd.gerber": { + "source": "iana" + }, + "application/vnd.globalplatform.card-content-mgt": { + "source": "iana" + }, + "application/vnd.globalplatform.card-content-mgt-response": { + "source": "iana" + }, + "application/vnd.gmx": { + "source": "iana", + "extensions": ["gmx"] + }, + "application/vnd.google-earth.kml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["kml"] + }, + "application/vnd.google-earth.kmz": { + "source": "iana", + "compressible": false, + "extensions": ["kmz"] + }, + "application/vnd.gov.sk.e-form+xml": { + "source": "iana" + }, + "application/vnd.gov.sk.e-form+zip": { + "source": "iana" + }, + "application/vnd.gov.sk.xmldatacontainer+xml": { + "source": "iana" + }, + "application/vnd.grafeq": { + "source": "iana", + "extensions": ["gqf","gqs"] + }, + "application/vnd.gridmp": { + "source": "iana" + }, + "application/vnd.groove-account": { + "source": "iana", + "extensions": ["gac"] + }, + "application/vnd.groove-help": { + "source": "iana", + "extensions": ["ghf"] + }, + "application/vnd.groove-identity-message": { + "source": "iana", + "extensions": ["gim"] + }, + "application/vnd.groove-injector": { + "source": "iana", + "extensions": ["grv"] + }, + "application/vnd.groove-tool-message": { + "source": "iana", + "extensions": ["gtm"] + }, + "application/vnd.groove-tool-template": { + "source": "iana", + "extensions": ["tpl"] + }, + "application/vnd.groove-vcard": { + "source": "iana", + "extensions": ["vcg"] + }, + "application/vnd.hal+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hal+xml": { + "source": "iana", + "extensions": ["hal"] + }, + "application/vnd.handheld-entertainment+xml": { + "source": "iana", + "extensions": ["zmm"] + }, + "application/vnd.hbci": { + "source": "iana", + "extensions": ["hbci"] + }, + "application/vnd.hcl-bireports": { + "source": "iana" + }, + "application/vnd.heroku+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hhe.lesson-player": { + "source": "iana", + "extensions": ["les"] + }, + "application/vnd.hp-hpgl": { + "source": "iana", + "extensions": ["hpgl"] + }, + "application/vnd.hp-hpid": { + "source": "iana", + "extensions": ["hpid"] + }, + "application/vnd.hp-hps": { + "source": "iana", + "extensions": ["hps"] + }, + "application/vnd.hp-jlyt": { + "source": "iana", + "extensions": ["jlt"] + }, + "application/vnd.hp-pcl": { + "source": "iana", + "extensions": ["pcl"] + }, + "application/vnd.hp-pclxl": { + "source": "iana", + "extensions": ["pclxl"] + }, + "application/vnd.httphone": { + "source": "iana" + }, + "application/vnd.hydrostatix.sof-data": { + "source": "iana", + "extensions": ["sfd-hdstx"] + }, + "application/vnd.hyperdrive+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hzn-3d-crossword": { + "source": "iana" + }, + "application/vnd.ibm.afplinedata": { + "source": "iana" + }, + "application/vnd.ibm.electronic-media": { + "source": "iana" + }, + "application/vnd.ibm.minipay": { + "source": "iana", + "extensions": ["mpy"] + }, + "application/vnd.ibm.modcap": { + "source": "iana", + "extensions": ["afp","listafp","list3820"] + }, + "application/vnd.ibm.rights-management": { + "source": "iana", + "extensions": ["irm"] + }, + "application/vnd.ibm.secure-container": { + "source": "iana", + "extensions": ["sc"] + }, + "application/vnd.iccprofile": { + "source": "iana", + "extensions": ["icc","icm"] + }, + "application/vnd.ieee.1905": { + "source": "iana" + }, + "application/vnd.igloader": { + "source": "iana", + "extensions": ["igl"] + }, + "application/vnd.immervision-ivp": { + "source": "iana", + "extensions": ["ivp"] + }, + "application/vnd.immervision-ivu": { + "source": "iana", + "extensions": ["ivu"] + }, + "application/vnd.ims.imsccv1p1": { + "source": "iana" + }, + "application/vnd.ims.imsccv1p2": { + "source": "iana" + }, + "application/vnd.ims.imsccv1p3": { + "source": "iana" + }, + "application/vnd.ims.lis.v2.result+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolconsumerprofile+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolproxy+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolproxy.id+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolsettings+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolsettings.simple+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.informedcontrol.rms+xml": { + "source": "iana" + }, + "application/vnd.informix-visionary": { + "source": "iana" + }, + "application/vnd.infotech.project": { + "source": "iana" + }, + "application/vnd.infotech.project+xml": { + "source": "iana" + }, + "application/vnd.innopath.wamp.notification": { + "source": "iana" + }, + "application/vnd.insors.igm": { + "source": "iana", + "extensions": ["igm"] + }, + "application/vnd.intercon.formnet": { + "source": "iana", + "extensions": ["xpw","xpx"] + }, + "application/vnd.intergeo": { + "source": "iana", + "extensions": ["i2g"] + }, + "application/vnd.intertrust.digibox": { + "source": "iana" + }, + "application/vnd.intertrust.nncp": { + "source": "iana" + }, + "application/vnd.intu.qbo": { + "source": "iana", + "extensions": ["qbo"] + }, + "application/vnd.intu.qfx": { + "source": "iana", + "extensions": ["qfx"] + }, + "application/vnd.iptc.g2.catalogitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.conceptitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.knowledgeitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.newsitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.newsmessage+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.packageitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.planningitem+xml": { + "source": "iana" + }, + "application/vnd.ipunplugged.rcprofile": { + "source": "iana", + "extensions": ["rcprofile"] + }, + "application/vnd.irepository.package+xml": { + "source": "iana", + "extensions": ["irp"] + }, + "application/vnd.is-xpr": { + "source": "iana", + "extensions": ["xpr"] + }, + "application/vnd.isac.fcs": { + "source": "iana", + "extensions": ["fcs"] + }, + "application/vnd.jam": { + "source": "iana", + "extensions": ["jam"] + }, + "application/vnd.japannet-directory-service": { + "source": "iana" + }, + "application/vnd.japannet-jpnstore-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-payment-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-registration": { + "source": "iana" + }, + "application/vnd.japannet-registration-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-setstore-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-verification": { + "source": "iana" + }, + "application/vnd.japannet-verification-wakeup": { + "source": "iana" + }, + "application/vnd.jcp.javame.midlet-rms": { + "source": "iana", + "extensions": ["rms"] + }, + "application/vnd.jisp": { + "source": "iana", + "extensions": ["jisp"] + }, + "application/vnd.joost.joda-archive": { + "source": "iana", + "extensions": ["joda"] + }, + "application/vnd.jsk.isdn-ngn": { + "source": "iana" + }, + "application/vnd.kahootz": { + "source": "iana", + "extensions": ["ktz","ktr"] + }, + "application/vnd.kde.karbon": { + "source": "iana", + "extensions": ["karbon"] + }, + "application/vnd.kde.kchart": { + "source": "iana", + "extensions": ["chrt"] + }, + "application/vnd.kde.kformula": { + "source": "iana", + "extensions": ["kfo"] + }, + "application/vnd.kde.kivio": { + "source": "iana", + "extensions": ["flw"] + }, + "application/vnd.kde.kontour": { + "source": "iana", + "extensions": ["kon"] + }, + "application/vnd.kde.kpresenter": { + "source": "iana", + "extensions": ["kpr","kpt"] + }, + "application/vnd.kde.kspread": { + "source": "iana", + "extensions": ["ksp"] + }, + "application/vnd.kde.kword": { + "source": "iana", + "extensions": ["kwd","kwt"] + }, + "application/vnd.kenameaapp": { + "source": "iana", + "extensions": ["htke"] + }, + "application/vnd.kidspiration": { + "source": "iana", + "extensions": ["kia"] + }, + "application/vnd.kinar": { + "source": "iana", + "extensions": ["kne","knp"] + }, + "application/vnd.koan": { + "source": "iana", + "extensions": ["skp","skd","skt","skm"] + }, + "application/vnd.kodak-descriptor": { + "source": "iana", + "extensions": ["sse"] + }, + "application/vnd.las.las+xml": { + "source": "iana", + "extensions": ["lasxml"] + }, + "application/vnd.liberty-request+xml": { + "source": "iana" + }, + "application/vnd.llamagraphics.life-balance.desktop": { + "source": "iana", + "extensions": ["lbd"] + }, + "application/vnd.llamagraphics.life-balance.exchange+xml": { + "source": "iana", + "extensions": ["lbe"] + }, + "application/vnd.lotus-1-2-3": { + "source": "iana", + "extensions": ["123"] + }, + "application/vnd.lotus-approach": { + "source": "iana", + "extensions": ["apr"] + }, + "application/vnd.lotus-freelance": { + "source": "iana", + "extensions": ["pre"] + }, + "application/vnd.lotus-notes": { + "source": "iana", + "extensions": ["nsf"] + }, + "application/vnd.lotus-organizer": { + "source": "iana", + "extensions": ["org"] + }, + "application/vnd.lotus-screencam": { + "source": "iana", + "extensions": ["scm"] + }, + "application/vnd.lotus-wordpro": { + "source": "iana", + "extensions": ["lwp"] + }, + "application/vnd.macports.portpkg": { + "source": "iana", + "extensions": ["portpkg"] + }, + "application/vnd.marlin.drm.actiontoken+xml": { + "source": "iana" + }, + "application/vnd.marlin.drm.conftoken+xml": { + "source": "iana" + }, + "application/vnd.marlin.drm.license+xml": { + "source": "iana" + }, + "application/vnd.marlin.drm.mdcf": { + "source": "iana" + }, + "application/vnd.mason+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.maxmind.maxmind-db": { + "source": "iana" + }, + "application/vnd.mcd": { + "source": "iana", + "extensions": ["mcd"] + }, + "application/vnd.medcalcdata": { + "source": "iana", + "extensions": ["mc1"] + }, + "application/vnd.mediastation.cdkey": { + "source": "iana", + "extensions": ["cdkey"] + }, + "application/vnd.meridian-slingshot": { + "source": "iana" + }, + "application/vnd.mfer": { + "source": "iana", + "extensions": ["mwf"] + }, + "application/vnd.mfmp": { + "source": "iana", + "extensions": ["mfm"] + }, + "application/vnd.micro+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.micrografx.flo": { + "source": "iana", + "extensions": ["flo"] + }, + "application/vnd.micrografx.igx": { + "source": "iana", + "extensions": ["igx"] + }, + "application/vnd.microsoft.portable-executable": { + "source": "iana" + }, + "application/vnd.miele+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.mif": { + "source": "iana", + "extensions": ["mif"] + }, + "application/vnd.minisoft-hp3000-save": { + "source": "iana" + }, + "application/vnd.mitsubishi.misty-guard.trustweb": { + "source": "iana" + }, + "application/vnd.mobius.daf": { + "source": "iana", + "extensions": ["daf"] + }, + "application/vnd.mobius.dis": { + "source": "iana", + "extensions": ["dis"] + }, + "application/vnd.mobius.mbk": { + "source": "iana", + "extensions": ["mbk"] + }, + "application/vnd.mobius.mqy": { + "source": "iana", + "extensions": ["mqy"] + }, + "application/vnd.mobius.msl": { + "source": "iana", + "extensions": ["msl"] + }, + "application/vnd.mobius.plc": { + "source": "iana", + "extensions": ["plc"] + }, + "application/vnd.mobius.txf": { + "source": "iana", + "extensions": ["txf"] + }, + "application/vnd.mophun.application": { + "source": "iana", + "extensions": ["mpn"] + }, + "application/vnd.mophun.certificate": { + "source": "iana", + "extensions": ["mpc"] + }, + "application/vnd.motorola.flexsuite": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.adsi": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.fis": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.gotap": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.kmr": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.ttc": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.wem": { + "source": "iana" + }, + "application/vnd.motorola.iprm": { + "source": "iana" + }, + "application/vnd.mozilla.xul+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xul"] + }, + "application/vnd.ms-3mfdocument": { + "source": "iana" + }, + "application/vnd.ms-artgalry": { + "source": "iana", + "extensions": ["cil"] + }, + "application/vnd.ms-asf": { + "source": "iana" + }, + "application/vnd.ms-cab-compressed": { + "source": "iana", + "extensions": ["cab"] + }, + "application/vnd.ms-color.iccprofile": { + "source": "apache" + }, + "application/vnd.ms-excel": { + "source": "iana", + "compressible": false, + "extensions": ["xls","xlm","xla","xlc","xlt","xlw"] + }, + "application/vnd.ms-excel.addin.macroenabled.12": { + "source": "iana", + "extensions": ["xlam"] + }, + "application/vnd.ms-excel.sheet.binary.macroenabled.12": { + "source": "iana", + "extensions": ["xlsb"] + }, + "application/vnd.ms-excel.sheet.macroenabled.12": { + "source": "iana", + "extensions": ["xlsm"] + }, + "application/vnd.ms-excel.template.macroenabled.12": { + "source": "iana", + "extensions": ["xltm"] + }, + "application/vnd.ms-fontobject": { + "source": "iana", + "compressible": true, + "extensions": ["eot"] + }, + "application/vnd.ms-htmlhelp": { + "source": "iana", + "extensions": ["chm"] + }, + "application/vnd.ms-ims": { + "source": "iana", + "extensions": ["ims"] + }, + "application/vnd.ms-lrm": { + "source": "iana", + "extensions": ["lrm"] + }, + "application/vnd.ms-office.activex+xml": { + "source": "iana" + }, + "application/vnd.ms-officetheme": { + "source": "iana", + "extensions": ["thmx"] + }, + "application/vnd.ms-opentype": { + "source": "apache", + "compressible": true + }, + "application/vnd.ms-package.obfuscated-opentype": { + "source": "apache" + }, + "application/vnd.ms-pki.seccat": { + "source": "apache", + "extensions": ["cat"] + }, + "application/vnd.ms-pki.stl": { + "source": "apache", + "extensions": ["stl"] + }, + "application/vnd.ms-playready.initiator+xml": { + "source": "iana" + }, + "application/vnd.ms-powerpoint": { + "source": "iana", + "compressible": false, + "extensions": ["ppt","pps","pot"] + }, + "application/vnd.ms-powerpoint.addin.macroenabled.12": { + "source": "iana", + "extensions": ["ppam"] + }, + "application/vnd.ms-powerpoint.presentation.macroenabled.12": { + "source": "iana", + "extensions": ["pptm"] + }, + "application/vnd.ms-powerpoint.slide.macroenabled.12": { + "source": "iana", + "extensions": ["sldm"] + }, + "application/vnd.ms-powerpoint.slideshow.macroenabled.12": { + "source": "iana", + "extensions": ["ppsm"] + }, + "application/vnd.ms-powerpoint.template.macroenabled.12": { + "source": "iana", + "extensions": ["potm"] + }, + "application/vnd.ms-printing.printticket+xml": { + "source": "apache" + }, + "application/vnd.ms-project": { + "source": "iana", + "extensions": ["mpp","mpt"] + }, + "application/vnd.ms-tnef": { + "source": "iana" + }, + "application/vnd.ms-windows.printerpairing": { + "source": "iana" + }, + "application/vnd.ms-windows.wsd.oob": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.lic-chlg-req": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.lic-resp": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.meter-chlg-req": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.meter-resp": { + "source": "iana" + }, + "application/vnd.ms-word.document.macroenabled.12": { + "source": "iana", + "extensions": ["docm"] + }, + "application/vnd.ms-word.template.macroenabled.12": { + "source": "iana", + "extensions": ["dotm"] + }, + "application/vnd.ms-works": { + "source": "iana", + "extensions": ["wps","wks","wcm","wdb"] + }, + "application/vnd.ms-wpl": { + "source": "iana", + "extensions": ["wpl"] + }, + "application/vnd.ms-xpsdocument": { + "source": "iana", + "compressible": false, + "extensions": ["xps"] + }, + "application/vnd.msa-disk-image": { + "source": "iana" + }, + "application/vnd.mseq": { + "source": "iana", + "extensions": ["mseq"] + }, + "application/vnd.msign": { + "source": "iana" + }, + "application/vnd.multiad.creator": { + "source": "iana" + }, + "application/vnd.multiad.creator.cif": { + "source": "iana" + }, + "application/vnd.music-niff": { + "source": "iana" + }, + "application/vnd.musician": { + "source": "iana", + "extensions": ["mus"] + }, + "application/vnd.muvee.style": { + "source": "iana", + "extensions": ["msty"] + }, + "application/vnd.mynfc": { + "source": "iana", + "extensions": ["taglet"] + }, + "application/vnd.ncd.control": { + "source": "iana" + }, + "application/vnd.ncd.reference": { + "source": "iana" + }, + "application/vnd.nervana": { + "source": "iana" + }, + "application/vnd.netfpx": { + "source": "iana" + }, + "application/vnd.neurolanguage.nlu": { + "source": "iana", + "extensions": ["nlu"] + }, + "application/vnd.nintendo.nitro.rom": { + "source": "iana" + }, + "application/vnd.nintendo.snes.rom": { + "source": "iana" + }, + "application/vnd.nitf": { + "source": "iana", + "extensions": ["ntf","nitf"] + }, + "application/vnd.noblenet-directory": { + "source": "iana", + "extensions": ["nnd"] + }, + "application/vnd.noblenet-sealer": { + "source": "iana", + "extensions": ["nns"] + }, + "application/vnd.noblenet-web": { + "source": "iana", + "extensions": ["nnw"] + }, + "application/vnd.nokia.catalogs": { + "source": "iana" + }, + "application/vnd.nokia.conml+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.conml+xml": { + "source": "iana" + }, + "application/vnd.nokia.iptv.config+xml": { + "source": "iana" + }, + "application/vnd.nokia.isds-radio-presets": { + "source": "iana" + }, + "application/vnd.nokia.landmark+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.landmark+xml": { + "source": "iana" + }, + "application/vnd.nokia.landmarkcollection+xml": { + "source": "iana" + }, + "application/vnd.nokia.n-gage.ac+xml": { + "source": "iana" + }, + "application/vnd.nokia.n-gage.data": { + "source": "iana", + "extensions": ["ngdat"] + }, + "application/vnd.nokia.n-gage.symbian.install": { + "source": "iana", + "extensions": ["n-gage"] + }, + "application/vnd.nokia.ncd": { + "source": "iana" + }, + "application/vnd.nokia.pcd+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.pcd+xml": { + "source": "iana" + }, + "application/vnd.nokia.radio-preset": { + "source": "iana", + "extensions": ["rpst"] + }, + "application/vnd.nokia.radio-presets": { + "source": "iana", + "extensions": ["rpss"] + }, + "application/vnd.novadigm.edm": { + "source": "iana", + "extensions": ["edm"] + }, + "application/vnd.novadigm.edx": { + "source": "iana", + "extensions": ["edx"] + }, + "application/vnd.novadigm.ext": { + "source": "iana", + "extensions": ["ext"] + }, + "application/vnd.ntt-local.content-share": { + "source": "iana" + }, + "application/vnd.ntt-local.file-transfer": { + "source": "iana" + }, + "application/vnd.ntt-local.ogw_remote-access": { + "source": "iana" + }, + "application/vnd.ntt-local.sip-ta_remote": { + "source": "iana" + }, + "application/vnd.ntt-local.sip-ta_tcp_stream": { + "source": "iana" + }, + "application/vnd.oasis.opendocument.chart": { + "source": "iana", + "extensions": ["odc"] + }, + "application/vnd.oasis.opendocument.chart-template": { + "source": "iana", + "extensions": ["otc"] + }, + "application/vnd.oasis.opendocument.database": { + "source": "iana", + "extensions": ["odb"] + }, + "application/vnd.oasis.opendocument.formula": { + "source": "iana", + "extensions": ["odf"] + }, + "application/vnd.oasis.opendocument.formula-template": { + "source": "iana", + "extensions": ["odft"] + }, + "application/vnd.oasis.opendocument.graphics": { + "source": "iana", + "compressible": false, + "extensions": ["odg"] + }, + "application/vnd.oasis.opendocument.graphics-template": { + "source": "iana", + "extensions": ["otg"] + }, + "application/vnd.oasis.opendocument.image": { + "source": "iana", + "extensions": ["odi"] + }, + "application/vnd.oasis.opendocument.image-template": { + "source": "iana", + "extensions": ["oti"] + }, + "application/vnd.oasis.opendocument.presentation": { + "source": "iana", + "compressible": false, + "extensions": ["odp"] + }, + "application/vnd.oasis.opendocument.presentation-template": { + "source": "iana", + "extensions": ["otp"] + }, + "application/vnd.oasis.opendocument.spreadsheet": { + "source": "iana", + "compressible": false, + "extensions": ["ods"] + }, + "application/vnd.oasis.opendocument.spreadsheet-template": { + "source": "iana", + "extensions": ["ots"] + }, + "application/vnd.oasis.opendocument.text": { + "source": "iana", + "compressible": false, + "extensions": ["odt"] + }, + "application/vnd.oasis.opendocument.text-master": { + "source": "iana", + "extensions": ["odm"] + }, + "application/vnd.oasis.opendocument.text-template": { + "source": "iana", + "extensions": ["ott"] + }, + "application/vnd.oasis.opendocument.text-web": { + "source": "iana", + "extensions": ["oth"] + }, + "application/vnd.obn": { + "source": "iana" + }, + "application/vnd.oftn.l10n+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.oipf.contentaccessdownload+xml": { + "source": "iana" + }, + "application/vnd.oipf.contentaccessstreaming+xml": { + "source": "iana" + }, + "application/vnd.oipf.cspg-hexbinary": { + "source": "iana" + }, + "application/vnd.oipf.dae.svg+xml": { + "source": "iana" + }, + "application/vnd.oipf.dae.xhtml+xml": { + "source": "iana" + }, + "application/vnd.oipf.mippvcontrolmessage+xml": { + "source": "iana" + }, + "application/vnd.oipf.pae.gem": { + "source": "iana" + }, + "application/vnd.oipf.spdiscovery+xml": { + "source": "iana" + }, + "application/vnd.oipf.spdlist+xml": { + "source": "iana" + }, + "application/vnd.oipf.ueprofile+xml": { + "source": "iana" + }, + "application/vnd.oipf.userprofile+xml": { + "source": "iana" + }, + "application/vnd.olpc-sugar": { + "source": "iana", + "extensions": ["xo"] + }, + "application/vnd.oma-scws-config": { + "source": "iana" + }, + "application/vnd.oma-scws-http-request": { + "source": "iana" + }, + "application/vnd.oma-scws-http-response": { + "source": "iana" + }, + "application/vnd.oma.bcast.associated-procedure-parameter+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.drm-trigger+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.imd+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.ltkm": { + "source": "iana" + }, + "application/vnd.oma.bcast.notification+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.provisioningtrigger": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgboot": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgdd+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgdu": { + "source": "iana" + }, + "application/vnd.oma.bcast.simple-symbol-container": { + "source": "iana" + }, + "application/vnd.oma.bcast.smartcard-trigger+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.sprov+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.stkm": { + "source": "iana" + }, + "application/vnd.oma.cab-address-book+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-feature-handler+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-pcc+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-subs-invite+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-user-prefs+xml": { + "source": "iana" + }, + "application/vnd.oma.dcd": { + "source": "iana" + }, + "application/vnd.oma.dcdc": { + "source": "iana" + }, + "application/vnd.oma.dd2+xml": { + "source": "iana", + "extensions": ["dd2"] + }, + "application/vnd.oma.drm.risd+xml": { + "source": "iana" + }, + "application/vnd.oma.group-usage-list+xml": { + "source": "iana" + }, + "application/vnd.oma.pal+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.detailed-progress-report+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.final-report+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.groups+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.invocation-descriptor+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.optimized-progress-report+xml": { + "source": "iana" + }, + "application/vnd.oma.push": { + "source": "iana" + }, + "application/vnd.oma.scidm.messages+xml": { + "source": "iana" + }, + "application/vnd.oma.xcap-directory+xml": { + "source": "iana" + }, + "application/vnd.omads-email+xml": { + "source": "iana" + }, + "application/vnd.omads-file+xml": { + "source": "iana" + }, + "application/vnd.omads-folder+xml": { + "source": "iana" + }, + "application/vnd.omaloc-supl-init": { + "source": "iana" + }, + "application/vnd.openblox.game+xml": { + "source": "iana" + }, + "application/vnd.openblox.game-binary": { + "source": "iana" + }, + "application/vnd.openeye.oeb": { + "source": "iana" + }, + "application/vnd.openofficeorg.extension": { + "source": "apache", + "extensions": ["oxt"] + }, + "application/vnd.openxmlformats-officedocument.custom-properties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.customxmlproperties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawing+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.chart+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.extended-properties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml-template": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.comments+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.presentation": { + "source": "iana", + "compressible": false, + "extensions": ["pptx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.presprops+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slide": { + "source": "iana", + "extensions": ["sldx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.slide+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideshow": { + "source": "iana", + "extensions": ["ppsx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.tags+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.template": { + "source": "apache", + "extensions": ["potx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml-template": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "source": "iana", + "compressible": false, + "extensions": ["xlsx"] + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.template": { + "source": "apache", + "extensions": ["xltx"] + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.theme+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.themeoverride+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.vmldrawing": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml-template": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document": { + "source": "iana", + "compressible": false, + "extensions": ["docx"] + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.template": { + "source": "apache", + "extensions": ["dotx"] + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-package.core-properties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-package.relationships+xml": { + "source": "iana" + }, + "application/vnd.oracle.resource+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.orange.indata": { + "source": "iana" + }, + "application/vnd.osa.netdeploy": { + "source": "iana" + }, + "application/vnd.osgeo.mapguide.package": { + "source": "iana", + "extensions": ["mgp"] + }, + "application/vnd.osgi.bundle": { + "source": "iana" + }, + "application/vnd.osgi.dp": { + "source": "iana", + "extensions": ["dp"] + }, + "application/vnd.osgi.subsystem": { + "source": "iana", + "extensions": ["esa"] + }, + "application/vnd.otps.ct-kip+xml": { + "source": "iana" + }, + "application/vnd.oxli.countgraph": { + "source": "iana" + }, + "application/vnd.pagerduty+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.palm": { + "source": "iana", + "extensions": ["pdb","pqa","oprc"] + }, + "application/vnd.panoply": { + "source": "iana" + }, + "application/vnd.paos+xml": { + "source": "iana" + }, + "application/vnd.paos.xml": { + "source": "apache" + }, + "application/vnd.pawaafile": { + "source": "iana", + "extensions": ["paw"] + }, + "application/vnd.pcos": { + "source": "iana" + }, + "application/vnd.pg.format": { + "source": "iana", + "extensions": ["str"] + }, + "application/vnd.pg.osasli": { + "source": "iana", + "extensions": ["ei6"] + }, + "application/vnd.piaccess.application-licence": { + "source": "iana" + }, + "application/vnd.picsel": { + "source": "iana", + "extensions": ["efif"] + }, + "application/vnd.pmi.widget": { + "source": "iana", + "extensions": ["wg"] + }, + "application/vnd.poc.group-advertisement+xml": { + "source": "iana" + }, + "application/vnd.pocketlearn": { + "source": "iana", + "extensions": ["plf"] + }, + "application/vnd.powerbuilder6": { + "source": "iana", + "extensions": ["pbd"] + }, + "application/vnd.powerbuilder6-s": { + "source": "iana" + }, + "application/vnd.powerbuilder7": { + "source": "iana" + }, + "application/vnd.powerbuilder7-s": { + "source": "iana" + }, + "application/vnd.powerbuilder75": { + "source": "iana" + }, + "application/vnd.powerbuilder75-s": { + "source": "iana" + }, + "application/vnd.preminet": { + "source": "iana" + }, + "application/vnd.previewsystems.box": { + "source": "iana", + "extensions": ["box"] + }, + "application/vnd.proteus.magazine": { + "source": "iana", + "extensions": ["mgz"] + }, + "application/vnd.publishare-delta-tree": { + "source": "iana", + "extensions": ["qps"] + }, + "application/vnd.pvi.ptid1": { + "source": "iana", + "extensions": ["ptid"] + }, + "application/vnd.pwg-multiplexed": { + "source": "iana" + }, + "application/vnd.pwg-xhtml-print+xml": { + "source": "iana" + }, + "application/vnd.qualcomm.brew-app-res": { + "source": "iana" + }, + "application/vnd.quark.quarkxpress": { + "source": "iana", + "extensions": ["qxd","qxt","qwd","qwt","qxl","qxb"] + }, + "application/vnd.quobject-quoxdocument": { + "source": "iana" + }, + "application/vnd.radisys.moml+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-conf+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-conn+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-dialog+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-stream+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-conf+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-base+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-fax-detect+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-fax-sendrecv+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-group+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-speech+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-transform+xml": { + "source": "iana" + }, + "application/vnd.rainstor.data": { + "source": "iana" + }, + "application/vnd.rapid": { + "source": "iana" + }, + "application/vnd.realvnc.bed": { + "source": "iana", + "extensions": ["bed"] + }, + "application/vnd.recordare.musicxml": { + "source": "iana", + "extensions": ["mxl"] + }, + "application/vnd.recordare.musicxml+xml": { + "source": "iana", + "extensions": ["musicxml"] + }, + "application/vnd.renlearn.rlprint": { + "source": "iana" + }, + "application/vnd.rig.cryptonote": { + "source": "iana", + "extensions": ["cryptonote"] + }, + "application/vnd.rim.cod": { + "source": "apache", + "extensions": ["cod"] + }, + "application/vnd.rn-realmedia": { + "source": "apache", + "extensions": ["rm"] + }, + "application/vnd.rn-realmedia-vbr": { + "source": "apache", + "extensions": ["rmvb"] + }, + "application/vnd.route66.link66+xml": { + "source": "iana", + "extensions": ["link66"] + }, + "application/vnd.rs-274x": { + "source": "iana" + }, + "application/vnd.ruckus.download": { + "source": "iana" + }, + "application/vnd.s3sms": { + "source": "iana" + }, + "application/vnd.sailingtracker.track": { + "source": "iana", + "extensions": ["st"] + }, + "application/vnd.sbm.cid": { + "source": "iana" + }, + "application/vnd.sbm.mid2": { + "source": "iana" + }, + "application/vnd.scribus": { + "source": "iana" + }, + "application/vnd.sealed.3df": { + "source": "iana" + }, + "application/vnd.sealed.csf": { + "source": "iana" + }, + "application/vnd.sealed.doc": { + "source": "iana" + }, + "application/vnd.sealed.eml": { + "source": "iana" + }, + "application/vnd.sealed.mht": { + "source": "iana" + }, + "application/vnd.sealed.net": { + "source": "iana" + }, + "application/vnd.sealed.ppt": { + "source": "iana" + }, + "application/vnd.sealed.tiff": { + "source": "iana" + }, + "application/vnd.sealed.xls": { + "source": "iana" + }, + "application/vnd.sealedmedia.softseal.html": { + "source": "iana" + }, + "application/vnd.sealedmedia.softseal.pdf": { + "source": "iana" + }, + "application/vnd.seemail": { + "source": "iana", + "extensions": ["see"] + }, + "application/vnd.sema": { + "source": "iana", + "extensions": ["sema"] + }, + "application/vnd.semd": { + "source": "iana", + "extensions": ["semd"] + }, + "application/vnd.semf": { + "source": "iana", + "extensions": ["semf"] + }, + "application/vnd.shana.informed.formdata": { + "source": "iana", + "extensions": ["ifm"] + }, + "application/vnd.shana.informed.formtemplate": { + "source": "iana", + "extensions": ["itp"] + }, + "application/vnd.shana.informed.interchange": { + "source": "iana", + "extensions": ["iif"] + }, + "application/vnd.shana.informed.package": { + "source": "iana", + "extensions": ["ipk"] + }, + "application/vnd.simtech-mindmapper": { + "source": "iana", + "extensions": ["twd","twds"] + }, + "application/vnd.siren+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.smaf": { + "source": "iana", + "extensions": ["mmf"] + }, + "application/vnd.smart.notebook": { + "source": "iana" + }, + "application/vnd.smart.teacher": { + "source": "iana", + "extensions": ["teacher"] + }, + "application/vnd.software602.filler.form+xml": { + "source": "iana" + }, + "application/vnd.software602.filler.form-xml-zip": { + "source": "iana" + }, + "application/vnd.solent.sdkm+xml": { + "source": "iana", + "extensions": ["sdkm","sdkd"] + }, + "application/vnd.spotfire.dxp": { + "source": "iana", + "extensions": ["dxp"] + }, + "application/vnd.spotfire.sfs": { + "source": "iana", + "extensions": ["sfs"] + }, + "application/vnd.sss-cod": { + "source": "iana" + }, + "application/vnd.sss-dtf": { + "source": "iana" + }, + "application/vnd.sss-ntf": { + "source": "iana" + }, + "application/vnd.stardivision.calc": { + "source": "apache", + "extensions": ["sdc"] + }, + "application/vnd.stardivision.draw": { + "source": "apache", + "extensions": ["sda"] + }, + "application/vnd.stardivision.impress": { + "source": "apache", + "extensions": ["sdd"] + }, + "application/vnd.stardivision.math": { + "source": "apache", + "extensions": ["smf"] + }, + "application/vnd.stardivision.writer": { + "source": "apache", + "extensions": ["sdw","vor"] + }, + "application/vnd.stardivision.writer-global": { + "source": "apache", + "extensions": ["sgl"] + }, + "application/vnd.stepmania.package": { + "source": "iana", + "extensions": ["smzip"] + }, + "application/vnd.stepmania.stepchart": { + "source": "iana", + "extensions": ["sm"] + }, + "application/vnd.street-stream": { + "source": "iana" + }, + "application/vnd.sun.wadl+xml": { + "source": "iana" + }, + "application/vnd.sun.xml.calc": { + "source": "apache", + "extensions": ["sxc"] + }, + "application/vnd.sun.xml.calc.template": { + "source": "apache", + "extensions": ["stc"] + }, + "application/vnd.sun.xml.draw": { + "source": "apache", + "extensions": ["sxd"] + }, + "application/vnd.sun.xml.draw.template": { + "source": "apache", + "extensions": ["std"] + }, + "application/vnd.sun.xml.impress": { + "source": "apache", + "extensions": ["sxi"] + }, + "application/vnd.sun.xml.impress.template": { + "source": "apache", + "extensions": ["sti"] + }, + "application/vnd.sun.xml.math": { + "source": "apache", + "extensions": ["sxm"] + }, + "application/vnd.sun.xml.writer": { + "source": "apache", + "extensions": ["sxw"] + }, + "application/vnd.sun.xml.writer.global": { + "source": "apache", + "extensions": ["sxg"] + }, + "application/vnd.sun.xml.writer.template": { + "source": "apache", + "extensions": ["stw"] + }, + "application/vnd.sus-calendar": { + "source": "iana", + "extensions": ["sus","susp"] + }, + "application/vnd.svd": { + "source": "iana", + "extensions": ["svd"] + }, + "application/vnd.swiftview-ics": { + "source": "iana" + }, + "application/vnd.symbian.install": { + "source": "apache", + "extensions": ["sis","sisx"] + }, + "application/vnd.syncml+xml": { + "source": "iana", + "extensions": ["xsm"] + }, + "application/vnd.syncml.dm+wbxml": { + "source": "iana", + "extensions": ["bdm"] + }, + "application/vnd.syncml.dm+xml": { + "source": "iana", + "extensions": ["xdm"] + }, + "application/vnd.syncml.dm.notification": { + "source": "iana" + }, + "application/vnd.syncml.dmddf+wbxml": { + "source": "iana" + }, + "application/vnd.syncml.dmddf+xml": { + "source": "iana" + }, + "application/vnd.syncml.dmtnds+wbxml": { + "source": "iana" + }, + "application/vnd.syncml.dmtnds+xml": { + "source": "iana" + }, + "application/vnd.syncml.ds.notification": { + "source": "iana" + }, + "application/vnd.tao.intent-module-archive": { + "source": "iana", + "extensions": ["tao"] + }, + "application/vnd.tcpdump.pcap": { + "source": "iana", + "extensions": ["pcap","cap","dmp"] + }, + "application/vnd.tmd.mediaflex.api+xml": { + "source": "iana" + }, + "application/vnd.tmobile-livetv": { + "source": "iana", + "extensions": ["tmo"] + }, + "application/vnd.trid.tpt": { + "source": "iana", + "extensions": ["tpt"] + }, + "application/vnd.triscape.mxs": { + "source": "iana", + "extensions": ["mxs"] + }, + "application/vnd.trueapp": { + "source": "iana", + "extensions": ["tra"] + }, + "application/vnd.truedoc": { + "source": "iana" + }, + "application/vnd.ubisoft.webplayer": { + "source": "iana" + }, + "application/vnd.ufdl": { + "source": "iana", + "extensions": ["ufd","ufdl"] + }, + "application/vnd.uiq.theme": { + "source": "iana", + "extensions": ["utz"] + }, + "application/vnd.umajin": { + "source": "iana", + "extensions": ["umj"] + }, + "application/vnd.unity": { + "source": "iana", + "extensions": ["unityweb"] + }, + "application/vnd.uoml+xml": { + "source": "iana", + "extensions": ["uoml"] + }, + "application/vnd.uplanet.alert": { + "source": "iana" + }, + "application/vnd.uplanet.alert-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.bearer-choice": { + "source": "iana" + }, + "application/vnd.uplanet.bearer-choice-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.cacheop": { + "source": "iana" + }, + "application/vnd.uplanet.cacheop-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.channel": { + "source": "iana" + }, + "application/vnd.uplanet.channel-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.list": { + "source": "iana" + }, + "application/vnd.uplanet.list-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.listcmd": { + "source": "iana" + }, + "application/vnd.uplanet.listcmd-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.signal": { + "source": "iana" + }, + "application/vnd.uri-map": { + "source": "iana" + }, + "application/vnd.valve.source.material": { + "source": "iana" + }, + "application/vnd.vcx": { + "source": "iana", + "extensions": ["vcx"] + }, + "application/vnd.vd-study": { + "source": "iana" + }, + "application/vnd.vectorworks": { + "source": "iana" + }, + "application/vnd.verimatrix.vcas": { + "source": "iana" + }, + "application/vnd.vidsoft.vidconference": { + "source": "iana" + }, + "application/vnd.visio": { + "source": "iana", + "extensions": ["vsd","vst","vss","vsw"] + }, + "application/vnd.visionary": { + "source": "iana", + "extensions": ["vis"] + }, + "application/vnd.vividence.scriptfile": { + "source": "iana" + }, + "application/vnd.vsf": { + "source": "iana", + "extensions": ["vsf"] + }, + "application/vnd.wap.sic": { + "source": "iana" + }, + "application/vnd.wap.slc": { + "source": "iana" + }, + "application/vnd.wap.wbxml": { + "source": "iana", + "extensions": ["wbxml"] + }, + "application/vnd.wap.wmlc": { + "source": "iana", + "extensions": ["wmlc"] + }, + "application/vnd.wap.wmlscriptc": { + "source": "iana", + "extensions": ["wmlsc"] + }, + "application/vnd.webturbo": { + "source": "iana", + "extensions": ["wtb"] + }, + "application/vnd.wfa.p2p": { + "source": "iana" + }, + "application/vnd.wfa.wsc": { + "source": "iana" + }, + "application/vnd.windows.devicepairing": { + "source": "iana" + }, + "application/vnd.wmc": { + "source": "iana" + }, + "application/vnd.wmf.bootstrap": { + "source": "iana" + }, + "application/vnd.wolfram.mathematica": { + "source": "iana" + }, + "application/vnd.wolfram.mathematica.package": { + "source": "iana" + }, + "application/vnd.wolfram.player": { + "source": "iana", + "extensions": ["nbp"] + }, + "application/vnd.wordperfect": { + "source": "iana", + "extensions": ["wpd"] + }, + "application/vnd.wqd": { + "source": "iana", + "extensions": ["wqd"] + }, + "application/vnd.wrq-hp3000-labelled": { + "source": "iana" + }, + "application/vnd.wt.stf": { + "source": "iana", + "extensions": ["stf"] + }, + "application/vnd.wv.csp+wbxml": { + "source": "iana" + }, + "application/vnd.wv.csp+xml": { + "source": "iana" + }, + "application/vnd.wv.ssp+xml": { + "source": "iana" + }, + "application/vnd.xacml+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.xara": { + "source": "iana", + "extensions": ["xar"] + }, + "application/vnd.xfdl": { + "source": "iana", + "extensions": ["xfdl"] + }, + "application/vnd.xfdl.webform": { + "source": "iana" + }, + "application/vnd.xmi+xml": { + "source": "iana" + }, + "application/vnd.xmpie.cpkg": { + "source": "iana" + }, + "application/vnd.xmpie.dpkg": { + "source": "iana" + }, + "application/vnd.xmpie.plan": { + "source": "iana" + }, + "application/vnd.xmpie.ppkg": { + "source": "iana" + }, + "application/vnd.xmpie.xlim": { + "source": "iana" + }, + "application/vnd.yamaha.hv-dic": { + "source": "iana", + "extensions": ["hvd"] + }, + "application/vnd.yamaha.hv-script": { + "source": "iana", + "extensions": ["hvs"] + }, + "application/vnd.yamaha.hv-voice": { + "source": "iana", + "extensions": ["hvp"] + }, + "application/vnd.yamaha.openscoreformat": { + "source": "iana", + "extensions": ["osf"] + }, + "application/vnd.yamaha.openscoreformat.osfpvg+xml": { + "source": "iana", + "extensions": ["osfpvg"] + }, + "application/vnd.yamaha.remote-setup": { + "source": "iana" + }, + "application/vnd.yamaha.smaf-audio": { + "source": "iana", + "extensions": ["saf"] + }, + "application/vnd.yamaha.smaf-phrase": { + "source": "iana", + "extensions": ["spf"] + }, + "application/vnd.yamaha.through-ngn": { + "source": "iana" + }, + "application/vnd.yamaha.tunnel-udpencap": { + "source": "iana" + }, + "application/vnd.yaoweme": { + "source": "iana" + }, + "application/vnd.yellowriver-custom-menu": { + "source": "iana", + "extensions": ["cmp"] + }, + "application/vnd.zul": { + "source": "iana", + "extensions": ["zir","zirz"] + }, + "application/vnd.zzazz.deck+xml": { + "source": "iana", + "extensions": ["zaz"] + }, + "application/voicexml+xml": { + "source": "iana", + "extensions": ["vxml"] + }, + "application/vq-rtcpxr": { + "source": "iana" + }, + "application/watcherinfo+xml": { + "source": "iana" + }, + "application/whoispp-query": { + "source": "iana" + }, + "application/whoispp-response": { + "source": "iana" + }, + "application/widget": { + "source": "iana", + "extensions": ["wgt"] + }, + "application/winhlp": { + "source": "apache", + "extensions": ["hlp"] + }, + "application/wita": { + "source": "iana" + }, + "application/wordperfect5.1": { + "source": "iana" + }, + "application/wsdl+xml": { + "source": "iana", + "extensions": ["wsdl"] + }, + "application/wspolicy+xml": { + "source": "iana", + "extensions": ["wspolicy"] + }, + "application/x-7z-compressed": { + "source": "apache", + "compressible": false, + "extensions": ["7z"] + }, + "application/x-abiword": { + "source": "apache", + "extensions": ["abw"] + }, + "application/x-ace-compressed": { + "source": "apache", + "extensions": ["ace"] + }, + "application/x-amf": { + "source": "apache" + }, + "application/x-apple-diskimage": { + "source": "apache", + "extensions": ["dmg"] + }, + "application/x-authorware-bin": { + "source": "apache", + "extensions": ["aab","x32","u32","vox"] + }, + "application/x-authorware-map": { + "source": "apache", + "extensions": ["aam"] + }, + "application/x-authorware-seg": { + "source": "apache", + "extensions": ["aas"] + }, + "application/x-bcpio": { + "source": "apache", + "extensions": ["bcpio"] + }, + "application/x-bdoc": { + "compressible": false, + "extensions": ["bdoc"] + }, + "application/x-bittorrent": { + "source": "apache", + "extensions": ["torrent"] + }, + "application/x-blorb": { + "source": "apache", + "extensions": ["blb","blorb"] + }, + "application/x-bzip": { + "source": "apache", + "compressible": false, + "extensions": ["bz"] + }, + "application/x-bzip2": { + "source": "apache", + "compressible": false, + "extensions": ["bz2","boz"] + }, + "application/x-cbr": { + "source": "apache", + "extensions": ["cbr","cba","cbt","cbz","cb7"] + }, + "application/x-cdlink": { + "source": "apache", + "extensions": ["vcd"] + }, + "application/x-cfs-compressed": { + "source": "apache", + "extensions": ["cfs"] + }, + "application/x-chat": { + "source": "apache", + "extensions": ["chat"] + }, + "application/x-chess-pgn": { + "source": "apache", + "extensions": ["pgn"] + }, + "application/x-chrome-extension": { + "extensions": ["crx"] + }, + "application/x-cocoa": { + "source": "nginx", + "extensions": ["cco"] + }, + "application/x-compress": { + "source": "apache" + }, + "application/x-conference": { + "source": "apache", + "extensions": ["nsc"] + }, + "application/x-cpio": { + "source": "apache", + "extensions": ["cpio"] + }, + "application/x-csh": { + "source": "apache", + "extensions": ["csh"] + }, + "application/x-deb": { + "compressible": false + }, + "application/x-debian-package": { + "source": "apache", + "extensions": ["deb","udeb"] + }, + "application/x-dgc-compressed": { + "source": "apache", + "extensions": ["dgc"] + }, + "application/x-director": { + "source": "apache", + "extensions": ["dir","dcr","dxr","cst","cct","cxt","w3d","fgd","swa"] + }, + "application/x-doom": { + "source": "apache", + "extensions": ["wad"] + }, + "application/x-dtbncx+xml": { + "source": "apache", + "extensions": ["ncx"] + }, + "application/x-dtbook+xml": { + "source": "apache", + "extensions": ["dtb"] + }, + "application/x-dtbresource+xml": { + "source": "apache", + "extensions": ["res"] + }, + "application/x-dvi": { + "source": "apache", + "compressible": false, + "extensions": ["dvi"] + }, + "application/x-envoy": { + "source": "apache", + "extensions": ["evy"] + }, + "application/x-eva": { + "source": "apache", + "extensions": ["eva"] + }, + "application/x-font-bdf": { + "source": "apache", + "extensions": ["bdf"] + }, + "application/x-font-dos": { + "source": "apache" + }, + "application/x-font-framemaker": { + "source": "apache" + }, + "application/x-font-ghostscript": { + "source": "apache", + "extensions": ["gsf"] + }, + "application/x-font-libgrx": { + "source": "apache" + }, + "application/x-font-linux-psf": { + "source": "apache", + "extensions": ["psf"] + }, + "application/x-font-otf": { + "source": "apache", + "compressible": true, + "extensions": ["otf"] + }, + "application/x-font-pcf": { + "source": "apache", + "extensions": ["pcf"] + }, + "application/x-font-snf": { + "source": "apache", + "extensions": ["snf"] + }, + "application/x-font-speedo": { + "source": "apache" + }, + "application/x-font-sunos-news": { + "source": "apache" + }, + "application/x-font-ttf": { + "source": "apache", + "compressible": true, + "extensions": ["ttf","ttc"] + }, + "application/x-font-type1": { + "source": "apache", + "extensions": ["pfa","pfb","pfm","afm"] + }, + "application/x-font-vfont": { + "source": "apache" + }, + "application/x-freearc": { + "source": "apache", + "extensions": ["arc"] + }, + "application/x-futuresplash": { + "source": "apache", + "extensions": ["spl"] + }, + "application/x-gca-compressed": { + "source": "apache", + "extensions": ["gca"] + }, + "application/x-glulx": { + "source": "apache", + "extensions": ["ulx"] + }, + "application/x-gnumeric": { + "source": "apache", + "extensions": ["gnumeric"] + }, + "application/x-gramps-xml": { + "source": "apache", + "extensions": ["gramps"] + }, + "application/x-gtar": { + "source": "apache", + "extensions": ["gtar"] + }, + "application/x-gzip": { + "source": "apache" + }, + "application/x-hdf": { + "source": "apache", + "extensions": ["hdf"] + }, + "application/x-httpd-php": { + "compressible": true, + "extensions": ["php"] + }, + "application/x-install-instructions": { + "source": "apache", + "extensions": ["install"] + }, + "application/x-iso9660-image": { + "source": "apache", + "extensions": ["iso"] + }, + "application/x-java-archive-diff": { + "source": "nginx", + "extensions": ["jardiff"] + }, + "application/x-java-jnlp-file": { + "source": "apache", + "compressible": false, + "extensions": ["jnlp"] + }, + "application/x-javascript": { + "compressible": true + }, + "application/x-latex": { + "source": "apache", + "compressible": false, + "extensions": ["latex"] + }, + "application/x-lua-bytecode": { + "extensions": ["luac"] + }, + "application/x-lzh-compressed": { + "source": "apache", + "extensions": ["lzh","lha"] + }, + "application/x-makeself": { + "source": "nginx", + "extensions": ["run"] + }, + "application/x-mie": { + "source": "apache", + "extensions": ["mie"] + }, + "application/x-mobipocket-ebook": { + "source": "apache", + "extensions": ["prc","mobi"] + }, + "application/x-mpegurl": { + "compressible": false + }, + "application/x-ms-application": { + "source": "apache", + "extensions": ["application"] + }, + "application/x-ms-shortcut": { + "source": "apache", + "extensions": ["lnk"] + }, + "application/x-ms-wmd": { + "source": "apache", + "extensions": ["wmd"] + }, + "application/x-ms-wmz": { + "source": "apache", + "extensions": ["wmz"] + }, + "application/x-ms-xbap": { + "source": "apache", + "extensions": ["xbap"] + }, + "application/x-msaccess": { + "source": "apache", + "extensions": ["mdb"] + }, + "application/x-msbinder": { + "source": "apache", + "extensions": ["obd"] + }, + "application/x-mscardfile": { + "source": "apache", + "extensions": ["crd"] + }, + "application/x-msclip": { + "source": "apache", + "extensions": ["clp"] + }, + "application/x-msdos-program": { + "extensions": ["exe"] + }, + "application/x-msdownload": { + "source": "apache", + "extensions": ["exe","dll","com","bat","msi"] + }, + "application/x-msmediaview": { + "source": "apache", + "extensions": ["mvb","m13","m14"] + }, + "application/x-msmetafile": { + "source": "apache", + "extensions": ["wmf","wmz","emf","emz"] + }, + "application/x-msmoney": { + "source": "apache", + "extensions": ["mny"] + }, + "application/x-mspublisher": { + "source": "apache", + "extensions": ["pub"] + }, + "application/x-msschedule": { + "source": "apache", + "extensions": ["scd"] + }, + "application/x-msterminal": { + "source": "apache", + "extensions": ["trm"] + }, + "application/x-mswrite": { + "source": "apache", + "extensions": ["wri"] + }, + "application/x-netcdf": { + "source": "apache", + "extensions": ["nc","cdf"] + }, + "application/x-ns-proxy-autoconfig": { + "compressible": true, + "extensions": ["pac"] + }, + "application/x-nzb": { + "source": "apache", + "extensions": ["nzb"] + }, + "application/x-perl": { + "source": "nginx", + "extensions": ["pl","pm"] + }, + "application/x-pilot": { + "source": "nginx", + "extensions": ["prc","pdb"] + }, + "application/x-pkcs12": { + "source": "apache", + "compressible": false, + "extensions": ["p12","pfx"] + }, + "application/x-pkcs7-certificates": { + "source": "apache", + "extensions": ["p7b","spc"] + }, + "application/x-pkcs7-certreqresp": { + "source": "apache", + "extensions": ["p7r"] + }, + "application/x-rar-compressed": { + "source": "apache", + "compressible": false, + "extensions": ["rar"] + }, + "application/x-redhat-package-manager": { + "source": "nginx", + "extensions": ["rpm"] + }, + "application/x-research-info-systems": { + "source": "apache", + "extensions": ["ris"] + }, + "application/x-sea": { + "source": "nginx", + "extensions": ["sea"] + }, + "application/x-sh": { + "source": "apache", + "compressible": true, + "extensions": ["sh"] + }, + "application/x-shar": { + "source": "apache", + "extensions": ["shar"] + }, + "application/x-shockwave-flash": { + "source": "apache", + "compressible": false, + "extensions": ["swf"] + }, + "application/x-silverlight-app": { + "source": "apache", + "extensions": ["xap"] + }, + "application/x-sql": { + "source": "apache", + "extensions": ["sql"] + }, + "application/x-stuffit": { + "source": "apache", + "compressible": false, + "extensions": ["sit"] + }, + "application/x-stuffitx": { + "source": "apache", + "extensions": ["sitx"] + }, + "application/x-subrip": { + "source": "apache", + "extensions": ["srt"] + }, + "application/x-sv4cpio": { + "source": "apache", + "extensions": ["sv4cpio"] + }, + "application/x-sv4crc": { + "source": "apache", + "extensions": ["sv4crc"] + }, + "application/x-t3vm-image": { + "source": "apache", + "extensions": ["t3"] + }, + "application/x-tads": { + "source": "apache", + "extensions": ["gam"] + }, + "application/x-tar": { + "source": "apache", + "compressible": true, + "extensions": ["tar"] + }, + "application/x-tcl": { + "source": "apache", + "extensions": ["tcl","tk"] + }, + "application/x-tex": { + "source": "apache", + "extensions": ["tex"] + }, + "application/x-tex-tfm": { + "source": "apache", + "extensions": ["tfm"] + }, + "application/x-texinfo": { + "source": "apache", + "extensions": ["texinfo","texi"] + }, + "application/x-tgif": { + "source": "apache", + "extensions": ["obj"] + }, + "application/x-ustar": { + "source": "apache", + "extensions": ["ustar"] + }, + "application/x-wais-source": { + "source": "apache", + "extensions": ["src"] + }, + "application/x-web-app-manifest+json": { + "compressible": true, + "extensions": ["webapp"] + }, + "application/x-www-form-urlencoded": { + "source": "iana", + "compressible": true + }, + "application/x-x509-ca-cert": { + "source": "apache", + "extensions": ["der","crt","pem"] + }, + "application/x-xfig": { + "source": "apache", + "extensions": ["fig"] + }, + "application/x-xliff+xml": { + "source": "apache", + "extensions": ["xlf"] + }, + "application/x-xpinstall": { + "source": "apache", + "compressible": false, + "extensions": ["xpi"] + }, + "application/x-xz": { + "source": "apache", + "extensions": ["xz"] + }, + "application/x-zmachine": { + "source": "apache", + "extensions": ["z1","z2","z3","z4","z5","z6","z7","z8"] + }, + "application/x400-bp": { + "source": "iana" + }, + "application/xacml+xml": { + "source": "iana" + }, + "application/xaml+xml": { + "source": "apache", + "extensions": ["xaml"] + }, + "application/xcap-att+xml": { + "source": "iana" + }, + "application/xcap-caps+xml": { + "source": "iana" + }, + "application/xcap-diff+xml": { + "source": "iana", + "extensions": ["xdf"] + }, + "application/xcap-el+xml": { + "source": "iana" + }, + "application/xcap-error+xml": { + "source": "iana" + }, + "application/xcap-ns+xml": { + "source": "iana" + }, + "application/xcon-conference-info+xml": { + "source": "iana" + }, + "application/xcon-conference-info-diff+xml": { + "source": "iana" + }, + "application/xenc+xml": { + "source": "iana", + "extensions": ["xenc"] + }, + "application/xhtml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xhtml","xht"] + }, + "application/xhtml-voice+xml": { + "source": "apache" + }, + "application/xml": { + "source": "iana", + "compressible": true, + "extensions": ["xml","xsl","xsd"] + }, + "application/xml-dtd": { + "source": "iana", + "compressible": true, + "extensions": ["dtd"] + }, + "application/xml-external-parsed-entity": { + "source": "iana" + }, + "application/xml-patch+xml": { + "source": "iana" + }, + "application/xmpp+xml": { + "source": "iana" + }, + "application/xop+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xop"] + }, + "application/xproc+xml": { + "source": "apache", + "extensions": ["xpl"] + }, + "application/xslt+xml": { + "source": "iana", + "extensions": ["xslt"] + }, + "application/xspf+xml": { + "source": "apache", + "extensions": ["xspf"] + }, + "application/xv+xml": { + "source": "iana", + "extensions": ["mxml","xhvml","xvml","xvm"] + }, + "application/yang": { + "source": "iana", + "extensions": ["yang"] + }, + "application/yin+xml": { + "source": "iana", + "extensions": ["yin"] + }, + "application/zip": { + "source": "iana", + "compressible": false, + "extensions": ["zip"] + }, + "application/zlib": { + "source": "iana" + }, + "audio/1d-interleaved-parityfec": { + "source": "iana" + }, + "audio/32kadpcm": { + "source": "iana" + }, + "audio/3gpp": { + "source": "iana" + }, + "audio/3gpp2": { + "source": "iana" + }, + "audio/ac3": { + "source": "iana" + }, + "audio/adpcm": { + "source": "apache", + "extensions": ["adp"] + }, + "audio/amr": { + "source": "iana" + }, + "audio/amr-wb": { + "source": "iana" + }, + "audio/amr-wb+": { + "source": "iana" + }, + "audio/aptx": { + "source": "iana" + }, + "audio/asc": { + "source": "iana" + }, + "audio/atrac-advanced-lossless": { + "source": "iana" + }, + "audio/atrac-x": { + "source": "iana" + }, + "audio/atrac3": { + "source": "iana" + }, + "audio/basic": { + "source": "iana", + "compressible": false, + "extensions": ["au","snd"] + }, + "audio/bv16": { + "source": "iana" + }, + "audio/bv32": { + "source": "iana" + }, + "audio/clearmode": { + "source": "iana" + }, + "audio/cn": { + "source": "iana" + }, + "audio/dat12": { + "source": "iana" + }, + "audio/dls": { + "source": "iana" + }, + "audio/dsr-es201108": { + "source": "iana" + }, + "audio/dsr-es202050": { + "source": "iana" + }, + "audio/dsr-es202211": { + "source": "iana" + }, + "audio/dsr-es202212": { + "source": "iana" + }, + "audio/dv": { + "source": "iana" + }, + "audio/dvi4": { + "source": "iana" + }, + "audio/eac3": { + "source": "iana" + }, + "audio/encaprtp": { + "source": "iana" + }, + "audio/evrc": { + "source": "iana" + }, + "audio/evrc-qcp": { + "source": "iana" + }, + "audio/evrc0": { + "source": "iana" + }, + "audio/evrc1": { + "source": "iana" + }, + "audio/evrcb": { + "source": "iana" + }, + "audio/evrcb0": { + "source": "iana" + }, + "audio/evrcb1": { + "source": "iana" + }, + "audio/evrcnw": { + "source": "iana" + }, + "audio/evrcnw0": { + "source": "iana" + }, + "audio/evrcnw1": { + "source": "iana" + }, + "audio/evrcwb": { + "source": "iana" + }, + "audio/evrcwb0": { + "source": "iana" + }, + "audio/evrcwb1": { + "source": "iana" + }, + "audio/fwdred": { + "source": "iana" + }, + "audio/g711-0": { + "source": "iana" + }, + "audio/g719": { + "source": "iana" + }, + "audio/g722": { + "source": "iana" + }, + "audio/g7221": { + "source": "iana" + }, + "audio/g723": { + "source": "iana" + }, + "audio/g726-16": { + "source": "iana" + }, + "audio/g726-24": { + "source": "iana" + }, + "audio/g726-32": { + "source": "iana" + }, + "audio/g726-40": { + "source": "iana" + }, + "audio/g728": { + "source": "iana" + }, + "audio/g729": { + "source": "iana" + }, + "audio/g7291": { + "source": "iana" + }, + "audio/g729d": { + "source": "iana" + }, + "audio/g729e": { + "source": "iana" + }, + "audio/gsm": { + "source": "iana" + }, + "audio/gsm-efr": { + "source": "iana" + }, + "audio/gsm-hr-08": { + "source": "iana" + }, + "audio/ilbc": { + "source": "iana" + }, + "audio/ip-mr_v2.5": { + "source": "iana" + }, + "audio/isac": { + "source": "apache" + }, + "audio/l16": { + "source": "iana" + }, + "audio/l20": { + "source": "iana" + }, + "audio/l24": { + "source": "iana", + "compressible": false + }, + "audio/l8": { + "source": "iana" + }, + "audio/lpc": { + "source": "iana" + }, + "audio/midi": { + "source": "apache", + "extensions": ["mid","midi","kar","rmi"] + }, + "audio/mobile-xmf": { + "source": "iana" + }, + "audio/mp4": { + "source": "iana", + "compressible": false, + "extensions": ["mp4a","m4a"] + }, + "audio/mp4a-latm": { + "source": "iana" + }, + "audio/mpa": { + "source": "iana" + }, + "audio/mpa-robust": { + "source": "iana" + }, + "audio/mpeg": { + "source": "iana", + "compressible": false, + "extensions": ["mpga","mp2","mp2a","mp3","m2a","m3a"] + }, + "audio/mpeg4-generic": { + "source": "iana" + }, + "audio/musepack": { + "source": "apache" + }, + "audio/ogg": { + "source": "iana", + "compressible": false, + "extensions": ["oga","ogg","spx"] + }, + "audio/opus": { + "source": "iana" + }, + "audio/parityfec": { + "source": "iana" + }, + "audio/pcma": { + "source": "iana" + }, + "audio/pcma-wb": { + "source": "iana" + }, + "audio/pcmu": { + "source": "iana" + }, + "audio/pcmu-wb": { + "source": "iana" + }, + "audio/prs.sid": { + "source": "iana" + }, + "audio/qcelp": { + "source": "iana" + }, + "audio/raptorfec": { + "source": "iana" + }, + "audio/red": { + "source": "iana" + }, + "audio/rtp-enc-aescm128": { + "source": "iana" + }, + "audio/rtp-midi": { + "source": "iana" + }, + "audio/rtploopback": { + "source": "iana" + }, + "audio/rtx": { + "source": "iana" + }, + "audio/s3m": { + "source": "apache", + "extensions": ["s3m"] + }, + "audio/silk": { + "source": "apache", + "extensions": ["sil"] + }, + "audio/smv": { + "source": "iana" + }, + "audio/smv-qcp": { + "source": "iana" + }, + "audio/smv0": { + "source": "iana" + }, + "audio/sp-midi": { + "source": "iana" + }, + "audio/speex": { + "source": "iana" + }, + "audio/t140c": { + "source": "iana" + }, + "audio/t38": { + "source": "iana" + }, + "audio/telephone-event": { + "source": "iana" + }, + "audio/tone": { + "source": "iana" + }, + "audio/uemclip": { + "source": "iana" + }, + "audio/ulpfec": { + "source": "iana" + }, + "audio/vdvi": { + "source": "iana" + }, + "audio/vmr-wb": { + "source": "iana" + }, + "audio/vnd.3gpp.iufp": { + "source": "iana" + }, + "audio/vnd.4sb": { + "source": "iana" + }, + "audio/vnd.audiokoz": { + "source": "iana" + }, + "audio/vnd.celp": { + "source": "iana" + }, + "audio/vnd.cisco.nse": { + "source": "iana" + }, + "audio/vnd.cmles.radio-events": { + "source": "iana" + }, + "audio/vnd.cns.anp1": { + "source": "iana" + }, + "audio/vnd.cns.inf1": { + "source": "iana" + }, + "audio/vnd.dece.audio": { + "source": "iana", + "extensions": ["uva","uvva"] + }, + "audio/vnd.digital-winds": { + "source": "iana", + "extensions": ["eol"] + }, + "audio/vnd.dlna.adts": { + "source": "iana" + }, + "audio/vnd.dolby.heaac.1": { + "source": "iana" + }, + "audio/vnd.dolby.heaac.2": { + "source": "iana" + }, + "audio/vnd.dolby.mlp": { + "source": "iana" + }, + "audio/vnd.dolby.mps": { + "source": "iana" + }, + "audio/vnd.dolby.pl2": { + "source": "iana" + }, + "audio/vnd.dolby.pl2x": { + "source": "iana" + }, + "audio/vnd.dolby.pl2z": { + "source": "iana" + }, + "audio/vnd.dolby.pulse.1": { + "source": "iana" + }, + "audio/vnd.dra": { + "source": "iana", + "extensions": ["dra"] + }, + "audio/vnd.dts": { + "source": "iana", + "extensions": ["dts"] + }, + "audio/vnd.dts.hd": { + "source": "iana", + "extensions": ["dtshd"] + }, + "audio/vnd.dvb.file": { + "source": "iana" + }, + "audio/vnd.everad.plj": { + "source": "iana" + }, + "audio/vnd.hns.audio": { + "source": "iana" + }, + "audio/vnd.lucent.voice": { + "source": "iana", + "extensions": ["lvp"] + }, + "audio/vnd.ms-playready.media.pya": { + "source": "iana", + "extensions": ["pya"] + }, + "audio/vnd.nokia.mobile-xmf": { + "source": "iana" + }, + "audio/vnd.nortel.vbk": { + "source": "iana" + }, + "audio/vnd.nuera.ecelp4800": { + "source": "iana", + "extensions": ["ecelp4800"] + }, + "audio/vnd.nuera.ecelp7470": { + "source": "iana", + "extensions": ["ecelp7470"] + }, + "audio/vnd.nuera.ecelp9600": { + "source": "iana", + "extensions": ["ecelp9600"] + }, + "audio/vnd.octel.sbc": { + "source": "iana" + }, + "audio/vnd.qcelp": { + "source": "iana" + }, + "audio/vnd.rhetorex.32kadpcm": { + "source": "iana" + }, + "audio/vnd.rip": { + "source": "iana", + "extensions": ["rip"] + }, + "audio/vnd.rn-realaudio": { + "compressible": false + }, + "audio/vnd.sealedmedia.softseal.mpeg": { + "source": "iana" + }, + "audio/vnd.vmx.cvsd": { + "source": "iana" + }, + "audio/vnd.wave": { + "compressible": false + }, + "audio/vorbis": { + "source": "iana", + "compressible": false + }, + "audio/vorbis-config": { + "source": "iana" + }, + "audio/wav": { + "compressible": false, + "extensions": ["wav"] + }, + "audio/wave": { + "compressible": false, + "extensions": ["wav"] + }, + "audio/webm": { + "source": "apache", + "compressible": false, + "extensions": ["weba"] + }, + "audio/x-aac": { + "source": "apache", + "compressible": false, + "extensions": ["aac"] + }, + "audio/x-aiff": { + "source": "apache", + "extensions": ["aif","aiff","aifc"] + }, + "audio/x-caf": { + "source": "apache", + "compressible": false, + "extensions": ["caf"] + }, + "audio/x-flac": { + "source": "apache", + "extensions": ["flac"] + }, + "audio/x-m4a": { + "source": "nginx", + "extensions": ["m4a"] + }, + "audio/x-matroska": { + "source": "apache", + "extensions": ["mka"] + }, + "audio/x-mpegurl": { + "source": "apache", + "extensions": ["m3u"] + }, + "audio/x-ms-wax": { + "source": "apache", + "extensions": ["wax"] + }, + "audio/x-ms-wma": { + "source": "apache", + "extensions": ["wma"] + }, + "audio/x-pn-realaudio": { + "source": "apache", + "extensions": ["ram","ra"] + }, + "audio/x-pn-realaudio-plugin": { + "source": "apache", + "extensions": ["rmp"] + }, + "audio/x-realaudio": { + "source": "nginx", + "extensions": ["ra"] + }, + "audio/x-tta": { + "source": "apache" + }, + "audio/x-wav": { + "source": "apache", + "extensions": ["wav"] + }, + "audio/xm": { + "source": "apache", + "extensions": ["xm"] + }, + "chemical/x-cdx": { + "source": "apache", + "extensions": ["cdx"] + }, + "chemical/x-cif": { + "source": "apache", + "extensions": ["cif"] + }, + "chemical/x-cmdf": { + "source": "apache", + "extensions": ["cmdf"] + }, + "chemical/x-cml": { + "source": "apache", + "extensions": ["cml"] + }, + "chemical/x-csml": { + "source": "apache", + "extensions": ["csml"] + }, + "chemical/x-pdb": { + "source": "apache" + }, + "chemical/x-xyz": { + "source": "apache", + "extensions": ["xyz"] + }, + "font/opentype": { + "compressible": true, + "extensions": ["otf"] + }, + "image/bmp": { + "source": "apache", + "compressible": true, + "extensions": ["bmp"] + }, + "image/cgm": { + "source": "iana", + "extensions": ["cgm"] + }, + "image/fits": { + "source": "iana" + }, + "image/g3fax": { + "source": "iana", + "extensions": ["g3"] + }, + "image/gif": { + "source": "iana", + "compressible": false, + "extensions": ["gif"] + }, + "image/ief": { + "source": "iana", + "extensions": ["ief"] + }, + "image/jp2": { + "source": "iana" + }, + "image/jpeg": { + "source": "iana", + "compressible": false, + "extensions": ["jpeg","jpg","jpe"] + }, + "image/jpm": { + "source": "iana" + }, + "image/jpx": { + "source": "iana" + }, + "image/ktx": { + "source": "iana", + "extensions": ["ktx"] + }, + "image/naplps": { + "source": "iana" + }, + "image/pjpeg": { + "compressible": false + }, + "image/png": { + "source": "iana", + "compressible": false, + "extensions": ["png"] + }, + "image/prs.btif": { + "source": "iana", + "extensions": ["btif"] + }, + "image/prs.pti": { + "source": "iana" + }, + "image/pwg-raster": { + "source": "iana" + }, + "image/sgi": { + "source": "apache", + "extensions": ["sgi"] + }, + "image/svg+xml": { + "source": "iana", + "compressible": true, + "extensions": ["svg","svgz"] + }, + "image/t38": { + "source": "iana" + }, + "image/tiff": { + "source": "iana", + "compressible": false, + "extensions": ["tiff","tif"] + }, + "image/tiff-fx": { + "source": "iana" + }, + "image/vnd.adobe.photoshop": { + "source": "iana", + "compressible": true, + "extensions": ["psd"] + }, + "image/vnd.airzip.accelerator.azv": { + "source": "iana" + }, + "image/vnd.cns.inf2": { + "source": "iana" + }, + "image/vnd.dece.graphic": { + "source": "iana", + "extensions": ["uvi","uvvi","uvg","uvvg"] + }, + "image/vnd.djvu": { + "source": "iana", + "extensions": ["djvu","djv"] + }, + "image/vnd.dvb.subtitle": { + "source": "iana", + "extensions": ["sub"] + }, + "image/vnd.dwg": { + "source": "iana", + "extensions": ["dwg"] + }, + "image/vnd.dxf": { + "source": "iana", + "extensions": ["dxf"] + }, + "image/vnd.fastbidsheet": { + "source": "iana", + "extensions": ["fbs"] + }, + "image/vnd.fpx": { + "source": "iana", + "extensions": ["fpx"] + }, + "image/vnd.fst": { + "source": "iana", + "extensions": ["fst"] + }, + "image/vnd.fujixerox.edmics-mmr": { + "source": "iana", + "extensions": ["mmr"] + }, + "image/vnd.fujixerox.edmics-rlc": { + "source": "iana", + "extensions": ["rlc"] + }, + "image/vnd.globalgraphics.pgb": { + "source": "iana" + }, + "image/vnd.microsoft.icon": { + "source": "iana" + }, + "image/vnd.mix": { + "source": "iana" + }, + "image/vnd.mozilla.apng": { + "source": "iana" + }, + "image/vnd.ms-modi": { + "source": "iana", + "extensions": ["mdi"] + }, + "image/vnd.ms-photo": { + "source": "apache", + "extensions": ["wdp"] + }, + "image/vnd.net-fpx": { + "source": "iana", + "extensions": ["npx"] + }, + "image/vnd.radiance": { + "source": "iana" + }, + "image/vnd.sealed.png": { + "source": "iana" + }, + "image/vnd.sealedmedia.softseal.gif": { + "source": "iana" + }, + "image/vnd.sealedmedia.softseal.jpg": { + "source": "iana" + }, + "image/vnd.svf": { + "source": "iana" + }, + "image/vnd.tencent.tap": { + "source": "iana" + }, + "image/vnd.valve.source.texture": { + "source": "iana" + }, + "image/vnd.wap.wbmp": { + "source": "iana", + "extensions": ["wbmp"] + }, + "image/vnd.xiff": { + "source": "iana", + "extensions": ["xif"] + }, + "image/vnd.zbrush.pcx": { + "source": "iana" + }, + "image/webp": { + "source": "apache", + "extensions": ["webp"] + }, + "image/x-3ds": { + "source": "apache", + "extensions": ["3ds"] + }, + "image/x-cmu-raster": { + "source": "apache", + "extensions": ["ras"] + }, + "image/x-cmx": { + "source": "apache", + "extensions": ["cmx"] + }, + "image/x-freehand": { + "source": "apache", + "extensions": ["fh","fhc","fh4","fh5","fh7"] + }, + "image/x-icon": { + "source": "apache", + "compressible": true, + "extensions": ["ico"] + }, + "image/x-jng": { + "source": "nginx", + "extensions": ["jng"] + }, + "image/x-mrsid-image": { + "source": "apache", + "extensions": ["sid"] + }, + "image/x-ms-bmp": { + "source": "nginx", + "compressible": true, + "extensions": ["bmp"] + }, + "image/x-pcx": { + "source": "apache", + "extensions": ["pcx"] + }, + "image/x-pict": { + "source": "apache", + "extensions": ["pic","pct"] + }, + "image/x-portable-anymap": { + "source": "apache", + "extensions": ["pnm"] + }, + "image/x-portable-bitmap": { + "source": "apache", + "extensions": ["pbm"] + }, + "image/x-portable-graymap": { + "source": "apache", + "extensions": ["pgm"] + }, + "image/x-portable-pixmap": { + "source": "apache", + "extensions": ["ppm"] + }, + "image/x-rgb": { + "source": "apache", + "extensions": ["rgb"] + }, + "image/x-tga": { + "source": "apache", + "extensions": ["tga"] + }, + "image/x-xbitmap": { + "source": "apache", + "extensions": ["xbm"] + }, + "image/x-xcf": { + "compressible": false + }, + "image/x-xpixmap": { + "source": "apache", + "extensions": ["xpm"] + }, + "image/x-xwindowdump": { + "source": "apache", + "extensions": ["xwd"] + }, + "message/cpim": { + "source": "iana" + }, + "message/delivery-status": { + "source": "iana" + }, + "message/disposition-notification": { + "source": "iana" + }, + "message/external-body": { + "source": "iana" + }, + "message/feedback-report": { + "source": "iana" + }, + "message/global": { + "source": "iana" + }, + "message/global-delivery-status": { + "source": "iana" + }, + "message/global-disposition-notification": { + "source": "iana" + }, + "message/global-headers": { + "source": "iana" + }, + "message/http": { + "source": "iana", + "compressible": false + }, + "message/imdn+xml": { + "source": "iana", + "compressible": true + }, + "message/news": { + "source": "iana" + }, + "message/partial": { + "source": "iana", + "compressible": false + }, + "message/rfc822": { + "source": "iana", + "compressible": true, + "extensions": ["eml","mime"] + }, + "message/s-http": { + "source": "iana" + }, + "message/sip": { + "source": "iana" + }, + "message/sipfrag": { + "source": "iana" + }, + "message/tracking-status": { + "source": "iana" + }, + "message/vnd.si.simp": { + "source": "iana" + }, + "message/vnd.wfa.wsc": { + "source": "iana" + }, + "model/iges": { + "source": "iana", + "compressible": false, + "extensions": ["igs","iges"] + }, + "model/mesh": { + "source": "iana", + "compressible": false, + "extensions": ["msh","mesh","silo"] + }, + "model/vnd.collada+xml": { + "source": "iana", + "extensions": ["dae"] + }, + "model/vnd.dwf": { + "source": "iana", + "extensions": ["dwf"] + }, + "model/vnd.flatland.3dml": { + "source": "iana" + }, + "model/vnd.gdl": { + "source": "iana", + "extensions": ["gdl"] + }, + "model/vnd.gs-gdl": { + "source": "apache" + }, + "model/vnd.gs.gdl": { + "source": "iana" + }, + "model/vnd.gtw": { + "source": "iana", + "extensions": ["gtw"] + }, + "model/vnd.moml+xml": { + "source": "iana" + }, + "model/vnd.mts": { + "source": "iana", + "extensions": ["mts"] + }, + "model/vnd.opengex": { + "source": "iana" + }, + "model/vnd.parasolid.transmit.binary": { + "source": "iana" + }, + "model/vnd.parasolid.transmit.text": { + "source": "iana" + }, + "model/vnd.valve.source.compiled-map": { + "source": "iana" + }, + "model/vnd.vtu": { + "source": "iana", + "extensions": ["vtu"] + }, + "model/vrml": { + "source": "iana", + "compressible": false, + "extensions": ["wrl","vrml"] + }, + "model/x3d+binary": { + "source": "apache", + "compressible": false, + "extensions": ["x3db","x3dbz"] + }, + "model/x3d+fastinfoset": { + "source": "iana" + }, + "model/x3d+vrml": { + "source": "apache", + "compressible": false, + "extensions": ["x3dv","x3dvz"] + }, + "model/x3d+xml": { + "source": "iana", + "compressible": true, + "extensions": ["x3d","x3dz"] + }, + "model/x3d-vrml": { + "source": "iana" + }, + "multipart/alternative": { + "source": "iana", + "compressible": false + }, + "multipart/appledouble": { + "source": "iana" + }, + "multipart/byteranges": { + "source": "iana" + }, + "multipart/digest": { + "source": "iana" + }, + "multipart/encrypted": { + "source": "iana", + "compressible": false + }, + "multipart/form-data": { + "source": "iana", + "compressible": false + }, + "multipart/header-set": { + "source": "iana" + }, + "multipart/mixed": { + "source": "iana", + "compressible": false + }, + "multipart/parallel": { + "source": "iana" + }, + "multipart/related": { + "source": "iana", + "compressible": false + }, + "multipart/report": { + "source": "iana" + }, + "multipart/signed": { + "source": "iana", + "compressible": false + }, + "multipart/voice-message": { + "source": "iana" + }, + "multipart/x-mixed-replace": { + "source": "iana" + }, + "text/1d-interleaved-parityfec": { + "source": "iana" + }, + "text/cache-manifest": { + "source": "iana", + "compressible": true, + "extensions": ["appcache","manifest"] + }, + "text/calendar": { + "source": "iana", + "extensions": ["ics","ifb"] + }, + "text/calender": { + "compressible": true + }, + "text/cmd": { + "compressible": true + }, + "text/coffeescript": { + "extensions": ["coffee","litcoffee"] + }, + "text/css": { + "source": "iana", + "compressible": true, + "extensions": ["css"] + }, + "text/csv": { + "source": "iana", + "compressible": true, + "extensions": ["csv"] + }, + "text/csv-schema": { + "source": "iana" + }, + "text/directory": { + "source": "iana" + }, + "text/dns": { + "source": "iana" + }, + "text/ecmascript": { + "source": "iana" + }, + "text/encaprtp": { + "source": "iana" + }, + "text/enriched": { + "source": "iana" + }, + "text/fwdred": { + "source": "iana" + }, + "text/grammar-ref-list": { + "source": "iana" + }, + "text/hjson": { + "extensions": ["hjson"] + }, + "text/html": { + "source": "iana", + "compressible": true, + "extensions": ["html","htm","shtml"] + }, + "text/jade": { + "extensions": ["jade"] + }, + "text/javascript": { + "source": "iana", + "compressible": true + }, + "text/jcr-cnd": { + "source": "iana" + }, + "text/jsx": { + "compressible": true, + "extensions": ["jsx"] + }, + "text/less": { + "extensions": ["less"] + }, + "text/markdown": { + "source": "iana" + }, + "text/mathml": { + "source": "nginx", + "extensions": ["mml"] + }, + "text/mizar": { + "source": "iana" + }, + "text/n3": { + "source": "iana", + "compressible": true, + "extensions": ["n3"] + }, + "text/parameters": { + "source": "iana" + }, + "text/parityfec": { + "source": "iana" + }, + "text/plain": { + "source": "iana", + "compressible": true, + "extensions": ["txt","text","conf","def","list","log","in","ini"] + }, + "text/provenance-notation": { + "source": "iana" + }, + "text/prs.fallenstein.rst": { + "source": "iana" + }, + "text/prs.lines.tag": { + "source": "iana", + "extensions": ["dsc"] + }, + "text/raptorfec": { + "source": "iana" + }, + "text/red": { + "source": "iana" + }, + "text/rfc822-headers": { + "source": "iana" + }, + "text/richtext": { + "source": "iana", + "compressible": true, + "extensions": ["rtx"] + }, + "text/rtf": { + "source": "iana", + "compressible": true, + "extensions": ["rtf"] + }, + "text/rtp-enc-aescm128": { + "source": "iana" + }, + "text/rtploopback": { + "source": "iana" + }, + "text/rtx": { + "source": "iana" + }, + "text/sgml": { + "source": "iana", + "extensions": ["sgml","sgm"] + }, + "text/stylus": { + "extensions": ["stylus","styl"] + }, + "text/t140": { + "source": "iana" + }, + "text/tab-separated-values": { + "source": "iana", + "compressible": true, + "extensions": ["tsv"] + }, + "text/troff": { + "source": "iana", + "extensions": ["t","tr","roff","man","me","ms"] + }, + "text/turtle": { + "source": "iana", + "extensions": ["ttl"] + }, + "text/ulpfec": { + "source": "iana" + }, + "text/uri-list": { + "source": "iana", + "compressible": true, + "extensions": ["uri","uris","urls"] + }, + "text/vcard": { + "source": "iana", + "compressible": true, + "extensions": ["vcard"] + }, + "text/vnd.a": { + "source": "iana" + }, + "text/vnd.abc": { + "source": "iana" + }, + "text/vnd.curl": { + "source": "iana", + "extensions": ["curl"] + }, + "text/vnd.curl.dcurl": { + "source": "apache", + "extensions": ["dcurl"] + }, + "text/vnd.curl.mcurl": { + "source": "apache", + "extensions": ["mcurl"] + }, + "text/vnd.curl.scurl": { + "source": "apache", + "extensions": ["scurl"] + }, + "text/vnd.debian.copyright": { + "source": "iana" + }, + "text/vnd.dmclientscript": { + "source": "iana" + }, + "text/vnd.dvb.subtitle": { + "source": "iana", + "extensions": ["sub"] + }, + "text/vnd.esmertec.theme-descriptor": { + "source": "iana" + }, + "text/vnd.fly": { + "source": "iana", + "extensions": ["fly"] + }, + "text/vnd.fmi.flexstor": { + "source": "iana", + "extensions": ["flx"] + }, + "text/vnd.graphviz": { + "source": "iana", + "extensions": ["gv"] + }, + "text/vnd.in3d.3dml": { + "source": "iana", + "extensions": ["3dml"] + }, + "text/vnd.in3d.spot": { + "source": "iana", + "extensions": ["spot"] + }, + "text/vnd.iptc.newsml": { + "source": "iana" + }, + "text/vnd.iptc.nitf": { + "source": "iana" + }, + "text/vnd.latex-z": { + "source": "iana" + }, + "text/vnd.motorola.reflex": { + "source": "iana" + }, + "text/vnd.ms-mediapackage": { + "source": "iana" + }, + "text/vnd.net2phone.commcenter.command": { + "source": "iana" + }, + "text/vnd.radisys.msml-basic-layout": { + "source": "iana" + }, + "text/vnd.si.uricatalogue": { + "source": "iana" + }, + "text/vnd.sun.j2me.app-descriptor": { + "source": "iana", + "extensions": ["jad"] + }, + "text/vnd.trolltech.linguist": { + "source": "iana" + }, + "text/vnd.wap.si": { + "source": "iana" + }, + "text/vnd.wap.sl": { + "source": "iana" + }, + "text/vnd.wap.wml": { + "source": "iana", + "extensions": ["wml"] + }, + "text/vnd.wap.wmlscript": { + "source": "iana", + "extensions": ["wmls"] + }, + "text/vtt": { + "charset": "UTF-8", + "compressible": true, + "extensions": ["vtt"] + }, + "text/x-asm": { + "source": "apache", + "extensions": ["s","asm"] + }, + "text/x-c": { + "source": "apache", + "extensions": ["c","cc","cxx","cpp","h","hh","dic"] + }, + "text/x-component": { + "source": "nginx", + "extensions": ["htc"] + }, + "text/x-fortran": { + "source": "apache", + "extensions": ["f","for","f77","f90"] + }, + "text/x-gwt-rpc": { + "compressible": true + }, + "text/x-handlebars-template": { + "extensions": ["hbs"] + }, + "text/x-java-source": { + "source": "apache", + "extensions": ["java"] + }, + "text/x-jquery-tmpl": { + "compressible": true + }, + "text/x-lua": { + "extensions": ["lua"] + }, + "text/x-markdown": { + "compressible": true, + "extensions": ["markdown","md","mkd"] + }, + "text/x-nfo": { + "source": "apache", + "extensions": ["nfo"] + }, + "text/x-opml": { + "source": "apache", + "extensions": ["opml"] + }, + "text/x-pascal": { + "source": "apache", + "extensions": ["p","pas"] + }, + "text/x-processing": { + "compressible": true, + "extensions": ["pde"] + }, + "text/x-sass": { + "extensions": ["sass"] + }, + "text/x-scss": { + "extensions": ["scss"] + }, + "text/x-setext": { + "source": "apache", + "extensions": ["etx"] + }, + "text/x-sfv": { + "source": "apache", + "extensions": ["sfv"] + }, + "text/x-suse-ymp": { + "compressible": true, + "extensions": ["ymp"] + }, + "text/x-uuencode": { + "source": "apache", + "extensions": ["uu"] + }, + "text/x-vcalendar": { + "source": "apache", + "extensions": ["vcs"] + }, + "text/x-vcard": { + "source": "apache", + "extensions": ["vcf"] + }, + "text/xml": { + "source": "iana", + "compressible": true, + "extensions": ["xml"] + }, + "text/xml-external-parsed-entity": { + "source": "iana" + }, + "text/yaml": { + "extensions": ["yaml","yml"] + }, + "video/1d-interleaved-parityfec": { + "source": "apache" + }, + "video/3gpp": { + "source": "apache", + "extensions": ["3gp","3gpp"] + }, + "video/3gpp-tt": { + "source": "apache" + }, + "video/3gpp2": { + "source": "apache", + "extensions": ["3g2"] + }, + "video/bmpeg": { + "source": "apache" + }, + "video/bt656": { + "source": "apache" + }, + "video/celb": { + "source": "apache" + }, + "video/dv": { + "source": "apache" + }, + "video/h261": { + "source": "apache", + "extensions": ["h261"] + }, + "video/h263": { + "source": "apache", + "extensions": ["h263"] + }, + "video/h263-1998": { + "source": "apache" + }, + "video/h263-2000": { + "source": "apache" + }, + "video/h264": { + "source": "apache", + "extensions": ["h264"] + }, + "video/h264-rcdo": { + "source": "apache" + }, + "video/h264-svc": { + "source": "apache" + }, + "video/jpeg": { + "source": "apache", + "extensions": ["jpgv"] + }, + "video/jpeg2000": { + "source": "apache" + }, + "video/jpm": { + "source": "apache", + "extensions": ["jpm","jpgm"] + }, + "video/mj2": { + "source": "apache", + "extensions": ["mj2","mjp2"] + }, + "video/mp1s": { + "source": "apache" + }, + "video/mp2p": { + "source": "apache" + }, + "video/mp2t": { + "source": "apache", + "extensions": ["ts"] + }, + "video/mp4": { + "source": "apache", + "compressible": false, + "extensions": ["mp4","mp4v","mpg4"] + }, + "video/mp4v-es": { + "source": "apache" + }, + "video/mpeg": { + "source": "apache", + "compressible": false, + "extensions": ["mpeg","mpg","mpe","m1v","m2v"] + }, + "video/mpeg4-generic": { + "source": "apache" + }, + "video/mpv": { + "source": "apache" + }, + "video/nv": { + "source": "apache" + }, + "video/ogg": { + "source": "apache", + "compressible": false, + "extensions": ["ogv"] + }, + "video/parityfec": { + "source": "apache" + }, + "video/pointer": { + "source": "apache" + }, + "video/quicktime": { + "source": "apache", + "compressible": false, + "extensions": ["qt","mov"] + }, + "video/raw": { + "source": "apache" + }, + "video/rtp-enc-aescm128": { + "source": "apache" + }, + "video/rtx": { + "source": "apache" + }, + "video/smpte292m": { + "source": "apache" + }, + "video/ulpfec": { + "source": "apache" + }, + "video/vc1": { + "source": "apache" + }, + "video/vnd.cctv": { + "source": "apache" + }, + "video/vnd.dece.hd": { + "source": "apache", + "extensions": ["uvh","uvvh"] + }, + "video/vnd.dece.mobile": { + "source": "apache", + "extensions": ["uvm","uvvm"] + }, + "video/vnd.dece.mp4": { + "source": "apache" + }, + "video/vnd.dece.pd": { + "source": "apache", + "extensions": ["uvp","uvvp"] + }, + "video/vnd.dece.sd": { + "source": "apache", + "extensions": ["uvs","uvvs"] + }, + "video/vnd.dece.video": { + "source": "apache", + "extensions": ["uvv","uvvv"] + }, + "video/vnd.directv.mpeg": { + "source": "apache" + }, + "video/vnd.directv.mpeg-tts": { + "source": "apache" + }, + "video/vnd.dlna.mpeg-tts": { + "source": "apache" + }, + "video/vnd.dvb.file": { + "source": "apache", + "extensions": ["dvb"] + }, + "video/vnd.fvt": { + "source": "apache", + "extensions": ["fvt"] + }, + "video/vnd.hns.video": { + "source": "apache" + }, + "video/vnd.iptvforum.1dparityfec-1010": { + "source": "apache" + }, + "video/vnd.iptvforum.1dparityfec-2005": { + "source": "apache" + }, + "video/vnd.iptvforum.2dparityfec-1010": { + "source": "apache" + }, + "video/vnd.iptvforum.2dparityfec-2005": { + "source": "apache" + }, + "video/vnd.iptvforum.ttsavc": { + "source": "apache" + }, + "video/vnd.iptvforum.ttsmpeg2": { + "source": "apache" + }, + "video/vnd.motorola.video": { + "source": "apache" + }, + "video/vnd.motorola.videop": { + "source": "apache" + }, + "video/vnd.mpegurl": { + "source": "apache", + "extensions": ["mxu","m4u"] + }, + "video/vnd.ms-playready.media.pyv": { + "source": "apache", + "extensions": ["pyv"] + }, + "video/vnd.nokia.interleaved-multimedia": { + "source": "apache" + }, + "video/vnd.nokia.videovoip": { + "source": "apache" + }, + "video/vnd.objectvideo": { + "source": "apache" + }, + "video/vnd.sealed.mpeg1": { + "source": "apache" + }, + "video/vnd.sealed.mpeg4": { + "source": "apache" + }, + "video/vnd.sealed.swf": { + "source": "apache" + }, + "video/vnd.sealedmedia.softseal.mov": { + "source": "apache" + }, + "video/vnd.uvvu.mp4": { + "source": "apache", + "extensions": ["uvu","uvvu"] + }, + "video/vnd.vivo": { + "source": "apache", + "extensions": ["viv"] + }, + "video/webm": { + "source": "apache", + "compressible": false, + "extensions": ["webm"] + }, + "video/x-f4v": { + "source": "apache", + "extensions": ["f4v"] + }, + "video/x-fli": { + "source": "apache", + "extensions": ["fli"] + }, + "video/x-flv": { + "source": "apache", + "compressible": false, + "extensions": ["flv"] + }, + "video/x-m4v": { + "source": "apache", + "extensions": ["m4v"] + }, + "video/x-matroska": { + "source": "apache", + "compressible": false, + "extensions": ["mkv","mk3d","mks"] + }, + "video/x-mng": { + "source": "apache", + "extensions": ["mng"] + }, + "video/x-ms-asf": { + "source": "apache", + "extensions": ["asf","asx"] + }, + "video/x-ms-vob": { + "source": "apache", + "extensions": ["vob"] + }, + "video/x-ms-wm": { + "source": "apache", + "extensions": ["wm"] + }, + "video/x-ms-wmv": { + "source": "apache", + "compressible": false, + "extensions": ["wmv"] + }, + "video/x-ms-wmx": { + "source": "apache", + "extensions": ["wmx"] + }, + "video/x-ms-wvx": { + "source": "apache", + "extensions": ["wvx"] + }, + "video/x-msvideo": { + "source": "apache", + "extensions": ["avi"] + }, + "video/x-sgi-movie": { + "source": "apache", + "extensions": ["movie"] + }, + "video/x-smv": { + "source": "apache", + "extensions": ["smv"] + }, + "x-conference/x-cooltalk": { + "source": "apache", + "extensions": ["ice"] + }, + "x-shader/x-fragment": { + "compressible": true + }, + "x-shader/x-vertex": { + "compressible": true + } +} diff --git a/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/index.js b/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/index.js new file mode 100644 index 0000000..551031f --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/index.js @@ -0,0 +1,11 @@ +/*! + * mime-db + * Copyright(c) 2014 Jonathan Ong + * MIT Licensed + */ + +/** + * Module exports. + */ + +module.exports = require('./db.json') diff --git a/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/package.json b/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/package.json new file mode 100644 index 0000000..509af96 --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db/package.json @@ -0,0 +1,93 @@ +{ + "name": "mime-db", + "description": "Media Type Database", + "version": "1.20.0", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + { + "name": "Robert Kieffer", + "email": "robert@broofa.com", + "url": "http://github.com/broofa" + } + ], + "license": "MIT", + "keywords": [ + "mime", + "db", + "type", + "types", + "database", + "charset", + "charsets" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/mime-db" + }, + "devDependencies": { + "bluebird": "2.10.0", + "co": "4.6.0", + "cogent": "1.0.1", + "csv-parse": "1.0.0", + "gnode": "0.1.1", + "istanbul": "0.4.0", + "mocha": "1.21.5", + "raw-body": "2.1.4", + "stream-to-array": "2.2.0" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "README.md", + "db.json", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "build": "node scripts/build", + "fetch": "gnode scripts/fetch-apache && gnode scripts/fetch-iana && gnode scripts/fetch-nginx", + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/", + "update": "npm run fetch && npm run build" + }, + "gitHead": "20c99312645c05ab8466701ede01bd5cd3ac7bc4", + "bugs": { + "url": "https://github.com/jshttp/mime-db/issues" + }, + "homepage": "https://github.com/jshttp/mime-db", + "_id": "mime-db@1.20.0", + "_shasum": "496f90fd01fe0e031c8823ec3aa9450ffda18ed8", + "_from": "mime-db@>=1.20.0 <1.21.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "496f90fd01fe0e031c8823ec3aa9450ffda18ed8", + "tarball": "http://registry.npmjs.org/mime-db/-/mime-db-1.20.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.20.0.tgz" +} diff --git a/node_modules/express/node_modules/accepts/node_modules/mime-types/package.json b/node_modules/express/node_modules/accepts/node_modules/mime-types/package.json new file mode 100644 index 0000000..ffd4924 --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/mime-types/package.json @@ -0,0 +1,84 @@ +{ + "name": "mime-types", + "description": "The ultimate javascript content-type utility.", + "version": "2.1.8", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jeremiah Senkpiel", + "email": "fishrock123@rocketmail.com", + "url": "https://searchbeam.jit.su" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + } + ], + "license": "MIT", + "keywords": [ + "mime", + "types" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/jshttp/mime-types.git" + }, + "dependencies": { + "mime-db": "~1.20.0" + }, + "devDependencies": { + "istanbul": "0.4.1", + "mocha": "~1.21.5" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec test/test.js", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot test/test.js", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter dot test/test.js" + }, + "gitHead": "100876a23fab896d8cf0d904fc9778dbdfc1695b", + "bugs": { + "url": "https://github.com/jshttp/mime-types/issues" + }, + "homepage": "https://github.com/jshttp/mime-types", + "_id": "mime-types@2.1.8", + "_shasum": "faf57823de04bc7cbff4ee82c6b63946e812ae72", + "_from": "mime-types@>=2.1.7 <2.2.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "faf57823de04bc7cbff4ee82c6b63946e812ae72", + "tarball": "http://registry.npmjs.org/mime-types/-/mime-types-2.1.8.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.8.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/express/node_modules/accepts/node_modules/negotiator/HISTORY.md b/node_modules/express/node_modules/accepts/node_modules/negotiator/HISTORY.md new file mode 100644 index 0000000..aa2a7c4 --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/negotiator/HISTORY.md @@ -0,0 +1,76 @@ +0.5.3 / 2015-05-10 +================== + + * Fix media type parameter matching to be case-insensitive + +0.5.2 / 2015-05-06 +================== + + * Fix comparing media types with quoted values + * Fix splitting media types with quoted commas + +0.5.1 / 2015-02-14 +================== + + * Fix preference sorting to be stable for long acceptable lists + +0.5.0 / 2014-12-18 +================== + + * Fix list return order when large accepted list + * Fix missing identity encoding when q=0 exists + * Remove dynamic building of Negotiator class + +0.4.9 / 2014-10-14 +================== + + * Fix error when media type has invalid parameter + +0.4.8 / 2014-09-28 +================== + + * Fix all negotiations to be case-insensitive + * Stable sort preferences of same quality according to client order + * Support Node.js 0.6 + +0.4.7 / 2014-06-24 +================== + + * Handle invalid provided languages + * Handle invalid provided media types + +0.4.6 / 2014-06-11 +================== + + * Order by specificity when quality is the same + +0.4.5 / 2014-05-29 +================== + + * Fix regression in empty header handling + +0.4.4 / 2014-05-29 +================== + + * Fix behaviors when headers are not present + +0.4.3 / 2014-04-16 +================== + + * Handle slashes on media params correctly + +0.4.2 / 2014-02-28 +================== + + * Fix media type sorting + * Handle media types params strictly + +0.4.1 / 2014-01-16 +================== + + * Use most specific matches + +0.4.0 / 2014-01-09 +================== + + * Remove preferred prefix from methods diff --git a/node_modules/express/node_modules/accepts/node_modules/negotiator/LICENSE b/node_modules/express/node_modules/accepts/node_modules/negotiator/LICENSE new file mode 100644 index 0000000..ea6b9e2 --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/negotiator/LICENSE @@ -0,0 +1,24 @@ +(The MIT License) + +Copyright (c) 2012-2014 Federico Romero +Copyright (c) 2012-2014 Isaac Z. Schlueter +Copyright (c) 2014-2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/accepts/node_modules/negotiator/README.md b/node_modules/express/node_modules/accepts/node_modules/negotiator/README.md new file mode 100644 index 0000000..ef507fa --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/negotiator/README.md @@ -0,0 +1,203 @@ +# negotiator + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +An HTTP content negotiator for Node.js + +## Installation + +```sh +$ npm install negotiator +``` + +## API + +```js +var Negotiator = require('negotiator') +``` + +### Accept Negotiation + +```js +availableMediaTypes = ['text/html', 'text/plain', 'application/json'] + +// The negotiator constructor receives a request object +negotiator = new Negotiator(request) + +// Let's say Accept header is 'text/html, application/*;q=0.2, image/jpeg;q=0.8' + +negotiator.mediaTypes() +// -> ['text/html', 'image/jpeg', 'application/*'] + +negotiator.mediaTypes(availableMediaTypes) +// -> ['text/html', 'application/json'] + +negotiator.mediaType(availableMediaTypes) +// -> 'text/html' +``` + +You can check a working example at `examples/accept.js`. + +#### Methods + +##### mediaType() + +Returns the most preferred media type from the client. + +##### mediaType(availableMediaType) + +Returns the most preferred media type from a list of available media types. + +##### mediaTypes() + +Returns an array of preferred media types ordered by the client preference. + +##### mediaTypes(availableMediaTypes) + +Returns an array of preferred media types ordered by priority from a list of +available media types. + +### Accept-Language Negotiation + +```js +negotiator = new Negotiator(request) + +availableLanguages = 'en', 'es', 'fr' + +// Let's say Accept-Language header is 'en;q=0.8, es, pt' + +negotiator.languages() +// -> ['es', 'pt', 'en'] + +negotiator.languages(availableLanguages) +// -> ['es', 'en'] + +language = negotiator.language(availableLanguages) +// -> 'es' +``` + +You can check a working example at `examples/language.js`. + +#### Methods + +##### language() + +Returns the most preferred language from the client. + +##### language(availableLanguages) + +Returns the most preferred language from a list of available languages. + +##### languages() + +Returns an array of preferred languages ordered by the client preference. + +##### languages(availableLanguages) + +Returns an array of preferred languages ordered by priority from a list of +available languages. + +### Accept-Charset Negotiation + +```js +availableCharsets = ['utf-8', 'iso-8859-1', 'iso-8859-5'] + +negotiator = new Negotiator(request) + +// Let's say Accept-Charset header is 'utf-8, iso-8859-1;q=0.8, utf-7;q=0.2' + +negotiator.charsets() +// -> ['utf-8', 'iso-8859-1', 'utf-7'] + +negotiator.charsets(availableCharsets) +// -> ['utf-8', 'iso-8859-1'] + +negotiator.charset(availableCharsets) +// -> 'utf-8' +``` + +You can check a working example at `examples/charset.js`. + +#### Methods + +##### charset() + +Returns the most preferred charset from the client. + +##### charset(availableCharsets) + +Returns the most preferred charset from a list of available charsets. + +##### charsets() + +Returns an array of preferred charsets ordered by the client preference. + +##### charsets(availableCharsets) + +Returns an array of preferred charsets ordered by priority from a list of +available charsets. + +### Accept-Encoding Negotiation + +```js +availableEncodings = ['identity', 'gzip'] + +negotiator = new Negotiator(request) + +// Let's say Accept-Encoding header is 'gzip, compress;q=0.2, identity;q=0.5' + +negotiator.encodings() +// -> ['gzip', 'identity', 'compress'] + +negotiator.encodings(availableEncodings) +// -> ['gzip', 'identity'] + +negotiator.encoding(availableEncodings) +// -> 'gzip' +``` + +You can check a working example at `examples/encoding.js`. + +#### Methods + +##### encoding() + +Returns the most preferred encoding from the client. + +##### encoding(availableEncodings) + +Returns the most preferred encoding from a list of available encodings. + +##### encodings() + +Returns an array of preferred encodings ordered by the client preference. + +##### encodings(availableEncodings) + +Returns an array of preferred encodings ordered by priority from a list of +available encodings. + +## See Also + +The [accepts](https://npmjs.org/package/accepts#readme) module builds on +this module and provides an alternative interface, mime type validation, +and more. + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/negotiator.svg +[npm-url]: https://npmjs.org/package/negotiator +[node-version-image]: https://img.shields.io/node/v/negotiator.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/negotiator/master.svg +[travis-url]: https://travis-ci.org/jshttp/negotiator +[coveralls-image]: https://img.shields.io/coveralls/jshttp/negotiator/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/negotiator?branch=master +[downloads-image]: https://img.shields.io/npm/dm/negotiator.svg +[downloads-url]: https://npmjs.org/package/negotiator diff --git a/node_modules/express/node_modules/accepts/node_modules/negotiator/index.js b/node_modules/express/node_modules/accepts/node_modules/negotiator/index.js new file mode 100644 index 0000000..edae9cf --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/negotiator/index.js @@ -0,0 +1,62 @@ + +var preferredCharsets = require('./lib/charset'); +var preferredEncodings = require('./lib/encoding'); +var preferredLanguages = require('./lib/language'); +var preferredMediaTypes = require('./lib/mediaType'); + +module.exports = Negotiator; +Negotiator.Negotiator = Negotiator; + +function Negotiator(request) { + if (!(this instanceof Negotiator)) { + return new Negotiator(request); + } + + this.request = request; +} + +Negotiator.prototype.charset = function charset(available) { + var set = this.charsets(available); + return set && set[0]; +}; + +Negotiator.prototype.charsets = function charsets(available) { + return preferredCharsets(this.request.headers['accept-charset'], available); +}; + +Negotiator.prototype.encoding = function encoding(available) { + var set = this.encodings(available); + return set && set[0]; +}; + +Negotiator.prototype.encodings = function encodings(available) { + return preferredEncodings(this.request.headers['accept-encoding'], available); +}; + +Negotiator.prototype.language = function language(available) { + var set = this.languages(available); + return set && set[0]; +}; + +Negotiator.prototype.languages = function languages(available) { + return preferredLanguages(this.request.headers['accept-language'], available); +}; + +Negotiator.prototype.mediaType = function mediaType(available) { + var set = this.mediaTypes(available); + return set && set[0]; +}; + +Negotiator.prototype.mediaTypes = function mediaTypes(available) { + return preferredMediaTypes(this.request.headers.accept, available); +}; + +// Backwards compatibility +Negotiator.prototype.preferredCharset = Negotiator.prototype.charset; +Negotiator.prototype.preferredCharsets = Negotiator.prototype.charsets; +Negotiator.prototype.preferredEncoding = Negotiator.prototype.encoding; +Negotiator.prototype.preferredEncodings = Negotiator.prototype.encodings; +Negotiator.prototype.preferredLanguage = Negotiator.prototype.language; +Negotiator.prototype.preferredLanguages = Negotiator.prototype.languages; +Negotiator.prototype.preferredMediaType = Negotiator.prototype.mediaType; +Negotiator.prototype.preferredMediaTypes = Negotiator.prototype.mediaTypes; diff --git a/node_modules/express/node_modules/accepts/node_modules/negotiator/lib/charset.js b/node_modules/express/node_modules/accepts/node_modules/negotiator/lib/charset.js new file mode 100644 index 0000000..7abd17c --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/negotiator/lib/charset.js @@ -0,0 +1,102 @@ +module.exports = preferredCharsets; +preferredCharsets.preferredCharsets = preferredCharsets; + +function parseAcceptCharset(accept) { + var accepts = accept.split(','); + + for (var i = 0, j = 0; i < accepts.length; i++) { + var charset = parseCharset(accepts[i].trim(), i); + + if (charset) { + accepts[j++] = charset; + } + } + + // trim accepts + accepts.length = j; + + return accepts; +} + +function parseCharset(s, i) { + var match = s.match(/^\s*(\S+?)\s*(?:;(.*))?$/); + if (!match) return null; + + var charset = match[1]; + var q = 1; + if (match[2]) { + var params = match[2].split(';') + for (var i = 0; i < params.length; i ++) { + var p = params[i].trim().split('='); + if (p[0] === 'q') { + q = parseFloat(p[1]); + break; + } + } + } + + return { + charset: charset, + q: q, + i: i + }; +} + +function getCharsetPriority(charset, accepted, index) { + var priority = {o: -1, q: 0, s: 0}; + + for (var i = 0; i < accepted.length; i++) { + var spec = specify(charset, accepted[i], index); + + if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) { + priority = spec; + } + } + + return priority; +} + +function specify(charset, spec, index) { + var s = 0; + if(spec.charset.toLowerCase() === charset.toLowerCase()){ + s |= 1; + } else if (spec.charset !== '*' ) { + return null + } + + return { + i: index, + o: spec.i, + q: spec.q, + s: s + } +} + +function preferredCharsets(accept, provided) { + // RFC 2616 sec 14.2: no header = * + var accepts = parseAcceptCharset(accept === undefined ? '*' : accept || ''); + + if (!provided) { + // sorted list of all charsets + return accepts.filter(isQuality).sort(compareSpecs).map(function getCharset(spec) { + return spec.charset; + }); + } + + var priorities = provided.map(function getPriority(type, index) { + return getCharsetPriority(type, accepts, index); + }); + + // sorted list of accepted charsets + return priorities.filter(isQuality).sort(compareSpecs).map(function getCharset(priority) { + return provided[priorities.indexOf(priority)]; + }); +} + +function compareSpecs(a, b) { + return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0; +} + +function isQuality(spec) { + return spec.q > 0; +} diff --git a/node_modules/express/node_modules/accepts/node_modules/negotiator/lib/encoding.js b/node_modules/express/node_modules/accepts/node_modules/negotiator/lib/encoding.js new file mode 100644 index 0000000..7fed673 --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/negotiator/lib/encoding.js @@ -0,0 +1,118 @@ +module.exports = preferredEncodings; +preferredEncodings.preferredEncodings = preferredEncodings; + +function parseAcceptEncoding(accept) { + var accepts = accept.split(','); + var hasIdentity = false; + var minQuality = 1; + + for (var i = 0, j = 0; i < accepts.length; i++) { + var encoding = parseEncoding(accepts[i].trim(), i); + + if (encoding) { + accepts[j++] = encoding; + hasIdentity = hasIdentity || specify('identity', encoding); + minQuality = Math.min(minQuality, encoding.q || 1); + } + } + + if (!hasIdentity) { + /* + * If identity doesn't explicitly appear in the accept-encoding header, + * it's added to the list of acceptable encoding with the lowest q + */ + accepts[j++] = { + encoding: 'identity', + q: minQuality, + i: i + }; + } + + // trim accepts + accepts.length = j; + + return accepts; +} + +function parseEncoding(s, i) { + var match = s.match(/^\s*(\S+?)\s*(?:;(.*))?$/); + + if (!match) return null; + + var encoding = match[1]; + var q = 1; + if (match[2]) { + var params = match[2].split(';'); + for (var i = 0; i < params.length; i ++) { + var p = params[i].trim().split('='); + if (p[0] === 'q') { + q = parseFloat(p[1]); + break; + } + } + } + + return { + encoding: encoding, + q: q, + i: i + }; +} + +function getEncodingPriority(encoding, accepted, index) { + var priority = {o: -1, q: 0, s: 0}; + + for (var i = 0; i < accepted.length; i++) { + var spec = specify(encoding, accepted[i], index); + + if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) { + priority = spec; + } + } + + return priority; +} + +function specify(encoding, spec, index) { + var s = 0; + if(spec.encoding.toLowerCase() === encoding.toLowerCase()){ + s |= 1; + } else if (spec.encoding !== '*' ) { + return null + } + + return { + i: index, + o: spec.i, + q: spec.q, + s: s + } +}; + +function preferredEncodings(accept, provided) { + var accepts = parseAcceptEncoding(accept || ''); + + if (!provided) { + // sorted list of all encodings + return accepts.filter(isQuality).sort(compareSpecs).map(function getEncoding(spec) { + return spec.encoding; + }); + } + + var priorities = provided.map(function getPriority(type, index) { + return getEncodingPriority(type, accepts, index); + }); + + // sorted list of accepted encodings + return priorities.filter(isQuality).sort(compareSpecs).map(function getEncoding(priority) { + return provided[priorities.indexOf(priority)]; + }); +} + +function compareSpecs(a, b) { + return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0; +} + +function isQuality(spec) { + return spec.q > 0; +} diff --git a/node_modules/express/node_modules/accepts/node_modules/negotiator/lib/language.js b/node_modules/express/node_modules/accepts/node_modules/negotiator/lib/language.js new file mode 100644 index 0000000..ed9e1ec --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/negotiator/lib/language.js @@ -0,0 +1,112 @@ +module.exports = preferredLanguages; +preferredLanguages.preferredLanguages = preferredLanguages; + +function parseAcceptLanguage(accept) { + var accepts = accept.split(','); + + for (var i = 0, j = 0; i < accepts.length; i++) { + var langauge = parseLanguage(accepts[i].trim(), i); + + if (langauge) { + accepts[j++] = langauge; + } + } + + // trim accepts + accepts.length = j; + + return accepts; +} + +function parseLanguage(s, i) { + var match = s.match(/^\s*(\S+?)(?:-(\S+?))?\s*(?:;(.*))?$/); + if (!match) return null; + + var prefix = match[1], + suffix = match[2], + full = prefix; + + if (suffix) full += "-" + suffix; + + var q = 1; + if (match[3]) { + var params = match[3].split(';') + for (var i = 0; i < params.length; i ++) { + var p = params[i].split('='); + if (p[0] === 'q') q = parseFloat(p[1]); + } + } + + return { + prefix: prefix, + suffix: suffix, + q: q, + i: i, + full: full + }; +} + +function getLanguagePriority(language, accepted, index) { + var priority = {o: -1, q: 0, s: 0}; + + for (var i = 0; i < accepted.length; i++) { + var spec = specify(language, accepted[i], index); + + if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) { + priority = spec; + } + } + + return priority; +} + +function specify(language, spec, index) { + var p = parseLanguage(language) + if (!p) return null; + var s = 0; + if(spec.full.toLowerCase() === p.full.toLowerCase()){ + s |= 4; + } else if (spec.prefix.toLowerCase() === p.full.toLowerCase()) { + s |= 2; + } else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) { + s |= 1; + } else if (spec.full !== '*' ) { + return null + } + + return { + i: index, + o: spec.i, + q: spec.q, + s: s + } +}; + +function preferredLanguages(accept, provided) { + // RFC 2616 sec 14.4: no header = * + var accepts = parseAcceptLanguage(accept === undefined ? '*' : accept || ''); + + if (!provided) { + // sorted list of all languages + return accepts.filter(isQuality).sort(compareSpecs).map(function getLanguage(spec) { + return spec.full; + }); + } + + var priorities = provided.map(function getPriority(type, index) { + return getLanguagePriority(type, accepts, index); + }); + + // sorted list of accepted languages + return priorities.filter(isQuality).sort(compareSpecs).map(function getLanguage(priority) { + return provided[priorities.indexOf(priority)]; + }); +} + +function compareSpecs(a, b) { + return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0; +} + +function isQuality(spec) { + return spec.q > 0; +} diff --git a/node_modules/express/node_modules/accepts/node_modules/negotiator/lib/mediaType.js b/node_modules/express/node_modules/accepts/node_modules/negotiator/lib/mediaType.js new file mode 100644 index 0000000..4170c25 --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/negotiator/lib/mediaType.js @@ -0,0 +1,179 @@ +/** + * negotiator + * Copyright(c) 2012 Isaac Z. Schlueter + * Copyright(c) 2014 Federico Romero + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +module.exports = preferredMediaTypes; +preferredMediaTypes.preferredMediaTypes = preferredMediaTypes; + +function parseAccept(accept) { + var accepts = splitMediaTypes(accept); + + for (var i = 0, j = 0; i < accepts.length; i++) { + var mediaType = parseMediaType(accepts[i].trim(), i); + + if (mediaType) { + accepts[j++] = mediaType; + } + } + + // trim accepts + accepts.length = j; + + return accepts; +}; + +function parseMediaType(s, i) { + var match = s.match(/\s*(\S+?)\/([^;\s]+)\s*(?:;(.*))?/); + if (!match) return null; + + var type = match[1], + subtype = match[2], + full = "" + type + "/" + subtype, + params = {}, + q = 1; + + if (match[3]) { + params = match[3].split(';').map(function(s) { + return s.trim().split('='); + }).reduce(function (set, p) { + var name = p[0].toLowerCase(); + var value = p[1]; + + set[name] = value && value[0] === '"' && value[value.length - 1] === '"' + ? value.substr(1, value.length - 2) + : value; + + return set; + }, params); + + if (params.q != null) { + q = parseFloat(params.q); + delete params.q; + } + } + + return { + type: type, + subtype: subtype, + params: params, + q: q, + i: i, + full: full + }; +} + +function getMediaTypePriority(type, accepted, index) { + var priority = {o: -1, q: 0, s: 0}; + + for (var i = 0; i < accepted.length; i++) { + var spec = specify(type, accepted[i], index); + + if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) { + priority = spec; + } + } + + return priority; +} + +function specify(type, spec, index) { + var p = parseMediaType(type); + var s = 0; + + if (!p) { + return null; + } + + if(spec.type.toLowerCase() == p.type.toLowerCase()) { + s |= 4 + } else if(spec.type != '*') { + return null; + } + + if(spec.subtype.toLowerCase() == p.subtype.toLowerCase()) { + s |= 2 + } else if(spec.subtype != '*') { + return null; + } + + var keys = Object.keys(spec.params); + if (keys.length > 0) { + if (keys.every(function (k) { + return spec.params[k] == '*' || (spec.params[k] || '').toLowerCase() == (p.params[k] || '').toLowerCase(); + })) { + s |= 1 + } else { + return null + } + } + + return { + i: index, + o: spec.i, + q: spec.q, + s: s, + } + +} + +function preferredMediaTypes(accept, provided) { + // RFC 2616 sec 14.2: no header = */* + var accepts = parseAccept(accept === undefined ? '*/*' : accept || ''); + + if (!provided) { + // sorted list of all types + return accepts.filter(isQuality).sort(compareSpecs).map(function getType(spec) { + return spec.full; + }); + } + + var priorities = provided.map(function getPriority(type, index) { + return getMediaTypePriority(type, accepts, index); + }); + + // sorted list of accepted types + return priorities.filter(isQuality).sort(compareSpecs).map(function getType(priority) { + return provided[priorities.indexOf(priority)]; + }); +} + +function compareSpecs(a, b) { + return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0; +} + +function isQuality(spec) { + return spec.q > 0; +} + +function quoteCount(string) { + var count = 0; + var index = 0; + + while ((index = string.indexOf('"', index)) !== -1) { + count++; + index++; + } + + return count; +} + +function splitMediaTypes(accept) { + var accepts = accept.split(','); + + for (var i = 1, j = 0; i < accepts.length; i++) { + if (quoteCount(accepts[j]) % 2 == 0) { + accepts[++j] = accepts[i]; + } else { + accepts[j] += ',' + accepts[i]; + } + } + + // trim accepts + accepts.length = j + 1; + + return accepts; +} diff --git a/node_modules/express/node_modules/accepts/node_modules/negotiator/package.json b/node_modules/express/node_modules/accepts/node_modules/negotiator/package.json new file mode 100644 index 0000000..f257e1d --- /dev/null +++ b/node_modules/express/node_modules/accepts/node_modules/negotiator/package.json @@ -0,0 +1,85 @@ +{ + "name": "negotiator", + "description": "HTTP content negotiation", + "version": "0.5.3", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Federico Romero", + "email": "federico.romero@outboxlabs.com" + }, + { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + } + ], + "license": "MIT", + "keywords": [ + "http", + "content negotiation", + "accept", + "accept-language", + "accept-encoding", + "accept-charset" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/negotiator" + }, + "devDependencies": { + "istanbul": "0.3.9", + "mocha": "~1.21.5" + }, + "files": [ + "lib/", + "HISTORY.md", + "LICENSE", + "index.js", + "README.md" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec --check-leaks --bail test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "cbb717b3f164f25820f90b160cda6d0166b9d922", + "bugs": { + "url": "https://github.com/jshttp/negotiator/issues" + }, + "homepage": "https://github.com/jshttp/negotiator", + "_id": "negotiator@0.5.3", + "_shasum": "269d5c476810ec92edbe7b6c2f28316384f9a7e8", + "_from": "negotiator@0.5.3", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "federomero", + "email": "federomero@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + } + ], + "dist": { + "shasum": "269d5c476810ec92edbe7b6c2f28316384f9a7e8", + "tarball": "http://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz" +} diff --git a/node_modules/express/node_modules/accepts/package.json b/node_modules/express/node_modules/accepts/package.json new file mode 100644 index 0000000..abd0b16 --- /dev/null +++ b/node_modules/express/node_modules/accepts/package.json @@ -0,0 +1,97 @@ +{ + "name": "accepts", + "description": "Higher-level content negotiation", + "version": "1.2.13", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/jshttp/accepts" + }, + "dependencies": { + "mime-types": "~2.1.6", + "negotiator": "0.5.3" + }, + "devDependencies": { + "istanbul": "0.3.19", + "mocha": "~1.21.5" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec --check-leaks --bail test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "keywords": [ + "content", + "negotiation", + "accept", + "accepts" + ], + "gitHead": "b7e15ecb25dacc0b2133ed0553d64f8a79537e01", + "bugs": { + "url": "https://github.com/jshttp/accepts/issues" + }, + "homepage": "https://github.com/jshttp/accepts", + "_id": "accepts@1.2.13", + "_shasum": "e5f1f3928c6d95fd96558c36ec3d9d0de4a6ecea", + "_from": "accepts@>=1.2.12 <1.3.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "federomero", + "email": "federomero@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + }, + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + { + "name": "defunctzombie", + "email": "shtylman@gmail.com" + } + ], + "dist": { + "shasum": "e5f1f3928c6d95fd96558c36ec3d9d0de4a6ecea", + "tarball": "http://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz" +} diff --git a/node_modules/express/node_modules/array-flatten/LICENSE b/node_modules/express/node_modules/array-flatten/LICENSE new file mode 100644 index 0000000..983fbe8 --- /dev/null +++ b/node_modules/express/node_modules/array-flatten/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Blake Embrey (hello@blakeembrey.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/express/node_modules/array-flatten/README.md b/node_modules/express/node_modules/array-flatten/README.md new file mode 100644 index 0000000..91fa5b6 --- /dev/null +++ b/node_modules/express/node_modules/array-flatten/README.md @@ -0,0 +1,43 @@ +# Array Flatten + +[![NPM version][npm-image]][npm-url] +[![NPM downloads][downloads-image]][downloads-url] +[![Build status][travis-image]][travis-url] +[![Test coverage][coveralls-image]][coveralls-url] + +> Flatten an array of nested arrays into a single flat array. Accepts an optional depth. + +## Installation + +``` +npm install array-flatten --save +``` + +## Usage + +```javascript +var flatten = require('array-flatten') + +flatten([1, [2, [3, [4, [5], 6], 7], 8], 9]) +//=> [1, 2, 3, 4, 5, 6, 7, 8, 9] + +flatten([1, [2, [3, [4, [5], 6], 7], 8], 9], 2) +//=> [1, 2, 3, [4, [5], 6], 7, 8, 9] + +(function () { + flatten(arguments) //=> [1, 2, 3] +})(1, [2, 3]) +``` + +## License + +MIT + +[npm-image]: https://img.shields.io/npm/v/array-flatten.svg?style=flat +[npm-url]: https://npmjs.org/package/array-flatten +[downloads-image]: https://img.shields.io/npm/dm/array-flatten.svg?style=flat +[downloads-url]: https://npmjs.org/package/array-flatten +[travis-image]: https://img.shields.io/travis/blakeembrey/array-flatten.svg?style=flat +[travis-url]: https://travis-ci.org/blakeembrey/array-flatten +[coveralls-image]: https://img.shields.io/coveralls/blakeembrey/array-flatten.svg?style=flat +[coveralls-url]: https://coveralls.io/r/blakeembrey/array-flatten?branch=master diff --git a/node_modules/express/node_modules/array-flatten/array-flatten.js b/node_modules/express/node_modules/array-flatten/array-flatten.js new file mode 100644 index 0000000..089117b --- /dev/null +++ b/node_modules/express/node_modules/array-flatten/array-flatten.js @@ -0,0 +1,64 @@ +'use strict' + +/** + * Expose `arrayFlatten`. + */ +module.exports = arrayFlatten + +/** + * Recursive flatten function with depth. + * + * @param {Array} array + * @param {Array} result + * @param {Number} depth + * @return {Array} + */ +function flattenWithDepth (array, result, depth) { + for (var i = 0; i < array.length; i++) { + var value = array[i] + + if (depth > 0 && Array.isArray(value)) { + flattenWithDepth(value, result, depth - 1) + } else { + result.push(value) + } + } + + return result +} + +/** + * Recursive flatten function. Omitting depth is slightly faster. + * + * @param {Array} array + * @param {Array} result + * @return {Array} + */ +function flattenForever (array, result) { + for (var i = 0; i < array.length; i++) { + var value = array[i] + + if (Array.isArray(value)) { + flattenForever(value, result) + } else { + result.push(value) + } + } + + return result +} + +/** + * Flatten an array, with the ability to define a depth. + * + * @param {Array} array + * @param {Number} depth + * @return {Array} + */ +function arrayFlatten (array, depth) { + if (depth == null) { + return flattenForever(array, []) + } + + return flattenWithDepth(array, [], depth) +} diff --git a/node_modules/express/node_modules/array-flatten/package.json b/node_modules/express/node_modules/array-flatten/package.json new file mode 100644 index 0000000..0ba4e42 --- /dev/null +++ b/node_modules/express/node_modules/array-flatten/package.json @@ -0,0 +1,61 @@ +{ + "name": "array-flatten", + "version": "1.1.1", + "description": "Flatten an array of nested arrays into a single flat array", + "main": "array-flatten.js", + "files": [ + "array-flatten.js", + "LICENSE" + ], + "scripts": { + "test": "istanbul cover _mocha -- -R spec" + }, + "repository": { + "type": "git", + "url": "git://github.com/blakeembrey/array-flatten.git" + }, + "keywords": [ + "array", + "flatten", + "arguments", + "depth" + ], + "author": { + "name": "Blake Embrey", + "email": "hello@blakeembrey.com", + "url": "http://blakeembrey.me" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/blakeembrey/array-flatten/issues" + }, + "homepage": "https://github.com/blakeembrey/array-flatten", + "devDependencies": { + "istanbul": "^0.3.13", + "mocha": "^2.2.4", + "pre-commit": "^1.0.7", + "standard": "^3.7.3" + }, + "gitHead": "1963a9189229d408e1e8f585a00c8be9edbd1803", + "_id": "array-flatten@1.1.1", + "_shasum": "9a5f699051b1e7073328f2a008968b64ea2955d2", + "_from": "array-flatten@1.1.1", + "_npmVersion": "2.11.3", + "_nodeVersion": "2.3.3", + "_npmUser": { + "name": "blakeembrey", + "email": "hello@blakeembrey.com" + }, + "maintainers": [ + { + "name": "blakeembrey", + "email": "hello@blakeembrey.com" + } + ], + "dist": { + "shasum": "9a5f699051b1e7073328f2a008968b64ea2955d2", + "tarball": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" +} diff --git a/node_modules/express/node_modules/content-disposition/HISTORY.md b/node_modules/express/node_modules/content-disposition/HISTORY.md new file mode 100644 index 0000000..1192551 --- /dev/null +++ b/node_modules/express/node_modules/content-disposition/HISTORY.md @@ -0,0 +1,40 @@ +0.5.0 / 2014-10-11 +================== + + * Add `parse` function + +0.4.0 / 2014-09-21 +================== + + * Expand non-Unicode `filename` to the full ISO-8859-1 charset + +0.3.0 / 2014-09-20 +================== + + * Add `fallback` option + * Add `type` option + +0.2.0 / 2014-09-19 +================== + + * Reduce ambiguity of file names with hex escape in buggy browsers + +0.1.2 / 2014-09-19 +================== + + * Fix periodic invalid Unicode filename header + +0.1.1 / 2014-09-19 +================== + + * Fix invalid characters appearing in `filename*` parameter + +0.1.0 / 2014-09-18 +================== + + * Make the `filename` argument optional + +0.0.0 / 2014-09-18 +================== + + * Initial release diff --git a/node_modules/express/node_modules/content-disposition/LICENSE b/node_modules/express/node_modules/content-disposition/LICENSE new file mode 100644 index 0000000..b7dce6c --- /dev/null +++ b/node_modules/express/node_modules/content-disposition/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2014 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/content-disposition/README.md b/node_modules/express/node_modules/content-disposition/README.md new file mode 100644 index 0000000..d265431 --- /dev/null +++ b/node_modules/express/node_modules/content-disposition/README.md @@ -0,0 +1,141 @@ +# content-disposition + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Create and parse HTTP `Content-Disposition` header + +## Installation + +```sh +$ npm install content-disposition +``` + +## API + +```js +var contentDisposition = require('content-disposition') +``` + +### contentDisposition(filename, options) + +Create an attachment `Content-Disposition` header value using the given file name, +if supplied. The `filename` is optional and if no file name is desired, but you +want to specify `options`, set `filename` to `undefined`. + +```js +res.setHeader('Content-Disposition', contentDisposition('∫ maths.pdf')) +``` + +**note** HTTP headers are of the ISO-8859-1 character set. If you are writing this +header through a means different from `setHeader` in Node.js, you'll want to specify +the `'binary'` encoding in Node.js. + +#### Options + +`contentDisposition` accepts these properties in the options object. + +##### fallback + +If the `filename` option is outside ISO-8859-1, then the file name is actually +stored in a supplemental field for clients that support Unicode file names and +a ISO-8859-1 version of the file name is automatically generated. + +This specifies the ISO-8859-1 file name to override the automatic generation or +disables the generation all together, defaults to `true`. + + - A string will specify the ISO-8859-1 file name to use in place of automatic + generation. + - `false` will disable including a ISO-8859-1 file name and only include the + Unicode version (unless the file name is already ISO-8859-1). + - `true` will enable automatic generation if the file name is outside ISO-8859-1. + +If the `filename` option is ISO-8859-1 and this option is specified and has a +different value, then the `filename` option is encoded in the extended field +and this set as the fallback field, even though they are both ISO-8859-1. + +##### type + +Specifies the disposition type, defaults to `"attachment"`. This can also be +`"inline"`, or any other value (all values except inline are treated like +`attachment`, but can convey additional information if both parties agree to +it). The type is normalized to lower-case. + +### contentDisposition.parse(string) + +```js +var disposition = contentDisposition.parse('attachment; filename="EURO rates.txt"; filename*=UTF-8\'\'%e2%82%ac%20rates.txt"'); +``` + +Parse a `Content-Disposition` header string. This automatically handles extended +("Unicode") parameters by decoding them and providing them under the standard +parameter name. This will return an object with the following properties (examples +are shown for the string `'attachment; filename="EURO rates.txt"; filename*=UTF-8\'\'%e2%82%ac%20rates.txt'`): + + - `type`: The disposition type (always lower case). Example: `'attachment'` + + - `parameters`: An object of the parameters in the disposition (name of parameter + always lower case and extended versions replace non-extended versions). Example: + `{filename: "€ rates.txt"}` + +## Examples + +### Send a file for download + +```js +var contentDisposition = require('content-disposition') +var destroy = require('destroy') +var http = require('http') +var onFinished = require('on-finished') + +var filePath = '/path/to/public/plans.pdf' + +http.createServer(function onRequest(req, res) { + // set headers + res.setHeader('Content-Type', 'application/pdf') + res.setHeader('Content-Disposition', contentDisposition(filePath)) + + // send file + var stream = fs.createReadStream(filePath) + stream.pipe(res) + onFinished(res, function (err) { + destroy(stream) + }) +}) +``` + +## Testing + +```sh +$ npm test +``` + +## References + +- [RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1][rfc-2616] +- [RFC 5987: Character Set and Language Encoding for Hypertext Transfer Protocol (HTTP) Header Field Parameters][rfc-5987] +- [RFC 6266: Use of the Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP)][rfc-6266] +- [Test Cases for HTTP Content-Disposition header field (RFC 6266) and the Encodings defined in RFCs 2047, 2231 and 5987][tc-2231] + +[rfc-2616]: https://tools.ietf.org/html/rfc2616 +[rfc-5987]: https://tools.ietf.org/html/rfc5987 +[rfc-6266]: https://tools.ietf.org/html/rfc6266 +[tc-2231]: http://greenbytes.de/tech/tc2231/ + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/content-disposition.svg?style=flat +[npm-url]: https://npmjs.org/package/content-disposition +[node-version-image]: https://img.shields.io/node/v/content-disposition.svg?style=flat +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/content-disposition.svg?style=flat +[travis-url]: https://travis-ci.org/jshttp/content-disposition +[coveralls-image]: https://img.shields.io/coveralls/jshttp/content-disposition.svg?style=flat +[coveralls-url]: https://coveralls.io/r/jshttp/content-disposition?branch=master +[downloads-image]: https://img.shields.io/npm/dm/content-disposition.svg?style=flat +[downloads-url]: https://npmjs.org/package/content-disposition diff --git a/node_modules/express/node_modules/content-disposition/index.js b/node_modules/express/node_modules/content-disposition/index.js new file mode 100644 index 0000000..fa3bc74 --- /dev/null +++ b/node_modules/express/node_modules/content-disposition/index.js @@ -0,0 +1,443 @@ +/*! + * content-disposition + * Copyright(c) 2014 Douglas Christopher Wilson + * MIT Licensed + */ + +/** + * Module exports. + */ + +module.exports = contentDisposition +module.exports.parse = parse + +/** + * Module dependencies. + */ + +var basename = require('path').basename + +/** + * RegExp to match non attr-char, *after* encodeURIComponent (i.e. not including "%") + */ + +var encodeUriAttrCharRegExp = /[\x00-\x20"'\(\)*,\/:;<=>?@\[\\\]\{\}\x7f]/g + +/** + * RegExp to match percent encoding escape. + */ + +var hexEscapeRegExp = /%[0-9A-Fa-f]{2}/ +var hexEscapeReplaceRegExp = /%([0-9A-Fa-f]{2})/g + +/** + * RegExp to match non-latin1 characters. + */ + +var nonLatin1RegExp = /[^\x20-\x7e\xa0-\xff]/g + +/** + * RegExp to match quoted-pair in RFC 2616 + * + * quoted-pair = "\" CHAR + * CHAR = + */ + +var qescRegExp = /\\([\u0000-\u007f])/g; + +/** + * RegExp to match chars that must be quoted-pair in RFC 2616 + */ + +var quoteRegExp = /([\\"])/g + +/** + * RegExp for various RFC 2616 grammar + * + * parameter = token "=" ( token | quoted-string ) + * token = 1* + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) + * qdtext = > + * quoted-pair = "\" CHAR + * CHAR = + * TEXT = + * LWS = [CRLF] 1*( SP | HT ) + * CRLF = CR LF + * CR = + * LF = + * SP = + * HT = + * CTL = + * OCTET = + */ + +var paramRegExp = /; *([!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+) *= *("(?:[ !\x23-\x5b\x5d-\x7e\x80-\xff]|\\[\x20-\x7e])*"|[!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+) */g +var textRegExp = /^[\x20-\x7e\x80-\xff]+$/ +var tokenRegExp = /^[!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+$/ + +/** + * RegExp for various RFC 5987 grammar + * + * ext-value = charset "'" [ language ] "'" value-chars + * charset = "UTF-8" / "ISO-8859-1" / mime-charset + * mime-charset = 1*mime-charsetc + * mime-charsetc = ALPHA / DIGIT + * / "!" / "#" / "$" / "%" / "&" + * / "+" / "-" / "^" / "_" / "`" + * / "{" / "}" / "~" + * language = ( 2*3ALPHA [ extlang ] ) + * / 4ALPHA + * / 5*8ALPHA + * extlang = *3( "-" 3ALPHA ) + * value-chars = *( pct-encoded / attr-char ) + * pct-encoded = "%" HEXDIG HEXDIG + * attr-char = ALPHA / DIGIT + * / "!" / "#" / "$" / "&" / "+" / "-" / "." + * / "^" / "_" / "`" / "|" / "~" + */ + +var extValueRegExp = /^([A-Za-z0-9!#$%&+\-^_`{}~]+)'(?:[A-Za-z]{2,3}(?:-[A-Za-z]{3}){0,3}|[A-Za-z]{4,8}|)'((?:%[0-9A-Fa-f]{2}|[A-Za-z0-9!#$&+\-\.^_`|~])+)$/ + +/** + * RegExp for various RFC 6266 grammar + * + * disposition-type = "inline" | "attachment" | disp-ext-type + * disp-ext-type = token + * disposition-parm = filename-parm | disp-ext-parm + * filename-parm = "filename" "=" value + * | "filename*" "=" ext-value + * disp-ext-parm = token "=" value + * | ext-token "=" ext-value + * ext-token = + */ + +var dispositionTypeRegExp = /^([!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+) *(?:$|;)/ + +/** + * Create an attachment Content-Disposition header. + * + * @param {string} [filename] + * @param {object} [options] + * @param {string} [options.type=attachment] + * @param {string|boolean} [options.fallback=true] + * @return {string} + * @api public + */ + +function contentDisposition(filename, options) { + var opts = options || {} + + // get type + var type = opts.type || 'attachment' + + // get parameters + var params = createparams(filename, opts.fallback) + + // format into string + return format(new ContentDisposition(type, params)) +} + +/** + * Create parameters object from filename and fallback. + * + * @param {string} [filename] + * @param {string|boolean} [fallback=true] + * @return {object} + * @api private + */ + +function createparams(filename, fallback) { + if (filename === undefined) { + return + } + + var params = {} + + if (typeof filename !== 'string') { + throw new TypeError('filename must be a string') + } + + // fallback defaults to true + if (fallback === undefined) { + fallback = true + } + + if (typeof fallback !== 'string' && typeof fallback !== 'boolean') { + throw new TypeError('fallback must be a string or boolean') + } + + if (typeof fallback === 'string' && nonLatin1RegExp.test(fallback)) { + throw new TypeError('fallback must be ISO-8859-1 string') + } + + // restrict to file base name + var name = basename(filename) + + // determine if name is suitable for quoted string + var isQuotedString = textRegExp.test(name) + + // generate fallback name + var fallbackName = typeof fallback !== 'string' + ? fallback && getlatin1(name) + : basename(fallback) + var hasFallback = typeof fallbackName === 'string' && fallbackName !== name + + // set extended filename parameter + if (hasFallback || !isQuotedString || hexEscapeRegExp.test(name)) { + params['filename*'] = name + } + + // set filename parameter + if (isQuotedString || hasFallback) { + params.filename = hasFallback + ? fallbackName + : name + } + + return params +} + +/** + * Format object to Content-Disposition header. + * + * @param {object} obj + * @param {string} obj.type + * @param {object} [obj.parameters] + * @return {string} + * @api private + */ + +function format(obj) { + var parameters = obj.parameters + var type = obj.type + + if (!type || typeof type !== 'string' || !tokenRegExp.test(type)) { + throw new TypeError('invalid type') + } + + // start with normalized type + var string = String(type).toLowerCase() + + // append parameters + if (parameters && typeof parameters === 'object') { + var param + var params = Object.keys(parameters).sort() + + for (var i = 0; i < params.length; i++) { + param = params[i] + + var val = param.substr(-1) === '*' + ? ustring(parameters[param]) + : qstring(parameters[param]) + + string += '; ' + param + '=' + val + } + } + + return string +} + +/** + * Decode a RFC 6987 field value (gracefully). + * + * @param {string} str + * @return {string} + * @api private + */ + +function decodefield(str) { + var match = extValueRegExp.exec(str) + + if (!match) { + throw new TypeError('invalid extended field value') + } + + var charset = match[1].toLowerCase() + var encoded = match[2] + var value + + // to binary string + var binary = encoded.replace(hexEscapeReplaceRegExp, pdecode) + + switch (charset) { + case 'iso-8859-1': + value = getlatin1(binary) + break + case 'utf-8': + value = new Buffer(binary, 'binary').toString('utf8') + break + default: + throw new TypeError('unsupported charset in extended field') + } + + return value +} + +/** + * Get ISO-8859-1 version of string. + * + * @param {string} val + * @return {string} + * @api private + */ + +function getlatin1(val) { + // simple Unicode -> ISO-8859-1 transformation + return String(val).replace(nonLatin1RegExp, '?') +} + +/** + * Parse Content-Disposition header string. + * + * @param {string} string + * @return {object} + * @api private + */ + +function parse(string) { + if (!string || typeof string !== 'string') { + throw new TypeError('argument string is required') + } + + var match = dispositionTypeRegExp.exec(string) + + if (!match) { + throw new TypeError('invalid type format') + } + + // normalize type + var index = match[0].length + var type = match[1].toLowerCase() + + var key + var names = [] + var params = {} + var value + + // calculate index to start at + index = paramRegExp.lastIndex = match[0].substr(-1) === ';' + ? index - 1 + : index + + // match parameters + while (match = paramRegExp.exec(string)) { + if (match.index !== index) { + throw new TypeError('invalid parameter format') + } + + index += match[0].length + key = match[1].toLowerCase() + value = match[2] + + if (names.indexOf(key) !== -1) { + throw new TypeError('invalid duplicate parameter') + } + + names.push(key) + + if (key.indexOf('*') + 1 === key.length) { + // decode extended value + key = key.slice(0, -1) + value = decodefield(value) + + // overwrite existing value + params[key] = value + continue + } + + if (typeof params[key] === 'string') { + continue + } + + if (value[0] === '"') { + // remove quotes and escapes + value = value + .substr(1, value.length - 2) + .replace(qescRegExp, '$1') + } + + params[key] = value + } + + if (index !== -1 && index !== string.length) { + throw new TypeError('invalid parameter format') + } + + return new ContentDisposition(type, params) +} + +/** + * Percent decode a single character. + * + * @param {string} str + * @param {string} hex + * @return {string} + * @api private + */ + +function pdecode(str, hex) { + return String.fromCharCode(parseInt(hex, 16)) +} + +/** + * Percent encode a single character. + * + * @param {string} char + * @return {string} + * @api private + */ + +function pencode(char) { + var hex = String(char) + .charCodeAt(0) + .toString(16) + .toUpperCase() + return hex.length === 1 + ? '%0' + hex + : '%' + hex +} + +/** + * Quote a string for HTTP. + * + * @param {string} val + * @return {string} + * @api private + */ + +function qstring(val) { + var str = String(val) + + return '"' + str.replace(quoteRegExp, '\\$1') + '"' +} + +/** + * Encode a Unicode string for HTTP (RFC 5987). + * + * @param {string} val + * @return {string} + * @api private + */ + +function ustring(val) { + var str = String(val) + + // percent encode as UTF-8 + var encoded = encodeURIComponent(str) + .replace(encodeUriAttrCharRegExp, pencode) + + return 'UTF-8\'\'' + encoded +} + +/** + * Class for parsed Content-Disposition header for v8 optimization + */ + +function ContentDisposition(type, parameters) { + this.type = type + this.parameters = parameters +} diff --git a/node_modules/express/node_modules/content-disposition/package.json b/node_modules/express/node_modules/content-disposition/package.json new file mode 100644 index 0000000..eb33aec --- /dev/null +++ b/node_modules/express/node_modules/content-disposition/package.json @@ -0,0 +1,65 @@ +{ + "name": "content-disposition", + "description": "Create and parse Content-Disposition header", + "version": "0.5.0", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + } + ], + "license": "MIT", + "keywords": [ + "content-disposition", + "http", + "rfc6266", + "res" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/content-disposition" + }, + "devDependencies": { + "istanbul": "0.3.2", + "mocha": "~1.21.4" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "README.md", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "f3c915f0c9d9f5ec79713dba24c8c6181b73305d", + "bugs": { + "url": "https://github.com/jshttp/content-disposition/issues" + }, + "homepage": "https://github.com/jshttp/content-disposition", + "_id": "content-disposition@0.5.0", + "_shasum": "4284fe6ae0630874639e44e80a418c2934135e9e", + "_from": "content-disposition@0.5.0", + "_npmVersion": "1.4.21", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "4284fe6ae0630874639e44e80a418c2934135e9e", + "tarball": "http://registry.npmjs.org/content-disposition/-/content-disposition-0.5.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.0.tgz" +} diff --git a/node_modules/express/node_modules/content-type/HISTORY.md b/node_modules/express/node_modules/content-type/HISTORY.md new file mode 100644 index 0000000..8a623a2 --- /dev/null +++ b/node_modules/express/node_modules/content-type/HISTORY.md @@ -0,0 +1,9 @@ +1.0.1 / 2015-02-13 +================== + + * Improve missing `Content-Type` header error message + +1.0.0 / 2015-02-01 +================== + + * Initial implementation, derived from `media-typer@0.3.0` diff --git a/node_modules/express/node_modules/content-type/LICENSE b/node_modules/express/node_modules/content-type/LICENSE new file mode 100644 index 0000000..34b1a2d --- /dev/null +++ b/node_modules/express/node_modules/content-type/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/content-type/README.md b/node_modules/express/node_modules/content-type/README.md new file mode 100644 index 0000000..3ed6741 --- /dev/null +++ b/node_modules/express/node_modules/content-type/README.md @@ -0,0 +1,92 @@ +# content-type + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Create and parse HTTP Content-Type header according to RFC 7231 + +## Installation + +```sh +$ npm install content-type +``` + +## API + +```js +var contentType = require('content-type') +``` + +### contentType.parse(string) + +```js +var obj = contentType.parse('image/svg+xml; charset=utf-8') +``` + +Parse a content type string. This will return an object with the following +properties (examples are shown for the string `'image/svg+xml; charset=utf-8'`): + + - `type`: The media type (the type and subtype, always lower case). + Example: `'image/svg+xml'` + + - `parameters`: An object of the parameters in the media type (name of parameter + always lower case). Example: `{charset: 'utf-8'}` + +Throws a `TypeError` if the string is missing or invalid. + +### contentType.parse(req) + +```js +var obj = contentType.parse(req) +``` + +Parse the `content-type` header from the given `req`. Short-cut for +`contentType.parse(req.headers['content-type'])`. + +Throws a `TypeError` if the `Content-Type` header is missing or invalid. + +### contentType.parse(res) + +```js +var obj = contentType.parse(res) +``` + +Parse the `content-type` header set on the given `res`. Short-cut for +`contentType.parse(res.getHeader('content-type'))`. + +Throws a `TypeError` if the `Content-Type` header is missing or invalid. + +### contentType.format(obj) + +```js +var str = contentType.format({type: 'image/svg+xml'}) +``` + +Format an object into a content type string. This will return a string of the +content type for the given object with the following properties (examples are +shown that produce the string `'image/svg+xml; charset=utf-8'`): + + - `type`: The media type (will be lower-cased). Example: `'image/svg+xml'` + + - `parameters`: An object of the parameters in the media type (name of the + parameter will be lower-cased). Example: `{charset: 'utf-8'}` + +Throws a `TypeError` if the object contains an invalid type or parameter names. + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/content-type.svg +[npm-url]: https://npmjs.org/package/content-type +[node-version-image]: https://img.shields.io/node/v/content-type.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/content-type/master.svg +[travis-url]: https://travis-ci.org/jshttp/content-type +[coveralls-image]: https://img.shields.io/coveralls/jshttp/content-type/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/content-type +[downloads-image]: https://img.shields.io/npm/dm/content-type.svg +[downloads-url]: https://npmjs.org/package/content-type diff --git a/node_modules/express/node_modules/content-type/index.js b/node_modules/express/node_modules/content-type/index.js new file mode 100644 index 0000000..6a2ea9f --- /dev/null +++ b/node_modules/express/node_modules/content-type/index.js @@ -0,0 +1,214 @@ +/*! + * content-type + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ + +/** + * RegExp to match *( ";" parameter ) in RFC 7231 sec 3.1.1.1 + * + * parameter = token "=" ( token / quoted-string ) + * token = 1*tchar + * tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" + * / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" + * / DIGIT / ALPHA + * ; any VCHAR, except delimiters + * quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE + * qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text + * obs-text = %x80-FF + * quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) + */ +var paramRegExp = /; *([!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+) */g +var textRegExp = /^[\u000b\u0020-\u007e\u0080-\u00ff]+$/ +var tokenRegExp = /^[!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+$/ + +/** + * RegExp to match quoted-pair in RFC 7230 sec 3.2.6 + * + * quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) + * obs-text = %x80-FF + */ +var qescRegExp = /\\([\u000b\u0020-\u00ff])/g + +/** + * RegExp to match chars that must be quoted-pair in RFC 7230 sec 3.2.6 + */ +var quoteRegExp = /([\\"])/g + +/** + * RegExp to match type in RFC 6838 + * + * media-type = type "/" subtype + * type = token + * subtype = token + */ +var typeRegExp = /^[!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+\/[!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+$/ + +/** + * Module exports. + * @public + */ + +exports.format = format +exports.parse = parse + +/** + * Format object to media type. + * + * @param {object} obj + * @return {string} + * @public + */ + +function format(obj) { + if (!obj || typeof obj !== 'object') { + throw new TypeError('argument obj is required') + } + + var parameters = obj.parameters + var type = obj.type + + if (!type || !typeRegExp.test(type)) { + throw new TypeError('invalid type') + } + + var string = type + + // append parameters + if (parameters && typeof parameters === 'object') { + var param + var params = Object.keys(parameters).sort() + + for (var i = 0; i < params.length; i++) { + param = params[i] + + if (!tokenRegExp.test(param)) { + throw new TypeError('invalid parameter name') + } + + string += '; ' + param + '=' + qstring(parameters[param]) + } + } + + return string +} + +/** + * Parse media type to object. + * + * @param {string|object} string + * @return {Object} + * @public + */ + +function parse(string) { + if (!string) { + throw new TypeError('argument string is required') + } + + if (typeof string === 'object') { + // support req/res-like objects as argument + string = getcontenttype(string) + + if (typeof string !== 'string') { + throw new TypeError('content-type header is missing from object'); + } + } + + if (typeof string !== 'string') { + throw new TypeError('argument string is required to be a string') + } + + var index = string.indexOf(';') + var type = index !== -1 + ? string.substr(0, index).trim() + : string.trim() + + if (!typeRegExp.test(type)) { + throw new TypeError('invalid media type') + } + + var key + var match + var obj = new ContentType(type.toLowerCase()) + var value + + paramRegExp.lastIndex = index + + while (match = paramRegExp.exec(string)) { + if (match.index !== index) { + throw new TypeError('invalid parameter format') + } + + index += match[0].length + key = match[1].toLowerCase() + value = match[2] + + if (value[0] === '"') { + // remove quotes and escapes + value = value + .substr(1, value.length - 2) + .replace(qescRegExp, '$1') + } + + obj.parameters[key] = value + } + + if (index !== -1 && index !== string.length) { + throw new TypeError('invalid parameter format') + } + + return obj +} + +/** + * Get content-type from req/res objects. + * + * @param {object} + * @return {Object} + * @private + */ + +function getcontenttype(obj) { + if (typeof obj.getHeader === 'function') { + // res-like + return obj.getHeader('content-type') + } + + if (typeof obj.headers === 'object') { + // req-like + return obj.headers && obj.headers['content-type'] + } +} + +/** + * Quote a string if necessary. + * + * @param {string} val + * @return {string} + * @private + */ + +function qstring(val) { + var str = String(val) + + // no need to quote tokens + if (tokenRegExp.test(str)) { + return str + } + + if (str.length > 0 && !textRegExp.test(str)) { + throw new TypeError('invalid parameter value') + } + + return '"' + str.replace(quoteRegExp, '\\$1') + '"' +} + +/** + * Class to represent a content type. + * @private + */ +function ContentType(type) { + this.parameters = Object.create(null) + this.type = type +} diff --git a/node_modules/express/node_modules/content-type/package.json b/node_modules/express/node_modules/content-type/package.json new file mode 100644 index 0000000..ca04345 --- /dev/null +++ b/node_modules/express/node_modules/content-type/package.json @@ -0,0 +1,64 @@ +{ + "name": "content-type", + "description": "Create and parse HTTP Content-Type header", + "version": "1.0.1", + "author": { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + "license": "MIT", + "keywords": [ + "content-type", + "http", + "req", + "res", + "rfc7231" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/content-type" + }, + "devDependencies": { + "istanbul": "0.3.5", + "mocha": "~1.21.5" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "README.md", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec --check-leaks --bail test/", + "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/" + }, + "gitHead": "3aa58f9c5a358a3634b8601602177888b4a477d8", + "bugs": { + "url": "https://github.com/jshttp/content-type/issues" + }, + "homepage": "https://github.com/jshttp/content-type", + "_id": "content-type@1.0.1", + "_shasum": "a19d2247327dc038050ce622b7a154ec59c5e600", + "_from": "content-type@>=1.0.1 <1.1.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "a19d2247327dc038050ce622b7a154ec59c5e600", + "tarball": "http://registry.npmjs.org/content-type/-/content-type-1.0.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.1.tgz" +} diff --git a/node_modules/express/node_modules/cookie-signature/.npmignore b/node_modules/express/node_modules/cookie-signature/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/node_modules/express/node_modules/cookie-signature/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/node_modules/express/node_modules/cookie-signature/History.md b/node_modules/express/node_modules/cookie-signature/History.md new file mode 100644 index 0000000..78513cc --- /dev/null +++ b/node_modules/express/node_modules/cookie-signature/History.md @@ -0,0 +1,38 @@ +1.0.6 / 2015-02-03 +================== + +* use `npm test` instead of `make test` to run tests +* clearer assertion messages when checking input + + +1.0.5 / 2014-09-05 +================== + +* add license to package.json + +1.0.4 / 2014-06-25 +================== + + * corrected avoidance of timing attacks (thanks @tenbits!) + +1.0.3 / 2014-01-28 +================== + + * [incorrect] fix for timing attacks + +1.0.2 / 2014-01-28 +================== + + * fix missing repository warning + * fix typo in test + +1.0.1 / 2013-04-15 +================== + + * Revert "Changed underlying HMAC algo. to sha512." + * Revert "Fix for timing attacks on MAC verification." + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/node_modules/express/node_modules/cookie-signature/Readme.md b/node_modules/express/node_modules/cookie-signature/Readme.md new file mode 100644 index 0000000..2559e84 --- /dev/null +++ b/node_modules/express/node_modules/cookie-signature/Readme.md @@ -0,0 +1,42 @@ + +# cookie-signature + + Sign and unsign cookies. + +## Example + +```js +var cookie = require('cookie-signature'); + +var val = cookie.sign('hello', 'tobiiscool'); +val.should.equal('hello.DGDUkGlIkCzPz+C0B064FNgHdEjox7ch8tOBGslZ5QI'); + +var val = cookie.sign('hello', 'tobiiscool'); +cookie.unsign(val, 'tobiiscool').should.equal('hello'); +cookie.unsign(val, 'luna').should.be.false; +``` + +## License + +(The MIT License) + +Copyright (c) 2012 LearnBoost <tj@learnboost.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/express/node_modules/cookie-signature/index.js b/node_modules/express/node_modules/cookie-signature/index.js new file mode 100644 index 0000000..b8c9463 --- /dev/null +++ b/node_modules/express/node_modules/cookie-signature/index.js @@ -0,0 +1,51 @@ +/** + * Module dependencies. + */ + +var crypto = require('crypto'); + +/** + * Sign the given `val` with `secret`. + * + * @param {String} val + * @param {String} secret + * @return {String} + * @api private + */ + +exports.sign = function(val, secret){ + if ('string' != typeof val) throw new TypeError("Cookie value must be provided as a string."); + if ('string' != typeof secret) throw new TypeError("Secret string must be provided."); + return val + '.' + crypto + .createHmac('sha256', secret) + .update(val) + .digest('base64') + .replace(/\=+$/, ''); +}; + +/** + * Unsign and decode the given `val` with `secret`, + * returning `false` if the signature is invalid. + * + * @param {String} val + * @param {String} secret + * @return {String|Boolean} + * @api private + */ + +exports.unsign = function(val, secret){ + if ('string' != typeof val) throw new TypeError("Signed cookie string must be provided."); + if ('string' != typeof secret) throw new TypeError("Secret string must be provided."); + var str = val.slice(0, val.lastIndexOf('.')) + , mac = exports.sign(str, secret); + + return sha1(mac) == sha1(val) ? str : false; +}; + +/** + * Private + */ + +function sha1(str){ + return crypto.createHash('sha1').update(str).digest('hex'); +} diff --git a/node_modules/express/node_modules/cookie-signature/package.json b/node_modules/express/node_modules/cookie-signature/package.json new file mode 100644 index 0000000..313f3e7 --- /dev/null +++ b/node_modules/express/node_modules/cookie-signature/package.json @@ -0,0 +1,58 @@ +{ + "name": "cookie-signature", + "version": "1.0.6", + "description": "Sign and unsign cookies", + "keywords": [ + "cookie", + "sign", + "unsign" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@learnboost.com" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/visionmedia/node-cookie-signature.git" + }, + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "scripts": { + "test": "mocha --require should --reporter spec" + }, + "main": "index", + "gitHead": "391b56cf44d88c493491b7e3fc53208cfb976d2a", + "bugs": { + "url": "https://github.com/visionmedia/node-cookie-signature/issues" + }, + "homepage": "https://github.com/visionmedia/node-cookie-signature", + "_id": "cookie-signature@1.0.6", + "_shasum": "e303a882b342cc3ee8ca513a79999734dab3ae2c", + "_from": "cookie-signature@1.0.6", + "_npmVersion": "2.3.0", + "_nodeVersion": "0.10.36", + "_npmUser": { + "name": "natevw", + "email": "natevw@yahoo.com" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "natevw", + "email": "natevw@yahoo.com" + } + ], + "dist": { + "shasum": "e303a882b342cc3ee8ca513a79999734dab3ae2c", + "tarball": "http://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" +} diff --git a/node_modules/express/node_modules/cookie/LICENSE b/node_modules/express/node_modules/cookie/LICENSE new file mode 100644 index 0000000..0855435 --- /dev/null +++ b/node_modules/express/node_modules/cookie/LICENSE @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2012-2014 Roman Shtylman + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/node_modules/express/node_modules/cookie/README.md b/node_modules/express/node_modules/cookie/README.md new file mode 100644 index 0000000..acdb5c2 --- /dev/null +++ b/node_modules/express/node_modules/cookie/README.md @@ -0,0 +1,64 @@ +# cookie + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +cookie is a basic cookie parser and serializer. It doesn't make assumptions about how you are going to deal with your cookies. It basically just provides a way to read and write the HTTP cookie headers. + +See [RFC6265](http://tools.ietf.org/html/rfc6265) for details about the http header for cookies. + +## how? + +``` +npm install cookie +``` + +```javascript +var cookie = require('cookie'); + +var hdr = cookie.serialize('foo', 'bar'); +// hdr = 'foo=bar'; + +var cookies = cookie.parse('foo=bar; cat=meow; dog=ruff'); +// cookies = { foo: 'bar', cat: 'meow', dog: 'ruff' }; +``` + +## more + +The serialize function takes a third parameter, an object, to set cookie options. See the RFC for valid values. + +### path +> cookie path + +### expires +> absolute expiration date for the cookie (Date object) + +### maxAge +> relative max age of the cookie from when the client receives it (seconds) + +### domain +> domain for the cookie + +### secure +> true or false + +### httpOnly +> true or false + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/cookie.svg +[npm-url]: https://npmjs.org/package/cookie +[node-version-image]: https://img.shields.io/node/v/cookie.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/cookie/master.svg +[travis-url]: https://travis-ci.org/jshttp/cookie +[coveralls-image]: https://img.shields.io/coveralls/jshttp/cookie/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/cookie?branch=master +[downloads-image]: https://img.shields.io/npm/dm/cookie.svg +[downloads-url]: https://npmjs.org/package/cookie diff --git a/node_modules/express/node_modules/cookie/index.js b/node_modules/express/node_modules/cookie/index.js new file mode 100644 index 0000000..8dea066 --- /dev/null +++ b/node_modules/express/node_modules/cookie/index.js @@ -0,0 +1,116 @@ +/*! + * cookie + * Copyright(c) 2012-2014 Roman Shtylman + * MIT Licensed + */ + +/** + * Module exports. + * @public + */ + +exports.parse = parse; +exports.serialize = serialize; + +/** + * Module variables. + * @private + */ + +var decode = decodeURIComponent; +var encode = encodeURIComponent; + +/** + * Parse a cookie header. + * + * Parse the given cookie header string into an object + * The object has the various cookies as keys(names) => values + * + * @param {string} str + * @param {object} [options] + * @return {string} + * @public + */ + +function parse(str, options) { + var obj = {} + var opt = options || {}; + var pairs = str.split(/; */); + var dec = opt.decode || decode; + + pairs.forEach(function(pair) { + var eq_idx = pair.indexOf('=') + + // skip things that don't look like key=value + if (eq_idx < 0) { + return; + } + + var key = pair.substr(0, eq_idx).trim() + var val = pair.substr(++eq_idx, pair.length).trim(); + + // quoted values + if ('"' == val[0]) { + val = val.slice(1, -1); + } + + // only assign once + if (undefined == obj[key]) { + obj[key] = tryDecode(val, dec); + } + }); + + return obj; +} + +/** + * Serialize data into a cookie header. + * + * Serialize the a name value pair into a cookie string suitable for + * http headers. An optional options object specified cookie parameters. + * + * serialize('foo', 'bar', { httpOnly: true }) + * => "foo=bar; httpOnly" + * + * @param {string} name + * @param {string} val + * @param {object} [options] + * @return {string} + * @public + */ + +function serialize(name, val, options) { + var opt = options || {}; + var enc = opt.encode || encode; + var pairs = [name + '=' + enc(val)]; + + if (null != opt.maxAge) { + var maxAge = opt.maxAge - 0; + if (isNaN(maxAge)) throw new Error('maxAge should be a Number'); + pairs.push('Max-Age=' + maxAge); + } + + if (opt.domain) pairs.push('Domain=' + opt.domain); + if (opt.path) pairs.push('Path=' + opt.path); + if (opt.expires) pairs.push('Expires=' + opt.expires.toUTCString()); + if (opt.httpOnly) pairs.push('HttpOnly'); + if (opt.secure) pairs.push('Secure'); + + return pairs.join('; '); +} + +/** + * Try decoding a string using a decoding function. + * + * @param {string} str + * @param {function} decode + * @private + */ + +function tryDecode(str, decode) { + try { + return decode(str); + } catch (e) { + return str; + } +} diff --git a/node_modules/express/node_modules/cookie/package.json b/node_modules/express/node_modules/cookie/package.json new file mode 100644 index 0000000..53a54a2 --- /dev/null +++ b/node_modules/express/node_modules/cookie/package.json @@ -0,0 +1,68 @@ +{ + "name": "cookie", + "description": "cookie parsing and serialization", + "version": "0.1.3", + "author": { + "name": "Roman Shtylman", + "email": "shtylman@gmail.com" + }, + "license": "MIT", + "keywords": [ + "cookie", + "cookies" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/cookie" + }, + "devDependencies": { + "istanbul": "0.3.9", + "mocha": "1.x.x" + }, + "files": [ + "LICENSE", + "README.md", + "index.js" + ], + "engines": { + "node": "*" + }, + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/" + }, + "gitHead": "f46097723c16f920a7b9759e154c34792e1d1a3b", + "bugs": { + "url": "https://github.com/jshttp/cookie/issues" + }, + "homepage": "https://github.com/jshttp/cookie", + "_id": "cookie@0.1.3", + "_shasum": "e734a5c1417fce472d5aef82c381cabb64d1a435", + "_from": "cookie@0.1.3", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "defunctzombie", + "email": "shtylman@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + } + ], + "dist": { + "shasum": "e734a5c1417fce472d5aef82c381cabb64d1a435", + "tarball": "http://registry.npmjs.org/cookie/-/cookie-0.1.3.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.3.tgz" +} diff --git a/node_modules/express/node_modules/debug/.jshintrc b/node_modules/express/node_modules/debug/.jshintrc new file mode 100644 index 0000000..299877f --- /dev/null +++ b/node_modules/express/node_modules/debug/.jshintrc @@ -0,0 +1,3 @@ +{ + "laxbreak": true +} diff --git a/node_modules/express/node_modules/debug/.npmignore b/node_modules/express/node_modules/debug/.npmignore new file mode 100644 index 0000000..7e6163d --- /dev/null +++ b/node_modules/express/node_modules/debug/.npmignore @@ -0,0 +1,6 @@ +support +test +examples +example +*.sock +dist diff --git a/node_modules/express/node_modules/debug/History.md b/node_modules/express/node_modules/debug/History.md new file mode 100644 index 0000000..854c971 --- /dev/null +++ b/node_modules/express/node_modules/debug/History.md @@ -0,0 +1,195 @@ + +2.2.0 / 2015-05-09 +================== + + * package: update "ms" to v0.7.1 (#202, @dougwilson) + * README: add logging to file example (#193, @DanielOchoa) + * README: fixed a typo (#191, @amir-s) + * browser: expose `storage` (#190, @stephenmathieson) + * Makefile: add a `distclean` target (#189, @stephenmathieson) + +2.1.3 / 2015-03-13 +================== + + * Updated stdout/stderr example (#186) + * Updated example/stdout.js to match debug current behaviour + * Renamed example/stderr.js to stdout.js + * Update Readme.md (#184) + * replace high intensity foreground color for bold (#182, #183) + +2.1.2 / 2015-03-01 +================== + + * dist: recompile + * update "ms" to v0.7.0 + * package: update "browserify" to v9.0.3 + * component: fix "ms.js" repo location + * changed bower package name + * updated documentation about using debug in a browser + * fix: security error on safari (#167, #168, @yields) + +2.1.1 / 2014-12-29 +================== + + * browser: use `typeof` to check for `console` existence + * browser: check for `console.log` truthiness (fix IE 8/9) + * browser: add support for Chrome apps + * Readme: added Windows usage remarks + * Add `bower.json` to properly support bower install + +2.1.0 / 2014-10-15 +================== + + * node: implement `DEBUG_FD` env variable support + * package: update "browserify" to v6.1.0 + * package: add "license" field to package.json (#135, @panuhorsmalahti) + +2.0.0 / 2014-09-01 +================== + + * package: update "browserify" to v5.11.0 + * node: use stderr rather than stdout for logging (#29, @stephenmathieson) + +1.0.4 / 2014-07-15 +================== + + * dist: recompile + * example: remove `console.info()` log usage + * example: add "Content-Type" UTF-8 header to browser example + * browser: place %c marker after the space character + * browser: reset the "content" color via `color: inherit` + * browser: add colors support for Firefox >= v31 + * debug: prefer an instance `log()` function over the global one (#119) + * Readme: update documentation about styled console logs for FF v31 (#116, @wryk) + +1.0.3 / 2014-07-09 +================== + + * Add support for multiple wildcards in namespaces (#122, @seegno) + * browser: fix lint + +1.0.2 / 2014-06-10 +================== + + * browser: update color palette (#113, @gscottolson) + * common: make console logging function configurable (#108, @timoxley) + * node: fix %o colors on old node <= 0.8.x + * Makefile: find node path using shell/which (#109, @timoxley) + +1.0.1 / 2014-06-06 +================== + + * browser: use `removeItem()` to clear localStorage + * browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777) + * package: add "contributors" section + * node: fix comment typo + * README: list authors + +1.0.0 / 2014-06-04 +================== + + * make ms diff be global, not be scope + * debug: ignore empty strings in enable() + * node: make DEBUG_COLORS able to disable coloring + * *: export the `colors` array + * npmignore: don't publish the `dist` dir + * Makefile: refactor to use browserify + * package: add "browserify" as a dev dependency + * Readme: add Web Inspector Colors section + * node: reset terminal color for the debug content + * node: map "%o" to `util.inspect()` + * browser: map "%j" to `JSON.stringify()` + * debug: add custom "formatters" + * debug: use "ms" module for humanizing the diff + * Readme: add "bash" syntax highlighting + * browser: add Firebug color support + * browser: add colors for WebKit browsers + * node: apply log to `console` + * rewrite: abstract common logic for Node & browsers + * add .jshintrc file + +0.8.1 / 2014-04-14 +================== + + * package: re-add the "component" section + +0.8.0 / 2014-03-30 +================== + + * add `enable()` method for nodejs. Closes #27 + * change from stderr to stdout + * remove unnecessary index.js file + +0.7.4 / 2013-11-13 +================== + + * remove "browserify" key from package.json (fixes something in browserify) + +0.7.3 / 2013-10-30 +================== + + * fix: catch localStorage security error when cookies are blocked (Chrome) + * add debug(err) support. Closes #46 + * add .browser prop to package.json. Closes #42 + +0.7.2 / 2013-02-06 +================== + + * fix package.json + * fix: Mobile Safari (private mode) is broken with debug + * fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript + +0.7.1 / 2013-02-05 +================== + + * add repository URL to package.json + * add DEBUG_COLORED to force colored output + * add browserify support + * fix component. Closes #24 + +0.7.0 / 2012-05-04 +================== + + * Added .component to package.json + * Added debug.component.js build + +0.6.0 / 2012-03-16 +================== + + * Added support for "-" prefix in DEBUG [Vinay Pulim] + * Added `.enabled` flag to the node version [TooTallNate] + +0.5.0 / 2012-02-02 +================== + + * Added: humanize diffs. Closes #8 + * Added `debug.disable()` to the CS variant + * Removed padding. Closes #10 + * Fixed: persist client-side variant again. Closes #9 + +0.4.0 / 2012-02-01 +================== + + * Added browser variant support for older browsers [TooTallNate] + * Added `debug.enable('project:*')` to browser variant [TooTallNate] + * Added padding to diff (moved it to the right) + +0.3.0 / 2012-01-26 +================== + + * Added millisecond diff when isatty, otherwise UTC string + +0.2.0 / 2012-01-22 +================== + + * Added wildcard support + +0.1.0 / 2011-12-02 +================== + + * Added: remove colors unless stderr isatty [TooTallNate] + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/node_modules/express/node_modules/debug/Makefile b/node_modules/express/node_modules/debug/Makefile new file mode 100644 index 0000000..5cf4a59 --- /dev/null +++ b/node_modules/express/node_modules/debug/Makefile @@ -0,0 +1,36 @@ + +# get Makefile directory name: http://stackoverflow.com/a/5982798/376773 +THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd) + +# BIN directory +BIN := $(THIS_DIR)/node_modules/.bin + +# applications +NODE ?= $(shell which node) +NPM ?= $(NODE) $(shell which npm) +BROWSERIFY ?= $(NODE) $(BIN)/browserify + +all: dist/debug.js + +install: node_modules + +clean: + @rm -rf dist + +dist: + @mkdir -p $@ + +dist/debug.js: node_modules browser.js debug.js dist + @$(BROWSERIFY) \ + --standalone debug \ + . > $@ + +distclean: clean + @rm -rf node_modules + +node_modules: package.json + @NODE_ENV= $(NPM) install + @touch node_modules + +.PHONY: all install clean distclean diff --git a/node_modules/express/node_modules/debug/Readme.md b/node_modules/express/node_modules/debug/Readme.md new file mode 100644 index 0000000..b4f45e3 --- /dev/null +++ b/node_modules/express/node_modules/debug/Readme.md @@ -0,0 +1,188 @@ +# debug + + tiny node.js debugging utility modelled after node core's debugging technique. + +## Installation + +```bash +$ npm install debug +``` + +## Usage + + With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility. + +Example _app.js_: + +```js +var debug = require('debug')('http') + , http = require('http') + , name = 'My App'; + +// fake app + +debug('booting %s', name); + +http.createServer(function(req, res){ + debug(req.method + ' ' + req.url); + res.end('hello\n'); +}).listen(3000, function(){ + debug('listening'); +}); + +// fake worker of some kind + +require('./worker'); +``` + +Example _worker.js_: + +```js +var debug = require('debug')('worker'); + +setInterval(function(){ + debug('doing some work'); +}, 1000); +``` + + The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples: + + ![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png) + + ![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png) + +#### Windows note + + On Windows the environment variable is set using the `set` command. + + ```cmd + set DEBUG=*,-not_this + ``` + +Then, run the program to be debugged as usual. + +## Millisecond diff + + When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls. + + ![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png) + + When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below: + + ![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png) + +## Conventions + + If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". + +## Wildcards + + The `*` character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`. + + You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with "connect:". + +## Browser support + + Debug works in the browser as well, currently persisted by `localStorage`. Consider the situation shown below where you have `worker:a` and `worker:b`, and wish to debug both. Somewhere in the code on your page, include: + +```js +window.myDebug = require("debug"); +``` + + ("debug" is a global object in the browser so we give this object a different name.) When your page is open in the browser, type the following in the console: + +```js +myDebug.enable("worker:*") +``` + + Refresh the page. Debug output will continue to be sent to the console until it is disabled by typing `myDebug.disable()` in the console. + +```js +a = debug('worker:a'); +b = debug('worker:b'); + +setInterval(function(){ + a('doing some work'); +}, 1000); + +setInterval(function(){ + b('doing some work'); +}, 1200); +``` + +#### Web Inspector Colors + + Colors are also enabled on "Web Inspectors" that understand the `%c` formatting + option. These are WebKit web inspectors, Firefox ([since version + 31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/)) + and the Firebug plugin for Firefox (any version). + + Colored output looks something like: + + ![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png) + +### stderr vs stdout + +You can set an alternative logging method per-namespace by overriding the `log` method on a per-namespace or globally: + +Example _stdout.js_: + +```js +var debug = require('debug'); +var error = debug('app:error'); + +// by default stderr is used +error('goes to stderr!'); + +var log = debug('app:log'); +// set this namespace to log via console.log +log.log = console.log.bind(console); // don't forget to bind to console! +log('goes to stdout'); +error('still goes to stderr!'); + +// set all output to go via console.info +// overrides all per-namespace log settings +debug.log = console.info.bind(console); +error('now goes to stdout via console.info'); +log('still goes to stdout, but via console.info now'); +``` + +### Save debug output to a file + +You can save all debug statements to a file by piping them. + +Example: + +```bash +$ DEBUG_FD=3 node your-app.js 3> whatever.log +``` + +## Authors + + - TJ Holowaychuk + - Nathan Rajlich + +## License + +(The MIT License) + +Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/debug/bower.json b/node_modules/express/node_modules/debug/bower.json new file mode 100644 index 0000000..6af573f --- /dev/null +++ b/node_modules/express/node_modules/debug/bower.json @@ -0,0 +1,28 @@ +{ + "name": "visionmedia-debug", + "main": "dist/debug.js", + "version": "2.2.0", + "homepage": "https://github.com/visionmedia/debug", + "authors": [ + "TJ Holowaychuk " + ], + "description": "visionmedia-debug", + "moduleType": [ + "amd", + "es6", + "globals", + "node" + ], + "keywords": [ + "visionmedia", + "debug" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/node_modules/express/node_modules/debug/browser.js b/node_modules/express/node_modules/debug/browser.js new file mode 100644 index 0000000..7c76452 --- /dev/null +++ b/node_modules/express/node_modules/debug/browser.js @@ -0,0 +1,168 @@ + +/** + * This is the web browser implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = require('./debug'); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +exports.storage = 'undefined' != typeof chrome + && 'undefined' != typeof chrome.storage + ? chrome.storage.local + : localstorage(); + +/** + * Colors. + */ + +exports.colors = [ + 'lightseagreen', + 'forestgreen', + 'goldenrod', + 'dodgerblue', + 'darkorchid', + 'crimson' +]; + +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + +function useColors() { + // is webkit? http://stackoverflow.com/a/16459606/376773 + return ('WebkitAppearance' in document.documentElement.style) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (window.console && (console.firebug || (console.exception && console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); +} + +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +exports.formatters.j = function(v) { + return JSON.stringify(v); +}; + + +/** + * Colorize log arguments if enabled. + * + * @api public + */ + +function formatArgs() { + var args = arguments; + var useColors = this.useColors; + + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); + + if (!useColors) return args; + + var c = 'color: ' + this.color; + args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); + + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); + return args; +} + +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + +function log() { + // this hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return 'object' === typeof console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + try { + if (null == namespaces) { + exports.storage.removeItem('debug'); + } else { + exports.storage.debug = namespaces; + } + } catch(e) {} +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + var r; + try { + r = exports.storage.debug; + } catch(e) {} + return r; +} + +/** + * Enable namespaces listed in `localStorage.debug` initially. + */ + +exports.enable(load()); + +/** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + +function localstorage(){ + try { + return window.localStorage; + } catch (e) {} +} diff --git a/node_modules/express/node_modules/debug/component.json b/node_modules/express/node_modules/debug/component.json new file mode 100644 index 0000000..ca10637 --- /dev/null +++ b/node_modules/express/node_modules/debug/component.json @@ -0,0 +1,19 @@ +{ + "name": "debug", + "repo": "visionmedia/debug", + "description": "small debugging utility", + "version": "2.2.0", + "keywords": [ + "debug", + "log", + "debugger" + ], + "main": "browser.js", + "scripts": [ + "browser.js", + "debug.js" + ], + "dependencies": { + "rauchg/ms.js": "0.7.1" + } +} diff --git a/node_modules/express/node_modules/debug/debug.js b/node_modules/express/node_modules/debug/debug.js new file mode 100644 index 0000000..7571a86 --- /dev/null +++ b/node_modules/express/node_modules/debug/debug.js @@ -0,0 +1,197 @@ + +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = debug; +exports.coerce = coerce; +exports.disable = disable; +exports.enable = enable; +exports.enabled = enabled; +exports.humanize = require('ms'); + +/** + * The currently active debug mode names, and names to skip. + */ + +exports.names = []; +exports.skips = []; + +/** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lowercased letter, i.e. "n". + */ + +exports.formatters = {}; + +/** + * Previously assigned color. + */ + +var prevColor = 0; + +/** + * Previous log timestamp. + */ + +var prevTime; + +/** + * Select a color. + * + * @return {Number} + * @api private + */ + +function selectColor() { + return exports.colors[prevColor++ % exports.colors.length]; +} + +/** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + +function debug(namespace) { + + // define the `disabled` version + function disabled() { + } + disabled.enabled = false; + + // define the `enabled` version + function enabled() { + + var self = enabled; + + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + // add the `color` if not set + if (null == self.useColors) self.useColors = exports.useColors(); + if (null == self.color && self.useColors) self.color = selectColor(); + + var args = Array.prototype.slice.call(arguments); + + args[0] = exports.coerce(args[0]); + + if ('string' !== typeof args[0]) { + // anything else let's inspect with %o + args = ['%o'].concat(args); + } + + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); + + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + if ('function' === typeof exports.formatArgs) { + args = exports.formatArgs.apply(self, args); + } + var logFn = enabled.log || exports.log || console.log.bind(console); + logFn.apply(self, args); + } + enabled.enabled = true; + + var fn = exports.enabled(namespace) ? enabled : disabled; + + fn.namespace = namespace; + + return fn; +} + +/** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + +function enable(namespaces) { + exports.save(namespaces); + + var split = (namespaces || '').split(/[\s,]+/); + var len = split.length; + + for (var i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } + } +} + +/** + * Disable debug output. + * + * @api public + */ + +function disable() { + exports.enable(''); +} + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +function enabled(name) { + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; + } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; + } + } + return false; +} + +/** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} diff --git a/node_modules/express/node_modules/debug/node.js b/node_modules/express/node_modules/debug/node.js new file mode 100644 index 0000000..1d392a8 --- /dev/null +++ b/node_modules/express/node_modules/debug/node.js @@ -0,0 +1,209 @@ + +/** + * Module dependencies. + */ + +var tty = require('tty'); +var util = require('util'); + +/** + * This is the Node.js implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = require('./debug'); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; + +/** + * Colors. + */ + +exports.colors = [6, 2, 3, 4, 5, 1]; + +/** + * The file descriptor to write the `debug()` calls to. + * Set the `DEBUG_FD` env variable to override with another value. i.e.: + * + * $ DEBUG_FD=3 node script.js 3>debug.log + */ + +var fd = parseInt(process.env.DEBUG_FD, 10) || 2; +var stream = 1 === fd ? process.stdout : + 2 === fd ? process.stderr : + createWritableStdioStream(fd); + +/** + * Is stdout a TTY? Colored output is enabled when `true`. + */ + +function useColors() { + var debugColors = (process.env.DEBUG_COLORS || '').trim().toLowerCase(); + if (0 === debugColors.length) { + return tty.isatty(fd); + } else { + return '0' !== debugColors + && 'no' !== debugColors + && 'false' !== debugColors + && 'disabled' !== debugColors; + } +} + +/** + * Map %o to `util.inspect()`, since Node doesn't do that out of the box. + */ + +var inspect = (4 === util.inspect.length ? + // node <= 0.8.x + function (v, colors) { + return util.inspect(v, void 0, void 0, colors); + } : + // node > 0.8.x + function (v, colors) { + return util.inspect(v, { colors: colors }); + } +); + +exports.formatters.o = function(v) { + return inspect(v, this.useColors) + .replace(/\s*\n\s*/g, ' '); +}; + +/** + * Adds ANSI color escape codes if enabled. + * + * @api public + */ + +function formatArgs() { + var args = arguments; + var useColors = this.useColors; + var name = this.namespace; + + if (useColors) { + var c = this.color; + + args[0] = ' \u001b[3' + c + ';1m' + name + ' ' + + '\u001b[0m' + + args[0] + '\u001b[3' + c + 'm' + + ' +' + exports.humanize(this.diff) + '\u001b[0m'; + } else { + args[0] = new Date().toUTCString() + + ' ' + name + ' ' + args[0]; + } + return args; +} + +/** + * Invokes `console.error()` with the specified arguments. + */ + +function log() { + return stream.write(util.format.apply(this, arguments) + '\n'); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + if (null == namespaces) { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } else { + process.env.DEBUG = namespaces; + } +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + return process.env.DEBUG; +} + +/** + * Copied from `node/src/node.js`. + * + * XXX: It's lame that node doesn't expose this API out-of-the-box. It also + * relies on the undocumented `tty_wrap.guessHandleType()` which is also lame. + */ + +function createWritableStdioStream (fd) { + var stream; + var tty_wrap = process.binding('tty_wrap'); + + // Note stream._type is used for test-module-load-list.js + + switch (tty_wrap.guessHandleType(fd)) { + case 'TTY': + stream = new tty.WriteStream(fd); + stream._type = 'tty'; + + // Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; + + case 'FILE': + var fs = require('fs'); + stream = new fs.SyncWriteStream(fd, { autoClose: false }); + stream._type = 'fs'; + break; + + case 'PIPE': + case 'TCP': + var net = require('net'); + stream = new net.Socket({ + fd: fd, + readable: false, + writable: true + }); + + // FIXME Should probably have an option in net.Socket to create a + // stream from an existing fd which is writable only. But for now + // we'll just add this hack and set the `readable` member to false. + // Test: ./node test/fixtures/echo.js < /etc/passwd + stream.readable = false; + stream.read = null; + stream._type = 'pipe'; + + // FIXME Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; + + default: + // Probably an error on in uv_guess_handle() + throw new Error('Implement me. Unknown stream file type!'); + } + + // For supporting legacy API we put the FD here. + stream.fd = fd; + + stream._isStdio = true; + + return stream; +} + +/** + * Enable namespaces listed in `process.env.DEBUG` initially. + */ + +exports.enable(load()); diff --git a/node_modules/express/node_modules/debug/node_modules/ms/.npmignore b/node_modules/express/node_modules/debug/node_modules/ms/.npmignore new file mode 100644 index 0000000..d1aa0ce --- /dev/null +++ b/node_modules/express/node_modules/debug/node_modules/ms/.npmignore @@ -0,0 +1,5 @@ +node_modules +test +History.md +Makefile +component.json diff --git a/node_modules/express/node_modules/debug/node_modules/ms/History.md b/node_modules/express/node_modules/debug/node_modules/ms/History.md new file mode 100644 index 0000000..32fdfc1 --- /dev/null +++ b/node_modules/express/node_modules/debug/node_modules/ms/History.md @@ -0,0 +1,66 @@ + +0.7.1 / 2015-04-20 +================== + + * prevent extraordinary long inputs (@evilpacket) + * Fixed broken readme link + +0.7.0 / 2014-11-24 +================== + + * add time abbreviations, updated tests and readme for the new units + * fix example in the readme. + * add LICENSE file + +0.6.2 / 2013-12-05 +================== + + * Adding repository section to package.json to suppress warning from NPM. + +0.6.1 / 2013-05-10 +================== + + * fix singularization [visionmedia] + +0.6.0 / 2013-03-15 +================== + + * fix minutes + +0.5.1 / 2013-02-24 +================== + + * add component namespace + +0.5.0 / 2012-11-09 +================== + + * add short formatting as default and .long option + * add .license property to component.json + * add version to component.json + +0.4.0 / 2012-10-22 +================== + + * add rounding to fix crazy decimals + +0.3.0 / 2012-09-07 +================== + + * fix `ms()` [visionmedia] + +0.2.0 / 2012-09-03 +================== + + * add component.json [visionmedia] + * add days support [visionmedia] + * add hours support [visionmedia] + * add minutes support [visionmedia] + * add seconds support [visionmedia] + * add ms string support [visionmedia] + * refactor tests to facilitate ms(number) [visionmedia] + +0.1.0 / 2012-03-07 +================== + + * Initial release diff --git a/node_modules/express/node_modules/debug/node_modules/ms/LICENSE b/node_modules/express/node_modules/debug/node_modules/ms/LICENSE new file mode 100644 index 0000000..6c07561 --- /dev/null +++ b/node_modules/express/node_modules/debug/node_modules/ms/LICENSE @@ -0,0 +1,20 @@ +(The MIT License) + +Copyright (c) 2014 Guillermo Rauch + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/debug/node_modules/ms/README.md b/node_modules/express/node_modules/debug/node_modules/ms/README.md new file mode 100644 index 0000000..9b4fd03 --- /dev/null +++ b/node_modules/express/node_modules/debug/node_modules/ms/README.md @@ -0,0 +1,35 @@ +# ms.js: miliseconds conversion utility + +```js +ms('2 days') // 172800000 +ms('1d') // 86400000 +ms('10h') // 36000000 +ms('2.5 hrs') // 9000000 +ms('2h') // 7200000 +ms('1m') // 60000 +ms('5s') // 5000 +ms('100') // 100 +``` + +```js +ms(60000) // "1m" +ms(2 * 60000) // "2m" +ms(ms('10 hours')) // "10h" +``` + +```js +ms(60000, { long: true }) // "1 minute" +ms(2 * 60000, { long: true }) // "2 minutes" +ms(ms('10 hours'), { long: true }) // "10 hours" +``` + +- Node/Browser compatible. Published as [`ms`](https://www.npmjs.org/package/ms) in [NPM](http://nodejs.org/download). +- If a number is supplied to `ms`, a string with a unit is returned. +- If a string that contains the number is supplied, it returns it as +a number (e.g: it returns `100` for `'100'`). +- If you pass a string with a number and a valid unit, the number of +equivalent ms is returned. + +## License + +MIT diff --git a/node_modules/express/node_modules/debug/node_modules/ms/index.js b/node_modules/express/node_modules/debug/node_modules/ms/index.js new file mode 100644 index 0000000..4f92771 --- /dev/null +++ b/node_modules/express/node_modules/debug/node_modules/ms/index.js @@ -0,0 +1,125 @@ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} options + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options){ + options = options || {}; + if ('string' == typeof val) return parse(val); + return options.long + ? long(val) + : short(val); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + str = '' + str; + if (str.length > 10000) return; + var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str); + if (!match) return; + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function short(ms) { + if (ms >= d) return Math.round(ms / d) + 'd'; + if (ms >= h) return Math.round(ms / h) + 'h'; + if (ms >= m) return Math.round(ms / m) + 'm'; + if (ms >= s) return Math.round(ms / s) + 's'; + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function long(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) return; + if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; + return Math.ceil(ms / n) + ' ' + name + 's'; +} diff --git a/node_modules/express/node_modules/debug/node_modules/ms/package.json b/node_modules/express/node_modules/debug/node_modules/ms/package.json new file mode 100644 index 0000000..b12c4a0 --- /dev/null +++ b/node_modules/express/node_modules/debug/node_modules/ms/package.json @@ -0,0 +1,47 @@ +{ + "name": "ms", + "version": "0.7.1", + "description": "Tiny ms conversion utility", + "repository": { + "type": "git", + "url": "git://github.com/guille/ms.js.git" + }, + "main": "./index", + "devDependencies": { + "mocha": "*", + "expect.js": "*", + "serve": "*" + }, + "component": { + "scripts": { + "ms/index.js": "index.js" + } + }, + "gitHead": "713dcf26d9e6fd9dbc95affe7eff9783b7f1b909", + "bugs": { + "url": "https://github.com/guille/ms.js/issues" + }, + "homepage": "https://github.com/guille/ms.js", + "_id": "ms@0.7.1", + "scripts": {}, + "_shasum": "9cd13c03adbff25b65effde7ce864ee952017098", + "_from": "ms@0.7.1", + "_npmVersion": "2.7.5", + "_nodeVersion": "0.12.2", + "_npmUser": { + "name": "rauchg", + "email": "rauchg@gmail.com" + }, + "maintainers": [ + { + "name": "rauchg", + "email": "rauchg@gmail.com" + } + ], + "dist": { + "shasum": "9cd13c03adbff25b65effde7ce864ee952017098", + "tarball": "http://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" +} diff --git a/node_modules/express/node_modules/debug/package.json b/node_modules/express/node_modules/debug/package.json new file mode 100644 index 0000000..91c6b99 --- /dev/null +++ b/node_modules/express/node_modules/debug/package.json @@ -0,0 +1,72 @@ +{ + "name": "debug", + "version": "2.2.0", + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/debug.git" + }, + "description": "small debugging utility", + "keywords": [ + "debug", + "log", + "debugger" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "contributors": [ + { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net", + "url": "http://n8.io" + } + ], + "license": "MIT", + "dependencies": { + "ms": "0.7.1" + }, + "devDependencies": { + "browserify": "9.0.3", + "mocha": "*" + }, + "main": "./node.js", + "browser": "./browser.js", + "component": { + "scripts": { + "debug/index.js": "browser.js", + "debug/debug.js": "debug.js" + } + }, + "gitHead": "b38458422b5aa8aa6d286b10dfe427e8a67e2b35", + "bugs": { + "url": "https://github.com/visionmedia/debug/issues" + }, + "homepage": "https://github.com/visionmedia/debug", + "_id": "debug@2.2.0", + "scripts": {}, + "_shasum": "f87057e995b1a1f6ae6a4960664137bc56f039da", + "_from": "debug@>=2.2.0 <2.3.0", + "_npmVersion": "2.7.4", + "_nodeVersion": "0.12.2", + "_npmUser": { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + } + ], + "dist": { + "shasum": "f87057e995b1a1f6ae6a4960664137bc56f039da", + "tarball": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" +} diff --git a/node_modules/express/node_modules/depd/History.md b/node_modules/express/node_modules/depd/History.md new file mode 100644 index 0000000..4a36a6c --- /dev/null +++ b/node_modules/express/node_modules/depd/History.md @@ -0,0 +1,75 @@ +1.0.1 / 2015-04-07 +================== + + * Fix `TypeError`s when under `'use strict'` code + * Fix useless type name on auto-generated messages + * Support io.js 1.x + * Support Node.js 0.12 + +1.0.0 / 2014-09-17 +================== + + * No changes + +0.4.5 / 2014-09-09 +================== + + * Improve call speed to functions using the function wrapper + * Support Node.js 0.6 + +0.4.4 / 2014-07-27 +================== + + * Work-around v8 generating empty stack traces + +0.4.3 / 2014-07-26 +================== + + * Fix exception when global `Error.stackTraceLimit` is too low + +0.4.2 / 2014-07-19 +================== + + * Correct call site for wrapped functions and properties + +0.4.1 / 2014-07-19 +================== + + * Improve automatic message generation for function properties + +0.4.0 / 2014-07-19 +================== + + * Add `TRACE_DEPRECATION` environment variable + * Remove non-standard grey color from color output + * Support `--no-deprecation` argument + * Support `--trace-deprecation` argument + * Support `deprecate.property(fn, prop, message)` + +0.3.0 / 2014-06-16 +================== + + * Add `NO_DEPRECATION` environment variable + +0.2.0 / 2014-06-15 +================== + + * Add `deprecate.property(obj, prop, message)` + * Remove `supports-color` dependency for node.js 0.8 + +0.1.0 / 2014-06-15 +================== + + * Add `deprecate.function(fn, message)` + * Add `process.on('deprecation', fn)` emitter + * Automatically generate message when omitted from `deprecate()` + +0.0.1 / 2014-06-15 +================== + + * Fix warning for dynamic calls at singe call site + +0.0.0 / 2014-06-15 +================== + + * Initial implementation diff --git a/node_modules/express/node_modules/depd/LICENSE b/node_modules/express/node_modules/depd/LICENSE new file mode 100644 index 0000000..b7dce6c --- /dev/null +++ b/node_modules/express/node_modules/depd/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2014 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/depd/Readme.md b/node_modules/express/node_modules/depd/Readme.md new file mode 100644 index 0000000..5ead5da --- /dev/null +++ b/node_modules/express/node_modules/depd/Readme.md @@ -0,0 +1,274 @@ +# depd + +[![NPM Version][npm-version-image]][npm-url] +[![NPM Downloads][npm-downloads-image]][npm-url] +[![Node.js Version][node-image]][node-url] +[![Linux Build][travis-image]][travis-url] +[![Windows Build][appveyor-image]][appveyor-url] +[![Coverage Status][coveralls-image]][coveralls-url] +[![Gratipay][gratipay-image]][gratipay-url] + +Deprecate all the things + +> With great modules comes great responsibility; mark things deprecated! + +## Install + +```sh +$ npm install depd +``` + +## API + +```js +var deprecate = require('depd')('my-module') +``` + +This library allows you to display deprecation messages to your users. +This library goes above and beyond with deprecation warnings by +introspection of the call stack (but only the bits that it is interested +in). + +Instead of just warning on the first invocation of a deprecated +function and never again, this module will warn on the first invocation +of a deprecated function per unique call site, making it ideal to alert +users of all deprecated uses across the code base, rather than just +whatever happens to execute first. + +The deprecation warnings from this module also include the file and line +information for the call into the module that the deprecated function was +in. + +**NOTE** this library has a similar interface to the `debug` module, and +this module uses the calling file to get the boundary for the call stacks, +so you should always create a new `deprecate` object in each file and not +within some central file. + +### depd(namespace) + +Create a new deprecate function that uses the given namespace name in the +messages and will display the call site prior to the stack entering the +file this function was called from. It is highly suggested you use the +name of your module as the namespace. + +### deprecate(message) + +Call this function from deprecated code to display a deprecation message. +This message will appear once per unique caller site. Caller site is the +first call site in the stack in a different file from the caller of this +function. + +If the message is omitted, a message is generated for you based on the site +of the `deprecate()` call and will display the name of the function called, +similar to the name displayed in a stack trace. + +### deprecate.function(fn, message) + +Call this function to wrap a given function in a deprecation message on any +call to the function. An optional message can be supplied to provide a custom +message. + +### deprecate.property(obj, prop, message) + +Call this function to wrap a given property on object in a deprecation message +on any accessing or setting of the property. An optional message can be supplied +to provide a custom message. + +The method must be called on the object where the property belongs (not +inherited from the prototype). + +If the property is a data descriptor, it will be converted to an accessor +descriptor in order to display the deprecation message. + +### process.on('deprecation', fn) + +This module will allow easy capturing of deprecation errors by emitting the +errors as the type "deprecation" on the global `process`. If there are no +listeners for this type, the errors are written to STDERR as normal, but if +there are any listeners, nothing will be written to STDERR and instead only +emitted. From there, you can write the errors in a different format or to a +logging source. + +The error represents the deprecation and is emitted only once with the same +rules as writing to STDERR. The error has the following properties: + + - `message` - This is the message given by the library + - `name` - This is always `'DeprecationError'` + - `namespace` - This is the namespace the deprecation came from + - `stack` - This is the stack of the call to the deprecated thing + +Example `error.stack` output: + +``` +DeprecationError: my-cool-module deprecated oldfunction + at Object. ([eval]-wrapper:6:22) + at Module._compile (module.js:456:26) + at evalScript (node.js:532:25) + at startup (node.js:80:7) + at node.js:902:3 +``` + +### process.env.NO_DEPRECATION + +As a user of modules that are deprecated, the environment variable `NO_DEPRECATION` +is provided as a quick solution to silencing deprecation warnings from being +output. The format of this is similar to that of `DEBUG`: + +```sh +$ NO_DEPRECATION=my-module,othermod node app.js +``` + +This will suppress deprecations from being output for "my-module" and "othermod". +The value is a list of comma-separated namespaces. To suppress every warning +across all namespaces, use the value `*` for a namespace. + +Providing the argument `--no-deprecation` to the `node` executable will suppress +all deprecations (only available in Node.js 0.8 or higher). + +**NOTE** This will not suppress the deperecations given to any "deprecation" +event listeners, just the output to STDERR. + +### process.env.TRACE_DEPRECATION + +As a user of modules that are deprecated, the environment variable `TRACE_DEPRECATION` +is provided as a solution to getting more detailed location information in deprecation +warnings by including the entire stack trace. The format of this is the same as +`NO_DEPRECATION`: + +```sh +$ TRACE_DEPRECATION=my-module,othermod node app.js +``` + +This will include stack traces for deprecations being output for "my-module" and +"othermod". The value is a list of comma-separated namespaces. To trace every +warning across all namespaces, use the value `*` for a namespace. + +Providing the argument `--trace-deprecation` to the `node` executable will trace +all deprecations (only available in Node.js 0.8 or higher). + +**NOTE** This will not trace the deperecations silenced by `NO_DEPRECATION`. + +## Display + +![message](files/message.png) + +When a user calls a function in your library that you mark deprecated, they +will see the following written to STDERR (in the given colors, similar colors +and layout to the `debug` module): + +``` +bright cyan bright yellow +| | reset cyan +| | | | +▼ ▼ ▼ ▼ +my-cool-module deprecated oldfunction [eval]-wrapper:6:22 +▲ ▲ ▲ ▲ +| | | | +namespace | | location of mycoolmod.oldfunction() call + | deprecation message + the word "deprecated" +``` + +If the user redirects their STDERR to a file or somewhere that does not support +colors, they see (similar layout to the `debug` module): + +``` +Sun, 15 Jun 2014 05:21:37 GMT my-cool-module deprecated oldfunction at [eval]-wrapper:6:22 +▲ ▲ ▲ ▲ ▲ +| | | | | +timestamp of message namespace | | location of mycoolmod.oldfunction() call + | deprecation message + the word "deprecated" +``` + +## Examples + +### Deprecating all calls to a function + +This will display a deprecated message about "oldfunction" being deprecated +from "my-module" on STDERR. + +```js +var deprecate = require('depd')('my-cool-module') + +// message automatically derived from function name +// Object.oldfunction +exports.oldfunction = deprecate.function(function oldfunction() { + // all calls to function are deprecated +}) + +// specific message +exports.oldfunction = deprecate.function(function () { + // all calls to function are deprecated +}, 'oldfunction') +``` + +### Conditionally deprecating a function call + +This will display a deprecated message about "weirdfunction" being deprecated +from "my-module" on STDERR when called with less than 2 arguments. + +```js +var deprecate = require('depd')('my-cool-module') + +exports.weirdfunction = function () { + if (arguments.length < 2) { + // calls with 0 or 1 args are deprecated + deprecate('weirdfunction args < 2') + } +} +``` + +When calling `deprecate` as a function, the warning is counted per call site +within your own module, so you can display different deprecations depending +on different situations and the users will still get all the warnings: + +```js +var deprecate = require('depd')('my-cool-module') + +exports.weirdfunction = function () { + if (arguments.length < 2) { + // calls with 0 or 1 args are deprecated + deprecate('weirdfunction args < 2') + } else if (typeof arguments[0] !== 'string') { + // calls with non-string first argument are deprecated + deprecate('weirdfunction non-string first arg') + } +} +``` + +### Deprecating property access + +This will display a deprecated message about "oldprop" being deprecated +from "my-module" on STDERR when accessed. A deprecation will be displayed +when setting the value and when getting the value. + +```js +var deprecate = require('depd')('my-cool-module') + +exports.oldprop = 'something' + +// message automatically derives from property name +deprecate.property(exports, 'oldprop') + +// explicit message +deprecate.property(exports, 'oldprop', 'oldprop >= 0.10') +``` + +## License + +[MIT](LICENSE) + +[npm-version-image]: https://img.shields.io/npm/v/depd.svg +[npm-downloads-image]: https://img.shields.io/npm/dm/depd.svg +[npm-url]: https://npmjs.org/package/depd +[travis-image]: https://img.shields.io/travis/dougwilson/nodejs-depd/master.svg?label=linux +[travis-url]: https://travis-ci.org/dougwilson/nodejs-depd +[appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/nodejs-depd/master.svg?label=windows +[appveyor-url]: https://ci.appveyor.com/project/dougwilson/nodejs-depd +[coveralls-image]: https://img.shields.io/coveralls/dougwilson/nodejs-depd/master.svg +[coveralls-url]: https://coveralls.io/r/dougwilson/nodejs-depd?branch=master +[node-image]: https://img.shields.io/node/v/depd.svg +[node-url]: http://nodejs.org/download/ +[gratipay-image]: https://img.shields.io/gratipay/dougwilson.svg +[gratipay-url]: https://www.gratipay.com/dougwilson/ diff --git a/node_modules/express/node_modules/depd/index.js b/node_modules/express/node_modules/depd/index.js new file mode 100644 index 0000000..d183b0a --- /dev/null +++ b/node_modules/express/node_modules/depd/index.js @@ -0,0 +1,529 @@ +/*! + * depd + * Copyright(c) 2014 Douglas Christopher Wilson + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var callSiteToString = require('./lib/compat').callSiteToString +var EventEmitter = require('events').EventEmitter +var relative = require('path').relative + +/** + * Module exports. + */ + +module.exports = depd + +/** + * Get the path to base files on. + */ + +var basePath = process.cwd() + +/** + * Get listener count on event emitter. + */ + +/*istanbul ignore next*/ +var eventListenerCount = EventEmitter.listenerCount + || function (emitter, type) { return emitter.listeners(type).length } + +/** + * Determine if namespace is contained in the string. + */ + +function containsNamespace(str, namespace) { + var val = str.split(/[ ,]+/) + + namespace = String(namespace).toLowerCase() + + for (var i = 0 ; i < val.length; i++) { + if (!(str = val[i])) continue; + + // namespace contained + if (str === '*' || str.toLowerCase() === namespace) { + return true + } + } + + return false +} + +/** + * Convert a data descriptor to accessor descriptor. + */ + +function convertDataDescriptorToAccessor(obj, prop, message) { + var descriptor = Object.getOwnPropertyDescriptor(obj, prop) + var value = descriptor.value + + descriptor.get = function getter() { return value } + + if (descriptor.writable) { + descriptor.set = function setter(val) { return value = val } + } + + delete descriptor.value + delete descriptor.writable + + Object.defineProperty(obj, prop, descriptor) + + return descriptor +} + +/** + * Create arguments string to keep arity. + */ + +function createArgumentsString(arity) { + var str = '' + + for (var i = 0; i < arity; i++) { + str += ', arg' + i + } + + return str.substr(2) +} + +/** + * Create stack string from stack. + */ + +function createStackString(stack) { + var str = this.name + ': ' + this.namespace + + if (this.message) { + str += ' deprecated ' + this.message + } + + for (var i = 0; i < stack.length; i++) { + str += '\n at ' + callSiteToString(stack[i]) + } + + return str +} + +/** + * Create deprecate for namespace in caller. + */ + +function depd(namespace) { + if (!namespace) { + throw new TypeError('argument namespace is required') + } + + var stack = getStack() + var site = callSiteLocation(stack[1]) + var file = site[0] + + function deprecate(message) { + // call to self as log + log.call(deprecate, message) + } + + deprecate._file = file + deprecate._ignored = isignored(namespace) + deprecate._namespace = namespace + deprecate._traced = istraced(namespace) + deprecate._warned = Object.create(null) + + deprecate.function = wrapfunction + deprecate.property = wrapproperty + + return deprecate +} + +/** + * Determine if namespace is ignored. + */ + +function isignored(namespace) { + /* istanbul ignore next: tested in a child processs */ + if (process.noDeprecation) { + // --no-deprecation support + return true + } + + var str = process.env.NO_DEPRECATION || '' + + // namespace ignored + return containsNamespace(str, namespace) +} + +/** + * Determine if namespace is traced. + */ + +function istraced(namespace) { + /* istanbul ignore next: tested in a child processs */ + if (process.traceDeprecation) { + // --trace-deprecation support + return true + } + + var str = process.env.TRACE_DEPRECATION || '' + + // namespace traced + return containsNamespace(str, namespace) +} + +/** + * Display deprecation message. + */ + +function log(message, site) { + var haslisteners = eventListenerCount(process, 'deprecation') !== 0 + + // abort early if no destination + if (!haslisteners && this._ignored) { + return + } + + var caller + var callFile + var callSite + var i = 0 + var seen = false + var stack = getStack() + var file = this._file + + if (site) { + // provided site + callSite = callSiteLocation(stack[1]) + callSite.name = site.name + file = callSite[0] + } else { + // get call site + i = 2 + site = callSiteLocation(stack[i]) + callSite = site + } + + // get caller of deprecated thing in relation to file + for (; i < stack.length; i++) { + caller = callSiteLocation(stack[i]) + callFile = caller[0] + + if (callFile === file) { + seen = true + } else if (callFile === this._file) { + file = this._file + } else if (seen) { + break + } + } + + var key = caller + ? site.join(':') + '__' + caller.join(':') + : undefined + + if (key !== undefined && key in this._warned) { + // already warned + return + } + + this._warned[key] = true + + // generate automatic message from call site + if (!message) { + message = callSite === site || !callSite.name + ? defaultMessage(site) + : defaultMessage(callSite) + } + + // emit deprecation if listeners exist + if (haslisteners) { + var err = DeprecationError(this._namespace, message, stack.slice(i)) + process.emit('deprecation', err) + return + } + + // format and write message + var format = process.stderr.isTTY + ? formatColor + : formatPlain + var msg = format.call(this, message, caller, stack.slice(i)) + process.stderr.write(msg + '\n', 'utf8') + + return +} + +/** + * Get call site location as array. + */ + +function callSiteLocation(callSite) { + var file = callSite.getFileName() || '' + var line = callSite.getLineNumber() + var colm = callSite.getColumnNumber() + + if (callSite.isEval()) { + file = callSite.getEvalOrigin() + ', ' + file + } + + var site = [file, line, colm] + + site.callSite = callSite + site.name = callSite.getFunctionName() + + return site +} + +/** + * Generate a default message from the site. + */ + +function defaultMessage(site) { + var callSite = site.callSite + var funcName = site.name + + // make useful anonymous name + if (!funcName) { + funcName = '' + } + + var context = callSite.getThis() + var typeName = context && callSite.getTypeName() + + // ignore useless type name + if (typeName === 'Object') { + typeName = undefined + } + + // make useful type name + if (typeName === 'Function') { + typeName = context.name || typeName + } + + return typeName && callSite.getMethodName() + ? typeName + '.' + funcName + : funcName +} + +/** + * Format deprecation message without color. + */ + +function formatPlain(msg, caller, stack) { + var timestamp = new Date().toUTCString() + + var formatted = timestamp + + ' ' + this._namespace + + ' deprecated ' + msg + + // add stack trace + if (this._traced) { + for (var i = 0; i < stack.length; i++) { + formatted += '\n at ' + callSiteToString(stack[i]) + } + + return formatted + } + + if (caller) { + formatted += ' at ' + formatLocation(caller) + } + + return formatted +} + +/** + * Format deprecation message with color. + */ + +function formatColor(msg, caller, stack) { + var formatted = '\x1b[36;1m' + this._namespace + '\x1b[22;39m' // bold cyan + + ' \x1b[33;1mdeprecated\x1b[22;39m' // bold yellow + + ' \x1b[0m' + msg + '\x1b[39m' // reset + + // add stack trace + if (this._traced) { + for (var i = 0; i < stack.length; i++) { + formatted += '\n \x1b[36mat ' + callSiteToString(stack[i]) + '\x1b[39m' // cyan + } + + return formatted + } + + if (caller) { + formatted += ' \x1b[36m' + formatLocation(caller) + '\x1b[39m' // cyan + } + + return formatted +} + +/** + * Format call site location. + */ + +function formatLocation(callSite) { + return relative(basePath, callSite[0]) + + ':' + callSite[1] + + ':' + callSite[2] +} + +/** + * Get the stack as array of call sites. + */ + +function getStack() { + var limit = Error.stackTraceLimit + var obj = {} + var prep = Error.prepareStackTrace + + Error.prepareStackTrace = prepareObjectStackTrace + Error.stackTraceLimit = Math.max(10, limit) + + // capture the stack + Error.captureStackTrace(obj) + + // slice this function off the top + var stack = obj.stack.slice(1) + + Error.prepareStackTrace = prep + Error.stackTraceLimit = limit + + return stack +} + +/** + * Capture call site stack from v8. + */ + +function prepareObjectStackTrace(obj, stack) { + return stack +} + +/** + * Return a wrapped function in a deprecation message. + */ + +function wrapfunction(fn, message) { + if (typeof fn !== 'function') { + throw new TypeError('argument fn must be a function') + } + + var args = createArgumentsString(fn.length) + var deprecate = this + var stack = getStack() + var site = callSiteLocation(stack[1]) + + site.name = fn.name + + var deprecatedfn = eval('(function (' + args + ') {\n' + + '"use strict"\n' + + 'log.call(deprecate, message, site)\n' + + 'return fn.apply(this, arguments)\n' + + '})') + + return deprecatedfn +} + +/** + * Wrap property in a deprecation message. + */ + +function wrapproperty(obj, prop, message) { + if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) { + throw new TypeError('argument obj must be object') + } + + var descriptor = Object.getOwnPropertyDescriptor(obj, prop) + + if (!descriptor) { + throw new TypeError('must call property on owner object') + } + + if (!descriptor.configurable) { + throw new TypeError('property must be configurable') + } + + var deprecate = this + var stack = getStack() + var site = callSiteLocation(stack[1]) + + // set site name + site.name = prop + + // convert data descriptor + if ('value' in descriptor) { + descriptor = convertDataDescriptorToAccessor(obj, prop, message) + } + + var get = descriptor.get + var set = descriptor.set + + // wrap getter + if (typeof get === 'function') { + descriptor.get = function getter() { + log.call(deprecate, message, site) + return get.apply(this, arguments) + } + } + + // wrap setter + if (typeof set === 'function') { + descriptor.set = function setter() { + log.call(deprecate, message, site) + return set.apply(this, arguments) + } + } + + Object.defineProperty(obj, prop, descriptor) +} + +/** + * Create DeprecationError for deprecation + */ + +function DeprecationError(namespace, message, stack) { + var error = new Error() + var stackString + + Object.defineProperty(error, 'constructor', { + value: DeprecationError + }) + + Object.defineProperty(error, 'message', { + configurable: true, + enumerable: false, + value: message, + writable: true + }) + + Object.defineProperty(error, 'name', { + enumerable: false, + configurable: true, + value: 'DeprecationError', + writable: true + }) + + Object.defineProperty(error, 'namespace', { + configurable: true, + enumerable: false, + value: namespace, + writable: true + }) + + Object.defineProperty(error, 'stack', { + configurable: true, + enumerable: false, + get: function () { + if (stackString !== undefined) { + return stackString + } + + // prepare stack trace + return stackString = createStackString.call(this, stack) + }, + set: function setter(val) { + stackString = val + } + }) + + return error +} diff --git a/node_modules/express/node_modules/depd/lib/compat/buffer-concat.js b/node_modules/express/node_modules/depd/lib/compat/buffer-concat.js new file mode 100644 index 0000000..09d9721 --- /dev/null +++ b/node_modules/express/node_modules/depd/lib/compat/buffer-concat.js @@ -0,0 +1,33 @@ +/*! + * depd + * Copyright(c) 2014 Douglas Christopher Wilson + * MIT Licensed + */ + +/** + * Module exports. + */ + +module.exports = bufferConcat + +/** + * Concatenate an array of Buffers. + */ + +function bufferConcat(bufs) { + var length = 0 + + for (var i = 0, len = bufs.length; i < len; i++) { + length += bufs[i].length + } + + var buf = new Buffer(length) + var pos = 0 + + for (var i = 0, len = bufs.length; i < len; i++) { + bufs[i].copy(buf, pos) + pos += bufs[i].length + } + + return buf +} diff --git a/node_modules/express/node_modules/depd/lib/compat/callsite-tostring.js b/node_modules/express/node_modules/depd/lib/compat/callsite-tostring.js new file mode 100644 index 0000000..17cf7ed --- /dev/null +++ b/node_modules/express/node_modules/depd/lib/compat/callsite-tostring.js @@ -0,0 +1,101 @@ +/*! + * depd + * Copyright(c) 2014 Douglas Christopher Wilson + * MIT Licensed + */ + +/** + * Module exports. + */ + +module.exports = callSiteToString + +/** + * Format a CallSite file location to a string. + */ + +function callSiteFileLocation(callSite) { + var fileName + var fileLocation = '' + + if (callSite.isNative()) { + fileLocation = 'native' + } else if (callSite.isEval()) { + fileName = callSite.getScriptNameOrSourceURL() + if (!fileName) { + fileLocation = callSite.getEvalOrigin() + } + } else { + fileName = callSite.getFileName() + } + + if (fileName) { + fileLocation += fileName + + var lineNumber = callSite.getLineNumber() + if (lineNumber != null) { + fileLocation += ':' + lineNumber + + var columnNumber = callSite.getColumnNumber() + if (columnNumber) { + fileLocation += ':' + columnNumber + } + } + } + + return fileLocation || 'unknown source' +} + +/** + * Format a CallSite to a string. + */ + +function callSiteToString(callSite) { + var addSuffix = true + var fileLocation = callSiteFileLocation(callSite) + var functionName = callSite.getFunctionName() + var isConstructor = callSite.isConstructor() + var isMethodCall = !(callSite.isToplevel() || isConstructor) + var line = '' + + if (isMethodCall) { + var methodName = callSite.getMethodName() + var typeName = getConstructorName(callSite) + + if (functionName) { + if (typeName && functionName.indexOf(typeName) !== 0) { + line += typeName + '.' + } + + line += functionName + + if (methodName && functionName.lastIndexOf('.' + methodName) !== functionName.length - methodName.length - 1) { + line += ' [as ' + methodName + ']' + } + } else { + line += typeName + '.' + (methodName || '') + } + } else if (isConstructor) { + line += 'new ' + (functionName || '') + } else if (functionName) { + line += functionName + } else { + addSuffix = false + line += fileLocation + } + + if (addSuffix) { + line += ' (' + fileLocation + ')' + } + + return line +} + +/** + * Get constructor name of reviver. + */ + +function getConstructorName(obj) { + var receiver = obj.receiver + return (receiver.constructor && receiver.constructor.name) || null +} diff --git a/node_modules/express/node_modules/depd/lib/compat/index.js b/node_modules/express/node_modules/depd/lib/compat/index.js new file mode 100644 index 0000000..7fee026 --- /dev/null +++ b/node_modules/express/node_modules/depd/lib/compat/index.js @@ -0,0 +1,69 @@ +/*! + * depd + * Copyright(c) 2014 Douglas Christopher Wilson + * MIT Licensed + */ + +/** + * Module exports. + */ + +lazyProperty(module.exports, 'bufferConcat', function bufferConcat() { + return Buffer.concat || require('./buffer-concat') +}) + +lazyProperty(module.exports, 'callSiteToString', function callSiteToString() { + var limit = Error.stackTraceLimit + var obj = {} + var prep = Error.prepareStackTrace + + function prepareObjectStackTrace(obj, stack) { + return stack + } + + Error.prepareStackTrace = prepareObjectStackTrace + Error.stackTraceLimit = 2 + + // capture the stack + Error.captureStackTrace(obj) + + // slice the stack + var stack = obj.stack.slice() + + Error.prepareStackTrace = prep + Error.stackTraceLimit = limit + + return stack[0].toString ? toString : require('./callsite-tostring') +}) + +/** + * Define a lazy property. + */ + +function lazyProperty(obj, prop, getter) { + function get() { + var val = getter() + + Object.defineProperty(obj, prop, { + configurable: true, + enumerable: true, + value: val + }) + + return val + } + + Object.defineProperty(obj, prop, { + configurable: true, + enumerable: true, + get: get + }) +} + +/** + * Call toString() on the obj + */ + +function toString(obj) { + return obj.toString() +} diff --git a/node_modules/express/node_modules/depd/package.json b/node_modules/express/node_modules/depd/package.json new file mode 100644 index 0000000..9be0625 --- /dev/null +++ b/node_modules/express/node_modules/depd/package.json @@ -0,0 +1,65 @@ +{ + "name": "depd", + "description": "Deprecate all the things", + "version": "1.0.1", + "author": { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + "license": "MIT", + "keywords": [ + "deprecate", + "deprecated" + ], + "repository": { + "type": "git", + "url": "https://github.com/dougwilson/nodejs-depd" + }, + "devDependencies": { + "benchmark": "1.0.0", + "beautify-benchmark": "0.2.4", + "istanbul": "0.3.5", + "mocha": "~1.21.5" + }, + "files": [ + "lib/", + "History.md", + "LICENSE", + "index.js", + "Readme.md" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "bench": "node benchmark/index.js", + "test": "mocha --reporter spec --bail test/", + "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --no-exit test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot test/" + }, + "gitHead": "769e0f8108463c35a6937a9d634ab19fee45100a", + "bugs": { + "url": "https://github.com/dougwilson/nodejs-depd/issues" + }, + "homepage": "https://github.com/dougwilson/nodejs-depd", + "_id": "depd@1.0.1", + "_shasum": "80aec64c9d6d97e65cc2a9caa93c0aa6abf73aaa", + "_from": "depd@>=1.0.1 <1.1.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "80aec64c9d6d97e65cc2a9caa93c0aa6abf73aaa", + "tarball": "http://registry.npmjs.org/depd/-/depd-1.0.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz" +} diff --git a/node_modules/express/node_modules/escape-html/LICENSE b/node_modules/express/node_modules/escape-html/LICENSE new file mode 100644 index 0000000..a3f0274 --- /dev/null +++ b/node_modules/express/node_modules/escape-html/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2012-2013 TJ Holowaychuk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/escape-html/Readme.md b/node_modules/express/node_modules/escape-html/Readme.md new file mode 100644 index 0000000..2cfcc99 --- /dev/null +++ b/node_modules/express/node_modules/escape-html/Readme.md @@ -0,0 +1,15 @@ + +# escape-html + + Escape HTML entities + +## Example + +```js +var escape = require('escape-html'); +escape(str); +``` + +## License + + MIT \ No newline at end of file diff --git a/node_modules/express/node_modules/escape-html/index.js b/node_modules/express/node_modules/escape-html/index.js new file mode 100644 index 0000000..d0f9256 --- /dev/null +++ b/node_modules/express/node_modules/escape-html/index.js @@ -0,0 +1,29 @@ +/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module exports. + * @public + */ + +module.exports = escapeHtml; + +/** + * Escape special characters in the given string of html. + * + * @param {string} str The string to escape for inserting into HTML + * @return {string} + * @public + */ + +function escapeHtml(html) { + return String(html) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(//g, '>'); +} diff --git a/node_modules/express/node_modules/escape-html/package.json b/node_modules/express/node_modules/escape-html/package.json new file mode 100644 index 0000000..c0cda2d --- /dev/null +++ b/node_modules/express/node_modules/escape-html/package.json @@ -0,0 +1,50 @@ +{ + "name": "escape-html", + "description": "Escape HTML entities", + "version": "1.0.2", + "license": "MIT", + "keywords": [ + "escape", + "html", + "utility" + ], + "repository": { + "type": "git", + "url": "https://github.com/component/escape-html" + }, + "files": [ + "LICENSE", + "Readme.md", + "index.js" + ], + "gitHead": "2477a23ae56f75e0a5622a20b5b55da00de3a23b", + "bugs": { + "url": "https://github.com/component/escape-html/issues" + }, + "homepage": "https://github.com/component/escape-html", + "_id": "escape-html@1.0.2", + "scripts": {}, + "_shasum": "d77d32fa98e38c2f41ae85e9278e0e0e6ba1022c", + "_from": "escape-html@1.0.2", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "d77d32fa98e38c2f41ae85e9278e0e0e6ba1022c", + "tarball": "http://registry.npmjs.org/escape-html/-/escape-html-1.0.2.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.2.tgz" +} diff --git a/node_modules/express/node_modules/etag/HISTORY.md b/node_modules/express/node_modules/etag/HISTORY.md new file mode 100644 index 0000000..bd0f26d --- /dev/null +++ b/node_modules/express/node_modules/etag/HISTORY.md @@ -0,0 +1,71 @@ +1.7.0 / 2015-06-08 +================== + + * Always include entity length in ETags for hash length extensions + * Generate non-Stats ETags using MD5 only (no longer CRC32) + * Improve stat performance by removing hashing + * Remove base64 padding in ETags to shorten + * Use MD5 instead of MD4 in weak ETags over 1KB + +1.6.0 / 2015-05-10 +================== + + * Improve support for JXcore + * Remove requirement of `atime` in the stats object + * Support "fake" stats objects in environments without `fs` + +1.5.1 / 2014-11-19 +================== + + * deps: crc@3.2.1 + - Minor fixes + +1.5.0 / 2014-10-14 +================== + + * Improve string performance + * Slightly improve speed for weak ETags over 1KB + +1.4.0 / 2014-09-21 +================== + + * Support "fake" stats objects + * Support Node.js 0.6 + +1.3.1 / 2014-09-14 +================== + + * Use the (new and improved) `crc` for crc32 + +1.3.0 / 2014-08-29 +================== + + * Default strings to strong ETags + * Improve speed for weak ETags over 1KB + +1.2.1 / 2014-08-29 +================== + + * Use the (much faster) `buffer-crc32` for crc32 + +1.2.0 / 2014-08-24 +================== + + * Add support for file stat objects + +1.1.0 / 2014-08-24 +================== + + * Add fast-path for empty entity + * Add weak ETag generation + * Shrink size of generated ETags + +1.0.1 / 2014-08-24 +================== + + * Fix behavior of string containing Unicode + +1.0.0 / 2014-05-18 +================== + + * Initial release diff --git a/node_modules/express/node_modules/etag/LICENSE b/node_modules/express/node_modules/etag/LICENSE new file mode 100644 index 0000000..142ede3 --- /dev/null +++ b/node_modules/express/node_modules/etag/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2014-2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/etag/README.md b/node_modules/express/node_modules/etag/README.md new file mode 100644 index 0000000..8da9e05 --- /dev/null +++ b/node_modules/express/node_modules/etag/README.md @@ -0,0 +1,165 @@ +# etag + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Create simple ETags + +## Installation + +```sh +$ npm install etag +``` + +## API + +```js +var etag = require('etag') +``` + +### etag(entity, [options]) + +Generate a strong ETag for the given entity. This should be the complete +body of the entity. Strings, `Buffer`s, and `fs.Stats` are accepted. By +default, a strong ETag is generated except for `fs.Stats`, which will +generate a weak ETag (this can be overwritten by `options.weak`). + +```js +res.setHeader('ETag', etag(body)) +``` + +#### Options + +`etag` accepts these properties in the options object. + +##### weak + +Specifies if the generated ETag will include the weak validator mark (that +is, the leading `W/`). The actual entity tag is the same. The default value +is `false`, unless the `entity` is `fs.Stats`, in which case it is `true`. + +## Testing + +```sh +$ npm test +``` + +## Benchmark + +```bash +$ npm run-script bench + +> etag@1.6.0 bench nodejs-etag +> node benchmark/index.js + + http_parser@1.0 + node@0.10.33 + v8@3.14.5.9 + ares@1.9.0-DEV + uv@0.10.29 + zlib@1.2.3 + modules@11 + openssl@1.0.1j + +> node benchmark/body0-100b.js + + 100B body + + 1 test completed. + 2 tests completed. + 3 tests completed. + 4 tests completed. + +* buffer - strong x 289,198 ops/sec ±1.09% (190 runs sampled) +* buffer - weak x 287,838 ops/sec ±0.91% (189 runs sampled) +* string - strong x 284,586 ops/sec ±1.05% (192 runs sampled) +* string - weak x 287,439 ops/sec ±0.82% (192 runs sampled) + +> node benchmark/body1-1kb.js + + 1KB body + + 1 test completed. + 2 tests completed. + 3 tests completed. + 4 tests completed. + +* buffer - strong x 212,423 ops/sec ±0.75% (193 runs sampled) +* buffer - weak x 211,871 ops/sec ±0.74% (194 runs sampled) + string - strong x 205,291 ops/sec ±0.86% (194 runs sampled) + string - weak x 208,463 ops/sec ±0.79% (192 runs sampled) + +> node benchmark/body2-5kb.js + + 5KB body + + 1 test completed. + 2 tests completed. + 3 tests completed. + 4 tests completed. + +* buffer - strong x 92,901 ops/sec ±0.58% (195 runs sampled) +* buffer - weak x 93,045 ops/sec ±0.65% (192 runs sampled) + string - strong x 89,621 ops/sec ±0.68% (194 runs sampled) + string - weak x 90,070 ops/sec ±0.70% (196 runs sampled) + +> node benchmark/body3-10kb.js + + 10KB body + + 1 test completed. + 2 tests completed. + 3 tests completed. + 4 tests completed. + +* buffer - strong x 54,220 ops/sec ±0.85% (192 runs sampled) +* buffer - weak x 54,069 ops/sec ±0.83% (191 runs sampled) + string - strong x 53,078 ops/sec ±0.53% (194 runs sampled) + string - weak x 53,849 ops/sec ±0.47% (197 runs sampled) + +> node benchmark/body4-100kb.js + + 100KB body + + 1 test completed. + 2 tests completed. + 3 tests completed. + 4 tests completed. + +* buffer - strong x 6,673 ops/sec ±0.15% (197 runs sampled) +* buffer - weak x 6,716 ops/sec ±0.12% (198 runs sampled) + string - strong x 6,357 ops/sec ±0.14% (197 runs sampled) + string - weak x 6,344 ops/sec ±0.21% (197 runs sampled) + +> node benchmark/stats.js + + stats + + 1 test completed. + 2 tests completed. + 3 tests completed. + 4 tests completed. + +* real - strong x 1,671,989 ops/sec ±0.13% (197 runs sampled) +* real - weak x 1,681,297 ops/sec ±0.12% (198 runs sampled) + fake - strong x 927,063 ops/sec ±0.14% (198 runs sampled) + fake - weak x 914,461 ops/sec ±0.41% (191 runs sampled) +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/etag.svg +[npm-url]: https://npmjs.org/package/etag +[node-version-image]: https://img.shields.io/node/v/etag.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/etag/master.svg +[travis-url]: https://travis-ci.org/jshttp/etag +[coveralls-image]: https://img.shields.io/coveralls/jshttp/etag/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/etag?branch=master +[downloads-image]: https://img.shields.io/npm/dm/etag.svg +[downloads-url]: https://npmjs.org/package/etag diff --git a/node_modules/express/node_modules/etag/index.js b/node_modules/express/node_modules/etag/index.js new file mode 100644 index 0000000..b582c84 --- /dev/null +++ b/node_modules/express/node_modules/etag/index.js @@ -0,0 +1,132 @@ +/*! + * etag + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module exports. + * @public + */ + +module.exports = etag + +/** + * Module dependencies. + * @private + */ + +var crypto = require('crypto') +var Stats = require('fs').Stats + +/** + * Module variables. + * @private + */ + +var base64PadCharRegExp = /=+$/ +var toString = Object.prototype.toString + +/** + * Generate an entity tag. + * + * @param {Buffer|string} entity + * @return {string} + * @private + */ + +function entitytag(entity) { + if (entity.length === 0) { + // fast-path empty + return '"0-1B2M2Y8AsgTpgAmY7PhCfg"' + } + + // compute hash of entity + var hash = crypto + .createHash('md5') + .update(entity, 'utf8') + .digest('base64') + .replace(base64PadCharRegExp, '') + + // compute length of entity + var len = typeof entity === 'string' + ? Buffer.byteLength(entity, 'utf8') + : entity.length + + return '"' + len.toString(16) + '-' + hash + '"' +} + +/** + * Create a simple ETag. + * + * @param {string|Buffer|Stats} entity + * @param {object} [options] + * @param {boolean} [options.weak] + * @return {String} + * @public + */ + +function etag(entity, options) { + if (entity == null) { + throw new TypeError('argument entity is required') + } + + // support fs.Stats object + var isStats = isstats(entity) + var weak = options && typeof options.weak === 'boolean' + ? options.weak + : isStats + + // validate argument + if (!isStats && typeof entity !== 'string' && !Buffer.isBuffer(entity)) { + throw new TypeError('argument entity must be string, Buffer, or fs.Stats') + } + + // generate entity tag + var tag = isStats + ? stattag(entity) + : entitytag(entity) + + return weak + ? 'W/' + tag + : tag +} + +/** + * Determine if object is a Stats object. + * + * @param {object} obj + * @return {boolean} + * @api private + */ + +function isstats(obj) { + // genuine fs.Stats + if (typeof Stats === 'function' && obj instanceof Stats) { + return true + } + + // quack quack + return obj && typeof obj === 'object' + && 'ctime' in obj && toString.call(obj.ctime) === '[object Date]' + && 'mtime' in obj && toString.call(obj.mtime) === '[object Date]' + && 'ino' in obj && typeof obj.ino === 'number' + && 'size' in obj && typeof obj.size === 'number' +} + +/** + * Generate a tag for a stat. + * + * @param {object} stat + * @return {string} + * @private + */ + +function stattag(stat) { + var mtime = stat.mtime.getTime().toString(16) + var size = stat.size.toString(16) + + return '"' + size + '-' + mtime + '"' +} diff --git a/node_modules/express/node_modules/etag/package.json b/node_modules/express/node_modules/etag/package.json new file mode 100644 index 0000000..1b274c9 --- /dev/null +++ b/node_modules/express/node_modules/etag/package.json @@ -0,0 +1,72 @@ +{ + "name": "etag", + "description": "Create simple ETags", + "version": "1.7.0", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "David Björklund", + "email": "david.bjorklund@gmail.com" + } + ], + "license": "MIT", + "keywords": [ + "etag", + "http", + "res" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/etag" + }, + "devDependencies": { + "benchmark": "1.0.0", + "beautify-benchmark": "0.2.4", + "istanbul": "0.3.14", + "mocha": "~1.21.4", + "seedrandom": "2.3.11" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "README.md", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "bench": "node benchmark/index.js", + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "a511f5c8c930fd9546dbd88acb080f96bc788cfc", + "bugs": { + "url": "https://github.com/jshttp/etag/issues" + }, + "homepage": "https://github.com/jshttp/etag", + "_id": "etag@1.7.0", + "_shasum": "03d30b5f67dd6e632d2945d30d6652731a34d5d8", + "_from": "etag@>=1.7.0 <1.8.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "03d30b5f67dd6e632d2945d30d6652731a34d5d8", + "tarball": "http://registry.npmjs.org/etag/-/etag-1.7.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz" +} diff --git a/node_modules/express/node_modules/finalhandler/HISTORY.md b/node_modules/express/node_modules/finalhandler/HISTORY.md new file mode 100644 index 0000000..26a9435 --- /dev/null +++ b/node_modules/express/node_modules/finalhandler/HISTORY.md @@ -0,0 +1,90 @@ +0.4.0 / 2015-06-14 +================== + + * Fix a false-positive when unpiping in Node.js 0.8 + * Support `statusCode` property on `Error` objects + * Use `unpipe` module for unpiping requests + * deps: escape-html@1.0.2 + * deps: on-finished@~2.3.0 + - Add defined behavior for HTTP `CONNECT` requests + - Add defined behavior for HTTP `Upgrade` requests + - deps: ee-first@1.1.1 + * perf: enable strict mode + * perf: remove argument reassignment + +0.3.6 / 2015-05-11 +================== + + * deps: debug@~2.2.0 + - deps: ms@0.7.1 + +0.3.5 / 2015-04-22 +================== + + * deps: on-finished@~2.2.1 + - Fix `isFinished(req)` when data buffered + +0.3.4 / 2015-03-15 +================== + + * deps: debug@~2.1.3 + - Fix high intensity foreground color for bold + - deps: ms@0.7.0 + +0.3.3 / 2015-01-01 +================== + + * deps: debug@~2.1.1 + * deps: on-finished@~2.2.0 + +0.3.2 / 2014-10-22 +================== + + * deps: on-finished@~2.1.1 + - Fix handling of pipelined requests + +0.3.1 / 2014-10-16 +================== + + * deps: debug@~2.1.0 + - Implement `DEBUG_FD` env variable support + +0.3.0 / 2014-09-17 +================== + + * Terminate in progress response only on error + * Use `on-finished` to determine request status + +0.2.0 / 2014-09-03 +================== + + * Set `X-Content-Type-Options: nosniff` header + * deps: debug@~2.0.0 + +0.1.0 / 2014-07-16 +================== + + * Respond after request fully read + - prevents hung responses and socket hang ups + * deps: debug@1.0.4 + +0.0.3 / 2014-07-11 +================== + + * deps: debug@1.0.3 + - Add support for multiple wildcards in namespaces + +0.0.2 / 2014-06-19 +================== + + * Handle invalid status codes + +0.0.1 / 2014-06-05 +================== + + * deps: debug@1.0.2 + +0.0.0 / 2014-06-05 +================== + + * Extracted from connect/express diff --git a/node_modules/express/node_modules/finalhandler/LICENSE b/node_modules/express/node_modules/finalhandler/LICENSE new file mode 100644 index 0000000..b60a5ad --- /dev/null +++ b/node_modules/express/node_modules/finalhandler/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2014-2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/finalhandler/README.md b/node_modules/express/node_modules/finalhandler/README.md new file mode 100644 index 0000000..6b171d4 --- /dev/null +++ b/node_modules/express/node_modules/finalhandler/README.md @@ -0,0 +1,133 @@ +# finalhandler + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-image]][node-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Node.js function to invoke as the final step to respond to HTTP request. + +## Installation + +```sh +$ npm install finalhandler +``` + +## API + +```js +var finalhandler = require('finalhandler') +``` + +### finalhandler(req, res, [options]) + +Returns function to be invoked as the final step for the given `req` and `res`. +This function is to be invoked as `fn(err)`. If `err` is falsy, the handler will +write out a 404 response to the `res`. If it is truthy, an error response will +be written out to the `res`, and `res.statusCode` is set from `err.status`. + +The final handler will also unpipe anything from `req` when it is invoked. + +#### options.env + +By default, the environment is determined by `NODE_ENV` variable, but it can be +overridden by this option. + +#### options.onerror + +Provide a function to be called with the `err` when it exists. Can be used for +writing errors to a central location without excessive function generation. Called +as `onerror(err, req, res)`. + +## Examples + +### always 404 + +```js +var finalhandler = require('finalhandler') +var http = require('http') + +var server = http.createServer(function (req, res) { + var done = finalhandler(req, res) + done() +}) + +server.listen(3000) +``` + +### perform simple action + +```js +var finalhandler = require('finalhandler') +var fs = require('fs') +var http = require('http') + +var server = http.createServer(function (req, res) { + var done = finalhandler(req, res) + + fs.readFile('index.html', function (err, buf) { + if (err) return done(err) + res.setHeader('Content-Type', 'text/html') + res.end(buf) + }) +}) + +server.listen(3000) +``` + +### use with middleware-style functions + +```js +var finalhandler = require('finalhandler') +var http = require('http') +var serveStatic = require('serve-static') + +var serve = serveStatic('public') + +var server = http.createServer(function (req, res) { + var done = finalhandler(req, res) + serve(req, res, done) +}) + +server.listen(3000) +``` + +### keep log of all errors + +```js +var finalhandler = require('finalhandler') +var fs = require('fs') +var http = require('http') + +var server = http.createServer(function (req, res) { + var done = finalhandler(req, res, {onerror: logerror}) + + fs.readFile('index.html', function (err, buf) { + if (err) return done(err) + res.setHeader('Content-Type', 'text/html') + res.end(buf) + }) +}) + +server.listen(3000) + +function logerror(err) { + console.error(err.stack || err.toString()) +} +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/finalhandler.svg +[npm-url]: https://npmjs.org/package/finalhandler +[node-image]: https://img.shields.io/node/v/finalhandler.svg +[node-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/pillarjs/finalhandler.svg +[travis-url]: https://travis-ci.org/pillarjs/finalhandler +[coveralls-image]: https://img.shields.io/coveralls/pillarjs/finalhandler.svg +[coveralls-url]: https://coveralls.io/r/pillarjs/finalhandler?branch=master +[downloads-image]: https://img.shields.io/npm/dm/finalhandler.svg +[downloads-url]: https://npmjs.org/package/finalhandler diff --git a/node_modules/express/node_modules/finalhandler/index.js b/node_modules/express/node_modules/finalhandler/index.js new file mode 100644 index 0000000..0de7c6b --- /dev/null +++ b/node_modules/express/node_modules/finalhandler/index.js @@ -0,0 +1,151 @@ +/*! + * finalhandler + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module dependencies. + * @private + */ + +var debug = require('debug')('finalhandler') +var escapeHtml = require('escape-html') +var http = require('http') +var onFinished = require('on-finished') +var unpipe = require('unpipe') + +/** + * Module variables. + * @private + */ + +/* istanbul ignore next */ +var defer = typeof setImmediate === 'function' + ? setImmediate + : function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) } +var isFinished = onFinished.isFinished + +/** + * Module exports. + * @public + */ + +module.exports = finalhandler + +/** + * Create a function to handle the final response. + * + * @param {Request} req + * @param {Response} res + * @param {Object} [options] + * @return {Function} + * @public + */ + +function finalhandler(req, res, options) { + var opts = options || {} + + // get environment + var env = opts.env || process.env.NODE_ENV || 'development' + + // get error callback + var onerror = opts.onerror + + return function (err) { + var status = res.statusCode + + // ignore 404 on in-flight response + if (!err && res._header) { + debug('cannot 404 after headers sent') + return + } + + // unhandled error + if (err) { + // respect err.statusCode + if (err.statusCode) { + status = err.statusCode + } + + // respect err.status + if (err.status) { + status = err.status + } + + // default status code to 500 + if (!status || status < 400) { + status = 500 + } + + // production gets a basic error message + var msg = env === 'production' + ? http.STATUS_CODES[status] + : err.stack || err.toString() + msg = escapeHtml(msg) + .replace(/\n/g, '
    ') + .replace(/ /g, '  ') + '\n' + } else { + status = 404 + msg = 'Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl || req.url) + '\n' + } + + debug('default %s', status) + + // schedule onerror callback + if (err && onerror) { + defer(onerror, err, req, res) + } + + // cannot actually respond + if (res._header) { + return req.socket.destroy() + } + + send(req, res, status, msg) + } +} + +/** + * Send response. + * + * @param {IncomingMessage} req + * @param {OutgoingMessage} res + * @param {number} status + * @param {string} body + * @private + */ + +function send(req, res, status, body) { + function write() { + res.statusCode = status + + // security header for content sniffing + res.setHeader('X-Content-Type-Options', 'nosniff') + + // standard headers + res.setHeader('Content-Type', 'text/html; charset=utf-8') + res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8')) + + if (req.method === 'HEAD') { + res.end() + return + } + + res.end(body, 'utf8') + } + + if (isFinished(req)) { + write() + return + } + + // unpipe everything from the request + unpipe(req) + + // flush the request + onFinished(req, write) + req.resume() +} diff --git a/node_modules/express/node_modules/finalhandler/node_modules/unpipe/HISTORY.md b/node_modules/express/node_modules/finalhandler/node_modules/unpipe/HISTORY.md new file mode 100644 index 0000000..85e0f8d --- /dev/null +++ b/node_modules/express/node_modules/finalhandler/node_modules/unpipe/HISTORY.md @@ -0,0 +1,4 @@ +1.0.0 / 2015-06-14 +================== + + * Initial release diff --git a/node_modules/express/node_modules/finalhandler/node_modules/unpipe/LICENSE b/node_modules/express/node_modules/finalhandler/node_modules/unpipe/LICENSE new file mode 100644 index 0000000..aed0138 --- /dev/null +++ b/node_modules/express/node_modules/finalhandler/node_modules/unpipe/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/finalhandler/node_modules/unpipe/README.md b/node_modules/express/node_modules/finalhandler/node_modules/unpipe/README.md new file mode 100644 index 0000000..e536ad2 --- /dev/null +++ b/node_modules/express/node_modules/finalhandler/node_modules/unpipe/README.md @@ -0,0 +1,43 @@ +# unpipe + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-image]][node-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Unpipe a stream from all destinations. + +## Installation + +```sh +$ npm install unpipe +``` + +## API + +```js +var unpipe = require('unpipe') +``` + +### unpipe(stream) + +Unpipes all destinations from a given stream. With stream 2+, this is +equivalent to `stream.unpipe()`. When used with streams 1 style streams +(typically Node.js 0.8 and below), this module attempts to undo the +actions done in `stream.pipe(dest)`. + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/unpipe.svg +[npm-url]: https://npmjs.org/package/unpipe +[node-image]: https://img.shields.io/node/v/unpipe.svg +[node-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/stream-utils/unpipe.svg +[travis-url]: https://travis-ci.org/stream-utils/unpipe +[coveralls-image]: https://img.shields.io/coveralls/stream-utils/unpipe.svg +[coveralls-url]: https://coveralls.io/r/stream-utils/unpipe?branch=master +[downloads-image]: https://img.shields.io/npm/dm/unpipe.svg +[downloads-url]: https://npmjs.org/package/unpipe diff --git a/node_modules/express/node_modules/finalhandler/node_modules/unpipe/index.js b/node_modules/express/node_modules/finalhandler/node_modules/unpipe/index.js new file mode 100644 index 0000000..15c3d97 --- /dev/null +++ b/node_modules/express/node_modules/finalhandler/node_modules/unpipe/index.js @@ -0,0 +1,69 @@ +/*! + * unpipe + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module exports. + * @public + */ + +module.exports = unpipe + +/** + * Determine if there are Node.js pipe-like data listeners. + * @private + */ + +function hasPipeDataListeners(stream) { + var listeners = stream.listeners('data') + + for (var i = 0; i < listeners.length; i++) { + if (listeners[i].name === 'ondata') { + return true + } + } + + return false +} + +/** + * Unpipe a stream from all destinations. + * + * @param {object} stream + * @public + */ + +function unpipe(stream) { + if (!stream) { + throw new TypeError('argument stream is required') + } + + if (typeof stream.unpipe === 'function') { + // new-style + stream.unpipe() + return + } + + // Node.js 0.8 hack + if (!hasPipeDataListeners(stream)) { + return + } + + var listener + var listeners = stream.listeners('close') + + for (var i = 0; i < listeners.length; i++) { + listener = listeners[i] + + if (listener.name !== 'cleanup' && listener.name !== 'onclose') { + continue + } + + // invoke the listener + listener.call(stream) + } +} diff --git a/node_modules/express/node_modules/finalhandler/node_modules/unpipe/package.json b/node_modules/express/node_modules/finalhandler/node_modules/unpipe/package.json new file mode 100644 index 0000000..8b6d9d0 --- /dev/null +++ b/node_modules/express/node_modules/finalhandler/node_modules/unpipe/package.json @@ -0,0 +1,58 @@ +{ + "name": "unpipe", + "description": "Unpipe a stream from all destinations", + "version": "1.0.0", + "author": { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/stream-utils/unpipe" + }, + "devDependencies": { + "istanbul": "0.3.15", + "mocha": "2.2.5", + "readable-stream": "1.1.13" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "README.md", + "index.js" + ], + "engines": { + "node": ">= 0.8" + }, + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "d2df901c06487430e78dca62b6edb8bb2fc5e99d", + "bugs": { + "url": "https://github.com/stream-utils/unpipe/issues" + }, + "homepage": "https://github.com/stream-utils/unpipe", + "_id": "unpipe@1.0.0", + "_shasum": "b2bf4ee8514aae6165b4817829d21b2ef49904ec", + "_from": "unpipe@>=1.0.0 <1.1.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "b2bf4ee8514aae6165b4817829d21b2ef49904ec", + "tarball": "http://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" +} diff --git a/node_modules/express/node_modules/finalhandler/package.json b/node_modules/express/node_modules/finalhandler/package.json new file mode 100644 index 0000000..f776db2 --- /dev/null +++ b/node_modules/express/node_modules/finalhandler/package.json @@ -0,0 +1,80 @@ +{ + "name": "finalhandler", + "description": "Node.js final http responder", + "version": "0.4.0", + "author": { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/pillarjs/finalhandler" + }, + "dependencies": { + "debug": "~2.2.0", + "escape-html": "1.0.2", + "on-finished": "~2.3.0", + "unpipe": "~1.0.0" + }, + "devDependencies": { + "istanbul": "0.3.15", + "mocha": "2.2.5", + "readable-stream": "2.0.0", + "supertest": "1.0.1" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "index.js" + ], + "engines": { + "node": ">= 0.8" + }, + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "fe4e4de9ebb0f3831493ad75119ee6ba40542853", + "bugs": { + "url": "https://github.com/pillarjs/finalhandler/issues" + }, + "homepage": "https://github.com/pillarjs/finalhandler", + "_id": "finalhandler@0.4.0", + "_shasum": "965a52d9e8d05d2b857548541fb89b53a2497d9b", + "_from": "finalhandler@0.4.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + }, + { + "name": "defunctzombie", + "email": "shtylman@gmail.com" + } + ], + "dist": { + "shasum": "965a52d9e8d05d2b857548541fb89b53a2497d9b", + "tarball": "http://registry.npmjs.org/finalhandler/-/finalhandler-0.4.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.0.tgz" +} diff --git a/node_modules/express/node_modules/fresh/HISTORY.md b/node_modules/express/node_modules/fresh/HISTORY.md new file mode 100644 index 0000000..3c95fbb --- /dev/null +++ b/node_modules/express/node_modules/fresh/HISTORY.md @@ -0,0 +1,38 @@ +0.3.0 / 2015-05-12 +================== + + * Add weak `ETag` matching support + +0.2.4 / 2014-09-07 +================== + + * Support Node.js 0.6 + +0.2.3 / 2014-09-07 +================== + + * Move repository to jshttp + +0.2.2 / 2014-02-19 +================== + + * Revert "Fix for blank page on Safari reload" + +0.2.1 / 2014-01-29 +================== + + * Fix for blank page on Safari reload + +0.2.0 / 2013-08-11 +================== + + * Return stale for `Cache-Control: no-cache` + +0.1.0 / 2012-06-15 +================== + * Add `If-None-Match: *` support + +0.0.1 / 2012-06-10 +================== + + * Initial release diff --git a/node_modules/express/node_modules/fresh/LICENSE b/node_modules/express/node_modules/fresh/LICENSE new file mode 100644 index 0000000..f527394 --- /dev/null +++ b/node_modules/express/node_modules/fresh/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2012 TJ Holowaychuk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/fresh/README.md b/node_modules/express/node_modules/fresh/README.md new file mode 100644 index 0000000..0813e30 --- /dev/null +++ b/node_modules/express/node_modules/fresh/README.md @@ -0,0 +1,58 @@ +# fresh + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +HTTP response freshness testing + +## Installation + +``` +$ npm install fresh +``` + +## API + +```js +var fresh = require('fresh') +``` + +### fresh(req, res) + + Check freshness of `req` and `res` headers. + + When the cache is "fresh" __true__ is returned, + otherwise __false__ is returned to indicate that + the cache is now stale. + +## Example + +```js +var req = { 'if-none-match': 'tobi' }; +var res = { 'etag': 'luna' }; +fresh(req, res); +// => false + +var req = { 'if-none-match': 'tobi' }; +var res = { 'etag': 'tobi' }; +fresh(req, res); +// => true +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/fresh.svg +[npm-url]: https://npmjs.org/package/fresh +[node-version-image]: https://img.shields.io/node/v/fresh.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/fresh/master.svg +[travis-url]: https://travis-ci.org/jshttp/fresh +[coveralls-image]: https://img.shields.io/coveralls/jshttp/fresh/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/fresh?branch=master +[downloads-image]: https://img.shields.io/npm/dm/fresh.svg +[downloads-url]: https://npmjs.org/package/fresh diff --git a/node_modules/express/node_modules/fresh/index.js b/node_modules/express/node_modules/fresh/index.js new file mode 100644 index 0000000..a900873 --- /dev/null +++ b/node_modules/express/node_modules/fresh/index.js @@ -0,0 +1,57 @@ + +/** + * Expose `fresh()`. + */ + +module.exports = fresh; + +/** + * Check freshness of `req` and `res` headers. + * + * When the cache is "fresh" __true__ is returned, + * otherwise __false__ is returned to indicate that + * the cache is now stale. + * + * @param {Object} req + * @param {Object} res + * @return {Boolean} + * @api public + */ + +function fresh(req, res) { + // defaults + var etagMatches = true; + var notModified = true; + + // fields + var modifiedSince = req['if-modified-since']; + var noneMatch = req['if-none-match']; + var lastModified = res['last-modified']; + var etag = res['etag']; + var cc = req['cache-control']; + + // unconditional request + if (!modifiedSince && !noneMatch) return false; + + // check for no-cache cache request directive + if (cc && cc.indexOf('no-cache') !== -1) return false; + + // parse if-none-match + if (noneMatch) noneMatch = noneMatch.split(/ *, */); + + // if-none-match + if (noneMatch) { + etagMatches = noneMatch.some(function (match) { + return match === '*' || match === etag || match === 'W/' + etag; + }); + } + + // if-modified-since + if (modifiedSince) { + modifiedSince = new Date(modifiedSince); + lastModified = new Date(lastModified); + notModified = lastModified <= modifiedSince; + } + + return !! (etagMatches && notModified); +} diff --git a/node_modules/express/node_modules/fresh/package.json b/node_modules/express/node_modules/fresh/package.json new file mode 100644 index 0000000..2278ccb --- /dev/null +++ b/node_modules/express/node_modules/fresh/package.json @@ -0,0 +1,86 @@ +{ + "name": "fresh", + "description": "HTTP response freshness testing", + "version": "0.3.0", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca", + "url": "http://tjholowaychuk.com" + }, + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + } + ], + "license": "MIT", + "keywords": [ + "fresh", + "http", + "conditional", + "cache" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/fresh" + }, + "devDependencies": { + "istanbul": "0.3.9", + "mocha": "1.21.5" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "14616c9748368ca08cd6a955dd88ab659b778634", + "bugs": { + "url": "https://github.com/jshttp/fresh/issues" + }, + "homepage": "https://github.com/jshttp/fresh", + "_id": "fresh@0.3.0", + "_shasum": "651f838e22424e7566de161d8358caa199f83d4f", + "_from": "fresh@0.3.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "jonathanong", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + } + ], + "dist": { + "shasum": "651f838e22424e7566de161d8358caa199f83d4f", + "tarball": "http://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz" +} diff --git a/node_modules/express/node_modules/merge-descriptors/LICENSE b/node_modules/express/node_modules/merge-descriptors/LICENSE new file mode 100644 index 0000000..a53a533 --- /dev/null +++ b/node_modules/express/node_modules/merge-descriptors/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2013 Jonathan Ong + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/merge-descriptors/README.md b/node_modules/express/node_modules/merge-descriptors/README.md new file mode 100644 index 0000000..ca4cf24 --- /dev/null +++ b/node_modules/express/node_modules/merge-descriptors/README.md @@ -0,0 +1,34 @@ +# Merge Descriptors + +Merge objects using descriptors. + +```js +var thing = { + get name() { + return 'jon' + } +} + +var animal = { + +} + +merge(animal, thing) + +animal.name === 'jon' +``` + +## API + +### merge(destination, source) + +Redefines `destination`'s descriptors with `source`'s. + +### merge(destination, source, false) + +Defines `source`'s descriptors on `destination` if `destination` does not have +a descriptor by the same name. + +## License + +[MIT](LICENSE) diff --git a/node_modules/express/node_modules/merge-descriptors/index.js b/node_modules/express/node_modules/merge-descriptors/index.js new file mode 100644 index 0000000..5d0af3a --- /dev/null +++ b/node_modules/express/node_modules/merge-descriptors/index.js @@ -0,0 +1,57 @@ +/*! + * merge-descriptors + * Copyright(c) 2014 Jonathan Ong + * MIT Licensed + */ + +/** + * Module exports. + * @public + */ + +module.exports = merge + +/** + * Module variables. + * @private + */ + +var hasOwnProperty = Object.prototype.hasOwnProperty + +/** + * Merge the property descriptors of `src` into `dest` + * + * @param {object} dest Object to add descriptors to + * @param {object} src Object to clone descriptors from + * @param {boolean} [redefine=true] Redefine `dest` properties with `src` properties + * @returns {object} Reference to dest + * @public + */ + +function merge(dest, src, redefine) { + if (!dest) { + throw new TypeError('argument dest is required') + } + + if (!src) { + throw new TypeError('argument src is required') + } + + if (redefine === undefined) { + // Default to true + redefine = true + } + + Object.getOwnPropertyNames(src).forEach(function forEachOwnPropertyName(name) { + if (!redefine && hasOwnProperty.call(dest, name)) { + // Skip desriptor + return + } + + // Copy descriptor + var descriptor = Object.getOwnPropertyDescriptor(src, name) + Object.defineProperty(dest, name, descriptor) + }) + + return dest +} diff --git a/node_modules/express/node_modules/merge-descriptors/package.json b/node_modules/express/node_modules/merge-descriptors/package.json new file mode 100644 index 0000000..d733faa --- /dev/null +++ b/node_modules/express/node_modules/merge-descriptors/package.json @@ -0,0 +1,124 @@ +{ + "name": "merge-descriptors", + "description": "Merge objects using descriptors", + "version": "1.0.0", + "author": { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/component/merge-descriptors.git" + }, + "bugs": { + "url": "https://github.com/component/merge-descriptors/issues" + }, + "files": [ + "LICENSE", + "README.md", + "index.js" + ], + "gitHead": "81d7a3c14099884c391bd237d7d8edf23c6d6f18", + "homepage": "https://github.com/component/merge-descriptors", + "_id": "merge-descriptors@1.0.0", + "scripts": {}, + "_shasum": "2169cf7538e1b0cc87fb88e1502d8474bbf79864", + "_from": "merge-descriptors@1.0.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + }, + { + "name": "juliangruber", + "email": "julian@juliangruber.com" + }, + { + "name": "yields", + "email": "yields@icloud.com" + }, + { + "name": "ianstormtaylor", + "email": "ian@ianstormtaylor.com" + }, + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "timoxley", + "email": "secoif@gmail.com" + }, + { + "name": "mattmueller", + "email": "mattmuelle@gmail.com" + }, + { + "name": "jonathanong", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "queckezz", + "email": "fabian.eichenberger@gmail.com" + }, + { + "name": "anthonyshort", + "email": "antshort@gmail.com" + }, + { + "name": "dominicbarnes", + "email": "dominic@dbarnes.info" + }, + { + "name": "clintwood", + "email": "clint@anotherway.co.za" + }, + { + "name": "thehydroimpulse", + "email": "dnfagnan@gmail.com" + }, + { + "name": "stephenmathieson", + "email": "me@stephenmathieson.com" + }, + { + "name": "trevorgerhardt", + "email": "trevorgerhardt@gmail.com" + }, + { + "name": "timaschew", + "email": "timaschew@gmail.com" + }, + { + "name": "hughsk", + "email": "hughskennedy@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "2169cf7538e1b0cc87fb88e1502d8474bbf79864", + "tarball": "http://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.0.tgz" +} diff --git a/node_modules/express/node_modules/methods/HISTORY.md b/node_modules/express/node_modules/methods/HISTORY.md new file mode 100644 index 0000000..c9e302c --- /dev/null +++ b/node_modules/express/node_modules/methods/HISTORY.md @@ -0,0 +1,24 @@ +1.1.1 / 2014-12-30 +================== + + * Improve `browserify` support + +1.1.0 / 2014-07-05 +================== + + * Add `CONNECT` method + +1.0.1 / 2014-06-02 +================== + + * Fix module to work with harmony transform + +1.0.0 / 2014-05-08 +================== + + * Add `PURGE` method + +0.1.0 / 2013-10-28 +================== + + * Add `http.METHODS` support diff --git a/node_modules/express/node_modules/methods/LICENSE b/node_modules/express/node_modules/methods/LICENSE new file mode 100644 index 0000000..8bce401 --- /dev/null +++ b/node_modules/express/node_modules/methods/LICENSE @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2013-2014 TJ Holowaychuk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/node_modules/express/node_modules/methods/README.md b/node_modules/express/node_modules/methods/README.md new file mode 100644 index 0000000..dccc473 --- /dev/null +++ b/node_modules/express/node_modules/methods/README.md @@ -0,0 +1,41 @@ +# Methods + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + + HTTP verbs that node core's parser supports. + + +## Install + +```bash +$ npm install methods +``` + +## API + +```js +var methods = require('methods') +``` + +### methods + +This is an array of lower-case method names that Node.js supports. + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/methods.svg?style=flat +[npm-url]: https://npmjs.org/package/methods +[node-version-image]: https://img.shields.io/node/v/methods.svg?style=flat +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/methods.svg?style=flat +[travis-url]: https://travis-ci.org/jshttp/methods +[coveralls-image]: https://img.shields.io/coveralls/jshttp/methods.svg?style=flat +[coveralls-url]: https://coveralls.io/r/jshttp/methods?branch=master +[downloads-image]: https://img.shields.io/npm/dm/methods.svg?style=flat +[downloads-url]: https://npmjs.org/package/methods diff --git a/node_modules/express/node_modules/methods/index.js b/node_modules/express/node_modules/methods/index.js new file mode 100644 index 0000000..e89c7fd --- /dev/null +++ b/node_modules/express/node_modules/methods/index.js @@ -0,0 +1,42 @@ + +var http = require('http'); + +/* istanbul ignore next: implementation differs on version */ +if (http.METHODS) { + + module.exports = http.METHODS.map(function(method){ + return method.toLowerCase(); + }); + +} else { + + module.exports = [ + 'get', + 'post', + 'put', + 'head', + 'delete', + 'options', + 'trace', + 'copy', + 'lock', + 'mkcol', + 'move', + 'purge', + 'propfind', + 'proppatch', + 'unlock', + 'report', + 'mkactivity', + 'checkout', + 'merge', + 'm-search', + 'notify', + 'subscribe', + 'unsubscribe', + 'patch', + 'search', + 'connect' + ]; + +} diff --git a/node_modules/express/node_modules/methods/package.json b/node_modules/express/node_modules/methods/package.json new file mode 100644 index 0000000..da0016b --- /dev/null +++ b/node_modules/express/node_modules/methods/package.json @@ -0,0 +1,87 @@ +{ + "name": "methods", + "description": "HTTP methods that node supports", + "version": "1.1.1", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca", + "url": "http://tjholowaychuk.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/jshttp/methods" + }, + "devDependencies": { + "istanbul": "0.3", + "mocha": "1" + }, + "files": [ + "index.js", + "HISTORY.md", + "LICENSE" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter dot" + }, + "browser": { + "http": false + }, + "keywords": [ + "http", + "methods" + ], + "gitHead": "6293c6b27c5fb963acf67a347af80ad2ebd7247f", + "bugs": { + "url": "https://github.com/jshttp/methods/issues" + }, + "homepage": "https://github.com/jshttp/methods", + "_id": "methods@1.1.1", + "_shasum": "17ea6366066d00c58e375b8ec7dfd0453c89822a", + "_from": "methods@>=1.1.1 <1.2.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "jonathanong", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "17ea6366066d00c58e375b8ec7dfd0453c89822a", + "tarball": "http://registry.npmjs.org/methods/-/methods-1.1.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/methods/-/methods-1.1.1.tgz" +} diff --git a/node_modules/express/node_modules/on-finished/HISTORY.md b/node_modules/express/node_modules/on-finished/HISTORY.md new file mode 100644 index 0000000..98ff0e9 --- /dev/null +++ b/node_modules/express/node_modules/on-finished/HISTORY.md @@ -0,0 +1,88 @@ +2.3.0 / 2015-05-26 +================== + + * Add defined behavior for HTTP `CONNECT` requests + * Add defined behavior for HTTP `Upgrade` requests + * deps: ee-first@1.1.1 + +2.2.1 / 2015-04-22 +================== + + * Fix `isFinished(req)` when data buffered + +2.2.0 / 2014-12-22 +================== + + * Add message object to callback arguments + +2.1.1 / 2014-10-22 +================== + + * Fix handling of pipelined requests + +2.1.0 / 2014-08-16 +================== + + * Check if `socket` is detached + * Return `undefined` for `isFinished` if state unknown + +2.0.0 / 2014-08-16 +================== + + * Add `isFinished` function + * Move to `jshttp` organization + * Remove support for plain socket argument + * Rename to `on-finished` + * Support both `req` and `res` as arguments + * deps: ee-first@1.0.5 + +1.2.2 / 2014-06-10 +================== + + * Reduce listeners added to emitters + - avoids "event emitter leak" warnings when used multiple times on same request + +1.2.1 / 2014-06-08 +================== + + * Fix returned value when already finished + +1.2.0 / 2014-06-05 +================== + + * Call callback when called on already-finished socket + +1.1.4 / 2014-05-27 +================== + + * Support node.js 0.8 + +1.1.3 / 2014-04-30 +================== + + * Make sure errors passed as instanceof `Error` + +1.1.2 / 2014-04-18 +================== + + * Default the `socket` to passed-in object + +1.1.1 / 2014-01-16 +================== + + * Rename module to `finished` + +1.1.0 / 2013-12-25 +================== + + * Call callback when called on already-errored socket + +1.0.1 / 2013-12-20 +================== + + * Actually pass the error to the callback + +1.0.0 / 2013-12-20 +================== + + * Initial release diff --git a/node_modules/express/node_modules/on-finished/LICENSE b/node_modules/express/node_modules/on-finished/LICENSE new file mode 100644 index 0000000..5931fd2 --- /dev/null +++ b/node_modules/express/node_modules/on-finished/LICENSE @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2013 Jonathan Ong +Copyright (c) 2014 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/on-finished/README.md b/node_modules/express/node_modules/on-finished/README.md new file mode 100644 index 0000000..a0e1157 --- /dev/null +++ b/node_modules/express/node_modules/on-finished/README.md @@ -0,0 +1,154 @@ +# on-finished + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Execute a callback when a HTTP request closes, finishes, or errors. + +## Install + +```sh +$ npm install on-finished +``` + +## API + +```js +var onFinished = require('on-finished') +``` + +### onFinished(res, listener) + +Attach a listener to listen for the response to finish. The listener will +be invoked only once when the response finished. If the response finished +to an error, the first argument will contain the error. If the response +has already finished, the listener will be invoked. + +Listening to the end of a response would be used to close things associated +with the response, like open files. + +Listener is invoked as `listener(err, res)`. + +```js +onFinished(res, function (err, res) { + // clean up open fds, etc. + // err contains the error is request error'd +}) +``` + +### onFinished(req, listener) + +Attach a listener to listen for the request to finish. The listener will +be invoked only once when the request finished. If the request finished +to an error, the first argument will contain the error. If the request +has already finished, the listener will be invoked. + +Listening to the end of a request would be used to know when to continue +after reading the data. + +Listener is invoked as `listener(err, req)`. + +```js +var data = '' + +req.setEncoding('utf8') +res.on('data', function (str) { + data += str +}) + +onFinished(req, function (err, req) { + // data is read unless there is err +}) +``` + +### onFinished.isFinished(res) + +Determine if `res` is already finished. This would be useful to check and +not even start certain operations if the response has already finished. + +### onFinished.isFinished(req) + +Determine if `req` is already finished. This would be useful to check and +not even start certain operations if the request has already finished. + +## Special Node.js requests + +### HTTP CONNECT method + +The meaning of the `CONNECT` method from RFC 7231, section 4.3.6: + +> The CONNECT method requests that the recipient establish a tunnel to +> the destination origin server identified by the request-target and, +> if successful, thereafter restrict its behavior to blind forwarding +> of packets, in both directions, until the tunnel is closed. Tunnels +> are commonly used to create an end-to-end virtual connection, through +> one or more proxies, which can then be secured using TLS (Transport +> Layer Security, [RFC5246]). + +In Node.js, these request objects come from the `'connect'` event on +the HTTP server. + +When this module is used on a HTTP `CONNECT` request, the request is +considered "finished" immediately, **due to limitations in the Node.js +interface**. This means if the `CONNECT` request contains a request entity, +the request will be considered "finished" even before it has been read. + +There is no such thing as a response object to a `CONNECT` request in +Node.js, so there is no support for for one. + +### HTTP Upgrade request + +The meaning of the `Upgrade` header from RFC 7230, section 6.1: + +> The "Upgrade" header field is intended to provide a simple mechanism +> for transitioning from HTTP/1.1 to some other protocol on the same +> connection. + +In Node.js, these request objects come from the `'upgrade'` event on +the HTTP server. + +When this module is used on a HTTP request with an `Upgrade` header, the +request is considered "finished" immediately, **due to limitations in the +Node.js interface**. This means if the `Upgrade` request contains a request +entity, the request will be considered "finished" even before it has been +read. + +There is no such thing as a response object to a `Upgrade` request in +Node.js, so there is no support for for one. + +## Example + +The following code ensures that file descriptors are always closed +once the response finishes. + +```js +var destroy = require('destroy') +var http = require('http') +var onFinished = require('on-finished') + +http.createServer(function onRequest(req, res) { + var stream = fs.createReadStream('package.json') + stream.pipe(res) + onFinished(res, function (err) { + destroy(stream) + }) +}) +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/on-finished.svg +[npm-url]: https://npmjs.org/package/on-finished +[node-version-image]: https://img.shields.io/node/v/on-finished.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/on-finished/master.svg +[travis-url]: https://travis-ci.org/jshttp/on-finished +[coveralls-image]: https://img.shields.io/coveralls/jshttp/on-finished/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/on-finished?branch=master +[downloads-image]: https://img.shields.io/npm/dm/on-finished.svg +[downloads-url]: https://npmjs.org/package/on-finished diff --git a/node_modules/express/node_modules/on-finished/index.js b/node_modules/express/node_modules/on-finished/index.js new file mode 100644 index 0000000..9abd98f --- /dev/null +++ b/node_modules/express/node_modules/on-finished/index.js @@ -0,0 +1,196 @@ +/*! + * on-finished + * Copyright(c) 2013 Jonathan Ong + * Copyright(c) 2014 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module exports. + * @public + */ + +module.exports = onFinished +module.exports.isFinished = isFinished + +/** + * Module dependencies. + * @private + */ + +var first = require('ee-first') + +/** + * Variables. + * @private + */ + +/* istanbul ignore next */ +var defer = typeof setImmediate === 'function' + ? setImmediate + : function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) } + +/** + * Invoke callback when the response has finished, useful for + * cleaning up resources afterwards. + * + * @param {object} msg + * @param {function} listener + * @return {object} + * @public + */ + +function onFinished(msg, listener) { + if (isFinished(msg) !== false) { + defer(listener, null, msg) + return msg + } + + // attach the listener to the message + attachListener(msg, listener) + + return msg +} + +/** + * Determine if message is already finished. + * + * @param {object} msg + * @return {boolean} + * @public + */ + +function isFinished(msg) { + var socket = msg.socket + + if (typeof msg.finished === 'boolean') { + // OutgoingMessage + return Boolean(msg.finished || (socket && !socket.writable)) + } + + if (typeof msg.complete === 'boolean') { + // IncomingMessage + return Boolean(msg.upgrade || !socket || !socket.readable || (msg.complete && !msg.readable)) + } + + // don't know + return undefined +} + +/** + * Attach a finished listener to the message. + * + * @param {object} msg + * @param {function} callback + * @private + */ + +function attachFinishedListener(msg, callback) { + var eeMsg + var eeSocket + var finished = false + + function onFinish(error) { + eeMsg.cancel() + eeSocket.cancel() + + finished = true + callback(error) + } + + // finished on first message event + eeMsg = eeSocket = first([[msg, 'end', 'finish']], onFinish) + + function onSocket(socket) { + // remove listener + msg.removeListener('socket', onSocket) + + if (finished) return + if (eeMsg !== eeSocket) return + + // finished on first socket event + eeSocket = first([[socket, 'error', 'close']], onFinish) + } + + if (msg.socket) { + // socket already assigned + onSocket(msg.socket) + return + } + + // wait for socket to be assigned + msg.on('socket', onSocket) + + if (msg.socket === undefined) { + // node.js 0.8 patch + patchAssignSocket(msg, onSocket) + } +} + +/** + * Attach the listener to the message. + * + * @param {object} msg + * @return {function} + * @private + */ + +function attachListener(msg, listener) { + var attached = msg.__onFinished + + // create a private single listener with queue + if (!attached || !attached.queue) { + attached = msg.__onFinished = createListener(msg) + attachFinishedListener(msg, attached) + } + + attached.queue.push(listener) +} + +/** + * Create listener on message. + * + * @param {object} msg + * @return {function} + * @private + */ + +function createListener(msg) { + function listener(err) { + if (msg.__onFinished === listener) msg.__onFinished = null + if (!listener.queue) return + + var queue = listener.queue + listener.queue = null + + for (var i = 0; i < queue.length; i++) { + queue[i](err, msg) + } + } + + listener.queue = [] + + return listener +} + +/** + * Patch ServerResponse.prototype.assignSocket for node.js 0.8. + * + * @param {ServerResponse} res + * @param {function} callback + * @private + */ + +function patchAssignSocket(res, callback) { + var assignSocket = res.assignSocket + + if (typeof assignSocket !== 'function') return + + // res.on('socket', callback) is broken in 0.8 + res.assignSocket = function _assignSocket(socket) { + assignSocket.call(this, socket) + callback(socket) + } +} diff --git a/node_modules/express/node_modules/on-finished/node_modules/ee-first/LICENSE b/node_modules/express/node_modules/on-finished/node_modules/ee-first/LICENSE new file mode 100644 index 0000000..a7ae8ee --- /dev/null +++ b/node_modules/express/node_modules/on-finished/node_modules/ee-first/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/express/node_modules/on-finished/node_modules/ee-first/README.md b/node_modules/express/node_modules/on-finished/node_modules/ee-first/README.md new file mode 100644 index 0000000..cbd2478 --- /dev/null +++ b/node_modules/express/node_modules/on-finished/node_modules/ee-first/README.md @@ -0,0 +1,80 @@ +# EE First + +[![NPM version][npm-image]][npm-url] +[![Build status][travis-image]][travis-url] +[![Test coverage][coveralls-image]][coveralls-url] +[![License][license-image]][license-url] +[![Downloads][downloads-image]][downloads-url] +[![Gittip][gittip-image]][gittip-url] + +Get the first event in a set of event emitters and event pairs, +then clean up after itself. + +## Install + +```sh +$ npm install ee-first +``` + +## API + +```js +var first = require('ee-first') +``` + +### first(arr, listener) + +Invoke `listener` on the first event from the list specified in `arr`. `arr` is +an array of arrays, with each array in the format `[ee, ...event]`. `listener` +will be called only once, the first time any of the given events are emitted. If +`error` is one of the listened events, then if that fires first, the `listener` +will be given the `err` argument. + +The `listener` is invoked as `listener(err, ee, event, args)`, where `err` is the +first argument emitted from an `error` event, if applicable; `ee` is the event +emitter that fired; `event` is the string event name that fired; and `args` is an +array of the arguments that were emitted on the event. + +```js +var ee1 = new EventEmitter() +var ee2 = new EventEmitter() + +first([ + [ee1, 'close', 'end', 'error'], + [ee2, 'error'] +], function (err, ee, event, args) { + // listener invoked +}) +``` + +#### .cancel() + +The group of listeners can be cancelled before being invoked and have all the event +listeners removed from the underlying event emitters. + +```js +var thunk = first([ + [ee1, 'close', 'end', 'error'], + [ee2, 'error'] +], function (err, ee, event, args) { + // listener invoked +}) + +// cancel and clean up +thunk.cancel() +``` + +[npm-image]: https://img.shields.io/npm/v/ee-first.svg?style=flat-square +[npm-url]: https://npmjs.org/package/ee-first +[github-tag]: http://img.shields.io/github/tag/jonathanong/ee-first.svg?style=flat-square +[github-url]: https://github.com/jonathanong/ee-first/tags +[travis-image]: https://img.shields.io/travis/jonathanong/ee-first.svg?style=flat-square +[travis-url]: https://travis-ci.org/jonathanong/ee-first +[coveralls-image]: https://img.shields.io/coveralls/jonathanong/ee-first.svg?style=flat-square +[coveralls-url]: https://coveralls.io/r/jonathanong/ee-first?branch=master +[license-image]: http://img.shields.io/npm/l/ee-first.svg?style=flat-square +[license-url]: LICENSE.md +[downloads-image]: http://img.shields.io/npm/dm/ee-first.svg?style=flat-square +[downloads-url]: https://npmjs.org/package/ee-first +[gittip-image]: https://img.shields.io/gittip/jonathanong.svg?style=flat-square +[gittip-url]: https://www.gittip.com/jonathanong/ diff --git a/node_modules/express/node_modules/on-finished/node_modules/ee-first/index.js b/node_modules/express/node_modules/on-finished/node_modules/ee-first/index.js new file mode 100644 index 0000000..501287c --- /dev/null +++ b/node_modules/express/node_modules/on-finished/node_modules/ee-first/index.js @@ -0,0 +1,95 @@ +/*! + * ee-first + * Copyright(c) 2014 Jonathan Ong + * MIT Licensed + */ + +'use strict' + +/** + * Module exports. + * @public + */ + +module.exports = first + +/** + * Get the first event in a set of event emitters and event pairs. + * + * @param {array} stuff + * @param {function} done + * @public + */ + +function first(stuff, done) { + if (!Array.isArray(stuff)) + throw new TypeError('arg must be an array of [ee, events...] arrays') + + var cleanups = [] + + for (var i = 0; i < stuff.length; i++) { + var arr = stuff[i] + + if (!Array.isArray(arr) || arr.length < 2) + throw new TypeError('each array member must be [ee, events...]') + + var ee = arr[0] + + for (var j = 1; j < arr.length; j++) { + var event = arr[j] + var fn = listener(event, callback) + + // listen to the event + ee.on(event, fn) + // push this listener to the list of cleanups + cleanups.push({ + ee: ee, + event: event, + fn: fn, + }) + } + } + + function callback() { + cleanup() + done.apply(null, arguments) + } + + function cleanup() { + var x + for (var i = 0; i < cleanups.length; i++) { + x = cleanups[i] + x.ee.removeListener(x.event, x.fn) + } + } + + function thunk(fn) { + done = fn + } + + thunk.cancel = cleanup + + return thunk +} + +/** + * Create the event listener. + * @private + */ + +function listener(event, done) { + return function onevent(arg1) { + var args = new Array(arguments.length) + var ee = this + var err = event === 'error' + ? arg1 + : null + + // copy args to prevent arguments escaping scope + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i] + } + + done(err, ee, event, args) + } +} diff --git a/node_modules/express/node_modules/on-finished/node_modules/ee-first/package.json b/node_modules/express/node_modules/on-finished/node_modules/ee-first/package.json new file mode 100644 index 0000000..35ab9c2 --- /dev/null +++ b/node_modules/express/node_modules/on-finished/node_modules/ee-first/package.json @@ -0,0 +1,63 @@ +{ + "name": "ee-first", + "description": "return the first event in a set of ee/event pairs", + "version": "1.1.1", + "author": { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/jonathanong/ee-first" + }, + "devDependencies": { + "istanbul": "0.3.9", + "mocha": "2.2.5" + }, + "files": [ + "index.js", + "LICENSE" + ], + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "512e0ce4cc3643f603708f965a97b61b1a9c0441", + "bugs": { + "url": "https://github.com/jonathanong/ee-first/issues" + }, + "homepage": "https://github.com/jonathanong/ee-first", + "_id": "ee-first@1.1.1", + "_shasum": "590c61156b0ae2f4f0255732a158b266bc56b21d", + "_from": "ee-first@1.1.1", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "590c61156b0ae2f4f0255732a158b266bc56b21d", + "tarball": "http://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" +} diff --git a/node_modules/express/node_modules/on-finished/package.json b/node_modules/express/node_modules/on-finished/package.json new file mode 100644 index 0000000..344c25c --- /dev/null +++ b/node_modules/express/node_modules/on-finished/package.json @@ -0,0 +1,70 @@ +{ + "name": "on-finished", + "description": "Execute a callback when a request closes, finishes, or errors", + "version": "2.3.0", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/jshttp/on-finished" + }, + "dependencies": { + "ee-first": "1.1.1" + }, + "devDependencies": { + "istanbul": "0.3.9", + "mocha": "2.2.5" + }, + "engines": { + "node": ">= 0.8" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "index.js" + ], + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "34babcb58126a416fcf5205768204f2e12699dda", + "bugs": { + "url": "https://github.com/jshttp/on-finished/issues" + }, + "homepage": "https://github.com/jshttp/on-finished", + "_id": "on-finished@2.3.0", + "_shasum": "20f1336481b083cd75337992a16971aa2d906947", + "_from": "on-finished@>=2.3.0 <2.4.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + } + ], + "dist": { + "shasum": "20f1336481b083cd75337992a16971aa2d906947", + "tarball": "http://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" +} diff --git a/node_modules/express/node_modules/parseurl/.npmignore b/node_modules/express/node_modules/parseurl/.npmignore new file mode 100644 index 0000000..85c82a5 --- /dev/null +++ b/node_modules/express/node_modules/parseurl/.npmignore @@ -0,0 +1,4 @@ +benchmark/ +coverage/ +test/ +.travis.yml diff --git a/node_modules/express/node_modules/parseurl/HISTORY.md b/node_modules/express/node_modules/parseurl/HISTORY.md new file mode 100644 index 0000000..65a0860 --- /dev/null +++ b/node_modules/express/node_modules/parseurl/HISTORY.md @@ -0,0 +1,42 @@ +1.3.0 / 2014-08-09 +================== + + * Add `parseurl.original` for parsing `req.originalUrl` with fallback + * Return `undefined` if `req.url` is `undefined` + +1.2.0 / 2014-07-21 +================== + + * Cache URLs based on original value + * Remove no-longer-needed URL mis-parse work-around + * Simplify the "fast-path" `RegExp` + +1.1.3 / 2014-07-08 +================== + + * Fix typo + +1.1.2 / 2014-07-08 +================== + + * Seriously fix Node.js 0.8 compatibility + +1.1.1 / 2014-07-08 +================== + + * Fix Node.js 0.8 compatibility + +1.1.0 / 2014-07-08 +================== + + * Incorporate URL href-only parse fast-path + +1.0.1 / 2014-03-08 +================== + + * Add missing `require` + +1.0.0 / 2014-03-08 +================== + + * Genesis from `connect` diff --git a/node_modules/express/node_modules/parseurl/LICENSE b/node_modules/express/node_modules/parseurl/LICENSE new file mode 100644 index 0000000..ec7dfe7 --- /dev/null +++ b/node_modules/express/node_modules/parseurl/LICENSE @@ -0,0 +1,24 @@ + +(The MIT License) + +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2014 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/parseurl/README.md b/node_modules/express/node_modules/parseurl/README.md new file mode 100644 index 0000000..0db1d02 --- /dev/null +++ b/node_modules/express/node_modules/parseurl/README.md @@ -0,0 +1,107 @@ +# parseurl + +[![NPM version](https://badge.fury.io/js/parseurl.svg)](http://badge.fury.io/js/parseurl) +[![Build Status](https://travis-ci.org/expressjs/parseurl.svg?branch=master)](https://travis-ci.org/expressjs/parseurl) +[![Coverage Status](https://img.shields.io/coveralls/expressjs/parseurl.svg?branch=master)](https://coveralls.io/r/expressjs/parseurl) + +Parse a URL with memoization. + +## Install + +```bash +$ npm install parseurl +``` + +## API + +```js +var parseurl = require('parseurl') +``` + +### parseurl(req) + +Parse the URL of the given request object (looks at the `req.url` property) +and return the result. The result is the same as `url.parse` in Node.js core. +Calling this function multiple times on the same `req` where `req.url` does +not change will return a cached parsed object, rather than parsing again. + +### parseurl.original(req) + +Parse the original URL of the given request object and return the result. +This works by trying to parse `req.originalUrl` if it is a string, otherwise +parses `req.url`. The result is the same as `url.parse` in Node.js core. +Calling this function multiple times on the same `req` where `req.originalUrl` +does not change will return a cached parsed object, rather than parsing again. + +## Benchmark + +```bash +$ npm run-script bench + +> parseurl@1.3.0 bench nodejs-parseurl +> node benchmark/index.js + +> node benchmark/fullurl.js + + Parsing URL "http://localhost:8888/foo/bar?user=tj&pet=fluffy" + + 1 test completed. + 2 tests completed. + 3 tests completed. + + fasturl x 1,290,780 ops/sec ±0.46% (195 runs sampled) + nativeurl x 56,401 ops/sec ±0.22% (196 runs sampled) + parseurl x 55,231 ops/sec ±0.22% (194 runs sampled) + +> node benchmark/pathquery.js + + Parsing URL "/foo/bar?user=tj&pet=fluffy" + + 1 test completed. + 2 tests completed. + 3 tests completed. + + fasturl x 1,986,668 ops/sec ±0.27% (190 runs sampled) + nativeurl x 98,740 ops/sec ±0.21% (195 runs sampled) + parseurl x 2,628,171 ops/sec ±0.36% (195 runs sampled) + +> node benchmark/samerequest.js + + Parsing URL "/foo/bar?user=tj&pet=fluffy" on same request object + + 1 test completed. + 2 tests completed. + 3 tests completed. + + fasturl x 2,184,468 ops/sec ±0.40% (194 runs sampled) + nativeurl x 99,437 ops/sec ±0.71% (194 runs sampled) + parseurl x 10,498,005 ops/sec ±0.61% (186 runs sampled) + +> node benchmark/simplepath.js + + Parsing URL "/foo/bar" + + 1 test completed. + 2 tests completed. + 3 tests completed. + + fasturl x 4,535,825 ops/sec ±0.27% (191 runs sampled) + nativeurl x 98,769 ops/sec ±0.54% (191 runs sampled) + parseurl x 4,164,865 ops/sec ±0.34% (192 runs sampled) + +> node benchmark/slash.js + + Parsing URL "/" + + 1 test completed. + 2 tests completed. + 3 tests completed. + + fasturl x 4,908,405 ops/sec ±0.42% (191 runs sampled) + nativeurl x 100,945 ops/sec ±0.59% (188 runs sampled) + parseurl x 4,333,208 ops/sec ±0.27% (194 runs sampled) +``` + +## License + + [MIT](LICENSE) diff --git a/node_modules/express/node_modules/parseurl/index.js b/node_modules/express/node_modules/parseurl/index.js new file mode 100644 index 0000000..8632347 --- /dev/null +++ b/node_modules/express/node_modules/parseurl/index.js @@ -0,0 +1,136 @@ +/*! + * parseurl + * Copyright(c) 2014 Jonathan Ong + * Copyright(c) 2014 Douglas Christopher Wilson + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var url = require('url') +var parse = url.parse +var Url = url.Url + +/** + * Pattern for a simple path case. + * See: https://github.com/joyent/node/pull/7878 + */ + +var simplePathRegExp = /^(\/\/?(?!\/)[^\?#\s]*)(\?[^#\s]*)?$/ + +/** + * Exports. + */ + +module.exports = parseurl +module.exports.original = originalurl + +/** + * Parse the `req` url with memoization. + * + * @param {ServerRequest} req + * @return {Object} + * @api public + */ + +function parseurl(req) { + var url = req.url + + if (url === undefined) { + // URL is undefined + return undefined + } + + var parsed = req._parsedUrl + + if (fresh(url, parsed)) { + // Return cached URL parse + return parsed + } + + // Parse the URL + parsed = fastparse(url) + parsed._raw = url + + return req._parsedUrl = parsed +}; + +/** + * Parse the `req` original url with fallback and memoization. + * + * @param {ServerRequest} req + * @return {Object} + * @api public + */ + +function originalurl(req) { + var url = req.originalUrl + + if (typeof url !== 'string') { + // Fallback + return parseurl(req) + } + + var parsed = req._parsedOriginalUrl + + if (fresh(url, parsed)) { + // Return cached URL parse + return parsed + } + + // Parse the URL + parsed = fastparse(url) + parsed._raw = url + + return req._parsedOriginalUrl = parsed +}; + +/** + * Parse the `str` url with fast-path short-cut. + * + * @param {string} str + * @return {Object} + * @api private + */ + +function fastparse(str) { + // Try fast path regexp + // See: https://github.com/joyent/node/pull/7878 + var simplePath = typeof str === 'string' && simplePathRegExp.exec(str) + + // Construct simple URL + if (simplePath) { + var pathname = simplePath[1] + var search = simplePath[2] || null + var url = Url !== undefined + ? new Url() + : {} + url.path = str + url.href = str + url.pathname = pathname + url.search = search + url.query = search && search.substr(1) + + return url + } + + return parse(str) +} + +/** + * Determine if parsed is still fresh for url. + * + * @param {string} url + * @param {object} parsedUrl + * @return {boolean} + * @api private + */ + +function fresh(url, parsedUrl) { + return typeof parsedUrl === 'object' + && parsedUrl !== null + && (Url === undefined || parsedUrl instanceof Url) + && parsedUrl._raw === url +} diff --git a/node_modules/express/node_modules/parseurl/package.json b/node_modules/express/node_modules/parseurl/package.json new file mode 100644 index 0000000..7d567c6 --- /dev/null +++ b/node_modules/express/node_modules/parseurl/package.json @@ -0,0 +1,79 @@ +{ + "name": "parseurl", + "description": "parse a url with memoization", + "version": "1.3.0", + "author": { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + } + ], + "repository": { + "type": "git", + "url": "https://github.com/expressjs/parseurl" + }, + "license": "MIT", + "devDependencies": { + "benchmark": "1.0.0", + "beautify-benchmark": "0.2.4", + "fast-url-parser": "~1.0.0", + "istanbul": "0.3.0", + "mocha": "~1.21.4" + }, + "scripts": { + "bench": "node benchmark/index.js", + "test": "mocha --check-leaks --bail --reporter spec test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --check-leaks --reporter dot test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --check-leaks --reporter spec test/" + }, + "gitHead": "03b7ccca240e2bef5df6c25797e99175d28fb2cb", + "bugs": { + "url": "https://github.com/expressjs/parseurl/issues" + }, + "homepage": "https://github.com/expressjs/parseurl", + "_id": "parseurl@1.3.0", + "_shasum": "b58046db4223e145afa76009e61bac87cc2281b3", + "_from": "parseurl@>=1.3.0 <1.4.0", + "_npmVersion": "1.4.21", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "shtylman", + "email": "shtylman@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + } + ], + "dist": { + "shasum": "b58046db4223e145afa76009e61bac87cc2281b3", + "tarball": "http://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz" +} diff --git a/node_modules/express/node_modules/path-to-regexp/History.md b/node_modules/express/node_modules/path-to-regexp/History.md new file mode 100644 index 0000000..7f65878 --- /dev/null +++ b/node_modules/express/node_modules/path-to-regexp/History.md @@ -0,0 +1,36 @@ +0.1.7 / 2015-07-28 +================== + + * Fixed regression with escaped round brackets and matching groups. + +0.1.6 / 2015-06-19 +================== + + * Replace `index` feature by outputting all parameters, unnamed and named. + +0.1.5 / 2015-05-08 +================== + + * Add an index property for position in match result. + +0.1.4 / 2015-03-05 +================== + + * Add license information + +0.1.3 / 2014-07-06 +================== + + * Better array support + * Improved support for trailing slash in non-ending mode + +0.1.0 / 2014-03-06 +================== + + * add options.end + +0.0.2 / 2013-02-10 +================== + + * Update to match current express + * add .license property to component.json diff --git a/node_modules/express/node_modules/path-to-regexp/LICENSE b/node_modules/express/node_modules/path-to-regexp/LICENSE new file mode 100644 index 0000000..983fbe8 --- /dev/null +++ b/node_modules/express/node_modules/path-to-regexp/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Blake Embrey (hello@blakeembrey.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/express/node_modules/path-to-regexp/Readme.md b/node_modules/express/node_modules/path-to-regexp/Readme.md new file mode 100644 index 0000000..95452a6 --- /dev/null +++ b/node_modules/express/node_modules/path-to-regexp/Readme.md @@ -0,0 +1,35 @@ +# Path-to-RegExp + +Turn an Express-style path string such as `/user/:name` into a regular expression. + +**Note:** This is a legacy branch. You should upgrade to `1.x`. + +## Usage + +```javascript +var pathToRegexp = require('path-to-regexp'); +``` + +### pathToRegexp(path, keys, options) + + - **path** A string in the express format, an array of such strings, or a regular expression + - **keys** An array to be populated with the keys present in the url. Once the function completes, this will be an array of strings. + - **options** + - **options.sensitive** Defaults to false, set this to true to make routes case sensitive + - **options.strict** Defaults to false, set this to true to make the trailing slash matter. + - **options.end** Defaults to true, set this to false to only match the prefix of the URL. + +```javascript +var keys = []; +var exp = pathToRegexp('/foo/:bar', keys); +//keys = ['bar'] +//exp = /^\/foo\/(?:([^\/]+?))\/?$/i +``` + +## Live Demo + +You can see a live demo of this library in use at [express-route-tester](http://forbeslindesay.github.com/express-route-tester/). + +## License + + MIT diff --git a/node_modules/express/node_modules/path-to-regexp/index.js b/node_modules/express/node_modules/path-to-regexp/index.js new file mode 100644 index 0000000..500d1da --- /dev/null +++ b/node_modules/express/node_modules/path-to-regexp/index.js @@ -0,0 +1,129 @@ +/** + * Expose `pathtoRegexp`. + */ + +module.exports = pathtoRegexp; + +/** + * Match matching groups in a regular expression. + */ +var MATCHING_GROUP_REGEXP = /\((?!\?)/g; + +/** + * Normalize the given path string, + * returning a regular expression. + * + * An empty array should be passed, + * which will contain the placeholder + * key names. For example "/user/:id" will + * then contain ["id"]. + * + * @param {String|RegExp|Array} path + * @param {Array} keys + * @param {Object} options + * @return {RegExp} + * @api private + */ + +function pathtoRegexp(path, keys, options) { + options = options || {}; + keys = keys || []; + var strict = options.strict; + var end = options.end !== false; + var flags = options.sensitive ? '' : 'i'; + var extraOffset = 0; + var keysOffset = keys.length; + var i = 0; + var name = 0; + var m; + + if (path instanceof RegExp) { + while (m = MATCHING_GROUP_REGEXP.exec(path.source)) { + keys.push({ + name: name++, + optional: false, + offset: m.index + }); + } + + return path; + } + + if (Array.isArray(path)) { + // Map array parts into regexps and return their source. We also pass + // the same keys and options instance into every generation to get + // consistent matching groups before we join the sources together. + path = path.map(function (value) { + return pathtoRegexp(value, keys, options).source; + }); + + return new RegExp('(?:' + path.join('|') + ')', flags); + } + + path = ('^' + path + (strict ? '' : path[path.length - 1] === '/' ? '?' : '/?')) + .replace(/\/\(/g, '/(?:') + .replace(/([\/\.])/g, '\\$1') + .replace(/(\\\/)?(\\\.)?:(\w+)(\(.*?\))?(\*)?(\?)?/g, function (match, slash, format, key, capture, star, optional, offset) { + slash = slash || ''; + format = format || ''; + capture = capture || '([^\\/' + format + ']+?)'; + optional = optional || ''; + + keys.push({ + name: key, + optional: !!optional, + offset: offset + extraOffset + }); + + var result = '' + + (optional ? '' : slash) + + '(?:' + + format + (optional ? slash : '') + capture + + (star ? '((?:[\\/' + format + '].+?)?)' : '') + + ')' + + optional; + + extraOffset += result.length - match.length; + + return result; + }) + .replace(/\*/g, function (star, index) { + var len = keys.length + + while (len-- > keysOffset && keys[len].offset > index) { + keys[len].offset += 3; // Replacement length minus asterisk length. + } + + return '(.*)'; + }); + + // This is a workaround for handling unnamed matching groups. + while (m = MATCHING_GROUP_REGEXP.exec(path)) { + var escapeCount = 0; + var index = m.index; + + while (path.charAt(--index) === '\\') { + escapeCount++; + } + + // It's possible to escape the bracket. + if (escapeCount % 2 === 1) { + continue; + } + + if (keysOffset + i === keys.length || keys[keysOffset + i].offset > m.index) { + keys.splice(keysOffset + i, 0, { + name: name++, // Unnamed matching groups must be consistently linear. + optional: false, + offset: m.index + }); + } + + i++; + } + + // If the path is non-ending, match until the end or a slash. + path += (end ? '$' : (path[path.length - 1] === '/' ? '' : '(?=\\/|$)')); + + return new RegExp(path, flags); +}; diff --git a/node_modules/express/node_modules/path-to-regexp/package.json b/node_modules/express/node_modules/path-to-regexp/package.json new file mode 100644 index 0000000..484cb5c --- /dev/null +++ b/node_modules/express/node_modules/path-to-regexp/package.json @@ -0,0 +1,184 @@ +{ + "name": "path-to-regexp", + "description": "Express style path to RegExp utility", + "version": "0.1.7", + "files": [ + "index.js", + "LICENSE" + ], + "scripts": { + "test": "istanbul cover _mocha -- -R spec" + }, + "keywords": [ + "express", + "regexp" + ], + "component": { + "scripts": { + "path-to-regexp": "index.js" + } + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/component/path-to-regexp.git" + }, + "devDependencies": { + "mocha": "^1.17.1", + "istanbul": "^0.2.6" + }, + "gitHead": "039118d6c3c186d3f176c73935ca887a32a33d93", + "bugs": { + "url": "https://github.com/component/path-to-regexp/issues" + }, + "homepage": "https://github.com/component/path-to-regexp#readme", + "_id": "path-to-regexp@0.1.7", + "_shasum": "df604178005f522f15eb4490e7247a1bfaa67f8c", + "_from": "path-to-regexp@0.1.7", + "_npmVersion": "2.13.2", + "_nodeVersion": "2.3.3", + "_npmUser": { + "name": "blakeembrey", + "email": "hello@blakeembrey.com" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "hughsk", + "email": "hughskennedy@gmail.com" + }, + { + "name": "timaschew", + "email": "timaschew@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dominicbarnes", + "email": "dominic@dbarnes.info" + }, + { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + }, + { + "name": "rauchg", + "email": "rauchg@gmail.com" + }, + { + "name": "retrofox", + "email": "rdsuarez@gmail.com" + }, + { + "name": "coreh", + "email": "thecoreh@gmail.com" + }, + { + "name": "forbeslindesay", + "email": "forbes@lindesay.co.uk" + }, + { + "name": "kelonye", + "email": "kelonyemitchel@gmail.com" + }, + { + "name": "mattmueller", + "email": "mattmuelle@gmail.com" + }, + { + "name": "yields", + "email": "yields@icloud.com" + }, + { + "name": "anthonyshort", + "email": "antshort@gmail.com" + }, + { + "name": "ianstormtaylor", + "email": "ian@ianstormtaylor.com" + }, + { + "name": "cristiandouce", + "email": "cristian@gravityonmars.com" + }, + { + "name": "swatinem", + "email": "arpad.borsos@googlemail.com" + }, + { + "name": "stagas", + "email": "gstagas@gmail.com" + }, + { + "name": "amasad", + "email": "amjad.masad@gmail.com" + }, + { + "name": "juliangruber", + "email": "julian@juliangruber.com" + }, + { + "name": "calvinfo", + "email": "calvin@calv.info" + }, + { + "name": "blakeembrey", + "email": "hello@blakeembrey.com" + }, + { + "name": "timoxley", + "email": "secoif@gmail.com" + }, + { + "name": "jonathanong", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "queckezz", + "email": "fabian.eichenberger@gmail.com" + }, + { + "name": "nami-doc", + "email": "vendethiel@hotmail.fr" + }, + { + "name": "clintwood", + "email": "clint@anotherway.co.za" + }, + { + "name": "thehydroimpulse", + "email": "dnfagnan@gmail.com" + }, + { + "name": "stephenmathieson", + "email": "me@stephenmathieson.com" + }, + { + "name": "trevorgerhardt", + "email": "trevorgerhardt@gmail.com" + }, + { + "name": "dfcreative", + "email": "df.creative@gmail.com" + }, + { + "name": "defunctzombie", + "email": "shtylman@gmail.com" + } + ], + "dist": { + "shasum": "df604178005f522f15eb4490e7247a1bfaa67f8c", + "tarball": "http://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" +} diff --git a/node_modules/express/node_modules/proxy-addr/HISTORY.md b/node_modules/express/node_modules/proxy-addr/HISTORY.md new file mode 100644 index 0000000..7248dbb --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/HISTORY.md @@ -0,0 +1,66 @@ +1.0.8 / 2015-05-10 +================== + + * deps: ipaddr.js@1.0.1 + +1.0.7 / 2015-03-16 +================== + + * deps: ipaddr.js@0.1.9 + - Fix OOM on certain inputs to `isValid` + +1.0.6 / 2015-02-01 +================== + + * deps: ipaddr.js@0.1.8 + +1.0.5 / 2015-01-08 +================== + + * deps: ipaddr.js@0.1.6 + +1.0.4 / 2014-11-23 +================== + + * deps: ipaddr.js@0.1.5 + - Fix edge cases with `isValid` + +1.0.3 / 2014-09-21 +================== + + * Use `forwarded` npm module + +1.0.2 / 2014-09-18 +================== + + * Fix a global leak when multiple subnets are trusted + * Support Node.js 0.6 + * deps: ipaddr.js@0.1.3 + +1.0.1 / 2014-06-03 +================== + + * Fix links in npm package + +1.0.0 / 2014-05-08 +================== + + * Add `trust` argument to determine proxy trust on + * Accepts custom function + * Accepts IPv4/IPv6 address(es) + * Accepts subnets + * Accepts pre-defined names + * Add optional `trust` argument to `proxyaddr.all` to + stop at first untrusted + * Add `proxyaddr.compile` to pre-compile `trust` function + to make subsequent calls faster + +0.0.1 / 2014-05-04 +================== + + * Fix bad npm publish + +0.0.0 / 2014-05-04 +================== + + * Initial release diff --git a/node_modules/express/node_modules/proxy-addr/LICENSE b/node_modules/express/node_modules/proxy-addr/LICENSE new file mode 100644 index 0000000..b7dce6c --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2014 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/proxy-addr/README.md b/node_modules/express/node_modules/proxy-addr/README.md new file mode 100644 index 0000000..26f7fc0 --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/README.md @@ -0,0 +1,137 @@ +# proxy-addr + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Determine address of proxied request + +## Install + +```sh +$ npm install proxy-addr +``` + +## API + +```js +var proxyaddr = require('proxy-addr') +``` + +### proxyaddr(req, trust) + +Return the address of the request, using the given `trust` parameter. + +The `trust` argument is a function that returns `true` if you trust +the address, `false` if you don't. The closest untrusted address is +returned. + +```js +proxyaddr(req, function(addr){ return addr === '127.0.0.1' }) +proxyaddr(req, function(addr, i){ return i < 1 }) +``` + +The `trust` arugment may also be a single IP address string or an +array of trusted addresses, as plain IP addresses, CIDR-formatted +strings, or IP/netmask strings. + +```js +proxyaddr(req, '127.0.0.1') +proxyaddr(req, ['127.0.0.0/8', '10.0.0.0/8']) +proxyaddr(req, ['127.0.0.0/255.0.0.0', '192.168.0.0/255.255.0.0']) +``` + +This module also supports IPv6. Your IPv6 addresses will be normalized +automatically (i.e. `fe80::00ed:1` equals `fe80:0:0:0:0:0:ed:1`). + +```js +proxyaddr(req, '::1') +proxyaddr(req, ['::1/128', 'fe80::/10']) +proxyaddr(req, ['fe80::/ffc0::']) +``` + +This module will automatically work with IPv4-mapped IPv6 addresses +as well to support node.js in IPv6-only mode. This means that you do +not have to specify both `::ffff:a00:1` and `10.0.0.1`. + +As a convenience, this module also takes certain pre-defined names +in addition to IP addresses, which expand into IP addresses: + +```js +proxyaddr(req, 'loopback') +proxyaddr(req, ['loopback', 'fc00:ac:1ab5:fff::1/64']) +``` + + * `loopback`: IPv4 and IPv6 loopback addresses (like `::1` and + `127.0.0.1`). + * `linklocal`: IPv4 and IPv6 link-local addresses (like + `fe80::1:1:1:1` and `169.254.0.1`). + * `uniquelocal`: IPv4 private addresses and IPv6 unique-local + addresses (like `fc00:ac:1ab5:fff::1` and `192.168.0.1`). + +When `trust` is specified as a function, it will be called for each +address to determine if it is a trusted address. The function is +given two arguments: `addr` and `i`, where `addr` is a string of +the address to check and `i` is a number that represents the distance +from the socket address. + +### proxyaddr.all(req, [trust]) + +Return all the addresses of the request, optionally stopping at the +first untrusted. This array is ordered from closest to furthest +(i.e. `arr[0] === req.connection.remoteAddress`). + +```js +proxyaddr.all(req) +``` + +The optional `trust` argument takes the same arguments as `trust` +does in `proxyaddr(req, trust)`. + +```js +proxyaddr.all(req, 'loopback') +``` + +### proxyaddr.compile(val) + +Compiles argument `val` into a `trust` function. This function takes +the same arguments as `trust` does in `proxyaddr(req, trust)` and +returns a function suitable for `proxyaddr(req, trust)`. + +```js +var trust = proxyaddr.compile('localhost') +var addr = proxyaddr(req, trust) +``` + +This function is meant to be optimized for use against every request. +It is recommend to compile a trust function up-front for the trusted +configuration and pass that to `proxyaddr(req, trust)` for each request. + +## Testing + +```sh +$ npm test +``` + +## Benchmarks + +```sh +$ npm run-script bench +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/proxy-addr.svg +[npm-url]: https://npmjs.org/package/proxy-addr +[node-version-image]: https://img.shields.io/node/v/proxy-addr.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/proxy-addr/master.svg +[travis-url]: https://travis-ci.org/jshttp/proxy-addr +[coveralls-image]: https://img.shields.io/coveralls/jshttp/proxy-addr/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/proxy-addr?branch=master +[downloads-image]: https://img.shields.io/npm/dm/proxy-addr.svg +[downloads-url]: https://npmjs.org/package/proxy-addr diff --git a/node_modules/express/node_modules/proxy-addr/index.js b/node_modules/express/node_modules/proxy-addr/index.js new file mode 100644 index 0000000..d739513 --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/index.js @@ -0,0 +1,345 @@ +/*! + * proxy-addr + * Copyright(c) 2014 Douglas Christopher Wilson + * MIT Licensed + */ + +/** + * Module exports. + */ + +module.exports = proxyaddr; +module.exports.all = alladdrs; +module.exports.compile = compile; + +/** + * Module dependencies. + */ + +var forwarded = require('forwarded'); +var ipaddr = require('ipaddr.js'); + +/** + * Variables. + */ + +var digitre = /^[0-9]+$/; +var isip = ipaddr.isValid; +var parseip = ipaddr.parse; + +/** + * Pre-defined IP ranges. + */ + +var ipranges = { + linklocal: ['169.254.0.0/16', 'fe80::/10'], + loopback: ['127.0.0.1/8', '::1/128'], + uniquelocal: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fc00::/7'] +}; + +/** + * Get all addresses in the request, optionally stopping + * at the first untrusted. + * + * @param {Object} request + * @param {Function|Array|String} [trust] + * @api public + */ + +function alladdrs(req, trust) { + // get addresses + var addrs = forwarded(req); + + if (!trust) { + // Return all addresses + return addrs; + } + + if (typeof trust !== 'function') { + trust = compile(trust); + } + + for (var i = 0; i < addrs.length - 1; i++) { + if (trust(addrs[i], i)) continue; + + addrs.length = i + 1; + } + + return addrs; +} + +/** + * Compile argument into trust function. + * + * @param {Array|String} val + * @api private + */ + +function compile(val) { + if (!val) { + throw new TypeError('argument is required'); + } + + var trust = typeof val === 'string' + ? [val] + : val; + + if (!Array.isArray(trust)) { + throw new TypeError('unsupported trust argument'); + } + + for (var i = 0; i < trust.length; i++) { + val = trust[i]; + + if (!ipranges.hasOwnProperty(val)) { + continue; + } + + // Splice in pre-defined range + val = ipranges[val]; + trust.splice.apply(trust, [i, 1].concat(val)); + i += val.length - 1; + } + + return compileTrust(compileRangeSubnets(trust)); +} + +/** + * Compile `arr` elements into range subnets. + * + * @param {Array} arr + * @api private + */ + +function compileRangeSubnets(arr) { + var rangeSubnets = new Array(arr.length); + + for (var i = 0; i < arr.length; i++) { + rangeSubnets[i] = parseipNotation(arr[i]); + } + + return rangeSubnets; +} + +/** + * Compile range subnet array into trust function. + * + * @param {Array} rangeSubnets + * @api private + */ + +function compileTrust(rangeSubnets) { + // Return optimized function based on length + var len = rangeSubnets.length; + return len === 0 + ? trustNone + : len === 1 + ? trustSingle(rangeSubnets[0]) + : trustMulti(rangeSubnets); +} + +/** + * Parse IP notation string into range subnet. + * + * @param {String} note + * @api private + */ + +function parseipNotation(note) { + var ip; + var kind; + var max; + var pos = note.lastIndexOf('/'); + var range; + + ip = pos !== -1 + ? note.substring(0, pos) + : note; + + if (!isip(ip)) { + throw new TypeError('invalid IP address: ' + ip); + } + + ip = parseip(ip); + + kind = ip.kind(); + max = kind === 'ipv6' + ? 128 + : 32; + + range = pos !== -1 + ? note.substring(pos + 1, note.length) + : max; + + if (typeof range !== 'number') { + range = digitre.test(range) + ? parseInt(range, 10) + : isip(range) + ? parseNetmask(range) + : 0; + } + + if (ip.kind() === 'ipv6' && ip.isIPv4MappedAddress()) { + // Store as IPv4 + ip = ip.toIPv4Address(); + range = range <= max + ? range - 96 + : range; + } + + if (range <= 0 || range > max) { + throw new TypeError('invalid range on address: ' + note); + } + + return [ip, range]; +} + +/** + * Parse netmask string into CIDR range. + * + * @param {String} note + * @api private + */ + +function parseNetmask(netmask) { + var ip = parseip(netmask); + var parts; + var size; + + switch (ip.kind()) { + case 'ipv4': + parts = ip.octets; + size = 8; + break; + case 'ipv6': + parts = ip.parts; + size = 16; + break; + } + + var max = Math.pow(2, size) - 1; + var part; + var range = 0; + + for (var i = 0; i < parts.length; i++) { + part = parts[i] & max; + + if (part === max) { + range += size; + continue; + } + + while (part) { + part = (part << 1) & max; + range += 1; + } + + break; + } + + return range; +} + +/** + * Determine address of proxied request. + * + * @param {Object} request + * @param {Function|Array|String} trust + * @api public + */ + +function proxyaddr(req, trust) { + if (!req) { + throw new TypeError('req argument is required'); + } + + if (!trust) { + throw new TypeError('trust argument is required'); + } + + var addrs = alladdrs(req, trust); + var addr = addrs[addrs.length - 1]; + + return addr; +} + +/** + * Static trust function to trust nothing. + * + * @api private + */ + +function trustNone() { + return false; +} + +/** + * Compile trust function for multiple subnets. + * + * @param {Array} subnets + * @api private + */ + +function trustMulti(subnets) { + return function trust(addr) { + if (!isip(addr)) return false; + + var ip = parseip(addr); + var ipv4; + var kind = ip.kind(); + var subnet; + var subnetip; + var subnetkind; + var subnetrange; + var trusted; + + for (var i = 0; i < subnets.length; i++) { + subnet = subnets[i]; + subnetip = subnet[0]; + subnetkind = subnetip.kind(); + subnetrange = subnet[1]; + trusted = ip; + + if (kind !== subnetkind) { + if (kind !== 'ipv6' || subnetkind !== 'ipv4' || !ip.isIPv4MappedAddress()) { + continue; + } + + // Store addr as IPv4 + ipv4 = ipv4 || ip.toIPv4Address(); + trusted = ipv4; + } + + if (trusted.match(subnetip, subnetrange)) return true; + } + + return false; + }; +} + +/** + * Compile trust function for single subnet. + * + * @param {Object} subnet + * @api private + */ + +function trustSingle(subnet) { + var subnetip = subnet[0]; + var subnetkind = subnetip.kind(); + var subnetisipv4 = subnetkind === 'ipv4'; + var subnetrange = subnet[1]; + + return function trust(addr) { + if (!isip(addr)) return false; + + var ip = parseip(addr); + var kind = ip.kind(); + + return kind === subnetkind + ? ip.match(subnetip, subnetrange) + : subnetisipv4 && kind === 'ipv6' && ip.isIPv4MappedAddress() + ? ip.toIPv4Address().match(subnetip, subnetrange) + : false; + }; +} diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/HISTORY.md b/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/HISTORY.md new file mode 100644 index 0000000..97fa1d1 --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/HISTORY.md @@ -0,0 +1,4 @@ +0.1.0 / 2014-09-21 +================== + + * Initial release diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/LICENSE b/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/LICENSE new file mode 100644 index 0000000..b7dce6c --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2014 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/README.md b/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/README.md new file mode 100644 index 0000000..2b4988f --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/README.md @@ -0,0 +1,53 @@ +# forwarded + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Parse HTTP X-Forwarded-For header + +## Installation + +```sh +$ npm install forwarded +``` + +## API + +```js +var forwarded = require('forwarded') +``` + +### forwarded(req) + +```js +var addresses = forwarded(req) +``` + +Parse the `X-Forwarded-For` header from the request. Returns an array +of the addresses, including the socket address for the `req`. In reverse +order (i.e. index `0` is the socket address and the last index is the +furthest address, typically the end-user). + +## Testing + +```sh +$ npm test +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/forwarded.svg?style=flat +[npm-url]: https://npmjs.org/package/forwarded +[node-version-image]: https://img.shields.io/node/v/forwarded.svg?style=flat +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/forwarded.svg?style=flat +[travis-url]: https://travis-ci.org/jshttp/forwarded +[coveralls-image]: https://img.shields.io/coveralls/jshttp/forwarded.svg?style=flat +[coveralls-url]: https://coveralls.io/r/jshttp/forwarded?branch=master +[downloads-image]: https://img.shields.io/npm/dm/forwarded.svg?style=flat +[downloads-url]: https://npmjs.org/package/forwarded diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/index.js b/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/index.js new file mode 100644 index 0000000..2f5c340 --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/index.js @@ -0,0 +1,35 @@ +/*! + * forwarded + * Copyright(c) 2014 Douglas Christopher Wilson + * MIT Licensed + */ + +/** + * Module exports. + */ + +module.exports = forwarded + +/** + * Get all addresses in the request, using the `X-Forwarded-For` header. + * + * @param {Object} req + * @api public + */ + +function forwarded(req) { + if (!req) { + throw new TypeError('argument req is required') + } + + // simple header parsing + var proxyAddrs = (req.headers['x-forwarded-for'] || '') + .split(/ *, */) + .filter(Boolean) + .reverse() + var socketAddr = req.connection.remoteAddress + var addrs = [socketAddr].concat(proxyAddrs) + + // return all addresses + return addrs +} diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/package.json b/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/package.json new file mode 100644 index 0000000..4c00960 --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/forwarded/package.json @@ -0,0 +1,64 @@ +{ + "name": "forwarded", + "description": "Parse HTTP X-Forwarded-For header", + "version": "0.1.0", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + } + ], + "license": "MIT", + "keywords": [ + "x-forwarded-for", + "http", + "req" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/forwarded" + }, + "devDependencies": { + "istanbul": "0.3.2", + "mocha": "~1.21.4" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "README.md", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "e9a9faeb3cfaadf40eb57d144fff26bca9b818e8", + "bugs": { + "url": "https://github.com/jshttp/forwarded/issues" + }, + "homepage": "https://github.com/jshttp/forwarded", + "_id": "forwarded@0.1.0", + "_shasum": "19ef9874c4ae1c297bcf078fde63a09b66a84363", + "_from": "forwarded@>=0.1.0 <0.2.0", + "_npmVersion": "1.4.21", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "19ef9874c4ae1c297bcf078fde63a09b66a84363", + "tarball": "http://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz" +} diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/.npmignore b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/.npmignore new file mode 100644 index 0000000..7a1537b --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/.npmignore @@ -0,0 +1,2 @@ +.idea +node_modules diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/Cakefile b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/Cakefile new file mode 100644 index 0000000..7fd355a --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/Cakefile @@ -0,0 +1,18 @@ +fs = require 'fs' +CoffeeScript = require 'coffee-script' +nodeunit = require 'nodeunit' +UglifyJS = require 'uglify-js' + +task 'build', 'build the JavaScript files from CoffeeScript source', build = (cb) -> + source = fs.readFileSync 'src/ipaddr.coffee' + fs.writeFileSync 'lib/ipaddr.js', CoffeeScript.compile source.toString() + + invoke 'test' + invoke 'compress' + +task 'test', 'run the bundled tests', (cb) -> + nodeunit.reporters.default.run ['test'] + +task 'compress', 'uglify the resulting javascript', (cb) -> + result = UglifyJS.minify('lib/ipaddr.js') + fs.writeFileSync('ipaddr.min.js', result.code) diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/LICENSE b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/LICENSE new file mode 100644 index 0000000..3493f0d --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2011 Peter Zotov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/README.md b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/README.md new file mode 100644 index 0000000..c596e7e --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/README.md @@ -0,0 +1,161 @@ +# ipaddr.js — an IPv6 and IPv4 address manipulation library + +ipaddr.js is a small (1.9K minified and gzipped) library for manipulating +IP addresses in JavaScript environments. It runs on both CommonJS runtimes +(e.g. [nodejs]) and in a web browser. + +ipaddr.js allows you to verify and parse string representation of an IP +address, match it against a CIDR range or range list, determine if it falls +into some reserved ranges (examples include loopback and private ranges), +and convert between IPv4 and IPv4-mapped IPv6 addresses. + +[nodejs]: http://nodejs.org + +## Installation + +`npm install ipaddr.js` + +## API + +ipaddr.js defines one object in the global scope: `ipaddr`. In CommonJS, +it is exported from the module: + +```js +var ipaddr = require('ipaddr.js'); +``` + +The API consists of several global methods and two classes: ipaddr.IPv6 and ipaddr.IPv4. + +### Global methods + +There are three global methods defined: `ipaddr.isValid`, `ipaddr.parse` and +`ipaddr.process`. All of them receive a string as a single parameter. + +The `ipaddr.isValid` method returns `true` if the address is a valid IPv4 or +IPv6 address, and `false` otherwise. It does not throw any exceptions. + +The `ipaddr.parse` method returns an object representing the IP address, +or throws an `Error` if the passed string is not a valid representation of an +IP address. + +The `ipaddr.process` method works just like the `ipaddr.parse` one, but it +automatically converts IPv4-mapped IPv6 addresses to their IPv4 couterparts +before returning. It is useful when you have a Node.js instance listening +on an IPv6 socket, and the `net.ivp6.bindv6only` sysctl parameter (or its +equivalent on non-Linux OS) is set to 0. In this case, you can accept IPv4 +connections on your IPv6-only socket, but the remote address will be mangled. +Use `ipaddr.process` method to automatically demangle it. + +### Object representation + +Parsing methods return an object which descends from `ipaddr.IPv6` or +`ipaddr.IPv4`. These objects share some properties, but most of them differ. + +#### Shared properties + +One can determine the type of address by calling `addr.kind()`. It will return +either `"ipv6"` or `"ipv4"`. + +An address can be converted back to its string representation with `addr.toString()`. +Note that this method: + * does not return the original string used to create the object (in fact, there is + no way of getting that string) + * returns a compact representation (when it is applicable) + +A `match(range, bits)` method can be used to check if the address falls into a +certain CIDR range. +Note that an address can be (obviously) matched only against an address of the same type. + +For example: + +```js +var addr = ipaddr.parse("2001:db8:1234::1"); +var range = ipaddr.parse("2001:db8::"); + +addr.match(range, 32); // => true +``` + +Alternatively, `match` can also be called as `match([range, bits])`. In this way, +it can be used together with the `parseCIDR(string)` method, which parses an IP +address together with a CIDR range. + +For example: + +```js +var addr = ipaddr.parse("2001:db8:1234::1"); + +addr.match(ipaddr.parseCIDR("2001:db8::/32")); // => true +``` + +A `range()` method returns one of predefined names for several special ranges defined +by IP protocols. The exact names (and their respective CIDR ranges) can be looked up +in the source: [IPv6 ranges] and [IPv4 ranges]. Some common ones include `"unicast"` +(the default one) and `"reserved"`. + +You can match against your own range list by using +`ipaddr.subnetMatch(address, rangeList, defaultName)` method. It can work with both +IPv6 and IPv4 addresses, and accepts a name-to-subnet map as the range list. For example: + +```js +var rangeList = { + documentationOnly: [ ipaddr.parse('2001:db8::'), 32 ], + tunnelProviders: [ + [ ipaddr.parse('2001:470::'), 32 ], // he.net + [ ipaddr.parse('2001:5c0::'), 32 ] // freenet6 + ] +}; +ipaddr.subnetMatch(ipaddr.parse('2001:470:8:66::1'), rangeList, 'unknown'); // => "he.net" +``` + +The addresses can be converted to their byte representation with `toByteArray()`. +(Actually, JavaScript mostly does not know about byte buffers. They are emulated with +arrays of numbers, each in range of 0..255.) + +```js +var bytes = ipaddr.parse('2a00:1450:8007::68').toByteArray(); // ipv6.google.com +bytes // => [42, 0x00, 0x14, 0x50, 0x80, 0x07, 0x00, , 0x00, 0x68 ] +``` + +The `ipaddr.IPv4` and `ipaddr.IPv6` objects have some methods defined, too. All of them +have the same interface for both protocols, and are similar to global methods. + +`ipaddr.IPvX.isValid(string)` can be used to check if the string is a valid address +for particular protocol, and `ipaddr.IPvX.parse(string)` is the error-throwing parser. + +[IPv6 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/src/ipaddr.coffee#L186 +[IPv4 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/src/ipaddr.coffee#L71 + +#### IPv6 properties + +Sometimes you will want to convert IPv6 not to a compact string representation (with +the `::` substitution); the `toNormalizedString()` method will return an address where +all zeroes are explicit. + +For example: + +```js +var addr = ipaddr.parse("2001:0db8::0001"); +addr.toString(); // => "2001:db8::1" +addr.toNormalizedString(); // => "2001:db8:0:0:0:0:0:1" +``` + +The `isIPv4MappedAddress()` method will return `true` if this address is an IPv4-mapped +one, and `toIPv4Address()` will return an IPv4 object address. + +To access the underlying binary representation of the address, use `addr.parts`. + +```js +var addr = ipaddr.parse("2001:db8:10::1234:DEAD"); +addr.parts // => [0x2001, 0xdb8, 0x10, 0, 0, 0, 0x1234, 0xdead] +``` + +#### IPv4 properties + +`toIPv4MappedAddress()` will return a corresponding IPv4-mapped IPv6 address. + +To access the underlying representation of the address, use `addr.octets`. + +```js +var addr = ipaddr.parse("192.168.1.1"); +addr.octets // => [192, 168, 1, 1] +``` diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/ipaddr.min.js b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/ipaddr.min.js new file mode 100644 index 0000000..9e2800d --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/ipaddr.min.js @@ -0,0 +1 @@ +(function(){var r,t,e,n,i,o,a,s;t={},s=this,"undefined"!=typeof module&&null!==module&&module.exports?module.exports=t:s.ipaddr=t,a=function(r,t,e,n){var i,o;if(r.length!==t.length)throw new Error("ipaddr: cannot match CIDR for objects with different lengths");for(i=0;n>0;){if(o=e-n,0>o&&(o=0),r[i]>>o!==t[i]>>o)return!1;n-=e,i+=1}return!0},t.subnetMatch=function(r,t,e){var n,i,o,a,s;null==e&&(e="unicast");for(n in t)for(i=t[n],"[object Array]"!==toString.call(i[0])&&(i=[i]),a=0,s=i.length;s>a;a++)if(o=i[a],r.match.apply(r,o))return n;return e},t.IPv4=function(){function r(r){var t,e,n;if(4!==r.length)throw new Error("ipaddr: ipv4 octet count should be 4");for(e=0,n=r.length;n>e;e++)if(t=r[e],!(t>=0&&255>=t))throw new Error("ipaddr: ipv4 octet is a byte");this.octets=r}return r.prototype.kind=function(){return"ipv4"},r.prototype.toString=function(){return this.octets.join(".")},r.prototype.toByteArray=function(){return this.octets.slice(0)},r.prototype.match=function(r,t){var e;if(void 0===t&&(e=r,r=e[0],t=e[1]),"ipv4"!==r.kind())throw new Error("ipaddr: cannot match ipv4 address with non-ipv4 one");return a(this.octets,r.octets,8,t)},r.prototype.SpecialRanges={unspecified:[[new r([0,0,0,0]),8]],broadcast:[[new r([255,255,255,255]),32]],multicast:[[new r([224,0,0,0]),4]],linkLocal:[[new r([169,254,0,0]),16]],loopback:[[new r([127,0,0,0]),8]],"private":[[new r([10,0,0,0]),8],[new r([172,16,0,0]),12],[new r([192,168,0,0]),16]],reserved:[[new r([192,0,0,0]),24],[new r([192,0,2,0]),24],[new r([192,88,99,0]),24],[new r([198,51,100,0]),24],[new r([203,0,113,0]),24],[new r([240,0,0,0]),4]]},r.prototype.range=function(){return t.subnetMatch(this,this.SpecialRanges)},r.prototype.toIPv4MappedAddress=function(){return t.IPv6.parse("::ffff:"+this.toString())},r}(),e="(0?\\d+|0x[a-f0-9]+)",n={fourOctet:new RegExp("^"+e+"\\."+e+"\\."+e+"\\."+e+"$","i"),longValue:new RegExp("^"+e+"$","i")},t.IPv4.parser=function(r){var t,e,i,o,a;if(e=function(r){return"0"===r[0]&&"x"!==r[1]?parseInt(r,8):parseInt(r)},t=r.match(n.fourOctet))return function(){var r,n,o,a;for(o=t.slice(1,6),a=[],r=0,n=o.length;n>r;r++)i=o[r],a.push(e(i));return a}();if(t=r.match(n.longValue)){if(a=e(t[1]),a>4294967295||0>a)throw new Error("ipaddr: address outside defined range");return function(){var r,t;for(t=[],o=r=0;24>=r;o=r+=8)t.push(a>>o&255);return t}().reverse()}return null},t.IPv6=function(){function r(r){var t,e,n;if(8!==r.length)throw new Error("ipaddr: ipv6 part count should be 8");for(e=0,n=r.length;n>e;e++)if(t=r[e],!(t>=0&&65535>=t))throw new Error("ipaddr: ipv6 part should fit to two octets");this.parts=r}return r.prototype.kind=function(){return"ipv6"},r.prototype.toString=function(){var r,t,e,n,i,o,a;for(i=function(){var r,e,n,i;for(n=this.parts,i=[],r=0,e=n.length;e>r;r++)t=n[r],i.push(t.toString(16));return i}.call(this),r=[],e=function(t){return r.push(t)},n=0,o=0,a=i.length;a>o;o++)switch(t=i[o],n){case 0:e("0"===t?"":t),n=1;break;case 1:"0"===t?n=2:e(t);break;case 2:"0"!==t&&(e(""),e(t),n=3);break;case 3:e(t)}return 2===n&&(e(""),e("")),r.join(":")},r.prototype.toByteArray=function(){var r,t,e,n,i;for(r=[],i=this.parts,e=0,n=i.length;n>e;e++)t=i[e],r.push(t>>8),r.push(255&t);return r},r.prototype.toNormalizedString=function(){var r;return function(){var t,e,n,i;for(n=this.parts,i=[],t=0,e=n.length;e>t;t++)r=n[t],i.push(r.toString(16));return i}.call(this).join(":")},r.prototype.match=function(r,t){var e;if(void 0===t&&(e=r,r=e[0],t=e[1]),"ipv6"!==r.kind())throw new Error("ipaddr: cannot match ipv6 address with non-ipv6 one");return a(this.parts,r.parts,16,t)},r.prototype.SpecialRanges={unspecified:[new r([0,0,0,0,0,0,0,0]),128],linkLocal:[new r([65152,0,0,0,0,0,0,0]),10],multicast:[new r([65280,0,0,0,0,0,0,0]),8],loopback:[new r([0,0,0,0,0,0,0,1]),128],uniqueLocal:[new r([64512,0,0,0,0,0,0,0]),7],ipv4Mapped:[new r([0,0,0,0,0,65535,0,0]),96],rfc6145:[new r([0,0,0,0,65535,0,0,0]),96],rfc6052:[new r([100,65435,0,0,0,0,0,0]),96],"6to4":[new r([8194,0,0,0,0,0,0,0]),16],teredo:[new r([8193,0,0,0,0,0,0,0]),32],reserved:[[new r([8193,3512,0,0,0,0,0,0]),32]]},r.prototype.range=function(){return t.subnetMatch(this,this.SpecialRanges)},r.prototype.isIPv4MappedAddress=function(){return"ipv4Mapped"===this.range()},r.prototype.toIPv4Address=function(){var r,e,n;if(!this.isIPv4MappedAddress())throw new Error("ipaddr: trying to convert a generic ipv6 address to ipv4");return n=this.parts.slice(-2),r=n[0],e=n[1],new t.IPv4([r>>8,255&r,e>>8,255&e])},r}(),i="(?:[0-9a-f]+::?)+",o={"native":new RegExp("^(::)?("+i+")?([0-9a-f]+)?(::)?$","i"),transitional:new RegExp("^((?:"+i+")|(?:::)(?:"+i+")?)"+(""+e+"\\."+e+"\\."+e+"\\."+e+"$"),"i")},r=function(r,t){var e,n,i,o,a;if(r.indexOf("::")!==r.lastIndexOf("::"))return null;for(e=0,n=-1;(n=r.indexOf(":",n+1))>=0;)e++;if(":"===r[0]&&e--,":"===r[r.length-1]&&e--,e>t)return null;for(a=t-e,o=":";a--;)o+="0:";return r=r.replace("::",o),":"===r[0]&&(r=r.slice(1)),":"===r[r.length-1]&&(r=r.slice(0,-1)),function(){var t,e,n,o;for(n=r.split(":"),o=[],t=0,e=n.length;e>t;t++)i=n[t],o.push(parseInt(i,16));return o}()},t.IPv6.parser=function(t){var e,n;return t.match(o["native"])?r(t,8):(e=t.match(o.transitional))&&(n=r(e[1].slice(0,-1),6))?(n.push(parseInt(e[2])<<8|parseInt(e[3])),n.push(parseInt(e[4])<<8|parseInt(e[5])),n):null},t.IPv4.isIPv4=t.IPv6.isIPv6=function(r){return null!==this.parser(r)},t.IPv4.isValid=t.IPv6.isValid=function(r){var t;try{return new this(this.parser(r)),!0}catch(e){return t=e,!1}},t.IPv4.parse=t.IPv6.parse=function(r){var t;if(t=this.parser(r),null===t)throw new Error("ipaddr: string is not formatted like ip address");return new this(t)},t.IPv4.parseCIDR=t.IPv6.parseCIDR=function(r){var t;if(t=r.match(/^(.+)\/(\d+)$/))return[this.parse(t[1]),parseInt(t[2])];throw new Error("ipaddr: string is not formatted like a CIDR range")},t.isValid=function(r){return t.IPv6.isValid(r)||t.IPv4.isValid(r)},t.parse=function(r){if(t.IPv6.isValid(r))return t.IPv6.parse(r);if(t.IPv4.isValid(r))return t.IPv4.parse(r);throw new Error("ipaddr: the address has neither IPv6 nor IPv4 format")},t.parseCIDR=function(r){var e;try{return t.IPv6.parseCIDR(r)}catch(n){e=n;try{return t.IPv4.parseCIDR(r)}catch(n){throw e=n,new Error("ipaddr: the address has neither IPv6 nor IPv4 CIDR format")}}},t.process=function(r){var t;return t=this.parse(r),"ipv6"===t.kind()&&t.isIPv4MappedAddress()?t.toIPv4Address():t}}).call(this); \ No newline at end of file diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/lib/ipaddr.js b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/lib/ipaddr.js new file mode 100644 index 0000000..5d99e08 --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/lib/ipaddr.js @@ -0,0 +1,439 @@ +(function() { + var expandIPv6, ipaddr, ipv4Part, ipv4Regexes, ipv6Part, ipv6Regexes, matchCIDR, root; + + ipaddr = {}; + + root = this; + + if ((typeof module !== "undefined" && module !== null) && module.exports) { + module.exports = ipaddr; + } else { + root['ipaddr'] = ipaddr; + } + + matchCIDR = function(first, second, partSize, cidrBits) { + var part, shift; + if (first.length !== second.length) { + throw new Error("ipaddr: cannot match CIDR for objects with different lengths"); + } + part = 0; + while (cidrBits > 0) { + shift = partSize - cidrBits; + if (shift < 0) { + shift = 0; + } + if (first[part] >> shift !== second[part] >> shift) { + return false; + } + cidrBits -= partSize; + part += 1; + } + return true; + }; + + ipaddr.subnetMatch = function(address, rangeList, defaultName) { + var rangeName, rangeSubnets, subnet, _i, _len; + if (defaultName == null) { + defaultName = 'unicast'; + } + for (rangeName in rangeList) { + rangeSubnets = rangeList[rangeName]; + if (toString.call(rangeSubnets[0]) !== '[object Array]') { + rangeSubnets = [rangeSubnets]; + } + for (_i = 0, _len = rangeSubnets.length; _i < _len; _i++) { + subnet = rangeSubnets[_i]; + if (address.match.apply(address, subnet)) { + return rangeName; + } + } + } + return defaultName; + }; + + ipaddr.IPv4 = (function() { + function IPv4(octets) { + var octet, _i, _len; + if (octets.length !== 4) { + throw new Error("ipaddr: ipv4 octet count should be 4"); + } + for (_i = 0, _len = octets.length; _i < _len; _i++) { + octet = octets[_i]; + if (!((0 <= octet && octet <= 255))) { + throw new Error("ipaddr: ipv4 octet is a byte"); + } + } + this.octets = octets; + } + + IPv4.prototype.kind = function() { + return 'ipv4'; + }; + + IPv4.prototype.toString = function() { + return this.octets.join("."); + }; + + IPv4.prototype.toByteArray = function() { + return this.octets.slice(0); + }; + + IPv4.prototype.match = function(other, cidrRange) { + var _ref; + if (cidrRange === void 0) { + _ref = other, other = _ref[0], cidrRange = _ref[1]; + } + if (other.kind() !== 'ipv4') { + throw new Error("ipaddr: cannot match ipv4 address with non-ipv4 one"); + } + return matchCIDR(this.octets, other.octets, 8, cidrRange); + }; + + IPv4.prototype.SpecialRanges = { + unspecified: [[new IPv4([0, 0, 0, 0]), 8]], + broadcast: [[new IPv4([255, 255, 255, 255]), 32]], + multicast: [[new IPv4([224, 0, 0, 0]), 4]], + linkLocal: [[new IPv4([169, 254, 0, 0]), 16]], + loopback: [[new IPv4([127, 0, 0, 0]), 8]], + "private": [[new IPv4([10, 0, 0, 0]), 8], [new IPv4([172, 16, 0, 0]), 12], [new IPv4([192, 168, 0, 0]), 16]], + reserved: [[new IPv4([192, 0, 0, 0]), 24], [new IPv4([192, 0, 2, 0]), 24], [new IPv4([192, 88, 99, 0]), 24], [new IPv4([198, 51, 100, 0]), 24], [new IPv4([203, 0, 113, 0]), 24], [new IPv4([240, 0, 0, 0]), 4]] + }; + + IPv4.prototype.range = function() { + return ipaddr.subnetMatch(this, this.SpecialRanges); + }; + + IPv4.prototype.toIPv4MappedAddress = function() { + return ipaddr.IPv6.parse("::ffff:" + (this.toString())); + }; + + return IPv4; + + })(); + + ipv4Part = "(0?\\d+|0x[a-f0-9]+)"; + + ipv4Regexes = { + fourOctet: new RegExp("^" + ipv4Part + "\\." + ipv4Part + "\\." + ipv4Part + "\\." + ipv4Part + "$", 'i'), + longValue: new RegExp("^" + ipv4Part + "$", 'i') + }; + + ipaddr.IPv4.parser = function(string) { + var match, parseIntAuto, part, shift, value; + parseIntAuto = function(string) { + if (string[0] === "0" && string[1] !== "x") { + return parseInt(string, 8); + } else { + return parseInt(string); + } + }; + if (match = string.match(ipv4Regexes.fourOctet)) { + return (function() { + var _i, _len, _ref, _results; + _ref = match.slice(1, 6); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + part = _ref[_i]; + _results.push(parseIntAuto(part)); + } + return _results; + })(); + } else if (match = string.match(ipv4Regexes.longValue)) { + value = parseIntAuto(match[1]); + if (value > 0xffffffff || value < 0) { + throw new Error("ipaddr: address outside defined range"); + } + return ((function() { + var _i, _results; + _results = []; + for (shift = _i = 0; _i <= 24; shift = _i += 8) { + _results.push((value >> shift) & 0xff); + } + return _results; + })()).reverse(); + } else { + return null; + } + }; + + ipaddr.IPv6 = (function() { + function IPv6(parts) { + var part, _i, _len; + if (parts.length !== 8) { + throw new Error("ipaddr: ipv6 part count should be 8"); + } + for (_i = 0, _len = parts.length; _i < _len; _i++) { + part = parts[_i]; + if (!((0 <= part && part <= 0xffff))) { + throw new Error("ipaddr: ipv6 part should fit to two octets"); + } + } + this.parts = parts; + } + + IPv6.prototype.kind = function() { + return 'ipv6'; + }; + + IPv6.prototype.toString = function() { + var compactStringParts, part, pushPart, state, stringParts, _i, _len; + stringParts = (function() { + var _i, _len, _ref, _results; + _ref = this.parts; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + part = _ref[_i]; + _results.push(part.toString(16)); + } + return _results; + }).call(this); + compactStringParts = []; + pushPart = function(part) { + return compactStringParts.push(part); + }; + state = 0; + for (_i = 0, _len = stringParts.length; _i < _len; _i++) { + part = stringParts[_i]; + switch (state) { + case 0: + if (part === '0') { + pushPart(''); + } else { + pushPart(part); + } + state = 1; + break; + case 1: + if (part === '0') { + state = 2; + } else { + pushPart(part); + } + break; + case 2: + if (part !== '0') { + pushPart(''); + pushPart(part); + state = 3; + } + break; + case 3: + pushPart(part); + } + } + if (state === 2) { + pushPart(''); + pushPart(''); + } + return compactStringParts.join(":"); + }; + + IPv6.prototype.toByteArray = function() { + var bytes, part, _i, _len, _ref; + bytes = []; + _ref = this.parts; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + part = _ref[_i]; + bytes.push(part >> 8); + bytes.push(part & 0xff); + } + return bytes; + }; + + IPv6.prototype.toNormalizedString = function() { + var part; + return ((function() { + var _i, _len, _ref, _results; + _ref = this.parts; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + part = _ref[_i]; + _results.push(part.toString(16)); + } + return _results; + }).call(this)).join(":"); + }; + + IPv6.prototype.match = function(other, cidrRange) { + var _ref; + if (cidrRange === void 0) { + _ref = other, other = _ref[0], cidrRange = _ref[1]; + } + if (other.kind() !== 'ipv6') { + throw new Error("ipaddr: cannot match ipv6 address with non-ipv6 one"); + } + return matchCIDR(this.parts, other.parts, 16, cidrRange); + }; + + IPv6.prototype.SpecialRanges = { + unspecified: [new IPv6([0, 0, 0, 0, 0, 0, 0, 0]), 128], + linkLocal: [new IPv6([0xfe80, 0, 0, 0, 0, 0, 0, 0]), 10], + multicast: [new IPv6([0xff00, 0, 0, 0, 0, 0, 0, 0]), 8], + loopback: [new IPv6([0, 0, 0, 0, 0, 0, 0, 1]), 128], + uniqueLocal: [new IPv6([0xfc00, 0, 0, 0, 0, 0, 0, 0]), 7], + ipv4Mapped: [new IPv6([0, 0, 0, 0, 0, 0xffff, 0, 0]), 96], + rfc6145: [new IPv6([0, 0, 0, 0, 0xffff, 0, 0, 0]), 96], + rfc6052: [new IPv6([0x64, 0xff9b, 0, 0, 0, 0, 0, 0]), 96], + '6to4': [new IPv6([0x2002, 0, 0, 0, 0, 0, 0, 0]), 16], + teredo: [new IPv6([0x2001, 0, 0, 0, 0, 0, 0, 0]), 32], + reserved: [[new IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]), 32]] + }; + + IPv6.prototype.range = function() { + return ipaddr.subnetMatch(this, this.SpecialRanges); + }; + + IPv6.prototype.isIPv4MappedAddress = function() { + return this.range() === 'ipv4Mapped'; + }; + + IPv6.prototype.toIPv4Address = function() { + var high, low, _ref; + if (!this.isIPv4MappedAddress()) { + throw new Error("ipaddr: trying to convert a generic ipv6 address to ipv4"); + } + _ref = this.parts.slice(-2), high = _ref[0], low = _ref[1]; + return new ipaddr.IPv4([high >> 8, high & 0xff, low >> 8, low & 0xff]); + }; + + return IPv6; + + })(); + + ipv6Part = "(?:[0-9a-f]+::?)+"; + + ipv6Regexes = { + "native": new RegExp("^(::)?(" + ipv6Part + ")?([0-9a-f]+)?(::)?$", 'i'), + transitional: new RegExp(("^((?:" + ipv6Part + ")|(?:::)(?:" + ipv6Part + ")?)") + ("" + ipv4Part + "\\." + ipv4Part + "\\." + ipv4Part + "\\." + ipv4Part + "$"), 'i') + }; + + expandIPv6 = function(string, parts) { + var colonCount, lastColon, part, replacement, replacementCount; + if (string.indexOf('::') !== string.lastIndexOf('::')) { + return null; + } + colonCount = 0; + lastColon = -1; + while ((lastColon = string.indexOf(':', lastColon + 1)) >= 0) { + colonCount++; + } + if (string[0] === ':') { + colonCount--; + } + if (string[string.length - 1] === ':') { + colonCount--; + } + if (colonCount > parts) { + return null; + } + replacementCount = parts - colonCount; + replacement = ':'; + while (replacementCount--) { + replacement += '0:'; + } + string = string.replace('::', replacement); + if (string[0] === ':') { + string = string.slice(1); + } + if (string[string.length - 1] === ':') { + string = string.slice(0, -1); + } + return (function() { + var _i, _len, _ref, _results; + _ref = string.split(":"); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + part = _ref[_i]; + _results.push(parseInt(part, 16)); + } + return _results; + })(); + }; + + ipaddr.IPv6.parser = function(string) { + var match, parts; + if (string.match(ipv6Regexes['native'])) { + return expandIPv6(string, 8); + } else if (match = string.match(ipv6Regexes['transitional'])) { + parts = expandIPv6(match[1].slice(0, -1), 6); + if (parts) { + parts.push(parseInt(match[2]) << 8 | parseInt(match[3])); + parts.push(parseInt(match[4]) << 8 | parseInt(match[5])); + return parts; + } + } + return null; + }; + + ipaddr.IPv4.isIPv4 = ipaddr.IPv6.isIPv6 = function(string) { + return this.parser(string) !== null; + }; + + ipaddr.IPv4.isValid = ipaddr.IPv6.isValid = function(string) { + var e; + try { + new this(this.parser(string)); + return true; + } catch (_error) { + e = _error; + return false; + } + }; + + ipaddr.IPv4.parse = ipaddr.IPv6.parse = function(string) { + var parts; + parts = this.parser(string); + if (parts === null) { + throw new Error("ipaddr: string is not formatted like ip address"); + } + return new this(parts); + }; + + ipaddr.IPv4.parseCIDR = ipaddr.IPv6.parseCIDR = function(string) { + var match; + if (match = string.match(/^(.+)\/(\d+)$/)) { + return [this.parse(match[1]), parseInt(match[2])]; + } + throw new Error("ipaddr: string is not formatted like a CIDR range"); + }; + + ipaddr.isValid = function(string) { + return ipaddr.IPv6.isValid(string) || ipaddr.IPv4.isValid(string); + }; + + ipaddr.parse = function(string) { + if (ipaddr.IPv6.isValid(string)) { + return ipaddr.IPv6.parse(string); + } else if (ipaddr.IPv4.isValid(string)) { + return ipaddr.IPv4.parse(string); + } else { + throw new Error("ipaddr: the address has neither IPv6 nor IPv4 format"); + } + }; + + ipaddr.parseCIDR = function(string) { + var e; + try { + return ipaddr.IPv6.parseCIDR(string); + } catch (_error) { + e = _error; + try { + return ipaddr.IPv4.parseCIDR(string); + } catch (_error) { + e = _error; + throw new Error("ipaddr: the address has neither IPv6 nor IPv4 CIDR format"); + } + } + }; + + ipaddr.process = function(string) { + var addr; + addr = this.parse(string); + if (addr.kind() === 'ipv6' && addr.isIPv4MappedAddress()) { + return addr.toIPv4Address(); + } else { + return addr; + } + }; + +}).call(this); diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/package.json b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/package.json new file mode 100644 index 0000000..dcb6a43 --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/package.json @@ -0,0 +1,58 @@ +{ + "name": "ipaddr.js", + "description": "A library for manipulating IPv4 and IPv6 addresses in JavaScript.", + "version": "1.0.1", + "author": { + "name": "Peter Zotov", + "email": "whitequark@whitequark.org" + }, + "directories": { + "lib": "./lib" + }, + "dependencies": {}, + "devDependencies": { + "coffee-script": "~1.6", + "nodeunit": "~0.5.3", + "uglify-js": "latest" + }, + "scripts": { + "test": "cake build test" + }, + "keywords": [ + "ip", + "ipv4", + "ipv6" + ], + "repository": { + "type": "git", + "url": "git://github.com/whitequark/ipaddr.js" + }, + "main": "./lib/ipaddr", + "engines": { + "node": ">= 0.2.5" + }, + "license": "MIT", + "gitHead": "0a5a26d9317a58d67047e7f32b5b1bbe7f2f7fbf", + "bugs": { + "url": "https://github.com/whitequark/ipaddr.js/issues" + }, + "_id": "ipaddr.js@1.0.1", + "_shasum": "5f38801dc73e0400fc7076386f6ed5215fbd8f95", + "_from": "ipaddr.js@1.0.1", + "_npmVersion": "1.4.21", + "_npmUser": { + "name": "whitequark", + "email": "whitequark@whitequark.org" + }, + "maintainers": [ + { + "name": "whitequark", + "email": "whitequark@whitequark.org" + } + ], + "dist": { + "shasum": "5f38801dc73e0400fc7076386f6ed5215fbd8f95", + "tarball": "http://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.1.tgz" + }, + "_resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.1.tgz" +} diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/src/ipaddr.coffee b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/src/ipaddr.coffee new file mode 100644 index 0000000..0a48080 --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/src/ipaddr.coffee @@ -0,0 +1,374 @@ +# Define the main object +ipaddr = {} + +root = this + +# Export for both the CommonJS and browser-like environment +if module? && module.exports + module.exports = ipaddr +else + root['ipaddr'] = ipaddr + +# A generic CIDR (Classless Inter-Domain Routing) RFC1518 range matcher. +matchCIDR = (first, second, partSize, cidrBits) -> + if first.length != second.length + throw new Error "ipaddr: cannot match CIDR for objects with different lengths" + + part = 0 + while cidrBits > 0 + shift = partSize - cidrBits + shift = 0 if shift < 0 + + if first[part] >> shift != second[part] >> shift + return false + + cidrBits -= partSize + part += 1 + + return true + +# An utility function to ease named range matching. See examples below. +ipaddr.subnetMatch = (address, rangeList, defaultName='unicast') -> + for rangeName, rangeSubnets of rangeList + # ECMA5 Array.isArray isn't available everywhere + if toString.call(rangeSubnets[0]) != '[object Array]' + rangeSubnets = [ rangeSubnets ] + + for subnet in rangeSubnets + return rangeName if address.match.apply(address, subnet) + + return defaultName + +# An IPv4 address (RFC791). +class ipaddr.IPv4 + # Constructs a new IPv4 address from an array of four octets. + # Verifies the input. + constructor: (octets) -> + if octets.length != 4 + throw new Error "ipaddr: ipv4 octet count should be 4" + + for octet in octets + if !(0 <= octet <= 255) + throw new Error "ipaddr: ipv4 octet is a byte" + + @octets = octets + + # The 'kind' method exists on both IPv4 and IPv6 classes. + kind: -> + return 'ipv4' + + # Returns the address in convenient, decimal-dotted format. + toString: -> + return @octets.join "." + + # Returns an array of byte-sized values in network order + toByteArray: -> + return @octets.slice(0) # octets.clone + + # Checks if this address matches other one within given CIDR range. + match: (other, cidrRange) -> + if cidrRange == undefined + [other, cidrRange] = other + + if other.kind() != 'ipv4' + throw new Error "ipaddr: cannot match ipv4 address with non-ipv4 one" + + return matchCIDR(this.octets, other.octets, 8, cidrRange) + + # Special IPv4 address ranges. + SpecialRanges: + unspecified: [ + [ new IPv4([0, 0, 0, 0]), 8 ] + ] + broadcast: [ + [ new IPv4([255, 255, 255, 255]), 32 ] + ] + multicast: [ # RFC3171 + [ new IPv4([224, 0, 0, 0]), 4 ] + ] + linkLocal: [ # RFC3927 + [ new IPv4([169, 254, 0, 0]), 16 ] + ] + loopback: [ # RFC5735 + [ new IPv4([127, 0, 0, 0]), 8 ] + ] + private: [ # RFC1918 + [ new IPv4([10, 0, 0, 0]), 8 ] + [ new IPv4([172, 16, 0, 0]), 12 ] + [ new IPv4([192, 168, 0, 0]), 16 ] + ] + reserved: [ # Reserved and testing-only ranges; RFCs 5735, 5737, 2544, 1700 + [ new IPv4([192, 0, 0, 0]), 24 ] + [ new IPv4([192, 0, 2, 0]), 24 ] + [ new IPv4([192, 88, 99, 0]), 24 ] + [ new IPv4([198, 51, 100, 0]), 24 ] + [ new IPv4([203, 0, 113, 0]), 24 ] + [ new IPv4([240, 0, 0, 0]), 4 ] + ] + + # Checks if the address corresponds to one of the special ranges. + range: -> + return ipaddr.subnetMatch(this, @SpecialRanges) + + # Convrets this IPv4 address to an IPv4-mapped IPv6 address. + toIPv4MappedAddress: -> + return ipaddr.IPv6.parse "::ffff:#{@toString()}" + +# A list of regular expressions that match arbitrary IPv4 addresses, +# for which a number of weird notations exist. +# Note that an address like 0010.0xa5.1.1 is considered legal. +ipv4Part = "(0?\\d+|0x[a-f0-9]+)" +ipv4Regexes = + fourOctet: new RegExp "^#{ipv4Part}\\.#{ipv4Part}\\.#{ipv4Part}\\.#{ipv4Part}$", 'i' + longValue: new RegExp "^#{ipv4Part}$", 'i' + +# Classful variants (like a.b, where a is an octet, and b is a 24-bit +# value representing last three octets; this corresponds to a class C +# address) are omitted due to classless nature of modern Internet. +ipaddr.IPv4.parser = (string) -> + parseIntAuto = (string) -> + if string[0] == "0" && string[1] != "x" + parseInt(string, 8) + else + parseInt(string) + + # parseInt recognizes all that octal & hexadecimal weirdness for us + if match = string.match(ipv4Regexes.fourOctet) + return (parseIntAuto(part) for part in match[1..5]) + else if match = string.match(ipv4Regexes.longValue) + value = parseIntAuto(match[1]) + if value > 0xffffffff || value < 0 + throw new Error "ipaddr: address outside defined range" + return ((value >> shift) & 0xff for shift in [0..24] by 8).reverse() + else + return null + +# An IPv6 address (RFC2460) +class ipaddr.IPv6 + # Constructs an IPv6 address from an array of eight 16-bit parts. + # Throws an error if the input is invalid. + constructor: (parts) -> + if parts.length != 8 + throw new Error "ipaddr: ipv6 part count should be 8" + + for part in parts + if !(0 <= part <= 0xffff) + throw new Error "ipaddr: ipv6 part should fit to two octets" + + @parts = parts + + # The 'kind' method exists on both IPv4 and IPv6 classes. + kind: -> + return 'ipv6' + + # Returns the address in compact, human-readable format like + # 2001:db8:8:66::1 + toString: -> + stringParts = (part.toString(16) for part in @parts) + + compactStringParts = [] + pushPart = (part) -> compactStringParts.push part + + state = 0 + for part in stringParts + switch state + when 0 + if part == '0' + pushPart('') + else + pushPart(part) + + state = 1 + when 1 + if part == '0' + state = 2 + else + pushPart(part) + when 2 + unless part == '0' + pushPart('') + pushPart(part) + state = 3 + when 3 + pushPart(part) + + if state == 2 + pushPart('') + pushPart('') + + return compactStringParts.join ":" + + # Returns an array of byte-sized values in network order + toByteArray: -> + bytes = [] + for part in @parts + bytes.push(part >> 8) + bytes.push(part & 0xff) + + return bytes + + # Returns the address in expanded format with all zeroes included, like + # 2001:db8:8:66:0:0:0:1 + toNormalizedString: -> + return (part.toString(16) for part in @parts).join ":" + + # Checks if this address matches other one within given CIDR range. + match: (other, cidrRange) -> + if cidrRange == undefined + [other, cidrRange] = other + + if other.kind() != 'ipv6' + throw new Error "ipaddr: cannot match ipv6 address with non-ipv6 one" + + return matchCIDR(this.parts, other.parts, 16, cidrRange) + + # Special IPv6 ranges + SpecialRanges: + unspecified: [ new IPv6([0, 0, 0, 0, 0, 0, 0, 0]), 128 ] # RFC4291, here and after + linkLocal: [ new IPv6([0xfe80, 0, 0, 0, 0, 0, 0, 0]), 10 ] + multicast: [ new IPv6([0xff00, 0, 0, 0, 0, 0, 0, 0]), 8 ] + loopback: [ new IPv6([0, 0, 0, 0, 0, 0, 0, 1]), 128 ] + uniqueLocal: [ new IPv6([0xfc00, 0, 0, 0, 0, 0, 0, 0]), 7 ] + ipv4Mapped: [ new IPv6([0, 0, 0, 0, 0, 0xffff, 0, 0]), 96 ] + rfc6145: [ new IPv6([0, 0, 0, 0, 0xffff, 0, 0, 0]), 96 ] # RFC6145 + rfc6052: [ new IPv6([0x64, 0xff9b, 0, 0, 0, 0, 0, 0]), 96 ] # RFC6052 + '6to4': [ new IPv6([0x2002, 0, 0, 0, 0, 0, 0, 0]), 16 ] # RFC3056 + teredo: [ new IPv6([0x2001, 0, 0, 0, 0, 0, 0, 0]), 32 ] # RFC6052, RFC6146 + reserved: [ + [ new IPv6([ 0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]), 32 ] # RFC4291 + ] + + # Checks if the address corresponds to one of the special ranges. + range: -> + return ipaddr.subnetMatch(this, @SpecialRanges) + + # Checks if this address is an IPv4-mapped IPv6 address. + isIPv4MappedAddress: -> + return @range() == 'ipv4Mapped' + + # Converts this address to IPv4 address if it is an IPv4-mapped IPv6 address. + # Throws an error otherwise. + toIPv4Address: -> + unless @isIPv4MappedAddress() + throw new Error "ipaddr: trying to convert a generic ipv6 address to ipv4" + + [high, low] = @parts[-2..-1] + + return new ipaddr.IPv4([high >> 8, high & 0xff, low >> 8, low & 0xff]) + +# IPv6-matching regular expressions. +# For IPv6, the task is simpler: it is enough to match the colon-delimited +# hexadecimal IPv6 and a transitional variant with dotted-decimal IPv4 at +# the end. +ipv6Part = "(?:[0-9a-f]+::?)+" +ipv6Regexes = + native: new RegExp "^(::)?(#{ipv6Part})?([0-9a-f]+)?(::)?$", 'i' + transitional: new RegExp "^((?:#{ipv6Part})|(?:::)(?:#{ipv6Part})?)" + + "#{ipv4Part}\\.#{ipv4Part}\\.#{ipv4Part}\\.#{ipv4Part}$", 'i' + +# Expand :: in an IPv6 address or address part consisting of `parts` groups. +expandIPv6 = (string, parts) -> + # More than one '::' means invalid adddress + if string.indexOf('::') != string.lastIndexOf('::') + return null + + # How many parts do we already have? + colonCount = 0 + lastColon = -1 + while (lastColon = string.indexOf(':', lastColon + 1)) >= 0 + colonCount++ + + # 0::0 is two parts more than :: + colonCount-- if string[0] == ':' + colonCount-- if string[string.length-1] == ':' + + # The following loop would hang if colonCount > parts + if colonCount > parts + return null + + # replacement = ':' + '0:' * (parts - colonCount) + replacementCount = parts - colonCount + replacement = ':' + while replacementCount-- + replacement += '0:' + + # Insert the missing zeroes + string = string.replace('::', replacement) + + # Trim any garbage which may be hanging around if :: was at the edge in + # the source string + string = string[1..-1] if string[0] == ':' + string = string[0..-2] if string[string.length-1] == ':' + + return (parseInt(part, 16) for part in string.split(":")) + +# Parse an IPv6 address. +ipaddr.IPv6.parser = (string) -> + if string.match(ipv6Regexes['native']) + return expandIPv6(string, 8) + + else if match = string.match(ipv6Regexes['transitional']) + parts = expandIPv6(match[1][0..-2], 6) + if parts + parts.push(parseInt(match[2]) << 8 | parseInt(match[3])) + parts.push(parseInt(match[4]) << 8 | parseInt(match[5])) + return parts + + return null + +# Checks if a given string is formatted like IPv4/IPv6 address. +ipaddr.IPv4.isIPv4 = ipaddr.IPv6.isIPv6 = (string) -> + return @parser(string) != null + +# Checks if a given string is a valid IPv4/IPv6 address. +ipaddr.IPv4.isValid = ipaddr.IPv6.isValid = (string) -> + try + new this(@parser(string)) + return true + catch e + return false + +# Tries to parse and validate a string with IPv4/IPv6 address. +# Throws an error if it fails. +ipaddr.IPv4.parse = ipaddr.IPv6.parse = (string) -> + parts = @parser(string) + if parts == null + throw new Error "ipaddr: string is not formatted like ip address" + + return new this(parts) + +ipaddr.IPv4.parseCIDR = ipaddr.IPv6.parseCIDR = (string) -> + if match = string.match(/^(.+)\/(\d+)$/) + return [@parse(match[1]), parseInt(match[2])] + + throw new Error "ipaddr: string is not formatted like a CIDR range" + +# Checks if the address is valid IP address +ipaddr.isValid = (string) -> + return ipaddr.IPv6.isValid(string) || ipaddr.IPv4.isValid(string) + +# Try to parse an address and throw an error if it is impossible +ipaddr.parse = (string) -> + if ipaddr.IPv6.isValid(string) + return ipaddr.IPv6.parse(string) + else if ipaddr.IPv4.isValid(string) + return ipaddr.IPv4.parse(string) + else + throw new Error "ipaddr: the address has neither IPv6 nor IPv4 format" + +ipaddr.parseCIDR = (string) -> + try + return ipaddr.IPv6.parseCIDR(string) + catch e + try + return ipaddr.IPv4.parseCIDR(string) + catch e + throw new Error "ipaddr: the address has neither IPv6 nor IPv4 CIDR format" + +# Parse an address and return plain IPv4 address if it is an IPv4-mapped address +ipaddr.process = (string) -> + addr = @parse(string) + if addr.kind() == 'ipv6' && addr.isIPv4MappedAddress() + return addr.toIPv4Address() + else + return addr diff --git a/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/test/ipaddr.test.coffee b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/test/ipaddr.test.coffee new file mode 100644 index 0000000..361561e --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js/test/ipaddr.test.coffee @@ -0,0 +1,262 @@ +ipaddr = require '../lib/ipaddr' + +module.exports = + 'should define main classes': (test) -> + test.ok(ipaddr.IPv4?, 'defines IPv4 class') + test.ok(ipaddr.IPv6?, 'defines IPv6 class') + test.done() + + 'can construct IPv4 from octets': (test) -> + test.doesNotThrow -> + new ipaddr.IPv4([192, 168, 1, 2]) + test.done() + + 'refuses to construct invalid IPv4': (test) -> + test.throws -> + new ipaddr.IPv4([300, 1, 2, 3]) + test.throws -> + new ipaddr.IPv4([8, 8, 8]) + test.done() + + 'converts IPv4 to string correctly': (test) -> + addr = new ipaddr.IPv4([192, 168, 1, 1]) + test.equal(addr.toString(), '192.168.1.1') + test.done() + + 'returns correct kind for IPv4': (test) -> + addr = new ipaddr.IPv4([1, 2, 3, 4]) + test.equal(addr.kind(), 'ipv4') + test.done() + + 'allows to access IPv4 octets': (test) -> + addr = new ipaddr.IPv4([42, 0, 0, 0]) + test.equal(addr.octets[0], 42) + test.done() + + 'checks IPv4 address format': (test) -> + test.equal(ipaddr.IPv4.isIPv4('192.168.007.0xa'), true) + test.equal(ipaddr.IPv4.isIPv4('1024.0.0.1'), true) + test.equal(ipaddr.IPv4.isIPv4('8.0xa.wtf.6'), false) + test.done() + + 'validates IPv4 addresses': (test) -> + test.equal(ipaddr.IPv4.isValid('192.168.007.0xa'), true) + test.equal(ipaddr.IPv4.isValid('1024.0.0.1'), false) + test.equal(ipaddr.IPv4.isValid('8.0xa.wtf.6'), false) + test.done() + + 'parses IPv4 in several weird formats': (test) -> + test.deepEqual(ipaddr.IPv4.parse('192.168.1.1').octets, [192, 168, 1, 1]) + test.deepEqual(ipaddr.IPv4.parse('0xc0.168.1.1').octets, [192, 168, 1, 1]) + test.deepEqual(ipaddr.IPv4.parse('192.0250.1.1').octets, [192, 168, 1, 1]) + test.deepEqual(ipaddr.IPv4.parse('0xc0a80101').octets, [192, 168, 1, 1]) + test.deepEqual(ipaddr.IPv4.parse('030052000401').octets, [192, 168, 1, 1]) + test.deepEqual(ipaddr.IPv4.parse('3232235777').octets, [192, 168, 1, 1]) + test.done() + + 'barfs at invalid IPv4': (test) -> + test.throws -> + ipaddr.IPv4.parse('10.0.0.wtf') + test.done() + + 'matches IPv4 CIDR correctly': (test) -> + addr = new ipaddr.IPv4([10, 5, 0, 1]) + test.equal(addr.match(ipaddr.IPv4.parse('0.0.0.0'), 0), true) + test.equal(addr.match(ipaddr.IPv4.parse('11.0.0.0'), 8), false) + test.equal(addr.match(ipaddr.IPv4.parse('10.0.0.0'), 8), true) + test.equal(addr.match(ipaddr.IPv4.parse('10.0.0.1'), 8), true) + test.equal(addr.match(ipaddr.IPv4.parse('10.0.0.10'), 8), true) + test.equal(addr.match(ipaddr.IPv4.parse('10.5.5.0'), 16), true) + test.equal(addr.match(ipaddr.IPv4.parse('10.4.5.0'), 16), false) + test.equal(addr.match(ipaddr.IPv4.parse('10.4.5.0'), 15), true) + test.equal(addr.match(ipaddr.IPv4.parse('10.5.0.2'), 32), false) + test.equal(addr.match(addr, 32), true) + test.done() + + 'parses IPv4 CIDR correctly': (test) -> + addr = new ipaddr.IPv4([10, 5, 0, 1]) + test.equal(addr.match(ipaddr.IPv4.parseCIDR('0.0.0.0/0')), true) + test.equal(addr.match(ipaddr.IPv4.parseCIDR('11.0.0.0/8')), false) + test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.0.0.0/8')), true) + test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.0.0.1/8')), true) + test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.0.0.10/8')), true) + test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.5.5.0/16')), true) + test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.4.5.0/16')), false) + test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.4.5.0/15')), true) + test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.5.0.2/32')), false) + test.equal(addr.match(ipaddr.IPv4.parseCIDR('10.5.0.1/32')), true) + test.throws -> + ipaddr.IPv4.parseCIDR('10.5.0.1') + test.done() + + 'detects reserved IPv4 networks': (test) -> + test.equal(ipaddr.IPv4.parse('0.0.0.0').range(), 'unspecified') + test.equal(ipaddr.IPv4.parse('0.1.0.0').range(), 'unspecified') + test.equal(ipaddr.IPv4.parse('10.1.0.1').range(), 'private') + test.equal(ipaddr.IPv4.parse('192.168.2.1').range(), 'private') + test.equal(ipaddr.IPv4.parse('224.100.0.1').range(), 'multicast') + test.equal(ipaddr.IPv4.parse('169.254.15.0').range(), 'linkLocal') + test.equal(ipaddr.IPv4.parse('127.1.1.1').range(), 'loopback') + test.equal(ipaddr.IPv4.parse('255.255.255.255').range(), 'broadcast') + test.equal(ipaddr.IPv4.parse('240.1.2.3').range(), 'reserved') + test.equal(ipaddr.IPv4.parse('8.8.8.8').range(), 'unicast') + test.done() + + 'can construct IPv6 from parts': (test) -> + test.doesNotThrow -> + new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]) + test.done() + + 'refuses to construct invalid IPv6': (test) -> + test.throws -> + new ipaddr.IPv6([0xfffff, 0, 0, 0, 0, 0, 0, 1]) + test.throws -> + new ipaddr.IPv6([0xfffff, 0, 0, 0, 0, 0, 1]) + test.done() + + 'converts IPv6 to string correctly': (test) -> + addr = new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]) + test.equal(addr.toNormalizedString(), '2001:db8:f53a:0:0:0:0:1') + test.equal(addr.toString(), '2001:db8:f53a::1') + test.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0, 1]).toString(), '::1') + test.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]).toString(), '2001:db8::') + test.done() + + 'returns correct kind for IPv6': (test) -> + addr = new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]) + test.equal(addr.kind(), 'ipv6') + test.done() + + 'allows to access IPv6 address parts': (test) -> + addr = new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 42, 0, 1]) + test.equal(addr.parts[5], 42) + test.done() + + 'checks IPv6 address format': (test) -> + test.equal(ipaddr.IPv6.isIPv6('2001:db8:F53A::1'), true) + test.equal(ipaddr.IPv6.isIPv6('200001::1'), true) + test.equal(ipaddr.IPv6.isIPv6('::ffff:192.168.1.1'), true) + test.equal(ipaddr.IPv6.isIPv6('::ffff:300.168.1.1'), true) + test.equal(ipaddr.IPv6.isIPv6('::ffff:300.168.1.1:0'), false) + test.equal(ipaddr.IPv6.isIPv6('fe80::wtf'), false) + test.done() + + 'validates IPv6 addresses': (test) -> + test.equal(ipaddr.IPv6.isValid('2001:db8:F53A::1'), true) + test.equal(ipaddr.IPv6.isValid('200001::1'), false) + test.equal(ipaddr.IPv6.isValid('::ffff:192.168.1.1'), true) + test.equal(ipaddr.IPv6.isValid('::ffff:300.168.1.1'), false) + test.equal(ipaddr.IPv6.isValid('::ffff:300.168.1.1:0'), false) + test.equal(ipaddr.IPv6.isValid('2001:db8::F53A::1'), false) + test.equal(ipaddr.IPv6.isValid('fe80::wtf'), false) + test.done() + + 'parses IPv6 in different formats': (test) -> + test.deepEqual(ipaddr.IPv6.parse('2001:db8:F53A:0:0:0:0:1').parts, [0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]) + test.deepEqual(ipaddr.IPv6.parse('fe80::10').parts, [0xfe80, 0, 0, 0, 0, 0, 0, 0x10]) + test.deepEqual(ipaddr.IPv6.parse('2001:db8:F53A::').parts, [0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 0]) + test.deepEqual(ipaddr.IPv6.parse('::1').parts, [0, 0, 0, 0, 0, 0, 0, 1]) + test.deepEqual(ipaddr.IPv6.parse('::').parts, [0, 0, 0, 0, 0, 0, 0, 0]) + test.done() + + 'barfs at invalid IPv6': (test) -> + test.throws -> + ipaddr.IPv6.parse('fe80::0::1') + test.done() + + 'matches IPv6 CIDR correctly': (test) -> + addr = ipaddr.IPv6.parse('2001:db8:f53a::1') + test.equal(addr.match(ipaddr.IPv6.parse('::'), 0), true) + test.equal(addr.match(ipaddr.IPv6.parse('2001:db8:f53a::1:1'), 64), true) + test.equal(addr.match(ipaddr.IPv6.parse('2001:db8:f53b::1:1'), 48), false) + test.equal(addr.match(ipaddr.IPv6.parse('2001:db8:f531::1:1'), 44), true) + test.equal(addr.match(ipaddr.IPv6.parse('2001:db8:f500::1'), 40), true) + test.equal(addr.match(ipaddr.IPv6.parse('2001:db9:f500::1'), 40), false) + test.equal(addr.match(addr, 128), true) + test.done() + + 'parses IPv6 CIDR correctly': (test) -> + addr = ipaddr.IPv6.parse('2001:db8:f53a::1') + test.equal(addr.match(ipaddr.IPv6.parseCIDR('::/0')), true) + test.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f53a::1:1/64')), true) + test.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f53b::1:1/48')), false) + test.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f531::1:1/44')), true) + test.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f500::1/40')), true) + test.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db9:f500::1/40')), false) + test.equal(addr.match(ipaddr.IPv6.parseCIDR('2001:db8:f53a::1/128')), true) + test.throws -> + ipaddr.IPv6.parseCIDR('2001:db8:f53a::1') + test.done() + + 'converts between IPv4-mapped IPv6 addresses and IPv4 addresses': (test) -> + addr = ipaddr.IPv4.parse('77.88.21.11') + mapped = addr.toIPv4MappedAddress() + test.deepEqual(mapped.parts, [0, 0, 0, 0, 0, 0xffff, 0x4d58, 0x150b]) + test.deepEqual(mapped.toIPv4Address().octets, addr.octets) + test.done() + + 'refuses to convert non-IPv4-mapped IPv6 address to IPv4 address': (test) -> + test.throws -> + ipaddr.IPv6.parse('2001:db8::1').toIPv4Address() + test.done() + + 'detects reserved IPv6 networks': (test) -> + test.equal(ipaddr.IPv6.parse('::').range(), 'unspecified') + test.equal(ipaddr.IPv6.parse('fe80::1234:5678:abcd:0123').range(), 'linkLocal') + test.equal(ipaddr.IPv6.parse('ff00::1234').range(), 'multicast') + test.equal(ipaddr.IPv6.parse('::1').range(), 'loopback') + test.equal(ipaddr.IPv6.parse('fc00::').range(), 'uniqueLocal') + test.equal(ipaddr.IPv6.parse('::ffff:192.168.1.10').range(), 'ipv4Mapped') + test.equal(ipaddr.IPv6.parse('::ffff:0:192.168.1.10').range(), 'rfc6145') + test.equal(ipaddr.IPv6.parse('64:ff9b::1234').range(), 'rfc6052') + test.equal(ipaddr.IPv6.parse('2002:1f63:45e8::1').range(), '6to4') + test.equal(ipaddr.IPv6.parse('2001::4242').range(), 'teredo') + test.equal(ipaddr.IPv6.parse('2001:db8::3210').range(), 'reserved') + test.equal(ipaddr.IPv6.parse('2001:470:8:66::1').range(), 'unicast') + test.done() + + 'is able to determine IP address type': (test) -> + test.equal(ipaddr.parse('8.8.8.8').kind(), 'ipv4') + test.equal(ipaddr.parse('2001:db8:3312::1').kind(), 'ipv6') + test.done() + + 'throws an error if tried to parse an invalid address': (test) -> + test.throws -> + ipaddr.parse('::some.nonsense') + test.done() + + 'correctly processes IPv4-mapped addresses': (test) -> + test.equal(ipaddr.process('8.8.8.8').kind(), 'ipv4') + test.equal(ipaddr.process('2001:db8:3312::1').kind(), 'ipv6') + test.equal(ipaddr.process('::ffff:192.168.1.1').kind(), 'ipv4') + test.done() + + 'correctly converts IPv6 and IPv4 addresses to byte arrays': (test) -> + test.deepEqual(ipaddr.parse('1.2.3.4').toByteArray(), + [0x1, 0x2, 0x3, 0x4]); + # Fuck yeah. The first byte of Google's IPv6 address is 42. 42! + test.deepEqual(ipaddr.parse('2a00:1450:8007::68').toByteArray(), + [42, 0x00, 0x14, 0x50, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68 ]) + test.done() + + 'correctly parses 1 as an IPv4 address': (test) -> + test.equal(ipaddr.IPv6.isValid('1'), false) + test.equal(ipaddr.IPv4.isValid('1'), true) + test.deepEqual(new ipaddr.IPv4([0, 0, 0, 1]), ipaddr.parse('1')) + test.done() + + 'correctly detects IPv4 and IPv6 CIDR addresses': (test) -> + test.deepEqual([ipaddr.IPv6.parse('fc00::'), 64], + ipaddr.parseCIDR('fc00::/64')) + test.deepEqual([ipaddr.IPv4.parse('1.2.3.4'), 5], + ipaddr.parseCIDR('1.2.3.4/5')) + test.done() + + 'does not consider a very large or very small number a valid IP address': (test) -> + test.equal(ipaddr.isValid('4999999999'), false) + test.equal(ipaddr.isValid('-1'), false) + test.done() + + 'does not hang on ::8:8:8:8:8:8:8:8:8': (test) -> + test.equal(ipaddr.IPv6.isValid('::8:8:8:8:8:8:8:8:8'), false) + test.done() diff --git a/node_modules/express/node_modules/proxy-addr/package.json b/node_modules/express/node_modules/proxy-addr/package.json new file mode 100644 index 0000000..0a6f3ba --- /dev/null +++ b/node_modules/express/node_modules/proxy-addr/package.json @@ -0,0 +1,89 @@ +{ + "name": "proxy-addr", + "description": "Determine address of proxied request", + "version": "1.0.8", + "author": { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + "license": "MIT", + "keywords": [ + "ip", + "proxy", + "x-forwarded-for" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/proxy-addr" + }, + "dependencies": { + "forwarded": "~0.1.0", + "ipaddr.js": "1.0.1" + }, + "devDependencies": { + "benchmark": "1.0.0", + "beautify-benchmark": "0.2.4", + "istanbul": "0.3.9", + "mocha": "~1.21.5" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "README.md", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "bench": "node benchmark/index.js", + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "b32d9bda51c92f67a5c2c7b4f81971dbef41783c", + "bugs": { + "url": "https://github.com/jshttp/proxy-addr/issues" + }, + "homepage": "https://github.com/jshttp/proxy-addr", + "_id": "proxy-addr@1.0.8", + "_shasum": "db54ec878bcc1053d57646609219b3715678bafe", + "_from": "proxy-addr@>=1.0.8 <1.1.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + }, + { + "name": "defunctzombie", + "email": "shtylman@gmail.com" + } + ], + "dist": { + "shasum": "db54ec878bcc1053d57646609219b3715678bafe", + "tarball": "http://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.8.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.8.tgz" +} diff --git a/node_modules/express/node_modules/qs/.eslintignore b/node_modules/express/node_modules/qs/.eslintignore new file mode 100644 index 0000000..1521c8b --- /dev/null +++ b/node_modules/express/node_modules/qs/.eslintignore @@ -0,0 +1 @@ +dist diff --git a/node_modules/express/node_modules/qs/.npmignore b/node_modules/express/node_modules/qs/.npmignore new file mode 100644 index 0000000..2abba8d --- /dev/null +++ b/node_modules/express/node_modules/qs/.npmignore @@ -0,0 +1,19 @@ +.idea +*.iml +npm-debug.log +dump.rdb +node_modules +results.tap +results.xml +npm-shrinkwrap.json +config.json +.DS_Store +*/.DS_Store +*/*/.DS_Store +._* +*/._* +*/*/._* +coverage.* +lib-cov +complexity.md +dist diff --git a/node_modules/express/node_modules/qs/.travis.yml b/node_modules/express/node_modules/qs/.travis.yml new file mode 100644 index 0000000..f502178 --- /dev/null +++ b/node_modules/express/node_modules/qs/.travis.yml @@ -0,0 +1,6 @@ +language: node_js + +node_js: + - 0.10 + - 0.12 + - iojs diff --git a/node_modules/express/node_modules/qs/CHANGELOG.md b/node_modules/express/node_modules/qs/CHANGELOG.md new file mode 100644 index 0000000..1fadc78 --- /dev/null +++ b/node_modules/express/node_modules/qs/CHANGELOG.md @@ -0,0 +1,88 @@ + +## [**3.1.0**](https://github.com/hapijs/qs/issues?milestone=24&state=open) +- [**#89**](https://github.com/hapijs/qs/issues/89) Add option to disable "Transform dot notation to bracket notation" + +## [**3.0.0**](https://github.com/hapijs/qs/issues?milestone=23&state=closed) +- [**#77**](https://github.com/hapijs/qs/issues/77) Perf boost +- [**#60**](https://github.com/hapijs/qs/issues/60) Add explicit option to disable array parsing +- [**#80**](https://github.com/hapijs/qs/issues/80) qs.parse silently drops properties +- [**#74**](https://github.com/hapijs/qs/issues/74) Bad parse when turning array into object +- [**#81**](https://github.com/hapijs/qs/issues/81) Add a `filter` option +- [**#68**](https://github.com/hapijs/qs/issues/68) Fixed issue with recursion and passing strings into objects. +- [**#66**](https://github.com/hapijs/qs/issues/66) Add mixed array and object dot notation support Closes: #47 +- [**#76**](https://github.com/hapijs/qs/issues/76) RFC 3986 +- [**#85**](https://github.com/hapijs/qs/issues/85) No equal sign +- [**#84**](https://github.com/hapijs/qs/issues/84) update license attribute + +## [**2.4.1**](https://github.com/hapijs/qs/issues?milestone=20&state=closed) +- [**#73**](https://github.com/hapijs/qs/issues/73) Property 'hasOwnProperty' of object # is not a function + +## [**2.4.0**](https://github.com/hapijs/qs/issues?milestone=19&state=closed) +- [**#70**](https://github.com/hapijs/qs/issues/70) Add arrayFormat option + +## [**2.3.3**](https://github.com/hapijs/qs/issues?milestone=18&state=closed) +- [**#59**](https://github.com/hapijs/qs/issues/59) make sure array indexes are >= 0, closes #57 +- [**#58**](https://github.com/hapijs/qs/issues/58) make qs usable for browser loader + +## [**2.3.2**](https://github.com/hapijs/qs/issues?milestone=17&state=closed) +- [**#55**](https://github.com/hapijs/qs/issues/55) allow merging a string into an object + +## [**2.3.1**](https://github.com/hapijs/qs/issues?milestone=16&state=closed) +- [**#52**](https://github.com/hapijs/qs/issues/52) Return "undefined" and "false" instead of throwing "TypeError". + +## [**2.3.0**](https://github.com/hapijs/qs/issues?milestone=15&state=closed) +- [**#50**](https://github.com/hapijs/qs/issues/50) add option to omit array indices, closes #46 + +## [**2.2.5**](https://github.com/hapijs/qs/issues?milestone=14&state=closed) +- [**#39**](https://github.com/hapijs/qs/issues/39) Is there an alternative to Buffer.isBuffer? +- [**#49**](https://github.com/hapijs/qs/issues/49) refactor utils.merge, fixes #45 +- [**#41**](https://github.com/hapijs/qs/issues/41) avoid browserifying Buffer, for #39 + +## [**2.2.4**](https://github.com/hapijs/qs/issues?milestone=13&state=closed) +- [**#38**](https://github.com/hapijs/qs/issues/38) how to handle object keys beginning with a number + +## [**2.2.3**](https://github.com/hapijs/qs/issues?milestone=12&state=closed) +- [**#37**](https://github.com/hapijs/qs/issues/37) parser discards first empty value in array +- [**#36**](https://github.com/hapijs/qs/issues/36) Update to lab 4.x + +## [**2.2.2**](https://github.com/hapijs/qs/issues?milestone=11&state=closed) +- [**#33**](https://github.com/hapijs/qs/issues/33) Error when plain object in a value +- [**#34**](https://github.com/hapijs/qs/issues/34) use Object.prototype.hasOwnProperty.call instead of obj.hasOwnProperty +- [**#24**](https://github.com/hapijs/qs/issues/24) Changelog? Semver? + +## [**2.2.1**](https://github.com/hapijs/qs/issues?milestone=10&state=closed) +- [**#32**](https://github.com/hapijs/qs/issues/32) account for circular references properly, closes #31 +- [**#31**](https://github.com/hapijs/qs/issues/31) qs.parse stackoverflow on circular objects + +## [**2.2.0**](https://github.com/hapijs/qs/issues?milestone=9&state=closed) +- [**#26**](https://github.com/hapijs/qs/issues/26) Don't use Buffer global if it's not present +- [**#30**](https://github.com/hapijs/qs/issues/30) Bug when merging non-object values into arrays +- [**#29**](https://github.com/hapijs/qs/issues/29) Don't call Utils.clone at the top of Utils.merge +- [**#23**](https://github.com/hapijs/qs/issues/23) Ability to not limit parameters? + +## [**2.1.0**](https://github.com/hapijs/qs/issues?milestone=8&state=closed) +- [**#22**](https://github.com/hapijs/qs/issues/22) Enable using a RegExp as delimiter + +## [**2.0.0**](https://github.com/hapijs/qs/issues?milestone=7&state=closed) +- [**#18**](https://github.com/hapijs/qs/issues/18) Why is there arrayLimit? +- [**#20**](https://github.com/hapijs/qs/issues/20) Configurable parametersLimit +- [**#21**](https://github.com/hapijs/qs/issues/21) make all limits optional, for #18, for #20 + +## [**1.2.2**](https://github.com/hapijs/qs/issues?milestone=6&state=closed) +- [**#19**](https://github.com/hapijs/qs/issues/19) Don't overwrite null values + +## [**1.2.1**](https://github.com/hapijs/qs/issues?milestone=5&state=closed) +- [**#16**](https://github.com/hapijs/qs/issues/16) ignore non-string delimiters +- [**#15**](https://github.com/hapijs/qs/issues/15) Close code block + +## [**1.2.0**](https://github.com/hapijs/qs/issues?milestone=4&state=closed) +- [**#12**](https://github.com/hapijs/qs/issues/12) Add optional delim argument +- [**#13**](https://github.com/hapijs/qs/issues/13) fix #11: flattened keys in array are now correctly parsed + +## [**1.1.0**](https://github.com/hapijs/qs/issues?milestone=3&state=closed) +- [**#7**](https://github.com/hapijs/qs/issues/7) Empty values of a POST array disappear after being submitted +- [**#9**](https://github.com/hapijs/qs/issues/9) Should not omit equals signs (=) when value is null +- [**#6**](https://github.com/hapijs/qs/issues/6) Minor grammar fix in README + +## [**1.0.2**](https://github.com/hapijs/qs/issues?milestone=2&state=closed) +- [**#5**](https://github.com/hapijs/qs/issues/5) array holes incorrectly copied into object on large index diff --git a/node_modules/express/node_modules/qs/CONTRIBUTING.md b/node_modules/express/node_modules/qs/CONTRIBUTING.md new file mode 100644 index 0000000..8928361 --- /dev/null +++ b/node_modules/express/node_modules/qs/CONTRIBUTING.md @@ -0,0 +1 @@ +Please view our [hapijs contributing guide](https://github.com/hapijs/hapi/blob/master/CONTRIBUTING.md). diff --git a/node_modules/express/node_modules/qs/LICENSE b/node_modules/express/node_modules/qs/LICENSE new file mode 100644 index 0000000..d456948 --- /dev/null +++ b/node_modules/express/node_modules/qs/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2014 Nathan LaFreniere and other contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of any contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * * * + +The complete list of contributors can be found at: https://github.com/hapijs/qs/graphs/contributors diff --git a/node_modules/express/node_modules/qs/README.md b/node_modules/express/node_modules/qs/README.md new file mode 100644 index 0000000..48a0de9 --- /dev/null +++ b/node_modules/express/node_modules/qs/README.md @@ -0,0 +1,317 @@ +# qs + +A querystring parsing and stringifying library with some added security. + +[![Build Status](https://secure.travis-ci.org/hapijs/qs.svg)](http://travis-ci.org/hapijs/qs) + +Lead Maintainer: [Nathan LaFreniere](https://github.com/nlf) + +The **qs** module was originally created and maintained by [TJ Holowaychuk](https://github.com/visionmedia/node-querystring). + +## Usage + +```javascript +var Qs = require('qs'); + +var obj = Qs.parse('a=c'); // { a: 'c' } +var str = Qs.stringify(obj); // 'a=c' +``` + +### Parsing Objects + +```javascript +Qs.parse(string, [options]); +``` + +**qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`, or prefixing the sub-key with a dot `.`. +For example, the string `'foo[bar]=baz'` converts to: + +```javascript +{ + foo: { + bar: 'baz' + } +} +``` + +When using the `plainObjects` option the parsed value is returned as a plain object, created via `Object.create(null)` and as such you should be aware that prototype methods will not exist on it and a user may set those names to whatever value they like: + +```javascript +Qs.parse('a.hasOwnProperty=b', { plainObjects: true }); +// { a: { hasOwnProperty: 'b' } } +``` + +By default parameters that would overwrite properties on the object prototype are ignored, if you wish to keep the data from those fields either use `plainObjects` as mentioned above, or set `allowPrototypes` to `true` which will allow user input to overwrite those properties. *WARNING* It is generally a bad idea to enable this option as it can cause problems when attempting to use the properties that have been overwritten. Always be careful with this option. + +```javascript +Qs.parse('a.hasOwnProperty=b', { allowPrototypes: true }); +// { a: { hasOwnProperty: 'b' } } +``` + +URI encoded strings work too: + +```javascript +Qs.parse('a%5Bb%5D=c'); +// { a: { b: 'c' } } +``` + +You can also nest your objects, like `'foo[bar][baz]=foobarbaz'`: + +```javascript +{ + foo: { + bar: { + baz: 'foobarbaz' + } + } +} +``` + +By default, when nesting objects **qs** will only parse up to 5 children deep. This means if you attempt to parse a string like +`'a[b][c][d][e][f][g][h][i]=j'` your resulting object will be: + +```javascript +{ + a: { + b: { + c: { + d: { + e: { + f: { + '[g][h][i]': 'j' + } + } + } + } + } + } +} +``` + +This depth can be overridden by passing a `depth` option to `Qs.parse(string, [options])`: + +```javascript +Qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 }); +// { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } } +``` + +The depth limit helps mitigate abuse when **qs** is used to parse user input, and it is recommended to keep it a reasonably small number. + +For similar reasons, by default **qs** will only parse up to 1000 parameters. This can be overridden by passing a `parameterLimit` option: + +```javascript +Qs.parse('a=b&c=d', { parameterLimit: 1 }); +// { a: 'b' } +``` + +An optional delimiter can also be passed: + +```javascript +Qs.parse('a=b;c=d', { delimiter: ';' }); +// { a: 'b', c: 'd' } +``` + +Delimiters can be a regular expression too: + +```javascript +Qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ }); +// { a: 'b', c: 'd', e: 'f' } +``` + +Option `allowDots` can be used to disable dot notation: + +```javascript +Qs.parse('a.b=c', { allowDots: false }); +// { 'a.b': 'c' } } +``` + +### Parsing Arrays + +**qs** can also parse arrays using a similar `[]` notation: + +```javascript +Qs.parse('a[]=b&a[]=c'); +// { a: ['b', 'c'] } +``` + +You may specify an index as well: + +```javascript +Qs.parse('a[1]=c&a[0]=b'); +// { a: ['b', 'c'] } +``` + +Note that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number +to create an array. When creating arrays with specific indices, **qs** will compact a sparse array to only the existing values preserving +their order: + +```javascript +Qs.parse('a[1]=b&a[15]=c'); +// { a: ['b', 'c'] } +``` + +Note that an empty string is also a value, and will be preserved: + +```javascript +Qs.parse('a[]=&a[]=b'); +// { a: ['', 'b'] } +Qs.parse('a[0]=b&a[1]=&a[2]=c'); +// { a: ['b', '', 'c'] } +``` + +**qs** will also limit specifying indices in an array to a maximum index of `20`. Any array members with an index of greater than `20` will +instead be converted to an object with the index as the key: + +```javascript +Qs.parse('a[100]=b'); +// { a: { '100': 'b' } } +``` + +This limit can be overridden by passing an `arrayLimit` option: + +```javascript +Qs.parse('a[1]=b', { arrayLimit: 0 }); +// { a: { '1': 'b' } } +``` + +To disable array parsing entirely, set `parseArrays` to `false`. + +```javascript +Qs.parse('a[]=b', { parseArrays: false }); +// { a: { '0': 'b' } } +``` + +If you mix notations, **qs** will merge the two items into an object: + +```javascript +Qs.parse('a[0]=b&a[b]=c'); +// { a: { '0': 'b', b: 'c' } } +``` + +You can also create arrays of objects: + +```javascript +Qs.parse('a[][b]=c'); +// { a: [{ b: 'c' }] } +``` + +### Stringifying + +```javascript +Qs.stringify(object, [options]); +``` + +When stringifying, **qs** always URI encodes output. Objects are stringified as you would expect: + +```javascript +Qs.stringify({ a: 'b' }); +// 'a=b' +Qs.stringify({ a: { b: 'c' } }); +// 'a%5Bb%5D=c' +``` + +Examples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases *will* be URI encoded during real usage. + +When arrays are stringified, by default they are given explicit indices: + +```javascript +Qs.stringify({ a: ['b', 'c', 'd'] }); +// 'a[0]=b&a[1]=c&a[2]=d' +``` + +You may override this by setting the `indices` option to `false`: + +```javascript +Qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false }); +// 'a=b&a=c&a=d' +``` + +You may use the `arrayFormat` option to specify the format of the output array + +```javascript +Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }) +// 'a[0]=b&a[1]=c' +Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }) +// 'a[]=b&a[]=c' +Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }) +// 'a=b&a=c' +``` + +Empty strings and null values will omit the value, but the equals sign (=) remains in place: + +```javascript +Qs.stringify({ a: '' }); +// 'a=' +``` + +Properties that are set to `undefined` will be omitted entirely: + +```javascript +Qs.stringify({ a: null, b: undefined }); +// 'a=' +``` + +The delimiter may be overridden with stringify as well: + +```javascript +Qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }); +// 'a=b;c=d' +``` + +Finally, you can use the `filter` option to restrict which keys will be included in the stringified output. +If you pass a function, it will be called for each key to obtain the replacement value. Otherwise, if you +pass an array, it will be used to select properties and array indices for stringification: + +```javascript +function filterFunc(prefix, value) { + if (prefix == 'b') { + // Return an `undefined` value to omit a property. + return; + } + if (prefix == 'e[f]') { + return value.getTime(); + } + if (prefix == 'e[g][0]') { + return value * 2; + } + return value; +} +Qs.stringify({ a: 'b', c: 'd', e: { f: new Date(123), g: [2] } }, { filter: filterFunc }) +// 'a=b&c=d&e[f]=123&e[g][0]=4' +Qs.stringify({ a: 'b', c: 'd', e: 'f' }, { filter: ['a', 'e'] }) +// 'a=b&e=f' +Qs.stringify({ a: ['b', 'c', 'd'], e: 'f' }, { filter: ['a', 0, 2] }) +// 'a[0]=b&a[2]=d' +``` + +### Handling of `null` values + +By default, `null` values are treated like empty strings: + +```javascript +Qs.stringify({ a: null, b: '' }); +// 'a=&b=' +``` + +Parsing does not distinguish between parameters with and without equal signs. Both are converted to empty strings. + +```javascript +Qs.parse('a&b=') +// { a: '', b: '' } +``` + +To distinguish between `null` values and empty strings use the `strictNullHandling` flag. In the result string the `null` +values have no `=` sign: + +```javascript +Qs.stringify({ a: null, b: '' }, { strictNullHandling: true }); +// 'a&b=' +``` + +To parse values without `=` back to `null` use the `strictNullHandling` flag: + +```javascript +Qs.parse('a&b=', { strictNullHandling: true }); +// { a: null, b: '' } + +``` diff --git a/node_modules/express/node_modules/qs/bower.json b/node_modules/express/node_modules/qs/bower.json new file mode 100644 index 0000000..ffd0641 --- /dev/null +++ b/node_modules/express/node_modules/qs/bower.json @@ -0,0 +1,22 @@ +{ + "name": "qs", + "main": "dist/qs.js", + "version": "3.0.0", + "homepage": "https://github.com/hapijs/qs", + "authors": [ + "Nathan LaFreniere " + ], + "description": "A querystring parser that supports nesting and arrays, with a depth limit", + "keywords": [ + "querystring", + "qs" + ], + "license": "BSD-3-Clause", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/node_modules/express/node_modules/qs/lib/index.js b/node_modules/express/node_modules/qs/lib/index.js new file mode 100644 index 0000000..0e09493 --- /dev/null +++ b/node_modules/express/node_modules/qs/lib/index.js @@ -0,0 +1,15 @@ +// Load modules + +var Stringify = require('./stringify'); +var Parse = require('./parse'); + + +// Declare internals + +var internals = {}; + + +module.exports = { + stringify: Stringify, + parse: Parse +}; diff --git a/node_modules/express/node_modules/qs/lib/parse.js b/node_modules/express/node_modules/qs/lib/parse.js new file mode 100644 index 0000000..e7c56c5 --- /dev/null +++ b/node_modules/express/node_modules/qs/lib/parse.js @@ -0,0 +1,186 @@ +// Load modules + +var Utils = require('./utils'); + + +// Declare internals + +var internals = { + delimiter: '&', + depth: 5, + arrayLimit: 20, + parameterLimit: 1000, + strictNullHandling: false, + plainObjects: false, + allowPrototypes: false +}; + + +internals.parseValues = function (str, options) { + + var obj = {}; + var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit); + + for (var i = 0, il = parts.length; i < il; ++i) { + var part = parts[i]; + var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1; + + if (pos === -1) { + obj[Utils.decode(part)] = ''; + + if (options.strictNullHandling) { + obj[Utils.decode(part)] = null; + } + } + else { + var key = Utils.decode(part.slice(0, pos)); + var val = Utils.decode(part.slice(pos + 1)); + + if (!Object.prototype.hasOwnProperty.call(obj, key)) { + obj[key] = val; + } + else { + obj[key] = [].concat(obj[key]).concat(val); + } + } + } + + return obj; +}; + + +internals.parseObject = function (chain, val, options) { + + if (!chain.length) { + return val; + } + + var root = chain.shift(); + + var obj; + if (root === '[]') { + obj = []; + obj = obj.concat(internals.parseObject(chain, val, options)); + } + else { + obj = options.plainObjects ? Object.create(null) : {}; + var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root; + var index = parseInt(cleanRoot, 10); + var indexString = '' + index; + if (!isNaN(index) && + root !== cleanRoot && + indexString === cleanRoot && + index >= 0 && + (options.parseArrays && + index <= options.arrayLimit)) { + + obj = []; + obj[index] = internals.parseObject(chain, val, options); + } + else { + obj[cleanRoot] = internals.parseObject(chain, val, options); + } + } + + return obj; +}; + + +internals.parseKeys = function (key, val, options) { + + if (!key) { + return; + } + + // Transform dot notation to bracket notation + + if (options.allowDots) { + key = key.replace(/\.([^\.\[]+)/g, '[$1]'); + } + + // The regex chunks + + var parent = /^([^\[\]]*)/; + var child = /(\[[^\[\]]*\])/g; + + // Get the parent + + var segment = parent.exec(key); + + // Stash the parent if it exists + + var keys = []; + if (segment[1]) { + // If we aren't using plain objects, optionally prefix keys + // that would overwrite object prototype properties + if (!options.plainObjects && + Object.prototype.hasOwnProperty(segment[1])) { + + if (!options.allowPrototypes) { + return; + } + } + + keys.push(segment[1]); + } + + // Loop through children appending to the array until we hit depth + + var i = 0; + while ((segment = child.exec(key)) !== null && i < options.depth) { + + ++i; + if (!options.plainObjects && + Object.prototype.hasOwnProperty(segment[1].replace(/\[|\]/g, ''))) { + + if (!options.allowPrototypes) { + continue; + } + } + keys.push(segment[1]); + } + + // If there's a remainder, just add whatever is left + + if (segment) { + keys.push('[' + key.slice(segment.index) + ']'); + } + + return internals.parseObject(keys, val, options); +}; + + +module.exports = function (str, options) { + + options = options || {}; + options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : internals.delimiter; + options.depth = typeof options.depth === 'number' ? options.depth : internals.depth; + options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : internals.arrayLimit; + options.parseArrays = options.parseArrays !== false; + options.allowDots = options.allowDots !== false; + options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : internals.plainObjects; + options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : internals.allowPrototypes; + options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : internals.parameterLimit; + options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling; + + if (str === '' || + str === null || + typeof str === 'undefined') { + + return options.plainObjects ? Object.create(null) : {}; + } + + var tempObj = typeof str === 'string' ? internals.parseValues(str, options) : str; + var obj = options.plainObjects ? Object.create(null) : {}; + + // Iterate over the keys and setup the new object + + var keys = Object.keys(tempObj); + for (var i = 0, il = keys.length; i < il; ++i) { + var key = keys[i]; + var newObj = internals.parseKeys(key, tempObj[key], options); + obj = Utils.merge(obj, newObj, options); + } + + return Utils.compact(obj); +}; diff --git a/node_modules/express/node_modules/qs/lib/stringify.js b/node_modules/express/node_modules/qs/lib/stringify.js new file mode 100644 index 0000000..7414284 --- /dev/null +++ b/node_modules/express/node_modules/qs/lib/stringify.js @@ -0,0 +1,121 @@ +// Load modules + +var Utils = require('./utils'); + + +// Declare internals + +var internals = { + delimiter: '&', + arrayPrefixGenerators: { + brackets: function (prefix, key) { + + return prefix + '[]'; + }, + indices: function (prefix, key) { + + return prefix + '[' + key + ']'; + }, + repeat: function (prefix, key) { + + return prefix; + } + }, + strictNullHandling: false +}; + + +internals.stringify = function (obj, prefix, generateArrayPrefix, strictNullHandling, filter) { + + if (typeof filter === 'function') { + obj = filter(prefix, obj); + } + else if (Utils.isBuffer(obj)) { + obj = obj.toString(); + } + else if (obj instanceof Date) { + obj = obj.toISOString(); + } + else if (obj === null) { + if (strictNullHandling) { + return Utils.encode(prefix); + } + + obj = ''; + } + + if (typeof obj === 'string' || + typeof obj === 'number' || + typeof obj === 'boolean') { + + return [Utils.encode(prefix) + '=' + Utils.encode(obj)]; + } + + var values = []; + + if (typeof obj === 'undefined') { + return values; + } + + var objKeys = Array.isArray(filter) ? filter : Object.keys(obj); + for (var i = 0, il = objKeys.length; i < il; ++i) { + var key = objKeys[i]; + + if (Array.isArray(obj)) { + values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, filter)); + } + else { + values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix, strictNullHandling, filter)); + } + } + + return values; +}; + + +module.exports = function (obj, options) { + + options = options || {}; + var delimiter = typeof options.delimiter === 'undefined' ? internals.delimiter : options.delimiter; + var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling; + var objKeys; + var filter; + if (typeof options.filter === 'function') { + filter = options.filter; + obj = filter('', obj); + } + else if (Array.isArray(options.filter)) { + objKeys = filter = options.filter; + } + + var keys = []; + + if (typeof obj !== 'object' || + obj === null) { + + return ''; + } + + var arrayFormat; + if (options.arrayFormat in internals.arrayPrefixGenerators) { + arrayFormat = options.arrayFormat; + } + else if ('indices' in options) { + arrayFormat = options.indices ? 'indices' : 'repeat'; + } + else { + arrayFormat = 'indices'; + } + + var generateArrayPrefix = internals.arrayPrefixGenerators[arrayFormat]; + + if (!objKeys) { + objKeys = Object.keys(obj); + } + for (var i = 0, il = objKeys.length; i < il; ++i) { + var key = objKeys[i]; + keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix, strictNullHandling, filter)); + } + + return keys.join(delimiter); +}; diff --git a/node_modules/express/node_modules/qs/lib/utils.js b/node_modules/express/node_modules/qs/lib/utils.js new file mode 100644 index 0000000..88f3147 --- /dev/null +++ b/node_modules/express/node_modules/qs/lib/utils.js @@ -0,0 +1,190 @@ +// Load modules + + +// Declare internals + +var internals = {}; +internals.hexTable = new Array(256); +for (var h = 0; h < 256; ++h) { + internals.hexTable[h] = '%' + ((h < 16 ? '0' : '') + h.toString(16)).toUpperCase(); +} + + +exports.arrayToObject = function (source, options) { + + var obj = options.plainObjects ? Object.create(null) : {}; + for (var i = 0, il = source.length; i < il; ++i) { + if (typeof source[i] !== 'undefined') { + + obj[i] = source[i]; + } + } + + return obj; +}; + + +exports.merge = function (target, source, options) { + + if (!source) { + return target; + } + + if (typeof source !== 'object') { + if (Array.isArray(target)) { + target.push(source); + } + else if (typeof target === 'object') { + target[source] = true; + } + else { + target = [target, source]; + } + + return target; + } + + if (typeof target !== 'object') { + target = [target].concat(source); + return target; + } + + if (Array.isArray(target) && + !Array.isArray(source)) { + + target = exports.arrayToObject(target, options); + } + + var keys = Object.keys(source); + for (var k = 0, kl = keys.length; k < kl; ++k) { + var key = keys[k]; + var value = source[key]; + + if (!Object.prototype.hasOwnProperty.call(target, key)) { + target[key] = value; + } + else { + target[key] = exports.merge(target[key], value, options); + } + } + + return target; +}; + + +exports.decode = function (str) { + + try { + return decodeURIComponent(str.replace(/\+/g, ' ')); + } catch (e) { + return str; + } +}; + +exports.encode = function (str) { + + // This code was originally written by Brian White (mscdex) for the io.js core querystring library. + // It has been adapted here for stricter adherence to RFC 3986 + if (str.length === 0) { + return str; + } + + if (typeof str !== 'string') { + str = '' + str; + } + + var out = ''; + for (var i = 0, il = str.length; i < il; ++i) { + var c = str.charCodeAt(i); + + if (c === 0x2D || // - + c === 0x2E || // . + c === 0x5F || // _ + c === 0x7E || // ~ + (c >= 0x30 && c <= 0x39) || // 0-9 + (c >= 0x41 && c <= 0x5A) || // a-z + (c >= 0x61 && c <= 0x7A)) { // A-Z + + out += str[i]; + continue; + } + + if (c < 0x80) { + out += internals.hexTable[c]; + continue; + } + + if (c < 0x800) { + out += internals.hexTable[0xC0 | (c >> 6)] + internals.hexTable[0x80 | (c & 0x3F)]; + continue; + } + + if (c < 0xD800 || c >= 0xE000) { + out += internals.hexTable[0xE0 | (c >> 12)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)]; + continue; + } + + ++i; + c = 0x10000 + (((c & 0x3FF) << 10) | (str.charCodeAt(i) & 0x3FF)); + out += internals.hexTable[0xF0 | (c >> 18)] + internals.hexTable[0x80 | ((c >> 12) & 0x3F)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)]; + } + + return out; +}; + +exports.compact = function (obj, refs) { + + if (typeof obj !== 'object' || + obj === null) { + + return obj; + } + + refs = refs || []; + var lookup = refs.indexOf(obj); + if (lookup !== -1) { + return refs[lookup]; + } + + refs.push(obj); + + if (Array.isArray(obj)) { + var compacted = []; + + for (var i = 0, il = obj.length; i < il; ++i) { + if (typeof obj[i] !== 'undefined') { + compacted.push(obj[i]); + } + } + + return compacted; + } + + var keys = Object.keys(obj); + for (i = 0, il = keys.length; i < il; ++i) { + var key = keys[i]; + obj[key] = exports.compact(obj[key], refs); + } + + return obj; +}; + + +exports.isRegExp = function (obj) { + + return Object.prototype.toString.call(obj) === '[object RegExp]'; +}; + + +exports.isBuffer = function (obj) { + + if (obj === null || + typeof obj === 'undefined') { + + return false; + } + + return !!(obj.constructor && + obj.constructor.isBuffer && + obj.constructor.isBuffer(obj)); +}; diff --git a/node_modules/express/node_modules/qs/package.json b/node_modules/express/node_modules/qs/package.json new file mode 100644 index 0000000..7cc5281 --- /dev/null +++ b/node_modules/express/node_modules/qs/package.json @@ -0,0 +1,56 @@ +{ + "name": "qs", + "version": "4.0.0", + "description": "A querystring parser that supports nesting and arrays, with a depth limit", + "homepage": "https://github.com/hapijs/qs", + "main": "lib/index.js", + "dependencies": {}, + "devDependencies": { + "browserify": "^10.2.1", + "code": "1.x.x", + "lab": "5.x.x" + }, + "scripts": { + "test": "lab -a code -t 100 -L", + "test-cov-html": "lab -a code -r html -o coverage.html", + "dist": "browserify --standalone Qs lib/index.js > dist/qs.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/hapijs/qs.git" + }, + "keywords": [ + "querystring", + "qs" + ], + "license": "BSD-3-Clause", + "gitHead": "e573dd08eae6cce30d2202704691a102dfa3782a", + "bugs": { + "url": "https://github.com/hapijs/qs/issues" + }, + "_id": "qs@4.0.0", + "_shasum": "c31d9b74ec27df75e543a86c78728ed8d4623607", + "_from": "qs@4.0.0", + "_npmVersion": "2.12.0", + "_nodeVersion": "0.12.4", + "_npmUser": { + "name": "nlf", + "email": "quitlahok@gmail.com" + }, + "dist": { + "shasum": "c31d9b74ec27df75e543a86c78728ed8d4623607", + "tarball": "http://registry.npmjs.org/qs/-/qs-4.0.0.tgz" + }, + "maintainers": [ + { + "name": "nlf", + "email": "quitlahok@gmail.com" + }, + { + "name": "hueniverse", + "email": "eran@hueniverse.com" + } + ], + "directories": {}, + "_resolved": "https://registry.npmjs.org/qs/-/qs-4.0.0.tgz" +} diff --git a/node_modules/express/node_modules/qs/test/parse.js b/node_modules/express/node_modules/qs/test/parse.js new file mode 100644 index 0000000..a19d764 --- /dev/null +++ b/node_modules/express/node_modules/qs/test/parse.js @@ -0,0 +1,478 @@ +/* eslint no-extend-native:0 */ +// Load modules + +var Code = require('code'); +var Lab = require('lab'); +var Qs = require('../'); + + +// Declare internals + +var internals = {}; + + +// Test shortcuts + +var lab = exports.lab = Lab.script(); +var expect = Code.expect; +var describe = lab.experiment; +var it = lab.test; + + +describe('parse()', function () { + + it('parses a simple string', function (done) { + + expect(Qs.parse('0=foo')).to.deep.equal({ '0': 'foo' }); + expect(Qs.parse('foo=c++')).to.deep.equal({ foo: 'c ' }); + expect(Qs.parse('a[>=]=23')).to.deep.equal({ a: { '>=': '23' } }); + expect(Qs.parse('a[<=>]==23')).to.deep.equal({ a: { '<=>': '=23' } }); + expect(Qs.parse('a[==]=23')).to.deep.equal({ a: { '==': '23' } }); + expect(Qs.parse('foo', { strictNullHandling: true })).to.deep.equal({ foo: null }); + expect(Qs.parse('foo' )).to.deep.equal({ foo: '' }); + expect(Qs.parse('foo=')).to.deep.equal({ foo: '' }); + expect(Qs.parse('foo=bar')).to.deep.equal({ foo: 'bar' }); + expect(Qs.parse(' foo = bar = baz ')).to.deep.equal({ ' foo ': ' bar = baz ' }); + expect(Qs.parse('foo=bar=baz')).to.deep.equal({ foo: 'bar=baz' }); + expect(Qs.parse('foo=bar&bar=baz')).to.deep.equal({ foo: 'bar', bar: 'baz' }); + expect(Qs.parse('foo2=bar2&baz2=')).to.deep.equal({ foo2: 'bar2', baz2: '' }); + expect(Qs.parse('foo=bar&baz', { strictNullHandling: true })).to.deep.equal({ foo: 'bar', baz: null }); + expect(Qs.parse('foo=bar&baz')).to.deep.equal({ foo: 'bar', baz: '' }); + expect(Qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World')).to.deep.equal({ + cht: 'p3', + chd: 't:60,40', + chs: '250x100', + chl: 'Hello|World' + }); + done(); + }); + + it('allows disabling dot notation', function (done) { + + expect(Qs.parse('a.b=c')).to.deep.equal({ a: { b: 'c' } }); + expect(Qs.parse('a.b=c', { allowDots: false })).to.deep.equal({ 'a.b': 'c' }); + done(); + }); + + it('parses a single nested string', function (done) { + + expect(Qs.parse('a[b]=c')).to.deep.equal({ a: { b: 'c' } }); + done(); + }); + + it('parses a double nested string', function (done) { + + expect(Qs.parse('a[b][c]=d')).to.deep.equal({ a: { b: { c: 'd' } } }); + done(); + }); + + it('defaults to a depth of 5', function (done) { + + expect(Qs.parse('a[b][c][d][e][f][g][h]=i')).to.deep.equal({ a: { b: { c: { d: { e: { f: { '[g][h]': 'i' } } } } } } }); + done(); + }); + + it('only parses one level when depth = 1', function (done) { + + expect(Qs.parse('a[b][c]=d', { depth: 1 })).to.deep.equal({ a: { b: { '[c]': 'd' } } }); + expect(Qs.parse('a[b][c][d]=e', { depth: 1 })).to.deep.equal({ a: { b: { '[c][d]': 'e' } } }); + done(); + }); + + it('parses a simple array', function (done) { + + expect(Qs.parse('a=b&a=c')).to.deep.equal({ a: ['b', 'c'] }); + done(); + }); + + it('parses an explicit array', function (done) { + + expect(Qs.parse('a[]=b')).to.deep.equal({ a: ['b'] }); + expect(Qs.parse('a[]=b&a[]=c')).to.deep.equal({ a: ['b', 'c'] }); + expect(Qs.parse('a[]=b&a[]=c&a[]=d')).to.deep.equal({ a: ['b', 'c', 'd'] }); + done(); + }); + + it('parses a mix of simple and explicit arrays', function (done) { + + expect(Qs.parse('a=b&a[]=c')).to.deep.equal({ a: ['b', 'c'] }); + expect(Qs.parse('a[]=b&a=c')).to.deep.equal({ a: ['b', 'c'] }); + expect(Qs.parse('a[0]=b&a=c')).to.deep.equal({ a: ['b', 'c'] }); + expect(Qs.parse('a=b&a[0]=c')).to.deep.equal({ a: ['b', 'c'] }); + expect(Qs.parse('a[1]=b&a=c')).to.deep.equal({ a: ['b', 'c'] }); + expect(Qs.parse('a=b&a[1]=c')).to.deep.equal({ a: ['b', 'c'] }); + done(); + }); + + it('parses a nested array', function (done) { + + expect(Qs.parse('a[b][]=c&a[b][]=d')).to.deep.equal({ a: { b: ['c', 'd'] } }); + expect(Qs.parse('a[>=]=25')).to.deep.equal({ a: { '>=': '25' } }); + done(); + }); + + it('allows to specify array indices', function (done) { + + expect(Qs.parse('a[1]=c&a[0]=b&a[2]=d')).to.deep.equal({ a: ['b', 'c', 'd'] }); + expect(Qs.parse('a[1]=c&a[0]=b')).to.deep.equal({ a: ['b', 'c'] }); + expect(Qs.parse('a[1]=c')).to.deep.equal({ a: ['c'] }); + done(); + }); + + it('limits specific array indices to 20', function (done) { + + expect(Qs.parse('a[20]=a')).to.deep.equal({ a: ['a'] }); + expect(Qs.parse('a[21]=a')).to.deep.equal({ a: { '21': 'a' } }); + done(); + }); + + it('supports keys that begin with a number', function (done) { + + expect(Qs.parse('a[12b]=c')).to.deep.equal({ a: { '12b': 'c' } }); + done(); + }); + + it('supports encoded = signs', function (done) { + + expect(Qs.parse('he%3Dllo=th%3Dere')).to.deep.equal({ 'he=llo': 'th=ere' }); + done(); + }); + + it('is ok with url encoded strings', function (done) { + + expect(Qs.parse('a[b%20c]=d')).to.deep.equal({ a: { 'b c': 'd' } }); + expect(Qs.parse('a[b]=c%20d')).to.deep.equal({ a: { b: 'c d' } }); + done(); + }); + + it('allows brackets in the value', function (done) { + + expect(Qs.parse('pets=["tobi"]')).to.deep.equal({ pets: '["tobi"]' }); + expect(Qs.parse('operators=[">=", "<="]')).to.deep.equal({ operators: '[">=", "<="]' }); + done(); + }); + + it('allows empty values', function (done) { + + expect(Qs.parse('')).to.deep.equal({}); + expect(Qs.parse(null)).to.deep.equal({}); + expect(Qs.parse(undefined)).to.deep.equal({}); + done(); + }); + + it('transforms arrays to objects', function (done) { + + expect(Qs.parse('foo[0]=bar&foo[bad]=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } }); + expect(Qs.parse('foo[bad]=baz&foo[0]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } }); + expect(Qs.parse('foo[bad]=baz&foo[]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } }); + expect(Qs.parse('foo[]=bar&foo[bad]=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } }); + expect(Qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } }); + expect(Qs.parse('foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb')).to.deep.equal({ foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] }); + expect(Qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c')).to.deep.equal({ a: { '0': 'b', t: 'u', c: true } }); + expect(Qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y')).to.deep.equal({ a: { '0': 'b', '1': 'c', x: 'y' } }); + done(); + }); + + it('transforms arrays to objects (dot notation)', function (done) { + + expect(Qs.parse('foo[0].baz=bar&fool.bad=baz')).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: 'baz' } }); + expect(Qs.parse('foo[0].baz=bar&fool.bad.boo=baz')).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: { boo: 'baz' } } }); + expect(Qs.parse('foo[0][0].baz=bar&fool.bad=baz')).to.deep.equal({ foo: [[{ baz: 'bar' }]], fool: { bad: 'baz' } }); + expect(Qs.parse('foo[0].baz[0]=15&foo[0].bar=2')).to.deep.equal({ foo: [{ baz: ['15'], bar: '2' }] }); + expect(Qs.parse('foo[0].baz[0]=15&foo[0].baz[1]=16&foo[0].bar=2')).to.deep.equal({ foo: [{ baz: ['15', '16'], bar: '2' }] }); + expect(Qs.parse('foo.bad=baz&foo[0]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } }); + expect(Qs.parse('foo.bad=baz&foo[]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } }); + expect(Qs.parse('foo[]=bar&foo.bad=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } }); + expect(Qs.parse('foo.bad=baz&foo[]=bar&foo[]=foo')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } }); + expect(Qs.parse('foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb')).to.deep.equal({ foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] }); + done(); + }); + + it('can add keys to objects', function (done) { + + expect(Qs.parse('a[b]=c&a=d')).to.deep.equal({ a: { b: 'c', d: true } }); + done(); + }); + + it('correctly prunes undefined values when converting an array to an object', function (done) { + + expect(Qs.parse('a[2]=b&a[99999999]=c')).to.deep.equal({ a: { '2': 'b', '99999999': 'c' } }); + done(); + }); + + it('supports malformed uri characters', function (done) { + + expect(Qs.parse('{%:%}', { strictNullHandling: true })).to.deep.equal({ '{%:%}': null }); + expect(Qs.parse('{%:%}=')).to.deep.equal({ '{%:%}': '' }); + expect(Qs.parse('foo=%:%}')).to.deep.equal({ foo: '%:%}' }); + done(); + }); + + it('doesn\'t produce empty keys', function (done) { + + expect(Qs.parse('_r=1&')).to.deep.equal({ '_r': '1' }); + done(); + }); + + it('cannot access Object prototype', function (done) { + + Qs.parse('constructor[prototype][bad]=bad'); + Qs.parse('bad[constructor][prototype][bad]=bad'); + expect(typeof Object.prototype.bad).to.equal('undefined'); + done(); + }); + + it('parses arrays of objects', function (done) { + + expect(Qs.parse('a[][b]=c')).to.deep.equal({ a: [{ b: 'c' }] }); + expect(Qs.parse('a[0][b]=c')).to.deep.equal({ a: [{ b: 'c' }] }); + done(); + }); + + it('allows for empty strings in arrays', function (done) { + + expect(Qs.parse('a[]=b&a[]=&a[]=c')).to.deep.equal({ a: ['b', '', 'c'] }); + expect(Qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true })).to.deep.equal({ a: ['b', null, 'c', ''] }); + expect(Qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true })).to.deep.equal({ a: ['b', '', 'c', null] }); + expect(Qs.parse('a[]=&a[]=b&a[]=c')).to.deep.equal({ a: ['', 'b', 'c'] }); + done(); + }); + + it('compacts sparse arrays', function (done) { + + expect(Qs.parse('a[10]=1&a[2]=2')).to.deep.equal({ a: ['2', '1'] }); + done(); + }); + + it('parses semi-parsed strings', function (done) { + + expect(Qs.parse({ 'a[b]': 'c' })).to.deep.equal({ a: { b: 'c' } }); + expect(Qs.parse({ 'a[b]': 'c', 'a[d]': 'e' })).to.deep.equal({ a: { b: 'c', d: 'e' } }); + done(); + }); + + it('parses buffers correctly', function (done) { + + var b = new Buffer('test'); + expect(Qs.parse({ a: b })).to.deep.equal({ a: b }); + done(); + }); + + it('continues parsing when no parent is found', function (done) { + + expect(Qs.parse('[]=&a=b')).to.deep.equal({ '0': '', a: 'b' }); + expect(Qs.parse('[]&a=b', { strictNullHandling: true })).to.deep.equal({ '0': null, a: 'b' }); + expect(Qs.parse('[foo]=bar')).to.deep.equal({ foo: 'bar' }); + done(); + }); + + it('does not error when parsing a very long array', function (done) { + + var str = 'a[]=a'; + while (Buffer.byteLength(str) < 128 * 1024) { + str += '&' + str; + } + + expect(function () { + + Qs.parse(str); + }).to.not.throw(); + + done(); + }); + + it('should not throw when a native prototype has an enumerable property', { parallel: false }, function (done) { + + Object.prototype.crash = ''; + Array.prototype.crash = ''; + expect(Qs.parse.bind(null, 'a=b')).to.not.throw(); + expect(Qs.parse('a=b')).to.deep.equal({ a: 'b' }); + expect(Qs.parse.bind(null, 'a[][b]=c')).to.not.throw(); + expect(Qs.parse('a[][b]=c')).to.deep.equal({ a: [{ b: 'c' }] }); + delete Object.prototype.crash; + delete Array.prototype.crash; + done(); + }); + + it('parses a string with an alternative string delimiter', function (done) { + + expect(Qs.parse('a=b;c=d', { delimiter: ';' })).to.deep.equal({ a: 'b', c: 'd' }); + done(); + }); + + it('parses a string with an alternative RegExp delimiter', function (done) { + + expect(Qs.parse('a=b; c=d', { delimiter: /[;,] */ })).to.deep.equal({ a: 'b', c: 'd' }); + done(); + }); + + it('does not use non-splittable objects as delimiters', function (done) { + + expect(Qs.parse('a=b&c=d', { delimiter: true })).to.deep.equal({ a: 'b', c: 'd' }); + done(); + }); + + it('allows overriding parameter limit', function (done) { + + expect(Qs.parse('a=b&c=d', { parameterLimit: 1 })).to.deep.equal({ a: 'b' }); + done(); + }); + + it('allows setting the parameter limit to Infinity', function (done) { + + expect(Qs.parse('a=b&c=d', { parameterLimit: Infinity })).to.deep.equal({ a: 'b', c: 'd' }); + done(); + }); + + it('allows overriding array limit', function (done) { + + expect(Qs.parse('a[0]=b', { arrayLimit: -1 })).to.deep.equal({ a: { '0': 'b' } }); + expect(Qs.parse('a[-1]=b', { arrayLimit: -1 })).to.deep.equal({ a: { '-1': 'b' } }); + expect(Qs.parse('a[0]=b&a[1]=c', { arrayLimit: 0 })).to.deep.equal({ a: { '0': 'b', '1': 'c' } }); + done(); + }); + + it('allows disabling array parsing', function (done) { + + expect(Qs.parse('a[0]=b&a[1]=c', { parseArrays: false })).to.deep.equal({ a: { '0': 'b', '1': 'c' } }); + done(); + }); + + it('parses an object', function (done) { + + var input = { + 'user[name]': { 'pop[bob]': 3 }, + 'user[email]': null + }; + + var expected = { + 'user': { + 'name': { 'pop[bob]': 3 }, + 'email': null + } + }; + + var result = Qs.parse(input); + + expect(result).to.deep.equal(expected); + done(); + }); + + it('parses an object in dot notation', function (done) { + + var input = { + 'user.name': { 'pop[bob]': 3 }, + 'user.email.': null + }; + + var expected = { + 'user': { + 'name': { 'pop[bob]': 3 }, + 'email': null + } + }; + + var result = Qs.parse(input); + + expect(result).to.deep.equal(expected); + done(); + }); + + it('parses an object and not child values', function (done) { + + var input = { + 'user[name]': { 'pop[bob]': { 'test': 3 } }, + 'user[email]': null + }; + + var expected = { + 'user': { + 'name': { 'pop[bob]': { 'test': 3 } }, + 'email': null + } + }; + + var result = Qs.parse(input); + + expect(result).to.deep.equal(expected); + done(); + }); + + it('does not blow up when Buffer global is missing', function (done) { + + var tempBuffer = global.Buffer; + delete global.Buffer; + var result = Qs.parse('a=b&c=d'); + global.Buffer = tempBuffer; + expect(result).to.deep.equal({ a: 'b', c: 'd' }); + done(); + }); + + it('does not crash when parsing circular references', function (done) { + + var a = {}; + a.b = a; + + var parsed; + + expect(function () { + + parsed = Qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a }); + }).to.not.throw(); + + expect(parsed).to.contain('foo'); + expect(parsed.foo).to.contain('bar', 'baz'); + expect(parsed.foo.bar).to.equal('baz'); + expect(parsed.foo.baz).to.deep.equal(a); + done(); + }); + + it('parses plain objects correctly', function (done) { + + var a = Object.create(null); + a.b = 'c'; + + expect(Qs.parse(a)).to.deep.equal({ b: 'c' }); + var result = Qs.parse({ a: a }); + expect(result).to.contain('a'); + expect(result.a).to.deep.equal(a); + done(); + }); + + it('parses dates correctly', function (done) { + + var now = new Date(); + expect(Qs.parse({ a: now })).to.deep.equal({ a: now }); + done(); + }); + + it('parses regular expressions correctly', function (done) { + + var re = /^test$/; + expect(Qs.parse({ a: re })).to.deep.equal({ a: re }); + done(); + }); + + it('can allow overwriting prototype properties', function (done) { + + expect(Qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true })).to.deep.equal({ a: { hasOwnProperty: 'b' } }, { prototype: false }); + expect(Qs.parse('hasOwnProperty=b', { allowPrototypes: true })).to.deep.equal({ hasOwnProperty: 'b' }, { prototype: false }); + done(); + }); + + it('can return plain objects', function (done) { + + var expected = Object.create(null); + expected.a = Object.create(null); + expected.a.b = 'c'; + expected.a.hasOwnProperty = 'd'; + expect(Qs.parse('a[b]=c&a[hasOwnProperty]=d', { plainObjects: true })).to.deep.equal(expected); + expect(Qs.parse(null, { plainObjects: true })).to.deep.equal(Object.create(null)); + var expectedArray = Object.create(null); + expectedArray.a = Object.create(null); + expectedArray.a['0'] = 'b'; + expectedArray.a.c = 'd'; + expect(Qs.parse('a[]=b&a[c]=d', { plainObjects: true })).to.deep.equal(expectedArray); + done(); + }); +}); diff --git a/node_modules/express/node_modules/qs/test/stringify.js b/node_modules/express/node_modules/qs/test/stringify.js new file mode 100644 index 0000000..48b7803 --- /dev/null +++ b/node_modules/express/node_modules/qs/test/stringify.js @@ -0,0 +1,259 @@ +/* eslint no-extend-native:0 */ +// Load modules + +var Code = require('code'); +var Lab = require('lab'); +var Qs = require('../'); + + +// Declare internals + +var internals = {}; + + +// Test shortcuts + +var lab = exports.lab = Lab.script(); +var expect = Code.expect; +var describe = lab.experiment; +var it = lab.test; + + +describe('stringify()', function () { + + it('stringifies a querystring object', function (done) { + + expect(Qs.stringify({ a: 'b' })).to.equal('a=b'); + expect(Qs.stringify({ a: 1 })).to.equal('a=1'); + expect(Qs.stringify({ a: 1, b: 2 })).to.equal('a=1&b=2'); + expect(Qs.stringify({ a: 'A_Z' })).to.equal('a=A_Z'); + expect(Qs.stringify({ a: '€' })).to.equal('a=%E2%82%AC'); + expect(Qs.stringify({ a: '' })).to.equal('a=%EE%80%80'); + expect(Qs.stringify({ a: 'א' })).to.equal('a=%D7%90'); + expect(Qs.stringify({ a: '𐐷' })).to.equal('a=%F0%90%90%B7'); + done(); + }); + + it('stringifies a nested object', function (done) { + + expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c'); + expect(Qs.stringify({ a: { b: { c: { d: 'e' } } } })).to.equal('a%5Bb%5D%5Bc%5D%5Bd%5D=e'); + done(); + }); + + it('stringifies an array value', function (done) { + + expect(Qs.stringify({ a: ['b', 'c', 'd'] })).to.equal('a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d'); + done(); + }); + + it('omits array indices when asked', function (done) { + + expect(Qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false })).to.equal('a=b&a=c&a=d'); + done(); + }); + + it('stringifies a nested array value', function (done) { + + expect(Qs.stringify({ a: { b: ['c', 'd'] } })).to.equal('a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d'); + done(); + }); + + it('stringifies an object inside an array', function (done) { + + expect(Qs.stringify({ a: [{ b: 'c' }] })).to.equal('a%5B0%5D%5Bb%5D=c'); + expect(Qs.stringify({ a: [{ b: { c: [1] } }] })).to.equal('a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1'); + done(); + }); + + it('does not omit object keys when indices = false', function (done) { + + expect(Qs.stringify({ a: [{ b: 'c' }] }, { indices: false })).to.equal('a%5Bb%5D=c'); + done(); + }); + + it('uses indices notation for arrays when indices=true', function (done) { + + expect(Qs.stringify({ a: ['b', 'c'] }, { indices: true })).to.equal('a%5B0%5D=b&a%5B1%5D=c'); + done(); + }); + + it('uses indices notation for arrays when no arrayFormat is specified', function (done) { + + expect(Qs.stringify({ a: ['b', 'c'] })).to.equal('a%5B0%5D=b&a%5B1%5D=c'); + done(); + }); + + it('uses indices notation for arrays when no arrayFormat=indices', function (done) { + + expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })).to.equal('a%5B0%5D=b&a%5B1%5D=c'); + done(); + }); + + it('uses repeat notation for arrays when no arrayFormat=repeat', function (done) { + + expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })).to.equal('a=b&a=c'); + done(); + }); + + it('uses brackets notation for arrays when no arrayFormat=brackets', function (done) { + + expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })).to.equal('a%5B%5D=b&a%5B%5D=c'); + done(); + }); + + it('stringifies a complicated object', function (done) { + + expect(Qs.stringify({ a: { b: 'c', d: 'e' } })).to.equal('a%5Bb%5D=c&a%5Bd%5D=e'); + done(); + }); + + it('stringifies an empty value', function (done) { + + expect(Qs.stringify({ a: '' })).to.equal('a='); + expect(Qs.stringify({ a: null }, { strictNullHandling: true })).to.equal('a'); + + expect(Qs.stringify({ a: '', b: '' })).to.equal('a=&b='); + expect(Qs.stringify({ a: null, b: '' }, { strictNullHandling: true })).to.equal('a&b='); + + expect(Qs.stringify({ a: { b: '' } })).to.equal('a%5Bb%5D='); + expect(Qs.stringify({ a: { b: null } }, { strictNullHandling: true })).to.equal('a%5Bb%5D'); + expect(Qs.stringify({ a: { b: null } }, { strictNullHandling: false })).to.equal('a%5Bb%5D='); + + done(); + }); + + it('stringifies an empty object', function (done) { + + var obj = Object.create(null); + obj.a = 'b'; + expect(Qs.stringify(obj)).to.equal('a=b'); + done(); + }); + + it('returns an empty string for invalid input', function (done) { + + expect(Qs.stringify(undefined)).to.equal(''); + expect(Qs.stringify(false)).to.equal(''); + expect(Qs.stringify(null)).to.equal(''); + expect(Qs.stringify('')).to.equal(''); + done(); + }); + + it('stringifies an object with an empty object as a child', function (done) { + + var obj = { + a: Object.create(null) + }; + + obj.a.b = 'c'; + expect(Qs.stringify(obj)).to.equal('a%5Bb%5D=c'); + done(); + }); + + it('drops keys with a value of undefined', function (done) { + + expect(Qs.stringify({ a: undefined })).to.equal(''); + + expect(Qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: true })).to.equal('a%5Bc%5D'); + expect(Qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: false })).to.equal('a%5Bc%5D='); + expect(Qs.stringify({ a: { b: undefined, c: '' } })).to.equal('a%5Bc%5D='); + done(); + }); + + it('url encodes values', function (done) { + + expect(Qs.stringify({ a: 'b c' })).to.equal('a=b%20c'); + done(); + }); + + it('stringifies a date', function (done) { + + var now = new Date(); + var str = 'a=' + encodeURIComponent(now.toISOString()); + expect(Qs.stringify({ a: now })).to.equal(str); + done(); + }); + + it('stringifies the weird object from qs', function (done) { + + expect(Qs.stringify({ 'my weird field': '~q1!2"\'w$5&7/z8)?' })).to.equal('my%20weird%20field=~q1%212%22%27w%245%267%2Fz8%29%3F'); + done(); + }); + + it('skips properties that are part of the object prototype', function (done) { + + Object.prototype.crash = 'test'; + expect(Qs.stringify({ a: 'b' })).to.equal('a=b'); + expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c'); + delete Object.prototype.crash; + done(); + }); + + it('stringifies boolean values', function (done) { + + expect(Qs.stringify({ a: true })).to.equal('a=true'); + expect(Qs.stringify({ a: { b: true } })).to.equal('a%5Bb%5D=true'); + expect(Qs.stringify({ b: false })).to.equal('b=false'); + expect(Qs.stringify({ b: { c: false } })).to.equal('b%5Bc%5D=false'); + done(); + }); + + it('stringifies buffer values', function (done) { + + expect(Qs.stringify({ a: new Buffer('test') })).to.equal('a=test'); + expect(Qs.stringify({ a: { b: new Buffer('test') } })).to.equal('a%5Bb%5D=test'); + done(); + }); + + it('stringifies an object using an alternative delimiter', function (done) { + + expect(Qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' })).to.equal('a=b;c=d'); + done(); + }); + + it('doesn\'t blow up when Buffer global is missing', function (done) { + + var tempBuffer = global.Buffer; + delete global.Buffer; + expect(Qs.stringify({ a: 'b', c: 'd' })).to.equal('a=b&c=d'); + global.Buffer = tempBuffer; + done(); + }); + + it('selects properties when filter=array', function (done) { + + expect(Qs.stringify({ a: 'b' }, { filter: ['a'] })).to.equal('a=b'); + expect(Qs.stringify({ a: 1 }, { filter: [] })).to.equal(''); + expect(Qs.stringify({ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, { filter: ['a', 'b', 0, 2] })).to.equal('a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3'); + done(); + + }); + + it('supports custom representations when filter=function', function (done) { + + var calls = 0; + var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } }; + var filterFunc = function (prefix, value) { + + calls++; + if (calls === 1) { + expect(prefix).to.be.empty(); + expect(value).to.equal(obj); + } + else if (prefix === 'c') { + return; + } + else if (value instanceof Date) { + expect(prefix).to.equal('e[f]'); + return value.getTime(); + } + return value; + }; + + expect(Qs.stringify(obj, { filter: filterFunc })).to.equal('a=b&e%5Bf%5D=1257894000000'); + expect(calls).to.equal(5); + done(); + + }); +}); diff --git a/node_modules/express/node_modules/qs/test/utils.js b/node_modules/express/node_modules/qs/test/utils.js new file mode 100644 index 0000000..a9a6b52 --- /dev/null +++ b/node_modules/express/node_modules/qs/test/utils.js @@ -0,0 +1,28 @@ +// Load modules + +var Code = require('code'); +var Lab = require('lab'); +var Utils = require('../lib/utils'); + + +// Declare internals + +var internals = {}; + + +// Test shortcuts + +var lab = exports.lab = Lab.script(); +var expect = Code.expect; +var describe = lab.experiment; +var it = lab.test; + + +describe('merge()', function () { + + it('can merge two objects with the same key', function (done) { + + expect(Utils.merge({ a: 'b' }, { a: 'c' })).to.deep.equal({ a: ['b', 'c'] }); + done(); + }); +}); diff --git a/node_modules/express/node_modules/range-parser/HISTORY.md b/node_modules/express/node_modules/range-parser/HISTORY.md new file mode 100644 index 0000000..f640bea --- /dev/null +++ b/node_modules/express/node_modules/range-parser/HISTORY.md @@ -0,0 +1,40 @@ +unreleased +========== + + * perf: enable strict mode + +1.0.2 / 2014-09-08 +================== + + * Support Node.js 0.6 + +1.0.1 / 2014-09-07 +================== + + * Move repository to jshttp + +1.0.0 / 2013-12-11 +================== + + * Add repository to package.json + * Add MIT license + +0.0.4 / 2012-06-17 +================== + + * Change ret -1 for unsatisfiable and -2 when invalid + +0.0.3 / 2012-06-17 +================== + + * Fix last-byte-pos default to len - 1 + +0.0.2 / 2012-06-14 +================== + + * Add `.type` + +0.0.1 / 2012-06-11 +================== + + * Initial release diff --git a/node_modules/express/node_modules/range-parser/LICENSE b/node_modules/express/node_modules/range-parser/LICENSE new file mode 100644 index 0000000..a491841 --- /dev/null +++ b/node_modules/express/node_modules/range-parser/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2012-2014 TJ Holowaychuk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/range-parser/README.md b/node_modules/express/node_modules/range-parser/README.md new file mode 100644 index 0000000..32f58f6 --- /dev/null +++ b/node_modules/express/node_modules/range-parser/README.md @@ -0,0 +1,57 @@ +# range-parser + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Range header field parser. + +## Installation + +``` +$ npm install range-parser +``` + +## API + +```js +var parseRange = require('range-parser') +``` + +### parseRange(size, header) + +Parse the given `header` string where `size` is the maximum size of the resource. +An array of ranges will be returned or negative numbers indicating an error parsing. + + * `-2` signals a malformed header string + * `-1` signals an invalid range + +```js +// parse header from request +var range = parseRange(req.headers.range) + +// the type of the range +if (range.type === 'bytes') { + // the ranges + range.forEach(function (r) { + // do something with r.start and r.end + }) +} +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/range-parser.svg +[npm-url]: https://npmjs.org/package/range-parser +[node-version-image]: https://img.shields.io/node/v/range-parser.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/range-parser.svg +[travis-url]: https://travis-ci.org/jshttp/range-parser +[coveralls-image]: https://img.shields.io/coveralls/jshttp/range-parser.svg +[coveralls-url]: https://coveralls.io/r/jshttp/range-parser +[downloads-image]: https://img.shields.io/npm/dm/range-parser.svg +[downloads-url]: https://npmjs.org/package/range-parser diff --git a/node_modules/express/node_modules/range-parser/index.js b/node_modules/express/node_modules/range-parser/index.js new file mode 100644 index 0000000..814e533 --- /dev/null +++ b/node_modules/express/node_modules/range-parser/index.js @@ -0,0 +1,63 @@ +/*! + * range-parser + * Copyright(c) 2012-2014 TJ Holowaychuk + * MIT Licensed + */ + +'use strict'; + +/** + * Module exports. + * @public + */ + +module.exports = rangeParser; + +/** + * Parse "Range" header `str` relative to the given file `size`. + * + * @param {Number} size + * @param {String} str + * @return {Array} + * @public + */ + +function rangeParser(size, str) { + var valid = true; + var i = str.indexOf('='); + + if (-1 == i) return -2; + + var arr = str.slice(i + 1).split(',').map(function(range){ + var range = range.split('-') + , start = parseInt(range[0], 10) + , end = parseInt(range[1], 10); + + // -nnn + if (isNaN(start)) { + start = size - end; + end = size - 1; + // nnn- + } else if (isNaN(end)) { + end = size - 1; + } + + // limit last-byte-pos to current length + if (end > size - 1) end = size - 1; + + // invalid + if (isNaN(start) + || isNaN(end) + || start > end + || start < 0) valid = false; + + return { + start: start, + end: end + }; + }); + + arr.type = str.slice(0, i); + + return valid ? arr : -1; +} diff --git a/node_modules/express/node_modules/range-parser/package.json b/node_modules/express/node_modules/range-parser/package.json new file mode 100644 index 0000000..ce81388 --- /dev/null +++ b/node_modules/express/node_modules/range-parser/package.json @@ -0,0 +1,74 @@ +{ + "name": "range-parser", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca", + "url": "http://tjholowaychuk.com" + }, + "description": "Range header field string parser", + "version": "1.0.3", + "license": "MIT", + "keywords": [ + "range", + "parser", + "http" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/range-parser" + }, + "devDependencies": { + "istanbul": "0.4.0", + "mocha": "1.21.5" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter dot" + }, + "gitHead": "18e46a3de74afff9f4e22717f11ddd6e9aa6d845", + "bugs": { + "url": "https://github.com/jshttp/range-parser/issues" + }, + "homepage": "https://github.com/jshttp/range-parser", + "_id": "range-parser@1.0.3", + "_shasum": "6872823535c692e2c2a0103826afd82c2e0ff175", + "_from": "range-parser@>=1.0.2 <1.1.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "jonathanong", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + } + ], + "dist": { + "shasum": "6872823535c692e2c2a0103826afd82c2e0ff175", + "tarball": "http://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz" +} diff --git a/node_modules/express/node_modules/send/HISTORY.md b/node_modules/express/node_modules/send/HISTORY.md new file mode 100644 index 0000000..1fa40b5 --- /dev/null +++ b/node_modules/express/node_modules/send/HISTORY.md @@ -0,0 +1,295 @@ +0.13.0 / 2015-06-16 +=================== + + * Allow Node.js HTTP server to set `Date` response header + * Fix incorrectly removing `Content-Location` on 304 response + * Improve the default redirect response headers + * Send appropriate headers on default error response + * Use `http-errors` for standard emitted errors + * Use `statuses` instead of `http` module for status messages + * deps: escape-html@1.0.2 + * deps: etag@~1.7.0 + - Improve stat performance by removing hashing + * deps: fresh@0.3.0 + - Add weak `ETag` matching support + * deps: on-finished@~2.3.0 + - Add defined behavior for HTTP `CONNECT` requests + - Add defined behavior for HTTP `Upgrade` requests + - deps: ee-first@1.1.1 + * perf: enable strict mode + * perf: remove unnecessary array allocations + +0.12.3 / 2015-05-13 +=================== + + * deps: debug@~2.2.0 + - deps: ms@0.7.1 + * deps: depd@~1.0.1 + * deps: etag@~1.6.0 + - Improve support for JXcore + - Support "fake" stats objects in environments without `fs` + * deps: ms@0.7.1 + - Prevent extraordinarily long inputs + * deps: on-finished@~2.2.1 + +0.12.2 / 2015-03-13 +=================== + + * Throw errors early for invalid `extensions` or `index` options + * deps: debug@~2.1.3 + - Fix high intensity foreground color for bold + - deps: ms@0.7.0 + +0.12.1 / 2015-02-17 +=================== + + * Fix regression sending zero-length files + +0.12.0 / 2015-02-16 +=================== + + * Always read the stat size from the file + * Fix mutating passed-in `options` + * deps: mime@1.3.4 + +0.11.1 / 2015-01-20 +=================== + + * Fix `root` path disclosure + +0.11.0 / 2015-01-05 +=================== + + * deps: debug@~2.1.1 + * deps: etag@~1.5.1 + - deps: crc@3.2.1 + * deps: ms@0.7.0 + - Add `milliseconds` + - Add `msecs` + - Add `secs` + - Add `mins` + - Add `hrs` + - Add `yrs` + * deps: on-finished@~2.2.0 + +0.10.1 / 2014-10-22 +=================== + + * deps: on-finished@~2.1.1 + - Fix handling of pipelined requests + +0.10.0 / 2014-10-15 +=================== + + * deps: debug@~2.1.0 + - Implement `DEBUG_FD` env variable support + * deps: depd@~1.0.0 + * deps: etag@~1.5.0 + - Improve string performance + - Slightly improve speed for weak ETags over 1KB + +0.9.3 / 2014-09-24 +================== + + * deps: etag@~1.4.0 + - Support "fake" stats objects + +0.9.2 / 2014-09-15 +================== + + * deps: depd@0.4.5 + * deps: etag@~1.3.1 + * deps: range-parser@~1.0.2 + +0.9.1 / 2014-09-07 +================== + + * deps: fresh@0.2.4 + +0.9.0 / 2014-09-07 +================== + + * Add `lastModified` option + * Use `etag` to generate `ETag` header + * deps: debug@~2.0.0 + +0.8.5 / 2014-09-04 +================== + + * Fix malicious path detection for empty string path + +0.8.4 / 2014-09-04 +================== + + * Fix a path traversal issue when using `root` + +0.8.3 / 2014-08-16 +================== + + * deps: destroy@1.0.3 + - renamed from dethroy + * deps: on-finished@2.1.0 + +0.8.2 / 2014-08-14 +================== + + * Work around `fd` leak in Node.js 0.10 for `fs.ReadStream` + * deps: dethroy@1.0.2 + +0.8.1 / 2014-08-05 +================== + + * Fix `extensions` behavior when file already has extension + +0.8.0 / 2014-08-05 +================== + + * Add `extensions` option + +0.7.4 / 2014-08-04 +================== + + * Fix serving index files without root dir + +0.7.3 / 2014-07-29 +================== + + * Fix incorrect 403 on Windows and Node.js 0.11 + +0.7.2 / 2014-07-27 +================== + + * deps: depd@0.4.4 + - Work-around v8 generating empty stack traces + +0.7.1 / 2014-07-26 +================== + + * deps: depd@0.4.3 + - Fix exception when global `Error.stackTraceLimit` is too low + +0.7.0 / 2014-07-20 +================== + + * Deprecate `hidden` option; use `dotfiles` option + * Add `dotfiles` option + * deps: debug@1.0.4 + * deps: depd@0.4.2 + - Add `TRACE_DEPRECATION` environment variable + - Remove non-standard grey color from color output + - Support `--no-deprecation` argument + - Support `--trace-deprecation` argument + +0.6.0 / 2014-07-11 +================== + + * Deprecate `from` option; use `root` option + * Deprecate `send.etag()` -- use `etag` in `options` + * Deprecate `send.hidden()` -- use `hidden` in `options` + * Deprecate `send.index()` -- use `index` in `options` + * Deprecate `send.maxage()` -- use `maxAge` in `options` + * Deprecate `send.root()` -- use `root` in `options` + * Cap `maxAge` value to 1 year + * deps: debug@1.0.3 + - Add support for multiple wildcards in namespaces + +0.5.0 / 2014-06-28 +================== + + * Accept string for `maxAge` (converted by `ms`) + * Add `headers` event + * Include link in default redirect response + * Use `EventEmitter.listenerCount` to count listeners + +0.4.3 / 2014-06-11 +================== + + * Do not throw un-catchable error on file open race condition + * Use `escape-html` for HTML escaping + * deps: debug@1.0.2 + - fix some debugging output colors on node.js 0.8 + * deps: finished@1.2.2 + * deps: fresh@0.2.2 + +0.4.2 / 2014-06-09 +================== + + * fix "event emitter leak" warnings + * deps: debug@1.0.1 + * deps: finished@1.2.1 + +0.4.1 / 2014-06-02 +================== + + * Send `max-age` in `Cache-Control` in correct format + +0.4.0 / 2014-05-27 +================== + + * Calculate ETag with md5 for reduced collisions + * Fix wrong behavior when index file matches directory + * Ignore stream errors after request ends + - Goodbye `EBADF, read` + * Skip directories in index file search + * deps: debug@0.8.1 + +0.3.0 / 2014-04-24 +================== + + * Fix sending files with dots without root set + * Coerce option types + * Accept API options in options object + * Set etags to "weak" + * Include file path in etag + * Make "Can't set headers after they are sent." catchable + * Send full entity-body for multi range requests + * Default directory access to 403 when index disabled + * Support multiple index paths + * Support "If-Range" header + * Control whether to generate etags + * deps: mime@1.2.11 + +0.2.0 / 2014-01-29 +================== + + * update range-parser and fresh + +0.1.4 / 2013-08-11 +================== + + * update fresh + +0.1.3 / 2013-07-08 +================== + + * Revert "Fix fd leak" + +0.1.2 / 2013-07-03 +================== + + * Fix fd leak + +0.1.0 / 2012-08-25 +================== + + * add options parameter to send() that is passed to fs.createReadStream() [kanongil] + +0.0.4 / 2012-08-16 +================== + + * allow custom "Accept-Ranges" definition + +0.0.3 / 2012-07-16 +================== + + * fix normalization of the root directory. Closes #3 + +0.0.2 / 2012-07-09 +================== + + * add passing of req explicitly for now (YUCK) + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/node_modules/express/node_modules/send/LICENSE b/node_modules/express/node_modules/send/LICENSE new file mode 100644 index 0000000..e4d595b --- /dev/null +++ b/node_modules/express/node_modules/send/LICENSE @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2012 TJ Holowaychuk +Copyright (c) 2014-2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/send/README.md b/node_modules/express/node_modules/send/README.md new file mode 100644 index 0000000..3586060 --- /dev/null +++ b/node_modules/express/node_modules/send/README.md @@ -0,0 +1,195 @@ +# send + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Linux Build][travis-image]][travis-url] +[![Windows Build][appveyor-image]][appveyor-url] +[![Test Coverage][coveralls-image]][coveralls-url] +[![Gratipay][gratipay-image]][gratipay-url] + +Send is a library for streaming files from the file system as a http response +supporting partial responses (Ranges), conditional-GET negotiation, high test +coverage, and granular events which may be leveraged to take appropriate actions +in your application or framework. + +Looking to serve up entire folders mapped to URLs? Try [serve-static](https://www.npmjs.org/package/serve-static). + +## Installation + +```bash +$ npm install send +``` + +## API + +```js +var send = require('send') +``` + +### send(req, path, [options]) + +Create a new `SendStream` for the given path to send to a `res`. The `req` is +the Node.js HTTP request and the `path` is a urlencoded path to send (urlencoded, +not the actual file-system path). + +#### Options + +##### dotfiles + +Set how "dotfiles" are treated when encountered. A dotfile is a file +or directory that begins with a dot ("."). Note this check is done on +the path itself without checking if the path actually exists on the +disk. If `root` is specified, only the dotfiles above the root are +checked (i.e. the root itself can be within a dotfile when when set +to "deny"). + + - `'allow'` No special treatment for dotfiles. + - `'deny'` Send a 403 for any request for a dotfile. + - `'ignore'` Pretend like the dotfile does not exist and 404. + +The default value is _similar_ to `'ignore'`, with the exception that +this default will not ignore the files within a directory that begins +with a dot, for backward-compatibility. + +##### etag + +Enable or disable etag generation, defaults to true. + +##### extensions + +If a given file doesn't exist, try appending one of the given extensions, +in the given order. By default, this is disabled (set to `false`). An +example value that will serve extension-less HTML files: `['html', 'htm']`. +This is skipped if the requested file already has an extension. + +##### index + +By default send supports "index.html" files, to disable this +set `false` or to supply a new index pass a string or an array +in preferred order. + +##### lastModified + +Enable or disable `Last-Modified` header, defaults to true. Uses the file +system's last modified value. + +##### maxAge + +Provide a max-age in milliseconds for http caching, defaults to 0. +This can also be a string accepted by the +[ms](https://www.npmjs.org/package/ms#readme) module. + +##### root + +Serve files relative to `path`. + +### Events + +The `SendStream` is an event emitter and will emit the following events: + + - `error` an error occurred `(err)` + - `directory` a directory was requested + - `file` a file was requested `(path, stat)` + - `headers` the headers are about to be set on a file `(res, path, stat)` + - `stream` file streaming has started `(stream)` + - `end` streaming has completed + +### .pipe + +The `pipe` method is used to pipe the response into the Node.js HTTP response +object, typically `send(req, path, options).pipe(res)`. + +## Error-handling + +By default when no `error` listeners are present an automatic response will be +made, otherwise you have full control over the response, aka you may show a 5xx +page etc. + +## Caching + +It does _not_ perform internal caching, you should use a reverse proxy cache +such as Varnish for this, or those fancy things called CDNs. If your +application is small enough that it would benefit from single-node memory +caching, it's small enough that it does not need caching at all ;). + +## Debugging + +To enable `debug()` instrumentation output export __DEBUG__: + +``` +$ DEBUG=send node app +``` + +## Running tests + +``` +$ npm install +$ npm test +``` + +## Examples + +### Small example + +```js +var http = require('http'); +var send = require('send'); + +var app = http.createServer(function(req, res){ + send(req, req.url).pipe(res); +}).listen(3000); +``` + +Serving from a root directory with custom error-handling: + +```js +var http = require('http'); +var send = require('send'); +var url = require('url'); + +var app = http.createServer(function(req, res){ + // your custom error-handling logic: + function error(err) { + res.statusCode = err.status || 500; + res.end(err.message); + } + + // your custom headers + function headers(res, path, stat) { + // serve all files for download + res.setHeader('Content-Disposition', 'attachment'); + } + + // your custom directory handling logic: + function redirect() { + res.statusCode = 301; + res.setHeader('Location', req.url + '/'); + res.end('Redirecting to ' + req.url + '/'); + } + + // transfer arbitrary files from within + // /www/example.com/public/* + send(req, url.parse(req.url).pathname, {root: '/www/example.com/public'}) + .on('error', error) + .on('directory', redirect) + .on('headers', headers) + .pipe(res); +}).listen(3000); +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/send.svg +[npm-url]: https://npmjs.org/package/send +[travis-image]: https://img.shields.io/travis/pillarjs/send/master.svg?label=linux +[travis-url]: https://travis-ci.org/pillarjs/send +[appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/send/master.svg?label=windows +[appveyor-url]: https://ci.appveyor.com/project/dougwilson/send +[coveralls-image]: https://img.shields.io/coveralls/pillarjs/send/master.svg +[coveralls-url]: https://coveralls.io/r/pillarjs/send?branch=master +[downloads-image]: https://img.shields.io/npm/dm/send.svg +[downloads-url]: https://npmjs.org/package/send +[gratipay-image]: https://img.shields.io/gratipay/dougwilson.svg +[gratipay-url]: https://www.gratipay.com/dougwilson/ diff --git a/node_modules/express/node_modules/send/index.js b/node_modules/express/node_modules/send/index.js new file mode 100644 index 0000000..3510989 --- /dev/null +++ b/node_modules/express/node_modules/send/index.js @@ -0,0 +1,820 @@ +/*! + * send + * Copyright(c) 2012 TJ Holowaychuk + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module dependencies. + * @private + */ + +var createError = require('http-errors') +var debug = require('debug')('send') +var deprecate = require('depd')('send') +var destroy = require('destroy') +var escapeHtml = require('escape-html') + , parseRange = require('range-parser') + , Stream = require('stream') + , mime = require('mime') + , fresh = require('fresh') + , path = require('path') + , fs = require('fs') + , normalize = path.normalize + , join = path.join +var etag = require('etag') +var EventEmitter = require('events').EventEmitter; +var ms = require('ms'); +var onFinished = require('on-finished') +var statuses = require('statuses') + +/** + * Variables. + */ +var extname = path.extname +var maxMaxAge = 60 * 60 * 24 * 365 * 1000; // 1 year +var resolve = path.resolve +var sep = path.sep +var toString = Object.prototype.toString +var upPathRegexp = /(?:^|[\\\/])\.\.(?:[\\\/]|$)/ + +/** + * Module exports. + * @public + */ + +module.exports = send +module.exports.mime = mime + +/** + * Shim EventEmitter.listenerCount for node.js < 0.10 + */ + +/* istanbul ignore next */ +var listenerCount = EventEmitter.listenerCount + || function(emitter, type){ return emitter.listeners(type).length; }; + +/** + * Return a `SendStream` for `req` and `path`. + * + * @param {object} req + * @param {string} path + * @param {object} [options] + * @return {SendStream} + * @public + */ + +function send(req, path, options) { + return new SendStream(req, path, options); +} + +/** + * Initialize a `SendStream` with the given `path`. + * + * @param {Request} req + * @param {String} path + * @param {object} [options] + * @private + */ + +function SendStream(req, path, options) { + var opts = options || {} + + this.options = opts + this.path = path + this.req = req + + this._etag = opts.etag !== undefined + ? Boolean(opts.etag) + : true + + this._dotfiles = opts.dotfiles !== undefined + ? opts.dotfiles + : 'ignore' + + if (this._dotfiles !== 'ignore' && this._dotfiles !== 'allow' && this._dotfiles !== 'deny') { + throw new TypeError('dotfiles option must be "allow", "deny", or "ignore"') + } + + this._hidden = Boolean(opts.hidden) + + if (opts.hidden !== undefined) { + deprecate('hidden: use dotfiles: \'' + (this._hidden ? 'allow' : 'ignore') + '\' instead') + } + + // legacy support + if (opts.dotfiles === undefined) { + this._dotfiles = undefined + } + + this._extensions = opts.extensions !== undefined + ? normalizeList(opts.extensions, 'extensions option') + : [] + + this._index = opts.index !== undefined + ? normalizeList(opts.index, 'index option') + : ['index.html'] + + this._lastModified = opts.lastModified !== undefined + ? Boolean(opts.lastModified) + : true + + this._maxage = opts.maxAge || opts.maxage + this._maxage = typeof this._maxage === 'string' + ? ms(this._maxage) + : Number(this._maxage) + this._maxage = !isNaN(this._maxage) + ? Math.min(Math.max(0, this._maxage), maxMaxAge) + : 0 + + this._root = opts.root + ? resolve(opts.root) + : null + + if (!this._root && opts.from) { + this.from(opts.from) + } +} + +/** + * Inherits from `Stream.prototype`. + */ + +SendStream.prototype.__proto__ = Stream.prototype; + +/** + * Enable or disable etag generation. + * + * @param {Boolean} val + * @return {SendStream} + * @api public + */ + +SendStream.prototype.etag = deprecate.function(function etag(val) { + val = Boolean(val); + debug('etag %s', val); + this._etag = val; + return this; +}, 'send.etag: pass etag as option'); + +/** + * Enable or disable "hidden" (dot) files. + * + * @param {Boolean} path + * @return {SendStream} + * @api public + */ + +SendStream.prototype.hidden = deprecate.function(function hidden(val) { + val = Boolean(val); + debug('hidden %s', val); + this._hidden = val; + this._dotfiles = undefined + return this; +}, 'send.hidden: use dotfiles option'); + +/** + * Set index `paths`, set to a falsy + * value to disable index support. + * + * @param {String|Boolean|Array} paths + * @return {SendStream} + * @api public + */ + +SendStream.prototype.index = deprecate.function(function index(paths) { + var index = !paths ? [] : normalizeList(paths, 'paths argument'); + debug('index %o', paths); + this._index = index; + return this; +}, 'send.index: pass index as option'); + +/** + * Set root `path`. + * + * @param {String} path + * @return {SendStream} + * @api public + */ + +SendStream.prototype.root = function(path){ + path = String(path); + this._root = resolve(path) + return this; +}; + +SendStream.prototype.from = deprecate.function(SendStream.prototype.root, + 'send.from: pass root as option'); + +SendStream.prototype.root = deprecate.function(SendStream.prototype.root, + 'send.root: pass root as option'); + +/** + * Set max-age to `maxAge`. + * + * @param {Number} maxAge + * @return {SendStream} + * @api public + */ + +SendStream.prototype.maxage = deprecate.function(function maxage(maxAge) { + maxAge = typeof maxAge === 'string' + ? ms(maxAge) + : Number(maxAge); + if (isNaN(maxAge)) maxAge = 0; + if (Infinity == maxAge) maxAge = 60 * 60 * 24 * 365 * 1000; + debug('max-age %d', maxAge); + this._maxage = maxAge; + return this; +}, 'send.maxage: pass maxAge as option'); + +/** + * Emit error with `status`. + * + * @param {number} status + * @param {Error} [error] + * @private + */ + +SendStream.prototype.error = function error(status, error) { + // emit if listeners instead of responding + if (listenerCount(this, 'error') !== 0) { + return this.emit('error', createError(error, status, { + expose: false + })) + } + + var res = this.res + var msg = statuses[status] + + // wipe all existing headers + res._headers = null + + // send basic response + res.statusCode = status + res.setHeader('Content-Type', 'text/plain; charset=UTF-8') + res.setHeader('Content-Length', Buffer.byteLength(msg)) + res.setHeader('X-Content-Type-Options', 'nosniff') + res.end(msg) +} + +/** + * Check if the pathname ends with "/". + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.hasTrailingSlash = function(){ + return '/' == this.path[this.path.length - 1]; +}; + +/** + * Check if this is a conditional GET request. + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isConditionalGET = function(){ + return this.req.headers['if-none-match'] + || this.req.headers['if-modified-since']; +}; + +/** + * Strip content-* header fields. + * + * @private + */ + +SendStream.prototype.removeContentHeaderFields = function removeContentHeaderFields() { + var res = this.res + var headers = Object.keys(res._headers || {}) + + for (var i = 0; i < headers.length; i++) { + var header = headers[i] + if (header.substr(0, 8) === 'content-' && header !== 'content-location') { + res.removeHeader(header) + } + } +} + +/** + * Respond with 304 not modified. + * + * @api private + */ + +SendStream.prototype.notModified = function(){ + var res = this.res; + debug('not modified'); + this.removeContentHeaderFields(); + res.statusCode = 304; + res.end(); +}; + +/** + * Raise error that headers already sent. + * + * @api private + */ + +SendStream.prototype.headersAlreadySent = function headersAlreadySent(){ + var err = new Error('Can\'t set headers after they are sent.'); + debug('headers already sent'); + this.error(500, err); +}; + +/** + * Check if the request is cacheable, aka + * responded with 2xx or 304 (see RFC 2616 section 14.2{5,6}). + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isCachable = function(){ + var res = this.res; + return (res.statusCode >= 200 && res.statusCode < 300) || 304 == res.statusCode; +}; + +/** + * Handle stat() error. + * + * @param {Error} error + * @private + */ + +SendStream.prototype.onStatError = function onStatError(error) { + switch (error.code) { + case 'ENAMETOOLONG': + case 'ENOENT': + case 'ENOTDIR': + this.error(404, error) + break + default: + this.error(500, error) + break + } +} + +/** + * Check if the cache is fresh. + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isFresh = function(){ + return fresh(this.req.headers, this.res._headers); +}; + +/** + * Check if the range is fresh. + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isRangeFresh = function isRangeFresh(){ + var ifRange = this.req.headers['if-range']; + + if (!ifRange) return true; + + return ~ifRange.indexOf('"') + ? ~ifRange.indexOf(this.res._headers['etag']) + : Date.parse(this.res._headers['last-modified']) <= Date.parse(ifRange); +}; + +/** + * Redirect to path. + * + * @param {string} path + * @private + */ + +SendStream.prototype.redirect = function redirect(path) { + if (listenerCount(this, 'directory') !== 0) { + this.emit('directory') + return + } + + if (this.hasTrailingSlash()) { + this.error(403) + return + } + + var loc = path + '/' + var msg = 'Redirecting to ' + escapeHtml(loc) + '\n' + var res = this.res + + // redirect + res.statusCode = 301 + res.setHeader('Content-Type', 'text/html; charset=UTF-8') + res.setHeader('Content-Length', Buffer.byteLength(msg)) + res.setHeader('X-Content-Type-Options', 'nosniff') + res.setHeader('Location', loc) + res.end(msg) +} + +/** + * Pipe to `res. + * + * @param {Stream} res + * @return {Stream} res + * @api public + */ + +SendStream.prototype.pipe = function(res){ + var self = this + , args = arguments + , root = this._root; + + // references + this.res = res; + + // decode the path + var path = decode(this.path) + if (path === -1) return this.error(400) + + // null byte(s) + if (~path.indexOf('\0')) return this.error(400); + + var parts + if (root !== null) { + // malicious path + if (upPathRegexp.test(normalize('.' + sep + path))) { + debug('malicious path "%s"', path) + return this.error(403) + } + + // join / normalize from optional root dir + path = normalize(join(root, path)) + root = normalize(root + sep) + + // explode path parts + parts = path.substr(root.length).split(sep) + } else { + // ".." is malicious without "root" + if (upPathRegexp.test(path)) { + debug('malicious path "%s"', path) + return this.error(403) + } + + // explode path parts + parts = normalize(path).split(sep) + + // resolve the path + path = resolve(path) + } + + // dotfile handling + if (containsDotFile(parts)) { + var access = this._dotfiles + + // legacy support + if (access === undefined) { + access = parts[parts.length - 1][0] === '.' + ? (this._hidden ? 'allow' : 'ignore') + : 'allow' + } + + debug('%s dotfile "%s"', access, path) + switch (access) { + case 'allow': + break + case 'deny': + return this.error(403) + case 'ignore': + default: + return this.error(404) + } + } + + // index file support + if (this._index.length && this.path[this.path.length - 1] === '/') { + this.sendIndex(path); + return res; + } + + this.sendFile(path); + return res; +}; + +/** + * Transfer `path`. + * + * @param {String} path + * @api public + */ + +SendStream.prototype.send = function(path, stat){ + var len = stat.size; + var options = this.options + var opts = {} + var res = this.res; + var req = this.req; + var ranges = req.headers.range; + var offset = options.start || 0; + + if (res._header) { + // impossible to send now + return this.headersAlreadySent(); + } + + debug('pipe "%s"', path) + + // set header fields + this.setHeader(path, stat); + + // set content-type + this.type(path); + + // conditional GET support + if (this.isConditionalGET() + && this.isCachable() + && this.isFresh()) { + return this.notModified(); + } + + // adjust len to start/end options + len = Math.max(0, len - offset); + if (options.end !== undefined) { + var bytes = options.end - offset + 1; + if (len > bytes) len = bytes; + } + + // Range support + if (ranges) { + ranges = parseRange(len, ranges); + + // If-Range support + if (!this.isRangeFresh()) { + debug('range stale'); + ranges = -2; + } + + // unsatisfiable + if (-1 == ranges) { + debug('range unsatisfiable'); + res.setHeader('Content-Range', 'bytes */' + stat.size); + return this.error(416); + } + + // valid (syntactically invalid/multiple ranges are treated as a regular response) + if (-2 != ranges && ranges.length === 1) { + debug('range %j', ranges); + + // Content-Range + res.statusCode = 206; + res.setHeader('Content-Range', 'bytes ' + + ranges[0].start + + '-' + + ranges[0].end + + '/' + + len); + + offset += ranges[0].start; + len = ranges[0].end - ranges[0].start + 1; + } + } + + // clone options + for (var prop in options) { + opts[prop] = options[prop] + } + + // set read options + opts.start = offset + opts.end = Math.max(offset, offset + len - 1) + + // content-length + res.setHeader('Content-Length', len); + + // HEAD support + if ('HEAD' == req.method) return res.end(); + + this.stream(path, opts) +}; + +/** + * Transfer file for `path`. + * + * @param {String} path + * @api private + */ +SendStream.prototype.sendFile = function sendFile(path) { + var i = 0 + var self = this + + debug('stat "%s"', path); + fs.stat(path, function onstat(err, stat) { + if (err && err.code === 'ENOENT' + && !extname(path) + && path[path.length - 1] !== sep) { + // not found, check extensions + return next(err) + } + if (err) return self.onStatError(err) + if (stat.isDirectory()) return self.redirect(self.path) + self.emit('file', path, stat) + self.send(path, stat) + }) + + function next(err) { + if (self._extensions.length <= i) { + return err + ? self.onStatError(err) + : self.error(404) + } + + var p = path + '.' + self._extensions[i++] + + debug('stat "%s"', p) + fs.stat(p, function (err, stat) { + if (err) return next(err) + if (stat.isDirectory()) return next() + self.emit('file', p, stat) + self.send(p, stat) + }) + } +} + +/** + * Transfer index for `path`. + * + * @param {String} path + * @api private + */ +SendStream.prototype.sendIndex = function sendIndex(path){ + var i = -1; + var self = this; + + function next(err){ + if (++i >= self._index.length) { + if (err) return self.onStatError(err); + return self.error(404); + } + + var p = join(path, self._index[i]); + + debug('stat "%s"', p); + fs.stat(p, function(err, stat){ + if (err) return next(err); + if (stat.isDirectory()) return next(); + self.emit('file', p, stat); + self.send(p, stat); + }); + } + + next(); +}; + +/** + * Stream `path` to the response. + * + * @param {String} path + * @param {Object} options + * @api private + */ + +SendStream.prototype.stream = function(path, options){ + // TODO: this is all lame, refactor meeee + var finished = false; + var self = this; + var res = this.res; + var req = this.req; + + // pipe + var stream = fs.createReadStream(path, options); + this.emit('stream', stream); + stream.pipe(res); + + // response finished, done with the fd + onFinished(res, function onfinished(){ + finished = true; + destroy(stream); + }); + + // error handling code-smell + stream.on('error', function onerror(err){ + // request already finished + if (finished) return; + + // clean up stream + finished = true; + destroy(stream); + + // error + self.onStatError(err); + }); + + // end + stream.on('end', function onend(){ + self.emit('end'); + }); +}; + +/** + * Set content-type based on `path` + * if it hasn't been explicitly set. + * + * @param {String} path + * @api private + */ + +SendStream.prototype.type = function(path){ + var res = this.res; + if (res.getHeader('Content-Type')) return; + var type = mime.lookup(path); + var charset = mime.charsets.lookup(type); + debug('content-type %s', type); + res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : '')); +}; + +/** + * Set response header fields, most + * fields may be pre-defined. + * + * @param {String} path + * @param {Object} stat + * @api private + */ + +SendStream.prototype.setHeader = function setHeader(path, stat){ + var res = this.res; + + this.emit('headers', res, path, stat); + + if (!res.getHeader('Accept-Ranges')) res.setHeader('Accept-Ranges', 'bytes'); + if (!res.getHeader('Cache-Control')) res.setHeader('Cache-Control', 'public, max-age=' + Math.floor(this._maxage / 1000)); + + if (this._lastModified && !res.getHeader('Last-Modified')) { + var modified = stat.mtime.toUTCString() + debug('modified %s', modified) + res.setHeader('Last-Modified', modified) + } + + if (this._etag && !res.getHeader('ETag')) { + var val = etag(stat) + debug('etag %s', val) + res.setHeader('ETag', val) + } +}; + +/** + * Determine if path parts contain a dotfile. + * + * @api private + */ + +function containsDotFile(parts) { + for (var i = 0; i < parts.length; i++) { + if (parts[i][0] === '.') { + return true + } + } + + return false +} + +/** + * decodeURIComponent. + * + * Allows V8 to only deoptimize this fn instead of all + * of send(). + * + * @param {String} path + * @api private + */ + +function decode(path) { + try { + return decodeURIComponent(path) + } catch (err) { + return -1 + } +} + +/** + * Normalize the index option into an array. + * + * @param {boolean|string|array} val + * @param {string} name + * @private + */ + +function normalizeList(val, name) { + var list = [].concat(val || []) + + for (var i = 0; i < list.length; i++) { + if (typeof list[i] !== 'string') { + throw new TypeError(name + ' must be array of strings or false') + } + } + + return list +} diff --git a/node_modules/express/node_modules/send/node_modules/.bin/mime b/node_modules/express/node_modules/send/node_modules/.bin/mime new file mode 120000 index 0000000..fbb7ee0 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/.bin/mime @@ -0,0 +1 @@ +../mime/cli.js \ No newline at end of file diff --git a/node_modules/express/node_modules/send/node_modules/destroy/README.md b/node_modules/express/node_modules/send/node_modules/destroy/README.md new file mode 100644 index 0000000..665acb7 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/destroy/README.md @@ -0,0 +1,38 @@ +# Destroy + +[![NPM version][npm-image]][npm-url] +[![Build status][travis-image]][travis-url] +[![Test coverage][coveralls-image]][coveralls-url] +[![Dependency Status][david-image]][david-url] +[![License][license-image]][license-url] +[![Downloads][downloads-image]][downloads-url] +[![Gittip][gittip-image]][gittip-url] + +Destroy a stream. + +## API + +```js +var destroy = require('destroy') + +var fs = require('fs') +var stream = fs.createReadStream('package.json') +destroy(stream) +``` + +[npm-image]: https://img.shields.io/npm/v/destroy.svg?style=flat-square +[npm-url]: https://npmjs.org/package/destroy +[github-tag]: http://img.shields.io/github/tag/stream-utils/destroy.svg?style=flat-square +[github-url]: https://github.com/stream-utils/destroy/tags +[travis-image]: https://img.shields.io/travis/stream-utils/destroy.svg?style=flat-square +[travis-url]: https://travis-ci.org/stream-utils/destroy +[coveralls-image]: https://img.shields.io/coveralls/stream-utils/destroy.svg?style=flat-square +[coveralls-url]: https://coveralls.io/r/stream-utils/destroy?branch=master +[david-image]: http://img.shields.io/david/stream-utils/destroy.svg?style=flat-square +[david-url]: https://david-dm.org/stream-utils/destroy +[license-image]: http://img.shields.io/npm/l/destroy.svg?style=flat-square +[license-url]: LICENSE.md +[downloads-image]: http://img.shields.io/npm/dm/destroy.svg?style=flat-square +[downloads-url]: https://npmjs.org/package/destroy +[gittip-image]: https://img.shields.io/gittip/jonathanong.svg?style=flat-square +[gittip-url]: https://www.gittip.com/jonathanong/ diff --git a/node_modules/express/node_modules/send/node_modules/destroy/index.js b/node_modules/express/node_modules/send/node_modules/destroy/index.js new file mode 100644 index 0000000..b455217 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/destroy/index.js @@ -0,0 +1,36 @@ +var ReadStream = require('fs').ReadStream +var Stream = require('stream') + +module.exports = function destroy(stream) { + if (stream instanceof ReadStream) { + return destroyReadStream(stream) + } + + if (!(stream instanceof Stream)) { + return stream + } + + if (typeof stream.destroy === 'function') { + stream.destroy() + } + + return stream +} + +function destroyReadStream(stream) { + stream.destroy() + + if (typeof stream.close === 'function') { + // node.js core bug work-around + stream.on('open', onopenClose) + } + + return stream +} + +function onopenClose() { + if (typeof this.fd === 'number') { + // actually close down the fd + this.close() + } +} diff --git a/node_modules/express/node_modules/send/node_modules/destroy/package.json b/node_modules/express/node_modules/send/node_modules/destroy/package.json new file mode 100644 index 0000000..8a58cad --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/destroy/package.json @@ -0,0 +1,66 @@ +{ + "name": "destroy", + "description": "destroy a stream if possible", + "version": "1.0.3", + "author": { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/stream-utils/destroy" + }, + "devDependencies": { + "istanbul": "0", + "mocha": "1" + }, + "scripts": { + "test": "mocha --reporter spec", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter dot" + }, + "files": [ + "index.js" + ], + "keywords": [ + "stream", + "streams", + "destroy", + "cleanup", + "leak", + "fd" + ], + "gitHead": "50af95ece4a70202f9301bc3edc8f9fdbbad0f26", + "bugs": { + "url": "https://github.com/stream-utils/destroy/issues" + }, + "homepage": "https://github.com/stream-utils/destroy", + "_id": "destroy@1.0.3", + "_shasum": "b433b4724e71fd8551d9885174851c5fc377e2c9", + "_from": "destroy@1.0.3", + "_npmVersion": "1.4.21", + "_npmUser": { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + } + ], + "dist": { + "shasum": "b433b4724e71fd8551d9885174851c5fc377e2c9", + "tarball": "http://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz" +} diff --git a/node_modules/express/node_modules/send/node_modules/http-errors/HISTORY.md b/node_modules/express/node_modules/send/node_modules/http-errors/HISTORY.md new file mode 100644 index 0000000..4c7087d --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/http-errors/HISTORY.md @@ -0,0 +1,76 @@ +2015-02-02 / 1.3.1 +================== + + * Fix regression where status can be overwritten in `createError` `props` + +2015-02-01 / 1.3.0 +================== + + * Construct errors using defined constructors from `createError` + * Fix error names that are not identifiers + - `createError["I'mateapot"]` is now `createError.ImATeapot` + * Set a meaningful `name` property on constructed errors + +2014-12-09 / 1.2.8 +================== + + * Fix stack trace from exported function + * Remove `arguments.callee` usage + +2014-10-14 / 1.2.7 +================== + + * Remove duplicate line + +2014-10-02 / 1.2.6 +================== + + * Fix `expose` to be `true` for `ClientError` constructor + +2014-09-28 / 1.2.5 +================== + + * deps: statuses@1 + +2014-09-21 / 1.2.4 +================== + + * Fix dependency version to work with old `npm`s + +2014-09-21 / 1.2.3 +================== + + * deps: statuses@~1.1.0 + +2014-09-21 / 1.2.2 +================== + + * Fix publish error + +2014-09-21 / 1.2.1 +================== + + * Support Node.js 0.6 + * Use `inherits` instead of `util` + +2014-09-09 / 1.2.0 +================== + + * Fix the way inheriting functions + * Support `expose` being provided in properties argument + +2014-09-08 / 1.1.0 +================== + + * Default status to 500 + * Support provided `error` to extend + +2014-09-08 / 1.0.1 +================== + + * Fix accepting string message + +2014-09-08 / 1.0.0 +================== + + * Initial release diff --git a/node_modules/express/node_modules/send/node_modules/http-errors/LICENSE b/node_modules/express/node_modules/send/node_modules/http-errors/LICENSE new file mode 100644 index 0000000..a7ae8ee --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/http-errors/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/express/node_modules/send/node_modules/http-errors/README.md b/node_modules/express/node_modules/send/node_modules/http-errors/README.md new file mode 100644 index 0000000..520271e --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/http-errors/README.md @@ -0,0 +1,63 @@ +# http-errors + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Create HTTP errors for Express, Koa, Connect, etc. with ease. + +## Example + +```js +var createError = require('http-errors'); + +app.use(function (req, res, next) { + if (!req.user) return next(createError(401, 'Please login to view this page.')); + next(); +}) +``` + +## API + +This is the current API, currently extracted from Koa and subject to change. + +### Error Properties + +- `message` +- `status` and `statusCode` - the status code of the error, defaulting to `500` + +### createError([status], [message], [properties]) + +```js +var err = createError(404, 'This video does not exist!'); +``` + +- `status: 500` - the status code as a number +- `message` - the message of the error, defaulting to node's text for that status code. +- `properties` - custom properties to attach to the object + +### new createError\[code || name\](\[msg]\)) + +```js +var err = new createError.NotFound(); +``` + +- `code` - the status code as a number +- `name` - the name of the error as a "bumpy case", i.e. `NotFound` or `InternalServerError`. + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/http-errors.svg?style=flat +[npm-url]: https://npmjs.org/package/http-errors +[node-version-image]: https://img.shields.io/node/v/http-errors.svg?style=flat +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/http-errors.svg?style=flat +[travis-url]: https://travis-ci.org/jshttp/http-errors +[coveralls-image]: https://img.shields.io/coveralls/jshttp/http-errors.svg?style=flat +[coveralls-url]: https://coveralls.io/r/jshttp/http-errors +[downloads-image]: https://img.shields.io/npm/dm/http-errors.svg?style=flat +[downloads-url]: https://npmjs.org/package/http-errors diff --git a/node_modules/express/node_modules/send/node_modules/http-errors/index.js b/node_modules/express/node_modules/send/node_modules/http-errors/index.js new file mode 100644 index 0000000..d84b114 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/http-errors/index.js @@ -0,0 +1,120 @@ + +var statuses = require('statuses'); +var inherits = require('inherits'); + +function toIdentifier(str) { + return str.split(' ').map(function (token) { + return token.slice(0, 1).toUpperCase() + token.slice(1) + }).join('').replace(/[^ _0-9a-z]/gi, '') +} + +exports = module.exports = function httpError() { + // so much arity going on ~_~ + var err; + var msg; + var status = 500; + var props = {}; + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i]; + if (arg instanceof Error) { + err = arg; + status = err.status || err.statusCode || status; + continue; + } + switch (typeof arg) { + case 'string': + msg = arg; + break; + case 'number': + status = arg; + break; + case 'object': + props = arg; + break; + } + } + + if (typeof status !== 'number' || !statuses[status]) { + status = 500 + } + + // constructor + var HttpError = exports[status] + + if (!err) { + // create error + err = HttpError + ? new HttpError(msg) + : new Error(msg || statuses[status]) + Error.captureStackTrace(err, httpError) + } + + if (!HttpError || !(err instanceof HttpError)) { + // add properties to generic error + err.expose = status < 500 + err.status = err.statusCode = status + } + + for (var key in props) { + if (key !== 'status' && key !== 'statusCode') { + err[key] = props[key] + } + } + + return err; +}; + +// create generic error objects +var codes = statuses.codes.filter(function (num) { + return num >= 400; +}); + +codes.forEach(function (code) { + var name = toIdentifier(statuses[code]) + var className = name.match(/Error$/) ? name : name + 'Error' + + if (code >= 500) { + var ServerError = function ServerError(msg) { + var self = new Error(msg != null ? msg : statuses[code]) + Error.captureStackTrace(self, ServerError) + self.__proto__ = ServerError.prototype + Object.defineProperty(self, 'name', { + enumerable: false, + configurable: true, + value: className, + writable: true + }) + return self + } + inherits(ServerError, Error); + ServerError.prototype.status = + ServerError.prototype.statusCode = code; + ServerError.prototype.expose = false; + exports[code] = + exports[name] = ServerError + return; + } + + var ClientError = function ClientError(msg) { + var self = new Error(msg != null ? msg : statuses[code]) + Error.captureStackTrace(self, ClientError) + self.__proto__ = ClientError.prototype + Object.defineProperty(self, 'name', { + enumerable: false, + configurable: true, + value: className, + writable: true + }) + return self + } + inherits(ClientError, Error); + ClientError.prototype.status = + ClientError.prototype.statusCode = code; + ClientError.prototype.expose = true; + exports[code] = + exports[name] = ClientError + return; +}); + +// backwards-compatibility +exports["I'mateapot"] = exports.ImATeapot diff --git a/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/LICENSE b/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/LICENSE new file mode 100644 index 0000000..dea3013 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/LICENSE @@ -0,0 +1,16 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + diff --git a/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/README.md b/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/README.md new file mode 100644 index 0000000..b1c5665 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/README.md @@ -0,0 +1,42 @@ +Browser-friendly inheritance fully compatible with standard node.js +[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor). + +This package exports standard `inherits` from node.js `util` module in +node environment, but also provides alternative browser-friendly +implementation through [browser +field](https://gist.github.com/shtylman/4339901). Alternative +implementation is a literal copy of standard one located in standalone +module to avoid requiring of `util`. It also has a shim for old +browsers with no `Object.create` support. + +While keeping you sure you are using standard `inherits` +implementation in node.js environment, it allows bundlers such as +[browserify](https://github.com/substack/node-browserify) to not +include full `util` package to your client code if all you need is +just `inherits` function. It worth, because browser shim for `util` +package is large and `inherits` is often the single function you need +from it. + +It's recommended to use this package instead of +`require('util').inherits` for any code that has chances to be used +not only in node.js but in browser too. + +## usage + +```js +var inherits = require('inherits'); +// then use exactly as the standard one +``` + +## note on version ~1.0 + +Version ~1.0 had completely different motivation and is not compatible +neither with 2.0 nor with standard node.js `inherits`. + +If you are using version ~1.0 and planning to switch to ~2.0, be +careful: + +* new version uses `super_` instead of `super` for referencing + superclass +* new version overwrites current prototype while old one preserves any + existing fields on it diff --git a/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/inherits.js b/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/inherits.js new file mode 100644 index 0000000..29f5e24 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/inherits.js @@ -0,0 +1 @@ +module.exports = require('util').inherits diff --git a/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/inherits_browser.js b/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/inherits_browser.js new file mode 100644 index 0000000..c1e78a7 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/inherits_browser.js @@ -0,0 +1,23 @@ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} diff --git a/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/package.json b/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/package.json new file mode 100644 index 0000000..93d5078 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/package.json @@ -0,0 +1,50 @@ +{ + "name": "inherits", + "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()", + "version": "2.0.1", + "keywords": [ + "inheritance", + "class", + "klass", + "oop", + "object-oriented", + "inherits", + "browser", + "browserify" + ], + "main": "./inherits.js", + "browser": "./inherits_browser.js", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/inherits.git" + }, + "license": "ISC", + "scripts": { + "test": "node test" + }, + "bugs": { + "url": "https://github.com/isaacs/inherits/issues" + }, + "_id": "inherits@2.0.1", + "dist": { + "shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1", + "tarball": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "_from": "inherits@>=2.0.1 <2.1.0", + "_npmVersion": "1.3.8", + "_npmUser": { + "name": "isaacs", + "email": "i@izs.me" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + } + ], + "directories": {}, + "_shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1", + "_resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "readme": "ERROR: No README data found!", + "homepage": "https://github.com/isaacs/inherits#readme" +} diff --git a/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/test.js b/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/test.js new file mode 100644 index 0000000..fc53012 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/http-errors/node_modules/inherits/test.js @@ -0,0 +1,25 @@ +var inherits = require('./inherits.js') +var assert = require('assert') + +function test(c) { + assert(c.constructor === Child) + assert(c.constructor.super_ === Parent) + assert(Object.getPrototypeOf(c) === Child.prototype) + assert(Object.getPrototypeOf(Object.getPrototypeOf(c)) === Parent.prototype) + assert(c instanceof Child) + assert(c instanceof Parent) +} + +function Child() { + Parent.call(this) + test(this) +} + +function Parent() {} + +inherits(Child, Parent) + +var c = new Child +test(c) + +console.log('ok') diff --git a/node_modules/express/node_modules/send/node_modules/http-errors/package.json b/node_modules/express/node_modules/send/node_modules/http-errors/package.json new file mode 100644 index 0000000..41f845d --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/http-errors/package.json @@ -0,0 +1,85 @@ +{ + "name": "http-errors", + "description": "Create HTTP error objects", + "version": "1.3.1", + "author": { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + "contributors": [ + { + "name": "Alan Plum", + "email": "me@pluma.io" + }, + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/jshttp/http-errors.git" + }, + "dependencies": { + "inherits": "~2.0.1", + "statuses": "1" + }, + "devDependencies": { + "istanbul": "0", + "mocha": "1" + }, + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec --bail", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter dot" + }, + "keywords": [ + "http", + "error" + ], + "files": [ + "index.js", + "HISTORY.md", + "LICENSE", + "README.md" + ], + "gitHead": "89a8502b40d5dd42da2908f265275e2eeb8d0699", + "bugs": { + "url": "https://github.com/jshttp/http-errors/issues" + }, + "homepage": "https://github.com/jshttp/http-errors", + "_id": "http-errors@1.3.1", + "_shasum": "197e22cdebd4198585e8694ef6786197b91ed942", + "_from": "http-errors@>=1.3.1 <1.4.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "egeste", + "email": "npm@egeste.net" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "197e22cdebd4198585e8694ef6786197b91ed942", + "tarball": "http://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/express/node_modules/send/node_modules/mime/.npmignore b/node_modules/express/node_modules/send/node_modules/mime/.npmignore new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/express/node_modules/send/node_modules/mime/LICENSE b/node_modules/express/node_modules/send/node_modules/mime/LICENSE new file mode 100644 index 0000000..451fc45 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/mime/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 Benjamin Thomas, Robert Kieffer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/express/node_modules/send/node_modules/mime/README.md b/node_modules/express/node_modules/send/node_modules/mime/README.md new file mode 100644 index 0000000..506fbe5 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/mime/README.md @@ -0,0 +1,90 @@ +# mime + +Comprehensive MIME type mapping API based on mime-db module. + +## Install + +Install with [npm](http://github.com/isaacs/npm): + + npm install mime + +## Contributing / Testing + + npm run test + +## Command Line + + mime [path_string] + +E.g. + + > mime scripts/jquery.js + application/javascript + +## API - Queries + +### mime.lookup(path) +Get the mime type associated with a file, if no mime type is found `application/octet-stream` is returned. Performs a case-insensitive lookup using the extension in `path` (the substring after the last '/' or '.'). E.g. + +```js +var mime = require('mime'); + +mime.lookup('/path/to/file.txt'); // => 'text/plain' +mime.lookup('file.txt'); // => 'text/plain' +mime.lookup('.TXT'); // => 'text/plain' +mime.lookup('htm'); // => 'text/html' +``` + +### mime.default_type +Sets the mime type returned when `mime.lookup` fails to find the extension searched for. (Default is `application/octet-stream`.) + +### mime.extension(type) +Get the default extension for `type` + +```js +mime.extension('text/html'); // => 'html' +mime.extension('application/octet-stream'); // => 'bin' +``` + +### mime.charsets.lookup() + +Map mime-type to charset + +```js +mime.charsets.lookup('text/plain'); // => 'UTF-8' +``` + +(The logic for charset lookups is pretty rudimentary. Feel free to suggest improvements.) + +## API - Defining Custom Types + +Custom type mappings can be added on a per-project basis via the following APIs. + +### mime.define() + +Add custom mime/extension mappings + +```js +mime.define({ + 'text/x-some-format': ['x-sf', 'x-sft', 'x-sfml'], + 'application/x-my-type': ['x-mt', 'x-mtt'], + // etc ... +}); + +mime.lookup('x-sft'); // => 'text/x-some-format' +``` + +The first entry in the extensions array is returned by `mime.extension()`. E.g. + +```js +mime.extension('text/x-some-format'); // => 'x-sf' +``` + +### mime.load(filepath) + +Load mappings from an Apache ".types" format file + +```js +mime.load('./my_project.types'); +``` +The .types file format is simple - See the `types` dir for examples. diff --git a/node_modules/express/node_modules/send/node_modules/mime/build/build.js b/node_modules/express/node_modules/send/node_modules/mime/build/build.js new file mode 100644 index 0000000..ed5313e --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/mime/build/build.js @@ -0,0 +1,11 @@ +var db = require('mime-db'); + +var mapByType = {}; +Object.keys(db).forEach(function(key) { + var extensions = db[key].extensions; + if (extensions) { + mapByType[key] = extensions; + } +}); + +console.log(JSON.stringify(mapByType)); diff --git a/node_modules/express/node_modules/send/node_modules/mime/build/test.js b/node_modules/express/node_modules/send/node_modules/mime/build/test.js new file mode 100644 index 0000000..58b9ba7 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/mime/build/test.js @@ -0,0 +1,57 @@ +/** + * Usage: node test.js + */ + +var mime = require('../mime'); +var assert = require('assert'); +var path = require('path'); + +// +// Test mime lookups +// + +assert.equal('text/plain', mime.lookup('text.txt')); // normal file +assert.equal('text/plain', mime.lookup('TEXT.TXT')); // uppercase +assert.equal('text/plain', mime.lookup('dir/text.txt')); // dir + file +assert.equal('text/plain', mime.lookup('.text.txt')); // hidden file +assert.equal('text/plain', mime.lookup('.txt')); // nameless +assert.equal('text/plain', mime.lookup('txt')); // extension-only +assert.equal('text/plain', mime.lookup('/txt')); // extension-less () +assert.equal('text/plain', mime.lookup('\\txt')); // Windows, extension-less +assert.equal('application/octet-stream', mime.lookup('text.nope')); // unrecognized +assert.equal('fallback', mime.lookup('text.fallback', 'fallback')); // alternate default + +// +// Test extensions +// + +assert.equal('txt', mime.extension(mime.types.text)); +assert.equal('html', mime.extension(mime.types.htm)); +assert.equal('bin', mime.extension('application/octet-stream')); +assert.equal('bin', mime.extension('application/octet-stream ')); +assert.equal('html', mime.extension(' text/html; charset=UTF-8')); +assert.equal('html', mime.extension('text/html; charset=UTF-8 ')); +assert.equal('html', mime.extension('text/html; charset=UTF-8')); +assert.equal('html', mime.extension('text/html ; charset=UTF-8')); +assert.equal('html', mime.extension('text/html;charset=UTF-8')); +assert.equal('html', mime.extension('text/Html;charset=UTF-8')); +assert.equal(undefined, mime.extension('unrecognized')); + +// +// Test node.types lookups +// + +assert.equal('application/font-woff', mime.lookup('file.woff')); +assert.equal('application/octet-stream', mime.lookup('file.buffer')); +assert.equal('audio/mp4', mime.lookup('file.m4a')); +assert.equal('font/opentype', mime.lookup('file.otf')); + +// +// Test charsets +// + +assert.equal('UTF-8', mime.charsets.lookup('text/plain')); +assert.equal(undefined, mime.charsets.lookup(mime.types.js)); +assert.equal('fallback', mime.charsets.lookup('application/octet-stream', 'fallback')); + +console.log('\nAll tests passed'); diff --git a/node_modules/express/node_modules/send/node_modules/mime/cli.js b/node_modules/express/node_modules/send/node_modules/mime/cli.js new file mode 100755 index 0000000..20b1ffe --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/mime/cli.js @@ -0,0 +1,8 @@ +#!/usr/bin/env node + +var mime = require('./mime.js'); +var file = process.argv[2]; +var type = mime.lookup(file); + +process.stdout.write(type + '\n'); + diff --git a/node_modules/express/node_modules/send/node_modules/mime/mime.js b/node_modules/express/node_modules/send/node_modules/mime/mime.js new file mode 100644 index 0000000..341b6a5 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/mime/mime.js @@ -0,0 +1,108 @@ +var path = require('path'); +var fs = require('fs'); + +function Mime() { + // Map of extension -> mime type + this.types = Object.create(null); + + // Map of mime type -> extension + this.extensions = Object.create(null); +} + +/** + * Define mimetype -> extension mappings. Each key is a mime-type that maps + * to an array of extensions associated with the type. The first extension is + * used as the default extension for the type. + * + * e.g. mime.define({'audio/ogg', ['oga', 'ogg', 'spx']}); + * + * @param map (Object) type definitions + */ +Mime.prototype.define = function (map) { + for (var type in map) { + var exts = map[type]; + for (var i = 0; i < exts.length; i++) { + if (process.env.DEBUG_MIME && this.types[exts]) { + console.warn(this._loading.replace(/.*\//, ''), 'changes "' + exts[i] + '" extension type from ' + + this.types[exts] + ' to ' + type); + } + + this.types[exts[i]] = type; + } + + // Default extension is the first one we encounter + if (!this.extensions[type]) { + this.extensions[type] = exts[0]; + } + } +}; + +/** + * Load an Apache2-style ".types" file + * + * This may be called multiple times (it's expected). Where files declare + * overlapping types/extensions, the last file wins. + * + * @param file (String) path of file to load. + */ +Mime.prototype.load = function(file) { + this._loading = file; + // Read file and split into lines + var map = {}, + content = fs.readFileSync(file, 'ascii'), + lines = content.split(/[\r\n]+/); + + lines.forEach(function(line) { + // Clean up whitespace/comments, and split into fields + var fields = line.replace(/\s*#.*|^\s*|\s*$/g, '').split(/\s+/); + map[fields.shift()] = fields; + }); + + this.define(map); + + this._loading = null; +}; + +/** + * Lookup a mime type based on extension + */ +Mime.prototype.lookup = function(path, fallback) { + var ext = path.replace(/.*[\.\/\\]/, '').toLowerCase(); + + return this.types[ext] || fallback || this.default_type; +}; + +/** + * Return file extension associated with a mime type + */ +Mime.prototype.extension = function(mimeType) { + var type = mimeType.match(/^\s*([^;\s]*)(?:;|\s|$)/)[1].toLowerCase(); + return this.extensions[type]; +}; + +// Default instance +var mime = new Mime(); + +// Define built-in types +mime.define(require('./types.json')); + +// Default type +mime.default_type = mime.lookup('bin'); + +// +// Additional API specific to the default instance +// + +mime.Mime = Mime; + +/** + * Lookup a charset based on mime type. + */ +mime.charsets = { + lookup: function(mimeType, fallback) { + // Assume text types are utf8 + return (/^text\//).test(mimeType) ? 'UTF-8' : fallback; + } +}; + +module.exports = mime; diff --git a/node_modules/express/node_modules/send/node_modules/mime/package.json b/node_modules/express/node_modules/send/node_modules/mime/package.json new file mode 100644 index 0000000..ffa287e --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/mime/package.json @@ -0,0 +1,72 @@ +{ + "author": { + "name": "Robert Kieffer", + "email": "robert@broofa.com", + "url": "http://github.com/broofa" + }, + "scripts": { + "prepublish": "node build/build.js > types.json", + "test": "node build/test.js" + }, + "bin": { + "mime": "cli.js" + }, + "contributors": [ + { + "name": "Benjamin Thomas", + "email": "benjamin@benjaminthomas.org", + "url": "http://github.com/bentomas" + } + ], + "description": "A comprehensive library for mime-type mapping", + "licenses": [ + { + "type": "MIT", + "url": "https://raw.github.com/broofa/node-mime/master/LICENSE" + } + ], + "dependencies": {}, + "devDependencies": { + "mime-db": "^1.2.0" + }, + "keywords": [ + "util", + "mime" + ], + "main": "mime.js", + "name": "mime", + "repository": { + "url": "https://github.com/broofa/node-mime", + "type": "git" + }, + "version": "1.3.4", + "gitHead": "1628f6e0187095009dcef4805c3a49706f137974", + "bugs": { + "url": "https://github.com/broofa/node-mime/issues" + }, + "homepage": "https://github.com/broofa/node-mime", + "_id": "mime@1.3.4", + "_shasum": "115f9e3b6b3daf2959983cb38f149a2d40eb5d53", + "_from": "mime@1.3.4", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "broofa", + "email": "robert@broofa.com" + }, + "maintainers": [ + { + "name": "broofa", + "email": "robert@broofa.com" + }, + { + "name": "bentomas", + "email": "benjamin@benjaminthomas.org" + } + ], + "dist": { + "shasum": "115f9e3b6b3daf2959983cb38f149a2d40eb5d53", + "tarball": "http://registry.npmjs.org/mime/-/mime-1.3.4.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz" +} diff --git a/node_modules/express/node_modules/send/node_modules/mime/types.json b/node_modules/express/node_modules/send/node_modules/mime/types.json new file mode 100644 index 0000000..c674b1c --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/mime/types.json @@ -0,0 +1 @@ +{"application/andrew-inset":["ez"],"application/applixware":["aw"],"application/atom+xml":["atom"],"application/atomcat+xml":["atomcat"],"application/atomsvc+xml":["atomsvc"],"application/ccxml+xml":["ccxml"],"application/cdmi-capability":["cdmia"],"application/cdmi-container":["cdmic"],"application/cdmi-domain":["cdmid"],"application/cdmi-object":["cdmio"],"application/cdmi-queue":["cdmiq"],"application/cu-seeme":["cu"],"application/dash+xml":["mdp"],"application/davmount+xml":["davmount"],"application/docbook+xml":["dbk"],"application/dssc+der":["dssc"],"application/dssc+xml":["xdssc"],"application/ecmascript":["ecma"],"application/emma+xml":["emma"],"application/epub+zip":["epub"],"application/exi":["exi"],"application/font-tdpfr":["pfr"],"application/font-woff":["woff"],"application/font-woff2":["woff2"],"application/gml+xml":["gml"],"application/gpx+xml":["gpx"],"application/gxf":["gxf"],"application/hyperstudio":["stk"],"application/inkml+xml":["ink","inkml"],"application/ipfix":["ipfix"],"application/java-archive":["jar"],"application/java-serialized-object":["ser"],"application/java-vm":["class"],"application/javascript":["js"],"application/json":["json","map"],"application/json5":["json5"],"application/jsonml+json":["jsonml"],"application/lost+xml":["lostxml"],"application/mac-binhex40":["hqx"],"application/mac-compactpro":["cpt"],"application/mads+xml":["mads"],"application/marc":["mrc"],"application/marcxml+xml":["mrcx"],"application/mathematica":["ma","nb","mb"],"application/mathml+xml":["mathml"],"application/mbox":["mbox"],"application/mediaservercontrol+xml":["mscml"],"application/metalink+xml":["metalink"],"application/metalink4+xml":["meta4"],"application/mets+xml":["mets"],"application/mods+xml":["mods"],"application/mp21":["m21","mp21"],"application/mp4":["mp4s","m4p"],"application/msword":["doc","dot"],"application/mxf":["mxf"],"application/octet-stream":["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","buffer"],"application/oda":["oda"],"application/oebps-package+xml":["opf"],"application/ogg":["ogx"],"application/omdoc+xml":["omdoc"],"application/onenote":["onetoc","onetoc2","onetmp","onepkg"],"application/oxps":["oxps"],"application/patch-ops-error+xml":["xer"],"application/pdf":["pdf"],"application/pgp-encrypted":["pgp"],"application/pgp-signature":["asc","sig"],"application/pics-rules":["prf"],"application/pkcs10":["p10"],"application/pkcs7-mime":["p7m","p7c"],"application/pkcs7-signature":["p7s"],"application/pkcs8":["p8"],"application/pkix-attr-cert":["ac"],"application/pkix-cert":["cer"],"application/pkix-crl":["crl"],"application/pkix-pkipath":["pkipath"],"application/pkixcmp":["pki"],"application/pls+xml":["pls"],"application/postscript":["ai","eps","ps"],"application/prs.cww":["cww"],"application/pskc+xml":["pskcxml"],"application/rdf+xml":["rdf"],"application/reginfo+xml":["rif"],"application/relax-ng-compact-syntax":["rnc"],"application/resource-lists+xml":["rl"],"application/resource-lists-diff+xml":["rld"],"application/rls-services+xml":["rs"],"application/rpki-ghostbusters":["gbr"],"application/rpki-manifest":["mft"],"application/rpki-roa":["roa"],"application/rsd+xml":["rsd"],"application/rss+xml":["rss"],"application/rtf":["rtf"],"application/sbml+xml":["sbml"],"application/scvp-cv-request":["scq"],"application/scvp-cv-response":["scs"],"application/scvp-vp-request":["spq"],"application/scvp-vp-response":["spp"],"application/sdp":["sdp"],"application/set-payment-initiation":["setpay"],"application/set-registration-initiation":["setreg"],"application/shf+xml":["shf"],"application/smil+xml":["smi","smil"],"application/sparql-query":["rq"],"application/sparql-results+xml":["srx"],"application/srgs":["gram"],"application/srgs+xml":["grxml"],"application/sru+xml":["sru"],"application/ssdl+xml":["ssdl"],"application/ssml+xml":["ssml"],"application/tei+xml":["tei","teicorpus"],"application/thraud+xml":["tfi"],"application/timestamped-data":["tsd"],"application/vnd.3gpp.pic-bw-large":["plb"],"application/vnd.3gpp.pic-bw-small":["psb"],"application/vnd.3gpp.pic-bw-var":["pvb"],"application/vnd.3gpp2.tcap":["tcap"],"application/vnd.3m.post-it-notes":["pwn"],"application/vnd.accpac.simply.aso":["aso"],"application/vnd.accpac.simply.imp":["imp"],"application/vnd.acucobol":["acu"],"application/vnd.acucorp":["atc","acutc"],"application/vnd.adobe.air-application-installer-package+zip":["air"],"application/vnd.adobe.formscentral.fcdt":["fcdt"],"application/vnd.adobe.fxp":["fxp","fxpl"],"application/vnd.adobe.xdp+xml":["xdp"],"application/vnd.adobe.xfdf":["xfdf"],"application/vnd.ahead.space":["ahead"],"application/vnd.airzip.filesecure.azf":["azf"],"application/vnd.airzip.filesecure.azs":["azs"],"application/vnd.amazon.ebook":["azw"],"application/vnd.americandynamics.acc":["acc"],"application/vnd.amiga.ami":["ami"],"application/vnd.android.package-archive":["apk"],"application/vnd.anser-web-certificate-issue-initiation":["cii"],"application/vnd.anser-web-funds-transfer-initiation":["fti"],"application/vnd.antix.game-component":["atx"],"application/vnd.apple.installer+xml":["mpkg"],"application/vnd.apple.mpegurl":["m3u8"],"application/vnd.aristanetworks.swi":["swi"],"application/vnd.astraea-software.iota":["iota"],"application/vnd.audiograph":["aep"],"application/vnd.blueice.multipass":["mpm"],"application/vnd.bmi":["bmi"],"application/vnd.businessobjects":["rep"],"application/vnd.chemdraw+xml":["cdxml"],"application/vnd.chipnuts.karaoke-mmd":["mmd"],"application/vnd.cinderella":["cdy"],"application/vnd.claymore":["cla"],"application/vnd.cloanto.rp9":["rp9"],"application/vnd.clonk.c4group":["c4g","c4d","c4f","c4p","c4u"],"application/vnd.cluetrust.cartomobile-config":["c11amc"],"application/vnd.cluetrust.cartomobile-config-pkg":["c11amz"],"application/vnd.commonspace":["csp"],"application/vnd.contact.cmsg":["cdbcmsg"],"application/vnd.cosmocaller":["cmc"],"application/vnd.crick.clicker":["clkx"],"application/vnd.crick.clicker.keyboard":["clkk"],"application/vnd.crick.clicker.palette":["clkp"],"application/vnd.crick.clicker.template":["clkt"],"application/vnd.crick.clicker.wordbank":["clkw"],"application/vnd.criticaltools.wbs+xml":["wbs"],"application/vnd.ctc-posml":["pml"],"application/vnd.cups-ppd":["ppd"],"application/vnd.curl.car":["car"],"application/vnd.curl.pcurl":["pcurl"],"application/vnd.dart":["dart"],"application/vnd.data-vision.rdz":["rdz"],"application/vnd.dece.data":["uvf","uvvf","uvd","uvvd"],"application/vnd.dece.ttml+xml":["uvt","uvvt"],"application/vnd.dece.unspecified":["uvx","uvvx"],"application/vnd.dece.zip":["uvz","uvvz"],"application/vnd.denovo.fcselayout-link":["fe_launch"],"application/vnd.dna":["dna"],"application/vnd.dolby.mlp":["mlp"],"application/vnd.dpgraph":["dpg"],"application/vnd.dreamfactory":["dfac"],"application/vnd.ds-keypoint":["kpxx"],"application/vnd.dvb.ait":["ait"],"application/vnd.dvb.service":["svc"],"application/vnd.dynageo":["geo"],"application/vnd.ecowin.chart":["mag"],"application/vnd.enliven":["nml"],"application/vnd.epson.esf":["esf"],"application/vnd.epson.msf":["msf"],"application/vnd.epson.quickanime":["qam"],"application/vnd.epson.salt":["slt"],"application/vnd.epson.ssf":["ssf"],"application/vnd.eszigno3+xml":["es3","et3"],"application/vnd.ezpix-album":["ez2"],"application/vnd.ezpix-package":["ez3"],"application/vnd.fdf":["fdf"],"application/vnd.fdsn.mseed":["mseed"],"application/vnd.fdsn.seed":["seed","dataless"],"application/vnd.flographit":["gph"],"application/vnd.fluxtime.clip":["ftc"],"application/vnd.framemaker":["fm","frame","maker","book"],"application/vnd.frogans.fnc":["fnc"],"application/vnd.frogans.ltf":["ltf"],"application/vnd.fsc.weblaunch":["fsc"],"application/vnd.fujitsu.oasys":["oas"],"application/vnd.fujitsu.oasys2":["oa2"],"application/vnd.fujitsu.oasys3":["oa3"],"application/vnd.fujitsu.oasysgp":["fg5"],"application/vnd.fujitsu.oasysprs":["bh2"],"application/vnd.fujixerox.ddd":["ddd"],"application/vnd.fujixerox.docuworks":["xdw"],"application/vnd.fujixerox.docuworks.binder":["xbd"],"application/vnd.fuzzysheet":["fzs"],"application/vnd.genomatix.tuxedo":["txd"],"application/vnd.geogebra.file":["ggb"],"application/vnd.geogebra.tool":["ggt"],"application/vnd.geometry-explorer":["gex","gre"],"application/vnd.geonext":["gxt"],"application/vnd.geoplan":["g2w"],"application/vnd.geospace":["g3w"],"application/vnd.gmx":["gmx"],"application/vnd.google-earth.kml+xml":["kml"],"application/vnd.google-earth.kmz":["kmz"],"application/vnd.grafeq":["gqf","gqs"],"application/vnd.groove-account":["gac"],"application/vnd.groove-help":["ghf"],"application/vnd.groove-identity-message":["gim"],"application/vnd.groove-injector":["grv"],"application/vnd.groove-tool-message":["gtm"],"application/vnd.groove-tool-template":["tpl"],"application/vnd.groove-vcard":["vcg"],"application/vnd.hal+xml":["hal"],"application/vnd.handheld-entertainment+xml":["zmm"],"application/vnd.hbci":["hbci"],"application/vnd.hhe.lesson-player":["les"],"application/vnd.hp-hpgl":["hpgl"],"application/vnd.hp-hpid":["hpid"],"application/vnd.hp-hps":["hps"],"application/vnd.hp-jlyt":["jlt"],"application/vnd.hp-pcl":["pcl"],"application/vnd.hp-pclxl":["pclxl"],"application/vnd.ibm.minipay":["mpy"],"application/vnd.ibm.modcap":["afp","listafp","list3820"],"application/vnd.ibm.rights-management":["irm"],"application/vnd.ibm.secure-container":["sc"],"application/vnd.iccprofile":["icc","icm"],"application/vnd.igloader":["igl"],"application/vnd.immervision-ivp":["ivp"],"application/vnd.immervision-ivu":["ivu"],"application/vnd.insors.igm":["igm"],"application/vnd.intercon.formnet":["xpw","xpx"],"application/vnd.intergeo":["i2g"],"application/vnd.intu.qbo":["qbo"],"application/vnd.intu.qfx":["qfx"],"application/vnd.ipunplugged.rcprofile":["rcprofile"],"application/vnd.irepository.package+xml":["irp"],"application/vnd.is-xpr":["xpr"],"application/vnd.isac.fcs":["fcs"],"application/vnd.jam":["jam"],"application/vnd.jcp.javame.midlet-rms":["rms"],"application/vnd.jisp":["jisp"],"application/vnd.joost.joda-archive":["joda"],"application/vnd.kahootz":["ktz","ktr"],"application/vnd.kde.karbon":["karbon"],"application/vnd.kde.kchart":["chrt"],"application/vnd.kde.kformula":["kfo"],"application/vnd.kde.kivio":["flw"],"application/vnd.kde.kontour":["kon"],"application/vnd.kde.kpresenter":["kpr","kpt"],"application/vnd.kde.kspread":["ksp"],"application/vnd.kde.kword":["kwd","kwt"],"application/vnd.kenameaapp":["htke"],"application/vnd.kidspiration":["kia"],"application/vnd.kinar":["kne","knp"],"application/vnd.koan":["skp","skd","skt","skm"],"application/vnd.kodak-descriptor":["sse"],"application/vnd.las.las+xml":["lasxml"],"application/vnd.llamagraphics.life-balance.desktop":["lbd"],"application/vnd.llamagraphics.life-balance.exchange+xml":["lbe"],"application/vnd.lotus-1-2-3":["123"],"application/vnd.lotus-approach":["apr"],"application/vnd.lotus-freelance":["pre"],"application/vnd.lotus-notes":["nsf"],"application/vnd.lotus-organizer":["org"],"application/vnd.lotus-screencam":["scm"],"application/vnd.lotus-wordpro":["lwp"],"application/vnd.macports.portpkg":["portpkg"],"application/vnd.mcd":["mcd"],"application/vnd.medcalcdata":["mc1"],"application/vnd.mediastation.cdkey":["cdkey"],"application/vnd.mfer":["mwf"],"application/vnd.mfmp":["mfm"],"application/vnd.micrografx.flo":["flo"],"application/vnd.micrografx.igx":["igx"],"application/vnd.mif":["mif"],"application/vnd.mobius.daf":["daf"],"application/vnd.mobius.dis":["dis"],"application/vnd.mobius.mbk":["mbk"],"application/vnd.mobius.mqy":["mqy"],"application/vnd.mobius.msl":["msl"],"application/vnd.mobius.plc":["plc"],"application/vnd.mobius.txf":["txf"],"application/vnd.mophun.application":["mpn"],"application/vnd.mophun.certificate":["mpc"],"application/vnd.mozilla.xul+xml":["xul"],"application/vnd.ms-artgalry":["cil"],"application/vnd.ms-cab-compressed":["cab"],"application/vnd.ms-excel":["xls","xlm","xla","xlc","xlt","xlw"],"application/vnd.ms-excel.addin.macroenabled.12":["xlam"],"application/vnd.ms-excel.sheet.binary.macroenabled.12":["xlsb"],"application/vnd.ms-excel.sheet.macroenabled.12":["xlsm"],"application/vnd.ms-excel.template.macroenabled.12":["xltm"],"application/vnd.ms-fontobject":["eot"],"application/vnd.ms-htmlhelp":["chm"],"application/vnd.ms-ims":["ims"],"application/vnd.ms-lrm":["lrm"],"application/vnd.ms-officetheme":["thmx"],"application/vnd.ms-pki.seccat":["cat"],"application/vnd.ms-pki.stl":["stl"],"application/vnd.ms-powerpoint":["ppt","pps","pot"],"application/vnd.ms-powerpoint.addin.macroenabled.12":["ppam"],"application/vnd.ms-powerpoint.presentation.macroenabled.12":["pptm"],"application/vnd.ms-powerpoint.slide.macroenabled.12":["sldm"],"application/vnd.ms-powerpoint.slideshow.macroenabled.12":["ppsm"],"application/vnd.ms-powerpoint.template.macroenabled.12":["potm"],"application/vnd.ms-project":["mpp","mpt"],"application/vnd.ms-word.document.macroenabled.12":["docm"],"application/vnd.ms-word.template.macroenabled.12":["dotm"],"application/vnd.ms-works":["wps","wks","wcm","wdb"],"application/vnd.ms-wpl":["wpl"],"application/vnd.ms-xpsdocument":["xps"],"application/vnd.mseq":["mseq"],"application/vnd.musician":["mus"],"application/vnd.muvee.style":["msty"],"application/vnd.mynfc":["taglet"],"application/vnd.neurolanguage.nlu":["nlu"],"application/vnd.nitf":["ntf","nitf"],"application/vnd.noblenet-directory":["nnd"],"application/vnd.noblenet-sealer":["nns"],"application/vnd.noblenet-web":["nnw"],"application/vnd.nokia.n-gage.data":["ngdat"],"application/vnd.nokia.radio-preset":["rpst"],"application/vnd.nokia.radio-presets":["rpss"],"application/vnd.novadigm.edm":["edm"],"application/vnd.novadigm.edx":["edx"],"application/vnd.novadigm.ext":["ext"],"application/vnd.oasis.opendocument.chart":["odc"],"application/vnd.oasis.opendocument.chart-template":["otc"],"application/vnd.oasis.opendocument.database":["odb"],"application/vnd.oasis.opendocument.formula":["odf"],"application/vnd.oasis.opendocument.formula-template":["odft"],"application/vnd.oasis.opendocument.graphics":["odg"],"application/vnd.oasis.opendocument.graphics-template":["otg"],"application/vnd.oasis.opendocument.image":["odi"],"application/vnd.oasis.opendocument.image-template":["oti"],"application/vnd.oasis.opendocument.presentation":["odp"],"application/vnd.oasis.opendocument.presentation-template":["otp"],"application/vnd.oasis.opendocument.spreadsheet":["ods"],"application/vnd.oasis.opendocument.spreadsheet-template":["ots"],"application/vnd.oasis.opendocument.text":["odt"],"application/vnd.oasis.opendocument.text-master":["odm"],"application/vnd.oasis.opendocument.text-template":["ott"],"application/vnd.oasis.opendocument.text-web":["oth"],"application/vnd.olpc-sugar":["xo"],"application/vnd.oma.dd2+xml":["dd2"],"application/vnd.openofficeorg.extension":["oxt"],"application/vnd.openxmlformats-officedocument.presentationml.presentation":["pptx"],"application/vnd.openxmlformats-officedocument.presentationml.slide":["sldx"],"application/vnd.openxmlformats-officedocument.presentationml.slideshow":["ppsx"],"application/vnd.openxmlformats-officedocument.presentationml.template":["potx"],"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":["xlsx"],"application/vnd.openxmlformats-officedocument.spreadsheetml.template":["xltx"],"application/vnd.openxmlformats-officedocument.wordprocessingml.document":["docx"],"application/vnd.openxmlformats-officedocument.wordprocessingml.template":["dotx"],"application/vnd.osgeo.mapguide.package":["mgp"],"application/vnd.osgi.dp":["dp"],"application/vnd.osgi.subsystem":["esa"],"application/vnd.palm":["pdb","pqa","oprc"],"application/vnd.pawaafile":["paw"],"application/vnd.pg.format":["str"],"application/vnd.pg.osasli":["ei6"],"application/vnd.picsel":["efif"],"application/vnd.pmi.widget":["wg"],"application/vnd.pocketlearn":["plf"],"application/vnd.powerbuilder6":["pbd"],"application/vnd.previewsystems.box":["box"],"application/vnd.proteus.magazine":["mgz"],"application/vnd.publishare-delta-tree":["qps"],"application/vnd.pvi.ptid1":["ptid"],"application/vnd.quark.quarkxpress":["qxd","qxt","qwd","qwt","qxl","qxb"],"application/vnd.realvnc.bed":["bed"],"application/vnd.recordare.musicxml":["mxl"],"application/vnd.recordare.musicxml+xml":["musicxml"],"application/vnd.rig.cryptonote":["cryptonote"],"application/vnd.rim.cod":["cod"],"application/vnd.rn-realmedia":["rm"],"application/vnd.rn-realmedia-vbr":["rmvb"],"application/vnd.route66.link66+xml":["link66"],"application/vnd.sailingtracker.track":["st"],"application/vnd.seemail":["see"],"application/vnd.sema":["sema"],"application/vnd.semd":["semd"],"application/vnd.semf":["semf"],"application/vnd.shana.informed.formdata":["ifm"],"application/vnd.shana.informed.formtemplate":["itp"],"application/vnd.shana.informed.interchange":["iif"],"application/vnd.shana.informed.package":["ipk"],"application/vnd.simtech-mindmapper":["twd","twds"],"application/vnd.smaf":["mmf"],"application/vnd.smart.teacher":["teacher"],"application/vnd.solent.sdkm+xml":["sdkm","sdkd"],"application/vnd.spotfire.dxp":["dxp"],"application/vnd.spotfire.sfs":["sfs"],"application/vnd.stardivision.calc":["sdc"],"application/vnd.stardivision.draw":["sda"],"application/vnd.stardivision.impress":["sdd"],"application/vnd.stardivision.math":["smf"],"application/vnd.stardivision.writer":["sdw","vor"],"application/vnd.stardivision.writer-global":["sgl"],"application/vnd.stepmania.package":["smzip"],"application/vnd.stepmania.stepchart":["sm"],"application/vnd.sun.xml.calc":["sxc"],"application/vnd.sun.xml.calc.template":["stc"],"application/vnd.sun.xml.draw":["sxd"],"application/vnd.sun.xml.draw.template":["std"],"application/vnd.sun.xml.impress":["sxi"],"application/vnd.sun.xml.impress.template":["sti"],"application/vnd.sun.xml.math":["sxm"],"application/vnd.sun.xml.writer":["sxw"],"application/vnd.sun.xml.writer.global":["sxg"],"application/vnd.sun.xml.writer.template":["stw"],"application/vnd.sus-calendar":["sus","susp"],"application/vnd.svd":["svd"],"application/vnd.symbian.install":["sis","sisx"],"application/vnd.syncml+xml":["xsm"],"application/vnd.syncml.dm+wbxml":["bdm"],"application/vnd.syncml.dm+xml":["xdm"],"application/vnd.tao.intent-module-archive":["tao"],"application/vnd.tcpdump.pcap":["pcap","cap","dmp"],"application/vnd.tmobile-livetv":["tmo"],"application/vnd.trid.tpt":["tpt"],"application/vnd.triscape.mxs":["mxs"],"application/vnd.trueapp":["tra"],"application/vnd.ufdl":["ufd","ufdl"],"application/vnd.uiq.theme":["utz"],"application/vnd.umajin":["umj"],"application/vnd.unity":["unityweb"],"application/vnd.uoml+xml":["uoml"],"application/vnd.vcx":["vcx"],"application/vnd.visio":["vsd","vst","vss","vsw"],"application/vnd.visionary":["vis"],"application/vnd.vsf":["vsf"],"application/vnd.wap.wbxml":["wbxml"],"application/vnd.wap.wmlc":["wmlc"],"application/vnd.wap.wmlscriptc":["wmlsc"],"application/vnd.webturbo":["wtb"],"application/vnd.wolfram.player":["nbp"],"application/vnd.wordperfect":["wpd"],"application/vnd.wqd":["wqd"],"application/vnd.wt.stf":["stf"],"application/vnd.xara":["xar"],"application/vnd.xfdl":["xfdl"],"application/vnd.yamaha.hv-dic":["hvd"],"application/vnd.yamaha.hv-script":["hvs"],"application/vnd.yamaha.hv-voice":["hvp"],"application/vnd.yamaha.openscoreformat":["osf"],"application/vnd.yamaha.openscoreformat.osfpvg+xml":["osfpvg"],"application/vnd.yamaha.smaf-audio":["saf"],"application/vnd.yamaha.smaf-phrase":["spf"],"application/vnd.yellowriver-custom-menu":["cmp"],"application/vnd.zul":["zir","zirz"],"application/vnd.zzazz.deck+xml":["zaz"],"application/voicexml+xml":["vxml"],"application/widget":["wgt"],"application/winhlp":["hlp"],"application/wsdl+xml":["wsdl"],"application/wspolicy+xml":["wspolicy"],"application/x-7z-compressed":["7z"],"application/x-abiword":["abw"],"application/x-ace-compressed":["ace"],"application/x-apple-diskimage":["dmg"],"application/x-authorware-bin":["aab","x32","u32","vox"],"application/x-authorware-map":["aam"],"application/x-authorware-seg":["aas"],"application/x-bcpio":["bcpio"],"application/x-bittorrent":["torrent"],"application/x-blorb":["blb","blorb"],"application/x-bzip":["bz"],"application/x-bzip2":["bz2","boz"],"application/x-cbr":["cbr","cba","cbt","cbz","cb7"],"application/x-cdlink":["vcd"],"application/x-cfs-compressed":["cfs"],"application/x-chat":["chat"],"application/x-chess-pgn":["pgn"],"application/x-chrome-extension":["crx"],"application/x-conference":["nsc"],"application/x-cpio":["cpio"],"application/x-csh":["csh"],"application/x-debian-package":["deb","udeb"],"application/x-dgc-compressed":["dgc"],"application/x-director":["dir","dcr","dxr","cst","cct","cxt","w3d","fgd","swa"],"application/x-doom":["wad"],"application/x-dtbncx+xml":["ncx"],"application/x-dtbook+xml":["dtb"],"application/x-dtbresource+xml":["res"],"application/x-dvi":["dvi"],"application/x-envoy":["evy"],"application/x-eva":["eva"],"application/x-font-bdf":["bdf"],"application/x-font-ghostscript":["gsf"],"application/x-font-linux-psf":["psf"],"application/x-font-otf":["otf"],"application/x-font-pcf":["pcf"],"application/x-font-snf":["snf"],"application/x-font-ttf":["ttf","ttc"],"application/x-font-type1":["pfa","pfb","pfm","afm"],"application/x-freearc":["arc"],"application/x-futuresplash":["spl"],"application/x-gca-compressed":["gca"],"application/x-glulx":["ulx"],"application/x-gnumeric":["gnumeric"],"application/x-gramps-xml":["gramps"],"application/x-gtar":["gtar"],"application/x-hdf":["hdf"],"application/x-install-instructions":["install"],"application/x-iso9660-image":["iso"],"application/x-java-jnlp-file":["jnlp"],"application/x-latex":["latex"],"application/x-lua-bytecode":["luac"],"application/x-lzh-compressed":["lzh","lha"],"application/x-mie":["mie"],"application/x-mobipocket-ebook":["prc","mobi"],"application/x-ms-application":["application"],"application/x-ms-shortcut":["lnk"],"application/x-ms-wmd":["wmd"],"application/x-ms-wmz":["wmz"],"application/x-ms-xbap":["xbap"],"application/x-msaccess":["mdb"],"application/x-msbinder":["obd"],"application/x-mscardfile":["crd"],"application/x-msclip":["clp"],"application/x-msdownload":["exe","dll","com","bat","msi"],"application/x-msmediaview":["mvb","m13","m14"],"application/x-msmetafile":["wmf","wmz","emf","emz"],"application/x-msmoney":["mny"],"application/x-mspublisher":["pub"],"application/x-msschedule":["scd"],"application/x-msterminal":["trm"],"application/x-mswrite":["wri"],"application/x-netcdf":["nc","cdf"],"application/x-nzb":["nzb"],"application/x-pkcs12":["p12","pfx"],"application/x-pkcs7-certificates":["p7b","spc"],"application/x-pkcs7-certreqresp":["p7r"],"application/x-rar-compressed":["rar"],"application/x-research-info-systems":["ris"],"application/x-sh":["sh"],"application/x-shar":["shar"],"application/x-shockwave-flash":["swf"],"application/x-silverlight-app":["xap"],"application/x-sql":["sql"],"application/x-stuffit":["sit"],"application/x-stuffitx":["sitx"],"application/x-subrip":["srt"],"application/x-sv4cpio":["sv4cpio"],"application/x-sv4crc":["sv4crc"],"application/x-t3vm-image":["t3"],"application/x-tads":["gam"],"application/x-tar":["tar"],"application/x-tcl":["tcl"],"application/x-tex":["tex"],"application/x-tex-tfm":["tfm"],"application/x-texinfo":["texinfo","texi"],"application/x-tgif":["obj"],"application/x-ustar":["ustar"],"application/x-wais-source":["src"],"application/x-web-app-manifest+json":["webapp"],"application/x-x509-ca-cert":["der","crt"],"application/x-xfig":["fig"],"application/x-xliff+xml":["xlf"],"application/x-xpinstall":["xpi"],"application/x-xz":["xz"],"application/x-zmachine":["z1","z2","z3","z4","z5","z6","z7","z8"],"application/xaml+xml":["xaml"],"application/xcap-diff+xml":["xdf"],"application/xenc+xml":["xenc"],"application/xhtml+xml":["xhtml","xht"],"application/xml":["xml","xsl","xsd"],"application/xml-dtd":["dtd"],"application/xop+xml":["xop"],"application/xproc+xml":["xpl"],"application/xslt+xml":["xslt"],"application/xspf+xml":["xspf"],"application/xv+xml":["mxml","xhvml","xvml","xvm"],"application/yang":["yang"],"application/yin+xml":["yin"],"application/zip":["zip"],"audio/adpcm":["adp"],"audio/basic":["au","snd"],"audio/midi":["mid","midi","kar","rmi"],"audio/mp4":["mp4a","m4a"],"audio/mpeg":["mpga","mp2","mp2a","mp3","m2a","m3a"],"audio/ogg":["oga","ogg","spx"],"audio/s3m":["s3m"],"audio/silk":["sil"],"audio/vnd.dece.audio":["uva","uvva"],"audio/vnd.digital-winds":["eol"],"audio/vnd.dra":["dra"],"audio/vnd.dts":["dts"],"audio/vnd.dts.hd":["dtshd"],"audio/vnd.lucent.voice":["lvp"],"audio/vnd.ms-playready.media.pya":["pya"],"audio/vnd.nuera.ecelp4800":["ecelp4800"],"audio/vnd.nuera.ecelp7470":["ecelp7470"],"audio/vnd.nuera.ecelp9600":["ecelp9600"],"audio/vnd.rip":["rip"],"audio/webm":["weba"],"audio/x-aac":["aac"],"audio/x-aiff":["aif","aiff","aifc"],"audio/x-caf":["caf"],"audio/x-flac":["flac"],"audio/x-matroska":["mka"],"audio/x-mpegurl":["m3u"],"audio/x-ms-wax":["wax"],"audio/x-ms-wma":["wma"],"audio/x-pn-realaudio":["ram","ra"],"audio/x-pn-realaudio-plugin":["rmp"],"audio/x-wav":["wav"],"audio/xm":["xm"],"chemical/x-cdx":["cdx"],"chemical/x-cif":["cif"],"chemical/x-cmdf":["cmdf"],"chemical/x-cml":["cml"],"chemical/x-csml":["csml"],"chemical/x-xyz":["xyz"],"font/opentype":["otf"],"image/bmp":["bmp"],"image/cgm":["cgm"],"image/g3fax":["g3"],"image/gif":["gif"],"image/ief":["ief"],"image/jpeg":["jpeg","jpg","jpe"],"image/ktx":["ktx"],"image/png":["png"],"image/prs.btif":["btif"],"image/sgi":["sgi"],"image/svg+xml":["svg","svgz"],"image/tiff":["tiff","tif"],"image/vnd.adobe.photoshop":["psd"],"image/vnd.dece.graphic":["uvi","uvvi","uvg","uvvg"],"image/vnd.djvu":["djvu","djv"],"image/vnd.dvb.subtitle":["sub"],"image/vnd.dwg":["dwg"],"image/vnd.dxf":["dxf"],"image/vnd.fastbidsheet":["fbs"],"image/vnd.fpx":["fpx"],"image/vnd.fst":["fst"],"image/vnd.fujixerox.edmics-mmr":["mmr"],"image/vnd.fujixerox.edmics-rlc":["rlc"],"image/vnd.ms-modi":["mdi"],"image/vnd.ms-photo":["wdp"],"image/vnd.net-fpx":["npx"],"image/vnd.wap.wbmp":["wbmp"],"image/vnd.xiff":["xif"],"image/webp":["webp"],"image/x-3ds":["3ds"],"image/x-cmu-raster":["ras"],"image/x-cmx":["cmx"],"image/x-freehand":["fh","fhc","fh4","fh5","fh7"],"image/x-icon":["ico"],"image/x-mrsid-image":["sid"],"image/x-pcx":["pcx"],"image/x-pict":["pic","pct"],"image/x-portable-anymap":["pnm"],"image/x-portable-bitmap":["pbm"],"image/x-portable-graymap":["pgm"],"image/x-portable-pixmap":["ppm"],"image/x-rgb":["rgb"],"image/x-tga":["tga"],"image/x-xbitmap":["xbm"],"image/x-xpixmap":["xpm"],"image/x-xwindowdump":["xwd"],"message/rfc822":["eml","mime"],"model/iges":["igs","iges"],"model/mesh":["msh","mesh","silo"],"model/vnd.collada+xml":["dae"],"model/vnd.dwf":["dwf"],"model/vnd.gdl":["gdl"],"model/vnd.gtw":["gtw"],"model/vnd.mts":["mts"],"model/vnd.vtu":["vtu"],"model/vrml":["wrl","vrml"],"model/x3d+binary":["x3db","x3dbz"],"model/x3d+vrml":["x3dv","x3dvz"],"model/x3d+xml":["x3d","x3dz"],"text/cache-manifest":["appcache","manifest"],"text/calendar":["ics","ifb"],"text/coffeescript":["coffee"],"text/css":["css"],"text/csv":["csv"],"text/hjson":["hjson"],"text/html":["html","htm"],"text/jade":["jade"],"text/jsx":["jsx"],"text/less":["less"],"text/n3":["n3"],"text/plain":["txt","text","conf","def","list","log","in","ini"],"text/prs.lines.tag":["dsc"],"text/richtext":["rtx"],"text/sgml":["sgml","sgm"],"text/stylus":["stylus","styl"],"text/tab-separated-values":["tsv"],"text/troff":["t","tr","roff","man","me","ms"],"text/turtle":["ttl"],"text/uri-list":["uri","uris","urls"],"text/vcard":["vcard"],"text/vnd.curl":["curl"],"text/vnd.curl.dcurl":["dcurl"],"text/vnd.curl.mcurl":["mcurl"],"text/vnd.curl.scurl":["scurl"],"text/vnd.dvb.subtitle":["sub"],"text/vnd.fly":["fly"],"text/vnd.fmi.flexstor":["flx"],"text/vnd.graphviz":["gv"],"text/vnd.in3d.3dml":["3dml"],"text/vnd.in3d.spot":["spot"],"text/vnd.sun.j2me.app-descriptor":["jad"],"text/vnd.wap.wml":["wml"],"text/vnd.wap.wmlscript":["wmls"],"text/vtt":["vtt"],"text/x-asm":["s","asm"],"text/x-c":["c","cc","cxx","cpp","h","hh","dic"],"text/x-component":["htc"],"text/x-fortran":["f","for","f77","f90"],"text/x-handlebars-template":["hbs"],"text/x-java-source":["java"],"text/x-lua":["lua"],"text/x-markdown":["markdown","md","mkd"],"text/x-nfo":["nfo"],"text/x-opml":["opml"],"text/x-pascal":["p","pas"],"text/x-sass":["sass"],"text/x-scss":["scss"],"text/x-setext":["etx"],"text/x-sfv":["sfv"],"text/x-uuencode":["uu"],"text/x-vcalendar":["vcs"],"text/x-vcard":["vcf"],"text/yaml":["yaml","yml"],"video/3gpp":["3gp"],"video/3gpp2":["3g2"],"video/h261":["h261"],"video/h263":["h263"],"video/h264":["h264"],"video/jpeg":["jpgv"],"video/jpm":["jpm","jpgm"],"video/mj2":["mj2","mjp2"],"video/mp2t":["ts"],"video/mp4":["mp4","mp4v","mpg4"],"video/mpeg":["mpeg","mpg","mpe","m1v","m2v"],"video/ogg":["ogv"],"video/quicktime":["qt","mov"],"video/vnd.dece.hd":["uvh","uvvh"],"video/vnd.dece.mobile":["uvm","uvvm"],"video/vnd.dece.pd":["uvp","uvvp"],"video/vnd.dece.sd":["uvs","uvvs"],"video/vnd.dece.video":["uvv","uvvv"],"video/vnd.dvb.file":["dvb"],"video/vnd.fvt":["fvt"],"video/vnd.mpegurl":["mxu","m4u"],"video/vnd.ms-playready.media.pyv":["pyv"],"video/vnd.uvvu.mp4":["uvu","uvvu"],"video/vnd.vivo":["viv"],"video/webm":["webm"],"video/x-f4v":["f4v"],"video/x-fli":["fli"],"video/x-flv":["flv"],"video/x-m4v":["m4v"],"video/x-matroska":["mkv","mk3d","mks"],"video/x-mng":["mng"],"video/x-ms-asf":["asf","asx"],"video/x-ms-vob":["vob"],"video/x-ms-wm":["wm"],"video/x-ms-wmv":["wmv"],"video/x-ms-wmx":["wmx"],"video/x-ms-wvx":["wvx"],"video/x-msvideo":["avi"],"video/x-sgi-movie":["movie"],"video/x-smv":["smv"],"x-conference/x-cooltalk":["ice"]} diff --git a/node_modules/express/node_modules/send/node_modules/ms/.npmignore b/node_modules/express/node_modules/send/node_modules/ms/.npmignore new file mode 100644 index 0000000..d1aa0ce --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/ms/.npmignore @@ -0,0 +1,5 @@ +node_modules +test +History.md +Makefile +component.json diff --git a/node_modules/express/node_modules/send/node_modules/ms/History.md b/node_modules/express/node_modules/send/node_modules/ms/History.md new file mode 100644 index 0000000..32fdfc1 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/ms/History.md @@ -0,0 +1,66 @@ + +0.7.1 / 2015-04-20 +================== + + * prevent extraordinary long inputs (@evilpacket) + * Fixed broken readme link + +0.7.0 / 2014-11-24 +================== + + * add time abbreviations, updated tests and readme for the new units + * fix example in the readme. + * add LICENSE file + +0.6.2 / 2013-12-05 +================== + + * Adding repository section to package.json to suppress warning from NPM. + +0.6.1 / 2013-05-10 +================== + + * fix singularization [visionmedia] + +0.6.0 / 2013-03-15 +================== + + * fix minutes + +0.5.1 / 2013-02-24 +================== + + * add component namespace + +0.5.0 / 2012-11-09 +================== + + * add short formatting as default and .long option + * add .license property to component.json + * add version to component.json + +0.4.0 / 2012-10-22 +================== + + * add rounding to fix crazy decimals + +0.3.0 / 2012-09-07 +================== + + * fix `ms()` [visionmedia] + +0.2.0 / 2012-09-03 +================== + + * add component.json [visionmedia] + * add days support [visionmedia] + * add hours support [visionmedia] + * add minutes support [visionmedia] + * add seconds support [visionmedia] + * add ms string support [visionmedia] + * refactor tests to facilitate ms(number) [visionmedia] + +0.1.0 / 2012-03-07 +================== + + * Initial release diff --git a/node_modules/express/node_modules/send/node_modules/ms/LICENSE b/node_modules/express/node_modules/send/node_modules/ms/LICENSE new file mode 100644 index 0000000..6c07561 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/ms/LICENSE @@ -0,0 +1,20 @@ +(The MIT License) + +Copyright (c) 2014 Guillermo Rauch + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/send/node_modules/ms/README.md b/node_modules/express/node_modules/send/node_modules/ms/README.md new file mode 100644 index 0000000..9b4fd03 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/ms/README.md @@ -0,0 +1,35 @@ +# ms.js: miliseconds conversion utility + +```js +ms('2 days') // 172800000 +ms('1d') // 86400000 +ms('10h') // 36000000 +ms('2.5 hrs') // 9000000 +ms('2h') // 7200000 +ms('1m') // 60000 +ms('5s') // 5000 +ms('100') // 100 +``` + +```js +ms(60000) // "1m" +ms(2 * 60000) // "2m" +ms(ms('10 hours')) // "10h" +``` + +```js +ms(60000, { long: true }) // "1 minute" +ms(2 * 60000, { long: true }) // "2 minutes" +ms(ms('10 hours'), { long: true }) // "10 hours" +``` + +- Node/Browser compatible. Published as [`ms`](https://www.npmjs.org/package/ms) in [NPM](http://nodejs.org/download). +- If a number is supplied to `ms`, a string with a unit is returned. +- If a string that contains the number is supplied, it returns it as +a number (e.g: it returns `100` for `'100'`). +- If you pass a string with a number and a valid unit, the number of +equivalent ms is returned. + +## License + +MIT diff --git a/node_modules/express/node_modules/send/node_modules/ms/index.js b/node_modules/express/node_modules/send/node_modules/ms/index.js new file mode 100644 index 0000000..4f92771 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/ms/index.js @@ -0,0 +1,125 @@ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} options + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options){ + options = options || {}; + if ('string' == typeof val) return parse(val); + return options.long + ? long(val) + : short(val); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + str = '' + str; + if (str.length > 10000) return; + var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str); + if (!match) return; + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function short(ms) { + if (ms >= d) return Math.round(ms / d) + 'd'; + if (ms >= h) return Math.round(ms / h) + 'h'; + if (ms >= m) return Math.round(ms / m) + 'm'; + if (ms >= s) return Math.round(ms / s) + 's'; + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function long(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) return; + if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; + return Math.ceil(ms / n) + ' ' + name + 's'; +} diff --git a/node_modules/express/node_modules/send/node_modules/ms/package.json b/node_modules/express/node_modules/send/node_modules/ms/package.json new file mode 100644 index 0000000..b12c4a0 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/ms/package.json @@ -0,0 +1,47 @@ +{ + "name": "ms", + "version": "0.7.1", + "description": "Tiny ms conversion utility", + "repository": { + "type": "git", + "url": "git://github.com/guille/ms.js.git" + }, + "main": "./index", + "devDependencies": { + "mocha": "*", + "expect.js": "*", + "serve": "*" + }, + "component": { + "scripts": { + "ms/index.js": "index.js" + } + }, + "gitHead": "713dcf26d9e6fd9dbc95affe7eff9783b7f1b909", + "bugs": { + "url": "https://github.com/guille/ms.js/issues" + }, + "homepage": "https://github.com/guille/ms.js", + "_id": "ms@0.7.1", + "scripts": {}, + "_shasum": "9cd13c03adbff25b65effde7ce864ee952017098", + "_from": "ms@0.7.1", + "_npmVersion": "2.7.5", + "_nodeVersion": "0.12.2", + "_npmUser": { + "name": "rauchg", + "email": "rauchg@gmail.com" + }, + "maintainers": [ + { + "name": "rauchg", + "email": "rauchg@gmail.com" + } + ], + "dist": { + "shasum": "9cd13c03adbff25b65effde7ce864ee952017098", + "tarball": "http://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" +} diff --git a/node_modules/express/node_modules/send/node_modules/statuses/LICENSE b/node_modules/express/node_modules/send/node_modules/statuses/LICENSE new file mode 100644 index 0000000..a7ae8ee --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/statuses/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/express/node_modules/send/node_modules/statuses/README.md b/node_modules/express/node_modules/send/node_modules/statuses/README.md new file mode 100644 index 0000000..f6ae24c --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/statuses/README.md @@ -0,0 +1,114 @@ +# Statuses + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +HTTP status utility for node. + +## API + +```js +var status = require('statuses'); +``` + +### var code = status(Integer || String) + +If `Integer` or `String` is a valid HTTP code or status message, then the appropriate `code` will be returned. Otherwise, an error will be thrown. + +```js +status(403) // => 'Forbidden' +status('403') // => 'Forbidden' +status('forbidden') // => 403 +status('Forbidden') // => 403 +status(306) // throws, as it's not supported by node.js +``` + +### status.codes + +Returns an array of all the status codes as `Integer`s. + +### var msg = status[code] + +Map of `code` to `status message`. `undefined` for invalid `code`s. + +```js +status[404] // => 'Not Found' +``` + +### var code = status[msg] + +Map of `status message` to `code`. `msg` can either be title-cased or lower-cased. `undefined` for invalid `status message`s. + +```js +status['not found'] // => 404 +status['Not Found'] // => 404 +``` + +### status.redirect[code] + +Returns `true` if a status code is a valid redirect status. + +```js +status.redirect[200] // => undefined +status.redirect[301] // => true +``` + +### status.empty[code] + +Returns `true` if a status code expects an empty body. + +```js +status.empty[200] // => undefined +status.empty[204] // => true +status.empty[304] // => true +``` + +### status.retry[code] + +Returns `true` if you should retry the rest. + +```js +status.retry[501] // => undefined +status.retry[503] // => true +``` + +### statuses/codes.json + +```js +var codes = require('statuses/codes.json'); +``` + +This is a JSON file of the status codes +taken from `require('http').STATUS_CODES`. +This is saved so that codes are consistent even in older node.js versions. +For example, `308` will be added in v0.12. + +## Adding Status Codes + +The status codes are primarily sourced from http://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv. +Additionally, custom codes are added from http://en.wikipedia.org/wiki/List_of_HTTP_status_codes. +These are added manually in the `lib/*.json` files. +If you would like to add a status code, add it to the appropriate JSON file. + +To rebuild `codes.json`, run the following: + +```bash +# update src/iana.json +npm run update +# build codes.json +npm run build +``` + +[npm-image]: https://img.shields.io/npm/v/statuses.svg?style=flat +[npm-url]: https://npmjs.org/package/statuses +[node-version-image]: http://img.shields.io/badge/node.js-%3E%3D_0.6-brightgreen.svg?style=flat +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/statuses.svg?style=flat +[travis-url]: https://travis-ci.org/jshttp/statuses +[coveralls-image]: https://img.shields.io/coveralls/jshttp/statuses.svg?style=flat +[coveralls-url]: https://coveralls.io/r/jshttp/statuses?branch=master +[downloads-image]: http://img.shields.io/npm/dm/statuses.svg?style=flat +[downloads-url]: https://npmjs.org/package/statuses diff --git a/node_modules/express/node_modules/send/node_modules/statuses/codes.json b/node_modules/express/node_modules/send/node_modules/statuses/codes.json new file mode 100644 index 0000000..4c45a88 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/statuses/codes.json @@ -0,0 +1,64 @@ +{ + "100": "Continue", + "101": "Switching Protocols", + "102": "Processing", + "200": "OK", + "201": "Created", + "202": "Accepted", + "203": "Non-Authoritative Information", + "204": "No Content", + "205": "Reset Content", + "206": "Partial Content", + "207": "Multi-Status", + "208": "Already Reported", + "226": "IM Used", + "300": "Multiple Choices", + "301": "Moved Permanently", + "302": "Found", + "303": "See Other", + "304": "Not Modified", + "305": "Use Proxy", + "306": "(Unused)", + "307": "Temporary Redirect", + "308": "Permanent Redirect", + "400": "Bad Request", + "401": "Unauthorized", + "402": "Payment Required", + "403": "Forbidden", + "404": "Not Found", + "405": "Method Not Allowed", + "406": "Not Acceptable", + "407": "Proxy Authentication Required", + "408": "Request Timeout", + "409": "Conflict", + "410": "Gone", + "411": "Length Required", + "412": "Precondition Failed", + "413": "Payload Too Large", + "414": "URI Too Long", + "415": "Unsupported Media Type", + "416": "Range Not Satisfiable", + "417": "Expectation Failed", + "418": "I'm a teapot", + "422": "Unprocessable Entity", + "423": "Locked", + "424": "Failed Dependency", + "425": "Unordered Collection", + "426": "Upgrade Required", + "428": "Precondition Required", + "429": "Too Many Requests", + "431": "Request Header Fields Too Large", + "451": "Unavailable For Legal Reasons", + "500": "Internal Server Error", + "501": "Not Implemented", + "502": "Bad Gateway", + "503": "Service Unavailable", + "504": "Gateway Timeout", + "505": "HTTP Version Not Supported", + "506": "Variant Also Negotiates", + "507": "Insufficient Storage", + "508": "Loop Detected", + "509": "Bandwidth Limit Exceeded", + "510": "Not Extended", + "511": "Network Authentication Required" +} \ No newline at end of file diff --git a/node_modules/express/node_modules/send/node_modules/statuses/index.js b/node_modules/express/node_modules/send/node_modules/statuses/index.js new file mode 100644 index 0000000..b06182d --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/statuses/index.js @@ -0,0 +1,60 @@ + +var codes = require('./codes.json'); + +module.exports = status; + +// [Integer...] +status.codes = Object.keys(codes).map(function (code) { + code = ~~code; + var msg = codes[code]; + status[code] = msg; + status[msg] = status[msg.toLowerCase()] = code; + return code; +}); + +// status codes for redirects +status.redirect = { + 300: true, + 301: true, + 302: true, + 303: true, + 305: true, + 307: true, + 308: true, +}; + +// status codes for empty bodies +status.empty = { + 204: true, + 205: true, + 304: true, +}; + +// status codes for when you should retry the request +status.retry = { + 502: true, + 503: true, + 504: true, +}; + +function status(code) { + if (typeof code === 'number') { + if (!status[code]) throw new Error('invalid status code: ' + code); + return code; + } + + if (typeof code !== 'string') { + throw new TypeError('code must be a number or string'); + } + + // '403' + var n = parseInt(code, 10) + if (!isNaN(n)) { + if (!status[n]) throw new Error('invalid status code: ' + n); + return n; + } + + n = status[code.toLowerCase()]; + if (!n) throw new Error('invalid status message: "' + code + '"'); + return n; +} diff --git a/node_modules/express/node_modules/send/node_modules/statuses/package.json b/node_modules/express/node_modules/send/node_modules/statuses/package.json new file mode 100644 index 0000000..f542bd1 --- /dev/null +++ b/node_modules/express/node_modules/send/node_modules/statuses/package.json @@ -0,0 +1,83 @@ +{ + "name": "statuses", + "description": "HTTP status utility", + "version": "1.2.1", + "author": { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/jshttp/statuses" + }, + "license": "MIT", + "keywords": [ + "http", + "status", + "code" + ], + "files": [ + "index.js", + "codes.json", + "LICENSE" + ], + "devDependencies": { + "csv-parse": "0.0.6", + "istanbul": "0", + "mocha": "1", + "stream-to-array": "2" + }, + "scripts": { + "build": "node scripts/build.js", + "update": "node scripts/update.js", + "test": "mocha --reporter spec --bail --check-leaks", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks" + }, + "gitHead": "49e6ac7ae4c63ee8186f56cb52112a7eeda28ed7", + "bugs": { + "url": "https://github.com/jshttp/statuses/issues" + }, + "homepage": "https://github.com/jshttp/statuses", + "_id": "statuses@1.2.1", + "_shasum": "dded45cc18256d51ed40aec142489d5c61026d28", + "_from": "statuses@>=1.2.1 <1.3.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "shtylman", + "email": "shtylman@gmail.com" + }, + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + } + ], + "dist": { + "shasum": "dded45cc18256d51ed40aec142489d5c61026d28", + "tarball": "http://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" +} diff --git a/node_modules/express/node_modules/send/package.json b/node_modules/express/node_modules/send/package.json new file mode 100644 index 0000000..bfe7697 --- /dev/null +++ b/node_modules/express/node_modules/send/package.json @@ -0,0 +1,88 @@ +{ + "name": "send", + "description": "Better streaming static file server with Range and conditional-GET support", + "version": "0.13.0", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/pillarjs/send" + }, + "keywords": [ + "static", + "file", + "server" + ], + "dependencies": { + "debug": "~2.2.0", + "depd": "~1.0.1", + "destroy": "1.0.3", + "escape-html": "1.0.2", + "etag": "~1.7.0", + "fresh": "0.3.0", + "http-errors": "~1.3.1", + "mime": "1.3.4", + "ms": "0.7.1", + "on-finished": "~2.3.0", + "range-parser": "~1.0.2", + "statuses": "~1.2.1" + }, + "devDependencies": { + "after": "0.8.1", + "istanbul": "0.3.9", + "mocha": "2.2.5", + "supertest": "1.0.1" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "README.md", + "index.js" + ], + "engines": { + "node": ">= 0.8.0" + }, + "scripts": { + "test": "mocha --check-leaks --reporter spec --bail", + "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --check-leaks --reporter spec", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --check-leaks --reporter dot" + }, + "gitHead": "80cfa7f54ce87c75e92619d5bc510406bd69133a", + "bugs": { + "url": "https://github.com/pillarjs/send/issues" + }, + "homepage": "https://github.com/pillarjs/send", + "_id": "send@0.13.0", + "_shasum": "518f921aeb0560aec7dcab2990b14cf6f3cce5de", + "_from": "send@0.13.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "518f921aeb0560aec7dcab2990b14cf6f3cce5de", + "tarball": "http://registry.npmjs.org/send/-/send-0.13.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/send/-/send-0.13.0.tgz" +} diff --git a/node_modules/express/node_modules/serve-static/HISTORY.md b/node_modules/express/node_modules/serve-static/HISTORY.md new file mode 100644 index 0000000..744b6f1 --- /dev/null +++ b/node_modules/express/node_modules/serve-static/HISTORY.md @@ -0,0 +1,284 @@ +1.10.0 / 2015-06-17 +=================== + + * Add `fallthrough` option + - Allows declaring this middleware is the final destination + - Provides better integration with Express patterns + * Fix reading options from options prototype + * Improve the default redirect response headers + * deps: escape-html@1.0.2 + * deps: send@0.13.0 + - Allow Node.js HTTP server to set `Date` response header + - Fix incorrectly removing `Content-Location` on 304 response + - Improve the default redirect response headers + - Send appropriate headers on default error response + - Use `http-errors` for standard emitted errors + - Use `statuses` instead of `http` module for status messages + - deps: escape-html@1.0.2 + - deps: etag@~1.7.0 + - deps: fresh@0.3.0 + - deps: on-finished@~2.3.0 + - perf: enable strict mode + - perf: remove unnecessary array allocations + * perf: enable strict mode + * perf: remove argument reassignment + +1.9.3 / 2015-05-14 +================== + + * deps: send@0.12.3 + - deps: debug@~2.2.0 + - deps: depd@~1.0.1 + - deps: etag@~1.6.0 + - deps: ms@0.7.1 + - deps: on-finished@~2.2.1 + +1.9.2 / 2015-03-14 +================== + + * deps: send@0.12.2 + - Throw errors early for invalid `extensions` or `index` options + - deps: debug@~2.1.3 + +1.9.1 / 2015-02-17 +================== + + * deps: send@0.12.1 + - Fix regression sending zero-length files + +1.9.0 / 2015-02-16 +================== + + * deps: send@0.12.0 + - Always read the stat size from the file + - Fix mutating passed-in `options` + - deps: mime@1.3.4 + +1.8.1 / 2015-01-20 +================== + + * Fix redirect loop in Node.js 0.11.14 + * deps: send@0.11.1 + - Fix root path disclosure + +1.8.0 / 2015-01-05 +================== + + * deps: send@0.11.0 + - deps: debug@~2.1.1 + - deps: etag@~1.5.1 + - deps: ms@0.7.0 + - deps: on-finished@~2.2.0 + +1.7.2 / 2015-01-02 +================== + + * Fix potential open redirect when mounted at root + +1.7.1 / 2014-10-22 +================== + + * deps: send@0.10.1 + - deps: on-finished@~2.1.1 + +1.7.0 / 2014-10-15 +================== + + * deps: send@0.10.0 + - deps: debug@~2.1.0 + - deps: depd@~1.0.0 + - deps: etag@~1.5.0 + +1.6.5 / 2015-02-04 +================== + + * Fix potential open redirect when mounted at root + - Back-ported from v1.7.2 + +1.6.4 / 2014-10-08 +================== + + * Fix redirect loop when index file serving disabled + +1.6.3 / 2014-09-24 +================== + + * deps: send@0.9.3 + - deps: etag@~1.4.0 + +1.6.2 / 2014-09-15 +================== + + * deps: send@0.9.2 + - deps: depd@0.4.5 + - deps: etag@~1.3.1 + - deps: range-parser@~1.0.2 + +1.6.1 / 2014-09-07 +================== + + * deps: send@0.9.1 + - deps: fresh@0.2.4 + +1.6.0 / 2014-09-07 +================== + + * deps: send@0.9.0 + - Add `lastModified` option + - Use `etag` to generate `ETag` header + - deps: debug@~2.0.0 + +1.5.4 / 2014-09-04 +================== + + * deps: send@0.8.5 + - Fix a path traversal issue when using `root` + - Fix malicious path detection for empty string path + +1.5.3 / 2014-08-17 +================== + + * deps: send@0.8.3 + +1.5.2 / 2014-08-14 +================== + + * deps: send@0.8.2 + - Work around `fd` leak in Node.js 0.10 for `fs.ReadStream` + +1.5.1 / 2014-08-09 +================== + + * Fix parsing of weird `req.originalUrl` values + * deps: parseurl@~1.3.0 + * deps: utils-merge@1.0.0 + +1.5.0 / 2014-08-05 +================== + + * deps: send@0.8.1 + - Add `extensions` option + +1.4.4 / 2014-08-04 +================== + + * deps: send@0.7.4 + - Fix serving index files without root dir + +1.4.3 / 2014-07-29 +================== + + * deps: send@0.7.3 + - Fix incorrect 403 on Windows and Node.js 0.11 + +1.4.2 / 2014-07-27 +================== + + * deps: send@0.7.2 + - deps: depd@0.4.4 + +1.4.1 / 2014-07-26 +================== + + * deps: send@0.7.1 + - deps: depd@0.4.3 + +1.4.0 / 2014-07-21 +================== + + * deps: parseurl@~1.2.0 + - Cache URLs based on original value + - Remove no-longer-needed URL mis-parse work-around + - Simplify the "fast-path" `RegExp` + * deps: send@0.7.0 + - Add `dotfiles` option + - deps: debug@1.0.4 + - deps: depd@0.4.2 + +1.3.2 / 2014-07-11 +================== + + * deps: send@0.6.0 + - Cap `maxAge` value to 1 year + - deps: debug@1.0.3 + +1.3.1 / 2014-07-09 +================== + + * deps: parseurl@~1.1.3 + - faster parsing of href-only URLs + +1.3.0 / 2014-06-28 +================== + + * Add `setHeaders` option + * Include HTML link in redirect response + * deps: send@0.5.0 + - Accept string for `maxAge` (converted by `ms`) + +1.2.3 / 2014-06-11 +================== + + * deps: send@0.4.3 + - Do not throw un-catchable error on file open race condition + - Use `escape-html` for HTML escaping + - deps: debug@1.0.2 + - deps: finished@1.2.2 + - deps: fresh@0.2.2 + +1.2.2 / 2014-06-09 +================== + + * deps: send@0.4.2 + - fix "event emitter leak" warnings + - deps: debug@1.0.1 + - deps: finished@1.2.1 + +1.2.1 / 2014-06-02 +================== + + * use `escape-html` for escaping + * deps: send@0.4.1 + - Send `max-age` in `Cache-Control` in correct format + +1.2.0 / 2014-05-29 +================== + + * deps: send@0.4.0 + - Calculate ETag with md5 for reduced collisions + - Fix wrong behavior when index file matches directory + - Ignore stream errors after request ends + - Skip directories in index file search + - deps: debug@0.8.1 + +1.1.0 / 2014-04-24 +================== + + * Accept options directly to `send` module + * deps: send@0.3.0 + +1.0.4 / 2014-04-07 +================== + + * Resolve relative paths at middleware setup + * Use parseurl to parse the URL from request + +1.0.3 / 2014-03-20 +================== + + * Do not rely on connect-like environments + +1.0.2 / 2014-03-06 +================== + + * deps: send@0.2.0 + +1.0.1 / 2014-03-05 +================== + + * Add mime export for back-compat + +1.0.0 / 2014-03-05 +================== + + * Genesis from `connect` diff --git a/node_modules/express/node_modules/serve-static/LICENSE b/node_modules/express/node_modules/serve-static/LICENSE new file mode 100644 index 0000000..d8cce67 --- /dev/null +++ b/node_modules/express/node_modules/serve-static/LICENSE @@ -0,0 +1,25 @@ +(The MIT License) + +Copyright (c) 2010 Sencha Inc. +Copyright (c) 2011 LearnBoost +Copyright (c) 2011 TJ Holowaychuk +Copyright (c) 2014-2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/serve-static/README.md b/node_modules/express/node_modules/serve-static/README.md new file mode 100644 index 0000000..1a7b054 --- /dev/null +++ b/node_modules/express/node_modules/serve-static/README.md @@ -0,0 +1,235 @@ +# serve-static + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Linux Build][travis-image]][travis-url] +[![Windows Build][appveyor-image]][appveyor-url] +[![Test Coverage][coveralls-image]][coveralls-url] +[![Gratipay][gratipay-image]][gratipay-url] + +## Install + +```sh +$ npm install serve-static +``` + +## API + +```js +var serveStatic = require('serve-static') +``` + +### serveStatic(root, options) + +Create a new middleware function to serve files from within a given root +directory. The file to serve will be determined by combining `req.url` +with the provided root directory. When a file is not found, instead of +sending a 404 response, this module will instead call `next()` to move on +to the next middleware, allowing for stacking and fall-backs. + +#### Options + +##### dotfiles + + Set how "dotfiles" are treated when encountered. A dotfile is a file +or directory that begins with a dot ("."). Note this check is done on +the path itself without checking if the path actually exists on the +disk. If `root` is specified, only the dotfiles above the root are +checked (i.e. the root itself can be within a dotfile when set +to "deny"). + +The default value is `'ignore'`. + + - `'allow'` No special treatment for dotfiles. + - `'deny'` Deny a request for a dotfile and 403/`next()`. + - `'ignore'` Pretend like the dotfile does not exist and 404/`next()`. + +##### etag + +Enable or disable etag generation, defaults to true. + +##### extensions + +Set file extension fallbacks. When set, if a file is not found, the given +extensions will be added to the file name and search for. The first that +exists will be served. Example: `['html', 'htm']`. + +The default value is `false`. + +##### fallthrough + +Set the middleware to have client errors fall-through as just unhandled +requests, otherwise forward a client error. The difference is that client +errors like a bad request or a request to a non-existent file will cause +this middleware to simply `next()` to your next middleware when this value +is `true`. When this value is `false`, these errors (even 404s), will invoke +`next(err)`. + +Typically `true` is desired such that multiple physical directories can be +mapped to the same web address or for routes to fill in non-existent files. + +The value `false` can be used if this middleware is mounted at a path that +is designed to be strictly a single file system directory, which allows for +short-circuiting 404s for less overhead. This middleware will also reply to +all methods. + +The default value is `true`. + +##### index + +By default this module will send "index.html" files in response to a request +on a directory. To disable this set `false` or to supply a new index pass a +string or an array in preferred order. + +##### lastModified + +Enable or disable `Last-Modified` header, defaults to true. Uses the file +system's last modified value. + +##### maxAge + +Provide a max-age in milliseconds for http caching, defaults to 0. This +can also be a string accepted by the [ms](https://www.npmjs.org/package/ms#readme) +module. + +##### redirect + +Redirect to trailing "/" when the pathname is a dir. Defaults to `true`. + +##### setHeaders + +Function to set custom headers on response. Alterations to the headers need to +occur synchronously. The function is called as `fn(res, path, stat)`, where +the arguments are: + + - `res` the response object + - `path` the file path that is being sent + - `stat` the stat object of the file that is being sent + +## Examples + +### Serve files with vanilla node.js http server + +```js +var finalhandler = require('finalhandler') +var http = require('http') +var serveStatic = require('serve-static') + +// Serve up public/ftp folder +var serve = serveStatic('public/ftp', {'index': ['index.html', 'index.htm']}) + +// Create server +var server = http.createServer(function(req, res){ + var done = finalhandler(req, res) + serve(req, res, done) +}) + +// Listen +server.listen(3000) +``` + +### Serve all files as downloads + +```js +var contentDisposition = require('content-disposition') +var finalhandler = require('finalhandler') +var http = require('http') +var serveStatic = require('serve-static') + +// Serve up public/ftp folder +var serve = serveStatic('public/ftp', { + 'index': false, + 'setHeaders': setHeaders +}) + +// Set header to force download +function setHeaders(res, path) { + res.setHeader('Content-Disposition', contentDisposition(path)) +} + +// Create server +var server = http.createServer(function(req, res){ + var done = finalhandler(req, res) + serve(req, res, done) +}) + +// Listen +server.listen(3000) +``` + +### Serving using express + +#### Simple + +This is a simple example of using Express. + +```js +var express = require('express') +var serveStatic = require('serve-static') + +var app = express() + +app.use(serveStatic('public/ftp', {'index': ['default.html', 'default.htm']})) +app.listen(3000) +``` + +#### Multiple roots + +This example shows a simple way to search through multiple directories. +Files are look for in `public-optimized/` first, then `public/` second as +a fallback. + +```js +var express = require('express') +var serveStatic = require('serve-static') + +var app = express() + +app.use(serveStatic(__dirname + '/public-optimized')) +app.use(serveStatic(__dirname + '/public')) +app.listen(3000) +``` + +#### Different settings for paths + +This example shows how to set a different max age depending on the served +file type. In this example, HTML files are not cached, while everything else +is for 1 day. + +```js +var express = require('express') +var serveStatic = require('serve-static') + +var app = express() + +app.use(serveStatic(__dirname + '/public', { + maxAge: '1d', + setHeaders: setCustomCacheControl +})) + +app.listen(3000) + +function setCustomCacheControl(res, path) { + if (serveStatic.mime.lookup(path) === 'text/html') { + // Custom Cache-Control for HTML files + res.setHeader('Cache-Control', 'public, max-age=0') + } +} +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/serve-static.svg +[npm-url]: https://npmjs.org/package/serve-static +[travis-image]: https://img.shields.io/travis/expressjs/serve-static/master.svg?label=linux +[travis-url]: https://travis-ci.org/expressjs/serve-static +[appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/serve-static/master.svg?label=windows +[appveyor-url]: https://ci.appveyor.com/project/dougwilson/serve-static +[coveralls-image]: https://img.shields.io/coveralls/expressjs/serve-static/master.svg +[coveralls-url]: https://coveralls.io/r/expressjs/serve-static +[downloads-image]: https://img.shields.io/npm/dm/serve-static.svg +[downloads-url]: https://npmjs.org/package/serve-static +[gratipay-image]: https://img.shields.io/gratipay/dougwilson.svg +[gratipay-url]: https://gratipay.com/dougwilson/ diff --git a/node_modules/express/node_modules/serve-static/index.js b/node_modules/express/node_modules/serve-static/index.js new file mode 100644 index 0000000..0a9f494 --- /dev/null +++ b/node_modules/express/node_modules/serve-static/index.js @@ -0,0 +1,187 @@ +/*! + * serve-static + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module dependencies. + * @private + */ + +var escapeHtml = require('escape-html') +var parseUrl = require('parseurl') +var resolve = require('path').resolve +var send = require('send') +var url = require('url') + +/** + * Module exports. + * @public + */ + +module.exports = serveStatic +module.exports.mime = send.mime + +/** + * @param {string} root + * @param {object} [options] + * @return {function} + * @public + */ + +function serveStatic(root, options) { + if (!root) { + throw new TypeError('root path required') + } + + if (typeof root !== 'string') { + throw new TypeError('root path must be a string') + } + + // copy options object + var opts = Object.create(options || null) + + // fall-though + var fallthrough = opts.fallthrough !== false + + // default redirect + var redirect = opts.redirect !== false + + // headers listener + var setHeaders = opts.setHeaders + + if (setHeaders && typeof setHeaders !== 'function') { + throw new TypeError('option setHeaders must be function') + } + + // setup options for send + opts.maxage = opts.maxage || opts.maxAge || 0 + opts.root = resolve(root) + + // construct directory listener + var onDirectory = redirect + ? createRedirectDirectoryListener() + : createNotFoundDirectoryListener() + + return function serveStatic(req, res, next) { + if (req.method !== 'GET' && req.method !== 'HEAD') { + if (fallthrough) { + return next() + } + + // method not allowed + res.statusCode = 405 + res.setHeader('Allow', 'GET, HEAD') + res.setHeader('Content-Length', '0') + res.end() + return + } + + var forwardError = !fallthrough + var originalUrl = parseUrl.original(req) + var path = parseUrl(req).pathname + + // make sure redirect occurs at mount + if (path === '/' && originalUrl.pathname.substr(-1) !== '/') { + path = '' + } + + // create send stream + var stream = send(req, path, opts) + + // add directory handler + stream.on('directory', onDirectory) + + // add headers listener + if (setHeaders) { + stream.on('headers', setHeaders) + } + + // add file listener for fallthrough + if (fallthrough) { + stream.on('file', function onFile() { + // once file is determined, always forward error + forwardError = true + }) + } + + // forward errors + stream.on('error', function error(err) { + if (forwardError || !(err.statusCode < 500)) { + next(err) + return + } + + next() + }) + + // pipe + stream.pipe(res) + } +} + +/** + * Collapse all leading slashes into a single slash + * @private + */ +function collapseLeadingSlashes(str) { + for (var i = 0; i < str.length; i++) { + if (str[i] !== '/') { + break + } + } + + return i > 1 + ? '/' + str.substr(i) + : str +} + +/** + * Create a directory listener that just 404s. + * @private + */ + +function createNotFoundDirectoryListener() { + return function notFound() { + this.error(404) + } +} + +/** + * Create a directory listener that performs a redirect. + * @private + */ + +function createRedirectDirectoryListener() { + return function redirect() { + if (this.hasTrailingSlash()) { + this.error(404) + return + } + + // get original URL + var originalUrl = parseUrl.original(this.req) + + // append trailing slash + originalUrl.path = null + originalUrl.pathname = collapseLeadingSlashes(originalUrl.pathname + '/') + + // reformat the URL + var loc = url.format(originalUrl) + var msg = 'Redirecting to ' + escapeHtml(loc) + '\n' + var res = this.res + + // send redirect response + res.statusCode = 303 + res.setHeader('Content-Type', 'text/html; charset=UTF-8') + res.setHeader('Content-Length', Buffer.byteLength(msg)) + res.setHeader('X-Content-Type-Options', 'nosniff') + res.setHeader('Location', loc) + res.end(msg) + } +} diff --git a/node_modules/express/node_modules/serve-static/package.json b/node_modules/express/node_modules/serve-static/package.json new file mode 100644 index 0000000..c7ac408 --- /dev/null +++ b/node_modules/express/node_modules/serve-static/package.json @@ -0,0 +1,82 @@ +{ + "name": "serve-static", + "description": "Serve static files", + "version": "1.10.0", + "author": { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/expressjs/serve-static" + }, + "dependencies": { + "escape-html": "1.0.2", + "parseurl": "~1.3.0", + "send": "0.13.0" + }, + "devDependencies": { + "istanbul": "0.3.9", + "mocha": "2.2.5", + "supertest": "1.0.1" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "index.js" + ], + "engines": { + "node": ">= 0.8.0" + }, + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/" + }, + "gitHead": "856c5e0f796a8988525c356018594bfb8c51a4fa", + "bugs": { + "url": "https://github.com/expressjs/serve-static/issues" + }, + "homepage": "https://github.com/expressjs/serve-static", + "_id": "serve-static@1.10.0", + "_shasum": "be632faa685820e4a43ed3df1379135cc4f370d7", + "_from": "serve-static@>=1.10.0 <1.11.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + }, + { + "name": "defunctzombie", + "email": "shtylman@gmail.com" + } + ], + "dist": { + "shasum": "be632faa685820e4a43ed3df1379135cc4f370d7", + "tarball": "http://registry.npmjs.org/serve-static/-/serve-static-1.10.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.10.0.tgz" +} diff --git a/node_modules/express/node_modules/type-is/HISTORY.md b/node_modules/express/node_modules/type-is/HISTORY.md new file mode 100644 index 0000000..fa39ff9 --- /dev/null +++ b/node_modules/express/node_modules/type-is/HISTORY.md @@ -0,0 +1,180 @@ +1.6.9 / 2015-09-27 +================== + + * deps: mime-types@~2.1.7 + - Add new mime types + +1.6.8 / 2015-09-04 +================== + + * deps: mime-types@~2.1.6 + - Add new mime types + +1.6.7 / 2015-08-20 +================== + + * Fix type error when given invalid type to match against + * deps: mime-types@~2.1.5 + - Add new mime types + +1.6.6 / 2015-07-31 +================== + + * deps: mime-types@~2.1.4 + - Add new mime types + +1.6.5 / 2015-07-16 +================== + + * deps: mime-types@~2.1.3 + - Add new mime types + +1.6.4 / 2015-07-01 +================== + + * deps: mime-types@~2.1.2 + - Add new mime types + * perf: enable strict mode + * perf: remove argument reassignment + +1.6.3 / 2015-06-08 +================== + + * deps: mime-types@~2.1.1 + - Add new mime types + * perf: reduce try block size + * perf: remove bitwise operations + +1.6.2 / 2015-05-10 +================== + + * deps: mime-types@~2.0.11 + - Add new mime types + +1.6.1 / 2015-03-13 +================== + + * deps: mime-types@~2.0.10 + - Add new mime types + +1.6.0 / 2015-02-12 +================== + + * fix false-positives in `hasBody` `Transfer-Encoding` check + * support wildcard for both type and subtype (`*/*`) + +1.5.7 / 2015-02-09 +================== + + * fix argument reassignment + * deps: mime-types@~2.0.9 + - Add new mime types + +1.5.6 / 2015-01-29 +================== + + * deps: mime-types@~2.0.8 + - Add new mime types + +1.5.5 / 2014-12-30 +================== + + * deps: mime-types@~2.0.7 + - Add new mime types + - Fix missing extensions + - Fix various invalid MIME type entries + - Remove example template MIME types + - deps: mime-db@~1.5.0 + +1.5.4 / 2014-12-10 +================== + + * deps: mime-types@~2.0.4 + - Add new mime types + - deps: mime-db@~1.3.0 + +1.5.3 / 2014-11-09 +================== + + * deps: mime-types@~2.0.3 + - Add new mime types + - deps: mime-db@~1.2.0 + +1.5.2 / 2014-09-28 +================== + + * deps: mime-types@~2.0.2 + - Add new mime types + - deps: mime-db@~1.1.0 + +1.5.1 / 2014-09-07 +================== + + * Support Node.js 0.6 + * deps: media-typer@0.3.0 + * deps: mime-types@~2.0.1 + - Support Node.js 0.6 + +1.5.0 / 2014-09-05 +================== + + * fix `hasbody` to be true for `content-length: 0` + +1.4.0 / 2014-09-02 +================== + + * update mime-types + +1.3.2 / 2014-06-24 +================== + + * use `~` range on mime-types + +1.3.1 / 2014-06-19 +================== + + * fix global variable leak + +1.3.0 / 2014-06-19 +================== + + * improve type parsing + + - invalid media type never matches + - media type not case-sensitive + - extra LWS does not affect results + +1.2.2 / 2014-06-19 +================== + + * fix behavior on unknown type argument + +1.2.1 / 2014-06-03 +================== + + * switch dependency from `mime` to `mime-types@1.0.0` + +1.2.0 / 2014-05-11 +================== + + * support suffix matching: + + - `+json` matches `application/vnd+json` + - `*/vnd+json` matches `application/vnd+json` + - `application/*+json` matches `application/vnd+json` + +1.1.0 / 2014-04-12 +================== + + * add non-array values support + * expose internal utilities: + + - `.is()` + - `.hasBody()` + - `.normalize()` + - `.match()` + +1.0.1 / 2014-03-30 +================== + + * add `multipart` as a shorthand diff --git a/node_modules/express/node_modules/type-is/LICENSE b/node_modules/express/node_modules/type-is/LICENSE new file mode 100644 index 0000000..386b7b6 --- /dev/null +++ b/node_modules/express/node_modules/type-is/LICENSE @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2014-2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/type-is/README.md b/node_modules/express/node_modules/type-is/README.md new file mode 100644 index 0000000..f75f6be --- /dev/null +++ b/node_modules/express/node_modules/type-is/README.md @@ -0,0 +1,132 @@ +# type-is + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Infer the content-type of a request. + +### Install + +```sh +$ npm install type-is +``` + +## API + +```js +var http = require('http') +var is = require('type-is') + +http.createServer(function (req, res) { + var istext = is(req, ['text/*']) + res.end('you ' + (istext ? 'sent' : 'did not send') + ' me text') +}) +``` + +### type = is(request, types) + +`request` is the node HTTP request. `types` is an array of types. + +```js +// req.headers.content-type = 'application/json' + +is(req, ['json']) // 'json' +is(req, ['html', 'json']) // 'json' +is(req, ['application/*']) // 'application/json' +is(req, ['application/json']) // 'application/json' + +is(req, ['html']) // false +``` + +### is.hasBody(request) + +Returns a Boolean if the given `request` has a body, regardless of the +`Content-Type` header. + +```js +if (is.hasBody(req)) { + // read the body, since there is one + + req.on('data', function (chunk) { + // ... + }) +} +``` + +### type = is.is(mediaType, types) + +`mediaType` is the [media type](https://tools.ietf.org/html/rfc6838) string. `types` is an array of types. + +```js +var mediaType = 'application/json' + +is.is(mediaType, ['json']) // 'json' +is.is(mediaType, ['html', 'json']) // 'json' +is.is(mediaType, ['application/*']) // 'application/json' +is.is(mediaType, ['application/json']) // 'application/json' + +is.is(mediaType, ['html']) // false +``` + +### Each type can be: + +- An extension name such as `json`. This name will be returned if matched. +- A mime type such as `application/json`. +- A mime type with a wildcard such as `*/*` or `*/json` or `application/*`. The full mime type will be returned if matched. +- A suffix such as `+json`. This can be combined with a wildcard such as `*/vnd+json` or `application/*+json`. The full mime type will be returned if matched. + +`false` will be returned if no type matches or the content type is invalid. + +`null` will be returned if the request does not have a body. + +## Examples + +#### Example body parser + +```js +var is = require('type-is'); + +function bodyParser(req, res, next) { + if (!is.hasBody(req)) { + return next() + } + + switch (is(req, ['urlencoded', 'json', 'multipart'])) { + case 'urlencoded': + // parse urlencoded body + throw new Error('implement urlencoded body parsing') + break + case 'json': + // parse json body + throw new Error('implement json body parsing') + break + case 'multipart': + // parse multipart body + throw new Error('implement multipart body parsing') + break + default: + // 415 error code + res.statusCode = 415 + res.end() + return + } +} +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/type-is.svg +[npm-url]: https://npmjs.org/package/type-is +[node-version-image]: https://img.shields.io/node/v/type-is.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/type-is/master.svg +[travis-url]: https://travis-ci.org/jshttp/type-is +[coveralls-image]: https://img.shields.io/coveralls/jshttp/type-is/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/type-is?branch=master +[downloads-image]: https://img.shields.io/npm/dm/type-is.svg +[downloads-url]: https://npmjs.org/package/type-is diff --git a/node_modules/express/node_modules/type-is/index.js b/node_modules/express/node_modules/type-is/index.js new file mode 100644 index 0000000..5c11ef1 --- /dev/null +++ b/node_modules/express/node_modules/type-is/index.js @@ -0,0 +1,262 @@ +/*! + * type-is + * Copyright(c) 2014 Jonathan Ong + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module dependencies. + * @private + */ + +var typer = require('media-typer') +var mime = require('mime-types') + +/** + * Module exports. + * @public + */ + +module.exports = typeofrequest +module.exports.is = typeis +module.exports.hasBody = hasbody +module.exports.normalize = normalize +module.exports.match = mimeMatch + +/** + * Compare a `value` content-type with `types`. + * Each `type` can be an extension like `html`, + * a special shortcut like `multipart` or `urlencoded`, + * or a mime type. + * + * If no types match, `false` is returned. + * Otherwise, the first `type` that matches is returned. + * + * @param {String} value + * @param {Array} types + * @public + */ + +function typeis(value, types_) { + var i + var types = types_ + + // remove parameters and normalize + var val = tryNormalizeType(value) + + // no type or invalid + if (!val) { + return false + } + + // support flattened arguments + if (types && !Array.isArray(types)) { + types = new Array(arguments.length - 1) + for (i = 0; i < types.length; i++) { + types[i] = arguments[i + 1] + } + } + + // no types, return the content type + if (!types || !types.length) { + return val + } + + var type + for (i = 0; i < types.length; i++) { + if (mimeMatch(normalize(type = types[i]), val)) { + return type[0] === '+' || type.indexOf('*') !== -1 + ? val + : type + } + } + + // no matches + return false +} + +/** + * Check if a request has a request body. + * A request with a body __must__ either have `transfer-encoding` + * or `content-length` headers set. + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3 + * + * @param {Object} request + * @return {Boolean} + * @public + */ + +function hasbody(req) { + return req.headers['transfer-encoding'] !== undefined + || !isNaN(req.headers['content-length']) +} + +/** + * Check if the incoming request contains the "Content-Type" + * header field, and it contains any of the give mime `type`s. + * If there is no request body, `null` is returned. + * If there is no content type, `false` is returned. + * Otherwise, it returns the first `type` that matches. + * + * Examples: + * + * // With Content-Type: text/html; charset=utf-8 + * this.is('html'); // => 'html' + * this.is('text/html'); // => 'text/html' + * this.is('text/*', 'application/json'); // => 'text/html' + * + * // When Content-Type is application/json + * this.is('json', 'urlencoded'); // => 'json' + * this.is('application/json'); // => 'application/json' + * this.is('html', 'application/*'); // => 'application/json' + * + * this.is('html'); // => false + * + * @param {String|Array} types... + * @return {String|false|null} + * @public + */ + +function typeofrequest(req, types_) { + var types = types_ + + // no body + if (!hasbody(req)) { + return null + } + + // support flattened arguments + if (arguments.length > 2) { + types = new Array(arguments.length - 1) + for (var i = 0; i < types.length; i++) { + types[i] = arguments[i + 1] + } + } + + // request content type + var value = req.headers['content-type'] + + return typeis(value, types) +} + +/** + * Normalize a mime type. + * If it's a shorthand, expand it to a valid mime type. + * + * In general, you probably want: + * + * var type = is(req, ['urlencoded', 'json', 'multipart']); + * + * Then use the appropriate body parsers. + * These three are the most common request body types + * and are thus ensured to work. + * + * @param {String} type + * @private + */ + +function normalize(type) { + if (typeof type !== 'string') { + // invalid type + return false + } + + switch (type) { + case 'urlencoded': + return 'application/x-www-form-urlencoded' + case 'multipart': + return 'multipart/*' + } + + if (type[0] === '+') { + // "+json" -> "*/*+json" expando + return '*/*' + type + } + + return type.indexOf('/') === -1 + ? mime.lookup(type) + : type +} + +/** + * Check if `exected` mime type + * matches `actual` mime type with + * wildcard and +suffix support. + * + * @param {String} expected + * @param {String} actual + * @return {Boolean} + * @private + */ + +function mimeMatch(expected, actual) { + // invalid type + if (expected === false) { + return false + } + + // split types + var actualParts = actual.split('/') + var expectedParts = expected.split('/') + + // invalid format + if (actualParts.length !== 2 || expectedParts.length !== 2) { + return false + } + + // validate type + if (expectedParts[0] !== '*' && expectedParts[0] !== actualParts[0]) { + return false + } + + // validate suffix wildcard + if (expectedParts[1].substr(0, 2) === '*+') { + return expectedParts[1].length <= actualParts[1].length + 1 + && expectedParts[1].substr(1) === actualParts[1].substr(1 - expectedParts[1].length) + } + + // validate subtype + if (expectedParts[1] !== '*' && expectedParts[1] !== actualParts[1]) { + return false + } + + return true +} + +/** + * Normalize a type and remove parameters. + * + * @param {string} value + * @return {string} + * @private + */ + +function normalizeType(value) { + // parse the type + var type = typer.parse(value) + + // remove the parameters + type.parameters = undefined + + // reformat it + return typer.format(type) +} + +/** + * Try to normalize a type and remove parameters. + * + * @param {string} value + * @return {string} + * @private + */ + +function tryNormalizeType(value) { + try { + return normalizeType(value) + } catch (err) { + return null + } +} diff --git a/node_modules/express/node_modules/type-is/node_modules/media-typer/HISTORY.md b/node_modules/express/node_modules/type-is/node_modules/media-typer/HISTORY.md new file mode 100644 index 0000000..62c2003 --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/media-typer/HISTORY.md @@ -0,0 +1,22 @@ +0.3.0 / 2014-09-07 +================== + + * Support Node.js 0.6 + * Throw error when parameter format invalid on parse + +0.2.0 / 2014-06-18 +================== + + * Add `typer.format()` to format media types + +0.1.0 / 2014-06-17 +================== + + * Accept `req` as argument to `parse` + * Accept `res` as argument to `parse` + * Parse media type with extra LWS between type and first parameter + +0.0.0 / 2014-06-13 +================== + + * Initial implementation diff --git a/node_modules/express/node_modules/type-is/node_modules/media-typer/LICENSE b/node_modules/express/node_modules/type-is/node_modules/media-typer/LICENSE new file mode 100644 index 0000000..b7dce6c --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/media-typer/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2014 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/type-is/node_modules/media-typer/README.md b/node_modules/express/node_modules/type-is/node_modules/media-typer/README.md new file mode 100644 index 0000000..d8df623 --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/media-typer/README.md @@ -0,0 +1,81 @@ +# media-typer + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Simple RFC 6838 media type parser + +## Installation + +```sh +$ npm install media-typer +``` + +## API + +```js +var typer = require('media-typer') +``` + +### typer.parse(string) + +```js +var obj = typer.parse('image/svg+xml; charset=utf-8') +``` + +Parse a media type string. This will return an object with the following +properties (examples are shown for the string `'image/svg+xml; charset=utf-8'`): + + - `type`: The type of the media type (always lower case). Example: `'image'` + + - `subtype`: The subtype of the media type (always lower case). Example: `'svg'` + + - `suffix`: The suffix of the media type (always lower case). Example: `'xml'` + + - `parameters`: An object of the parameters in the media type (name of parameter always lower case). Example: `{charset: 'utf-8'}` + +### typer.parse(req) + +```js +var obj = typer.parse(req) +``` + +Parse the `content-type` header from the given `req`. Short-cut for +`typer.parse(req.headers['content-type'])`. + +### typer.parse(res) + +```js +var obj = typer.parse(res) +``` + +Parse the `content-type` header set on the given `res`. Short-cut for +`typer.parse(res.getHeader('content-type'))`. + +### typer.format(obj) + +```js +var obj = typer.format({type: 'image', subtype: 'svg', suffix: 'xml'}) +``` + +Format an object into a media type string. This will return a string of the +mime type for the given object. For the properties of the object, see the +documentation for `typer.parse(string)`. + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/media-typer.svg?style=flat +[npm-url]: https://npmjs.org/package/media-typer +[node-version-image]: https://img.shields.io/badge/node.js-%3E%3D_0.6-brightgreen.svg?style=flat +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/media-typer.svg?style=flat +[travis-url]: https://travis-ci.org/jshttp/media-typer +[coveralls-image]: https://img.shields.io/coveralls/jshttp/media-typer.svg?style=flat +[coveralls-url]: https://coveralls.io/r/jshttp/media-typer +[downloads-image]: https://img.shields.io/npm/dm/media-typer.svg?style=flat +[downloads-url]: https://npmjs.org/package/media-typer diff --git a/node_modules/express/node_modules/type-is/node_modules/media-typer/index.js b/node_modules/express/node_modules/type-is/node_modules/media-typer/index.js new file mode 100644 index 0000000..07f7295 --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/media-typer/index.js @@ -0,0 +1,270 @@ +/*! + * media-typer + * Copyright(c) 2014 Douglas Christopher Wilson + * MIT Licensed + */ + +/** + * RegExp to match *( ";" parameter ) in RFC 2616 sec 3.7 + * + * parameter = token "=" ( token | quoted-string ) + * token = 1* + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) + * qdtext = > + * quoted-pair = "\" CHAR + * CHAR = + * TEXT = + * LWS = [CRLF] 1*( SP | HT ) + * CRLF = CR LF + * CR = + * LF = + * SP = + * SHT = + * CTL = + * OCTET = + */ +var paramRegExp = /; *([!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+) *= *("(?:[ !\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u0020-\u007e])*"|[!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+) */g; +var textRegExp = /^[\u0020-\u007e\u0080-\u00ff]+$/ +var tokenRegExp = /^[!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+$/ + +/** + * RegExp to match quoted-pair in RFC 2616 + * + * quoted-pair = "\" CHAR + * CHAR = + */ +var qescRegExp = /\\([\u0000-\u007f])/g; + +/** + * RegExp to match chars that must be quoted-pair in RFC 2616 + */ +var quoteRegExp = /([\\"])/g; + +/** + * RegExp to match type in RFC 6838 + * + * type-name = restricted-name + * subtype-name = restricted-name + * restricted-name = restricted-name-first *126restricted-name-chars + * restricted-name-first = ALPHA / DIGIT + * restricted-name-chars = ALPHA / DIGIT / "!" / "#" / + * "$" / "&" / "-" / "^" / "_" + * restricted-name-chars =/ "." ; Characters before first dot always + * ; specify a facet name + * restricted-name-chars =/ "+" ; Characters after last plus always + * ; specify a structured syntax suffix + * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z + * DIGIT = %x30-39 ; 0-9 + */ +var subtypeNameRegExp = /^[A-Za-z0-9][A-Za-z0-9!#$&^_.-]{0,126}$/ +var typeNameRegExp = /^[A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126}$/ +var typeRegExp = /^ *([A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126})\/([A-Za-z0-9][A-Za-z0-9!#$&^_.+-]{0,126}) *$/; + +/** + * Module exports. + */ + +exports.format = format +exports.parse = parse + +/** + * Format object to media type. + * + * @param {object} obj + * @return {string} + * @api public + */ + +function format(obj) { + if (!obj || typeof obj !== 'object') { + throw new TypeError('argument obj is required') + } + + var parameters = obj.parameters + var subtype = obj.subtype + var suffix = obj.suffix + var type = obj.type + + if (!type || !typeNameRegExp.test(type)) { + throw new TypeError('invalid type') + } + + if (!subtype || !subtypeNameRegExp.test(subtype)) { + throw new TypeError('invalid subtype') + } + + // format as type/subtype + var string = type + '/' + subtype + + // append +suffix + if (suffix) { + if (!typeNameRegExp.test(suffix)) { + throw new TypeError('invalid suffix') + } + + string += '+' + suffix + } + + // append parameters + if (parameters && typeof parameters === 'object') { + var param + var params = Object.keys(parameters).sort() + + for (var i = 0; i < params.length; i++) { + param = params[i] + + if (!tokenRegExp.test(param)) { + throw new TypeError('invalid parameter name') + } + + string += '; ' + param + '=' + qstring(parameters[param]) + } + } + + return string +} + +/** + * Parse media type to object. + * + * @param {string|object} string + * @return {Object} + * @api public + */ + +function parse(string) { + if (!string) { + throw new TypeError('argument string is required') + } + + // support req/res-like objects as argument + if (typeof string === 'object') { + string = getcontenttype(string) + } + + if (typeof string !== 'string') { + throw new TypeError('argument string is required to be a string') + } + + var index = string.indexOf(';') + var type = index !== -1 + ? string.substr(0, index) + : string + + var key + var match + var obj = splitType(type) + var params = {} + var value + + paramRegExp.lastIndex = index + + while (match = paramRegExp.exec(string)) { + if (match.index !== index) { + throw new TypeError('invalid parameter format') + } + + index += match[0].length + key = match[1].toLowerCase() + value = match[2] + + if (value[0] === '"') { + // remove quotes and escapes + value = value + .substr(1, value.length - 2) + .replace(qescRegExp, '$1') + } + + params[key] = value + } + + if (index !== -1 && index !== string.length) { + throw new TypeError('invalid parameter format') + } + + obj.parameters = params + + return obj +} + +/** + * Get content-type from req/res objects. + * + * @param {object} + * @return {Object} + * @api private + */ + +function getcontenttype(obj) { + if (typeof obj.getHeader === 'function') { + // res-like + return obj.getHeader('content-type') + } + + if (typeof obj.headers === 'object') { + // req-like + return obj.headers && obj.headers['content-type'] + } +} + +/** + * Quote a string if necessary. + * + * @param {string} val + * @return {string} + * @api private + */ + +function qstring(val) { + var str = String(val) + + // no need to quote tokens + if (tokenRegExp.test(str)) { + return str + } + + if (str.length > 0 && !textRegExp.test(str)) { + throw new TypeError('invalid parameter value') + } + + return '"' + str.replace(quoteRegExp, '\\$1') + '"' +} + +/** + * Simply "type/subtype+siffx" into parts. + * + * @param {string} string + * @return {Object} + * @api private + */ + +function splitType(string) { + var match = typeRegExp.exec(string.toLowerCase()) + + if (!match) { + throw new TypeError('invalid media type') + } + + var type = match[1] + var subtype = match[2] + var suffix + + // suffix after last + + var index = subtype.lastIndexOf('+') + if (index !== -1) { + suffix = subtype.substr(index + 1) + subtype = subtype.substr(0, index) + } + + var obj = { + type: type, + subtype: subtype, + suffix: suffix + } + + return obj +} diff --git a/node_modules/express/node_modules/type-is/node_modules/media-typer/package.json b/node_modules/express/node_modules/type-is/node_modules/media-typer/package.json new file mode 100644 index 0000000..88bb0a8 --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/media-typer/package.json @@ -0,0 +1,57 @@ +{ + "name": "media-typer", + "description": "Simple RFC 6838 media type parser and formatter", + "version": "0.3.0", + "author": { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/jshttp/media-typer" + }, + "devDependencies": { + "istanbul": "0.3.2", + "mocha": "~1.21.4", + "should": "~4.0.4" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec --check-leaks --bail test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "d49d41ffd0bb5a0655fa44a59df2ec0bfc835b16", + "bugs": { + "url": "https://github.com/jshttp/media-typer/issues" + }, + "homepage": "https://github.com/jshttp/media-typer", + "_id": "media-typer@0.3.0", + "_shasum": "8710d7af0aa626f8fffa1ce00168545263255748", + "_from": "media-typer@0.3.0", + "_npmVersion": "1.4.21", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "8710d7af0aa626f8fffa1ce00168545263255748", + "tarball": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" +} diff --git a/node_modules/express/node_modules/type-is/node_modules/mime-types/HISTORY.md b/node_modules/express/node_modules/type-is/node_modules/mime-types/HISTORY.md new file mode 100644 index 0000000..64241d9 --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/mime-types/HISTORY.md @@ -0,0 +1,177 @@ +2.1.8 / 2015-11-30 +================== + + * deps: mime-db@~1.20.0 + - Add new mime types + +2.1.7 / 2015-09-20 +================== + + * deps: mime-db@~1.19.0 + - Add new mime types + +2.1.6 / 2015-09-03 +================== + + * deps: mime-db@~1.18.0 + - Add new mime types + +2.1.5 / 2015-08-20 +================== + + * deps: mime-db@~1.17.0 + - Add new mime types + +2.1.4 / 2015-07-30 +================== + + * deps: mime-db@~1.16.0 + - Add new mime types + +2.1.3 / 2015-07-13 +================== + + * deps: mime-db@~1.15.0 + - Add new mime types + +2.1.2 / 2015-06-25 +================== + + * deps: mime-db@~1.14.0 + - Add new mime types + +2.1.1 / 2015-06-08 +================== + + * perf: fix deopt during mapping + +2.1.0 / 2015-06-07 +================== + + * Fix incorrectly treating extension-less file name as extension + - i.e. `'path/to/json'` will no longer return `application/json` + * Fix `.charset(type)` to accept parameters + * Fix `.charset(type)` to match case-insensitive + * Improve generation of extension to MIME mapping + * Refactor internals for readability and no argument reassignment + * Prefer `application/*` MIME types from the same source + * Prefer any type over `application/octet-stream` + * deps: mime-db@~1.13.0 + - Add nginx as a source + - Add new mime types + +2.0.14 / 2015-06-06 +=================== + + * deps: mime-db@~1.12.0 + - Add new mime types + +2.0.13 / 2015-05-31 +=================== + + * deps: mime-db@~1.11.0 + - Add new mime types + +2.0.12 / 2015-05-19 +=================== + + * deps: mime-db@~1.10.0 + - Add new mime types + +2.0.11 / 2015-05-05 +=================== + + * deps: mime-db@~1.9.1 + - Add new mime types + +2.0.10 / 2015-03-13 +=================== + + * deps: mime-db@~1.8.0 + - Add new mime types + +2.0.9 / 2015-02-09 +================== + + * deps: mime-db@~1.7.0 + - Add new mime types + - Community extensions ownership transferred from `node-mime` + +2.0.8 / 2015-01-29 +================== + + * deps: mime-db@~1.6.0 + - Add new mime types + +2.0.7 / 2014-12-30 +================== + + * deps: mime-db@~1.5.0 + - Add new mime types + - Fix various invalid MIME type entries + +2.0.6 / 2014-12-30 +================== + + * deps: mime-db@~1.4.0 + - Add new mime types + - Fix various invalid MIME type entries + - Remove example template MIME types + +2.0.5 / 2014-12-29 +================== + + * deps: mime-db@~1.3.1 + - Fix missing extensions + +2.0.4 / 2014-12-10 +================== + + * deps: mime-db@~1.3.0 + - Add new mime types + +2.0.3 / 2014-11-09 +================== + + * deps: mime-db@~1.2.0 + - Add new mime types + +2.0.2 / 2014-09-28 +================== + + * deps: mime-db@~1.1.0 + - Add new mime types + - Add additional compressible + - Update charsets + +2.0.1 / 2014-09-07 +================== + + * Support Node.js 0.6 + +2.0.0 / 2014-09-02 +================== + + * Use `mime-db` + * Remove `.define()` + +1.0.2 / 2014-08-04 +================== + + * Set charset=utf-8 for `text/javascript` + +1.0.1 / 2014-06-24 +================== + + * Add `text/jsx` type + +1.0.0 / 2014-05-12 +================== + + * Return `false` for unknown types + * Set charset=utf-8 for `application/json` + +0.1.0 / 2014-05-02 +================== + + * Initial release diff --git a/node_modules/express/node_modules/type-is/node_modules/mime-types/LICENSE b/node_modules/express/node_modules/type-is/node_modules/mime-types/LICENSE new file mode 100644 index 0000000..0616607 --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/mime-types/LICENSE @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/type-is/node_modules/mime-types/README.md b/node_modules/express/node_modules/type-is/node_modules/mime-types/README.md new file mode 100644 index 0000000..e26295d --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/mime-types/README.md @@ -0,0 +1,103 @@ +# mime-types + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +The ultimate javascript content-type utility. + +Similar to [node-mime](https://github.com/broofa/node-mime), except: + +- __No fallbacks.__ Instead of naively returning the first available type, `mime-types` simply returns `false`, + so do `var type = mime.lookup('unrecognized') || 'application/octet-stream'`. +- No `new Mime()` business, so you could do `var lookup = require('mime-types').lookup`. +- Additional mime types are added such as jade and stylus via [mime-db](https://github.com/jshttp/mime-db) +- No `.define()` functionality + +Otherwise, the API is compatible. + +## Install + +```sh +$ npm install mime-types +``` + +## Adding Types + +All mime types are based on [mime-db](https://github.com/jshttp/mime-db), +so open a PR there if you'd like to add mime types. + +## API + +```js +var mime = require('mime-types') +``` + +All functions return `false` if input is invalid or not found. + +### mime.lookup(path) + +Lookup the content-type associated with a file. + +```js +mime.lookup('json') // 'application/json' +mime.lookup('.md') // 'text/x-markdown' +mime.lookup('file.html') // 'text/html' +mime.lookup('folder/file.js') // 'application/javascript' +mime.lookup('folder/.htaccess') // false + +mime.lookup('cats') // false +``` + +### mime.contentType(type) + +Create a full content-type header given a content-type or extension. + +```js +mime.contentType('markdown') // 'text/x-markdown; charset=utf-8' +mime.contentType('file.json') // 'application/json; charset=utf-8' + +// from a full path +mime.contentType(path.extname('/path/to/file.json')) // 'application/json; charset=utf-8' +``` + +### mime.extension(type) + +Get the default extension for a content-type. + +```js +mime.extension('application/octet-stream') // 'bin' +``` + +### mime.charset(type) + +Lookup the implied default charset of a content-type. + +```js +mime.charset('text/x-markdown') // 'UTF-8' +``` + +### var type = mime.types[extension] + +A map of content-types by extension. + +### [extensions...] = mime.extensions[type] + +A map of extensions by content-type. + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/mime-types.svg +[npm-url]: https://npmjs.org/package/mime-types +[node-version-image]: https://img.shields.io/node/v/mime-types.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/mime-types/master.svg +[travis-url]: https://travis-ci.org/jshttp/mime-types +[coveralls-image]: https://img.shields.io/coveralls/jshttp/mime-types/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/mime-types +[downloads-image]: https://img.shields.io/npm/dm/mime-types.svg +[downloads-url]: https://npmjs.org/package/mime-types diff --git a/node_modules/express/node_modules/type-is/node_modules/mime-types/index.js b/node_modules/express/node_modules/type-is/node_modules/mime-types/index.js new file mode 100644 index 0000000..f7008b2 --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/mime-types/index.js @@ -0,0 +1,188 @@ +/*! + * mime-types + * Copyright(c) 2014 Jonathan Ong + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module dependencies. + * @private + */ + +var db = require('mime-db') +var extname = require('path').extname + +/** + * Module variables. + * @private + */ + +var extractTypeRegExp = /^\s*([^;\s]*)(?:;|\s|$)/ +var textTypeRegExp = /^text\//i + +/** + * Module exports. + * @public + */ + +exports.charset = charset +exports.charsets = { lookup: charset } +exports.contentType = contentType +exports.extension = extension +exports.extensions = Object.create(null) +exports.lookup = lookup +exports.types = Object.create(null) + +// Populate the extensions/types maps +populateMaps(exports.extensions, exports.types) + +/** + * Get the default charset for a MIME type. + * + * @param {string} type + * @return {boolean|string} + */ + +function charset(type) { + if (!type || typeof type !== 'string') { + return false + } + + // TODO: use media-typer + var match = extractTypeRegExp.exec(type) + var mime = match && db[match[1].toLowerCase()] + + if (mime && mime.charset) { + return mime.charset + } + + // default text/* to utf-8 + if (match && textTypeRegExp.test(match[1])) { + return 'UTF-8' + } + + return false +} + +/** + * Create a full Content-Type header given a MIME type or extension. + * + * @param {string} str + * @return {boolean|string} + */ + +function contentType(str) { + // TODO: should this even be in this module? + if (!str || typeof str !== 'string') { + return false + } + + var mime = str.indexOf('/') === -1 + ? exports.lookup(str) + : str + + if (!mime) { + return false + } + + // TODO: use content-type or other module + if (mime.indexOf('charset') === -1) { + var charset = exports.charset(mime) + if (charset) mime += '; charset=' + charset.toLowerCase() + } + + return mime +} + +/** + * Get the default extension for a MIME type. + * + * @param {string} type + * @return {boolean|string} + */ + +function extension(type) { + if (!type || typeof type !== 'string') { + return false + } + + // TODO: use media-typer + var match = extractTypeRegExp.exec(type) + + // get extensions + var exts = match && exports.extensions[match[1].toLowerCase()] + + if (!exts || !exts.length) { + return false + } + + return exts[0] +} + +/** + * Lookup the MIME type for a file path/extension. + * + * @param {string} path + * @return {boolean|string} + */ + +function lookup(path) { + if (!path || typeof path !== 'string') { + return false + } + + // get the extension ("ext" or ".ext" or full path) + var extension = extname('x.' + path) + .toLowerCase() + .substr(1) + + if (!extension) { + return false + } + + return exports.types[extension] || false +} + +/** + * Populate the extensions and types maps. + * @private + */ + +function populateMaps(extensions, types) { + // source preference (least -> most) + var preference = ['nginx', 'apache', undefined, 'iana'] + + Object.keys(db).forEach(function forEachMimeType(type) { + var mime = db[type] + var exts = mime.extensions + + if (!exts || !exts.length) { + return + } + + // mime -> extensions + extensions[type] = exts + + // extension -> mime + for (var i = 0; i < exts.length; i++) { + var extension = exts[i] + + if (types[extension]) { + var from = preference.indexOf(db[types[extension]].source) + var to = preference.indexOf(mime.source) + + if (types[extension] !== 'application/octet-stream' + && from > to || (from === to && types[extension].substr(0, 12) === 'application/')) { + // skip the remapping + continue + } + } + + // set the extension -> mime + types[extension] = type + } + }) +} diff --git a/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/HISTORY.md b/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/HISTORY.md new file mode 100644 index 0000000..c7f8b5a --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/HISTORY.md @@ -0,0 +1,287 @@ +1.20.0 / 2015-11-10 +=================== + + * Add `application/cdni` + * Add `application/csvm+json` + * Add `application/rfc+xml` + * Add `application/vnd.3gpp.access-transfer-events+xml` + * Add `application/vnd.3gpp.srvcc-ext+xml` + * Add `application/vnd.ms-windows.wsd.oob` + * Add `application/vnd.oxli.countgraph` + * Add `application/vnd.pagerduty+json` + * Add `text/x-suse-ymp` + +1.19.0 / 2015-09-17 +=================== + + * Add `application/vnd.3gpp-prose-pc3ch+xml` + * Add `application/vnd.3gpp.srvcc-info+xml` + * Add `application/vnd.apple.pkpass` + * Add `application/vnd.drive+json` + +1.18.0 / 2015-09-03 +=================== + + * Add `application/pkcs12` + * Add `application/vnd.3gpp-prose+xml` + * Add `application/vnd.3gpp.mid-call+xml` + * Add `application/vnd.3gpp.state-and-event-info+xml` + * Add `application/vnd.anki` + * Add `application/vnd.firemonkeys.cloudcell` + * Add `application/vnd.openblox.game+xml` + * Add `application/vnd.openblox.game-binary` + +1.17.0 / 2015-08-13 +=================== + + * Add `application/x-msdos-program` + * Add `audio/g711-0` + * Add `image/vnd.mozilla.apng` + * Add extension `.exe` to `application/x-msdos-program` + +1.16.0 / 2015-07-29 +=================== + + * Add `application/vnd.uri-map` + +1.15.0 / 2015-07-13 +=================== + + * Add `application/x-httpd-php` + +1.14.0 / 2015-06-25 +=================== + + * Add `application/scim+json` + * Add `application/vnd.3gpp.ussd+xml` + * Add `application/vnd.biopax.rdf+xml` + * Add `text/x-processing` + +1.13.0 / 2015-06-07 +=================== + + * Add nginx as a source + * Add `application/x-cocoa` + * Add `application/x-java-archive-diff` + * Add `application/x-makeself` + * Add `application/x-perl` + * Add `application/x-pilot` + * Add `application/x-redhat-package-manager` + * Add `application/x-sea` + * Add `audio/x-m4a` + * Add `audio/x-realaudio` + * Add `image/x-jng` + * Add `text/mathml` + +1.12.0 / 2015-06-05 +=================== + + * Add `application/bdoc` + * Add `application/vnd.hyperdrive+json` + * Add `application/x-bdoc` + * Add extension `.rtf` to `text/rtf` + +1.11.0 / 2015-05-31 +=================== + + * Add `audio/wav` + * Add `audio/wave` + * Add extension `.litcoffee` to `text/coffeescript` + * Add extension `.sfd-hdstx` to `application/vnd.hydrostatix.sof-data` + * Add extension `.n-gage` to `application/vnd.nokia.n-gage.symbian.install` + +1.10.0 / 2015-05-19 +=================== + + * Add `application/vnd.balsamiq.bmpr` + * Add `application/vnd.microsoft.portable-executable` + * Add `application/x-ns-proxy-autoconfig` + +1.9.1 / 2015-04-19 +================== + + * Remove `.json` extension from `application/manifest+json` + - This is causing bugs downstream + +1.9.0 / 2015-04-19 +================== + + * Add `application/manifest+json` + * Add `application/vnd.micro+json` + * Add `image/vnd.zbrush.pcx` + * Add `image/x-ms-bmp` + +1.8.0 / 2015-03-13 +================== + + * Add `application/vnd.citationstyles.style+xml` + * Add `application/vnd.fastcopy-disk-image` + * Add `application/vnd.gov.sk.xmldatacontainer+xml` + * Add extension `.jsonld` to `application/ld+json` + +1.7.0 / 2015-02-08 +================== + + * Add `application/vnd.gerber` + * Add `application/vnd.msa-disk-image` + +1.6.1 / 2015-02-05 +================== + + * Community extensions ownership transferred from `node-mime` + +1.6.0 / 2015-01-29 +================== + + * Add `application/jose` + * Add `application/jose+json` + * Add `application/json-seq` + * Add `application/jwk+json` + * Add `application/jwk-set+json` + * Add `application/jwt` + * Add `application/rdap+json` + * Add `application/vnd.gov.sk.e-form+xml` + * Add `application/vnd.ims.imsccv1p3` + +1.5.0 / 2014-12-30 +================== + + * Add `application/vnd.oracle.resource+json` + * Fix various invalid MIME type entries + - `application/mbox+xml` + - `application/oscp-response` + - `application/vwg-multiplexed` + - `audio/g721` + +1.4.0 / 2014-12-21 +================== + + * Add `application/vnd.ims.imsccv1p2` + * Fix various invalid MIME type entries + - `application/vnd-acucobol` + - `application/vnd-curl` + - `application/vnd-dart` + - `application/vnd-dxr` + - `application/vnd-fdf` + - `application/vnd-mif` + - `application/vnd-sema` + - `application/vnd-wap-wmlc` + - `application/vnd.adobe.flash-movie` + - `application/vnd.dece-zip` + - `application/vnd.dvb_service` + - `application/vnd.micrografx-igx` + - `application/vnd.sealed-doc` + - `application/vnd.sealed-eml` + - `application/vnd.sealed-mht` + - `application/vnd.sealed-ppt` + - `application/vnd.sealed-tiff` + - `application/vnd.sealed-xls` + - `application/vnd.sealedmedia.softseal-html` + - `application/vnd.sealedmedia.softseal-pdf` + - `application/vnd.wap-slc` + - `application/vnd.wap-wbxml` + - `audio/vnd.sealedmedia.softseal-mpeg` + - `image/vnd-djvu` + - `image/vnd-svf` + - `image/vnd-wap-wbmp` + - `image/vnd.sealed-png` + - `image/vnd.sealedmedia.softseal-gif` + - `image/vnd.sealedmedia.softseal-jpg` + - `model/vnd-dwf` + - `model/vnd.parasolid.transmit-binary` + - `model/vnd.parasolid.transmit-text` + - `text/vnd-a` + - `text/vnd-curl` + - `text/vnd.wap-wml` + * Remove example template MIME types + - `application/example` + - `audio/example` + - `image/example` + - `message/example` + - `model/example` + - `multipart/example` + - `text/example` + - `video/example` + +1.3.1 / 2014-12-16 +================== + + * Fix missing extensions + - `application/json5` + - `text/hjson` + +1.3.0 / 2014-12-07 +================== + + * Add `application/a2l` + * Add `application/aml` + * Add `application/atfx` + * Add `application/atxml` + * Add `application/cdfx+xml` + * Add `application/dii` + * Add `application/json5` + * Add `application/lxf` + * Add `application/mf4` + * Add `application/vnd.apache.thrift.compact` + * Add `application/vnd.apache.thrift.json` + * Add `application/vnd.coffeescript` + * Add `application/vnd.enphase.envoy` + * Add `application/vnd.ims.imsccv1p1` + * Add `text/csv-schema` + * Add `text/hjson` + * Add `text/markdown` + * Add `text/yaml` + +1.2.0 / 2014-11-09 +================== + + * Add `application/cea` + * Add `application/dit` + * Add `application/vnd.gov.sk.e-form+zip` + * Add `application/vnd.tmd.mediaflex.api+xml` + * Type `application/epub+zip` is now IANA-registered + +1.1.2 / 2014-10-23 +================== + + * Rebuild database for `application/x-www-form-urlencoded` change + +1.1.1 / 2014-10-20 +================== + + * Mark `application/x-www-form-urlencoded` as compressible. + +1.1.0 / 2014-09-28 +================== + + * Add `application/font-woff2` + +1.0.3 / 2014-09-25 +================== + + * Fix engine requirement in package + +1.0.2 / 2014-09-25 +================== + + * Add `application/coap-group+json` + * Add `application/dcd` + * Add `application/vnd.apache.thrift.binary` + * Add `image/vnd.tencent.tap` + * Mark all JSON-derived types as compressible + * Update `text/vtt` data + +1.0.1 / 2014-08-30 +================== + + * Fix extension ordering + +1.0.0 / 2014-08-30 +================== + + * Add `application/atf` + * Add `application/merge-patch+json` + * Add `multipart/x-mixed-replace` + * Add `source: 'apache'` metadata + * Add `source: 'iana'` metadata + * Remove badly-assumed charset data diff --git a/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/LICENSE b/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/LICENSE new file mode 100644 index 0000000..a7ae8ee --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/README.md b/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/README.md new file mode 100644 index 0000000..164cca0 --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/README.md @@ -0,0 +1,82 @@ +# mime-db + +[![NPM Version][npm-version-image]][npm-url] +[![NPM Downloads][npm-downloads-image]][npm-url] +[![Node.js Version][node-image]][node-url] +[![Build Status][travis-image]][travis-url] +[![Coverage Status][coveralls-image]][coveralls-url] + +This is a database of all mime types. +It consists of a single, public JSON file and does not include any logic, +allowing it to remain as un-opinionated as possible with an API. +It aggregates data from the following sources: + +- http://www.iana.org/assignments/media-types/media-types.xhtml +- http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types +- http://hg.nginx.org/nginx/raw-file/default/conf/mime.types + +## Installation + +```bash +npm install mime-db +``` + +### Database Download + +If you're crazy enough to use this in the browser, you can just grab the +JSON file using [RawGit](https://rawgit.com/). It is recommended to replace +`master` with [a release tag](https://github.com/jshttp/mime-db/tags) as the +JSON format may change in the future. + +``` +https://cdn.rawgit.com/jshttp/mime-db/master/db.json +``` + +## Usage + +```js +var db = require('mime-db'); + +// grab data on .js files +var data = db['application/javascript']; +``` + +## Data Structure + +The JSON file is a map lookup for lowercased mime types. +Each mime type has the following properties: + +- `.source` - where the mime type is defined. + If not set, it's probably a custom media type. + - `apache` - [Apache common media types](http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types) + - `iana` - [IANA-defined media types](http://www.iana.org/assignments/media-types/media-types.xhtml) + - `nginx` - [nginx media types](http://hg.nginx.org/nginx/raw-file/default/conf/mime.types) +- `.extensions[]` - known extensions associated with this mime type. +- `.compressible` - whether a file of this type is can be gzipped. +- `.charset` - the default charset associated with this type, if any. + +If unknown, every property could be `undefined`. + +## Contributing + +To edit the database, only make PRs against `src/custom.json` or +`src/custom-suffix.json`. + +To update the build, run `npm run build`. + +## Adding Custom Media Types + +The best way to get new media types included in this library is to register +them with the IANA. The community registration procedure is outlined in +[RFC 6838 section 5](http://tools.ietf.org/html/rfc6838#section-5). Types +registered with the IANA are automatically pulled into this library. + +[npm-version-image]: https://img.shields.io/npm/v/mime-db.svg +[npm-downloads-image]: https://img.shields.io/npm/dm/mime-db.svg +[npm-url]: https://npmjs.org/package/mime-db +[travis-image]: https://img.shields.io/travis/jshttp/mime-db/master.svg +[travis-url]: https://travis-ci.org/jshttp/mime-db +[coveralls-image]: https://img.shields.io/coveralls/jshttp/mime-db/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/mime-db?branch=master +[node-image]: https://img.shields.io/node/v/mime-db.svg +[node-url]: http://nodejs.org/download/ diff --git a/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/db.json b/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/db.json new file mode 100644 index 0000000..123e7f9 --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/db.json @@ -0,0 +1,6504 @@ +{ + "application/1d-interleaved-parityfec": { + "source": "iana" + }, + "application/3gpdash-qoe-report+xml": { + "source": "iana" + }, + "application/3gpp-ims+xml": { + "source": "iana" + }, + "application/a2l": { + "source": "iana" + }, + "application/activemessage": { + "source": "iana" + }, + "application/alto-costmap+json": { + "source": "iana", + "compressible": true + }, + "application/alto-costmapfilter+json": { + "source": "iana", + "compressible": true + }, + "application/alto-directory+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointcost+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointcostparams+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointprop+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointpropparams+json": { + "source": "iana", + "compressible": true + }, + "application/alto-error+json": { + "source": "iana", + "compressible": true + }, + "application/alto-networkmap+json": { + "source": "iana", + "compressible": true + }, + "application/alto-networkmapfilter+json": { + "source": "iana", + "compressible": true + }, + "application/aml": { + "source": "iana" + }, + "application/andrew-inset": { + "source": "iana", + "extensions": ["ez"] + }, + "application/applefile": { + "source": "iana" + }, + "application/applixware": { + "source": "apache", + "extensions": ["aw"] + }, + "application/atf": { + "source": "iana" + }, + "application/atfx": { + "source": "iana" + }, + "application/atom+xml": { + "source": "iana", + "compressible": true, + "extensions": ["atom"] + }, + "application/atomcat+xml": { + "source": "iana", + "extensions": ["atomcat"] + }, + "application/atomdeleted+xml": { + "source": "iana" + }, + "application/atomicmail": { + "source": "iana" + }, + "application/atomsvc+xml": { + "source": "iana", + "extensions": ["atomsvc"] + }, + "application/atxml": { + "source": "iana" + }, + "application/auth-policy+xml": { + "source": "iana" + }, + "application/bacnet-xdd+zip": { + "source": "iana" + }, + "application/batch-smtp": { + "source": "iana" + }, + "application/bdoc": { + "compressible": false, + "extensions": ["bdoc"] + }, + "application/beep+xml": { + "source": "iana" + }, + "application/calendar+json": { + "source": "iana", + "compressible": true + }, + "application/calendar+xml": { + "source": "iana" + }, + "application/call-completion": { + "source": "iana" + }, + "application/cals-1840": { + "source": "iana" + }, + "application/cbor": { + "source": "iana" + }, + "application/ccmp+xml": { + "source": "iana" + }, + "application/ccxml+xml": { + "source": "iana", + "extensions": ["ccxml"] + }, + "application/cdfx+xml": { + "source": "iana" + }, + "application/cdmi-capability": { + "source": "iana", + "extensions": ["cdmia"] + }, + "application/cdmi-container": { + "source": "iana", + "extensions": ["cdmic"] + }, + "application/cdmi-domain": { + "source": "iana", + "extensions": ["cdmid"] + }, + "application/cdmi-object": { + "source": "iana", + "extensions": ["cdmio"] + }, + "application/cdmi-queue": { + "source": "iana", + "extensions": ["cdmiq"] + }, + "application/cdni": { + "source": "iana" + }, + "application/cea": { + "source": "iana" + }, + "application/cea-2018+xml": { + "source": "iana" + }, + "application/cellml+xml": { + "source": "iana" + }, + "application/cfw": { + "source": "iana" + }, + "application/cms": { + "source": "iana" + }, + "application/cnrp+xml": { + "source": "iana" + }, + "application/coap-group+json": { + "source": "iana", + "compressible": true + }, + "application/commonground": { + "source": "iana" + }, + "application/conference-info+xml": { + "source": "iana" + }, + "application/cpl+xml": { + "source": "iana" + }, + "application/csrattrs": { + "source": "iana" + }, + "application/csta+xml": { + "source": "iana" + }, + "application/cstadata+xml": { + "source": "iana" + }, + "application/csvm+json": { + "source": "iana", + "compressible": true + }, + "application/cu-seeme": { + "source": "apache", + "extensions": ["cu"] + }, + "application/cybercash": { + "source": "iana" + }, + "application/dart": { + "compressible": true + }, + "application/dash+xml": { + "source": "iana", + "extensions": ["mdp"] + }, + "application/dashdelta": { + "source": "iana" + }, + "application/davmount+xml": { + "source": "iana", + "extensions": ["davmount"] + }, + "application/dca-rft": { + "source": "iana" + }, + "application/dcd": { + "source": "iana" + }, + "application/dec-dx": { + "source": "iana" + }, + "application/dialog-info+xml": { + "source": "iana" + }, + "application/dicom": { + "source": "iana" + }, + "application/dii": { + "source": "iana" + }, + "application/dit": { + "source": "iana" + }, + "application/dns": { + "source": "iana" + }, + "application/docbook+xml": { + "source": "apache", + "extensions": ["dbk"] + }, + "application/dskpp+xml": { + "source": "iana" + }, + "application/dssc+der": { + "source": "iana", + "extensions": ["dssc"] + }, + "application/dssc+xml": { + "source": "iana", + "extensions": ["xdssc"] + }, + "application/dvcs": { + "source": "iana" + }, + "application/ecmascript": { + "source": "iana", + "compressible": true, + "extensions": ["ecma"] + }, + "application/edi-consent": { + "source": "iana" + }, + "application/edi-x12": { + "source": "iana", + "compressible": false + }, + "application/edifact": { + "source": "iana", + "compressible": false + }, + "application/emma+xml": { + "source": "iana", + "extensions": ["emma"] + }, + "application/emotionml+xml": { + "source": "iana" + }, + "application/encaprtp": { + "source": "iana" + }, + "application/epp+xml": { + "source": "iana" + }, + "application/epub+zip": { + "source": "iana", + "extensions": ["epub"] + }, + "application/eshop": { + "source": "iana" + }, + "application/exi": { + "source": "iana", + "extensions": ["exi"] + }, + "application/fastinfoset": { + "source": "iana" + }, + "application/fastsoap": { + "source": "iana" + }, + "application/fdt+xml": { + "source": "iana" + }, + "application/fits": { + "source": "iana" + }, + "application/font-sfnt": { + "source": "iana" + }, + "application/font-tdpfr": { + "source": "iana", + "extensions": ["pfr"] + }, + "application/font-woff": { + "source": "iana", + "compressible": false, + "extensions": ["woff"] + }, + "application/font-woff2": { + "compressible": false, + "extensions": ["woff2"] + }, + "application/framework-attributes+xml": { + "source": "iana" + }, + "application/gml+xml": { + "source": "apache", + "extensions": ["gml"] + }, + "application/gpx+xml": { + "source": "apache", + "extensions": ["gpx"] + }, + "application/gxf": { + "source": "apache", + "extensions": ["gxf"] + }, + "application/gzip": { + "source": "iana", + "compressible": false + }, + "application/h224": { + "source": "iana" + }, + "application/held+xml": { + "source": "iana" + }, + "application/http": { + "source": "iana" + }, + "application/hyperstudio": { + "source": "iana", + "extensions": ["stk"] + }, + "application/ibe-key-request+xml": { + "source": "iana" + }, + "application/ibe-pkg-reply+xml": { + "source": "iana" + }, + "application/ibe-pp-data": { + "source": "iana" + }, + "application/iges": { + "source": "iana" + }, + "application/im-iscomposing+xml": { + "source": "iana" + }, + "application/index": { + "source": "iana" + }, + "application/index.cmd": { + "source": "iana" + }, + "application/index.obj": { + "source": "iana" + }, + "application/index.response": { + "source": "iana" + }, + "application/index.vnd": { + "source": "iana" + }, + "application/inkml+xml": { + "source": "iana", + "extensions": ["ink","inkml"] + }, + "application/iotp": { + "source": "iana" + }, + "application/ipfix": { + "source": "iana", + "extensions": ["ipfix"] + }, + "application/ipp": { + "source": "iana" + }, + "application/isup": { + "source": "iana" + }, + "application/its+xml": { + "source": "iana" + }, + "application/java-archive": { + "source": "apache", + "compressible": false, + "extensions": ["jar","war","ear"] + }, + "application/java-serialized-object": { + "source": "apache", + "compressible": false, + "extensions": ["ser"] + }, + "application/java-vm": { + "source": "apache", + "compressible": false, + "extensions": ["class"] + }, + "application/javascript": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["js"] + }, + "application/jose": { + "source": "iana" + }, + "application/jose+json": { + "source": "iana", + "compressible": true + }, + "application/jrd+json": { + "source": "iana", + "compressible": true + }, + "application/json": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["json","map"] + }, + "application/json-patch+json": { + "source": "iana", + "compressible": true + }, + "application/json-seq": { + "source": "iana" + }, + "application/json5": { + "extensions": ["json5"] + }, + "application/jsonml+json": { + "source": "apache", + "compressible": true, + "extensions": ["jsonml"] + }, + "application/jwk+json": { + "source": "iana", + "compressible": true + }, + "application/jwk-set+json": { + "source": "iana", + "compressible": true + }, + "application/jwt": { + "source": "iana" + }, + "application/kpml-request+xml": { + "source": "iana" + }, + "application/kpml-response+xml": { + "source": "iana" + }, + "application/ld+json": { + "source": "iana", + "compressible": true, + "extensions": ["jsonld"] + }, + "application/link-format": { + "source": "iana" + }, + "application/load-control+xml": { + "source": "iana" + }, + "application/lost+xml": { + "source": "iana", + "extensions": ["lostxml"] + }, + "application/lostsync+xml": { + "source": "iana" + }, + "application/lxf": { + "source": "iana" + }, + "application/mac-binhex40": { + "source": "iana", + "extensions": ["hqx"] + }, + "application/mac-compactpro": { + "source": "apache", + "extensions": ["cpt"] + }, + "application/macwriteii": { + "source": "iana" + }, + "application/mads+xml": { + "source": "iana", + "extensions": ["mads"] + }, + "application/manifest+json": { + "charset": "UTF-8", + "compressible": true, + "extensions": ["webmanifest"] + }, + "application/marc": { + "source": "iana", + "extensions": ["mrc"] + }, + "application/marcxml+xml": { + "source": "iana", + "extensions": ["mrcx"] + }, + "application/mathematica": { + "source": "iana", + "extensions": ["ma","nb","mb"] + }, + "application/mathml+xml": { + "source": "iana", + "extensions": ["mathml"] + }, + "application/mathml-content+xml": { + "source": "iana" + }, + "application/mathml-presentation+xml": { + "source": "iana" + }, + "application/mbms-associated-procedure-description+xml": { + "source": "iana" + }, + "application/mbms-deregister+xml": { + "source": "iana" + }, + "application/mbms-envelope+xml": { + "source": "iana" + }, + "application/mbms-msk+xml": { + "source": "iana" + }, + "application/mbms-msk-response+xml": { + "source": "iana" + }, + "application/mbms-protection-description+xml": { + "source": "iana" + }, + "application/mbms-reception-report+xml": { + "source": "iana" + }, + "application/mbms-register+xml": { + "source": "iana" + }, + "application/mbms-register-response+xml": { + "source": "iana" + }, + "application/mbms-schedule+xml": { + "source": "iana" + }, + "application/mbms-user-service-description+xml": { + "source": "iana" + }, + "application/mbox": { + "source": "iana", + "extensions": ["mbox"] + }, + "application/media-policy-dataset+xml": { + "source": "iana" + }, + "application/media_control+xml": { + "source": "iana" + }, + "application/mediaservercontrol+xml": { + "source": "iana", + "extensions": ["mscml"] + }, + "application/merge-patch+json": { + "source": "iana", + "compressible": true + }, + "application/metalink+xml": { + "source": "apache", + "extensions": ["metalink"] + }, + "application/metalink4+xml": { + "source": "iana", + "extensions": ["meta4"] + }, + "application/mets+xml": { + "source": "iana", + "extensions": ["mets"] + }, + "application/mf4": { + "source": "iana" + }, + "application/mikey": { + "source": "iana" + }, + "application/mods+xml": { + "source": "iana", + "extensions": ["mods"] + }, + "application/moss-keys": { + "source": "iana" + }, + "application/moss-signature": { + "source": "iana" + }, + "application/mosskey-data": { + "source": "iana" + }, + "application/mosskey-request": { + "source": "iana" + }, + "application/mp21": { + "source": "iana", + "extensions": ["m21","mp21"] + }, + "application/mp4": { + "source": "iana", + "extensions": ["mp4s","m4p"] + }, + "application/mpeg4-generic": { + "source": "iana" + }, + "application/mpeg4-iod": { + "source": "iana" + }, + "application/mpeg4-iod-xmt": { + "source": "iana" + }, + "application/mrb-consumer+xml": { + "source": "iana" + }, + "application/mrb-publish+xml": { + "source": "iana" + }, + "application/msc-ivr+xml": { + "source": "iana" + }, + "application/msc-mixer+xml": { + "source": "iana" + }, + "application/msword": { + "source": "iana", + "compressible": false, + "extensions": ["doc","dot"] + }, + "application/mxf": { + "source": "iana", + "extensions": ["mxf"] + }, + "application/nasdata": { + "source": "iana" + }, + "application/news-checkgroups": { + "source": "iana" + }, + "application/news-groupinfo": { + "source": "iana" + }, + "application/news-transmission": { + "source": "iana" + }, + "application/nlsml+xml": { + "source": "iana" + }, + "application/nss": { + "source": "iana" + }, + "application/ocsp-request": { + "source": "iana" + }, + "application/ocsp-response": { + "source": "iana" + }, + "application/octet-stream": { + "source": "iana", + "compressible": false, + "extensions": ["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","exe","dll","deb","dmg","iso","img","msi","msp","msm","buffer"] + }, + "application/oda": { + "source": "iana", + "extensions": ["oda"] + }, + "application/odx": { + "source": "iana" + }, + "application/oebps-package+xml": { + "source": "iana", + "extensions": ["opf"] + }, + "application/ogg": { + "source": "iana", + "compressible": false, + "extensions": ["ogx"] + }, + "application/omdoc+xml": { + "source": "apache", + "extensions": ["omdoc"] + }, + "application/onenote": { + "source": "apache", + "extensions": ["onetoc","onetoc2","onetmp","onepkg"] + }, + "application/oxps": { + "source": "iana", + "extensions": ["oxps"] + }, + "application/p2p-overlay+xml": { + "source": "iana" + }, + "application/parityfec": { + "source": "iana" + }, + "application/patch-ops-error+xml": { + "source": "iana", + "extensions": ["xer"] + }, + "application/pdf": { + "source": "iana", + "compressible": false, + "extensions": ["pdf"] + }, + "application/pdx": { + "source": "iana" + }, + "application/pgp-encrypted": { + "source": "iana", + "compressible": false, + "extensions": ["pgp"] + }, + "application/pgp-keys": { + "source": "iana" + }, + "application/pgp-signature": { + "source": "iana", + "extensions": ["asc","sig"] + }, + "application/pics-rules": { + "source": "apache", + "extensions": ["prf"] + }, + "application/pidf+xml": { + "source": "iana" + }, + "application/pidf-diff+xml": { + "source": "iana" + }, + "application/pkcs10": { + "source": "iana", + "extensions": ["p10"] + }, + "application/pkcs12": { + "source": "iana" + }, + "application/pkcs7-mime": { + "source": "iana", + "extensions": ["p7m","p7c"] + }, + "application/pkcs7-signature": { + "source": "iana", + "extensions": ["p7s"] + }, + "application/pkcs8": { + "source": "iana", + "extensions": ["p8"] + }, + "application/pkix-attr-cert": { + "source": "iana", + "extensions": ["ac"] + }, + "application/pkix-cert": { + "source": "iana", + "extensions": ["cer"] + }, + "application/pkix-crl": { + "source": "iana", + "extensions": ["crl"] + }, + "application/pkix-pkipath": { + "source": "iana", + "extensions": ["pkipath"] + }, + "application/pkixcmp": { + "source": "iana", + "extensions": ["pki"] + }, + "application/pls+xml": { + "source": "iana", + "extensions": ["pls"] + }, + "application/poc-settings+xml": { + "source": "iana" + }, + "application/postscript": { + "source": "iana", + "compressible": true, + "extensions": ["ai","eps","ps"] + }, + "application/provenance+xml": { + "source": "iana" + }, + "application/prs.alvestrand.titrax-sheet": { + "source": "iana" + }, + "application/prs.cww": { + "source": "iana", + "extensions": ["cww"] + }, + "application/prs.hpub+zip": { + "source": "iana" + }, + "application/prs.nprend": { + "source": "iana" + }, + "application/prs.plucker": { + "source": "iana" + }, + "application/prs.rdf-xml-crypt": { + "source": "iana" + }, + "application/prs.xsf+xml": { + "source": "iana" + }, + "application/pskc+xml": { + "source": "iana", + "extensions": ["pskcxml"] + }, + "application/qsig": { + "source": "iana" + }, + "application/raptorfec": { + "source": "iana" + }, + "application/rdap+json": { + "source": "iana", + "compressible": true + }, + "application/rdf+xml": { + "source": "iana", + "compressible": true, + "extensions": ["rdf"] + }, + "application/reginfo+xml": { + "source": "iana", + "extensions": ["rif"] + }, + "application/relax-ng-compact-syntax": { + "source": "iana", + "extensions": ["rnc"] + }, + "application/remote-printing": { + "source": "iana" + }, + "application/reputon+json": { + "source": "iana", + "compressible": true + }, + "application/resource-lists+xml": { + "source": "iana", + "extensions": ["rl"] + }, + "application/resource-lists-diff+xml": { + "source": "iana", + "extensions": ["rld"] + }, + "application/rfc+xml": { + "source": "iana" + }, + "application/riscos": { + "source": "iana" + }, + "application/rlmi+xml": { + "source": "iana" + }, + "application/rls-services+xml": { + "source": "iana", + "extensions": ["rs"] + }, + "application/rpki-ghostbusters": { + "source": "iana", + "extensions": ["gbr"] + }, + "application/rpki-manifest": { + "source": "iana", + "extensions": ["mft"] + }, + "application/rpki-roa": { + "source": "iana", + "extensions": ["roa"] + }, + "application/rpki-updown": { + "source": "iana" + }, + "application/rsd+xml": { + "source": "apache", + "extensions": ["rsd"] + }, + "application/rss+xml": { + "source": "apache", + "compressible": true, + "extensions": ["rss"] + }, + "application/rtf": { + "source": "iana", + "compressible": true, + "extensions": ["rtf"] + }, + "application/rtploopback": { + "source": "iana" + }, + "application/rtx": { + "source": "iana" + }, + "application/samlassertion+xml": { + "source": "iana" + }, + "application/samlmetadata+xml": { + "source": "iana" + }, + "application/sbml+xml": { + "source": "iana", + "extensions": ["sbml"] + }, + "application/scaip+xml": { + "source": "iana" + }, + "application/scim+json": { + "source": "iana", + "compressible": true + }, + "application/scvp-cv-request": { + "source": "iana", + "extensions": ["scq"] + }, + "application/scvp-cv-response": { + "source": "iana", + "extensions": ["scs"] + }, + "application/scvp-vp-request": { + "source": "iana", + "extensions": ["spq"] + }, + "application/scvp-vp-response": { + "source": "iana", + "extensions": ["spp"] + }, + "application/sdp": { + "source": "iana", + "extensions": ["sdp"] + }, + "application/sep+xml": { + "source": "iana" + }, + "application/sep-exi": { + "source": "iana" + }, + "application/session-info": { + "source": "iana" + }, + "application/set-payment": { + "source": "iana" + }, + "application/set-payment-initiation": { + "source": "iana", + "extensions": ["setpay"] + }, + "application/set-registration": { + "source": "iana" + }, + "application/set-registration-initiation": { + "source": "iana", + "extensions": ["setreg"] + }, + "application/sgml": { + "source": "iana" + }, + "application/sgml-open-catalog": { + "source": "iana" + }, + "application/shf+xml": { + "source": "iana", + "extensions": ["shf"] + }, + "application/sieve": { + "source": "iana" + }, + "application/simple-filter+xml": { + "source": "iana" + }, + "application/simple-message-summary": { + "source": "iana" + }, + "application/simplesymbolcontainer": { + "source": "iana" + }, + "application/slate": { + "source": "iana" + }, + "application/smil": { + "source": "iana" + }, + "application/smil+xml": { + "source": "iana", + "extensions": ["smi","smil"] + }, + "application/smpte336m": { + "source": "iana" + }, + "application/soap+fastinfoset": { + "source": "iana" + }, + "application/soap+xml": { + "source": "iana", + "compressible": true + }, + "application/sparql-query": { + "source": "iana", + "extensions": ["rq"] + }, + "application/sparql-results+xml": { + "source": "iana", + "extensions": ["srx"] + }, + "application/spirits-event+xml": { + "source": "iana" + }, + "application/sql": { + "source": "iana" + }, + "application/srgs": { + "source": "iana", + "extensions": ["gram"] + }, + "application/srgs+xml": { + "source": "iana", + "extensions": ["grxml"] + }, + "application/sru+xml": { + "source": "iana", + "extensions": ["sru"] + }, + "application/ssdl+xml": { + "source": "apache", + "extensions": ["ssdl"] + }, + "application/ssml+xml": { + "source": "iana", + "extensions": ["ssml"] + }, + "application/tamp-apex-update": { + "source": "iana" + }, + "application/tamp-apex-update-confirm": { + "source": "iana" + }, + "application/tamp-community-update": { + "source": "iana" + }, + "application/tamp-community-update-confirm": { + "source": "iana" + }, + "application/tamp-error": { + "source": "iana" + }, + "application/tamp-sequence-adjust": { + "source": "iana" + }, + "application/tamp-sequence-adjust-confirm": { + "source": "iana" + }, + "application/tamp-status-query": { + "source": "iana" + }, + "application/tamp-status-response": { + "source": "iana" + }, + "application/tamp-update": { + "source": "iana" + }, + "application/tamp-update-confirm": { + "source": "iana" + }, + "application/tar": { + "compressible": true + }, + "application/tei+xml": { + "source": "iana", + "extensions": ["tei","teicorpus"] + }, + "application/thraud+xml": { + "source": "iana", + "extensions": ["tfi"] + }, + "application/timestamp-query": { + "source": "iana" + }, + "application/timestamp-reply": { + "source": "iana" + }, + "application/timestamped-data": { + "source": "iana", + "extensions": ["tsd"] + }, + "application/ttml+xml": { + "source": "iana" + }, + "application/tve-trigger": { + "source": "iana" + }, + "application/ulpfec": { + "source": "iana" + }, + "application/urc-grpsheet+xml": { + "source": "iana" + }, + "application/urc-ressheet+xml": { + "source": "iana" + }, + "application/urc-targetdesc+xml": { + "source": "iana" + }, + "application/urc-uisocketdesc+xml": { + "source": "iana" + }, + "application/vcard+json": { + "source": "iana", + "compressible": true + }, + "application/vcard+xml": { + "source": "iana" + }, + "application/vemmi": { + "source": "iana" + }, + "application/vividence.scriptfile": { + "source": "apache" + }, + "application/vnd.3gpp-prose+xml": { + "source": "iana" + }, + "application/vnd.3gpp-prose-pc3ch+xml": { + "source": "iana" + }, + "application/vnd.3gpp.access-transfer-events+xml": { + "source": "iana" + }, + "application/vnd.3gpp.bsf+xml": { + "source": "iana" + }, + "application/vnd.3gpp.mid-call+xml": { + "source": "iana" + }, + "application/vnd.3gpp.pic-bw-large": { + "source": "iana", + "extensions": ["plb"] + }, + "application/vnd.3gpp.pic-bw-small": { + "source": "iana", + "extensions": ["psb"] + }, + "application/vnd.3gpp.pic-bw-var": { + "source": "iana", + "extensions": ["pvb"] + }, + "application/vnd.3gpp.sms": { + "source": "iana" + }, + "application/vnd.3gpp.srvcc-ext+xml": { + "source": "iana" + }, + "application/vnd.3gpp.srvcc-info+xml": { + "source": "iana" + }, + "application/vnd.3gpp.state-and-event-info+xml": { + "source": "iana" + }, + "application/vnd.3gpp.ussd+xml": { + "source": "iana" + }, + "application/vnd.3gpp2.bcmcsinfo+xml": { + "source": "iana" + }, + "application/vnd.3gpp2.sms": { + "source": "iana" + }, + "application/vnd.3gpp2.tcap": { + "source": "iana", + "extensions": ["tcap"] + }, + "application/vnd.3m.post-it-notes": { + "source": "iana", + "extensions": ["pwn"] + }, + "application/vnd.accpac.simply.aso": { + "source": "iana", + "extensions": ["aso"] + }, + "application/vnd.accpac.simply.imp": { + "source": "iana", + "extensions": ["imp"] + }, + "application/vnd.acucobol": { + "source": "iana", + "extensions": ["acu"] + }, + "application/vnd.acucorp": { + "source": "iana", + "extensions": ["atc","acutc"] + }, + "application/vnd.adobe.air-application-installer-package+zip": { + "source": "apache", + "extensions": ["air"] + }, + "application/vnd.adobe.flash.movie": { + "source": "iana" + }, + "application/vnd.adobe.formscentral.fcdt": { + "source": "iana", + "extensions": ["fcdt"] + }, + "application/vnd.adobe.fxp": { + "source": "iana", + "extensions": ["fxp","fxpl"] + }, + "application/vnd.adobe.partial-upload": { + "source": "iana" + }, + "application/vnd.adobe.xdp+xml": { + "source": "iana", + "extensions": ["xdp"] + }, + "application/vnd.adobe.xfdf": { + "source": "iana", + "extensions": ["xfdf"] + }, + "application/vnd.aether.imp": { + "source": "iana" + }, + "application/vnd.ah-barcode": { + "source": "iana" + }, + "application/vnd.ahead.space": { + "source": "iana", + "extensions": ["ahead"] + }, + "application/vnd.airzip.filesecure.azf": { + "source": "iana", + "extensions": ["azf"] + }, + "application/vnd.airzip.filesecure.azs": { + "source": "iana", + "extensions": ["azs"] + }, + "application/vnd.amazon.ebook": { + "source": "apache", + "extensions": ["azw"] + }, + "application/vnd.americandynamics.acc": { + "source": "iana", + "extensions": ["acc"] + }, + "application/vnd.amiga.ami": { + "source": "iana", + "extensions": ["ami"] + }, + "application/vnd.amundsen.maze+xml": { + "source": "iana" + }, + "application/vnd.android.package-archive": { + "source": "apache", + "compressible": false, + "extensions": ["apk"] + }, + "application/vnd.anki": { + "source": "iana" + }, + "application/vnd.anser-web-certificate-issue-initiation": { + "source": "iana", + "extensions": ["cii"] + }, + "application/vnd.anser-web-funds-transfer-initiation": { + "source": "apache", + "extensions": ["fti"] + }, + "application/vnd.antix.game-component": { + "source": "iana", + "extensions": ["atx"] + }, + "application/vnd.apache.thrift.binary": { + "source": "iana" + }, + "application/vnd.apache.thrift.compact": { + "source": "iana" + }, + "application/vnd.apache.thrift.json": { + "source": "iana" + }, + "application/vnd.api+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.apple.installer+xml": { + "source": "iana", + "extensions": ["mpkg"] + }, + "application/vnd.apple.mpegurl": { + "source": "iana", + "extensions": ["m3u8"] + }, + "application/vnd.apple.pkpass": { + "compressible": false, + "extensions": ["pkpass"] + }, + "application/vnd.arastra.swi": { + "source": "iana" + }, + "application/vnd.aristanetworks.swi": { + "source": "iana", + "extensions": ["swi"] + }, + "application/vnd.artsquare": { + "source": "iana" + }, + "application/vnd.astraea-software.iota": { + "source": "iana", + "extensions": ["iota"] + }, + "application/vnd.audiograph": { + "source": "iana", + "extensions": ["aep"] + }, + "application/vnd.autopackage": { + "source": "iana" + }, + "application/vnd.avistar+xml": { + "source": "iana" + }, + "application/vnd.balsamiq.bmml+xml": { + "source": "iana" + }, + "application/vnd.balsamiq.bmpr": { + "source": "iana" + }, + "application/vnd.bekitzur-stech+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.biopax.rdf+xml": { + "source": "iana" + }, + "application/vnd.blueice.multipass": { + "source": "iana", + "extensions": ["mpm"] + }, + "application/vnd.bluetooth.ep.oob": { + "source": "iana" + }, + "application/vnd.bluetooth.le.oob": { + "source": "iana" + }, + "application/vnd.bmi": { + "source": "iana", + "extensions": ["bmi"] + }, + "application/vnd.businessobjects": { + "source": "iana", + "extensions": ["rep"] + }, + "application/vnd.cab-jscript": { + "source": "iana" + }, + "application/vnd.canon-cpdl": { + "source": "iana" + }, + "application/vnd.canon-lips": { + "source": "iana" + }, + "application/vnd.cendio.thinlinc.clientconf": { + "source": "iana" + }, + "application/vnd.century-systems.tcp_stream": { + "source": "iana" + }, + "application/vnd.chemdraw+xml": { + "source": "iana", + "extensions": ["cdxml"] + }, + "application/vnd.chipnuts.karaoke-mmd": { + "source": "iana", + "extensions": ["mmd"] + }, + "application/vnd.cinderella": { + "source": "iana", + "extensions": ["cdy"] + }, + "application/vnd.cirpack.isdn-ext": { + "source": "iana" + }, + "application/vnd.citationstyles.style+xml": { + "source": "iana" + }, + "application/vnd.claymore": { + "source": "iana", + "extensions": ["cla"] + }, + "application/vnd.cloanto.rp9": { + "source": "iana", + "extensions": ["rp9"] + }, + "application/vnd.clonk.c4group": { + "source": "iana", + "extensions": ["c4g","c4d","c4f","c4p","c4u"] + }, + "application/vnd.cluetrust.cartomobile-config": { + "source": "iana", + "extensions": ["c11amc"] + }, + "application/vnd.cluetrust.cartomobile-config-pkg": { + "source": "iana", + "extensions": ["c11amz"] + }, + "application/vnd.coffeescript": { + "source": "iana" + }, + "application/vnd.collection+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.collection.doc+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.collection.next+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.commerce-battelle": { + "source": "iana" + }, + "application/vnd.commonspace": { + "source": "iana", + "extensions": ["csp"] + }, + "application/vnd.contact.cmsg": { + "source": "iana", + "extensions": ["cdbcmsg"] + }, + "application/vnd.cosmocaller": { + "source": "iana", + "extensions": ["cmc"] + }, + "application/vnd.crick.clicker": { + "source": "iana", + "extensions": ["clkx"] + }, + "application/vnd.crick.clicker.keyboard": { + "source": "iana", + "extensions": ["clkk"] + }, + "application/vnd.crick.clicker.palette": { + "source": "iana", + "extensions": ["clkp"] + }, + "application/vnd.crick.clicker.template": { + "source": "iana", + "extensions": ["clkt"] + }, + "application/vnd.crick.clicker.wordbank": { + "source": "iana", + "extensions": ["clkw"] + }, + "application/vnd.criticaltools.wbs+xml": { + "source": "iana", + "extensions": ["wbs"] + }, + "application/vnd.ctc-posml": { + "source": "iana", + "extensions": ["pml"] + }, + "application/vnd.ctct.ws+xml": { + "source": "iana" + }, + "application/vnd.cups-pdf": { + "source": "iana" + }, + "application/vnd.cups-postscript": { + "source": "iana" + }, + "application/vnd.cups-ppd": { + "source": "iana", + "extensions": ["ppd"] + }, + "application/vnd.cups-raster": { + "source": "iana" + }, + "application/vnd.cups-raw": { + "source": "iana" + }, + "application/vnd.curl": { + "source": "iana" + }, + "application/vnd.curl.car": { + "source": "apache", + "extensions": ["car"] + }, + "application/vnd.curl.pcurl": { + "source": "apache", + "extensions": ["pcurl"] + }, + "application/vnd.cyan.dean.root+xml": { + "source": "iana" + }, + "application/vnd.cybank": { + "source": "iana" + }, + "application/vnd.dart": { + "source": "iana", + "compressible": true, + "extensions": ["dart"] + }, + "application/vnd.data-vision.rdz": { + "source": "iana", + "extensions": ["rdz"] + }, + "application/vnd.debian.binary-package": { + "source": "iana" + }, + "application/vnd.dece.data": { + "source": "iana", + "extensions": ["uvf","uvvf","uvd","uvvd"] + }, + "application/vnd.dece.ttml+xml": { + "source": "iana", + "extensions": ["uvt","uvvt"] + }, + "application/vnd.dece.unspecified": { + "source": "iana", + "extensions": ["uvx","uvvx"] + }, + "application/vnd.dece.zip": { + "source": "iana", + "extensions": ["uvz","uvvz"] + }, + "application/vnd.denovo.fcselayout-link": { + "source": "iana", + "extensions": ["fe_launch"] + }, + "application/vnd.desmume-movie": { + "source": "iana" + }, + "application/vnd.dir-bi.plate-dl-nosuffix": { + "source": "iana" + }, + "application/vnd.dm.delegation+xml": { + "source": "iana" + }, + "application/vnd.dna": { + "source": "iana", + "extensions": ["dna"] + }, + "application/vnd.document+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.dolby.mlp": { + "source": "apache", + "extensions": ["mlp"] + }, + "application/vnd.dolby.mobile.1": { + "source": "iana" + }, + "application/vnd.dolby.mobile.2": { + "source": "iana" + }, + "application/vnd.doremir.scorecloud-binary-document": { + "source": "iana" + }, + "application/vnd.dpgraph": { + "source": "iana", + "extensions": ["dpg"] + }, + "application/vnd.dreamfactory": { + "source": "iana", + "extensions": ["dfac"] + }, + "application/vnd.drive+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ds-keypoint": { + "source": "apache", + "extensions": ["kpxx"] + }, + "application/vnd.dtg.local": { + "source": "iana" + }, + "application/vnd.dtg.local.flash": { + "source": "iana" + }, + "application/vnd.dtg.local.html": { + "source": "iana" + }, + "application/vnd.dvb.ait": { + "source": "iana", + "extensions": ["ait"] + }, + "application/vnd.dvb.dvbj": { + "source": "iana" + }, + "application/vnd.dvb.esgcontainer": { + "source": "iana" + }, + "application/vnd.dvb.ipdcdftnotifaccess": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgaccess": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgaccess2": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgpdd": { + "source": "iana" + }, + "application/vnd.dvb.ipdcroaming": { + "source": "iana" + }, + "application/vnd.dvb.iptv.alfec-base": { + "source": "iana" + }, + "application/vnd.dvb.iptv.alfec-enhancement": { + "source": "iana" + }, + "application/vnd.dvb.notif-aggregate-root+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-container+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-generic+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-ia-msglist+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-ia-registration-request+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-ia-registration-response+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-init+xml": { + "source": "iana" + }, + "application/vnd.dvb.pfr": { + "source": "iana" + }, + "application/vnd.dvb.service": { + "source": "iana", + "extensions": ["svc"] + }, + "application/vnd.dxr": { + "source": "iana" + }, + "application/vnd.dynageo": { + "source": "iana", + "extensions": ["geo"] + }, + "application/vnd.dzr": { + "source": "iana" + }, + "application/vnd.easykaraoke.cdgdownload": { + "source": "iana" + }, + "application/vnd.ecdis-update": { + "source": "iana" + }, + "application/vnd.ecowin.chart": { + "source": "iana", + "extensions": ["mag"] + }, + "application/vnd.ecowin.filerequest": { + "source": "iana" + }, + "application/vnd.ecowin.fileupdate": { + "source": "iana" + }, + "application/vnd.ecowin.series": { + "source": "iana" + }, + "application/vnd.ecowin.seriesrequest": { + "source": "iana" + }, + "application/vnd.ecowin.seriesupdate": { + "source": "iana" + }, + "application/vnd.emclient.accessrequest+xml": { + "source": "iana" + }, + "application/vnd.enliven": { + "source": "iana", + "extensions": ["nml"] + }, + "application/vnd.enphase.envoy": { + "source": "iana" + }, + "application/vnd.eprints.data+xml": { + "source": "iana" + }, + "application/vnd.epson.esf": { + "source": "iana", + "extensions": ["esf"] + }, + "application/vnd.epson.msf": { + "source": "iana", + "extensions": ["msf"] + }, + "application/vnd.epson.quickanime": { + "source": "iana", + "extensions": ["qam"] + }, + "application/vnd.epson.salt": { + "source": "iana", + "extensions": ["slt"] + }, + "application/vnd.epson.ssf": { + "source": "iana", + "extensions": ["ssf"] + }, + "application/vnd.ericsson.quickcall": { + "source": "iana" + }, + "application/vnd.eszigno3+xml": { + "source": "iana", + "extensions": ["es3","et3"] + }, + "application/vnd.etsi.aoc+xml": { + "source": "iana" + }, + "application/vnd.etsi.asic-e+zip": { + "source": "iana" + }, + "application/vnd.etsi.asic-s+zip": { + "source": "iana" + }, + "application/vnd.etsi.cug+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvcommand+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvdiscovery+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvprofile+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsad-bc+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsad-cod+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsad-npvr+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvservice+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsync+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvueprofile+xml": { + "source": "iana" + }, + "application/vnd.etsi.mcid+xml": { + "source": "iana" + }, + "application/vnd.etsi.mheg5": { + "source": "iana" + }, + "application/vnd.etsi.overload-control-policy-dataset+xml": { + "source": "iana" + }, + "application/vnd.etsi.pstn+xml": { + "source": "iana" + }, + "application/vnd.etsi.sci+xml": { + "source": "iana" + }, + "application/vnd.etsi.simservs+xml": { + "source": "iana" + }, + "application/vnd.etsi.timestamp-token": { + "source": "iana" + }, + "application/vnd.etsi.tsl+xml": { + "source": "iana" + }, + "application/vnd.etsi.tsl.der": { + "source": "iana" + }, + "application/vnd.eudora.data": { + "source": "iana" + }, + "application/vnd.ezpix-album": { + "source": "iana", + "extensions": ["ez2"] + }, + "application/vnd.ezpix-package": { + "source": "iana", + "extensions": ["ez3"] + }, + "application/vnd.f-secure.mobile": { + "source": "iana" + }, + "application/vnd.fastcopy-disk-image": { + "source": "iana" + }, + "application/vnd.fdf": { + "source": "iana", + "extensions": ["fdf"] + }, + "application/vnd.fdsn.mseed": { + "source": "iana", + "extensions": ["mseed"] + }, + "application/vnd.fdsn.seed": { + "source": "iana", + "extensions": ["seed","dataless"] + }, + "application/vnd.ffsns": { + "source": "iana" + }, + "application/vnd.fints": { + "source": "iana" + }, + "application/vnd.firemonkeys.cloudcell": { + "source": "iana" + }, + "application/vnd.flographit": { + "source": "iana", + "extensions": ["gph"] + }, + "application/vnd.fluxtime.clip": { + "source": "iana", + "extensions": ["ftc"] + }, + "application/vnd.font-fontforge-sfd": { + "source": "iana" + }, + "application/vnd.framemaker": { + "source": "iana", + "extensions": ["fm","frame","maker","book"] + }, + "application/vnd.frogans.fnc": { + "source": "iana", + "extensions": ["fnc"] + }, + "application/vnd.frogans.ltf": { + "source": "iana", + "extensions": ["ltf"] + }, + "application/vnd.fsc.weblaunch": { + "source": "iana", + "extensions": ["fsc"] + }, + "application/vnd.fujitsu.oasys": { + "source": "iana", + "extensions": ["oas"] + }, + "application/vnd.fujitsu.oasys2": { + "source": "iana", + "extensions": ["oa2"] + }, + "application/vnd.fujitsu.oasys3": { + "source": "iana", + "extensions": ["oa3"] + }, + "application/vnd.fujitsu.oasysgp": { + "source": "iana", + "extensions": ["fg5"] + }, + "application/vnd.fujitsu.oasysprs": { + "source": "iana", + "extensions": ["bh2"] + }, + "application/vnd.fujixerox.art-ex": { + "source": "iana" + }, + "application/vnd.fujixerox.art4": { + "source": "iana" + }, + "application/vnd.fujixerox.ddd": { + "source": "iana", + "extensions": ["ddd"] + }, + "application/vnd.fujixerox.docuworks": { + "source": "iana", + "extensions": ["xdw"] + }, + "application/vnd.fujixerox.docuworks.binder": { + "source": "iana", + "extensions": ["xbd"] + }, + "application/vnd.fujixerox.docuworks.container": { + "source": "iana" + }, + "application/vnd.fujixerox.hbpl": { + "source": "iana" + }, + "application/vnd.fut-misnet": { + "source": "iana" + }, + "application/vnd.fuzzysheet": { + "source": "iana", + "extensions": ["fzs"] + }, + "application/vnd.genomatix.tuxedo": { + "source": "iana", + "extensions": ["txd"] + }, + "application/vnd.geo+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.geocube+xml": { + "source": "iana" + }, + "application/vnd.geogebra.file": { + "source": "iana", + "extensions": ["ggb"] + }, + "application/vnd.geogebra.tool": { + "source": "iana", + "extensions": ["ggt"] + }, + "application/vnd.geometry-explorer": { + "source": "iana", + "extensions": ["gex","gre"] + }, + "application/vnd.geonext": { + "source": "iana", + "extensions": ["gxt"] + }, + "application/vnd.geoplan": { + "source": "iana", + "extensions": ["g2w"] + }, + "application/vnd.geospace": { + "source": "iana", + "extensions": ["g3w"] + }, + "application/vnd.gerber": { + "source": "iana" + }, + "application/vnd.globalplatform.card-content-mgt": { + "source": "iana" + }, + "application/vnd.globalplatform.card-content-mgt-response": { + "source": "iana" + }, + "application/vnd.gmx": { + "source": "iana", + "extensions": ["gmx"] + }, + "application/vnd.google-earth.kml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["kml"] + }, + "application/vnd.google-earth.kmz": { + "source": "iana", + "compressible": false, + "extensions": ["kmz"] + }, + "application/vnd.gov.sk.e-form+xml": { + "source": "iana" + }, + "application/vnd.gov.sk.e-form+zip": { + "source": "iana" + }, + "application/vnd.gov.sk.xmldatacontainer+xml": { + "source": "iana" + }, + "application/vnd.grafeq": { + "source": "iana", + "extensions": ["gqf","gqs"] + }, + "application/vnd.gridmp": { + "source": "iana" + }, + "application/vnd.groove-account": { + "source": "iana", + "extensions": ["gac"] + }, + "application/vnd.groove-help": { + "source": "iana", + "extensions": ["ghf"] + }, + "application/vnd.groove-identity-message": { + "source": "iana", + "extensions": ["gim"] + }, + "application/vnd.groove-injector": { + "source": "iana", + "extensions": ["grv"] + }, + "application/vnd.groove-tool-message": { + "source": "iana", + "extensions": ["gtm"] + }, + "application/vnd.groove-tool-template": { + "source": "iana", + "extensions": ["tpl"] + }, + "application/vnd.groove-vcard": { + "source": "iana", + "extensions": ["vcg"] + }, + "application/vnd.hal+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hal+xml": { + "source": "iana", + "extensions": ["hal"] + }, + "application/vnd.handheld-entertainment+xml": { + "source": "iana", + "extensions": ["zmm"] + }, + "application/vnd.hbci": { + "source": "iana", + "extensions": ["hbci"] + }, + "application/vnd.hcl-bireports": { + "source": "iana" + }, + "application/vnd.heroku+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hhe.lesson-player": { + "source": "iana", + "extensions": ["les"] + }, + "application/vnd.hp-hpgl": { + "source": "iana", + "extensions": ["hpgl"] + }, + "application/vnd.hp-hpid": { + "source": "iana", + "extensions": ["hpid"] + }, + "application/vnd.hp-hps": { + "source": "iana", + "extensions": ["hps"] + }, + "application/vnd.hp-jlyt": { + "source": "iana", + "extensions": ["jlt"] + }, + "application/vnd.hp-pcl": { + "source": "iana", + "extensions": ["pcl"] + }, + "application/vnd.hp-pclxl": { + "source": "iana", + "extensions": ["pclxl"] + }, + "application/vnd.httphone": { + "source": "iana" + }, + "application/vnd.hydrostatix.sof-data": { + "source": "iana", + "extensions": ["sfd-hdstx"] + }, + "application/vnd.hyperdrive+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hzn-3d-crossword": { + "source": "iana" + }, + "application/vnd.ibm.afplinedata": { + "source": "iana" + }, + "application/vnd.ibm.electronic-media": { + "source": "iana" + }, + "application/vnd.ibm.minipay": { + "source": "iana", + "extensions": ["mpy"] + }, + "application/vnd.ibm.modcap": { + "source": "iana", + "extensions": ["afp","listafp","list3820"] + }, + "application/vnd.ibm.rights-management": { + "source": "iana", + "extensions": ["irm"] + }, + "application/vnd.ibm.secure-container": { + "source": "iana", + "extensions": ["sc"] + }, + "application/vnd.iccprofile": { + "source": "iana", + "extensions": ["icc","icm"] + }, + "application/vnd.ieee.1905": { + "source": "iana" + }, + "application/vnd.igloader": { + "source": "iana", + "extensions": ["igl"] + }, + "application/vnd.immervision-ivp": { + "source": "iana", + "extensions": ["ivp"] + }, + "application/vnd.immervision-ivu": { + "source": "iana", + "extensions": ["ivu"] + }, + "application/vnd.ims.imsccv1p1": { + "source": "iana" + }, + "application/vnd.ims.imsccv1p2": { + "source": "iana" + }, + "application/vnd.ims.imsccv1p3": { + "source": "iana" + }, + "application/vnd.ims.lis.v2.result+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolconsumerprofile+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolproxy+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolproxy.id+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolsettings+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolsettings.simple+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.informedcontrol.rms+xml": { + "source": "iana" + }, + "application/vnd.informix-visionary": { + "source": "iana" + }, + "application/vnd.infotech.project": { + "source": "iana" + }, + "application/vnd.infotech.project+xml": { + "source": "iana" + }, + "application/vnd.innopath.wamp.notification": { + "source": "iana" + }, + "application/vnd.insors.igm": { + "source": "iana", + "extensions": ["igm"] + }, + "application/vnd.intercon.formnet": { + "source": "iana", + "extensions": ["xpw","xpx"] + }, + "application/vnd.intergeo": { + "source": "iana", + "extensions": ["i2g"] + }, + "application/vnd.intertrust.digibox": { + "source": "iana" + }, + "application/vnd.intertrust.nncp": { + "source": "iana" + }, + "application/vnd.intu.qbo": { + "source": "iana", + "extensions": ["qbo"] + }, + "application/vnd.intu.qfx": { + "source": "iana", + "extensions": ["qfx"] + }, + "application/vnd.iptc.g2.catalogitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.conceptitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.knowledgeitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.newsitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.newsmessage+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.packageitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.planningitem+xml": { + "source": "iana" + }, + "application/vnd.ipunplugged.rcprofile": { + "source": "iana", + "extensions": ["rcprofile"] + }, + "application/vnd.irepository.package+xml": { + "source": "iana", + "extensions": ["irp"] + }, + "application/vnd.is-xpr": { + "source": "iana", + "extensions": ["xpr"] + }, + "application/vnd.isac.fcs": { + "source": "iana", + "extensions": ["fcs"] + }, + "application/vnd.jam": { + "source": "iana", + "extensions": ["jam"] + }, + "application/vnd.japannet-directory-service": { + "source": "iana" + }, + "application/vnd.japannet-jpnstore-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-payment-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-registration": { + "source": "iana" + }, + "application/vnd.japannet-registration-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-setstore-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-verification": { + "source": "iana" + }, + "application/vnd.japannet-verification-wakeup": { + "source": "iana" + }, + "application/vnd.jcp.javame.midlet-rms": { + "source": "iana", + "extensions": ["rms"] + }, + "application/vnd.jisp": { + "source": "iana", + "extensions": ["jisp"] + }, + "application/vnd.joost.joda-archive": { + "source": "iana", + "extensions": ["joda"] + }, + "application/vnd.jsk.isdn-ngn": { + "source": "iana" + }, + "application/vnd.kahootz": { + "source": "iana", + "extensions": ["ktz","ktr"] + }, + "application/vnd.kde.karbon": { + "source": "iana", + "extensions": ["karbon"] + }, + "application/vnd.kde.kchart": { + "source": "iana", + "extensions": ["chrt"] + }, + "application/vnd.kde.kformula": { + "source": "iana", + "extensions": ["kfo"] + }, + "application/vnd.kde.kivio": { + "source": "iana", + "extensions": ["flw"] + }, + "application/vnd.kde.kontour": { + "source": "iana", + "extensions": ["kon"] + }, + "application/vnd.kde.kpresenter": { + "source": "iana", + "extensions": ["kpr","kpt"] + }, + "application/vnd.kde.kspread": { + "source": "iana", + "extensions": ["ksp"] + }, + "application/vnd.kde.kword": { + "source": "iana", + "extensions": ["kwd","kwt"] + }, + "application/vnd.kenameaapp": { + "source": "iana", + "extensions": ["htke"] + }, + "application/vnd.kidspiration": { + "source": "iana", + "extensions": ["kia"] + }, + "application/vnd.kinar": { + "source": "iana", + "extensions": ["kne","knp"] + }, + "application/vnd.koan": { + "source": "iana", + "extensions": ["skp","skd","skt","skm"] + }, + "application/vnd.kodak-descriptor": { + "source": "iana", + "extensions": ["sse"] + }, + "application/vnd.las.las+xml": { + "source": "iana", + "extensions": ["lasxml"] + }, + "application/vnd.liberty-request+xml": { + "source": "iana" + }, + "application/vnd.llamagraphics.life-balance.desktop": { + "source": "iana", + "extensions": ["lbd"] + }, + "application/vnd.llamagraphics.life-balance.exchange+xml": { + "source": "iana", + "extensions": ["lbe"] + }, + "application/vnd.lotus-1-2-3": { + "source": "iana", + "extensions": ["123"] + }, + "application/vnd.lotus-approach": { + "source": "iana", + "extensions": ["apr"] + }, + "application/vnd.lotus-freelance": { + "source": "iana", + "extensions": ["pre"] + }, + "application/vnd.lotus-notes": { + "source": "iana", + "extensions": ["nsf"] + }, + "application/vnd.lotus-organizer": { + "source": "iana", + "extensions": ["org"] + }, + "application/vnd.lotus-screencam": { + "source": "iana", + "extensions": ["scm"] + }, + "application/vnd.lotus-wordpro": { + "source": "iana", + "extensions": ["lwp"] + }, + "application/vnd.macports.portpkg": { + "source": "iana", + "extensions": ["portpkg"] + }, + "application/vnd.marlin.drm.actiontoken+xml": { + "source": "iana" + }, + "application/vnd.marlin.drm.conftoken+xml": { + "source": "iana" + }, + "application/vnd.marlin.drm.license+xml": { + "source": "iana" + }, + "application/vnd.marlin.drm.mdcf": { + "source": "iana" + }, + "application/vnd.mason+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.maxmind.maxmind-db": { + "source": "iana" + }, + "application/vnd.mcd": { + "source": "iana", + "extensions": ["mcd"] + }, + "application/vnd.medcalcdata": { + "source": "iana", + "extensions": ["mc1"] + }, + "application/vnd.mediastation.cdkey": { + "source": "iana", + "extensions": ["cdkey"] + }, + "application/vnd.meridian-slingshot": { + "source": "iana" + }, + "application/vnd.mfer": { + "source": "iana", + "extensions": ["mwf"] + }, + "application/vnd.mfmp": { + "source": "iana", + "extensions": ["mfm"] + }, + "application/vnd.micro+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.micrografx.flo": { + "source": "iana", + "extensions": ["flo"] + }, + "application/vnd.micrografx.igx": { + "source": "iana", + "extensions": ["igx"] + }, + "application/vnd.microsoft.portable-executable": { + "source": "iana" + }, + "application/vnd.miele+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.mif": { + "source": "iana", + "extensions": ["mif"] + }, + "application/vnd.minisoft-hp3000-save": { + "source": "iana" + }, + "application/vnd.mitsubishi.misty-guard.trustweb": { + "source": "iana" + }, + "application/vnd.mobius.daf": { + "source": "iana", + "extensions": ["daf"] + }, + "application/vnd.mobius.dis": { + "source": "iana", + "extensions": ["dis"] + }, + "application/vnd.mobius.mbk": { + "source": "iana", + "extensions": ["mbk"] + }, + "application/vnd.mobius.mqy": { + "source": "iana", + "extensions": ["mqy"] + }, + "application/vnd.mobius.msl": { + "source": "iana", + "extensions": ["msl"] + }, + "application/vnd.mobius.plc": { + "source": "iana", + "extensions": ["plc"] + }, + "application/vnd.mobius.txf": { + "source": "iana", + "extensions": ["txf"] + }, + "application/vnd.mophun.application": { + "source": "iana", + "extensions": ["mpn"] + }, + "application/vnd.mophun.certificate": { + "source": "iana", + "extensions": ["mpc"] + }, + "application/vnd.motorola.flexsuite": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.adsi": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.fis": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.gotap": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.kmr": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.ttc": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.wem": { + "source": "iana" + }, + "application/vnd.motorola.iprm": { + "source": "iana" + }, + "application/vnd.mozilla.xul+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xul"] + }, + "application/vnd.ms-3mfdocument": { + "source": "iana" + }, + "application/vnd.ms-artgalry": { + "source": "iana", + "extensions": ["cil"] + }, + "application/vnd.ms-asf": { + "source": "iana" + }, + "application/vnd.ms-cab-compressed": { + "source": "iana", + "extensions": ["cab"] + }, + "application/vnd.ms-color.iccprofile": { + "source": "apache" + }, + "application/vnd.ms-excel": { + "source": "iana", + "compressible": false, + "extensions": ["xls","xlm","xla","xlc","xlt","xlw"] + }, + "application/vnd.ms-excel.addin.macroenabled.12": { + "source": "iana", + "extensions": ["xlam"] + }, + "application/vnd.ms-excel.sheet.binary.macroenabled.12": { + "source": "iana", + "extensions": ["xlsb"] + }, + "application/vnd.ms-excel.sheet.macroenabled.12": { + "source": "iana", + "extensions": ["xlsm"] + }, + "application/vnd.ms-excel.template.macroenabled.12": { + "source": "iana", + "extensions": ["xltm"] + }, + "application/vnd.ms-fontobject": { + "source": "iana", + "compressible": true, + "extensions": ["eot"] + }, + "application/vnd.ms-htmlhelp": { + "source": "iana", + "extensions": ["chm"] + }, + "application/vnd.ms-ims": { + "source": "iana", + "extensions": ["ims"] + }, + "application/vnd.ms-lrm": { + "source": "iana", + "extensions": ["lrm"] + }, + "application/vnd.ms-office.activex+xml": { + "source": "iana" + }, + "application/vnd.ms-officetheme": { + "source": "iana", + "extensions": ["thmx"] + }, + "application/vnd.ms-opentype": { + "source": "apache", + "compressible": true + }, + "application/vnd.ms-package.obfuscated-opentype": { + "source": "apache" + }, + "application/vnd.ms-pki.seccat": { + "source": "apache", + "extensions": ["cat"] + }, + "application/vnd.ms-pki.stl": { + "source": "apache", + "extensions": ["stl"] + }, + "application/vnd.ms-playready.initiator+xml": { + "source": "iana" + }, + "application/vnd.ms-powerpoint": { + "source": "iana", + "compressible": false, + "extensions": ["ppt","pps","pot"] + }, + "application/vnd.ms-powerpoint.addin.macroenabled.12": { + "source": "iana", + "extensions": ["ppam"] + }, + "application/vnd.ms-powerpoint.presentation.macroenabled.12": { + "source": "iana", + "extensions": ["pptm"] + }, + "application/vnd.ms-powerpoint.slide.macroenabled.12": { + "source": "iana", + "extensions": ["sldm"] + }, + "application/vnd.ms-powerpoint.slideshow.macroenabled.12": { + "source": "iana", + "extensions": ["ppsm"] + }, + "application/vnd.ms-powerpoint.template.macroenabled.12": { + "source": "iana", + "extensions": ["potm"] + }, + "application/vnd.ms-printing.printticket+xml": { + "source": "apache" + }, + "application/vnd.ms-project": { + "source": "iana", + "extensions": ["mpp","mpt"] + }, + "application/vnd.ms-tnef": { + "source": "iana" + }, + "application/vnd.ms-windows.printerpairing": { + "source": "iana" + }, + "application/vnd.ms-windows.wsd.oob": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.lic-chlg-req": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.lic-resp": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.meter-chlg-req": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.meter-resp": { + "source": "iana" + }, + "application/vnd.ms-word.document.macroenabled.12": { + "source": "iana", + "extensions": ["docm"] + }, + "application/vnd.ms-word.template.macroenabled.12": { + "source": "iana", + "extensions": ["dotm"] + }, + "application/vnd.ms-works": { + "source": "iana", + "extensions": ["wps","wks","wcm","wdb"] + }, + "application/vnd.ms-wpl": { + "source": "iana", + "extensions": ["wpl"] + }, + "application/vnd.ms-xpsdocument": { + "source": "iana", + "compressible": false, + "extensions": ["xps"] + }, + "application/vnd.msa-disk-image": { + "source": "iana" + }, + "application/vnd.mseq": { + "source": "iana", + "extensions": ["mseq"] + }, + "application/vnd.msign": { + "source": "iana" + }, + "application/vnd.multiad.creator": { + "source": "iana" + }, + "application/vnd.multiad.creator.cif": { + "source": "iana" + }, + "application/vnd.music-niff": { + "source": "iana" + }, + "application/vnd.musician": { + "source": "iana", + "extensions": ["mus"] + }, + "application/vnd.muvee.style": { + "source": "iana", + "extensions": ["msty"] + }, + "application/vnd.mynfc": { + "source": "iana", + "extensions": ["taglet"] + }, + "application/vnd.ncd.control": { + "source": "iana" + }, + "application/vnd.ncd.reference": { + "source": "iana" + }, + "application/vnd.nervana": { + "source": "iana" + }, + "application/vnd.netfpx": { + "source": "iana" + }, + "application/vnd.neurolanguage.nlu": { + "source": "iana", + "extensions": ["nlu"] + }, + "application/vnd.nintendo.nitro.rom": { + "source": "iana" + }, + "application/vnd.nintendo.snes.rom": { + "source": "iana" + }, + "application/vnd.nitf": { + "source": "iana", + "extensions": ["ntf","nitf"] + }, + "application/vnd.noblenet-directory": { + "source": "iana", + "extensions": ["nnd"] + }, + "application/vnd.noblenet-sealer": { + "source": "iana", + "extensions": ["nns"] + }, + "application/vnd.noblenet-web": { + "source": "iana", + "extensions": ["nnw"] + }, + "application/vnd.nokia.catalogs": { + "source": "iana" + }, + "application/vnd.nokia.conml+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.conml+xml": { + "source": "iana" + }, + "application/vnd.nokia.iptv.config+xml": { + "source": "iana" + }, + "application/vnd.nokia.isds-radio-presets": { + "source": "iana" + }, + "application/vnd.nokia.landmark+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.landmark+xml": { + "source": "iana" + }, + "application/vnd.nokia.landmarkcollection+xml": { + "source": "iana" + }, + "application/vnd.nokia.n-gage.ac+xml": { + "source": "iana" + }, + "application/vnd.nokia.n-gage.data": { + "source": "iana", + "extensions": ["ngdat"] + }, + "application/vnd.nokia.n-gage.symbian.install": { + "source": "iana", + "extensions": ["n-gage"] + }, + "application/vnd.nokia.ncd": { + "source": "iana" + }, + "application/vnd.nokia.pcd+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.pcd+xml": { + "source": "iana" + }, + "application/vnd.nokia.radio-preset": { + "source": "iana", + "extensions": ["rpst"] + }, + "application/vnd.nokia.radio-presets": { + "source": "iana", + "extensions": ["rpss"] + }, + "application/vnd.novadigm.edm": { + "source": "iana", + "extensions": ["edm"] + }, + "application/vnd.novadigm.edx": { + "source": "iana", + "extensions": ["edx"] + }, + "application/vnd.novadigm.ext": { + "source": "iana", + "extensions": ["ext"] + }, + "application/vnd.ntt-local.content-share": { + "source": "iana" + }, + "application/vnd.ntt-local.file-transfer": { + "source": "iana" + }, + "application/vnd.ntt-local.ogw_remote-access": { + "source": "iana" + }, + "application/vnd.ntt-local.sip-ta_remote": { + "source": "iana" + }, + "application/vnd.ntt-local.sip-ta_tcp_stream": { + "source": "iana" + }, + "application/vnd.oasis.opendocument.chart": { + "source": "iana", + "extensions": ["odc"] + }, + "application/vnd.oasis.opendocument.chart-template": { + "source": "iana", + "extensions": ["otc"] + }, + "application/vnd.oasis.opendocument.database": { + "source": "iana", + "extensions": ["odb"] + }, + "application/vnd.oasis.opendocument.formula": { + "source": "iana", + "extensions": ["odf"] + }, + "application/vnd.oasis.opendocument.formula-template": { + "source": "iana", + "extensions": ["odft"] + }, + "application/vnd.oasis.opendocument.graphics": { + "source": "iana", + "compressible": false, + "extensions": ["odg"] + }, + "application/vnd.oasis.opendocument.graphics-template": { + "source": "iana", + "extensions": ["otg"] + }, + "application/vnd.oasis.opendocument.image": { + "source": "iana", + "extensions": ["odi"] + }, + "application/vnd.oasis.opendocument.image-template": { + "source": "iana", + "extensions": ["oti"] + }, + "application/vnd.oasis.opendocument.presentation": { + "source": "iana", + "compressible": false, + "extensions": ["odp"] + }, + "application/vnd.oasis.opendocument.presentation-template": { + "source": "iana", + "extensions": ["otp"] + }, + "application/vnd.oasis.opendocument.spreadsheet": { + "source": "iana", + "compressible": false, + "extensions": ["ods"] + }, + "application/vnd.oasis.opendocument.spreadsheet-template": { + "source": "iana", + "extensions": ["ots"] + }, + "application/vnd.oasis.opendocument.text": { + "source": "iana", + "compressible": false, + "extensions": ["odt"] + }, + "application/vnd.oasis.opendocument.text-master": { + "source": "iana", + "extensions": ["odm"] + }, + "application/vnd.oasis.opendocument.text-template": { + "source": "iana", + "extensions": ["ott"] + }, + "application/vnd.oasis.opendocument.text-web": { + "source": "iana", + "extensions": ["oth"] + }, + "application/vnd.obn": { + "source": "iana" + }, + "application/vnd.oftn.l10n+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.oipf.contentaccessdownload+xml": { + "source": "iana" + }, + "application/vnd.oipf.contentaccessstreaming+xml": { + "source": "iana" + }, + "application/vnd.oipf.cspg-hexbinary": { + "source": "iana" + }, + "application/vnd.oipf.dae.svg+xml": { + "source": "iana" + }, + "application/vnd.oipf.dae.xhtml+xml": { + "source": "iana" + }, + "application/vnd.oipf.mippvcontrolmessage+xml": { + "source": "iana" + }, + "application/vnd.oipf.pae.gem": { + "source": "iana" + }, + "application/vnd.oipf.spdiscovery+xml": { + "source": "iana" + }, + "application/vnd.oipf.spdlist+xml": { + "source": "iana" + }, + "application/vnd.oipf.ueprofile+xml": { + "source": "iana" + }, + "application/vnd.oipf.userprofile+xml": { + "source": "iana" + }, + "application/vnd.olpc-sugar": { + "source": "iana", + "extensions": ["xo"] + }, + "application/vnd.oma-scws-config": { + "source": "iana" + }, + "application/vnd.oma-scws-http-request": { + "source": "iana" + }, + "application/vnd.oma-scws-http-response": { + "source": "iana" + }, + "application/vnd.oma.bcast.associated-procedure-parameter+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.drm-trigger+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.imd+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.ltkm": { + "source": "iana" + }, + "application/vnd.oma.bcast.notification+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.provisioningtrigger": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgboot": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgdd+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgdu": { + "source": "iana" + }, + "application/vnd.oma.bcast.simple-symbol-container": { + "source": "iana" + }, + "application/vnd.oma.bcast.smartcard-trigger+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.sprov+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.stkm": { + "source": "iana" + }, + "application/vnd.oma.cab-address-book+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-feature-handler+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-pcc+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-subs-invite+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-user-prefs+xml": { + "source": "iana" + }, + "application/vnd.oma.dcd": { + "source": "iana" + }, + "application/vnd.oma.dcdc": { + "source": "iana" + }, + "application/vnd.oma.dd2+xml": { + "source": "iana", + "extensions": ["dd2"] + }, + "application/vnd.oma.drm.risd+xml": { + "source": "iana" + }, + "application/vnd.oma.group-usage-list+xml": { + "source": "iana" + }, + "application/vnd.oma.pal+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.detailed-progress-report+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.final-report+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.groups+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.invocation-descriptor+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.optimized-progress-report+xml": { + "source": "iana" + }, + "application/vnd.oma.push": { + "source": "iana" + }, + "application/vnd.oma.scidm.messages+xml": { + "source": "iana" + }, + "application/vnd.oma.xcap-directory+xml": { + "source": "iana" + }, + "application/vnd.omads-email+xml": { + "source": "iana" + }, + "application/vnd.omads-file+xml": { + "source": "iana" + }, + "application/vnd.omads-folder+xml": { + "source": "iana" + }, + "application/vnd.omaloc-supl-init": { + "source": "iana" + }, + "application/vnd.openblox.game+xml": { + "source": "iana" + }, + "application/vnd.openblox.game-binary": { + "source": "iana" + }, + "application/vnd.openeye.oeb": { + "source": "iana" + }, + "application/vnd.openofficeorg.extension": { + "source": "apache", + "extensions": ["oxt"] + }, + "application/vnd.openxmlformats-officedocument.custom-properties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.customxmlproperties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawing+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.chart+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.extended-properties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml-template": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.comments+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.presentation": { + "source": "iana", + "compressible": false, + "extensions": ["pptx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.presprops+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slide": { + "source": "iana", + "extensions": ["sldx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.slide+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideshow": { + "source": "iana", + "extensions": ["ppsx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.tags+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.template": { + "source": "apache", + "extensions": ["potx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml-template": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "source": "iana", + "compressible": false, + "extensions": ["xlsx"] + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.template": { + "source": "apache", + "extensions": ["xltx"] + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.theme+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.themeoverride+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.vmldrawing": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml-template": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document": { + "source": "iana", + "compressible": false, + "extensions": ["docx"] + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.template": { + "source": "apache", + "extensions": ["dotx"] + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-package.core-properties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-package.relationships+xml": { + "source": "iana" + }, + "application/vnd.oracle.resource+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.orange.indata": { + "source": "iana" + }, + "application/vnd.osa.netdeploy": { + "source": "iana" + }, + "application/vnd.osgeo.mapguide.package": { + "source": "iana", + "extensions": ["mgp"] + }, + "application/vnd.osgi.bundle": { + "source": "iana" + }, + "application/vnd.osgi.dp": { + "source": "iana", + "extensions": ["dp"] + }, + "application/vnd.osgi.subsystem": { + "source": "iana", + "extensions": ["esa"] + }, + "application/vnd.otps.ct-kip+xml": { + "source": "iana" + }, + "application/vnd.oxli.countgraph": { + "source": "iana" + }, + "application/vnd.pagerduty+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.palm": { + "source": "iana", + "extensions": ["pdb","pqa","oprc"] + }, + "application/vnd.panoply": { + "source": "iana" + }, + "application/vnd.paos+xml": { + "source": "iana" + }, + "application/vnd.paos.xml": { + "source": "apache" + }, + "application/vnd.pawaafile": { + "source": "iana", + "extensions": ["paw"] + }, + "application/vnd.pcos": { + "source": "iana" + }, + "application/vnd.pg.format": { + "source": "iana", + "extensions": ["str"] + }, + "application/vnd.pg.osasli": { + "source": "iana", + "extensions": ["ei6"] + }, + "application/vnd.piaccess.application-licence": { + "source": "iana" + }, + "application/vnd.picsel": { + "source": "iana", + "extensions": ["efif"] + }, + "application/vnd.pmi.widget": { + "source": "iana", + "extensions": ["wg"] + }, + "application/vnd.poc.group-advertisement+xml": { + "source": "iana" + }, + "application/vnd.pocketlearn": { + "source": "iana", + "extensions": ["plf"] + }, + "application/vnd.powerbuilder6": { + "source": "iana", + "extensions": ["pbd"] + }, + "application/vnd.powerbuilder6-s": { + "source": "iana" + }, + "application/vnd.powerbuilder7": { + "source": "iana" + }, + "application/vnd.powerbuilder7-s": { + "source": "iana" + }, + "application/vnd.powerbuilder75": { + "source": "iana" + }, + "application/vnd.powerbuilder75-s": { + "source": "iana" + }, + "application/vnd.preminet": { + "source": "iana" + }, + "application/vnd.previewsystems.box": { + "source": "iana", + "extensions": ["box"] + }, + "application/vnd.proteus.magazine": { + "source": "iana", + "extensions": ["mgz"] + }, + "application/vnd.publishare-delta-tree": { + "source": "iana", + "extensions": ["qps"] + }, + "application/vnd.pvi.ptid1": { + "source": "iana", + "extensions": ["ptid"] + }, + "application/vnd.pwg-multiplexed": { + "source": "iana" + }, + "application/vnd.pwg-xhtml-print+xml": { + "source": "iana" + }, + "application/vnd.qualcomm.brew-app-res": { + "source": "iana" + }, + "application/vnd.quark.quarkxpress": { + "source": "iana", + "extensions": ["qxd","qxt","qwd","qwt","qxl","qxb"] + }, + "application/vnd.quobject-quoxdocument": { + "source": "iana" + }, + "application/vnd.radisys.moml+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-conf+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-conn+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-dialog+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-stream+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-conf+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-base+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-fax-detect+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-fax-sendrecv+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-group+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-speech+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-transform+xml": { + "source": "iana" + }, + "application/vnd.rainstor.data": { + "source": "iana" + }, + "application/vnd.rapid": { + "source": "iana" + }, + "application/vnd.realvnc.bed": { + "source": "iana", + "extensions": ["bed"] + }, + "application/vnd.recordare.musicxml": { + "source": "iana", + "extensions": ["mxl"] + }, + "application/vnd.recordare.musicxml+xml": { + "source": "iana", + "extensions": ["musicxml"] + }, + "application/vnd.renlearn.rlprint": { + "source": "iana" + }, + "application/vnd.rig.cryptonote": { + "source": "iana", + "extensions": ["cryptonote"] + }, + "application/vnd.rim.cod": { + "source": "apache", + "extensions": ["cod"] + }, + "application/vnd.rn-realmedia": { + "source": "apache", + "extensions": ["rm"] + }, + "application/vnd.rn-realmedia-vbr": { + "source": "apache", + "extensions": ["rmvb"] + }, + "application/vnd.route66.link66+xml": { + "source": "iana", + "extensions": ["link66"] + }, + "application/vnd.rs-274x": { + "source": "iana" + }, + "application/vnd.ruckus.download": { + "source": "iana" + }, + "application/vnd.s3sms": { + "source": "iana" + }, + "application/vnd.sailingtracker.track": { + "source": "iana", + "extensions": ["st"] + }, + "application/vnd.sbm.cid": { + "source": "iana" + }, + "application/vnd.sbm.mid2": { + "source": "iana" + }, + "application/vnd.scribus": { + "source": "iana" + }, + "application/vnd.sealed.3df": { + "source": "iana" + }, + "application/vnd.sealed.csf": { + "source": "iana" + }, + "application/vnd.sealed.doc": { + "source": "iana" + }, + "application/vnd.sealed.eml": { + "source": "iana" + }, + "application/vnd.sealed.mht": { + "source": "iana" + }, + "application/vnd.sealed.net": { + "source": "iana" + }, + "application/vnd.sealed.ppt": { + "source": "iana" + }, + "application/vnd.sealed.tiff": { + "source": "iana" + }, + "application/vnd.sealed.xls": { + "source": "iana" + }, + "application/vnd.sealedmedia.softseal.html": { + "source": "iana" + }, + "application/vnd.sealedmedia.softseal.pdf": { + "source": "iana" + }, + "application/vnd.seemail": { + "source": "iana", + "extensions": ["see"] + }, + "application/vnd.sema": { + "source": "iana", + "extensions": ["sema"] + }, + "application/vnd.semd": { + "source": "iana", + "extensions": ["semd"] + }, + "application/vnd.semf": { + "source": "iana", + "extensions": ["semf"] + }, + "application/vnd.shana.informed.formdata": { + "source": "iana", + "extensions": ["ifm"] + }, + "application/vnd.shana.informed.formtemplate": { + "source": "iana", + "extensions": ["itp"] + }, + "application/vnd.shana.informed.interchange": { + "source": "iana", + "extensions": ["iif"] + }, + "application/vnd.shana.informed.package": { + "source": "iana", + "extensions": ["ipk"] + }, + "application/vnd.simtech-mindmapper": { + "source": "iana", + "extensions": ["twd","twds"] + }, + "application/vnd.siren+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.smaf": { + "source": "iana", + "extensions": ["mmf"] + }, + "application/vnd.smart.notebook": { + "source": "iana" + }, + "application/vnd.smart.teacher": { + "source": "iana", + "extensions": ["teacher"] + }, + "application/vnd.software602.filler.form+xml": { + "source": "iana" + }, + "application/vnd.software602.filler.form-xml-zip": { + "source": "iana" + }, + "application/vnd.solent.sdkm+xml": { + "source": "iana", + "extensions": ["sdkm","sdkd"] + }, + "application/vnd.spotfire.dxp": { + "source": "iana", + "extensions": ["dxp"] + }, + "application/vnd.spotfire.sfs": { + "source": "iana", + "extensions": ["sfs"] + }, + "application/vnd.sss-cod": { + "source": "iana" + }, + "application/vnd.sss-dtf": { + "source": "iana" + }, + "application/vnd.sss-ntf": { + "source": "iana" + }, + "application/vnd.stardivision.calc": { + "source": "apache", + "extensions": ["sdc"] + }, + "application/vnd.stardivision.draw": { + "source": "apache", + "extensions": ["sda"] + }, + "application/vnd.stardivision.impress": { + "source": "apache", + "extensions": ["sdd"] + }, + "application/vnd.stardivision.math": { + "source": "apache", + "extensions": ["smf"] + }, + "application/vnd.stardivision.writer": { + "source": "apache", + "extensions": ["sdw","vor"] + }, + "application/vnd.stardivision.writer-global": { + "source": "apache", + "extensions": ["sgl"] + }, + "application/vnd.stepmania.package": { + "source": "iana", + "extensions": ["smzip"] + }, + "application/vnd.stepmania.stepchart": { + "source": "iana", + "extensions": ["sm"] + }, + "application/vnd.street-stream": { + "source": "iana" + }, + "application/vnd.sun.wadl+xml": { + "source": "iana" + }, + "application/vnd.sun.xml.calc": { + "source": "apache", + "extensions": ["sxc"] + }, + "application/vnd.sun.xml.calc.template": { + "source": "apache", + "extensions": ["stc"] + }, + "application/vnd.sun.xml.draw": { + "source": "apache", + "extensions": ["sxd"] + }, + "application/vnd.sun.xml.draw.template": { + "source": "apache", + "extensions": ["std"] + }, + "application/vnd.sun.xml.impress": { + "source": "apache", + "extensions": ["sxi"] + }, + "application/vnd.sun.xml.impress.template": { + "source": "apache", + "extensions": ["sti"] + }, + "application/vnd.sun.xml.math": { + "source": "apache", + "extensions": ["sxm"] + }, + "application/vnd.sun.xml.writer": { + "source": "apache", + "extensions": ["sxw"] + }, + "application/vnd.sun.xml.writer.global": { + "source": "apache", + "extensions": ["sxg"] + }, + "application/vnd.sun.xml.writer.template": { + "source": "apache", + "extensions": ["stw"] + }, + "application/vnd.sus-calendar": { + "source": "iana", + "extensions": ["sus","susp"] + }, + "application/vnd.svd": { + "source": "iana", + "extensions": ["svd"] + }, + "application/vnd.swiftview-ics": { + "source": "iana" + }, + "application/vnd.symbian.install": { + "source": "apache", + "extensions": ["sis","sisx"] + }, + "application/vnd.syncml+xml": { + "source": "iana", + "extensions": ["xsm"] + }, + "application/vnd.syncml.dm+wbxml": { + "source": "iana", + "extensions": ["bdm"] + }, + "application/vnd.syncml.dm+xml": { + "source": "iana", + "extensions": ["xdm"] + }, + "application/vnd.syncml.dm.notification": { + "source": "iana" + }, + "application/vnd.syncml.dmddf+wbxml": { + "source": "iana" + }, + "application/vnd.syncml.dmddf+xml": { + "source": "iana" + }, + "application/vnd.syncml.dmtnds+wbxml": { + "source": "iana" + }, + "application/vnd.syncml.dmtnds+xml": { + "source": "iana" + }, + "application/vnd.syncml.ds.notification": { + "source": "iana" + }, + "application/vnd.tao.intent-module-archive": { + "source": "iana", + "extensions": ["tao"] + }, + "application/vnd.tcpdump.pcap": { + "source": "iana", + "extensions": ["pcap","cap","dmp"] + }, + "application/vnd.tmd.mediaflex.api+xml": { + "source": "iana" + }, + "application/vnd.tmobile-livetv": { + "source": "iana", + "extensions": ["tmo"] + }, + "application/vnd.trid.tpt": { + "source": "iana", + "extensions": ["tpt"] + }, + "application/vnd.triscape.mxs": { + "source": "iana", + "extensions": ["mxs"] + }, + "application/vnd.trueapp": { + "source": "iana", + "extensions": ["tra"] + }, + "application/vnd.truedoc": { + "source": "iana" + }, + "application/vnd.ubisoft.webplayer": { + "source": "iana" + }, + "application/vnd.ufdl": { + "source": "iana", + "extensions": ["ufd","ufdl"] + }, + "application/vnd.uiq.theme": { + "source": "iana", + "extensions": ["utz"] + }, + "application/vnd.umajin": { + "source": "iana", + "extensions": ["umj"] + }, + "application/vnd.unity": { + "source": "iana", + "extensions": ["unityweb"] + }, + "application/vnd.uoml+xml": { + "source": "iana", + "extensions": ["uoml"] + }, + "application/vnd.uplanet.alert": { + "source": "iana" + }, + "application/vnd.uplanet.alert-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.bearer-choice": { + "source": "iana" + }, + "application/vnd.uplanet.bearer-choice-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.cacheop": { + "source": "iana" + }, + "application/vnd.uplanet.cacheop-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.channel": { + "source": "iana" + }, + "application/vnd.uplanet.channel-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.list": { + "source": "iana" + }, + "application/vnd.uplanet.list-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.listcmd": { + "source": "iana" + }, + "application/vnd.uplanet.listcmd-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.signal": { + "source": "iana" + }, + "application/vnd.uri-map": { + "source": "iana" + }, + "application/vnd.valve.source.material": { + "source": "iana" + }, + "application/vnd.vcx": { + "source": "iana", + "extensions": ["vcx"] + }, + "application/vnd.vd-study": { + "source": "iana" + }, + "application/vnd.vectorworks": { + "source": "iana" + }, + "application/vnd.verimatrix.vcas": { + "source": "iana" + }, + "application/vnd.vidsoft.vidconference": { + "source": "iana" + }, + "application/vnd.visio": { + "source": "iana", + "extensions": ["vsd","vst","vss","vsw"] + }, + "application/vnd.visionary": { + "source": "iana", + "extensions": ["vis"] + }, + "application/vnd.vividence.scriptfile": { + "source": "iana" + }, + "application/vnd.vsf": { + "source": "iana", + "extensions": ["vsf"] + }, + "application/vnd.wap.sic": { + "source": "iana" + }, + "application/vnd.wap.slc": { + "source": "iana" + }, + "application/vnd.wap.wbxml": { + "source": "iana", + "extensions": ["wbxml"] + }, + "application/vnd.wap.wmlc": { + "source": "iana", + "extensions": ["wmlc"] + }, + "application/vnd.wap.wmlscriptc": { + "source": "iana", + "extensions": ["wmlsc"] + }, + "application/vnd.webturbo": { + "source": "iana", + "extensions": ["wtb"] + }, + "application/vnd.wfa.p2p": { + "source": "iana" + }, + "application/vnd.wfa.wsc": { + "source": "iana" + }, + "application/vnd.windows.devicepairing": { + "source": "iana" + }, + "application/vnd.wmc": { + "source": "iana" + }, + "application/vnd.wmf.bootstrap": { + "source": "iana" + }, + "application/vnd.wolfram.mathematica": { + "source": "iana" + }, + "application/vnd.wolfram.mathematica.package": { + "source": "iana" + }, + "application/vnd.wolfram.player": { + "source": "iana", + "extensions": ["nbp"] + }, + "application/vnd.wordperfect": { + "source": "iana", + "extensions": ["wpd"] + }, + "application/vnd.wqd": { + "source": "iana", + "extensions": ["wqd"] + }, + "application/vnd.wrq-hp3000-labelled": { + "source": "iana" + }, + "application/vnd.wt.stf": { + "source": "iana", + "extensions": ["stf"] + }, + "application/vnd.wv.csp+wbxml": { + "source": "iana" + }, + "application/vnd.wv.csp+xml": { + "source": "iana" + }, + "application/vnd.wv.ssp+xml": { + "source": "iana" + }, + "application/vnd.xacml+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.xara": { + "source": "iana", + "extensions": ["xar"] + }, + "application/vnd.xfdl": { + "source": "iana", + "extensions": ["xfdl"] + }, + "application/vnd.xfdl.webform": { + "source": "iana" + }, + "application/vnd.xmi+xml": { + "source": "iana" + }, + "application/vnd.xmpie.cpkg": { + "source": "iana" + }, + "application/vnd.xmpie.dpkg": { + "source": "iana" + }, + "application/vnd.xmpie.plan": { + "source": "iana" + }, + "application/vnd.xmpie.ppkg": { + "source": "iana" + }, + "application/vnd.xmpie.xlim": { + "source": "iana" + }, + "application/vnd.yamaha.hv-dic": { + "source": "iana", + "extensions": ["hvd"] + }, + "application/vnd.yamaha.hv-script": { + "source": "iana", + "extensions": ["hvs"] + }, + "application/vnd.yamaha.hv-voice": { + "source": "iana", + "extensions": ["hvp"] + }, + "application/vnd.yamaha.openscoreformat": { + "source": "iana", + "extensions": ["osf"] + }, + "application/vnd.yamaha.openscoreformat.osfpvg+xml": { + "source": "iana", + "extensions": ["osfpvg"] + }, + "application/vnd.yamaha.remote-setup": { + "source": "iana" + }, + "application/vnd.yamaha.smaf-audio": { + "source": "iana", + "extensions": ["saf"] + }, + "application/vnd.yamaha.smaf-phrase": { + "source": "iana", + "extensions": ["spf"] + }, + "application/vnd.yamaha.through-ngn": { + "source": "iana" + }, + "application/vnd.yamaha.tunnel-udpencap": { + "source": "iana" + }, + "application/vnd.yaoweme": { + "source": "iana" + }, + "application/vnd.yellowriver-custom-menu": { + "source": "iana", + "extensions": ["cmp"] + }, + "application/vnd.zul": { + "source": "iana", + "extensions": ["zir","zirz"] + }, + "application/vnd.zzazz.deck+xml": { + "source": "iana", + "extensions": ["zaz"] + }, + "application/voicexml+xml": { + "source": "iana", + "extensions": ["vxml"] + }, + "application/vq-rtcpxr": { + "source": "iana" + }, + "application/watcherinfo+xml": { + "source": "iana" + }, + "application/whoispp-query": { + "source": "iana" + }, + "application/whoispp-response": { + "source": "iana" + }, + "application/widget": { + "source": "iana", + "extensions": ["wgt"] + }, + "application/winhlp": { + "source": "apache", + "extensions": ["hlp"] + }, + "application/wita": { + "source": "iana" + }, + "application/wordperfect5.1": { + "source": "iana" + }, + "application/wsdl+xml": { + "source": "iana", + "extensions": ["wsdl"] + }, + "application/wspolicy+xml": { + "source": "iana", + "extensions": ["wspolicy"] + }, + "application/x-7z-compressed": { + "source": "apache", + "compressible": false, + "extensions": ["7z"] + }, + "application/x-abiword": { + "source": "apache", + "extensions": ["abw"] + }, + "application/x-ace-compressed": { + "source": "apache", + "extensions": ["ace"] + }, + "application/x-amf": { + "source": "apache" + }, + "application/x-apple-diskimage": { + "source": "apache", + "extensions": ["dmg"] + }, + "application/x-authorware-bin": { + "source": "apache", + "extensions": ["aab","x32","u32","vox"] + }, + "application/x-authorware-map": { + "source": "apache", + "extensions": ["aam"] + }, + "application/x-authorware-seg": { + "source": "apache", + "extensions": ["aas"] + }, + "application/x-bcpio": { + "source": "apache", + "extensions": ["bcpio"] + }, + "application/x-bdoc": { + "compressible": false, + "extensions": ["bdoc"] + }, + "application/x-bittorrent": { + "source": "apache", + "extensions": ["torrent"] + }, + "application/x-blorb": { + "source": "apache", + "extensions": ["blb","blorb"] + }, + "application/x-bzip": { + "source": "apache", + "compressible": false, + "extensions": ["bz"] + }, + "application/x-bzip2": { + "source": "apache", + "compressible": false, + "extensions": ["bz2","boz"] + }, + "application/x-cbr": { + "source": "apache", + "extensions": ["cbr","cba","cbt","cbz","cb7"] + }, + "application/x-cdlink": { + "source": "apache", + "extensions": ["vcd"] + }, + "application/x-cfs-compressed": { + "source": "apache", + "extensions": ["cfs"] + }, + "application/x-chat": { + "source": "apache", + "extensions": ["chat"] + }, + "application/x-chess-pgn": { + "source": "apache", + "extensions": ["pgn"] + }, + "application/x-chrome-extension": { + "extensions": ["crx"] + }, + "application/x-cocoa": { + "source": "nginx", + "extensions": ["cco"] + }, + "application/x-compress": { + "source": "apache" + }, + "application/x-conference": { + "source": "apache", + "extensions": ["nsc"] + }, + "application/x-cpio": { + "source": "apache", + "extensions": ["cpio"] + }, + "application/x-csh": { + "source": "apache", + "extensions": ["csh"] + }, + "application/x-deb": { + "compressible": false + }, + "application/x-debian-package": { + "source": "apache", + "extensions": ["deb","udeb"] + }, + "application/x-dgc-compressed": { + "source": "apache", + "extensions": ["dgc"] + }, + "application/x-director": { + "source": "apache", + "extensions": ["dir","dcr","dxr","cst","cct","cxt","w3d","fgd","swa"] + }, + "application/x-doom": { + "source": "apache", + "extensions": ["wad"] + }, + "application/x-dtbncx+xml": { + "source": "apache", + "extensions": ["ncx"] + }, + "application/x-dtbook+xml": { + "source": "apache", + "extensions": ["dtb"] + }, + "application/x-dtbresource+xml": { + "source": "apache", + "extensions": ["res"] + }, + "application/x-dvi": { + "source": "apache", + "compressible": false, + "extensions": ["dvi"] + }, + "application/x-envoy": { + "source": "apache", + "extensions": ["evy"] + }, + "application/x-eva": { + "source": "apache", + "extensions": ["eva"] + }, + "application/x-font-bdf": { + "source": "apache", + "extensions": ["bdf"] + }, + "application/x-font-dos": { + "source": "apache" + }, + "application/x-font-framemaker": { + "source": "apache" + }, + "application/x-font-ghostscript": { + "source": "apache", + "extensions": ["gsf"] + }, + "application/x-font-libgrx": { + "source": "apache" + }, + "application/x-font-linux-psf": { + "source": "apache", + "extensions": ["psf"] + }, + "application/x-font-otf": { + "source": "apache", + "compressible": true, + "extensions": ["otf"] + }, + "application/x-font-pcf": { + "source": "apache", + "extensions": ["pcf"] + }, + "application/x-font-snf": { + "source": "apache", + "extensions": ["snf"] + }, + "application/x-font-speedo": { + "source": "apache" + }, + "application/x-font-sunos-news": { + "source": "apache" + }, + "application/x-font-ttf": { + "source": "apache", + "compressible": true, + "extensions": ["ttf","ttc"] + }, + "application/x-font-type1": { + "source": "apache", + "extensions": ["pfa","pfb","pfm","afm"] + }, + "application/x-font-vfont": { + "source": "apache" + }, + "application/x-freearc": { + "source": "apache", + "extensions": ["arc"] + }, + "application/x-futuresplash": { + "source": "apache", + "extensions": ["spl"] + }, + "application/x-gca-compressed": { + "source": "apache", + "extensions": ["gca"] + }, + "application/x-glulx": { + "source": "apache", + "extensions": ["ulx"] + }, + "application/x-gnumeric": { + "source": "apache", + "extensions": ["gnumeric"] + }, + "application/x-gramps-xml": { + "source": "apache", + "extensions": ["gramps"] + }, + "application/x-gtar": { + "source": "apache", + "extensions": ["gtar"] + }, + "application/x-gzip": { + "source": "apache" + }, + "application/x-hdf": { + "source": "apache", + "extensions": ["hdf"] + }, + "application/x-httpd-php": { + "compressible": true, + "extensions": ["php"] + }, + "application/x-install-instructions": { + "source": "apache", + "extensions": ["install"] + }, + "application/x-iso9660-image": { + "source": "apache", + "extensions": ["iso"] + }, + "application/x-java-archive-diff": { + "source": "nginx", + "extensions": ["jardiff"] + }, + "application/x-java-jnlp-file": { + "source": "apache", + "compressible": false, + "extensions": ["jnlp"] + }, + "application/x-javascript": { + "compressible": true + }, + "application/x-latex": { + "source": "apache", + "compressible": false, + "extensions": ["latex"] + }, + "application/x-lua-bytecode": { + "extensions": ["luac"] + }, + "application/x-lzh-compressed": { + "source": "apache", + "extensions": ["lzh","lha"] + }, + "application/x-makeself": { + "source": "nginx", + "extensions": ["run"] + }, + "application/x-mie": { + "source": "apache", + "extensions": ["mie"] + }, + "application/x-mobipocket-ebook": { + "source": "apache", + "extensions": ["prc","mobi"] + }, + "application/x-mpegurl": { + "compressible": false + }, + "application/x-ms-application": { + "source": "apache", + "extensions": ["application"] + }, + "application/x-ms-shortcut": { + "source": "apache", + "extensions": ["lnk"] + }, + "application/x-ms-wmd": { + "source": "apache", + "extensions": ["wmd"] + }, + "application/x-ms-wmz": { + "source": "apache", + "extensions": ["wmz"] + }, + "application/x-ms-xbap": { + "source": "apache", + "extensions": ["xbap"] + }, + "application/x-msaccess": { + "source": "apache", + "extensions": ["mdb"] + }, + "application/x-msbinder": { + "source": "apache", + "extensions": ["obd"] + }, + "application/x-mscardfile": { + "source": "apache", + "extensions": ["crd"] + }, + "application/x-msclip": { + "source": "apache", + "extensions": ["clp"] + }, + "application/x-msdos-program": { + "extensions": ["exe"] + }, + "application/x-msdownload": { + "source": "apache", + "extensions": ["exe","dll","com","bat","msi"] + }, + "application/x-msmediaview": { + "source": "apache", + "extensions": ["mvb","m13","m14"] + }, + "application/x-msmetafile": { + "source": "apache", + "extensions": ["wmf","wmz","emf","emz"] + }, + "application/x-msmoney": { + "source": "apache", + "extensions": ["mny"] + }, + "application/x-mspublisher": { + "source": "apache", + "extensions": ["pub"] + }, + "application/x-msschedule": { + "source": "apache", + "extensions": ["scd"] + }, + "application/x-msterminal": { + "source": "apache", + "extensions": ["trm"] + }, + "application/x-mswrite": { + "source": "apache", + "extensions": ["wri"] + }, + "application/x-netcdf": { + "source": "apache", + "extensions": ["nc","cdf"] + }, + "application/x-ns-proxy-autoconfig": { + "compressible": true, + "extensions": ["pac"] + }, + "application/x-nzb": { + "source": "apache", + "extensions": ["nzb"] + }, + "application/x-perl": { + "source": "nginx", + "extensions": ["pl","pm"] + }, + "application/x-pilot": { + "source": "nginx", + "extensions": ["prc","pdb"] + }, + "application/x-pkcs12": { + "source": "apache", + "compressible": false, + "extensions": ["p12","pfx"] + }, + "application/x-pkcs7-certificates": { + "source": "apache", + "extensions": ["p7b","spc"] + }, + "application/x-pkcs7-certreqresp": { + "source": "apache", + "extensions": ["p7r"] + }, + "application/x-rar-compressed": { + "source": "apache", + "compressible": false, + "extensions": ["rar"] + }, + "application/x-redhat-package-manager": { + "source": "nginx", + "extensions": ["rpm"] + }, + "application/x-research-info-systems": { + "source": "apache", + "extensions": ["ris"] + }, + "application/x-sea": { + "source": "nginx", + "extensions": ["sea"] + }, + "application/x-sh": { + "source": "apache", + "compressible": true, + "extensions": ["sh"] + }, + "application/x-shar": { + "source": "apache", + "extensions": ["shar"] + }, + "application/x-shockwave-flash": { + "source": "apache", + "compressible": false, + "extensions": ["swf"] + }, + "application/x-silverlight-app": { + "source": "apache", + "extensions": ["xap"] + }, + "application/x-sql": { + "source": "apache", + "extensions": ["sql"] + }, + "application/x-stuffit": { + "source": "apache", + "compressible": false, + "extensions": ["sit"] + }, + "application/x-stuffitx": { + "source": "apache", + "extensions": ["sitx"] + }, + "application/x-subrip": { + "source": "apache", + "extensions": ["srt"] + }, + "application/x-sv4cpio": { + "source": "apache", + "extensions": ["sv4cpio"] + }, + "application/x-sv4crc": { + "source": "apache", + "extensions": ["sv4crc"] + }, + "application/x-t3vm-image": { + "source": "apache", + "extensions": ["t3"] + }, + "application/x-tads": { + "source": "apache", + "extensions": ["gam"] + }, + "application/x-tar": { + "source": "apache", + "compressible": true, + "extensions": ["tar"] + }, + "application/x-tcl": { + "source": "apache", + "extensions": ["tcl","tk"] + }, + "application/x-tex": { + "source": "apache", + "extensions": ["tex"] + }, + "application/x-tex-tfm": { + "source": "apache", + "extensions": ["tfm"] + }, + "application/x-texinfo": { + "source": "apache", + "extensions": ["texinfo","texi"] + }, + "application/x-tgif": { + "source": "apache", + "extensions": ["obj"] + }, + "application/x-ustar": { + "source": "apache", + "extensions": ["ustar"] + }, + "application/x-wais-source": { + "source": "apache", + "extensions": ["src"] + }, + "application/x-web-app-manifest+json": { + "compressible": true, + "extensions": ["webapp"] + }, + "application/x-www-form-urlencoded": { + "source": "iana", + "compressible": true + }, + "application/x-x509-ca-cert": { + "source": "apache", + "extensions": ["der","crt","pem"] + }, + "application/x-xfig": { + "source": "apache", + "extensions": ["fig"] + }, + "application/x-xliff+xml": { + "source": "apache", + "extensions": ["xlf"] + }, + "application/x-xpinstall": { + "source": "apache", + "compressible": false, + "extensions": ["xpi"] + }, + "application/x-xz": { + "source": "apache", + "extensions": ["xz"] + }, + "application/x-zmachine": { + "source": "apache", + "extensions": ["z1","z2","z3","z4","z5","z6","z7","z8"] + }, + "application/x400-bp": { + "source": "iana" + }, + "application/xacml+xml": { + "source": "iana" + }, + "application/xaml+xml": { + "source": "apache", + "extensions": ["xaml"] + }, + "application/xcap-att+xml": { + "source": "iana" + }, + "application/xcap-caps+xml": { + "source": "iana" + }, + "application/xcap-diff+xml": { + "source": "iana", + "extensions": ["xdf"] + }, + "application/xcap-el+xml": { + "source": "iana" + }, + "application/xcap-error+xml": { + "source": "iana" + }, + "application/xcap-ns+xml": { + "source": "iana" + }, + "application/xcon-conference-info+xml": { + "source": "iana" + }, + "application/xcon-conference-info-diff+xml": { + "source": "iana" + }, + "application/xenc+xml": { + "source": "iana", + "extensions": ["xenc"] + }, + "application/xhtml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xhtml","xht"] + }, + "application/xhtml-voice+xml": { + "source": "apache" + }, + "application/xml": { + "source": "iana", + "compressible": true, + "extensions": ["xml","xsl","xsd"] + }, + "application/xml-dtd": { + "source": "iana", + "compressible": true, + "extensions": ["dtd"] + }, + "application/xml-external-parsed-entity": { + "source": "iana" + }, + "application/xml-patch+xml": { + "source": "iana" + }, + "application/xmpp+xml": { + "source": "iana" + }, + "application/xop+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xop"] + }, + "application/xproc+xml": { + "source": "apache", + "extensions": ["xpl"] + }, + "application/xslt+xml": { + "source": "iana", + "extensions": ["xslt"] + }, + "application/xspf+xml": { + "source": "apache", + "extensions": ["xspf"] + }, + "application/xv+xml": { + "source": "iana", + "extensions": ["mxml","xhvml","xvml","xvm"] + }, + "application/yang": { + "source": "iana", + "extensions": ["yang"] + }, + "application/yin+xml": { + "source": "iana", + "extensions": ["yin"] + }, + "application/zip": { + "source": "iana", + "compressible": false, + "extensions": ["zip"] + }, + "application/zlib": { + "source": "iana" + }, + "audio/1d-interleaved-parityfec": { + "source": "iana" + }, + "audio/32kadpcm": { + "source": "iana" + }, + "audio/3gpp": { + "source": "iana" + }, + "audio/3gpp2": { + "source": "iana" + }, + "audio/ac3": { + "source": "iana" + }, + "audio/adpcm": { + "source": "apache", + "extensions": ["adp"] + }, + "audio/amr": { + "source": "iana" + }, + "audio/amr-wb": { + "source": "iana" + }, + "audio/amr-wb+": { + "source": "iana" + }, + "audio/aptx": { + "source": "iana" + }, + "audio/asc": { + "source": "iana" + }, + "audio/atrac-advanced-lossless": { + "source": "iana" + }, + "audio/atrac-x": { + "source": "iana" + }, + "audio/atrac3": { + "source": "iana" + }, + "audio/basic": { + "source": "iana", + "compressible": false, + "extensions": ["au","snd"] + }, + "audio/bv16": { + "source": "iana" + }, + "audio/bv32": { + "source": "iana" + }, + "audio/clearmode": { + "source": "iana" + }, + "audio/cn": { + "source": "iana" + }, + "audio/dat12": { + "source": "iana" + }, + "audio/dls": { + "source": "iana" + }, + "audio/dsr-es201108": { + "source": "iana" + }, + "audio/dsr-es202050": { + "source": "iana" + }, + "audio/dsr-es202211": { + "source": "iana" + }, + "audio/dsr-es202212": { + "source": "iana" + }, + "audio/dv": { + "source": "iana" + }, + "audio/dvi4": { + "source": "iana" + }, + "audio/eac3": { + "source": "iana" + }, + "audio/encaprtp": { + "source": "iana" + }, + "audio/evrc": { + "source": "iana" + }, + "audio/evrc-qcp": { + "source": "iana" + }, + "audio/evrc0": { + "source": "iana" + }, + "audio/evrc1": { + "source": "iana" + }, + "audio/evrcb": { + "source": "iana" + }, + "audio/evrcb0": { + "source": "iana" + }, + "audio/evrcb1": { + "source": "iana" + }, + "audio/evrcnw": { + "source": "iana" + }, + "audio/evrcnw0": { + "source": "iana" + }, + "audio/evrcnw1": { + "source": "iana" + }, + "audio/evrcwb": { + "source": "iana" + }, + "audio/evrcwb0": { + "source": "iana" + }, + "audio/evrcwb1": { + "source": "iana" + }, + "audio/fwdred": { + "source": "iana" + }, + "audio/g711-0": { + "source": "iana" + }, + "audio/g719": { + "source": "iana" + }, + "audio/g722": { + "source": "iana" + }, + "audio/g7221": { + "source": "iana" + }, + "audio/g723": { + "source": "iana" + }, + "audio/g726-16": { + "source": "iana" + }, + "audio/g726-24": { + "source": "iana" + }, + "audio/g726-32": { + "source": "iana" + }, + "audio/g726-40": { + "source": "iana" + }, + "audio/g728": { + "source": "iana" + }, + "audio/g729": { + "source": "iana" + }, + "audio/g7291": { + "source": "iana" + }, + "audio/g729d": { + "source": "iana" + }, + "audio/g729e": { + "source": "iana" + }, + "audio/gsm": { + "source": "iana" + }, + "audio/gsm-efr": { + "source": "iana" + }, + "audio/gsm-hr-08": { + "source": "iana" + }, + "audio/ilbc": { + "source": "iana" + }, + "audio/ip-mr_v2.5": { + "source": "iana" + }, + "audio/isac": { + "source": "apache" + }, + "audio/l16": { + "source": "iana" + }, + "audio/l20": { + "source": "iana" + }, + "audio/l24": { + "source": "iana", + "compressible": false + }, + "audio/l8": { + "source": "iana" + }, + "audio/lpc": { + "source": "iana" + }, + "audio/midi": { + "source": "apache", + "extensions": ["mid","midi","kar","rmi"] + }, + "audio/mobile-xmf": { + "source": "iana" + }, + "audio/mp4": { + "source": "iana", + "compressible": false, + "extensions": ["mp4a","m4a"] + }, + "audio/mp4a-latm": { + "source": "iana" + }, + "audio/mpa": { + "source": "iana" + }, + "audio/mpa-robust": { + "source": "iana" + }, + "audio/mpeg": { + "source": "iana", + "compressible": false, + "extensions": ["mpga","mp2","mp2a","mp3","m2a","m3a"] + }, + "audio/mpeg4-generic": { + "source": "iana" + }, + "audio/musepack": { + "source": "apache" + }, + "audio/ogg": { + "source": "iana", + "compressible": false, + "extensions": ["oga","ogg","spx"] + }, + "audio/opus": { + "source": "iana" + }, + "audio/parityfec": { + "source": "iana" + }, + "audio/pcma": { + "source": "iana" + }, + "audio/pcma-wb": { + "source": "iana" + }, + "audio/pcmu": { + "source": "iana" + }, + "audio/pcmu-wb": { + "source": "iana" + }, + "audio/prs.sid": { + "source": "iana" + }, + "audio/qcelp": { + "source": "iana" + }, + "audio/raptorfec": { + "source": "iana" + }, + "audio/red": { + "source": "iana" + }, + "audio/rtp-enc-aescm128": { + "source": "iana" + }, + "audio/rtp-midi": { + "source": "iana" + }, + "audio/rtploopback": { + "source": "iana" + }, + "audio/rtx": { + "source": "iana" + }, + "audio/s3m": { + "source": "apache", + "extensions": ["s3m"] + }, + "audio/silk": { + "source": "apache", + "extensions": ["sil"] + }, + "audio/smv": { + "source": "iana" + }, + "audio/smv-qcp": { + "source": "iana" + }, + "audio/smv0": { + "source": "iana" + }, + "audio/sp-midi": { + "source": "iana" + }, + "audio/speex": { + "source": "iana" + }, + "audio/t140c": { + "source": "iana" + }, + "audio/t38": { + "source": "iana" + }, + "audio/telephone-event": { + "source": "iana" + }, + "audio/tone": { + "source": "iana" + }, + "audio/uemclip": { + "source": "iana" + }, + "audio/ulpfec": { + "source": "iana" + }, + "audio/vdvi": { + "source": "iana" + }, + "audio/vmr-wb": { + "source": "iana" + }, + "audio/vnd.3gpp.iufp": { + "source": "iana" + }, + "audio/vnd.4sb": { + "source": "iana" + }, + "audio/vnd.audiokoz": { + "source": "iana" + }, + "audio/vnd.celp": { + "source": "iana" + }, + "audio/vnd.cisco.nse": { + "source": "iana" + }, + "audio/vnd.cmles.radio-events": { + "source": "iana" + }, + "audio/vnd.cns.anp1": { + "source": "iana" + }, + "audio/vnd.cns.inf1": { + "source": "iana" + }, + "audio/vnd.dece.audio": { + "source": "iana", + "extensions": ["uva","uvva"] + }, + "audio/vnd.digital-winds": { + "source": "iana", + "extensions": ["eol"] + }, + "audio/vnd.dlna.adts": { + "source": "iana" + }, + "audio/vnd.dolby.heaac.1": { + "source": "iana" + }, + "audio/vnd.dolby.heaac.2": { + "source": "iana" + }, + "audio/vnd.dolby.mlp": { + "source": "iana" + }, + "audio/vnd.dolby.mps": { + "source": "iana" + }, + "audio/vnd.dolby.pl2": { + "source": "iana" + }, + "audio/vnd.dolby.pl2x": { + "source": "iana" + }, + "audio/vnd.dolby.pl2z": { + "source": "iana" + }, + "audio/vnd.dolby.pulse.1": { + "source": "iana" + }, + "audio/vnd.dra": { + "source": "iana", + "extensions": ["dra"] + }, + "audio/vnd.dts": { + "source": "iana", + "extensions": ["dts"] + }, + "audio/vnd.dts.hd": { + "source": "iana", + "extensions": ["dtshd"] + }, + "audio/vnd.dvb.file": { + "source": "iana" + }, + "audio/vnd.everad.plj": { + "source": "iana" + }, + "audio/vnd.hns.audio": { + "source": "iana" + }, + "audio/vnd.lucent.voice": { + "source": "iana", + "extensions": ["lvp"] + }, + "audio/vnd.ms-playready.media.pya": { + "source": "iana", + "extensions": ["pya"] + }, + "audio/vnd.nokia.mobile-xmf": { + "source": "iana" + }, + "audio/vnd.nortel.vbk": { + "source": "iana" + }, + "audio/vnd.nuera.ecelp4800": { + "source": "iana", + "extensions": ["ecelp4800"] + }, + "audio/vnd.nuera.ecelp7470": { + "source": "iana", + "extensions": ["ecelp7470"] + }, + "audio/vnd.nuera.ecelp9600": { + "source": "iana", + "extensions": ["ecelp9600"] + }, + "audio/vnd.octel.sbc": { + "source": "iana" + }, + "audio/vnd.qcelp": { + "source": "iana" + }, + "audio/vnd.rhetorex.32kadpcm": { + "source": "iana" + }, + "audio/vnd.rip": { + "source": "iana", + "extensions": ["rip"] + }, + "audio/vnd.rn-realaudio": { + "compressible": false + }, + "audio/vnd.sealedmedia.softseal.mpeg": { + "source": "iana" + }, + "audio/vnd.vmx.cvsd": { + "source": "iana" + }, + "audio/vnd.wave": { + "compressible": false + }, + "audio/vorbis": { + "source": "iana", + "compressible": false + }, + "audio/vorbis-config": { + "source": "iana" + }, + "audio/wav": { + "compressible": false, + "extensions": ["wav"] + }, + "audio/wave": { + "compressible": false, + "extensions": ["wav"] + }, + "audio/webm": { + "source": "apache", + "compressible": false, + "extensions": ["weba"] + }, + "audio/x-aac": { + "source": "apache", + "compressible": false, + "extensions": ["aac"] + }, + "audio/x-aiff": { + "source": "apache", + "extensions": ["aif","aiff","aifc"] + }, + "audio/x-caf": { + "source": "apache", + "compressible": false, + "extensions": ["caf"] + }, + "audio/x-flac": { + "source": "apache", + "extensions": ["flac"] + }, + "audio/x-m4a": { + "source": "nginx", + "extensions": ["m4a"] + }, + "audio/x-matroska": { + "source": "apache", + "extensions": ["mka"] + }, + "audio/x-mpegurl": { + "source": "apache", + "extensions": ["m3u"] + }, + "audio/x-ms-wax": { + "source": "apache", + "extensions": ["wax"] + }, + "audio/x-ms-wma": { + "source": "apache", + "extensions": ["wma"] + }, + "audio/x-pn-realaudio": { + "source": "apache", + "extensions": ["ram","ra"] + }, + "audio/x-pn-realaudio-plugin": { + "source": "apache", + "extensions": ["rmp"] + }, + "audio/x-realaudio": { + "source": "nginx", + "extensions": ["ra"] + }, + "audio/x-tta": { + "source": "apache" + }, + "audio/x-wav": { + "source": "apache", + "extensions": ["wav"] + }, + "audio/xm": { + "source": "apache", + "extensions": ["xm"] + }, + "chemical/x-cdx": { + "source": "apache", + "extensions": ["cdx"] + }, + "chemical/x-cif": { + "source": "apache", + "extensions": ["cif"] + }, + "chemical/x-cmdf": { + "source": "apache", + "extensions": ["cmdf"] + }, + "chemical/x-cml": { + "source": "apache", + "extensions": ["cml"] + }, + "chemical/x-csml": { + "source": "apache", + "extensions": ["csml"] + }, + "chemical/x-pdb": { + "source": "apache" + }, + "chemical/x-xyz": { + "source": "apache", + "extensions": ["xyz"] + }, + "font/opentype": { + "compressible": true, + "extensions": ["otf"] + }, + "image/bmp": { + "source": "apache", + "compressible": true, + "extensions": ["bmp"] + }, + "image/cgm": { + "source": "iana", + "extensions": ["cgm"] + }, + "image/fits": { + "source": "iana" + }, + "image/g3fax": { + "source": "iana", + "extensions": ["g3"] + }, + "image/gif": { + "source": "iana", + "compressible": false, + "extensions": ["gif"] + }, + "image/ief": { + "source": "iana", + "extensions": ["ief"] + }, + "image/jp2": { + "source": "iana" + }, + "image/jpeg": { + "source": "iana", + "compressible": false, + "extensions": ["jpeg","jpg","jpe"] + }, + "image/jpm": { + "source": "iana" + }, + "image/jpx": { + "source": "iana" + }, + "image/ktx": { + "source": "iana", + "extensions": ["ktx"] + }, + "image/naplps": { + "source": "iana" + }, + "image/pjpeg": { + "compressible": false + }, + "image/png": { + "source": "iana", + "compressible": false, + "extensions": ["png"] + }, + "image/prs.btif": { + "source": "iana", + "extensions": ["btif"] + }, + "image/prs.pti": { + "source": "iana" + }, + "image/pwg-raster": { + "source": "iana" + }, + "image/sgi": { + "source": "apache", + "extensions": ["sgi"] + }, + "image/svg+xml": { + "source": "iana", + "compressible": true, + "extensions": ["svg","svgz"] + }, + "image/t38": { + "source": "iana" + }, + "image/tiff": { + "source": "iana", + "compressible": false, + "extensions": ["tiff","tif"] + }, + "image/tiff-fx": { + "source": "iana" + }, + "image/vnd.adobe.photoshop": { + "source": "iana", + "compressible": true, + "extensions": ["psd"] + }, + "image/vnd.airzip.accelerator.azv": { + "source": "iana" + }, + "image/vnd.cns.inf2": { + "source": "iana" + }, + "image/vnd.dece.graphic": { + "source": "iana", + "extensions": ["uvi","uvvi","uvg","uvvg"] + }, + "image/vnd.djvu": { + "source": "iana", + "extensions": ["djvu","djv"] + }, + "image/vnd.dvb.subtitle": { + "source": "iana", + "extensions": ["sub"] + }, + "image/vnd.dwg": { + "source": "iana", + "extensions": ["dwg"] + }, + "image/vnd.dxf": { + "source": "iana", + "extensions": ["dxf"] + }, + "image/vnd.fastbidsheet": { + "source": "iana", + "extensions": ["fbs"] + }, + "image/vnd.fpx": { + "source": "iana", + "extensions": ["fpx"] + }, + "image/vnd.fst": { + "source": "iana", + "extensions": ["fst"] + }, + "image/vnd.fujixerox.edmics-mmr": { + "source": "iana", + "extensions": ["mmr"] + }, + "image/vnd.fujixerox.edmics-rlc": { + "source": "iana", + "extensions": ["rlc"] + }, + "image/vnd.globalgraphics.pgb": { + "source": "iana" + }, + "image/vnd.microsoft.icon": { + "source": "iana" + }, + "image/vnd.mix": { + "source": "iana" + }, + "image/vnd.mozilla.apng": { + "source": "iana" + }, + "image/vnd.ms-modi": { + "source": "iana", + "extensions": ["mdi"] + }, + "image/vnd.ms-photo": { + "source": "apache", + "extensions": ["wdp"] + }, + "image/vnd.net-fpx": { + "source": "iana", + "extensions": ["npx"] + }, + "image/vnd.radiance": { + "source": "iana" + }, + "image/vnd.sealed.png": { + "source": "iana" + }, + "image/vnd.sealedmedia.softseal.gif": { + "source": "iana" + }, + "image/vnd.sealedmedia.softseal.jpg": { + "source": "iana" + }, + "image/vnd.svf": { + "source": "iana" + }, + "image/vnd.tencent.tap": { + "source": "iana" + }, + "image/vnd.valve.source.texture": { + "source": "iana" + }, + "image/vnd.wap.wbmp": { + "source": "iana", + "extensions": ["wbmp"] + }, + "image/vnd.xiff": { + "source": "iana", + "extensions": ["xif"] + }, + "image/vnd.zbrush.pcx": { + "source": "iana" + }, + "image/webp": { + "source": "apache", + "extensions": ["webp"] + }, + "image/x-3ds": { + "source": "apache", + "extensions": ["3ds"] + }, + "image/x-cmu-raster": { + "source": "apache", + "extensions": ["ras"] + }, + "image/x-cmx": { + "source": "apache", + "extensions": ["cmx"] + }, + "image/x-freehand": { + "source": "apache", + "extensions": ["fh","fhc","fh4","fh5","fh7"] + }, + "image/x-icon": { + "source": "apache", + "compressible": true, + "extensions": ["ico"] + }, + "image/x-jng": { + "source": "nginx", + "extensions": ["jng"] + }, + "image/x-mrsid-image": { + "source": "apache", + "extensions": ["sid"] + }, + "image/x-ms-bmp": { + "source": "nginx", + "compressible": true, + "extensions": ["bmp"] + }, + "image/x-pcx": { + "source": "apache", + "extensions": ["pcx"] + }, + "image/x-pict": { + "source": "apache", + "extensions": ["pic","pct"] + }, + "image/x-portable-anymap": { + "source": "apache", + "extensions": ["pnm"] + }, + "image/x-portable-bitmap": { + "source": "apache", + "extensions": ["pbm"] + }, + "image/x-portable-graymap": { + "source": "apache", + "extensions": ["pgm"] + }, + "image/x-portable-pixmap": { + "source": "apache", + "extensions": ["ppm"] + }, + "image/x-rgb": { + "source": "apache", + "extensions": ["rgb"] + }, + "image/x-tga": { + "source": "apache", + "extensions": ["tga"] + }, + "image/x-xbitmap": { + "source": "apache", + "extensions": ["xbm"] + }, + "image/x-xcf": { + "compressible": false + }, + "image/x-xpixmap": { + "source": "apache", + "extensions": ["xpm"] + }, + "image/x-xwindowdump": { + "source": "apache", + "extensions": ["xwd"] + }, + "message/cpim": { + "source": "iana" + }, + "message/delivery-status": { + "source": "iana" + }, + "message/disposition-notification": { + "source": "iana" + }, + "message/external-body": { + "source": "iana" + }, + "message/feedback-report": { + "source": "iana" + }, + "message/global": { + "source": "iana" + }, + "message/global-delivery-status": { + "source": "iana" + }, + "message/global-disposition-notification": { + "source": "iana" + }, + "message/global-headers": { + "source": "iana" + }, + "message/http": { + "source": "iana", + "compressible": false + }, + "message/imdn+xml": { + "source": "iana", + "compressible": true + }, + "message/news": { + "source": "iana" + }, + "message/partial": { + "source": "iana", + "compressible": false + }, + "message/rfc822": { + "source": "iana", + "compressible": true, + "extensions": ["eml","mime"] + }, + "message/s-http": { + "source": "iana" + }, + "message/sip": { + "source": "iana" + }, + "message/sipfrag": { + "source": "iana" + }, + "message/tracking-status": { + "source": "iana" + }, + "message/vnd.si.simp": { + "source": "iana" + }, + "message/vnd.wfa.wsc": { + "source": "iana" + }, + "model/iges": { + "source": "iana", + "compressible": false, + "extensions": ["igs","iges"] + }, + "model/mesh": { + "source": "iana", + "compressible": false, + "extensions": ["msh","mesh","silo"] + }, + "model/vnd.collada+xml": { + "source": "iana", + "extensions": ["dae"] + }, + "model/vnd.dwf": { + "source": "iana", + "extensions": ["dwf"] + }, + "model/vnd.flatland.3dml": { + "source": "iana" + }, + "model/vnd.gdl": { + "source": "iana", + "extensions": ["gdl"] + }, + "model/vnd.gs-gdl": { + "source": "apache" + }, + "model/vnd.gs.gdl": { + "source": "iana" + }, + "model/vnd.gtw": { + "source": "iana", + "extensions": ["gtw"] + }, + "model/vnd.moml+xml": { + "source": "iana" + }, + "model/vnd.mts": { + "source": "iana", + "extensions": ["mts"] + }, + "model/vnd.opengex": { + "source": "iana" + }, + "model/vnd.parasolid.transmit.binary": { + "source": "iana" + }, + "model/vnd.parasolid.transmit.text": { + "source": "iana" + }, + "model/vnd.valve.source.compiled-map": { + "source": "iana" + }, + "model/vnd.vtu": { + "source": "iana", + "extensions": ["vtu"] + }, + "model/vrml": { + "source": "iana", + "compressible": false, + "extensions": ["wrl","vrml"] + }, + "model/x3d+binary": { + "source": "apache", + "compressible": false, + "extensions": ["x3db","x3dbz"] + }, + "model/x3d+fastinfoset": { + "source": "iana" + }, + "model/x3d+vrml": { + "source": "apache", + "compressible": false, + "extensions": ["x3dv","x3dvz"] + }, + "model/x3d+xml": { + "source": "iana", + "compressible": true, + "extensions": ["x3d","x3dz"] + }, + "model/x3d-vrml": { + "source": "iana" + }, + "multipart/alternative": { + "source": "iana", + "compressible": false + }, + "multipart/appledouble": { + "source": "iana" + }, + "multipart/byteranges": { + "source": "iana" + }, + "multipart/digest": { + "source": "iana" + }, + "multipart/encrypted": { + "source": "iana", + "compressible": false + }, + "multipart/form-data": { + "source": "iana", + "compressible": false + }, + "multipart/header-set": { + "source": "iana" + }, + "multipart/mixed": { + "source": "iana", + "compressible": false + }, + "multipart/parallel": { + "source": "iana" + }, + "multipart/related": { + "source": "iana", + "compressible": false + }, + "multipart/report": { + "source": "iana" + }, + "multipart/signed": { + "source": "iana", + "compressible": false + }, + "multipart/voice-message": { + "source": "iana" + }, + "multipart/x-mixed-replace": { + "source": "iana" + }, + "text/1d-interleaved-parityfec": { + "source": "iana" + }, + "text/cache-manifest": { + "source": "iana", + "compressible": true, + "extensions": ["appcache","manifest"] + }, + "text/calendar": { + "source": "iana", + "extensions": ["ics","ifb"] + }, + "text/calender": { + "compressible": true + }, + "text/cmd": { + "compressible": true + }, + "text/coffeescript": { + "extensions": ["coffee","litcoffee"] + }, + "text/css": { + "source": "iana", + "compressible": true, + "extensions": ["css"] + }, + "text/csv": { + "source": "iana", + "compressible": true, + "extensions": ["csv"] + }, + "text/csv-schema": { + "source": "iana" + }, + "text/directory": { + "source": "iana" + }, + "text/dns": { + "source": "iana" + }, + "text/ecmascript": { + "source": "iana" + }, + "text/encaprtp": { + "source": "iana" + }, + "text/enriched": { + "source": "iana" + }, + "text/fwdred": { + "source": "iana" + }, + "text/grammar-ref-list": { + "source": "iana" + }, + "text/hjson": { + "extensions": ["hjson"] + }, + "text/html": { + "source": "iana", + "compressible": true, + "extensions": ["html","htm","shtml"] + }, + "text/jade": { + "extensions": ["jade"] + }, + "text/javascript": { + "source": "iana", + "compressible": true + }, + "text/jcr-cnd": { + "source": "iana" + }, + "text/jsx": { + "compressible": true, + "extensions": ["jsx"] + }, + "text/less": { + "extensions": ["less"] + }, + "text/markdown": { + "source": "iana" + }, + "text/mathml": { + "source": "nginx", + "extensions": ["mml"] + }, + "text/mizar": { + "source": "iana" + }, + "text/n3": { + "source": "iana", + "compressible": true, + "extensions": ["n3"] + }, + "text/parameters": { + "source": "iana" + }, + "text/parityfec": { + "source": "iana" + }, + "text/plain": { + "source": "iana", + "compressible": true, + "extensions": ["txt","text","conf","def","list","log","in","ini"] + }, + "text/provenance-notation": { + "source": "iana" + }, + "text/prs.fallenstein.rst": { + "source": "iana" + }, + "text/prs.lines.tag": { + "source": "iana", + "extensions": ["dsc"] + }, + "text/raptorfec": { + "source": "iana" + }, + "text/red": { + "source": "iana" + }, + "text/rfc822-headers": { + "source": "iana" + }, + "text/richtext": { + "source": "iana", + "compressible": true, + "extensions": ["rtx"] + }, + "text/rtf": { + "source": "iana", + "compressible": true, + "extensions": ["rtf"] + }, + "text/rtp-enc-aescm128": { + "source": "iana" + }, + "text/rtploopback": { + "source": "iana" + }, + "text/rtx": { + "source": "iana" + }, + "text/sgml": { + "source": "iana", + "extensions": ["sgml","sgm"] + }, + "text/stylus": { + "extensions": ["stylus","styl"] + }, + "text/t140": { + "source": "iana" + }, + "text/tab-separated-values": { + "source": "iana", + "compressible": true, + "extensions": ["tsv"] + }, + "text/troff": { + "source": "iana", + "extensions": ["t","tr","roff","man","me","ms"] + }, + "text/turtle": { + "source": "iana", + "extensions": ["ttl"] + }, + "text/ulpfec": { + "source": "iana" + }, + "text/uri-list": { + "source": "iana", + "compressible": true, + "extensions": ["uri","uris","urls"] + }, + "text/vcard": { + "source": "iana", + "compressible": true, + "extensions": ["vcard"] + }, + "text/vnd.a": { + "source": "iana" + }, + "text/vnd.abc": { + "source": "iana" + }, + "text/vnd.curl": { + "source": "iana", + "extensions": ["curl"] + }, + "text/vnd.curl.dcurl": { + "source": "apache", + "extensions": ["dcurl"] + }, + "text/vnd.curl.mcurl": { + "source": "apache", + "extensions": ["mcurl"] + }, + "text/vnd.curl.scurl": { + "source": "apache", + "extensions": ["scurl"] + }, + "text/vnd.debian.copyright": { + "source": "iana" + }, + "text/vnd.dmclientscript": { + "source": "iana" + }, + "text/vnd.dvb.subtitle": { + "source": "iana", + "extensions": ["sub"] + }, + "text/vnd.esmertec.theme-descriptor": { + "source": "iana" + }, + "text/vnd.fly": { + "source": "iana", + "extensions": ["fly"] + }, + "text/vnd.fmi.flexstor": { + "source": "iana", + "extensions": ["flx"] + }, + "text/vnd.graphviz": { + "source": "iana", + "extensions": ["gv"] + }, + "text/vnd.in3d.3dml": { + "source": "iana", + "extensions": ["3dml"] + }, + "text/vnd.in3d.spot": { + "source": "iana", + "extensions": ["spot"] + }, + "text/vnd.iptc.newsml": { + "source": "iana" + }, + "text/vnd.iptc.nitf": { + "source": "iana" + }, + "text/vnd.latex-z": { + "source": "iana" + }, + "text/vnd.motorola.reflex": { + "source": "iana" + }, + "text/vnd.ms-mediapackage": { + "source": "iana" + }, + "text/vnd.net2phone.commcenter.command": { + "source": "iana" + }, + "text/vnd.radisys.msml-basic-layout": { + "source": "iana" + }, + "text/vnd.si.uricatalogue": { + "source": "iana" + }, + "text/vnd.sun.j2me.app-descriptor": { + "source": "iana", + "extensions": ["jad"] + }, + "text/vnd.trolltech.linguist": { + "source": "iana" + }, + "text/vnd.wap.si": { + "source": "iana" + }, + "text/vnd.wap.sl": { + "source": "iana" + }, + "text/vnd.wap.wml": { + "source": "iana", + "extensions": ["wml"] + }, + "text/vnd.wap.wmlscript": { + "source": "iana", + "extensions": ["wmls"] + }, + "text/vtt": { + "charset": "UTF-8", + "compressible": true, + "extensions": ["vtt"] + }, + "text/x-asm": { + "source": "apache", + "extensions": ["s","asm"] + }, + "text/x-c": { + "source": "apache", + "extensions": ["c","cc","cxx","cpp","h","hh","dic"] + }, + "text/x-component": { + "source": "nginx", + "extensions": ["htc"] + }, + "text/x-fortran": { + "source": "apache", + "extensions": ["f","for","f77","f90"] + }, + "text/x-gwt-rpc": { + "compressible": true + }, + "text/x-handlebars-template": { + "extensions": ["hbs"] + }, + "text/x-java-source": { + "source": "apache", + "extensions": ["java"] + }, + "text/x-jquery-tmpl": { + "compressible": true + }, + "text/x-lua": { + "extensions": ["lua"] + }, + "text/x-markdown": { + "compressible": true, + "extensions": ["markdown","md","mkd"] + }, + "text/x-nfo": { + "source": "apache", + "extensions": ["nfo"] + }, + "text/x-opml": { + "source": "apache", + "extensions": ["opml"] + }, + "text/x-pascal": { + "source": "apache", + "extensions": ["p","pas"] + }, + "text/x-processing": { + "compressible": true, + "extensions": ["pde"] + }, + "text/x-sass": { + "extensions": ["sass"] + }, + "text/x-scss": { + "extensions": ["scss"] + }, + "text/x-setext": { + "source": "apache", + "extensions": ["etx"] + }, + "text/x-sfv": { + "source": "apache", + "extensions": ["sfv"] + }, + "text/x-suse-ymp": { + "compressible": true, + "extensions": ["ymp"] + }, + "text/x-uuencode": { + "source": "apache", + "extensions": ["uu"] + }, + "text/x-vcalendar": { + "source": "apache", + "extensions": ["vcs"] + }, + "text/x-vcard": { + "source": "apache", + "extensions": ["vcf"] + }, + "text/xml": { + "source": "iana", + "compressible": true, + "extensions": ["xml"] + }, + "text/xml-external-parsed-entity": { + "source": "iana" + }, + "text/yaml": { + "extensions": ["yaml","yml"] + }, + "video/1d-interleaved-parityfec": { + "source": "apache" + }, + "video/3gpp": { + "source": "apache", + "extensions": ["3gp","3gpp"] + }, + "video/3gpp-tt": { + "source": "apache" + }, + "video/3gpp2": { + "source": "apache", + "extensions": ["3g2"] + }, + "video/bmpeg": { + "source": "apache" + }, + "video/bt656": { + "source": "apache" + }, + "video/celb": { + "source": "apache" + }, + "video/dv": { + "source": "apache" + }, + "video/h261": { + "source": "apache", + "extensions": ["h261"] + }, + "video/h263": { + "source": "apache", + "extensions": ["h263"] + }, + "video/h263-1998": { + "source": "apache" + }, + "video/h263-2000": { + "source": "apache" + }, + "video/h264": { + "source": "apache", + "extensions": ["h264"] + }, + "video/h264-rcdo": { + "source": "apache" + }, + "video/h264-svc": { + "source": "apache" + }, + "video/jpeg": { + "source": "apache", + "extensions": ["jpgv"] + }, + "video/jpeg2000": { + "source": "apache" + }, + "video/jpm": { + "source": "apache", + "extensions": ["jpm","jpgm"] + }, + "video/mj2": { + "source": "apache", + "extensions": ["mj2","mjp2"] + }, + "video/mp1s": { + "source": "apache" + }, + "video/mp2p": { + "source": "apache" + }, + "video/mp2t": { + "source": "apache", + "extensions": ["ts"] + }, + "video/mp4": { + "source": "apache", + "compressible": false, + "extensions": ["mp4","mp4v","mpg4"] + }, + "video/mp4v-es": { + "source": "apache" + }, + "video/mpeg": { + "source": "apache", + "compressible": false, + "extensions": ["mpeg","mpg","mpe","m1v","m2v"] + }, + "video/mpeg4-generic": { + "source": "apache" + }, + "video/mpv": { + "source": "apache" + }, + "video/nv": { + "source": "apache" + }, + "video/ogg": { + "source": "apache", + "compressible": false, + "extensions": ["ogv"] + }, + "video/parityfec": { + "source": "apache" + }, + "video/pointer": { + "source": "apache" + }, + "video/quicktime": { + "source": "apache", + "compressible": false, + "extensions": ["qt","mov"] + }, + "video/raw": { + "source": "apache" + }, + "video/rtp-enc-aescm128": { + "source": "apache" + }, + "video/rtx": { + "source": "apache" + }, + "video/smpte292m": { + "source": "apache" + }, + "video/ulpfec": { + "source": "apache" + }, + "video/vc1": { + "source": "apache" + }, + "video/vnd.cctv": { + "source": "apache" + }, + "video/vnd.dece.hd": { + "source": "apache", + "extensions": ["uvh","uvvh"] + }, + "video/vnd.dece.mobile": { + "source": "apache", + "extensions": ["uvm","uvvm"] + }, + "video/vnd.dece.mp4": { + "source": "apache" + }, + "video/vnd.dece.pd": { + "source": "apache", + "extensions": ["uvp","uvvp"] + }, + "video/vnd.dece.sd": { + "source": "apache", + "extensions": ["uvs","uvvs"] + }, + "video/vnd.dece.video": { + "source": "apache", + "extensions": ["uvv","uvvv"] + }, + "video/vnd.directv.mpeg": { + "source": "apache" + }, + "video/vnd.directv.mpeg-tts": { + "source": "apache" + }, + "video/vnd.dlna.mpeg-tts": { + "source": "apache" + }, + "video/vnd.dvb.file": { + "source": "apache", + "extensions": ["dvb"] + }, + "video/vnd.fvt": { + "source": "apache", + "extensions": ["fvt"] + }, + "video/vnd.hns.video": { + "source": "apache" + }, + "video/vnd.iptvforum.1dparityfec-1010": { + "source": "apache" + }, + "video/vnd.iptvforum.1dparityfec-2005": { + "source": "apache" + }, + "video/vnd.iptvforum.2dparityfec-1010": { + "source": "apache" + }, + "video/vnd.iptvforum.2dparityfec-2005": { + "source": "apache" + }, + "video/vnd.iptvforum.ttsavc": { + "source": "apache" + }, + "video/vnd.iptvforum.ttsmpeg2": { + "source": "apache" + }, + "video/vnd.motorola.video": { + "source": "apache" + }, + "video/vnd.motorola.videop": { + "source": "apache" + }, + "video/vnd.mpegurl": { + "source": "apache", + "extensions": ["mxu","m4u"] + }, + "video/vnd.ms-playready.media.pyv": { + "source": "apache", + "extensions": ["pyv"] + }, + "video/vnd.nokia.interleaved-multimedia": { + "source": "apache" + }, + "video/vnd.nokia.videovoip": { + "source": "apache" + }, + "video/vnd.objectvideo": { + "source": "apache" + }, + "video/vnd.sealed.mpeg1": { + "source": "apache" + }, + "video/vnd.sealed.mpeg4": { + "source": "apache" + }, + "video/vnd.sealed.swf": { + "source": "apache" + }, + "video/vnd.sealedmedia.softseal.mov": { + "source": "apache" + }, + "video/vnd.uvvu.mp4": { + "source": "apache", + "extensions": ["uvu","uvvu"] + }, + "video/vnd.vivo": { + "source": "apache", + "extensions": ["viv"] + }, + "video/webm": { + "source": "apache", + "compressible": false, + "extensions": ["webm"] + }, + "video/x-f4v": { + "source": "apache", + "extensions": ["f4v"] + }, + "video/x-fli": { + "source": "apache", + "extensions": ["fli"] + }, + "video/x-flv": { + "source": "apache", + "compressible": false, + "extensions": ["flv"] + }, + "video/x-m4v": { + "source": "apache", + "extensions": ["m4v"] + }, + "video/x-matroska": { + "source": "apache", + "compressible": false, + "extensions": ["mkv","mk3d","mks"] + }, + "video/x-mng": { + "source": "apache", + "extensions": ["mng"] + }, + "video/x-ms-asf": { + "source": "apache", + "extensions": ["asf","asx"] + }, + "video/x-ms-vob": { + "source": "apache", + "extensions": ["vob"] + }, + "video/x-ms-wm": { + "source": "apache", + "extensions": ["wm"] + }, + "video/x-ms-wmv": { + "source": "apache", + "compressible": false, + "extensions": ["wmv"] + }, + "video/x-ms-wmx": { + "source": "apache", + "extensions": ["wmx"] + }, + "video/x-ms-wvx": { + "source": "apache", + "extensions": ["wvx"] + }, + "video/x-msvideo": { + "source": "apache", + "extensions": ["avi"] + }, + "video/x-sgi-movie": { + "source": "apache", + "extensions": ["movie"] + }, + "video/x-smv": { + "source": "apache", + "extensions": ["smv"] + }, + "x-conference/x-cooltalk": { + "source": "apache", + "extensions": ["ice"] + }, + "x-shader/x-fragment": { + "compressible": true + }, + "x-shader/x-vertex": { + "compressible": true + } +} diff --git a/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/index.js b/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/index.js new file mode 100644 index 0000000..551031f --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/index.js @@ -0,0 +1,11 @@ +/*! + * mime-db + * Copyright(c) 2014 Jonathan Ong + * MIT Licensed + */ + +/** + * Module exports. + */ + +module.exports = require('./db.json') diff --git a/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/package.json b/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/package.json new file mode 100644 index 0000000..9c2119a --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/package.json @@ -0,0 +1,94 @@ +{ + "name": "mime-db", + "description": "Media Type Database", + "version": "1.20.0", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + { + "name": "Robert Kieffer", + "email": "robert@broofa.com", + "url": "http://github.com/broofa" + } + ], + "license": "MIT", + "keywords": [ + "mime", + "db", + "type", + "types", + "database", + "charset", + "charsets" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/jshttp/mime-db.git" + }, + "devDependencies": { + "bluebird": "2.10.0", + "co": "4.6.0", + "cogent": "1.0.1", + "csv-parse": "1.0.0", + "gnode": "0.1.1", + "istanbul": "0.4.0", + "mocha": "1.21.5", + "raw-body": "2.1.4", + "stream-to-array": "2.2.0" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "README.md", + "db.json", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "build": "node scripts/build", + "fetch": "gnode scripts/fetch-apache && gnode scripts/fetch-iana && gnode scripts/fetch-nginx", + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/", + "update": "npm run fetch && npm run build" + }, + "gitHead": "20c99312645c05ab8466701ede01bd5cd3ac7bc4", + "bugs": { + "url": "https://github.com/jshttp/mime-db/issues" + }, + "homepage": "https://github.com/jshttp/mime-db", + "_id": "mime-db@1.20.0", + "_shasum": "496f90fd01fe0e031c8823ec3aa9450ffda18ed8", + "_from": "mime-db@>=1.20.0 <1.21.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "496f90fd01fe0e031c8823ec3aa9450ffda18ed8", + "tarball": "http://registry.npmjs.org/mime-db/-/mime-db-1.20.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.20.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/express/node_modules/type-is/node_modules/mime-types/package.json b/node_modules/express/node_modules/type-is/node_modules/mime-types/package.json new file mode 100644 index 0000000..ffd4924 --- /dev/null +++ b/node_modules/express/node_modules/type-is/node_modules/mime-types/package.json @@ -0,0 +1,84 @@ +{ + "name": "mime-types", + "description": "The ultimate javascript content-type utility.", + "version": "2.1.8", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jeremiah Senkpiel", + "email": "fishrock123@rocketmail.com", + "url": "https://searchbeam.jit.su" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + } + ], + "license": "MIT", + "keywords": [ + "mime", + "types" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/jshttp/mime-types.git" + }, + "dependencies": { + "mime-db": "~1.20.0" + }, + "devDependencies": { + "istanbul": "0.4.1", + "mocha": "~1.21.5" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec test/test.js", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot test/test.js", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter dot test/test.js" + }, + "gitHead": "100876a23fab896d8cf0d904fc9778dbdfc1695b", + "bugs": { + "url": "https://github.com/jshttp/mime-types/issues" + }, + "homepage": "https://github.com/jshttp/mime-types", + "_id": "mime-types@2.1.8", + "_shasum": "faf57823de04bc7cbff4ee82c6b63946e812ae72", + "_from": "mime-types@>=2.1.7 <2.2.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "faf57823de04bc7cbff4ee82c6b63946e812ae72", + "tarball": "http://registry.npmjs.org/mime-types/-/mime-types-2.1.8.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.8.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/express/node_modules/type-is/package.json b/node_modules/express/node_modules/type-is/package.json new file mode 100644 index 0000000..5c695e4 --- /dev/null +++ b/node_modules/express/node_modules/type-is/package.json @@ -0,0 +1,92 @@ +{ + "name": "type-is", + "description": "Infer the content-type of a request.", + "version": "1.6.9", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/jshttp/type-is" + }, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.7" + }, + "devDependencies": { + "istanbul": "0.3.21", + "mocha": "~1.21.5" + }, + "engines": { + "node": ">= 0.6" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "index.js" + ], + "scripts": { + "test": "mocha --reporter spec --check-leaks --bail test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "keywords": [ + "content", + "type", + "checking" + ], + "gitHead": "2f5999d6f2d88f2f36eeb1e8db78c2ec43fdbf13", + "bugs": { + "url": "https://github.com/jshttp/type-is/issues" + }, + "homepage": "https://github.com/jshttp/type-is", + "_id": "type-is@1.6.9", + "_shasum": "87f3e88b92ff5ac30fbc1acf9a9d00cbc38b3d7a", + "_from": "type-is@>=1.6.6 <1.7.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + }, + { + "name": "defunctzombie", + "email": "shtylman@gmail.com" + } + ], + "dist": { + "shasum": "87f3e88b92ff5ac30fbc1acf9a9d00cbc38b3d7a", + "tarball": "http://registry.npmjs.org/type-is/-/type-is-1.6.9.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.9.tgz" +} diff --git a/node_modules/express/node_modules/utils-merge/.travis.yml b/node_modules/express/node_modules/utils-merge/.travis.yml new file mode 100644 index 0000000..af92b02 --- /dev/null +++ b/node_modules/express/node_modules/utils-merge/.travis.yml @@ -0,0 +1,6 @@ +language: "node_js" +node_js: + - "0.4" + - "0.6" + - "0.8" + - "0.10" diff --git a/node_modules/express/node_modules/utils-merge/LICENSE b/node_modules/express/node_modules/utils-merge/LICENSE new file mode 100644 index 0000000..e33bd10 --- /dev/null +++ b/node_modules/express/node_modules/utils-merge/LICENSE @@ -0,0 +1,20 @@ +(The MIT License) + +Copyright (c) 2013 Jared Hanson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/utils-merge/README.md b/node_modules/express/node_modules/utils-merge/README.md new file mode 100644 index 0000000..2f94e9b --- /dev/null +++ b/node_modules/express/node_modules/utils-merge/README.md @@ -0,0 +1,34 @@ +# utils-merge + +Merges the properties from a source object into a destination object. + +## Install + + $ npm install utils-merge + +## Usage + +```javascript +var a = { foo: 'bar' } + , b = { bar: 'baz' }; + +merge(a, b); +// => { foo: 'bar', bar: 'baz' } +``` + +## Tests + + $ npm install + $ npm test + +[![Build Status](https://secure.travis-ci.org/jaredhanson/utils-merge.png)](http://travis-ci.org/jaredhanson/utils-merge) + +## Credits + + - [Jared Hanson](http://github.com/jaredhanson) + +## License + +[The MIT License](http://opensource.org/licenses/MIT) + +Copyright (c) 2013 Jared Hanson <[http://jaredhanson.net/](http://jaredhanson.net/)> diff --git a/node_modules/express/node_modules/utils-merge/index.js b/node_modules/express/node_modules/utils-merge/index.js new file mode 100644 index 0000000..4265c69 --- /dev/null +++ b/node_modules/express/node_modules/utils-merge/index.js @@ -0,0 +1,23 @@ +/** + * Merge object b with object a. + * + * var a = { foo: 'bar' } + * , b = { bar: 'baz' }; + * + * merge(a, b); + * // => { foo: 'bar', bar: 'baz' } + * + * @param {Object} a + * @param {Object} b + * @return {Object} + * @api public + */ + +exports = module.exports = function(a, b){ + if (a && b) { + for (var key in b) { + a[key] = b[key]; + } + } + return a; +}; diff --git a/node_modules/express/node_modules/utils-merge/package.json b/node_modules/express/node_modules/utils-merge/package.json new file mode 100644 index 0000000..7c115b0 --- /dev/null +++ b/node_modules/express/node_modules/utils-merge/package.json @@ -0,0 +1,58 @@ +{ + "name": "utils-merge", + "version": "1.0.0", + "description": "merge() utility function", + "keywords": [ + "util" + ], + "repository": { + "type": "git", + "url": "git://github.com/jaredhanson/utils-merge.git" + }, + "bugs": { + "url": "http://github.com/jaredhanson/utils-merge/issues" + }, + "author": { + "name": "Jared Hanson", + "email": "jaredhanson@gmail.com", + "url": "http://www.jaredhanson.net/" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://www.opensource.org/licenses/MIT" + } + ], + "main": "./index", + "dependencies": {}, + "devDependencies": { + "mocha": "1.x.x", + "chai": "1.x.x" + }, + "scripts": { + "test": "node_modules/.bin/mocha --reporter spec --require test/bootstrap/node test/*.test.js" + }, + "engines": { + "node": ">= 0.4.0" + }, + "_id": "utils-merge@1.0.0", + "dist": { + "shasum": "0294fb922bb9375153541c4f7096231f287c8af8", + "tarball": "http://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" + }, + "_from": "utils-merge@1.0.0", + "_npmVersion": "1.2.25", + "_npmUser": { + "name": "jaredhanson", + "email": "jaredhanson@gmail.com" + }, + "maintainers": [ + { + "name": "jaredhanson", + "email": "jaredhanson@gmail.com" + } + ], + "directories": {}, + "_shasum": "0294fb922bb9375153541c4f7096231f287c8af8", + "_resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" +} diff --git a/node_modules/express/node_modules/vary/HISTORY.md b/node_modules/express/node_modules/vary/HISTORY.md new file mode 100644 index 0000000..cddbcd4 --- /dev/null +++ b/node_modules/express/node_modules/vary/HISTORY.md @@ -0,0 +1,23 @@ +1.0.1 / 2015-07-08 +================== + + * Fix setting empty header from empty `field` + * perf: enable strict mode + * perf: remove argument reassignments + +1.0.0 / 2014-08-10 +================== + + * Accept valid `Vary` header string as `field` + * Add `vary.append` for low-level string manipulation + * Move to `jshttp` orgainzation + +0.1.0 / 2014-06-05 +================== + + * Support array of fields to set + +0.0.0 / 2014-06-04 +================== + + * Initial release diff --git a/node_modules/express/node_modules/vary/LICENSE b/node_modules/express/node_modules/vary/LICENSE new file mode 100644 index 0000000..142ede3 --- /dev/null +++ b/node_modules/express/node_modules/vary/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2014-2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express/node_modules/vary/README.md b/node_modules/express/node_modules/vary/README.md new file mode 100644 index 0000000..5966542 --- /dev/null +++ b/node_modules/express/node_modules/vary/README.md @@ -0,0 +1,91 @@ +# vary + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Manipulate the HTTP Vary header + +## Installation + +```sh +$ npm install vary +``` + +## API + +```js +var vary = require('vary') +``` + +### vary(res, field) + +Adds the given header `field` to the `Vary` response header of `res`. +This can be a string of a single field, a string of a valid `Vary` +header, or an array of multiple fields. + +This will append the header if not already listed, otherwise leaves +it listed in the current location. + +```js +// Append "Origin" to the Vary header of the response +vary(res, 'Origin') +``` + +### vary.append(header, field) + +Adds the given header `field` to the `Vary` response header string `header`. +This can be a string of a single field, a string of a valid `Vary` header, +or an array of multiple fields. + +This will append the header if not already listed, otherwise leaves +it listed in the current location. The new header string is returned. + +```js +// Get header string appending "Origin" to "Accept, User-Agent" +vary.append('Accept, User-Agent', 'Origin') +``` + +## Examples + +### Updating the Vary header when content is based on it + +```js +var http = require('http') +var vary = require('vary') + +http.createServer(function onRequest(req, res) { + // about to user-agent sniff + vary(res, 'User-Agent') + + var ua = req.headers['user-agent'] || '' + var isMobile = /mobi|android|touch|mini/i.test(ua) + + // serve site, depending on isMobile + res.setHeader('Content-Type', 'text/html') + res.end('You are (probably) ' + (isMobile ? '' : 'not ') + 'a mobile user') +}) +``` + +## Testing + +```sh +$ npm test +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/vary.svg +[npm-url]: https://npmjs.org/package/vary +[node-version-image]: https://img.shields.io/node/v/vary.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/vary/master.svg +[travis-url]: https://travis-ci.org/jshttp/vary +[coveralls-image]: https://img.shields.io/coveralls/jshttp/vary/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/vary +[downloads-image]: https://img.shields.io/npm/dm/vary.svg +[downloads-url]: https://npmjs.org/package/vary diff --git a/node_modules/express/node_modules/vary/index.js b/node_modules/express/node_modules/vary/index.js new file mode 100644 index 0000000..e818dbb --- /dev/null +++ b/node_modules/express/node_modules/vary/index.js @@ -0,0 +1,117 @@ +/*! + * vary + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Module exports. + */ + +module.exports = vary; +module.exports.append = append; + +/** + * Variables. + */ + +var separators = /[\(\)<>@,;:\\"\/\[\]\?=\{\}\u0020\u0009]/; + +/** + * Append a field to a vary header. + * + * @param {String} header + * @param {String|Array} field + * @return {String} + * @api public + */ + +function append(header, field) { + if (typeof header !== 'string') { + throw new TypeError('header argument is required'); + } + + if (!field) { + throw new TypeError('field argument is required'); + } + + // get fields array + var fields = !Array.isArray(field) + ? parse(String(field)) + : field; + + // assert on invalid fields + for (var i = 0; i < fields.length; i++) { + if (separators.test(fields[i])) { + throw new TypeError('field argument contains an invalid header'); + } + } + + // existing, unspecified vary + if (header === '*') { + return header; + } + + // enumerate current values + var val = header; + var vals = parse(header.toLowerCase()); + + // unspecified vary + if (fields.indexOf('*') !== -1 || vals.indexOf('*') !== -1) { + return '*'; + } + + for (var i = 0; i < fields.length; i++) { + var fld = fields[i].toLowerCase(); + + // append value (case-preserving) + if (vals.indexOf(fld) === -1) { + vals.push(fld); + val = val + ? val + ', ' + fields[i] + : fields[i]; + } + } + + return val; +} + +/** + * Parse a vary header into an array. + * + * @param {String} header + * @return {Array} + * @api private + */ + +function parse(header) { + return header.trim().split(/ *, */); +} + +/** + * Mark that a request is varied on a header field. + * + * @param {Object} res + * @param {String|Array} field + * @api public + */ + +function vary(res, field) { + if (!res || !res.getHeader || !res.setHeader) { + // quack quack + throw new TypeError('res argument is required'); + } + + // get existing header + var val = res.getHeader('Vary') || '' + var header = Array.isArray(val) + ? val.join(', ') + : String(val); + + // set new header + if ((val = append(header, field))) { + res.setHeader('Vary', val); + } +} diff --git a/node_modules/express/node_modules/vary/package.json b/node_modules/express/node_modules/vary/package.json new file mode 100644 index 0000000..9fa4038 --- /dev/null +++ b/node_modules/express/node_modules/vary/package.json @@ -0,0 +1,71 @@ +{ + "name": "vary", + "description": "Manipulate the HTTP Vary header", + "version": "1.0.1", + "author": { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + "license": "MIT", + "keywords": [ + "http", + "res", + "vary" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/vary" + }, + "devDependencies": { + "istanbul": "0.3.17", + "mocha": "2.2.5", + "supertest": "1.0.1" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "README.md", + "index.js" + ], + "engines": { + "node": ">= 0.8" + }, + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "650282ff8e614731837040a23e10f51c20728392", + "bugs": { + "url": "https://github.com/jshttp/vary/issues" + }, + "homepage": "https://github.com/jshttp/vary", + "_id": "vary@1.0.1", + "_shasum": "99e4981566a286118dfb2b817357df7993376d10", + "_from": "vary@>=1.0.1 <1.1.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + } + ], + "dist": { + "shasum": "99e4981566a286118dfb2b817357df7993376d10", + "tarball": "http://registry.npmjs.org/vary/-/vary-1.0.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/vary/-/vary-1.0.1.tgz" +} diff --git a/node_modules/express/package.json b/node_modules/express/package.json new file mode 100644 index 0000000..e58e801 --- /dev/null +++ b/node_modules/express/package.json @@ -0,0 +1,166 @@ +{ + "name": "express", + "description": "Fast, unopinionated, minimalist web framework", + "version": "4.13.3", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "contributors": [ + { + "name": "Aaron Heckmann", + "email": "aaron.heckmann+github@gmail.com" + }, + { + "name": "Ciaran Jessup", + "email": "ciaranj@gmail.com" + }, + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Guillermo Rauch", + "email": "rauchg@gmail.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com" + }, + { + "name": "Roman Shtylman", + "email": "shtylman+expressjs@gmail.com" + }, + { + "name": "Young Jae Sim", + "email": "hanul@hanul.me" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/strongloop/express" + }, + "homepage": "http://expressjs.com/", + "keywords": [ + "express", + "framework", + "sinatra", + "web", + "rest", + "restful", + "router", + "app", + "api" + ], + "dependencies": { + "accepts": "~1.2.12", + "array-flatten": "1.1.1", + "content-disposition": "0.5.0", + "content-type": "~1.0.1", + "cookie": "0.1.3", + "cookie-signature": "1.0.6", + "debug": "~2.2.0", + "depd": "~1.0.1", + "escape-html": "1.0.2", + "etag": "~1.7.0", + "finalhandler": "0.4.0", + "fresh": "0.3.0", + "merge-descriptors": "1.0.0", + "methods": "~1.1.1", + "on-finished": "~2.3.0", + "parseurl": "~1.3.0", + "path-to-regexp": "0.1.7", + "proxy-addr": "~1.0.8", + "qs": "4.0.0", + "range-parser": "~1.0.2", + "send": "0.13.0", + "serve-static": "~1.10.0", + "type-is": "~1.6.6", + "utils-merge": "1.0.0", + "vary": "~1.0.1" + }, + "devDependencies": { + "after": "0.8.1", + "ejs": "2.3.3", + "istanbul": "0.3.17", + "marked": "0.3.5", + "mocha": "2.2.5", + "should": "7.0.2", + "supertest": "1.0.1", + "body-parser": "~1.13.3", + "connect-redis": "~2.4.1", + "cookie-parser": "~1.3.5", + "cookie-session": "~1.2.0", + "express-session": "~1.11.3", + "jade": "~1.11.0", + "method-override": "~2.3.5", + "morgan": "~1.6.1", + "multiparty": "~4.1.2", + "vhost": "~3.0.1" + }, + "engines": { + "node": ">= 0.10.0" + }, + "files": [ + "LICENSE", + "History.md", + "Readme.md", + "index.js", + "lib/" + ], + "scripts": { + "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/", + "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/", + "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/" + }, + "gitHead": "ef7ad681b245fba023843ce94f6bcb8e275bbb8e", + "bugs": { + "url": "https://github.com/strongloop/express/issues" + }, + "_id": "express@4.13.3", + "_shasum": "ddb2f1fb4502bf33598d2b032b037960ca6c80a3", + "_from": "express@>=4.11.2 <5.0.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "rfeng", + "email": "enjoyjava@gmail.com" + }, + { + "name": "aredridel", + "email": "aredridel@dinhe.net" + }, + { + "name": "strongloop", + "email": "callback@strongloop.com" + }, + { + "name": "defunctzombie", + "email": "shtylman@gmail.com" + } + ], + "dist": { + "shasum": "ddb2f1fb4502bf33598d2b032b037960ca6c80a3", + "tarball": "http://registry.npmjs.org/express/-/express-4.13.3.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/express/-/express-4.13.3.tgz" +} diff --git a/node_modules/keep-alive-agent/.jshintrc b/node_modules/keep-alive-agent/.jshintrc new file mode 100644 index 0000000..85182f1 --- /dev/null +++ b/node_modules/keep-alive-agent/.jshintrc @@ -0,0 +1,99 @@ +{ + // == Enforcing Options =============================================== + // + // These options tell JSHint to be more strict towards your code. Use + // them if you want to allow only a safe subset of JavaScript, very + // useful when your codebase is shared with a big number of developers + // with different skill levels. + + "bitwise" : true, // Prohibit bitwise operators (&, |, ^, etc.). + "curly" : false, // Require {} for every new block or scope. + "eqeqeq" : true, // Require triple equals i.e. `===`. + "forin" : true, // Tolerate `for in` loops without `hasOwnPrototype`. + "immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` + "latedef" : true, // Prohibit variable use before definition. + "newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`. + "noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`. + "noempty" : true, // Prohibit use of empty blocks. + "nonew" : true, // Prohibit use of constructors for side-effects. + "plusplus" : false, // Prohibit use of `++` & `--`. + "regexp" : false, // Prohibit `.` and `[^...]` in regular expressions. + "undef" : true, // Require all non-global variables be declared before they are used. + "strict" : false, // Require `use strict` pragma in every file. + "trailing" : false, // Prohibit trailing whitespaces. + + // == Relaxing Options ================================================ + // + // These options allow you to suppress certain types of warnings. Use + // them only if you are absolutely positive that you know what you are + // doing. + + "asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons). + "boss" : true, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments. + "debug" : false, // Allow debugger statements e.g. browser breakpoints. + "eqnull" : false, // Tolerate use of `== null`. + "es5" : true, // Allow EcmaScript 5 syntax. + "esnext" : false, // Allow ES.next specific features such as `const` and `let`. + "evil" : false, // Tolerate use of `eval`. + "expr" : false, // Tolerate `ExpressionStatement` as Programs. + "funcscope" : false, // Tolerate declarations of variables inside of control structures while accessing them later from the outside. + "globalstrict" : false, // Allow global "use strict" (also enables 'strict'). + "iterator" : false, // Allow usage of __iterator__ property. + "lastsemic" : false, // Tolerat missing semicolons when the it is omitted for the last statement in a one-line block. + "laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons. + "laxcomma" : true, // Suppress warnings about comma-first coding style. + "loopfunc" : false, // Allow functions to be defined within loops. + "multistr" : false, // Tolerate multi-line strings. + "onecase" : false, // Tolerate switches with just one case. + "proto" : false, // Tolerate __proto__ property. This property is deprecated. + "regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`. + "scripturl" : false, // Tolerate script-targeted URLs. + "smarttabs" : true, // Tolerate mixed tabs and spaces when the latter are used for alignmnent only. + "shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`. + "sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. + "supernew" : false, // Tolerate `new function () { ... };` and `new Object;`. + "validthis" : false, // Tolerate strict violations when the code is running in strict mode and you use this in a non-constructor function. + + // == Environments ==================================================== + // + // These options pre-define global variables that are exposed by + // popular JavaScript libraries and runtime environments—such as + // browser or node.js. + + "browser" : false, // Standard browser globals e.g. `window`, `document`. + "couch" : false, // Enable globals exposed by CouchDB. + "devel" : false, // Allow development statements e.g. `console.log();`. + "dojo" : false, // Enable globals exposed by Dojo Toolkit. + "jquery" : false, // Enable globals exposed by jQuery JavaScript library. + "mootools" : false, // Enable globals exposed by MooTools JavaScript framework. + "node" : true, // Enable globals available when code is running inside of the NodeJS runtime environment. + "nonstandard" : false, // Define non-standard but widely adopted globals such as escape and unescape. + "prototypejs" : false, // Enable globals exposed by Prototype JavaScript framework. + "rhino" : false, // Enable globals available when your code is running inside of the Rhino runtime environment. + "wsh" : false, // Enable globals available when your code is running as a script for the Windows Script Host. + + // == JSLint Legacy =================================================== + // + // These options are legacy from JSLint. Aside from bug fixes they will + // not be improved in any way and might be removed at any point. + + "nomen" : false, // Prohibit use of initial or trailing underbars in names. + "onevar" : false, // Allow only one `var` statement per function. + "passfail" : false, // Stop on first error. + "white" : false, // Check against strict whitespace and indentation rules. + + // == Undocumented Options ============================================ + // + // While I've found these options in [example1][2] and [example2][3] + // they are not described in the [JSHint Options documentation][4]. + // + // [4]: http://www.jshint.com/options/ + + "maxerr" : 100, // Maximum errors before stopping. + "predef" : [ // Extra globals. + //"exampleVar", + //"anotherCoolGlobal", + //"iLoveDouglas" + ], + "indent" : 4 // Specify indentation spacing +} diff --git a/node_modules/keep-alive-agent/.npmignore b/node_modules/keep-alive-agent/.npmignore new file mode 100644 index 0000000..acf1074 --- /dev/null +++ b/node_modules/keep-alive-agent/.npmignore @@ -0,0 +1,3 @@ +node_modules +**log +.DS_Store diff --git a/node_modules/keep-alive-agent/LICENSE b/node_modules/keep-alive-agent/LICENSE new file mode 100644 index 0000000..a1fafaf --- /dev/null +++ b/node_modules/keep-alive-agent/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2012 C J Silverio + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/keep-alive-agent/README.md b/node_modules/keep-alive-agent/README.md new file mode 100644 index 0000000..e1d9ee3 --- /dev/null +++ b/node_modules/keep-alive-agent/README.md @@ -0,0 +1,59 @@ +# keep-alive-agent + +keep-alive-agent is an HTTP connection pool agent for node.js that re-uses sockets. It is simpler than some agents that also solve this problem because it does not attempt to replace the Agent provided by node. If you want to re-use connections, use this agent. If you want the default node behavior, use the default global agent. + +## Usage + +__new KeepAliveAgent(*options-hash*)__ + +Create an instance of the agent, passing the options hash through to the node Agent constructor. These options are in turn passed along to `createConnection()`. The KeepAliveAgent constructor does not use the options itself. The option you are most likely to change is `maxSockets`, which defaults to 5. + +To use the agent instance, set it in the `agent` field of the options passed to `http.request()` or `http.get()`. See the [http.request() documentation](http://nodejs.org/api/http.html#http_http_request_options_callback) for details. + +__new KeepAliveAgent.Secure(*options-hash*)__ + +A keep-alive agent that creates tls sockets. Use it the same way you use the http agent. + +## Examples + +```javascript +var http = require('http'), + KeepAliveAgent = require('keep-alive-agent'); + +var getOptions = { + hostname: 'twitter.com', + port: 80, + path: '/dshaw', + agent: new KeepAliveAgent(), +}; +http.get(getOptions, function(response) +{ + response.pipe(process.stdout); +}); +``` + +To re-use secure connections, use the Secure keep-alive agent: + +```javascript +var https = require('https'), + KeepAliveAgent = require('keep-alive-agent'); + +var getOptions = { + hostname: 'www.duckduckgo.com', + port: 443, + path: '/?q=unicorns', + agent: new KeepAliveAgent.Secure(), +}; +https.get(getOptions, function(response) +{ + response.pipe(process.stdout); +}); +``` + +## See Also + +For other implementations, see [agentkeepalive](https://github.com/TBEDP/agentkeepalive) and the [request](https://github.com/mikeal/request) module's [ForeverAgent](https://github.com/mikeal/request/blob/master/forever.js). + +## Licence + +MIT. diff --git a/node_modules/keep-alive-agent/index.js b/node_modules/keep-alive-agent/index.js new file mode 100644 index 0000000..3b8b664 --- /dev/null +++ b/node_modules/keep-alive-agent/index.js @@ -0,0 +1,131 @@ +var + http = require('http'), + https = require('https'), + util = require('util') + ; + +//---------------------------------------------------------------------------------------- + +function KeepAliveAgent(options) +{ + options = options || {}; + http.Agent.call(this, options); + + // Keys are host:port names, values are lists of sockets. + this.idleSockets = {}; + + // Replace the 'free' listener set up by the default node Agent above. + this.removeAllListeners('free'); + this.on('free', KeepAliveAgent.prototype.freeHandler.bind(this)); + +} +util.inherits(KeepAliveAgent, http.Agent); + +function buildNameKey(host, port, localAddress) +{ + var name = host + ':' + port; + if (localAddress) + name += ':' + localAddress; + + return name; +} + +KeepAliveAgent.prototype.freeHandler = function(socket, host, port, localAddress) +{ + var name = buildNameKey(host, port, localAddress); + + // If the socket is still useful, return it to the idle pool. + if (this.isSocketUsable(socket)) + { + socket._requestCount = socket._requestCount ? socket._requestCount + 1 : 1; + + if (!this.idleSockets[name]) + this.idleSockets[name] = []; + + this.idleSockets[name].push(socket); + } + + // If we had any pending requests for this name, send the next one off now. + if (this.requests[name] && this.requests[name].length) + { + var nextRequest = this.requests[name].shift(); + + if (!this.requests[name].length) + delete this.requests[name]; + + this.addRequest(nextRequest, host, port, localAddress); + } +}; + +KeepAliveAgent.prototype.addRequest = function(request, host, port, localAddress) +{ + var name = buildNameKey(host, port, localAddress); + + var socket = this.nextIdleSocket(name); + if (socket) + request.onSocket(socket); + else + return http.Agent.prototype.addRequest.call(this, request, host, port, localAddress); +}; + +KeepAliveAgent.prototype.nextIdleSocket = function(name) +{ + if (!this.idleSockets[name]) + return null; + + var socket; + while(socket = this.idleSockets[name].shift()) + { + // Check that this socket is still healthy after sitting around on the shelf. + // This check is the reason this module exists. + if (this.isSocketUsable(socket)) + return socket; + } + + return null; +}; + +KeepAliveAgent.prototype.isSocketUsable = function(socket) +{ + return !socket.destroyed; +}; + + +KeepAliveAgent.prototype.removeSocket = function(socket, name, host, port, localAddress) +{ + if (this.idleSockets[name]) + { + var idx = this.idleSockets[name].indexOf(socket); + if (idx !== -1) + { + this.idleSockets[name].splice(idx, 1); + if (!this.idleSockets[name].length) + delete this.idleSockets[name]; + } + } + + http.Agent.prototype.removeSocket.call(this, socket, name, host, port, localAddress); +}; + +//---------------------------------------------------------------------------------------- + +function HTTPSKeepAliveAgent(options) +{ + KeepAliveAgent.call(this, options); + this.createConnection = https.globalAgent.createConnection; +} +util.inherits(HTTPSKeepAliveAgent, KeepAliveAgent); + +HTTPSKeepAliveAgent.prototype.defaultPort = 443; + +HTTPSKeepAliveAgent.prototype.isSocketUsable = function(socket) +{ + // TLS sockets null out their secure pair's ssl field in destroy() and + // do not set a destroyed flag the way non-secure sockets do. + return socket.pair && socket.pair.ssl; +}; + +//---------------------------------------------------------------------------------------- + +module.exports = KeepAliveAgent; +KeepAliveAgent.Secure = HTTPSKeepAliveAgent; diff --git a/node_modules/keep-alive-agent/package.json b/node_modules/keep-alive-agent/package.json new file mode 100644 index 0000000..8ba2276 --- /dev/null +++ b/node_modules/keep-alive-agent/package.json @@ -0,0 +1,62 @@ +{ + "name": "keep-alive-agent", + "version": "0.0.1", + "description": "a keep-alive agent for http and https", + "contributors": [ + { + "name": "C J Silverio", + "email": "ceejceej@gmail.com", + "url": "http://ceejbot.tumblr.com/" + }, + { + "name": "Danny Coates", + "email": "dannycoates@gmail.com", + "url": "https://github.com/dannycoates/" + } + ], + "main": "index.js", + "scripts": { + "test": "mocha -R spec test/*.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/ceejbot/keep-alive-agent.git" + }, + "keywords": [ + "keep-alive", + "http", + "https", + "agent" + ], + "license": "MIT", + "devDependencies": { + "chai": "*", + "mocha": "*" + }, + "readme": "# keep-alive-agent\n\nkeep-alive-agent is an HTTP connection pool agent for node.js that re-uses sockets. It is simpler than some agents that also solve this problem because it does not attempt to replace the Agent provided by node. If you want to re-use connections, use this agent. If you want the default node behavior, use the default global agent.\n\n## Usage\n\n__new KeepAliveAgent(*options-hash*)__\n\nCreate an instance of the agent, passing the options hash through to the node Agent constructor. These options are in turn passed along to `createConnection()`. The KeepAliveAgent constructor does not use the options itself. The option you are most likely to change is `maxSockets`, which defaults to 5.\n\nTo use the agent instance, set it in the `agent` field of the options passed to `http.request()` or `http.get()`. See the [http.request() documentation](http://nodejs.org/api/http.html#http_http_request_options_callback) for details.\n\n__new KeepAliveAgent.Secure(*options-hash*)__\n\nA keep-alive agent that creates tls sockets. Use it the same way you use the http agent.\n\n## Examples\n\n```javascript\nvar http = require('http'),\n KeepAliveAgent = require('keep-alive-agent');\n\nvar getOptions = {\n hostname: 'twitter.com',\n port: 80,\n path: '/dshaw',\n agent: new KeepAliveAgent(),\n};\nhttp.get(getOptions, function(response)\n{\n\tresponse.pipe(process.stdout);\n});\n```\n\nTo re-use secure connections, use the Secure keep-alive agent:\n\n```javascript\nvar https = require('https'),\n KeepAliveAgent = require('keep-alive-agent');\n\nvar getOptions = {\n hostname: 'www.duckduckgo.com',\n port: 443,\n path: '/?q=unicorns',\n agent: new KeepAliveAgent.Secure(),\n};\nhttps.get(getOptions, function(response)\n{\n\tresponse.pipe(process.stdout);\n});\n```\n\n## See Also\n\nFor other implementations, see [agentkeepalive](https://github.com/TBEDP/agentkeepalive) and the [request](https://github.com/mikeal/request) module's [ForeverAgent](https://github.com/mikeal/request/blob/master/forever.js).\n\n## Licence\n\nMIT.\n", + "readmeFilename": "README.md", + "_id": "keep-alive-agent@0.0.1", + "dist": { + "shasum": "44847ca394ce8d6b521ae85816bd64509942b385", + "tarball": "http://registry.npmjs.org/keep-alive-agent/-/keep-alive-agent-0.0.1.tgz" + }, + "_npmVersion": "1.1.65", + "_npmUser": { + "name": "ceejbot", + "email": "ceejceej@gmail.com" + }, + "maintainers": [ + { + "name": "ceejbot", + "email": "ceejceej@gmail.com" + } + ], + "directories": {}, + "_shasum": "44847ca394ce8d6b521ae85816bd64509942b385", + "_from": "keep-alive-agent@*", + "_resolved": "https://registry.npmjs.org/keep-alive-agent/-/keep-alive-agent-0.0.1.tgz", + "bugs": { + "url": "https://github.com/ceejbot/keep-alive-agent/issues" + }, + "homepage": "https://github.com/ceejbot/keep-alive-agent" +} diff --git a/node_modules/keep-alive-agent/test/test.js b/node_modules/keep-alive-agent/test/test.js new file mode 100644 index 0000000..81d3f26 --- /dev/null +++ b/node_modules/keep-alive-agent/test/test.js @@ -0,0 +1,253 @@ +/*global describe:true, it:true, before:true, after:true */ + +var + chai = require('chai'), + assert = chai.assert, + should = chai.should(), + http = require('http'), + https = require('https'), + KeepAliveAgent = require('../index'), + util = require('util') + ; + + +var serverConfig = { + hostname: 'localhost', + port: 8000 +}; + +function makeTestRequest(agent, callback) +{ + http.get( + { + hostname: serverConfig.hostname, + port: serverConfig.port, + path: '/', + agent: agent + }, callback); +} + +describe('KeepAliveAgent', function() +{ + var server; + + beforeEach(function(done) + { + server = http.createServer(function(request, response) + { + response.end("pong") + }); + server.on('listening', done); + server.listen(serverConfig.port); + }); + + afterEach(function() + { + server.close(); + server = null; + }); + + it('constructs an agent with the passed-in options', function() + { + var agent = new KeepAliveAgent({ maxSockets: 3 }); + + assert(agent.maxSockets === 3, 'max sockets option not passed through'); + agent.should.have.property('idleSockets'); + }); + + it('provides a socket to a request', function(done) + { + var agent = new KeepAliveAgent(); + http.get( + { + hostname: serverConfig.hostname, + port: serverConfig.port, + path: '/', + agent: agent + }, function(res) + { + // if we get here at all, it worked + done(); + }); + }); + + it('re-uses sockets on repeated requests to the same host:port', function(done) + { + var agent = new KeepAliveAgent(); + var getOptions = { + hostname: serverConfig.hostname, + port: serverConfig.port, + path: '/', + agent: agent + }; + + var requestsToDo = 10; + var intervalID; + + var requestOne = function() + { + http.get(getOptions, function(res) + { + if (--requestsToDo === 0) + { + clearInterval(intervalID); + + process.nextTick(function() + { + var name = serverConfig.hostname + ':' + serverConfig.port; + + agent.idleSockets.should.have.property(name); + agent.idleSockets[name].should.be.an('array'); + agent.idleSockets[name].length.should.equal(1); + var socket = agent.idleSockets[name][0]; + socket._requestCount.should.equal(10); + + done(); + }); + } + }); + }; + + intervalID = setInterval(requestOne, 15); + }); + + it('does not return destroyed sockets to the idle pool', function(done) + { + var agent = new KeepAliveAgent(); + makeTestRequest(agent, function(response) + { + response.connection.destroy(); + + process.nextTick(function() + { + var name = serverConfig.hostname + ':' + serverConfig.port; + agent.idleSockets.should.not.have.property(name); + done(); + }); + }); + }); + + it('does not attempt to use destroyed sockets from the idle list', function() + { + var agent = new KeepAliveAgent(); + var name = serverConfig.hostname + ':' + serverConfig.port; + + agent.idleSockets[name] = []; + agent.idleSockets[name].push({ destroyed: true }); + agent.idleSockets[name].push({ destroyed: true }); + agent.idleSockets[name].push({ destroyed: true }); + agent.idleSockets[name].push({ destroyed: true }); + + var socket = agent.nextIdleSocket(name); + assert.equal(socket, null); + assert.equal(agent.idleSockets[name].length, 0); + }); + + it('reuses a good socket until it is destroyed', function(done) + { + var agent = new KeepAliveAgent(); + var name = serverConfig.hostname + ':' + serverConfig.port; + + makeTestRequest(agent, function(response) + { + + process.nextTick(function() + { + agent.idleSockets.should.have.property(name); + assert(Array.isArray(agent.idleSockets[name]), 'expected idle sockets list for ' + name + ' to be an array'); + assert.equal(agent.idleSockets[name].length, 1, 'expected idle sockets list to contain exactly 1 item'); + var socket = agent.idleSockets[name][0]; + assert.equal(socket._requestCount, 1, 'expected socket request count to be 1') + + makeTestRequest(agent, function(response) + { + process.nextTick(function() + { + agent.idleSockets.should.have.property(name); + assert.equal(agent.idleSockets[name].length, 0, 'expected zero sockets in our idle queue'); + done(); + }); + response.connection.destroy(); + }); + }); + }); + }); +}); + +describe('KeepAliveAgent.Secure', function() +{ + it('can construct a secure keep-alive agent', function() + { + var secureAgent = new KeepAliveAgent.Secure({}); + assert(secureAgent.defaultPort === 443); + }); + + it('provides a socket to a request', function(done) + { + https.get( + { + hostname: 'one.voxer.com', + port: 443, + path: '/ping', + agent: new KeepAliveAgent.Secure(), + }, function(response) + { + // if we get here at all, it worked + done(); + }); + }); + + it('reuses sockets for secure connections', function(done) + { + var agent = new KeepAliveAgent.Secure(); + var getOptions = { + hostname: 'one.voxer.com', + port: 443, + path: '/ping', + agent: agent, + }; + var name = 'one.voxer.com:443'; + + https.get(getOptions, function(response) + { + + process.nextTick(function() + { + agent.idleSockets.should.have.property(name); + assert(Array.isArray(agent.idleSockets[name]), 'expected idle sockets list for ' + name + ' to be an array'); + assert.equal(agent.idleSockets[name].length, 1, 'expected idle sockets list to contain exactly 1 item'); + var socket = agent.idleSockets[name][0]; + assert.equal(socket._requestCount, 1, 'expected socket request count to be 1') + + https.get(getOptions, function(response) + { + process.nextTick(function() + { + agent.idleSockets.should.have.property(name); + assert.equal(agent.idleSockets[name].length, 0, 'expected zero sockets in our idle queue'); + done(); + }); + response.connection.destroy(); + }); + }); + }); + }); + + it('does not attempt to use destroyed sockets from the idle list', function() + { + var agent = new KeepAliveAgent.Secure(); + var name = serverConfig.hostname + ':' + serverConfig.port; + + agent.idleSockets[name] = []; + agent.idleSockets[name].push({ pair: { ssl: null } }); + agent.idleSockets[name].push({ pair: { ssl: null } }); + agent.idleSockets[name].push({ pair: { ssl: null } }); + agent.idleSockets[name].push({ pair: { ssl: null } }); + agent.idleSockets[name].push({ pair: { ssl: null } }); + + var socket = agent.nextIdleSocket(name); + assert.equal(socket, null); + assert.equal(agent.idleSockets[name].length, 0); + }); + +}); diff --git a/node_modules/mocha/HISTORY.md b/node_modules/mocha/HISTORY.md new file mode 100644 index 0000000..5a9a363 --- /dev/null +++ b/node_modules/mocha/HISTORY.md @@ -0,0 +1,1042 @@ + +2.3.4 / 2015-11-15 +================== + + * Update debug dependency to 2.2.0 + * remove duplication of mocha.opts on process.argv + * Fix typo in test/reporters/nyan.js + +2.3.3 / 2015-09-19 +================== + + * [#1875] - Fix Markdown reporter exceeds maximum call stack size ([@danielstjules]) + * [#1864] - Fix xunit missing output with --reporter-options output ([@danielstjules]) + * [#1846] - Support all harmony flags ([@danielstjules]) + * Fix fragile xunit reporter spec ([@danielstjules]) + * [#1669] - Fix catch uncaught errors outside test suite execution ([@danielstjules]) + * [#1868] - Revert jade to support npm < v1.3.7 ([@danielstjules]) + * [#1766] - Don't remove modules/components from stack trace in the browser ([@danielstjules]) + * [#1798] - Fix correctly attribute mutiple done err with hooks ([@danielstjules]) + * Fix use utils.reduce for IE8 compatibility ([@wsw0108]) + * Some linting errors fixed by [@danielstjules] + * Call the inspect() function if message is not set ([@kevinburke]) + +[#1875]: https://github.com/mochajs/mocha/issues/1875 +[#1864]: https://github.com/mochajs/mocha/issues/1864 +[#1846]: https://github.com/mochajs/mocha/issues/1846 +[#1669]: https://github.com/mochajs/mocha/issues/1669 +[#1868]: https://github.com/mochajs/mocha/issues/1868 +[#1766]: https://github.com/mochajs/mocha/issues/1766 +[#1798]: https://github.com/mochajs/mocha/issues/1798 +[@danielstjules]: https://github.com/danielstjules +[@wsw0108]: https://github.com/wsw0108 +[@kevinburke]: https://github.com/kevinburke + +2.3.2 / 2015-09-07 +================== + * [#1868] - Fix compatibility with older versions of NPM ([@boneskull]) + + [#1868]: https://github.com/mochajs/mocha/issues/1868 + +2.3.1 / 2015-09-06 +================== + + * [#1812] - Fix: Bail flag causes before() hooks to be run even after a failure ([@aaroncrows]) + + [#1812]: https://github.com/mochajs/mocha/issues/1812 + [aaroncrows]: https://github.com/aaroncrows + +2.3.0 / 2015-08-30 +================== + + * [#553] - added --allowUncaught option ([@amsul]) + * [#1490] - Allow --async-only to be satisfied by returning a promise ([@jlai]) + * [#1829] - support --max-old-space-size ([@gigadude]) + * [#1811] - upgrade Jade dependency ([@outsideris]) + * [#1769] - Fix async hook error handling ([@ajaykodali]) + * [#1230] - More descriptive beforeEach/afterEach messages ([@duncanbeevers]) + * [#1787] - Scope loading behaviour instead of using early return ([@aryeguy]) + * [#1789] - Fix: html-runner crashing ([@sunesimonsen]) + * [#1749] - Fix maximum call stack error on large amount of tests ([@tinganho]) + * [#1230] - Decorate failed hook titles with test title ([@duncanbeevers]) + * [#1260] - Build using Browserify ([@ndhoule]) + * [#1728] - Don't use `__proto__` ([@ndhoule]) + * [#1781] - Fix hook error tests ([@glenjamin]) + * [#1754] - Allow boolean --reporter-options ([@papandreou]) + * [#1766] - Fix overly aggressive stack suppression ([@moll]) + * [#1752] - Avoid potential infinite loop ([@gsilk]) + * [#1761] - Fix problems running under PhantomJS ([@chromakode]) + * [#1700] - Fix more problems running under PhantomJS ([@jbnicolai]) + * [#1774] - Support escaped spaces in CLI options ([@adamgruber]) + * [#1687] - Fix HTML reporter links with special chars ([@benvinegar]) + * [#1359] - Adopt code style and enforce it using ESLint ([@ndhoule] w/ assist from [@jbnicolai] & [@boneskull]) + * various refactors ([@jbnicolai]) + * [#1758] - Add cross-frame compatible Error checking ([@outdooricon]) + * [#1741] - Remove moot `version` property from bower.json ([@kkirsche]) + * [#1739] - Improve `HISTORY.md` ([@rstacruz]) + * [#1730] - Support more io.js flags ([@ryedog]) + * [#1349] - Allow HTML in HTML reporter errors ([@papandreou] / [@sunesimonsen]) + * [#1572] - Prevent default browser behavior for failure/pass links ([@jschilli]) + * [#1630] - Support underscored harmony flags ([@dominicbarnes]) + * [#1718] - Support more harmony flags ([@slyg]) + * [#1689] - Add stack to JSON-stream reporter ([@jonathandelgado]) + * [#1654] - Fix `ReferenceError` "location is not defined" ([@jakemmarsh]) + + [#553]: https://github.com/mochajs/mocha/issues/553 + [#1490]: https://github.com/mochajs/mocha/issues/1490 + [#1829]: https://github.com/mochajs/mocha/issues/1829 + [#1811]: https://github.com/mochajs/mocha/issues/1811 + [#1769]: https://github.com/mochajs/mocha/issues/1769 + [#1230]: https://github.com/mochajs/mocha/issues/1230 + [#1787]: https://github.com/mochajs/mocha/issues/1787 + [#1789]: https://github.com/mochajs/mocha/issues/1789 + [#1749]: https://github.com/mochajs/mocha/issues/1749 + [#1230]: https://github.com/mochajs/mocha/issues/1230 + [#1260]: https://github.com/mochajs/mocha/issues/1260 + [#1728]: https://github.com/mochajs/mocha/issues/1728 + [#1781]: https://github.com/mochajs/mocha/issues/1781 + [#1754]: https://github.com/mochajs/mocha/issues/1754 + [#1766]: https://github.com/mochajs/mocha/issues/1766 + [#1752]: https://github.com/mochajs/mocha/issues/1752 + [#1761]: https://github.com/mochajs/mocha/issues/1761 + [#1700]: https://github.com/mochajs/mocha/issues/1700 + [#1774]: https://github.com/mochajs/mocha/issues/1774 + [#1687]: https://github.com/mochajs/mocha/issues/1687 + [#1359]: https://github.com/mochajs/mocha/issues/1359 + [#1758]: https://github.com/mochajs/mocha/issues/1758 + [#1741]: https://github.com/mochajs/mocha/issues/1741 + [#1739]: https://github.com/mochajs/mocha/issues/1739 + [#1730]: https://github.com/mochajs/mocha/issues/1730 + [#1349]: https://github.com/mochajs/mocha/issues/1349 + [#1572]: https://github.com/mochajs/mocha/issues/1572 + [#1630]: https://github.com/mochajs/mocha/issues/1630 + [#1718]: https://github.com/mochajs/mocha/issues/1718 + [#1689]: https://github.com/mochajs/mocha/issues/1689 + [#1654]: https://github.com/mochajs/mocha/issues/1654 + [@adamgruber]: https://github.com/adamgruber + [@ajaykodali]: https://github.com/ajaykodali + [@amsul]: https://github.com/amsul + [@aryeguy]: https://github.com/aryeguy + [@benvinegar]: https://github.com/benvinegar + [@boneskull]: https://github.com/boneskull + [@chromakode]: https://github.com/chromakode + [@dominicbarnes]: https://github.com/dominicbarnes + [@duncanbeevers]: https://github.com/duncanbeevers + [@gigadude]: https://github.com/gigadude + [@glenjamin]: https://github.com/glenjamin + [@gsilk]: https://github.com/gsilk + [@jakemmarsh]: https://github.com/jakemmarsh + [@jbnicolai]: https://github.com/jbnicolai + [@jlai]: https://github.com/jlai + [@jonathandelgado]: https://github.com/jonathandelgado + [@jschilli]: https://github.com/jschilli + [@kkirsche]: https://github.com/kkirsche + [@moll]: https://github.com/moll + [@ndhoule]: https://github.com/ndhoule + [@outdooricon]: https://github.com/outdooricon + [@outsideris]: https://github.com/outsideris + [@papandreou]: https://github.com/papandreou + [@rstacruz]: https://github.com/rstacruz + [@ryedog]: https://github.com/ryedog + [@slyg]: https://github.com/slyg + [@sunesimonsen]: https://github.com/sunesimonsen + [@tinganho]: https://github.com/tinganho + +2.2.5 / 2015-05-14 +================== + + * [#1699] - Upgrade jsdiff to v1.4.0 ([@nylen]) + * [#1648] - fix diff background colors in the console ([@nylen]) + * [#1327] - fix tests running twice, a regression issue. ([#1686], [@danielstjules]) + * [#1675] - add integration tests ([@danielstjules]) + * [#1682] - use a valid SPDX license identifier in package.json ([@kemitchell]) + * [#1660] - fix assertion of invalid dates ([#1661], [@a8m]) + * [#1241] - fix issue with multiline diffs appearing as single line ([#1655], [@a8m]) + +[#1699]: https://github.com/mochajs/mocha/issues/1699 +[#1648]: https://github.com/mochajs/mocha/issues/1648 +[#1327]: https://github.com/mochajs/mocha/issues/1327 +[#1686]: https://github.com/mochajs/mocha/issues/1686 +[#1675]: https://github.com/mochajs/mocha/issues/1675 +[#1682]: https://github.com/mochajs/mocha/issues/1682 +[#1660]: https://github.com/mochajs/mocha/issues/1660 +[#1661]: https://github.com/mochajs/mocha/issues/1661 +[#1241]: https://github.com/mochajs/mocha/issues/1241 +[#1655]: https://github.com/mochajs/mocha/issues/1655 +[@nylen]: https://github.com/nylen +[@danielstjules]: https://github.com/danielstjules +[@kemitchell]: https://github.com/kemitchell +[@a8m]: https://github.com/a8m + +2.2.4 / 2015-04-08 +================== + + * Load mocha.opts in _mocha for now (close #1645) + +2.2.3 / 2015-04-07 +================== + + * fix(reporter/base): string diff - issue #1241 + * fix(reporter/base): string diff - issue #1241 + * fix(reporter/base): don't show diffs for errors without expectation + * fix(reporter/base): don't assume error message is first line of stack + * improve: dry up reporter/base test + * fix(reporter/base): explicitly ignore showDiff #1614 + * Add iojs to travis build + * Pass `--allow-natives-syntax` flag to node. + * Support --harmony_classes flag for io.js + * Fix 1556: Update utils.clean to handle newlines in func declarations + * Fix 1606: fix err handling in IE <= 8 and non-ES5 browsers + * Fix 1585: make _mocha executable again + * chore(package.json): add a8m as a contributor + * Fixed broken link on html-cov reporter + * support --es_staging flag + * fix issue where menu overlaps content. + * update contributors in package.json + * Remove trailing whitespace from reporter output + * Remove contributors list from readme + * log third-party reporter errors + * [Fix] Exclude not own properties when looping on options + * fix: support node args in mocha.opts (close #1573) + * fix(reporter/base): string diff - issue #1241 + +2.2.1 / 2015-03-09 +================== + + * Fix passing of args intended for node/iojs. + +2.2.0 / 2015-03-06 +================== + + * Update mocha.js + * Add --fgrep. Use grep for RegExp, fgrep for str + * Ignore async global errors after spec resolution + * Fixing errors that prevent mocha.js from loading in the browser - fixes #1558 + * fix(utils): issue #1558 + make + * add ability to delay root suite; closes #362, closes #1124 + * fix insanity in http tests + * update travis: add node 0.12, add gitter, remove slack + * building + * resolve #1548: ensure the environment's "node" executable is used + * reporters/base: use supports-color to detect colorable term + * travis: use docker containers + * small fix: commander option for --expose-gc + * Ignore asynchronous errors after global failure + * Improve error output when a test fails with a non-error + * updated travis badge, uses svg instead of img + * Allow skip from test context for #332 + * [JSHINT] Unnecessary semicolon fixed in bin/_mocha + * Added a reminder about the done() callback to test timeout error messages + * fixes #1496, in Mocha.run(fn), check if fn exists before executing it, added tests too + * Add Harmony Proxy flag for iojs + * test(utils|ms|*): test existing units + * add support for some iojs flags + * fix(utils.stringify): issue #1229, diff viewer + * Remove slack link + * Prevent multiple 'grep=' querystring params in html reporter + * Use grep as regexp (close #1381) + * utils.stringify should handle objects without an Object prototype + * in runnable test, comparing to undefined error's message rather than a literal + * Fix test running output truncation on async STDIO + * ammended for deprecated customFds option in child_process + +2.1.0 / 2014-12-23 +================== + + * showDiff: don’t stringify strings + * Clean up unused module dependencies. + * Filter zero-length strings from mocha.opts + * only write to stdout in reporters + * Revert "only write to stdout in reporters" + * Print colored output only to a tty + * update summary in README.md + * rename Readme.md/History.md to README.md/HISTORY.md because neurotic + * add .mailmap to fix "git shortlog" or "git summary" output + * fixes #1461: nyan-reporter now respects Base.useColors, fixed bug where Base.color would not return a string when str wasn't a string. + * Use existing test URL builder in failed replay links + * modify .travis.yml: use travis_retry; closes #1449 + * fix -t 0 behavior; closes #1446 + * fix tests (whoops) + * improve diff behavior + * Preserve pathname when linking to individual tests + * Fix test + * Tiny typo in comments fixed + * after hooks now being called on failed tests when using bail, fixes #1093 + * fix throwing undefined/null now makes tests fail, fixes #1395 + * compiler extensions are added as watched extensions, removed non-standard extensions from watch regex, resolves #1221 + * prefix/namespace for suite titles in markdown reporter, fixes #554 + * fix more bad markdown in CONTRIBUTING.md + * fix bad markdown in CONTRIBUTING.md + * add setImmediate/clearImmediate to globals; closes #1435 + * Fix buffer diffs (closes #1132, closes #1433) + * add a CONTRIBUTING.md. closes #882 + * fix intermittent build failures (maybe). closes #1407 + * add Slack notification to .travis.yml + * Fix slack link + * Add slack room to readme + * Update maintainers + * update maintainers and contributors + * resolves #1393: kill children with more effort on SIGINT + * xunit reporter support for optionally writing to a file + * if a reporter has a .done method, call it before exiting + * add support for reporter options + * only write to stdout in reporters + +2.0.0 / 2014-10-21 +================== + + * remove: support for node 0.6.x, 0.4.x + * fix: landing reporter with non ansi characters (#211) + * fix: html reporter - preserve query params when navigating to suites/tests (#1358) + * fix: json stream reporter add error message to failed test + * fix: fixes for visionmedia -> mochajs + * fix: use stdio, fixes node deprecation warnings (#1391) + +1.21.5 / 2014-10-11 +================== + + * fix: build for NodeJS v0.6.x + * fix: do not attempt to highlight syntax when non-HTML reporter is used + * update: escape-string-regexp to 1.0.2. + * fix: botched indentation in canonicalize() + * fix: .gitignore: ignore .patch and .diff files + * fix: changed 'Catched' to 'Caught' in uncaught exception error handler messages + * add: `pending` field for json reporter + * fix: Runner.prototype.uncaught: don't double-end runnables that already have a state. + * fix: --recursive, broken by f0facd2e + * update: replaces escapeRegexp with the escape-string-regexp package. + * update: commander to 2.3.0. + * update: diff to 1.0.8. + * fix: ability to disable syntax highlighting (#1329) + * fix: added empty object to errorJSON() call to catch when no error is present + * fix: never time out after calling enableTimeouts(false) + * fix: timeout(0) will work at suite level (#1300) + * Fix for --watch+only() issue (#888 ) + * fix: respect err.showDiff, add Base reporter test (#810) + +1.22.1-3 / 2014-07-27 +================== + + * fix: disabling timeouts with this.timeout(0) (#1301) + +1.22.1-3 / 2014-07-27 +================== + + * fix: local uis and reporters (#1288) + * fix: building 1.21.0's changes in the browser (#1284) + +1.21.0 / 2014-07-23 +================== + + * add: --no-timeouts option (#1262, #1268) + * add: --*- deprecation node flags (#1217) + * add: --watch-extensions argument (#1247) + * change: spec reporter is default (#1228) + * fix: diff output showing incorrect +/- (#1182) + * fix: diffs of circular structures (#1179) + * fix: re-render the progress bar when progress has changed only (#1151) + * fix support for environments with global and window (#1159) + * fix: reverting to previously defined onerror handler (#1178) + * fix: stringify non error objects passed to done() (#1270) + * fix: using local ui, reporters (#1267) + * fix: cleaning es6 arrows (#1176) + * fix: don't include attrs in failure tag for xunit (#1244) + * fix: fail tests that return a promise if promise is rejected w/o a reason (#1224) + * fix: showing failed tests in doc reporter (#1117) + * fix: dot reporter dots being off (#1204) + * fix: catch empty throws (#1219) + * fix: honoring timeout for sync operations (#1242) + * update: growl to 1.8.0 + +1.20.1 / 2014-06-03 +================== + + * update: should dev dependency to ~4.0.0 (#1231) + +1.20.0 / 2014-05-28 +================== + + * add: filenames to suite objects (#1222) + +1.19.0 / 2014-05-17 +================== + + * add: browser script option to package.json + * add: export file in Mocha.Test objects (#1174) + * add: add docs for wrapped node flags + * fix: mocha.run() to return error status in browser (#1216) + * fix: clean() to show failure details (#1205) + * fix: regex that generates html for new keyword (#1201) + * fix: sibling suites have inherited but separate contexts (#1164) + + +1.18.2 / 2014-03-18 +================== + + * fix: html runner was prevented from using #mocha as the default root el (#1162) + +1.18.1 / 2014-03-18 +================== + + * fix: named before/after hooks in bdd, tdd, qunit interfaces (#1161) + +1.18.0 / 2014-03-13 +================== + + * add: promise support (#329) + * add: named before/after hooks (#966) + +1.17.1 / 2014-01-22 +================== + + * fix: expected messages in should.js (should.js#168) + * fix: expect errno global in node versions < v0.9.11 (#1111) + * fix: unreliable checkGlobals optimization (#1110) + +1.17.0 / 2014-01-09 +================== + + * add: able to require globals (describe, it, etc.) through mocha (#1077) + * fix: abort previous run on --watch change (#1100) + * fix: reset context for each --watch triggered run (#1099) + * fix: error when cli can't resolve path or pattern (#799) + * fix: canonicalize objects before stringifying and diffing them (#1079) + * fix: make CR call behave like carriage return for non tty (#1087) + + +1.16.2 / 2013-12-23 +================== + + * fix: couple issues with ie 8 (#1082, #1081) + * fix: issue running the xunit reporter in browsers (#1068) + * fix: issue with firefox < 3.5 (#725) + + +1.16.1 / 2013-12-19 +================== + + * fix: recompiled for missed changes from the last release + + +1.16.0 / 2013-12-19 +================== + + * add: Runnable.globals(arr) for per test global whitelist (#1046) + * add: mocha.throwError(err) for assertion libs to call (#985) + * remove: --watch's spinner (#806) + * fix: duplicate test output for multi-line specs in spec reporter (#1006) + * fix: gracefully exit on SIGINT (#1063) + * fix expose the specified ui only in the browser (#984) + * fix: ensure process exit code is preserved when using --no-exit (#1059) + * fix: return true from window.onerror handler (#868) + * fix: xunit reporter to use process.stdout.write (#1068) + * fix: utils.clean(str) indentation (#761) + * fix: xunit reporter returning test duration a NaN (#1039) + +1.15.1 / 2013-12-03 +================== + + * fix: recompiled for missed changes from the last release + +1.15.0 / 2013-12-02 +================== + + * add: `--no-exit` to prevent `process.exit()` (#1018) + * fix: using inline diffs (#1044) + * fix: show pending test details in xunit reporter (#1051) + * fix: faster global leak detection (#1024) + * fix: yui compression (#1035) + * fix: wrapping long lines in test results (#1030, #1031) + * fix: handle errors in hooks (#1043) + +1.14.0 / 2013-11-02 +================== + + * add: unified diff (#862) + * add: set MOCHA_COLORS env var to use colors (#965) + * add: able to override tests links in html reporters (#776) + * remove: teamcity reporter (#954) + * update: commander dependency to 2.0.0 (#1010) + * fix: mocha --ui will try to require the ui if not built in, as --reporter does (#1022) + * fix: send cursor commands only if isatty (#184, #1003) + * fix: include assertion message in base reporter (#993, #991) + * fix: consistent return of it, it.only, and describe, describe.only (#840) + +1.13.0 / 2013-09-15 +================== + + * add: sort test files with --sort (#813) + * update: diff depedency to 1.0.7 + * update: glob dependency to 3.2.3 (#927) + * fix: diffs show whitespace differences (#976) + * fix: improve global leaks (#783) + * fix: firefox window.getInterface leak + * fix: accessing iframe via window[iframeIndex] leak + * fix: faster global leak checking + * fix: reporter pending css selector (#970) + +1.12.1 / 2013-08-29 +================== + + * remove test.js from .gitignore + * update included version of ms.js + +1.12.0 / 2013-07-01 +================== + + * add: prevent diffs for differing types. Closes #900 + * add `Mocha.process` hack for phantomjs + * fix: use compilers with requires + * fix regexps in diffs. Closes #890 + * fix xunit NaN on failure. Closes #894 + * fix: strip tab indentation in `clean` utility method + * fix: textmate bundle installation + +1.11.0 / 2013-06-12 +================== + + * add --prof support + * add --harmony support + * add --harmony-generators support + * add "Uncaught " prefix to uncaught exceptions + * add web workers support + * add `suite.skip()` + * change to output # of pending / passing even on failures. Closes #872 + * fix: prevent hooks from being called if we are bailing + * fix `this.timeout(0)` + +1.10.0 / 2013-05-21 +================== + + * add add better globbing support for windows via `glob` module + * add support to pass through flags such as --debug-brk=1234. Closes #852 + * add test.only, test.skip to qunit interface + * change to always use word-based diffs for now. Closes #733 + * change `mocha init` tests.html to index.html + * fix `process` global leak in the browser + * fix: use resolve() instead of join() for --require + * fix: filterLeaks() condition to not consider indices in global object as leaks + * fix: restrict mocha.css styling to #mocha id + * fix: save timer references to avoid Sinon interfering in the browser build. + +1.9.0 / 2013-04-03 +================== + + * add improved setImmediate implementation + * replace --ignore-leaks with --check-leaks + * change default of ignoreLeaks to true. Closes #791 + * remove scrolling for HTML reporter + * fix retina support + * fix tmbundle, restrict to js scope + +1.8.2 / 2013-03-11 +================== + + * add `setImmediate` support for 0.10.x + * fix mocha -w spinner on windows + +1.8.1 / 2013-01-09 +================== + + * fix .bail() arity check causing it to default to true + +1.8.0 / 2013-01-08 +================== + + * add Mocha() options bail support + * add `Mocha#bail()` method + * add instanceof check back for inheriting from Error + * add component.json + * add diff.js to browser build + * update growl + * fix TAP reporter failures comment :D + +1.7.4 / 2012-12-06 +================== + + * add total number of passes and failures to TAP + * remove .bind() calls. re #680 + * fix indexOf. Closes #680 + +1.7.3 / 2012-11-30 +================== + + * fix uncaught error support for the browser + * revert uncaught "fix" which breaks node + +1.7.2 / 2012-11-28 +================== + + * fix uncaught errors to expose the original error message + +1.7.0 / 2012-11-07 +================== + + * add `--async-only` support to prevent false positives for missing `done()` + * add sorting by filename in code coverage + * add HTML 5 doctype to browser template. + * add play button to html reporter to rerun a single test + * add `this.timeout(ms)` as Suite#timeout(ms). Closes #599 + * update growl dependency to 1.6.x + * fix encoding of test-case ?grep. Closes #637 + * fix unicode chars on windows + * fix dom globals in Opera/IE. Closes #243 + * fix markdown reporter a tags + * fix `this.timeout("5s")` support + +1.6.0 / 2012-10-02 +================== + + * add object diffs when `err.showDiff` is present + * add hiding of empty suites when pass/failures are toggled + * add faster `.length` checks to `checkGlobals()` before performing the filter + +1.5.0 / 2012-09-21 +================== + + * add `ms()` to `.slow()` and `.timeout()` + * add `Mocha#checkLeaks()` to re-enable global leak checks + * add `this.slow()` option [aheckmann] + * add tab, CR, LF to error diffs for now + * add faster `.checkGlobals()` solution [guille] + * remove `fn.call()` from reduce util + * remove `fn.call()` from filter util + * fix forEach. Closes #582 + * fix relaying of signals [TooTallNate] + * fix TAP reporter grep number + +1.4.2 / 2012-09-01 +================== + + * add support to multiple `Mocha#globals()` calls, and strings + * add `mocha.reporter()` constructor support [jfirebaugh] + * add `mocha.timeout()` + * move query-string parser to utils.js + * move highlight code to utils.js + * fix third-party reporter support [exogen] + * fix client-side API to match node-side [jfirebaugh] + * fix mocha in iframe [joliss] + +1.4.1 / 2012-08-28 +================== + + * add missing `Markdown` export + * fix `Mocha#grep()`, escape regexp strings + * fix reference error when `devicePixelRatio` is not defined. Closes #549 + +1.4.0 / 2012-08-22 +================== + + * add mkdir -p to `mocha init`. Closes #539 + * add `.only()`. Closes #524 + * add `.skip()`. Closes #524 + * change str.trim() to use utils.trim(). Closes #533 + * fix HTML progress indicator retina display + * fix url-encoding of click-to-grep HTML functionality + +1.3.2 / 2012-08-01 +================== + + * fix exports double-execution regression. Closes #531 + +1.3.1 / 2012-08-01 +================== + + * add passes/failures toggling to HTML reporter + * add pending state to `xit()` and `xdescribe()` [Brian Moore] + * add the @charset "UTF-8"; to fix #522 with FireFox. [Jonathan Creamer] + * add border-bottom to #stats links + * add check for runnable in `Runner#uncaught()`. Closes #494 + * add 0.4 and 0.6 back to travis.yml + * add `-E, --growl-errors` to growl on failures only + * add prefixes to debug() names. Closes #497 + * add `Mocha#invert()` to js api + * change dot reporter to use sexy unicode dots + * fix error when clicking pending test in HTML reporter + * fix `make tm` + +1.3.0 / 2012-07-05 +================== + + * add window scrolling to `HTML` reporter + * add v8 `--trace-*` option support + * add support for custom reports via `--reporter MODULE` + * add `--invert` switch to invert `--grep` matches + * fix export of `Nyan` reporter. Closes #495 + * fix escaping of `HTML` suite titles. Closes #486 + * fix `done()` called multiple times with an error test + * change `--grep` - regexp escape the input + +1.2.2 / 2012-06-28 +================== + + * Added 0.8.0 support + +1.2.1 / 2012-06-25 +================== + + * Added `this.test.error(err)` support to after each hooks. Closes #287 + * Added: export top-level suite on global mocha object (mocha.suite). Closes #448 + * Fixed `js` code block format error in markdown reporter + * Fixed deprecation warning when using `path.existsSync` + * Fixed --globals with wildcard + * Fixed chars in nyan when his head moves back + * Remove `--growl` from test/mocha.opts. Closes #289 + +1.2.0 / 2012-06-17 +================== + + * Added `nyan` reporter [Atsuya Takagi] + * Added `mocha init ` to copy client files + * Added "specify" synonym for "it" [domenic] + * Added global leak wildcard support [nathanbowser] + * Fixed runner emitter leak. closes #432 + * Fixed omission of .js extension. Closes #454 + +1.1.0 / 2012-05-30 +================== + + * Added: check each `mocha(1)` arg for directories to walk + * Added `--recursive` [tricknotes] + * Added `context` for BDD [hokaccha] + * Added styling for new clickable titles + * Added clickable suite titles to HTML reporter + * Added warning when strings are thrown as errors + * Changed: green arrows again in HTML reporter styling + * Changed ul/li elements instead of divs for better copy-and-pasting [joliss] + * Fixed issue #325 - add better grep support to js api + * Fixed: save timer references to avoid Sinon interfering. + +1.0.3 / 2012-04-30 +================== + + * Fixed string diff newlines + * Fixed: removed mocha.css target. Closes #401 + +1.0.2 / 2012-04-25 +================== + + * Added HTML reporter duration. Closes #47 + * Fixed: one postMessage event listener [exogen] + * Fixed: allow --globals to be used multiple times. Closes #100 [brendannee] + * Fixed #158: removes jquery include from browser tests + * Fixed grep. Closes #372 [brendannee] + * Fixed #166 - When grepping don't display the empty suites + * Removed test/browser/style.css. Closes #385 + +1.0.1 / 2012-04-04 +================== + + * Fixed `.timeout()` in hooks + * Fixed: allow callback for `mocha.run()` in client version + * Fixed browser hook error display. Closes #361 + +1.0.0 / 2012-03-24 +================== + + * Added js API. Closes #265 + * Added: initial run of tests with `--watch`. Closes #345 + * Added: mark `location` as a global on the CS. Closes #311 + * Added `markdown` reporter (github flavour) + * Added: scrolling menu to coverage.html. Closes #335 + * Added source line to html report for Safari [Tyson Tate] + * Added "min" reporter, useful for `--watch` [Jakub Nešetřil] + * Added support for arbitrary compilers via . Closes #338 [Ian Young] + * Added Teamcity export to lib/reporters/index [Michael Riley] + * Fixed chopping of first char in error reporting. Closes #334 [reported by topfunky] + * Fixed terrible FF / Opera stack traces + +0.14.1 / 2012-03-06 +================== + + * Added lib-cov to _.npmignore_ + * Added reporter to `mocha.run([reporter])` as argument + * Added some margin-top to the HTML reporter + * Removed jQuery dependency + * Fixed `--watch`: purge require cache. Closes #266 + +0.14.0 / 2012-03-01 +================== + + * Added string diff support for terminal reporters + +0.13.0 / 2012-02-23 +================== + + * Added preliminary test coverage support. Closes #5 + * Added `HTMLCov` reporter + * Added `JSONCov` reporter [kunklejr] + * Added `xdescribe()` and `xit()` to the BDD interface. Closes #263 (docs * Changed: make json reporter output pretty json + * Fixed node-inspector support, swapped `--debug` for `debug` to match node. +needed) +Closes #247 + +0.12.1 / 2012-02-14 +================== + + * Added `npm docs mocha` support [TooTallNate] + * Added a `Context` object used for hook and test-case this. Closes #253 + * Fixed `Suite#clone()` `.ctx` reference. Closes #262 + +0.12.0 / 2012-02-02 +================== + + * Added .coffee `--watch` support. Closes #242 + * Added support to `--require` files relative to the CWD. Closes #241 + * Added quick n dirty syntax highlighting. Closes #248 + * Changed: made HTML progress indicator smaller + * Fixed xunit errors attribute [dhendo] + +0.10.2 / 2012-01-21 +================== + + * Fixed suite count in reporter stats. Closes #222 + * Fixed `done()` after timeout error reporting [Phil Sung] + * Changed the 0-based errors to 1 + +0.10.1 / 2012-01-17 +================== + + * Added support for node 0.7.x + * Fixed absolute path support. Closes #215 [kompiro] + * Fixed `--no-colors` option [Jussi Virtanen] + * Fixed Arial CSS typo in the correct file + +0.10.0 / 2012-01-13 +================== + + * Added `-b, --bail` to exit on first exception [guillermo] + * Added support for `-gc` / `--expose-gc` [TooTallNate] + * Added `qunit`-inspired interface + * Added MIT LICENSE. Closes #194 + * Added: `--watch` all .js in the CWD. Closes #139 + * Fixed `self.test` reference in runner. Closes #189 + * Fixed double reporting of uncaught exceptions after timeout. Closes #195 + +0.8.2 / 2012-01-05 +================== + + * Added test-case context support. Closes #113 + * Fixed exit status. Closes #187 + * Update commander. Closes #190 + +0.8.1 / 2011-12-30 +================== + + * Fixed reporting of uncaught exceptions. Closes #183 + * Fixed error message defaulting [indutny] + * Changed mocha(1) from bash to node for windows [Nathan Rajlich] + +0.8.0 / 2011-12-28 +================== + + * Added `XUnit` reporter [FeeFighters/visionmedia] + * Added `say(1)` notification support [Maciej Małecki] + * Changed: fail when done() is invoked with a non-Error. Closes #171 + * Fixed `err.stack`, defaulting to message. Closes #180 + * Fixed: `make tm` mkdir -p the dest. Closes #137 + * Fixed mocha(1) --help bin name + * Fixed `-d` for `--debug` support + +0.7.1 / 2011-12-22 +================== + + * Removed `mocha-debug(1)`, use `mocha --debug` + * Fixed CWD relative requires + * Fixed growl issue on windows [Raynos] + * Fixed: platform specific line endings [TooTallNate] + * Fixed: escape strings in HTML reporter. Closes #164 + +0.7.0 / 2011-12-18 +================== + + * Added support for IE{7,8} [guille] + * Changed: better browser nextTick implementation [guille] + +0.6.0 / 2011-12-18 +================== + + * Added setZeroTimeout timeout for browser (nicer stack traces). Closes #153 + * Added "view source" on hover for HTML reporter to make it obvious + * Changed: replace custom growl with growl lib + * Fixed duplicate reporting for HTML reporter. Closes #154 + * Fixed silent hook errors in the HTML reporter. Closes #150 + +0.5.0 / 2011-12-14 +================== + + * Added: push node_modules directory onto module.paths for relative require Closes #93 + * Added teamcity reporter [blindsey] + * Fixed: recover from uncaught exceptions for tests. Closes #94 + * Fixed: only emit "test end" for uncaught within test, not hook + +0.4.0 / 2011-12-14 +================== + + * Added support for test-specific timeouts via `this.timeout(0)`. Closes #134 + * Added guillermo's client-side EventEmitter. Closes #132 + * Added progress indicator to the HTML reporter + * Fixed slow browser tests. Closes #135 + * Fixed "suite" color for light terminals + * Fixed `require()` leak spotted by [guillermo] + +0.3.6 / 2011-12-09 +================== + + * Removed suite merging (for now) + +0.3.5 / 2011-12-08 +================== + + * Added support for `window.onerror` [guillermo] + * Fixed: clear timeout on uncaught exceptions. Closes #131 [guillermo] + * Added `mocha.css` to PHONY list. + * Added `mocha.js` to PHONY list. + +0.3.4 / 2011-12-08 +================== + + * Added: allow `done()` to be called with non-Error + * Added: return Runner from `mocha.run()`. Closes #126 + * Fixed: run afterEach even on failures. Closes #125 + * Fixed clobbering of current runnable. Closes #121 + +0.3.3 / 2011-12-08 +================== + + * Fixed hook timeouts. Closes #120 + * Fixed uncaught exceptions in hooks + +0.3.2 / 2011-12-05 +================== + + * Fixed weird reporting when `err.message` is not present + +0.3.1 / 2011-12-04 +================== + + * Fixed hook event emitter leak. Closes #117 + * Fixed: export `Spec` constructor. Closes #116 + +0.3.0 / 2011-12-04 +================== + + * Added `-w, --watch`. Closes #72 + * Added `--ignore-leaks` to ignore global leak checking + * Added browser `?grep=pattern` support + * Added `--globals ` to specify accepted globals. Closes #99 + * Fixed `mocha-debug(1)` on some systems. Closes #232 + * Fixed growl total, use `runner.total` + +0.2.0 / 2011-11-30 +================== + + * Added `--globals ` to specify accepted globals. Closes #99 + * Fixed funky highlighting of messages. Closes #97 + * Fixed `mocha-debug(1)`. Closes #232 + * Fixed growl total, use runner.total + +0.1.0 / 2011-11-29 +================== + + * Added `suiteSetup` and `suiteTeardown` to TDD interface [David Henderson] + * Added growl icons. Closes #84 + * Fixed coffee-script support + +0.0.8 / 2011-11-25 +================== + + * Fixed: use `Runner#total` for accurate reporting + +0.0.7 / 2011-11-25 +================== + + * Added `Hook` + * Added `Runnable` + * Changed: `Test` is `Runnable` + * Fixed global leak reporting in hooks + * Fixed: > 2 calls to done() only report the error once + * Fixed: clear timer on failure. Closes #80 + +0.0.6 / 2011-11-25 +================== + + * Fixed return on immediate async error. Closes #80 + +0.0.5 / 2011-11-24 +================== + + * Fixed: make mocha.opts whitespace less picky [kkaefer] + +0.0.4 / 2011-11-24 +================== + + * Added `--interfaces` + * Added `--reporters` + * Added `-c, --colors`. Closes #69 + * Fixed hook timeouts + +0.0.3 / 2011-11-23 +================== + + * Added `-C, --no-colors` to explicitly disable + * Added coffee-script support + +0.0.2 / 2011-11-22 +================== + + * Fixed global leak detection due to Safari bind() change + * Fixed: escape html entities in Doc reporter + * Fixed: escape html entities in HTML reporter + * Fixed pending test support for HTML reporter. Closes #66 + +0.0.1 / 2011-11-22 +================== + + * Added `--timeout` second shorthand support, ex `--timeout 3s`. + * Fixed "test end" event for uncaughtExceptions. Closes #61 + +0.0.1-alpha6 / 2011-11-19 +================== + + * Added travis CI support (needs enabling when public) + * Added preliminary browser support + * Added `make mocha.css` target. Closes #45 + * Added stack trace to TAP errors. Closes #52 + * Renamed tearDown to teardown. Closes #49 + * Fixed: cascading hooksc. Closes #30 + * Fixed some colors for non-tty + * Fixed errors thrown in sync test-cases due to nextTick + * Fixed Base.window.width... again give precedence to 0.6.x + +0.0.1-alpha5 / 2011-11-17 +================== + + * Added `doc` reporter. Closes #33 + * Added suite merging. Closes #28 + * Added TextMate bundle and `make tm`. Closes #20 + +0.0.1-alpha4 / 2011-11-15 +================== + + * Fixed getWindowSize() for 0.4.x + +0.0.1-alpha3 / 2011-11-15 +================== + + * Added `-s, --slow ` to specify "slow" test threshold + * Added `mocha-debug(1)` + * Added `mocha.opts` support. Closes #31 + * Added: default [files] to _test/*.js_ + * Added protection against multiple calls to `done()`. Closes #35 + * Changed: bright yellow for slow Dot reporter tests + +0.0.1-alpha1 / 2011-11-08 +================== + + * Missed this one :) + +0.0.1-alpha1 / 2011-11-08 +================== + + * Initial release diff --git a/node_modules/mocha/LICENSE b/node_modules/mocha/LICENSE new file mode 100644 index 0000000..ca47f26 --- /dev/null +++ b/node_modules/mocha/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2011-2015 TJ Holowaychuk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/mocha/README.md b/node_modules/mocha/README.md new file mode 100644 index 0000000..44692d3 --- /dev/null +++ b/node_modules/mocha/README.md @@ -0,0 +1,11 @@ +[![Build Status](https://api.travis-ci.org/mochajs/mocha.svg?branch=master)](http://travis-ci.org/mochajs/mocha) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mochajs/mocha?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + + [![Mocha test framework](http://f.cl.ly/items/3l1k0n2A1U3M1I1L210p/Screen%20Shot%202012-02-24%20at%202.21.43%20PM.png)](http://mochajs.org) + + Mocha is a simple, flexible, fun JavaScript test framework for node.js and the browser. For more information view the [documentation](http://mochajs.org). + +## Links + + - [Google Group](http://groups.google.com/group/mochajs) + - [Wiki](https://github.com/mochajs/mocha/wiki) + - Mocha [Extensions and reporters](https://github.com/mochajs/mocha/wiki) diff --git a/node_modules/mocha/bin/.eslintrc b/node_modules/mocha/bin/.eslintrc new file mode 100644 index 0000000..db74246 --- /dev/null +++ b/node_modules/mocha/bin/.eslintrc @@ -0,0 +1,3 @@ +--- +rules: + no-process-exit: 0 diff --git a/node_modules/mocha/bin/_mocha b/node_modules/mocha/bin/_mocha new file mode 100755 index 0000000..f806883 --- /dev/null +++ b/node_modules/mocha/bin/_mocha @@ -0,0 +1,489 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander'), + path = require('path'), + fs = require('fs'), + resolve = path.resolve, + exists = fs.existsSync || path.existsSync, + Mocha = require('../'), + utils = Mocha.utils, + join = path.join, + cwd = process.cwd(), + getOptions = require('./options'), + mocha = new Mocha; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Files. + */ + +var files = []; + +/** + * Globals. + */ + +var globals = []; + +/** + * Requires. + */ + +var requires = []; + +/** + * Images. + */ + +var images = { + fail: __dirname + '/../images/error.png' + , pass: __dirname + '/../images/ok.png' +}; + +// options + +program + .version(JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version) + .usage('[debug] [options] [files]') + .option('-A, --async-only', "force all tests to take a callback (async) or return a promise") + .option('-c, --colors', 'force enabling of colors') + .option('-C, --no-colors', 'force disabling of colors') + .option('-G, --growl', 'enable growl notification support') + .option('-O, --reporter-options ', 'reporter-specific options') + .option('-R, --reporter ', 'specify the reporter to use', 'spec') + .option('-S, --sort', "sort test files") + .option('-b, --bail', "bail after first test failure") + .option('-d, --debug', "enable node's debugger, synonym for node --debug") + .option('-g, --grep ', 'only run tests matching ') + .option('-f, --fgrep ', 'only run tests containing ') + .option('-gc, --expose-gc', 'expose gc extension') + .option('-i, --invert', 'inverts --grep and --fgrep matches') + .option('-r, --require ', 'require the given module') + .option('-s, --slow ', '"slow" test threshold in milliseconds [75]') + .option('-t, --timeout ', 'set test-case timeout in milliseconds [2000]') + .option('-u, --ui ', 'specify user-interface (bdd|tdd|exports)', 'bdd') + .option('-w, --watch', 'watch files for changes') + .option('--check-leaks', 'check for global variable leaks') + .option('--full-trace', 'display the full stack trace') + .option('--compilers :,...', 'use the given module(s) to compile files', list, []) + .option('--debug-brk', "enable node's debugger breaking on the first line") + .option('--globals ', 'allow the given comma-delimited global [names]', list, []) + .option('--es_staging', 'enable all staged features') + .option('--harmony<_classes,_generators,...>', 'all node --harmony* flags are available') + .option('--inline-diffs', 'display actual/expected differences inline within each string') + .option('--interfaces', 'display available interfaces') + .option('--no-deprecation', 'silence deprecation warnings') + .option('--no-exit', 'require a clean shutdown of the event loop: mocha will not call process.exit') + .option('--no-timeouts', 'disables timeouts, given implicitly with --debug') + .option('--opts ', 'specify opts path', 'test/mocha.opts') + .option('--prof', 'log statistical profiling information') + .option('--recursive', 'include sub directories') + .option('--reporters', 'display available reporters') + .option('--throw-deprecation', 'throw an exception anytime a deprecated function is used') + .option('--trace', 'trace function calls') + .option('--trace-deprecation', 'show stack traces on deprecations') + .option('--watch-extensions ,...', 'additional extensions to monitor with --watch', list, []) + .option('--delay', 'wait for async suite definition') + +program.name = 'mocha'; + +// init command + +program + .command('init ') + .description('initialize a client-side mocha setup at ') + .action(function(path){ + var mkdir = require('mkdirp'); + mkdir.sync(path); + var css = fs.readFileSync(join(__dirname, '..', 'mocha.css')); + var js = fs.readFileSync(join(__dirname, '..', 'mocha.js')); + var tmpl = fs.readFileSync(join(__dirname, '..', 'lib/template.html')); + fs.writeFileSync(join(path, 'mocha.css'), css); + fs.writeFileSync(join(path, 'mocha.js'), js); + fs.writeFileSync(join(path, 'tests.js'), ''); + fs.writeFileSync(join(path, 'index.html'), tmpl); + process.exit(0); + }); + +// --globals + +program.on('globals', function(val){ + globals = globals.concat(list(val)); +}); + +// --reporters + +program.on('reporters', function(){ + console.log(); + console.log(' dot - dot matrix'); + console.log(' doc - html documentation'); + console.log(' spec - hierarchical spec list'); + console.log(' json - single json object'); + console.log(' progress - progress bar'); + console.log(' list - spec-style listing'); + console.log(' tap - test-anything-protocol'); + console.log(' landing - unicode landing strip'); + console.log(' xunit - xunit reporter'); + console.log(' html-cov - HTML test coverage'); + console.log(' json-cov - JSON test coverage'); + console.log(' min - minimal reporter (great with --watch)'); + console.log(' json-stream - newline delimited json events'); + console.log(' markdown - markdown documentation (github flavour)'); + console.log(' nyan - nyan cat!'); + console.log(); + process.exit(); +}); + +// --interfaces + +program.on('interfaces', function(){ + console.log(''); + console.log(' bdd'); + console.log(' tdd'); + console.log(' qunit'); + console.log(' exports'); + console.log(''); + process.exit(); +}); + +// -r, --require + +module.paths.push(cwd, join(cwd, 'node_modules')); + +program.on('require', function(mod){ + var abs = exists(mod) || exists(mod + '.js'); + if (abs) mod = resolve(mod); + requires.push(mod); +}); + +// load mocha.opts into process.argv + +getOptions(); + +// parse args + +program.parse(process.argv); + +// infinite stack traces + +Error.stackTraceLimit = Infinity; // TODO: config + +// reporter options + +var reporterOptions = {}; +if (program.reporterOptions !== undefined) { + program.reporterOptions.split(",").forEach(function(opt) { + var L = opt.split("="); + if (L.length > 2 || L.length === 0) { + throw new Error("invalid reporter option '" + opt + "'"); + } else if (L.length === 2) { + reporterOptions[L[0]] = L[1]; + } else { + reporterOptions[L[0]] = true; + } + }); +} + +// reporter + +mocha.reporter(program.reporter, reporterOptions); + +// interface + +mocha.ui(program.ui); + +// load reporter + +try { + Reporter = require('../lib/reporters/' + program.reporter); +} catch (err) { + try { + Reporter = require(program.reporter); + } catch (err) { + throw new Error('reporter "' + program.reporter + '" does not exist'); + } +} + +// --no-colors + +if (!program.colors) mocha.useColors(false); + +// --colors + +if (~process.argv.indexOf('--colors') || + ~process.argv.indexOf('-c')) { + mocha.useColors(true); +} + +// --inline-diffs + +if (program.inlineDiffs) mocha.useInlineDiffs(true); + +// --slow + +if (program.slow) mocha.suite.slow(program.slow); + +// --no-timeouts + +if (!program.timeouts) mocha.enableTimeouts(false); + +// --timeout + +if (program.timeout) mocha.suite.timeout(program.timeout); + +// --bail + +mocha.suite.bail(program.bail); + +// --grep + +if (program.grep) mocha.grep(new RegExp(program.grep)); + +// --fgrep + +if (program.fgrep) mocha.grep(program.fgrep); + +// --invert + +if (program.invert) mocha.invert(); + +// --check-leaks + +if (program.checkLeaks) mocha.checkLeaks(); + +// --stack-trace + +if(program.fullTrace) mocha.fullTrace(); + +// --growl + +if (program.growl) mocha.growl(); + +// --async-only + +if (program.asyncOnly) mocha.asyncOnly(); + +// --delay + +if (program.delay) mocha.delay(); + +// --globals + +mocha.globals(globals); + +// custom compiler support + +var extensions = ['js']; +program.compilers.forEach(function(c) { + var compiler = c.split(':') + , ext = compiler[0] + , mod = compiler[1]; + + if (mod[0] == '.') mod = join(process.cwd(), mod); + require(mod); + extensions.push(ext); + program.watchExtensions.push(ext); +}); + +// requires + +requires.forEach(function(mod) { + require(mod); +}); + +//args + +var args = program.args; + +// default files to test/*.{js,coffee} + +if (!args.length) args.push('test'); + +args.forEach(function(arg){ + files = files.concat(utils.lookupFiles(arg, extensions, program.recursive)); +}); + +// resolve + +files = files.map(function(path){ + return resolve(path); +}); + +if (program.sort) { + files.sort(); +} + +// --watch + +var runner; +if (program.watch) { + console.log(); + hideCursor(); + process.on('SIGINT', function(){ + showCursor(); + console.log('\n'); + process.exit(); + }); + + + var watchFiles = utils.files(cwd, [ 'js' ].concat(program.watchExtensions)); + var runAgain = false; + + function loadAndRun() { + try { + mocha.files = files; + runAgain = false; + runner = mocha.run(function(){ + runner = null; + if (runAgain) { + rerun(); + } + }); + } catch(e) { + console.log(e.stack); + } + } + + function purge() { + watchFiles.forEach(function(file){ + delete require.cache[file]; + }); + } + + loadAndRun(); + + function rerun() { + purge(); + stop() + if (!program.grep) + mocha.grep(null); + mocha.suite = mocha.suite.clone(); + mocha.suite.ctx = new Mocha.Context; + mocha.ui(program.ui); + loadAndRun(); + } + + utils.watch(watchFiles, function(){ + runAgain = true; + if (runner) { + runner.abort(); + } else { + rerun(); + } + }); + +} else { + +// load + + mocha.files = files; + runner = mocha.run(program.exit ? exit : exitLater); + +} + +function exitLater(code) { + process.on('exit', function() { process.exit(code) }) +} + +function exit(code) { + // flush output for Node.js Windows pipe bug + // https://github.com/joyent/node/issues/6247 is just one bug example + // https://github.com/visionmedia/mocha/issues/333 has a good discussion + function done() { + if (!(draining--)) process.exit(code); + } + + var draining = 0; + var streams = [process.stdout, process.stderr]; + + streams.forEach(function(stream){ + // submit empty write request and wait for completion + draining += 1; + stream.write('', done); + }); + + done(); +} + +process.on('SIGINT', function() { runner.abort(); }) + +// enable growl notifications + +function growl(runner, reporter) { + var notify = require('growl'); + + runner.on('end', function(){ + var stats = reporter.stats; + if (stats.failures) { + var msg = stats.failures + ' of ' + runner.total + ' tests failed'; + notify(msg, { name: 'mocha', title: 'Failed', image: images.fail }); + } else { + notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { + name: 'mocha' + , title: 'Passed' + , image: images.pass + }); + } + }); +} + +/** + * Parse list. + */ + +function list(str) { + return str.split(/ *, */); +} + +/** + * Hide the cursor. + */ + +function hideCursor(){ + process.stdout.write('\u001b[?25l'); +} + +/** + * Show the cursor. + */ + +function showCursor(){ + process.stdout.write('\u001b[?25h'); +} + +/** + * Stop play()ing. + */ + +function stop() { + process.stdout.write('\u001b[2K'); + clearInterval(play.timer); +} + +/** + * Play the given array of strings. + */ + +function play(arr, interval) { + var len = arr.length + , interval = interval || 100 + , i = 0; + + play.timer = setInterval(function(){ + var str = arr[i++ % len]; + process.stdout.write('\u001b[0G' + str); + }, interval); +} diff --git a/node_modules/mocha/bin/mocha b/node_modules/mocha/bin/mocha new file mode 100755 index 0000000..031ef56 --- /dev/null +++ b/node_modules/mocha/bin/mocha @@ -0,0 +1,64 @@ +#!/usr/bin/env node + +/** + * This tiny wrapper file checks for known node flags and appends them + * when found, before invoking the "real" _mocha(1) executable. + */ + +var spawn = require('child_process').spawn, + path = require('path'), + fs = require('fs'), + args = [path.join(__dirname, '_mocha')]; + +process.argv.slice(2).forEach(function(arg){ + var flag = arg.split('=')[0]; + + switch (flag) { + case '-d': + args.unshift('--debug'); + args.push('--no-timeouts'); + break; + case 'debug': + case '--debug': + case '--debug-brk': + args.unshift(arg); + args.push('--no-timeouts'); + break; + case '-gc': + case '--expose-gc': + args.unshift('--expose-gc'); + break; + case '--gc-global': + case '--es_staging': + case '--no-deprecation': + case '--prof': + case '--throw-deprecation': + case '--trace-deprecation': + case '--allow-natives-syntax': + args.unshift(arg); + break; + default: + if (0 == arg.indexOf('--harmony')) args.unshift(arg); + else if (0 == arg.indexOf('--trace')) args.unshift(arg); + else if (0 == arg.indexOf('--max-old-space-size')) args.unshift(arg); + else args.push(arg); + break; + } +}); + +var proc = spawn(process.execPath, args, { stdio: 'inherit' }); +proc.on('exit', function (code, signal) { + process.on('exit', function(){ + if (signal) { + process.kill(process.pid, signal); + } else { + process.exit(code); + } + }); +}); + +// terminate children. +process.on('SIGINT', function () { + proc.kill('SIGINT'); // calls runner.abort() + proc.kill('SIGTERM'); // if that didn't work, we're probably in an infinite loop, so make it die. +}); diff --git a/node_modules/mocha/bin/options.js b/node_modules/mocha/bin/options.js new file mode 100644 index 0000000..6e12d51 --- /dev/null +++ b/node_modules/mocha/bin/options.js @@ -0,0 +1,37 @@ +/** + * Dependencies. + */ + +var fs = require('fs'); + +/** + * Export `getOptions`. + */ + +module.exports = getOptions; + +/** + * Get options. + */ + +function getOptions() { + var optsPath = process.argv.indexOf('--opts') !== -1 + ? process.argv[process.argv.indexOf('--opts') + 1] + : 'test/mocha.opts'; + + try { + var opts = fs.readFileSync(optsPath, 'utf8') + .replace(/\\\s/g, '%20') + .split(/\s/) + .filter(Boolean) + .map(function(value) { + return value.replace(/%20/g, ' '); + }); + + process.argv = process.argv + .slice(0, 2) + .concat(opts.concat(process.argv.slice(2))); + } catch (err) { + // ignore + } +} diff --git a/node_modules/mocha/images/error.png b/node_modules/mocha/images/error.png new file mode 100644 index 0000000..a07a1ba Binary files /dev/null and b/node_modules/mocha/images/error.png differ diff --git a/node_modules/mocha/images/ok.png b/node_modules/mocha/images/ok.png new file mode 100644 index 0000000..b3623a5 Binary files /dev/null and b/node_modules/mocha/images/ok.png differ diff --git a/node_modules/mocha/index.js b/node_modules/mocha/index.js new file mode 100644 index 0000000..169b271 --- /dev/null +++ b/node_modules/mocha/index.js @@ -0,0 +1,3 @@ +module.exports = process.env.COV + ? require('./lib-cov/mocha') + : require('./lib/mocha'); diff --git a/node_modules/mocha/lib/browser/debug.js b/node_modules/mocha/lib/browser/debug.js new file mode 100644 index 0000000..ba23289 --- /dev/null +++ b/node_modules/mocha/lib/browser/debug.js @@ -0,0 +1,4 @@ +/* eslint-disable no-unused-vars */ +module.exports = function(type) { + return function() {}; +}; diff --git a/node_modules/mocha/lib/browser/diff.js b/node_modules/mocha/lib/browser/diff.js new file mode 100644 index 0000000..c084609 --- /dev/null +++ b/node_modules/mocha/lib/browser/diff.js @@ -0,0 +1,369 @@ +/* See LICENSE file for terms of use */ + +/* + * Text diff implementation. + * + * This library supports the following APIS: + * JsDiff.diffChars: Character by character diff + * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace + * JsDiff.diffLines: Line based diff + * + * JsDiff.diffCss: Diff targeted at CSS content + * + * These methods are based on the implementation proposed in + * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 + */ +var JsDiff = (function() { + /*jshint maxparams: 5*/ + function clonePath(path) { + return { newPos: path.newPos, components: path.components.slice(0) }; + } + function removeEmpty(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + if (array[i]) { + ret.push(array[i]); + } + } + return ret; + } + function escapeHTML(s) { + var n = s; + n = n.replace(/&/g, '&'); + n = n.replace(//g, '>'); + n = n.replace(/"/g, '"'); + + return n; + } + + var Diff = function(ignoreWhitespace) { + this.ignoreWhitespace = ignoreWhitespace; + }; + Diff.prototype = { + diff: function(oldString, newString) { + // Handle the identity case (this is due to unrolling editLength == 0 + if (newString === oldString) { + return [{ value: newString }]; + } + if (!newString) { + return [{ value: oldString, removed: true }]; + } + if (!oldString) { + return [{ value: newString, added: true }]; + } + + newString = this.tokenize(newString); + oldString = this.tokenize(oldString); + + var newLen = newString.length, oldLen = oldString.length; + var maxEditLength = newLen + oldLen; + var bestPath = [{ newPos: -1, components: [] }]; + + // Seed editLength = 0 + var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); + if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) { + return bestPath[0].components; + } + + for (var editLength = 1; editLength <= maxEditLength; editLength++) { + for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) { + var basePath; + var addPath = bestPath[diagonalPath-1], + removePath = bestPath[diagonalPath+1]; + oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; + if (addPath) { + // No one else is going to attempt to use this value, clear it + bestPath[diagonalPath-1] = undefined; + } + + var canAdd = addPath && addPath.newPos+1 < newLen; + var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; + if (!canAdd && !canRemove) { + bestPath[diagonalPath] = undefined; + continue; + } + + // Select the diagonal that we want to branch from. We select the prior + // path whose position in the new string is the farthest from the origin + // and does not pass the bounds of the diff graph + if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { + basePath = clonePath(removePath); + this.pushComponent(basePath.components, oldString[oldPos], undefined, true); + } else { + basePath = clonePath(addPath); + basePath.newPos++; + this.pushComponent(basePath.components, newString[basePath.newPos], true, undefined); + } + + var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath); + + if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) { + return basePath.components; + } else { + bestPath[diagonalPath] = basePath; + } + } + } + }, + + pushComponent: function(components, value, added, removed) { + var last = components[components.length-1]; + if (last && last.added === added && last.removed === removed) { + // We need to clone here as the component clone operation is just + // as shallow array clone + components[components.length-1] = + {value: this.join(last.value, value), added: added, removed: removed }; + } else { + components.push({value: value, added: added, removed: removed }); + } + }, + extractCommon: function(basePath, newString, oldString, diagonalPath) { + var newLen = newString.length, + oldLen = oldString.length, + newPos = basePath.newPos, + oldPos = newPos - diagonalPath; + while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) { + newPos++; + oldPos++; + + this.pushComponent(basePath.components, newString[newPos], undefined, undefined); + } + basePath.newPos = newPos; + return oldPos; + }, + + equals: function(left, right) { + var reWhitespace = /\S/; + if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) { + return true; + } else { + return left === right; + } + }, + join: function(left, right) { + return left + right; + }, + tokenize: function(value) { + return value; + } + }; + + var CharDiff = new Diff(); + + var WordDiff = new Diff(true); + var WordWithSpaceDiff = new Diff(); + WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { + return removeEmpty(value.split(/(\s+|\b)/)); + }; + + var CssDiff = new Diff(true); + CssDiff.tokenize = function(value) { + return removeEmpty(value.split(/([{}:;,]|\s+)/)); + }; + + var LineDiff = new Diff(); + LineDiff.tokenize = function(value) { + var retLines = [], + lines = value.split(/^/m); + + for(var i = 0; i < lines.length; i++) { + var line = lines[i], + lastLine = lines[i - 1]; + + // Merge lines that may contain windows new lines + if (line == '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') { + retLines[retLines.length - 1] += '\n'; + } else if (line) { + retLines.push(line); + } + } + + return retLines; + }; + + return { + Diff: Diff, + + diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); }, + diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); }, + diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); }, + diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); }, + + diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); }, + + createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { + var ret = []; + + ret.push('Index: ' + fileName); + ret.push('==================================================================='); + ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); + ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); + + var diff = LineDiff.diff(oldStr, newStr); + if (!diff[diff.length-1].value) { + diff.pop(); // Remove trailing newline add + } + diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier + + function contextLines(lines) { + return lines.map(function(entry) { return ' ' + entry; }); + } + function eofNL(curRange, i, current) { + var last = diff[diff.length-2], + isLast = i === diff.length-2, + isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed); + + // Figure out if this is the last line for the given file and missing NL + if (!/\n$/.test(current.value) && (isLast || isLastOfType)) { + curRange.push('\\ No newline at end of file'); + } + } + + var oldRangeStart = 0, newRangeStart = 0, curRange = [], + oldLine = 1, newLine = 1; + for (var i = 0; i < diff.length; i++) { + var current = diff[i], + lines = current.lines || current.value.replace(/\n$/, '').split('\n'); + current.lines = lines; + + if (current.added || current.removed) { + if (!oldRangeStart) { + var prev = diff[i-1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + + if (prev) { + curRange = contextLines(prev.lines.slice(-4)); + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; + } + } + curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; })); + eofNL(curRange, i, current); + + if (current.added) { + newLine += lines.length; + } else { + oldLine += lines.length; + } + } else { + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= 8 && i < diff.length-2) { + // Overlapping + curRange.push.apply(curRange, contextLines(lines)); + } else { + // end the range and output + var contextSize = Math.min(lines.length, 4); + ret.push( + '@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize) + + ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize) + + ' @@'); + ret.push.apply(ret, curRange); + ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); + if (lines.length <= 4) { + eofNL(ret, i, current); + } + + oldRangeStart = 0; newRangeStart = 0; curRange = []; + } + } + oldLine += lines.length; + newLine += lines.length; + } + } + + return ret.join('\n') + '\n'; + }, + + applyPatch: function(oldStr, uniDiff) { + var diffstr = uniDiff.split('\n'); + var diff = []; + var remEOFNL = false, + addEOFNL = false; + + for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) { + if(diffstr[i][0] === '@') { + var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); + diff.unshift({ + start:meh[3], + oldlength:meh[2], + oldlines:[], + newlength:meh[4], + newlines:[] + }); + } else if(diffstr[i][0] === '+') { + diff[0].newlines.push(diffstr[i].substr(1)); + } else if(diffstr[i][0] === '-') { + diff[0].oldlines.push(diffstr[i].substr(1)); + } else if(diffstr[i][0] === ' ') { + diff[0].newlines.push(diffstr[i].substr(1)); + diff[0].oldlines.push(diffstr[i].substr(1)); + } else if(diffstr[i][0] === '\\') { + if (diffstr[i-1][0] === '+') { + remEOFNL = true; + } else if(diffstr[i-1][0] === '-') { + addEOFNL = true; + } + } + } + + var str = oldStr.split('\n'); + for (var i = diff.length - 1; i >= 0; i--) { + var d = diff[i]; + for (var j = 0; j < d.oldlength; j++) { + if(str[d.start-1+j] !== d.oldlines[j]) { + return false; + } + } + Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines)); + } + + if (remEOFNL) { + while (!str[str.length-1]) { + str.pop(); + } + } else if (addEOFNL) { + str.push(''); + } + return str.join('\n'); + }, + + convertChangesToXML: function(changes){ + var ret = []; + for ( var i = 0; i < changes.length; i++) { + var change = changes[i]; + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + + ret.push(escapeHTML(change.value)); + + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + } + return ret.join(''); + }, + + // See: http://code.google.com/p/google-diff-match-patch/wiki/API + convertChangesToDMP: function(changes){ + var ret = [], change; + for ( var i = 0; i < changes.length; i++) { + change = changes[i]; + ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]); + } + return ret; + } + }; +})(); + +if (typeof module !== 'undefined') { + module.exports = JsDiff; +} diff --git a/node_modules/mocha/lib/browser/events.js b/node_modules/mocha/lib/browser/events.js new file mode 100644 index 0000000..3140169 --- /dev/null +++ b/node_modules/mocha/lib/browser/events.js @@ -0,0 +1,193 @@ +/** + * Module exports. + */ + +exports.EventEmitter = EventEmitter; + +/** + * Object#hasOwnProperty reference. + */ +var objToString = Object.prototype.toString; + +/** + * Check if a value is an array. + * + * @api private + * @param {*} val The value to test. + * @return {boolean} true if the value is a boolean, otherwise false. + */ +function isArray(val) { + return objToString.call(val) === '[object Array]'; +} + +/** + * Event emitter constructor. + * + * @api public + */ +function EventEmitter() {} + +/** + * Add a listener. + * + * @api public + * @param {string} name Event name. + * @param {Function} fn Event handler. + * @return {EventEmitter} Emitter instance. + */ +EventEmitter.prototype.on = function(name, fn) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = fn; + } else if (isArray(this.$events[name])) { + this.$events[name].push(fn); + } else { + this.$events[name] = [this.$events[name], fn]; + } + + return this; +}; + +EventEmitter.prototype.addListener = EventEmitter.prototype.on; + +/** + * Adds a volatile listener. + * + * @api public + * @param {string} name Event name. + * @param {Function} fn Event handler. + * @return {EventEmitter} Emitter instance. + */ +EventEmitter.prototype.once = function(name, fn) { + var self = this; + + function on() { + self.removeListener(name, on); + fn.apply(this, arguments); + } + + on.listener = fn; + this.on(name, on); + + return this; +}; + +/** + * Remove a listener. + * + * @api public + * @param {string} name Event name. + * @param {Function} fn Event handler. + * @return {EventEmitter} Emitter instance. + */ +EventEmitter.prototype.removeListener = function(name, fn) { + if (this.$events && this.$events[name]) { + var list = this.$events[name]; + + if (isArray(list)) { + var pos = -1; + + for (var i = 0, l = list.length; i < l; i++) { + if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { + pos = i; + break; + } + } + + if (pos < 0) { + return this; + } + + list.splice(pos, 1); + + if (!list.length) { + delete this.$events[name]; + } + } else if (list === fn || (list.listener && list.listener === fn)) { + delete this.$events[name]; + } + } + + return this; +}; + +/** + * Remove all listeners for an event. + * + * @api public + * @param {string} name Event name. + * @return {EventEmitter} Emitter instance. + */ +EventEmitter.prototype.removeAllListeners = function(name) { + if (name === undefined) { + this.$events = {}; + return this; + } + + if (this.$events && this.$events[name]) { + this.$events[name] = null; + } + + return this; +}; + +/** + * Get all listeners for a given event. + * + * @api public + * @param {string} name Event name. + * @return {EventEmitter} Emitter instance. + */ +EventEmitter.prototype.listeners = function(name) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = []; + } + + if (!isArray(this.$events[name])) { + this.$events[name] = [this.$events[name]]; + } + + return this.$events[name]; +}; + +/** + * Emit an event. + * + * @api public + * @param {string} name Event name. + * @return {boolean} true if at least one handler was invoked, else false. + */ +EventEmitter.prototype.emit = function(name) { + if (!this.$events) { + return false; + } + + var handler = this.$events[name]; + + if (!handler) { + return false; + } + + var args = Array.prototype.slice.call(arguments, 1); + + if (typeof handler === 'function') { + handler.apply(this, args); + } else if (isArray(handler)) { + var listeners = handler.slice(); + + for (var i = 0, l = listeners.length; i < l; i++) { + listeners[i].apply(this, args); + } + } else { + return false; + } + + return true; +}; diff --git a/node_modules/mocha/lib/browser/progress.js b/node_modules/mocha/lib/browser/progress.js new file mode 100644 index 0000000..3186b6e --- /dev/null +++ b/node_modules/mocha/lib/browser/progress.js @@ -0,0 +1,117 @@ +/** + * Expose `Progress`. + */ + +module.exports = Progress; + +/** + * Initialize a new `Progress` indicator. + */ +function Progress() { + this.percent = 0; + this.size(0); + this.fontSize(11); + this.font('helvetica, arial, sans-serif'); +} + +/** + * Set progress size to `size`. + * + * @api public + * @param {number} size + * @return {Progress} Progress instance. + */ +Progress.prototype.size = function(size) { + this._size = size; + return this; +}; + +/** + * Set text to `text`. + * + * @api public + * @param {string} text + * @return {Progress} Progress instance. + */ +Progress.prototype.text = function(text) { + this._text = text; + return this; +}; + +/** + * Set font size to `size`. + * + * @api public + * @param {number} size + * @return {Progress} Progress instance. + */ +Progress.prototype.fontSize = function(size) { + this._fontSize = size; + return this; +}; + +/** + * Set font to `family`. + * + * @param {string} family + * @return {Progress} Progress instance. + */ +Progress.prototype.font = function(family) { + this._font = family; + return this; +}; + +/** + * Update percentage to `n`. + * + * @param {number} n + * @return {Progress} Progress instance. + */ +Progress.prototype.update = function(n) { + this.percent = n; + return this; +}; + +/** + * Draw on `ctx`. + * + * @param {CanvasRenderingContext2d} ctx + * @return {Progress} Progress instance. + */ +Progress.prototype.draw = function(ctx) { + try { + var percent = Math.min(this.percent, 100); + var size = this._size; + var half = size / 2; + var x = half; + var y = half; + var rad = half - 1; + var fontSize = this._fontSize; + + ctx.font = fontSize + 'px ' + this._font; + + var angle = Math.PI * 2 * (percent / 100); + ctx.clearRect(0, 0, size, size); + + // outer circle + ctx.strokeStyle = '#9f9f9f'; + ctx.beginPath(); + ctx.arc(x, y, rad, 0, angle, false); + ctx.stroke(); + + // inner circle + ctx.strokeStyle = '#eee'; + ctx.beginPath(); + ctx.arc(x, y, rad - 1, 0, angle, true); + ctx.stroke(); + + // text + var text = this._text || (percent | 0) + '%'; + var w = ctx.measureText(text).width; + + ctx.fillText(text, x - w / 2 + 1, y + fontSize / 2 - 1); + } catch (err) { + // don't fail if we can't render progress + } + return this; +}; diff --git a/node_modules/mocha/lib/browser/tty.js b/node_modules/mocha/lib/browser/tty.js new file mode 100644 index 0000000..840d669 --- /dev/null +++ b/node_modules/mocha/lib/browser/tty.js @@ -0,0 +1,11 @@ +exports.isatty = function isatty() { + return true; +}; + +exports.getWindowSize = function getWindowSize() { + if ('innerHeight' in global) { + return [global.innerHeight, global.innerWidth]; + } + // In a Web Worker, the DOM Window is not available. + return [640, 480]; +}; diff --git a/node_modules/mocha/lib/context.js b/node_modules/mocha/lib/context.js new file mode 100644 index 0000000..0b0242f --- /dev/null +++ b/node_modules/mocha/lib/context.js @@ -0,0 +1,89 @@ +/** + * Expose `Context`. + */ + +module.exports = Context; + +/** + * Initialize a new `Context`. + * + * @api private + */ +function Context() {} + +/** + * Set or get the context `Runnable` to `runnable`. + * + * @api private + * @param {Runnable} runnable + * @return {Context} + */ +Context.prototype.runnable = function(runnable) { + if (!arguments.length) { + return this._runnable; + } + this.test = this._runnable = runnable; + return this; +}; + +/** + * Set test timeout `ms`. + * + * @api private + * @param {number} ms + * @return {Context} self + */ +Context.prototype.timeout = function(ms) { + if (!arguments.length) { + return this.runnable().timeout(); + } + this.runnable().timeout(ms); + return this; +}; + +/** + * Set test timeout `enabled`. + * + * @api private + * @param {boolean} enabled + * @return {Context} self + */ +Context.prototype.enableTimeouts = function(enabled) { + this.runnable().enableTimeouts(enabled); + return this; +}; + +/** + * Set test slowness threshold `ms`. + * + * @api private + * @param {number} ms + * @return {Context} self + */ +Context.prototype.slow = function(ms) { + this.runnable().slow(ms); + return this; +}; + +/** + * Mark a test as skipped. + * + * @api private + * @return {Context} self + */ +Context.prototype.skip = function() { + this.runnable().skip(); + return this; +}; + +/** + * Inspect the context void of `._runnable`. + * + * @api private + * @return {string} + */ +Context.prototype.inspect = function() { + return JSON.stringify(this, function(key, val) { + return key === 'runnable' || key === 'test' ? undefined : val; + }, 2); +}; diff --git a/node_modules/mocha/lib/hook.js b/node_modules/mocha/lib/hook.js new file mode 100644 index 0000000..0417e01 --- /dev/null +++ b/node_modules/mocha/lib/hook.js @@ -0,0 +1,46 @@ +/** + * Module dependencies. + */ + +var Runnable = require('./runnable'); +var inherits = require('./utils').inherits; + +/** + * Expose `Hook`. + */ + +module.exports = Hook; + +/** + * Initialize a new `Hook` with the given `title` and callback `fn`. + * + * @param {String} title + * @param {Function} fn + * @api private + */ +function Hook(title, fn) { + Runnable.call(this, title, fn); + this.type = 'hook'; +} + +/** + * Inherit from `Runnable.prototype`. + */ +inherits(Hook, Runnable); + +/** + * Get or set the test `err`. + * + * @param {Error} err + * @return {Error} + * @api public + */ +Hook.prototype.error = function(err) { + if (!arguments.length) { + err = this._error; + this._error = null; + return err; + } + + this._error = err; +}; diff --git a/node_modules/mocha/lib/interfaces/bdd.js b/node_modules/mocha/lib/interfaces/bdd.js new file mode 100644 index 0000000..253f24e --- /dev/null +++ b/node_modules/mocha/lib/interfaces/bdd.js @@ -0,0 +1,110 @@ +/** + * Module dependencies. + */ + +var Suite = require('../suite'); +var Test = require('../test'); +var escapeRe = require('escape-string-regexp'); + +/** + * BDD-style interface: + * + * describe('Array', function() { + * describe('#indexOf()', function() { + * it('should return -1 when not present', function() { + * // ... + * }); + * + * it('should return the index when present', function() { + * // ... + * }); + * }); + * }); + * + * @param {Suite} suite Root suite. + */ +module.exports = function(suite) { + var suites = [suite]; + + suite.on('pre-require', function(context, file, mocha) { + var common = require('./common')(suites, context); + + context.before = common.before; + context.after = common.after; + context.beforeEach = common.beforeEach; + context.afterEach = common.afterEach; + context.run = mocha.options.delay && common.runWithSuite(suite); + /** + * Describe a "suite" with the given `title` + * and callback `fn` containing nested suites + * and/or tests. + */ + + context.describe = context.context = function(title, fn) { + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + return suite; + }; + + /** + * Pending describe. + */ + + context.xdescribe = context.xcontext = context.describe.skip = function(title, fn) { + var suite = Suite.create(suites[0], title); + suite.pending = true; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + }; + + /** + * Exclusive suite. + */ + + context.describe.only = function(title, fn) { + var suite = context.describe(title, fn); + mocha.grep(suite.fullTitle()); + return suite; + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.it = context.specify = function(title, fn) { + var suite = suites[0]; + if (suite.pending) { + fn = null; + } + var test = new Test(title, fn); + test.file = file; + suite.addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.it.only = function(title, fn) { + var test = context.it(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + return test; + }; + + /** + * Pending test case. + */ + + context.xit = context.xspecify = context.it.skip = function(title) { + context.it(title); + }; + }); +}; diff --git a/node_modules/mocha/lib/interfaces/common.js b/node_modules/mocha/lib/interfaces/common.js new file mode 100644 index 0000000..4a21c16 --- /dev/null +++ b/node_modules/mocha/lib/interfaces/common.js @@ -0,0 +1,76 @@ +'use strict'; + +/** + * Functions common to more than one interface. + * + * @param {Suite[]} suites + * @param {Context} context + * @return {Object} An object containing common functions. + */ +module.exports = function(suites, context) { + return { + /** + * This is only present if flag --delay is passed into Mocha. It triggers + * root suite execution. + * + * @param {Suite} suite The root wuite. + * @return {Function} A function which runs the root suite + */ + runWithSuite: function runWithSuite(suite) { + return function run() { + suite.run(); + }; + }, + + /** + * Execute before running tests. + * + * @param {string} name + * @param {Function} fn + */ + before: function(name, fn) { + suites[0].beforeAll(name, fn); + }, + + /** + * Execute after running tests. + * + * @param {string} name + * @param {Function} fn + */ + after: function(name, fn) { + suites[0].afterAll(name, fn); + }, + + /** + * Execute before each test case. + * + * @param {string} name + * @param {Function} fn + */ + beforeEach: function(name, fn) { + suites[0].beforeEach(name, fn); + }, + + /** + * Execute after each test case. + * + * @param {string} name + * @param {Function} fn + */ + afterEach: function(name, fn) { + suites[0].afterEach(name, fn); + }, + + test: { + /** + * Pending test case. + * + * @param {string} title + */ + skip: function(title) { + context.test(title); + } + } + }; +}; diff --git a/node_modules/mocha/lib/interfaces/exports.js b/node_modules/mocha/lib/interfaces/exports.js new file mode 100644 index 0000000..a64692a --- /dev/null +++ b/node_modules/mocha/lib/interfaces/exports.js @@ -0,0 +1,61 @@ +/** + * Module dependencies. + */ + +var Suite = require('../suite'); +var Test = require('../test'); + +/** + * TDD-style interface: + * + * exports.Array = { + * '#indexOf()': { + * 'should return -1 when the value is not present': function() { + * + * }, + * + * 'should return the correct index when the value is present': function() { + * + * } + * } + * }; + * + * @param {Suite} suite Root suite. + */ +module.exports = function(suite) { + var suites = [suite]; + + suite.on('require', visit); + + function visit(obj, file) { + var suite; + for (var key in obj) { + if (typeof obj[key] === 'function') { + var fn = obj[key]; + switch (key) { + case 'before': + suites[0].beforeAll(fn); + break; + case 'after': + suites[0].afterAll(fn); + break; + case 'beforeEach': + suites[0].beforeEach(fn); + break; + case 'afterEach': + suites[0].afterEach(fn); + break; + default: + var test = new Test(key, fn); + test.file = file; + suites[0].addTest(test); + } + } else { + suite = Suite.create(suites[0], key); + suites.unshift(suite); + visit(obj[key]); + suites.shift(); + } + } + } +}; diff --git a/node_modules/mocha/lib/interfaces/index.js b/node_modules/mocha/lib/interfaces/index.js new file mode 100644 index 0000000..4f825d1 --- /dev/null +++ b/node_modules/mocha/lib/interfaces/index.js @@ -0,0 +1,4 @@ +exports.bdd = require('./bdd'); +exports.tdd = require('./tdd'); +exports.qunit = require('./qunit'); +exports.exports = require('./exports'); diff --git a/node_modules/mocha/lib/interfaces/qunit.js b/node_modules/mocha/lib/interfaces/qunit.js new file mode 100644 index 0000000..be7f50f --- /dev/null +++ b/node_modules/mocha/lib/interfaces/qunit.js @@ -0,0 +1,93 @@ +/** + * Module dependencies. + */ + +var Suite = require('../suite'); +var Test = require('../test'); +var escapeRe = require('escape-string-regexp'); + +/** + * QUnit-style interface: + * + * suite('Array'); + * + * test('#length', function() { + * var arr = [1,2,3]; + * ok(arr.length == 3); + * }); + * + * test('#indexOf()', function() { + * var arr = [1,2,3]; + * ok(arr.indexOf(1) == 0); + * ok(arr.indexOf(2) == 1); + * ok(arr.indexOf(3) == 2); + * }); + * + * suite('String'); + * + * test('#length', function() { + * ok('foo'.length == 3); + * }); + * + * @param {Suite} suite Root suite. + */ +module.exports = function(suite) { + var suites = [suite]; + + suite.on('pre-require', function(context, file, mocha) { + var common = require('./common')(suites, context); + + context.before = common.before; + context.after = common.after; + context.beforeEach = common.beforeEach; + context.afterEach = common.afterEach; + context.run = mocha.options.delay && common.runWithSuite(suite); + /** + * Describe a "suite" with the given `title`. + */ + + context.suite = function(title) { + if (suites.length > 1) { + suites.shift(); + } + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + return suite; + }; + + /** + * Exclusive test-case. + */ + + context.suite.only = function(title, fn) { + var suite = context.suite(title, fn); + mocha.grep(suite.fullTitle()); + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.test = function(title, fn) { + var test = new Test(title, fn); + test.file = file; + suites[0].addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn) { + var test = context.test(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + }; + + context.test.skip = common.test.skip; + }); +}; diff --git a/node_modules/mocha/lib/interfaces/tdd.js b/node_modules/mocha/lib/interfaces/tdd.js new file mode 100644 index 0000000..fb22a79 --- /dev/null +++ b/node_modules/mocha/lib/interfaces/tdd.js @@ -0,0 +1,105 @@ +/** + * Module dependencies. + */ + +var Suite = require('../suite'); +var Test = require('../test'); +var escapeRe = require('escape-string-regexp'); + +/** + * TDD-style interface: + * + * suite('Array', function() { + * suite('#indexOf()', function() { + * suiteSetup(function() { + * + * }); + * + * test('should return -1 when not present', function() { + * + * }); + * + * test('should return the index when present', function() { + * + * }); + * + * suiteTeardown(function() { + * + * }); + * }); + * }); + * + * @param {Suite} suite Root suite. + */ +module.exports = function(suite) { + var suites = [suite]; + + suite.on('pre-require', function(context, file, mocha) { + var common = require('./common')(suites, context); + + context.setup = common.beforeEach; + context.teardown = common.afterEach; + context.suiteSetup = common.before; + context.suiteTeardown = common.after; + context.run = mocha.options.delay && common.runWithSuite(suite); + + /** + * Describe a "suite" with the given `title` and callback `fn` containing + * nested suites and/or tests. + */ + context.suite = function(title, fn) { + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + return suite; + }; + + /** + * Pending suite. + */ + context.suite.skip = function(title, fn) { + var suite = Suite.create(suites[0], title); + suite.pending = true; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + }; + + /** + * Exclusive test-case. + */ + context.suite.only = function(title, fn) { + var suite = context.suite(title, fn); + mocha.grep(suite.fullTitle()); + }; + + /** + * Describe a specification or test-case with the given `title` and + * callback `fn` acting as a thunk. + */ + context.test = function(title, fn) { + var suite = suites[0]; + if (suite.pending) { + fn = null; + } + var test = new Test(title, fn); + test.file = file; + suite.addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn) { + var test = context.test(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + }; + + context.test.skip = common.test.skip; + }); +}; diff --git a/node_modules/mocha/lib/mocha.js b/node_modules/mocha/lib/mocha.js new file mode 100644 index 0000000..eb6c8d0 --- /dev/null +++ b/node_modules/mocha/lib/mocha.js @@ -0,0 +1,487 @@ +/*! + * mocha + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var escapeRe = require('escape-string-regexp'); +var path = require('path'); +var reporters = require('./reporters'); +var utils = require('./utils'); + +/** + * Expose `Mocha`. + */ + +exports = module.exports = Mocha; + +/** + * To require local UIs and reporters when running in node. + */ + +if (!process.browser) { + var cwd = process.cwd(); + module.paths.push(cwd, path.join(cwd, 'node_modules')); +} + +/** + * Expose internals. + */ + +exports.utils = utils; +exports.interfaces = require('./interfaces'); +exports.reporters = reporters; +exports.Runnable = require('./runnable'); +exports.Context = require('./context'); +exports.Runner = require('./runner'); +exports.Suite = require('./suite'); +exports.Hook = require('./hook'); +exports.Test = require('./test'); + +/** + * Return image `name` path. + * + * @api private + * @param {string} name + * @return {string} + */ +function image(name) { + return path.join(__dirname, '../images', name + '.png'); +} + +/** + * Set up mocha with `options`. + * + * Options: + * + * - `ui` name "bdd", "tdd", "exports" etc + * - `reporter` reporter instance, defaults to `mocha.reporters.spec` + * - `globals` array of accepted globals + * - `timeout` timeout in milliseconds + * - `bail` bail on the first test failure + * - `slow` milliseconds to wait before considering a test slow + * - `ignoreLeaks` ignore global leaks + * - `fullTrace` display the full stack-trace on failing + * - `grep` string or regexp to filter tests with + * + * @param {Object} options + * @api public + */ +function Mocha(options) { + options = options || {}; + this.files = []; + this.options = options; + if (options.grep) { + this.grep(new RegExp(options.grep)); + } + if (options.fgrep) { + this.grep(options.fgrep); + } + this.suite = new exports.Suite('', new exports.Context()); + this.ui(options.ui); + this.bail(options.bail); + this.reporter(options.reporter, options.reporterOptions); + if (typeof options.timeout !== 'undefined' && options.timeout !== null) { + this.timeout(options.timeout); + } + this.useColors(options.useColors); + if (options.enableTimeouts !== null) { + this.enableTimeouts(options.enableTimeouts); + } + if (options.slow) { + this.slow(options.slow); + } + + this.suite.on('pre-require', function(context) { + exports.afterEach = context.afterEach || context.teardown; + exports.after = context.after || context.suiteTeardown; + exports.beforeEach = context.beforeEach || context.setup; + exports.before = context.before || context.suiteSetup; + exports.describe = context.describe || context.suite; + exports.it = context.it || context.test; + exports.setup = context.setup || context.beforeEach; + exports.suiteSetup = context.suiteSetup || context.before; + exports.suiteTeardown = context.suiteTeardown || context.after; + exports.suite = context.suite || context.describe; + exports.teardown = context.teardown || context.afterEach; + exports.test = context.test || context.it; + exports.run = context.run; + }); +} + +/** + * Enable or disable bailing on the first failure. + * + * @api public + * @param {boolean} [bail] + */ +Mocha.prototype.bail = function(bail) { + if (!arguments.length) { + bail = true; + } + this.suite.bail(bail); + return this; +}; + +/** + * Add test `file`. + * + * @api public + * @param {string} file + */ +Mocha.prototype.addFile = function(file) { + this.files.push(file); + return this; +}; + +/** + * Set reporter to `reporter`, defaults to "spec". + * + * @param {String|Function} reporter name or constructor + * @param {Object} reporterOptions optional options + * @api public + * @param {string|Function} reporter name or constructor + * @param {Object} reporterOptions optional options + */ +Mocha.prototype.reporter = function(reporter, reporterOptions) { + if (typeof reporter === 'function') { + this._reporter = reporter; + } else { + reporter = reporter || 'spec'; + var _reporter; + // Try to load a built-in reporter. + if (reporters[reporter]) { + _reporter = reporters[reporter]; + } + // Try to load reporters from process.cwd() and node_modules + if (!_reporter) { + try { + _reporter = require(reporter); + } catch (err) { + err.message.indexOf('Cannot find module') !== -1 + ? console.warn('"' + reporter + '" reporter not found') + : console.warn('"' + reporter + '" reporter blew up with error:\n' + err.stack); + } + } + if (!_reporter && reporter === 'teamcity') { + console.warn('The Teamcity reporter was moved to a package named ' + + 'mocha-teamcity-reporter ' + + '(https://npmjs.org/package/mocha-teamcity-reporter).'); + } + if (!_reporter) { + throw new Error('invalid reporter "' + reporter + '"'); + } + this._reporter = _reporter; + } + this.options.reporterOptions = reporterOptions; + return this; +}; + +/** + * Set test UI `name`, defaults to "bdd". + * + * @api public + * @param {string} bdd + */ +Mocha.prototype.ui = function(name) { + name = name || 'bdd'; + this._ui = exports.interfaces[name]; + if (!this._ui) { + try { + this._ui = require(name); + } catch (err) { + throw new Error('invalid interface "' + name + '"'); + } + } + this._ui = this._ui(this.suite); + return this; +}; + +/** + * Load registered files. + * + * @api private + */ +Mocha.prototype.loadFiles = function(fn) { + var self = this; + var suite = this.suite; + var pending = this.files.length; + this.files.forEach(function(file) { + file = path.resolve(file); + suite.emit('pre-require', global, file, self); + suite.emit('require', require(file), file, self); + suite.emit('post-require', global, file, self); + --pending || (fn && fn()); + }); +}; + +/** + * Enable growl support. + * + * @api private + */ +Mocha.prototype._growl = function(runner, reporter) { + var notify = require('growl'); + + runner.on('end', function() { + var stats = reporter.stats; + if (stats.failures) { + var msg = stats.failures + ' of ' + runner.total + ' tests failed'; + notify(msg, { name: 'mocha', title: 'Failed', image: image('error') }); + } else { + notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { + name: 'mocha', + title: 'Passed', + image: image('ok') + }); + } + }); +}; + +/** + * Add regexp to grep, if `re` is a string it is escaped. + * + * @param {RegExp|String} re + * @return {Mocha} + * @api public + * @param {RegExp|string} re + * @return {Mocha} + */ +Mocha.prototype.grep = function(re) { + this.options.grep = typeof re === 'string' ? new RegExp(escapeRe(re)) : re; + return this; +}; + +/** + * Invert `.grep()` matches. + * + * @return {Mocha} + * @api public + */ +Mocha.prototype.invert = function() { + this.options.invert = true; + return this; +}; + +/** + * Ignore global leaks. + * + * @param {Boolean} ignore + * @return {Mocha} + * @api public + * @param {boolean} ignore + * @return {Mocha} + */ +Mocha.prototype.ignoreLeaks = function(ignore) { + this.options.ignoreLeaks = Boolean(ignore); + return this; +}; + +/** + * Enable global leak checking. + * + * @return {Mocha} + * @api public + */ +Mocha.prototype.checkLeaks = function() { + this.options.ignoreLeaks = false; + return this; +}; + +/** + * Display long stack-trace on failing + * + * @return {Mocha} + * @api public + */ +Mocha.prototype.fullTrace = function() { + this.options.fullStackTrace = true; + return this; +}; + +/** + * Enable growl support. + * + * @return {Mocha} + * @api public + */ +Mocha.prototype.growl = function() { + this.options.growl = true; + return this; +}; + +/** + * Ignore `globals` array or string. + * + * @param {Array|String} globals + * @return {Mocha} + * @api public + * @param {Array|string} globals + * @return {Mocha} + */ +Mocha.prototype.globals = function(globals) { + this.options.globals = (this.options.globals || []).concat(globals); + return this; +}; + +/** + * Emit color output. + * + * @param {Boolean} colors + * @return {Mocha} + * @api public + * @param {boolean} colors + * @return {Mocha} + */ +Mocha.prototype.useColors = function(colors) { + if (colors !== undefined) { + this.options.useColors = colors; + } + return this; +}; + +/** + * Use inline diffs rather than +/-. + * + * @param {Boolean} inlineDiffs + * @return {Mocha} + * @api public + * @param {boolean} inlineDiffs + * @return {Mocha} + */ +Mocha.prototype.useInlineDiffs = function(inlineDiffs) { + this.options.useInlineDiffs = inlineDiffs !== undefined && inlineDiffs; + return this; +}; + +/** + * Set the timeout in milliseconds. + * + * @param {Number} timeout + * @return {Mocha} + * @api public + * @param {number} timeout + * @return {Mocha} + */ +Mocha.prototype.timeout = function(timeout) { + this.suite.timeout(timeout); + return this; +}; + +/** + * Set slowness threshold in milliseconds. + * + * @param {Number} slow + * @return {Mocha} + * @api public + * @param {number} slow + * @return {Mocha} + */ +Mocha.prototype.slow = function(slow) { + this.suite.slow(slow); + return this; +}; + +/** + * Enable timeouts. + * + * @param {Boolean} enabled + * @return {Mocha} + * @api public + * @param {boolean} enabled + * @return {Mocha} + */ +Mocha.prototype.enableTimeouts = function(enabled) { + this.suite.enableTimeouts(arguments.length && enabled !== undefined ? enabled : true); + return this; +}; + +/** + * Makes all tests async (accepting a callback) + * + * @return {Mocha} + * @api public + */ +Mocha.prototype.asyncOnly = function() { + this.options.asyncOnly = true; + return this; +}; + +/** + * Disable syntax highlighting (in browser). + * + * @api public + */ +Mocha.prototype.noHighlighting = function() { + this.options.noHighlighting = true; + return this; +}; + +/** + * Enable uncaught errors to propagate (in browser). + * + * @return {Mocha} + * @api public + */ +Mocha.prototype.allowUncaught = function() { + this.options.allowUncaught = true; + return this; +}; + +/** + * Delay root suite execution. + * @returns {Mocha} + */ +Mocha.prototype.delay = function delay() { + this.options.delay = true; + return this; +}; + +/** + * Run tests and invoke `fn()` when complete. + * + * @api public + * @param {Function} fn + * @return {Runner} + */ +Mocha.prototype.run = function(fn) { + if (this.files.length) { + this.loadFiles(); + } + var suite = this.suite; + var options = this.options; + options.files = this.files; + var runner = new exports.Runner(suite, options.delay); + var reporter = new this._reporter(runner, options); + runner.ignoreLeaks = options.ignoreLeaks !== false; + runner.fullStackTrace = options.fullStackTrace; + runner.asyncOnly = options.asyncOnly; + runner.allowUncaught = options.allowUncaught; + if (options.grep) { + runner.grep(options.grep, options.invert); + } + if (options.globals) { + runner.globals(options.globals); + } + if (options.growl) { + this._growl(runner, reporter); + } + if (options.useColors !== undefined) { + exports.reporters.Base.useColors = options.useColors; + } + exports.reporters.Base.inlineDiffs = options.useInlineDiffs; + + function done(failures) { + if (reporter.done) { + reporter.done(failures, fn); + } else { + fn && fn(failures); + } + } + + return runner.run(done); +}; diff --git a/node_modules/mocha/lib/ms.js b/node_modules/mocha/lib/ms.js new file mode 100644 index 0000000..12fddc1 --- /dev/null +++ b/node_modules/mocha/lib/ms.js @@ -0,0 +1,128 @@ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @api public + * @param {string|number} val + * @param {Object} options + * @return {string|number} + */ +module.exports = function(val, options) { + options = options || {}; + if (typeof val === 'string') { + return parse(val); + } + // https://github.com/mochajs/mocha/pull/1035 + return options['long'] ? longFormat(val) : shortFormat(val); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @api private + * @param {string} str + * @return {number} + */ +function parse(str) { + var match = (/^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i).exec(str); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 's': + return n * s; + case 'ms': + return n; + default: + // No default case + } +} + +/** + * Short format for `ms`. + * + * @api private + * @param {number} ms + * @return {string} + */ +function shortFormat(ms) { + if (ms >= d) { + return Math.round(ms / d) + 'd'; + } + if (ms >= h) { + return Math.round(ms / h) + 'h'; + } + if (ms >= m) { + return Math.round(ms / m) + 'm'; + } + if (ms >= s) { + return Math.round(ms / s) + 's'; + } + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @api private + * @param {number} ms + * @return {string} + */ +function longFormat(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; +} + +/** + * Pluralization helper. + * + * @api private + * @param {number} ms + * @param {number} n + * @param {string} name + */ +function plural(ms, n, name) { + if (ms < n) { + return; + } + if (ms < n * 1.5) { + return Math.floor(ms / n) + ' ' + name; + } + return Math.ceil(ms / n) + ' ' + name + 's'; +} diff --git a/node_modules/mocha/lib/pending.js b/node_modules/mocha/lib/pending.js new file mode 100644 index 0000000..c847e04 --- /dev/null +++ b/node_modules/mocha/lib/pending.js @@ -0,0 +1,15 @@ + +/** + * Expose `Pending`. + */ + +module.exports = Pending; + +/** + * Initialize a new `Pending` error with the given message. + * + * @param {string} message + */ +function Pending(message) { + this.message = message; +} diff --git a/node_modules/mocha/lib/reporters/base.js b/node_modules/mocha/lib/reporters/base.js new file mode 100644 index 0000000..bb107ba --- /dev/null +++ b/node_modules/mocha/lib/reporters/base.js @@ -0,0 +1,487 @@ +/** + * Module dependencies. + */ + +var tty = require('tty'); +var diff = require('diff'); +var ms = require('../ms'); +var utils = require('../utils'); +var supportsColor = process.browser ? null : require('supports-color'); + +/** + * Expose `Base`. + */ + +exports = module.exports = Base; + +/** + * Save timer references to avoid Sinon interfering. + * See: https://github.com/mochajs/mocha/issues/237 + */ + +/* eslint-disable no-unused-vars, no-native-reassign */ +var Date = global.Date; +var setTimeout = global.setTimeout; +var setInterval = global.setInterval; +var clearTimeout = global.clearTimeout; +var clearInterval = global.clearInterval; +/* eslint-enable no-unused-vars, no-native-reassign */ + +/** + * Check if both stdio streams are associated with a tty. + */ + +var isatty = tty.isatty(1) && tty.isatty(2); + +/** + * Enable coloring by default, except in the browser interface. + */ + +exports.useColors = !process.browser && (supportsColor || (process.env.MOCHA_COLORS !== undefined)); + +/** + * Inline diffs instead of +/- + */ + +exports.inlineDiffs = false; + +/** + * Default color map. + */ + +exports.colors = { + pass: 90, + fail: 31, + 'bright pass': 92, + 'bright fail': 91, + 'bright yellow': 93, + pending: 36, + suite: 0, + 'error title': 0, + 'error message': 31, + 'error stack': 90, + checkmark: 32, + fast: 90, + medium: 33, + slow: 31, + green: 32, + light: 90, + 'diff gutter': 90, + 'diff added': 32, + 'diff removed': 31 +}; + +/** + * Default symbol map. + */ + +exports.symbols = { + ok: '✓', + err: '✖', + dot: '․' +}; + +// With node.js on Windows: use symbols available in terminal default fonts +if (process.platform === 'win32') { + exports.symbols.ok = '\u221A'; + exports.symbols.err = '\u00D7'; + exports.symbols.dot = '.'; +} + +/** + * Color `str` with the given `type`, + * allowing colors to be disabled, + * as well as user-defined color + * schemes. + * + * @param {string} type + * @param {string} str + * @return {string} + * @api private + */ +var color = exports.color = function(type, str) { + if (!exports.useColors) { + return String(str); + } + return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; +}; + +/** + * Expose term window size, with some defaults for when stderr is not a tty. + */ + +exports.window = { + width: 75 +}; + +if (isatty) { + exports.window.width = process.stdout.getWindowSize + ? process.stdout.getWindowSize(1)[0] + : tty.getWindowSize()[1]; +} + +/** + * Expose some basic cursor interactions that are common among reporters. + */ + +exports.cursor = { + hide: function() { + isatty && process.stdout.write('\u001b[?25l'); + }, + + show: function() { + isatty && process.stdout.write('\u001b[?25h'); + }, + + deleteLine: function() { + isatty && process.stdout.write('\u001b[2K'); + }, + + beginningOfLine: function() { + isatty && process.stdout.write('\u001b[0G'); + }, + + CR: function() { + if (isatty) { + exports.cursor.deleteLine(); + exports.cursor.beginningOfLine(); + } else { + process.stdout.write('\r'); + } + } +}; + +/** + * Outut the given `failures` as a list. + * + * @param {Array} failures + * @api public + */ + +exports.list = function(failures) { + console.log(); + failures.forEach(function(test, i) { + // format + var fmt = color('error title', ' %s) %s:\n') + + color('error message', ' %s') + + color('error stack', '\n%s\n'); + + // msg + var msg; + var err = test.err; + var message; + if (err.message) { + message = err.message; + } else if (typeof err.inspect === 'function') { + message = err.inspect() + ''; + } else { + message = ''; + } + var stack = err.stack || message; + var index = stack.indexOf(message); + var actual = err.actual; + var expected = err.expected; + var escape = true; + + if (index === -1) { + msg = message; + } else { + index += message.length; + msg = stack.slice(0, index); + // remove msg from stack + stack = stack.slice(index + 1); + } + + // uncaught + if (err.uncaught) { + msg = 'Uncaught ' + msg; + } + // explicitly show diff + if (err.showDiff !== false && sameType(actual, expected) && expected !== undefined) { + escape = false; + if (!(utils.isString(actual) && utils.isString(expected))) { + err.actual = actual = utils.stringify(actual); + err.expected = expected = utils.stringify(expected); + } + + fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n'); + var match = message.match(/^([^:]+): expected/); + msg = '\n ' + color('error message', match ? match[1] : msg); + + if (exports.inlineDiffs) { + msg += inlineDiff(err, escape); + } else { + msg += unifiedDiff(err, escape); + } + } + + // indent stack trace + stack = stack.replace(/^/gm, ' '); + + console.log(fmt, (i + 1), test.fullTitle(), msg, stack); + }); +}; + +/** + * Initialize a new `Base` reporter. + * + * All other reporters generally + * inherit from this reporter, providing + * stats such as test duration, number + * of tests passed / failed etc. + * + * @param {Runner} runner + * @api public + */ + +function Base(runner) { + var stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 }; + var failures = this.failures = []; + + if (!runner) { + return; + } + this.runner = runner; + + runner.stats = stats; + + runner.on('start', function() { + stats.start = new Date(); + }); + + runner.on('suite', function(suite) { + stats.suites = stats.suites || 0; + suite.root || stats.suites++; + }); + + runner.on('test end', function() { + stats.tests = stats.tests || 0; + stats.tests++; + }); + + runner.on('pass', function(test) { + stats.passes = stats.passes || 0; + + if (test.duration > test.slow()) { + test.speed = 'slow'; + } else if (test.duration > test.slow() / 2) { + test.speed = 'medium'; + } else { + test.speed = 'fast'; + } + + stats.passes++; + }); + + runner.on('fail', function(test, err) { + stats.failures = stats.failures || 0; + stats.failures++; + test.err = err; + failures.push(test); + }); + + runner.on('end', function() { + stats.end = new Date(); + stats.duration = new Date() - stats.start; + }); + + runner.on('pending', function() { + stats.pending++; + }); +} + +/** + * Output common epilogue used by many of + * the bundled reporters. + * + * @api public + */ +Base.prototype.epilogue = function() { + var stats = this.stats; + var fmt; + + console.log(); + + // passes + fmt = color('bright pass', ' ') + + color('green', ' %d passing') + + color('light', ' (%s)'); + + console.log(fmt, + stats.passes || 0, + ms(stats.duration)); + + // pending + if (stats.pending) { + fmt = color('pending', ' ') + + color('pending', ' %d pending'); + + console.log(fmt, stats.pending); + } + + // failures + if (stats.failures) { + fmt = color('fail', ' %d failing'); + + console.log(fmt, stats.failures); + + Base.list(this.failures); + console.log(); + } + + console.log(); +}; + +/** + * Pad the given `str` to `len`. + * + * @api private + * @param {string} str + * @param {string} len + * @return {string} + */ +function pad(str, len) { + str = String(str); + return Array(len - str.length + 1).join(' ') + str; +} + +/** + * Returns an inline diff between 2 strings with coloured ANSI output + * + * @api private + * @param {Error} err with actual/expected + * @param {boolean} escape + * @return {string} Diff + */ +function inlineDiff(err, escape) { + var msg = errorDiff(err, 'WordsWithSpace', escape); + + // linenos + var lines = msg.split('\n'); + if (lines.length > 4) { + var width = String(lines.length).length; + msg = lines.map(function(str, i) { + return pad(++i, width) + ' |' + ' ' + str; + }).join('\n'); + } + + // legend + msg = '\n' + + color('diff removed', 'actual') + + ' ' + + color('diff added', 'expected') + + '\n\n' + + msg + + '\n'; + + // indent + msg = msg.replace(/^/gm, ' '); + return msg; +} + +/** + * Returns a unified diff between two strings. + * + * @api private + * @param {Error} err with actual/expected + * @param {boolean} escape + * @return {string} The diff. + */ +function unifiedDiff(err, escape) { + var indent = ' '; + function cleanUp(line) { + if (escape) { + line = escapeInvisibles(line); + } + if (line[0] === '+') { + return indent + colorLines('diff added', line); + } + if (line[0] === '-') { + return indent + colorLines('diff removed', line); + } + if (line.match(/\@\@/)) { + return null; + } + if (line.match(/\\ No newline/)) { + return null; + } + return indent + line; + } + function notBlank(line) { + return typeof line !== 'undefined' && line !== null; + } + var msg = diff.createPatch('string', err.actual, err.expected); + var lines = msg.split('\n').splice(4); + return '\n ' + + colorLines('diff added', '+ expected') + ' ' + + colorLines('diff removed', '- actual') + + '\n\n' + + lines.map(cleanUp).filter(notBlank).join('\n'); +} + +/** + * Return a character diff for `err`. + * + * @api private + * @param {Error} err + * @param {string} type + * @param {boolean} escape + * @return {string} + */ +function errorDiff(err, type, escape) { + var actual = escape ? escapeInvisibles(err.actual) : err.actual; + var expected = escape ? escapeInvisibles(err.expected) : err.expected; + return diff['diff' + type](actual, expected).map(function(str) { + if (str.added) { + return colorLines('diff added', str.value); + } + if (str.removed) { + return colorLines('diff removed', str.value); + } + return str.value; + }).join(''); +} + +/** + * Returns a string with all invisible characters in plain text + * + * @api private + * @param {string} line + * @return {string} + */ +function escapeInvisibles(line) { + return line.replace(/\t/g, '') + .replace(/\r/g, '') + .replace(/\n/g, '\n'); +} + +/** + * Color lines for `str`, using the color `name`. + * + * @api private + * @param {string} name + * @param {string} str + * @return {string} + */ +function colorLines(name, str) { + return str.split('\n').map(function(str) { + return color(name, str); + }).join('\n'); +} + +/** + * Object#toString reference. + */ +var objToString = Object.prototype.toString; + +/** + * Check that a / b have the same type. + * + * @api private + * @param {Object} a + * @param {Object} b + * @return {boolean} + */ +function sameType(a, b) { + return objToString.call(a) === objToString.call(b); +} diff --git a/node_modules/mocha/lib/reporters/doc.js b/node_modules/mocha/lib/reporters/doc.js new file mode 100644 index 0000000..8c1fd3a --- /dev/null +++ b/node_modules/mocha/lib/reporters/doc.js @@ -0,0 +1,62 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); + +/** + * Expose `Doc`. + */ + +exports = module.exports = Doc; + +/** + * Initialize a new `Doc` reporter. + * + * @param {Runner} runner + * @api public + */ +function Doc(runner) { + Base.call(this, runner); + + var indents = 2; + + function indent() { + return Array(indents).join(' '); + } + + runner.on('suite', function(suite) { + if (suite.root) { + return; + } + ++indents; + console.log('%s
    ', indent()); + ++indents; + console.log('%s

    %s

    ', indent(), utils.escape(suite.title)); + console.log('%s
    ', indent()); + }); + + runner.on('suite end', function(suite) { + if (suite.root) { + return; + } + console.log('%s
    ', indent()); + --indents; + console.log('%s
    ', indent()); + --indents; + }); + + runner.on('pass', function(test) { + console.log('%s
    %s
    ', indent(), utils.escape(test.title)); + var code = utils.escape(utils.clean(test.fn.toString())); + console.log('%s
    %s
    ', indent(), code); + }); + + runner.on('fail', function(test, err) { + console.log('%s
    %s
    ', indent(), utils.escape(test.title)); + var code = utils.escape(utils.clean(test.fn.toString())); + console.log('%s
    %s
    ', indent(), code); + console.log('%s
    %s
    ', indent(), utils.escape(err)); + }); +} diff --git a/node_modules/mocha/lib/reporters/dot.js b/node_modules/mocha/lib/reporters/dot.js new file mode 100644 index 0000000..e905dc6 --- /dev/null +++ b/node_modules/mocha/lib/reporters/dot.js @@ -0,0 +1,66 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var color = Base.color; + +/** + * Expose `Dot`. + */ + +exports = module.exports = Dot; + +/** + * Initialize a new `Dot` matrix test reporter. + * + * @api public + * @param {Runner} runner + */ +function Dot(runner) { + Base.call(this, runner); + + var self = this; + var width = Base.window.width * .75 | 0; + var n = -1; + + runner.on('start', function() { + process.stdout.write('\n'); + }); + + runner.on('pending', function() { + if (++n % width === 0) { + process.stdout.write('\n '); + } + process.stdout.write(color('pending', Base.symbols.dot)); + }); + + runner.on('pass', function(test) { + if (++n % width === 0) { + process.stdout.write('\n '); + } + if (test.speed === 'slow') { + process.stdout.write(color('bright yellow', Base.symbols.dot)); + } else { + process.stdout.write(color(test.speed, Base.symbols.dot)); + } + }); + + runner.on('fail', function() { + if (++n % width === 0) { + process.stdout.write('\n '); + } + process.stdout.write(color('fail', Base.symbols.dot)); + }); + + runner.on('end', function() { + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Dot, Base); diff --git a/node_modules/mocha/lib/reporters/html-cov.js b/node_modules/mocha/lib/reporters/html-cov.js new file mode 100644 index 0000000..e3f2dd9 --- /dev/null +++ b/node_modules/mocha/lib/reporters/html-cov.js @@ -0,0 +1,56 @@ +/** + * Module dependencies. + */ + +var JSONCov = require('./json-cov'); +var readFileSync = require('fs').readFileSync; +var join = require('path').join; + +/** + * Expose `HTMLCov`. + */ + +exports = module.exports = HTMLCov; + +/** + * Initialize a new `JsCoverage` reporter. + * + * @api public + * @param {Runner} runner + */ +function HTMLCov(runner) { + var jade = require('jade'); + var file = join(__dirname, '/templates/coverage.jade'); + var str = readFileSync(file, 'utf8'); + var fn = jade.compile(str, { filename: file }); + var self = this; + + JSONCov.call(this, runner, false); + + runner.on('end', function() { + process.stdout.write(fn({ + cov: self.cov, + coverageClass: coverageClass + })); + }); +} + +/** + * Return coverage class for a given coverage percentage. + * + * @api private + * @param {number} coveragePctg + * @return {string} + */ +function coverageClass(coveragePctg) { + if (coveragePctg >= 75) { + return 'high'; + } + if (coveragePctg >= 50) { + return 'medium'; + } + if (coveragePctg >= 25) { + return 'low'; + } + return 'terrible'; +} diff --git a/node_modules/mocha/lib/reporters/html.js b/node_modules/mocha/lib/reporters/html.js new file mode 100644 index 0000000..62643ec --- /dev/null +++ b/node_modules/mocha/lib/reporters/html.js @@ -0,0 +1,326 @@ +/* eslint-env browser */ + +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); +var Progress = require('../browser/progress'); +var escapeRe = require('escape-string-regexp'); +var escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +/* eslint-disable no-unused-vars, no-native-reassign */ +var Date = global.Date; +var setTimeout = global.setTimeout; +var setInterval = global.setInterval; +var clearTimeout = global.clearTimeout; +var clearInterval = global.clearInterval; +/* eslint-enable no-unused-vars, no-native-reassign */ + +/** + * Expose `HTML`. + */ + +exports = module.exports = HTML; + +/** + * Stats template. + */ + +var statsTemplate = ''; + +/** + * Initialize a new `HTML` reporter. + * + * @api public + * @param {Runner} runner + */ +function HTML(runner) { + Base.call(this, runner); + + var self = this; + var stats = this.stats; + var stat = fragment(statsTemplate); + var items = stat.getElementsByTagName('li'); + var passes = items[1].getElementsByTagName('em')[0]; + var passesLink = items[1].getElementsByTagName('a')[0]; + var failures = items[2].getElementsByTagName('em')[0]; + var failuresLink = items[2].getElementsByTagName('a')[0]; + var duration = items[3].getElementsByTagName('em')[0]; + var canvas = stat.getElementsByTagName('canvas')[0]; + var report = fragment('
      '); + var stack = [report]; + var progress; + var ctx; + var root = document.getElementById('mocha'); + + if (canvas.getContext) { + var ratio = window.devicePixelRatio || 1; + canvas.style.width = canvas.width; + canvas.style.height = canvas.height; + canvas.width *= ratio; + canvas.height *= ratio; + ctx = canvas.getContext('2d'); + ctx.scale(ratio, ratio); + progress = new Progress(); + } + + if (!root) { + return error('#mocha div missing, add it to your document'); + } + + // pass toggle + on(passesLink, 'click', function() { + unhide(); + var name = (/pass/).test(report.className) ? '' : ' pass'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) { + hideSuitesWithout('test pass'); + } + }); + + // failure toggle + on(failuresLink, 'click', function() { + unhide(); + var name = (/fail/).test(report.className) ? '' : ' fail'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) { + hideSuitesWithout('test fail'); + } + }); + + root.appendChild(stat); + root.appendChild(report); + + if (progress) { + progress.size(40); + } + + runner.on('suite', function(suite) { + if (suite.root) { + return; + } + + // suite + var url = self.suiteURL(suite); + var el = fragment('
    • %s

    • ', url, escape(suite.title)); + + // container + stack[0].appendChild(el); + stack.unshift(document.createElement('ul')); + el.appendChild(stack[0]); + }); + + runner.on('suite end', function(suite) { + if (suite.root) { + return; + } + stack.shift(); + }); + + runner.on('fail', function(test) { + if (test.type === 'hook') { + runner.emit('test end', test); + } + }); + + runner.on('test end', function(test) { + // TODO: add to stats + var percent = stats.tests / this.total * 100 | 0; + if (progress) { + progress.update(percent).draw(ctx); + } + + // update stats + var ms = new Date() - stats.start; + text(passes, stats.passes); + text(failures, stats.failures); + text(duration, (ms / 1000).toFixed(2)); + + // test + var el; + if (test.state === 'passed') { + var url = self.testURL(test); + el = fragment('
    • %e%ems

    • ', test.speed, test.title, test.duration, url); + } else if (test.pending) { + el = fragment('
    • %e

    • ', test.title); + } else { + el = fragment('
    • %e

    • ', test.title, self.testURL(test)); + var stackString; // Note: Includes leading newline + var message = test.err.toString(); + + // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we + // check for the result of the stringifying. + if (message === '[object Error]') { + message = test.err.message; + } + + if (test.err.stack) { + var indexOfMessage = test.err.stack.indexOf(test.err.message); + if (indexOfMessage === -1) { + stackString = test.err.stack; + } else { + stackString = test.err.stack.substr(test.err.message.length + indexOfMessage); + } + } else if (test.err.sourceURL && test.err.line !== undefined) { + // Safari doesn't give you a stack. Let's at least provide a source line. + stackString = '\n(' + test.err.sourceURL + ':' + test.err.line + ')'; + } + + stackString = stackString || ''; + + if (test.err.htmlMessage && stackString) { + el.appendChild(fragment('
      %s\n
      %e
      ', test.err.htmlMessage, stackString)); + } else if (test.err.htmlMessage) { + el.appendChild(fragment('
      %s
      ', test.err.htmlMessage)); + } else { + el.appendChild(fragment('
      %e%e
      ', message, stackString)); + } + } + + // toggle code + // TODO: defer + if (!test.pending) { + var h2 = el.getElementsByTagName('h2')[0]; + + on(h2, 'click', function() { + pre.style.display = pre.style.display === 'none' ? 'block' : 'none'; + }); + + var pre = fragment('
      %e
      ', utils.clean(test.fn.toString())); + el.appendChild(pre); + pre.style.display = 'none'; + } + + // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. + if (stack[0]) { + stack[0].appendChild(el); + } + }); +} + +/** + * Makes a URL, preserving querystring ("search") parameters. + * + * @param {string} s + * @return {string} A new URL. + */ +function makeUrl(s) { + var search = window.location.search; + + // Remove previous grep query parameter if present + if (search) { + search = search.replace(/[?&]grep=[^&\s]*/g, '').replace(/^&/, '?'); + } + + return window.location.pathname + (search ? search + '&' : '?') + 'grep=' + encodeURIComponent(escapeRe(s)); +} + +/** + * Provide suite URL. + * + * @param {Object} [suite] + */ +HTML.prototype.suiteURL = function(suite) { + return makeUrl(suite.fullTitle()); +}; + +/** + * Provide test URL. + * + * @param {Object} [test] + */ +HTML.prototype.testURL = function(test) { + return makeUrl(test.fullTitle()); +}; + +/** + * Display error `msg`. + * + * @param {string} msg + */ +function error(msg) { + document.body.appendChild(fragment('
      %s
      ', msg)); +} + +/** + * Return a DOM fragment from `html`. + * + * @param {string} html + */ +function fragment(html) { + var args = arguments; + var div = document.createElement('div'); + var i = 1; + + div.innerHTML = html.replace(/%([se])/g, function(_, type) { + switch (type) { + case 's': return String(args[i++]); + case 'e': return escape(args[i++]); + // no default + } + }); + + return div.firstChild; +} + +/** + * Check for suites that do not have elements + * with `classname`, and hide them. + * + * @param {text} classname + */ +function hideSuitesWithout(classname) { + var suites = document.getElementsByClassName('suite'); + for (var i = 0; i < suites.length; i++) { + var els = suites[i].getElementsByClassName(classname); + if (!els.length) { + suites[i].className += ' hidden'; + } + } +} + +/** + * Unhide .hidden suites. + */ +function unhide() { + var els = document.getElementsByClassName('suite hidden'); + for (var i = 0; i < els.length; ++i) { + els[i].className = els[i].className.replace('suite hidden', 'suite'); + } +} + +/** + * Set an element's text contents. + * + * @param {HTMLElement} el + * @param {string} contents + */ +function text(el, contents) { + if (el.textContent) { + el.textContent = contents; + } else { + el.innerText = contents; + } +} + +/** + * Listen on `event` with callback `fn`. + */ +function on(el, event, fn) { + if (el.addEventListener) { + el.addEventListener(event, fn, false); + } else { + el.attachEvent('on' + event, fn); + } +} diff --git a/node_modules/mocha/lib/reporters/index.js b/node_modules/mocha/lib/reporters/index.js new file mode 100644 index 0000000..51f5cff --- /dev/null +++ b/node_modules/mocha/lib/reporters/index.js @@ -0,0 +1,19 @@ +// Alias exports to a their normalized format Mocha#reporter to prevent a need +// for dynamic (try/catch) requires, which Browserify doesn't handle. +exports.Base = exports.base = require('./base'); +exports.Dot = exports.dot = require('./dot'); +exports.Doc = exports.doc = require('./doc'); +exports.TAP = exports.tap = require('./tap'); +exports.JSON = exports.json = require('./json'); +exports.HTML = exports.html = require('./html'); +exports.List = exports.list = require('./list'); +exports.Min = exports.min = require('./min'); +exports.Spec = exports.spec = require('./spec'); +exports.Nyan = exports.nyan = require('./nyan'); +exports.XUnit = exports.xunit = require('./xunit'); +exports.Markdown = exports.markdown = require('./markdown'); +exports.Progress = exports.progress = require('./progress'); +exports.Landing = exports.landing = require('./landing'); +exports.JSONCov = exports['json-cov'] = require('./json-cov'); +exports.HTMLCov = exports['html-cov'] = require('./html-cov'); +exports.JSONStream = exports['json-stream'] = require('./json-stream'); diff --git a/node_modules/mocha/lib/reporters/json-cov.js b/node_modules/mocha/lib/reporters/json-cov.js new file mode 100644 index 0000000..ed444e4 --- /dev/null +++ b/node_modules/mocha/lib/reporters/json-cov.js @@ -0,0 +1,150 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `JSONCov`. + */ + +exports = module.exports = JSONCov; + +/** + * Initialize a new `JsCoverage` reporter. + * + * @api public + * @param {Runner} runner + * @param {boolean} output + */ +function JSONCov(runner, output) { + Base.call(this, runner); + + output = arguments.length === 1 || output; + var self = this; + var tests = []; + var failures = []; + var passes = []; + + runner.on('test end', function(test) { + tests.push(test); + }); + + runner.on('pass', function(test) { + passes.push(test); + }); + + runner.on('fail', function(test) { + failures.push(test); + }); + + runner.on('end', function() { + var cov = global._$jscoverage || {}; + var result = self.cov = map(cov); + result.stats = self.stats; + result.tests = tests.map(clean); + result.failures = failures.map(clean); + result.passes = passes.map(clean); + if (!output) { + return; + } + process.stdout.write(JSON.stringify(result, null, 2)); + }); +} + +/** + * Map jscoverage data to a JSON structure + * suitable for reporting. + * + * @api private + * @param {Object} cov + * @return {Object} + */ + +function map(cov) { + var ret = { + instrumentation: 'node-jscoverage', + sloc: 0, + hits: 0, + misses: 0, + coverage: 0, + files: [] + }; + + for (var filename in cov) { + if (Object.prototype.hasOwnProperty.call(cov, filename)) { + var data = coverage(filename, cov[filename]); + ret.files.push(data); + ret.hits += data.hits; + ret.misses += data.misses; + ret.sloc += data.sloc; + } + } + + ret.files.sort(function(a, b) { + return a.filename.localeCompare(b.filename); + }); + + if (ret.sloc > 0) { + ret.coverage = (ret.hits / ret.sloc) * 100; + } + + return ret; +} + +/** + * Map jscoverage data for a single source file + * to a JSON structure suitable for reporting. + * + * @api private + * @param {string} filename name of the source file + * @param {Object} data jscoverage coverage data + * @return {Object} + */ +function coverage(filename, data) { + var ret = { + filename: filename, + coverage: 0, + hits: 0, + misses: 0, + sloc: 0, + source: {} + }; + + data.source.forEach(function(line, num) { + num++; + + if (data[num] === 0) { + ret.misses++; + ret.sloc++; + } else if (data[num] !== undefined) { + ret.hits++; + ret.sloc++; + } + + ret.source[num] = { + source: line, + coverage: data[num] === undefined ? '' : data[num] + }; + }); + + ret.coverage = ret.hits / ret.sloc * 100; + + return ret; +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @api private + * @param {Object} test + * @return {Object} + */ +function clean(test) { + return { + duration: test.duration, + fullTitle: test.fullTitle(), + title: test.title + }; +} diff --git a/node_modules/mocha/lib/reporters/json-stream.js b/node_modules/mocha/lib/reporters/json-stream.js new file mode 100644 index 0000000..c8f0e11 --- /dev/null +++ b/node_modules/mocha/lib/reporters/json-stream.js @@ -0,0 +1,59 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `List`. + */ + +exports = module.exports = List; + +/** + * Initialize a new `List` test reporter. + * + * @api public + * @param {Runner} runner + */ +function List(runner) { + Base.call(this, runner); + + var self = this; + var total = runner.total; + + runner.on('start', function() { + console.log(JSON.stringify(['start', { total: total }])); + }); + + runner.on('pass', function(test) { + console.log(JSON.stringify(['pass', clean(test)])); + }); + + runner.on('fail', function(test, err) { + test = clean(test); + test.err = err.message; + test.stack = err.stack || null; + console.log(JSON.stringify(['fail', test])); + }); + + runner.on('end', function() { + process.stdout.write(JSON.stringify(['end', self.stats])); + }); +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @api private + * @param {Object} test + * @return {Object} + */ +function clean(test) { + return { + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration + }; +} diff --git a/node_modules/mocha/lib/reporters/json.js b/node_modules/mocha/lib/reporters/json.js new file mode 100644 index 0000000..35b760e --- /dev/null +++ b/node_modules/mocha/lib/reporters/json.js @@ -0,0 +1,89 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `JSON`. + */ + +exports = module.exports = JSONReporter; + +/** + * Initialize a new `JSON` reporter. + * + * @api public + * @param {Runner} runner + */ +function JSONReporter(runner) { + Base.call(this, runner); + + var self = this; + var tests = []; + var pending = []; + var failures = []; + var passes = []; + + runner.on('test end', function(test) { + tests.push(test); + }); + + runner.on('pass', function(test) { + passes.push(test); + }); + + runner.on('fail', function(test) { + failures.push(test); + }); + + runner.on('pending', function(test) { + pending.push(test); + }); + + runner.on('end', function() { + var obj = { + stats: self.stats, + tests: tests.map(clean), + pending: pending.map(clean), + failures: failures.map(clean), + passes: passes.map(clean) + }; + + runner.testResults = obj; + + process.stdout.write(JSON.stringify(obj, null, 2)); + }); +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @api private + * @param {Object} test + * @return {Object} + */ +function clean(test) { + return { + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration, + err: errorJSON(test.err || {}) + }; +} + +/** + * Transform `error` into a JSON object. + * + * @api private + * @param {Error} err + * @return {Object} + */ +function errorJSON(err) { + var res = {}; + Object.getOwnPropertyNames(err).forEach(function(key) { + res[key] = err[key]; + }, err); + return res; +} diff --git a/node_modules/mocha/lib/reporters/landing.js b/node_modules/mocha/lib/reporters/landing.js new file mode 100644 index 0000000..b66b200 --- /dev/null +++ b/node_modules/mocha/lib/reporters/landing.js @@ -0,0 +1,92 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var cursor = Base.cursor; +var color = Base.color; + +/** + * Expose `Landing`. + */ + +exports = module.exports = Landing; + +/** + * Airplane color. + */ + +Base.colors.plane = 0; + +/** + * Airplane crash color. + */ + +Base.colors['plane crash'] = 31; + +/** + * Runway color. + */ + +Base.colors.runway = 90; + +/** + * Initialize a new `Landing` reporter. + * + * @api public + * @param {Runner} runner + */ +function Landing(runner) { + Base.call(this, runner); + + var self = this; + var width = Base.window.width * .75 | 0; + var total = runner.total; + var stream = process.stdout; + var plane = color('plane', '✈'); + var crashed = -1; + var n = 0; + + function runway() { + var buf = Array(width).join('-'); + return ' ' + color('runway', buf); + } + + runner.on('start', function() { + stream.write('\n\n\n '); + cursor.hide(); + }); + + runner.on('test end', function(test) { + // check if the plane crashed + var col = crashed === -1 ? width * ++n / total | 0 : crashed; + + // show the crash + if (test.state === 'failed') { + plane = color('plane crash', '✈'); + crashed = col; + } + + // render landing strip + stream.write('\u001b[' + (width + 1) + 'D\u001b[2A'); + stream.write(runway()); + stream.write('\n '); + stream.write(color('runway', Array(col).join('⋅'))); + stream.write(plane); + stream.write(color('runway', Array(width - col).join('⋅') + '\n')); + stream.write(runway()); + stream.write('\u001b[0m'); + }); + + runner.on('end', function() { + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Landing, Base); diff --git a/node_modules/mocha/lib/reporters/list.js b/node_modules/mocha/lib/reporters/list.js new file mode 100644 index 0000000..0e5f910 --- /dev/null +++ b/node_modules/mocha/lib/reporters/list.js @@ -0,0 +1,61 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var color = Base.color; +var cursor = Base.cursor; + +/** + * Expose `List`. + */ + +exports = module.exports = List; + +/** + * Initialize a new `List` test reporter. + * + * @api public + * @param {Runner} runner + */ +function List(runner) { + Base.call(this, runner); + + var self = this; + var n = 0; + + runner.on('start', function() { + console.log(); + }); + + runner.on('test', function(test) { + process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); + }); + + runner.on('pending', function(test) { + var fmt = color('checkmark', ' -') + + color('pending', ' %s'); + console.log(fmt, test.fullTitle()); + }); + + runner.on('pass', function(test) { + var fmt = color('checkmark', ' ' + Base.symbols.dot) + + color('pass', ' %s: ') + + color(test.speed, '%dms'); + cursor.CR(); + console.log(fmt, test.fullTitle(), test.duration); + }); + + runner.on('fail', function(test) { + cursor.CR(); + console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); + }); + + runner.on('end', self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(List, Base); diff --git a/node_modules/mocha/lib/reporters/markdown.js b/node_modules/mocha/lib/reporters/markdown.js new file mode 100644 index 0000000..88b1f4d --- /dev/null +++ b/node_modules/mocha/lib/reporters/markdown.js @@ -0,0 +1,97 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); + +/** + * Constants + */ + +var SUITE_PREFIX = '$'; + +/** + * Expose `Markdown`. + */ + +exports = module.exports = Markdown; + +/** + * Initialize a new `Markdown` reporter. + * + * @api public + * @param {Runner} runner + */ +function Markdown(runner) { + Base.call(this, runner); + + var level = 0; + var buf = ''; + + function title(str) { + return Array(level).join('#') + ' ' + str; + } + + function mapTOC(suite, obj) { + var ret = obj; + var key = SUITE_PREFIX + suite.title; + + obj = obj[key] = obj[key] || { suite: suite }; + suite.suites.forEach(function(suite) { + mapTOC(suite, obj); + }); + + return ret; + } + + function stringifyTOC(obj, level) { + ++level; + var buf = ''; + var link; + for (var key in obj) { + if (key === 'suite') { + continue; + } + if (key !== SUITE_PREFIX) { + link = ' - [' + key.substring(1) + ']'; + link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; + buf += Array(level).join(' ') + link; + } + buf += stringifyTOC(obj[key], level); + } + return buf; + } + + function generateTOC(suite) { + var obj = mapTOC(suite, {}); + return stringifyTOC(obj, 0); + } + + generateTOC(runner.suite); + + runner.on('suite', function(suite) { + ++level; + var slug = utils.slug(suite.fullTitle()); + buf += '' + '\n'; + buf += title(suite.title) + '\n'; + }); + + runner.on('suite end', function() { + --level; + }); + + runner.on('pass', function(test) { + var code = utils.clean(test.fn.toString()); + buf += test.title + '.\n'; + buf += '\n```js\n'; + buf += code + '\n'; + buf += '```\n\n'; + }); + + runner.on('end', function() { + process.stdout.write('# TOC\n'); + process.stdout.write(generateTOC(runner.suite)); + process.stdout.write(buf); + }); +} diff --git a/node_modules/mocha/lib/reporters/min.js b/node_modules/mocha/lib/reporters/min.js new file mode 100644 index 0000000..2b48212 --- /dev/null +++ b/node_modules/mocha/lib/reporters/min.js @@ -0,0 +1,36 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; + +/** + * Expose `Min`. + */ + +exports = module.exports = Min; + +/** + * Initialize a new `Min` minimal test reporter (best used with --watch). + * + * @api public + * @param {Runner} runner + */ +function Min(runner) { + Base.call(this, runner); + + runner.on('start', function() { + // clear screen + process.stdout.write('\u001b[2J'); + // set cursor position + process.stdout.write('\u001b[1;3H'); + }); + + runner.on('end', this.epilogue.bind(this)); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Min, Base); diff --git a/node_modules/mocha/lib/reporters/nyan.js b/node_modules/mocha/lib/reporters/nyan.js new file mode 100644 index 0000000..ba1b050 --- /dev/null +++ b/node_modules/mocha/lib/reporters/nyan.js @@ -0,0 +1,261 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; + +/** + * Expose `Dot`. + */ + +exports = module.exports = NyanCat; + +/** + * Initialize a new `Dot` matrix test reporter. + * + * @param {Runner} runner + * @api public + */ + +function NyanCat(runner) { + Base.call(this, runner); + + var self = this; + var width = Base.window.width * .75 | 0; + var nyanCatWidth = this.nyanCatWidth = 11; + + this.colorIndex = 0; + this.numberOfLines = 4; + this.rainbowColors = self.generateColors(); + this.scoreboardWidth = 5; + this.tick = 0; + this.trajectories = [[], [], [], []]; + this.trajectoryWidthMax = (width - nyanCatWidth); + + runner.on('start', function() { + Base.cursor.hide(); + self.draw(); + }); + + runner.on('pending', function() { + self.draw(); + }); + + runner.on('pass', function() { + self.draw(); + }); + + runner.on('fail', function() { + self.draw(); + }); + + runner.on('end', function() { + Base.cursor.show(); + for (var i = 0; i < self.numberOfLines; i++) { + write('\n'); + } + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(NyanCat, Base); + +/** + * Draw the nyan cat + * + * @api private + */ + +NyanCat.prototype.draw = function() { + this.appendRainbow(); + this.drawScoreboard(); + this.drawRainbow(); + this.drawNyanCat(); + this.tick = !this.tick; +}; + +/** + * Draw the "scoreboard" showing the number + * of passes, failures and pending tests. + * + * @api private + */ + +NyanCat.prototype.drawScoreboard = function() { + var stats = this.stats; + + function draw(type, n) { + write(' '); + write(Base.color(type, n)); + write('\n'); + } + + draw('green', stats.passes); + draw('fail', stats.failures); + draw('pending', stats.pending); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Append the rainbow. + * + * @api private + */ + +NyanCat.prototype.appendRainbow = function() { + var segment = this.tick ? '_' : '-'; + var rainbowified = this.rainbowify(segment); + + for (var index = 0; index < this.numberOfLines; index++) { + var trajectory = this.trajectories[index]; + if (trajectory.length >= this.trajectoryWidthMax) { + trajectory.shift(); + } + trajectory.push(rainbowified); + } +}; + +/** + * Draw the rainbow. + * + * @api private + */ + +NyanCat.prototype.drawRainbow = function() { + var self = this; + + this.trajectories.forEach(function(line) { + write('\u001b[' + self.scoreboardWidth + 'C'); + write(line.join('')); + write('\n'); + }); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw the nyan cat + * + * @api private + */ +NyanCat.prototype.drawNyanCat = function() { + var self = this; + var startWidth = this.scoreboardWidth + this.trajectories[0].length; + var dist = '\u001b[' + startWidth + 'C'; + var padding = ''; + + write(dist); + write('_,------,'); + write('\n'); + + write(dist); + padding = self.tick ? ' ' : ' '; + write('_|' + padding + '/\\_/\\ '); + write('\n'); + + write(dist); + padding = self.tick ? '_' : '__'; + var tail = self.tick ? '~' : '^'; + write(tail + '|' + padding + this.face() + ' '); + write('\n'); + + write(dist); + padding = self.tick ? ' ' : ' '; + write(padding + '"" "" '); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw nyan cat face. + * + * @api private + * @return {string} + */ + +NyanCat.prototype.face = function() { + var stats = this.stats; + if (stats.failures) { + return '( x .x)'; + } else if (stats.pending) { + return '( o .o)'; + } else if (stats.passes) { + return '( ^ .^)'; + } + return '( - .-)'; +}; + +/** + * Move cursor up `n`. + * + * @api private + * @param {number} n + */ + +NyanCat.prototype.cursorUp = function(n) { + write('\u001b[' + n + 'A'); +}; + +/** + * Move cursor down `n`. + * + * @api private + * @param {number} n + */ + +NyanCat.prototype.cursorDown = function(n) { + write('\u001b[' + n + 'B'); +}; + +/** + * Generate rainbow colors. + * + * @api private + * @return {Array} + */ +NyanCat.prototype.generateColors = function() { + var colors = []; + + for (var i = 0; i < (6 * 7); i++) { + var pi3 = Math.floor(Math.PI / 3); + var n = (i * (1.0 / 6)); + var r = Math.floor(3 * Math.sin(n) + 3); + var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); + var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); + colors.push(36 * r + 6 * g + b + 16); + } + + return colors; +}; + +/** + * Apply rainbow to the given `str`. + * + * @api private + * @param {string} str + * @return {string} + */ +NyanCat.prototype.rainbowify = function(str) { + if (!Base.useColors) { + return str; + } + var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; + this.colorIndex += 1; + return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; +}; + +/** + * Stdout helper. + * + * @param {string} string A message to write to stdout. + */ +function write(string) { + process.stdout.write(string); +} diff --git a/node_modules/mocha/lib/reporters/progress.js b/node_modules/mocha/lib/reporters/progress.js new file mode 100644 index 0000000..5349ca8 --- /dev/null +++ b/node_modules/mocha/lib/reporters/progress.js @@ -0,0 +1,89 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var color = Base.color; +var cursor = Base.cursor; + +/** + * Expose `Progress`. + */ + +exports = module.exports = Progress; + +/** + * General progress bar color. + */ + +Base.colors.progress = 90; + +/** + * Initialize a new `Progress` bar test reporter. + * + * @api public + * @param {Runner} runner + * @param {Object} options + */ +function Progress(runner, options) { + Base.call(this, runner); + + var self = this; + var width = Base.window.width * .50 | 0; + var total = runner.total; + var complete = 0; + var lastN = -1; + + // default chars + options = options || {}; + options.open = options.open || '['; + options.complete = options.complete || '▬'; + options.incomplete = options.incomplete || Base.symbols.dot; + options.close = options.close || ']'; + options.verbose = false; + + // tests started + runner.on('start', function() { + console.log(); + cursor.hide(); + }); + + // tests complete + runner.on('test end', function() { + complete++; + + var percent = complete / total; + var n = width * percent | 0; + var i = width - n; + + if (n === lastN && !options.verbose) { + // Don't re-render the line if it hasn't changed + return; + } + lastN = n; + + cursor.CR(); + process.stdout.write('\u001b[J'); + process.stdout.write(color('progress', ' ' + options.open)); + process.stdout.write(Array(n).join(options.complete)); + process.stdout.write(Array(i).join(options.incomplete)); + process.stdout.write(color('progress', options.close)); + if (options.verbose) { + process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); + } + }); + + // tests are complete, output some stats + // and the failures if any + runner.on('end', function() { + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Progress, Base); diff --git a/node_modules/mocha/lib/reporters/spec.js b/node_modules/mocha/lib/reporters/spec.js new file mode 100644 index 0000000..77a73c4 --- /dev/null +++ b/node_modules/mocha/lib/reporters/spec.js @@ -0,0 +1,83 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var color = Base.color; +var cursor = Base.cursor; + +/** + * Expose `Spec`. + */ + +exports = module.exports = Spec; + +/** + * Initialize a new `Spec` test reporter. + * + * @api public + * @param {Runner} runner + */ +function Spec(runner) { + Base.call(this, runner); + + var self = this; + var indents = 0; + var n = 0; + + function indent() { + return Array(indents).join(' '); + } + + runner.on('start', function() { + console.log(); + }); + + runner.on('suite', function(suite) { + ++indents; + console.log(color('suite', '%s%s'), indent(), suite.title); + }); + + runner.on('suite end', function() { + --indents; + if (indents === 1) { + console.log(); + } + }); + + runner.on('pending', function(test) { + var fmt = indent() + color('pending', ' - %s'); + console.log(fmt, test.title); + }); + + runner.on('pass', function(test) { + var fmt; + if (test.speed === 'fast') { + fmt = indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s'); + cursor.CR(); + console.log(fmt, test.title); + } else { + fmt = indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s') + + color(test.speed, ' (%dms)'); + cursor.CR(); + console.log(fmt, test.title, test.duration); + } + }); + + runner.on('fail', function(test) { + cursor.CR(); + console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); + }); + + runner.on('end', self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Spec, Base); diff --git a/node_modules/mocha/lib/reporters/tap.js b/node_modules/mocha/lib/reporters/tap.js new file mode 100644 index 0000000..d9b1b95 --- /dev/null +++ b/node_modules/mocha/lib/reporters/tap.js @@ -0,0 +1,68 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `TAP`. + */ + +exports = module.exports = TAP; + +/** + * Initialize a new `TAP` reporter. + * + * @api public + * @param {Runner} runner + */ +function TAP(runner) { + Base.call(this, runner); + + var n = 1; + var passes = 0; + var failures = 0; + + runner.on('start', function() { + var total = runner.grepTotal(runner.suite); + console.log('%d..%d', 1, total); + }); + + runner.on('test end', function() { + ++n; + }); + + runner.on('pending', function(test) { + console.log('ok %d %s # SKIP -', n, title(test)); + }); + + runner.on('pass', function(test) { + passes++; + console.log('ok %d %s', n, title(test)); + }); + + runner.on('fail', function(test, err) { + failures++; + console.log('not ok %d %s', n, title(test)); + if (err.stack) { + console.log(err.stack.replace(/^/gm, ' ')); + } + }); + + runner.on('end', function() { + console.log('# tests ' + (passes + failures)); + console.log('# pass ' + passes); + console.log('# fail ' + failures); + }); +} + +/** + * Return a TAP-safe title of `test` + * + * @api private + * @param {Object} test + * @return {String} + */ +function title(test) { + return test.fullTitle().replace(/#/g, ''); +} diff --git a/node_modules/mocha/lib/reporters/templates/coverage.jade b/node_modules/mocha/lib/reporters/templates/coverage.jade new file mode 100644 index 0000000..edd59d8 --- /dev/null +++ b/node_modules/mocha/lib/reporters/templates/coverage.jade @@ -0,0 +1,51 @@ +doctype html +html + head + title Coverage + meta(charset='utf-8') + include script.html + include style.html + body + #coverage + h1#overview Coverage + include menu + + #stats(class=coverageClass(cov.coverage)) + .percentage #{cov.coverage | 0}% + .sloc= cov.sloc + .hits= cov.hits + .misses= cov.misses + + #files + for file in cov.files + .file + h2(id=file.filename)= file.filename + #stats(class=coverageClass(file.coverage)) + .percentage #{file.coverage | 0}% + .sloc= file.sloc + .hits= file.hits + .misses= file.misses + + table#source + thead + tr + th Line + th Hits + th Source + tbody + for line, number in file.source + if line.coverage > 0 + tr.hit + td.line= number + td.hits= line.coverage + td.source= line.source + else if 0 === line.coverage + tr.miss + td.line= number + td.hits 0 + td.source= line.source + else + tr + td.line= number + td.hits + td.source= line.source || ' ' diff --git a/node_modules/mocha/lib/reporters/templates/menu.jade b/node_modules/mocha/lib/reporters/templates/menu.jade new file mode 100644 index 0000000..c682e3f --- /dev/null +++ b/node_modules/mocha/lib/reporters/templates/menu.jade @@ -0,0 +1,13 @@ +#menu + li + a(href='#overview') overview + for file in cov.files + li + span.cov(class=coverageClass(file.coverage)) #{file.coverage | 0} + a(href='##{file.filename}') + segments = file.filename.split('/') + basename = segments.pop() + if segments.length + span.dirname= segments.join('/') + '/' + span.basename= basename + a#logo(href='http://mochajs.org/') m diff --git a/node_modules/mocha/lib/reporters/templates/script.html b/node_modules/mocha/lib/reporters/templates/script.html new file mode 100644 index 0000000..073cf79 --- /dev/null +++ b/node_modules/mocha/lib/reporters/templates/script.html @@ -0,0 +1,34 @@ + diff --git a/node_modules/mocha/lib/reporters/templates/style.html b/node_modules/mocha/lib/reporters/templates/style.html new file mode 100644 index 0000000..4c9c37c --- /dev/null +++ b/node_modules/mocha/lib/reporters/templates/style.html @@ -0,0 +1,324 @@ + diff --git a/node_modules/mocha/lib/reporters/xunit.js b/node_modules/mocha/lib/reporters/xunit.js new file mode 100644 index 0000000..d9f58b9 --- /dev/null +++ b/node_modules/mocha/lib/reporters/xunit.js @@ -0,0 +1,169 @@ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); +var inherits = utils.inherits; +var fs = require('fs'); +var escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +/* eslint-disable no-unused-vars, no-native-reassign */ +var Date = global.Date; +var setTimeout = global.setTimeout; +var setInterval = global.setInterval; +var clearTimeout = global.clearTimeout; +var clearInterval = global.clearInterval; +/* eslint-enable no-unused-vars, no-native-reassign */ + +/** + * Expose `XUnit`. + */ + +exports = module.exports = XUnit; + +/** + * Initialize a new `XUnit` reporter. + * + * @api public + * @param {Runner} runner + */ +function XUnit(runner, options) { + Base.call(this, runner); + + var stats = this.stats; + var tests = []; + var self = this; + + if (options.reporterOptions && options.reporterOptions.output) { + if (!fs.createWriteStream) { + throw new Error('file output not supported in browser'); + } + self.fileStream = fs.createWriteStream(options.reporterOptions.output); + } + + runner.on('pending', function(test) { + tests.push(test); + }); + + runner.on('pass', function(test) { + tests.push(test); + }); + + runner.on('fail', function(test) { + tests.push(test); + }); + + runner.on('end', function() { + self.write(tag('testsuite', { + name: 'Mocha Tests', + tests: stats.tests, + failures: stats.failures, + errors: stats.failures, + skipped: stats.tests - stats.failures - stats.passes, + timestamp: (new Date()).toUTCString(), + time: (stats.duration / 1000) || 0 + }, false)); + + tests.forEach(function(t) { + self.test(t); + }); + + self.write(''); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(XUnit, Base); + +/** + * Override done to close the stream (if it's a file). + * + * @param failures + * @param {Function} fn + */ +XUnit.prototype.done = function(failures, fn) { + if (this.fileStream) { + this.fileStream.end(function() { + fn(failures); + }); + } else { + fn(failures); + } +}; + +/** + * Write out the given line. + * + * @param {string} line + */ +XUnit.prototype.write = function(line) { + if (this.fileStream) { + this.fileStream.write(line + '\n'); + } else { + console.log(line); + } +}; + +/** + * Output tag for the given `test.` + * + * @param {Test} test + */ +XUnit.prototype.test = function(test) { + var attrs = { + classname: test.parent.fullTitle(), + name: test.title, + time: (test.duration / 1000) || 0 + }; + + if (test.state === 'failed') { + var err = test.err; + this.write(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + '\n' + err.stack)))); + } else if (test.pending) { + this.write(tag('testcase', attrs, false, tag('skipped', {}, true))); + } else { + this.write(tag('testcase', attrs, true)); + } +}; + +/** + * HTML tag helper. + * + * @param name + * @param attrs + * @param close + * @param content + * @return {string} + */ +function tag(name, attrs, close, content) { + var end = close ? '/>' : '>'; + var pairs = []; + var tag; + + for (var key in attrs) { + if (Object.prototype.hasOwnProperty.call(attrs, key)) { + pairs.push(key + '="' + escape(attrs[key]) + '"'); + } + } + + tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; + if (content) { + tag += content + ''; +} diff --git a/node_modules/mocha/lib/runnable.js b/node_modules/mocha/lib/runnable.js new file mode 100644 index 0000000..0750178 --- /dev/null +++ b/node_modules/mocha/lib/runnable.js @@ -0,0 +1,320 @@ +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter; +var Pending = require('./pending'); +var debug = require('debug')('mocha:runnable'); +var milliseconds = require('./ms'); +var utils = require('./utils'); +var inherits = utils.inherits; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +/* eslint-disable no-unused-vars, no-native-reassign */ +var Date = global.Date; +var setTimeout = global.setTimeout; +var setInterval = global.setInterval; +var clearTimeout = global.clearTimeout; +var clearInterval = global.clearInterval; +/* eslint-enable no-unused-vars, no-native-reassign */ + +/** + * Object#toString(). + */ + +var toString = Object.prototype.toString; + +/** + * Expose `Runnable`. + */ + +module.exports = Runnable; + +/** + * Initialize a new `Runnable` with the given `title` and callback `fn`. + * + * @param {String} title + * @param {Function} fn + * @api private + * @param {string} title + * @param {Function} fn + */ +function Runnable(title, fn) { + this.title = title; + this.fn = fn; + this.async = fn && fn.length; + this.sync = !this.async; + this._timeout = 2000; + this._slow = 75; + this._enableTimeouts = true; + this.timedOut = false; + this._trace = new Error('done() called multiple times'); +} + +/** + * Inherit from `EventEmitter.prototype`. + */ +inherits(Runnable, EventEmitter); + +/** + * Set & get timeout `ms`. + * + * @api private + * @param {number|string} ms + * @return {Runnable|number} ms or Runnable instance. + */ +Runnable.prototype.timeout = function(ms) { + if (!arguments.length) { + return this._timeout; + } + if (ms === 0) { + this._enableTimeouts = false; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('timeout %d', ms); + this._timeout = ms; + if (this.timer) { + this.resetTimeout(); + } + return this; +}; + +/** + * Set & get slow `ms`. + * + * @api private + * @param {number|string} ms + * @return {Runnable|number} ms or Runnable instance. + */ +Runnable.prototype.slow = function(ms) { + if (!arguments.length) { + return this._slow; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('timeout %d', ms); + this._slow = ms; + return this; +}; + +/** + * Set and get whether timeout is `enabled`. + * + * @api private + * @param {boolean} enabled + * @return {Runnable|boolean} enabled or Runnable instance. + */ +Runnable.prototype.enableTimeouts = function(enabled) { + if (!arguments.length) { + return this._enableTimeouts; + } + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Halt and mark as pending. + * + * @api private + */ +Runnable.prototype.skip = function() { + throw new Pending(); +}; + +/** + * Return the full title generated by recursively concatenating the parent's + * full title. + * + * @api public + * @return {string} + */ +Runnable.prototype.fullTitle = function() { + return this.parent.fullTitle() + ' ' + this.title; +}; + +/** + * Clear the timeout. + * + * @api private + */ +Runnable.prototype.clearTimeout = function() { + clearTimeout(this.timer); +}; + +/** + * Inspect the runnable void of private properties. + * + * @api private + * @return {string} + */ +Runnable.prototype.inspect = function() { + return JSON.stringify(this, function(key, val) { + if (key[0] === '_') { + return; + } + if (key === 'parent') { + return '#'; + } + if (key === 'ctx') { + return '#'; + } + return val; + }, 2); +}; + +/** + * Reset the timeout. + * + * @api private + */ +Runnable.prototype.resetTimeout = function() { + var self = this; + var ms = this.timeout() || 1e9; + + if (!this._enableTimeouts) { + return; + } + this.clearTimeout(); + this.timer = setTimeout(function() { + if (!self._enableTimeouts) { + return; + } + self.callback(new Error('timeout of ' + ms + 'ms exceeded. Ensure the done() callback is being called in this test.')); + self.timedOut = true; + }, ms); +}; + +/** + * Whitelist a list of globals for this test run. + * + * @api private + * @param {string[]} globals + */ +Runnable.prototype.globals = function(globals) { + this._allowedGlobals = globals; +}; + +/** + * Run the test and invoke `fn(err)`. + * + * @param {Function} fn + * @api private + */ +Runnable.prototype.run = function(fn) { + var self = this; + var start = new Date(); + var ctx = this.ctx; + var finished; + var emitted; + + // Sometimes the ctx exists, but it is not runnable + if (ctx && ctx.runnable) { + ctx.runnable(this); + } + + // called multiple times + function multiple(err) { + if (emitted) { + return; + } + emitted = true; + self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate')); + } + + // finished + function done(err) { + var ms = self.timeout(); + if (self.timedOut) { + return; + } + if (finished) { + return multiple(err || self._trace); + } + + self.clearTimeout(); + self.duration = new Date() - start; + finished = true; + if (!err && self.duration > ms && self._enableTimeouts) { + err = new Error('timeout of ' + ms + 'ms exceeded. Ensure the done() callback is being called in this test.'); + } + fn(err); + } + + // for .resetTimeout() + this.callback = done; + + // explicit async with `done` argument + if (this.async) { + this.resetTimeout(); + + if (this.allowUncaught) { + return callFnAsync(this.fn); + } + try { + callFnAsync(this.fn); + } catch (err) { + done(utils.getError(err)); + } + return; + } + + if (this.allowUncaught) { + callFn(this.fn); + done(); + return; + } + + // sync or promise-returning + try { + if (this.pending) { + done(); + } else { + callFn(this.fn); + } + } catch (err) { + done(utils.getError(err)); + } + + function callFn(fn) { + var result = fn.call(ctx); + if (result && typeof result.then === 'function') { + self.resetTimeout(); + result + .then(function() { + done(); + }, + function(reason) { + done(reason || new Error('Promise rejected with no or falsy reason')); + }); + } else { + if (self.asyncOnly) { + return done(new Error('--async-only option in use without declaring `done()` or returning a promise')); + } + + done(); + } + } + + function callFnAsync(fn) { + fn.call(ctx, function(err) { + if (err instanceof Error || toString.call(err) === '[object Error]') { + return done(err); + } + if (err) { + if (Object.prototype.toString.call(err) === '[object Object]') { + return done(new Error('done() invoked with non-Error: ' + + JSON.stringify(err))); + } + return done(new Error('done() invoked with non-Error: ' + err)); + } + done(); + }); + } +}; diff --git a/node_modules/mocha/lib/runner.js b/node_modules/mocha/lib/runner.js new file mode 100644 index 0000000..d7656cd --- /dev/null +++ b/node_modules/mocha/lib/runner.js @@ -0,0 +1,840 @@ +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter; +var Pending = require('./pending'); +var utils = require('./utils'); +var inherits = utils.inherits; +var debug = require('debug')('mocha:runner'); +var Runnable = require('./runnable'); +var filter = utils.filter; +var indexOf = utils.indexOf; +var keys = utils.keys; +var stackFilter = utils.stackTraceFilter(); +var stringify = utils.stringify; +var type = utils.type; +var undefinedError = utils.undefinedError; + +/** + * Non-enumerable globals. + */ + +var globals = [ + 'setTimeout', + 'clearTimeout', + 'setInterval', + 'clearInterval', + 'XMLHttpRequest', + 'Date', + 'setImmediate', + 'clearImmediate' +]; + +/** + * Expose `Runner`. + */ + +module.exports = Runner; + +/** + * Initialize a `Runner` for the given `suite`. + * + * Events: + * + * - `start` execution started + * - `end` execution complete + * - `suite` (suite) test suite execution started + * - `suite end` (suite) all tests (and sub-suites) have finished + * - `test` (test) test execution started + * - `test end` (test) test completed + * - `hook` (hook) hook execution started + * - `hook end` (hook) hook complete + * - `pass` (test) test passed + * - `fail` (test, err) test failed + * - `pending` (test) test pending + * + * @api public + * @param {Suite} suite Root suite + * @param {boolean} [delay] Whether or not to delay execution of root suite + * until ready. + */ +function Runner(suite, delay) { + var self = this; + this._globals = []; + this._abort = false; + this._delay = delay; + this.suite = suite; + this.started = false; + this.total = suite.total(); + this.failures = 0; + this.on('test end', function(test) { + self.checkGlobals(test); + }); + this.on('hook end', function(hook) { + self.checkGlobals(hook); + }); + this._defaultGrep = /.*/; + this.grep(this._defaultGrep); + this.globals(this.globalProps().concat(extraGlobals())); +} + +/** + * Wrapper for setImmediate, process.nextTick, or browser polyfill. + * + * @param {Function} fn + * @api private + */ +Runner.immediately = global.setImmediate || process.nextTick; + +/** + * Inherit from `EventEmitter.prototype`. + */ +inherits(Runner, EventEmitter); + +/** + * Run tests with full titles matching `re`. Updates runner.total + * with number of tests matched. + * + * @param {RegExp} re + * @param {Boolean} invert + * @return {Runner} for chaining + * @api public + * @param {RegExp} re + * @param {boolean} invert + * @return {Runner} Runner instance. + */ +Runner.prototype.grep = function(re, invert) { + debug('grep %s', re); + this._grep = re; + this._invert = invert; + this.total = this.grepTotal(this.suite); + return this; +}; + +/** + * Returns the number of tests matching the grep search for the + * given suite. + * + * @param {Suite} suite + * @return {Number} + * @api public + * @param {Suite} suite + * @return {number} + */ +Runner.prototype.grepTotal = function(suite) { + var self = this; + var total = 0; + + suite.eachTest(function(test) { + var match = self._grep.test(test.fullTitle()); + if (self._invert) { + match = !match; + } + if (match) { + total++; + } + }); + + return total; +}; + +/** + * Return a list of global properties. + * + * @return {Array} + * @api private + */ +Runner.prototype.globalProps = function() { + var props = keys(global); + + // non-enumerables + for (var i = 0; i < globals.length; ++i) { + if (~indexOf(props, globals[i])) { + continue; + } + props.push(globals[i]); + } + + return props; +}; + +/** + * Allow the given `arr` of globals. + * + * @param {Array} arr + * @return {Runner} for chaining + * @api public + * @param {Array} arr + * @return {Runner} Runner instance. + */ +Runner.prototype.globals = function(arr) { + if (!arguments.length) { + return this._globals; + } + debug('globals %j', arr); + this._globals = this._globals.concat(arr); + return this; +}; + +/** + * Check for global variable leaks. + * + * @api private + */ +Runner.prototype.checkGlobals = function(test) { + if (this.ignoreLeaks) { + return; + } + var ok = this._globals; + + var globals = this.globalProps(); + var leaks; + + if (test) { + ok = ok.concat(test._allowedGlobals || []); + } + + if (this.prevGlobalsLength === globals.length) { + return; + } + this.prevGlobalsLength = globals.length; + + leaks = filterLeaks(ok, globals); + this._globals = this._globals.concat(leaks); + + if (leaks.length > 1) { + this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + '')); + } else if (leaks.length) { + this.fail(test, new Error('global leak detected: ' + leaks[0])); + } +}; + +/** + * Fail the given `test`. + * + * @api private + * @param {Test} test + * @param {Error} err + */ +Runner.prototype.fail = function(test, err) { + ++this.failures; + test.state = 'failed'; + + if (!(err instanceof Error || err && typeof err.message === 'string')) { + err = new Error('the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)'); + } + + err.stack = (this.fullStackTrace || !err.stack) + ? err.stack + : stackFilter(err.stack); + + this.emit('fail', test, err); +}; + +/** + * Fail the given `hook` with `err`. + * + * Hook failures work in the following pattern: + * - If bail, then exit + * - Failed `before` hook skips all tests in a suite and subsuites, + * but jumps to corresponding `after` hook + * - Failed `before each` hook skips remaining tests in a + * suite and jumps to corresponding `after each` hook, + * which is run only once + * - Failed `after` hook does not alter + * execution order + * - Failed `after each` hook skips remaining tests in a + * suite and subsuites, but executes other `after each` + * hooks + * + * @api private + * @param {Hook} hook + * @param {Error} err + */ +Runner.prototype.failHook = function(hook, err) { + if (hook.ctx && hook.ctx.currentTest) { + hook.originalTitle = hook.originalTitle || hook.title; + hook.title = hook.originalTitle + ' for "' + hook.ctx.currentTest.title + '"'; + } + + this.fail(hook, err); + if (this.suite.bail()) { + this.emit('end'); + } +}; + +/** + * Run hook `name` callbacks and then invoke `fn()`. + * + * @api private + * @param {string} name + * @param {Function} fn + */ + +Runner.prototype.hook = function(name, fn) { + var suite = this.suite; + var hooks = suite['_' + name]; + var self = this; + + function next(i) { + var hook = hooks[i]; + if (!hook) { + return fn(); + } + self.currentRunnable = hook; + + hook.ctx.currentTest = self.test; + + self.emit('hook', hook); + + if (!hook.listeners('error').length) { + hook.on('error', function(err) { + self.failHook(hook, err); + }); + } + + hook.run(function(err) { + var testError = hook.error(); + if (testError) { + self.fail(self.test, testError); + } + if (err) { + if (err instanceof Pending) { + suite.pending = true; + } else { + self.failHook(hook, err); + + // stop executing hooks, notify callee of hook err + return fn(err); + } + } + self.emit('hook end', hook); + delete hook.ctx.currentTest; + next(++i); + }); + } + + Runner.immediately(function() { + next(0); + }); +}; + +/** + * Run hook `name` for the given array of `suites` + * in order, and callback `fn(err, errSuite)`. + * + * @api private + * @param {string} name + * @param {Array} suites + * @param {Function} fn + */ +Runner.prototype.hooks = function(name, suites, fn) { + var self = this; + var orig = this.suite; + + function next(suite) { + self.suite = suite; + + if (!suite) { + self.suite = orig; + return fn(); + } + + self.hook(name, function(err) { + if (err) { + var errSuite = self.suite; + self.suite = orig; + return fn(err, errSuite); + } + + next(suites.pop()); + }); + } + + next(suites.pop()); +}; + +/** + * Run hooks from the top level down. + * + * @param {String} name + * @param {Function} fn + * @api private + */ +Runner.prototype.hookUp = function(name, fn) { + var suites = [this.suite].concat(this.parents()).reverse(); + this.hooks(name, suites, fn); +}; + +/** + * Run hooks from the bottom up. + * + * @param {String} name + * @param {Function} fn + * @api private + */ +Runner.prototype.hookDown = function(name, fn) { + var suites = [this.suite].concat(this.parents()); + this.hooks(name, suites, fn); +}; + +/** + * Return an array of parent Suites from + * closest to furthest. + * + * @return {Array} + * @api private + */ +Runner.prototype.parents = function() { + var suite = this.suite; + var suites = []; + while (suite.parent) { + suite = suite.parent; + suites.push(suite); + } + return suites; +}; + +/** + * Run the current test and callback `fn(err)`. + * + * @param {Function} fn + * @api private + */ +Runner.prototype.runTest = function(fn) { + var self = this; + var test = this.test; + + if (this.asyncOnly) { + test.asyncOnly = true; + } + + if (this.allowUncaught) { + test.allowUncaught = true; + return test.run(fn); + } + try { + test.on('error', function(err) { + self.fail(test, err); + }); + test.run(fn); + } catch (err) { + fn(err); + } +}; + +/** + * Run tests in the given `suite` and invoke the callback `fn()` when complete. + * + * @api private + * @param {Suite} suite + * @param {Function} fn + */ +Runner.prototype.runTests = function(suite, fn) { + var self = this; + var tests = suite.tests.slice(); + var test; + + function hookErr(_, errSuite, after) { + // before/after Each hook for errSuite failed: + var orig = self.suite; + + // for failed 'after each' hook start from errSuite parent, + // otherwise start from errSuite itself + self.suite = after ? errSuite.parent : errSuite; + + if (self.suite) { + // call hookUp afterEach + self.hookUp('afterEach', function(err2, errSuite2) { + self.suite = orig; + // some hooks may fail even now + if (err2) { + return hookErr(err2, errSuite2, true); + } + // report error suite + fn(errSuite); + }); + } else { + // there is no need calling other 'after each' hooks + self.suite = orig; + fn(errSuite); + } + } + + function next(err, errSuite) { + // if we bail after first err + if (self.failures && suite._bail) { + return fn(); + } + + if (self._abort) { + return fn(); + } + + if (err) { + return hookErr(err, errSuite, true); + } + + // next test + test = tests.shift(); + + // all done + if (!test) { + return fn(); + } + + // grep + var match = self._grep.test(test.fullTitle()); + if (self._invert) { + match = !match; + } + if (!match) { + // Run immediately only if we have defined a grep. When we + // define a grep — It can cause maximum callstack error if + // the grep is doing a large recursive loop by neglecting + // all tests. The run immediately function also comes with + // a performance cost. So we don't want to run immediately + // if we run the whole test suite, because running the whole + // test suite don't do any immediate recursive loops. Thus, + // allowing a JS runtime to breathe. + if (self._grep !== self._defaultGrep) { + Runner.immediately(next); + } else { + next(); + } + return; + } + + // pending + if (test.pending) { + self.emit('pending', test); + self.emit('test end', test); + return next(); + } + + // execute test and hook(s) + self.emit('test', self.test = test); + self.hookDown('beforeEach', function(err, errSuite) { + if (suite.pending) { + self.emit('pending', test); + self.emit('test end', test); + return next(); + } + if (err) { + return hookErr(err, errSuite, false); + } + self.currentRunnable = self.test; + self.runTest(function(err) { + test = self.test; + + if (err) { + if (err instanceof Pending) { + self.emit('pending', test); + } else { + self.fail(test, err); + } + self.emit('test end', test); + + if (err instanceof Pending) { + return next(); + } + + return self.hookUp('afterEach', next); + } + + test.state = 'passed'; + self.emit('pass', test); + self.emit('test end', test); + self.hookUp('afterEach', next); + }); + }); + } + + this.next = next; + this.hookErr = hookErr; + next(); +}; + +/** + * Run the given `suite` and invoke the callback `fn()` when complete. + * + * @api private + * @param {Suite} suite + * @param {Function} fn + */ +Runner.prototype.runSuite = function(suite, fn) { + var i = 0; + var self = this; + var total = this.grepTotal(suite); + var afterAllHookCalled = false; + + debug('run suite %s', suite.fullTitle()); + + if (!total || (self.failures && suite._bail)) { + return fn(); + } + + this.emit('suite', this.suite = suite); + + function next(errSuite) { + if (errSuite) { + // current suite failed on a hook from errSuite + if (errSuite === suite) { + // if errSuite is current suite + // continue to the next sibling suite + return done(); + } + // errSuite is among the parents of current suite + // stop execution of errSuite and all sub-suites + return done(errSuite); + } + + if (self._abort) { + return done(); + } + + var curr = suite.suites[i++]; + if (!curr) { + return done(); + } + + // Avoid grep neglecting large number of tests causing a + // huge recursive loop and thus a maximum call stack error. + // See comment in `this.runTests()` for more information. + if (self._grep !== self._defaultGrep) { + Runner.immediately(function() { + self.runSuite(curr, next); + }); + } else { + self.runSuite(curr, next); + } + } + + function done(errSuite) { + self.suite = suite; + self.nextSuite = next; + + if (afterAllHookCalled) { + fn(errSuite); + } else { + // mark that the afterAll block has been called once + // and so can be skipped if there is an error in it. + afterAllHookCalled = true; + self.hook('afterAll', function() { + self.emit('suite end', suite); + fn(errSuite); + }); + } + } + + this.nextSuite = next; + + this.hook('beforeAll', function(err) { + if (err) { + return done(); + } + self.runTests(suite, next); + }); +}; + +/** + * Handle uncaught exceptions. + * + * @param {Error} err + * @api private + */ +Runner.prototype.uncaught = function(err) { + if (err) { + debug('uncaught exception %s', err !== function() { + return this; + }.call(err) ? err : (err.message || err)); + } else { + debug('uncaught undefined exception'); + err = undefinedError(); + } + err.uncaught = true; + + var runnable = this.currentRunnable; + + if (!runnable) { + runnable = new Runnable('Uncaught error outside test suite'); + runnable.parent = this.suite; + + if (this.started) { + this.fail(runnable, err); + } else { + // Can't recover from this failure + this.emit('start'); + this.fail(runnable, err); + this.emit('end'); + } + + return; + } + + runnable.clearTimeout(); + + // Ignore errors if complete + if (runnable.state) { + return; + } + this.fail(runnable, err); + + // recover from test + if (runnable.type === 'test') { + this.emit('test end', runnable); + this.hookUp('afterEach', this.next); + return; + } + + // recover from hooks + if (runnable.type === 'hook') { + var errSuite = this.suite; + // if hook failure is in afterEach block + if (runnable.fullTitle().indexOf('after each') > -1) { + return this.hookErr(err, errSuite, true); + } + // if hook failure is in beforeEach block + if (runnable.fullTitle().indexOf('before each') > -1) { + return this.hookErr(err, errSuite, false); + } + // if hook failure is in after or before blocks + return this.nextSuite(errSuite); + } + + // bail + this.emit('end'); +}; + +/** + * Run the root suite and invoke `fn(failures)` + * on completion. + * + * @param {Function} fn + * @return {Runner} for chaining + * @api public + * @param {Function} fn + * @return {Runner} Runner instance. + */ +Runner.prototype.run = function(fn) { + var self = this; + var rootSuite = this.suite; + + fn = fn || function() {}; + + function uncaught(err) { + self.uncaught(err); + } + + function start() { + self.started = true; + self.emit('start'); + self.runSuite(rootSuite, function() { + debug('finished running'); + self.emit('end'); + }); + } + + debug('start'); + + // callback + this.on('end', function() { + debug('end'); + process.removeListener('uncaughtException', uncaught); + fn(self.failures); + }); + + // uncaught exception + process.on('uncaughtException', uncaught); + + if (this._delay) { + // for reporters, I guess. + // might be nice to debounce some dots while we wait. + this.emit('waiting', rootSuite); + rootSuite.once('run', start); + } else { + start(); + } + + return this; +}; + +/** + * Cleanly abort execution. + * + * @api public + * @return {Runner} Runner instance. + */ +Runner.prototype.abort = function() { + debug('aborting'); + this._abort = true; + + return this; +}; + +/** + * Filter leaks with the given globals flagged as `ok`. + * + * @api private + * @param {Array} ok + * @param {Array} globals + * @return {Array} + */ +function filterLeaks(ok, globals) { + return filter(globals, function(key) { + // Firefox and Chrome exposes iframes as index inside the window object + if (/^d+/.test(key)) { + return false; + } + + // in firefox + // if runner runs in an iframe, this iframe's window.getInterface method not init at first + // it is assigned in some seconds + if (global.navigator && (/^getInterface/).test(key)) { + return false; + } + + // an iframe could be approached by window[iframeIndex] + // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak + if (global.navigator && (/^\d+/).test(key)) { + return false; + } + + // Opera and IE expose global variables for HTML element IDs (issue #243) + if (/^mocha-/.test(key)) { + return false; + } + + var matched = filter(ok, function(ok) { + if (~ok.indexOf('*')) { + return key.indexOf(ok.split('*')[0]) === 0; + } + return key === ok; + }); + return !matched.length && (!global.navigator || key !== 'onerror'); + }); +} + +/** + * Array of globals dependent on the environment. + * + * @return {Array} + * @api private + */ +function extraGlobals() { + if (typeof process === 'object' && typeof process.version === 'string') { + var parts = process.version.split('.'); + var nodeVersion = utils.reduce(parts, function(a, v) { + return a << 8 | v; + }); + + // 'errno' was renamed to process._errno in v0.9.11. + + if (nodeVersion < 0x00090B) { + return ['errno']; + } + } + + return []; +} diff --git a/node_modules/mocha/lib/suite.js b/node_modules/mocha/lib/suite.js new file mode 100644 index 0000000..7834e28 --- /dev/null +++ b/node_modules/mocha/lib/suite.js @@ -0,0 +1,365 @@ +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter; +var Hook = require('./hook'); +var utils = require('./utils'); +var inherits = utils.inherits; +var debug = require('debug')('mocha:suite'); +var milliseconds = require('./ms'); + +/** + * Expose `Suite`. + */ + +exports = module.exports = Suite; + +/** + * Create a new `Suite` with the given `title` and parent `Suite`. When a suite + * with the same title is already present, that suite is returned to provide + * nicer reporter and more flexible meta-testing. + * + * @api public + * @param {Suite} parent + * @param {string} title + * @return {Suite} + */ +exports.create = function(parent, title) { + var suite = new Suite(title, parent.ctx); + suite.parent = parent; + if (parent.pending) { + suite.pending = true; + } + title = suite.fullTitle(); + parent.addSuite(suite); + return suite; +}; + +/** + * Initialize a new `Suite` with the given `title` and `ctx`. + * + * @api private + * @param {string} title + * @param {Context} parentContext + */ +function Suite(title, parentContext) { + this.title = title; + function Context() {} + Context.prototype = parentContext; + this.ctx = new Context(); + this.suites = []; + this.tests = []; + this.pending = false; + this._beforeEach = []; + this._beforeAll = []; + this._afterEach = []; + this._afterAll = []; + this.root = !title; + this._timeout = 2000; + this._enableTimeouts = true; + this._slow = 75; + this._bail = false; + this.delayed = false; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ +inherits(Suite, EventEmitter); + +/** + * Return a clone of this `Suite`. + * + * @api private + * @return {Suite} + */ +Suite.prototype.clone = function() { + var suite = new Suite(this.title); + debug('clone'); + suite.ctx = this.ctx; + suite.timeout(this.timeout()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + return suite; +}; + +/** + * Set timeout `ms` or short-hand such as "2s". + * + * @api private + * @param {number|string} ms + * @return {Suite|number} for chaining + */ +Suite.prototype.timeout = function(ms) { + if (!arguments.length) { + return this._timeout; + } + if (ms.toString() === '0') { + this._enableTimeouts = false; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('timeout %d', ms); + this._timeout = parseInt(ms, 10); + return this; +}; + +/** + * Set timeout to `enabled`. + * + * @api private + * @param {boolean} enabled + * @return {Suite|boolean} self or enabled + */ +Suite.prototype.enableTimeouts = function(enabled) { + if (!arguments.length) { + return this._enableTimeouts; + } + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Set slow `ms` or short-hand such as "2s". + * + * @api private + * @param {number|string} ms + * @return {Suite|number} for chaining + */ +Suite.prototype.slow = function(ms) { + if (!arguments.length) { + return this._slow; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('slow %d', ms); + this._slow = ms; + return this; +}; + +/** + * Sets whether to bail after first error. + * + * @api private + * @param {boolean} bail + * @return {Suite|number} for chaining + */ +Suite.prototype.bail = function(bail) { + if (!arguments.length) { + return this._bail; + } + debug('bail %s', bail); + this._bail = bail; + return this; +}; + +/** + * Run `fn(test[, done])` before running tests. + * + * @api private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.beforeAll = function(title, fn) { + if (this.pending) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"before all" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._beforeAll.push(hook); + this.emit('beforeAll', hook); + return this; +}; + +/** + * Run `fn(test[, done])` after running tests. + * + * @api private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.afterAll = function(title, fn) { + if (this.pending) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"after all" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._afterAll.push(hook); + this.emit('afterAll', hook); + return this; +}; + +/** + * Run `fn(test[, done])` before each test case. + * + * @api private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.beforeEach = function(title, fn) { + if (this.pending) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"before each" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._beforeEach.push(hook); + this.emit('beforeEach', hook); + return this; +}; + +/** + * Run `fn(test[, done])` after each test case. + * + * @api private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.afterEach = function(title, fn) { + if (this.pending) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"after each" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._afterEach.push(hook); + this.emit('afterEach', hook); + return this; +}; + +/** + * Add a test `suite`. + * + * @api private + * @param {Suite} suite + * @return {Suite} for chaining + */ +Suite.prototype.addSuite = function(suite) { + suite.parent = this; + suite.timeout(this.timeout()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + this.suites.push(suite); + this.emit('suite', suite); + return this; +}; + +/** + * Add a `test` to this suite. + * + * @api private + * @param {Test} test + * @return {Suite} for chaining + */ +Suite.prototype.addTest = function(test) { + test.parent = this; + test.timeout(this.timeout()); + test.enableTimeouts(this.enableTimeouts()); + test.slow(this.slow()); + test.ctx = this.ctx; + this.tests.push(test); + this.emit('test', test); + return this; +}; + +/** + * Return the full title generated by recursively concatenating the parent's + * full title. + * + * @api public + * @return {string} + */ +Suite.prototype.fullTitle = function() { + if (this.parent) { + var full = this.parent.fullTitle(); + if (full) { + return full + ' ' + this.title; + } + } + return this.title; +}; + +/** + * Return the total number of tests. + * + * @api public + * @return {number} + */ +Suite.prototype.total = function() { + return utils.reduce(this.suites, function(sum, suite) { + return sum + suite.total(); + }, 0) + this.tests.length; +}; + +/** + * Iterates through each suite recursively to find all tests. Applies a + * function in the format `fn(test)`. + * + * @api private + * @param {Function} fn + * @return {Suite} + */ +Suite.prototype.eachTest = function(fn) { + utils.forEach(this.tests, fn); + utils.forEach(this.suites, function(suite) { + suite.eachTest(fn); + }); + return this; +}; + +/** + * This will run the root suite if we happen to be running in delayed mode. + */ +Suite.prototype.run = function run() { + if (this.root) { + this.emit('run'); + } +}; diff --git a/node_modules/mocha/lib/template.html b/node_modules/mocha/lib/template.html new file mode 100644 index 0000000..36c5e0b --- /dev/null +++ b/node_modules/mocha/lib/template.html @@ -0,0 +1,18 @@ + + + + Mocha + + + + + +
      + + + + + + diff --git a/node_modules/mocha/lib/test.js b/node_modules/mocha/lib/test.js new file mode 100644 index 0000000..bb744e6 --- /dev/null +++ b/node_modules/mocha/lib/test.js @@ -0,0 +1,30 @@ +/** + * Module dependencies. + */ + +var Runnable = require('./runnable'); +var inherits = require('./utils').inherits; + +/** + * Expose `Test`. + */ + +module.exports = Test; + +/** + * Initialize a new `Test` with the given `title` and callback `fn`. + * + * @api private + * @param {String} title + * @param {Function} fn + */ +function Test(title, fn) { + Runnable.call(this, title, fn); + this.pending = !fn; + this.type = 'test'; +} + +/** + * Inherit from `Runnable.prototype`. + */ +inherits(Test, Runnable); diff --git a/node_modules/mocha/lib/utils.js b/node_modules/mocha/lib/utils.js new file mode 100644 index 0000000..0b97025 --- /dev/null +++ b/node_modules/mocha/lib/utils.js @@ -0,0 +1,738 @@ +/* eslint-env browser */ + +/** + * Module dependencies. + */ + +var basename = require('path').basename; +var debug = require('debug')('mocha:watch'); +var exists = require('fs').existsSync || require('path').existsSync; +var glob = require('glob'); +var join = require('path').join; +var readdirSync = require('fs').readdirSync; +var statSync = require('fs').statSync; +var watchFile = require('fs').watchFile; + +/** + * Ignored directories. + */ + +var ignore = ['node_modules', '.git']; + +exports.inherits = require('util').inherits; + +/** + * Escape special characters in the given string of html. + * + * @api private + * @param {string} html + * @return {string} + */ +exports.escape = function(html) { + return String(html) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); +}; + +/** + * Array#forEach (<=IE8) + * + * @api private + * @param {Array} arr + * @param {Function} fn + * @param {Object} scope + */ +exports.forEach = function(arr, fn, scope) { + for (var i = 0, l = arr.length; i < l; i++) { + fn.call(scope, arr[i], i); + } +}; + +/** + * Test if the given obj is type of string. + * + * @api private + * @param {Object} obj + * @return {boolean} + */ +exports.isString = function(obj) { + return typeof obj === 'string'; +}; + +/** + * Array#map (<=IE8) + * + * @api private + * @param {Array} arr + * @param {Function} fn + * @param {Object} scope + * @return {Array} + */ +exports.map = function(arr, fn, scope) { + var result = []; + for (var i = 0, l = arr.length; i < l; i++) { + result.push(fn.call(scope, arr[i], i, arr)); + } + return result; +}; + +/** + * Array#indexOf (<=IE8) + * + * @api private + * @param {Array} arr + * @param {Object} obj to find index of + * @param {number} start + * @return {number} + */ +exports.indexOf = function(arr, obj, start) { + for (var i = start || 0, l = arr.length; i < l; i++) { + if (arr[i] === obj) { + return i; + } + } + return -1; +}; + +/** + * Array#reduce (<=IE8) + * + * @api private + * @param {Array} arr + * @param {Function} fn + * @param {Object} val Initial value. + * @return {*} + */ +exports.reduce = function(arr, fn, val) { + var rval = val; + + for (var i = 0, l = arr.length; i < l; i++) { + rval = fn(rval, arr[i], i, arr); + } + + return rval; +}; + +/** + * Array#filter (<=IE8) + * + * @api private + * @param {Array} arr + * @param {Function} fn + * @return {Array} + */ +exports.filter = function(arr, fn) { + var ret = []; + + for (var i = 0, l = arr.length; i < l; i++) { + var val = arr[i]; + if (fn(val, i, arr)) { + ret.push(val); + } + } + + return ret; +}; + +/** + * Object.keys (<=IE8) + * + * @api private + * @param {Object} obj + * @return {Array} keys + */ +exports.keys = typeof Object.keys === 'function' ? Object.keys : function(obj) { + var keys = []; + var has = Object.prototype.hasOwnProperty; // for `window` on <=IE8 + + for (var key in obj) { + if (has.call(obj, key)) { + keys.push(key); + } + } + + return keys; +}; + +/** + * Watch the given `files` for changes + * and invoke `fn(file)` on modification. + * + * @api private + * @param {Array} files + * @param {Function} fn + */ +exports.watch = function(files, fn) { + var options = { interval: 100 }; + files.forEach(function(file) { + debug('file %s', file); + watchFile(file, options, function(curr, prev) { + if (prev.mtime < curr.mtime) { + fn(file); + } + }); + }); +}; + +/** + * Array.isArray (<=IE8) + * + * @api private + * @param {Object} obj + * @return {Boolean} + */ +var isArray = typeof Array.isArray === 'function' ? Array.isArray : function(obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; +}; + +/** + * Buffer.prototype.toJSON polyfill. + * + * @type {Function} + */ +if (typeof Buffer !== 'undefined' && Buffer.prototype) { + Buffer.prototype.toJSON = Buffer.prototype.toJSON || function() { + return Array.prototype.slice.call(this, 0); + }; +} + +/** + * Ignored files. + * + * @api private + * @param {string} path + * @return {boolean} + */ +function ignored(path) { + return !~ignore.indexOf(path); +} + +/** + * Lookup files in the given `dir`. + * + * @api private + * @param {string} dir + * @param {string[]} [ext=['.js']] + * @param {Array} [ret=[]] + * @return {Array} + */ +exports.files = function(dir, ext, ret) { + ret = ret || []; + ext = ext || ['js']; + + var re = new RegExp('\\.(' + ext.join('|') + ')$'); + + readdirSync(dir) + .filter(ignored) + .forEach(function(path) { + path = join(dir, path); + if (statSync(path).isDirectory()) { + exports.files(path, ext, ret); + } else if (path.match(re)) { + ret.push(path); + } + }); + + return ret; +}; + +/** + * Compute a slug from the given `str`. + * + * @api private + * @param {string} str + * @return {string} + */ +exports.slug = function(str) { + return str + .toLowerCase() + .replace(/ +/g, '-') + .replace(/[^-\w]/g, ''); +}; + +/** + * Strip the function definition from `str`, and re-indent for pre whitespace. + * + * @param {string} str + * @return {string} + */ +exports.clean = function(str) { + str = str + .replace(/\r\n?|[\n\u2028\u2029]/g, '\n').replace(/^\uFEFF/, '') + .replace(/^function *\(.*\)\s*{|\(.*\) *=> *{?/, '') + .replace(/\s+\}$/, ''); + + var spaces = str.match(/^\n?( *)/)[1].length; + var tabs = str.match(/^\n?(\t*)/)[1].length; + var re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs ? tabs : spaces) + '}', 'gm'); + + str = str.replace(re, ''); + + return exports.trim(str); +}; + +/** + * Trim the given `str`. + * + * @api private + * @param {string} str + * @return {string} + */ +exports.trim = function(str) { + return str.replace(/^\s+|\s+$/g, ''); +}; + +/** + * Parse the given `qs`. + * + * @api private + * @param {string} qs + * @return {Object} + */ +exports.parseQuery = function(qs) { + return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair) { + var i = pair.indexOf('='); + var key = pair.slice(0, i); + var val = pair.slice(++i); + + obj[key] = decodeURIComponent(val); + return obj; + }, {}); +}; + +/** + * Highlight the given string of `js`. + * + * @api private + * @param {string} js + * @return {string} + */ +function highlight(js) { + return js + .replace(//g, '>') + .replace(/\/\/(.*)/gm, '//$1') + .replace(/('.*?')/gm, '$1') + .replace(/(\d+\.\d+)/gm, '$1') + .replace(/(\d+)/gm, '$1') + .replace(/\bnew[ \t]+(\w+)/gm, 'new $1') + .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1'); +} + +/** + * Highlight the contents of tag `name`. + * + * @api private + * @param {string} name + */ +exports.highlightTags = function(name) { + var code = document.getElementById('mocha').getElementsByTagName(name); + for (var i = 0, len = code.length; i < len; ++i) { + code[i].innerHTML = highlight(code[i].innerHTML); + } +}; + +/** + * If a value could have properties, and has none, this function is called, + * which returns a string representation of the empty value. + * + * Functions w/ no properties return `'[Function]'` + * Arrays w/ length === 0 return `'[]'` + * Objects w/ no properties return `'{}'` + * All else: return result of `value.toString()` + * + * @api private + * @param {*} value The value to inspect. + * @param {string} [type] The type of the value, if known. + * @returns {string} + */ +function emptyRepresentation(value, type) { + type = type || exports.type(value); + + switch (type) { + case 'function': + return '[Function]'; + case 'object': + return '{}'; + case 'array': + return '[]'; + default: + return value.toString(); + } +} + +/** + * Takes some variable and asks `Object.prototype.toString()` what it thinks it + * is. + * + * @api private + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString + * @param {*} value The value to test. + * @returns {string} + * @example + * type({}) // 'object' + * type([]) // 'array' + * type(1) // 'number' + * type(false) // 'boolean' + * type(Infinity) // 'number' + * type(null) // 'null' + * type(new Date()) // 'date' + * type(/foo/) // 'regexp' + * type('type') // 'string' + * type(global) // 'global' + */ +exports.type = function type(value) { + if (value === undefined) { + return 'undefined'; + } else if (value === null) { + return 'null'; + } else if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) { + return 'buffer'; + } + return Object.prototype.toString.call(value) + .replace(/^\[.+\s(.+?)\]$/, '$1') + .toLowerCase(); +}; + +/** + * Stringify `value`. Different behavior depending on type of value: + * + * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively. + * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes. + * - If `value` is an *empty* object, function, or array, return result of function + * {@link emptyRepresentation}. + * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of + * JSON.stringify(). + * + * @api private + * @see exports.type + * @param {*} value + * @return {string} + */ +exports.stringify = function(value) { + var type = exports.type(value); + + if (!~exports.indexOf(['object', 'array', 'function'], type)) { + if (type !== 'buffer') { + return jsonStringify(value); + } + var json = value.toJSON(); + // Based on the toJSON result + return jsonStringify(json.data && json.type ? json.data : json, 2) + .replace(/,(\n|$)/g, '$1'); + } + + for (var prop in value) { + if (Object.prototype.hasOwnProperty.call(value, prop)) { + return jsonStringify(exports.canonicalize(value), 2).replace(/,(\n|$)/g, '$1'); + } + } + + return emptyRepresentation(value, type); +}; + +/** + * like JSON.stringify but more sense. + * + * @api private + * @param {Object} object + * @param {number=} spaces + * @param {number=} depth + * @returns {*} + */ +function jsonStringify(object, spaces, depth) { + if (typeof spaces === 'undefined') { + // primitive types + return _stringify(object); + } + + depth = depth || 1; + var space = spaces * depth; + var str = isArray(object) ? '[' : '{'; + var end = isArray(object) ? ']' : '}'; + var length = object.length || exports.keys(object).length; + // `.repeat()` polyfill + function repeat(s, n) { + return new Array(n).join(s); + } + + function _stringify(val) { + switch (exports.type(val)) { + case 'null': + case 'undefined': + val = '[' + val + ']'; + break; + case 'array': + case 'object': + val = jsonStringify(val, spaces, depth + 1); + break; + case 'boolean': + case 'regexp': + case 'number': + val = val === 0 && (1 / val) === -Infinity // `-0` + ? '-0' + : val.toString(); + break; + case 'date': + var sDate = isNaN(val.getTime()) // Invalid date + ? val.toString() + : val.toISOString(); + val = '[Date: ' + sDate + ']'; + break; + case 'buffer': + var json = val.toJSON(); + // Based on the toJSON result + json = json.data && json.type ? json.data : json; + val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']'; + break; + default: + val = (val === '[Function]' || val === '[Circular]') + ? val + : JSON.stringify(val); // string + } + return val; + } + + for (var i in object) { + if (!object.hasOwnProperty(i)) { + continue; // not my business + } + --length; + str += '\n ' + repeat(' ', space) + + (isArray(object) ? '' : '"' + i + '": ') // key + + _stringify(object[i]) // value + + (length ? ',' : ''); // comma + } + + return str + // [], {} + + (str.length !== 1 ? '\n' + repeat(' ', --space) + end : end); +} + +/** + * Test if a value is a buffer. + * + * @api private + * @param {*} value The value to test. + * @return {boolean} True if `value` is a buffer, otherwise false + */ +exports.isBuffer = function(value) { + return typeof Buffer !== 'undefined' && Buffer.isBuffer(value); +}; + +/** + * Return a new Thing that has the keys in sorted order. Recursive. + * + * If the Thing... + * - has already been seen, return string `'[Circular]'` + * - is `undefined`, return string `'[undefined]'` + * - is `null`, return value `null` + * - is some other primitive, return the value + * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method + * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again. + * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()` + * + * @api private + * @see {@link exports.stringify} + * @param {*} value Thing to inspect. May or may not have properties. + * @param {Array} [stack=[]] Stack of seen values + * @return {(Object|Array|Function|string|undefined)} + */ +exports.canonicalize = function(value, stack) { + var canonicalizedObj; + /* eslint-disable no-unused-vars */ + var prop; + /* eslint-enable no-unused-vars */ + var type = exports.type(value); + function withStack(value, fn) { + stack.push(value); + fn(); + stack.pop(); + } + + stack = stack || []; + + if (exports.indexOf(stack, value) !== -1) { + return '[Circular]'; + } + + switch (type) { + case 'undefined': + case 'buffer': + case 'null': + canonicalizedObj = value; + break; + case 'array': + withStack(value, function() { + canonicalizedObj = exports.map(value, function(item) { + return exports.canonicalize(item, stack); + }); + }); + break; + case 'function': + /* eslint-disable guard-for-in */ + for (prop in value) { + canonicalizedObj = {}; + break; + } + /* eslint-enable guard-for-in */ + if (!canonicalizedObj) { + canonicalizedObj = emptyRepresentation(value, type); + break; + } + /* falls through */ + case 'object': + canonicalizedObj = canonicalizedObj || {}; + withStack(value, function() { + exports.forEach(exports.keys(value).sort(), function(key) { + canonicalizedObj[key] = exports.canonicalize(value[key], stack); + }); + }); + break; + case 'date': + case 'number': + case 'regexp': + case 'boolean': + canonicalizedObj = value; + break; + default: + canonicalizedObj = value.toString(); + } + + return canonicalizedObj; +}; + +/** + * Lookup file names at the given `path`. + * + * @api public + * @param {string} path Base path to start searching from. + * @param {string[]} extensions File extensions to look for. + * @param {boolean} recursive Whether or not to recurse into subdirectories. + * @return {string[]} An array of paths. + */ +exports.lookupFiles = function lookupFiles(path, extensions, recursive) { + var files = []; + var re = new RegExp('\\.(' + extensions.join('|') + ')$'); + + if (!exists(path)) { + if (exists(path + '.js')) { + path += '.js'; + } else { + files = glob.sync(path); + if (!files.length) { + throw new Error("cannot resolve path (or pattern) '" + path + "'"); + } + return files; + } + } + + try { + var stat = statSync(path); + if (stat.isFile()) { + return path; + } + } catch (err) { + // ignore error + return; + } + + readdirSync(path).forEach(function(file) { + file = join(path, file); + try { + var stat = statSync(file); + if (stat.isDirectory()) { + if (recursive) { + files = files.concat(lookupFiles(file, extensions, recursive)); + } + return; + } + } catch (err) { + // ignore error + return; + } + if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') { + return; + } + files.push(file); + }); + + return files; +}; + +/** + * Generate an undefined error with a message warning the user. + * + * @return {Error} + */ + +exports.undefinedError = function() { + return new Error('Caught undefined error, did you throw without specifying what?'); +}; + +/** + * Generate an undefined error if `err` is not defined. + * + * @param {Error} err + * @return {Error} + */ + +exports.getError = function(err) { + return err || exports.undefinedError(); +}; + +/** + * @summary + * This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`) + * @description + * When invoking this function you get a filter function that get the Error.stack as an input, + * and return a prettify output. + * (i.e: strip Mocha and internal node functions from stack trace). + * @returns {Function} + */ +exports.stackTraceFilter = function() { + // TODO: Replace with `process.browser` + var slash = '/'; + var is = typeof document === 'undefined' ? { node: true } : { browser: true }; + var cwd = is.node + ? process.cwd() + slash + : (typeof location === 'undefined' ? window.location : location).href.replace(/\/[^\/]*$/, '/'); + + function isMochaInternal(line) { + return (~line.indexOf('node_modules' + slash + 'mocha' + slash)) + || (~line.indexOf('components' + slash + 'mochajs' + slash)) + || (~line.indexOf('components' + slash + 'mocha' + slash)) + || (~line.indexOf(slash + 'mocha.js')); + } + + function isNodeInternal(line) { + return (~line.indexOf('(timers.js:')) + || (~line.indexOf('(events.js:')) + || (~line.indexOf('(node.js:')) + || (~line.indexOf('(module.js:')) + || (~line.indexOf('GeneratorFunctionPrototype.next (native)')) + || false; + } + + return function(stack) { + stack = stack.split('\n'); + + stack = exports.reduce(stack, function(list, line) { + if (isMochaInternal(line)) { + return list; + } + + if (is.node && isNodeInternal(line)) { + return list; + } + + // Clean up cwd(absolute) + list.push(line.replace(cwd, '')); + return list; + }, []); + + return stack.join('\n'); + }; +}; diff --git a/node_modules/mocha/mocha.css b/node_modules/mocha/mocha.css new file mode 100644 index 0000000..3b82ae9 --- /dev/null +++ b/node_modules/mocha/mocha.css @@ -0,0 +1,305 @@ +@charset "utf-8"; + +body { + margin:0; +} + +#mocha { + font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; + margin: 60px 50px; +} + +#mocha ul, +#mocha li { + margin: 0; + padding: 0; +} + +#mocha ul { + list-style: none; +} + +#mocha h1, +#mocha h2 { + margin: 0; +} + +#mocha h1 { + margin-top: 15px; + font-size: 1em; + font-weight: 200; +} + +#mocha h1 a { + text-decoration: none; + color: inherit; +} + +#mocha h1 a:hover { + text-decoration: underline; +} + +#mocha .suite .suite h1 { + margin-top: 0; + font-size: .8em; +} + +#mocha .hidden { + display: none; +} + +#mocha h2 { + font-size: 12px; + font-weight: normal; + cursor: pointer; +} + +#mocha .suite { + margin-left: 15px; +} + +#mocha .test { + margin-left: 15px; + overflow: hidden; +} + +#mocha .test.pending:hover h2::after { + content: '(pending)'; + font-family: arial, sans-serif; +} + +#mocha .test.pass.medium .duration { + background: #c09853; +} + +#mocha .test.pass.slow .duration { + background: #b94a48; +} + +#mocha .test.pass::before { + content: '✓'; + font-size: 12px; + display: block; + float: left; + margin-right: 5px; + color: #00d6b2; +} + +#mocha .test.pass .duration { + font-size: 9px; + margin-left: 5px; + padding: 2px 5px; + color: #fff; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); + -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); + box-shadow: inset 0 1px 1px rgba(0,0,0,.2); + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + -ms-border-radius: 5px; + -o-border-radius: 5px; + border-radius: 5px; +} + +#mocha .test.pass.fast .duration { + display: none; +} + +#mocha .test.pending { + color: #0b97c4; +} + +#mocha .test.pending::before { + content: '◦'; + color: #0b97c4; +} + +#mocha .test.fail { + color: #c00; +} + +#mocha .test.fail pre { + color: black; +} + +#mocha .test.fail::before { + content: '✖'; + font-size: 12px; + display: block; + float: left; + margin-right: 5px; + color: #c00; +} + +#mocha .test pre.error { + color: #c00; + max-height: 300px; + overflow: auto; +} + +#mocha .test .html-error { + overflow: auto; + color: black; + line-height: 1.5; + display: block; + float: left; + clear: left; + font: 12px/1.5 monaco, monospace; + margin: 5px; + padding: 15px; + border: 1px solid #eee; + max-width: 85%; /*(1)*/ + max-width: calc(100% - 42px); /*(2)*/ + max-height: 300px; + word-wrap: break-word; + border-bottom-color: #ddd; + -webkit-border-radius: 3px; + -webkit-box-shadow: 0 1px 3px #eee; + -moz-border-radius: 3px; + -moz-box-shadow: 0 1px 3px #eee; + border-radius: 3px; +} + +#mocha .test .html-error pre.error { + border: none; + -webkit-border-radius: none; + -webkit-box-shadow: none; + -moz-border-radius: none; + -moz-box-shadow: none; + padding: 0; + margin: 0; + margin-top: 18px; + max-height: none; +} + +/** + * (1): approximate for browsers not supporting calc + * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) + * ^^ seriously + */ +#mocha .test pre { + display: block; + float: left; + clear: left; + font: 12px/1.5 monaco, monospace; + margin: 5px; + padding: 15px; + border: 1px solid #eee; + max-width: 85%; /*(1)*/ + max-width: calc(100% - 42px); /*(2)*/ + word-wrap: break-word; + border-bottom-color: #ddd; + -webkit-border-radius: 3px; + -webkit-box-shadow: 0 1px 3px #eee; + -moz-border-radius: 3px; + -moz-box-shadow: 0 1px 3px #eee; + border-radius: 3px; +} + +#mocha .test h2 { + position: relative; +} + +#mocha .test a.replay { + position: absolute; + top: 3px; + right: 0; + text-decoration: none; + vertical-align: middle; + display: block; + width: 15px; + height: 15px; + line-height: 15px; + text-align: center; + background: #eee; + font-size: 15px; + -moz-border-radius: 15px; + border-radius: 15px; + -webkit-transition: opacity 200ms; + -moz-transition: opacity 200ms; + transition: opacity 200ms; + opacity: 0.3; + color: #888; +} + +#mocha .test:hover a.replay { + opacity: 1; +} + +#mocha-report.pass .test.fail { + display: none; +} + +#mocha-report.fail .test.pass { + display: none; +} + +#mocha-report.pending .test.pass, +#mocha-report.pending .test.fail { + display: none; +} +#mocha-report.pending .test.pass.pending { + display: block; +} + +#mocha-error { + color: #c00; + font-size: 1.5em; + font-weight: 100; + letter-spacing: 1px; +} + +#mocha-stats { + position: fixed; + top: 15px; + right: 10px; + font-size: 12px; + margin: 0; + color: #888; + z-index: 1; +} + +#mocha-stats .progress { + float: right; + padding-top: 0; +} + +#mocha-stats em { + color: black; +} + +#mocha-stats a { + text-decoration: none; + color: inherit; +} + +#mocha-stats a:hover { + border-bottom: 1px solid #eee; +} + +#mocha-stats li { + display: inline-block; + margin: 0 5px; + list-style: none; + padding-top: 11px; +} + +#mocha-stats canvas { + width: 40px; + height: 40px; +} + +#mocha code .comment { color: #ddd; } +#mocha code .init { color: #2f6fad; } +#mocha code .string { color: #5890ad; } +#mocha code .keyword { color: #8a6343; } +#mocha code .number { color: #2f6fad; } + +@media screen and (max-device-width: 480px) { + #mocha { + margin: 60px 0px; + } + + #mocha #stats { + position: absolute; + } +} diff --git a/node_modules/mocha/mocha.js b/node_modules/mocha/mocha.js new file mode 100644 index 0000000..84c384b --- /dev/null +++ b/node_modules/mocha/mocha.js @@ -0,0 +1,12417 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1) { + suites.shift(); + } + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + return suite; + }; + + /** + * Exclusive test-case. + */ + + context.suite.only = function(title, fn) { + var suite = context.suite(title, fn); + mocha.grep(suite.fullTitle()); + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.test = function(title, fn) { + var test = new Test(title, fn); + test.file = file; + suites[0].addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn) { + var test = context.test(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + }; + + context.test.skip = common.test.skip; + }); +}; + +},{"../suite":37,"../test":38,"./common":9,"escape-string-regexp":68}],13:[function(require,module,exports){ +/** + * Module dependencies. + */ + +var Suite = require('../suite'); +var Test = require('../test'); +var escapeRe = require('escape-string-regexp'); + +/** + * TDD-style interface: + * + * suite('Array', function() { + * suite('#indexOf()', function() { + * suiteSetup(function() { + * + * }); + * + * test('should return -1 when not present', function() { + * + * }); + * + * test('should return the index when present', function() { + * + * }); + * + * suiteTeardown(function() { + * + * }); + * }); + * }); + * + * @param {Suite} suite Root suite. + */ +module.exports = function(suite) { + var suites = [suite]; + + suite.on('pre-require', function(context, file, mocha) { + var common = require('./common')(suites, context); + + context.setup = common.beforeEach; + context.teardown = common.afterEach; + context.suiteSetup = common.before; + context.suiteTeardown = common.after; + context.run = mocha.options.delay && common.runWithSuite(suite); + + /** + * Describe a "suite" with the given `title` and callback `fn` containing + * nested suites and/or tests. + */ + context.suite = function(title, fn) { + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + return suite; + }; + + /** + * Pending suite. + */ + context.suite.skip = function(title, fn) { + var suite = Suite.create(suites[0], title); + suite.pending = true; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + }; + + /** + * Exclusive test-case. + */ + context.suite.only = function(title, fn) { + var suite = context.suite(title, fn); + mocha.grep(suite.fullTitle()); + }; + + /** + * Describe a specification or test-case with the given `title` and + * callback `fn` acting as a thunk. + */ + context.test = function(title, fn) { + var suite = suites[0]; + if (suite.pending) { + fn = null; + } + var test = new Test(title, fn); + test.file = file; + suite.addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn) { + var test = context.test(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + }; + + context.test.skip = common.test.skip; + }); +}; + +},{"../suite":37,"../test":38,"./common":9,"escape-string-regexp":68}],14:[function(require,module,exports){ +(function (process,global,__dirname){ +/*! + * mocha + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var escapeRe = require('escape-string-regexp'); +var path = require('path'); +var reporters = require('./reporters'); +var utils = require('./utils'); + +/** + * Expose `Mocha`. + */ + +exports = module.exports = Mocha; + +/** + * To require local UIs and reporters when running in node. + */ + +if (!process.browser) { + var cwd = process.cwd(); + module.paths.push(cwd, path.join(cwd, 'node_modules')); +} + +/** + * Expose internals. + */ + +exports.utils = utils; +exports.interfaces = require('./interfaces'); +exports.reporters = reporters; +exports.Runnable = require('./runnable'); +exports.Context = require('./context'); +exports.Runner = require('./runner'); +exports.Suite = require('./suite'); +exports.Hook = require('./hook'); +exports.Test = require('./test'); + +/** + * Return image `name` path. + * + * @api private + * @param {string} name + * @return {string} + */ +function image(name) { + return path.join(__dirname, '../images', name + '.png'); +} + +/** + * Set up mocha with `options`. + * + * Options: + * + * - `ui` name "bdd", "tdd", "exports" etc + * - `reporter` reporter instance, defaults to `mocha.reporters.spec` + * - `globals` array of accepted globals + * - `timeout` timeout in milliseconds + * - `bail` bail on the first test failure + * - `slow` milliseconds to wait before considering a test slow + * - `ignoreLeaks` ignore global leaks + * - `fullTrace` display the full stack-trace on failing + * - `grep` string or regexp to filter tests with + * + * @param {Object} options + * @api public + */ +function Mocha(options) { + options = options || {}; + this.files = []; + this.options = options; + if (options.grep) { + this.grep(new RegExp(options.grep)); + } + if (options.fgrep) { + this.grep(options.fgrep); + } + this.suite = new exports.Suite('', new exports.Context()); + this.ui(options.ui); + this.bail(options.bail); + this.reporter(options.reporter, options.reporterOptions); + if (typeof options.timeout !== 'undefined' && options.timeout !== null) { + this.timeout(options.timeout); + } + this.useColors(options.useColors); + if (options.enableTimeouts !== null) { + this.enableTimeouts(options.enableTimeouts); + } + if (options.slow) { + this.slow(options.slow); + } + + this.suite.on('pre-require', function(context) { + exports.afterEach = context.afterEach || context.teardown; + exports.after = context.after || context.suiteTeardown; + exports.beforeEach = context.beforeEach || context.setup; + exports.before = context.before || context.suiteSetup; + exports.describe = context.describe || context.suite; + exports.it = context.it || context.test; + exports.setup = context.setup || context.beforeEach; + exports.suiteSetup = context.suiteSetup || context.before; + exports.suiteTeardown = context.suiteTeardown || context.after; + exports.suite = context.suite || context.describe; + exports.teardown = context.teardown || context.afterEach; + exports.test = context.test || context.it; + exports.run = context.run; + }); +} + +/** + * Enable or disable bailing on the first failure. + * + * @api public + * @param {boolean} [bail] + */ +Mocha.prototype.bail = function(bail) { + if (!arguments.length) { + bail = true; + } + this.suite.bail(bail); + return this; +}; + +/** + * Add test `file`. + * + * @api public + * @param {string} file + */ +Mocha.prototype.addFile = function(file) { + this.files.push(file); + return this; +}; + +/** + * Set reporter to `reporter`, defaults to "spec". + * + * @param {String|Function} reporter name or constructor + * @param {Object} reporterOptions optional options + * @api public + * @param {string|Function} reporter name or constructor + * @param {Object} reporterOptions optional options + */ +Mocha.prototype.reporter = function(reporter, reporterOptions) { + if (typeof reporter === 'function') { + this._reporter = reporter; + } else { + reporter = reporter || 'spec'; + var _reporter; + // Try to load a built-in reporter. + if (reporters[reporter]) { + _reporter = reporters[reporter]; + } + // Try to load reporters from process.cwd() and node_modules + if (!_reporter) { + try { + _reporter = require(reporter); + } catch (err) { + err.message.indexOf('Cannot find module') !== -1 + ? console.warn('"' + reporter + '" reporter not found') + : console.warn('"' + reporter + '" reporter blew up with error:\n' + err.stack); + } + } + if (!_reporter && reporter === 'teamcity') { + console.warn('The Teamcity reporter was moved to a package named ' + + 'mocha-teamcity-reporter ' + + '(https://npmjs.org/package/mocha-teamcity-reporter).'); + } + if (!_reporter) { + throw new Error('invalid reporter "' + reporter + '"'); + } + this._reporter = _reporter; + } + this.options.reporterOptions = reporterOptions; + return this; +}; + +/** + * Set test UI `name`, defaults to "bdd". + * + * @api public + * @param {string} bdd + */ +Mocha.prototype.ui = function(name) { + name = name || 'bdd'; + this._ui = exports.interfaces[name]; + if (!this._ui) { + try { + this._ui = require(name); + } catch (err) { + throw new Error('invalid interface "' + name + '"'); + } + } + this._ui = this._ui(this.suite); + return this; +}; + +/** + * Load registered files. + * + * @api private + */ +Mocha.prototype.loadFiles = function(fn) { + var self = this; + var suite = this.suite; + var pending = this.files.length; + this.files.forEach(function(file) { + file = path.resolve(file); + suite.emit('pre-require', global, file, self); + suite.emit('require', require(file), file, self); + suite.emit('post-require', global, file, self); + --pending || (fn && fn()); + }); +}; + +/** + * Enable growl support. + * + * @api private + */ +Mocha.prototype._growl = function(runner, reporter) { + var notify = require('growl'); + + runner.on('end', function() { + var stats = reporter.stats; + if (stats.failures) { + var msg = stats.failures + ' of ' + runner.total + ' tests failed'; + notify(msg, { name: 'mocha', title: 'Failed', image: image('error') }); + } else { + notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { + name: 'mocha', + title: 'Passed', + image: image('ok') + }); + } + }); +}; + +/** + * Add regexp to grep, if `re` is a string it is escaped. + * + * @param {RegExp|String} re + * @return {Mocha} + * @api public + * @param {RegExp|string} re + * @return {Mocha} + */ +Mocha.prototype.grep = function(re) { + this.options.grep = typeof re === 'string' ? new RegExp(escapeRe(re)) : re; + return this; +}; + +/** + * Invert `.grep()` matches. + * + * @return {Mocha} + * @api public + */ +Mocha.prototype.invert = function() { + this.options.invert = true; + return this; +}; + +/** + * Ignore global leaks. + * + * @param {Boolean} ignore + * @return {Mocha} + * @api public + * @param {boolean} ignore + * @return {Mocha} + */ +Mocha.prototype.ignoreLeaks = function(ignore) { + this.options.ignoreLeaks = Boolean(ignore); + return this; +}; + +/** + * Enable global leak checking. + * + * @return {Mocha} + * @api public + */ +Mocha.prototype.checkLeaks = function() { + this.options.ignoreLeaks = false; + return this; +}; + +/** + * Display long stack-trace on failing + * + * @return {Mocha} + * @api public + */ +Mocha.prototype.fullTrace = function() { + this.options.fullStackTrace = true; + return this; +}; + +/** + * Enable growl support. + * + * @return {Mocha} + * @api public + */ +Mocha.prototype.growl = function() { + this.options.growl = true; + return this; +}; + +/** + * Ignore `globals` array or string. + * + * @param {Array|String} globals + * @return {Mocha} + * @api public + * @param {Array|string} globals + * @return {Mocha} + */ +Mocha.prototype.globals = function(globals) { + this.options.globals = (this.options.globals || []).concat(globals); + return this; +}; + +/** + * Emit color output. + * + * @param {Boolean} colors + * @return {Mocha} + * @api public + * @param {boolean} colors + * @return {Mocha} + */ +Mocha.prototype.useColors = function(colors) { + if (colors !== undefined) { + this.options.useColors = colors; + } + return this; +}; + +/** + * Use inline diffs rather than +/-. + * + * @param {Boolean} inlineDiffs + * @return {Mocha} + * @api public + * @param {boolean} inlineDiffs + * @return {Mocha} + */ +Mocha.prototype.useInlineDiffs = function(inlineDiffs) { + this.options.useInlineDiffs = inlineDiffs !== undefined && inlineDiffs; + return this; +}; + +/** + * Set the timeout in milliseconds. + * + * @param {Number} timeout + * @return {Mocha} + * @api public + * @param {number} timeout + * @return {Mocha} + */ +Mocha.prototype.timeout = function(timeout) { + this.suite.timeout(timeout); + return this; +}; + +/** + * Set slowness threshold in milliseconds. + * + * @param {Number} slow + * @return {Mocha} + * @api public + * @param {number} slow + * @return {Mocha} + */ +Mocha.prototype.slow = function(slow) { + this.suite.slow(slow); + return this; +}; + +/** + * Enable timeouts. + * + * @param {Boolean} enabled + * @return {Mocha} + * @api public + * @param {boolean} enabled + * @return {Mocha} + */ +Mocha.prototype.enableTimeouts = function(enabled) { + this.suite.enableTimeouts(arguments.length && enabled !== undefined ? enabled : true); + return this; +}; + +/** + * Makes all tests async (accepting a callback) + * + * @return {Mocha} + * @api public + */ +Mocha.prototype.asyncOnly = function() { + this.options.asyncOnly = true; + return this; +}; + +/** + * Disable syntax highlighting (in browser). + * + * @api public + */ +Mocha.prototype.noHighlighting = function() { + this.options.noHighlighting = true; + return this; +}; + +/** + * Enable uncaught errors to propagate (in browser). + * + * @return {Mocha} + * @api public + */ +Mocha.prototype.allowUncaught = function() { + this.options.allowUncaught = true; + return this; +}; + +/** + * Delay root suite execution. + * @returns {Mocha} + */ +Mocha.prototype.delay = function delay() { + this.options.delay = true; + return this; +}; + +/** + * Run tests and invoke `fn()` when complete. + * + * @api public + * @param {Function} fn + * @return {Runner} + */ +Mocha.prototype.run = function(fn) { + if (this.files.length) { + this.loadFiles(); + } + var suite = this.suite; + var options = this.options; + options.files = this.files; + var runner = new exports.Runner(suite, options.delay); + var reporter = new this._reporter(runner, options); + runner.ignoreLeaks = options.ignoreLeaks !== false; + runner.fullStackTrace = options.fullStackTrace; + runner.asyncOnly = options.asyncOnly; + runner.allowUncaught = options.allowUncaught; + if (options.grep) { + runner.grep(options.grep, options.invert); + } + if (options.globals) { + runner.globals(options.globals); + } + if (options.growl) { + this._growl(runner, reporter); + } + if (options.useColors !== undefined) { + exports.reporters.Base.useColors = options.useColors; + } + exports.reporters.Base.inlineDiffs = options.useInlineDiffs; + + function done(failures) { + if (reporter.done) { + reporter.done(failures, fn); + } else { + fn && fn(failures); + } + } + + return runner.run(done); +}; + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},"/lib") +},{"./context":6,"./hook":7,"./interfaces":11,"./reporters":22,"./runnable":35,"./runner":36,"./suite":37,"./test":38,"./utils":39,"_process":51,"escape-string-regexp":68,"growl":69,"path":41}],15:[function(require,module,exports){ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @api public + * @param {string|number} val + * @param {Object} options + * @return {string|number} + */ +module.exports = function(val, options) { + options = options || {}; + if (typeof val === 'string') { + return parse(val); + } + // https://github.com/mochajs/mocha/pull/1035 + return options['long'] ? longFormat(val) : shortFormat(val); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @api private + * @param {string} str + * @return {number} + */ +function parse(str) { + var match = (/^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i).exec(str); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 's': + return n * s; + case 'ms': + return n; + default: + // No default case + } +} + +/** + * Short format for `ms`. + * + * @api private + * @param {number} ms + * @return {string} + */ +function shortFormat(ms) { + if (ms >= d) { + return Math.round(ms / d) + 'd'; + } + if (ms >= h) { + return Math.round(ms / h) + 'h'; + } + if (ms >= m) { + return Math.round(ms / m) + 'm'; + } + if (ms >= s) { + return Math.round(ms / s) + 's'; + } + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @api private + * @param {number} ms + * @return {string} + */ +function longFormat(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; +} + +/** + * Pluralization helper. + * + * @api private + * @param {number} ms + * @param {number} n + * @param {string} name + */ +function plural(ms, n, name) { + if (ms < n) { + return; + } + if (ms < n * 1.5) { + return Math.floor(ms / n) + ' ' + name; + } + return Math.ceil(ms / n) + ' ' + name + 's'; +} + +},{}],16:[function(require,module,exports){ + +/** + * Expose `Pending`. + */ + +module.exports = Pending; + +/** + * Initialize a new `Pending` error with the given message. + * + * @param {string} message + */ +function Pending(message) { + this.message = message; +} + +},{}],17:[function(require,module,exports){ +(function (process,global){ +/** + * Module dependencies. + */ + +var tty = require('tty'); +var diff = require('diff'); +var ms = require('../ms'); +var utils = require('../utils'); +var supportsColor = process.browser ? null : require('supports-color'); + +/** + * Expose `Base`. + */ + +exports = module.exports = Base; + +/** + * Save timer references to avoid Sinon interfering. + * See: https://github.com/mochajs/mocha/issues/237 + */ + +/* eslint-disable no-unused-vars, no-native-reassign */ +var Date = global.Date; +var setTimeout = global.setTimeout; +var setInterval = global.setInterval; +var clearTimeout = global.clearTimeout; +var clearInterval = global.clearInterval; +/* eslint-enable no-unused-vars, no-native-reassign */ + +/** + * Check if both stdio streams are associated with a tty. + */ + +var isatty = tty.isatty(1) && tty.isatty(2); + +/** + * Enable coloring by default, except in the browser interface. + */ + +exports.useColors = !process.browser && (supportsColor || (process.env.MOCHA_COLORS !== undefined)); + +/** + * Inline diffs instead of +/- + */ + +exports.inlineDiffs = false; + +/** + * Default color map. + */ + +exports.colors = { + pass: 90, + fail: 31, + 'bright pass': 92, + 'bright fail': 91, + 'bright yellow': 93, + pending: 36, + suite: 0, + 'error title': 0, + 'error message': 31, + 'error stack': 90, + checkmark: 32, + fast: 90, + medium: 33, + slow: 31, + green: 32, + light: 90, + 'diff gutter': 90, + 'diff added': 32, + 'diff removed': 31 +}; + +/** + * Default symbol map. + */ + +exports.symbols = { + ok: '✓', + err: '✖', + dot: '․' +}; + +// With node.js on Windows: use symbols available in terminal default fonts +if (process.platform === 'win32') { + exports.symbols.ok = '\u221A'; + exports.symbols.err = '\u00D7'; + exports.symbols.dot = '.'; +} + +/** + * Color `str` with the given `type`, + * allowing colors to be disabled, + * as well as user-defined color + * schemes. + * + * @param {string} type + * @param {string} str + * @return {string} + * @api private + */ +var color = exports.color = function(type, str) { + if (!exports.useColors) { + return String(str); + } + return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; +}; + +/** + * Expose term window size, with some defaults for when stderr is not a tty. + */ + +exports.window = { + width: 75 +}; + +if (isatty) { + exports.window.width = process.stdout.getWindowSize + ? process.stdout.getWindowSize(1)[0] + : tty.getWindowSize()[1]; +} + +/** + * Expose some basic cursor interactions that are common among reporters. + */ + +exports.cursor = { + hide: function() { + isatty && process.stdout.write('\u001b[?25l'); + }, + + show: function() { + isatty && process.stdout.write('\u001b[?25h'); + }, + + deleteLine: function() { + isatty && process.stdout.write('\u001b[2K'); + }, + + beginningOfLine: function() { + isatty && process.stdout.write('\u001b[0G'); + }, + + CR: function() { + if (isatty) { + exports.cursor.deleteLine(); + exports.cursor.beginningOfLine(); + } else { + process.stdout.write('\r'); + } + } +}; + +/** + * Outut the given `failures` as a list. + * + * @param {Array} failures + * @api public + */ + +exports.list = function(failures) { + console.log(); + failures.forEach(function(test, i) { + // format + var fmt = color('error title', ' %s) %s:\n') + + color('error message', ' %s') + + color('error stack', '\n%s\n'); + + // msg + var msg; + var err = test.err; + var message; + if (err.message) { + message = err.message; + } else if (typeof err.inspect === 'function') { + message = err.inspect() + ''; + } else { + message = ''; + } + var stack = err.stack || message; + var index = stack.indexOf(message); + var actual = err.actual; + var expected = err.expected; + var escape = true; + + if (index === -1) { + msg = message; + } else { + index += message.length; + msg = stack.slice(0, index); + // remove msg from stack + stack = stack.slice(index + 1); + } + + // uncaught + if (err.uncaught) { + msg = 'Uncaught ' + msg; + } + // explicitly show diff + if (err.showDiff !== false && sameType(actual, expected) && expected !== undefined) { + escape = false; + if (!(utils.isString(actual) && utils.isString(expected))) { + err.actual = actual = utils.stringify(actual); + err.expected = expected = utils.stringify(expected); + } + + fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n'); + var match = message.match(/^([^:]+): expected/); + msg = '\n ' + color('error message', match ? match[1] : msg); + + if (exports.inlineDiffs) { + msg += inlineDiff(err, escape); + } else { + msg += unifiedDiff(err, escape); + } + } + + // indent stack trace + stack = stack.replace(/^/gm, ' '); + + console.log(fmt, (i + 1), test.fullTitle(), msg, stack); + }); +}; + +/** + * Initialize a new `Base` reporter. + * + * All other reporters generally + * inherit from this reporter, providing + * stats such as test duration, number + * of tests passed / failed etc. + * + * @param {Runner} runner + * @api public + */ + +function Base(runner) { + var stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 }; + var failures = this.failures = []; + + if (!runner) { + return; + } + this.runner = runner; + + runner.stats = stats; + + runner.on('start', function() { + stats.start = new Date(); + }); + + runner.on('suite', function(suite) { + stats.suites = stats.suites || 0; + suite.root || stats.suites++; + }); + + runner.on('test end', function() { + stats.tests = stats.tests || 0; + stats.tests++; + }); + + runner.on('pass', function(test) { + stats.passes = stats.passes || 0; + + if (test.duration > test.slow()) { + test.speed = 'slow'; + } else if (test.duration > test.slow() / 2) { + test.speed = 'medium'; + } else { + test.speed = 'fast'; + } + + stats.passes++; + }); + + runner.on('fail', function(test, err) { + stats.failures = stats.failures || 0; + stats.failures++; + test.err = err; + failures.push(test); + }); + + runner.on('end', function() { + stats.end = new Date(); + stats.duration = new Date() - stats.start; + }); + + runner.on('pending', function() { + stats.pending++; + }); +} + +/** + * Output common epilogue used by many of + * the bundled reporters. + * + * @api public + */ +Base.prototype.epilogue = function() { + var stats = this.stats; + var fmt; + + console.log(); + + // passes + fmt = color('bright pass', ' ') + + color('green', ' %d passing') + + color('light', ' (%s)'); + + console.log(fmt, + stats.passes || 0, + ms(stats.duration)); + + // pending + if (stats.pending) { + fmt = color('pending', ' ') + + color('pending', ' %d pending'); + + console.log(fmt, stats.pending); + } + + // failures + if (stats.failures) { + fmt = color('fail', ' %d failing'); + + console.log(fmt, stats.failures); + + Base.list(this.failures); + console.log(); + } + + console.log(); +}; + +/** + * Pad the given `str` to `len`. + * + * @api private + * @param {string} str + * @param {string} len + * @return {string} + */ +function pad(str, len) { + str = String(str); + return Array(len - str.length + 1).join(' ') + str; +} + +/** + * Returns an inline diff between 2 strings with coloured ANSI output + * + * @api private + * @param {Error} err with actual/expected + * @param {boolean} escape + * @return {string} Diff + */ +function inlineDiff(err, escape) { + var msg = errorDiff(err, 'WordsWithSpace', escape); + + // linenos + var lines = msg.split('\n'); + if (lines.length > 4) { + var width = String(lines.length).length; + msg = lines.map(function(str, i) { + return pad(++i, width) + ' |' + ' ' + str; + }).join('\n'); + } + + // legend + msg = '\n' + + color('diff removed', 'actual') + + ' ' + + color('diff added', 'expected') + + '\n\n' + + msg + + '\n'; + + // indent + msg = msg.replace(/^/gm, ' '); + return msg; +} + +/** + * Returns a unified diff between two strings. + * + * @api private + * @param {Error} err with actual/expected + * @param {boolean} escape + * @return {string} The diff. + */ +function unifiedDiff(err, escape) { + var indent = ' '; + function cleanUp(line) { + if (escape) { + line = escapeInvisibles(line); + } + if (line[0] === '+') { + return indent + colorLines('diff added', line); + } + if (line[0] === '-') { + return indent + colorLines('diff removed', line); + } + if (line.match(/\@\@/)) { + return null; + } + if (line.match(/\\ No newline/)) { + return null; + } + return indent + line; + } + function notBlank(line) { + return typeof line !== 'undefined' && line !== null; + } + var msg = diff.createPatch('string', err.actual, err.expected); + var lines = msg.split('\n').splice(4); + return '\n ' + + colorLines('diff added', '+ expected') + ' ' + + colorLines('diff removed', '- actual') + + '\n\n' + + lines.map(cleanUp).filter(notBlank).join('\n'); +} + +/** + * Return a character diff for `err`. + * + * @api private + * @param {Error} err + * @param {string} type + * @param {boolean} escape + * @return {string} + */ +function errorDiff(err, type, escape) { + var actual = escape ? escapeInvisibles(err.actual) : err.actual; + var expected = escape ? escapeInvisibles(err.expected) : err.expected; + return diff['diff' + type](actual, expected).map(function(str) { + if (str.added) { + return colorLines('diff added', str.value); + } + if (str.removed) { + return colorLines('diff removed', str.value); + } + return str.value; + }).join(''); +} + +/** + * Returns a string with all invisible characters in plain text + * + * @api private + * @param {string} line + * @return {string} + */ +function escapeInvisibles(line) { + return line.replace(/\t/g, '') + .replace(/\r/g, '') + .replace(/\n/g, '\n'); +} + +/** + * Color lines for `str`, using the color `name`. + * + * @api private + * @param {string} name + * @param {string} str + * @return {string} + */ +function colorLines(name, str) { + return str.split('\n').map(function(str) { + return color(name, str); + }).join('\n'); +} + +/** + * Object#toString reference. + */ +var objToString = Object.prototype.toString; + +/** + * Check that a / b have the same type. + * + * @api private + * @param {Object} a + * @param {Object} b + * @return {boolean} + */ +function sameType(a, b) { + return objToString.call(a) === objToString.call(b); +} + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../ms":15,"../utils":39,"_process":51,"diff":67,"supports-color":41,"tty":5}],18:[function(require,module,exports){ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); + +/** + * Expose `Doc`. + */ + +exports = module.exports = Doc; + +/** + * Initialize a new `Doc` reporter. + * + * @param {Runner} runner + * @api public + */ +function Doc(runner) { + Base.call(this, runner); + + var indents = 2; + + function indent() { + return Array(indents).join(' '); + } + + runner.on('suite', function(suite) { + if (suite.root) { + return; + } + ++indents; + console.log('%s
      ', indent()); + ++indents; + console.log('%s

      %s

      ', indent(), utils.escape(suite.title)); + console.log('%s
      ', indent()); + }); + + runner.on('suite end', function(suite) { + if (suite.root) { + return; + } + console.log('%s
      ', indent()); + --indents; + console.log('%s
      ', indent()); + --indents; + }); + + runner.on('pass', function(test) { + console.log('%s
      %s
      ', indent(), utils.escape(test.title)); + var code = utils.escape(utils.clean(test.fn.toString())); + console.log('%s
      %s
      ', indent(), code); + }); + + runner.on('fail', function(test, err) { + console.log('%s
      %s
      ', indent(), utils.escape(test.title)); + var code = utils.escape(utils.clean(test.fn.toString())); + console.log('%s
      %s
      ', indent(), code); + console.log('%s
      %s
      ', indent(), utils.escape(err)); + }); +} + +},{"../utils":39,"./base":17}],19:[function(require,module,exports){ +(function (process){ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var color = Base.color; + +/** + * Expose `Dot`. + */ + +exports = module.exports = Dot; + +/** + * Initialize a new `Dot` matrix test reporter. + * + * @api public + * @param {Runner} runner + */ +function Dot(runner) { + Base.call(this, runner); + + var self = this; + var width = Base.window.width * .75 | 0; + var n = -1; + + runner.on('start', function() { + process.stdout.write('\n'); + }); + + runner.on('pending', function() { + if (++n % width === 0) { + process.stdout.write('\n '); + } + process.stdout.write(color('pending', Base.symbols.dot)); + }); + + runner.on('pass', function(test) { + if (++n % width === 0) { + process.stdout.write('\n '); + } + if (test.speed === 'slow') { + process.stdout.write(color('bright yellow', Base.symbols.dot)); + } else { + process.stdout.write(color(test.speed, Base.symbols.dot)); + } + }); + + runner.on('fail', function() { + if (++n % width === 0) { + process.stdout.write('\n '); + } + process.stdout.write(color('fail', Base.symbols.dot)); + }); + + runner.on('end', function() { + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Dot, Base); + +}).call(this,require('_process')) +},{"../utils":39,"./base":17,"_process":51}],20:[function(require,module,exports){ +(function (process,__dirname){ +/** + * Module dependencies. + */ + +var JSONCov = require('./json-cov'); +var readFileSync = require('fs').readFileSync; +var join = require('path').join; + +/** + * Expose `HTMLCov`. + */ + +exports = module.exports = HTMLCov; + +/** + * Initialize a new `JsCoverage` reporter. + * + * @api public + * @param {Runner} runner + */ +function HTMLCov(runner) { + var jade = require('jade'); + var file = join(__dirname, '/templates/coverage.jade'); + var str = readFileSync(file, 'utf8'); + var fn = jade.compile(str, { filename: file }); + var self = this; + + JSONCov.call(this, runner, false); + + runner.on('end', function() { + process.stdout.write(fn({ + cov: self.cov, + coverageClass: coverageClass + })); + }); +} + +/** + * Return coverage class for a given coverage percentage. + * + * @api private + * @param {number} coveragePctg + * @return {string} + */ +function coverageClass(coveragePctg) { + if (coveragePctg >= 75) { + return 'high'; + } + if (coveragePctg >= 50) { + return 'medium'; + } + if (coveragePctg >= 25) { + return 'low'; + } + return 'terrible'; +} + +}).call(this,require('_process'),"/lib/reporters") +},{"./json-cov":23,"_process":51,"fs":41,"jade":41,"path":41}],21:[function(require,module,exports){ +(function (global){ +/* eslint-env browser */ + +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); +var Progress = require('../browser/progress'); +var escapeRe = require('escape-string-regexp'); +var escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +/* eslint-disable no-unused-vars, no-native-reassign */ +var Date = global.Date; +var setTimeout = global.setTimeout; +var setInterval = global.setInterval; +var clearTimeout = global.clearTimeout; +var clearInterval = global.clearInterval; +/* eslint-enable no-unused-vars, no-native-reassign */ + +/** + * Expose `HTML`. + */ + +exports = module.exports = HTML; + +/** + * Stats template. + */ + +var statsTemplate = ''; + +/** + * Initialize a new `HTML` reporter. + * + * @api public + * @param {Runner} runner + */ +function HTML(runner) { + Base.call(this, runner); + + var self = this; + var stats = this.stats; + var stat = fragment(statsTemplate); + var items = stat.getElementsByTagName('li'); + var passes = items[1].getElementsByTagName('em')[0]; + var passesLink = items[1].getElementsByTagName('a')[0]; + var failures = items[2].getElementsByTagName('em')[0]; + var failuresLink = items[2].getElementsByTagName('a')[0]; + var duration = items[3].getElementsByTagName('em')[0]; + var canvas = stat.getElementsByTagName('canvas')[0]; + var report = fragment('
        '); + var stack = [report]; + var progress; + var ctx; + var root = document.getElementById('mocha'); + + if (canvas.getContext) { + var ratio = window.devicePixelRatio || 1; + canvas.style.width = canvas.width; + canvas.style.height = canvas.height; + canvas.width *= ratio; + canvas.height *= ratio; + ctx = canvas.getContext('2d'); + ctx.scale(ratio, ratio); + progress = new Progress(); + } + + if (!root) { + return error('#mocha div missing, add it to your document'); + } + + // pass toggle + on(passesLink, 'click', function() { + unhide(); + var name = (/pass/).test(report.className) ? '' : ' pass'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) { + hideSuitesWithout('test pass'); + } + }); + + // failure toggle + on(failuresLink, 'click', function() { + unhide(); + var name = (/fail/).test(report.className) ? '' : ' fail'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) { + hideSuitesWithout('test fail'); + } + }); + + root.appendChild(stat); + root.appendChild(report); + + if (progress) { + progress.size(40); + } + + runner.on('suite', function(suite) { + if (suite.root) { + return; + } + + // suite + var url = self.suiteURL(suite); + var el = fragment('
      • %s

      • ', url, escape(suite.title)); + + // container + stack[0].appendChild(el); + stack.unshift(document.createElement('ul')); + el.appendChild(stack[0]); + }); + + runner.on('suite end', function(suite) { + if (suite.root) { + return; + } + stack.shift(); + }); + + runner.on('fail', function(test) { + if (test.type === 'hook') { + runner.emit('test end', test); + } + }); + + runner.on('test end', function(test) { + // TODO: add to stats + var percent = stats.tests / this.total * 100 | 0; + if (progress) { + progress.update(percent).draw(ctx); + } + + // update stats + var ms = new Date() - stats.start; + text(passes, stats.passes); + text(failures, stats.failures); + text(duration, (ms / 1000).toFixed(2)); + + // test + var el; + if (test.state === 'passed') { + var url = self.testURL(test); + el = fragment('
      • %e%ems

      • ', test.speed, test.title, test.duration, url); + } else if (test.pending) { + el = fragment('
      • %e

      • ', test.title); + } else { + el = fragment('
      • %e

      • ', test.title, self.testURL(test)); + var stackString; // Note: Includes leading newline + var message = test.err.toString(); + + // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we + // check for the result of the stringifying. + if (message === '[object Error]') { + message = test.err.message; + } + + if (test.err.stack) { + var indexOfMessage = test.err.stack.indexOf(test.err.message); + if (indexOfMessage === -1) { + stackString = test.err.stack; + } else { + stackString = test.err.stack.substr(test.err.message.length + indexOfMessage); + } + } else if (test.err.sourceURL && test.err.line !== undefined) { + // Safari doesn't give you a stack. Let's at least provide a source line. + stackString = '\n(' + test.err.sourceURL + ':' + test.err.line + ')'; + } + + stackString = stackString || ''; + + if (test.err.htmlMessage && stackString) { + el.appendChild(fragment('
        %s\n
        %e
        ', test.err.htmlMessage, stackString)); + } else if (test.err.htmlMessage) { + el.appendChild(fragment('
        %s
        ', test.err.htmlMessage)); + } else { + el.appendChild(fragment('
        %e%e
        ', message, stackString)); + } + } + + // toggle code + // TODO: defer + if (!test.pending) { + var h2 = el.getElementsByTagName('h2')[0]; + + on(h2, 'click', function() { + pre.style.display = pre.style.display === 'none' ? 'block' : 'none'; + }); + + var pre = fragment('
        %e
        ', utils.clean(test.fn.toString())); + el.appendChild(pre); + pre.style.display = 'none'; + } + + // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. + if (stack[0]) { + stack[0].appendChild(el); + } + }); +} + +/** + * Makes a URL, preserving querystring ("search") parameters. + * + * @param {string} s + * @return {string} A new URL. + */ +function makeUrl(s) { + var search = window.location.search; + + // Remove previous grep query parameter if present + if (search) { + search = search.replace(/[?&]grep=[^&\s]*/g, '').replace(/^&/, '?'); + } + + return window.location.pathname + (search ? search + '&' : '?') + 'grep=' + encodeURIComponent(escapeRe(s)); +} + +/** + * Provide suite URL. + * + * @param {Object} [suite] + */ +HTML.prototype.suiteURL = function(suite) { + return makeUrl(suite.fullTitle()); +}; + +/** + * Provide test URL. + * + * @param {Object} [test] + */ +HTML.prototype.testURL = function(test) { + return makeUrl(test.fullTitle()); +}; + +/** + * Display error `msg`. + * + * @param {string} msg + */ +function error(msg) { + document.body.appendChild(fragment('
        %s
        ', msg)); +} + +/** + * Return a DOM fragment from `html`. + * + * @param {string} html + */ +function fragment(html) { + var args = arguments; + var div = document.createElement('div'); + var i = 1; + + div.innerHTML = html.replace(/%([se])/g, function(_, type) { + switch (type) { + case 's': return String(args[i++]); + case 'e': return escape(args[i++]); + // no default + } + }); + + return div.firstChild; +} + +/** + * Check for suites that do not have elements + * with `classname`, and hide them. + * + * @param {text} classname + */ +function hideSuitesWithout(classname) { + var suites = document.getElementsByClassName('suite'); + for (var i = 0; i < suites.length; i++) { + var els = suites[i].getElementsByClassName(classname); + if (!els.length) { + suites[i].className += ' hidden'; + } + } +} + +/** + * Unhide .hidden suites. + */ +function unhide() { + var els = document.getElementsByClassName('suite hidden'); + for (var i = 0; i < els.length; ++i) { + els[i].className = els[i].className.replace('suite hidden', 'suite'); + } +} + +/** + * Set an element's text contents. + * + * @param {HTMLElement} el + * @param {string} contents + */ +function text(el, contents) { + if (el.textContent) { + el.textContent = contents; + } else { + el.innerText = contents; + } +} + +/** + * Listen on `event` with callback `fn`. + */ +function on(el, event, fn) { + if (el.addEventListener) { + el.addEventListener(event, fn, false); + } else { + el.attachEvent('on' + event, fn); + } +} + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../browser/progress":4,"../utils":39,"./base":17,"escape-string-regexp":68}],22:[function(require,module,exports){ +// Alias exports to a their normalized format Mocha#reporter to prevent a need +// for dynamic (try/catch) requires, which Browserify doesn't handle. +exports.Base = exports.base = require('./base'); +exports.Dot = exports.dot = require('./dot'); +exports.Doc = exports.doc = require('./doc'); +exports.TAP = exports.tap = require('./tap'); +exports.JSON = exports.json = require('./json'); +exports.HTML = exports.html = require('./html'); +exports.List = exports.list = require('./list'); +exports.Min = exports.min = require('./min'); +exports.Spec = exports.spec = require('./spec'); +exports.Nyan = exports.nyan = require('./nyan'); +exports.XUnit = exports.xunit = require('./xunit'); +exports.Markdown = exports.markdown = require('./markdown'); +exports.Progress = exports.progress = require('./progress'); +exports.Landing = exports.landing = require('./landing'); +exports.JSONCov = exports['json-cov'] = require('./json-cov'); +exports.HTMLCov = exports['html-cov'] = require('./html-cov'); +exports.JSONStream = exports['json-stream'] = require('./json-stream'); + +},{"./base":17,"./doc":18,"./dot":19,"./html":21,"./html-cov":20,"./json":25,"./json-cov":23,"./json-stream":24,"./landing":26,"./list":27,"./markdown":28,"./min":29,"./nyan":30,"./progress":31,"./spec":32,"./tap":33,"./xunit":34}],23:[function(require,module,exports){ +(function (process,global){ +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `JSONCov`. + */ + +exports = module.exports = JSONCov; + +/** + * Initialize a new `JsCoverage` reporter. + * + * @api public + * @param {Runner} runner + * @param {boolean} output + */ +function JSONCov(runner, output) { + Base.call(this, runner); + + output = arguments.length === 1 || output; + var self = this; + var tests = []; + var failures = []; + var passes = []; + + runner.on('test end', function(test) { + tests.push(test); + }); + + runner.on('pass', function(test) { + passes.push(test); + }); + + runner.on('fail', function(test) { + failures.push(test); + }); + + runner.on('end', function() { + var cov = global._$jscoverage || {}; + var result = self.cov = map(cov); + result.stats = self.stats; + result.tests = tests.map(clean); + result.failures = failures.map(clean); + result.passes = passes.map(clean); + if (!output) { + return; + } + process.stdout.write(JSON.stringify(result, null, 2)); + }); +} + +/** + * Map jscoverage data to a JSON structure + * suitable for reporting. + * + * @api private + * @param {Object} cov + * @return {Object} + */ + +function map(cov) { + var ret = { + instrumentation: 'node-jscoverage', + sloc: 0, + hits: 0, + misses: 0, + coverage: 0, + files: [] + }; + + for (var filename in cov) { + if (Object.prototype.hasOwnProperty.call(cov, filename)) { + var data = coverage(filename, cov[filename]); + ret.files.push(data); + ret.hits += data.hits; + ret.misses += data.misses; + ret.sloc += data.sloc; + } + } + + ret.files.sort(function(a, b) { + return a.filename.localeCompare(b.filename); + }); + + if (ret.sloc > 0) { + ret.coverage = (ret.hits / ret.sloc) * 100; + } + + return ret; +} + +/** + * Map jscoverage data for a single source file + * to a JSON structure suitable for reporting. + * + * @api private + * @param {string} filename name of the source file + * @param {Object} data jscoverage coverage data + * @return {Object} + */ +function coverage(filename, data) { + var ret = { + filename: filename, + coverage: 0, + hits: 0, + misses: 0, + sloc: 0, + source: {} + }; + + data.source.forEach(function(line, num) { + num++; + + if (data[num] === 0) { + ret.misses++; + ret.sloc++; + } else if (data[num] !== undefined) { + ret.hits++; + ret.sloc++; + } + + ret.source[num] = { + source: line, + coverage: data[num] === undefined ? '' : data[num] + }; + }); + + ret.coverage = ret.hits / ret.sloc * 100; + + return ret; +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @api private + * @param {Object} test + * @return {Object} + */ +function clean(test) { + return { + duration: test.duration, + fullTitle: test.fullTitle(), + title: test.title + }; +} + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./base":17,"_process":51}],24:[function(require,module,exports){ +(function (process){ +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `List`. + */ + +exports = module.exports = List; + +/** + * Initialize a new `List` test reporter. + * + * @api public + * @param {Runner} runner + */ +function List(runner) { + Base.call(this, runner); + + var self = this; + var total = runner.total; + + runner.on('start', function() { + console.log(JSON.stringify(['start', { total: total }])); + }); + + runner.on('pass', function(test) { + console.log(JSON.stringify(['pass', clean(test)])); + }); + + runner.on('fail', function(test, err) { + test = clean(test); + test.err = err.message; + test.stack = err.stack || null; + console.log(JSON.stringify(['fail', test])); + }); + + runner.on('end', function() { + process.stdout.write(JSON.stringify(['end', self.stats])); + }); +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @api private + * @param {Object} test + * @return {Object} + */ +function clean(test) { + return { + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration + }; +} + +}).call(this,require('_process')) +},{"./base":17,"_process":51}],25:[function(require,module,exports){ +(function (process){ +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `JSON`. + */ + +exports = module.exports = JSONReporter; + +/** + * Initialize a new `JSON` reporter. + * + * @api public + * @param {Runner} runner + */ +function JSONReporter(runner) { + Base.call(this, runner); + + var self = this; + var tests = []; + var pending = []; + var failures = []; + var passes = []; + + runner.on('test end', function(test) { + tests.push(test); + }); + + runner.on('pass', function(test) { + passes.push(test); + }); + + runner.on('fail', function(test) { + failures.push(test); + }); + + runner.on('pending', function(test) { + pending.push(test); + }); + + runner.on('end', function() { + var obj = { + stats: self.stats, + tests: tests.map(clean), + pending: pending.map(clean), + failures: failures.map(clean), + passes: passes.map(clean) + }; + + runner.testResults = obj; + + process.stdout.write(JSON.stringify(obj, null, 2)); + }); +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @api private + * @param {Object} test + * @return {Object} + */ +function clean(test) { + return { + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration, + err: errorJSON(test.err || {}) + }; +} + +/** + * Transform `error` into a JSON object. + * + * @api private + * @param {Error} err + * @return {Object} + */ +function errorJSON(err) { + var res = {}; + Object.getOwnPropertyNames(err).forEach(function(key) { + res[key] = err[key]; + }, err); + return res; +} + +}).call(this,require('_process')) +},{"./base":17,"_process":51}],26:[function(require,module,exports){ +(function (process){ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var cursor = Base.cursor; +var color = Base.color; + +/** + * Expose `Landing`. + */ + +exports = module.exports = Landing; + +/** + * Airplane color. + */ + +Base.colors.plane = 0; + +/** + * Airplane crash color. + */ + +Base.colors['plane crash'] = 31; + +/** + * Runway color. + */ + +Base.colors.runway = 90; + +/** + * Initialize a new `Landing` reporter. + * + * @api public + * @param {Runner} runner + */ +function Landing(runner) { + Base.call(this, runner); + + var self = this; + var width = Base.window.width * .75 | 0; + var total = runner.total; + var stream = process.stdout; + var plane = color('plane', '✈'); + var crashed = -1; + var n = 0; + + function runway() { + var buf = Array(width).join('-'); + return ' ' + color('runway', buf); + } + + runner.on('start', function() { + stream.write('\n\n\n '); + cursor.hide(); + }); + + runner.on('test end', function(test) { + // check if the plane crashed + var col = crashed === -1 ? width * ++n / total | 0 : crashed; + + // show the crash + if (test.state === 'failed') { + plane = color('plane crash', '✈'); + crashed = col; + } + + // render landing strip + stream.write('\u001b[' + (width + 1) + 'D\u001b[2A'); + stream.write(runway()); + stream.write('\n '); + stream.write(color('runway', Array(col).join('⋅'))); + stream.write(plane); + stream.write(color('runway', Array(width - col).join('⋅') + '\n')); + stream.write(runway()); + stream.write('\u001b[0m'); + }); + + runner.on('end', function() { + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Landing, Base); + +}).call(this,require('_process')) +},{"../utils":39,"./base":17,"_process":51}],27:[function(require,module,exports){ +(function (process){ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var color = Base.color; +var cursor = Base.cursor; + +/** + * Expose `List`. + */ + +exports = module.exports = List; + +/** + * Initialize a new `List` test reporter. + * + * @api public + * @param {Runner} runner + */ +function List(runner) { + Base.call(this, runner); + + var self = this; + var n = 0; + + runner.on('start', function() { + console.log(); + }); + + runner.on('test', function(test) { + process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); + }); + + runner.on('pending', function(test) { + var fmt = color('checkmark', ' -') + + color('pending', ' %s'); + console.log(fmt, test.fullTitle()); + }); + + runner.on('pass', function(test) { + var fmt = color('checkmark', ' ' + Base.symbols.dot) + + color('pass', ' %s: ') + + color(test.speed, '%dms'); + cursor.CR(); + console.log(fmt, test.fullTitle(), test.duration); + }); + + runner.on('fail', function(test) { + cursor.CR(); + console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); + }); + + runner.on('end', self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(List, Base); + +}).call(this,require('_process')) +},{"../utils":39,"./base":17,"_process":51}],28:[function(require,module,exports){ +(function (process){ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); + +/** + * Constants + */ + +var SUITE_PREFIX = '$'; + +/** + * Expose `Markdown`. + */ + +exports = module.exports = Markdown; + +/** + * Initialize a new `Markdown` reporter. + * + * @api public + * @param {Runner} runner + */ +function Markdown(runner) { + Base.call(this, runner); + + var level = 0; + var buf = ''; + + function title(str) { + return Array(level).join('#') + ' ' + str; + } + + function mapTOC(suite, obj) { + var ret = obj; + var key = SUITE_PREFIX + suite.title; + + obj = obj[key] = obj[key] || { suite: suite }; + suite.suites.forEach(function(suite) { + mapTOC(suite, obj); + }); + + return ret; + } + + function stringifyTOC(obj, level) { + ++level; + var buf = ''; + var link; + for (var key in obj) { + if (key === 'suite') { + continue; + } + if (key !== SUITE_PREFIX) { + link = ' - [' + key.substring(1) + ']'; + link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; + buf += Array(level).join(' ') + link; + } + buf += stringifyTOC(obj[key], level); + } + return buf; + } + + function generateTOC(suite) { + var obj = mapTOC(suite, {}); + return stringifyTOC(obj, 0); + } + + generateTOC(runner.suite); + + runner.on('suite', function(suite) { + ++level; + var slug = utils.slug(suite.fullTitle()); + buf += '' + '\n'; + buf += title(suite.title) + '\n'; + }); + + runner.on('suite end', function() { + --level; + }); + + runner.on('pass', function(test) { + var code = utils.clean(test.fn.toString()); + buf += test.title + '.\n'; + buf += '\n```js\n'; + buf += code + '\n'; + buf += '```\n\n'; + }); + + runner.on('end', function() { + process.stdout.write('# TOC\n'); + process.stdout.write(generateTOC(runner.suite)); + process.stdout.write(buf); + }); +} + +}).call(this,require('_process')) +},{"../utils":39,"./base":17,"_process":51}],29:[function(require,module,exports){ +(function (process){ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; + +/** + * Expose `Min`. + */ + +exports = module.exports = Min; + +/** + * Initialize a new `Min` minimal test reporter (best used with --watch). + * + * @api public + * @param {Runner} runner + */ +function Min(runner) { + Base.call(this, runner); + + runner.on('start', function() { + // clear screen + process.stdout.write('\u001b[2J'); + // set cursor position + process.stdout.write('\u001b[1;3H'); + }); + + runner.on('end', this.epilogue.bind(this)); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Min, Base); + +}).call(this,require('_process')) +},{"../utils":39,"./base":17,"_process":51}],30:[function(require,module,exports){ +(function (process){ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; + +/** + * Expose `Dot`. + */ + +exports = module.exports = NyanCat; + +/** + * Initialize a new `Dot` matrix test reporter. + * + * @param {Runner} runner + * @api public + */ + +function NyanCat(runner) { + Base.call(this, runner); + + var self = this; + var width = Base.window.width * .75 | 0; + var nyanCatWidth = this.nyanCatWidth = 11; + + this.colorIndex = 0; + this.numberOfLines = 4; + this.rainbowColors = self.generateColors(); + this.scoreboardWidth = 5; + this.tick = 0; + this.trajectories = [[], [], [], []]; + this.trajectoryWidthMax = (width - nyanCatWidth); + + runner.on('start', function() { + Base.cursor.hide(); + self.draw(); + }); + + runner.on('pending', function() { + self.draw(); + }); + + runner.on('pass', function() { + self.draw(); + }); + + runner.on('fail', function() { + self.draw(); + }); + + runner.on('end', function() { + Base.cursor.show(); + for (var i = 0; i < self.numberOfLines; i++) { + write('\n'); + } + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(NyanCat, Base); + +/** + * Draw the nyan cat + * + * @api private + */ + +NyanCat.prototype.draw = function() { + this.appendRainbow(); + this.drawScoreboard(); + this.drawRainbow(); + this.drawNyanCat(); + this.tick = !this.tick; +}; + +/** + * Draw the "scoreboard" showing the number + * of passes, failures and pending tests. + * + * @api private + */ + +NyanCat.prototype.drawScoreboard = function() { + var stats = this.stats; + + function draw(type, n) { + write(' '); + write(Base.color(type, n)); + write('\n'); + } + + draw('green', stats.passes); + draw('fail', stats.failures); + draw('pending', stats.pending); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Append the rainbow. + * + * @api private + */ + +NyanCat.prototype.appendRainbow = function() { + var segment = this.tick ? '_' : '-'; + var rainbowified = this.rainbowify(segment); + + for (var index = 0; index < this.numberOfLines; index++) { + var trajectory = this.trajectories[index]; + if (trajectory.length >= this.trajectoryWidthMax) { + trajectory.shift(); + } + trajectory.push(rainbowified); + } +}; + +/** + * Draw the rainbow. + * + * @api private + */ + +NyanCat.prototype.drawRainbow = function() { + var self = this; + + this.trajectories.forEach(function(line) { + write('\u001b[' + self.scoreboardWidth + 'C'); + write(line.join('')); + write('\n'); + }); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw the nyan cat + * + * @api private + */ +NyanCat.prototype.drawNyanCat = function() { + var self = this; + var startWidth = this.scoreboardWidth + this.trajectories[0].length; + var dist = '\u001b[' + startWidth + 'C'; + var padding = ''; + + write(dist); + write('_,------,'); + write('\n'); + + write(dist); + padding = self.tick ? ' ' : ' '; + write('_|' + padding + '/\\_/\\ '); + write('\n'); + + write(dist); + padding = self.tick ? '_' : '__'; + var tail = self.tick ? '~' : '^'; + write(tail + '|' + padding + this.face() + ' '); + write('\n'); + + write(dist); + padding = self.tick ? ' ' : ' '; + write(padding + '"" "" '); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw nyan cat face. + * + * @api private + * @return {string} + */ + +NyanCat.prototype.face = function() { + var stats = this.stats; + if (stats.failures) { + return '( x .x)'; + } else if (stats.pending) { + return '( o .o)'; + } else if (stats.passes) { + return '( ^ .^)'; + } + return '( - .-)'; +}; + +/** + * Move cursor up `n`. + * + * @api private + * @param {number} n + */ + +NyanCat.prototype.cursorUp = function(n) { + write('\u001b[' + n + 'A'); +}; + +/** + * Move cursor down `n`. + * + * @api private + * @param {number} n + */ + +NyanCat.prototype.cursorDown = function(n) { + write('\u001b[' + n + 'B'); +}; + +/** + * Generate rainbow colors. + * + * @api private + * @return {Array} + */ +NyanCat.prototype.generateColors = function() { + var colors = []; + + for (var i = 0; i < (6 * 7); i++) { + var pi3 = Math.floor(Math.PI / 3); + var n = (i * (1.0 / 6)); + var r = Math.floor(3 * Math.sin(n) + 3); + var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); + var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); + colors.push(36 * r + 6 * g + b + 16); + } + + return colors; +}; + +/** + * Apply rainbow to the given `str`. + * + * @api private + * @param {string} str + * @return {string} + */ +NyanCat.prototype.rainbowify = function(str) { + if (!Base.useColors) { + return str; + } + var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; + this.colorIndex += 1; + return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; +}; + +/** + * Stdout helper. + * + * @param {string} string A message to write to stdout. + */ +function write(string) { + process.stdout.write(string); +} + +}).call(this,require('_process')) +},{"../utils":39,"./base":17,"_process":51}],31:[function(require,module,exports){ +(function (process){ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var color = Base.color; +var cursor = Base.cursor; + +/** + * Expose `Progress`. + */ + +exports = module.exports = Progress; + +/** + * General progress bar color. + */ + +Base.colors.progress = 90; + +/** + * Initialize a new `Progress` bar test reporter. + * + * @api public + * @param {Runner} runner + * @param {Object} options + */ +function Progress(runner, options) { + Base.call(this, runner); + + var self = this; + var width = Base.window.width * .50 | 0; + var total = runner.total; + var complete = 0; + var lastN = -1; + + // default chars + options = options || {}; + options.open = options.open || '['; + options.complete = options.complete || '▬'; + options.incomplete = options.incomplete || Base.symbols.dot; + options.close = options.close || ']'; + options.verbose = false; + + // tests started + runner.on('start', function() { + console.log(); + cursor.hide(); + }); + + // tests complete + runner.on('test end', function() { + complete++; + + var percent = complete / total; + var n = width * percent | 0; + var i = width - n; + + if (n === lastN && !options.verbose) { + // Don't re-render the line if it hasn't changed + return; + } + lastN = n; + + cursor.CR(); + process.stdout.write('\u001b[J'); + process.stdout.write(color('progress', ' ' + options.open)); + process.stdout.write(Array(n).join(options.complete)); + process.stdout.write(Array(i).join(options.incomplete)); + process.stdout.write(color('progress', options.close)); + if (options.verbose) { + process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); + } + }); + + // tests are complete, output some stats + // and the failures if any + runner.on('end', function() { + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Progress, Base); + +}).call(this,require('_process')) +},{"../utils":39,"./base":17,"_process":51}],32:[function(require,module,exports){ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var color = Base.color; +var cursor = Base.cursor; + +/** + * Expose `Spec`. + */ + +exports = module.exports = Spec; + +/** + * Initialize a new `Spec` test reporter. + * + * @api public + * @param {Runner} runner + */ +function Spec(runner) { + Base.call(this, runner); + + var self = this; + var indents = 0; + var n = 0; + + function indent() { + return Array(indents).join(' '); + } + + runner.on('start', function() { + console.log(); + }); + + runner.on('suite', function(suite) { + ++indents; + console.log(color('suite', '%s%s'), indent(), suite.title); + }); + + runner.on('suite end', function() { + --indents; + if (indents === 1) { + console.log(); + } + }); + + runner.on('pending', function(test) { + var fmt = indent() + color('pending', ' - %s'); + console.log(fmt, test.title); + }); + + runner.on('pass', function(test) { + var fmt; + if (test.speed === 'fast') { + fmt = indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s'); + cursor.CR(); + console.log(fmt, test.title); + } else { + fmt = indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s') + + color(test.speed, ' (%dms)'); + cursor.CR(); + console.log(fmt, test.title, test.duration); + } + }); + + runner.on('fail', function(test) { + cursor.CR(); + console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); + }); + + runner.on('end', self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Spec, Base); + +},{"../utils":39,"./base":17}],33:[function(require,module,exports){ +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `TAP`. + */ + +exports = module.exports = TAP; + +/** + * Initialize a new `TAP` reporter. + * + * @api public + * @param {Runner} runner + */ +function TAP(runner) { + Base.call(this, runner); + + var n = 1; + var passes = 0; + var failures = 0; + + runner.on('start', function() { + var total = runner.grepTotal(runner.suite); + console.log('%d..%d', 1, total); + }); + + runner.on('test end', function() { + ++n; + }); + + runner.on('pending', function(test) { + console.log('ok %d %s # SKIP -', n, title(test)); + }); + + runner.on('pass', function(test) { + passes++; + console.log('ok %d %s', n, title(test)); + }); + + runner.on('fail', function(test, err) { + failures++; + console.log('not ok %d %s', n, title(test)); + if (err.stack) { + console.log(err.stack.replace(/^/gm, ' ')); + } + }); + + runner.on('end', function() { + console.log('# tests ' + (passes + failures)); + console.log('# pass ' + passes); + console.log('# fail ' + failures); + }); +} + +/** + * Return a TAP-safe title of `test` + * + * @api private + * @param {Object} test + * @return {String} + */ +function title(test) { + return test.fullTitle().replace(/#/g, ''); +} + +},{"./base":17}],34:[function(require,module,exports){ +(function (global){ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); +var inherits = utils.inherits; +var fs = require('fs'); +var escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +/* eslint-disable no-unused-vars, no-native-reassign */ +var Date = global.Date; +var setTimeout = global.setTimeout; +var setInterval = global.setInterval; +var clearTimeout = global.clearTimeout; +var clearInterval = global.clearInterval; +/* eslint-enable no-unused-vars, no-native-reassign */ + +/** + * Expose `XUnit`. + */ + +exports = module.exports = XUnit; + +/** + * Initialize a new `XUnit` reporter. + * + * @api public + * @param {Runner} runner + */ +function XUnit(runner, options) { + Base.call(this, runner); + + var stats = this.stats; + var tests = []; + var self = this; + + if (options.reporterOptions && options.reporterOptions.output) { + if (!fs.createWriteStream) { + throw new Error('file output not supported in browser'); + } + self.fileStream = fs.createWriteStream(options.reporterOptions.output); + } + + runner.on('pending', function(test) { + tests.push(test); + }); + + runner.on('pass', function(test) { + tests.push(test); + }); + + runner.on('fail', function(test) { + tests.push(test); + }); + + runner.on('end', function() { + self.write(tag('testsuite', { + name: 'Mocha Tests', + tests: stats.tests, + failures: stats.failures, + errors: stats.failures, + skipped: stats.tests - stats.failures - stats.passes, + timestamp: (new Date()).toUTCString(), + time: (stats.duration / 1000) || 0 + }, false)); + + tests.forEach(function(t) { + self.test(t); + }); + + self.write(''); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(XUnit, Base); + +/** + * Override done to close the stream (if it's a file). + * + * @param failures + * @param {Function} fn + */ +XUnit.prototype.done = function(failures, fn) { + if (this.fileStream) { + this.fileStream.end(function() { + fn(failures); + }); + } else { + fn(failures); + } +}; + +/** + * Write out the given line. + * + * @param {string} line + */ +XUnit.prototype.write = function(line) { + if (this.fileStream) { + this.fileStream.write(line + '\n'); + } else { + console.log(line); + } +}; + +/** + * Output tag for the given `test.` + * + * @param {Test} test + */ +XUnit.prototype.test = function(test) { + var attrs = { + classname: test.parent.fullTitle(), + name: test.title, + time: (test.duration / 1000) || 0 + }; + + if (test.state === 'failed') { + var err = test.err; + this.write(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + '\n' + err.stack)))); + } else if (test.pending) { + this.write(tag('testcase', attrs, false, tag('skipped', {}, true))); + } else { + this.write(tag('testcase', attrs, true)); + } +}; + +/** + * HTML tag helper. + * + * @param name + * @param attrs + * @param close + * @param content + * @return {string} + */ +function tag(name, attrs, close, content) { + var end = close ? '/>' : '>'; + var pairs = []; + var tag; + + for (var key in attrs) { + if (Object.prototype.hasOwnProperty.call(attrs, key)) { + pairs.push(key + '="' + escape(attrs[key]) + '"'); + } + } + + tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; + if (content) { + tag += content + ''; +} + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../utils":39,"./base":17,"fs":41}],35:[function(require,module,exports){ +(function (global){ +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter; +var Pending = require('./pending'); +var debug = require('debug')('mocha:runnable'); +var milliseconds = require('./ms'); +var utils = require('./utils'); +var inherits = utils.inherits; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +/* eslint-disable no-unused-vars, no-native-reassign */ +var Date = global.Date; +var setTimeout = global.setTimeout; +var setInterval = global.setInterval; +var clearTimeout = global.clearTimeout; +var clearInterval = global.clearInterval; +/* eslint-enable no-unused-vars, no-native-reassign */ + +/** + * Object#toString(). + */ + +var toString = Object.prototype.toString; + +/** + * Expose `Runnable`. + */ + +module.exports = Runnable; + +/** + * Initialize a new `Runnable` with the given `title` and callback `fn`. + * + * @param {String} title + * @param {Function} fn + * @api private + * @param {string} title + * @param {Function} fn + */ +function Runnable(title, fn) { + this.title = title; + this.fn = fn; + this.async = fn && fn.length; + this.sync = !this.async; + this._timeout = 2000; + this._slow = 75; + this._enableTimeouts = true; + this.timedOut = false; + this._trace = new Error('done() called multiple times'); +} + +/** + * Inherit from `EventEmitter.prototype`. + */ +inherits(Runnable, EventEmitter); + +/** + * Set & get timeout `ms`. + * + * @api private + * @param {number|string} ms + * @return {Runnable|number} ms or Runnable instance. + */ +Runnable.prototype.timeout = function(ms) { + if (!arguments.length) { + return this._timeout; + } + if (ms === 0) { + this._enableTimeouts = false; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('timeout %d', ms); + this._timeout = ms; + if (this.timer) { + this.resetTimeout(); + } + return this; +}; + +/** + * Set & get slow `ms`. + * + * @api private + * @param {number|string} ms + * @return {Runnable|number} ms or Runnable instance. + */ +Runnable.prototype.slow = function(ms) { + if (!arguments.length) { + return this._slow; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('timeout %d', ms); + this._slow = ms; + return this; +}; + +/** + * Set and get whether timeout is `enabled`. + * + * @api private + * @param {boolean} enabled + * @return {Runnable|boolean} enabled or Runnable instance. + */ +Runnable.prototype.enableTimeouts = function(enabled) { + if (!arguments.length) { + return this._enableTimeouts; + } + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Halt and mark as pending. + * + * @api private + */ +Runnable.prototype.skip = function() { + throw new Pending(); +}; + +/** + * Return the full title generated by recursively concatenating the parent's + * full title. + * + * @api public + * @return {string} + */ +Runnable.prototype.fullTitle = function() { + return this.parent.fullTitle() + ' ' + this.title; +}; + +/** + * Clear the timeout. + * + * @api private + */ +Runnable.prototype.clearTimeout = function() { + clearTimeout(this.timer); +}; + +/** + * Inspect the runnable void of private properties. + * + * @api private + * @return {string} + */ +Runnable.prototype.inspect = function() { + return JSON.stringify(this, function(key, val) { + if (key[0] === '_') { + return; + } + if (key === 'parent') { + return '#'; + } + if (key === 'ctx') { + return '#'; + } + return val; + }, 2); +}; + +/** + * Reset the timeout. + * + * @api private + */ +Runnable.prototype.resetTimeout = function() { + var self = this; + var ms = this.timeout() || 1e9; + + if (!this._enableTimeouts) { + return; + } + this.clearTimeout(); + this.timer = setTimeout(function() { + if (!self._enableTimeouts) { + return; + } + self.callback(new Error('timeout of ' + ms + 'ms exceeded. Ensure the done() callback is being called in this test.')); + self.timedOut = true; + }, ms); +}; + +/** + * Whitelist a list of globals for this test run. + * + * @api private + * @param {string[]} globals + */ +Runnable.prototype.globals = function(globals) { + this._allowedGlobals = globals; +}; + +/** + * Run the test and invoke `fn(err)`. + * + * @param {Function} fn + * @api private + */ +Runnable.prototype.run = function(fn) { + var self = this; + var start = new Date(); + var ctx = this.ctx; + var finished; + var emitted; + + // Sometimes the ctx exists, but it is not runnable + if (ctx && ctx.runnable) { + ctx.runnable(this); + } + + // called multiple times + function multiple(err) { + if (emitted) { + return; + } + emitted = true; + self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate')); + } + + // finished + function done(err) { + var ms = self.timeout(); + if (self.timedOut) { + return; + } + if (finished) { + return multiple(err || self._trace); + } + + self.clearTimeout(); + self.duration = new Date() - start; + finished = true; + if (!err && self.duration > ms && self._enableTimeouts) { + err = new Error('timeout of ' + ms + 'ms exceeded. Ensure the done() callback is being called in this test.'); + } + fn(err); + } + + // for .resetTimeout() + this.callback = done; + + // explicit async with `done` argument + if (this.async) { + this.resetTimeout(); + + if (this.allowUncaught) { + return callFnAsync(this.fn); + } + try { + callFnAsync(this.fn); + } catch (err) { + done(utils.getError(err)); + } + return; + } + + if (this.allowUncaught) { + callFn(this.fn); + done(); + return; + } + + // sync or promise-returning + try { + if (this.pending) { + done(); + } else { + callFn(this.fn); + } + } catch (err) { + done(utils.getError(err)); + } + + function callFn(fn) { + var result = fn.call(ctx); + if (result && typeof result.then === 'function') { + self.resetTimeout(); + result + .then(function() { + done(); + }, + function(reason) { + done(reason || new Error('Promise rejected with no or falsy reason')); + }); + } else { + if (self.asyncOnly) { + return done(new Error('--async-only option in use without declaring `done()` or returning a promise')); + } + + done(); + } + } + + function callFnAsync(fn) { + fn.call(ctx, function(err) { + if (err instanceof Error || toString.call(err) === '[object Error]') { + return done(err); + } + if (err) { + if (Object.prototype.toString.call(err) === '[object Object]') { + return done(new Error('done() invoked with non-Error: ' + + JSON.stringify(err))); + } + return done(new Error('done() invoked with non-Error: ' + err)); + } + done(); + }); + } +}; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./ms":15,"./pending":16,"./utils":39,"debug":2,"events":3}],36:[function(require,module,exports){ +(function (process,global){ +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter; +var Pending = require('./pending'); +var utils = require('./utils'); +var inherits = utils.inherits; +var debug = require('debug')('mocha:runner'); +var Runnable = require('./runnable'); +var filter = utils.filter; +var indexOf = utils.indexOf; +var keys = utils.keys; +var stackFilter = utils.stackTraceFilter(); +var stringify = utils.stringify; +var type = utils.type; +var undefinedError = utils.undefinedError; + +/** + * Non-enumerable globals. + */ + +var globals = [ + 'setTimeout', + 'clearTimeout', + 'setInterval', + 'clearInterval', + 'XMLHttpRequest', + 'Date', + 'setImmediate', + 'clearImmediate' +]; + +/** + * Expose `Runner`. + */ + +module.exports = Runner; + +/** + * Initialize a `Runner` for the given `suite`. + * + * Events: + * + * - `start` execution started + * - `end` execution complete + * - `suite` (suite) test suite execution started + * - `suite end` (suite) all tests (and sub-suites) have finished + * - `test` (test) test execution started + * - `test end` (test) test completed + * - `hook` (hook) hook execution started + * - `hook end` (hook) hook complete + * - `pass` (test) test passed + * - `fail` (test, err) test failed + * - `pending` (test) test pending + * + * @api public + * @param {Suite} suite Root suite + * @param {boolean} [delay] Whether or not to delay execution of root suite + * until ready. + */ +function Runner(suite, delay) { + var self = this; + this._globals = []; + this._abort = false; + this._delay = delay; + this.suite = suite; + this.started = false; + this.total = suite.total(); + this.failures = 0; + this.on('test end', function(test) { + self.checkGlobals(test); + }); + this.on('hook end', function(hook) { + self.checkGlobals(hook); + }); + this._defaultGrep = /.*/; + this.grep(this._defaultGrep); + this.globals(this.globalProps().concat(extraGlobals())); +} + +/** + * Wrapper for setImmediate, process.nextTick, or browser polyfill. + * + * @param {Function} fn + * @api private + */ +Runner.immediately = global.setImmediate || process.nextTick; + +/** + * Inherit from `EventEmitter.prototype`. + */ +inherits(Runner, EventEmitter); + +/** + * Run tests with full titles matching `re`. Updates runner.total + * with number of tests matched. + * + * @param {RegExp} re + * @param {Boolean} invert + * @return {Runner} for chaining + * @api public + * @param {RegExp} re + * @param {boolean} invert + * @return {Runner} Runner instance. + */ +Runner.prototype.grep = function(re, invert) { + debug('grep %s', re); + this._grep = re; + this._invert = invert; + this.total = this.grepTotal(this.suite); + return this; +}; + +/** + * Returns the number of tests matching the grep search for the + * given suite. + * + * @param {Suite} suite + * @return {Number} + * @api public + * @param {Suite} suite + * @return {number} + */ +Runner.prototype.grepTotal = function(suite) { + var self = this; + var total = 0; + + suite.eachTest(function(test) { + var match = self._grep.test(test.fullTitle()); + if (self._invert) { + match = !match; + } + if (match) { + total++; + } + }); + + return total; +}; + +/** + * Return a list of global properties. + * + * @return {Array} + * @api private + */ +Runner.prototype.globalProps = function() { + var props = keys(global); + + // non-enumerables + for (var i = 0; i < globals.length; ++i) { + if (~indexOf(props, globals[i])) { + continue; + } + props.push(globals[i]); + } + + return props; +}; + +/** + * Allow the given `arr` of globals. + * + * @param {Array} arr + * @return {Runner} for chaining + * @api public + * @param {Array} arr + * @return {Runner} Runner instance. + */ +Runner.prototype.globals = function(arr) { + if (!arguments.length) { + return this._globals; + } + debug('globals %j', arr); + this._globals = this._globals.concat(arr); + return this; +}; + +/** + * Check for global variable leaks. + * + * @api private + */ +Runner.prototype.checkGlobals = function(test) { + if (this.ignoreLeaks) { + return; + } + var ok = this._globals; + + var globals = this.globalProps(); + var leaks; + + if (test) { + ok = ok.concat(test._allowedGlobals || []); + } + + if (this.prevGlobalsLength === globals.length) { + return; + } + this.prevGlobalsLength = globals.length; + + leaks = filterLeaks(ok, globals); + this._globals = this._globals.concat(leaks); + + if (leaks.length > 1) { + this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + '')); + } else if (leaks.length) { + this.fail(test, new Error('global leak detected: ' + leaks[0])); + } +}; + +/** + * Fail the given `test`. + * + * @api private + * @param {Test} test + * @param {Error} err + */ +Runner.prototype.fail = function(test, err) { + ++this.failures; + test.state = 'failed'; + + if (!(err instanceof Error || err && typeof err.message === 'string')) { + err = new Error('the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)'); + } + + err.stack = (this.fullStackTrace || !err.stack) + ? err.stack + : stackFilter(err.stack); + + this.emit('fail', test, err); +}; + +/** + * Fail the given `hook` with `err`. + * + * Hook failures work in the following pattern: + * - If bail, then exit + * - Failed `before` hook skips all tests in a suite and subsuites, + * but jumps to corresponding `after` hook + * - Failed `before each` hook skips remaining tests in a + * suite and jumps to corresponding `after each` hook, + * which is run only once + * - Failed `after` hook does not alter + * execution order + * - Failed `after each` hook skips remaining tests in a + * suite and subsuites, but executes other `after each` + * hooks + * + * @api private + * @param {Hook} hook + * @param {Error} err + */ +Runner.prototype.failHook = function(hook, err) { + if (hook.ctx && hook.ctx.currentTest) { + hook.originalTitle = hook.originalTitle || hook.title; + hook.title = hook.originalTitle + ' for "' + hook.ctx.currentTest.title + '"'; + } + + this.fail(hook, err); + if (this.suite.bail()) { + this.emit('end'); + } +}; + +/** + * Run hook `name` callbacks and then invoke `fn()`. + * + * @api private + * @param {string} name + * @param {Function} fn + */ + +Runner.prototype.hook = function(name, fn) { + var suite = this.suite; + var hooks = suite['_' + name]; + var self = this; + + function next(i) { + var hook = hooks[i]; + if (!hook) { + return fn(); + } + self.currentRunnable = hook; + + hook.ctx.currentTest = self.test; + + self.emit('hook', hook); + + if (!hook.listeners('error').length) { + hook.on('error', function(err) { + self.failHook(hook, err); + }); + } + + hook.run(function(err) { + var testError = hook.error(); + if (testError) { + self.fail(self.test, testError); + } + if (err) { + if (err instanceof Pending) { + suite.pending = true; + } else { + self.failHook(hook, err); + + // stop executing hooks, notify callee of hook err + return fn(err); + } + } + self.emit('hook end', hook); + delete hook.ctx.currentTest; + next(++i); + }); + } + + Runner.immediately(function() { + next(0); + }); +}; + +/** + * Run hook `name` for the given array of `suites` + * in order, and callback `fn(err, errSuite)`. + * + * @api private + * @param {string} name + * @param {Array} suites + * @param {Function} fn + */ +Runner.prototype.hooks = function(name, suites, fn) { + var self = this; + var orig = this.suite; + + function next(suite) { + self.suite = suite; + + if (!suite) { + self.suite = orig; + return fn(); + } + + self.hook(name, function(err) { + if (err) { + var errSuite = self.suite; + self.suite = orig; + return fn(err, errSuite); + } + + next(suites.pop()); + }); + } + + next(suites.pop()); +}; + +/** + * Run hooks from the top level down. + * + * @param {String} name + * @param {Function} fn + * @api private + */ +Runner.prototype.hookUp = function(name, fn) { + var suites = [this.suite].concat(this.parents()).reverse(); + this.hooks(name, suites, fn); +}; + +/** + * Run hooks from the bottom up. + * + * @param {String} name + * @param {Function} fn + * @api private + */ +Runner.prototype.hookDown = function(name, fn) { + var suites = [this.suite].concat(this.parents()); + this.hooks(name, suites, fn); +}; + +/** + * Return an array of parent Suites from + * closest to furthest. + * + * @return {Array} + * @api private + */ +Runner.prototype.parents = function() { + var suite = this.suite; + var suites = []; + while (suite.parent) { + suite = suite.parent; + suites.push(suite); + } + return suites; +}; + +/** + * Run the current test and callback `fn(err)`. + * + * @param {Function} fn + * @api private + */ +Runner.prototype.runTest = function(fn) { + var self = this; + var test = this.test; + + if (this.asyncOnly) { + test.asyncOnly = true; + } + + if (this.allowUncaught) { + test.allowUncaught = true; + return test.run(fn); + } + try { + test.on('error', function(err) { + self.fail(test, err); + }); + test.run(fn); + } catch (err) { + fn(err); + } +}; + +/** + * Run tests in the given `suite` and invoke the callback `fn()` when complete. + * + * @api private + * @param {Suite} suite + * @param {Function} fn + */ +Runner.prototype.runTests = function(suite, fn) { + var self = this; + var tests = suite.tests.slice(); + var test; + + function hookErr(_, errSuite, after) { + // before/after Each hook for errSuite failed: + var orig = self.suite; + + // for failed 'after each' hook start from errSuite parent, + // otherwise start from errSuite itself + self.suite = after ? errSuite.parent : errSuite; + + if (self.suite) { + // call hookUp afterEach + self.hookUp('afterEach', function(err2, errSuite2) { + self.suite = orig; + // some hooks may fail even now + if (err2) { + return hookErr(err2, errSuite2, true); + } + // report error suite + fn(errSuite); + }); + } else { + // there is no need calling other 'after each' hooks + self.suite = orig; + fn(errSuite); + } + } + + function next(err, errSuite) { + // if we bail after first err + if (self.failures && suite._bail) { + return fn(); + } + + if (self._abort) { + return fn(); + } + + if (err) { + return hookErr(err, errSuite, true); + } + + // next test + test = tests.shift(); + + // all done + if (!test) { + return fn(); + } + + // grep + var match = self._grep.test(test.fullTitle()); + if (self._invert) { + match = !match; + } + if (!match) { + // Run immediately only if we have defined a grep. When we + // define a grep — It can cause maximum callstack error if + // the grep is doing a large recursive loop by neglecting + // all tests. The run immediately function also comes with + // a performance cost. So we don't want to run immediately + // if we run the whole test suite, because running the whole + // test suite don't do any immediate recursive loops. Thus, + // allowing a JS runtime to breathe. + if (self._grep !== self._defaultGrep) { + Runner.immediately(next); + } else { + next(); + } + return; + } + + // pending + if (test.pending) { + self.emit('pending', test); + self.emit('test end', test); + return next(); + } + + // execute test and hook(s) + self.emit('test', self.test = test); + self.hookDown('beforeEach', function(err, errSuite) { + if (suite.pending) { + self.emit('pending', test); + self.emit('test end', test); + return next(); + } + if (err) { + return hookErr(err, errSuite, false); + } + self.currentRunnable = self.test; + self.runTest(function(err) { + test = self.test; + + if (err) { + if (err instanceof Pending) { + self.emit('pending', test); + } else { + self.fail(test, err); + } + self.emit('test end', test); + + if (err instanceof Pending) { + return next(); + } + + return self.hookUp('afterEach', next); + } + + test.state = 'passed'; + self.emit('pass', test); + self.emit('test end', test); + self.hookUp('afterEach', next); + }); + }); + } + + this.next = next; + this.hookErr = hookErr; + next(); +}; + +/** + * Run the given `suite` and invoke the callback `fn()` when complete. + * + * @api private + * @param {Suite} suite + * @param {Function} fn + */ +Runner.prototype.runSuite = function(suite, fn) { + var i = 0; + var self = this; + var total = this.grepTotal(suite); + var afterAllHookCalled = false; + + debug('run suite %s', suite.fullTitle()); + + if (!total || (self.failures && suite._bail)) { + return fn(); + } + + this.emit('suite', this.suite = suite); + + function next(errSuite) { + if (errSuite) { + // current suite failed on a hook from errSuite + if (errSuite === suite) { + // if errSuite is current suite + // continue to the next sibling suite + return done(); + } + // errSuite is among the parents of current suite + // stop execution of errSuite and all sub-suites + return done(errSuite); + } + + if (self._abort) { + return done(); + } + + var curr = suite.suites[i++]; + if (!curr) { + return done(); + } + + // Avoid grep neglecting large number of tests causing a + // huge recursive loop and thus a maximum call stack error. + // See comment in `this.runTests()` for more information. + if (self._grep !== self._defaultGrep) { + Runner.immediately(function() { + self.runSuite(curr, next); + }); + } else { + self.runSuite(curr, next); + } + } + + function done(errSuite) { + self.suite = suite; + self.nextSuite = next; + + if (afterAllHookCalled) { + fn(errSuite); + } else { + // mark that the afterAll block has been called once + // and so can be skipped if there is an error in it. + afterAllHookCalled = true; + self.hook('afterAll', function() { + self.emit('suite end', suite); + fn(errSuite); + }); + } + } + + this.nextSuite = next; + + this.hook('beforeAll', function(err) { + if (err) { + return done(); + } + self.runTests(suite, next); + }); +}; + +/** + * Handle uncaught exceptions. + * + * @param {Error} err + * @api private + */ +Runner.prototype.uncaught = function(err) { + if (err) { + debug('uncaught exception %s', err !== function() { + return this; + }.call(err) ? err : (err.message || err)); + } else { + debug('uncaught undefined exception'); + err = undefinedError(); + } + err.uncaught = true; + + var runnable = this.currentRunnable; + + if (!runnable) { + runnable = new Runnable('Uncaught error outside test suite'); + runnable.parent = this.suite; + + if (this.started) { + this.fail(runnable, err); + } else { + // Can't recover from this failure + this.emit('start'); + this.fail(runnable, err); + this.emit('end'); + } + + return; + } + + runnable.clearTimeout(); + + // Ignore errors if complete + if (runnable.state) { + return; + } + this.fail(runnable, err); + + // recover from test + if (runnable.type === 'test') { + this.emit('test end', runnable); + this.hookUp('afterEach', this.next); + return; + } + + // recover from hooks + if (runnable.type === 'hook') { + var errSuite = this.suite; + // if hook failure is in afterEach block + if (runnable.fullTitle().indexOf('after each') > -1) { + return this.hookErr(err, errSuite, true); + } + // if hook failure is in beforeEach block + if (runnable.fullTitle().indexOf('before each') > -1) { + return this.hookErr(err, errSuite, false); + } + // if hook failure is in after or before blocks + return this.nextSuite(errSuite); + } + + // bail + this.emit('end'); +}; + +/** + * Run the root suite and invoke `fn(failures)` + * on completion. + * + * @param {Function} fn + * @return {Runner} for chaining + * @api public + * @param {Function} fn + * @return {Runner} Runner instance. + */ +Runner.prototype.run = function(fn) { + var self = this; + var rootSuite = this.suite; + + fn = fn || function() {}; + + function uncaught(err) { + self.uncaught(err); + } + + function start() { + self.started = true; + self.emit('start'); + self.runSuite(rootSuite, function() { + debug('finished running'); + self.emit('end'); + }); + } + + debug('start'); + + // callback + this.on('end', function() { + debug('end'); + process.removeListener('uncaughtException', uncaught); + fn(self.failures); + }); + + // uncaught exception + process.on('uncaughtException', uncaught); + + if (this._delay) { + // for reporters, I guess. + // might be nice to debounce some dots while we wait. + this.emit('waiting', rootSuite); + rootSuite.once('run', start); + } else { + start(); + } + + return this; +}; + +/** + * Cleanly abort execution. + * + * @api public + * @return {Runner} Runner instance. + */ +Runner.prototype.abort = function() { + debug('aborting'); + this._abort = true; + + return this; +}; + +/** + * Filter leaks with the given globals flagged as `ok`. + * + * @api private + * @param {Array} ok + * @param {Array} globals + * @return {Array} + */ +function filterLeaks(ok, globals) { + return filter(globals, function(key) { + // Firefox and Chrome exposes iframes as index inside the window object + if (/^d+/.test(key)) { + return false; + } + + // in firefox + // if runner runs in an iframe, this iframe's window.getInterface method not init at first + // it is assigned in some seconds + if (global.navigator && (/^getInterface/).test(key)) { + return false; + } + + // an iframe could be approached by window[iframeIndex] + // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak + if (global.navigator && (/^\d+/).test(key)) { + return false; + } + + // Opera and IE expose global variables for HTML element IDs (issue #243) + if (/^mocha-/.test(key)) { + return false; + } + + var matched = filter(ok, function(ok) { + if (~ok.indexOf('*')) { + return key.indexOf(ok.split('*')[0]) === 0; + } + return key === ok; + }); + return !matched.length && (!global.navigator || key !== 'onerror'); + }); +} + +/** + * Array of globals dependent on the environment. + * + * @return {Array} + * @api private + */ +function extraGlobals() { + if (typeof process === 'object' && typeof process.version === 'string') { + var parts = process.version.split('.'); + var nodeVersion = utils.reduce(parts, function(a, v) { + return a << 8 | v; + }); + + // 'errno' was renamed to process._errno in v0.9.11. + + if (nodeVersion < 0x00090B) { + return ['errno']; + } + } + + return []; +} + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./pending":16,"./runnable":35,"./utils":39,"_process":51,"debug":2,"events":3}],37:[function(require,module,exports){ +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter; +var Hook = require('./hook'); +var utils = require('./utils'); +var inherits = utils.inherits; +var debug = require('debug')('mocha:suite'); +var milliseconds = require('./ms'); + +/** + * Expose `Suite`. + */ + +exports = module.exports = Suite; + +/** + * Create a new `Suite` with the given `title` and parent `Suite`. When a suite + * with the same title is already present, that suite is returned to provide + * nicer reporter and more flexible meta-testing. + * + * @api public + * @param {Suite} parent + * @param {string} title + * @return {Suite} + */ +exports.create = function(parent, title) { + var suite = new Suite(title, parent.ctx); + suite.parent = parent; + if (parent.pending) { + suite.pending = true; + } + title = suite.fullTitle(); + parent.addSuite(suite); + return suite; +}; + +/** + * Initialize a new `Suite` with the given `title` and `ctx`. + * + * @api private + * @param {string} title + * @param {Context} parentContext + */ +function Suite(title, parentContext) { + this.title = title; + function Context() {} + Context.prototype = parentContext; + this.ctx = new Context(); + this.suites = []; + this.tests = []; + this.pending = false; + this._beforeEach = []; + this._beforeAll = []; + this._afterEach = []; + this._afterAll = []; + this.root = !title; + this._timeout = 2000; + this._enableTimeouts = true; + this._slow = 75; + this._bail = false; + this.delayed = false; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ +inherits(Suite, EventEmitter); + +/** + * Return a clone of this `Suite`. + * + * @api private + * @return {Suite} + */ +Suite.prototype.clone = function() { + var suite = new Suite(this.title); + debug('clone'); + suite.ctx = this.ctx; + suite.timeout(this.timeout()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + return suite; +}; + +/** + * Set timeout `ms` or short-hand such as "2s". + * + * @api private + * @param {number|string} ms + * @return {Suite|number} for chaining + */ +Suite.prototype.timeout = function(ms) { + if (!arguments.length) { + return this._timeout; + } + if (ms.toString() === '0') { + this._enableTimeouts = false; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('timeout %d', ms); + this._timeout = parseInt(ms, 10); + return this; +}; + +/** + * Set timeout to `enabled`. + * + * @api private + * @param {boolean} enabled + * @return {Suite|boolean} self or enabled + */ +Suite.prototype.enableTimeouts = function(enabled) { + if (!arguments.length) { + return this._enableTimeouts; + } + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Set slow `ms` or short-hand such as "2s". + * + * @api private + * @param {number|string} ms + * @return {Suite|number} for chaining + */ +Suite.prototype.slow = function(ms) { + if (!arguments.length) { + return this._slow; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('slow %d', ms); + this._slow = ms; + return this; +}; + +/** + * Sets whether to bail after first error. + * + * @api private + * @param {boolean} bail + * @return {Suite|number} for chaining + */ +Suite.prototype.bail = function(bail) { + if (!arguments.length) { + return this._bail; + } + debug('bail %s', bail); + this._bail = bail; + return this; +}; + +/** + * Run `fn(test[, done])` before running tests. + * + * @api private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.beforeAll = function(title, fn) { + if (this.pending) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"before all" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._beforeAll.push(hook); + this.emit('beforeAll', hook); + return this; +}; + +/** + * Run `fn(test[, done])` after running tests. + * + * @api private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.afterAll = function(title, fn) { + if (this.pending) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"after all" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._afterAll.push(hook); + this.emit('afterAll', hook); + return this; +}; + +/** + * Run `fn(test[, done])` before each test case. + * + * @api private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.beforeEach = function(title, fn) { + if (this.pending) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"before each" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._beforeEach.push(hook); + this.emit('beforeEach', hook); + return this; +}; + +/** + * Run `fn(test[, done])` after each test case. + * + * @api private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.afterEach = function(title, fn) { + if (this.pending) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"after each" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._afterEach.push(hook); + this.emit('afterEach', hook); + return this; +}; + +/** + * Add a test `suite`. + * + * @api private + * @param {Suite} suite + * @return {Suite} for chaining + */ +Suite.prototype.addSuite = function(suite) { + suite.parent = this; + suite.timeout(this.timeout()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + this.suites.push(suite); + this.emit('suite', suite); + return this; +}; + +/** + * Add a `test` to this suite. + * + * @api private + * @param {Test} test + * @return {Suite} for chaining + */ +Suite.prototype.addTest = function(test) { + test.parent = this; + test.timeout(this.timeout()); + test.enableTimeouts(this.enableTimeouts()); + test.slow(this.slow()); + test.ctx = this.ctx; + this.tests.push(test); + this.emit('test', test); + return this; +}; + +/** + * Return the full title generated by recursively concatenating the parent's + * full title. + * + * @api public + * @return {string} + */ +Suite.prototype.fullTitle = function() { + if (this.parent) { + var full = this.parent.fullTitle(); + if (full) { + return full + ' ' + this.title; + } + } + return this.title; +}; + +/** + * Return the total number of tests. + * + * @api public + * @return {number} + */ +Suite.prototype.total = function() { + return utils.reduce(this.suites, function(sum, suite) { + return sum + suite.total(); + }, 0) + this.tests.length; +}; + +/** + * Iterates through each suite recursively to find all tests. Applies a + * function in the format `fn(test)`. + * + * @api private + * @param {Function} fn + * @return {Suite} + */ +Suite.prototype.eachTest = function(fn) { + utils.forEach(this.tests, fn); + utils.forEach(this.suites, function(suite) { + suite.eachTest(fn); + }); + return this; +}; + +/** + * This will run the root suite if we happen to be running in delayed mode. + */ +Suite.prototype.run = function run() { + if (this.root) { + this.emit('run'); + } +}; + +},{"./hook":7,"./ms":15,"./utils":39,"debug":2,"events":3}],38:[function(require,module,exports){ +/** + * Module dependencies. + */ + +var Runnable = require('./runnable'); +var inherits = require('./utils').inherits; + +/** + * Expose `Test`. + */ + +module.exports = Test; + +/** + * Initialize a new `Test` with the given `title` and callback `fn`. + * + * @api private + * @param {String} title + * @param {Function} fn + */ +function Test(title, fn) { + Runnable.call(this, title, fn); + this.pending = !fn; + this.type = 'test'; +} + +/** + * Inherit from `Runnable.prototype`. + */ +inherits(Test, Runnable); + +},{"./runnable":35,"./utils":39}],39:[function(require,module,exports){ +(function (process,Buffer){ +/* eslint-env browser */ + +/** + * Module dependencies. + */ + +var basename = require('path').basename; +var debug = require('debug')('mocha:watch'); +var exists = require('fs').existsSync || require('path').existsSync; +var glob = require('glob'); +var join = require('path').join; +var readdirSync = require('fs').readdirSync; +var statSync = require('fs').statSync; +var watchFile = require('fs').watchFile; + +/** + * Ignored directories. + */ + +var ignore = ['node_modules', '.git']; + +exports.inherits = require('util').inherits; + +/** + * Escape special characters in the given string of html. + * + * @api private + * @param {string} html + * @return {string} + */ +exports.escape = function(html) { + return String(html) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); +}; + +/** + * Array#forEach (<=IE8) + * + * @api private + * @param {Array} arr + * @param {Function} fn + * @param {Object} scope + */ +exports.forEach = function(arr, fn, scope) { + for (var i = 0, l = arr.length; i < l; i++) { + fn.call(scope, arr[i], i); + } +}; + +/** + * Test if the given obj is type of string. + * + * @api private + * @param {Object} obj + * @return {boolean} + */ +exports.isString = function(obj) { + return typeof obj === 'string'; +}; + +/** + * Array#map (<=IE8) + * + * @api private + * @param {Array} arr + * @param {Function} fn + * @param {Object} scope + * @return {Array} + */ +exports.map = function(arr, fn, scope) { + var result = []; + for (var i = 0, l = arr.length; i < l; i++) { + result.push(fn.call(scope, arr[i], i, arr)); + } + return result; +}; + +/** + * Array#indexOf (<=IE8) + * + * @api private + * @param {Array} arr + * @param {Object} obj to find index of + * @param {number} start + * @return {number} + */ +exports.indexOf = function(arr, obj, start) { + for (var i = start || 0, l = arr.length; i < l; i++) { + if (arr[i] === obj) { + return i; + } + } + return -1; +}; + +/** + * Array#reduce (<=IE8) + * + * @api private + * @param {Array} arr + * @param {Function} fn + * @param {Object} val Initial value. + * @return {*} + */ +exports.reduce = function(arr, fn, val) { + var rval = val; + + for (var i = 0, l = arr.length; i < l; i++) { + rval = fn(rval, arr[i], i, arr); + } + + return rval; +}; + +/** + * Array#filter (<=IE8) + * + * @api private + * @param {Array} arr + * @param {Function} fn + * @return {Array} + */ +exports.filter = function(arr, fn) { + var ret = []; + + for (var i = 0, l = arr.length; i < l; i++) { + var val = arr[i]; + if (fn(val, i, arr)) { + ret.push(val); + } + } + + return ret; +}; + +/** + * Object.keys (<=IE8) + * + * @api private + * @param {Object} obj + * @return {Array} keys + */ +exports.keys = typeof Object.keys === 'function' ? Object.keys : function(obj) { + var keys = []; + var has = Object.prototype.hasOwnProperty; // for `window` on <=IE8 + + for (var key in obj) { + if (has.call(obj, key)) { + keys.push(key); + } + } + + return keys; +}; + +/** + * Watch the given `files` for changes + * and invoke `fn(file)` on modification. + * + * @api private + * @param {Array} files + * @param {Function} fn + */ +exports.watch = function(files, fn) { + var options = { interval: 100 }; + files.forEach(function(file) { + debug('file %s', file); + watchFile(file, options, function(curr, prev) { + if (prev.mtime < curr.mtime) { + fn(file); + } + }); + }); +}; + +/** + * Array.isArray (<=IE8) + * + * @api private + * @param {Object} obj + * @return {Boolean} + */ +var isArray = typeof Array.isArray === 'function' ? Array.isArray : function(obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; +}; + +/** + * Buffer.prototype.toJSON polyfill. + * + * @type {Function} + */ +if (typeof Buffer !== 'undefined' && Buffer.prototype) { + Buffer.prototype.toJSON = Buffer.prototype.toJSON || function() { + return Array.prototype.slice.call(this, 0); + }; +} + +/** + * Ignored files. + * + * @api private + * @param {string} path + * @return {boolean} + */ +function ignored(path) { + return !~ignore.indexOf(path); +} + +/** + * Lookup files in the given `dir`. + * + * @api private + * @param {string} dir + * @param {string[]} [ext=['.js']] + * @param {Array} [ret=[]] + * @return {Array} + */ +exports.files = function(dir, ext, ret) { + ret = ret || []; + ext = ext || ['js']; + + var re = new RegExp('\\.(' + ext.join('|') + ')$'); + + readdirSync(dir) + .filter(ignored) + .forEach(function(path) { + path = join(dir, path); + if (statSync(path).isDirectory()) { + exports.files(path, ext, ret); + } else if (path.match(re)) { + ret.push(path); + } + }); + + return ret; +}; + +/** + * Compute a slug from the given `str`. + * + * @api private + * @param {string} str + * @return {string} + */ +exports.slug = function(str) { + return str + .toLowerCase() + .replace(/ +/g, '-') + .replace(/[^-\w]/g, ''); +}; + +/** + * Strip the function definition from `str`, and re-indent for pre whitespace. + * + * @param {string} str + * @return {string} + */ +exports.clean = function(str) { + str = str + .replace(/\r\n?|[\n\u2028\u2029]/g, '\n').replace(/^\uFEFF/, '') + .replace(/^function *\(.*\)\s*{|\(.*\) *=> *{?/, '') + .replace(/\s+\}$/, ''); + + var spaces = str.match(/^\n?( *)/)[1].length; + var tabs = str.match(/^\n?(\t*)/)[1].length; + var re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs ? tabs : spaces) + '}', 'gm'); + + str = str.replace(re, ''); + + return exports.trim(str); +}; + +/** + * Trim the given `str`. + * + * @api private + * @param {string} str + * @return {string} + */ +exports.trim = function(str) { + return str.replace(/^\s+|\s+$/g, ''); +}; + +/** + * Parse the given `qs`. + * + * @api private + * @param {string} qs + * @return {Object} + */ +exports.parseQuery = function(qs) { + return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair) { + var i = pair.indexOf('='); + var key = pair.slice(0, i); + var val = pair.slice(++i); + + obj[key] = decodeURIComponent(val); + return obj; + }, {}); +}; + +/** + * Highlight the given string of `js`. + * + * @api private + * @param {string} js + * @return {string} + */ +function highlight(js) { + return js + .replace(//g, '>') + .replace(/\/\/(.*)/gm, '//$1') + .replace(/('.*?')/gm, '$1') + .replace(/(\d+\.\d+)/gm, '$1') + .replace(/(\d+)/gm, '$1') + .replace(/\bnew[ \t]+(\w+)/gm, 'new $1') + .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1'); +} + +/** + * Highlight the contents of tag `name`. + * + * @api private + * @param {string} name + */ +exports.highlightTags = function(name) { + var code = document.getElementById('mocha').getElementsByTagName(name); + for (var i = 0, len = code.length; i < len; ++i) { + code[i].innerHTML = highlight(code[i].innerHTML); + } +}; + +/** + * If a value could have properties, and has none, this function is called, + * which returns a string representation of the empty value. + * + * Functions w/ no properties return `'[Function]'` + * Arrays w/ length === 0 return `'[]'` + * Objects w/ no properties return `'{}'` + * All else: return result of `value.toString()` + * + * @api private + * @param {*} value The value to inspect. + * @param {string} [type] The type of the value, if known. + * @returns {string} + */ +function emptyRepresentation(value, type) { + type = type || exports.type(value); + + switch (type) { + case 'function': + return '[Function]'; + case 'object': + return '{}'; + case 'array': + return '[]'; + default: + return value.toString(); + } +} + +/** + * Takes some variable and asks `Object.prototype.toString()` what it thinks it + * is. + * + * @api private + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString + * @param {*} value The value to test. + * @returns {string} + * @example + * type({}) // 'object' + * type([]) // 'array' + * type(1) // 'number' + * type(false) // 'boolean' + * type(Infinity) // 'number' + * type(null) // 'null' + * type(new Date()) // 'date' + * type(/foo/) // 'regexp' + * type('type') // 'string' + * type(global) // 'global' + */ +exports.type = function type(value) { + if (value === undefined) { + return 'undefined'; + } else if (value === null) { + return 'null'; + } else if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) { + return 'buffer'; + } + return Object.prototype.toString.call(value) + .replace(/^\[.+\s(.+?)\]$/, '$1') + .toLowerCase(); +}; + +/** + * Stringify `value`. Different behavior depending on type of value: + * + * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively. + * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes. + * - If `value` is an *empty* object, function, or array, return result of function + * {@link emptyRepresentation}. + * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of + * JSON.stringify(). + * + * @api private + * @see exports.type + * @param {*} value + * @return {string} + */ +exports.stringify = function(value) { + var type = exports.type(value); + + if (!~exports.indexOf(['object', 'array', 'function'], type)) { + if (type !== 'buffer') { + return jsonStringify(value); + } + var json = value.toJSON(); + // Based on the toJSON result + return jsonStringify(json.data && json.type ? json.data : json, 2) + .replace(/,(\n|$)/g, '$1'); + } + + for (var prop in value) { + if (Object.prototype.hasOwnProperty.call(value, prop)) { + return jsonStringify(exports.canonicalize(value), 2).replace(/,(\n|$)/g, '$1'); + } + } + + return emptyRepresentation(value, type); +}; + +/** + * like JSON.stringify but more sense. + * + * @api private + * @param {Object} object + * @param {number=} spaces + * @param {number=} depth + * @returns {*} + */ +function jsonStringify(object, spaces, depth) { + if (typeof spaces === 'undefined') { + // primitive types + return _stringify(object); + } + + depth = depth || 1; + var space = spaces * depth; + var str = isArray(object) ? '[' : '{'; + var end = isArray(object) ? ']' : '}'; + var length = object.length || exports.keys(object).length; + // `.repeat()` polyfill + function repeat(s, n) { + return new Array(n).join(s); + } + + function _stringify(val) { + switch (exports.type(val)) { + case 'null': + case 'undefined': + val = '[' + val + ']'; + break; + case 'array': + case 'object': + val = jsonStringify(val, spaces, depth + 1); + break; + case 'boolean': + case 'regexp': + case 'number': + val = val === 0 && (1 / val) === -Infinity // `-0` + ? '-0' + : val.toString(); + break; + case 'date': + var sDate = isNaN(val.getTime()) // Invalid date + ? val.toString() + : val.toISOString(); + val = '[Date: ' + sDate + ']'; + break; + case 'buffer': + var json = val.toJSON(); + // Based on the toJSON result + json = json.data && json.type ? json.data : json; + val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']'; + break; + default: + val = (val === '[Function]' || val === '[Circular]') + ? val + : JSON.stringify(val); // string + } + return val; + } + + for (var i in object) { + if (!object.hasOwnProperty(i)) { + continue; // not my business + } + --length; + str += '\n ' + repeat(' ', space) + + (isArray(object) ? '' : '"' + i + '": ') // key + + _stringify(object[i]) // value + + (length ? ',' : ''); // comma + } + + return str + // [], {} + + (str.length !== 1 ? '\n' + repeat(' ', --space) + end : end); +} + +/** + * Test if a value is a buffer. + * + * @api private + * @param {*} value The value to test. + * @return {boolean} True if `value` is a buffer, otherwise false + */ +exports.isBuffer = function(value) { + return typeof Buffer !== 'undefined' && Buffer.isBuffer(value); +}; + +/** + * Return a new Thing that has the keys in sorted order. Recursive. + * + * If the Thing... + * - has already been seen, return string `'[Circular]'` + * - is `undefined`, return string `'[undefined]'` + * - is `null`, return value `null` + * - is some other primitive, return the value + * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method + * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again. + * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()` + * + * @api private + * @see {@link exports.stringify} + * @param {*} value Thing to inspect. May or may not have properties. + * @param {Array} [stack=[]] Stack of seen values + * @return {(Object|Array|Function|string|undefined)} + */ +exports.canonicalize = function(value, stack) { + var canonicalizedObj; + /* eslint-disable no-unused-vars */ + var prop; + /* eslint-enable no-unused-vars */ + var type = exports.type(value); + function withStack(value, fn) { + stack.push(value); + fn(); + stack.pop(); + } + + stack = stack || []; + + if (exports.indexOf(stack, value) !== -1) { + return '[Circular]'; + } + + switch (type) { + case 'undefined': + case 'buffer': + case 'null': + canonicalizedObj = value; + break; + case 'array': + withStack(value, function() { + canonicalizedObj = exports.map(value, function(item) { + return exports.canonicalize(item, stack); + }); + }); + break; + case 'function': + /* eslint-disable guard-for-in */ + for (prop in value) { + canonicalizedObj = {}; + break; + } + /* eslint-enable guard-for-in */ + if (!canonicalizedObj) { + canonicalizedObj = emptyRepresentation(value, type); + break; + } + /* falls through */ + case 'object': + canonicalizedObj = canonicalizedObj || {}; + withStack(value, function() { + exports.forEach(exports.keys(value).sort(), function(key) { + canonicalizedObj[key] = exports.canonicalize(value[key], stack); + }); + }); + break; + case 'date': + case 'number': + case 'regexp': + case 'boolean': + canonicalizedObj = value; + break; + default: + canonicalizedObj = value.toString(); + } + + return canonicalizedObj; +}; + +/** + * Lookup file names at the given `path`. + * + * @api public + * @param {string} path Base path to start searching from. + * @param {string[]} extensions File extensions to look for. + * @param {boolean} recursive Whether or not to recurse into subdirectories. + * @return {string[]} An array of paths. + */ +exports.lookupFiles = function lookupFiles(path, extensions, recursive) { + var files = []; + var re = new RegExp('\\.(' + extensions.join('|') + ')$'); + + if (!exists(path)) { + if (exists(path + '.js')) { + path += '.js'; + } else { + files = glob.sync(path); + if (!files.length) { + throw new Error("cannot resolve path (or pattern) '" + path + "'"); + } + return files; + } + } + + try { + var stat = statSync(path); + if (stat.isFile()) { + return path; + } + } catch (err) { + // ignore error + return; + } + + readdirSync(path).forEach(function(file) { + file = join(path, file); + try { + var stat = statSync(file); + if (stat.isDirectory()) { + if (recursive) { + files = files.concat(lookupFiles(file, extensions, recursive)); + } + return; + } + } catch (err) { + // ignore error + return; + } + if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') { + return; + } + files.push(file); + }); + + return files; +}; + +/** + * Generate an undefined error with a message warning the user. + * + * @return {Error} + */ + +exports.undefinedError = function() { + return new Error('Caught undefined error, did you throw without specifying what?'); +}; + +/** + * Generate an undefined error if `err` is not defined. + * + * @param {Error} err + * @return {Error} + */ + +exports.getError = function(err) { + return err || exports.undefinedError(); +}; + +/** + * @summary + * This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`) + * @description + * When invoking this function you get a filter function that get the Error.stack as an input, + * and return a prettify output. + * (i.e: strip Mocha and internal node functions from stack trace). + * @returns {Function} + */ +exports.stackTraceFilter = function() { + // TODO: Replace with `process.browser` + var slash = '/'; + var is = typeof document === 'undefined' ? { node: true } : { browser: true }; + var cwd = is.node + ? process.cwd() + slash + : (typeof location === 'undefined' ? window.location : location).href.replace(/\/[^\/]*$/, '/'); + + function isMochaInternal(line) { + return (~line.indexOf('node_modules' + slash + 'mocha' + slash)) + || (~line.indexOf('components' + slash + 'mochajs' + slash)) + || (~line.indexOf('components' + slash + 'mocha' + slash)) + || (~line.indexOf(slash + 'mocha.js')); + } + + function isNodeInternal(line) { + return (~line.indexOf('(timers.js:')) + || (~line.indexOf('(events.js:')) + || (~line.indexOf('(node.js:')) + || (~line.indexOf('(module.js:')) + || (~line.indexOf('GeneratorFunctionPrototype.next (native)')) + || false; + } + + return function(stack) { + stack = stack.split('\n'); + + stack = exports.reduce(stack, function(list, line) { + if (isMochaInternal(line)) { + return list; + } + + if (is.node && isNodeInternal(line)) { + return list; + } + + // Clean up cwd(absolute) + list.push(line.replace(cwd, '')); + return list; + }, []); + + return stack.join('\n'); + }; +}; + +}).call(this,require('_process'),require("buffer").Buffer) +},{"_process":51,"buffer":43,"debug":2,"fs":41,"glob":41,"path":41,"util":66}],40:[function(require,module,exports){ +(function (process){ +var WritableStream = require('stream').Writable +var inherits = require('util').inherits + +module.exports = BrowserStdout + + +inherits(BrowserStdout, WritableStream) + +function BrowserStdout(opts) { + if (!(this instanceof BrowserStdout)) return new BrowserStdout(opts) + + opts = opts || {} + WritableStream.call(this, opts) + this.label = (opts.label !== undefined) ? opts.label : 'stdout' +} + +BrowserStdout.prototype._write = function(chunks, encoding, cb) { + var output = chunks.toString ? chunks.toString() : chunks + if (this.label === false) { + console.log(output) + } else { + console.log(this.label+':', output) + } + process.nextTick(cb) +} + +}).call(this,require('_process')) +},{"_process":51,"stream":63,"util":66}],41:[function(require,module,exports){ + +},{}],42:[function(require,module,exports){ +arguments[4][41][0].apply(exports,arguments) +},{"dup":41}],43:[function(require,module,exports){ +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ + +var base64 = require('base64-js') +var ieee754 = require('ieee754') +var isArray = require('is-array') + +exports.Buffer = Buffer +exports.SlowBuffer = SlowBuffer +exports.INSPECT_MAX_BYTES = 50 +Buffer.poolSize = 8192 // not used by this implementation + +var rootParent = {} + +/** + * If `Buffer.TYPED_ARRAY_SUPPORT`: + * === true Use Uint8Array implementation (fastest) + * === false Use Object implementation (most compatible, even IE6) + * + * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, + * Opera 11.6+, iOS 4.2+. + * + * Due to various browser bugs, sometimes the Object implementation will be used even + * when the browser supports typed arrays. + * + * Note: + * + * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances, + * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. + * + * - Safari 5-7 lacks support for changing the `Object.prototype.constructor` property + * on objects. + * + * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. + * + * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of + * incorrect length in some situations. + + * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they + * get the Object implementation, which is slower but behaves correctly. + */ +Buffer.TYPED_ARRAY_SUPPORT = (function () { + function Bar () {} + try { + var arr = new Uint8Array(1) + arr.foo = function () { return 42 } + arr.constructor = Bar + return arr.foo() === 42 && // typed array instances can be augmented + arr.constructor === Bar && // constructor can be set + typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` + arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` + } catch (e) { + return false + } +})() + +function kMaxLength () { + return Buffer.TYPED_ARRAY_SUPPORT + ? 0x7fffffff + : 0x3fffffff +} + +/** + * Class: Buffer + * ============= + * + * The Buffer constructor returns instances of `Uint8Array` that are augmented + * with function properties for all the node `Buffer` API functions. We use + * `Uint8Array` so that square bracket notation works as expected -- it returns + * a single octet. + * + * By augmenting the instances, we can avoid modifying the `Uint8Array` + * prototype. + */ +function Buffer (arg) { + if (!(this instanceof Buffer)) { + // Avoid going through an ArgumentsAdaptorTrampoline in the common case. + if (arguments.length > 1) return new Buffer(arg, arguments[1]) + return new Buffer(arg) + } + + this.length = 0 + this.parent = undefined + + // Common case. + if (typeof arg === 'number') { + return fromNumber(this, arg) + } + + // Slightly less common case. + if (typeof arg === 'string') { + return fromString(this, arg, arguments.length > 1 ? arguments[1] : 'utf8') + } + + // Unusual. + return fromObject(this, arg) +} + +function fromNumber (that, length) { + that = allocate(that, length < 0 ? 0 : checked(length) | 0) + if (!Buffer.TYPED_ARRAY_SUPPORT) { + for (var i = 0; i < length; i++) { + that[i] = 0 + } + } + return that +} + +function fromString (that, string, encoding) { + if (typeof encoding !== 'string' || encoding === '') encoding = 'utf8' + + // Assumption: byteLength() return value is always < kMaxLength. + var length = byteLength(string, encoding) | 0 + that = allocate(that, length) + + that.write(string, encoding) + return that +} + +function fromObject (that, object) { + if (Buffer.isBuffer(object)) return fromBuffer(that, object) + + if (isArray(object)) return fromArray(that, object) + + if (object == null) { + throw new TypeError('must start with number, buffer, array or string') + } + + if (typeof ArrayBuffer !== 'undefined') { + if (object.buffer instanceof ArrayBuffer) { + return fromTypedArray(that, object) + } + if (object instanceof ArrayBuffer) { + return fromArrayBuffer(that, object) + } + } + + if (object.length) return fromArrayLike(that, object) + + return fromJsonObject(that, object) +} + +function fromBuffer (that, buffer) { + var length = checked(buffer.length) | 0 + that = allocate(that, length) + buffer.copy(that, 0, 0, length) + return that +} + +function fromArray (that, array) { + var length = checked(array.length) | 0 + that = allocate(that, length) + for (var i = 0; i < length; i += 1) { + that[i] = array[i] & 255 + } + return that +} + +// Duplicate of fromArray() to keep fromArray() monomorphic. +function fromTypedArray (that, array) { + var length = checked(array.length) | 0 + that = allocate(that, length) + // Truncating the elements is probably not what people expect from typed + // arrays with BYTES_PER_ELEMENT > 1 but it's compatible with the behavior + // of the old Buffer constructor. + for (var i = 0; i < length; i += 1) { + that[i] = array[i] & 255 + } + return that +} + +function fromArrayBuffer (that, array) { + if (Buffer.TYPED_ARRAY_SUPPORT) { + // Return an augmented `Uint8Array` instance, for best performance + array.byteLength + that = Buffer._augment(new Uint8Array(array)) + } else { + // Fallback: Return an object instance of the Buffer class + that = fromTypedArray(that, new Uint8Array(array)) + } + return that +} + +function fromArrayLike (that, array) { + var length = checked(array.length) | 0 + that = allocate(that, length) + for (var i = 0; i < length; i += 1) { + that[i] = array[i] & 255 + } + return that +} + +// Deserialize { type: 'Buffer', data: [1,2,3,...] } into a Buffer object. +// Returns a zero-length buffer for inputs that don't conform to the spec. +function fromJsonObject (that, object) { + var array + var length = 0 + + if (object.type === 'Buffer' && isArray(object.data)) { + array = object.data + length = checked(array.length) | 0 + } + that = allocate(that, length) + + for (var i = 0; i < length; i += 1) { + that[i] = array[i] & 255 + } + return that +} + +function allocate (that, length) { + if (Buffer.TYPED_ARRAY_SUPPORT) { + // Return an augmented `Uint8Array` instance, for best performance + that = Buffer._augment(new Uint8Array(length)) + } else { + // Fallback: Return an object instance of the Buffer class + that.length = length + that._isBuffer = true + } + + var fromPool = length !== 0 && length <= Buffer.poolSize >>> 1 + if (fromPool) that.parent = rootParent + + return that +} + +function checked (length) { + // Note: cannot use `length < kMaxLength` here because that fails when + // length is NaN (which is otherwise coerced to zero.) + if (length >= kMaxLength()) { + throw new RangeError('Attempt to allocate Buffer larger than maximum ' + + 'size: 0x' + kMaxLength().toString(16) + ' bytes') + } + return length | 0 +} + +function SlowBuffer (subject, encoding) { + if (!(this instanceof SlowBuffer)) return new SlowBuffer(subject, encoding) + + var buf = new Buffer(subject, encoding) + delete buf.parent + return buf +} + +Buffer.isBuffer = function isBuffer (b) { + return !!(b != null && b._isBuffer) +} + +Buffer.compare = function compare (a, b) { + if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { + throw new TypeError('Arguments must be Buffers') + } + + if (a === b) return 0 + + var x = a.length + var y = b.length + + var i = 0 + var len = Math.min(x, y) + while (i < len) { + if (a[i] !== b[i]) break + + ++i + } + + if (i !== len) { + x = a[i] + y = b[i] + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +Buffer.isEncoding = function isEncoding (encoding) { + switch (String(encoding).toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'binary': + case 'base64': + case 'raw': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return true + default: + return false + } +} + +Buffer.concat = function concat (list, length) { + if (!isArray(list)) throw new TypeError('list argument must be an Array of Buffers.') + + if (list.length === 0) { + return new Buffer(0) + } + + var i + if (length === undefined) { + length = 0 + for (i = 0; i < list.length; i++) { + length += list[i].length + } + } + + var buf = new Buffer(length) + var pos = 0 + for (i = 0; i < list.length; i++) { + var item = list[i] + item.copy(buf, pos) + pos += item.length + } + return buf +} + +function byteLength (string, encoding) { + if (typeof string !== 'string') string = '' + string + + var len = string.length + if (len === 0) return 0 + + // Use a for loop to avoid recursion + var loweredCase = false + for (;;) { + switch (encoding) { + case 'ascii': + case 'binary': + // Deprecated + case 'raw': + case 'raws': + return len + case 'utf8': + case 'utf-8': + return utf8ToBytes(string).length + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return len * 2 + case 'hex': + return len >>> 1 + case 'base64': + return base64ToBytes(string).length + default: + if (loweredCase) return utf8ToBytes(string).length // assume utf8 + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} +Buffer.byteLength = byteLength + +// pre-set for values that may exist in the future +Buffer.prototype.length = undefined +Buffer.prototype.parent = undefined + +function slowToString (encoding, start, end) { + var loweredCase = false + + start = start | 0 + end = end === undefined || end === Infinity ? this.length : end | 0 + + if (!encoding) encoding = 'utf8' + if (start < 0) start = 0 + if (end > this.length) end = this.length + if (end <= start) return '' + + while (true) { + switch (encoding) { + case 'hex': + return hexSlice(this, start, end) + + case 'utf8': + case 'utf-8': + return utf8Slice(this, start, end) + + case 'ascii': + return asciiSlice(this, start, end) + + case 'binary': + return binarySlice(this, start, end) + + case 'base64': + return base64Slice(this, start, end) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return utf16leSlice(this, start, end) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = (encoding + '').toLowerCase() + loweredCase = true + } + } +} + +Buffer.prototype.toString = function toString () { + var length = this.length | 0 + if (length === 0) return '' + if (arguments.length === 0) return utf8Slice(this, 0, length) + return slowToString.apply(this, arguments) +} + +Buffer.prototype.equals = function equals (b) { + if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') + if (this === b) return true + return Buffer.compare(this, b) === 0 +} + +Buffer.prototype.inspect = function inspect () { + var str = '' + var max = exports.INSPECT_MAX_BYTES + if (this.length > 0) { + str = this.toString('hex', 0, max).match(/.{2}/g).join(' ') + if (this.length > max) str += ' ... ' + } + return '' +} + +Buffer.prototype.compare = function compare (b) { + if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') + if (this === b) return 0 + return Buffer.compare(this, b) +} + +Buffer.prototype.indexOf = function indexOf (val, byteOffset) { + if (byteOffset > 0x7fffffff) byteOffset = 0x7fffffff + else if (byteOffset < -0x80000000) byteOffset = -0x80000000 + byteOffset >>= 0 + + if (this.length === 0) return -1 + if (byteOffset >= this.length) return -1 + + // Negative offsets start from the end of the buffer + if (byteOffset < 0) byteOffset = Math.max(this.length + byteOffset, 0) + + if (typeof val === 'string') { + if (val.length === 0) return -1 // special case: looking for empty string always fails + return String.prototype.indexOf.call(this, val, byteOffset) + } + if (Buffer.isBuffer(val)) { + return arrayIndexOf(this, val, byteOffset) + } + if (typeof val === 'number') { + if (Buffer.TYPED_ARRAY_SUPPORT && Uint8Array.prototype.indexOf === 'function') { + return Uint8Array.prototype.indexOf.call(this, val, byteOffset) + } + return arrayIndexOf(this, [ val ], byteOffset) + } + + function arrayIndexOf (arr, val, byteOffset) { + var foundIndex = -1 + for (var i = 0; byteOffset + i < arr.length; i++) { + if (arr[byteOffset + i] === val[foundIndex === -1 ? 0 : i - foundIndex]) { + if (foundIndex === -1) foundIndex = i + if (i - foundIndex + 1 === val.length) return byteOffset + foundIndex + } else { + foundIndex = -1 + } + } + return -1 + } + + throw new TypeError('val must be string, number or Buffer') +} + +// `get` is deprecated +Buffer.prototype.get = function get (offset) { + console.log('.get() is deprecated. Access using array indexes instead.') + return this.readUInt8(offset) +} + +// `set` is deprecated +Buffer.prototype.set = function set (v, offset) { + console.log('.set() is deprecated. Access using array indexes instead.') + return this.writeUInt8(v, offset) +} + +function hexWrite (buf, string, offset, length) { + offset = Number(offset) || 0 + var remaining = buf.length - offset + if (!length) { + length = remaining + } else { + length = Number(length) + if (length > remaining) { + length = remaining + } + } + + // must be an even number of digits + var strLen = string.length + if (strLen % 2 !== 0) throw new Error('Invalid hex string') + + if (length > strLen / 2) { + length = strLen / 2 + } + for (var i = 0; i < length; i++) { + var parsed = parseInt(string.substr(i * 2, 2), 16) + if (isNaN(parsed)) throw new Error('Invalid hex string') + buf[offset + i] = parsed + } + return i +} + +function utf8Write (buf, string, offset, length) { + return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) +} + +function asciiWrite (buf, string, offset, length) { + return blitBuffer(asciiToBytes(string), buf, offset, length) +} + +function binaryWrite (buf, string, offset, length) { + return asciiWrite(buf, string, offset, length) +} + +function base64Write (buf, string, offset, length) { + return blitBuffer(base64ToBytes(string), buf, offset, length) +} + +function ucs2Write (buf, string, offset, length) { + return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) +} + +Buffer.prototype.write = function write (string, offset, length, encoding) { + // Buffer#write(string) + if (offset === undefined) { + encoding = 'utf8' + length = this.length + offset = 0 + // Buffer#write(string, encoding) + } else if (length === undefined && typeof offset === 'string') { + encoding = offset + length = this.length + offset = 0 + // Buffer#write(string, offset[, length][, encoding]) + } else if (isFinite(offset)) { + offset = offset | 0 + if (isFinite(length)) { + length = length | 0 + if (encoding === undefined) encoding = 'utf8' + } else { + encoding = length + length = undefined + } + // legacy write(string, encoding, offset, length) - remove in v0.13 + } else { + var swap = encoding + encoding = offset + offset = length | 0 + length = swap + } + + var remaining = this.length - offset + if (length === undefined || length > remaining) length = remaining + + if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { + throw new RangeError('attempt to write outside buffer bounds') + } + + if (!encoding) encoding = 'utf8' + + var loweredCase = false + for (;;) { + switch (encoding) { + case 'hex': + return hexWrite(this, string, offset, length) + + case 'utf8': + case 'utf-8': + return utf8Write(this, string, offset, length) + + case 'ascii': + return asciiWrite(this, string, offset, length) + + case 'binary': + return binaryWrite(this, string, offset, length) + + case 'base64': + // Warning: maxLength not taken into account in base64Write + return base64Write(this, string, offset, length) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return ucs2Write(this, string, offset, length) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} + +Buffer.prototype.toJSON = function toJSON () { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this._arr || this, 0) + } +} + +function base64Slice (buf, start, end) { + if (start === 0 && end === buf.length) { + return base64.fromByteArray(buf) + } else { + return base64.fromByteArray(buf.slice(start, end)) + } +} + +function utf8Slice (buf, start, end) { + end = Math.min(buf.length, end) + var res = [] + + var i = start + while (i < end) { + var firstByte = buf[i] + var codePoint = null + var bytesPerSequence = (firstByte > 0xEF) ? 4 + : (firstByte > 0xDF) ? 3 + : (firstByte > 0xBF) ? 2 + : 1 + + if (i + bytesPerSequence <= end) { + var secondByte, thirdByte, fourthByte, tempCodePoint + + switch (bytesPerSequence) { + case 1: + if (firstByte < 0x80) { + codePoint = firstByte + } + break + case 2: + secondByte = buf[i + 1] + if ((secondByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) + if (tempCodePoint > 0x7F) { + codePoint = tempCodePoint + } + } + break + case 3: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) + if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { + codePoint = tempCodePoint + } + } + break + case 4: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + fourthByte = buf[i + 3] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) + if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { + codePoint = tempCodePoint + } + } + } + } + + if (codePoint === null) { + // we did not generate a valid codePoint so insert a + // replacement char (U+FFFD) and advance only 1 byte + codePoint = 0xFFFD + bytesPerSequence = 1 + } else if (codePoint > 0xFFFF) { + // encode to utf16 (surrogate pair dance) + codePoint -= 0x10000 + res.push(codePoint >>> 10 & 0x3FF | 0xD800) + codePoint = 0xDC00 | codePoint & 0x3FF + } + + res.push(codePoint) + i += bytesPerSequence + } + + return decodeCodePointsArray(res) +} + +// Based on http://stackoverflow.com/a/22747272/680742, the browser with +// the lowest limit is Chrome, with 0x10000 args. +// We go 1 magnitude less, for safety +var MAX_ARGUMENTS_LENGTH = 0x1000 + +function decodeCodePointsArray (codePoints) { + var len = codePoints.length + if (len <= MAX_ARGUMENTS_LENGTH) { + return String.fromCharCode.apply(String, codePoints) // avoid extra slice() + } + + // Decode in chunks to avoid "call stack size exceeded". + var res = '' + var i = 0 + while (i < len) { + res += String.fromCharCode.apply( + String, + codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) + ) + } + return res +} + +function asciiSlice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; i++) { + ret += String.fromCharCode(buf[i] & 0x7F) + } + return ret +} + +function binarySlice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; i++) { + ret += String.fromCharCode(buf[i]) + } + return ret +} + +function hexSlice (buf, start, end) { + var len = buf.length + + if (!start || start < 0) start = 0 + if (!end || end < 0 || end > len) end = len + + var out = '' + for (var i = start; i < end; i++) { + out += toHex(buf[i]) + } + return out +} + +function utf16leSlice (buf, start, end) { + var bytes = buf.slice(start, end) + var res = '' + for (var i = 0; i < bytes.length; i += 2) { + res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256) + } + return res +} + +Buffer.prototype.slice = function slice (start, end) { + var len = this.length + start = ~~start + end = end === undefined ? len : ~~end + + if (start < 0) { + start += len + if (start < 0) start = 0 + } else if (start > len) { + start = len + } + + if (end < 0) { + end += len + if (end < 0) end = 0 + } else if (end > len) { + end = len + } + + if (end < start) end = start + + var newBuf + if (Buffer.TYPED_ARRAY_SUPPORT) { + newBuf = Buffer._augment(this.subarray(start, end)) + } else { + var sliceLen = end - start + newBuf = new Buffer(sliceLen, undefined) + for (var i = 0; i < sliceLen; i++) { + newBuf[i] = this[i + start] + } + } + + if (newBuf.length) newBuf.parent = this.parent || this + + return newBuf +} + +/* + * Need to make sure that buffer isn't trying to write out of bounds. + */ +function checkOffset (offset, ext, length) { + if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') + if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') +} + +Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + + return val +} + +Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) { + checkOffset(offset, byteLength, this.length) + } + + var val = this[offset + --byteLength] + var mul = 1 + while (byteLength > 0 && (mul *= 0x100)) { + val += this[offset + --byteLength] * mul + } + + return val +} + +Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { + if (!noAssert) checkOffset(offset, 1, this.length) + return this[offset] +} + +Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 2, this.length) + return this[offset] | (this[offset + 1] << 8) +} + +Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 2, this.length) + return (this[offset] << 8) | this[offset + 1] +} + +Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + + return ((this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16)) + + (this[offset + 3] * 0x1000000) +} + +Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] * 0x1000000) + + ((this[offset + 1] << 16) | + (this[offset + 2] << 8) | + this[offset + 3]) +} + +Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var i = byteLength + var mul = 1 + var val = this[offset + --i] + while (i > 0 && (mul *= 0x100)) { + val += this[offset + --i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { + if (!noAssert) checkOffset(offset, 1, this.length) + if (!(this[offset] & 0x80)) return (this[offset]) + return ((0xff - this[offset] + 1) * -1) +} + +Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset] | (this[offset + 1] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset + 1] | (this[offset] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16) | + (this[offset + 3] << 24) +} + +Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] << 24) | + (this[offset + 1] << 16) | + (this[offset + 2] << 8) | + (this[offset + 3]) +} + +Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, true, 23, 4) +} + +Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, false, 23, 4) +} + +Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, true, 52, 8) +} + +Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, false, 52, 8) +} + +function checkInt (buf, value, offset, ext, max, min) { + if (!Buffer.isBuffer(buf)) throw new TypeError('buffer must be a Buffer instance') + if (value > max || value < min) throw new RangeError('value is out of bounds') + if (offset + ext > buf.length) throw new RangeError('index out of range') +} + +Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) + + var mul = 1 + var i = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) + + var i = byteLength - 1 + var mul = 1 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) + if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) + this[offset] = value + return offset + 1 +} + +function objectWriteUInt16 (buf, value, offset, littleEndian) { + if (value < 0) value = 0xffff + value + 1 + for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) { + buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> + (littleEndian ? i : 1 - i) * 8 + } +} + +Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = value + this[offset + 1] = (value >>> 8) + } else { + objectWriteUInt16(this, value, offset, true) + } + return offset + 2 +} + +Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value >>> 8) + this[offset + 1] = value + } else { + objectWriteUInt16(this, value, offset, false) + } + return offset + 2 +} + +function objectWriteUInt32 (buf, value, offset, littleEndian) { + if (value < 0) value = 0xffffffff + value + 1 + for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) { + buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff + } +} + +Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset + 3] = (value >>> 24) + this[offset + 2] = (value >>> 16) + this[offset + 1] = (value >>> 8) + this[offset] = value + } else { + objectWriteUInt32(this, value, offset, true) + } + return offset + 4 +} + +Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = value + } else { + objectWriteUInt32(this, value, offset, false) + } + return offset + 4 +} + +Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) { + var limit = Math.pow(2, 8 * byteLength - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = 0 + var mul = 1 + var sub = value < 0 ? 1 : 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) { + var limit = Math.pow(2, 8 * byteLength - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = byteLength - 1 + var mul = 1 + var sub = value < 0 ? 1 : 0 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) + if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) + if (value < 0) value = 0xff + value + 1 + this[offset] = value + return offset + 1 +} + +Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = value + this[offset + 1] = (value >>> 8) + } else { + objectWriteUInt16(this, value, offset, true) + } + return offset + 2 +} + +Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value >>> 8) + this[offset + 1] = value + } else { + objectWriteUInt16(this, value, offset, false) + } + return offset + 2 +} + +Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = value + this[offset + 1] = (value >>> 8) + this[offset + 2] = (value >>> 16) + this[offset + 3] = (value >>> 24) + } else { + objectWriteUInt32(this, value, offset, true) + } + return offset + 4 +} + +Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + if (value < 0) value = 0xffffffff + value + 1 + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = value + } else { + objectWriteUInt32(this, value, offset, false) + } + return offset + 4 +} + +function checkIEEE754 (buf, value, offset, ext, max, min) { + if (value > max || value < min) throw new RangeError('value is out of bounds') + if (offset + ext > buf.length) throw new RangeError('index out of range') + if (offset < 0) throw new RangeError('index out of range') +} + +function writeFloat (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) + } + ieee754.write(buf, value, offset, littleEndian, 23, 4) + return offset + 4 +} + +Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { + return writeFloat(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { + return writeFloat(this, value, offset, false, noAssert) +} + +function writeDouble (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) + } + ieee754.write(buf, value, offset, littleEndian, 52, 8) + return offset + 8 +} + +Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { + return writeDouble(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { + return writeDouble(this, value, offset, false, noAssert) +} + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function copy (target, targetStart, start, end) { + if (!start) start = 0 + if (!end && end !== 0) end = this.length + if (targetStart >= target.length) targetStart = target.length + if (!targetStart) targetStart = 0 + if (end > 0 && end < start) end = start + + // Copy 0 bytes; we're done + if (end === start) return 0 + if (target.length === 0 || this.length === 0) return 0 + + // Fatal error conditions + if (targetStart < 0) { + throw new RangeError('targetStart out of bounds') + } + if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds') + if (end < 0) throw new RangeError('sourceEnd out of bounds') + + // Are we oob? + if (end > this.length) end = this.length + if (target.length - targetStart < end - start) { + end = target.length - targetStart + start + } + + var len = end - start + var i + + if (this === target && start < targetStart && targetStart < end) { + // descending copy from end + for (i = len - 1; i >= 0; i--) { + target[i + targetStart] = this[i + start] + } + } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) { + // ascending copy from start + for (i = 0; i < len; i++) { + target[i + targetStart] = this[i + start] + } + } else { + target._set(this.subarray(start, start + len), targetStart) + } + + return len +} + +// fill(value, start=0, end=buffer.length) +Buffer.prototype.fill = function fill (value, start, end) { + if (!value) value = 0 + if (!start) start = 0 + if (!end) end = this.length + + if (end < start) throw new RangeError('end < start') + + // Fill 0 bytes; we're done + if (end === start) return + if (this.length === 0) return + + if (start < 0 || start >= this.length) throw new RangeError('start out of bounds') + if (end < 0 || end > this.length) throw new RangeError('end out of bounds') + + var i + if (typeof value === 'number') { + for (i = start; i < end; i++) { + this[i] = value + } + } else { + var bytes = utf8ToBytes(value.toString()) + var len = bytes.length + for (i = start; i < end; i++) { + this[i] = bytes[i % len] + } + } + + return this +} + +/** + * Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance. + * Added in Node 0.12. Only available in browsers that support ArrayBuffer. + */ +Buffer.prototype.toArrayBuffer = function toArrayBuffer () { + if (typeof Uint8Array !== 'undefined') { + if (Buffer.TYPED_ARRAY_SUPPORT) { + return (new Buffer(this)).buffer + } else { + var buf = new Uint8Array(this.length) + for (var i = 0, len = buf.length; i < len; i += 1) { + buf[i] = this[i] + } + return buf.buffer + } + } else { + throw new TypeError('Buffer.toArrayBuffer not supported in this browser') + } +} + +// HELPER FUNCTIONS +// ================ + +var BP = Buffer.prototype + +/** + * Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods + */ +Buffer._augment = function _augment (arr) { + arr.constructor = Buffer + arr._isBuffer = true + + // save reference to original Uint8Array set method before overwriting + arr._set = arr.set + + // deprecated + arr.get = BP.get + arr.set = BP.set + + arr.write = BP.write + arr.toString = BP.toString + arr.toLocaleString = BP.toString + arr.toJSON = BP.toJSON + arr.equals = BP.equals + arr.compare = BP.compare + arr.indexOf = BP.indexOf + arr.copy = BP.copy + arr.slice = BP.slice + arr.readUIntLE = BP.readUIntLE + arr.readUIntBE = BP.readUIntBE + arr.readUInt8 = BP.readUInt8 + arr.readUInt16LE = BP.readUInt16LE + arr.readUInt16BE = BP.readUInt16BE + arr.readUInt32LE = BP.readUInt32LE + arr.readUInt32BE = BP.readUInt32BE + arr.readIntLE = BP.readIntLE + arr.readIntBE = BP.readIntBE + arr.readInt8 = BP.readInt8 + arr.readInt16LE = BP.readInt16LE + arr.readInt16BE = BP.readInt16BE + arr.readInt32LE = BP.readInt32LE + arr.readInt32BE = BP.readInt32BE + arr.readFloatLE = BP.readFloatLE + arr.readFloatBE = BP.readFloatBE + arr.readDoubleLE = BP.readDoubleLE + arr.readDoubleBE = BP.readDoubleBE + arr.writeUInt8 = BP.writeUInt8 + arr.writeUIntLE = BP.writeUIntLE + arr.writeUIntBE = BP.writeUIntBE + arr.writeUInt16LE = BP.writeUInt16LE + arr.writeUInt16BE = BP.writeUInt16BE + arr.writeUInt32LE = BP.writeUInt32LE + arr.writeUInt32BE = BP.writeUInt32BE + arr.writeIntLE = BP.writeIntLE + arr.writeIntBE = BP.writeIntBE + arr.writeInt8 = BP.writeInt8 + arr.writeInt16LE = BP.writeInt16LE + arr.writeInt16BE = BP.writeInt16BE + arr.writeInt32LE = BP.writeInt32LE + arr.writeInt32BE = BP.writeInt32BE + arr.writeFloatLE = BP.writeFloatLE + arr.writeFloatBE = BP.writeFloatBE + arr.writeDoubleLE = BP.writeDoubleLE + arr.writeDoubleBE = BP.writeDoubleBE + arr.fill = BP.fill + arr.inspect = BP.inspect + arr.toArrayBuffer = BP.toArrayBuffer + + return arr +} + +var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g + +function base64clean (str) { + // Node strips out invalid characters like \n and \t from the string, base64-js does not + str = stringtrim(str).replace(INVALID_BASE64_RE, '') + // Node converts strings with length < 2 to '' + if (str.length < 2) return '' + // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not + while (str.length % 4 !== 0) { + str = str + '=' + } + return str +} + +function stringtrim (str) { + if (str.trim) return str.trim() + return str.replace(/^\s+|\s+$/g, '') +} + +function toHex (n) { + if (n < 16) return '0' + n.toString(16) + return n.toString(16) +} + +function utf8ToBytes (string, units) { + units = units || Infinity + var codePoint + var length = string.length + var leadSurrogate = null + var bytes = [] + + for (var i = 0; i < length; i++) { + codePoint = string.charCodeAt(i) + + // is surrogate component + if (codePoint > 0xD7FF && codePoint < 0xE000) { + // last char was a lead + if (!leadSurrogate) { + // no lead yet + if (codePoint > 0xDBFF) { + // unexpected trail + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } else if (i + 1 === length) { + // unpaired lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } + + // valid lead + leadSurrogate = codePoint + + continue + } + + // 2 leads in a row + if (codePoint < 0xDC00) { + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + leadSurrogate = codePoint + continue + } + + // valid surrogate pair + codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000 + } else if (leadSurrogate) { + // valid bmp char, but last char was a lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + } + + leadSurrogate = null + + // encode utf8 + if (codePoint < 0x80) { + if ((units -= 1) < 0) break + bytes.push(codePoint) + } else if (codePoint < 0x800) { + if ((units -= 2) < 0) break + bytes.push( + codePoint >> 0x6 | 0xC0, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x10000) { + if ((units -= 3) < 0) break + bytes.push( + codePoint >> 0xC | 0xE0, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x110000) { + if ((units -= 4) < 0) break + bytes.push( + codePoint >> 0x12 | 0xF0, + codePoint >> 0xC & 0x3F | 0x80, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else { + throw new Error('Invalid code point') + } + } + + return bytes +} + +function asciiToBytes (str) { + var byteArray = [] + for (var i = 0; i < str.length; i++) { + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push(str.charCodeAt(i) & 0xFF) + } + return byteArray +} + +function utf16leToBytes (str, units) { + var c, hi, lo + var byteArray = [] + for (var i = 0; i < str.length; i++) { + if ((units -= 2) < 0) break + + c = str.charCodeAt(i) + hi = c >> 8 + lo = c % 256 + byteArray.push(lo) + byteArray.push(hi) + } + + return byteArray +} + +function base64ToBytes (str) { + return base64.toByteArray(base64clean(str)) +} + +function blitBuffer (src, dst, offset, length) { + for (var i = 0; i < length; i++) { + if ((i + offset >= dst.length) || (i >= src.length)) break + dst[i + offset] = src[i] + } + return i +} + +},{"base64-js":44,"ieee754":45,"is-array":46}],44:[function(require,module,exports){ +var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + +;(function (exports) { + 'use strict'; + + var Arr = (typeof Uint8Array !== 'undefined') + ? Uint8Array + : Array + + var PLUS = '+'.charCodeAt(0) + var SLASH = '/'.charCodeAt(0) + var NUMBER = '0'.charCodeAt(0) + var LOWER = 'a'.charCodeAt(0) + var UPPER = 'A'.charCodeAt(0) + var PLUS_URL_SAFE = '-'.charCodeAt(0) + var SLASH_URL_SAFE = '_'.charCodeAt(0) + + function decode (elt) { + var code = elt.charCodeAt(0) + if (code === PLUS || + code === PLUS_URL_SAFE) + return 62 // '+' + if (code === SLASH || + code === SLASH_URL_SAFE) + return 63 // '/' + if (code < NUMBER) + return -1 //no match + if (code < NUMBER + 10) + return code - NUMBER + 26 + 26 + if (code < UPPER + 26) + return code - UPPER + if (code < LOWER + 26) + return code - LOWER + 26 + } + + function b64ToByteArray (b64) { + var i, j, l, tmp, placeHolders, arr + + if (b64.length % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // the number of equal signs (place holders) + // if there are two placeholders, than the two characters before it + // represent one byte + // if there is only one, then the three characters before it represent 2 bytes + // this is just a cheap hack to not do indexOf twice + var len = b64.length + placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0 + + // base64 is 4/3 + up to two characters of the original data + arr = new Arr(b64.length * 3 / 4 - placeHolders) + + // if there are placeholders, only get up to the last complete 4 chars + l = placeHolders > 0 ? b64.length - 4 : b64.length + + var L = 0 + + function push (v) { + arr[L++] = v + } + + for (i = 0, j = 0; i < l; i += 4, j += 3) { + tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)) + push((tmp & 0xFF0000) >> 16) + push((tmp & 0xFF00) >> 8) + push(tmp & 0xFF) + } + + if (placeHolders === 2) { + tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4) + push(tmp & 0xFF) + } else if (placeHolders === 1) { + tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2) + push((tmp >> 8) & 0xFF) + push(tmp & 0xFF) + } + + return arr + } + + function uint8ToBase64 (uint8) { + var i, + extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes + output = "", + temp, length + + function encode (num) { + return lookup.charAt(num) + } + + function tripletToBase64 (num) { + return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F) + } + + // go through the array every three bytes, we'll deal with trailing stuff later + for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { + temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) + output += tripletToBase64(temp) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + switch (extraBytes) { + case 1: + temp = uint8[uint8.length - 1] + output += encode(temp >> 2) + output += encode((temp << 4) & 0x3F) + output += '==' + break + case 2: + temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) + output += encode(temp >> 10) + output += encode((temp >> 4) & 0x3F) + output += encode((temp << 2) & 0x3F) + output += '=' + break + } + + return output + } + + exports.toByteArray = b64ToByteArray + exports.fromByteArray = uint8ToBase64 +}(typeof exports === 'undefined' ? (this.base64js = {}) : exports)) + +},{}],45:[function(require,module,exports){ +exports.read = function (buffer, offset, isLE, mLen, nBytes) { + var e, m + var eLen = nBytes * 8 - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var nBits = -7 + var i = isLE ? (nBytes - 1) : 0 + var d = isLE ? -1 : 1 + var s = buffer[offset + i] + + i += d + + e = s & ((1 << (-nBits)) - 1) + s >>= (-nBits) + nBits += eLen + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + m = e & ((1 << (-nBits)) - 1) + e >>= (-nBits) + nBits += mLen + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + if (e === 0) { + e = 1 - eBias + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity) + } else { + m = m + Math.pow(2, mLen) + e = e - eBias + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen) +} + +exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c + var eLen = nBytes * 8 - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) + var i = isLE ? 0 : (nBytes - 1) + var d = isLE ? 1 : -1 + var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 + + value = Math.abs(value) + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0 + e = eMax + } else { + e = Math.floor(Math.log(value) / Math.LN2) + if (value * (c = Math.pow(2, -e)) < 1) { + e-- + c *= 2 + } + if (e + eBias >= 1) { + value += rt / c + } else { + value += rt * Math.pow(2, 1 - eBias) + } + if (value * c >= 2) { + e++ + c /= 2 + } + + if (e + eBias >= eMax) { + m = 0 + e = eMax + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen) + e = e + eBias + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) + e = 0 + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + + e = (e << mLen) | m + eLen += mLen + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + + buffer[offset + i - d] |= s * 128 +} + +},{}],46:[function(require,module,exports){ + +/** + * isArray + */ + +var isArray = Array.isArray; + +/** + * toString + */ + +var str = Object.prototype.toString; + +/** + * Whether or not the given `val` + * is an array. + * + * example: + * + * isArray([]); + * // > true + * isArray(arguments); + * // > false + * isArray(''); + * // > false + * + * @param {mixed} val + * @return {bool} + */ + +module.exports = isArray || function (val) { + return !! val && '[object Array]' == str.call(val); +}; + +},{}],47:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +function EventEmitter() { + this._events = this._events || {}; + this._maxListeners = this._maxListeners || undefined; +} +module.exports = EventEmitter; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +EventEmitter.defaultMaxListeners = 10; + +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +EventEmitter.prototype.setMaxListeners = function(n) { + if (!isNumber(n) || n < 0 || isNaN(n)) + throw TypeError('n must be a positive number'); + this._maxListeners = n; + return this; +}; + +EventEmitter.prototype.emit = function(type) { + var er, handler, len, args, i, listeners; + + if (!this._events) + this._events = {}; + + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events.error || + (isObject(this._events.error) && !this._events.error.length)) { + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } + throw TypeError('Uncaught, unspecified "error" event.'); + } + } + + handler = this._events[type]; + + if (isUndefined(handler)) + return false; + + if (isFunction(handler)) { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + handler.apply(this, args); + } + } else if (isObject(handler)) { + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + + listeners = handler.slice(); + len = listeners.length; + for (i = 0; i < len; i++) + listeners[i].apply(this, args); + } + + return true; +}; + +EventEmitter.prototype.addListener = function(type, listener) { + var m; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events) + this._events = {}; + + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (this._events.newListener) + this.emit('newListener', type, + isFunction(listener.listener) ? + listener.listener : listener); + + if (!this._events[type]) + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + else if (isObject(this._events[type])) + // If we've already got an array, just append. + this._events[type].push(listener); + else + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + + // Check for listener leak + if (isObject(this._events[type]) && !this._events[type].warned) { + var m; + if (!isUndefined(this._maxListeners)) { + m = this._maxListeners; + } else { + m = EventEmitter.defaultMaxListeners; + } + + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + if (typeof console.trace === 'function') { + // not supported in IE 10 + console.trace(); + } + } + } + + return this; +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.once = function(type, listener) { + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + var fired = false; + + function g() { + this.removeListener(type, g); + + if (!fired) { + fired = true; + listener.apply(this, arguments); + } + } + + g.listener = listener; + this.on(type, g); + + return this; +}; + +// emits a 'removeListener' event iff the listener was removed +EventEmitter.prototype.removeListener = function(type, listener) { + var list, position, length, i; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events || !this._events[type]) + return this; + + list = this._events[type]; + length = list.length; + position = -1; + + if (list === listener || + (isFunction(list.listener) && list.listener === listener)) { + delete this._events[type]; + if (this._events.removeListener) + this.emit('removeListener', type, listener); + + } else if (isObject(list)) { + for (i = length; i-- > 0;) { + if (list[i] === listener || + (list[i].listener && list[i].listener === listener)) { + position = i; + break; + } + } + + if (position < 0) + return this; + + if (list.length === 1) { + list.length = 0; + delete this._events[type]; + } else { + list.splice(position, 1); + } + + if (this._events.removeListener) + this.emit('removeListener', type, listener); + } + + return this; +}; + +EventEmitter.prototype.removeAllListeners = function(type) { + var key, listeners; + + if (!this._events) + return this; + + // not listening for removeListener, no need to emit + if (!this._events.removeListener) { + if (arguments.length === 0) + this._events = {}; + else if (this._events[type]) + delete this._events[type]; + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (key in this._events) { + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = {}; + return this; + } + + listeners = this._events[type]; + + if (isFunction(listeners)) { + this.removeListener(type, listeners); + } else { + // LIFO order + while (listeners.length) + this.removeListener(type, listeners[listeners.length - 1]); + } + delete this._events[type]; + + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + var ret; + if (!this._events || !this._events[type]) + ret = []; + else if (isFunction(this._events[type])) + ret = [this._events[type]]; + else + ret = this._events[type].slice(); + return ret; +}; + +EventEmitter.listenerCount = function(emitter, type) { + var ret; + if (!emitter._events || !emitter._events[type]) + ret = 0; + else if (isFunction(emitter._events[type])) + ret = 1; + else + ret = emitter._events[type].length; + return ret; +}; + +function isFunction(arg) { + return typeof arg === 'function'; +} + +function isNumber(arg) { + return typeof arg === 'number'; +} + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} + +function isUndefined(arg) { + return arg === void 0; +} + +},{}],48:[function(require,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} + +},{}],49:[function(require,module,exports){ +module.exports = Array.isArray || function (arr) { + return Object.prototype.toString.call(arr) == '[object Array]'; +}; + +},{}],50:[function(require,module,exports){ +exports.endianness = function () { return 'LE' }; + +exports.hostname = function () { + if (typeof location !== 'undefined') { + return location.hostname + } + else return ''; +}; + +exports.loadavg = function () { return [] }; + +exports.uptime = function () { return 0 }; + +exports.freemem = function () { + return Number.MAX_VALUE; +}; + +exports.totalmem = function () { + return Number.MAX_VALUE; +}; + +exports.cpus = function () { return [] }; + +exports.type = function () { return 'Browser' }; + +exports.release = function () { + if (typeof navigator !== 'undefined') { + return navigator.appVersion; + } + return ''; +}; + +exports.networkInterfaces += exports.getNetworkInterfaces += function () { return {} }; + +exports.arch = function () { return 'javascript' }; + +exports.platform = function () { return 'browser' }; + +exports.tmpdir = exports.tmpDir = function () { + return '/tmp'; +}; + +exports.EOL = '\n'; + +},{}],51:[function(require,module,exports){ +// shim for using process in browser + +var process = module.exports = {}; +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = setTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + clearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + setTimeout(drainQueue, 0); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],52:[function(require,module,exports){ +module.exports = require("./lib/_stream_duplex.js") + +},{"./lib/_stream_duplex.js":53}],53:[function(require,module,exports){ +(function (process){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a duplex stream is just a stream that is both readable and writable. +// Since JS doesn't have multiple prototypal inheritance, this class +// prototypally inherits from Readable, and then parasitically from +// Writable. + +module.exports = Duplex; + +/**/ +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) keys.push(key); + return keys; +} +/**/ + + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +var Readable = require('./_stream_readable'); +var Writable = require('./_stream_writable'); + +util.inherits(Duplex, Readable); + +forEach(objectKeys(Writable.prototype), function(method) { + if (!Duplex.prototype[method]) + Duplex.prototype[method] = Writable.prototype[method]; +}); + +function Duplex(options) { + if (!(this instanceof Duplex)) + return new Duplex(options); + + Readable.call(this, options); + Writable.call(this, options); + + if (options && options.readable === false) + this.readable = false; + + if (options && options.writable === false) + this.writable = false; + + this.allowHalfOpen = true; + if (options && options.allowHalfOpen === false) + this.allowHalfOpen = false; + + this.once('end', onend); +} + +// the no-half-open enforcer +function onend() { + // if we allow half-open state, or if the writable side ended, + // then we're ok. + if (this.allowHalfOpen || this._writableState.ended) + return; + + // no more data can be written. + // But allow more writes to happen in this tick. + process.nextTick(this.end.bind(this)); +} + +function forEach (xs, f) { + for (var i = 0, l = xs.length; i < l; i++) { + f(xs[i], i); + } +} + +}).call(this,require('_process')) +},{"./_stream_readable":55,"./_stream_writable":57,"_process":51,"core-util-is":58,"inherits":48}],54:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a passthrough stream. +// basically just the most minimal sort of Transform stream. +// Every written chunk gets output as-is. + +module.exports = PassThrough; + +var Transform = require('./_stream_transform'); + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +util.inherits(PassThrough, Transform); + +function PassThrough(options) { + if (!(this instanceof PassThrough)) + return new PassThrough(options); + + Transform.call(this, options); +} + +PassThrough.prototype._transform = function(chunk, encoding, cb) { + cb(null, chunk); +}; + +},{"./_stream_transform":56,"core-util-is":58,"inherits":48}],55:[function(require,module,exports){ +(function (process){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +module.exports = Readable; + +/**/ +var isArray = require('isarray'); +/**/ + + +/**/ +var Buffer = require('buffer').Buffer; +/**/ + +Readable.ReadableState = ReadableState; + +var EE = require('events').EventEmitter; + +/**/ +if (!EE.listenerCount) EE.listenerCount = function(emitter, type) { + return emitter.listeners(type).length; +}; +/**/ + +var Stream = require('stream'); + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +var StringDecoder; + + +/**/ +var debug = require('util'); +if (debug && debug.debuglog) { + debug = debug.debuglog('stream'); +} else { + debug = function () {}; +} +/**/ + + +util.inherits(Readable, Stream); + +function ReadableState(options, stream) { + var Duplex = require('./_stream_duplex'); + + options = options || {}; + + // the point at which it stops calling _read() to fill the buffer + // Note: 0 is a valid value, means "don't call _read preemptively ever" + var hwm = options.highWaterMark; + var defaultHwm = options.objectMode ? 16 : 16 * 1024; + this.highWaterMark = (hwm || hwm === 0) ? hwm : defaultHwm; + + // cast to ints. + this.highWaterMark = ~~this.highWaterMark; + + this.buffer = []; + this.length = 0; + this.pipes = null; + this.pipesCount = 0; + this.flowing = null; + this.ended = false; + this.endEmitted = false; + this.reading = false; + + // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + this.sync = true; + + // whenever we return null, then we set a flag to say + // that we're awaiting a 'readable' event emission. + this.needReadable = false; + this.emittedReadable = false; + this.readableListening = false; + + + // object stream flag. Used to make read(n) ignore n and to + // make all the buffer merging and length checks go away + this.objectMode = !!options.objectMode; + + if (stream instanceof Duplex) + this.objectMode = this.objectMode || !!options.readableObjectMode; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; + + // when piping, we only care about 'readable' events that happen + // after read()ing all the bytes and not getting any pushback. + this.ranOut = false; + + // the number of writers that are awaiting a drain event in .pipe()s + this.awaitDrain = 0; + + // if true, a maybeReadMore has been scheduled + this.readingMore = false; + + this.decoder = null; + this.encoding = null; + if (options.encoding) { + if (!StringDecoder) + StringDecoder = require('string_decoder/').StringDecoder; + this.decoder = new StringDecoder(options.encoding); + this.encoding = options.encoding; + } +} + +function Readable(options) { + var Duplex = require('./_stream_duplex'); + + if (!(this instanceof Readable)) + return new Readable(options); + + this._readableState = new ReadableState(options, this); + + // legacy + this.readable = true; + + Stream.call(this); +} + +// Manually shove something into the read() buffer. +// This returns true if the highWaterMark has not been hit yet, +// similar to how Writable.write() returns true if you should +// write() some more. +Readable.prototype.push = function(chunk, encoding) { + var state = this._readableState; + + if (util.isString(chunk) && !state.objectMode) { + encoding = encoding || state.defaultEncoding; + if (encoding !== state.encoding) { + chunk = new Buffer(chunk, encoding); + encoding = ''; + } + } + + return readableAddChunk(this, state, chunk, encoding, false); +}; + +// Unshift should *always* be something directly out of read() +Readable.prototype.unshift = function(chunk) { + var state = this._readableState; + return readableAddChunk(this, state, chunk, '', true); +}; + +function readableAddChunk(stream, state, chunk, encoding, addToFront) { + var er = chunkInvalid(state, chunk); + if (er) { + stream.emit('error', er); + } else if (util.isNullOrUndefined(chunk)) { + state.reading = false; + if (!state.ended) + onEofChunk(stream, state); + } else if (state.objectMode || chunk && chunk.length > 0) { + if (state.ended && !addToFront) { + var e = new Error('stream.push() after EOF'); + stream.emit('error', e); + } else if (state.endEmitted && addToFront) { + var e = new Error('stream.unshift() after end event'); + stream.emit('error', e); + } else { + if (state.decoder && !addToFront && !encoding) + chunk = state.decoder.write(chunk); + + if (!addToFront) + state.reading = false; + + // if we want the data now, just emit it. + if (state.flowing && state.length === 0 && !state.sync) { + stream.emit('data', chunk); + stream.read(0); + } else { + // update the buffer info. + state.length += state.objectMode ? 1 : chunk.length; + if (addToFront) + state.buffer.unshift(chunk); + else + state.buffer.push(chunk); + + if (state.needReadable) + emitReadable(stream); + } + + maybeReadMore(stream, state); + } + } else if (!addToFront) { + state.reading = false; + } + + return needMoreData(state); +} + + + +// if it's past the high water mark, we can push in some more. +// Also, if we have no data yet, we can stand some +// more bytes. This is to work around cases where hwm=0, +// such as the repl. Also, if the push() triggered a +// readable event, and the user called read(largeNumber) such that +// needReadable was set, then we ought to push more, so that another +// 'readable' event will be triggered. +function needMoreData(state) { + return !state.ended && + (state.needReadable || + state.length < state.highWaterMark || + state.length === 0); +} + +// backwards compatibility. +Readable.prototype.setEncoding = function(enc) { + if (!StringDecoder) + StringDecoder = require('string_decoder/').StringDecoder; + this._readableState.decoder = new StringDecoder(enc); + this._readableState.encoding = enc; + return this; +}; + +// Don't raise the hwm > 128MB +var MAX_HWM = 0x800000; +function roundUpToNextPowerOf2(n) { + if (n >= MAX_HWM) { + n = MAX_HWM; + } else { + // Get the next highest power of 2 + n--; + for (var p = 1; p < 32; p <<= 1) n |= n >> p; + n++; + } + return n; +} + +function howMuchToRead(n, state) { + if (state.length === 0 && state.ended) + return 0; + + if (state.objectMode) + return n === 0 ? 0 : 1; + + if (isNaN(n) || util.isNull(n)) { + // only flow one buffer at a time + if (state.flowing && state.buffer.length) + return state.buffer[0].length; + else + return state.length; + } + + if (n <= 0) + return 0; + + // If we're asking for more than the target buffer level, + // then raise the water mark. Bump up to the next highest + // power of 2, to prevent increasing it excessively in tiny + // amounts. + if (n > state.highWaterMark) + state.highWaterMark = roundUpToNextPowerOf2(n); + + // don't have that much. return null, unless we've ended. + if (n > state.length) { + if (!state.ended) { + state.needReadable = true; + return 0; + } else + return state.length; + } + + return n; +} + +// you can override either this method, or the async _read(n) below. +Readable.prototype.read = function(n) { + debug('read', n); + var state = this._readableState; + var nOrig = n; + + if (!util.isNumber(n) || n > 0) + state.emittedReadable = false; + + // if we're doing read(0) to trigger a readable event, but we + // already have a bunch of data in the buffer, then just trigger + // the 'readable' event and move on. + if (n === 0 && + state.needReadable && + (state.length >= state.highWaterMark || state.ended)) { + debug('read: emitReadable', state.length, state.ended); + if (state.length === 0 && state.ended) + endReadable(this); + else + emitReadable(this); + return null; + } + + n = howMuchToRead(n, state); + + // if we've ended, and we're now clear, then finish it up. + if (n === 0 && state.ended) { + if (state.length === 0) + endReadable(this); + return null; + } + + // All the actual chunk generation logic needs to be + // *below* the call to _read. The reason is that in certain + // synthetic stream cases, such as passthrough streams, _read + // may be a completely synchronous operation which may change + // the state of the read buffer, providing enough data when + // before there was *not* enough. + // + // So, the steps are: + // 1. Figure out what the state of things will be after we do + // a read from the buffer. + // + // 2. If that resulting state will trigger a _read, then call _read. + // Note that this may be asynchronous, or synchronous. Yes, it is + // deeply ugly to write APIs this way, but that still doesn't mean + // that the Readable class should behave improperly, as streams are + // designed to be sync/async agnostic. + // Take note if the _read call is sync or async (ie, if the read call + // has returned yet), so that we know whether or not it's safe to emit + // 'readable' etc. + // + // 3. Actually pull the requested chunks out of the buffer and return. + + // if we need a readable event, then we need to do some reading. + var doRead = state.needReadable; + debug('need readable', doRead); + + // if we currently have less than the highWaterMark, then also read some + if (state.length === 0 || state.length - n < state.highWaterMark) { + doRead = true; + debug('length less than watermark', doRead); + } + + // however, if we've ended, then there's no point, and if we're already + // reading, then it's unnecessary. + if (state.ended || state.reading) { + doRead = false; + debug('reading or ended', doRead); + } + + if (doRead) { + debug('do read'); + state.reading = true; + state.sync = true; + // if the length is currently zero, then we *need* a readable event. + if (state.length === 0) + state.needReadable = true; + // call internal read method + this._read(state.highWaterMark); + state.sync = false; + } + + // If _read pushed data synchronously, then `reading` will be false, + // and we need to re-evaluate how much data we can return to the user. + if (doRead && !state.reading) + n = howMuchToRead(nOrig, state); + + var ret; + if (n > 0) + ret = fromList(n, state); + else + ret = null; + + if (util.isNull(ret)) { + state.needReadable = true; + n = 0; + } + + state.length -= n; + + // If we have nothing in the buffer, then we want to know + // as soon as we *do* get something into the buffer. + if (state.length === 0 && !state.ended) + state.needReadable = true; + + // If we tried to read() past the EOF, then emit end on the next tick. + if (nOrig !== n && state.ended && state.length === 0) + endReadable(this); + + if (!util.isNull(ret)) + this.emit('data', ret); + + return ret; +}; + +function chunkInvalid(state, chunk) { + var er = null; + if (!util.isBuffer(chunk) && + !util.isString(chunk) && + !util.isNullOrUndefined(chunk) && + !state.objectMode) { + er = new TypeError('Invalid non-string/buffer chunk'); + } + return er; +} + + +function onEofChunk(stream, state) { + if (state.decoder && !state.ended) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) { + state.buffer.push(chunk); + state.length += state.objectMode ? 1 : chunk.length; + } + } + state.ended = true; + + // emit 'readable' now to make sure it gets picked up. + emitReadable(stream); +} + +// Don't emit readable right away in sync mode, because this can trigger +// another read() call => stack overflow. This way, it might trigger +// a nextTick recursion warning, but that's not so bad. +function emitReadable(stream) { + var state = stream._readableState; + state.needReadable = false; + if (!state.emittedReadable) { + debug('emitReadable', state.flowing); + state.emittedReadable = true; + if (state.sync) + process.nextTick(function() { + emitReadable_(stream); + }); + else + emitReadable_(stream); + } +} + +function emitReadable_(stream) { + debug('emit readable'); + stream.emit('readable'); + flow(stream); +} + + +// at this point, the user has presumably seen the 'readable' event, +// and called read() to consume some data. that may have triggered +// in turn another _read(n) call, in which case reading = true if +// it's in progress. +// However, if we're not ended, or reading, and the length < hwm, +// then go ahead and try to read some more preemptively. +function maybeReadMore(stream, state) { + if (!state.readingMore) { + state.readingMore = true; + process.nextTick(function() { + maybeReadMore_(stream, state); + }); + } +} + +function maybeReadMore_(stream, state) { + var len = state.length; + while (!state.reading && !state.flowing && !state.ended && + state.length < state.highWaterMark) { + debug('maybeReadMore read 0'); + stream.read(0); + if (len === state.length) + // didn't get any data, stop spinning. + break; + else + len = state.length; + } + state.readingMore = false; +} + +// abstract method. to be overridden in specific implementation classes. +// call cb(er, data) where data is <= n in length. +// for virtual (non-string, non-buffer) streams, "length" is somewhat +// arbitrary, and perhaps not very meaningful. +Readable.prototype._read = function(n) { + this.emit('error', new Error('not implemented')); +}; + +Readable.prototype.pipe = function(dest, pipeOpts) { + var src = this; + var state = this._readableState; + + switch (state.pipesCount) { + case 0: + state.pipes = dest; + break; + case 1: + state.pipes = [state.pipes, dest]; + break; + default: + state.pipes.push(dest); + break; + } + state.pipesCount += 1; + debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); + + var doEnd = (!pipeOpts || pipeOpts.end !== false) && + dest !== process.stdout && + dest !== process.stderr; + + var endFn = doEnd ? onend : cleanup; + if (state.endEmitted) + process.nextTick(endFn); + else + src.once('end', endFn); + + dest.on('unpipe', onunpipe); + function onunpipe(readable) { + debug('onunpipe'); + if (readable === src) { + cleanup(); + } + } + + function onend() { + debug('onend'); + dest.end(); + } + + // when the dest drains, it reduces the awaitDrain counter + // on the source. This would be more elegant with a .once() + // handler in flow(), but adding and removing repeatedly is + // too slow. + var ondrain = pipeOnDrain(src); + dest.on('drain', ondrain); + + function cleanup() { + debug('cleanup'); + // cleanup event handlers once the pipe is broken + dest.removeListener('close', onclose); + dest.removeListener('finish', onfinish); + dest.removeListener('drain', ondrain); + dest.removeListener('error', onerror); + dest.removeListener('unpipe', onunpipe); + src.removeListener('end', onend); + src.removeListener('end', cleanup); + src.removeListener('data', ondata); + + // if the reader is waiting for a drain event from this + // specific writer, then it would cause it to never start + // flowing again. + // So, if this is awaiting a drain, then we just call it now. + // If we don't know, then assume that we are waiting for one. + if (state.awaitDrain && + (!dest._writableState || dest._writableState.needDrain)) + ondrain(); + } + + src.on('data', ondata); + function ondata(chunk) { + debug('ondata'); + var ret = dest.write(chunk); + if (false === ret) { + debug('false write response, pause', + src._readableState.awaitDrain); + src._readableState.awaitDrain++; + src.pause(); + } + } + + // if the dest has an error, then stop piping into it. + // however, don't suppress the throwing behavior for this. + function onerror(er) { + debug('onerror', er); + unpipe(); + dest.removeListener('error', onerror); + if (EE.listenerCount(dest, 'error') === 0) + dest.emit('error', er); + } + // This is a brutally ugly hack to make sure that our error handler + // is attached before any userland ones. NEVER DO THIS. + if (!dest._events || !dest._events.error) + dest.on('error', onerror); + else if (isArray(dest._events.error)) + dest._events.error.unshift(onerror); + else + dest._events.error = [onerror, dest._events.error]; + + + + // Both close and finish should trigger unpipe, but only once. + function onclose() { + dest.removeListener('finish', onfinish); + unpipe(); + } + dest.once('close', onclose); + function onfinish() { + debug('onfinish'); + dest.removeListener('close', onclose); + unpipe(); + } + dest.once('finish', onfinish); + + function unpipe() { + debug('unpipe'); + src.unpipe(dest); + } + + // tell the dest that it's being piped to + dest.emit('pipe', src); + + // start the flow if it hasn't been started already. + if (!state.flowing) { + debug('pipe resume'); + src.resume(); + } + + return dest; +}; + +function pipeOnDrain(src) { + return function() { + var state = src._readableState; + debug('pipeOnDrain', state.awaitDrain); + if (state.awaitDrain) + state.awaitDrain--; + if (state.awaitDrain === 0 && EE.listenerCount(src, 'data')) { + state.flowing = true; + flow(src); + } + }; +} + + +Readable.prototype.unpipe = function(dest) { + var state = this._readableState; + + // if we're not piping anywhere, then do nothing. + if (state.pipesCount === 0) + return this; + + // just one destination. most common case. + if (state.pipesCount === 1) { + // passed in one, but it's not the right one. + if (dest && dest !== state.pipes) + return this; + + if (!dest) + dest = state.pipes; + + // got a match. + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + if (dest) + dest.emit('unpipe', this); + return this; + } + + // slow case. multiple pipe destinations. + + if (!dest) { + // remove all. + var dests = state.pipes; + var len = state.pipesCount; + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + + for (var i = 0; i < len; i++) + dests[i].emit('unpipe', this); + return this; + } + + // try to find the right one. + var i = indexOf(state.pipes, dest); + if (i === -1) + return this; + + state.pipes.splice(i, 1); + state.pipesCount -= 1; + if (state.pipesCount === 1) + state.pipes = state.pipes[0]; + + dest.emit('unpipe', this); + + return this; +}; + +// set up data events if they are asked for +// Ensure readable listeners eventually get something +Readable.prototype.on = function(ev, fn) { + var res = Stream.prototype.on.call(this, ev, fn); + + // If listening to data, and it has not explicitly been paused, + // then call resume to start the flow of data on the next tick. + if (ev === 'data' && false !== this._readableState.flowing) { + this.resume(); + } + + if (ev === 'readable' && this.readable) { + var state = this._readableState; + if (!state.readableListening) { + state.readableListening = true; + state.emittedReadable = false; + state.needReadable = true; + if (!state.reading) { + var self = this; + process.nextTick(function() { + debug('readable nexttick read 0'); + self.read(0); + }); + } else if (state.length) { + emitReadable(this, state); + } + } + } + + return res; +}; +Readable.prototype.addListener = Readable.prototype.on; + +// pause() and resume() are remnants of the legacy readable stream API +// If the user uses them, then switch into old mode. +Readable.prototype.resume = function() { + var state = this._readableState; + if (!state.flowing) { + debug('resume'); + state.flowing = true; + if (!state.reading) { + debug('resume read 0'); + this.read(0); + } + resume(this, state); + } + return this; +}; + +function resume(stream, state) { + if (!state.resumeScheduled) { + state.resumeScheduled = true; + process.nextTick(function() { + resume_(stream, state); + }); + } +} + +function resume_(stream, state) { + state.resumeScheduled = false; + stream.emit('resume'); + flow(stream); + if (state.flowing && !state.reading) + stream.read(0); +} + +Readable.prototype.pause = function() { + debug('call pause flowing=%j', this._readableState.flowing); + if (false !== this._readableState.flowing) { + debug('pause'); + this._readableState.flowing = false; + this.emit('pause'); + } + return this; +}; + +function flow(stream) { + var state = stream._readableState; + debug('flow', state.flowing); + if (state.flowing) { + do { + var chunk = stream.read(); + } while (null !== chunk && state.flowing); + } +} + +// wrap an old-style stream as the async data source. +// This is *not* part of the readable stream interface. +// It is an ugly unfortunate mess of history. +Readable.prototype.wrap = function(stream) { + var state = this._readableState; + var paused = false; + + var self = this; + stream.on('end', function() { + debug('wrapped end'); + if (state.decoder && !state.ended) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) + self.push(chunk); + } + + self.push(null); + }); + + stream.on('data', function(chunk) { + debug('wrapped data'); + if (state.decoder) + chunk = state.decoder.write(chunk); + if (!chunk || !state.objectMode && !chunk.length) + return; + + var ret = self.push(chunk); + if (!ret) { + paused = true; + stream.pause(); + } + }); + + // proxy all the other methods. + // important when wrapping filters and duplexes. + for (var i in stream) { + if (util.isFunction(stream[i]) && util.isUndefined(this[i])) { + this[i] = function(method) { return function() { + return stream[method].apply(stream, arguments); + }}(i); + } + } + + // proxy certain important events. + var events = ['error', 'close', 'destroy', 'pause', 'resume']; + forEach(events, function(ev) { + stream.on(ev, self.emit.bind(self, ev)); + }); + + // when we try to consume some more bytes, simply unpause the + // underlying stream. + self._read = function(n) { + debug('wrapped _read', n); + if (paused) { + paused = false; + stream.resume(); + } + }; + + return self; +}; + + + +// exposed for testing purposes only. +Readable._fromList = fromList; + +// Pluck off n bytes from an array of buffers. +// Length is the combined lengths of all the buffers in the list. +function fromList(n, state) { + var list = state.buffer; + var length = state.length; + var stringMode = !!state.decoder; + var objectMode = !!state.objectMode; + var ret; + + // nothing in the list, definitely empty. + if (list.length === 0) + return null; + + if (length === 0) + ret = null; + else if (objectMode) + ret = list.shift(); + else if (!n || n >= length) { + // read it all, truncate the array. + if (stringMode) + ret = list.join(''); + else + ret = Buffer.concat(list, length); + list.length = 0; + } else { + // read just some of it. + if (n < list[0].length) { + // just take a part of the first list item. + // slice is the same for buffers and strings. + var buf = list[0]; + ret = buf.slice(0, n); + list[0] = buf.slice(n); + } else if (n === list[0].length) { + // first list is a perfect match + ret = list.shift(); + } else { + // complex case. + // we have enough to cover it, but it spans past the first buffer. + if (stringMode) + ret = ''; + else + ret = new Buffer(n); + + var c = 0; + for (var i = 0, l = list.length; i < l && c < n; i++) { + var buf = list[0]; + var cpy = Math.min(n - c, buf.length); + + if (stringMode) + ret += buf.slice(0, cpy); + else + buf.copy(ret, c, 0, cpy); + + if (cpy < buf.length) + list[0] = buf.slice(cpy); + else + list.shift(); + + c += cpy; + } + } + } + + return ret; +} + +function endReadable(stream) { + var state = stream._readableState; + + // If we get here before consuming all the bytes, then that is a + // bug in node. Should never happen. + if (state.length > 0) + throw new Error('endReadable called on non-empty stream'); + + if (!state.endEmitted) { + state.ended = true; + process.nextTick(function() { + // Check that we didn't get one last unshift. + if (!state.endEmitted && state.length === 0) { + state.endEmitted = true; + stream.readable = false; + stream.emit('end'); + } + }); + } +} + +function forEach (xs, f) { + for (var i = 0, l = xs.length; i < l; i++) { + f(xs[i], i); + } +} + +function indexOf (xs, x) { + for (var i = 0, l = xs.length; i < l; i++) { + if (xs[i] === x) return i; + } + return -1; +} + +}).call(this,require('_process')) +},{"./_stream_duplex":53,"_process":51,"buffer":43,"core-util-is":58,"events":47,"inherits":48,"isarray":49,"stream":63,"string_decoder/":64,"util":42}],56:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +// a transform stream is a readable/writable stream where you do +// something with the data. Sometimes it's called a "filter", +// but that's not a great name for it, since that implies a thing where +// some bits pass through, and others are simply ignored. (That would +// be a valid example of a transform, of course.) +// +// While the output is causally related to the input, it's not a +// necessarily symmetric or synchronous transformation. For example, +// a zlib stream might take multiple plain-text writes(), and then +// emit a single compressed chunk some time in the future. +// +// Here's how this works: +// +// The Transform stream has all the aspects of the readable and writable +// stream classes. When you write(chunk), that calls _write(chunk,cb) +// internally, and returns false if there's a lot of pending writes +// buffered up. When you call read(), that calls _read(n) until +// there's enough pending readable data buffered up. +// +// In a transform stream, the written data is placed in a buffer. When +// _read(n) is called, it transforms the queued up data, calling the +// buffered _write cb's as it consumes chunks. If consuming a single +// written chunk would result in multiple output chunks, then the first +// outputted bit calls the readcb, and subsequent chunks just go into +// the read buffer, and will cause it to emit 'readable' if necessary. +// +// This way, back-pressure is actually determined by the reading side, +// since _read has to be called to start processing a new chunk. However, +// a pathological inflate type of transform can cause excessive buffering +// here. For example, imagine a stream where every byte of input is +// interpreted as an integer from 0-255, and then results in that many +// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in +// 1kb of data being output. In this case, you could write a very small +// amount of input, and end up with a very large amount of output. In +// such a pathological inflating mechanism, there'd be no way to tell +// the system to stop doing the transform. A single 4MB write could +// cause the system to run out of memory. +// +// However, even in such a pathological case, only a single written chunk +// would be consumed, and then the rest would wait (un-transformed) until +// the results of the previous transformed chunk were consumed. + +module.exports = Transform; + +var Duplex = require('./_stream_duplex'); + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +util.inherits(Transform, Duplex); + + +function TransformState(options, stream) { + this.afterTransform = function(er, data) { + return afterTransform(stream, er, data); + }; + + this.needTransform = false; + this.transforming = false; + this.writecb = null; + this.writechunk = null; +} + +function afterTransform(stream, er, data) { + var ts = stream._transformState; + ts.transforming = false; + + var cb = ts.writecb; + + if (!cb) + return stream.emit('error', new Error('no writecb in Transform class')); + + ts.writechunk = null; + ts.writecb = null; + + if (!util.isNullOrUndefined(data)) + stream.push(data); + + if (cb) + cb(er); + + var rs = stream._readableState; + rs.reading = false; + if (rs.needReadable || rs.length < rs.highWaterMark) { + stream._read(rs.highWaterMark); + } +} + + +function Transform(options) { + if (!(this instanceof Transform)) + return new Transform(options); + + Duplex.call(this, options); + + this._transformState = new TransformState(options, this); + + // when the writable side finishes, then flush out anything remaining. + var stream = this; + + // start out asking for a readable event once data is transformed. + this._readableState.needReadable = true; + + // we have implemented the _read method, and done the other things + // that Readable wants before the first _read call, so unset the + // sync guard flag. + this._readableState.sync = false; + + this.once('prefinish', function() { + if (util.isFunction(this._flush)) + this._flush(function(er) { + done(stream, er); + }); + else + done(stream); + }); +} + +Transform.prototype.push = function(chunk, encoding) { + this._transformState.needTransform = false; + return Duplex.prototype.push.call(this, chunk, encoding); +}; + +// This is the part where you do stuff! +// override this function in implementation classes. +// 'chunk' is an input chunk. +// +// Call `push(newChunk)` to pass along transformed output +// to the readable side. You may call 'push' zero or more times. +// +// Call `cb(err)` when you are done with this chunk. If you pass +// an error, then that'll put the hurt on the whole operation. If you +// never call cb(), then you'll never get another chunk. +Transform.prototype._transform = function(chunk, encoding, cb) { + throw new Error('not implemented'); +}; + +Transform.prototype._write = function(chunk, encoding, cb) { + var ts = this._transformState; + ts.writecb = cb; + ts.writechunk = chunk; + ts.writeencoding = encoding; + if (!ts.transforming) { + var rs = this._readableState; + if (ts.needTransform || + rs.needReadable || + rs.length < rs.highWaterMark) + this._read(rs.highWaterMark); + } +}; + +// Doesn't matter what the args are here. +// _transform does all the work. +// That we got here means that the readable side wants more data. +Transform.prototype._read = function(n) { + var ts = this._transformState; + + if (!util.isNull(ts.writechunk) && ts.writecb && !ts.transforming) { + ts.transforming = true; + this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); + } else { + // mark that we need a transform, so that any data that comes in + // will get processed, now that we've asked for it. + ts.needTransform = true; + } +}; + + +function done(stream, er) { + if (er) + return stream.emit('error', er); + + // if there's nothing in the write buffer, then that means + // that nothing more will ever be provided + var ws = stream._writableState; + var ts = stream._transformState; + + if (ws.length) + throw new Error('calling transform done when ws.length != 0'); + + if (ts.transforming) + throw new Error('calling transform done when still transforming'); + + return stream.push(null); +} + +},{"./_stream_duplex":53,"core-util-is":58,"inherits":48}],57:[function(require,module,exports){ +(function (process){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// A bit simpler than readable streams. +// Implement an async ._write(chunk, cb), and it'll handle all +// the drain event emission and buffering. + +module.exports = Writable; + +/**/ +var Buffer = require('buffer').Buffer; +/**/ + +Writable.WritableState = WritableState; + + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +var Stream = require('stream'); + +util.inherits(Writable, Stream); + +function WriteReq(chunk, encoding, cb) { + this.chunk = chunk; + this.encoding = encoding; + this.callback = cb; +} + +function WritableState(options, stream) { + var Duplex = require('./_stream_duplex'); + + options = options || {}; + + // the point at which write() starts returning false + // Note: 0 is a valid value, means that we always return false if + // the entire buffer is not flushed immediately on write() + var hwm = options.highWaterMark; + var defaultHwm = options.objectMode ? 16 : 16 * 1024; + this.highWaterMark = (hwm || hwm === 0) ? hwm : defaultHwm; + + // object stream flag to indicate whether or not this stream + // contains buffers or objects. + this.objectMode = !!options.objectMode; + + if (stream instanceof Duplex) + this.objectMode = this.objectMode || !!options.writableObjectMode; + + // cast to ints. + this.highWaterMark = ~~this.highWaterMark; + + this.needDrain = false; + // at the start of calling end() + this.ending = false; + // when end() has been called, and returned + this.ended = false; + // when 'finish' is emitted + this.finished = false; + + // should we decode strings into buffers before passing to _write? + // this is here so that some node-core streams can optimize string + // handling at a lower level. + var noDecode = options.decodeStrings === false; + this.decodeStrings = !noDecode; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; + + // not an actual buffer we keep track of, but a measurement + // of how much we're waiting to get pushed to some underlying + // socket or file. + this.length = 0; + + // a flag to see when we're in the middle of a write. + this.writing = false; + + // when true all writes will be buffered until .uncork() call + this.corked = 0; + + // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + this.sync = true; + + // a flag to know if we're processing previously buffered items, which + // may call the _write() callback in the same tick, so that we don't + // end up in an overlapped onwrite situation. + this.bufferProcessing = false; + + // the callback that's passed to _write(chunk,cb) + this.onwrite = function(er) { + onwrite(stream, er); + }; + + // the callback that the user supplies to write(chunk,encoding,cb) + this.writecb = null; + + // the amount that is being written when _write is called. + this.writelen = 0; + + this.buffer = []; + + // number of pending user-supplied write callbacks + // this must be 0 before 'finish' can be emitted + this.pendingcb = 0; + + // emit prefinish if the only thing we're waiting for is _write cbs + // This is relevant for synchronous Transform streams + this.prefinished = false; + + // True if the error was already emitted and should not be thrown again + this.errorEmitted = false; +} + +function Writable(options) { + var Duplex = require('./_stream_duplex'); + + // Writable ctor is applied to Duplexes, though they're not + // instanceof Writable, they're instanceof Readable. + if (!(this instanceof Writable) && !(this instanceof Duplex)) + return new Writable(options); + + this._writableState = new WritableState(options, this); + + // legacy. + this.writable = true; + + Stream.call(this); +} + +// Otherwise people can pipe Writable streams, which is just wrong. +Writable.prototype.pipe = function() { + this.emit('error', new Error('Cannot pipe. Not readable.')); +}; + + +function writeAfterEnd(stream, state, cb) { + var er = new Error('write after end'); + // TODO: defer error events consistently everywhere, not just the cb + stream.emit('error', er); + process.nextTick(function() { + cb(er); + }); +} + +// If we get something that is not a buffer, string, null, or undefined, +// and we're not in objectMode, then that's an error. +// Otherwise stream chunks are all considered to be of length=1, and the +// watermarks determine how many objects to keep in the buffer, rather than +// how many bytes or characters. +function validChunk(stream, state, chunk, cb) { + var valid = true; + if (!util.isBuffer(chunk) && + !util.isString(chunk) && + !util.isNullOrUndefined(chunk) && + !state.objectMode) { + var er = new TypeError('Invalid non-string/buffer chunk'); + stream.emit('error', er); + process.nextTick(function() { + cb(er); + }); + valid = false; + } + return valid; +} + +Writable.prototype.write = function(chunk, encoding, cb) { + var state = this._writableState; + var ret = false; + + if (util.isFunction(encoding)) { + cb = encoding; + encoding = null; + } + + if (util.isBuffer(chunk)) + encoding = 'buffer'; + else if (!encoding) + encoding = state.defaultEncoding; + + if (!util.isFunction(cb)) + cb = function() {}; + + if (state.ended) + writeAfterEnd(this, state, cb); + else if (validChunk(this, state, chunk, cb)) { + state.pendingcb++; + ret = writeOrBuffer(this, state, chunk, encoding, cb); + } + + return ret; +}; + +Writable.prototype.cork = function() { + var state = this._writableState; + + state.corked++; +}; + +Writable.prototype.uncork = function() { + var state = this._writableState; + + if (state.corked) { + state.corked--; + + if (!state.writing && + !state.corked && + !state.finished && + !state.bufferProcessing && + state.buffer.length) + clearBuffer(this, state); + } +}; + +function decodeChunk(state, chunk, encoding) { + if (!state.objectMode && + state.decodeStrings !== false && + util.isString(chunk)) { + chunk = new Buffer(chunk, encoding); + } + return chunk; +} + +// if we're already writing something, then just put this +// in the queue, and wait our turn. Otherwise, call _write +// If we return false, then we need a drain event, so set that flag. +function writeOrBuffer(stream, state, chunk, encoding, cb) { + chunk = decodeChunk(state, chunk, encoding); + if (util.isBuffer(chunk)) + encoding = 'buffer'; + var len = state.objectMode ? 1 : chunk.length; + + state.length += len; + + var ret = state.length < state.highWaterMark; + // we must ensure that previous needDrain will not be reset to false. + if (!ret) + state.needDrain = true; + + if (state.writing || state.corked) + state.buffer.push(new WriteReq(chunk, encoding, cb)); + else + doWrite(stream, state, false, len, chunk, encoding, cb); + + return ret; +} + +function doWrite(stream, state, writev, len, chunk, encoding, cb) { + state.writelen = len; + state.writecb = cb; + state.writing = true; + state.sync = true; + if (writev) + stream._writev(chunk, state.onwrite); + else + stream._write(chunk, encoding, state.onwrite); + state.sync = false; +} + +function onwriteError(stream, state, sync, er, cb) { + if (sync) + process.nextTick(function() { + state.pendingcb--; + cb(er); + }); + else { + state.pendingcb--; + cb(er); + } + + stream._writableState.errorEmitted = true; + stream.emit('error', er); +} + +function onwriteStateUpdate(state) { + state.writing = false; + state.writecb = null; + state.length -= state.writelen; + state.writelen = 0; +} + +function onwrite(stream, er) { + var state = stream._writableState; + var sync = state.sync; + var cb = state.writecb; + + onwriteStateUpdate(state); + + if (er) + onwriteError(stream, state, sync, er, cb); + else { + // Check if we're actually ready to finish, but don't emit yet + var finished = needFinish(stream, state); + + if (!finished && + !state.corked && + !state.bufferProcessing && + state.buffer.length) { + clearBuffer(stream, state); + } + + if (sync) { + process.nextTick(function() { + afterWrite(stream, state, finished, cb); + }); + } else { + afterWrite(stream, state, finished, cb); + } + } +} + +function afterWrite(stream, state, finished, cb) { + if (!finished) + onwriteDrain(stream, state); + state.pendingcb--; + cb(); + finishMaybe(stream, state); +} + +// Must force callback to be called on nextTick, so that we don't +// emit 'drain' before the write() consumer gets the 'false' return +// value, and has a chance to attach a 'drain' listener. +function onwriteDrain(stream, state) { + if (state.length === 0 && state.needDrain) { + state.needDrain = false; + stream.emit('drain'); + } +} + + +// if there's something in the buffer waiting, then process it +function clearBuffer(stream, state) { + state.bufferProcessing = true; + + if (stream._writev && state.buffer.length > 1) { + // Fast case, write everything using _writev() + var cbs = []; + for (var c = 0; c < state.buffer.length; c++) + cbs.push(state.buffer[c].callback); + + // count the one we are adding, as well. + // TODO(isaacs) clean this up + state.pendingcb++; + doWrite(stream, state, true, state.length, state.buffer, '', function(err) { + for (var i = 0; i < cbs.length; i++) { + state.pendingcb--; + cbs[i](err); + } + }); + + // Clear buffer + state.buffer = []; + } else { + // Slow case, write chunks one-by-one + for (var c = 0; c < state.buffer.length; c++) { + var entry = state.buffer[c]; + var chunk = entry.chunk; + var encoding = entry.encoding; + var cb = entry.callback; + var len = state.objectMode ? 1 : chunk.length; + + doWrite(stream, state, false, len, chunk, encoding, cb); + + // if we didn't call the onwrite immediately, then + // it means that we need to wait until it does. + // also, that means that the chunk and cb are currently + // being processed, so move the buffer counter past them. + if (state.writing) { + c++; + break; + } + } + + if (c < state.buffer.length) + state.buffer = state.buffer.slice(c); + else + state.buffer.length = 0; + } + + state.bufferProcessing = false; +} + +Writable.prototype._write = function(chunk, encoding, cb) { + cb(new Error('not implemented')); + +}; + +Writable.prototype._writev = null; + +Writable.prototype.end = function(chunk, encoding, cb) { + var state = this._writableState; + + if (util.isFunction(chunk)) { + cb = chunk; + chunk = null; + encoding = null; + } else if (util.isFunction(encoding)) { + cb = encoding; + encoding = null; + } + + if (!util.isNullOrUndefined(chunk)) + this.write(chunk, encoding); + + // .end() fully uncorks + if (state.corked) { + state.corked = 1; + this.uncork(); + } + + // ignore unnecessary end() calls. + if (!state.ending && !state.finished) + endWritable(this, state, cb); +}; + + +function needFinish(stream, state) { + return (state.ending && + state.length === 0 && + !state.finished && + !state.writing); +} + +function prefinish(stream, state) { + if (!state.prefinished) { + state.prefinished = true; + stream.emit('prefinish'); + } +} + +function finishMaybe(stream, state) { + var need = needFinish(stream, state); + if (need) { + if (state.pendingcb === 0) { + prefinish(stream, state); + state.finished = true; + stream.emit('finish'); + } else + prefinish(stream, state); + } + return need; +} + +function endWritable(stream, state, cb) { + state.ending = true; + finishMaybe(stream, state); + if (cb) { + if (state.finished) + process.nextTick(cb); + else + stream.once('finish', cb); + } + state.ended = true; +} + +}).call(this,require('_process')) +},{"./_stream_duplex":53,"_process":51,"buffer":43,"core-util-is":58,"inherits":48,"stream":63}],58:[function(require,module,exports){ +(function (Buffer){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +function isBuffer(arg) { + return Buffer.isBuffer(arg); +} +exports.isBuffer = isBuffer; + +function objectToString(o) { + return Object.prototype.toString.call(o); +} +}).call(this,require("buffer").Buffer) +},{"buffer":43}],59:[function(require,module,exports){ +module.exports = require("./lib/_stream_passthrough.js") + +},{"./lib/_stream_passthrough.js":54}],60:[function(require,module,exports){ +exports = module.exports = require('./lib/_stream_readable.js'); +exports.Stream = require('stream'); +exports.Readable = exports; +exports.Writable = require('./lib/_stream_writable.js'); +exports.Duplex = require('./lib/_stream_duplex.js'); +exports.Transform = require('./lib/_stream_transform.js'); +exports.PassThrough = require('./lib/_stream_passthrough.js'); + +},{"./lib/_stream_duplex.js":53,"./lib/_stream_passthrough.js":54,"./lib/_stream_readable.js":55,"./lib/_stream_transform.js":56,"./lib/_stream_writable.js":57,"stream":63}],61:[function(require,module,exports){ +module.exports = require("./lib/_stream_transform.js") + +},{"./lib/_stream_transform.js":56}],62:[function(require,module,exports){ +module.exports = require("./lib/_stream_writable.js") + +},{"./lib/_stream_writable.js":57}],63:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +module.exports = Stream; + +var EE = require('events').EventEmitter; +var inherits = require('inherits'); + +inherits(Stream, EE); +Stream.Readable = require('readable-stream/readable.js'); +Stream.Writable = require('readable-stream/writable.js'); +Stream.Duplex = require('readable-stream/duplex.js'); +Stream.Transform = require('readable-stream/transform.js'); +Stream.PassThrough = require('readable-stream/passthrough.js'); + +// Backwards-compat with node 0.4.x +Stream.Stream = Stream; + + + +// old-style streams. Note that the pipe method (the only relevant +// part of this class) is overridden in the Readable class. + +function Stream() { + EE.call(this); +} + +Stream.prototype.pipe = function(dest, options) { + var source = this; + + function ondata(chunk) { + if (dest.writable) { + if (false === dest.write(chunk) && source.pause) { + source.pause(); + } + } + } + + source.on('data', ondata); + + function ondrain() { + if (source.readable && source.resume) { + source.resume(); + } + } + + dest.on('drain', ondrain); + + // If the 'end' option is not supplied, dest.end() will be called when + // source gets the 'end' or 'close' events. Only dest.end() once. + if (!dest._isStdio && (!options || options.end !== false)) { + source.on('end', onend); + source.on('close', onclose); + } + + var didOnEnd = false; + function onend() { + if (didOnEnd) return; + didOnEnd = true; + + dest.end(); + } + + + function onclose() { + if (didOnEnd) return; + didOnEnd = true; + + if (typeof dest.destroy === 'function') dest.destroy(); + } + + // don't leave dangling pipes when there are errors. + function onerror(er) { + cleanup(); + if (EE.listenerCount(this, 'error') === 0) { + throw er; // Unhandled stream error in pipe. + } + } + + source.on('error', onerror); + dest.on('error', onerror); + + // remove all the event listeners that were added. + function cleanup() { + source.removeListener('data', ondata); + dest.removeListener('drain', ondrain); + + source.removeListener('end', onend); + source.removeListener('close', onclose); + + source.removeListener('error', onerror); + dest.removeListener('error', onerror); + + source.removeListener('end', cleanup); + source.removeListener('close', cleanup); + + dest.removeListener('close', cleanup); + } + + source.on('end', cleanup); + source.on('close', cleanup); + + dest.on('close', cleanup); + + dest.emit('pipe', source); + + // Allow for unix-like usage: A.pipe(B).pipe(C) + return dest; +}; + +},{"events":47,"inherits":48,"readable-stream/duplex.js":52,"readable-stream/passthrough.js":59,"readable-stream/readable.js":60,"readable-stream/transform.js":61,"readable-stream/writable.js":62}],64:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var Buffer = require('buffer').Buffer; + +var isBufferEncoding = Buffer.isEncoding + || function(encoding) { + switch (encoding && encoding.toLowerCase()) { + case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': case 'raw': return true; + default: return false; + } + } + + +function assertEncoding(encoding) { + if (encoding && !isBufferEncoding(encoding)) { + throw new Error('Unknown encoding: ' + encoding); + } +} + +// StringDecoder provides an interface for efficiently splitting a series of +// buffers into a series of JS strings without breaking apart multi-byte +// characters. CESU-8 is handled as part of the UTF-8 encoding. +// +// @TODO Handling all encodings inside a single object makes it very difficult +// to reason about this code, so it should be split up in the future. +// @TODO There should be a utf8-strict encoding that rejects invalid UTF-8 code +// points as used by CESU-8. +var StringDecoder = exports.StringDecoder = function(encoding) { + this.encoding = (encoding || 'utf8').toLowerCase().replace(/[-_]/, ''); + assertEncoding(encoding); + switch (this.encoding) { + case 'utf8': + // CESU-8 represents each of Surrogate Pair by 3-bytes + this.surrogateSize = 3; + break; + case 'ucs2': + case 'utf16le': + // UTF-16 represents each of Surrogate Pair by 2-bytes + this.surrogateSize = 2; + this.detectIncompleteChar = utf16DetectIncompleteChar; + break; + case 'base64': + // Base-64 stores 3 bytes in 4 chars, and pads the remainder. + this.surrogateSize = 3; + this.detectIncompleteChar = base64DetectIncompleteChar; + break; + default: + this.write = passThroughWrite; + return; + } + + // Enough space to store all bytes of a single character. UTF-8 needs 4 + // bytes, but CESU-8 may require up to 6 (3 bytes per surrogate). + this.charBuffer = new Buffer(6); + // Number of bytes received for the current incomplete multi-byte character. + this.charReceived = 0; + // Number of bytes expected for the current incomplete multi-byte character. + this.charLength = 0; +}; + + +// write decodes the given buffer and returns it as JS string that is +// guaranteed to not contain any partial multi-byte characters. Any partial +// character found at the end of the buffer is buffered up, and will be +// returned when calling write again with the remaining bytes. +// +// Note: Converting a Buffer containing an orphan surrogate to a String +// currently works, but converting a String to a Buffer (via `new Buffer`, or +// Buffer#write) will replace incomplete surrogates with the unicode +// replacement character. See https://codereview.chromium.org/121173009/ . +StringDecoder.prototype.write = function(buffer) { + var charStr = ''; + // if our last write ended with an incomplete multibyte character + while (this.charLength) { + // determine how many remaining bytes this buffer has to offer for this char + var available = (buffer.length >= this.charLength - this.charReceived) ? + this.charLength - this.charReceived : + buffer.length; + + // add the new bytes to the char buffer + buffer.copy(this.charBuffer, this.charReceived, 0, available); + this.charReceived += available; + + if (this.charReceived < this.charLength) { + // still not enough chars in this buffer? wait for more ... + return ''; + } + + // remove bytes belonging to the current character from the buffer + buffer = buffer.slice(available, buffer.length); + + // get the character that was split + charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding); + + // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character + var charCode = charStr.charCodeAt(charStr.length - 1); + if (charCode >= 0xD800 && charCode <= 0xDBFF) { + this.charLength += this.surrogateSize; + charStr = ''; + continue; + } + this.charReceived = this.charLength = 0; + + // if there are no more bytes in this buffer, just emit our char + if (buffer.length === 0) { + return charStr; + } + break; + } + + // determine and set charLength / charReceived + this.detectIncompleteChar(buffer); + + var end = buffer.length; + if (this.charLength) { + // buffer the incomplete character bytes we got + buffer.copy(this.charBuffer, 0, buffer.length - this.charReceived, end); + end -= this.charReceived; + } + + charStr += buffer.toString(this.encoding, 0, end); + + var end = charStr.length - 1; + var charCode = charStr.charCodeAt(end); + // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character + if (charCode >= 0xD800 && charCode <= 0xDBFF) { + var size = this.surrogateSize; + this.charLength += size; + this.charReceived += size; + this.charBuffer.copy(this.charBuffer, size, 0, size); + buffer.copy(this.charBuffer, 0, 0, size); + return charStr.substring(0, end); + } + + // or just emit the charStr + return charStr; +}; + +// detectIncompleteChar determines if there is an incomplete UTF-8 character at +// the end of the given buffer. If so, it sets this.charLength to the byte +// length that character, and sets this.charReceived to the number of bytes +// that are available for this character. +StringDecoder.prototype.detectIncompleteChar = function(buffer) { + // determine how many bytes we have to check at the end of this buffer + var i = (buffer.length >= 3) ? 3 : buffer.length; + + // Figure out if one of the last i bytes of our buffer announces an + // incomplete char. + for (; i > 0; i--) { + var c = buffer[buffer.length - i]; + + // See http://en.wikipedia.org/wiki/UTF-8#Description + + // 110XXXXX + if (i == 1 && c >> 5 == 0x06) { + this.charLength = 2; + break; + } + + // 1110XXXX + if (i <= 2 && c >> 4 == 0x0E) { + this.charLength = 3; + break; + } + + // 11110XXX + if (i <= 3 && c >> 3 == 0x1E) { + this.charLength = 4; + break; + } + } + this.charReceived = i; +}; + +StringDecoder.prototype.end = function(buffer) { + var res = ''; + if (buffer && buffer.length) + res = this.write(buffer); + + if (this.charReceived) { + var cr = this.charReceived; + var buf = this.charBuffer; + var enc = this.encoding; + res += buf.slice(0, cr).toString(enc); + } + + return res; +}; + +function passThroughWrite(buffer) { + return buffer.toString(this.encoding); +} + +function utf16DetectIncompleteChar(buffer) { + this.charReceived = buffer.length % 2; + this.charLength = this.charReceived ? 2 : 0; +} + +function base64DetectIncompleteChar(buffer) { + this.charReceived = buffer.length % 3; + this.charLength = this.charReceived ? 3 : 0; +} + +},{"buffer":43}],65:[function(require,module,exports){ +module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; +} +},{}],66:[function(require,module,exports){ +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; + + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = require('./support/isBuffer'); + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ +exports.inherits = require('inherits'); + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./support/isBuffer":65,"_process":51,"inherits":48}],67:[function(require,module,exports){ +/* See LICENSE file for terms of use */ + +/* + * Text diff implementation. + * + * This library supports the following APIS: + * JsDiff.diffChars: Character by character diff + * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace + * JsDiff.diffLines: Line based diff + * + * JsDiff.diffCss: Diff targeted at CSS content + * + * These methods are based on the implementation proposed in + * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 + */ +(function(global, undefined) { + var objectPrototypeToString = Object.prototype.toString; + + /*istanbul ignore next*/ + function map(arr, mapper, that) { + if (Array.prototype.map) { + return Array.prototype.map.call(arr, mapper, that); + } + + var other = new Array(arr.length); + + for (var i = 0, n = arr.length; i < n; i++) { + other[i] = mapper.call(that, arr[i], i, arr); + } + return other; + } + function clonePath(path) { + return { newPos: path.newPos, components: path.components.slice(0) }; + } + function removeEmpty(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + if (array[i]) { + ret.push(array[i]); + } + } + return ret; + } + function escapeHTML(s) { + var n = s; + n = n.replace(/&/g, '&'); + n = n.replace(//g, '>'); + n = n.replace(/"/g, '"'); + + return n; + } + + // This function handles the presence of circular references by bailing out when encountering an + // object that is already on the "stack" of items being processed. + function canonicalize(obj, stack, replacementStack) { + stack = stack || []; + replacementStack = replacementStack || []; + + var i; + + for (i = 0; i < stack.length; i += 1) { + if (stack[i] === obj) { + return replacementStack[i]; + } + } + + var canonicalizedObj; + + if ('[object Array]' === objectPrototypeToString.call(obj)) { + stack.push(obj); + canonicalizedObj = new Array(obj.length); + replacementStack.push(canonicalizedObj); + for (i = 0; i < obj.length; i += 1) { + canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack); + } + stack.pop(); + replacementStack.pop(); + } else if (typeof obj === 'object' && obj !== null) { + stack.push(obj); + canonicalizedObj = {}; + replacementStack.push(canonicalizedObj); + var sortedKeys = [], + key; + for (key in obj) { + sortedKeys.push(key); + } + sortedKeys.sort(); + for (i = 0; i < sortedKeys.length; i += 1) { + key = sortedKeys[i]; + canonicalizedObj[key] = canonicalize(obj[key], stack, replacementStack); + } + stack.pop(); + replacementStack.pop(); + } else { + canonicalizedObj = obj; + } + return canonicalizedObj; + } + + function buildValues(components, newString, oldString, useLongestToken) { + var componentPos = 0, + componentLen = components.length, + newPos = 0, + oldPos = 0; + + for (; componentPos < componentLen; componentPos++) { + var component = components[componentPos]; + if (!component.removed) { + if (!component.added && useLongestToken) { + var value = newString.slice(newPos, newPos + component.count); + value = map(value, function(value, i) { + var oldValue = oldString[oldPos + i]; + return oldValue.length > value.length ? oldValue : value; + }); + + component.value = value.join(''); + } else { + component.value = newString.slice(newPos, newPos + component.count).join(''); + } + newPos += component.count; + + // Common case + if (!component.added) { + oldPos += component.count; + } + } else { + component.value = oldString.slice(oldPos, oldPos + component.count).join(''); + oldPos += component.count; + + // Reverse add and remove so removes are output first to match common convention + // The diffing algorithm is tied to add then remove output and this is the simplest + // route to get the desired output with minimal overhead. + if (componentPos && components[componentPos - 1].added) { + var tmp = components[componentPos - 1]; + components[componentPos - 1] = components[componentPos]; + components[componentPos] = tmp; + } + } + } + + return components; + } + + function Diff(ignoreWhitespace) { + this.ignoreWhitespace = ignoreWhitespace; + } + Diff.prototype = { + diff: function(oldString, newString, callback) { + var self = this; + + function done(value) { + if (callback) { + setTimeout(function() { callback(undefined, value); }, 0); + return true; + } else { + return value; + } + } + + // Handle the identity case (this is due to unrolling editLength == 0 + if (newString === oldString) { + return done([{ value: newString }]); + } + if (!newString) { + return done([{ value: oldString, removed: true }]); + } + if (!oldString) { + return done([{ value: newString, added: true }]); + } + + newString = this.tokenize(newString); + oldString = this.tokenize(oldString); + + var newLen = newString.length, oldLen = oldString.length; + var editLength = 1; + var maxEditLength = newLen + oldLen; + var bestPath = [{ newPos: -1, components: [] }]; + + // Seed editLength = 0, i.e. the content starts with the same values + var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); + if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) { + // Identity per the equality and tokenizer + return done([{value: newString.join('')}]); + } + + // Main worker method. checks all permutations of a given edit length for acceptance. + function execEditLength() { + for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { + var basePath; + var addPath = bestPath[diagonalPath - 1], + removePath = bestPath[diagonalPath + 1], + oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; + if (addPath) { + // No one else is going to attempt to use this value, clear it + bestPath[diagonalPath - 1] = undefined; + } + + var canAdd = addPath && addPath.newPos + 1 < newLen, + canRemove = removePath && 0 <= oldPos && oldPos < oldLen; + if (!canAdd && !canRemove) { + // If this path is a terminal then prune + bestPath[diagonalPath] = undefined; + continue; + } + + // Select the diagonal that we want to branch from. We select the prior + // path whose position in the new string is the farthest from the origin + // and does not pass the bounds of the diff graph + if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { + basePath = clonePath(removePath); + self.pushComponent(basePath.components, undefined, true); + } else { + basePath = addPath; // No need to clone, we've pulled it from the list + basePath.newPos++; + self.pushComponent(basePath.components, true, undefined); + } + + oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); + + // If we have hit the end of both strings, then we are done + if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) { + return done(buildValues(basePath.components, newString, oldString, self.useLongestToken)); + } else { + // Otherwise track this path as a potential candidate and continue. + bestPath[diagonalPath] = basePath; + } + } + + editLength++; + } + + // Performs the length of edit iteration. Is a bit fugly as this has to support the + // sync and async mode which is never fun. Loops over execEditLength until a value + // is produced. + if (callback) { + (function exec() { + setTimeout(function() { + // This should not happen, but we want to be safe. + /*istanbul ignore next */ + if (editLength > maxEditLength) { + return callback(); + } + + if (!execEditLength()) { + exec(); + } + }, 0); + }()); + } else { + while (editLength <= maxEditLength) { + var ret = execEditLength(); + if (ret) { + return ret; + } + } + } + }, + + pushComponent: function(components, added, removed) { + var last = components[components.length - 1]; + if (last && last.added === added && last.removed === removed) { + // We need to clone here as the component clone operation is just + // as shallow array clone + components[components.length - 1] = {count: last.count + 1, added: added, removed: removed }; + } else { + components.push({count: 1, added: added, removed: removed }); + } + }, + extractCommon: function(basePath, newString, oldString, diagonalPath) { + var newLen = newString.length, + oldLen = oldString.length, + newPos = basePath.newPos, + oldPos = newPos - diagonalPath, + + commonCount = 0; + while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { + newPos++; + oldPos++; + commonCount++; + } + + if (commonCount) { + basePath.components.push({count: commonCount}); + } + + basePath.newPos = newPos; + return oldPos; + }, + + equals: function(left, right) { + var reWhitespace = /\S/; + return left === right || (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)); + }, + tokenize: function(value) { + return value.split(''); + } + }; + + var CharDiff = new Diff(); + + var WordDiff = new Diff(true); + var WordWithSpaceDiff = new Diff(); + WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { + return removeEmpty(value.split(/(\s+|\b)/)); + }; + + var CssDiff = new Diff(true); + CssDiff.tokenize = function(value) { + return removeEmpty(value.split(/([{}:;,]|\s+)/)); + }; + + var LineDiff = new Diff(); + + var TrimmedLineDiff = new Diff(); + TrimmedLineDiff.ignoreTrim = true; + + LineDiff.tokenize = TrimmedLineDiff.tokenize = function(value) { + var retLines = [], + lines = value.split(/^/m); + for (var i = 0; i < lines.length; i++) { + var line = lines[i], + lastLine = lines[i - 1], + lastLineLastChar = lastLine && lastLine[lastLine.length - 1]; + + // Merge lines that may contain windows new lines + if (line === '\n' && lastLineLastChar === '\r') { + retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0, -1) + '\r\n'; + } else { + if (this.ignoreTrim) { + line = line.trim(); + // add a newline unless this is the last line. + if (i < lines.length - 1) { + line += '\n'; + } + } + retLines.push(line); + } + } + + return retLines; + }; + + var PatchDiff = new Diff(); + PatchDiff.tokenize = function(value) { + var ret = [], + linesAndNewlines = value.split(/(\n|\r\n)/); + + // Ignore the final empty token that occurs if the string ends with a new line + if (!linesAndNewlines[linesAndNewlines.length - 1]) { + linesAndNewlines.pop(); + } + + // Merge the content and line separators into single tokens + for (var i = 0; i < linesAndNewlines.length; i++) { + var line = linesAndNewlines[i]; + + if (i % 2) { + ret[ret.length - 1] += line; + } else { + ret.push(line); + } + } + return ret; + }; + + var SentenceDiff = new Diff(); + SentenceDiff.tokenize = function(value) { + return removeEmpty(value.split(/(\S.+?[.!?])(?=\s+|$)/)); + }; + + var JsonDiff = new Diff(); + // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a + // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: + JsonDiff.useLongestToken = true; + JsonDiff.tokenize = LineDiff.tokenize; + JsonDiff.equals = function(left, right) { + return LineDiff.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')); + }; + + var JsDiff = { + Diff: Diff, + + diffChars: function(oldStr, newStr, callback) { return CharDiff.diff(oldStr, newStr, callback); }, + diffWords: function(oldStr, newStr, callback) { return WordDiff.diff(oldStr, newStr, callback); }, + diffWordsWithSpace: function(oldStr, newStr, callback) { return WordWithSpaceDiff.diff(oldStr, newStr, callback); }, + diffLines: function(oldStr, newStr, callback) { return LineDiff.diff(oldStr, newStr, callback); }, + diffTrimmedLines: function(oldStr, newStr, callback) { return TrimmedLineDiff.diff(oldStr, newStr, callback); }, + + diffSentences: function(oldStr, newStr, callback) { return SentenceDiff.diff(oldStr, newStr, callback); }, + + diffCss: function(oldStr, newStr, callback) { return CssDiff.diff(oldStr, newStr, callback); }, + diffJson: function(oldObj, newObj, callback) { + return JsonDiff.diff( + typeof oldObj === 'string' ? oldObj : JSON.stringify(canonicalize(oldObj), undefined, ' '), + typeof newObj === 'string' ? newObj : JSON.stringify(canonicalize(newObj), undefined, ' '), + callback + ); + }, + + createTwoFilesPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader) { + var ret = []; + + if (oldFileName == newFileName) { + ret.push('Index: ' + oldFileName); + } + ret.push('==================================================================='); + ret.push('--- ' + oldFileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); + ret.push('+++ ' + newFileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); + + var diff = PatchDiff.diff(oldStr, newStr); + diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier + + // Formats a given set of lines for printing as context lines in a patch + function contextLines(lines) { + return map(lines, function(entry) { return ' ' + entry; }); + } + + // Outputs the no newline at end of file warning if needed + function eofNL(curRange, i, current) { + var last = diff[diff.length - 2], + isLast = i === diff.length - 2, + isLastOfType = i === diff.length - 3 && current.added !== last.added; + + // Figure out if this is the last line for the given file and missing NL + if (!(/\n$/.test(current.value)) && (isLast || isLastOfType)) { + curRange.push('\\ No newline at end of file'); + } + } + + var oldRangeStart = 0, newRangeStart = 0, curRange = [], + oldLine = 1, newLine = 1; + for (var i = 0; i < diff.length; i++) { + var current = diff[i], + lines = current.lines || current.value.replace(/\n$/, '').split('\n'); + current.lines = lines; + + if (current.added || current.removed) { + // If we have previous context, start with that + if (!oldRangeStart) { + var prev = diff[i - 1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + + if (prev) { + curRange = contextLines(prev.lines.slice(-4)); + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; + } + } + + // Output our changes + curRange.push.apply(curRange, map(lines, function(entry) { + return (current.added ? '+' : '-') + entry; + })); + eofNL(curRange, i, current); + + // Track the updated file position + if (current.added) { + newLine += lines.length; + } else { + oldLine += lines.length; + } + } else { + // Identical context lines. Track line changes + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= 8 && i < diff.length - 2) { + // Overlapping + curRange.push.apply(curRange, contextLines(lines)); + } else { + // end the range and output + var contextSize = Math.min(lines.length, 4); + ret.push( + '@@ -' + oldRangeStart + ',' + (oldLine - oldRangeStart + contextSize) + + ' +' + newRangeStart + ',' + (newLine - newRangeStart + contextSize) + + ' @@'); + ret.push.apply(ret, curRange); + ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); + if (lines.length <= 4) { + eofNL(ret, i, current); + } + + oldRangeStart = 0; + newRangeStart = 0; + curRange = []; + } + } + oldLine += lines.length; + newLine += lines.length; + } + } + + return ret.join('\n') + '\n'; + }, + + createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { + return JsDiff.createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader); + }, + + applyPatch: function(oldStr, uniDiff) { + var diffstr = uniDiff.split('\n'), + hunks = [], + i = 0, + remEOFNL = false, + addEOFNL = false; + + // Skip to the first change hunk + while (i < diffstr.length && !(/^@@/.test(diffstr[i]))) { + i++; + } + + // Parse the unified diff + for (; i < diffstr.length; i++) { + if (diffstr[i][0] === '@') { + var chnukHeader = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); + hunks.unshift({ + start: chnukHeader[3], + oldlength: +chnukHeader[2], + removed: [], + newlength: chnukHeader[4], + added: [] + }); + } else if (diffstr[i][0] === '+') { + hunks[0].added.push(diffstr[i].substr(1)); + } else if (diffstr[i][0] === '-') { + hunks[0].removed.push(diffstr[i].substr(1)); + } else if (diffstr[i][0] === ' ') { + hunks[0].added.push(diffstr[i].substr(1)); + hunks[0].removed.push(diffstr[i].substr(1)); + } else if (diffstr[i][0] === '\\') { + if (diffstr[i - 1][0] === '+') { + remEOFNL = true; + } else if (diffstr[i - 1][0] === '-') { + addEOFNL = true; + } + } + } + + // Apply the diff to the input + var lines = oldStr.split('\n'); + for (i = hunks.length - 1; i >= 0; i--) { + var hunk = hunks[i]; + // Sanity check the input string. Bail if we don't match. + for (var j = 0; j < hunk.oldlength; j++) { + if (lines[hunk.start - 1 + j] !== hunk.removed[j]) { + return false; + } + } + Array.prototype.splice.apply(lines, [hunk.start - 1, hunk.oldlength].concat(hunk.added)); + } + + // Handle EOFNL insertion/removal + if (remEOFNL) { + while (!lines[lines.length - 1]) { + lines.pop(); + } + } else if (addEOFNL) { + lines.push(''); + } + return lines.join('\n'); + }, + + convertChangesToXML: function(changes) { + var ret = []; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + + ret.push(escapeHTML(change.value)); + + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + } + return ret.join(''); + }, + + // See: http://code.google.com/p/google-diff-match-patch/wiki/API + convertChangesToDMP: function(changes) { + var ret = [], + change, + operation; + for (var i = 0; i < changes.length; i++) { + change = changes[i]; + if (change.added) { + operation = 1; + } else if (change.removed) { + operation = -1; + } else { + operation = 0; + } + + ret.push([operation, change.value]); + } + return ret; + }, + + canonicalize: canonicalize + }; + + /*istanbul ignore next */ + /*global module */ + if (typeof module !== 'undefined' && module.exports) { + module.exports = JsDiff; + } else if (typeof define === 'function' && define.amd) { + /*global define */ + define([], function() { return JsDiff; }); + } else if (typeof global.JsDiff === 'undefined') { + global.JsDiff = JsDiff; + } +}(this)); + +},{}],68:[function(require,module,exports){ +'use strict'; + +var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; + +module.exports = function (str) { + if (typeof str !== 'string') { + throw new TypeError('Expected a string'); + } + + return str.replace(matchOperatorsRe, '\\$&'); +}; + +},{}],69:[function(require,module,exports){ +(function (process){ +// Growl - Copyright TJ Holowaychuk (MIT Licensed) + +/** + * Module dependencies. + */ + +var exec = require('child_process').exec + , fs = require('fs') + , path = require('path') + , exists = fs.existsSync || path.existsSync + , os = require('os') + , quote = JSON.stringify + , cmd; + +function which(name) { + var paths = process.env.PATH.split(':'); + var loc; + + for (var i = 0, len = paths.length; i < len; ++i) { + loc = path.join(paths[i], name); + if (exists(loc)) return loc; + } +} + +switch(os.type()) { + case 'Darwin': + if (which('terminal-notifier')) { + cmd = { + type: "Darwin-NotificationCenter" + , pkg: "terminal-notifier" + , msg: '-message' + , title: '-title' + , subtitle: '-subtitle' + , priority: { + cmd: '-execute' + , range: [] + } + }; + } else { + cmd = { + type: "Darwin-Growl" + , pkg: "growlnotify" + , msg: '-m' + , sticky: '--sticky' + , priority: { + cmd: '--priority' + , range: [ + -2 + , -1 + , 0 + , 1 + , 2 + , "Very Low" + , "Moderate" + , "Normal" + , "High" + , "Emergency" + ] + } + }; + } + break; + case 'Linux': + cmd = { + type: "Linux" + , pkg: "notify-send" + , msg: '' + , sticky: '-t 0' + , icon: '-i' + , priority: { + cmd: '-u' + , range: [ + "low" + , "normal" + , "critical" + ] + } + }; + break; + case 'Windows_NT': + cmd = { + type: "Windows" + , pkg: "growlnotify" + , msg: '' + , sticky: '/s:true' + , title: '/t:' + , icon: '/i:' + , priority: { + cmd: '/p:' + , range: [ + -2 + , -1 + , 0 + , 1 + , 2 + ] + } + }; + break; +} + +/** + * Expose `growl`. + */ + +exports = module.exports = growl; + +/** + * Node-growl version. + */ + +exports.version = '1.4.1' + +/** + * Send growl notification _msg_ with _options_. + * + * Options: + * + * - title Notification title + * - sticky Make the notification stick (defaults to false) + * - priority Specify an int or named key (default is 0) + * - name Application name (defaults to growlnotify) + * - image + * - path to an icon sets --iconpath + * - path to an image sets --image + * - capitalized word sets --appIcon + * - filename uses extname as --icon + * - otherwise treated as --icon + * + * Examples: + * + * growl('New email') + * growl('5 new emails', { title: 'Thunderbird' }) + * growl('Email sent', function(){ + * // ... notification sent + * }) + * + * @param {string} msg + * @param {object} options + * @param {function} fn + * @api public + */ + +function growl(msg, options, fn) { + var image + , args + , options = options || {} + , fn = fn || function(){}; + + // noop + if (!cmd) return fn(new Error('growl not supported on this platform')); + args = [cmd.pkg]; + + // image + if (image = options.image) { + switch(cmd.type) { + case 'Darwin-Growl': + var flag, ext = path.extname(image).substr(1) + flag = flag || ext == 'icns' && 'iconpath' + flag = flag || /^[A-Z]/.test(image) && 'appIcon' + flag = flag || /^png|gif|jpe?g$/.test(ext) && 'image' + flag = flag || ext && (image = ext) && 'icon' + flag = flag || 'icon' + args.push('--' + flag, quote(image)) + break; + case 'Linux': + args.push(cmd.icon, quote(image)); + // libnotify defaults to sticky, set a hint for transient notifications + if (!options.sticky) args.push('--hint=int:transient:1'); + break; + case 'Windows': + args.push(cmd.icon + quote(image)); + break; + } + } + + // sticky + if (options.sticky) args.push(cmd.sticky); + + // priority + if (options.priority) { + var priority = options.priority + ''; + var checkindexOf = cmd.priority.range.indexOf(priority); + if (~cmd.priority.range.indexOf(priority)) { + args.push(cmd.priority, options.priority); + } + } + + // name + if (options.name && cmd.type === "Darwin-Growl") { + args.push('--name', options.name); + } + + switch(cmd.type) { + case 'Darwin-Growl': + args.push(cmd.msg); + args.push(quote(msg)); + if (options.title) args.push(quote(options.title)); + break; + case 'Darwin-NotificationCenter': + args.push(cmd.msg); + args.push(quote(msg)); + if (options.title) { + args.push(cmd.title); + args.push(quote(options.title)); + } + if (options.subtitle) { + args.push(cmd.subtitle); + args.push(quote(options.subtitle)); + } + break; + case 'Darwin-Growl': + args.push(cmd.msg); + args.push(quote(msg)); + if (options.title) args.push(quote(options.title)); + break; + case 'Linux': + if (options.title) { + args.push(quote(options.title)); + args.push(cmd.msg); + args.push(quote(msg)); + } else { + args.push(quote(msg)); + } + break; + case 'Windows': + args.push(quote(msg)); + if (options.title) args.push(cmd.title + quote(options.title)); + break; + } + + // execute + exec(args.join(' '), fn); +}; + +}).call(this,require('_process')) +},{"_process":51,"child_process":41,"fs":41,"os":50,"path":41}],70:[function(require,module,exports){ +(function (process,global){ +/** + * Shim process.stdout. + */ + +process.stdout = require('browser-stdout')(); + +var Mocha = require('../'); + +/** + * Create a Mocha instance. + * + * @return {undefined} + */ + +var mocha = new Mocha({ reporter: 'html' }); + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date; +var setTimeout = global.setTimeout; +var setInterval = global.setInterval; +var clearTimeout = global.clearTimeout; +var clearInterval = global.clearInterval; + +var uncaughtExceptionHandlers = []; + +var originalOnerrorHandler = global.onerror; + +/** + * Remove uncaughtException listener. + * Revert to original onerror handler if previously defined. + */ + +process.removeListener = function(e, fn){ + if ('uncaughtException' == e) { + if (originalOnerrorHandler) { + global.onerror = originalOnerrorHandler; + } else { + global.onerror = function() {}; + } + var i = Mocha.utils.indexOf(uncaughtExceptionHandlers, fn); + if (i != -1) { uncaughtExceptionHandlers.splice(i, 1); } + } +}; + +/** + * Implements uncaughtException listener. + */ + +process.on = function(e, fn){ + if ('uncaughtException' == e) { + global.onerror = function(err, url, line){ + fn(new Error(err + ' (' + url + ':' + line + ')')); + return !mocha.allowUncaught; + }; + uncaughtExceptionHandlers.push(fn); + } +}; + +// The BDD UI is registered by default, but no UI will be functional in the +// browser without an explicit call to the overridden `mocha.ui` (see below). +// Ensure that this default UI does not expose its methods to the global scope. +mocha.suite.removeAllListeners('pre-require'); + +var immediateQueue = [] + , immediateTimeout; + +function timeslice() { + var immediateStart = new Date().getTime(); + while (immediateQueue.length && (new Date().getTime() - immediateStart) < 100) { + immediateQueue.shift()(); + } + if (immediateQueue.length) { + immediateTimeout = setTimeout(timeslice, 0); + } else { + immediateTimeout = null; + } +} + +/** + * High-performance override of Runner.immediately. + */ + +Mocha.Runner.immediately = function(callback) { + immediateQueue.push(callback); + if (!immediateTimeout) { + immediateTimeout = setTimeout(timeslice, 0); + } +}; + +/** + * Function to allow assertion libraries to throw errors directly into mocha. + * This is useful when running tests in a browser because window.onerror will + * only receive the 'message' attribute of the Error. + */ +mocha.throwError = function(err) { + Mocha.utils.forEach(uncaughtExceptionHandlers, function (fn) { + fn(err); + }); + throw err; +}; + +/** + * Override ui to ensure that the ui functions are initialized. + * Normally this would happen in Mocha.prototype.loadFiles. + */ + +mocha.ui = function(ui){ + Mocha.prototype.ui.call(this, ui); + this.suite.emit('pre-require', global, null, this); + return this; +}; + +/** + * Setup mocha with the given setting options. + */ + +mocha.setup = function(opts){ + if ('string' == typeof opts) opts = { ui: opts }; + for (var opt in opts) this[opt](opts[opt]); + return this; +}; + +/** + * Run mocha, returning the Runner. + */ + +mocha.run = function(fn){ + var options = mocha.options; + mocha.globals('location'); + + var query = Mocha.utils.parseQuery(global.location.search || ''); + if (query.grep) mocha.grep(new RegExp(query.grep)); + if (query.fgrep) mocha.grep(query.fgrep); + if (query.invert) mocha.invert(); + + return Mocha.prototype.run.call(mocha, function(err){ + // The DOM Document is not available in Web Workers. + var document = global.document; + if (document && document.getElementById('mocha') && options.noHighlighting !== true) { + Mocha.utils.highlightTags('code'); + } + if (fn) fn(err); + }); +}; + +/** + * Expose the process shim. + * https://github.com/mochajs/mocha/pull/916 + */ + +Mocha.process = process; + +/** + * Expose mocha. + */ + +window.Mocha = Mocha; +window.mocha = mocha; + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../":1,"_process":51,"browser-stdout":40}]},{},[70]); diff --git a/node_modules/mocha/node_modules/.bin/jade b/node_modules/mocha/node_modules/.bin/jade new file mode 120000 index 0000000..571fae7 --- /dev/null +++ b/node_modules/mocha/node_modules/.bin/jade @@ -0,0 +1 @@ +../jade/bin/jade \ No newline at end of file diff --git a/node_modules/mocha/node_modules/.bin/mkdirp b/node_modules/mocha/node_modules/.bin/mkdirp new file mode 120000 index 0000000..017896c --- /dev/null +++ b/node_modules/mocha/node_modules/.bin/mkdirp @@ -0,0 +1 @@ +../mkdirp/bin/cmd.js \ No newline at end of file diff --git a/node_modules/mocha/node_modules/.bin/supports-color b/node_modules/mocha/node_modules/.bin/supports-color new file mode 120000 index 0000000..af0f05e --- /dev/null +++ b/node_modules/mocha/node_modules/.bin/supports-color @@ -0,0 +1 @@ +../supports-color/cli.js \ No newline at end of file diff --git a/node_modules/mocha/node_modules/commander/Readme.md b/node_modules/mocha/node_modules/commander/Readme.md new file mode 100644 index 0000000..7bb60b2 --- /dev/null +++ b/node_modules/mocha/node_modules/commander/Readme.md @@ -0,0 +1,208 @@ +# Commander.js + + The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander). + + [![Build Status](https://api.travis-ci.org/visionmedia/commander.js.svg)](http://travis-ci.org/visionmedia/commander.js) + +## Installation + + $ npm install commander + +## Option parsing + + Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander'); + +program + .version('0.0.1') + .option('-p, --peppers', 'Add peppers') + .option('-P, --pineapple', 'Add pineapple') + .option('-b, --bbq', 'Add bbq sauce') + .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble') + .parse(process.argv); + +console.log('you ordered a pizza with:'); +if (program.peppers) console.log(' - peppers'); +if (program.pineapple) console.log(' - pineapple'); +if (program.bbq) console.log(' - bbq'); +console.log(' - %s cheese', program.cheese); +``` + + Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. + +## Automated --help + + The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free: + +``` + $ ./examples/pizza --help + + Usage: pizza [options] + + Options: + + -V, --version output the version number + -p, --peppers Add peppers + -P, --pineapple Add pineapple + -b, --bbq Add bbq sauce + -c, --cheese Add the specified type of cheese [marble] + -h, --help output usage information + +``` + +## Coercion + +```js +function range(val) { + return val.split('..').map(Number); +} + +function list(val) { + return val.split(','); +} + +function collect(val, memo) { + memo.push(val); + return memo; +} + +function increaseVerbosity(v, total) { + return total + 1; +} + +program + .version('0.0.1') + .usage('[options] ') + .option('-i, --integer ', 'An integer argument', parseInt) + .option('-f, --float ', 'A float argument', parseFloat) + .option('-r, --range ..', 'A range', range) + .option('-l, --list ', 'A list', list) + .option('-o, --optional [value]', 'An optional value') + .option('-c, --collect [value]', 'A repeatable value', collect, []) + .option('-v, --verbose', 'A value that can be increased', increaseVerbosity, 0) + .parse(process.argv); + +console.log(' int: %j', program.integer); +console.log(' float: %j', program.float); +console.log(' optional: %j', program.optional); +program.range = program.range || []; +console.log(' range: %j..%j', program.range[0], program.range[1]); +console.log(' list: %j', program.list); +console.log(' collect: %j', program.collect); +console.log(' verbosity: %j', program.verbose); +console.log(' args: %j', program.args); +``` + +## Custom help + + You can display arbitrary `-h, --help` information + by listening for "--help". Commander will automatically + exit once you are done so that the remainder of your program + does not execute causing undesired behaviours, for example + in the following executable "stuff" will not output when + `--help` is used. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('../'); + +function list(val) { + return val.split(',').map(Number); +} + +program + .version('0.0.1') + .option('-f, --foo', 'enable some foo') + .option('-b, --bar', 'enable some bar') + .option('-B, --baz', 'enable some baz'); + +// must be before .parse() since +// node's emit() is immediate + +program.on('--help', function(){ + console.log(' Examples:'); + console.log(''); + console.log(' $ custom-help --help'); + console.log(' $ custom-help -h'); + console.log(''); +}); + +program.parse(process.argv); + +console.log('stuff'); +``` + +yielding the following help output: + +``` + +Usage: custom-help [options] + +Options: + + -h, --help output usage information + -V, --version output the version number + -f, --foo enable some foo + -b, --bar enable some bar + -B, --baz enable some baz + +Examples: + + $ custom-help --help + $ custom-help -h + +``` + +## .outputHelp() + + Output help information without exiting. + +## .help() + + Output help information and exit immediately. + +## Links + + - [API documentation](http://visionmedia.github.com/commander.js/) + - [ascii tables](https://github.com/LearnBoost/cli-table) + - [progress bars](https://github.com/visionmedia/node-progress) + - [more progress bars](https://github.com/substack/node-multimeter) + - [examples](https://github.com/visionmedia/commander.js/tree/master/examples) + +## License + +(The MIT License) + +Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/mocha/node_modules/commander/index.js b/node_modules/mocha/node_modules/commander/index.js new file mode 100644 index 0000000..8378d19 --- /dev/null +++ b/node_modules/mocha/node_modules/commander/index.js @@ -0,0 +1,876 @@ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter; +var spawn = require('child_process').spawn; +var path = require('path'); +var dirname = path.dirname; +var basename = path.basename; + +/** + * Expose the root command. + */ + +exports = module.exports = new Command; + +/** + * Expose `Command`. + */ + +exports.Command = Command; + +/** + * Expose `Option`. + */ + +exports.Option = Option; + +/** + * Initialize a new `Option` with the given `flags` and `description`. + * + * @param {String} flags + * @param {String} description + * @api public + */ + +function Option(flags, description) { + this.flags = flags; + this.required = ~flags.indexOf('<'); + this.optional = ~flags.indexOf('['); + this.bool = !~flags.indexOf('-no-'); + flags = flags.split(/[ ,|]+/); + if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); + this.long = flags.shift(); + this.description = description || ''; +} + +/** + * Return option name. + * + * @return {String} + * @api private + */ + +Option.prototype.name = function(){ + return this.long + .replace('--', '') + .replace('no-', ''); +}; + +/** + * Check if `arg` matches the short or long flag. + * + * @param {String} arg + * @return {Boolean} + * @api private + */ + +Option.prototype.is = function(arg){ + return arg == this.short + || arg == this.long; +}; + +/** + * Initialize a new `Command`. + * + * @param {String} name + * @api public + */ + +function Command(name) { + this.commands = []; + this.options = []; + this._execs = []; + this._args = []; + this._name = name; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Command.prototype.__proto__ = EventEmitter.prototype; + +/** + * Add command `name`. + * + * The `.action()` callback is invoked when the + * command `name` is specified via __ARGV__, + * and the remaining arguments are applied to the + * function for access. + * + * When the `name` is "*" an un-matched command + * will be passed as the first arg, followed by + * the rest of __ARGV__ remaining. + * + * Examples: + * + * program + * .version('0.0.1') + * .option('-C, --chdir ', 'change the working directory') + * .option('-c, --config ', 'set config path. defaults to ./deploy.conf') + * .option('-T, --no-tests', 'ignore test hook') + * + * program + * .command('setup') + * .description('run remote setup commands') + * .action(function(){ + * console.log('setup'); + * }); + * + * program + * .command('exec ') + * .description('run the given remote command') + * .action(function(cmd){ + * console.log('exec "%s"', cmd); + * }); + * + * program + * .command('*') + * .description('deploy the given env') + * .action(function(env){ + * console.log('deploying "%s"', env); + * }); + * + * program.parse(process.argv); + * + * @param {String} name + * @param {String} [desc] + * @return {Command} the new command + * @api public + */ + +Command.prototype.command = function(name, desc) { + var args = name.split(/ +/); + var cmd = new Command(args.shift()); + if (desc) cmd.description(desc); + if (desc) this.executables = true; + if (desc) this._execs[cmd._name] = true; + this.commands.push(cmd); + cmd.parseExpectedArgs(args); + cmd.parent = this; + if (desc) return this; + return cmd; +}; + +/** + * Add an implicit `help [cmd]` subcommand + * which invokes `--help` for the given command. + * + * @api private + */ + +Command.prototype.addImplicitHelpCommand = function() { + this.command('help [cmd]', 'display help for [cmd]'); +}; + +/** + * Parse expected `args`. + * + * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`. + * + * @param {Array} args + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parseExpectedArgs = function(args){ + if (!args.length) return; + var self = this; + args.forEach(function(arg){ + switch (arg[0]) { + case '<': + self._args.push({ required: true, name: arg.slice(1, -1) }); + break; + case '[': + self._args.push({ required: false, name: arg.slice(1, -1) }); + break; + } + }); + return this; +}; + +/** + * Register callback `fn` for the command. + * + * Examples: + * + * program + * .command('help') + * .description('display verbose help') + * .action(function(){ + * // output help here + * }); + * + * @param {Function} fn + * @return {Command} for chaining + * @api public + */ + +Command.prototype.action = function(fn){ + var self = this; + var listener = function(args, unknown){ + // Parse any so-far unknown options + args = args || []; + unknown = unknown || []; + + var parsed = self.parseOptions(unknown); + + // Output help if necessary + outputHelpIfNecessary(self, parsed.unknown); + + // If there are still any unknown options, then we simply + // die, unless someone asked for help, in which case we give it + // to them, and then we die. + if (parsed.unknown.length > 0) { + self.unknownOption(parsed.unknown[0]); + } + + // Leftover arguments need to be pushed back. Fixes issue #56 + if (parsed.args.length) args = parsed.args.concat(args); + + self._args.forEach(function(arg, i){ + if (arg.required && null == args[i]) { + self.missingArgument(arg.name); + } + }); + + // Always append ourselves to the end of the arguments, + // to make sure we match the number of arguments the user + // expects + if (self._args.length) { + args[self._args.length] = self; + } else { + args.push(self); + } + + fn.apply(this, args); + }; + this.parent.on(this._name, listener); + if (this._alias) this.parent.on(this._alias, listener); + return this; +}; + +/** + * Define option with `flags`, `description` and optional + * coercion `fn`. + * + * The `flags` string should contain both the short and long flags, + * separated by comma, a pipe or space. The following are all valid + * all will output this way when `--help` is used. + * + * "-p, --pepper" + * "-p|--pepper" + * "-p --pepper" + * + * Examples: + * + * // simple boolean defaulting to false + * program.option('-p, --pepper', 'add pepper'); + * + * --pepper + * program.pepper + * // => Boolean + * + * // simple boolean defaulting to true + * program.option('-C, --no-cheese', 'remove cheese'); + * + * program.cheese + * // => true + * + * --no-cheese + * program.cheese + * // => false + * + * // required argument + * program.option('-C, --chdir ', 'change the working directory'); + * + * --chdir /tmp + * program.chdir + * // => "/tmp" + * + * // optional argument + * program.option('-c, --cheese [type]', 'add cheese [marble]'); + * + * @param {String} flags + * @param {String} description + * @param {Function|Mixed} fn or default + * @param {Mixed} defaultValue + * @return {Command} for chaining + * @api public + */ + +Command.prototype.option = function(flags, description, fn, defaultValue){ + var self = this + , option = new Option(flags, description) + , oname = option.name() + , name = camelcase(oname); + + // default as 3rd arg + if ('function' != typeof fn) defaultValue = fn, fn = null; + + // preassign default value only for --no-*, [optional], or + if (false == option.bool || option.optional || option.required) { + // when --no-* we make sure default is true + if (false == option.bool) defaultValue = true; + // preassign only if we have a default + if (undefined !== defaultValue) self[name] = defaultValue; + } + + // register the option + this.options.push(option); + + // when it's passed assign the value + // and conditionally invoke the callback + this.on(oname, function(val){ + // coercion + if (null !== val && fn) val = fn(val, undefined === self[name] ? defaultValue : self[name]); + + // unassigned or bool + if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) { + // if no value, bool true, and we have a default, then use it! + if (null == val) { + self[name] = option.bool + ? defaultValue || true + : false; + } else { + self[name] = val; + } + } else if (null !== val) { + // reassign + self[name] = val; + } + }); + + return this; +}; + +/** + * Parse `argv`, settings options and invoking commands when defined. + * + * @param {Array} argv + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parse = function(argv){ + // implicit help + if (this.executables) this.addImplicitHelpCommand(); + + // store raw args + this.rawArgs = argv; + + // guess name + this._name = this._name || basename(argv[1], '.js'); + + // process argv + var parsed = this.parseOptions(this.normalize(argv.slice(2))); + var args = this.args = parsed.args; + + var result = this.parseArgs(this.args, parsed.unknown); + + // executable sub-commands + var name = result.args[0]; + if (this._execs[name]) return this.executeSubCommand(argv, args, parsed.unknown); + + return result; +}; + +/** + * Execute a sub-command executable. + * + * @param {Array} argv + * @param {Array} args + * @param {Array} unknown + * @api private + */ + +Command.prototype.executeSubCommand = function(argv, args, unknown) { + args = args.concat(unknown); + + if (!args.length) this.help(); + if ('help' == args[0] && 1 == args.length) this.help(); + + // --help + if ('help' == args[0]) { + args[0] = args[1]; + args[1] = '--help'; + } + + // executable + var dir = dirname(argv[1]); + var bin = basename(argv[1], '.js') + '-' + args[0]; + + // check for ./ first + var local = path.join(dir, bin); + + // run it + args = args.slice(1); + args.unshift(local); + var proc = spawn('node', args, { stdio: 'inherit', customFds: [0, 1, 2] }); + proc.on('error', function(err){ + if (err.code == "ENOENT") { + console.error('\n %s(1) does not exist, try --help\n', bin); + } else if (err.code == "EACCES") { + console.error('\n %s(1) not executable. try chmod or run with root\n', bin); + } + }); + + this.runningCommand = proc; +}; + +/** + * Normalize `args`, splitting joined short flags. For example + * the arg "-abc" is equivalent to "-a -b -c". + * This also normalizes equal sign and splits "--abc=def" into "--abc def". + * + * @param {Array} args + * @return {Array} + * @api private + */ + +Command.prototype.normalize = function(args){ + var ret = [] + , arg + , lastOpt + , index; + + for (var i = 0, len = args.length; i < len; ++i) { + arg = args[i]; + i > 0 && (lastOpt = this.optionFor(args[i-1])); + + if (lastOpt && lastOpt.required) { + ret.push(arg); + } else if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) { + arg.slice(1).split('').forEach(function(c){ + ret.push('-' + c); + }); + } else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) { + ret.push(arg.slice(0, index), arg.slice(index + 1)); + } else { + ret.push(arg); + } + } + + return ret; +}; + +/** + * Parse command `args`. + * + * When listener(s) are available those + * callbacks are invoked, otherwise the "*" + * event is emitted and those actions are invoked. + * + * @param {Array} args + * @return {Command} for chaining + * @api private + */ + +Command.prototype.parseArgs = function(args, unknown){ + var cmds = this.commands + , len = cmds.length + , name; + + if (args.length) { + name = args[0]; + if (this.listeners(name).length) { + this.emit(args.shift(), args, unknown); + } else { + this.emit('*', args); + } + } else { + outputHelpIfNecessary(this, unknown); + + // If there were no args and we have unknown options, + // then they are extraneous and we need to error. + if (unknown.length > 0) { + this.unknownOption(unknown[0]); + } + } + + return this; +}; + +/** + * Return an option matching `arg` if any. + * + * @param {String} arg + * @return {Option} + * @api private + */ + +Command.prototype.optionFor = function(arg){ + for (var i = 0, len = this.options.length; i < len; ++i) { + if (this.options[i].is(arg)) { + return this.options[i]; + } + } +}; + +/** + * Parse options from `argv` returning `argv` + * void of these options. + * + * @param {Array} argv + * @return {Array} + * @api public + */ + +Command.prototype.parseOptions = function(argv){ + var args = [] + , len = argv.length + , literal + , option + , arg; + + var unknownOptions = []; + + // parse options + for (var i = 0; i < len; ++i) { + arg = argv[i]; + + // literal args after -- + if ('--' == arg) { + literal = true; + continue; + } + + if (literal) { + args.push(arg); + continue; + } + + // find matching Option + option = this.optionFor(arg); + + // option is defined + if (option) { + // requires arg + if (option.required) { + arg = argv[++i]; + if (null == arg) return this.optionMissingArgument(option); + this.emit(option.name(), arg); + // optional arg + } else if (option.optional) { + arg = argv[i+1]; + if (null == arg || ('-' == arg[0] && '-' != arg)) { + arg = null; + } else { + ++i; + } + this.emit(option.name(), arg); + // bool + } else { + this.emit(option.name()); + } + continue; + } + + // looks like an option + if (arg.length > 1 && '-' == arg[0]) { + unknownOptions.push(arg); + + // If the next argument looks like it might be + // an argument for this option, we pass it on. + // If it isn't, then it'll simply be ignored + if (argv[i+1] && '-' != argv[i+1][0]) { + unknownOptions.push(argv[++i]); + } + continue; + } + + // arg + args.push(arg); + } + + return { args: args, unknown: unknownOptions }; +}; + +/** + * Argument `name` is missing. + * + * @param {String} name + * @api private + */ + +Command.prototype.missingArgument = function(name){ + console.error(); + console.error(" error: missing required argument `%s'", name); + console.error(); + process.exit(1); +}; + +/** + * `Option` is missing an argument, but received `flag` or nothing. + * + * @param {String} option + * @param {String} flag + * @api private + */ + +Command.prototype.optionMissingArgument = function(option, flag){ + console.error(); + if (flag) { + console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag); + } else { + console.error(" error: option `%s' argument missing", option.flags); + } + console.error(); + process.exit(1); +}; + +/** + * Unknown option `flag`. + * + * @param {String} flag + * @api private + */ + +Command.prototype.unknownOption = function(flag){ + console.error(); + console.error(" error: unknown option `%s'", flag); + console.error(); + process.exit(1); +}; + + +/** + * Set the program version to `str`. + * + * This method auto-registers the "-V, --version" flag + * which will print the version number when passed. + * + * @param {String} str + * @param {String} flags + * @return {Command} for chaining + * @api public + */ + +Command.prototype.version = function(str, flags){ + if (0 == arguments.length) return this._version; + this._version = str; + flags = flags || '-V, --version'; + this.option(flags, 'output the version number'); + this.on('version', function(){ + console.log(str); + process.exit(0); + }); + return this; +}; + +/** + * Set the description `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.description = function(str){ + if (0 == arguments.length) return this._description; + this._description = str; + return this; +}; + +/** + * Set an alias for the command + * + * @param {String} alias + * @return {String|Command} + * @api public + */ + +Command.prototype.alias = function(alias){ + if (0 == arguments.length) return this._alias; + this._alias = alias; + return this; +}; + +/** + * Set / get the command usage `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.usage = function(str){ + var args = this._args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }); + + var usage = '[options' + + (this.commands.length ? '] [command' : '') + + ']' + + (this._args.length ? ' ' + args : ''); + + if (0 == arguments.length) return this._usage || usage; + this._usage = str; + + return this; +}; + +/** + * Return the largest option length. + * + * @return {Number} + * @api private + */ + +Command.prototype.largestOptionLength = function(){ + return this.options.reduce(function(max, option){ + return Math.max(max, option.flags.length); + }, 0); +}; + +/** + * Return help for options. + * + * @return {String} + * @api private + */ + +Command.prototype.optionHelp = function(){ + var width = this.largestOptionLength(); + + // Prepend the help information + return [pad('-h, --help', width) + ' ' + 'output usage information'] + .concat(this.options.map(function(option){ + return pad(option.flags, width) + + ' ' + option.description; + })) + .join('\n'); +}; + +/** + * Return command help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.commandHelp = function(){ + if (!this.commands.length) return ''; + return [ + '' + , ' Commands:' + , '' + , this.commands.map(function(cmd){ + var args = cmd._args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }).join(' '); + + return cmd._name + + (cmd._alias + ? '|' + cmd._alias + : '') + + (cmd.options.length + ? ' [options]' + : '') + ' ' + args + + (cmd.description() + ? '\n ' + cmd.description() + : '') + + '\n'; + }).join('\n').replace(/^/gm, ' ') + , '' + ].join('\n'); +}; + +/** + * Return program help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.helpInformation = function(){ + return [ + '' + , ' Usage: ' + this._name + + (this._alias + ? '|' + this._alias + : '') + + ' ' + this.usage() + , '' + this.commandHelp() + , ' Options:' + , '' + , '' + this.optionHelp().replace(/^/gm, ' ') + , '' + , '' + ].join('\n'); +}; + +/** + * Output help information for this command + * + * @api public + */ + +Command.prototype.outputHelp = function(){ + process.stdout.write(this.helpInformation()); + this.emit('--help'); +}; + +/** + * Output help information and exit. + * + * @api public + */ + +Command.prototype.help = function(){ + this.outputHelp(); + process.exit(); +}; + +/** + * Camel-case the given `flag` + * + * @param {String} flag + * @return {String} + * @api private + */ + +function camelcase(flag) { + return flag.split('-').reduce(function(str, word){ + return str + word[0].toUpperCase() + word.slice(1); + }); +} + +/** + * Pad `str` to `width`. + * + * @param {String} str + * @param {Number} width + * @return {String} + * @api private + */ + +function pad(str, width) { + var len = Math.max(0, width - str.length); + return str + Array(len + 1).join(' '); +} + +/** + * Output help information if necessary + * + * @param {Command} command to output help for + * @param {Array} array of options to search for -h or --help + * @api private + */ + +function outputHelpIfNecessary(cmd, options) { + options = options || []; + for (var i = 0; i < options.length; i++) { + if (options[i] == '--help' || options[i] == '-h') { + cmd.outputHelp(); + process.exit(0); + } + } +} diff --git a/node_modules/mocha/node_modules/commander/package.json b/node_modules/mocha/node_modules/commander/package.json new file mode 100644 index 0000000..f2b43dd --- /dev/null +++ b/node_modules/mocha/node_modules/commander/package.json @@ -0,0 +1,70 @@ +{ + "name": "commander", + "version": "2.3.0", + "description": "the complete solution for node.js command-line programs", + "keywords": [ + "command", + "option", + "parser", + "prompt", + "stdin" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "repository": { + "type": "git", + "url": "https://github.com/visionmedia/commander.js.git" + }, + "devDependencies": { + "should": ">= 0.0.1" + }, + "scripts": { + "test": "make test" + }, + "main": "index", + "engines": { + "node": ">= 0.6.x" + }, + "files": [ + "index.js" + ], + "gitHead": "7e9f407ec03d4371a478c2fe417db4998ecb6169", + "bugs": { + "url": "https://github.com/visionmedia/commander.js/issues" + }, + "homepage": "https://github.com/visionmedia/commander.js", + "_id": "commander@2.3.0", + "_shasum": "fd430e889832ec353b9acd1de217c11cb3eef873", + "_from": "commander@2.3.0", + "_npmVersion": "1.4.21", + "_npmUser": { + "name": "somekittens", + "email": "rkoutnik@gmail.com" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "somekittens", + "email": "rkoutnik@gmail.com" + }, + { + "name": "zhiyelee", + "email": "zhiyelee@gmail.com" + }, + { + "name": "thethomaseffect", + "email": "thethomaseffect@gmail.com" + } + ], + "dist": { + "shasum": "fd430e889832ec353b9acd1de217c11cb3eef873", + "tarball": "http://registry.npmjs.org/commander/-/commander-2.3.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz" +} diff --git a/node_modules/mocha/node_modules/debug/.jshintrc b/node_modules/mocha/node_modules/debug/.jshintrc new file mode 100644 index 0000000..299877f --- /dev/null +++ b/node_modules/mocha/node_modules/debug/.jshintrc @@ -0,0 +1,3 @@ +{ + "laxbreak": true +} diff --git a/node_modules/mocha/node_modules/debug/.npmignore b/node_modules/mocha/node_modules/debug/.npmignore new file mode 100644 index 0000000..7e6163d --- /dev/null +++ b/node_modules/mocha/node_modules/debug/.npmignore @@ -0,0 +1,6 @@ +support +test +examples +example +*.sock +dist diff --git a/node_modules/mocha/node_modules/debug/History.md b/node_modules/mocha/node_modules/debug/History.md new file mode 100644 index 0000000..854c971 --- /dev/null +++ b/node_modules/mocha/node_modules/debug/History.md @@ -0,0 +1,195 @@ + +2.2.0 / 2015-05-09 +================== + + * package: update "ms" to v0.7.1 (#202, @dougwilson) + * README: add logging to file example (#193, @DanielOchoa) + * README: fixed a typo (#191, @amir-s) + * browser: expose `storage` (#190, @stephenmathieson) + * Makefile: add a `distclean` target (#189, @stephenmathieson) + +2.1.3 / 2015-03-13 +================== + + * Updated stdout/stderr example (#186) + * Updated example/stdout.js to match debug current behaviour + * Renamed example/stderr.js to stdout.js + * Update Readme.md (#184) + * replace high intensity foreground color for bold (#182, #183) + +2.1.2 / 2015-03-01 +================== + + * dist: recompile + * update "ms" to v0.7.0 + * package: update "browserify" to v9.0.3 + * component: fix "ms.js" repo location + * changed bower package name + * updated documentation about using debug in a browser + * fix: security error on safari (#167, #168, @yields) + +2.1.1 / 2014-12-29 +================== + + * browser: use `typeof` to check for `console` existence + * browser: check for `console.log` truthiness (fix IE 8/9) + * browser: add support for Chrome apps + * Readme: added Windows usage remarks + * Add `bower.json` to properly support bower install + +2.1.0 / 2014-10-15 +================== + + * node: implement `DEBUG_FD` env variable support + * package: update "browserify" to v6.1.0 + * package: add "license" field to package.json (#135, @panuhorsmalahti) + +2.0.0 / 2014-09-01 +================== + + * package: update "browserify" to v5.11.0 + * node: use stderr rather than stdout for logging (#29, @stephenmathieson) + +1.0.4 / 2014-07-15 +================== + + * dist: recompile + * example: remove `console.info()` log usage + * example: add "Content-Type" UTF-8 header to browser example + * browser: place %c marker after the space character + * browser: reset the "content" color via `color: inherit` + * browser: add colors support for Firefox >= v31 + * debug: prefer an instance `log()` function over the global one (#119) + * Readme: update documentation about styled console logs for FF v31 (#116, @wryk) + +1.0.3 / 2014-07-09 +================== + + * Add support for multiple wildcards in namespaces (#122, @seegno) + * browser: fix lint + +1.0.2 / 2014-06-10 +================== + + * browser: update color palette (#113, @gscottolson) + * common: make console logging function configurable (#108, @timoxley) + * node: fix %o colors on old node <= 0.8.x + * Makefile: find node path using shell/which (#109, @timoxley) + +1.0.1 / 2014-06-06 +================== + + * browser: use `removeItem()` to clear localStorage + * browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777) + * package: add "contributors" section + * node: fix comment typo + * README: list authors + +1.0.0 / 2014-06-04 +================== + + * make ms diff be global, not be scope + * debug: ignore empty strings in enable() + * node: make DEBUG_COLORS able to disable coloring + * *: export the `colors` array + * npmignore: don't publish the `dist` dir + * Makefile: refactor to use browserify + * package: add "browserify" as a dev dependency + * Readme: add Web Inspector Colors section + * node: reset terminal color for the debug content + * node: map "%o" to `util.inspect()` + * browser: map "%j" to `JSON.stringify()` + * debug: add custom "formatters" + * debug: use "ms" module for humanizing the diff + * Readme: add "bash" syntax highlighting + * browser: add Firebug color support + * browser: add colors for WebKit browsers + * node: apply log to `console` + * rewrite: abstract common logic for Node & browsers + * add .jshintrc file + +0.8.1 / 2014-04-14 +================== + + * package: re-add the "component" section + +0.8.0 / 2014-03-30 +================== + + * add `enable()` method for nodejs. Closes #27 + * change from stderr to stdout + * remove unnecessary index.js file + +0.7.4 / 2013-11-13 +================== + + * remove "browserify" key from package.json (fixes something in browserify) + +0.7.3 / 2013-10-30 +================== + + * fix: catch localStorage security error when cookies are blocked (Chrome) + * add debug(err) support. Closes #46 + * add .browser prop to package.json. Closes #42 + +0.7.2 / 2013-02-06 +================== + + * fix package.json + * fix: Mobile Safari (private mode) is broken with debug + * fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript + +0.7.1 / 2013-02-05 +================== + + * add repository URL to package.json + * add DEBUG_COLORED to force colored output + * add browserify support + * fix component. Closes #24 + +0.7.0 / 2012-05-04 +================== + + * Added .component to package.json + * Added debug.component.js build + +0.6.0 / 2012-03-16 +================== + + * Added support for "-" prefix in DEBUG [Vinay Pulim] + * Added `.enabled` flag to the node version [TooTallNate] + +0.5.0 / 2012-02-02 +================== + + * Added: humanize diffs. Closes #8 + * Added `debug.disable()` to the CS variant + * Removed padding. Closes #10 + * Fixed: persist client-side variant again. Closes #9 + +0.4.0 / 2012-02-01 +================== + + * Added browser variant support for older browsers [TooTallNate] + * Added `debug.enable('project:*')` to browser variant [TooTallNate] + * Added padding to diff (moved it to the right) + +0.3.0 / 2012-01-26 +================== + + * Added millisecond diff when isatty, otherwise UTC string + +0.2.0 / 2012-01-22 +================== + + * Added wildcard support + +0.1.0 / 2011-12-02 +================== + + * Added: remove colors unless stderr isatty [TooTallNate] + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/node_modules/mocha/node_modules/debug/Makefile b/node_modules/mocha/node_modules/debug/Makefile new file mode 100644 index 0000000..5cf4a59 --- /dev/null +++ b/node_modules/mocha/node_modules/debug/Makefile @@ -0,0 +1,36 @@ + +# get Makefile directory name: http://stackoverflow.com/a/5982798/376773 +THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd) + +# BIN directory +BIN := $(THIS_DIR)/node_modules/.bin + +# applications +NODE ?= $(shell which node) +NPM ?= $(NODE) $(shell which npm) +BROWSERIFY ?= $(NODE) $(BIN)/browserify + +all: dist/debug.js + +install: node_modules + +clean: + @rm -rf dist + +dist: + @mkdir -p $@ + +dist/debug.js: node_modules browser.js debug.js dist + @$(BROWSERIFY) \ + --standalone debug \ + . > $@ + +distclean: clean + @rm -rf node_modules + +node_modules: package.json + @NODE_ENV= $(NPM) install + @touch node_modules + +.PHONY: all install clean distclean diff --git a/node_modules/mocha/node_modules/debug/Readme.md b/node_modules/mocha/node_modules/debug/Readme.md new file mode 100644 index 0000000..b4f45e3 --- /dev/null +++ b/node_modules/mocha/node_modules/debug/Readme.md @@ -0,0 +1,188 @@ +# debug + + tiny node.js debugging utility modelled after node core's debugging technique. + +## Installation + +```bash +$ npm install debug +``` + +## Usage + + With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility. + +Example _app.js_: + +```js +var debug = require('debug')('http') + , http = require('http') + , name = 'My App'; + +// fake app + +debug('booting %s', name); + +http.createServer(function(req, res){ + debug(req.method + ' ' + req.url); + res.end('hello\n'); +}).listen(3000, function(){ + debug('listening'); +}); + +// fake worker of some kind + +require('./worker'); +``` + +Example _worker.js_: + +```js +var debug = require('debug')('worker'); + +setInterval(function(){ + debug('doing some work'); +}, 1000); +``` + + The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples: + + ![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png) + + ![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png) + +#### Windows note + + On Windows the environment variable is set using the `set` command. + + ```cmd + set DEBUG=*,-not_this + ``` + +Then, run the program to be debugged as usual. + +## Millisecond diff + + When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls. + + ![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png) + + When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below: + + ![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png) + +## Conventions + + If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". + +## Wildcards + + The `*` character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`. + + You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with "connect:". + +## Browser support + + Debug works in the browser as well, currently persisted by `localStorage`. Consider the situation shown below where you have `worker:a` and `worker:b`, and wish to debug both. Somewhere in the code on your page, include: + +```js +window.myDebug = require("debug"); +``` + + ("debug" is a global object in the browser so we give this object a different name.) When your page is open in the browser, type the following in the console: + +```js +myDebug.enable("worker:*") +``` + + Refresh the page. Debug output will continue to be sent to the console until it is disabled by typing `myDebug.disable()` in the console. + +```js +a = debug('worker:a'); +b = debug('worker:b'); + +setInterval(function(){ + a('doing some work'); +}, 1000); + +setInterval(function(){ + b('doing some work'); +}, 1200); +``` + +#### Web Inspector Colors + + Colors are also enabled on "Web Inspectors" that understand the `%c` formatting + option. These are WebKit web inspectors, Firefox ([since version + 31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/)) + and the Firebug plugin for Firefox (any version). + + Colored output looks something like: + + ![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png) + +### stderr vs stdout + +You can set an alternative logging method per-namespace by overriding the `log` method on a per-namespace or globally: + +Example _stdout.js_: + +```js +var debug = require('debug'); +var error = debug('app:error'); + +// by default stderr is used +error('goes to stderr!'); + +var log = debug('app:log'); +// set this namespace to log via console.log +log.log = console.log.bind(console); // don't forget to bind to console! +log('goes to stdout'); +error('still goes to stderr!'); + +// set all output to go via console.info +// overrides all per-namespace log settings +debug.log = console.info.bind(console); +error('now goes to stdout via console.info'); +log('still goes to stdout, but via console.info now'); +``` + +### Save debug output to a file + +You can save all debug statements to a file by piping them. + +Example: + +```bash +$ DEBUG_FD=3 node your-app.js 3> whatever.log +``` + +## Authors + + - TJ Holowaychuk + - Nathan Rajlich + +## License + +(The MIT License) + +Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/mocha/node_modules/debug/bower.json b/node_modules/mocha/node_modules/debug/bower.json new file mode 100644 index 0000000..6af573f --- /dev/null +++ b/node_modules/mocha/node_modules/debug/bower.json @@ -0,0 +1,28 @@ +{ + "name": "visionmedia-debug", + "main": "dist/debug.js", + "version": "2.2.0", + "homepage": "https://github.com/visionmedia/debug", + "authors": [ + "TJ Holowaychuk " + ], + "description": "visionmedia-debug", + "moduleType": [ + "amd", + "es6", + "globals", + "node" + ], + "keywords": [ + "visionmedia", + "debug" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/node_modules/mocha/node_modules/debug/browser.js b/node_modules/mocha/node_modules/debug/browser.js new file mode 100644 index 0000000..7c76452 --- /dev/null +++ b/node_modules/mocha/node_modules/debug/browser.js @@ -0,0 +1,168 @@ + +/** + * This is the web browser implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = require('./debug'); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +exports.storage = 'undefined' != typeof chrome + && 'undefined' != typeof chrome.storage + ? chrome.storage.local + : localstorage(); + +/** + * Colors. + */ + +exports.colors = [ + 'lightseagreen', + 'forestgreen', + 'goldenrod', + 'dodgerblue', + 'darkorchid', + 'crimson' +]; + +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + +function useColors() { + // is webkit? http://stackoverflow.com/a/16459606/376773 + return ('WebkitAppearance' in document.documentElement.style) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (window.console && (console.firebug || (console.exception && console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); +} + +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +exports.formatters.j = function(v) { + return JSON.stringify(v); +}; + + +/** + * Colorize log arguments if enabled. + * + * @api public + */ + +function formatArgs() { + var args = arguments; + var useColors = this.useColors; + + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); + + if (!useColors) return args; + + var c = 'color: ' + this.color; + args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); + + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); + return args; +} + +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + +function log() { + // this hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return 'object' === typeof console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + try { + if (null == namespaces) { + exports.storage.removeItem('debug'); + } else { + exports.storage.debug = namespaces; + } + } catch(e) {} +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + var r; + try { + r = exports.storage.debug; + } catch(e) {} + return r; +} + +/** + * Enable namespaces listed in `localStorage.debug` initially. + */ + +exports.enable(load()); + +/** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + +function localstorage(){ + try { + return window.localStorage; + } catch (e) {} +} diff --git a/node_modules/mocha/node_modules/debug/component.json b/node_modules/mocha/node_modules/debug/component.json new file mode 100644 index 0000000..ca10637 --- /dev/null +++ b/node_modules/mocha/node_modules/debug/component.json @@ -0,0 +1,19 @@ +{ + "name": "debug", + "repo": "visionmedia/debug", + "description": "small debugging utility", + "version": "2.2.0", + "keywords": [ + "debug", + "log", + "debugger" + ], + "main": "browser.js", + "scripts": [ + "browser.js", + "debug.js" + ], + "dependencies": { + "rauchg/ms.js": "0.7.1" + } +} diff --git a/node_modules/mocha/node_modules/debug/debug.js b/node_modules/mocha/node_modules/debug/debug.js new file mode 100644 index 0000000..7571a86 --- /dev/null +++ b/node_modules/mocha/node_modules/debug/debug.js @@ -0,0 +1,197 @@ + +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = debug; +exports.coerce = coerce; +exports.disable = disable; +exports.enable = enable; +exports.enabled = enabled; +exports.humanize = require('ms'); + +/** + * The currently active debug mode names, and names to skip. + */ + +exports.names = []; +exports.skips = []; + +/** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lowercased letter, i.e. "n". + */ + +exports.formatters = {}; + +/** + * Previously assigned color. + */ + +var prevColor = 0; + +/** + * Previous log timestamp. + */ + +var prevTime; + +/** + * Select a color. + * + * @return {Number} + * @api private + */ + +function selectColor() { + return exports.colors[prevColor++ % exports.colors.length]; +} + +/** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + +function debug(namespace) { + + // define the `disabled` version + function disabled() { + } + disabled.enabled = false; + + // define the `enabled` version + function enabled() { + + var self = enabled; + + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + // add the `color` if not set + if (null == self.useColors) self.useColors = exports.useColors(); + if (null == self.color && self.useColors) self.color = selectColor(); + + var args = Array.prototype.slice.call(arguments); + + args[0] = exports.coerce(args[0]); + + if ('string' !== typeof args[0]) { + // anything else let's inspect with %o + args = ['%o'].concat(args); + } + + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); + + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + if ('function' === typeof exports.formatArgs) { + args = exports.formatArgs.apply(self, args); + } + var logFn = enabled.log || exports.log || console.log.bind(console); + logFn.apply(self, args); + } + enabled.enabled = true; + + var fn = exports.enabled(namespace) ? enabled : disabled; + + fn.namespace = namespace; + + return fn; +} + +/** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + +function enable(namespaces) { + exports.save(namespaces); + + var split = (namespaces || '').split(/[\s,]+/); + var len = split.length; + + for (var i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } + } +} + +/** + * Disable debug output. + * + * @api public + */ + +function disable() { + exports.enable(''); +} + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +function enabled(name) { + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; + } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; + } + } + return false; +} + +/** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} diff --git a/node_modules/mocha/node_modules/debug/node.js b/node_modules/mocha/node_modules/debug/node.js new file mode 100644 index 0000000..1d392a8 --- /dev/null +++ b/node_modules/mocha/node_modules/debug/node.js @@ -0,0 +1,209 @@ + +/** + * Module dependencies. + */ + +var tty = require('tty'); +var util = require('util'); + +/** + * This is the Node.js implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = require('./debug'); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; + +/** + * Colors. + */ + +exports.colors = [6, 2, 3, 4, 5, 1]; + +/** + * The file descriptor to write the `debug()` calls to. + * Set the `DEBUG_FD` env variable to override with another value. i.e.: + * + * $ DEBUG_FD=3 node script.js 3>debug.log + */ + +var fd = parseInt(process.env.DEBUG_FD, 10) || 2; +var stream = 1 === fd ? process.stdout : + 2 === fd ? process.stderr : + createWritableStdioStream(fd); + +/** + * Is stdout a TTY? Colored output is enabled when `true`. + */ + +function useColors() { + var debugColors = (process.env.DEBUG_COLORS || '').trim().toLowerCase(); + if (0 === debugColors.length) { + return tty.isatty(fd); + } else { + return '0' !== debugColors + && 'no' !== debugColors + && 'false' !== debugColors + && 'disabled' !== debugColors; + } +} + +/** + * Map %o to `util.inspect()`, since Node doesn't do that out of the box. + */ + +var inspect = (4 === util.inspect.length ? + // node <= 0.8.x + function (v, colors) { + return util.inspect(v, void 0, void 0, colors); + } : + // node > 0.8.x + function (v, colors) { + return util.inspect(v, { colors: colors }); + } +); + +exports.formatters.o = function(v) { + return inspect(v, this.useColors) + .replace(/\s*\n\s*/g, ' '); +}; + +/** + * Adds ANSI color escape codes if enabled. + * + * @api public + */ + +function formatArgs() { + var args = arguments; + var useColors = this.useColors; + var name = this.namespace; + + if (useColors) { + var c = this.color; + + args[0] = ' \u001b[3' + c + ';1m' + name + ' ' + + '\u001b[0m' + + args[0] + '\u001b[3' + c + 'm' + + ' +' + exports.humanize(this.diff) + '\u001b[0m'; + } else { + args[0] = new Date().toUTCString() + + ' ' + name + ' ' + args[0]; + } + return args; +} + +/** + * Invokes `console.error()` with the specified arguments. + */ + +function log() { + return stream.write(util.format.apply(this, arguments) + '\n'); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + if (null == namespaces) { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } else { + process.env.DEBUG = namespaces; + } +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + return process.env.DEBUG; +} + +/** + * Copied from `node/src/node.js`. + * + * XXX: It's lame that node doesn't expose this API out-of-the-box. It also + * relies on the undocumented `tty_wrap.guessHandleType()` which is also lame. + */ + +function createWritableStdioStream (fd) { + var stream; + var tty_wrap = process.binding('tty_wrap'); + + // Note stream._type is used for test-module-load-list.js + + switch (tty_wrap.guessHandleType(fd)) { + case 'TTY': + stream = new tty.WriteStream(fd); + stream._type = 'tty'; + + // Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; + + case 'FILE': + var fs = require('fs'); + stream = new fs.SyncWriteStream(fd, { autoClose: false }); + stream._type = 'fs'; + break; + + case 'PIPE': + case 'TCP': + var net = require('net'); + stream = new net.Socket({ + fd: fd, + readable: false, + writable: true + }); + + // FIXME Should probably have an option in net.Socket to create a + // stream from an existing fd which is writable only. But for now + // we'll just add this hack and set the `readable` member to false. + // Test: ./node test/fixtures/echo.js < /etc/passwd + stream.readable = false; + stream.read = null; + stream._type = 'pipe'; + + // FIXME Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; + + default: + // Probably an error on in uv_guess_handle() + throw new Error('Implement me. Unknown stream file type!'); + } + + // For supporting legacy API we put the FD here. + stream.fd = fd; + + stream._isStdio = true; + + return stream; +} + +/** + * Enable namespaces listed in `process.env.DEBUG` initially. + */ + +exports.enable(load()); diff --git a/node_modules/mocha/node_modules/debug/node_modules/ms/.npmignore b/node_modules/mocha/node_modules/debug/node_modules/ms/.npmignore new file mode 100644 index 0000000..d1aa0ce --- /dev/null +++ b/node_modules/mocha/node_modules/debug/node_modules/ms/.npmignore @@ -0,0 +1,5 @@ +node_modules +test +History.md +Makefile +component.json diff --git a/node_modules/mocha/node_modules/debug/node_modules/ms/History.md b/node_modules/mocha/node_modules/debug/node_modules/ms/History.md new file mode 100644 index 0000000..32fdfc1 --- /dev/null +++ b/node_modules/mocha/node_modules/debug/node_modules/ms/History.md @@ -0,0 +1,66 @@ + +0.7.1 / 2015-04-20 +================== + + * prevent extraordinary long inputs (@evilpacket) + * Fixed broken readme link + +0.7.0 / 2014-11-24 +================== + + * add time abbreviations, updated tests and readme for the new units + * fix example in the readme. + * add LICENSE file + +0.6.2 / 2013-12-05 +================== + + * Adding repository section to package.json to suppress warning from NPM. + +0.6.1 / 2013-05-10 +================== + + * fix singularization [visionmedia] + +0.6.0 / 2013-03-15 +================== + + * fix minutes + +0.5.1 / 2013-02-24 +================== + + * add component namespace + +0.5.0 / 2012-11-09 +================== + + * add short formatting as default and .long option + * add .license property to component.json + * add version to component.json + +0.4.0 / 2012-10-22 +================== + + * add rounding to fix crazy decimals + +0.3.0 / 2012-09-07 +================== + + * fix `ms()` [visionmedia] + +0.2.0 / 2012-09-03 +================== + + * add component.json [visionmedia] + * add days support [visionmedia] + * add hours support [visionmedia] + * add minutes support [visionmedia] + * add seconds support [visionmedia] + * add ms string support [visionmedia] + * refactor tests to facilitate ms(number) [visionmedia] + +0.1.0 / 2012-03-07 +================== + + * Initial release diff --git a/node_modules/mocha/node_modules/debug/node_modules/ms/LICENSE b/node_modules/mocha/node_modules/debug/node_modules/ms/LICENSE new file mode 100644 index 0000000..6c07561 --- /dev/null +++ b/node_modules/mocha/node_modules/debug/node_modules/ms/LICENSE @@ -0,0 +1,20 @@ +(The MIT License) + +Copyright (c) 2014 Guillermo Rauch + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/mocha/node_modules/debug/node_modules/ms/README.md b/node_modules/mocha/node_modules/debug/node_modules/ms/README.md new file mode 100644 index 0000000..9b4fd03 --- /dev/null +++ b/node_modules/mocha/node_modules/debug/node_modules/ms/README.md @@ -0,0 +1,35 @@ +# ms.js: miliseconds conversion utility + +```js +ms('2 days') // 172800000 +ms('1d') // 86400000 +ms('10h') // 36000000 +ms('2.5 hrs') // 9000000 +ms('2h') // 7200000 +ms('1m') // 60000 +ms('5s') // 5000 +ms('100') // 100 +``` + +```js +ms(60000) // "1m" +ms(2 * 60000) // "2m" +ms(ms('10 hours')) // "10h" +``` + +```js +ms(60000, { long: true }) // "1 minute" +ms(2 * 60000, { long: true }) // "2 minutes" +ms(ms('10 hours'), { long: true }) // "10 hours" +``` + +- Node/Browser compatible. Published as [`ms`](https://www.npmjs.org/package/ms) in [NPM](http://nodejs.org/download). +- If a number is supplied to `ms`, a string with a unit is returned. +- If a string that contains the number is supplied, it returns it as +a number (e.g: it returns `100` for `'100'`). +- If you pass a string with a number and a valid unit, the number of +equivalent ms is returned. + +## License + +MIT diff --git a/node_modules/mocha/node_modules/debug/node_modules/ms/index.js b/node_modules/mocha/node_modules/debug/node_modules/ms/index.js new file mode 100644 index 0000000..4f92771 --- /dev/null +++ b/node_modules/mocha/node_modules/debug/node_modules/ms/index.js @@ -0,0 +1,125 @@ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} options + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options){ + options = options || {}; + if ('string' == typeof val) return parse(val); + return options.long + ? long(val) + : short(val); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + str = '' + str; + if (str.length > 10000) return; + var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str); + if (!match) return; + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function short(ms) { + if (ms >= d) return Math.round(ms / d) + 'd'; + if (ms >= h) return Math.round(ms / h) + 'h'; + if (ms >= m) return Math.round(ms / m) + 'm'; + if (ms >= s) return Math.round(ms / s) + 's'; + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function long(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) return; + if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; + return Math.ceil(ms / n) + ' ' + name + 's'; +} diff --git a/node_modules/mocha/node_modules/debug/node_modules/ms/package.json b/node_modules/mocha/node_modules/debug/node_modules/ms/package.json new file mode 100644 index 0000000..b12c4a0 --- /dev/null +++ b/node_modules/mocha/node_modules/debug/node_modules/ms/package.json @@ -0,0 +1,47 @@ +{ + "name": "ms", + "version": "0.7.1", + "description": "Tiny ms conversion utility", + "repository": { + "type": "git", + "url": "git://github.com/guille/ms.js.git" + }, + "main": "./index", + "devDependencies": { + "mocha": "*", + "expect.js": "*", + "serve": "*" + }, + "component": { + "scripts": { + "ms/index.js": "index.js" + } + }, + "gitHead": "713dcf26d9e6fd9dbc95affe7eff9783b7f1b909", + "bugs": { + "url": "https://github.com/guille/ms.js/issues" + }, + "homepage": "https://github.com/guille/ms.js", + "_id": "ms@0.7.1", + "scripts": {}, + "_shasum": "9cd13c03adbff25b65effde7ce864ee952017098", + "_from": "ms@0.7.1", + "_npmVersion": "2.7.5", + "_nodeVersion": "0.12.2", + "_npmUser": { + "name": "rauchg", + "email": "rauchg@gmail.com" + }, + "maintainers": [ + { + "name": "rauchg", + "email": "rauchg@gmail.com" + } + ], + "dist": { + "shasum": "9cd13c03adbff25b65effde7ce864ee952017098", + "tarball": "http://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" +} diff --git a/node_modules/mocha/node_modules/debug/package.json b/node_modules/mocha/node_modules/debug/package.json new file mode 100644 index 0000000..91c6b99 --- /dev/null +++ b/node_modules/mocha/node_modules/debug/package.json @@ -0,0 +1,72 @@ +{ + "name": "debug", + "version": "2.2.0", + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/debug.git" + }, + "description": "small debugging utility", + "keywords": [ + "debug", + "log", + "debugger" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "contributors": [ + { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net", + "url": "http://n8.io" + } + ], + "license": "MIT", + "dependencies": { + "ms": "0.7.1" + }, + "devDependencies": { + "browserify": "9.0.3", + "mocha": "*" + }, + "main": "./node.js", + "browser": "./browser.js", + "component": { + "scripts": { + "debug/index.js": "browser.js", + "debug/debug.js": "debug.js" + } + }, + "gitHead": "b38458422b5aa8aa6d286b10dfe427e8a67e2b35", + "bugs": { + "url": "https://github.com/visionmedia/debug/issues" + }, + "homepage": "https://github.com/visionmedia/debug", + "_id": "debug@2.2.0", + "scripts": {}, + "_shasum": "f87057e995b1a1f6ae6a4960664137bc56f039da", + "_from": "debug@>=2.2.0 <2.3.0", + "_npmVersion": "2.7.4", + "_nodeVersion": "0.12.2", + "_npmUser": { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + } + ], + "dist": { + "shasum": "f87057e995b1a1f6ae6a4960664137bc56f039da", + "tarball": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" +} diff --git a/node_modules/mocha/node_modules/diff/README.md b/node_modules/mocha/node_modules/diff/README.md new file mode 100644 index 0000000..b867e19 --- /dev/null +++ b/node_modules/mocha/node_modules/diff/README.md @@ -0,0 +1,181 @@ +# jsdiff + +[![Build Status](https://secure.travis-ci.org/kpdecker/jsdiff.png)](http://travis-ci.org/kpdecker/jsdiff) + +A javascript text differencing implementation. + +Based on the algorithm proposed in +["An O(ND) Difference Algorithm and its Variations" (Myers, 1986)](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927). + +## Installation + + npm install diff + +or + + bower install jsdiff + +or + + git clone git://github.com/kpdecker/jsdiff.git + +## API + +* `JsDiff.diffChars(oldStr, newStr[, callback])` - diffs two blocks of text, comparing character by character. + + Returns a list of change objects (See below). + +* `JsDiff.diffWords(oldStr, newStr[, callback])` - diffs two blocks of text, comparing word by word, ignoring whitespace. + + Returns a list of change objects (See below). + +* `JsDiff.diffWordsWithSpace(oldStr, newStr[, callback])` - diffs two blocks of text, comparing word by word, treating whitespace as significant. + + Returns a list of change objects (See below). + +* `JsDiff.diffLines(oldStr, newStr[, callback])` - diffs two blocks of text, comparing line by line. + + Returns a list of change objects (See below). + +* `JsDiff.diffTrimmedLines(oldStr, newStr[, callback])` - diffs two blocks of text, comparing line by line, ignoring leading and trailing whitespace. + + Returns a list of change objects (See below). + +* `JsDiff.diffSentences(oldStr, newStr[, callback])` - diffs two blocks of text, comparing sentence by sentence. + + Returns a list of change objects (See below). + +* `JsDiff.diffCss(oldStr, newStr[, callback])` - diffs two blocks of text, comparing CSS tokens. + + Returns a list of change objects (See below). + +* `JsDiff.diffJson(oldObj, newObj[, callback])` - diffs two JSON objects, comparing the fields defined on each. The order of fields, etc does not matter in this comparison. + + Returns a list of change objects (See below). + +* `JsDiff.createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader)` - creates a unified diff patch. + + Parameters: + * `oldFileName` : String to be output in the filename section of the patch for the removals + * `newFileName` : String to be output in the filename section of the patch for the additions + * `oldStr` : Original string value + * `newStr` : New string value + * `oldHeader` : Additional information to include in the old file header + * `newHeader` : Additional information to include in thew new file header + +* `JsDiff.createPatch(fileName, oldStr, newStr, oldHeader, newHeader)` - creates a unified diff patch. + + Just like JsDiff.createTwoFilesPatch, but with oldFileName being equal to newFileName. + +* `JsDiff.applyPatch(oldStr, diffStr)` - applies a unified diff patch. + + Return a string containing new version of provided data. + +* `convertChangesToXML(changes)` - converts a list of changes to a serialized XML format + + +All methods above which accept the optional callback method will run in sync mode when that parameter is omitted and in async mode when supplied. This allows for larger diffs without blocking the event loop. + +### Change Objects +Many of the methods above return change objects. These objects are consist of the following fields: + +* `value`: Text content +* `added`: True if the value was inserted into the new string +* `removed`: True of the value was removed from the old string + +Note that some cases may omit a particular flag field. Comparison on the flag fields should always be done in a truthy or falsy manner. + +## Examples + +Basic example in Node + +```js +require('colors') +var jsdiff = require('diff'); + +var one = 'beep boop'; +var other = 'beep boob blah'; + +var diff = jsdiff.diffChars(one, other); + +diff.forEach(function(part){ + // green for additions, red for deletions + // grey for common parts + var color = part.added ? 'green' : + part.removed ? 'red' : 'grey'; + process.stderr.write(part.value[color]); +}); + +console.log() +``` +Running the above program should yield + +Node Example + +Basic example in a web page + +```html +
        
        +
        +
        +```
        +
        +Open the above .html file in a browser and you should see
        +
        +Node Example
        +
        +**[Full online demo](http://kpdecker.github.com/jsdiff)**
        +
        +## License
        +
        +Software License Agreement (BSD License)
        +
        +Copyright (c) 2009-2011, Kevin Decker kpdecker@gmail.com
        +
        +All rights reserved.
        +
        +Redistribution and use of this software in source and binary forms, with or without modification,
        +are permitted provided that the following conditions are met:
        +
        +* Redistributions of source code must retain the above
        +  copyright notice, this list of conditions and the
        +  following disclaimer.
        +
        +* Redistributions in binary form must reproduce the above
        +  copyright notice, this list of conditions and the
        +  following disclaimer in the documentation and/or other
        +  materials provided with the distribution.
        +
        +* Neither the name of Kevin Decker nor the names of its
        +  contributors may be used to endorse or promote products
        +  derived from this software without specific prior
        +  written permission.
        +
        +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
        +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
        +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
        +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
        +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
        +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
        +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        +
        +
        +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/kpdecker/jsdiff/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
        diff --git a/node_modules/mocha/node_modules/diff/diff.js b/node_modules/mocha/node_modules/diff/diff.js
        new file mode 100644
        index 0000000..421854a
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/diff/diff.js
        @@ -0,0 +1,619 @@
        +/* See LICENSE file for terms of use */
        +
        +/*
        + * Text diff implementation.
        + *
        + * This library supports the following APIS:
        + * JsDiff.diffChars: Character by character diff
        + * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace
        + * JsDiff.diffLines: Line based diff
        + *
        + * JsDiff.diffCss: Diff targeted at CSS content
        + *
        + * These methods are based on the implementation proposed in
        + * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986).
        + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927
        + */
        +(function(global, undefined) {
        +  var objectPrototypeToString = Object.prototype.toString;
        +
        +  /*istanbul ignore next*/
        +  function map(arr, mapper, that) {
        +    if (Array.prototype.map) {
        +      return Array.prototype.map.call(arr, mapper, that);
        +    }
        +
        +    var other = new Array(arr.length);
        +
        +    for (var i = 0, n = arr.length; i < n; i++) {
        +      other[i] = mapper.call(that, arr[i], i, arr);
        +    }
        +    return other;
        +  }
        +  function clonePath(path) {
        +    return { newPos: path.newPos, components: path.components.slice(0) };
        +  }
        +  function removeEmpty(array) {
        +    var ret = [];
        +    for (var i = 0; i < array.length; i++) {
        +      if (array[i]) {
        +        ret.push(array[i]);
        +      }
        +    }
        +    return ret;
        +  }
        +  function escapeHTML(s) {
        +    var n = s;
        +    n = n.replace(/&/g, '&');
        +    n = n.replace(//g, '>');
        +    n = n.replace(/"/g, '"');
        +
        +    return n;
        +  }
        +
        +  // This function handles the presence of circular references by bailing out when encountering an
        +  // object that is already on the "stack" of items being processed.
        +  function canonicalize(obj, stack, replacementStack) {
        +    stack = stack || [];
        +    replacementStack = replacementStack || [];
        +
        +    var i;
        +
        +    for (i = 0; i < stack.length; i += 1) {
        +      if (stack[i] === obj) {
        +        return replacementStack[i];
        +      }
        +    }
        +
        +    var canonicalizedObj;
        +
        +    if ('[object Array]' === objectPrototypeToString.call(obj)) {
        +      stack.push(obj);
        +      canonicalizedObj = new Array(obj.length);
        +      replacementStack.push(canonicalizedObj);
        +      for (i = 0; i < obj.length; i += 1) {
        +        canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack);
        +      }
        +      stack.pop();
        +      replacementStack.pop();
        +    } else if (typeof obj === 'object' && obj !== null) {
        +      stack.push(obj);
        +      canonicalizedObj = {};
        +      replacementStack.push(canonicalizedObj);
        +      var sortedKeys = [],
        +          key;
        +      for (key in obj) {
        +        sortedKeys.push(key);
        +      }
        +      sortedKeys.sort();
        +      for (i = 0; i < sortedKeys.length; i += 1) {
        +        key = sortedKeys[i];
        +        canonicalizedObj[key] = canonicalize(obj[key], stack, replacementStack);
        +      }
        +      stack.pop();
        +      replacementStack.pop();
        +    } else {
        +      canonicalizedObj = obj;
        +    }
        +    return canonicalizedObj;
        +  }
        +
        +  function buildValues(components, newString, oldString, useLongestToken) {
        +    var componentPos = 0,
        +        componentLen = components.length,
        +        newPos = 0,
        +        oldPos = 0;
        +
        +    for (; componentPos < componentLen; componentPos++) {
        +      var component = components[componentPos];
        +      if (!component.removed) {
        +        if (!component.added && useLongestToken) {
        +          var value = newString.slice(newPos, newPos + component.count);
        +          value = map(value, function(value, i) {
        +            var oldValue = oldString[oldPos + i];
        +            return oldValue.length > value.length ? oldValue : value;
        +          });
        +
        +          component.value = value.join('');
        +        } else {
        +          component.value = newString.slice(newPos, newPos + component.count).join('');
        +        }
        +        newPos += component.count;
        +
        +        // Common case
        +        if (!component.added) {
        +          oldPos += component.count;
        +        }
        +      } else {
        +        component.value = oldString.slice(oldPos, oldPos + component.count).join('');
        +        oldPos += component.count;
        +
        +        // Reverse add and remove so removes are output first to match common convention
        +        // The diffing algorithm is tied to add then remove output and this is the simplest
        +        // route to get the desired output with minimal overhead.
        +        if (componentPos && components[componentPos - 1].added) {
        +          var tmp = components[componentPos - 1];
        +          components[componentPos - 1] = components[componentPos];
        +          components[componentPos] = tmp;
        +        }
        +      }
        +    }
        +
        +    return components;
        +  }
        +
        +  function Diff(ignoreWhitespace) {
        +    this.ignoreWhitespace = ignoreWhitespace;
        +  }
        +  Diff.prototype = {
        +    diff: function(oldString, newString, callback) {
        +      var self = this;
        +
        +      function done(value) {
        +        if (callback) {
        +          setTimeout(function() { callback(undefined, value); }, 0);
        +          return true;
        +        } else {
        +          return value;
        +        }
        +      }
        +
        +      // Handle the identity case (this is due to unrolling editLength == 0
        +      if (newString === oldString) {
        +        return done([{ value: newString }]);
        +      }
        +      if (!newString) {
        +        return done([{ value: oldString, removed: true }]);
        +      }
        +      if (!oldString) {
        +        return done([{ value: newString, added: true }]);
        +      }
        +
        +      newString = this.tokenize(newString);
        +      oldString = this.tokenize(oldString);
        +
        +      var newLen = newString.length, oldLen = oldString.length;
        +      var editLength = 1;
        +      var maxEditLength = newLen + oldLen;
        +      var bestPath = [{ newPos: -1, components: [] }];
        +
        +      // Seed editLength = 0, i.e. the content starts with the same values
        +      var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0);
        +      if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) {
        +        // Identity per the equality and tokenizer
        +        return done([{value: newString.join('')}]);
        +      }
        +
        +      // Main worker method. checks all permutations of a given edit length for acceptance.
        +      function execEditLength() {
        +        for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) {
        +          var basePath;
        +          var addPath = bestPath[diagonalPath - 1],
        +              removePath = bestPath[diagonalPath + 1],
        +              oldPos = (removePath ? removePath.newPos : 0) - diagonalPath;
        +          if (addPath) {
        +            // No one else is going to attempt to use this value, clear it
        +            bestPath[diagonalPath - 1] = undefined;
        +          }
        +
        +          var canAdd = addPath && addPath.newPos + 1 < newLen,
        +              canRemove = removePath && 0 <= oldPos && oldPos < oldLen;
        +          if (!canAdd && !canRemove) {
        +            // If this path is a terminal then prune
        +            bestPath[diagonalPath] = undefined;
        +            continue;
        +          }
        +
        +          // Select the diagonal that we want to branch from. We select the prior
        +          // path whose position in the new string is the farthest from the origin
        +          // and does not pass the bounds of the diff graph
        +          if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) {
        +            basePath = clonePath(removePath);
        +            self.pushComponent(basePath.components, undefined, true);
        +          } else {
        +            basePath = addPath;   // No need to clone, we've pulled it from the list
        +            basePath.newPos++;
        +            self.pushComponent(basePath.components, true, undefined);
        +          }
        +
        +          oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath);
        +
        +          // If we have hit the end of both strings, then we are done
        +          if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) {
        +            return done(buildValues(basePath.components, newString, oldString, self.useLongestToken));
        +          } else {
        +            // Otherwise track this path as a potential candidate and continue.
        +            bestPath[diagonalPath] = basePath;
        +          }
        +        }
        +
        +        editLength++;
        +      }
        +
        +      // Performs the length of edit iteration. Is a bit fugly as this has to support the
        +      // sync and async mode which is never fun. Loops over execEditLength until a value
        +      // is produced.
        +      if (callback) {
        +        (function exec() {
        +          setTimeout(function() {
        +            // This should not happen, but we want to be safe.
        +            /*istanbul ignore next */
        +            if (editLength > maxEditLength) {
        +              return callback();
        +            }
        +
        +            if (!execEditLength()) {
        +              exec();
        +            }
        +          }, 0);
        +        }());
        +      } else {
        +        while (editLength <= maxEditLength) {
        +          var ret = execEditLength();
        +          if (ret) {
        +            return ret;
        +          }
        +        }
        +      }
        +    },
        +
        +    pushComponent: function(components, added, removed) {
        +      var last = components[components.length - 1];
        +      if (last && last.added === added && last.removed === removed) {
        +        // We need to clone here as the component clone operation is just
        +        // as shallow array clone
        +        components[components.length - 1] = {count: last.count + 1, added: added, removed: removed };
        +      } else {
        +        components.push({count: 1, added: added, removed: removed });
        +      }
        +    },
        +    extractCommon: function(basePath, newString, oldString, diagonalPath) {
        +      var newLen = newString.length,
        +          oldLen = oldString.length,
        +          newPos = basePath.newPos,
        +          oldPos = newPos - diagonalPath,
        +
        +          commonCount = 0;
        +      while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) {
        +        newPos++;
        +        oldPos++;
        +        commonCount++;
        +      }
        +
        +      if (commonCount) {
        +        basePath.components.push({count: commonCount});
        +      }
        +
        +      basePath.newPos = newPos;
        +      return oldPos;
        +    },
        +
        +    equals: function(left, right) {
        +      var reWhitespace = /\S/;
        +      return left === right || (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right));
        +    },
        +    tokenize: function(value) {
        +      return value.split('');
        +    }
        +  };
        +
        +  var CharDiff = new Diff();
        +
        +  var WordDiff = new Diff(true);
        +  var WordWithSpaceDiff = new Diff();
        +  WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) {
        +    return removeEmpty(value.split(/(\s+|\b)/));
        +  };
        +
        +  var CssDiff = new Diff(true);
        +  CssDiff.tokenize = function(value) {
        +    return removeEmpty(value.split(/([{}:;,]|\s+)/));
        +  };
        +
        +  var LineDiff = new Diff();
        +
        +  var TrimmedLineDiff = new Diff();
        +  TrimmedLineDiff.ignoreTrim = true;
        +
        +  LineDiff.tokenize = TrimmedLineDiff.tokenize = function(value) {
        +    var retLines = [],
        +        lines = value.split(/^/m);
        +    for (var i = 0; i < lines.length; i++) {
        +      var line = lines[i],
        +          lastLine = lines[i - 1],
        +          lastLineLastChar = lastLine && lastLine[lastLine.length - 1];
        +
        +      // Merge lines that may contain windows new lines
        +      if (line === '\n' && lastLineLastChar === '\r') {
        +          retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0, -1) + '\r\n';
        +      } else {
        +        if (this.ignoreTrim) {
        +          line = line.trim();
        +          // add a newline unless this is the last line.
        +          if (i < lines.length - 1) {
        +            line += '\n';
        +          }
        +        }
        +        retLines.push(line);
        +      }
        +    }
        +
        +    return retLines;
        +  };
        +
        +  var PatchDiff = new Diff();
        +  PatchDiff.tokenize = function(value) {
        +    var ret = [],
        +        linesAndNewlines = value.split(/(\n|\r\n)/);
        +
        +    // Ignore the final empty token that occurs if the string ends with a new line
        +    if (!linesAndNewlines[linesAndNewlines.length - 1]) {
        +      linesAndNewlines.pop();
        +    }
        +
        +    // Merge the content and line separators into single tokens
        +    for (var i = 0; i < linesAndNewlines.length; i++) {
        +      var line = linesAndNewlines[i];
        +
        +      if (i % 2) {
        +        ret[ret.length - 1] += line;
        +      } else {
        +        ret.push(line);
        +      }
        +    }
        +    return ret;
        +  };
        +
        +  var SentenceDiff = new Diff();
        +  SentenceDiff.tokenize = function(value) {
        +    return removeEmpty(value.split(/(\S.+?[.!?])(?=\s+|$)/));
        +  };
        +
        +  var JsonDiff = new Diff();
        +  // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a
        +  // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output:
        +  JsonDiff.useLongestToken = true;
        +  JsonDiff.tokenize = LineDiff.tokenize;
        +  JsonDiff.equals = function(left, right) {
        +    return LineDiff.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'));
        +  };
        +
        +  var JsDiff = {
        +    Diff: Diff,
        +
        +    diffChars: function(oldStr, newStr, callback) { return CharDiff.diff(oldStr, newStr, callback); },
        +    diffWords: function(oldStr, newStr, callback) { return WordDiff.diff(oldStr, newStr, callback); },
        +    diffWordsWithSpace: function(oldStr, newStr, callback) { return WordWithSpaceDiff.diff(oldStr, newStr, callback); },
        +    diffLines: function(oldStr, newStr, callback) { return LineDiff.diff(oldStr, newStr, callback); },
        +    diffTrimmedLines: function(oldStr, newStr, callback) { return TrimmedLineDiff.diff(oldStr, newStr, callback); },
        +
        +    diffSentences: function(oldStr, newStr, callback) { return SentenceDiff.diff(oldStr, newStr, callback); },
        +
        +    diffCss: function(oldStr, newStr, callback) { return CssDiff.diff(oldStr, newStr, callback); },
        +    diffJson: function(oldObj, newObj, callback) {
        +      return JsonDiff.diff(
        +        typeof oldObj === 'string' ? oldObj : JSON.stringify(canonicalize(oldObj), undefined, '  '),
        +        typeof newObj === 'string' ? newObj : JSON.stringify(canonicalize(newObj), undefined, '  '),
        +        callback
        +      );
        +    },
        +
        +    createTwoFilesPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader) {
        +      var ret = [];
        +
        +      if (oldFileName == newFileName) {
        +        ret.push('Index: ' + oldFileName);
        +      }
        +      ret.push('===================================================================');
        +      ret.push('--- ' + oldFileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader));
        +      ret.push('+++ ' + newFileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader));
        +
        +      var diff = PatchDiff.diff(oldStr, newStr);
        +      diff.push({value: '', lines: []});   // Append an empty value to make cleanup easier
        +
        +      // Formats a given set of lines for printing as context lines in a patch
        +      function contextLines(lines) {
        +        return map(lines, function(entry) { return ' ' + entry; });
        +      }
        +
        +      // Outputs the no newline at end of file warning if needed
        +      function eofNL(curRange, i, current) {
        +        var last = diff[diff.length - 2],
        +            isLast = i === diff.length - 2,
        +            isLastOfType = i === diff.length - 3 && current.added !== last.added;
        +
        +        // Figure out if this is the last line for the given file and missing NL
        +        if (!(/\n$/.test(current.value)) && (isLast || isLastOfType)) {
        +          curRange.push('\\ No newline at end of file');
        +        }
        +      }
        +
        +      var oldRangeStart = 0, newRangeStart = 0, curRange = [],
        +          oldLine = 1, newLine = 1;
        +      for (var i = 0; i < diff.length; i++) {
        +        var current = diff[i],
        +            lines = current.lines || current.value.replace(/\n$/, '').split('\n');
        +        current.lines = lines;
        +
        +        if (current.added || current.removed) {
        +          // If we have previous context, start with that
        +          if (!oldRangeStart) {
        +            var prev = diff[i - 1];
        +            oldRangeStart = oldLine;
        +            newRangeStart = newLine;
        +
        +            if (prev) {
        +              curRange = contextLines(prev.lines.slice(-4));
        +              oldRangeStart -= curRange.length;
        +              newRangeStart -= curRange.length;
        +            }
        +          }
        +
        +          // Output our changes
        +          curRange.push.apply(curRange, map(lines, function(entry) {
        +            return (current.added ? '+' : '-') + entry;
        +          }));
        +          eofNL(curRange, i, current);
        +
        +          // Track the updated file position
        +          if (current.added) {
        +            newLine += lines.length;
        +          } else {
        +            oldLine += lines.length;
        +          }
        +        } else {
        +          // Identical context lines. Track line changes
        +          if (oldRangeStart) {
        +            // Close out any changes that have been output (or join overlapping)
        +            if (lines.length <= 8 && i < diff.length - 2) {
        +              // Overlapping
        +              curRange.push.apply(curRange, contextLines(lines));
        +            } else {
        +              // end the range and output
        +              var contextSize = Math.min(lines.length, 4);
        +              ret.push(
        +                  '@@ -' + oldRangeStart + ',' + (oldLine - oldRangeStart + contextSize)
        +                  + ' +' + newRangeStart + ',' + (newLine - newRangeStart + contextSize)
        +                  + ' @@');
        +              ret.push.apply(ret, curRange);
        +              ret.push.apply(ret, contextLines(lines.slice(0, contextSize)));
        +              if (lines.length <= 4) {
        +                eofNL(ret, i, current);
        +              }
        +
        +              oldRangeStart = 0;
        +              newRangeStart = 0;
        +              curRange = [];
        +            }
        +          }
        +          oldLine += lines.length;
        +          newLine += lines.length;
        +        }
        +      }
        +
        +      return ret.join('\n') + '\n';
        +    },
        +
        +    createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) {
        +      return JsDiff.createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader);
        +    },
        +
        +    applyPatch: function(oldStr, uniDiff) {
        +      var diffstr = uniDiff.split('\n'),
        +          hunks = [],
        +          i = 0,
        +          remEOFNL = false,
        +          addEOFNL = false;
        +
        +      // Skip to the first change hunk
        +      while (i < diffstr.length && !(/^@@/.test(diffstr[i]))) {
        +        i++;
        +      }
        +
        +      // Parse the unified diff
        +      for (; i < diffstr.length; i++) {
        +        if (diffstr[i][0] === '@') {
        +          var chnukHeader = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/);
        +          hunks.unshift({
        +            start: chnukHeader[3],
        +            oldlength: +chnukHeader[2],
        +            removed: [],
        +            newlength: chnukHeader[4],
        +            added: []
        +          });
        +        } else if (diffstr[i][0] === '+') {
        +          hunks[0].added.push(diffstr[i].substr(1));
        +        } else if (diffstr[i][0] === '-') {
        +          hunks[0].removed.push(diffstr[i].substr(1));
        +        } else if (diffstr[i][0] === ' ') {
        +          hunks[0].added.push(diffstr[i].substr(1));
        +          hunks[0].removed.push(diffstr[i].substr(1));
        +        } else if (diffstr[i][0] === '\\') {
        +          if (diffstr[i - 1][0] === '+') {
        +            remEOFNL = true;
        +          } else if (diffstr[i - 1][0] === '-') {
        +            addEOFNL = true;
        +          }
        +        }
        +      }
        +
        +      // Apply the diff to the input
        +      var lines = oldStr.split('\n');
        +      for (i = hunks.length - 1; i >= 0; i--) {
        +        var hunk = hunks[i];
        +        // Sanity check the input string. Bail if we don't match.
        +        for (var j = 0; j < hunk.oldlength; j++) {
        +          if (lines[hunk.start - 1 + j] !== hunk.removed[j]) {
        +            return false;
        +          }
        +        }
        +        Array.prototype.splice.apply(lines, [hunk.start - 1, hunk.oldlength].concat(hunk.added));
        +      }
        +
        +      // Handle EOFNL insertion/removal
        +      if (remEOFNL) {
        +        while (!lines[lines.length - 1]) {
        +          lines.pop();
        +        }
        +      } else if (addEOFNL) {
        +        lines.push('');
        +      }
        +      return lines.join('\n');
        +    },
        +
        +    convertChangesToXML: function(changes) {
        +      var ret = [];
        +      for (var i = 0; i < changes.length; i++) {
        +        var change = changes[i];
        +        if (change.added) {
        +          ret.push('');
        +        } else if (change.removed) {
        +          ret.push('');
        +        }
        +
        +        ret.push(escapeHTML(change.value));
        +
        +        if (change.added) {
        +          ret.push('');
        +        } else if (change.removed) {
        +          ret.push('');
        +        }
        +      }
        +      return ret.join('');
        +    },
        +
        +    // See: http://code.google.com/p/google-diff-match-patch/wiki/API
        +    convertChangesToDMP: function(changes) {
        +      var ret = [],
        +          change,
        +          operation;
        +      for (var i = 0; i < changes.length; i++) {
        +        change = changes[i];
        +        if (change.added) {
        +          operation = 1;
        +        } else if (change.removed) {
        +          operation = -1;
        +        } else {
        +          operation = 0;
        +        }
        +
        +        ret.push([operation, change.value]);
        +      }
        +      return ret;
        +    },
        +
        +    canonicalize: canonicalize
        +  };
        +
        +  /*istanbul ignore next */
        +  /*global module */
        +  if (typeof module !== 'undefined' && module.exports) {
        +    module.exports = JsDiff;
        +  } else if (typeof define === 'function' && define.amd) {
        +    /*global define */
        +    define([], function() { return JsDiff; });
        +  } else if (typeof global.JsDiff === 'undefined') {
        +    global.JsDiff = JsDiff;
        +  }
        +}(this));
        diff --git a/node_modules/mocha/node_modules/diff/package.json b/node_modules/mocha/node_modules/diff/package.json
        new file mode 100644
        index 0000000..6b1a82b
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/diff/package.json
        @@ -0,0 +1,63 @@
        +{
        +  "name": "diff",
        +  "version": "1.4.0",
        +  "description": "A javascript text diff implementation.",
        +  "keywords": [
        +    "diff",
        +    "javascript"
        +  ],
        +  "maintainers": [
        +    {
        +      "name": "kpdecker",
        +      "email": "kpdecker@gmail.com"
        +    }
        +  ],
        +  "bugs": {
        +    "url": "http://github.com/kpdecker/jsdiff/issues",
        +    "email": "kpdecker@gmail.com"
        +  },
        +  "licenses": [
        +    {
        +      "type": "BSD",
        +      "url": "http://github.com/kpdecker/jsdiff/blob/master/LICENSE"
        +    }
        +  ],
        +  "repository": {
        +    "type": "git",
        +    "url": "git://github.com/kpdecker/jsdiff.git"
        +  },
        +  "engines": {
        +    "node": ">=0.3.1"
        +  },
        +  "main": "./diff",
        +  "scripts": {
        +    "test": "istanbul cover node_modules/.bin/_mocha test/*.js && istanbul check-coverage --statements 100 --functions 100 --branches 100 --lines 100 coverage/coverage.json"
        +  },
        +  "dependencies": {},
        +  "devDependencies": {
        +    "colors": "^1.1.0",
        +    "istanbul": "^0.3.2",
        +    "mocha": "^2.2.4",
        +    "should": "^6.0.1"
        +  },
        +  "optionalDependencies": {},
        +  "files": [
        +    "diff.js"
        +  ],
        +  "gitHead": "27a750e9116e6ade6303bc24a9be72f6845e00ed",
        +  "homepage": "https://github.com/kpdecker/jsdiff",
        +  "_id": "diff@1.4.0",
        +  "_shasum": "7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf",
        +  "_from": "diff@1.4.0",
        +  "_npmVersion": "1.4.28",
        +  "_npmUser": {
        +    "name": "kpdecker",
        +    "email": "kpdecker@gmail.com"
        +  },
        +  "dist": {
        +    "shasum": "7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf",
        +    "tarball": "http://registry.npmjs.org/diff/-/diff-1.4.0.tgz"
        +  },
        +  "directories": {},
        +  "_resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz"
        +}
        diff --git a/node_modules/mocha/node_modules/escape-string-regexp/index.js b/node_modules/mocha/node_modules/escape-string-regexp/index.js
        new file mode 100644
        index 0000000..ac6572c
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/escape-string-regexp/index.js
        @@ -0,0 +1,11 @@
        +'use strict';
        +
        +var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
        +
        +module.exports = function (str) {
        +	if (typeof str !== 'string') {
        +		throw new TypeError('Expected a string');
        +	}
        +
        +	return str.replace(matchOperatorsRe,  '\\$&');
        +};
        diff --git a/node_modules/mocha/node_modules/escape-string-regexp/package.json b/node_modules/mocha/node_modules/escape-string-regexp/package.json
        new file mode 100644
        index 0000000..b47d665
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/escape-string-regexp/package.json
        @@ -0,0 +1,68 @@
        +{
        +  "name": "escape-string-regexp",
        +  "version": "1.0.2",
        +  "description": "Escape RegExp special characters",
        +  "license": "MIT",
        +  "repository": {
        +    "type": "git",
        +    "url": "https://github.com/sindresorhus/escape-string-regexp"
        +  },
        +  "author": {
        +    "name": "Sindre Sorhus",
        +    "email": "sindresorhus@gmail.com",
        +    "url": "http://sindresorhus.com"
        +  },
        +  "engines": {
        +    "node": ">=0.8.0"
        +  },
        +  "scripts": {
        +    "test": "mocha"
        +  },
        +  "files": [
        +    "index.js"
        +  ],
        +  "keywords": [
        +    "regex",
        +    "regexp",
        +    "re",
        +    "regular",
        +    "expression",
        +    "escape",
        +    "string",
        +    "str",
        +    "special",
        +    "characters"
        +  ],
        +  "devDependencies": {
        +    "mocha": "*"
        +  },
        +  "gitHead": "0587ee0ee03ea3fcbfa3c15cf67b47f214e20987",
        +  "bugs": {
        +    "url": "https://github.com/sindresorhus/escape-string-regexp/issues"
        +  },
        +  "homepage": "https://github.com/sindresorhus/escape-string-regexp",
        +  "_id": "escape-string-regexp@1.0.2",
        +  "_shasum": "4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1",
        +  "_from": "escape-string-regexp@1.0.2",
        +  "_npmVersion": "1.4.23",
        +  "_npmUser": {
        +    "name": "jbnicolai",
        +    "email": "jappelman@xebia.com"
        +  },
        +  "maintainers": [
        +    {
        +      "name": "sindresorhus",
        +      "email": "sindresorhus@gmail.com"
        +    },
        +    {
        +      "name": "jbnicolai",
        +      "email": "jappelman@xebia.com"
        +    }
        +  ],
        +  "dist": {
        +    "shasum": "4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1",
        +    "tarball": "http://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz"
        +  },
        +  "directories": {},
        +  "_resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz"
        +}
        diff --git a/node_modules/mocha/node_modules/escape-string-regexp/readme.md b/node_modules/mocha/node_modules/escape-string-regexp/readme.md
        new file mode 100644
        index 0000000..808a963
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/escape-string-regexp/readme.md
        @@ -0,0 +1,27 @@
        +# escape-string-regexp [![Build Status](https://travis-ci.org/sindresorhus/escape-string-regexp.svg?branch=master)](https://travis-ci.org/sindresorhus/escape-string-regexp)
        +
        +> Escape RegExp special characters
        +
        +
        +## Install
        +
        +```sh
        +$ npm install --save escape-string-regexp
        +```
        +
        +
        +## Usage
        +
        +```js
        +var escapeStringRegexp = require('escape-string-regexp');
        +
        +var escapedString = escapeStringRegexp('how much $ for a unicorn?');
        +//=> how much \$ for a unicorn\?
        +
        +new RegExp(escapedString);
        +```
        +
        +
        +## License
        +
        +MIT © [Sindre Sorhus](http://sindresorhus.com)
        diff --git a/node_modules/mocha/node_modules/glob/.npmignore b/node_modules/mocha/node_modules/glob/.npmignore
        new file mode 100644
        index 0000000..2af4b71
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/.npmignore
        @@ -0,0 +1,2 @@
        +.*.swp
        +test/a/
        diff --git a/node_modules/mocha/node_modules/glob/.travis.yml b/node_modules/mocha/node_modules/glob/.travis.yml
        new file mode 100644
        index 0000000..baa0031
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/.travis.yml
        @@ -0,0 +1,3 @@
        +language: node_js
        +node_js:
        +  - 0.8
        diff --git a/node_modules/mocha/node_modules/glob/LICENSE b/node_modules/mocha/node_modules/glob/LICENSE
        new file mode 100644
        index 0000000..0c44ae7
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/LICENSE
        @@ -0,0 +1,27 @@
        +Copyright (c) Isaac Z. Schlueter ("Author")
        +All rights reserved.
        +
        +The BSD License
        +
        +Redistribution and use in source and binary forms, with or without
        +modification, are permitted provided that the following conditions
        +are met:
        +
        +1. Redistributions of source code must retain the above copyright
        +   notice, this list of conditions and the following disclaimer.
        +
        +2. Redistributions in binary form must reproduce the above copyright
        +   notice, this list of conditions and the following disclaimer in the
        +   documentation and/or other materials provided with the distribution.
        +
        +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
        +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
        +PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
        +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
        +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
        +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
        +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
        +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
        +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
        +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        diff --git a/node_modules/mocha/node_modules/glob/README.md b/node_modules/mocha/node_modules/glob/README.md
        new file mode 100644
        index 0000000..cc69164
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/README.md
        @@ -0,0 +1,250 @@
        +# Glob
        +
        +Match files using the patterns the shell uses, like stars and stuff.
        +
        +This is a glob implementation in JavaScript.  It uses the `minimatch`
        +library to do its matching.
        +
        +## Attention: node-glob users!
        +
        +The API has changed dramatically between 2.x and 3.x. This library is
        +now 100% JavaScript, and the integer flags have been replaced with an
        +options object.
        +
        +Also, there's an event emitter class, proper tests, and all the other
        +things you've come to expect from node modules.
        +
        +And best of all, no compilation!
        +
        +## Usage
        +
        +```javascript
        +var glob = require("glob")
        +
        +// options is optional
        +glob("**/*.js", options, function (er, files) {
        +  // files is an array of filenames.
        +  // If the `nonull` option is set, and nothing
        +  // was found, then files is ["**/*.js"]
        +  // er is an error object or null.
        +})
        +```
        +
        +## Features
        +
        +Please see the [minimatch
        +documentation](https://github.com/isaacs/minimatch) for more details.
        +
        +Supports these glob features:
        +
        +* Brace Expansion
        +* Extended glob matching
        +* "Globstar" `**` matching
        +
        +See:
        +
        +* `man sh`
        +* `man bash`
        +* `man 3 fnmatch`
        +* `man 5 gitignore`
        +* [minimatch documentation](https://github.com/isaacs/minimatch)
        +
        +## glob(pattern, [options], cb)
        +
        +* `pattern` {String} Pattern to be matched
        +* `options` {Object}
        +* `cb` {Function}
        +  * `err` {Error | null}
        +  * `matches` {Array} filenames found matching the pattern
        +
        +Perform an asynchronous glob search.
        +
        +## glob.sync(pattern, [options])
        +
        +* `pattern` {String} Pattern to be matched
        +* `options` {Object}
        +* return: {Array} filenames found matching the pattern
        +
        +Perform a synchronous glob search.
        +
        +## Class: glob.Glob
        +
        +Create a Glob object by instanting the `glob.Glob` class.
        +
        +```javascript
        +var Glob = require("glob").Glob
        +var mg = new Glob(pattern, options, cb)
        +```
        +
        +It's an EventEmitter, and starts walking the filesystem to find matches
        +immediately.
        +
        +### new glob.Glob(pattern, [options], [cb])
        +
        +* `pattern` {String} pattern to search for
        +* `options` {Object}
        +* `cb` {Function} Called when an error occurs, or matches are found
        +  * `err` {Error | null}
        +  * `matches` {Array} filenames found matching the pattern
        +
        +Note that if the `sync` flag is set in the options, then matches will
        +be immediately available on the `g.found` member.
        +
        +### Properties
        +
        +* `minimatch` The minimatch object that the glob uses.
        +* `options` The options object passed in.
        +* `error` The error encountered.  When an error is encountered, the
        +  glob object is in an undefined state, and should be discarded.
        +* `aborted` Boolean which is set to true when calling `abort()`.  There
        +  is no way at this time to continue a glob search after aborting, but
        +  you can re-use the statCache to avoid having to duplicate syscalls.
        +* `statCache` Collection of all the stat results the glob search
        +  performed.
        +* `cache` Convenience object.  Each field has the following possible
        +  values:
        +  * `false` - Path does not exist
        +  * `true` - Path exists
        +  * `1` - Path exists, and is not a directory
        +  * `2` - Path exists, and is a directory
        +  * `[file, entries, ...]` - Path exists, is a directory, and the
        +    array value is the results of `fs.readdir`
        +
        +### Events
        +
        +* `end` When the matching is finished, this is emitted with all the
        +  matches found.  If the `nonull` option is set, and no match was found,
        +  then the `matches` list contains the original pattern.  The matches
        +  are sorted, unless the `nosort` flag is set.
        +* `match` Every time a match is found, this is emitted with the matched.
        +* `error` Emitted when an unexpected error is encountered, or whenever
        +  any fs error occurs if `options.strict` is set.
        +* `abort` When `abort()` is called, this event is raised.
        +
        +### Methods
        +
        +* `abort` Stop the search.
        +
        +### Options
        +
        +All the options that can be passed to Minimatch can also be passed to
        +Glob to change pattern matching behavior.  Also, some have been added,
        +or have glob-specific ramifications.
        +
        +All options are false by default, unless otherwise noted.
        +
        +All options are added to the glob object, as well.
        +
        +* `cwd` The current working directory in which to search.  Defaults
        +  to `process.cwd()`.
        +* `root` The place where patterns starting with `/` will be mounted
        +  onto.  Defaults to `path.resolve(options.cwd, "/")` (`/` on Unix
        +  systems, and `C:\` or some such on Windows.)
        +* `dot` Include `.dot` files in normal matches and `globstar` matches.
        +  Note that an explicit dot in a portion of the pattern will always
        +  match dot files.
        +* `nomount` By default, a pattern starting with a forward-slash will be
        +  "mounted" onto the root setting, so that a valid filesystem path is
        +  returned.  Set this flag to disable that behavior.
        +* `mark` Add a `/` character to directory matches.  Note that this
        +  requires additional stat calls.
        +* `nosort` Don't sort the results.
        +* `stat` Set to true to stat *all* results.  This reduces performance
        +  somewhat, and is completely unnecessary, unless `readdir` is presumed
        +  to be an untrustworthy indicator of file existence.  It will cause
        +  ELOOP to be triggered one level sooner in the case of cyclical
        +  symbolic links.
        +* `silent` When an unusual error is encountered
        +  when attempting to read a directory, a warning will be printed to
        +  stderr.  Set the `silent` option to true to suppress these warnings.
        +* `strict` When an unusual error is encountered
        +  when attempting to read a directory, the process will just continue on
        +  in search of other matches.  Set the `strict` option to raise an error
        +  in these cases.
        +* `cache` See `cache` property above.  Pass in a previously generated
        +  cache object to save some fs calls.
        +* `statCache` A cache of results of filesystem information, to prevent
        +  unnecessary stat calls.  While it should not normally be necessary to
        +  set this, you may pass the statCache from one glob() call to the
        +  options object of another, if you know that the filesystem will not
        +  change between calls.  (See "Race Conditions" below.)
        +* `sync` Perform a synchronous glob search.
        +* `nounique` In some cases, brace-expanded patterns can result in the
        +  same file showing up multiple times in the result set.  By default,
        +  this implementation prevents duplicates in the result set.
        +  Set this flag to disable that behavior.
        +* `nonull` Set to never return an empty set, instead returning a set
        +  containing the pattern itself.  This is the default in glob(3).
        +* `nocase` Perform a case-insensitive match.  Note that case-insensitive
        +  filesystems will sometimes result in glob returning results that are
        +  case-insensitively matched anyway, since readdir and stat will not
        +  raise an error.
        +* `debug` Set to enable debug logging in minimatch and glob.
        +* `globDebug` Set to enable debug logging in glob, but not minimatch.
        +
        +## Comparisons to other fnmatch/glob implementations
        +
        +While strict compliance with the existing standards is a worthwhile
        +goal, some discrepancies exist between node-glob and other
        +implementations, and are intentional.
        +
        +If the pattern starts with a `!` character, then it is negated.  Set the
        +`nonegate` flag to suppress this behavior, and treat leading `!`
        +characters normally.  This is perhaps relevant if you wish to start the
        +pattern with a negative extglob pattern like `!(a|B)`.  Multiple `!`
        +characters at the start of a pattern will negate the pattern multiple
        +times.
        +
        +If a pattern starts with `#`, then it is treated as a comment, and
        +will not match anything.  Use `\#` to match a literal `#` at the
        +start of a line, or set the `nocomment` flag to suppress this behavior.
        +
        +The double-star character `**` is supported by default, unless the
        +`noglobstar` flag is set.  This is supported in the manner of bsdglob
        +and bash 4.1, where `**` only has special significance if it is the only
        +thing in a path part.  That is, `a/**/b` will match `a/x/y/b`, but
        +`a/**b` will not.
        +
        +If an escaped pattern has no matches, and the `nonull` flag is set,
        +then glob returns the pattern as-provided, rather than
        +interpreting the character escapes.  For example,
        +`glob.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
        +`"*a?"`.  This is akin to setting the `nullglob` option in bash, except
        +that it does not resolve escaped pattern characters.
        +
        +If brace expansion is not disabled, then it is performed before any
        +other interpretation of the glob pattern.  Thus, a pattern like
        +`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
        +**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
        +checked for validity.  Since those two are valid, matching proceeds.
        +
        +## Windows
        +
        +**Please only use forward-slashes in glob expressions.**
        +
        +Though windows uses either `/` or `\` as its path separator, only `/`
        +characters are used by this glob implementation.  You must use
        +forward-slashes **only** in glob expressions.  Back-slashes will always
        +be interpreted as escape characters, not path separators.
        +
        +Results from absolute patterns such as `/foo/*` are mounted onto the
        +root setting using `path.join`.  On windows, this will by default result
        +in `/foo/*` matching `C:\foo\bar.txt`.
        +
        +## Race Conditions
        +
        +Glob searching, by its very nature, is susceptible to race conditions,
        +since it relies on directory walking and such.
        +
        +As a result, it is possible that a file that exists when glob looks for
        +it may have been deleted or modified by the time it returns the result.
        +
        +As part of its internal implementation, this program caches all stat
        +and readdir calls that it makes, in order to cut down on system
        +overhead.  However, this also makes it even more susceptible to races,
        +especially if the cache or statCache objects are reused between glob
        +calls.
        +
        +Users are thus advised not to use a glob result as a guarantee of
        +filesystem state in the face of rapid changes.  For the vast majority
        +of operations, this is never a problem.
        diff --git a/node_modules/mocha/node_modules/glob/examples/g.js b/node_modules/mocha/node_modules/glob/examples/g.js
        new file mode 100644
        index 0000000..be122df
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/examples/g.js
        @@ -0,0 +1,9 @@
        +var Glob = require("../").Glob
        +
        +var pattern = "test/a/**/[cg]/../[cg]"
        +console.log(pattern)
        +
        +var mg = new Glob(pattern, {mark: true, sync:true}, function (er, matches) {
        +  console.log("matches", matches)
        +})
        +console.log("after")
        diff --git a/node_modules/mocha/node_modules/glob/examples/usr-local.js b/node_modules/mocha/node_modules/glob/examples/usr-local.js
        new file mode 100644
        index 0000000..327a425
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/examples/usr-local.js
        @@ -0,0 +1,9 @@
        +var Glob = require("../").Glob
        +
        +var pattern = "{./*/*,/*,/usr/local/*}"
        +console.log(pattern)
        +
        +var mg = new Glob(pattern, {mark: true}, function (er, matches) {
        +  console.log("matches", matches)
        +})
        +console.log("after")
        diff --git a/node_modules/mocha/node_modules/glob/glob.js b/node_modules/mocha/node_modules/glob/glob.js
        new file mode 100644
        index 0000000..f0118a4
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/glob.js
        @@ -0,0 +1,675 @@
        +// Approach:
        +//
        +// 1. Get the minimatch set
        +// 2. For each pattern in the set, PROCESS(pattern)
        +// 3. Store matches per-set, then uniq them
        +//
        +// PROCESS(pattern)
        +// Get the first [n] items from pattern that are all strings
        +// Join these together.  This is PREFIX.
        +//   If there is no more remaining, then stat(PREFIX) and
        +//   add to matches if it succeeds.  END.
        +// readdir(PREFIX) as ENTRIES
        +//   If fails, END
        +//   If pattern[n] is GLOBSTAR
        +//     // handle the case where the globstar match is empty
        +//     // by pruning it out, and testing the resulting pattern
        +//     PROCESS(pattern[0..n] + pattern[n+1 .. $])
        +//     // handle other cases.
        +//     for ENTRY in ENTRIES (not dotfiles)
        +//       // attach globstar + tail onto the entry
        +//       PROCESS(pattern[0..n] + ENTRY + pattern[n .. $])
        +//
        +//   else // not globstar
        +//     for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot)
        +//       Test ENTRY against pattern[n]
        +//       If fails, continue
        +//       If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $])
        +//
        +// Caveat:
        +//   Cache all stats and readdirs results to minimize syscall.  Since all
        +//   we ever care about is existence and directory-ness, we can just keep
        +//   `true` for files, and [children,...] for directories, or `false` for
        +//   things that don't exist.
        +
        +
        +
        +module.exports = glob
        +
        +var fs = require("graceful-fs")
        +, minimatch = require("minimatch")
        +, Minimatch = minimatch.Minimatch
        +, inherits = require("inherits")
        +, EE = require("events").EventEmitter
        +, path = require("path")
        +, isDir = {}
        +, assert = require("assert").ok
        +
        +function glob (pattern, options, cb) {
        +  if (typeof options === "function") cb = options, options = {}
        +  if (!options) options = {}
        +
        +  if (typeof options === "number") {
        +    deprecated()
        +    return
        +  }
        +
        +  var g = new Glob(pattern, options, cb)
        +  return g.sync ? g.found : g
        +}
        +
        +glob.fnmatch = deprecated
        +
        +function deprecated () {
        +  throw new Error("glob's interface has changed. Please see the docs.")
        +}
        +
        +glob.sync = globSync
        +function globSync (pattern, options) {
        +  if (typeof options === "number") {
        +    deprecated()
        +    return
        +  }
        +
        +  options = options || {}
        +  options.sync = true
        +  return glob(pattern, options)
        +}
        +
        +
        +glob.Glob = Glob
        +inherits(Glob, EE)
        +function Glob (pattern, options, cb) {
        +  if (!(this instanceof Glob)) {
        +    return new Glob(pattern, options, cb)
        +  }
        +
        +  if (typeof cb === "function") {
        +    this.on("error", cb)
        +    this.on("end", function (matches) {
        +      cb(null, matches)
        +    })
        +  }
        +
        +  options = options || {}
        +
        +  this.EOF = {}
        +  this._emitQueue = []
        +
        +  this.maxDepth = options.maxDepth || 1000
        +  this.maxLength = options.maxLength || Infinity
        +  this.cache = options.cache || {}
        +  this.statCache = options.statCache || {}
        +
        +  this.changedCwd = false
        +  var cwd = process.cwd()
        +  if (!options.hasOwnProperty("cwd")) this.cwd = cwd
        +  else {
        +    this.cwd = options.cwd
        +    this.changedCwd = path.resolve(options.cwd) !== cwd
        +  }
        +
        +  this.root = options.root || path.resolve(this.cwd, "/")
        +  this.root = path.resolve(this.root)
        +  if (process.platform === "win32")
        +    this.root = this.root.replace(/\\/g, "/")
        +
        +  this.nomount = !!options.nomount
        +
        +  if (!pattern) {
        +    throw new Error("must provide pattern")
        +  }
        +
        +  // base-matching: just use globstar for that.
        +  if (options.matchBase && -1 === pattern.indexOf("/")) {
        +    if (options.noglobstar) {
        +      throw new Error("base matching requires globstar")
        +    }
        +    pattern = "**/" + pattern
        +  }
        +
        +  this.strict = options.strict !== false
        +  this.dot = !!options.dot
        +  this.mark = !!options.mark
        +  this.sync = !!options.sync
        +  this.nounique = !!options.nounique
        +  this.nonull = !!options.nonull
        +  this.nosort = !!options.nosort
        +  this.nocase = !!options.nocase
        +  this.stat = !!options.stat
        +
        +  this.debug = !!options.debug || !!options.globDebug
        +  if (this.debug)
        +    this.log = console.error
        +
        +  this.silent = !!options.silent
        +
        +  var mm = this.minimatch = new Minimatch(pattern, options)
        +  this.options = mm.options
        +  pattern = this.pattern = mm.pattern
        +
        +  this.error = null
        +  this.aborted = false
        +
        +  // list of all the patterns that ** has resolved do, so
        +  // we can avoid visiting multiple times.
        +  this._globstars = {}
        +
        +  EE.call(this)
        +
        +  // process each pattern in the minimatch set
        +  var n = this.minimatch.set.length
        +
        +  // The matches are stored as {: true,...} so that
        +  // duplicates are automagically pruned.
        +  // Later, we do an Object.keys() on these.
        +  // Keep them as a list so we can fill in when nonull is set.
        +  this.matches = new Array(n)
        +
        +  this.minimatch.set.forEach(iterator.bind(this))
        +  function iterator (pattern, i, set) {
        +    this._process(pattern, 0, i, function (er) {
        +      if (er) this.emit("error", er)
        +      if (-- n <= 0) this._finish()
        +    })
        +  }
        +}
        +
        +Glob.prototype.log = function () {}
        +
        +Glob.prototype._finish = function () {
        +  assert(this instanceof Glob)
        +
        +  var nou = this.nounique
        +  , all = nou ? [] : {}
        +
        +  for (var i = 0, l = this.matches.length; i < l; i ++) {
        +    var matches = this.matches[i]
        +    this.log("matches[%d] =", i, matches)
        +    // do like the shell, and spit out the literal glob
        +    if (!matches) {
        +      if (this.nonull) {
        +        var literal = this.minimatch.globSet[i]
        +        if (nou) all.push(literal)
        +        else all[literal] = true
        +      }
        +    } else {
        +      // had matches
        +      var m = Object.keys(matches)
        +      if (nou) all.push.apply(all, m)
        +      else m.forEach(function (m) {
        +        all[m] = true
        +      })
        +    }
        +  }
        +
        +  if (!nou) all = Object.keys(all)
        +
        +  if (!this.nosort) {
        +    all = all.sort(this.nocase ? alphasorti : alphasort)
        +  }
        +
        +  if (this.mark) {
        +    // at *some* point we statted all of these
        +    all = all.map(function (m) {
        +      var sc = this.cache[m]
        +      if (!sc)
        +        return m
        +      var isDir = (Array.isArray(sc) || sc === 2)
        +      if (isDir && m.slice(-1) !== "/") {
        +        return m + "/"
        +      }
        +      if (!isDir && m.slice(-1) === "/") {
        +        return m.replace(/\/+$/, "")
        +      }
        +      return m
        +    }, this)
        +  }
        +
        +  this.log("emitting end", all)
        +
        +  this.EOF = this.found = all
        +  this.emitMatch(this.EOF)
        +}
        +
        +function alphasorti (a, b) {
        +  a = a.toLowerCase()
        +  b = b.toLowerCase()
        +  return alphasort(a, b)
        +}
        +
        +function alphasort (a, b) {
        +  return a > b ? 1 : a < b ? -1 : 0
        +}
        +
        +Glob.prototype.abort = function () {
        +  this.aborted = true
        +  this.emit("abort")
        +}
        +
        +Glob.prototype.pause = function () {
        +  if (this.paused) return
        +  if (this.sync)
        +    this.emit("error", new Error("Can't pause/resume sync glob"))
        +  this.paused = true
        +  this.emit("pause")
        +}
        +
        +Glob.prototype.resume = function () {
        +  if (!this.paused) return
        +  if (this.sync)
        +    this.emit("error", new Error("Can't pause/resume sync glob"))
        +  this.paused = false
        +  this.emit("resume")
        +  this._processEmitQueue()
        +  //process.nextTick(this.emit.bind(this, "resume"))
        +}
        +
        +Glob.prototype.emitMatch = function (m) {
        +  if (!this.stat || this.statCache[m] || m === this.EOF) {
        +    this._emitQueue.push(m)
        +    this._processEmitQueue()
        +  } else {
        +    this._stat(m, function(exists, isDir) {
        +      if (exists) {
        +        this._emitQueue.push(m)
        +        this._processEmitQueue()
        +      }
        +    })
        +  }
        +}
        +
        +Glob.prototype._processEmitQueue = function (m) {
        +  while (!this._processingEmitQueue &&
        +         !this.paused) {
        +    this._processingEmitQueue = true
        +    var m = this._emitQueue.shift()
        +    if (!m) {
        +      this._processingEmitQueue = false
        +      break
        +    }
        +
        +    this.log('emit!', m === this.EOF ? "end" : "match")
        +
        +    this.emit(m === this.EOF ? "end" : "match", m)
        +    this._processingEmitQueue = false
        +  }
        +}
        +
        +Glob.prototype._process = function (pattern, depth, index, cb_) {
        +  assert(this instanceof Glob)
        +
        +  var cb = function cb (er, res) {
        +    assert(this instanceof Glob)
        +    if (this.paused) {
        +      if (!this._processQueue) {
        +        this._processQueue = []
        +        this.once("resume", function () {
        +          var q = this._processQueue
        +          this._processQueue = null
        +          q.forEach(function (cb) { cb() })
        +        })
        +      }
        +      this._processQueue.push(cb_.bind(this, er, res))
        +    } else {
        +      cb_.call(this, er, res)
        +    }
        +  }.bind(this)
        +
        +  if (this.aborted) return cb()
        +
        +  if (depth > this.maxDepth) return cb()
        +
        +  // Get the first [n] parts of pattern that are all strings.
        +  var n = 0
        +  while (typeof pattern[n] === "string") {
        +    n ++
        +  }
        +  // now n is the index of the first one that is *not* a string.
        +
        +  // see if there's anything else
        +  var prefix
        +  switch (n) {
        +    // if not, then this is rather simple
        +    case pattern.length:
        +      prefix = pattern.join("/")
        +      this._stat(prefix, function (exists, isDir) {
        +        // either it's there, or it isn't.
        +        // nothing more to do, either way.
        +        if (exists) {
        +          if (prefix && isAbsolute(prefix) && !this.nomount) {
        +            if (prefix.charAt(0) === "/") {
        +              prefix = path.join(this.root, prefix)
        +            } else {
        +              prefix = path.resolve(this.root, prefix)
        +            }
        +          }
        +
        +          if (process.platform === "win32")
        +            prefix = prefix.replace(/\\/g, "/")
        +
        +          this.matches[index] = this.matches[index] || {}
        +          this.matches[index][prefix] = true
        +          this.emitMatch(prefix)
        +        }
        +        return cb()
        +      })
        +      return
        +
        +    case 0:
        +      // pattern *starts* with some non-trivial item.
        +      // going to readdir(cwd), but not include the prefix in matches.
        +      prefix = null
        +      break
        +
        +    default:
        +      // pattern has some string bits in the front.
        +      // whatever it starts with, whether that's "absolute" like /foo/bar,
        +      // or "relative" like "../baz"
        +      prefix = pattern.slice(0, n)
        +      prefix = prefix.join("/")
        +      break
        +  }
        +
        +  // get the list of entries.
        +  var read
        +  if (prefix === null) read = "."
        +  else if (isAbsolute(prefix) || isAbsolute(pattern.join("/"))) {
        +    if (!prefix || !isAbsolute(prefix)) {
        +      prefix = path.join("/", prefix)
        +    }
        +    read = prefix = path.resolve(prefix)
        +
        +    // if (process.platform === "win32")
        +    //   read = prefix = prefix.replace(/^[a-zA-Z]:|\\/g, "/")
        +
        +    this.log('absolute: ', prefix, this.root, pattern, read)
        +  } else {
        +    read = prefix
        +  }
        +
        +  this.log('readdir(%j)', read, this.cwd, this.root)
        +
        +  return this._readdir(read, function (er, entries) {
        +    if (er) {
        +      // not a directory!
        +      // this means that, whatever else comes after this, it can never match
        +      return cb()
        +    }
        +
        +    // globstar is special
        +    if (pattern[n] === minimatch.GLOBSTAR) {
        +      // test without the globstar, and with every child both below
        +      // and replacing the globstar.
        +      var s = [ pattern.slice(0, n).concat(pattern.slice(n + 1)) ]
        +      entries.forEach(function (e) {
        +        if (e.charAt(0) === "." && !this.dot) return
        +        // instead of the globstar
        +        s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1)))
        +        // below the globstar
        +        s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n)))
        +      }, this)
        +
        +      s = s.filter(function (pattern) {
        +        var key = gsKey(pattern)
        +        var seen = !this._globstars[key]
        +        this._globstars[key] = true
        +        return seen
        +      }, this)
        +
        +      if (!s.length)
        +        return cb()
        +
        +      // now asyncForEach over this
        +      var l = s.length
        +      , errState = null
        +      s.forEach(function (gsPattern) {
        +        this._process(gsPattern, depth + 1, index, function (er) {
        +          if (errState) return
        +          if (er) return cb(errState = er)
        +          if (--l <= 0) return cb()
        +        })
        +      }, this)
        +
        +      return
        +    }
        +
        +    // not a globstar
        +    // It will only match dot entries if it starts with a dot, or if
        +    // dot is set.  Stuff like @(.foo|.bar) isn't allowed.
        +    var pn = pattern[n]
        +    var rawGlob = pattern[n]._glob
        +    , dotOk = this.dot || rawGlob.charAt(0) === "."
        +
        +    entries = entries.filter(function (e) {
        +      return (e.charAt(0) !== "." || dotOk) &&
        +             e.match(pattern[n])
        +    })
        +
        +    // If n === pattern.length - 1, then there's no need for the extra stat
        +    // *unless* the user has specified "mark" or "stat" explicitly.
        +    // We know that they exist, since the readdir returned them.
        +    if (n === pattern.length - 1 &&
        +        !this.mark &&
        +        !this.stat) {
        +      entries.forEach(function (e) {
        +        if (prefix) {
        +          if (prefix !== "/") e = prefix + "/" + e
        +          else e = prefix + e
        +        }
        +        if (e.charAt(0) === "/" && !this.nomount) {
        +          e = path.join(this.root, e)
        +        }
        +
        +        if (process.platform === "win32")
        +          e = e.replace(/\\/g, "/")
        +
        +        this.matches[index] = this.matches[index] || {}
        +        this.matches[index][e] = true
        +        this.emitMatch(e)
        +      }, this)
        +      return cb.call(this)
        +    }
        +
        +
        +    // now test all the remaining entries as stand-ins for that part
        +    // of the pattern.
        +    var l = entries.length
        +    , errState = null
        +    if (l === 0) return cb() // no matches possible
        +    entries.forEach(function (e) {
        +      var p = pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1))
        +      this._process(p, depth + 1, index, function (er) {
        +        if (errState) return
        +        if (er) return cb(errState = er)
        +        if (--l === 0) return cb.call(this)
        +      })
        +    }, this)
        +  })
        +
        +}
        +
        +function gsKey (pattern) {
        +  return '**' + pattern.map(function (p) {
        +    return (p === minimatch.GLOBSTAR) ? '**' : (''+p)
        +  }).join('/')
        +}
        +
        +Glob.prototype._stat = function (f, cb) {
        +  assert(this instanceof Glob)
        +  var abs = f
        +  if (f.charAt(0) === "/") {
        +    abs = path.join(this.root, f)
        +  } else if (this.changedCwd) {
        +    abs = path.resolve(this.cwd, f)
        +  }
        +
        +  if (f.length > this.maxLength) {
        +    var er = new Error("Path name too long")
        +    er.code = "ENAMETOOLONG"
        +    er.path = f
        +    return this._afterStat(f, abs, cb, er)
        +  }
        +
        +  this.log('stat', [this.cwd, f, '=', abs])
        +
        +  if (!this.stat && this.cache.hasOwnProperty(f)) {
        +    var exists = this.cache[f]
        +    , isDir = exists && (Array.isArray(exists) || exists === 2)
        +    if (this.sync) return cb.call(this, !!exists, isDir)
        +    return process.nextTick(cb.bind(this, !!exists, isDir))
        +  }
        +
        +  var stat = this.statCache[abs]
        +  if (this.sync || stat) {
        +    var er
        +    try {
        +      stat = fs.statSync(abs)
        +    } catch (e) {
        +      er = e
        +    }
        +    this._afterStat(f, abs, cb, er, stat)
        +  } else {
        +    fs.stat(abs, this._afterStat.bind(this, f, abs, cb))
        +  }
        +}
        +
        +Glob.prototype._afterStat = function (f, abs, cb, er, stat) {
        +  var exists
        +  assert(this instanceof Glob)
        +
        +  if (abs.slice(-1) === "/" && stat && !stat.isDirectory()) {
        +    this.log("should be ENOTDIR, fake it")
        +
        +    er = new Error("ENOTDIR, not a directory '" + abs + "'")
        +    er.path = abs
        +    er.code = "ENOTDIR"
        +    stat = null
        +  }
        +
        +  var emit = !this.statCache[abs]
        +  this.statCache[abs] = stat
        +
        +  if (er || !stat) {
        +    exists = false
        +  } else {
        +    exists = stat.isDirectory() ? 2 : 1
        +    if (emit)
        +      this.emit('stat', f, stat)
        +  }
        +  this.cache[f] = this.cache[f] || exists
        +  cb.call(this, !!exists, exists === 2)
        +}
        +
        +Glob.prototype._readdir = function (f, cb) {
        +  assert(this instanceof Glob)
        +  var abs = f
        +  if (f.charAt(0) === "/") {
        +    abs = path.join(this.root, f)
        +  } else if (isAbsolute(f)) {
        +    abs = f
        +  } else if (this.changedCwd) {
        +    abs = path.resolve(this.cwd, f)
        +  }
        +
        +  if (f.length > this.maxLength) {
        +    var er = new Error("Path name too long")
        +    er.code = "ENAMETOOLONG"
        +    er.path = f
        +    return this._afterReaddir(f, abs, cb, er)
        +  }
        +
        +  this.log('readdir', [this.cwd, f, abs])
        +  if (this.cache.hasOwnProperty(f)) {
        +    var c = this.cache[f]
        +    if (Array.isArray(c)) {
        +      if (this.sync) return cb.call(this, null, c)
        +      return process.nextTick(cb.bind(this, null, c))
        +    }
        +
        +    if (!c || c === 1) {
        +      // either ENOENT or ENOTDIR
        +      var code = c ? "ENOTDIR" : "ENOENT"
        +      , er = new Error((c ? "Not a directory" : "Not found") + ": " + f)
        +      er.path = f
        +      er.code = code
        +      this.log(f, er)
        +      if (this.sync) return cb.call(this, er)
        +      return process.nextTick(cb.bind(this, er))
        +    }
        +
        +    // at this point, c === 2, meaning it's a dir, but we haven't
        +    // had to read it yet, or c === true, meaning it's *something*
        +    // but we don't have any idea what.  Need to read it, either way.
        +  }
        +
        +  if (this.sync) {
        +    var er, entries
        +    try {
        +      entries = fs.readdirSync(abs)
        +    } catch (e) {
        +      er = e
        +    }
        +    return this._afterReaddir(f, abs, cb, er, entries)
        +  }
        +
        +  fs.readdir(abs, this._afterReaddir.bind(this, f, abs, cb))
        +}
        +
        +Glob.prototype._afterReaddir = function (f, abs, cb, er, entries) {
        +  assert(this instanceof Glob)
        +  if (entries && !er) {
        +    this.cache[f] = entries
        +    // if we haven't asked to stat everything for suresies, then just
        +    // assume that everything in there exists, so we can avoid
        +    // having to stat it a second time.  This also gets us one step
        +    // further into ELOOP territory.
        +    if (!this.mark && !this.stat) {
        +      entries.forEach(function (e) {
        +        if (f === "/") e = f + e
        +        else e = f + "/" + e
        +        this.cache[e] = true
        +      }, this)
        +    }
        +
        +    return cb.call(this, er, entries)
        +  }
        +
        +  // now handle errors, and cache the information
        +  if (er) switch (er.code) {
        +    case "ENOTDIR": // totally normal. means it *does* exist.
        +      this.cache[f] = 1
        +      return cb.call(this, er)
        +    case "ENOENT": // not terribly unusual
        +    case "ELOOP":
        +    case "ENAMETOOLONG":
        +    case "UNKNOWN":
        +      this.cache[f] = false
        +      return cb.call(this, er)
        +    default: // some unusual error.  Treat as failure.
        +      this.cache[f] = false
        +      if (this.strict) this.emit("error", er)
        +      if (!this.silent) console.error("glob error", er)
        +      return cb.call(this, er)
        +  }
        +}
        +
        +var isAbsolute = process.platform === "win32" ? absWin : absUnix
        +
        +function absWin (p) {
        +  if (absUnix(p)) return true
        +  // pull off the device/UNC bit from a windows path.
        +  // from node's lib/path.js
        +  var splitDeviceRe =
        +      /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/
        +    , result = splitDeviceRe.exec(p)
        +    , device = result[1] || ''
        +    , isUnc = device && device.charAt(1) !== ':'
        +    , isAbsolute = !!result[2] || isUnc // UNC paths are always absolute
        +
        +  return isAbsolute
        +}
        +
        +function absUnix (p) {
        +  return p.charAt(0) === "/" || p === ""
        +}
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/.npmignore b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/.npmignore
        new file mode 100644
        index 0000000..c2658d7
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/.npmignore
        @@ -0,0 +1 @@
        +node_modules/
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/LICENSE b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/LICENSE
        new file mode 100644
        index 0000000..0c44ae7
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/LICENSE
        @@ -0,0 +1,27 @@
        +Copyright (c) Isaac Z. Schlueter ("Author")
        +All rights reserved.
        +
        +The BSD License
        +
        +Redistribution and use in source and binary forms, with or without
        +modification, are permitted provided that the following conditions
        +are met:
        +
        +1. Redistributions of source code must retain the above copyright
        +   notice, this list of conditions and the following disclaimer.
        +
        +2. Redistributions in binary form must reproduce the above copyright
        +   notice, this list of conditions and the following disclaimer in the
        +   documentation and/or other materials provided with the distribution.
        +
        +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
        +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
        +PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
        +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
        +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
        +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
        +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
        +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
        +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
        +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/README.md b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/README.md
        new file mode 100644
        index 0000000..eb1a109
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/README.md
        @@ -0,0 +1,26 @@
        +# graceful-fs
        +
        +graceful-fs functions as a drop-in replacement for the fs module,
        +making various improvements.
        +
        +The improvements are meant to normalize behavior across different
        +platforms and environments, and to make filesystem access more
        +resilient to errors.
        +
        +## Improvements over fs module
        +
        +graceful-fs:
        +
        +* Queues up `open` and `readdir` calls, and retries them once
        +  something closes if there is an EMFILE error from too many file
        +  descriptors.
        +* fixes `lchmod` for Node versions prior to 0.6.2.
        +* implements `fs.lutimes` if possible. Otherwise it becomes a noop.
        +* ignores `EINVAL` and `EPERM` errors in `chown`, `fchown` or
        +  `lchown` if the user isn't root.
        +* makes `lchmod` and `lchown` become noops, if not available.
        +* retries reading a file if `read` results in EAGAIN error.
        +
        +On Windows, it retries renaming a file for up to one second if `EACCESS`
        +or `EPERM` error occurs, likely because antivirus software has locked
        +the directory.
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/graceful-fs.js b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/graceful-fs.js
        new file mode 100644
        index 0000000..c84db91
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/graceful-fs.js
        @@ -0,0 +1,160 @@
        +// Monkey-patching the fs module.
        +// It's ugly, but there is simply no other way to do this.
        +var fs = module.exports = require('fs')
        +
        +var assert = require('assert')
        +
        +// fix up some busted stuff, mostly on windows and old nodes
        +require('./polyfills.js')
        +
        +// The EMFILE enqueuing stuff
        +
        +var util = require('util')
        +
        +function noop () {}
        +
        +var debug = noop
        +if (util.debuglog)
        +  debug = util.debuglog('gfs')
        +else if (/\bgfs\b/i.test(process.env.NODE_DEBUG || ''))
        +  debug = function() {
        +    var m = util.format.apply(util, arguments)
        +    m = 'GFS: ' + m.split(/\n/).join('\nGFS: ')
        +    console.error(m)
        +  }
        +
        +if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) {
        +  process.on('exit', function() {
        +    debug('fds', fds)
        +    debug(queue)
        +    assert.equal(queue.length, 0)
        +  })
        +}
        +
        +
        +var originalOpen = fs.open
        +fs.open = open
        +
        +function open(path, flags, mode, cb) {
        +  if (typeof mode === "function") cb = mode, mode = null
        +  if (typeof cb !== "function") cb = noop
        +  new OpenReq(path, flags, mode, cb)
        +}
        +
        +function OpenReq(path, flags, mode, cb) {
        +  this.path = path
        +  this.flags = flags
        +  this.mode = mode
        +  this.cb = cb
        +  Req.call(this)
        +}
        +
        +util.inherits(OpenReq, Req)
        +
        +OpenReq.prototype.process = function() {
        +  originalOpen.call(fs, this.path, this.flags, this.mode, this.done)
        +}
        +
        +var fds = {}
        +OpenReq.prototype.done = function(er, fd) {
        +  debug('open done', er, fd)
        +  if (fd)
        +    fds['fd' + fd] = this.path
        +  Req.prototype.done.call(this, er, fd)
        +}
        +
        +
        +var originalReaddir = fs.readdir
        +fs.readdir = readdir
        +
        +function readdir(path, cb) {
        +  if (typeof cb !== "function") cb = noop
        +  new ReaddirReq(path, cb)
        +}
        +
        +function ReaddirReq(path, cb) {
        +  this.path = path
        +  this.cb = cb
        +  Req.call(this)
        +}
        +
        +util.inherits(ReaddirReq, Req)
        +
        +ReaddirReq.prototype.process = function() {
        +  originalReaddir.call(fs, this.path, this.done)
        +}
        +
        +ReaddirReq.prototype.done = function(er, files) {
        +  if (files && files.sort)
        +    files = files.sort()
        +  Req.prototype.done.call(this, er, files)
        +  onclose()
        +}
        +
        +
        +var originalClose = fs.close
        +fs.close = close
        +
        +function close (fd, cb) {
        +  debug('close', fd)
        +  if (typeof cb !== "function") cb = noop
        +  delete fds['fd' + fd]
        +  originalClose.call(fs, fd, function(er) {
        +    onclose()
        +    cb(er)
        +  })
        +}
        +
        +
        +var originalCloseSync = fs.closeSync
        +fs.closeSync = closeSync
        +
        +function closeSync (fd) {
        +  try {
        +    return originalCloseSync(fd)
        +  } finally {
        +    onclose()
        +  }
        +}
        +
        +
        +// Req class
        +function Req () {
        +  // start processing
        +  this.done = this.done.bind(this)
        +  this.failures = 0
        +  this.process()
        +}
        +
        +Req.prototype.done = function (er, result) {
        +  var tryAgain = false
        +  if (er) {
        +    var code = er.code
        +    var tryAgain = code === "EMFILE"
        +    if (process.platform === "win32")
        +      tryAgain = tryAgain || code === "OK"
        +  }
        +
        +  if (tryAgain) {
        +    this.failures ++
        +    enqueue(this)
        +  } else {
        +    var cb = this.cb
        +    cb(er, result)
        +  }
        +}
        +
        +var queue = []
        +
        +function enqueue(req) {
        +  queue.push(req)
        +  debug('enqueue %d %s', queue.length, req.constructor.name, req)
        +}
        +
        +function onclose() {
        +  var req = queue.shift()
        +  if (req) {
        +    debug('process', req.constructor.name, req)
        +    req.process()
        +  }
        +}
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/package.json b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/package.json
        new file mode 100644
        index 0000000..f73fdd5
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/package.json
        @@ -0,0 +1,64 @@
        +{
        +  "author": {
        +    "name": "Isaac Z. Schlueter",
        +    "email": "i@izs.me",
        +    "url": "http://blog.izs.me"
        +  },
        +  "name": "graceful-fs",
        +  "description": "A drop-in replacement for fs, making various improvements.",
        +  "version": "2.0.3",
        +  "repository": {
        +    "type": "git",
        +    "url": "git://github.com/isaacs/node-graceful-fs.git"
        +  },
        +  "main": "graceful-fs.js",
        +  "engines": {
        +    "node": ">=0.4.0"
        +  },
        +  "directories": {
        +    "test": "test"
        +  },
        +  "scripts": {
        +    "test": "tap test/*.js"
        +  },
        +  "keywords": [
        +    "fs",
        +    "module",
        +    "reading",
        +    "retry",
        +    "retries",
        +    "queue",
        +    "error",
        +    "errors",
        +    "handling",
        +    "EMFILE",
        +    "EAGAIN",
        +    "EINVAL",
        +    "EPERM",
        +    "EACCESS"
        +  ],
        +  "license": "BSD",
        +  "bugs": {
        +    "url": "https://github.com/isaacs/node-graceful-fs/issues"
        +  },
        +  "homepage": "https://github.com/isaacs/node-graceful-fs",
        +  "_id": "graceful-fs@2.0.3",
        +  "dist": {
        +    "shasum": "7cd2cdb228a4a3f36e95efa6cc142de7d1a136d0",
        +    "tarball": "http://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz"
        +  },
        +  "_from": "graceful-fs@>=2.0.0 <2.1.0",
        +  "_npmVersion": "1.4.6",
        +  "_npmUser": {
        +    "name": "isaacs",
        +    "email": "i@izs.me"
        +  },
        +  "maintainers": [
        +    {
        +      "name": "isaacs",
        +      "email": "i@izs.me"
        +    }
        +  ],
        +  "_shasum": "7cd2cdb228a4a3f36e95efa6cc142de7d1a136d0",
        +  "_resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz"
        +}
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/polyfills.js b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/polyfills.js
        new file mode 100644
        index 0000000..afc83b3
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/polyfills.js
        @@ -0,0 +1,228 @@
        +var fs = require('fs')
        +var constants = require('constants')
        +
        +var origCwd = process.cwd
        +var cwd = null
        +process.cwd = function() {
        +  if (!cwd)
        +    cwd = origCwd.call(process)
        +  return cwd
        +}
        +var chdir = process.chdir
        +process.chdir = function(d) {
        +  cwd = null
        +  chdir.call(process, d)
        +}
        +
        +// (re-)implement some things that are known busted or missing.
        +
        +// lchmod, broken prior to 0.6.2
        +// back-port the fix here.
        +if (constants.hasOwnProperty('O_SYMLINK') &&
        +    process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
        +  fs.lchmod = function (path, mode, callback) {
        +    callback = callback || noop
        +    fs.open( path
        +           , constants.O_WRONLY | constants.O_SYMLINK
        +           , mode
        +           , function (err, fd) {
        +      if (err) {
        +        callback(err)
        +        return
        +      }
        +      // prefer to return the chmod error, if one occurs,
        +      // but still try to close, and report closing errors if they occur.
        +      fs.fchmod(fd, mode, function (err) {
        +        fs.close(fd, function(err2) {
        +          callback(err || err2)
        +        })
        +      })
        +    })
        +  }
        +
        +  fs.lchmodSync = function (path, mode) {
        +    var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode)
        +
        +    // prefer to return the chmod error, if one occurs,
        +    // but still try to close, and report closing errors if they occur.
        +    var err, err2
        +    try {
        +      var ret = fs.fchmodSync(fd, mode)
        +    } catch (er) {
        +      err = er
        +    }
        +    try {
        +      fs.closeSync(fd)
        +    } catch (er) {
        +      err2 = er
        +    }
        +    if (err || err2) throw (err || err2)
        +    return ret
        +  }
        +}
        +
        +
        +// lutimes implementation, or no-op
        +if (!fs.lutimes) {
        +  if (constants.hasOwnProperty("O_SYMLINK")) {
        +    fs.lutimes = function (path, at, mt, cb) {
        +      fs.open(path, constants.O_SYMLINK, function (er, fd) {
        +        cb = cb || noop
        +        if (er) return cb(er)
        +        fs.futimes(fd, at, mt, function (er) {
        +          fs.close(fd, function (er2) {
        +            return cb(er || er2)
        +          })
        +        })
        +      })
        +    }
        +
        +    fs.lutimesSync = function (path, at, mt) {
        +      var fd = fs.openSync(path, constants.O_SYMLINK)
        +        , err
        +        , err2
        +        , ret
        +
        +      try {
        +        var ret = fs.futimesSync(fd, at, mt)
        +      } catch (er) {
        +        err = er
        +      }
        +      try {
        +        fs.closeSync(fd)
        +      } catch (er) {
        +        err2 = er
        +      }
        +      if (err || err2) throw (err || err2)
        +      return ret
        +    }
        +
        +  } else if (fs.utimensat && constants.hasOwnProperty("AT_SYMLINK_NOFOLLOW")) {
        +    // maybe utimensat will be bound soonish?
        +    fs.lutimes = function (path, at, mt, cb) {
        +      fs.utimensat(path, at, mt, constants.AT_SYMLINK_NOFOLLOW, cb)
        +    }
        +
        +    fs.lutimesSync = function (path, at, mt) {
        +      return fs.utimensatSync(path, at, mt, constants.AT_SYMLINK_NOFOLLOW)
        +    }
        +
        +  } else {
        +    fs.lutimes = function (_a, _b, _c, cb) { process.nextTick(cb) }
        +    fs.lutimesSync = function () {}
        +  }
        +}
        +
        +
        +// https://github.com/isaacs/node-graceful-fs/issues/4
        +// Chown should not fail on einval or eperm if non-root.
        +
        +fs.chown = chownFix(fs.chown)
        +fs.fchown = chownFix(fs.fchown)
        +fs.lchown = chownFix(fs.lchown)
        +
        +fs.chownSync = chownFixSync(fs.chownSync)
        +fs.fchownSync = chownFixSync(fs.fchownSync)
        +fs.lchownSync = chownFixSync(fs.lchownSync)
        +
        +function chownFix (orig) {
        +  if (!orig) return orig
        +  return function (target, uid, gid, cb) {
        +    return orig.call(fs, target, uid, gid, function (er, res) {
        +      if (chownErOk(er)) er = null
        +      cb(er, res)
        +    })
        +  }
        +}
        +
        +function chownFixSync (orig) {
        +  if (!orig) return orig
        +  return function (target, uid, gid) {
        +    try {
        +      return orig.call(fs, target, uid, gid)
        +    } catch (er) {
        +      if (!chownErOk(er)) throw er
        +    }
        +  }
        +}
        +
        +function chownErOk (er) {
        +  // if there's no getuid, or if getuid() is something other than 0,
        +  // and the error is EINVAL or EPERM, then just ignore it.
        +  // This specific case is a silent failure in cp, install, tar,
        +  // and most other unix tools that manage permissions.
        +  // When running as root, or if other types of errors are encountered,
        +  // then it's strict.
        +  if (!er || (!process.getuid || process.getuid() !== 0)
        +      && (er.code === "EINVAL" || er.code === "EPERM")) return true
        +}
        +
        +
        +// if lchmod/lchown do not exist, then make them no-ops
        +if (!fs.lchmod) {
        +  fs.lchmod = function (path, mode, cb) {
        +    process.nextTick(cb)
        +  }
        +  fs.lchmodSync = function () {}
        +}
        +if (!fs.lchown) {
        +  fs.lchown = function (path, uid, gid, cb) {
        +    process.nextTick(cb)
        +  }
        +  fs.lchownSync = function () {}
        +}
        +
        +
        +
        +// on Windows, A/V software can lock the directory, causing this
        +// to fail with an EACCES or EPERM if the directory contains newly
        +// created files.  Try again on failure, for up to 1 second.
        +if (process.platform === "win32") {
        +  var rename_ = fs.rename
        +  fs.rename = function rename (from, to, cb) {
        +    var start = Date.now()
        +    rename_(from, to, function CB (er) {
        +      if (er
        +          && (er.code === "EACCES" || er.code === "EPERM")
        +          && Date.now() - start < 1000) {
        +        return rename_(from, to, CB)
        +      }
        +      cb(er)
        +    })
        +  }
        +}
        +
        +
        +// if read() returns EAGAIN, then just try it again.
        +var read = fs.read
        +fs.read = function (fd, buffer, offset, length, position, callback_) {
        +  var callback
        +  if (callback_ && typeof callback_ === 'function') {
        +    var eagCounter = 0
        +    callback = function (er, _, __) {
        +      if (er && er.code === 'EAGAIN' && eagCounter < 10) {
        +        eagCounter ++
        +        return read.call(fs, fd, buffer, offset, length, position, callback)
        +      }
        +      callback_.apply(this, arguments)
        +    }
        +  }
        +  return read.call(fs, fd, buffer, offset, length, position, callback)
        +}
        +
        +var readSync = fs.readSync
        +fs.readSync = function (fd, buffer, offset, length, position) {
        +  var eagCounter = 0
        +  while (true) {
        +    try {
        +      return readSync.call(fs, fd, buffer, offset, length, position)
        +    } catch (er) {
        +      if (er.code === 'EAGAIN' && eagCounter < 10) {
        +        eagCounter ++
        +        continue
        +      }
        +      throw er
        +    }
        +  }
        +}
        +
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/test/open.js b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/test/open.js
        new file mode 100644
        index 0000000..104f36b
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/test/open.js
        @@ -0,0 +1,39 @@
        +var test = require('tap').test
        +var fs = require('../graceful-fs.js')
        +
        +test('graceful fs is monkeypatched fs', function (t) {
        +  t.equal(fs, require('fs'))
        +  t.end()
        +})
        +
        +test('open an existing file works', function (t) {
        +  var fd = fs.openSync(__filename, 'r')
        +  fs.closeSync(fd)
        +  fs.open(__filename, 'r', function (er, fd) {
        +    if (er) throw er
        +    fs.close(fd, function (er) {
        +      if (er) throw er
        +      t.pass('works')
        +      t.end()
        +    })
        +  })
        +})
        +
        +test('open a non-existing file throws', function (t) {
        +  var er
        +  try {
        +    var fd = fs.openSync('this file does not exist', 'r')
        +  } catch (x) {
        +    er = x
        +  }
        +  t.ok(er, 'should throw')
        +  t.notOk(fd, 'should not get an fd')
        +  t.equal(er.code, 'ENOENT')
        +
        +  fs.open('neither does this file', 'r', function (er, fd) {
        +    t.ok(er, 'should throw')
        +    t.notOk(fd, 'should not get an fd')
        +    t.equal(er.code, 'ENOENT')
        +    t.end()
        +  })
        +})
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/test/readdir-sort.js b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/test/readdir-sort.js
        new file mode 100644
        index 0000000..aeaedf1
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/test/readdir-sort.js
        @@ -0,0 +1,21 @@
        +var test = require("tap").test
        +var fs = require("fs")
        +
        +var readdir = fs.readdir
        +fs.readdir = function(path, cb) {
        +  process.nextTick(function() {
        +    cb(null, ["b", "z", "a"])
        +  })
        +}
        +
        +var g = require("../")
        +
        +test("readdir reorder", function (t) {
        +  g.readdir("whatevers", function (er, files) {
        +    if (er)
        +      throw er
        +    console.error(files)
        +    t.same(files, [ "a", "b", "z" ])
        +    t.end()
        +  })
        +})
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/inherits/LICENSE b/node_modules/mocha/node_modules/glob/node_modules/inherits/LICENSE
        new file mode 100644
        index 0000000..dea3013
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/inherits/LICENSE
        @@ -0,0 +1,16 @@
        +The ISC License
        +
        +Copyright (c) Isaac Z. Schlueter
        +
        +Permission to use, copy, modify, and/or distribute this software for any
        +purpose with or without fee is hereby granted, provided that the above
        +copyright notice and this permission notice appear in all copies.
        +
        +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
        +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
        +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
        +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
        +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
        +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
        +PERFORMANCE OF THIS SOFTWARE.
        +
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/inherits/README.md b/node_modules/mocha/node_modules/glob/node_modules/inherits/README.md
        new file mode 100644
        index 0000000..b1c5665
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/inherits/README.md
        @@ -0,0 +1,42 @@
        +Browser-friendly inheritance fully compatible with standard node.js
        +[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor).
        +
        +This package exports standard `inherits` from node.js `util` module in
        +node environment, but also provides alternative browser-friendly
        +implementation through [browser
        +field](https://gist.github.com/shtylman/4339901). Alternative
        +implementation is a literal copy of standard one located in standalone
        +module to avoid requiring of `util`. It also has a shim for old
        +browsers with no `Object.create` support.
        +
        +While keeping you sure you are using standard `inherits`
        +implementation in node.js environment, it allows bundlers such as
        +[browserify](https://github.com/substack/node-browserify) to not
        +include full `util` package to your client code if all you need is
        +just `inherits` function. It worth, because browser shim for `util`
        +package is large and `inherits` is often the single function you need
        +from it.
        +
        +It's recommended to use this package instead of
        +`require('util').inherits` for any code that has chances to be used
        +not only in node.js but in browser too.
        +
        +## usage
        +
        +```js
        +var inherits = require('inherits');
        +// then use exactly as the standard one
        +```
        +
        +## note on version ~1.0
        +
        +Version ~1.0 had completely different motivation and is not compatible
        +neither with 2.0 nor with standard node.js `inherits`.
        +
        +If you are using version ~1.0 and planning to switch to ~2.0, be
        +careful:
        +
        +* new version uses `super_` instead of `super` for referencing
        +  superclass
        +* new version overwrites current prototype while old one preserves any
        +  existing fields on it
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/inherits/inherits.js b/node_modules/mocha/node_modules/glob/node_modules/inherits/inherits.js
        new file mode 100644
        index 0000000..29f5e24
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/inherits/inherits.js
        @@ -0,0 +1 @@
        +module.exports = require('util').inherits
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/inherits/inherits_browser.js b/node_modules/mocha/node_modules/glob/node_modules/inherits/inherits_browser.js
        new file mode 100644
        index 0000000..c1e78a7
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/inherits/inherits_browser.js
        @@ -0,0 +1,23 @@
        +if (typeof Object.create === 'function') {
        +  // implementation from standard node.js 'util' module
        +  module.exports = function inherits(ctor, superCtor) {
        +    ctor.super_ = superCtor
        +    ctor.prototype = Object.create(superCtor.prototype, {
        +      constructor: {
        +        value: ctor,
        +        enumerable: false,
        +        writable: true,
        +        configurable: true
        +      }
        +    });
        +  };
        +} else {
        +  // old school shim for old browsers
        +  module.exports = function inherits(ctor, superCtor) {
        +    ctor.super_ = superCtor
        +    var TempCtor = function () {}
        +    TempCtor.prototype = superCtor.prototype
        +    ctor.prototype = new TempCtor()
        +    ctor.prototype.constructor = ctor
        +  }
        +}
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/inherits/package.json b/node_modules/mocha/node_modules/glob/node_modules/inherits/package.json
        new file mode 100644
        index 0000000..bb245d2
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/inherits/package.json
        @@ -0,0 +1,50 @@
        +{
        +  "name": "inherits",
        +  "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()",
        +  "version": "2.0.1",
        +  "keywords": [
        +    "inheritance",
        +    "class",
        +    "klass",
        +    "oop",
        +    "object-oriented",
        +    "inherits",
        +    "browser",
        +    "browserify"
        +  ],
        +  "main": "./inherits.js",
        +  "browser": "./inherits_browser.js",
        +  "repository": {
        +    "type": "git",
        +    "url": "git://github.com/isaacs/inherits.git"
        +  },
        +  "license": "ISC",
        +  "scripts": {
        +    "test": "node test"
        +  },
        +  "bugs": {
        +    "url": "https://github.com/isaacs/inherits/issues"
        +  },
        +  "_id": "inherits@2.0.1",
        +  "dist": {
        +    "shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1",
        +    "tarball": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
        +  },
        +  "_from": "inherits@>=2.0.0 <3.0.0",
        +  "_npmVersion": "1.3.8",
        +  "_npmUser": {
        +    "name": "isaacs",
        +    "email": "i@izs.me"
        +  },
        +  "maintainers": [
        +    {
        +      "name": "isaacs",
        +      "email": "i@izs.me"
        +    }
        +  ],
        +  "directories": {},
        +  "_shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1",
        +  "_resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
        +  "readme": "ERROR: No README data found!",
        +  "homepage": "https://github.com/isaacs/inherits#readme"
        +}
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/inherits/test.js b/node_modules/mocha/node_modules/glob/node_modules/inherits/test.js
        new file mode 100644
        index 0000000..fc53012
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/inherits/test.js
        @@ -0,0 +1,25 @@
        +var inherits = require('./inherits.js')
        +var assert = require('assert')
        +
        +function test(c) {
        +  assert(c.constructor === Child)
        +  assert(c.constructor.super_ === Parent)
        +  assert(Object.getPrototypeOf(c) === Child.prototype)
        +  assert(Object.getPrototypeOf(Object.getPrototypeOf(c)) === Parent.prototype)
        +  assert(c instanceof Child)
        +  assert(c instanceof Parent)
        +}
        +
        +function Child() {
        +  Parent.call(this)
        +  test(this)
        +}
        +
        +function Parent() {}
        +
        +inherits(Child, Parent)
        +
        +var c = new Child
        +test(c)
        +
        +console.log('ok')
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/.npmignore b/node_modules/mocha/node_modules/glob/node_modules/minimatch/.npmignore
        new file mode 100644
        index 0000000..3c3629e
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/.npmignore
        @@ -0,0 +1 @@
        +node_modules
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/LICENSE b/node_modules/mocha/node_modules/glob/node_modules/minimatch/LICENSE
        new file mode 100644
        index 0000000..05a4010
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/LICENSE
        @@ -0,0 +1,23 @@
        +Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
        +All rights reserved.
        +
        +Permission is hereby granted, free of charge, to any person
        +obtaining a copy of this software and associated documentation
        +files (the "Software"), to deal in the Software without
        +restriction, including without limitation the rights to use,
        +copy, modify, merge, publish, distribute, sublicense, and/or sell
        +copies of the Software, and to permit persons to whom the
        +Software is furnished to do so, subject to the following
        +conditions:
        +
        +The above copyright notice and this permission notice shall be
        +included in all copies or substantial portions of the Software.
        +
        +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
        +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
        +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
        +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
        +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
        +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
        +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
        +OTHER DEALINGS IN THE SOFTWARE.
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/README.md b/node_modules/mocha/node_modules/glob/node_modules/minimatch/README.md
        new file mode 100644
        index 0000000..978268e
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/README.md
        @@ -0,0 +1,218 @@
        +# minimatch
        +
        +A minimal matching utility.
        +
        +[![Build Status](https://secure.travis-ci.org/isaacs/minimatch.png)](http://travis-ci.org/isaacs/minimatch)
        +
        +
        +This is the matching library used internally by npm.
        +
        +Eventually, it will replace the C binding in node-glob.
        +
        +It works by converting glob expressions into JavaScript `RegExp`
        +objects.
        +
        +## Usage
        +
        +```javascript
        +var minimatch = require("minimatch")
        +
        +minimatch("bar.foo", "*.foo") // true!
        +minimatch("bar.foo", "*.bar") // false!
        +minimatch("bar.foo", "*.+(bar|foo)", { debug: true }) // true, and noisy!
        +```
        +
        +## Features
        +
        +Supports these glob features:
        +
        +* Brace Expansion
        +* Extended glob matching
        +* "Globstar" `**` matching
        +
        +See:
        +
        +* `man sh`
        +* `man bash`
        +* `man 3 fnmatch`
        +* `man 5 gitignore`
        +
        +## Minimatch Class
        +
        +Create a minimatch object by instanting the `minimatch.Minimatch` class.
        +
        +```javascript
        +var Minimatch = require("minimatch").Minimatch
        +var mm = new Minimatch(pattern, options)
        +```
        +
        +### Properties
        +
        +* `pattern` The original pattern the minimatch object represents.
        +* `options` The options supplied to the constructor.
        +* `set` A 2-dimensional array of regexp or string expressions.
        +  Each row in the
        +  array corresponds to a brace-expanded pattern.  Each item in the row
        +  corresponds to a single path-part.  For example, the pattern
        +  `{a,b/c}/d` would expand to a set of patterns like:
        +
        +        [ [ a, d ]
        +        , [ b, c, d ] ]
        +
        +    If a portion of the pattern doesn't have any "magic" in it
        +    (that is, it's something like `"foo"` rather than `fo*o?`), then it
        +    will be left as a string rather than converted to a regular
        +    expression.
        +
        +* `regexp` Created by the `makeRe` method.  A single regular expression
        +  expressing the entire pattern.  This is useful in cases where you wish
        +  to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled.
        +* `negate` True if the pattern is negated.
        +* `comment` True if the pattern is a comment.
        +* `empty` True if the pattern is `""`.
        +
        +### Methods
        +
        +* `makeRe` Generate the `regexp` member if necessary, and return it.
        +  Will return `false` if the pattern is invalid.
        +* `match(fname)` Return true if the filename matches the pattern, or
        +  false otherwise.
        +* `matchOne(fileArray, patternArray, partial)` Take a `/`-split
        +  filename, and match it against a single row in the `regExpSet`.  This
        +  method is mainly for internal use, but is exposed so that it can be
        +  used by a glob-walker that needs to avoid excessive filesystem calls.
        +
        +All other methods are internal, and will be called as necessary.
        +
        +## Functions
        +
        +The top-level exported function has a `cache` property, which is an LRU
        +cache set to store 100 items.  So, calling these methods repeatedly
        +with the same pattern and options will use the same Minimatch object,
        +saving the cost of parsing it multiple times.
        +
        +### minimatch(path, pattern, options)
        +
        +Main export.  Tests a path against the pattern using the options.
        +
        +```javascript
        +var isJS = minimatch(file, "*.js", { matchBase: true })
        +```
        +
        +### minimatch.filter(pattern, options)
        +
        +Returns a function that tests its
        +supplied argument, suitable for use with `Array.filter`.  Example:
        +
        +```javascript
        +var javascripts = fileList.filter(minimatch.filter("*.js", {matchBase: true}))
        +```
        +
        +### minimatch.match(list, pattern, options)
        +
        +Match against the list of
        +files, in the style of fnmatch or glob.  If nothing is matched, and
        +options.nonull is set, then return a list containing the pattern itself.
        +
        +```javascript
        +var javascripts = minimatch.match(fileList, "*.js", {matchBase: true}))
        +```
        +
        +### minimatch.makeRe(pattern, options)
        +
        +Make a regular expression object from the pattern.
        +
        +## Options
        +
        +All options are `false` by default.
        +
        +### debug
        +
        +Dump a ton of stuff to stderr.
        +
        +### nobrace
        +
        +Do not expand `{a,b}` and `{1..3}` brace sets.
        +
        +### noglobstar
        +
        +Disable `**` matching against multiple folder names.
        +
        +### dot
        +
        +Allow patterns to match filenames starting with a period, even if
        +the pattern does not explicitly have a period in that spot.
        +
        +Note that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot`
        +is set.
        +
        +### noext
        +
        +Disable "extglob" style patterns like `+(a|b)`.
        +
        +### nocase
        +
        +Perform a case-insensitive match.
        +
        +### nonull
        +
        +When a match is not found by `minimatch.match`, return a list containing
        +the pattern itself.  When set, an empty list is returned if there are
        +no matches.
        +
        +### matchBase
        +
        +If set, then patterns without slashes will be matched
        +against the basename of the path if it contains slashes.  For example,
        +`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`.
        +
        +### nocomment
        +
        +Suppress the behavior of treating `#` at the start of a pattern as a
        +comment.
        +
        +### nonegate
        +
        +Suppress the behavior of treating a leading `!` character as negation.
        +
        +### flipNegate
        +
        +Returns from negate expressions the same as if they were not negated.
        +(Ie, true on a hit, false on a miss.)
        +
        +
        +## Comparisons to other fnmatch/glob implementations
        +
        +While strict compliance with the existing standards is a worthwhile
        +goal, some discrepancies exist between minimatch and other
        +implementations, and are intentional.
        +
        +If the pattern starts with a `!` character, then it is negated.  Set the
        +`nonegate` flag to suppress this behavior, and treat leading `!`
        +characters normally.  This is perhaps relevant if you wish to start the
        +pattern with a negative extglob pattern like `!(a|B)`.  Multiple `!`
        +characters at the start of a pattern will negate the pattern multiple
        +times.
        +
        +If a pattern starts with `#`, then it is treated as a comment, and
        +will not match anything.  Use `\#` to match a literal `#` at the
        +start of a line, or set the `nocomment` flag to suppress this behavior.
        +
        +The double-star character `**` is supported by default, unless the
        +`noglobstar` flag is set.  This is supported in the manner of bsdglob
        +and bash 4.1, where `**` only has special significance if it is the only
        +thing in a path part.  That is, `a/**/b` will match `a/x/y/b`, but
        +`a/**b` will not.
        +
        +If an escaped pattern has no matches, and the `nonull` flag is set,
        +then minimatch.match returns the pattern as-provided, rather than
        +interpreting the character escapes.  For example,
        +`minimatch.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
        +`"*a?"`.  This is akin to setting the `nullglob` option in bash, except
        +that it does not resolve escaped pattern characters.
        +
        +If brace expansion is not disabled, then it is performed before any
        +other interpretation of the glob pattern.  Thus, a pattern like
        +`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
        +**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
        +checked for validity.  Since those two are valid, matching proceeds.
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/minimatch.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/minimatch.js
        new file mode 100644
        index 0000000..c633f89
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/minimatch.js
        @@ -0,0 +1,1055 @@
        +;(function (require, exports, module, platform) {
        +
        +if (module) module.exports = minimatch
        +else exports.minimatch = minimatch
        +
        +if (!require) {
        +  require = function (id) {
        +    switch (id) {
        +      case "sigmund": return function sigmund (obj) {
        +        return JSON.stringify(obj)
        +      }
        +      case "path": return { basename: function (f) {
        +        f = f.split(/[\/\\]/)
        +        var e = f.pop()
        +        if (!e) e = f.pop()
        +        return e
        +      }}
        +      case "lru-cache": return function LRUCache () {
        +        // not quite an LRU, but still space-limited.
        +        var cache = {}
        +        var cnt = 0
        +        this.set = function (k, v) {
        +          cnt ++
        +          if (cnt >= 100) cache = {}
        +          cache[k] = v
        +        }
        +        this.get = function (k) { return cache[k] }
        +      }
        +    }
        +  }
        +}
        +
        +minimatch.Minimatch = Minimatch
        +
        +var LRU = require("lru-cache")
        +  , cache = minimatch.cache = new LRU({max: 100})
        +  , GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {}
        +  , sigmund = require("sigmund")
        +
        +var path = require("path")
        +  // any single thing other than /
        +  // don't need to escape / when using new RegExp()
        +  , qmark = "[^/]"
        +
        +  // * => any number of characters
        +  , star = qmark + "*?"
        +
        +  // ** when dots are allowed.  Anything goes, except .. and .
        +  // not (^ or / followed by one or two dots followed by $ or /),
        +  // followed by anything, any number of times.
        +  , twoStarDot = "(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?"
        +
        +  // not a ^ or / followed by a dot,
        +  // followed by anything, any number of times.
        +  , twoStarNoDot = "(?:(?!(?:\\\/|^)\\.).)*?"
        +
        +  // characters that need to be escaped in RegExp.
        +  , reSpecials = charSet("().*{}+?[]^$\\!")
        +
        +// "abc" -> { a:true, b:true, c:true }
        +function charSet (s) {
        +  return s.split("").reduce(function (set, c) {
        +    set[c] = true
        +    return set
        +  }, {})
        +}
        +
        +// normalizes slashes.
        +var slashSplit = /\/+/
        +
        +minimatch.filter = filter
        +function filter (pattern, options) {
        +  options = options || {}
        +  return function (p, i, list) {
        +    return minimatch(p, pattern, options)
        +  }
        +}
        +
        +function ext (a, b) {
        +  a = a || {}
        +  b = b || {}
        +  var t = {}
        +  Object.keys(b).forEach(function (k) {
        +    t[k] = b[k]
        +  })
        +  Object.keys(a).forEach(function (k) {
        +    t[k] = a[k]
        +  })
        +  return t
        +}
        +
        +minimatch.defaults = function (def) {
        +  if (!def || !Object.keys(def).length) return minimatch
        +
        +  var orig = minimatch
        +
        +  var m = function minimatch (p, pattern, options) {
        +    return orig.minimatch(p, pattern, ext(def, options))
        +  }
        +
        +  m.Minimatch = function Minimatch (pattern, options) {
        +    return new orig.Minimatch(pattern, ext(def, options))
        +  }
        +
        +  return m
        +}
        +
        +Minimatch.defaults = function (def) {
        +  if (!def || !Object.keys(def).length) return Minimatch
        +  return minimatch.defaults(def).Minimatch
        +}
        +
        +
        +function minimatch (p, pattern, options) {
        +  if (typeof pattern !== "string") {
        +    throw new TypeError("glob pattern string required")
        +  }
        +
        +  if (!options) options = {}
        +
        +  // shortcut: comments match nothing.
        +  if (!options.nocomment && pattern.charAt(0) === "#") {
        +    return false
        +  }
        +
        +  // "" only matches ""
        +  if (pattern.trim() === "") return p === ""
        +
        +  return new Minimatch(pattern, options).match(p)
        +}
        +
        +function Minimatch (pattern, options) {
        +  if (!(this instanceof Minimatch)) {
        +    return new Minimatch(pattern, options, cache)
        +  }
        +
        +  if (typeof pattern !== "string") {
        +    throw new TypeError("glob pattern string required")
        +  }
        +
        +  if (!options) options = {}
        +  pattern = pattern.trim()
        +
        +  // windows: need to use /, not \
        +  // On other platforms, \ is a valid (albeit bad) filename char.
        +  if (platform === "win32") {
        +    pattern = pattern.split("\\").join("/")
        +  }
        +
        +  // lru storage.
        +  // these things aren't particularly big, but walking down the string
        +  // and turning it into a regexp can get pretty costly.
        +  var cacheKey = pattern + "\n" + sigmund(options)
        +  var cached = minimatch.cache.get(cacheKey)
        +  if (cached) return cached
        +  minimatch.cache.set(cacheKey, this)
        +
        +  this.options = options
        +  this.set = []
        +  this.pattern = pattern
        +  this.regexp = null
        +  this.negate = false
        +  this.comment = false
        +  this.empty = false
        +
        +  // make the set of regexps etc.
        +  this.make()
        +}
        +
        +Minimatch.prototype.debug = function() {}
        +
        +Minimatch.prototype.make = make
        +function make () {
        +  // don't do it more than once.
        +  if (this._made) return
        +
        +  var pattern = this.pattern
        +  var options = this.options
        +
        +  // empty patterns and comments match nothing.
        +  if (!options.nocomment && pattern.charAt(0) === "#") {
        +    this.comment = true
        +    return
        +  }
        +  if (!pattern) {
        +    this.empty = true
        +    return
        +  }
        +
        +  // step 1: figure out negation, etc.
        +  this.parseNegate()
        +
        +  // step 2: expand braces
        +  var set = this.globSet = this.braceExpand()
        +
        +  if (options.debug) this.debug = console.error
        +
        +  this.debug(this.pattern, set)
        +
        +  // step 3: now we have a set, so turn each one into a series of path-portion
        +  // matching patterns.
        +  // These will be regexps, except in the case of "**", which is
        +  // set to the GLOBSTAR object for globstar behavior,
        +  // and will not contain any / characters
        +  set = this.globParts = set.map(function (s) {
        +    return s.split(slashSplit)
        +  })
        +
        +  this.debug(this.pattern, set)
        +
        +  // glob --> regexps
        +  set = set.map(function (s, si, set) {
        +    return s.map(this.parse, this)
        +  }, this)
        +
        +  this.debug(this.pattern, set)
        +
        +  // filter out everything that didn't compile properly.
        +  set = set.filter(function (s) {
        +    return -1 === s.indexOf(false)
        +  })
        +
        +  this.debug(this.pattern, set)
        +
        +  this.set = set
        +}
        +
        +Minimatch.prototype.parseNegate = parseNegate
        +function parseNegate () {
        +  var pattern = this.pattern
        +    , negate = false
        +    , options = this.options
        +    , negateOffset = 0
        +
        +  if (options.nonegate) return
        +
        +  for ( var i = 0, l = pattern.length
        +      ; i < l && pattern.charAt(i) === "!"
        +      ; i ++) {
        +    negate = !negate
        +    negateOffset ++
        +  }
        +
        +  if (negateOffset) this.pattern = pattern.substr(negateOffset)
        +  this.negate = negate
        +}
        +
        +// Brace expansion:
        +// a{b,c}d -> abd acd
        +// a{b,}c -> abc ac
        +// a{0..3}d -> a0d a1d a2d a3d
        +// a{b,c{d,e}f}g -> abg acdfg acefg
        +// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
        +//
        +// Invalid sets are not expanded.
        +// a{2..}b -> a{2..}b
        +// a{b}c -> a{b}c
        +minimatch.braceExpand = function (pattern, options) {
        +  return new Minimatch(pattern, options).braceExpand()
        +}
        +
        +Minimatch.prototype.braceExpand = braceExpand
        +function braceExpand (pattern, options) {
        +  options = options || this.options
        +  pattern = typeof pattern === "undefined"
        +    ? this.pattern : pattern
        +
        +  if (typeof pattern === "undefined") {
        +    throw new Error("undefined pattern")
        +  }
        +
        +  if (options.nobrace ||
        +      !pattern.match(/\{.*\}/)) {
        +    // shortcut. no need to expand.
        +    return [pattern]
        +  }
        +
        +  var escaping = false
        +
        +  // examples and comments refer to this crazy pattern:
        +  // a{b,c{d,e},{f,g}h}x{y,z}
        +  // expected:
        +  // abxy
        +  // abxz
        +  // acdxy
        +  // acdxz
        +  // acexy
        +  // acexz
        +  // afhxy
        +  // afhxz
        +  // aghxy
        +  // aghxz
        +
        +  // everything before the first \{ is just a prefix.
        +  // So, we pluck that off, and work with the rest,
        +  // and then prepend it to everything we find.
        +  if (pattern.charAt(0) !== "{") {
        +    this.debug(pattern)
        +    var prefix = null
        +    for (var i = 0, l = pattern.length; i < l; i ++) {
        +      var c = pattern.charAt(i)
        +      this.debug(i, c)
        +      if (c === "\\") {
        +        escaping = !escaping
        +      } else if (c === "{" && !escaping) {
        +        prefix = pattern.substr(0, i)
        +        break
        +      }
        +    }
        +
        +    // actually no sets, all { were escaped.
        +    if (prefix === null) {
        +      this.debug("no sets")
        +      return [pattern]
        +    }
        +
        +   var tail = braceExpand.call(this, pattern.substr(i), options)
        +    return tail.map(function (t) {
        +      return prefix + t
        +    })
        +  }
        +
        +  // now we have something like:
        +  // {b,c{d,e},{f,g}h}x{y,z}
        +  // walk through the set, expanding each part, until
        +  // the set ends.  then, we'll expand the suffix.
        +  // If the set only has a single member, then'll put the {} back
        +
        +  // first, handle numeric sets, since they're easier
        +  var numset = pattern.match(/^\{(-?[0-9]+)\.\.(-?[0-9]+)\}/)
        +  if (numset) {
        +    this.debug("numset", numset[1], numset[2])
        +    var suf = braceExpand.call(this, pattern.substr(numset[0].length), options)
        +      , start = +numset[1]
        +      , end = +numset[2]
        +      , inc = start > end ? -1 : 1
        +      , set = []
        +    for (var i = start; i != (end + inc); i += inc) {
        +      // append all the suffixes
        +      for (var ii = 0, ll = suf.length; ii < ll; ii ++) {
        +        set.push(i + suf[ii])
        +      }
        +    }
        +    return set
        +  }
        +
        +  // ok, walk through the set
        +  // We hope, somewhat optimistically, that there
        +  // will be a } at the end.
        +  // If the closing brace isn't found, then the pattern is
        +  // interpreted as braceExpand("\\" + pattern) so that
        +  // the leading \{ will be interpreted literally.
        +  var i = 1 // skip the \{
        +    , depth = 1
        +    , set = []
        +    , member = ""
        +    , sawEnd = false
        +    , escaping = false
        +
        +  function addMember () {
        +    set.push(member)
        +    member = ""
        +  }
        +
        +  this.debug("Entering for")
        +  FOR: for (i = 1, l = pattern.length; i < l; i ++) {
        +    var c = pattern.charAt(i)
        +    this.debug("", i, c)
        +
        +    if (escaping) {
        +      escaping = false
        +      member += "\\" + c
        +    } else {
        +      switch (c) {
        +        case "\\":
        +          escaping = true
        +          continue
        +
        +        case "{":
        +          depth ++
        +          member += "{"
        +          continue
        +
        +        case "}":
        +          depth --
        +          // if this closes the actual set, then we're done
        +          if (depth === 0) {
        +            addMember()
        +            // pluck off the close-brace
        +            i ++
        +            break FOR
        +          } else {
        +            member += c
        +            continue
        +          }
        +
        +        case ",":
        +          if (depth === 1) {
        +            addMember()
        +          } else {
        +            member += c
        +          }
        +          continue
        +
        +        default:
        +          member += c
        +          continue
        +      } // switch
        +    } // else
        +  } // for
        +
        +  // now we've either finished the set, and the suffix is
        +  // pattern.substr(i), or we have *not* closed the set,
        +  // and need to escape the leading brace
        +  if (depth !== 0) {
        +    this.debug("didn't close", pattern)
        +    return braceExpand.call(this, "\\" + pattern, options)
        +  }
        +
        +  // x{y,z} -> ["xy", "xz"]
        +  this.debug("set", set)
        +  this.debug("suffix", pattern.substr(i))
        +  var suf = braceExpand.call(this, pattern.substr(i), options)
        +  // ["b", "c{d,e}","{f,g}h"] ->
        +  //   [["b"], ["cd", "ce"], ["fh", "gh"]]
        +  var addBraces = set.length === 1
        +  this.debug("set pre-expanded", set)
        +  set = set.map(function (p) {
        +    return braceExpand.call(this, p, options)
        +  }, this)
        +  this.debug("set expanded", set)
        +
        +
        +  // [["b"], ["cd", "ce"], ["fh", "gh"]] ->
        +  //   ["b", "cd", "ce", "fh", "gh"]
        +  set = set.reduce(function (l, r) {
        +    return l.concat(r)
        +  })
        +
        +  if (addBraces) {
        +    set = set.map(function (s) {
        +      return "{" + s + "}"
        +    })
        +  }
        +
        +  // now attach the suffixes.
        +  var ret = []
        +  for (var i = 0, l = set.length; i < l; i ++) {
        +    for (var ii = 0, ll = suf.length; ii < ll; ii ++) {
        +      ret.push(set[i] + suf[ii])
        +    }
        +  }
        +  return ret
        +}
        +
        +// parse a component of the expanded set.
        +// At this point, no pattern may contain "/" in it
        +// so we're going to return a 2d array, where each entry is the full
        +// pattern, split on '/', and then turned into a regular expression.
        +// A regexp is made at the end which joins each array with an
        +// escaped /, and another full one which joins each regexp with |.
        +//
        +// Following the lead of Bash 4.1, note that "**" only has special meaning
        +// when it is the *only* thing in a path portion.  Otherwise, any series
        +// of * is equivalent to a single *.  Globstar behavior is enabled by
        +// default, and can be disabled by setting options.noglobstar.
        +Minimatch.prototype.parse = parse
        +var SUBPARSE = {}
        +function parse (pattern, isSub) {
        +  var options = this.options
        +
        +  // shortcuts
        +  if (!options.noglobstar && pattern === "**") return GLOBSTAR
        +  if (pattern === "") return ""
        +
        +  var re = ""
        +    , hasMagic = !!options.nocase
        +    , escaping = false
        +    // ? => one single character
        +    , patternListStack = []
        +    , plType
        +    , stateChar
        +    , inClass = false
        +    , reClassStart = -1
        +    , classStart = -1
        +    // . and .. never match anything that doesn't start with .,
        +    // even when options.dot is set.
        +    , patternStart = pattern.charAt(0) === "." ? "" // anything
        +      // not (start or / followed by . or .. followed by / or end)
        +      : options.dot ? "(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))"
        +      : "(?!\\.)"
        +    , self = this
        +
        +  function clearStateChar () {
        +    if (stateChar) {
        +      // we had some state-tracking character
        +      // that wasn't consumed by this pass.
        +      switch (stateChar) {
        +        case "*":
        +          re += star
        +          hasMagic = true
        +          break
        +        case "?":
        +          re += qmark
        +          hasMagic = true
        +          break
        +        default:
        +          re += "\\"+stateChar
        +          break
        +      }
        +      self.debug('clearStateChar %j %j', stateChar, re)
        +      stateChar = false
        +    }
        +  }
        +
        +  for ( var i = 0, len = pattern.length, c
        +      ; (i < len) && (c = pattern.charAt(i))
        +      ; i ++ ) {
        +
        +    this.debug("%s\t%s %s %j", pattern, i, re, c)
        +
        +    // skip over any that are escaped.
        +    if (escaping && reSpecials[c]) {
        +      re += "\\" + c
        +      escaping = false
        +      continue
        +    }
        +
        +    SWITCH: switch (c) {
        +      case "/":
        +        // completely not allowed, even escaped.
        +        // Should already be path-split by now.
        +        return false
        +
        +      case "\\":
        +        clearStateChar()
        +        escaping = true
        +        continue
        +
        +      // the various stateChar values
        +      // for the "extglob" stuff.
        +      case "?":
        +      case "*":
        +      case "+":
        +      case "@":
        +      case "!":
        +        this.debug("%s\t%s %s %j <-- stateChar", pattern, i, re, c)
        +
        +        // all of those are literals inside a class, except that
        +        // the glob [!a] means [^a] in regexp
        +        if (inClass) {
        +          this.debug('  in class')
        +          if (c === "!" && i === classStart + 1) c = "^"
        +          re += c
        +          continue
        +        }
        +
        +        // if we already have a stateChar, then it means
        +        // that there was something like ** or +? in there.
        +        // Handle the stateChar, then proceed with this one.
        +        self.debug('call clearStateChar %j', stateChar)
        +        clearStateChar()
        +        stateChar = c
        +        // if extglob is disabled, then +(asdf|foo) isn't a thing.
        +        // just clear the statechar *now*, rather than even diving into
        +        // the patternList stuff.
        +        if (options.noext) clearStateChar()
        +        continue
        +
        +      case "(":
        +        if (inClass) {
        +          re += "("
        +          continue
        +        }
        +
        +        if (!stateChar) {
        +          re += "\\("
        +          continue
        +        }
        +
        +        plType = stateChar
        +        patternListStack.push({ type: plType
        +                              , start: i - 1
        +                              , reStart: re.length })
        +        // negation is (?:(?!js)[^/]*)
        +        re += stateChar === "!" ? "(?:(?!" : "(?:"
        +        this.debug('plType %j %j', stateChar, re)
        +        stateChar = false
        +        continue
        +
        +      case ")":
        +        if (inClass || !patternListStack.length) {
        +          re += "\\)"
        +          continue
        +        }
        +
        +        clearStateChar()
        +        hasMagic = true
        +        re += ")"
        +        plType = patternListStack.pop().type
        +        // negation is (?:(?!js)[^/]*)
        +        // The others are (?:)
        +        switch (plType) {
        +          case "!":
        +            re += "[^/]*?)"
        +            break
        +          case "?":
        +          case "+":
        +          case "*": re += plType
        +          case "@": break // the default anyway
        +        }
        +        continue
        +
        +      case "|":
        +        if (inClass || !patternListStack.length || escaping) {
        +          re += "\\|"
        +          escaping = false
        +          continue
        +        }
        +
        +        clearStateChar()
        +        re += "|"
        +        continue
        +
        +      // these are mostly the same in regexp and glob
        +      case "[":
        +        // swallow any state-tracking char before the [
        +        clearStateChar()
        +
        +        if (inClass) {
        +          re += "\\" + c
        +          continue
        +        }
        +
        +        inClass = true
        +        classStart = i
        +        reClassStart = re.length
        +        re += c
        +        continue
        +
        +      case "]":
        +        //  a right bracket shall lose its special
        +        //  meaning and represent itself in
        +        //  a bracket expression if it occurs
        +        //  first in the list.  -- POSIX.2 2.8.3.2
        +        if (i === classStart + 1 || !inClass) {
        +          re += "\\" + c
        +          escaping = false
        +          continue
        +        }
        +
        +        // finish up the class.
        +        hasMagic = true
        +        inClass = false
        +        re += c
        +        continue
        +
        +      default:
        +        // swallow any state char that wasn't consumed
        +        clearStateChar()
        +
        +        if (escaping) {
        +          // no need
        +          escaping = false
        +        } else if (reSpecials[c]
        +                   && !(c === "^" && inClass)) {
        +          re += "\\"
        +        }
        +
        +        re += c
        +
        +    } // switch
        +  } // for
        +
        +
        +  // handle the case where we left a class open.
        +  // "[abc" is valid, equivalent to "\[abc"
        +  if (inClass) {
        +    // split where the last [ was, and escape it
        +    // this is a huge pita.  We now have to re-walk
        +    // the contents of the would-be class to re-translate
        +    // any characters that were passed through as-is
        +    var cs = pattern.substr(classStart + 1)
        +      , sp = this.parse(cs, SUBPARSE)
        +    re = re.substr(0, reClassStart) + "\\[" + sp[0]
        +    hasMagic = hasMagic || sp[1]
        +  }
        +
        +  // handle the case where we had a +( thing at the *end*
        +  // of the pattern.
        +  // each pattern list stack adds 3 chars, and we need to go through
        +  // and escape any | chars that were passed through as-is for the regexp.
        +  // Go through and escape them, taking care not to double-escape any
        +  // | chars that were already escaped.
        +  var pl
        +  while (pl = patternListStack.pop()) {
        +    var tail = re.slice(pl.reStart + 3)
        +    // maybe some even number of \, then maybe 1 \, followed by a |
        +    tail = tail.replace(/((?:\\{2})*)(\\?)\|/g, function (_, $1, $2) {
        +      if (!$2) {
        +        // the | isn't already escaped, so escape it.
        +        $2 = "\\"
        +      }
        +
        +      // need to escape all those slashes *again*, without escaping the
        +      // one that we need for escaping the | character.  As it works out,
        +      // escaping an even number of slashes can be done by simply repeating
        +      // it exactly after itself.  That's why this trick works.
        +      //
        +      // I am sorry that you have to see this.
        +      return $1 + $1 + $2 + "|"
        +    })
        +
        +    this.debug("tail=%j\n   %s", tail, tail)
        +    var t = pl.type === "*" ? star
        +          : pl.type === "?" ? qmark
        +          : "\\" + pl.type
        +
        +    hasMagic = true
        +    re = re.slice(0, pl.reStart)
        +       + t + "\\("
        +       + tail
        +  }
        +
        +  // handle trailing things that only matter at the very end.
        +  clearStateChar()
        +  if (escaping) {
        +    // trailing \\
        +    re += "\\\\"
        +  }
        +
        +  // only need to apply the nodot start if the re starts with
        +  // something that could conceivably capture a dot
        +  var addPatternStart = false
        +  switch (re.charAt(0)) {
        +    case ".":
        +    case "[":
        +    case "(": addPatternStart = true
        +  }
        +
        +  // if the re is not "" at this point, then we need to make sure
        +  // it doesn't match against an empty path part.
        +  // Otherwise a/* will match a/, which it should not.
        +  if (re !== "" && hasMagic) re = "(?=.)" + re
        +
        +  if (addPatternStart) re = patternStart + re
        +
        +  // parsing just a piece of a larger pattern.
        +  if (isSub === SUBPARSE) {
        +    return [ re, hasMagic ]
        +  }
        +
        +  // skip the regexp for non-magical patterns
        +  // unescape anything in it, though, so that it'll be
        +  // an exact match against a file etc.
        +  if (!hasMagic) {
        +    return globUnescape(pattern)
        +  }
        +
        +  var flags = options.nocase ? "i" : ""
        +    , regExp = new RegExp("^" + re + "$", flags)
        +
        +  regExp._glob = pattern
        +  regExp._src = re
        +
        +  return regExp
        +}
        +
        +minimatch.makeRe = function (pattern, options) {
        +  return new Minimatch(pattern, options || {}).makeRe()
        +}
        +
        +Minimatch.prototype.makeRe = makeRe
        +function makeRe () {
        +  if (this.regexp || this.regexp === false) return this.regexp
        +
        +  // at this point, this.set is a 2d array of partial
        +  // pattern strings, or "**".
        +  //
        +  // It's better to use .match().  This function shouldn't
        +  // be used, really, but it's pretty convenient sometimes,
        +  // when you just want to work with a regex.
        +  var set = this.set
        +
        +  if (!set.length) return this.regexp = false
        +  var options = this.options
        +
        +  var twoStar = options.noglobstar ? star
        +      : options.dot ? twoStarDot
        +      : twoStarNoDot
        +    , flags = options.nocase ? "i" : ""
        +
        +  var re = set.map(function (pattern) {
        +    return pattern.map(function (p) {
        +      return (p === GLOBSTAR) ? twoStar
        +           : (typeof p === "string") ? regExpEscape(p)
        +           : p._src
        +    }).join("\\\/")
        +  }).join("|")
        +
        +  // must match entire pattern
        +  // ending in a * or ** will make it less strict.
        +  re = "^(?:" + re + ")$"
        +
        +  // can match anything, as long as it's not this.
        +  if (this.negate) re = "^(?!" + re + ").*$"
        +
        +  try {
        +    return this.regexp = new RegExp(re, flags)
        +  } catch (ex) {
        +    return this.regexp = false
        +  }
        +}
        +
        +minimatch.match = function (list, pattern, options) {
        +  var mm = new Minimatch(pattern, options)
        +  list = list.filter(function (f) {
        +    return mm.match(f)
        +  })
        +  if (options.nonull && !list.length) {
        +    list.push(pattern)
        +  }
        +  return list
        +}
        +
        +Minimatch.prototype.match = match
        +function match (f, partial) {
        +  this.debug("match", f, this.pattern)
        +  // short-circuit in the case of busted things.
        +  // comments, etc.
        +  if (this.comment) return false
        +  if (this.empty) return f === ""
        +
        +  if (f === "/" && partial) return true
        +
        +  var options = this.options
        +
        +  // windows: need to use /, not \
        +  // On other platforms, \ is a valid (albeit bad) filename char.
        +  if (platform === "win32") {
        +    f = f.split("\\").join("/")
        +  }
        +
        +  // treat the test path as a set of pathparts.
        +  f = f.split(slashSplit)
        +  this.debug(this.pattern, "split", f)
        +
        +  // just ONE of the pattern sets in this.set needs to match
        +  // in order for it to be valid.  If negating, then just one
        +  // match means that we have failed.
        +  // Either way, return on the first hit.
        +
        +  var set = this.set
        +  this.debug(this.pattern, "set", set)
        +
        +  var splitFile = path.basename(f.join("/")).split("/")
        +
        +  for (var i = 0, l = set.length; i < l; i ++) {
        +    var pattern = set[i], file = f
        +    if (options.matchBase && pattern.length === 1) {
        +      file = splitFile
        +    }
        +    var hit = this.matchOne(file, pattern, partial)
        +    if (hit) {
        +      if (options.flipNegate) return true
        +      return !this.negate
        +    }
        +  }
        +
        +  // didn't get any hits.  this is success if it's a negative
        +  // pattern, failure otherwise.
        +  if (options.flipNegate) return false
        +  return this.negate
        +}
        +
        +// set partial to true to test if, for example,
        +// "/a/b" matches the start of "/*/b/*/d"
        +// Partial means, if you run out of file before you run
        +// out of pattern, then that's fine, as long as all
        +// the parts match.
        +Minimatch.prototype.matchOne = function (file, pattern, partial) {
        +  var options = this.options
        +
        +  this.debug("matchOne",
        +              { "this": this
        +              , file: file
        +              , pattern: pattern })
        +
        +  this.debug("matchOne", file.length, pattern.length)
        +
        +  for ( var fi = 0
        +          , pi = 0
        +          , fl = file.length
        +          , pl = pattern.length
        +      ; (fi < fl) && (pi < pl)
        +      ; fi ++, pi ++ ) {
        +
        +    this.debug("matchOne loop")
        +    var p = pattern[pi]
        +      , f = file[fi]
        +
        +    this.debug(pattern, p, f)
        +
        +    // should be impossible.
        +    // some invalid regexp stuff in the set.
        +    if (p === false) return false
        +
        +    if (p === GLOBSTAR) {
        +      this.debug('GLOBSTAR', [pattern, p, f])
        +
        +      // "**"
        +      // a/**/b/**/c would match the following:
        +      // a/b/x/y/z/c
        +      // a/x/y/z/b/c
        +      // a/b/x/b/x/c
        +      // a/b/c
        +      // To do this, take the rest of the pattern after
        +      // the **, and see if it would match the file remainder.
        +      // If so, return success.
        +      // If not, the ** "swallows" a segment, and try again.
        +      // This is recursively awful.
        +      //
        +      // a/**/b/**/c matching a/b/x/y/z/c
        +      // - a matches a
        +      // - doublestar
        +      //   - matchOne(b/x/y/z/c, b/**/c)
        +      //     - b matches b
        +      //     - doublestar
        +      //       - matchOne(x/y/z/c, c) -> no
        +      //       - matchOne(y/z/c, c) -> no
        +      //       - matchOne(z/c, c) -> no
        +      //       - matchOne(c, c) yes, hit
        +      var fr = fi
        +        , pr = pi + 1
        +      if (pr === pl) {
        +        this.debug('** at the end')
        +        // a ** at the end will just swallow the rest.
        +        // We have found a match.
        +        // however, it will not swallow /.x, unless
        +        // options.dot is set.
        +        // . and .. are *never* matched by **, for explosively
        +        // exponential reasons.
        +        for ( ; fi < fl; fi ++) {
        +          if (file[fi] === "." || file[fi] === ".." ||
        +              (!options.dot && file[fi].charAt(0) === ".")) return false
        +        }
        +        return true
        +      }
        +
        +      // ok, let's see if we can swallow whatever we can.
        +      WHILE: while (fr < fl) {
        +        var swallowee = file[fr]
        +
        +        this.debug('\nglobstar while',
        +                    file, fr, pattern, pr, swallowee)
        +
        +        // XXX remove this slice.  Just pass the start index.
        +        if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
        +          this.debug('globstar found match!', fr, fl, swallowee)
        +          // found a match.
        +          return true
        +        } else {
        +          // can't swallow "." or ".." ever.
        +          // can only swallow ".foo" when explicitly asked.
        +          if (swallowee === "." || swallowee === ".." ||
        +              (!options.dot && swallowee.charAt(0) === ".")) {
        +            this.debug("dot detected!", file, fr, pattern, pr)
        +            break WHILE
        +          }
        +
        +          // ** swallows a segment, and continue.
        +          this.debug('globstar swallow a segment, and continue')
        +          fr ++
        +        }
        +      }
        +      // no match was found.
        +      // However, in partial mode, we can't say this is necessarily over.
        +      // If there's more *pattern* left, then 
        +      if (partial) {
        +        // ran out of file
        +        this.debug("\n>>> no match, partial?", file, fr, pattern, pr)
        +        if (fr === fl) return true
        +      }
        +      return false
        +    }
        +
        +    // something other than **
        +    // non-magic patterns just have to match exactly
        +    // patterns with magic have been turned into regexps.
        +    var hit
        +    if (typeof p === "string") {
        +      if (options.nocase) {
        +        hit = f.toLowerCase() === p.toLowerCase()
        +      } else {
        +        hit = f === p
        +      }
        +      this.debug("string match", p, f, hit)
        +    } else {
        +      hit = f.match(p)
        +      this.debug("pattern match", p, f, hit)
        +    }
        +
        +    if (!hit) return false
        +  }
        +
        +  // Note: ending in / means that we'll get a final ""
        +  // at the end of the pattern.  This can only match a
        +  // corresponding "" at the end of the file.
        +  // If the file ends in /, then it can only match a
        +  // a pattern that ends in /, unless the pattern just
        +  // doesn't have any more for it. But, a/b/ should *not*
        +  // match "a/b/*", even though "" matches against the
        +  // [^/]*? pattern, except in partial mode, where it might
        +  // simply not be reached yet.
        +  // However, a/b/ should still satisfy a/*
        +
        +  // now either we fell off the end of the pattern, or we're done.
        +  if (fi === fl && pi === pl) {
        +    // ran out of pattern and filename at the same time.
        +    // an exact hit!
        +    return true
        +  } else if (fi === fl) {
        +    // ran out of file, but still had pattern left.
        +    // this is ok if we're doing the match as part of
        +    // a glob fs traversal.
        +    return partial
        +  } else if (pi === pl) {
        +    // ran out of pattern, still have file left.
        +    // this is only acceptable if we're on the very last
        +    // empty segment of a file with a trailing slash.
        +    // a/* should match a/b/
        +    var emptyFileEnd = (fi === fl - 1) && (file[fi] === "")
        +    return emptyFileEnd
        +  }
        +
        +  // should be unreachable.
        +  throw new Error("wtf?")
        +}
        +
        +
        +// replace stuff like \* with *
        +function globUnescape (s) {
        +  return s.replace(/\\(.)/g, "$1")
        +}
        +
        +
        +function regExpEscape (s) {
        +  return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")
        +}
        +
        +})( typeof require === "function" ? require : null,
        +    this,
        +    typeof module === "object" ? module : null,
        +    typeof process === "object" ? process.platform : "win32"
        +  )
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.npmignore b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.npmignore
        new file mode 100644
        index 0000000..07e6e47
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.npmignore
        @@ -0,0 +1 @@
        +/node_modules
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.travis.yml b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.travis.yml
        new file mode 100644
        index 0000000..4af02b3
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.travis.yml
        @@ -0,0 +1,8 @@
        +language: node_js
        +node_js:
        +  - '0.8'
        +  - '0.10'
        +  - '0.12'
        +  - 'iojs'
        +before_install:
        +  - npm install -g npm@latest
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/CONTRIBUTORS b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/CONTRIBUTORS
        new file mode 100644
        index 0000000..4a0bc50
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/CONTRIBUTORS
        @@ -0,0 +1,14 @@
        +# Authors, sorted by whether or not they are me
        +Isaac Z. Schlueter 
        +Brian Cottingham 
        +Carlos Brito Lage 
        +Jesse Dailey 
        +Kevin O'Hara 
        +Marco Rogers 
        +Mark Cavage 
        +Marko Mikulicic 
        +Nathan Rajlich 
        +Satheesh Natesan 
        +Trent Mick 
        +ashleybrener 
        +n4kz 
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/LICENSE b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/LICENSE
        new file mode 100644
        index 0000000..19129e3
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/LICENSE
        @@ -0,0 +1,15 @@
        +The ISC License
        +
        +Copyright (c) Isaac Z. Schlueter and Contributors
        +
        +Permission to use, copy, modify, and/or distribute this software for any
        +purpose with or without fee is hereby granted, provided that the above
        +copyright notice and this permission notice appear in all copies.
        +
        +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
        +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/README.md b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/README.md
        new file mode 100644
        index 0000000..c06814e
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/README.md
        @@ -0,0 +1,137 @@
        +# lru cache
        +
        +A cache object that deletes the least-recently-used items.
        +
        +## Usage:
        +
        +```javascript
        +var LRU = require("lru-cache")
        +  , options = { max: 500
        +              , length: function (n) { return n * 2 }
        +              , dispose: function (key, n) { n.close() }
        +              , maxAge: 1000 * 60 * 60 }
        +  , cache = LRU(options)
        +  , otherCache = LRU(50) // sets just the max size
        +
        +cache.set("key", "value")
        +cache.get("key") // "value"
        +
        +cache.reset()    // empty the cache
        +```
        +
        +If you put more stuff in it, then items will fall out.
        +
        +If you try to put an oversized thing in it, then it'll fall out right
        +away.
        +
        +## Keys should always be Strings or Numbers
        +
        +Note: this module will print warnings to `console.error` if you use a
        +key that is not a String or Number.  Because items are stored in an
        +object, which coerces keys to a string, it won't go well for you if
        +you try to use a key that is not a unique string, it'll cause surprise
        +collisions.  For example:
        +
        +```JavaScript
        +// Bad Example!  Dont' do this!
        +var cache = LRU()
        +var a = {}
        +var b = {}
        +cache.set(a, 'this is a')
        +cache.set(b, 'this is b')
        +console.log(cache.get(a)) // prints: 'this is b'
        +```
        +
        +## Options
        +
        +* `max` The maximum size of the cache, checked by applying the length
        +  function to all values in the cache.  Not setting this is kind of
        +  silly, since that's the whole purpose of this lib, but it defaults
        +  to `Infinity`.
        +* `maxAge` Maximum age in ms.  Items are not pro-actively pruned out
        +  as they age, but if you try to get an item that is too old, it'll
        +  drop it and return undefined instead of giving it to you.
        +* `length` Function that is used to calculate the length of stored
        +  items.  If you're storing strings or buffers, then you probably want
        +  to do something like `function(n){return n.length}`.  The default is
        +  `function(n){return 1}`, which is fine if you want to store `max`
        +  like-sized things.
        +* `dispose` Function that is called on items when they are dropped
        +  from the cache.  This can be handy if you want to close file
        +  descriptors or do other cleanup tasks when items are no longer
        +  accessible.  Called with `key, value`.  It's called *before*
        +  actually removing the item from the internal cache, so if you want
        +  to immediately put it back in, you'll have to do that in a
        +  `nextTick` or `setTimeout` callback or it won't do anything.
        +* `stale` By default, if you set a `maxAge`, it'll only actually pull
        +  stale items out of the cache when you `get(key)`.  (That is, it's
        +  not pre-emptively doing a `setTimeout` or anything.)  If you set
        +  `stale:true`, it'll return the stale value before deleting it.  If
        +  you don't set this, then it'll return `undefined` when you try to
        +  get a stale entry, as if it had already been deleted.
        +
        +## API
        +
        +* `set(key, value, maxAge)`
        +* `get(key) => value`
        +
        +    Both of these will update the "recently used"-ness of the key.
        +    They do what you think. `max` is optional and overrides the
        +    cache `max` option if provided.
        +
        +* `peek(key)`
        +
        +    Returns the key value (or `undefined` if not found) without
        +    updating the "recently used"-ness of the key.
        +
        +    (If you find yourself using this a lot, you *might* be using the
        +    wrong sort of data structure, but there are some use cases where
        +    it's handy.)
        +
        +* `del(key)`
        +
        +    Deletes a key out of the cache.
        +
        +* `reset()`
        +
        +    Clear the cache entirely, throwing away all values.
        +
        +* `has(key)`
        +
        +    Check if a key is in the cache, without updating the recent-ness
        +    or deleting it for being stale.
        +
        +* `forEach(function(value,key,cache), [thisp])`
        +
        +    Just like `Array.prototype.forEach`.  Iterates over all the keys
        +    in the cache, in order of recent-ness.  (Ie, more recently used
        +    items are iterated over first.)
        +
        +* `keys()`
        +
        +    Return an array of the keys in the cache.
        +
        +* `values()`
        +
        +    Return an array of the values in the cache.
        +
        +* `length()`
        +
        +    Return total length of objects in cache taking into account
        +    `length` options function.
        +
        +* `itemCount`
        +
        +    Return total quantity of objects currently in cache. Note, that
        +    `stale` (see options) items are returned as part of this item
        +    count.
        +
        +* `dump()`
        +
        +    Return an array of the cache entries ready for serialization and usage
        +    with 'destinationCache.load(arr)`.
        +
        +* `load(cacheEntriesArray)`
        +
        +    Loads another cache entries array, obtained with `sourceCache.dump()`,
        +    into the cache. The destination cache is reset before loading new entries
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/lib/lru-cache.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/lib/lru-cache.js
        new file mode 100644
        index 0000000..2bbe653
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/lib/lru-cache.js
        @@ -0,0 +1,334 @@
        +;(function () { // closure for web browsers
        +
        +if (typeof module === 'object' && module.exports) {
        +  module.exports = LRUCache
        +} else {
        +  // just set the global for non-node platforms.
        +  this.LRUCache = LRUCache
        +}
        +
        +function hOP (obj, key) {
        +  return Object.prototype.hasOwnProperty.call(obj, key)
        +}
        +
        +function naiveLength () { return 1 }
        +
        +var didTypeWarning = false
        +function typeCheckKey(key) {
        +  if (!didTypeWarning && typeof key !== 'string' && typeof key !== 'number') {
        +    didTypeWarning = true
        +    console.error(new TypeError("LRU: key must be a string or number. Almost certainly a bug! " + typeof key).stack)
        +  }
        +}
        +
        +function LRUCache (options) {
        +  if (!(this instanceof LRUCache))
        +    return new LRUCache(options)
        +
        +  if (typeof options === 'number')
        +    options = { max: options }
        +
        +  if (!options)
        +    options = {}
        +
        +  this._max = options.max
        +  // Kind of weird to have a default max of Infinity, but oh well.
        +  if (!this._max || !(typeof this._max === "number") || this._max <= 0 )
        +    this._max = Infinity
        +
        +  this._lengthCalculator = options.length || naiveLength
        +  if (typeof this._lengthCalculator !== "function")
        +    this._lengthCalculator = naiveLength
        +
        +  this._allowStale = options.stale || false
        +  this._maxAge = options.maxAge || null
        +  this._dispose = options.dispose
        +  this.reset()
        +}
        +
        +// resize the cache when the max changes.
        +Object.defineProperty(LRUCache.prototype, "max",
        +  { set : function (mL) {
        +      if (!mL || !(typeof mL === "number") || mL <= 0 ) mL = Infinity
        +      this._max = mL
        +      if (this._length > this._max) trim(this)
        +    }
        +  , get : function () { return this._max }
        +  , enumerable : true
        +  })
        +
        +// resize the cache when the lengthCalculator changes.
        +Object.defineProperty(LRUCache.prototype, "lengthCalculator",
        +  { set : function (lC) {
        +      if (typeof lC !== "function") {
        +        this._lengthCalculator = naiveLength
        +        this._length = this._itemCount
        +        for (var key in this._cache) {
        +          this._cache[key].length = 1
        +        }
        +      } else {
        +        this._lengthCalculator = lC
        +        this._length = 0
        +        for (var key in this._cache) {
        +          this._cache[key].length = this._lengthCalculator(this._cache[key].value)
        +          this._length += this._cache[key].length
        +        }
        +      }
        +
        +      if (this._length > this._max) trim(this)
        +    }
        +  , get : function () { return this._lengthCalculator }
        +  , enumerable : true
        +  })
        +
        +Object.defineProperty(LRUCache.prototype, "length",
        +  { get : function () { return this._length }
        +  , enumerable : true
        +  })
        +
        +
        +Object.defineProperty(LRUCache.prototype, "itemCount",
        +  { get : function () { return this._itemCount }
        +  , enumerable : true
        +  })
        +
        +LRUCache.prototype.forEach = function (fn, thisp) {
        +  thisp = thisp || this
        +  var i = 0
        +  var itemCount = this._itemCount
        +
        +  for (var k = this._mru - 1; k >= 0 && i < itemCount; k--) if (this._lruList[k]) {
        +    i++
        +    var hit = this._lruList[k]
        +    if (isStale(this, hit)) {
        +      del(this, hit)
        +      if (!this._allowStale) hit = undefined
        +    }
        +    if (hit) {
        +      fn.call(thisp, hit.value, hit.key, this)
        +    }
        +  }
        +}
        +
        +LRUCache.prototype.keys = function () {
        +  var keys = new Array(this._itemCount)
        +  var i = 0
        +  for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) {
        +    var hit = this._lruList[k]
        +    keys[i++] = hit.key
        +  }
        +  return keys
        +}
        +
        +LRUCache.prototype.values = function () {
        +  var values = new Array(this._itemCount)
        +  var i = 0
        +  for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) {
        +    var hit = this._lruList[k]
        +    values[i++] = hit.value
        +  }
        +  return values
        +}
        +
        +LRUCache.prototype.reset = function () {
        +  if (this._dispose && this._cache) {
        +    for (var k in this._cache) {
        +      this._dispose(k, this._cache[k].value)
        +    }
        +  }
        +
        +  this._cache = Object.create(null) // hash of items by key
        +  this._lruList = Object.create(null) // list of items in order of use recency
        +  this._mru = 0 // most recently used
        +  this._lru = 0 // least recently used
        +  this._length = 0 // number of items in the list
        +  this._itemCount = 0
        +}
        +
        +LRUCache.prototype.dump = function () {
        +  var arr = []
        +  var i = 0
        +
        +  for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) {
        +    var hit = this._lruList[k]
        +    if (!isStale(this, hit)) {
        +      //Do not store staled hits
        +      ++i
        +      arr.push({
        +        k: hit.key,
        +        v: hit.value,
        +        e: hit.now + (hit.maxAge || 0)
        +      });
        +    }
        +  }
        +  //arr has the most read first
        +  return arr
        +}
        +
        +LRUCache.prototype.dumpLru = function () {
        +  return this._lruList
        +}
        +
        +LRUCache.prototype.set = function (key, value, maxAge) {
        +  maxAge = maxAge || this._maxAge
        +  typeCheckKey(key)
        +
        +  var now = maxAge ? Date.now() : 0
        +  var len = this._lengthCalculator(value)
        +
        +  if (hOP(this._cache, key)) {
        +    if (len > this._max) {
        +      del(this, this._cache[key])
        +      return false
        +    }
        +    // dispose of the old one before overwriting
        +    if (this._dispose)
        +      this._dispose(key, this._cache[key].value)
        +
        +    this._cache[key].now = now
        +    this._cache[key].maxAge = maxAge
        +    this._cache[key].value = value
        +    this._length += (len - this._cache[key].length)
        +    this._cache[key].length = len
        +    this.get(key)
        +
        +    if (this._length > this._max)
        +      trim(this)
        +
        +    return true
        +  }
        +
        +  var hit = new Entry(key, value, this._mru++, len, now, maxAge)
        +
        +  // oversized objects fall out of cache automatically.
        +  if (hit.length > this._max) {
        +    if (this._dispose) this._dispose(key, value)
        +    return false
        +  }
        +
        +  this._length += hit.length
        +  this._lruList[hit.lu] = this._cache[key] = hit
        +  this._itemCount ++
        +
        +  if (this._length > this._max)
        +    trim(this)
        +
        +  return true
        +}
        +
        +LRUCache.prototype.has = function (key) {
        +  typeCheckKey(key)
        +  if (!hOP(this._cache, key)) return false
        +  var hit = this._cache[key]
        +  if (isStale(this, hit)) {
        +    return false
        +  }
        +  return true
        +}
        +
        +LRUCache.prototype.get = function (key) {
        +  typeCheckKey(key)
        +  return get(this, key, true)
        +}
        +
        +LRUCache.prototype.peek = function (key) {
        +  typeCheckKey(key)
        +  return get(this, key, false)
        +}
        +
        +LRUCache.prototype.pop = function () {
        +  var hit = this._lruList[this._lru]
        +  del(this, hit)
        +  return hit || null
        +}
        +
        +LRUCache.prototype.del = function (key) {
        +  typeCheckKey(key)
        +  del(this, this._cache[key])
        +}
        +
        +LRUCache.prototype.load = function (arr) {
        +  //reset the cache
        +  this.reset();
        +
        +  var now = Date.now()
        +  //A previous serialized cache has the most recent items first
        +  for (var l = arr.length - 1; l >= 0; l-- ) {
        +    var hit = arr[l]
        +    typeCheckKey(hit.k)
        +    var expiresAt = hit.e || 0
        +    if (expiresAt === 0) {
        +      //the item was created without expiration in a non aged cache
        +      this.set(hit.k, hit.v)
        +    } else {
        +      var maxAge = expiresAt - now
        +      //dont add already expired items
        +      if (maxAge > 0) this.set(hit.k, hit.v, maxAge)
        +    }
        +  }
        +}
        +
        +function get (self, key, doUse) {
        +  typeCheckKey(key)
        +  var hit = self._cache[key]
        +  if (hit) {
        +    if (isStale(self, hit)) {
        +      del(self, hit)
        +      if (!self._allowStale) hit = undefined
        +    } else {
        +      if (doUse) use(self, hit)
        +    }
        +    if (hit) hit = hit.value
        +  }
        +  return hit
        +}
        +
        +function isStale(self, hit) {
        +  if (!hit || (!hit.maxAge && !self._maxAge)) return false
        +  var stale = false;
        +  var diff = Date.now() - hit.now
        +  if (hit.maxAge) {
        +    stale = diff > hit.maxAge
        +  } else {
        +    stale = self._maxAge && (diff > self._maxAge)
        +  }
        +  return stale;
        +}
        +
        +function use (self, hit) {
        +  shiftLU(self, hit)
        +  hit.lu = self._mru ++
        +  self._lruList[hit.lu] = hit
        +}
        +
        +function trim (self) {
        +  while (self._lru < self._mru && self._length > self._max)
        +    del(self, self._lruList[self._lru])
        +}
        +
        +function shiftLU (self, hit) {
        +  delete self._lruList[ hit.lu ]
        +  while (self._lru < self._mru && !self._lruList[self._lru]) self._lru ++
        +}
        +
        +function del (self, hit) {
        +  if (hit) {
        +    if (self._dispose) self._dispose(hit.key, hit.value)
        +    self._length -= hit.length
        +    self._itemCount --
        +    delete self._cache[ hit.key ]
        +    shiftLU(self, hit)
        +  }
        +}
        +
        +// classy, since V8 prefers predictable objects.
        +function Entry (key, value, lu, length, now, maxAge) {
        +  this.key = key
        +  this.value = value
        +  this.lu = lu
        +  this.length = length
        +  this.now = now
        +  if (maxAge) this.maxAge = maxAge
        +}
        +
        +})()
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/package.json b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/package.json
        new file mode 100644
        index 0000000..0f73137
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/package.json
        @@ -0,0 +1,57 @@
        +{
        +  "name": "lru-cache",
        +  "description": "A cache object that deletes the least-recently-used items.",
        +  "version": "2.7.3",
        +  "author": {
        +    "name": "Isaac Z. Schlueter",
        +    "email": "i@izs.me"
        +  },
        +  "keywords": [
        +    "mru",
        +    "lru",
        +    "cache"
        +  ],
        +  "scripts": {
        +    "test": "tap test --gc"
        +  },
        +  "main": "lib/lru-cache.js",
        +  "repository": {
        +    "type": "git",
        +    "url": "git://github.com/isaacs/node-lru-cache.git"
        +  },
        +  "devDependencies": {
        +    "tap": "^1.2.0",
        +    "weak": ""
        +  },
        +  "license": "ISC",
        +  "gitHead": "292048199f6d28b77fbe584279a1898e25e4c714",
        +  "bugs": {
        +    "url": "https://github.com/isaacs/node-lru-cache/issues"
        +  },
        +  "homepage": "https://github.com/isaacs/node-lru-cache#readme",
        +  "_id": "lru-cache@2.7.3",
        +  "_shasum": "6d4524e8b955f95d4f5b58851ce21dd72fb4e952",
        +  "_from": "lru-cache@>=2.0.0 <3.0.0",
        +  "_npmVersion": "3.3.2",
        +  "_nodeVersion": "4.0.0",
        +  "_npmUser": {
        +    "name": "isaacs",
        +    "email": "i@izs.me"
        +  },
        +  "dist": {
        +    "shasum": "6d4524e8b955f95d4f5b58851ce21dd72fb4e952",
        +    "tarball": "http://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz"
        +  },
        +  "maintainers": [
        +    {
        +      "name": "isaacs",
        +      "email": "isaacs@npmjs.com"
        +    },
        +    {
        +      "name": "othiym23",
        +      "email": "ogd@aoaioxxysz.net"
        +    }
        +  ],
        +  "directories": {},
        +  "_resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz"
        +}
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/basic.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/basic.js
        new file mode 100644
        index 0000000..b47225f
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/basic.js
        @@ -0,0 +1,396 @@
        +var test = require("tap").test
        +  , LRU = require("../")
        +
        +test("basic", function (t) {
        +  var cache = new LRU({max: 10})
        +  cache.set("key", "value")
        +  t.equal(cache.get("key"), "value")
        +  t.equal(cache.get("nada"), undefined)
        +  t.equal(cache.length, 1)
        +  t.equal(cache.max, 10)
        +  t.end()
        +})
        +
        +test("least recently set", function (t) {
        +  var cache = new LRU(2)
        +  cache.set("a", "A")
        +  cache.set("b", "B")
        +  cache.set("c", "C")
        +  t.equal(cache.get("c"), "C")
        +  t.equal(cache.get("b"), "B")
        +  t.equal(cache.get("a"), undefined)
        +  t.end()
        +})
        +
        +test("lru recently gotten", function (t) {
        +  var cache = new LRU(2)
        +  cache.set("a", "A")
        +  cache.set("b", "B")
        +  cache.get("a")
        +  cache.set("c", "C")
        +  t.equal(cache.get("c"), "C")
        +  t.equal(cache.get("b"), undefined)
        +  t.equal(cache.get("a"), "A")
        +  t.end()
        +})
        +
        +test("del", function (t) {
        +  var cache = new LRU(2)
        +  cache.set("a", "A")
        +  cache.del("a")
        +  t.equal(cache.get("a"), undefined)
        +  t.end()
        +})
        +
        +test("max", function (t) {
        +  var cache = new LRU(3)
        +
        +  // test changing the max, verify that the LRU items get dropped.
        +  cache.max = 100
        +  for (var i = 0; i < 100; i ++) cache.set(i, i)
        +  t.equal(cache.length, 100)
        +  for (var i = 0; i < 100; i ++) {
        +    t.equal(cache.get(i), i)
        +  }
        +  cache.max = 3
        +  t.equal(cache.length, 3)
        +  for (var i = 0; i < 97; i ++) {
        +    t.equal(cache.get(i), undefined)
        +  }
        +  for (var i = 98; i < 100; i ++) {
        +    t.equal(cache.get(i), i)
        +  }
        +
        +  // now remove the max restriction, and try again.
        +  cache.max = "hello"
        +  for (var i = 0; i < 100; i ++) cache.set(i, i)
        +  t.equal(cache.length, 100)
        +  for (var i = 0; i < 100; i ++) {
        +    t.equal(cache.get(i), i)
        +  }
        +  // should trigger an immediate resize
        +  cache.max = 3
        +  t.equal(cache.length, 3)
        +  for (var i = 0; i < 97; i ++) {
        +    t.equal(cache.get(i), undefined)
        +  }
        +  for (var i = 98; i < 100; i ++) {
        +    t.equal(cache.get(i), i)
        +  }
        +  t.end()
        +})
        +
        +test("reset", function (t) {
        +  var cache = new LRU(10)
        +  cache.set("a", "A")
        +  cache.set("b", "B")
        +  cache.reset()
        +  t.equal(cache.length, 0)
        +  t.equal(cache.max, 10)
        +  t.equal(cache.get("a"), undefined)
        +  t.equal(cache.get("b"), undefined)
        +  t.end()
        +})
        +
        +
        +test("basic with weighed length", function (t) {
        +  var cache = new LRU({
        +    max: 100,
        +    length: function (item) { return item.size }
        +  })
        +  cache.set("key", {val: "value", size: 50})
        +  t.equal(cache.get("key").val, "value")
        +  t.equal(cache.get("nada"), undefined)
        +  t.equal(cache.lengthCalculator(cache.get("key")), 50)
        +  t.equal(cache.length, 50)
        +  t.equal(cache.max, 100)
        +  t.end()
        +})
        +
        +
        +test("weighed length item too large", function (t) {
        +  var cache = new LRU({
        +    max: 10,
        +    length: function (item) { return item.size }
        +  })
        +  t.equal(cache.max, 10)
        +
        +  // should fall out immediately
        +  cache.set("key", {val: "value", size: 50})
        +
        +  t.equal(cache.length, 0)
        +  t.equal(cache.get("key"), undefined)
        +  t.end()
        +})
        +
        +test("least recently set with weighed length", function (t) {
        +  var cache = new LRU({
        +    max:8,
        +    length: function (item) { return item.length }
        +  })
        +  cache.set("a", "A")
        +  cache.set("b", "BB")
        +  cache.set("c", "CCC")
        +  cache.set("d", "DDDD")
        +  t.equal(cache.get("d"), "DDDD")
        +  t.equal(cache.get("c"), "CCC")
        +  t.equal(cache.get("b"), undefined)
        +  t.equal(cache.get("a"), undefined)
        +  t.end()
        +})
        +
        +test("lru recently gotten with weighed length", function (t) {
        +  var cache = new LRU({
        +    max: 8,
        +    length: function (item) { return item.length }
        +  })
        +  cache.set("a", "A")
        +  cache.set("b", "BB")
        +  cache.set("c", "CCC")
        +  cache.get("a")
        +  cache.get("b")
        +  cache.set("d", "DDDD")
        +  t.equal(cache.get("c"), undefined)
        +  t.equal(cache.get("d"), "DDDD")
        +  t.equal(cache.get("b"), "BB")
        +  t.equal(cache.get("a"), "A")
        +  t.end()
        +})
        +
        +test("lru recently updated with weighed length", function (t) {
        +  var cache = new LRU({
        +    max: 8,
        +    length: function (item) { return item.length }
        +  })
        +  cache.set("a", "A")
        +  cache.set("b", "BB")
        +  cache.set("c", "CCC")
        +  t.equal(cache.length, 6) //CCC BB A
        +  cache.set("a", "+A")
        +  t.equal(cache.length, 7) //+A CCC BB
        +  cache.set("b", "++BB")
        +  t.equal(cache.length, 6) //++BB +A
        +  t.equal(cache.get("c"), undefined)
        +
        +  cache.set("c", "oversized")
        +  t.equal(cache.length, 6) //++BB +A
        +  t.equal(cache.get("c"), undefined)
        +
        +  cache.set("a", "oversized")
        +  t.equal(cache.length, 4) //++BB
        +  t.equal(cache.get("a"), undefined)
        +  t.equal(cache.get("b"), "++BB")
        +  t.end()
        +})
        +
        +test("set returns proper booleans", function(t) {
        +  var cache = new LRU({
        +    max: 5,
        +    length: function (item) { return item.length }
        +  })
        +
        +  t.equal(cache.set("a", "A"), true)
        +
        +  // should return false for max exceeded
        +  t.equal(cache.set("b", "donuts"), false)
        +
        +  t.equal(cache.set("b", "B"), true)
        +  t.equal(cache.set("c", "CCCC"), true)
        +  t.end()
        +})
        +
        +test("drop the old items", function(t) {
        +  var cache = new LRU({
        +    max: 5,
        +    maxAge: 50
        +  })
        +
        +  cache.set("a", "A")
        +
        +  setTimeout(function () {
        +    cache.set("b", "b")
        +    t.equal(cache.get("a"), "A")
        +  }, 25)
        +
        +  setTimeout(function () {
        +    cache.set("c", "C")
        +    // timed out
        +    t.notOk(cache.get("a"))
        +  }, 60 + 25)
        +
        +  setTimeout(function () {
        +    t.notOk(cache.get("b"))
        +    t.equal(cache.get("c"), "C")
        +  }, 90)
        +
        +  setTimeout(function () {
        +    t.notOk(cache.get("c"))
        +    t.end()
        +  }, 155)
        +})
        +
        +test("individual item can have it's own maxAge", function(t) {
        +  var cache = new LRU({
        +    max: 5,
        +    maxAge: 50
        +  })
        +
        +  cache.set("a", "A", 20)
        +  setTimeout(function () {
        +    t.notOk(cache.get("a"))
        +    t.end()
        +  }, 25)
        +})
        +
        +test("individual item can have it's own maxAge > cache's", function(t) {
        +  var cache = new LRU({
        +    max: 5,
        +    maxAge: 20
        +  })
        +
        +  cache.set("a", "A", 50)
        +  setTimeout(function () {
        +    t.equal(cache.get("a"), "A")
        +    t.end()
        +  }, 25)
        +})
        +
        +test("disposal function", function(t) {
        +  var disposed = false
        +  var cache = new LRU({
        +    max: 1,
        +    dispose: function (k, n) {
        +      disposed = n
        +    }
        +  })
        +
        +  cache.set(1, 1)
        +  cache.set(2, 2)
        +  t.equal(disposed, 1)
        +  cache.set(3, 3)
        +  t.equal(disposed, 2)
        +  cache.reset()
        +  t.equal(disposed, 3)
        +  t.end()
        +})
        +
        +test("disposal function on too big of item", function(t) {
        +  var disposed = false
        +  var cache = new LRU({
        +    max: 1,
        +    length: function (k) {
        +      return k.length
        +    },
        +    dispose: function (k, n) {
        +      disposed = n
        +    }
        +  })
        +  var obj = [ 1, 2 ]
        +
        +  t.equal(disposed, false)
        +  cache.set("obj", obj)
        +  t.equal(disposed, obj)
        +  t.end()
        +})
        +
        +test("has()", function(t) {
        +  var cache = new LRU({
        +    max: 1,
        +    maxAge: 10
        +  })
        +
        +  cache.set('foo', 'bar')
        +  t.equal(cache.has('foo'), true)
        +  cache.set('blu', 'baz')
        +  t.equal(cache.has('foo'), false)
        +  t.equal(cache.has('blu'), true)
        +  setTimeout(function() {
        +    t.equal(cache.has('blu'), false)
        +    t.end()
        +  }, 15)
        +})
        +
        +test("stale", function(t) {
        +  var cache = new LRU({
        +    maxAge: 10,
        +    stale: true
        +  })
        +
        +  cache.set('foo', 'bar')
        +  t.equal(cache.get('foo'), 'bar')
        +  t.equal(cache.has('foo'), true)
        +  setTimeout(function() {
        +    t.equal(cache.has('foo'), false)
        +    t.equal(cache.get('foo'), 'bar')
        +    t.equal(cache.get('foo'), undefined)
        +    t.end()
        +  }, 15)
        +})
        +
        +test("lru update via set", function(t) {
        +  var cache = LRU({ max: 2 });
        +
        +  cache.set('foo', 1);
        +  cache.set('bar', 2);
        +  cache.del('bar');
        +  cache.set('baz', 3);
        +  cache.set('qux', 4);
        +
        +  t.equal(cache.get('foo'), undefined)
        +  t.equal(cache.get('bar'), undefined)
        +  t.equal(cache.get('baz'), 3)
        +  t.equal(cache.get('qux'), 4)
        +  t.end()
        +})
        +
        +test("least recently set w/ peek", function (t) {
        +  var cache = new LRU(2)
        +  cache.set("a", "A")
        +  cache.set("b", "B")
        +  t.equal(cache.peek("a"), "A")
        +  cache.set("c", "C")
        +  t.equal(cache.get("c"), "C")
        +  t.equal(cache.get("b"), "B")
        +  t.equal(cache.get("a"), undefined)
        +  t.end()
        +})
        +
        +test("pop the least used item", function (t) {
        +  var cache = new LRU(3)
        +  , last
        +
        +  cache.set("a", "A")
        +  cache.set("b", "B")
        +  cache.set("c", "C")
        +
        +  t.equal(cache.length, 3)
        +  t.equal(cache.max, 3)
        +
        +  // Ensure we pop a, c, b
        +  cache.get("b", "B")
        +
        +  last = cache.pop()
        +  t.equal(last.key, "a")
        +  t.equal(last.value, "A")
        +  t.equal(cache.length, 2)
        +  t.equal(cache.max, 3)
        +
        +  last = cache.pop()
        +  t.equal(last.key, "c")
        +  t.equal(last.value, "C")
        +  t.equal(cache.length, 1)
        +  t.equal(cache.max, 3)
        +
        +  last = cache.pop()
        +  t.equal(last.key, "b")
        +  t.equal(last.value, "B")
        +  t.equal(cache.length, 0)
        +  t.equal(cache.max, 3)
        +
        +  last = cache.pop()
        +  t.equal(last, null)
        +  t.equal(cache.length, 0)
        +  t.equal(cache.max, 3)
        +
        +  t.end()
        +})
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/foreach.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/foreach.js
        new file mode 100644
        index 0000000..4190417
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/foreach.js
        @@ -0,0 +1,120 @@
        +var test = require('tap').test
        +var LRU = require('../')
        +
        +test('forEach', function (t) {
        +  var l = new LRU(5)
        +  for (var i = 0; i < 10; i ++) {
        +    l.set(i.toString(), i.toString(2))
        +  }
        +
        +  var i = 9
        +  l.forEach(function (val, key, cache) {
        +    t.equal(cache, l)
        +    t.equal(key, i.toString())
        +    t.equal(val, i.toString(2))
        +    i -= 1
        +  })
        +
        +  // get in order of most recently used
        +  l.get(6)
        +  l.get(8)
        +
        +  var order = [ 8, 6, 9, 7, 5 ]
        +  var i = 0
        +
        +  l.forEach(function (val, key, cache) {
        +    var j = order[i ++]
        +    t.equal(cache, l)
        +    t.equal(key, j.toString())
        +    t.equal(val, j.toString(2))
        +  })
        +  t.equal(i, order.length);
        +
        +  t.end()
        +})
        +
        +test('keys() and values()', function (t) {
        +  var l = new LRU(5)
        +  for (var i = 0; i < 10; i ++) {
        +    l.set(i.toString(), i.toString(2))
        +  }
        +
        +  t.similar(l.keys(), ['9', '8', '7', '6', '5'])
        +  t.similar(l.values(), ['1001', '1000', '111', '110', '101'])
        +
        +  // get in order of most recently used
        +  l.get(6)
        +  l.get(8)
        +
        +  t.similar(l.keys(), ['8', '6', '9', '7', '5'])
        +  t.similar(l.values(), ['1000', '110', '1001', '111', '101'])
        +
        +  t.end()
        +})
        +
        +test('all entries are iterated over', function(t) {
        +  var l = new LRU(5)
        +  for (var i = 0; i < 10; i ++) {
        +    l.set(i.toString(), i.toString(2))
        +  }
        +
        +  var i = 0
        +  l.forEach(function (val, key, cache) {
        +    if (i > 0) {
        +      cache.del(key)
        +    }
        +    i += 1
        +  })
        +
        +  t.equal(i, 5)
        +  t.equal(l.keys().length, 1)
        +
        +  t.end()
        +})
        +
        +test('all stale entries are removed', function(t) {
        +  var l = new LRU({ max: 5, maxAge: -5, stale: true })
        +  for (var i = 0; i < 10; i ++) {
        +    l.set(i.toString(), i.toString(2))
        +  }
        +
        +  var i = 0
        +  l.forEach(function () {
        +    i += 1
        +  })
        +
        +  t.equal(i, 5)
        +  t.equal(l.keys().length, 0)
        +
        +  t.end()
        +})
        +
        +test('expires', function (t) {
        +  var l = new LRU({
        +    max: 10,
        +    maxAge: 50
        +  })
        +  for (var i = 0; i < 10; i++) {
        +    l.set(i.toString(), i.toString(2), ((i % 2) ? 25 : undefined))
        +  }
        +
        +  var i = 0
        +  var order = [ 8, 6, 4, 2, 0 ]
        +  setTimeout(function () {
        +    l.forEach(function (val, key, cache) {
        +      var j = order[i++]
        +      t.equal(cache, l)
        +      t.equal(key, j.toString())
        +      t.equal(val, j.toString(2))
        +    })
        +    t.equal(i, order.length);
        +
        +    setTimeout(function () {
        +      var count = 0;
        +      l.forEach(function (val, key, cache) { count++; })
        +      t.equal(0, count);
        +      t.end()
        +    }, 25)
        +
        +  }, 26)
        +})
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/memory-leak.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/memory-leak.js
        new file mode 100644
        index 0000000..b5912f6
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/memory-leak.js
        @@ -0,0 +1,51 @@
        +#!/usr/bin/env node --expose_gc
        +
        +
        +var weak = require('weak');
        +var test = require('tap').test
        +var LRU = require('../')
        +var l = new LRU({ max: 10 })
        +var refs = 0
        +function X() {
        +  refs ++
        +  weak(this, deref)
        +}
        +
        +function deref() {
        +  refs --
        +}
        +
        +test('no leaks', function (t) {
        +  // fill up the cache
        +  for (var i = 0; i < 100; i++) {
        +    l.set(i, new X);
        +    // throw some gets in there, too.
        +    if (i % 2 === 0)
        +      l.get(i / 2)
        +  }
        +
        +  gc()
        +
        +  var start = process.memoryUsage()
        +
        +  // capture the memory
        +  var startRefs = refs
        +
        +  // do it again, but more
        +  for (var i = 0; i < 10000; i++) {
        +    l.set(i, new X);
        +    // throw some gets in there, too.
        +    if (i % 2 === 0)
        +      l.get(i / 2)
        +  }
        +
        +  gc()
        +
        +  var end = process.memoryUsage()
        +  t.equal(refs, startRefs, 'no leaky refs')
        +
        +  console.error('start: %j\n' +
        +                'end:   %j', start, end);
        +  t.pass();
        +  t.end();
        +})
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/serialize.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/serialize.js
        new file mode 100644
        index 0000000..1094194
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/serialize.js
        @@ -0,0 +1,216 @@
        +var test = require('tap').test
        +var LRU = require('../')
        +
        +test('dump', function (t) {
        +  var cache = new LRU()
        +
        +  t.equal(cache.dump().length, 0, "nothing in dump for empty cache")
        +
        +  cache.set("a", "A")
        +  cache.set("b", "B")
        +  t.deepEqual(cache.dump(), [
        +    { k: "b", v: "B", e: 0 },
        +    { k: "a", v: "A", e: 0 }
        +  ])
        +
        +  cache.set("a", "A");
        +  t.deepEqual(cache.dump(), [
        +    { k: "a", v: "A", e: 0 },
        +    { k: "b", v: "B", e: 0 }
        +  ])
        +
        +  cache.get("b");
        +  t.deepEqual(cache.dump(), [
        +    { k: "b", v: "B", e: 0 },
        +    { k: "a", v: "A", e: 0 }
        +  ])
        +
        +  cache.del("a");
        +  t.deepEqual(cache.dump(), [
        +    { k: "b", v: "B",  e: 0 }
        +  ])
        +
        +  t.end()
        +})
        +
        +test("do not dump stale items", function(t) {
        +  var cache = new LRU({
        +    max: 5,
        +    maxAge: 50
        +  })
        +
        +  //expires at 50
        +  cache.set("a", "A")
        +
        +  setTimeout(function () {
        +    //expires at 75
        +    cache.set("b", "B")
        +    var s = cache.dump()
        +    t.equal(s.length, 2)
        +    t.equal(s[0].k, "b")
        +    t.equal(s[1].k, "a")
        +  }, 25)
        +
        +  setTimeout(function () {
        +    //expires at 110
        +    cache.set("c", "C")
        +    var s = cache.dump()
        +    t.equal(s.length, 2)
        +    t.equal(s[0].k, "c")
        +    t.equal(s[1].k, "b")
        +  }, 60)
        +
        +  setTimeout(function () {
        +    //expires at 130
        +    cache.set("d", "D", 40)
        +    var s = cache.dump()
        +    t.equal(s.length, 2)
        +    t.equal(s[0].k, "d")
        +    t.equal(s[1].k, "c")
        +  }, 90)
        +
        +  setTimeout(function () {
        +    var s = cache.dump()
        +    t.equal(s.length, 1)
        +    t.equal(s[0].k, "d")
        +  }, 120)
        +
        +  setTimeout(function () {
        +    var s = cache.dump()
        +    t.deepEqual(s, [])
        +    t.end()
        +  }, 155)
        +})
        +
        +test("load basic cache", function(t) {
        +  var cache = new LRU(),
        +      copy = new LRU()
        +
        +  cache.set("a", "A")
        +  cache.set("b", "B")
        +
        +  copy.load(cache.dump())
        +  t.deepEquals(cache.dump(), copy.dump())
        +
        +  t.end()
        +})
        +
        +
        +test("load staled cache", function(t) {
        +  var cache = new LRU({maxAge: 50}),
        +      copy = new LRU({maxAge: 50}),
        +      arr
        +
        +  //expires at 50
        +  cache.set("a", "A")
        +  setTimeout(function () {
        +    //expires at 80
        +    cache.set("b", "B")
        +    arr = cache.dump()
        +    t.equal(arr.length, 2)
        +  }, 30)
        +
        +  setTimeout(function () {
        +    copy.load(arr)
        +    t.equal(copy.get("a"), undefined)
        +    t.equal(copy.get("b"), "B")
        +  }, 60)
        +
        +  setTimeout(function () {
        +    t.equal(copy.get("b"), undefined)
        +    t.end()
        +  }, 90)
        +})
        +
        +test("load to other size cache", function(t) {
        +  var cache = new LRU({max: 2}),
        +      copy = new LRU({max: 1})
        +
        +  cache.set("a", "A")
        +  cache.set("b", "B")
        +
        +  copy.load(cache.dump())
        +  t.equal(copy.get("a"), undefined)
        +  t.equal(copy.get("b"), "B")
        +
        +  //update the last read from original cache
        +  cache.get("a")
        +  copy.load(cache.dump())
        +  t.equal(copy.get("a"), "A")
        +  t.equal(copy.get("b"), undefined)
        +
        +  t.end()
        +})
        +
        +
        +test("load to other age cache", function(t) {
        +  var cache = new LRU({maxAge: 50}),
        +      aged = new LRU({maxAge: 100}),
        +      simple = new LRU(),
        +      arr,
        +      expired
        +
        +  //created at 0
        +  //a would be valid till 0 + 50
        +  cache.set("a", "A")
        +  setTimeout(function () {
        +    //created at 20
        +    //b would be valid till 20 + 50
        +    cache.set("b", "B")
        +    //b would be valid till 20 + 70
        +    cache.set("c", "C", 70)
        +    arr = cache.dump()
        +    t.equal(arr.length, 3)
        +  }, 20)
        +
        +  setTimeout(function () {
        +    t.equal(cache.get("a"), undefined)
        +    t.equal(cache.get("b"), "B")
        +    t.equal(cache.get("c"), "C")
        +
        +    aged.load(arr)
        +    t.equal(aged.get("a"), undefined)
        +    t.equal(aged.get("b"), "B")
        +    t.equal(aged.get("c"), "C")
        +
        +    simple.load(arr)
        +    t.equal(simple.get("a"), undefined)
        +    t.equal(simple.get("b"), "B")
        +    t.equal(simple.get("c"), "C")
        +  }, 60)
        +
        +  setTimeout(function () {
        +    t.equal(cache.get("a"), undefined)
        +    t.equal(cache.get("b"), undefined)
        +    t.equal(cache.get("c"), "C")
        +
        +    aged.load(arr)
        +    t.equal(aged.get("a"), undefined)
        +    t.equal(aged.get("b"), undefined)
        +    t.equal(aged.get("c"), "C")
        +
        +    simple.load(arr)
        +    t.equal(simple.get("a"), undefined)
        +    t.equal(simple.get("b"), undefined)
        +    t.equal(simple.get("c"), "C")
        +  }, 80)
        +
        +  setTimeout(function () {
        +    t.equal(cache.get("a"), undefined)
        +    t.equal(cache.get("b"), undefined)
        +    t.equal(cache.get("c"), undefined)
        +
        +    aged.load(arr)
        +    t.equal(aged.get("a"), undefined)
        +    t.equal(aged.get("b"), undefined)
        +    t.equal(aged.get("c"), undefined)
        +
        +    simple.load(arr)
        +    t.equal(simple.get("a"), undefined)
        +    t.equal(simple.get("b"), undefined)
        +    t.equal(simple.get("c"), undefined)
        +    t.end()
        +  }, 100)
        +
        +})
        +
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/LICENSE b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/LICENSE
        new file mode 100644
        index 0000000..19129e3
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/LICENSE
        @@ -0,0 +1,15 @@
        +The ISC License
        +
        +Copyright (c) Isaac Z. Schlueter and Contributors
        +
        +Permission to use, copy, modify, and/or distribute this software for any
        +purpose with or without fee is hereby granted, provided that the above
        +copyright notice and this permission notice appear in all copies.
        +
        +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
        +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/README.md b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/README.md
        new file mode 100644
        index 0000000..25a38a5
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/README.md
        @@ -0,0 +1,53 @@
        +# sigmund
        +
        +Quick and dirty signatures for Objects.
        +
        +This is like a much faster `deepEquals` comparison, which returns a
        +string key suitable for caches and the like.
        +
        +## Usage
        +
        +```javascript
        +function doSomething (someObj) {
        +  var key = sigmund(someObj, maxDepth) // max depth defaults to 10
        +  var cached = cache.get(key)
        +  if (cached) return cached
        +
        +  var result = expensiveCalculation(someObj)
        +  cache.set(key, result)
        +  return result
        +}
        +```
        +
        +The resulting key will be as unique and reproducible as calling
        +`JSON.stringify` or `util.inspect` on the object, but is much faster.
        +In order to achieve this speed, some differences are glossed over.
        +For example, the object `{0:'foo'}` will be treated identically to the
        +array `['foo']`.
        +
        +Also, just as there is no way to summon the soul from the scribblings
        +of a cocaine-addled psychoanalyst, there is no way to revive the object
        +from the signature string that sigmund gives you.  In fact, it's
        +barely even readable.
        +
        +As with `util.inspect` and `JSON.stringify`, larger objects will
        +produce larger signature strings.
        +
        +Because sigmund is a bit less strict than the more thorough
        +alternatives, the strings will be shorter, and also there is a
        +slightly higher chance for collisions.  For example, these objects
        +have the same signature:
        +
        +    var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]}
        +    var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']}
        +
        +Like a good Freudian, sigmund is most effective when you already have
        +some understanding of what you're looking for.  It can help you help
        +yourself, but you must be willing to do some work as well.
        +
        +Cycles are handled, and cyclical objects are silently omitted (though
        +the key is included in the signature output.)
        +
        +The second argument is the maximum depth, which defaults to 10,
        +because that is the maximum object traversal depth covered by most
        +insurance carriers.
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/bench.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/bench.js
        new file mode 100644
        index 0000000..5acfd6d
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/bench.js
        @@ -0,0 +1,283 @@
        +// different ways to id objects
        +// use a req/res pair, since it's crazy deep and cyclical
        +
        +// sparseFE10 and sigmund are usually pretty close, which is to be expected,
        +// since they are essentially the same algorithm, except that sigmund handles
        +// regular expression objects properly.
        +
        +
        +var http = require('http')
        +var util = require('util')
        +var sigmund = require('./sigmund.js')
        +var sreq, sres, creq, cres, test
        +
        +http.createServer(function (q, s) {
        +  sreq = q
        +  sres = s
        +  sres.end('ok')
        +  this.close(function () { setTimeout(function () {
        +    start()
        +  }, 200) })
        +}).listen(1337, function () {
        +  creq = http.get({ port: 1337 })
        +  creq.on('response', function (s) { cres = s })
        +})
        +
        +function start () {
        +  test = [sreq, sres, creq, cres]
        +  // test = sreq
        +  // sreq.sres = sres
        +  // sreq.creq = creq
        +  // sreq.cres = cres
        +
        +  for (var i in exports.compare) {
        +    console.log(i)
        +    var hash = exports.compare[i]()
        +    console.log(hash)
        +    console.log(hash.length)
        +    console.log('')
        +  }
        +
        +  require('bench').runMain()
        +}
        +
        +function customWs (obj, md, d) {
        +  d = d || 0
        +  var to = typeof obj
        +  if (to === 'undefined' || to === 'function' || to === null) return ''
        +  if (d > md || !obj || to !== 'object') return ('' + obj).replace(/[\n ]+/g, '')
        +
        +  if (Array.isArray(obj)) {
        +    return obj.map(function (i, _, __) {
        +      return customWs(i, md, d + 1)
        +    }).reduce(function (a, b) { return a + b }, '')
        +  }
        +
        +  var keys = Object.keys(obj)
        +  return keys.map(function (k, _, __) {
        +    return k + ':' + customWs(obj[k], md, d + 1)
        +  }).reduce(function (a, b) { return a + b }, '')
        +}
        +
        +function custom (obj, md, d) {
        +  d = d || 0
        +  var to = typeof obj
        +  if (to === 'undefined' || to === 'function' || to === null) return ''
        +  if (d > md || !obj || to !== 'object') return '' + obj
        +
        +  if (Array.isArray(obj)) {
        +    return obj.map(function (i, _, __) {
        +      return custom(i, md, d + 1)
        +    }).reduce(function (a, b) { return a + b }, '')
        +  }
        +
        +  var keys = Object.keys(obj)
        +  return keys.map(function (k, _, __) {
        +    return k + ':' + custom(obj[k], md, d + 1)
        +  }).reduce(function (a, b) { return a + b }, '')
        +}
        +
        +function sparseFE2 (obj, maxDepth) {
        +  var seen = []
        +  var soFar = ''
        +  function ch (v, depth) {
        +    if (depth > maxDepth) return
        +    if (typeof v === 'function' || typeof v === 'undefined') return
        +    if (typeof v !== 'object' || !v) {
        +      soFar += v
        +      return
        +    }
        +    if (seen.indexOf(v) !== -1 || depth === maxDepth) return
        +    seen.push(v)
        +    soFar += '{'
        +    Object.keys(v).forEach(function (k, _, __) {
        +      // pseudo-private values.  skip those.
        +      if (k.charAt(0) === '_') return
        +      var to = typeof v[k]
        +      if (to === 'function' || to === 'undefined') return
        +      soFar += k + ':'
        +      ch(v[k], depth + 1)
        +    })
        +    soFar += '}'
        +  }
        +  ch(obj, 0)
        +  return soFar
        +}
        +
        +function sparseFE (obj, maxDepth) {
        +  var seen = []
        +  var soFar = ''
        +  function ch (v, depth) {
        +    if (depth > maxDepth) return
        +    if (typeof v === 'function' || typeof v === 'undefined') return
        +    if (typeof v !== 'object' || !v) {
        +      soFar += v
        +      return
        +    }
        +    if (seen.indexOf(v) !== -1 || depth === maxDepth) return
        +    seen.push(v)
        +    soFar += '{'
        +    Object.keys(v).forEach(function (k, _, __) {
        +      // pseudo-private values.  skip those.
        +      if (k.charAt(0) === '_') return
        +      var to = typeof v[k]
        +      if (to === 'function' || to === 'undefined') return
        +      soFar += k
        +      ch(v[k], depth + 1)
        +    })
        +  }
        +  ch(obj, 0)
        +  return soFar
        +}
        +
        +function sparse (obj, maxDepth) {
        +  var seen = []
        +  var soFar = ''
        +  function ch (v, depth) {
        +    if (depth > maxDepth) return
        +    if (typeof v === 'function' || typeof v === 'undefined') return
        +    if (typeof v !== 'object' || !v) {
        +      soFar += v
        +      return
        +    }
        +    if (seen.indexOf(v) !== -1 || depth === maxDepth) return
        +    seen.push(v)
        +    soFar += '{'
        +    for (var k in v) {
        +      // pseudo-private values.  skip those.
        +      if (k.charAt(0) === '_') continue
        +      var to = typeof v[k]
        +      if (to === 'function' || to === 'undefined') continue
        +      soFar += k
        +      ch(v[k], depth + 1)
        +    }
        +  }
        +  ch(obj, 0)
        +  return soFar
        +}
        +
        +function noCommas (obj, maxDepth) {
        +  var seen = []
        +  var soFar = ''
        +  function ch (v, depth) {
        +    if (depth > maxDepth) return
        +    if (typeof v === 'function' || typeof v === 'undefined') return
        +    if (typeof v !== 'object' || !v) {
        +      soFar += v
        +      return
        +    }
        +    if (seen.indexOf(v) !== -1 || depth === maxDepth) return
        +    seen.push(v)
        +    soFar += '{'
        +    for (var k in v) {
        +      // pseudo-private values.  skip those.
        +      if (k.charAt(0) === '_') continue
        +      var to = typeof v[k]
        +      if (to === 'function' || to === 'undefined') continue
        +      soFar += k + ':'
        +      ch(v[k], depth + 1)
        +    }
        +    soFar += '}'
        +  }
        +  ch(obj, 0)
        +  return soFar
        +}
        +
        +
        +function flatten (obj, maxDepth) {
        +  var seen = []
        +  var soFar = ''
        +  function ch (v, depth) {
        +    if (depth > maxDepth) return
        +    if (typeof v === 'function' || typeof v === 'undefined') return
        +    if (typeof v !== 'object' || !v) {
        +      soFar += v
        +      return
        +    }
        +    if (seen.indexOf(v) !== -1 || depth === maxDepth) return
        +    seen.push(v)
        +    soFar += '{'
        +    for (var k in v) {
        +      // pseudo-private values.  skip those.
        +      if (k.charAt(0) === '_') continue
        +      var to = typeof v[k]
        +      if (to === 'function' || to === 'undefined') continue
        +      soFar += k + ':'
        +      ch(v[k], depth + 1)
        +      soFar += ','
        +    }
        +    soFar += '}'
        +  }
        +  ch(obj, 0)
        +  return soFar
        +}
        +
        +exports.compare =
        +{
        +  // 'custom 2': function () {
        +  //   return custom(test, 2, 0)
        +  // },
        +  // 'customWs 2': function () {
        +  //   return customWs(test, 2, 0)
        +  // },
        +  'JSON.stringify (guarded)': function () {
        +    var seen = []
        +    return JSON.stringify(test, function (k, v) {
        +      if (typeof v !== 'object' || !v) return v
        +      if (seen.indexOf(v) !== -1) return undefined
        +      seen.push(v)
        +      return v
        +    })
        +  },
        +
        +  'flatten 10': function () {
        +    return flatten(test, 10)
        +  },
        +
        +  // 'flattenFE 10': function () {
        +  //   return flattenFE(test, 10)
        +  // },
        +
        +  'noCommas 10': function () {
        +    return noCommas(test, 10)
        +  },
        +
        +  'sparse 10': function () {
        +    return sparse(test, 10)
        +  },
        +
        +  'sparseFE 10': function () {
        +    return sparseFE(test, 10)
        +  },
        +
        +  'sparseFE2 10': function () {
        +    return sparseFE2(test, 10)
        +  },
        +
        +  sigmund: function() {
        +    return sigmund(test, 10)
        +  },
        +
        +
        +  // 'util.inspect 1': function () {
        +  //   return util.inspect(test, false, 1, false)
        +  // },
        +  // 'util.inspect undefined': function () {
        +  //   util.inspect(test)
        +  // },
        +  // 'util.inspect 2': function () {
        +  //   util.inspect(test, false, 2, false)
        +  // },
        +  // 'util.inspect 3': function () {
        +  //   util.inspect(test, false, 3, false)
        +  // },
        +  // 'util.inspect 4': function () {
        +  //   util.inspect(test, false, 4, false)
        +  // },
        +  // 'util.inspect Infinity': function () {
        +  //   util.inspect(test, false, Infinity, false)
        +  // }
        +}
        +
        +/** results
        +**/
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/package.json b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/package.json
        new file mode 100644
        index 0000000..edb68a4
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/package.json
        @@ -0,0 +1,59 @@
        +{
        +  "name": "sigmund",
        +  "version": "1.0.1",
        +  "description": "Quick and dirty signatures for Objects.",
        +  "main": "sigmund.js",
        +  "directories": {
        +    "test": "test"
        +  },
        +  "dependencies": {},
        +  "devDependencies": {
        +    "tap": "~0.3.0"
        +  },
        +  "scripts": {
        +    "test": "tap test/*.js",
        +    "bench": "node bench.js"
        +  },
        +  "repository": {
        +    "type": "git",
        +    "url": "git://github.com/isaacs/sigmund.git"
        +  },
        +  "keywords": [
        +    "object",
        +    "signature",
        +    "key",
        +    "data",
        +    "psychoanalysis"
        +  ],
        +  "author": {
        +    "name": "Isaac Z. Schlueter",
        +    "email": "i@izs.me",
        +    "url": "http://blog.izs.me/"
        +  },
        +  "license": "ISC",
        +  "gitHead": "527f97aa5bb253d927348698c0cd3bb267d098c6",
        +  "bugs": {
        +    "url": "https://github.com/isaacs/sigmund/issues"
        +  },
        +  "homepage": "https://github.com/isaacs/sigmund#readme",
        +  "_id": "sigmund@1.0.1",
        +  "_shasum": "3ff21f198cad2175f9f3b781853fd94d0d19b590",
        +  "_from": "sigmund@>=1.0.0 <1.1.0",
        +  "_npmVersion": "2.10.0",
        +  "_nodeVersion": "2.0.1",
        +  "_npmUser": {
        +    "name": "isaacs",
        +    "email": "isaacs@npmjs.com"
        +  },
        +  "dist": {
        +    "shasum": "3ff21f198cad2175f9f3b781853fd94d0d19b590",
        +    "tarball": "http://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
        +  },
        +  "maintainers": [
        +    {
        +      "name": "isaacs",
        +      "email": "i@izs.me"
        +    }
        +  ],
        +  "_resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
        +}
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/sigmund.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/sigmund.js
        new file mode 100644
        index 0000000..82c7ab8
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/sigmund.js
        @@ -0,0 +1,39 @@
        +module.exports = sigmund
        +function sigmund (subject, maxSessions) {
        +    maxSessions = maxSessions || 10;
        +    var notes = [];
        +    var analysis = '';
        +    var RE = RegExp;
        +
        +    function psychoAnalyze (subject, session) {
        +        if (session > maxSessions) return;
        +
        +        if (typeof subject === 'function' ||
        +            typeof subject === 'undefined') {
        +            return;
        +        }
        +
        +        if (typeof subject !== 'object' || !subject ||
        +            (subject instanceof RE)) {
        +            analysis += subject;
        +            return;
        +        }
        +
        +        if (notes.indexOf(subject) !== -1 || session === maxSessions) return;
        +
        +        notes.push(subject);
        +        analysis += '{';
        +        Object.keys(subject).forEach(function (issue, _, __) {
        +            // pseudo-private values.  skip those.
        +            if (issue.charAt(0) === '_') return;
        +            var to = typeof subject[issue];
        +            if (to === 'function' || to === 'undefined') return;
        +            analysis += issue;
        +            psychoAnalyze(subject[issue], session + 1);
        +        });
        +    }
        +    psychoAnalyze(subject, 0);
        +    return analysis;
        +}
        +
        +// vim: set softtabstop=4 shiftwidth=4:
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/test/basic.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/test/basic.js
        new file mode 100644
        index 0000000..50c53a1
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/test/basic.js
        @@ -0,0 +1,24 @@
        +var test = require('tap').test
        +var sigmund = require('../sigmund.js')
        +
        +
        +// occasionally there are duplicates
        +// that's an acceptable edge-case.  JSON.stringify and util.inspect
        +// have some collision potential as well, though less, and collision
        +// detection is expensive.
        +var hash = '{abc/def/g{0h1i2{jkl'
        +var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]}
        +var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']}
        +
        +var obj3 = JSON.parse(JSON.stringify(obj1))
        +obj3.c = /def/
        +obj3.g[2].cycle = obj3
        +var cycleHash = '{abc/def/g{0h1i2{jklcycle'
        +
        +test('basic', function (t) {
        +    t.equal(sigmund(obj1), hash)
        +    t.equal(sigmund(obj2), hash)
        +    t.equal(sigmund(obj3), cycleHash)
        +    t.end()
        +})
        +
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/package.json b/node_modules/mocha/node_modules/glob/node_modules/minimatch/package.json
        new file mode 100644
        index 0000000..b7c2484
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/package.json
        @@ -0,0 +1,56 @@
        +{
        +  "author": {
        +    "name": "Isaac Z. Schlueter",
        +    "email": "i@izs.me",
        +    "url": "http://blog.izs.me"
        +  },
        +  "name": "minimatch",
        +  "description": "a glob matcher in javascript",
        +  "version": "0.2.14",
        +  "repository": {
        +    "type": "git",
        +    "url": "git://github.com/isaacs/minimatch.git"
        +  },
        +  "main": "minimatch.js",
        +  "scripts": {
        +    "test": "tap test/*.js"
        +  },
        +  "engines": {
        +    "node": "*"
        +  },
        +  "dependencies": {
        +    "lru-cache": "2",
        +    "sigmund": "~1.0.0"
        +  },
        +  "devDependencies": {
        +    "tap": ""
        +  },
        +  "license": {
        +    "type": "MIT",
        +    "url": "http://github.com/isaacs/minimatch/raw/master/LICENSE"
        +  },
        +  "bugs": {
        +    "url": "https://github.com/isaacs/minimatch/issues"
        +  },
        +  "homepage": "https://github.com/isaacs/minimatch",
        +  "_id": "minimatch@0.2.14",
        +  "dist": {
        +    "shasum": "c74e780574f63c6f9a090e90efbe6ef53a6a756a",
        +    "tarball": "http://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz"
        +  },
        +  "_from": "minimatch@>=0.2.11 <0.3.0",
        +  "_npmVersion": "1.3.17",
        +  "_npmUser": {
        +    "name": "isaacs",
        +    "email": "i@izs.me"
        +  },
        +  "maintainers": [
        +    {
        +      "name": "isaacs",
        +      "email": "i@izs.me"
        +    }
        +  ],
        +  "directories": {},
        +  "_shasum": "c74e780574f63c6f9a090e90efbe6ef53a6a756a",
        +  "_resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz"
        +}
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/basic.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/basic.js
        new file mode 100644
        index 0000000..ae7ac73
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/basic.js
        @@ -0,0 +1,399 @@
        +// http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test
        +//
        +// TODO: Some of these tests do very bad things with backslashes, and will
        +// most likely fail badly on windows.  They should probably be skipped.
        +
        +var tap = require("tap")
        +  , globalBefore = Object.keys(global)
        +  , mm = require("../")
        +  , files = [ "a", "b", "c", "d", "abc"
        +            , "abd", "abe", "bb", "bcd"
        +            , "ca", "cb", "dd", "de"
        +            , "bdir/", "bdir/cfile"]
        +  , next = files.concat([ "a-b", "aXb"
        +                        , ".x", ".y" ])
        +
        +
        +var patterns =
        +  [ "http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test"
        +  , ["a*", ["a", "abc", "abd", "abe"]]
        +  , ["X*", ["X*"], {nonull: true}]
        +
        +  // allow null glob expansion
        +  , ["X*", []]
        +
        +  // isaacs: Slightly different than bash/sh/ksh
        +  // \\* is not un-escaped to literal "*" in a failed match,
        +  // but it does make it get treated as a literal star
        +  , ["\\*", ["\\*"], {nonull: true}]
        +  , ["\\**", ["\\**"], {nonull: true}]
        +  , ["\\*\\*", ["\\*\\*"], {nonull: true}]
        +
        +  , ["b*/", ["bdir/"]]
        +  , ["c*", ["c", "ca", "cb"]]
        +  , ["**", files]
        +
        +  , ["\\.\\./*/", ["\\.\\./*/"], {nonull: true}]
        +  , ["s/\\..*//", ["s/\\..*//"], {nonull: true}]
        +
        +  , "legendary larry crashes bashes"
        +  , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"
        +    , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"], {nonull: true}]
        +  , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"
        +    , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"], {nonull: true}]
        +
        +  , "character classes"
        +  , ["[a-c]b*", ["abc", "abd", "abe", "bb", "cb"]]
        +  , ["[a-y]*[^c]", ["abd", "abe", "bb", "bcd",
        +     "bdir/", "ca", "cb", "dd", "de"]]
        +  , ["a*[^c]", ["abd", "abe"]]
        +  , function () { files.push("a-b", "aXb") }
        +  , ["a[X-]b", ["a-b", "aXb"]]
        +  , function () { files.push(".x", ".y") }
        +  , ["[^a-c]*", ["d", "dd", "de"]]
        +  , function () { files.push("a*b/", "a*b/ooo") }
        +  , ["a\\*b/*", ["a*b/ooo"]]
        +  , ["a\\*?/*", ["a*b/ooo"]]
        +  , ["*\\\\!*", [], {null: true}, ["echo !7"]]
        +  , ["*\\!*", ["echo !7"], null, ["echo !7"]]
        +  , ["*.\\*", ["r.*"], null, ["r.*"]]
        +  , ["a[b]c", ["abc"]]
        +  , ["a[\\b]c", ["abc"]]
        +  , ["a?c", ["abc"]]
        +  , ["a\\*c", [], {null: true}, ["abc"]]
        +  , ["", [""], { null: true }, [""]]
        +
        +  , "http://www.opensource.apple.com/source/bash/bash-23/" +
        +    "bash/tests/glob-test"
        +  , function () { files.push("man/", "man/man1/", "man/man1/bash.1") }
        +  , ["*/man*/bash.*", ["man/man1/bash.1"]]
        +  , ["man/man1/bash.1", ["man/man1/bash.1"]]
        +  , ["a***c", ["abc"], null, ["abc"]]
        +  , ["a*****?c", ["abc"], null, ["abc"]]
        +  , ["?*****??", ["abc"], null, ["abc"]]
        +  , ["*****??", ["abc"], null, ["abc"]]
        +  , ["?*****?c", ["abc"], null, ["abc"]]
        +  , ["?***?****c", ["abc"], null, ["abc"]]
        +  , ["?***?****?", ["abc"], null, ["abc"]]
        +  , ["?***?****", ["abc"], null, ["abc"]]
        +  , ["*******c", ["abc"], null, ["abc"]]
        +  , ["*******?", ["abc"], null, ["abc"]]
        +  , ["a*cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
        +  , ["a**?**cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
        +  , ["a**?**cd**?**??k***", ["abcdecdhjk"], null, ["abcdecdhjk"]]
        +  , ["a**?**cd**?**??***k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
        +  , ["a**?**cd**?**??***k**", ["abcdecdhjk"], null, ["abcdecdhjk"]]
        +  , ["a****c**?**??*****", ["abcdecdhjk"], null, ["abcdecdhjk"]]
        +  , ["[-abc]", ["-"], null, ["-"]]
        +  , ["[abc-]", ["-"], null, ["-"]]
        +  , ["\\", ["\\"], null, ["\\"]]
        +  , ["[\\\\]", ["\\"], null, ["\\"]]
        +  , ["[[]", ["["], null, ["["]]
        +  , ["[", ["["], null, ["["]]
        +  , ["[*", ["[abc"], null, ["[abc"]]
        +  , "a right bracket shall lose its special meaning and\n" +
        +    "represent itself in a bracket expression if it occurs\n" +
        +    "first in the list.  -- POSIX.2 2.8.3.2"
        +  , ["[]]", ["]"], null, ["]"]]
        +  , ["[]-]", ["]"], null, ["]"]]
        +  , ["[a-\z]", ["p"], null, ["p"]]
        +  , ["??**********?****?", [], { null: true }, ["abc"]]
        +  , ["??**********?****c", [], { null: true }, ["abc"]]
        +  , ["?************c****?****", [], { null: true }, ["abc"]]
        +  , ["*c*?**", [], { null: true }, ["abc"]]
        +  , ["a*****c*?**", [], { null: true }, ["abc"]]
        +  , ["a********???*******", [], { null: true }, ["abc"]]
        +  , ["[]", [], { null: true }, ["a"]]
        +  , ["[abc", [], { null: true }, ["["]]
        +
        +  , "nocase tests"
        +  , ["XYZ", ["xYz"], { nocase: true, null: true }
        +    , ["xYz", "ABC", "IjK"]]
        +  , ["ab*", ["ABC"], { nocase: true, null: true }
        +    , ["xYz", "ABC", "IjK"]]
        +  , ["[ia]?[ck]", ["ABC", "IjK"], { nocase: true, null: true }
        +    , ["xYz", "ABC", "IjK"]]
        +
        +  // [ pattern, [matches], MM opts, files, TAP opts]
        +  , "onestar/twostar"
        +  , ["{/*,*}", [], {null: true}, ["/asdf/asdf/asdf"]]
        +  , ["{/?,*}", ["/a", "bb"], {null: true}
        +    , ["/a", "/b/b", "/a/b/c", "bb"]]
        +
        +  , "dots should not match unless requested"
        +  , ["**", ["a/b"], {}, ["a/b", "a/.d", ".a/.d"]]
        +
        +  // .. and . can only match patterns starting with .,
        +  // even when options.dot is set.
        +  , function () {
        +      files = ["a/./b", "a/../b", "a/c/b", "a/.d/b"]
        +    }
        +  , ["a/*/b", ["a/c/b", "a/.d/b"], {dot: true}]
        +  , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: true}]
        +  , ["a/*/b", ["a/c/b"], {dot:false}]
        +  , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: false}]
        +
        +
        +  // this also tests that changing the options needs
        +  // to change the cache key, even if the pattern is
        +  // the same!
        +  , ["**", ["a/b","a/.d",".a/.d"], { dot: true }
        +    , [ ".a/.d", "a/.d", "a/b"]]
        +
        +  , "paren sets cannot contain slashes"
        +  , ["*(a/b)", ["*(a/b)"], {nonull: true}, ["a/b"]]
        +
        +  // brace sets trump all else.
        +  //
        +  // invalid glob pattern.  fails on bash4 and bsdglob.
        +  // however, in this implementation, it's easier just
        +  // to do the intuitive thing, and let brace-expansion
        +  // actually come before parsing any extglob patterns,
        +  // like the documentation seems to say.
        +  //
        +  // XXX: if anyone complains about this, either fix it
        +  // or tell them to grow up and stop complaining.
        +  //
        +  // bash/bsdglob says this:
        +  // , ["*(a|{b),c)}", ["*(a|{b),c)}"], {}, ["a", "ab", "ac", "ad"]]
        +  // but we do this instead:
        +  , ["*(a|{b),c)}", ["a", "ab", "ac"], {}, ["a", "ab", "ac", "ad"]]
        +
        +  // test partial parsing in the presence of comment/negation chars
        +  , ["[!a*", ["[!ab"], {}, ["[!ab", "[ab"]]
        +  , ["[#a*", ["[#ab"], {}, ["[#ab", "[ab"]]
        +
        +  // like: {a,b|c\\,d\\\|e} except it's unclosed, so it has to be escaped.
        +  , ["+(a|*\\|c\\\\|d\\\\\\|e\\\\\\\\|f\\\\\\\\\\|g"
        +    , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g"]
        +    , {}
        +    , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g", "a", "b\\c"]]
        +
        +
        +  // crazy nested {,,} and *(||) tests.
        +  , function () {
        +      files = [ "a", "b", "c", "d"
        +              , "ab", "ac", "ad"
        +              , "bc", "cb"
        +              , "bc,d", "c,db", "c,d"
        +              , "d)", "(b|c", "*(b|c"
        +              , "b|c", "b|cc", "cb|c"
        +              , "x(a|b|c)", "x(a|c)"
        +              , "(a|b|c)", "(a|c)"]
        +    }
        +  , ["*(a|{b,c})", ["a", "b", "c", "ab", "ac"]]
        +  , ["{a,*(b|c,d)}", ["a","(b|c", "*(b|c", "d)"]]
        +  // a
        +  // *(b|c)
        +  // *(b|d)
        +  , ["{a,*(b|{c,d})}", ["a","b", "bc", "cb", "c", "d"]]
        +  , ["*(a|{b|c,c})", ["a", "b", "c", "ab", "ac", "bc", "cb"]]
        +
        +
        +  // test various flag settings.
        +  , [ "*(a|{b|c,c})", ["x(a|b|c)", "x(a|c)", "(a|b|c)", "(a|c)"]
        +    , { noext: true } ]
        +  , ["a?b", ["x/y/acb", "acb/"], {matchBase: true}
        +    , ["x/y/acb", "acb/", "acb/d/e", "x/y/acb/d"] ]
        +  , ["#*", ["#a", "#b"], {nocomment: true}, ["#a", "#b", "c#d"]]
        +
        +
        +  // begin channelling Boole and deMorgan...
        +  , "negation tests"
        +  , function () {
        +      files = ["d", "e", "!ab", "!abc", "a!b", "\\!a"]
        +    }
        +
        +  // anything that is NOT a* matches.
        +  , ["!a*", ["\\!a", "d", "e", "!ab", "!abc"]]
        +
        +  // anything that IS !a* matches.
        +  , ["!a*", ["!ab", "!abc"], {nonegate: true}]
        +
        +  // anything that IS a* matches
        +  , ["!!a*", ["a!b"]]
        +
        +  // anything that is NOT !a* matches
        +  , ["!\\!a*", ["a!b", "d", "e", "\\!a"]]
        +
        +  // negation nestled within a pattern
        +  , function () {
        +      files = [ "foo.js"
        +              , "foo.bar"
        +              // can't match this one without negative lookbehind.
        +              , "foo.js.js"
        +              , "blar.js"
        +              , "foo."
        +              , "boo.js.boo" ]
        +    }
        +  , ["*.!(js)", ["foo.bar", "foo.", "boo.js.boo"] ]
        +
        +  // https://github.com/isaacs/minimatch/issues/5
        +  , function () {
        +      files = [ 'a/b/.x/c'
        +              , 'a/b/.x/c/d'
        +              , 'a/b/.x/c/d/e'
        +              , 'a/b/.x'
        +              , 'a/b/.x/'
        +              , 'a/.x/b'
        +              , '.x'
        +              , '.x/'
        +              , '.x/a'
        +              , '.x/a/b'
        +              , 'a/.x/b/.x/c'
        +              , '.x/.x' ]
        +  }
        +  , ["**/.x/**", [ '.x/'
        +                 , '.x/a'
        +                 , '.x/a/b'
        +                 , 'a/.x/b'
        +                 , 'a/b/.x/'
        +                 , 'a/b/.x/c'
        +                 , 'a/b/.x/c/d'
        +                 , 'a/b/.x/c/d/e' ] ]
        +
        +  ]
        +
        +var regexps =
        +  [ '/^(?:(?=.)a[^/]*?)$/',
        +    '/^(?:(?=.)X[^/]*?)$/',
        +    '/^(?:(?=.)X[^/]*?)$/',
        +    '/^(?:\\*)$/',
        +    '/^(?:(?=.)\\*[^/]*?)$/',
        +    '/^(?:\\*\\*)$/',
        +    '/^(?:(?=.)b[^/]*?\\/)$/',
        +    '/^(?:(?=.)c[^/]*?)$/',
        +    '/^(?:(?:(?!(?:\\/|^)\\.).)*?)$/',
        +    '/^(?:\\.\\.\\/(?!\\.)(?=.)[^/]*?\\/)$/',
        +    '/^(?:s\\/(?=.)\\.\\.[^/]*?\\/)$/',
        +    '/^(?:\\/\\^root:\\/\\{s\\/(?=.)\\^[^:][^/]*?:[^:][^/]*?:\\([^:]\\)[^/]*?\\.[^/]*?\\$\\/1\\/)$/',
        +    '/^(?:\\/\\^root:\\/\\{s\\/(?=.)\\^[^:][^/]*?:[^:][^/]*?:\\([^:]\\)[^/]*?\\.[^/]*?\\$\\/\u0001\\/)$/',
        +    '/^(?:(?!\\.)(?=.)[a-c]b[^/]*?)$/',
        +    '/^(?:(?!\\.)(?=.)[a-y][^/]*?[^c])$/',
        +    '/^(?:(?=.)a[^/]*?[^c])$/',
        +    '/^(?:(?=.)a[X-]b)$/',
        +    '/^(?:(?!\\.)(?=.)[^a-c][^/]*?)$/',
        +    '/^(?:a\\*b\\/(?!\\.)(?=.)[^/]*?)$/',
        +    '/^(?:(?=.)a\\*[^/]\\/(?!\\.)(?=.)[^/]*?)$/',
        +    '/^(?:(?!\\.)(?=.)[^/]*?\\\\\\![^/]*?)$/',
        +    '/^(?:(?!\\.)(?=.)[^/]*?\\![^/]*?)$/',
        +    '/^(?:(?!\\.)(?=.)[^/]*?\\.\\*)$/',
        +    '/^(?:(?=.)a[b]c)$/',
        +    '/^(?:(?=.)a[b]c)$/',
        +    '/^(?:(?=.)a[^/]c)$/',
        +    '/^(?:a\\*c)$/',
        +    'false',
        +    '/^(?:(?!\\.)(?=.)[^/]*?\\/(?=.)man[^/]*?\\/(?=.)bash\\.[^/]*?)$/',
        +    '/^(?:man\\/man1\\/bash\\.1)$/',
        +    '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?c)$/',
        +    '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]c)$/',
        +    '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/])$/',
        +    '/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/])$/',
        +    '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]c)$/',
        +    '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?c)$/',
        +    '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/])$/',
        +    '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?)$/',
        +    '/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c)$/',
        +    '/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/])$/',
        +    '/^(?:(?=.)a[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k)$/',
        +    '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k)$/',
        +    '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k[^/]*?[^/]*?[^/]*?)$/',
        +    '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?k)$/',
        +    '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?k[^/]*?[^/]*?)$/',
        +    '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?)$/',
        +    '/^(?:(?!\\.)(?=.)[-abc])$/',
        +    '/^(?:(?!\\.)(?=.)[abc-])$/',
        +    '/^(?:\\\\)$/',
        +    '/^(?:(?!\\.)(?=.)[\\\\])$/',
        +    '/^(?:(?!\\.)(?=.)[\\[])$/',
        +    '/^(?:\\[)$/',
        +    '/^(?:(?=.)\\[(?!\\.)(?=.)[^/]*?)$/',
        +    '/^(?:(?!\\.)(?=.)[\\]])$/',
        +    '/^(?:(?!\\.)(?=.)[\\]-])$/',
        +    '/^(?:(?!\\.)(?=.)[a-z])$/',
        +    '/^(?:(?!\\.)(?=.)[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/])$/',
        +    '/^(?:(?!\\.)(?=.)[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?c)$/',
        +    '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?)$/',
        +    '/^(?:(?!\\.)(?=.)[^/]*?c[^/]*?[^/][^/]*?[^/]*?)$/',
        +    '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/][^/]*?[^/]*?)$/',
        +    '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?)$/',
        +    '/^(?:\\[\\])$/',
        +    '/^(?:\\[abc)$/',
        +    '/^(?:(?=.)XYZ)$/i',
        +    '/^(?:(?=.)ab[^/]*?)$/i',
        +    '/^(?:(?!\\.)(?=.)[ia][^/][ck])$/i',
        +    '/^(?:\\/(?!\\.)(?=.)[^/]*?|(?!\\.)(?=.)[^/]*?)$/',
        +    '/^(?:\\/(?!\\.)(?=.)[^/]|(?!\\.)(?=.)[^/]*?)$/',
        +    '/^(?:(?:(?!(?:\\/|^)\\.).)*?)$/',
        +    '/^(?:a\\/(?!(?:^|\\/)\\.{1,2}(?:$|\\/))(?=.)[^/]*?\\/b)$/',
        +    '/^(?:a\\/(?=.)\\.[^/]*?\\/b)$/',
        +    '/^(?:a\\/(?!\\.)(?=.)[^/]*?\\/b)$/',
        +    '/^(?:a\\/(?=.)\\.[^/]*?\\/b)$/',
        +    '/^(?:(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?)$/',
        +    '/^(?:(?!\\.)(?=.)[^/]*?\\(a\\/b\\))$/',
        +    '/^(?:(?!\\.)(?=.)(?:a|b)*|(?!\\.)(?=.)(?:a|c)*)$/',
        +    '/^(?:(?=.)\\[(?=.)\\!a[^/]*?)$/',
        +    '/^(?:(?=.)\\[(?=.)#a[^/]*?)$/',
        +    '/^(?:(?=.)\\+\\(a\\|[^/]*?\\|c\\\\\\\\\\|d\\\\\\\\\\|e\\\\\\\\\\\\\\\\\\|f\\\\\\\\\\\\\\\\\\|g)$/',
        +    '/^(?:(?!\\.)(?=.)(?:a|b)*|(?!\\.)(?=.)(?:a|c)*)$/',
        +    '/^(?:a|(?!\\.)(?=.)[^/]*?\\(b\\|c|d\\))$/',
        +    '/^(?:a|(?!\\.)(?=.)(?:b|c)*|(?!\\.)(?=.)(?:b|d)*)$/',
        +    '/^(?:(?!\\.)(?=.)(?:a|b|c)*|(?!\\.)(?=.)(?:a|c)*)$/',
        +    '/^(?:(?!\\.)(?=.)[^/]*?\\(a\\|b\\|c\\)|(?!\\.)(?=.)[^/]*?\\(a\\|c\\))$/',
        +    '/^(?:(?=.)a[^/]b)$/',
        +    '/^(?:(?=.)#[^/]*?)$/',
        +    '/^(?!^(?:(?=.)a[^/]*?)$).*$/',
        +    '/^(?:(?=.)\\!a[^/]*?)$/',
        +    '/^(?:(?=.)a[^/]*?)$/',
        +    '/^(?!^(?:(?=.)\\!a[^/]*?)$).*$/',
        +    '/^(?:(?!\\.)(?=.)[^/]*?\\.(?:(?!js)[^/]*?))$/',
        +    '/^(?:(?:(?!(?:\\/|^)\\.).)*?\\/\\.x\\/(?:(?!(?:\\/|^)\\.).)*?)$/' ]
        +var re = 0;
        +
        +tap.test("basic tests", function (t) {
        +  var start = Date.now()
        +
        +  // [ pattern, [matches], MM opts, files, TAP opts]
        +  patterns.forEach(function (c) {
        +    if (typeof c === "function") return c()
        +    if (typeof c === "string") return t.comment(c)
        +
        +    var pattern = c[0]
        +      , expect = c[1].sort(alpha)
        +      , options = c[2] || {}
        +      , f = c[3] || files
        +      , tapOpts = c[4] || {}
        +
        +    // options.debug = true
        +    var m = new mm.Minimatch(pattern, options)
        +    var r = m.makeRe()
        +    var expectRe = regexps[re++]
        +    tapOpts.re = String(r) || JSON.stringify(r)
        +    tapOpts.files = JSON.stringify(f)
        +    tapOpts.pattern = pattern
        +    tapOpts.set = m.set
        +    tapOpts.negated = m.negate
        +
        +    var actual = mm.match(f, pattern, options)
        +    actual.sort(alpha)
        +
        +    t.equivalent( actual, expect
        +                , JSON.stringify(pattern) + " " + JSON.stringify(expect)
        +                , tapOpts )
        +
        +    t.equal(tapOpts.re, expectRe, tapOpts)
        +  })
        +
        +  t.comment("time=" + (Date.now() - start) + "ms")
        +  t.end()
        +})
        +
        +tap.test("global leak test", function (t) {
        +  var globalAfter = Object.keys(global)
        +  t.equivalent(globalAfter, globalBefore, "no new globals, please")
        +  t.end()
        +})
        +
        +function alpha (a, b) {
        +  return a > b ? 1 : -1
        +}
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/brace-expand.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/brace-expand.js
        new file mode 100644
        index 0000000..7ee278a
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/brace-expand.js
        @@ -0,0 +1,33 @@
        +var tap = require("tap")
        +  , minimatch = require("../")
        +
        +tap.test("brace expansion", function (t) {
        +  // [ pattern, [expanded] ]
        +  ; [ [ "a{b,c{d,e},{f,g}h}x{y,z}"
        +      , [ "abxy"
        +        , "abxz"
        +        , "acdxy"
        +        , "acdxz"
        +        , "acexy"
        +        , "acexz"
        +        , "afhxy"
        +        , "afhxz"
        +        , "aghxy"
        +        , "aghxz" ] ]
        +    , [ "a{1..5}b"
        +      , [ "a1b"
        +        , "a2b"
        +        , "a3b"
        +        , "a4b"
        +        , "a5b" ] ]
        +    , [ "a{b}c", ["a{b}c"] ]
        +  ].forEach(function (tc) {
        +    var p = tc[0]
        +      , expect = tc[1]
        +    t.equivalent(minimatch.braceExpand(p), expect, p)
        +  })
        +  console.error("ending")
        +  t.end()
        +})
        +
        +
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/caching.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/caching.js
        new file mode 100644
        index 0000000..0fec4b0
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/caching.js
        @@ -0,0 +1,14 @@
        +var Minimatch = require("../minimatch.js").Minimatch
        +var tap = require("tap")
        +tap.test("cache test", function (t) {
        +  var mm1 = new Minimatch("a?b")
        +  var mm2 = new Minimatch("a?b")
        +  t.equal(mm1, mm2, "should get the same object")
        +  // the lru should drop it after 100 entries
        +  for (var i = 0; i < 100; i ++) {
        +    new Minimatch("a"+i)
        +  }
        +  mm2 = new Minimatch("a?b")
        +  t.notEqual(mm1, mm2, "cache should have dropped")
        +  t.end()
        +})
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/defaults.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/defaults.js
        new file mode 100644
        index 0000000..25f1f60
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/defaults.js
        @@ -0,0 +1,274 @@
        +// http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test
        +//
        +// TODO: Some of these tests do very bad things with backslashes, and will
        +// most likely fail badly on windows.  They should probably be skipped.
        +
        +var tap = require("tap")
        +  , globalBefore = Object.keys(global)
        +  , mm = require("../")
        +  , files = [ "a", "b", "c", "d", "abc"
        +            , "abd", "abe", "bb", "bcd"
        +            , "ca", "cb", "dd", "de"
        +            , "bdir/", "bdir/cfile"]
        +  , next = files.concat([ "a-b", "aXb"
        +                        , ".x", ".y" ])
        +
        +tap.test("basic tests", function (t) {
        +  var start = Date.now()
        +
        +  // [ pattern, [matches], MM opts, files, TAP opts]
        +  ; [ "http://www.bashcookbook.com/bashinfo" +
        +      "/source/bash-1.14.7/tests/glob-test"
        +    , ["a*", ["a", "abc", "abd", "abe"]]
        +    , ["X*", ["X*"], {nonull: true}]
        +
        +    // allow null glob expansion
        +    , ["X*", []]
        +
        +    // isaacs: Slightly different than bash/sh/ksh
        +    // \\* is not un-escaped to literal "*" in a failed match,
        +    // but it does make it get treated as a literal star
        +    , ["\\*", ["\\*"], {nonull: true}]
        +    , ["\\**", ["\\**"], {nonull: true}]
        +    , ["\\*\\*", ["\\*\\*"], {nonull: true}]
        +
        +    , ["b*/", ["bdir/"]]
        +    , ["c*", ["c", "ca", "cb"]]
        +    , ["**", files]
        +
        +    , ["\\.\\./*/", ["\\.\\./*/"], {nonull: true}]
        +    , ["s/\\..*//", ["s/\\..*//"], {nonull: true}]
        +
        +    , "legendary larry crashes bashes"
        +    , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"
        +      , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"], {nonull: true}]
        +    , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"
        +      , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"], {nonull: true}]
        +
        +    , "character classes"
        +    , ["[a-c]b*", ["abc", "abd", "abe", "bb", "cb"]]
        +    , ["[a-y]*[^c]", ["abd", "abe", "bb", "bcd",
        +       "bdir/", "ca", "cb", "dd", "de"]]
        +    , ["a*[^c]", ["abd", "abe"]]
        +    , function () { files.push("a-b", "aXb") }
        +    , ["a[X-]b", ["a-b", "aXb"]]
        +    , function () { files.push(".x", ".y") }
        +    , ["[^a-c]*", ["d", "dd", "de"]]
        +    , function () { files.push("a*b/", "a*b/ooo") }
        +    , ["a\\*b/*", ["a*b/ooo"]]
        +    , ["a\\*?/*", ["a*b/ooo"]]
        +    , ["*\\\\!*", [], {null: true}, ["echo !7"]]
        +    , ["*\\!*", ["echo !7"], null, ["echo !7"]]
        +    , ["*.\\*", ["r.*"], null, ["r.*"]]
        +    , ["a[b]c", ["abc"]]
        +    , ["a[\\b]c", ["abc"]]
        +    , ["a?c", ["abc"]]
        +    , ["a\\*c", [], {null: true}, ["abc"]]
        +    , ["", [""], { null: true }, [""]]
        +
        +    , "http://www.opensource.apple.com/source/bash/bash-23/" +
        +      "bash/tests/glob-test"
        +    , function () { files.push("man/", "man/man1/", "man/man1/bash.1") }
        +    , ["*/man*/bash.*", ["man/man1/bash.1"]]
        +    , ["man/man1/bash.1", ["man/man1/bash.1"]]
        +    , ["a***c", ["abc"], null, ["abc"]]
        +    , ["a*****?c", ["abc"], null, ["abc"]]
        +    , ["?*****??", ["abc"], null, ["abc"]]
        +    , ["*****??", ["abc"], null, ["abc"]]
        +    , ["?*****?c", ["abc"], null, ["abc"]]
        +    , ["?***?****c", ["abc"], null, ["abc"]]
        +    , ["?***?****?", ["abc"], null, ["abc"]]
        +    , ["?***?****", ["abc"], null, ["abc"]]
        +    , ["*******c", ["abc"], null, ["abc"]]
        +    , ["*******?", ["abc"], null, ["abc"]]
        +    , ["a*cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
        +    , ["a**?**cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
        +    , ["a**?**cd**?**??k***", ["abcdecdhjk"], null, ["abcdecdhjk"]]
        +    , ["a**?**cd**?**??***k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
        +    , ["a**?**cd**?**??***k**", ["abcdecdhjk"], null, ["abcdecdhjk"]]
        +    , ["a****c**?**??*****", ["abcdecdhjk"], null, ["abcdecdhjk"]]
        +    , ["[-abc]", ["-"], null, ["-"]]
        +    , ["[abc-]", ["-"], null, ["-"]]
        +    , ["\\", ["\\"], null, ["\\"]]
        +    , ["[\\\\]", ["\\"], null, ["\\"]]
        +    , ["[[]", ["["], null, ["["]]
        +    , ["[", ["["], null, ["["]]
        +    , ["[*", ["[abc"], null, ["[abc"]]
        +    , "a right bracket shall lose its special meaning and\n" +
        +      "represent itself in a bracket expression if it occurs\n" +
        +      "first in the list.  -- POSIX.2 2.8.3.2"
        +    , ["[]]", ["]"], null, ["]"]]
        +    , ["[]-]", ["]"], null, ["]"]]
        +    , ["[a-\z]", ["p"], null, ["p"]]
        +    , ["??**********?****?", [], { null: true }, ["abc"]]
        +    , ["??**********?****c", [], { null: true }, ["abc"]]
        +    , ["?************c****?****", [], { null: true }, ["abc"]]
        +    , ["*c*?**", [], { null: true }, ["abc"]]
        +    , ["a*****c*?**", [], { null: true }, ["abc"]]
        +    , ["a********???*******", [], { null: true }, ["abc"]]
        +    , ["[]", [], { null: true }, ["a"]]
        +    , ["[abc", [], { null: true }, ["["]]
        +
        +    , "nocase tests"
        +    , ["XYZ", ["xYz"], { nocase: true, null: true }
        +      , ["xYz", "ABC", "IjK"]]
        +    , ["ab*", ["ABC"], { nocase: true, null: true }
        +      , ["xYz", "ABC", "IjK"]]
        +    , ["[ia]?[ck]", ["ABC", "IjK"], { nocase: true, null: true }
        +      , ["xYz", "ABC", "IjK"]]
        +
        +    // [ pattern, [matches], MM opts, files, TAP opts]
        +    , "onestar/twostar"
        +    , ["{/*,*}", [], {null: true}, ["/asdf/asdf/asdf"]]
        +    , ["{/?,*}", ["/a", "bb"], {null: true}
        +      , ["/a", "/b/b", "/a/b/c", "bb"]]
        +
        +    , "dots should not match unless requested"
        +    , ["**", ["a/b"], {}, ["a/b", "a/.d", ".a/.d"]]
        +
        +    // .. and . can only match patterns starting with .,
        +    // even when options.dot is set.
        +    , function () {
        +        files = ["a/./b", "a/../b", "a/c/b", "a/.d/b"]
        +      }
        +    , ["a/*/b", ["a/c/b", "a/.d/b"], {dot: true}]
        +    , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: true}]
        +    , ["a/*/b", ["a/c/b"], {dot:false}]
        +    , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: false}]
        +
        +
        +    // this also tests that changing the options needs
        +    // to change the cache key, even if the pattern is
        +    // the same!
        +    , ["**", ["a/b","a/.d",".a/.d"], { dot: true }
        +      , [ ".a/.d", "a/.d", "a/b"]]
        +
        +    , "paren sets cannot contain slashes"
        +    , ["*(a/b)", ["*(a/b)"], {nonull: true}, ["a/b"]]
        +
        +    // brace sets trump all else.
        +    //
        +    // invalid glob pattern.  fails on bash4 and bsdglob.
        +    // however, in this implementation, it's easier just
        +    // to do the intuitive thing, and let brace-expansion
        +    // actually come before parsing any extglob patterns,
        +    // like the documentation seems to say.
        +    //
        +    // XXX: if anyone complains about this, either fix it
        +    // or tell them to grow up and stop complaining.
        +    //
        +    // bash/bsdglob says this:
        +    // , ["*(a|{b),c)}", ["*(a|{b),c)}"], {}, ["a", "ab", "ac", "ad"]]
        +    // but we do this instead:
        +    , ["*(a|{b),c)}", ["a", "ab", "ac"], {}, ["a", "ab", "ac", "ad"]]
        +
        +    // test partial parsing in the presence of comment/negation chars
        +    , ["[!a*", ["[!ab"], {}, ["[!ab", "[ab"]]
        +    , ["[#a*", ["[#ab"], {}, ["[#ab", "[ab"]]
        +
        +    // like: {a,b|c\\,d\\\|e} except it's unclosed, so it has to be escaped.
        +    , ["+(a|*\\|c\\\\|d\\\\\\|e\\\\\\\\|f\\\\\\\\\\|g"
        +      , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g"]
        +      , {}
        +      , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g", "a", "b\\c"]]
        +
        +
        +    // crazy nested {,,} and *(||) tests.
        +    , function () {
        +        files = [ "a", "b", "c", "d"
        +                , "ab", "ac", "ad"
        +                , "bc", "cb"
        +                , "bc,d", "c,db", "c,d"
        +                , "d)", "(b|c", "*(b|c"
        +                , "b|c", "b|cc", "cb|c"
        +                , "x(a|b|c)", "x(a|c)"
        +                , "(a|b|c)", "(a|c)"]
        +      }
        +    , ["*(a|{b,c})", ["a", "b", "c", "ab", "ac"]]
        +    , ["{a,*(b|c,d)}", ["a","(b|c", "*(b|c", "d)"]]
        +    // a
        +    // *(b|c)
        +    // *(b|d)
        +    , ["{a,*(b|{c,d})}", ["a","b", "bc", "cb", "c", "d"]]
        +    , ["*(a|{b|c,c})", ["a", "b", "c", "ab", "ac", "bc", "cb"]]
        +
        +
        +    // test various flag settings.
        +    , [ "*(a|{b|c,c})", ["x(a|b|c)", "x(a|c)", "(a|b|c)", "(a|c)"]
        +      , { noext: true } ]
        +    , ["a?b", ["x/y/acb", "acb/"], {matchBase: true}
        +      , ["x/y/acb", "acb/", "acb/d/e", "x/y/acb/d"] ]
        +    , ["#*", ["#a", "#b"], {nocomment: true}, ["#a", "#b", "c#d"]]
        +
        +
        +    // begin channelling Boole and deMorgan...
        +    , "negation tests"
        +    , function () {
        +        files = ["d", "e", "!ab", "!abc", "a!b", "\\!a"]
        +      }
        +
        +    // anything that is NOT a* matches.
        +    , ["!a*", ["\\!a", "d", "e", "!ab", "!abc"]]
        +
        +    // anything that IS !a* matches.
        +    , ["!a*", ["!ab", "!abc"], {nonegate: true}]
        +
        +    // anything that IS a* matches
        +    , ["!!a*", ["a!b"]]
        +
        +    // anything that is NOT !a* matches
        +    , ["!\\!a*", ["a!b", "d", "e", "\\!a"]]
        +
        +    // negation nestled within a pattern
        +    , function () {
        +        files = [ "foo.js"
        +                , "foo.bar"
        +                // can't match this one without negative lookbehind.
        +                , "foo.js.js"
        +                , "blar.js"
        +                , "foo."
        +                , "boo.js.boo" ]
        +      }
        +    , ["*.!(js)", ["foo.bar", "foo.", "boo.js.boo"] ]
        +
        +    ].forEach(function (c) {
        +      if (typeof c === "function") return c()
        +      if (typeof c === "string") return t.comment(c)
        +
        +      var pattern = c[0]
        +        , expect = c[1].sort(alpha)
        +        , options = c[2] || {}
        +        , f = c[3] || files
        +        , tapOpts = c[4] || {}
        +
        +      // options.debug = true
        +      var Class = mm.defaults(options).Minimatch
        +      var m = new Class(pattern, {})
        +      var r = m.makeRe()
        +      tapOpts.re = String(r) || JSON.stringify(r)
        +      tapOpts.files = JSON.stringify(f)
        +      tapOpts.pattern = pattern
        +      tapOpts.set = m.set
        +      tapOpts.negated = m.negate
        +
        +      var actual = mm.match(f, pattern, options)
        +      actual.sort(alpha)
        +
        +      t.equivalent( actual, expect
        +                  , JSON.stringify(pattern) + " " + JSON.stringify(expect)
        +                  , tapOpts )
        +    })
        +
        +  t.comment("time=" + (Date.now() - start) + "ms")
        +  t.end()
        +})
        +
        +tap.test("global leak test", function (t) {
        +  var globalAfter = Object.keys(global)
        +  t.equivalent(globalAfter, globalBefore, "no new globals, please")
        +  t.end()
        +})
        +
        +function alpha (a, b) {
        +  return a > b ? 1 : -1
        +}
        diff --git a/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/extglob-ending-with-state-char.js b/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/extglob-ending-with-state-char.js
        new file mode 100644
        index 0000000..6676e26
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/extglob-ending-with-state-char.js
        @@ -0,0 +1,8 @@
        +var test = require('tap').test
        +var minimatch = require('../')
        +
        +test('extglob ending with statechar', function(t) {
        +  t.notOk(minimatch('ax', 'a?(b*)'))
        +  t.ok(minimatch('ax', '?(a*|b)'))
        +  t.end()
        +})
        diff --git a/node_modules/mocha/node_modules/glob/package.json b/node_modules/mocha/node_modules/glob/package.json
        new file mode 100644
        index 0000000..b00d5b9
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/package.json
        @@ -0,0 +1,55 @@
        +{
        +  "author": {
        +    "name": "Isaac Z. Schlueter",
        +    "email": "i@izs.me",
        +    "url": "http://blog.izs.me/"
        +  },
        +  "name": "glob",
        +  "description": "a little globber",
        +  "version": "3.2.3",
        +  "repository": {
        +    "type": "git",
        +    "url": "git://github.com/isaacs/node-glob.git"
        +  },
        +  "main": "glob.js",
        +  "engines": {
        +    "node": "*"
        +  },
        +  "dependencies": {
        +    "minimatch": "~0.2.11",
        +    "graceful-fs": "~2.0.0",
        +    "inherits": "2"
        +  },
        +  "devDependencies": {
        +    "tap": "~0.4.0",
        +    "mkdirp": "0",
        +    "rimraf": "1"
        +  },
        +  "scripts": {
        +    "test": "tap test/*.js"
        +  },
        +  "license": "BSD",
        +  "bugs": {
        +    "url": "https://github.com/isaacs/node-glob/issues"
        +  },
        +  "_id": "glob@3.2.3",
        +  "dist": {
        +    "shasum": "e313eeb249c7affaa5c475286b0e115b59839467",
        +    "tarball": "http://registry.npmjs.org/glob/-/glob-3.2.3.tgz"
        +  },
        +  "_from": "glob@3.2.3",
        +  "_npmVersion": "1.3.2",
        +  "_npmUser": {
        +    "name": "isaacs",
        +    "email": "i@izs.me"
        +  },
        +  "maintainers": [
        +    {
        +      "name": "isaacs",
        +      "email": "i@izs.me"
        +    }
        +  ],
        +  "directories": {},
        +  "_shasum": "e313eeb249c7affaa5c475286b0e115b59839467",
        +  "_resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz"
        +}
        diff --git a/node_modules/mocha/node_modules/glob/test/00-setup.js b/node_modules/mocha/node_modules/glob/test/00-setup.js
        new file mode 100644
        index 0000000..245afaf
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/test/00-setup.js
        @@ -0,0 +1,176 @@
        +// just a little pre-run script to set up the fixtures.
        +// zz-finish cleans it up
        +
        +var mkdirp = require("mkdirp")
        +var path = require("path")
        +var i = 0
        +var tap = require("tap")
        +var fs = require("fs")
        +var rimraf = require("rimraf")
        +
        +var files =
        +[ "a/.abcdef/x/y/z/a"
        +, "a/abcdef/g/h"
        +, "a/abcfed/g/h"
        +, "a/b/c/d"
        +, "a/bc/e/f"
        +, "a/c/d/c/b"
        +, "a/cb/e/f"
        +]
        +
        +var symlinkTo = path.resolve(__dirname, "a/symlink/a/b/c")
        +var symlinkFrom = "../.."
        +
        +files = files.map(function (f) {
        +  return path.resolve(__dirname, f)
        +})
        +
        +tap.test("remove fixtures", function (t) {
        +  rimraf(path.resolve(__dirname, "a"), function (er) {
        +    t.ifError(er, "remove fixtures")
        +    t.end()
        +  })
        +})
        +
        +files.forEach(function (f) {
        +  tap.test(f, function (t) {
        +    var d = path.dirname(f)
        +    mkdirp(d, 0755, function (er) {
        +      if (er) {
        +        t.fail(er)
        +        return t.bailout()
        +      }
        +      fs.writeFile(f, "i like tests", function (er) {
        +        t.ifError(er, "make file")
        +        t.end()
        +      })
        +    })
        +  })
        +})
        +
        +if (process.platform !== "win32") {
        +  tap.test("symlinky", function (t) {
        +    var d = path.dirname(symlinkTo)
        +    console.error("mkdirp", d)
        +    mkdirp(d, 0755, function (er) {
        +      t.ifError(er)
        +      fs.symlink(symlinkFrom, symlinkTo, "dir", function (er) {
        +        t.ifError(er, "make symlink")
        +        t.end()
        +      })
        +    })
        +  })
        +}
        +
        +;["foo","bar","baz","asdf","quux","qwer","rewq"].forEach(function (w) {
        +  w = "/tmp/glob-test/" + w
        +  tap.test("create " + w, function (t) {
        +    mkdirp(w, function (er) {
        +      if (er)
        +        throw er
        +      t.pass(w)
        +      t.end()
        +    })
        +  })
        +})
        +
        +
        +// generate the bash pattern test-fixtures if possible
        +if (process.platform === "win32" || !process.env.TEST_REGEN) {
        +  console.error("Windows, or TEST_REGEN unset.  Using cached fixtures.")
        +  return
        +}
        +
        +var spawn = require("child_process").spawn;
        +var globs =
        +  // put more patterns here.
        +  // anything that would be directly in / should be in /tmp/glob-test
        +  ["test/a/*/+(c|g)/./d"
        +  ,"test/a/**/[cg]/../[cg]"
        +  ,"test/a/{b,c,d,e,f}/**/g"
        +  ,"test/a/b/**"
        +  ,"test/**/g"
        +  ,"test/a/abc{fed,def}/g/h"
        +  ,"test/a/abc{fed/g,def}/**/"
        +  ,"test/a/abc{fed/g,def}/**///**/"
        +  ,"test/**/a/**/"
        +  ,"test/+(a|b|c)/a{/,bc*}/**"
        +  ,"test/*/*/*/f"
        +  ,"test/**/f"
        +  ,"test/a/symlink/a/b/c/a/b/c/a/b/c//a/b/c////a/b/c/**/b/c/**"
        +  ,"{./*/*,/tmp/glob-test/*}"
        +  ,"{/tmp/glob-test/*,*}" // evil owl face!  how you taunt me!
        +  ,"test/a/!(symlink)/**"
        +  ]
        +var bashOutput = {}
        +var fs = require("fs")
        +
        +globs.forEach(function (pattern) {
        +  tap.test("generate fixture " + pattern, function (t) {
        +    var cmd = "shopt -s globstar && " +
        +              "shopt -s extglob && " +
        +              "shopt -s nullglob && " +
        +              // "shopt >&2; " +
        +              "eval \'for i in " + pattern + "; do echo $i; done\'"
        +    var cp = spawn("bash", ["-c", cmd], { cwd: path.dirname(__dirname) })
        +    var out = []
        +    cp.stdout.on("data", function (c) {
        +      out.push(c)
        +    })
        +    cp.stderr.pipe(process.stderr)
        +    cp.on("close", function (code) {
        +      out = flatten(out)
        +      if (!out)
        +        out = []
        +      else
        +        out = cleanResults(out.split(/\r*\n/))
        +
        +      bashOutput[pattern] = out
        +      t.notOk(code, "bash test should finish nicely")
        +      t.end()
        +    })
        +  })
        +})
        +
        +tap.test("save fixtures", function (t) {
        +  var fname = path.resolve(__dirname, "bash-results.json")
        +  var data = JSON.stringify(bashOutput, null, 2) + "\n"
        +  fs.writeFile(fname, data, function (er) {
        +    t.ifError(er)
        +    t.end()
        +  })
        +})
        +
        +function cleanResults (m) {
        +  // normalize discrepancies in ordering, duplication,
        +  // and ending slashes.
        +  return m.map(function (m) {
        +    return m.replace(/\/+/g, "/").replace(/\/$/, "")
        +  }).sort(alphasort).reduce(function (set, f) {
        +    if (f !== set[set.length - 1]) set.push(f)
        +    return set
        +  }, []).sort(alphasort).map(function (f) {
        +    // de-windows
        +    return (process.platform !== 'win32') ? f
        +           : f.replace(/^[a-zA-Z]:\\\\/, '/').replace(/\\/g, '/')
        +  })
        +}
        +
        +function flatten (chunks) {
        +  var s = 0
        +  chunks.forEach(function (c) { s += c.length })
        +  var out = new Buffer(s)
        +  s = 0
        +  chunks.forEach(function (c) {
        +    c.copy(out, s)
        +    s += c.length
        +  })
        +
        +  return out.toString().trim()
        +}
        +
        +function alphasort (a, b) {
        +  a = a.toLowerCase()
        +  b = b.toLowerCase()
        +  return a > b ? 1 : a < b ? -1 : 0
        +}
        diff --git a/node_modules/mocha/node_modules/glob/test/bash-comparison.js b/node_modules/mocha/node_modules/glob/test/bash-comparison.js
        new file mode 100644
        index 0000000..239ed1a
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/test/bash-comparison.js
        @@ -0,0 +1,63 @@
        +// basic test
        +// show that it does the same thing by default as the shell.
        +var tap = require("tap")
        +, child_process = require("child_process")
        +, bashResults = require("./bash-results.json")
        +, globs = Object.keys(bashResults)
        +, glob = require("../")
        +, path = require("path")
        +
        +// run from the root of the project
        +// this is usually where you're at anyway, but be sure.
        +process.chdir(path.resolve(__dirname, ".."))
        +
        +function alphasort (a, b) {
        +  a = a.toLowerCase()
        +  b = b.toLowerCase()
        +  return a > b ? 1 : a < b ? -1 : 0
        +}
        +
        +globs.forEach(function (pattern) {
        +  var expect = bashResults[pattern]
        +  // anything regarding the symlink thing will fail on windows, so just skip it
        +  if (process.platform === "win32" &&
        +      expect.some(function (m) {
        +        return /\/symlink\//.test(m)
        +      }))
        +    return
        +
        +  tap.test(pattern, function (t) {
        +    glob(pattern, function (er, matches) {
        +      if (er)
        +        throw er
        +
        +      // sort and unmark, just to match the shell results
        +      matches = cleanResults(matches)
        +
        +      t.deepEqual(matches, expect, pattern)
        +      t.end()
        +    })
        +  })
        +
        +  tap.test(pattern + " sync", function (t) {
        +    var matches = cleanResults(glob.sync(pattern))
        +
        +    t.deepEqual(matches, expect, "should match shell")
        +    t.end()
        +  })
        +})
        +
        +function cleanResults (m) {
        +  // normalize discrepancies in ordering, duplication,
        +  // and ending slashes.
        +  return m.map(function (m) {
        +    return m.replace(/\/+/g, "/").replace(/\/$/, "")
        +  }).sort(alphasort).reduce(function (set, f) {
        +    if (f !== set[set.length - 1]) set.push(f)
        +    return set
        +  }, []).sort(alphasort).map(function (f) {
        +    // de-windows
        +    return (process.platform !== 'win32') ? f
        +           : f.replace(/^[a-zA-Z]:[\/\\]+/, '/').replace(/[\\\/]+/g, '/')
        +  })
        +}
        diff --git a/node_modules/mocha/node_modules/glob/test/bash-results.json b/node_modules/mocha/node_modules/glob/test/bash-results.json
        new file mode 100644
        index 0000000..a9bc347
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/test/bash-results.json
        @@ -0,0 +1,350 @@
        +{
        +  "test/a/*/+(c|g)/./d": [
        +    "test/a/b/c/./d"
        +  ],
        +  "test/a/**/[cg]/../[cg]": [
        +    "test/a/abcdef/g/../g",
        +    "test/a/abcfed/g/../g",
        +    "test/a/b/c/../c",
        +    "test/a/c/../c",
        +    "test/a/c/d/c/../c",
        +    "test/a/symlink/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c"
        +  ],
        +  "test/a/{b,c,d,e,f}/**/g": [],
        +  "test/a/b/**": [
        +    "test/a/b",
        +    "test/a/b/c",
        +    "test/a/b/c/d"
        +  ],
        +  "test/**/g": [
        +    "test/a/abcdef/g",
        +    "test/a/abcfed/g"
        +  ],
        +  "test/a/abc{fed,def}/g/h": [
        +    "test/a/abcdef/g/h",
        +    "test/a/abcfed/g/h"
        +  ],
        +  "test/a/abc{fed/g,def}/**/": [
        +    "test/a/abcdef",
        +    "test/a/abcdef/g",
        +    "test/a/abcfed/g"
        +  ],
        +  "test/a/abc{fed/g,def}/**///**/": [
        +    "test/a/abcdef",
        +    "test/a/abcdef/g",
        +    "test/a/abcfed/g"
        +  ],
        +  "test/**/a/**/": [
        +    "test/a",
        +    "test/a/abcdef",
        +    "test/a/abcdef/g",
        +    "test/a/abcfed",
        +    "test/a/abcfed/g",
        +    "test/a/b",
        +    "test/a/b/c",
        +    "test/a/bc",
        +    "test/a/bc/e",
        +    "test/a/c",
        +    "test/a/c/d",
        +    "test/a/c/d/c",
        +    "test/a/cb",
        +    "test/a/cb/e",
        +    "test/a/symlink",
        +    "test/a/symlink/a",
        +    "test/a/symlink/a/b",
        +    "test/a/symlink/a/b/c",
        +    "test/a/symlink/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b"
        +  ],
        +  "test/+(a|b|c)/a{/,bc*}/**": [
        +    "test/a/abcdef",
        +    "test/a/abcdef/g",
        +    "test/a/abcdef/g/h",
        +    "test/a/abcfed",
        +    "test/a/abcfed/g",
        +    "test/a/abcfed/g/h"
        +  ],
        +  "test/*/*/*/f": [
        +    "test/a/bc/e/f",
        +    "test/a/cb/e/f"
        +  ],
        +  "test/**/f": [
        +    "test/a/bc/e/f",
        +    "test/a/cb/e/f"
        +  ],
        +  "test/a/symlink/a/b/c/a/b/c/a/b/c//a/b/c////a/b/c/**/b/c/**": [
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
        +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c"
        +  ],
        +  "{./*/*,/tmp/glob-test/*}": [
        +    "./examples/g.js",
        +    "./examples/usr-local.js",
        +    "./node_modules/graceful-fs",
        +    "./node_modules/inherits",
        +    "./node_modules/minimatch",
        +    "./node_modules/mkdirp",
        +    "./node_modules/rimraf",
        +    "./node_modules/tap",
        +    "./test/00-setup.js",
        +    "./test/a",
        +    "./test/bash-comparison.js",
        +    "./test/bash-results.json",
        +    "./test/cwd-test.js",
        +    "./test/globstar-match.js",
        +    "./test/mark.js",
        +    "./test/nocase-nomagic.js",
        +    "./test/pause-resume.js",
        +    "./test/root-nomount.js",
        +    "./test/root.js",
        +    "./test/stat.js",
        +    "./test/zz-cleanup.js",
        +    "/tmp/glob-test/asdf",
        +    "/tmp/glob-test/bar",
        +    "/tmp/glob-test/baz",
        +    "/tmp/glob-test/foo",
        +    "/tmp/glob-test/quux",
        +    "/tmp/glob-test/qwer",
        +    "/tmp/glob-test/rewq"
        +  ],
        +  "{/tmp/glob-test/*,*}": [
        +    "/tmp/glob-test/asdf",
        +    "/tmp/glob-test/bar",
        +    "/tmp/glob-test/baz",
        +    "/tmp/glob-test/foo",
        +    "/tmp/glob-test/quux",
        +    "/tmp/glob-test/qwer",
        +    "/tmp/glob-test/rewq",
        +    "examples",
        +    "glob.js",
        +    "LICENSE",
        +    "node_modules",
        +    "package.json",
        +    "README.md",
        +    "test"
        +  ],
        +  "test/a/!(symlink)/**": [
        +    "test/a/abcdef",
        +    "test/a/abcdef/g",
        +    "test/a/abcdef/g/h",
        +    "test/a/abcfed",
        +    "test/a/abcfed/g",
        +    "test/a/abcfed/g/h",
        +    "test/a/b",
        +    "test/a/b/c",
        +    "test/a/b/c/d",
        +    "test/a/bc",
        +    "test/a/bc/e",
        +    "test/a/bc/e/f",
        +    "test/a/c",
        +    "test/a/c/d",
        +    "test/a/c/d/c",
        +    "test/a/c/d/c/b",
        +    "test/a/cb",
        +    "test/a/cb/e",
        +    "test/a/cb/e/f"
        +  ]
        +}
        diff --git a/node_modules/mocha/node_modules/glob/test/cwd-test.js b/node_modules/mocha/node_modules/glob/test/cwd-test.js
        new file mode 100644
        index 0000000..352c27e
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/test/cwd-test.js
        @@ -0,0 +1,55 @@
        +var tap = require("tap")
        +
        +var origCwd = process.cwd()
        +process.chdir(__dirname)
        +
        +tap.test("changing cwd and searching for **/d", function (t) {
        +  var glob = require('../')
        +  var path = require('path')
        +  t.test('.', function (t) {
        +    glob('**/d', function (er, matches) {
        +      t.ifError(er)
        +      t.like(matches, [ 'a/b/c/d', 'a/c/d' ])
        +      t.end()
        +    })
        +  })
        +
        +  t.test('a', function (t) {
        +    glob('**/d', {cwd:path.resolve('a')}, function (er, matches) {
        +      t.ifError(er)
        +      t.like(matches, [ 'b/c/d', 'c/d' ])
        +      t.end()
        +    })
        +  })
        +
        +  t.test('a/b', function (t) {
        +    glob('**/d', {cwd:path.resolve('a/b')}, function (er, matches) {
        +      t.ifError(er)
        +      t.like(matches, [ 'c/d' ])
        +      t.end()
        +    })
        +  })
        +
        +  t.test('a/b/', function (t) {
        +    glob('**/d', {cwd:path.resolve('a/b/')}, function (er, matches) {
        +      t.ifError(er)
        +      t.like(matches, [ 'c/d' ])
        +      t.end()
        +    })
        +  })
        +
        +  t.test('.', function (t) {
        +    glob('**/d', {cwd: process.cwd()}, function (er, matches) {
        +      t.ifError(er)
        +      t.like(matches, [ 'a/b/c/d', 'a/c/d' ])
        +      t.end()
        +    })
        +  })
        +
        +  t.test('cd -', function (t) {
        +    process.chdir(origCwd)
        +    t.end()
        +  })
        +
        +  t.end()
        +})
        diff --git a/node_modules/mocha/node_modules/glob/test/globstar-match.js b/node_modules/mocha/node_modules/glob/test/globstar-match.js
        new file mode 100644
        index 0000000..9b234fa
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/test/globstar-match.js
        @@ -0,0 +1,19 @@
        +var Glob = require("../glob.js").Glob
        +var test = require('tap').test
        +
        +test('globstar should not have dupe matches', function(t) {
        +  var pattern = 'a/**/[gh]'
        +  var g = new Glob(pattern, { cwd: __dirname })
        +  var matches = []
        +  g.on('match', function(m) {
        +    console.error('match %j', m)
        +    matches.push(m)
        +  })
        +  g.on('end', function(set) {
        +    console.error('set', set)
        +    matches = matches.sort()
        +    set = set.sort()
        +    t.same(matches, set, 'should have same set of matches')
        +    t.end()
        +  })
        +})
        diff --git a/node_modules/mocha/node_modules/glob/test/mark.js b/node_modules/mocha/node_modules/glob/test/mark.js
        new file mode 100644
        index 0000000..ed68a33
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/test/mark.js
        @@ -0,0 +1,74 @@
        +var test = require("tap").test
        +var glob = require('../')
        +process.chdir(__dirname)
        +
        +test("mark, no / on pattern", function (t) {
        +  glob("a/*", {mark: true}, function (er, results) {
        +    if (er)
        +      throw er
        +    var expect = [ 'a/abcdef/',
        +                   'a/abcfed/',
        +                   'a/b/',
        +                   'a/bc/',
        +                   'a/c/',
        +                   'a/cb/' ]
        +
        +    if (process.platform !== "win32")
        +      expect.push('a/symlink/')
        +
        +    t.same(results, expect)
        +    t.end()
        +  })
        +})
        +
        +test("mark=false, no / on pattern", function (t) {
        +  glob("a/*", function (er, results) {
        +    if (er)
        +      throw er
        +    var expect = [ 'a/abcdef',
        +                   'a/abcfed',
        +                   'a/b',
        +                   'a/bc',
        +                   'a/c',
        +                   'a/cb' ]
        +
        +    if (process.platform !== "win32")
        +      expect.push('a/symlink')
        +    t.same(results, expect)
        +    t.end()
        +  })
        +})
        +
        +test("mark=true, / on pattern", function (t) {
        +  glob("a/*/", {mark: true}, function (er, results) {
        +    if (er)
        +      throw er
        +    var expect = [ 'a/abcdef/',
        +                    'a/abcfed/',
        +                    'a/b/',
        +                    'a/bc/',
        +                    'a/c/',
        +                    'a/cb/' ]
        +    if (process.platform !== "win32")
        +      expect.push('a/symlink/')
        +    t.same(results, expect)
        +    t.end()
        +  })
        +})
        +
        +test("mark=false, / on pattern", function (t) {
        +  glob("a/*/", function (er, results) {
        +    if (er)
        +      throw er
        +    var expect = [ 'a/abcdef/',
        +                   'a/abcfed/',
        +                   'a/b/',
        +                   'a/bc/',
        +                   'a/c/',
        +                   'a/cb/' ]
        +    if (process.platform !== "win32")
        +      expect.push('a/symlink/')
        +    t.same(results, expect)
        +    t.end()
        +  })
        +})
        diff --git a/node_modules/mocha/node_modules/glob/test/nocase-nomagic.js b/node_modules/mocha/node_modules/glob/test/nocase-nomagic.js
        new file mode 100644
        index 0000000..d862970
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/test/nocase-nomagic.js
        @@ -0,0 +1,113 @@
        +var fs = require('graceful-fs');
        +var test = require('tap').test;
        +var glob = require('../');
        +
        +test('mock fs', function(t) {
        +  var stat = fs.stat
        +  var statSync = fs.statSync
        +  var readdir = fs.readdir
        +  var readdirSync = fs.readdirSync
        +
        +  function fakeStat(path) {
        +    var ret
        +    switch (path.toLowerCase()) {
        +      case '/tmp': case '/tmp/':
        +        ret = { isDirectory: function() { return true } }
        +        break
        +      case '/tmp/a':
        +        ret = { isDirectory: function() { return false } }
        +        break
        +    }
        +    return ret
        +  }
        +
        +  fs.stat = function(path, cb) {
        +    var f = fakeStat(path);
        +    if (f) {
        +      process.nextTick(function() {
        +        cb(null, f)
        +      })
        +    } else {
        +      stat.call(fs, path, cb)
        +    }
        +  }
        +
        +  fs.statSync = function(path) {
        +    return fakeStat(path) || statSync.call(fs, path)
        +  }
        +
        +  function fakeReaddir(path) {
        +    var ret
        +    switch (path.toLowerCase()) {
        +      case '/tmp': case '/tmp/':
        +        ret = [ 'a', 'A' ]
        +        break
        +      case '/':
        +        ret = ['tmp', 'tMp', 'tMP', 'TMP']
        +    }
        +    return ret
        +  }
        +
        +  fs.readdir = function(path, cb) {
        +    var f = fakeReaddir(path)
        +    if (f)
        +      process.nextTick(function() {
        +        cb(null, f)
        +      })
        +    else
        +      readdir.call(fs, path, cb)
        +  }
        +
        +  fs.readdirSync = function(path) {
        +    return fakeReaddir(path) || readdirSync.call(fs, path)
        +  }
        +
        +  t.pass('mocked')
        +  t.end()
        +})
        +
        +test('nocase, nomagic', function(t) {
        +  var n = 2
        +  var want = [ '/TMP/A',
        +               '/TMP/a',
        +               '/tMP/A',
        +               '/tMP/a',
        +               '/tMp/A',
        +               '/tMp/a',
        +               '/tmp/A',
        +               '/tmp/a' ]
        +  glob('/tmp/a', { nocase: true }, function(er, res) {
        +    if (er)
        +      throw er
        +    t.same(res.sort(), want)
        +    if (--n === 0) t.end()
        +  })
        +  glob('/tmp/A', { nocase: true }, function(er, res) {
        +    if (er)
        +      throw er
        +    t.same(res.sort(), want)
        +    if (--n === 0) t.end()
        +  })
        +})
        +
        +test('nocase, with some magic', function(t) {
        +  t.plan(2)
        +  var want = [ '/TMP/A',
        +               '/TMP/a',
        +               '/tMP/A',
        +               '/tMP/a',
        +               '/tMp/A',
        +               '/tMp/a',
        +               '/tmp/A',
        +               '/tmp/a' ]
        +  glob('/tmp/*', { nocase: true }, function(er, res) {
        +    if (er)
        +      throw er
        +    t.same(res.sort(), want)
        +  })
        +  glob('/tmp/*', { nocase: true }, function(er, res) {
        +    if (er)
        +      throw er
        +    t.same(res.sort(), want)
        +  })
        +})
        diff --git a/node_modules/mocha/node_modules/glob/test/pause-resume.js b/node_modules/mocha/node_modules/glob/test/pause-resume.js
        new file mode 100644
        index 0000000..e1ffbab
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/test/pause-resume.js
        @@ -0,0 +1,73 @@
        +// show that no match events happen while paused.
        +var tap = require("tap")
        +, child_process = require("child_process")
        +// just some gnarly pattern with lots of matches
        +, pattern = "test/a/!(symlink)/**"
        +, bashResults = require("./bash-results.json")
        +, patterns = Object.keys(bashResults)
        +, glob = require("../")
        +, Glob = glob.Glob
        +, path = require("path")
        +
        +// run from the root of the project
        +// this is usually where you're at anyway, but be sure.
        +process.chdir(path.resolve(__dirname, ".."))
        +
        +function alphasort (a, b) {
        +  a = a.toLowerCase()
        +  b = b.toLowerCase()
        +  return a > b ? 1 : a < b ? -1 : 0
        +}
        +
        +function cleanResults (m) {
        +  // normalize discrepancies in ordering, duplication,
        +  // and ending slashes.
        +  return m.map(function (m) {
        +    return m.replace(/\/+/g, "/").replace(/\/$/, "")
        +  }).sort(alphasort).reduce(function (set, f) {
        +    if (f !== set[set.length - 1]) set.push(f)
        +    return set
        +  }, []).sort(alphasort).map(function (f) {
        +    // de-windows
        +    return (process.platform !== 'win32') ? f
        +           : f.replace(/^[a-zA-Z]:\\\\/, '/').replace(/\\/g, '/')
        +  })
        +}
        +
        +var globResults = []
        +tap.test("use a Glob object, and pause/resume it", function (t) {
        +  var g = new Glob(pattern)
        +  , paused = false
        +  , res = []
        +  , expect = bashResults[pattern]
        +
        +  g.on("pause", function () {
        +    console.error("pause")
        +  })
        +
        +  g.on("resume", function () {
        +    console.error("resume")
        +  })
        +
        +  g.on("match", function (m) {
        +    t.notOk(g.paused, "must not be paused")
        +    globResults.push(m)
        +    g.pause()
        +    t.ok(g.paused, "must be paused")
        +    setTimeout(g.resume.bind(g), 10)
        +  })
        +
        +  g.on("end", function (matches) {
        +    t.pass("reached glob end")
        +    globResults = cleanResults(globResults)
        +    matches = cleanResults(matches)
        +    t.deepEqual(matches, globResults,
        +      "end event matches should be the same as match events")
        +
        +    t.deepEqual(matches, expect,
        +      "glob matches should be the same as bash results")
        +
        +    t.end()
        +  })
        +})
        +
        diff --git a/node_modules/mocha/node_modules/glob/test/root-nomount.js b/node_modules/mocha/node_modules/glob/test/root-nomount.js
        new file mode 100644
        index 0000000..3ac5979
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/test/root-nomount.js
        @@ -0,0 +1,39 @@
        +var tap = require("tap")
        +
        +var origCwd = process.cwd()
        +process.chdir(__dirname)
        +
        +tap.test("changing root and searching for /b*/**", function (t) {
        +  var glob = require('../')
        +  var path = require('path')
        +  t.test('.', function (t) {
        +    glob('/b*/**', { globDebug: true, root: '.', nomount: true }, function (er, matches) {
        +      t.ifError(er)
        +      t.like(matches, [])
        +      t.end()
        +    })
        +  })
        +
        +  t.test('a', function (t) {
        +    glob('/b*/**', { globDebug: true, root: path.resolve('a'), nomount: true }, function (er, matches) {
        +      t.ifError(er)
        +      t.like(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ])
        +      t.end()
        +    })
        +  })
        +
        +  t.test('root=a, cwd=a/b', function (t) {
        +    glob('/b*/**', { globDebug: true, root: 'a', cwd: path.resolve('a/b'), nomount: true }, function (er, matches) {
        +      t.ifError(er)
        +      t.like(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ])
        +      t.end()
        +    })
        +  })
        +
        +  t.test('cd -', function (t) {
        +    process.chdir(origCwd)
        +    t.end()
        +  })
        +
        +  t.end()
        +})
        diff --git a/node_modules/mocha/node_modules/glob/test/root.js b/node_modules/mocha/node_modules/glob/test/root.js
        new file mode 100644
        index 0000000..95c23f9
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/test/root.js
        @@ -0,0 +1,46 @@
        +var t = require("tap")
        +
        +var origCwd = process.cwd()
        +process.chdir(__dirname)
        +
        +var glob = require('../')
        +var path = require('path')
        +
        +t.test('.', function (t) {
        +  glob('/b*/**', { globDebug: true, root: '.' }, function (er, matches) {
        +    t.ifError(er)
        +    t.like(matches, [])
        +    t.end()
        +  })
        +})
        +
        +
        +t.test('a', function (t) {
        +  console.error("root=" + path.resolve('a'))
        +  glob('/b*/**', { globDebug: true, root: path.resolve('a') }, function (er, matches) {
        +    t.ifError(er)
        +    var wanted = [
        +        '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f'
        +      ].map(function (m) {
        +        return path.join(path.resolve('a'), m).replace(/\\/g, '/')
        +      })
        +
        +    t.like(matches, wanted)
        +    t.end()
        +  })
        +})
        +
        +t.test('root=a, cwd=a/b', function (t) {
        +  glob('/b*/**', { globDebug: true, root: 'a', cwd: path.resolve('a/b') }, function (er, matches) {
        +    t.ifError(er)
        +    t.like(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ].map(function (m) {
        +      return path.join(path.resolve('a'), m).replace(/\\/g, '/')
        +    }))
        +    t.end()
        +  })
        +})
        +
        +t.test('cd -', function (t) {
        +  process.chdir(origCwd)
        +  t.end()
        +})
        diff --git a/node_modules/mocha/node_modules/glob/test/stat.js b/node_modules/mocha/node_modules/glob/test/stat.js
        new file mode 100644
        index 0000000..6291711
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/test/stat.js
        @@ -0,0 +1,32 @@
        +var glob = require('../')
        +var test = require('tap').test
        +var path = require('path')
        +
        +test('stat all the things', function(t) {
        +  var g = new glob.Glob('a/*abc*/**', { stat: true, cwd: __dirname })
        +  var matches = []
        +  g.on('match', function(m) {
        +    matches.push(m)
        +  })
        +  var stats = []
        +  g.on('stat', function(m) {
        +    stats.push(m)
        +  })
        +  g.on('end', function(eof) {
        +    stats = stats.sort()
        +    matches = matches.sort()
        +    eof = eof.sort()
        +    t.same(stats, matches)
        +    t.same(eof, matches)
        +    var cache = Object.keys(this.statCache)
        +    t.same(cache.map(function (f) {
        +      return path.relative(__dirname, f)
        +    }).sort(), matches)
        +
        +    cache.forEach(function(c) {
        +      t.equal(typeof this.statCache[c], 'object')
        +    }, this)
        +
        +    t.end()
        +  })
        +})
        diff --git a/node_modules/mocha/node_modules/glob/test/zz-cleanup.js b/node_modules/mocha/node_modules/glob/test/zz-cleanup.js
        new file mode 100644
        index 0000000..e085f0f
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/glob/test/zz-cleanup.js
        @@ -0,0 +1,11 @@
        +// remove the fixtures
        +var tap = require("tap")
        +, rimraf = require("rimraf")
        +, path = require("path")
        +
        +tap.test("cleanup fixtures", function (t) {
        +  rimraf(path.resolve(__dirname, "a"), function (er) {
        +    t.ifError(er, "removed")
        +    t.end()
        +  })
        +})
        diff --git a/node_modules/mocha/node_modules/growl/History.md b/node_modules/mocha/node_modules/growl/History.md
        new file mode 100644
        index 0000000..a4b7b49
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/growl/History.md
        @@ -0,0 +1,63 @@
        +
        +1.7.0 / 2012-12-30 
        +==================
        +
        +  * support transient notifications in Gnome
        +
        +1.6.1 / 2012-09-25 
        +==================
        +
        +  * restore compatibility with node < 0.8 [fgnass]
        +
        +1.6.0 / 2012-09-06 
        +==================
        +
        +  * add notification center support [drudge]
        +
        +1.5.1 / 2012-04-08 
        +==================
        +
        +  * Merge pull request #16 from KyleAMathews/patch-1
        +  * Fixes #15
        +
        +1.5.0 / 2012-02-08 
        +==================
        +
        +  * Added windows support [perfusorius]
        +
        +1.4.1 / 2011-12-28 
        +==================
        +
        +  * Fixed: dont exit(). Closes #9
        +
        +1.4.0 / 2011-12-17 
        +==================
        +
        +  * Changed API: `growl.notify()` -> `growl()`
        +
        +1.3.0 / 2011-12-17 
        +==================
        +
        +  * Added support for Ubuntu/Debian/Linux users [niftylettuce]
        +  * Fixed: send notifications even if title not specified [alessioalex]
        +
        +1.2.0 / 2011-10-06 
        +==================
        +
        +  * Add support for priority.
        +
        +1.1.0 / 2011-03-15 
        +==================
        +
        +  * Added optional callbacks
        +  * Added parsing of version
        +
        +1.0.1 / 2010-03-26
        +==================
        +
        +  * Fixed; sys.exec -> child_process.exec to support latest node
        +
        +1.0.0 / 2010-03-19
        +==================
        +  
        +  * Initial release
        diff --git a/node_modules/mocha/node_modules/growl/Readme.md b/node_modules/mocha/node_modules/growl/Readme.md
        new file mode 100644
        index 0000000..48d717c
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/growl/Readme.md
        @@ -0,0 +1,99 @@
        +# Growl for nodejs
        +
        +Growl support for Nodejs. This is essentially a port of my [Ruby Growl Library](http://github.com/visionmedia/growl). Ubuntu/Linux support added thanks to [@niftylettuce](http://github.com/niftylettuce). 
        +
        +## Installation
        +
        +### Install 
        +
        +### Mac OS X (Darwin):
        +
        +  Install [growlnotify(1)](http://growl.info/extras.php#growlnotify). On OS X 10.8, Notification Center is supported using [terminal-notifier](https://github.com/alloy/terminal-notifier). To install:
        +  
        +      $ sudo gem install terminal-notifier
        +      
        +  Install [npm](http://npmjs.org/) and run:
        +  
        +      $ npm install growl
        +
        +### Ubuntu (Linux):
        +
        +  Install `notify-send` through the [libnotify-bin](http://packages.ubuntu.com/libnotify-bin) package:
        +
        +      $ sudo apt-get install libnotify-bin
        +
        +  Install [npm](http://npmjs.org/) and run:
        +  
        +      $ npm install growl
        +
        +### Windows:
        +
        +  Download and install [Growl for Windows](http://www.growlforwindows.com/gfw/default.aspx)
        +
        +  Download [growlnotify](http://www.growlforwindows.com/gfw/help/growlnotify.aspx) - **IMPORTANT :** Unpack growlnotify to a folder that is present in your path!
        +
        +  Install [npm](http://npmjs.org/) and run:
        +  
        +      $ npm install growl
        +
        +## Examples
        +
        +Callback functions are optional
        +
        +    var growl = require('growl')
        +    growl('You have mail!')
        +    growl('5 new messages', { sticky: true })
        +    growl('5 new emails', { title: 'Email Client', image: 'Safari', sticky: true })
        +    growl('Message with title', { title: 'Title'})
        +    growl('Set priority', { priority: 2 })
        +    growl('Show Safari icon', { image: 'Safari' })
        +    growl('Show icon', { image: 'path/to/icon.icns' })
        +    growl('Show image', { image: 'path/to/my.image.png' })
        +    growl('Show png filesystem icon', { image: 'png' })
        +    growl('Show pdf filesystem icon', { image: 'article.pdf' })
        +    growl('Show pdf filesystem icon', { image: 'article.pdf' }, function(err){
        +      // ... notified
        +    })
        +
        +## Options
        +
        +  - title
        +    - notification title
        +  - name
        +    - application name
        +  - priority
        +    - priority for the notification (default is 0)
        +  - sticky
        +    - weither or not the notification should remainin until closed
        +  - image
        +    - Auto-detects the context:
        +      - path to an icon sets --iconpath
        +      - path to an image sets --image
        +      - capitalized word sets --appIcon
        +      - filename uses extname as --icon
        +      - otherwise treated as --icon
        +      
        +## License 
        +
        +(The MIT License)
        +
        +Copyright (c) 2009 TJ Holowaychuk 
        +
        +Permission is hereby granted, free of charge, to any person obtaining
        +a copy of this software and associated documentation files (the
        +'Software'), to deal in the Software without restriction, including
        +without limitation the rights to use, copy, modify, merge, publish,
        +distribute, sublicense, and/or sell copies of the Software, and to
        +permit persons to whom the Software is furnished to do so, subject to
        +the following conditions:
        +
        +The above copyright notice and this permission notice shall be
        +included in all copies or substantial portions of the Software.
        +
        +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
        +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
        +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
        +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
        +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
        +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
        +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
        diff --git a/node_modules/mocha/node_modules/growl/lib/growl.js b/node_modules/mocha/node_modules/growl/lib/growl.js
        new file mode 100644
        index 0000000..c034c3e
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/growl/lib/growl.js
        @@ -0,0 +1,234 @@
        +// Growl - Copyright TJ Holowaychuk  (MIT Licensed)
        +
        +/**
        + * Module dependencies.
        + */
        +
        +var exec = require('child_process').exec
        +  , fs = require('fs')
        +  , path = require('path')
        +  , exists = fs.existsSync || path.existsSync
        +  , os = require('os')
        +  , quote = JSON.stringify
        +  , cmd;
        +
        +function which(name) {
        +  var paths = process.env.PATH.split(':');
        +  var loc;
        +  
        +  for (var i = 0, len = paths.length; i < len; ++i) {
        +    loc = path.join(paths[i], name);
        +    if (exists(loc)) return loc;
        +  }
        +}
        +
        +switch(os.type()) {
        +  case 'Darwin':
        +    if (which('terminal-notifier')) {
        +      cmd = {
        +          type: "Darwin-NotificationCenter"
        +        , pkg: "terminal-notifier"
        +        , msg: '-message'
        +        , title: '-title'
        +        , subtitle: '-subtitle'
        +        , priority: {
        +              cmd: '-execute'
        +            , range: []
        +          }
        +      };
        +    } else {
        +      cmd = {
        +          type: "Darwin-Growl"
        +        , pkg: "growlnotify"
        +        , msg: '-m'
        +        , sticky: '--sticky'
        +        , priority: {
        +              cmd: '--priority'
        +            , range: [
        +                -2
        +              , -1
        +              , 0
        +              , 1
        +              , 2
        +              , "Very Low"
        +              , "Moderate"
        +              , "Normal"
        +              , "High"
        +              , "Emergency"
        +            ]
        +          }
        +      };
        +    }
        +    break;
        +  case 'Linux':
        +    cmd = {
        +        type: "Linux"
        +      , pkg: "notify-send"
        +      , msg: ''
        +      , sticky: '-t 0'
        +      , icon: '-i'
        +      , priority: {
        +          cmd: '-u'
        +        , range: [
        +            "low"
        +          , "normal"
        +          , "critical"
        +        ]
        +      }
        +    };
        +    break;
        +  case 'Windows_NT':
        +    cmd = {
        +        type: "Windows"
        +      , pkg: "growlnotify"
        +      , msg: ''
        +      , sticky: '/s:true'
        +      , title: '/t:'
        +      , icon: '/i:'
        +      , priority: {
        +            cmd: '/p:'
        +          , range: [
        +              -2
        +            , -1
        +            , 0
        +            , 1
        +            , 2
        +          ]
        +        }
        +    };
        +    break;
        +}
        +
        +/**
        + * Expose `growl`.
        + */
        +
        +exports = module.exports = growl;
        +
        +/**
        + * Node-growl version.
        + */
        +
        +exports.version = '1.4.1'
        +
        +/**
        + * Send growl notification _msg_ with _options_.
        + *
        + * Options:
        + *
        + *  - title   Notification title
        + *  - sticky  Make the notification stick (defaults to false)
        + *  - priority  Specify an int or named key (default is 0)
        + *  - name    Application name (defaults to growlnotify)
        + *  - image
        + *    - path to an icon sets --iconpath
        + *    - path to an image sets --image
        + *    - capitalized word sets --appIcon
        + *    - filename uses extname as --icon
        + *    - otherwise treated as --icon
        + *
        + * Examples:
        + *
        + *   growl('New email')
        + *   growl('5 new emails', { title: 'Thunderbird' })
        + *   growl('Email sent', function(){
        + *     // ... notification sent
        + *   })
        + *
        + * @param {string} msg
        + * @param {object} options
        + * @param {function} fn
        + * @api public
        + */
        +
        +function growl(msg, options, fn) {
        +  var image
        +    , args
        +    , options = options || {}
        +    , fn = fn || function(){};
        +
        +  // noop
        +  if (!cmd) return fn(new Error('growl not supported on this platform'));
        +  args = [cmd.pkg];
        +
        +  // image
        +  if (image = options.image) {
        +    switch(cmd.type) {
        +      case 'Darwin-Growl':
        +        var flag, ext = path.extname(image).substr(1)
        +        flag = flag || ext == 'icns' && 'iconpath'
        +        flag = flag || /^[A-Z]/.test(image) && 'appIcon'
        +        flag = flag || /^png|gif|jpe?g$/.test(ext) && 'image'
        +        flag = flag || ext && (image = ext) && 'icon'
        +        flag = flag || 'icon'
        +        args.push('--' + flag, quote(image))
        +        break;
        +      case 'Linux':
        +        args.push(cmd.icon, quote(image));
        +        // libnotify defaults to sticky, set a hint for transient notifications
        +        if (!options.sticky) args.push('--hint=int:transient:1');
        +        break;
        +      case 'Windows':
        +        args.push(cmd.icon + quote(image));
        +        break;
        +    }
        +  }
        +
        +  // sticky
        +  if (options.sticky) args.push(cmd.sticky);
        +
        +  // priority
        +  if (options.priority) {
        +    var priority = options.priority + '';
        +    var checkindexOf = cmd.priority.range.indexOf(priority);
        +    if (~cmd.priority.range.indexOf(priority)) {
        +      args.push(cmd.priority, options.priority);
        +    }
        +  }
        +
        +  // name
        +  if (options.name && cmd.type === "Darwin-Growl") {
        +    args.push('--name', options.name);
        +  }
        +
        +  switch(cmd.type) {
        +    case 'Darwin-Growl':
        +      args.push(cmd.msg);
        +      args.push(quote(msg));
        +      if (options.title) args.push(quote(options.title));
        +      break;
        +    case 'Darwin-NotificationCenter':
        +      args.push(cmd.msg);
        +      args.push(quote(msg));
        +      if (options.title) {
        +        args.push(cmd.title);
        +        args.push(quote(options.title));
        +      }
        +      if (options.subtitle) {
        +        args.push(cmd.subtitle);
        +        args.push(quote(options.subtitle));
        +      }
        +      break;
        +    case 'Darwin-Growl':
        +      args.push(cmd.msg);
        +      args.push(quote(msg));
        +      if (options.title) args.push(quote(options.title));
        +      break;
        +    case 'Linux':
        +      if (options.title) {
        +        args.push(quote(options.title));
        +        args.push(cmd.msg);
        +        args.push(quote(msg));
        +      } else {
        +        args.push(quote(msg));
        +      }
        +      break;
        +    case 'Windows':
        +      args.push(quote(msg));
        +      if (options.title) args.push(cmd.title + quote(options.title));
        +      break;
        +  }
        +
        +  // execute
        +  exec(args.join(' '), fn);
        +};
        diff --git a/node_modules/mocha/node_modules/growl/package.json b/node_modules/mocha/node_modules/growl/package.json
        new file mode 100644
        index 0000000..cd168b2
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/growl/package.json
        @@ -0,0 +1,44 @@
        +{
        +  "name": "growl",
        +  "version": "1.8.1",
        +  "description": "Growl unobtrusive notifications",
        +  "author": {
        +    "name": "TJ Holowaychuk",
        +    "email": "tj@vision-media.ca"
        +  },
        +  "maintainers": [
        +    {
        +      "name": "tjholowaychuk",
        +      "email": "tj@vision-media.ca"
        +    },
        +    {
        +      "name": "jbnicolai",
        +      "email": "jappelman@xebia.com"
        +    }
        +  ],
        +  "repository": {
        +    "type": "git",
        +    "url": "git://github.com/visionmedia/node-growl.git"
        +  },
        +  "main": "./lib/growl.js",
        +  "gitHead": "882ced3155a57f566887c884d5c6dccb7df435c1",
        +  "bugs": {
        +    "url": "https://github.com/visionmedia/node-growl/issues"
        +  },
        +  "homepage": "https://github.com/visionmedia/node-growl",
        +  "_id": "growl@1.8.1",
        +  "scripts": {},
        +  "_shasum": "4b2dec8d907e93db336624dcec0183502f8c9428",
        +  "_from": "growl@1.8.1",
        +  "_npmVersion": "1.4.20",
        +  "_npmUser": {
        +    "name": "jbnicolai",
        +    "email": "jappelman@xebia.com"
        +  },
        +  "dist": {
        +    "shasum": "4b2dec8d907e93db336624dcec0183502f8c9428",
        +    "tarball": "http://registry.npmjs.org/growl/-/growl-1.8.1.tgz"
        +  },
        +  "directories": {},
        +  "_resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz"
        +}
        diff --git a/node_modules/mocha/node_modules/growl/test.js b/node_modules/mocha/node_modules/growl/test.js
        new file mode 100644
        index 0000000..cf22d90
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/growl/test.js
        @@ -0,0 +1,20 @@
        +
        +var growl = require('./lib/growl')
        +
        +growl('You have mail!')
        +growl('5 new messages', { sticky: true })
        +growl('5 new emails', { title: 'Email Client', image: 'Safari', sticky: true })
        +growl('Message with title', { title: 'Title'})
        +growl('Set priority', { priority: 2 })
        +growl('Show Safari icon', { image: 'Safari' })
        +growl('Show icon', { image: 'path/to/icon.icns' })
        +growl('Show image', { image: 'path/to/my.image.png' })
        +growl('Show png filesystem icon', { image: 'png' })
        +growl('Show pdf filesystem icon', { image: 'article.pdf' })
        +growl('Show pdf filesystem icon', { image: 'article.pdf' }, function(){
        +  console.log('callback');
        +})
        +growl('Show pdf filesystem icon', { title: 'Use show()', image: 'article.pdf' })
        +growl('here \' are \n some \\ characters that " need escaping', {}, function(error, stdout, stderr) {
        +  if (error !== null) throw new Error('escaping failed:\n' + stdout + stderr);
        +})
        diff --git a/node_modules/mocha/node_modules/jade/.npmignore b/node_modules/mocha/node_modules/jade/.npmignore
        new file mode 100644
        index 0000000..b9af3d4
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/jade/.npmignore
        @@ -0,0 +1,15 @@
        +test
        +support
        +benchmarks
        +examples
        +lib-cov
        +coverage.html
        +.gitmodules
        +.travis.yml
        +History.md
        +Readme.md
        +Makefile
        +test/
        +support/
        +benchmarks/
        +examples/
        diff --git a/node_modules/mocha/node_modules/jade/LICENSE b/node_modules/mocha/node_modules/jade/LICENSE
        new file mode 100644
        index 0000000..8ad0e0d
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/jade/LICENSE
        @@ -0,0 +1,22 @@
        +(The MIT License)
        +
        +Copyright (c) 2009-2010 TJ Holowaychuk 
        +
        +Permission is hereby granted, free of charge, to any person obtaining
        +a copy of this software and associated documentation files (the
        +'Software'), to deal in the Software without restriction, including
        +without limitation the rights to use, copy, modify, merge, publish,
        +distribute, sublicense, and/or sell copies of the Software, and to
        +permit persons to whom the Software is furnished to do so, subject to
        +the following conditions:
        +
        +The above copyright notice and this permission notice shall be
        +included in all copies or substantial portions of the Software.
        +
        +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
        +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
        +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
        +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
        +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
        +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
        +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
        \ No newline at end of file
        diff --git a/node_modules/mocha/node_modules/jade/bin/jade b/node_modules/mocha/node_modules/jade/bin/jade
        new file mode 100755
        index 0000000..7e6002f
        --- /dev/null
        +++ b/node_modules/mocha/node_modules/jade/bin/jade
        @@ -0,0 +1,147 @@
        +#!/usr/bin/env node
        +
        +/**
        + * Module dependencies.
        + */
        +
        +var fs = require('fs')
        +  , program = require('commander')
        +  , path = require('path')
        +  , basename = path.basename
        +  , dirname = path.dirname
        +  , resolve = path.resolve
        +  , join = path.join
        +  , mkdirp = require('mkdirp')
        +  , jade = require('../');
        +
        +// jade options
        +
        +var options = {};
        +
        +// options
        +
        +program
        +  .version(jade.version)
        +  .usage('[options] [dir|file ...]')
        +  .option('-o, --obj ', 'javascript options object')
        +  .option('-O, --out 
        ', 'output the compiled html to ') + .option('-p, --path ', 'filename used to resolve includes') + .option('-P, --pretty', 'compile pretty html output') + .option('-c, --client', 'compile for client-side runtime.js') + .option('-D, --no-debug', 'compile without debugging (smaller functions)') + +program.on('--help', function(){ + console.log(' Examples:'); + console.log(''); + console.log(' # translate jade the templates dir'); + console.log(' $ jade templates'); + console.log(''); + console.log(' # create {foo,bar}.html'); + console.log(' $ jade {foo,bar}.jade'); + console.log(''); + console.log(' # jade over stdio'); + console.log(' $ jade < my.jade > my.html'); + console.log(''); + console.log(' # jade over stdio'); + console.log(' $ echo "h1 Jade!" | jade'); + console.log(''); + console.log(' # foo, bar dirs rendering to /tmp'); + console.log(' $ jade foo bar --out /tmp '); + console.log(''); +}); + +program.parse(process.argv); + +// options given, parse them + +if (program.obj) options = eval('(' + program.obj + ')'); + +// --filename + +if (program.path) options.filename = program.path; + +// --no-debug + +options.compileDebug = program.debug; + +// --client + +options.client = program.client; + +// --pretty + +options.pretty = program.pretty; + +// left-over args are file paths + +var files = program.args; + +// compile files + +if (files.length) { + console.log(); + files.forEach(renderFile); + process.on('exit', console.log); +// stdio +} else { + stdin(); +} + +/** + * Compile from stdin. + */ + +function stdin() { + var buf = ''; + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function(chunk){ buf += chunk; }); + process.stdin.on('end', function(){ + var fn = jade.compile(buf, options); + var output = options.client + ? fn.toString() + : fn(options); + process.stdout.write(output); + }).resume(); +} + +/** + * Process the given path, compiling the jade files found. + * Always walk the subdirectories. + */ + +function renderFile(path) { + var re = /\.jade$/; + fs.lstat(path, function(err, stat) { + if (err) throw err; + // Found jade file + if (stat.isFile() && re.test(path)) { + fs.readFile(path, 'utf8', function(err, str){ + if (err) throw err; + options.filename = path; + var fn = jade.compile(str, options); + var extname = options.client ? '.js' : '.html'; + path = path.replace(re, extname); + if (program.out) path = join(program.out, basename(path)); + var dir = resolve(dirname(path)); + mkdirp(dir, 0755, function(err){ + if (err) throw err; + var output = options.client + ? fn.toString() + : fn(options); + fs.writeFile(path, output, function(err){ + if (err) throw err; + console.log(' \033[90mrendered \033[36m%s\033[0m', path); + }); + }); + }); + // Found directory + } else if (stat.isDirectory()) { + fs.readdir(path, function(err, files) { + if (err) throw err; + files.map(function(filename) { + return path + '/' + filename; + }).forEach(renderFile); + }); + } + }); +} diff --git a/node_modules/mocha/node_modules/jade/index.js b/node_modules/mocha/node_modules/jade/index.js new file mode 100644 index 0000000..8ad059f --- /dev/null +++ b/node_modules/mocha/node_modules/jade/index.js @@ -0,0 +1,4 @@ + +module.exports = process.env.JADE_COV + ? require('./lib-cov/jade') + : require('./lib/jade'); \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/jade.js b/node_modules/mocha/node_modules/jade/jade.js new file mode 100644 index 0000000..1983a20 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/jade.js @@ -0,0 +1,3586 @@ +(function() { + +// CommonJS require() + +function require(p){ + var path = require.resolve(p) + , mod = require.modules[path]; + if (!mod) throw new Error('failed to require "' + p + '"'); + if (!mod.exports) { + mod.exports = {}; + mod.call(mod.exports, mod, mod.exports, require.relative(path)); + } + return mod.exports; + } + +require.modules = {}; + +require.resolve = function (path){ + var orig = path + , reg = path + '.js' + , index = path + '/index.js'; + return require.modules[reg] && reg + || require.modules[index] && index + || orig; + }; + +require.register = function (path, fn){ + require.modules[path] = fn; + }; + +require.relative = function (parent) { + return function(p){ + if ('.' != p.charAt(0)) return require(p); + + var path = parent.split('/') + , segs = p.split('/'); + path.pop(); + + for (var i = 0; i < segs.length; i++) { + var seg = segs[i]; + if ('..' == seg) path.pop(); + else if ('.' != seg) path.push(seg); + } + + return require(path.join('/')); + }; + }; + + +require.register("compiler.js", function(module, exports, require){ + +/*! + * Jade - Compiler + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var nodes = require('./nodes') + , filters = require('./filters') + , doctypes = require('./doctypes') + , selfClosing = require('./self-closing') + , runtime = require('./runtime') + , utils = require('./utils'); + + + if (!Object.keys) { + Object.keys = function(obj){ + var arr = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + arr.push(key); + } + } + return arr; + } + } + + if (!String.prototype.trimLeft) { + String.prototype.trimLeft = function(){ + return this.replace(/^\s+/, ''); + } + } + + + +/** + * Initialize `Compiler` with the given `node`. + * + * @param {Node} node + * @param {Object} options + * @api public + */ + +var Compiler = module.exports = function Compiler(node, options) { + this.options = options = options || {}; + this.node = node; + this.hasCompiledDoctype = false; + this.hasCompiledTag = false; + this.pp = options.pretty || false; + this.debug = false !== options.compileDebug; + this.indents = 0; + this.parentIndents = 0; + if (options.doctype) this.setDoctype(options.doctype); +}; + +/** + * Compiler prototype. + */ + +Compiler.prototype = { + + /** + * Compile parse tree to JavaScript. + * + * @api public + */ + + compile: function(){ + this.buf = ['var interp;']; + if (this.pp) this.buf.push("var __indent = [];"); + this.lastBufferedIdx = -1; + this.visit(this.node); + return this.buf.join('\n'); + }, + + /** + * Sets the default doctype `name`. Sets terse mode to `true` when + * html 5 is used, causing self-closing tags to end with ">" vs "/>", + * and boolean attributes are not mirrored. + * + * @param {string} name + * @api public + */ + + setDoctype: function(name){ + var doctype = doctypes[(name || 'default').toLowerCase()]; + doctype = doctype || ''; + this.doctype = doctype; + this.terse = '5' == name || 'html' == name; + this.xml = 0 == this.doctype.indexOf(' 1 && !escape && block.nodes[0].isText && block.nodes[1].isText) + this.prettyIndent(1, true); + + for (var i = 0; i < len; ++i) { + // Pretty print text + if (pp && i > 0 && !escape && block.nodes[i].isText && block.nodes[i-1].isText) + this.prettyIndent(1, false); + + this.visit(block.nodes[i]); + // Multiple text nodes are separated by newlines + if (block.nodes[i+1] && block.nodes[i].isText && block.nodes[i+1].isText) + this.buffer('\\n'); + } + }, + + /** + * Visit `doctype`. Sets terse mode to `true` when html 5 + * is used, causing self-closing tags to end with ">" vs "/>", + * and boolean attributes are not mirrored. + * + * @param {Doctype} doctype + * @api public + */ + + visitDoctype: function(doctype){ + if (doctype && (doctype.val || !this.doctype)) { + this.setDoctype(doctype.val || 'default'); + } + + if (this.doctype) this.buffer(this.doctype); + this.hasCompiledDoctype = true; + }, + + /** + * Visit `mixin`, generating a function that + * may be called within the template. + * + * @param {Mixin} mixin + * @api public + */ + + visitMixin: function(mixin){ + var name = mixin.name.replace(/-/g, '_') + '_mixin' + , args = mixin.args || '' + , block = mixin.block + , attrs = mixin.attrs + , pp = this.pp; + + if (mixin.call) { + if (pp) this.buf.push("__indent.push('" + Array(this.indents + 1).join(' ') + "');") + if (block || attrs.length) { + + this.buf.push(name + '.call({'); + + if (block) { + this.buf.push('block: function(){'); + + // Render block with no indents, dynamically added when rendered + this.parentIndents++; + var _indents = this.indents; + this.indents = 0; + this.visit(mixin.block); + this.indents = _indents; + this.parentIndents--; + + if (attrs.length) { + this.buf.push('},'); + } else { + this.buf.push('}'); + } + } + + if (attrs.length) { + var val = this.attrs(attrs); + if (val.inherits) { + this.buf.push('attributes: merge({' + val.buf + + '}, attributes), escaped: merge(' + val.escaped + ', escaped, true)'); + } else { + this.buf.push('attributes: {' + val.buf + '}, escaped: ' + val.escaped); + } + } + + if (args) { + this.buf.push('}, ' + args + ');'); + } else { + this.buf.push('});'); + } + + } else { + this.buf.push(name + '(' + args + ');'); + } + if (pp) this.buf.push("__indent.pop();") + } else { + this.buf.push('var ' + name + ' = function(' + args + '){'); + this.buf.push('var block = this.block, attributes = this.attributes || {}, escaped = this.escaped || {};'); + this.parentIndents++; + this.visit(block); + this.parentIndents--; + this.buf.push('};'); + } + }, + + /** + * Visit `tag` buffering tag markup, generating + * attributes, visiting the `tag`'s code and block. + * + * @param {Tag} tag + * @api public + */ + + visitTag: function(tag){ + this.indents++; + var name = tag.name + , pp = this.pp; + + if (tag.buffer) name = "' + (" + name + ") + '"; + + if (!this.hasCompiledTag) { + if (!this.hasCompiledDoctype && 'html' == name) { + this.visitDoctype(); + } + this.hasCompiledTag = true; + } + + // pretty print + if (pp && !tag.isInline()) + this.prettyIndent(0, true); + + if ((~selfClosing.indexOf(name) || tag.selfClosing) && !this.xml) { + this.buffer('<' + name); + this.visitAttributes(tag.attrs); + this.terse + ? this.buffer('>') + : this.buffer('/>'); + } else { + // Optimize attributes buffering + if (tag.attrs.length) { + this.buffer('<' + name); + if (tag.attrs.length) this.visitAttributes(tag.attrs); + this.buffer('>'); + } else { + this.buffer('<' + name + '>'); + } + if (tag.code) this.visitCode(tag.code); + this.escape = 'pre' == tag.name; + this.visit(tag.block); + + // pretty print + if (pp && !tag.isInline() && 'pre' != tag.name && !tag.canInline()) + this.prettyIndent(0, true); + + this.buffer(''); + } + this.indents--; + }, + + /** + * Visit `filter`, throwing when the filter does not exist. + * + * @param {Filter} filter + * @api public + */ + + visitFilter: function(filter){ + var fn = filters[filter.name]; + + // unknown filter + if (!fn) { + if (filter.isASTFilter) { + throw new Error('unknown ast filter "' + filter.name + ':"'); + } else { + throw new Error('unknown filter ":' + filter.name + '"'); + } + } + + if (filter.isASTFilter) { + this.buf.push(fn(filter.block, this, filter.attrs)); + } else { + var text = filter.block.nodes.map(function(node){ return node.val }).join('\n'); + filter.attrs = filter.attrs || {}; + filter.attrs.filename = this.options.filename; + this.buffer(utils.text(fn(text, filter.attrs))); + } + }, + + /** + * Visit `text` node. + * + * @param {Text} text + * @api public + */ + + visitText: function(text){ + text = utils.text(text.val.replace(/\\/g, '\\\\')); + if (this.escape) text = escape(text); + this.buffer(text); + }, + + /** + * Visit a `comment`, only buffering when the buffer flag is set. + * + * @param {Comment} comment + * @api public + */ + + visitComment: function(comment){ + if (!comment.buffer) return; + if (this.pp) this.prettyIndent(1, true); + this.buffer(''); + }, + + /** + * Visit a `BlockComment`. + * + * @param {Comment} comment + * @api public + */ + + visitBlockComment: function(comment){ + if (!comment.buffer) return; + if (0 == comment.val.trim().indexOf('if')) { + this.buffer(''); + } else { + this.buffer(''); + } + }, + + /** + * Visit `code`, respecting buffer / escape flags. + * If the code is followed by a block, wrap it in + * a self-calling function. + * + * @param {Code} code + * @api public + */ + + visitCode: function(code){ + // Wrap code blocks with {}. + // we only wrap unbuffered code blocks ATM + // since they are usually flow control + + // Buffer code + if (code.buffer) { + var val = code.val.trimLeft(); + this.buf.push('var __val__ = ' + val); + val = 'null == __val__ ? "" : __val__'; + if (code.escape) val = 'escape(' + val + ')'; + this.buf.push("buf.push(" + val + ");"); + } else { + this.buf.push(code.val); + } + + // Block support + if (code.block) { + if (!code.buffer) this.buf.push('{'); + this.visit(code.block); + if (!code.buffer) this.buf.push('}'); + } + }, + + /** + * Visit `each` block. + * + * @param {Each} each + * @api public + */ + + visitEach: function(each){ + this.buf.push('' + + '// iterate ' + each.obj + '\n' + + ';(function(){\n' + + ' if (\'number\' == typeof ' + each.obj + '.length) {\n' + + ' for (var ' + each.key + ' = 0, $$l = ' + each.obj + '.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n' + + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); + + this.visit(each.block); + + this.buf.push('' + + ' }\n' + + ' } else {\n' + + ' for (var ' + each.key + ' in ' + each.obj + ') {\n' + + ' if (' + each.obj + '.hasOwnProperty(' + each.key + ')){' + + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); + + this.visit(each.block); + + this.buf.push(' }\n'); + + this.buf.push(' }\n }\n}).call(this);\n'); + }, + + /** + * Visit `attrs`. + * + * @param {Array} attrs + * @api public + */ + + visitAttributes: function(attrs){ + var val = this.attrs(attrs); + if (val.inherits) { + this.buf.push("buf.push(attrs(merge({ " + val.buf + + " }, attributes), merge(" + val.escaped + ", escaped, true)));"); + } else if (val.constant) { + eval('var buf={' + val.buf + '};'); + this.buffer(runtime.attrs(buf, JSON.parse(val.escaped)), true); + } else { + this.buf.push("buf.push(attrs({ " + val.buf + " }, " + val.escaped + "));"); + } + }, + + /** + * Compile attributes. + */ + + attrs: function(attrs){ + var buf = [] + , classes = [] + , escaped = {} + , constant = attrs.every(function(attr){ return isConstant(attr.val) }) + , inherits = false; + + if (this.terse) buf.push('terse: true'); + + attrs.forEach(function(attr){ + if (attr.name == 'attributes') return inherits = true; + escaped[attr.name] = attr.escaped; + if (attr.name == 'class') { + classes.push('(' + attr.val + ')'); + } else { + var pair = "'" + attr.name + "':(" + attr.val + ')'; + buf.push(pair); + } + }); + + if (classes.length) { + classes = classes.join(" + ' ' + "); + buf.push("class: " + classes); + } + + return { + buf: buf.join(', ').replace('class:', '"class":'), + escaped: JSON.stringify(escaped), + inherits: inherits, + constant: constant + }; + } +}; + +/** + * Check if expression can be evaluated to a constant + * + * @param {String} expression + * @return {Boolean} + * @api private + */ + +function isConstant(val){ + // Check strings/literals + if (/^ *("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|true|false|null|undefined) *$/i.test(val)) + return true; + + // Check numbers + if (!isNaN(Number(val))) + return true; + + // Check arrays + var matches; + if (matches = /^ *\[(.*)\] *$/.exec(val)) + return matches[1].split(',').every(isConstant); + + return false; +} + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +function escape(html){ + return String(html) + .replace(/&(?!\w+;)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} +}); // module: compiler.js + +require.register("doctypes.js", function(module, exports, require){ + +/*! + * Jade - doctypes + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = { + '5': '' + , 'default': '' + , 'xml': '' + , 'transitional': '' + , 'strict': '' + , 'frameset': '' + , '1.1': '' + , 'basic': '' + , 'mobile': '' +}; +}); // module: doctypes.js + +require.register("filters.js", function(module, exports, require){ + +/*! + * Jade - filters + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = { + + /** + * Wrap text with CDATA block. + */ + + cdata: function(str){ + return ''; + }, + + /** + * Transform sass to css, wrapped in style tags. + */ + + sass: function(str){ + str = str.replace(/\\n/g, '\n'); + var sass = require('sass').render(str).replace(/\n/g, '\\n'); + return ''; + }, + + /** + * Transform stylus to css, wrapped in style tags. + */ + + stylus: function(str, options){ + var ret; + str = str.replace(/\\n/g, '\n'); + var stylus = require('stylus'); + stylus(str, options).render(function(err, css){ + if (err) throw err; + ret = css.replace(/\n/g, '\\n'); + }); + return ''; + }, + + /** + * Transform less to css, wrapped in style tags. + */ + + less: function(str){ + var ret; + str = str.replace(/\\n/g, '\n'); + require('less').render(str, function(err, css){ + if (err) throw err; + ret = ''; + }); + return ret; + }, + + /** + * Transform markdown to html. + */ + + markdown: function(str){ + var md; + + // support markdown / discount + try { + md = require('markdown'); + } catch (err){ + try { + md = require('discount'); + } catch (err) { + try { + md = require('markdown-js'); + } catch (err) { + try { + md = require('marked'); + } catch (err) { + throw new + Error('Cannot find markdown library, install markdown, discount, or marked.'); + } + } + } + } + + str = str.replace(/\\n/g, '\n'); + return md.parse(str).replace(/\n/g, '\\n').replace(/'/g,'''); + }, + + /** + * Transform coffeescript to javascript. + */ + + coffeescript: function(str){ + str = str.replace(/\\n/g, '\n'); + var js = require('coffee-script').compile(str).replace(/\\/g, '\\\\').replace(/\n/g, '\\n'); + return ''; + } +}; + +}); // module: filters.js + +require.register("inline-tags.js", function(module, exports, require){ + +/*! + * Jade - inline tags + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = [ + 'a' + , 'abbr' + , 'acronym' + , 'b' + , 'br' + , 'code' + , 'em' + , 'font' + , 'i' + , 'img' + , 'ins' + , 'kbd' + , 'map' + , 'samp' + , 'small' + , 'span' + , 'strong' + , 'sub' + , 'sup' +]; +}); // module: inline-tags.js + +require.register("jade.js", function(module, exports, require){ +/*! + * Jade + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Parser = require('./parser') + , Lexer = require('./lexer') + , Compiler = require('./compiler') + , runtime = require('./runtime') + +/** + * Library version. + */ + +exports.version = '0.26.1'; + +/** + * Expose self closing tags. + */ + +exports.selfClosing = require('./self-closing'); + +/** + * Default supported doctypes. + */ + +exports.doctypes = require('./doctypes'); + +/** + * Text filters. + */ + +exports.filters = require('./filters'); + +/** + * Utilities. + */ + +exports.utils = require('./utils'); + +/** + * Expose `Compiler`. + */ + +exports.Compiler = Compiler; + +/** + * Expose `Parser`. + */ + +exports.Parser = Parser; + +/** + * Expose `Lexer`. + */ + +exports.Lexer = Lexer; + +/** + * Nodes. + */ + +exports.nodes = require('./nodes'); + +/** + * Jade runtime helpers. + */ + +exports.runtime = runtime; + +/** + * Template function cache. + */ + +exports.cache = {}; + +/** + * Parse the given `str` of jade and return a function body. + * + * @param {String} str + * @param {Object} options + * @return {String} + * @api private + */ + +function parse(str, options){ + try { + // Parse + var parser = new Parser(str, options.filename, options); + + // Compile + var compiler = new (options.compiler || Compiler)(parser.parse(), options) + , js = compiler.compile(); + + // Debug compiler + if (options.debug) { + console.error('\nCompiled Function:\n\n\033[90m%s\033[0m', js.replace(/^/gm, ' ')); + } + + return '' + + 'var buf = [];\n' + + (options.self + ? 'var self = locals || {};\n' + js + : 'with (locals || {}) {\n' + js + '\n}\n') + + 'return buf.join("");'; + } catch (err) { + parser = parser.context(); + runtime.rethrow(err, parser.filename, parser.lexer.lineno); + } +} + +/** + * Compile a `Function` representation of the given jade `str`. + * + * Options: + * + * - `compileDebug` when `false` debugging code is stripped from the compiled template + * - `client` when `true` the helper functions `escape()` etc will reference `jade.escape()` + * for use with the Jade client-side runtime.js + * + * @param {String} str + * @param {Options} options + * @return {Function} + * @api public + */ + +exports.compile = function(str, options){ + var options = options || {} + , client = options.client + , filename = options.filename + ? JSON.stringify(options.filename) + : 'undefined' + , fn; + + if (options.compileDebug !== false) { + fn = [ + 'var __jade = [{ lineno: 1, filename: ' + filename + ' }];' + , 'try {' + , parse(String(str), options) + , '} catch (err) {' + , ' rethrow(err, __jade[0].filename, __jade[0].lineno);' + , '}' + ].join('\n'); + } else { + fn = parse(String(str), options); + } + + if (client) { + fn = 'attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;\n' + fn; + } + + fn = new Function('locals, attrs, escape, rethrow, merge', fn); + + if (client) return fn; + + return function(locals){ + return fn(locals, runtime.attrs, runtime.escape, runtime.rethrow, runtime.merge); + }; +}; + +/** + * Render the given `str` of jade and invoke + * the callback `fn(err, str)`. + * + * Options: + * + * - `cache` enable template caching + * - `filename` filename required for `include` / `extends` and caching + * + * @param {String} str + * @param {Object|Function} options or fn + * @param {Function} fn + * @api public + */ + +exports.render = function(str, options, fn){ + // swap args + if ('function' == typeof options) { + fn = options, options = {}; + } + + // cache requires .filename + if (options.cache && !options.filename) { + return fn(new Error('the "filename" option is required for caching')); + } + + try { + var path = options.filename; + var tmpl = options.cache + ? exports.cache[path] || (exports.cache[path] = exports.compile(str, options)) + : exports.compile(str, options); + fn(null, tmpl(options)); + } catch (err) { + fn(err); + } +}; + +/** + * Render a Jade file at the given `path` and callback `fn(err, str)`. + * + * @param {String} path + * @param {Object|Function} options or callback + * @param {Function} fn + * @api public + */ + +exports.renderFile = function(path, options, fn){ + var key = path + ':string'; + + if ('function' == typeof options) { + fn = options, options = {}; + } + + try { + options.filename = path; + var str = options.cache + ? exports.cache[key] || (exports.cache[key] = fs.readFileSync(path, 'utf8')) + : fs.readFileSync(path, 'utf8'); + exports.render(str, options, fn); + } catch (err) { + fn(err); + } +}; + +/** + * Express support. + */ + +exports.__express = exports.renderFile; + +}); // module: jade.js + +require.register("lexer.js", function(module, exports, require){ + +/*! + * Jade - Lexer + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Initialize `Lexer` with the given `str`. + * + * Options: + * + * - `colons` allow colons for attr delimiters + * + * @param {String} str + * @param {Object} options + * @api private + */ + +var Lexer = module.exports = function Lexer(str, options) { + options = options || {}; + this.input = str.replace(/\r\n|\r/g, '\n'); + this.colons = options.colons; + this.deferredTokens = []; + this.lastIndents = 0; + this.lineno = 1; + this.stash = []; + this.indentStack = []; + this.indentRe = null; + this.pipeless = false; +}; + +/** + * Lexer prototype. + */ + +Lexer.prototype = { + + /** + * Construct a token with the given `type` and `val`. + * + * @param {String} type + * @param {String} val + * @return {Object} + * @api private + */ + + tok: function(type, val){ + return { + type: type + , line: this.lineno + , val: val + } + }, + + /** + * Consume the given `len` of input. + * + * @param {Number} len + * @api private + */ + + consume: function(len){ + this.input = this.input.substr(len); + }, + + /** + * Scan for `type` with the given `regexp`. + * + * @param {String} type + * @param {RegExp} regexp + * @return {Object} + * @api private + */ + + scan: function(regexp, type){ + var captures; + if (captures = regexp.exec(this.input)) { + this.consume(captures[0].length); + return this.tok(type, captures[1]); + } + }, + + /** + * Defer the given `tok`. + * + * @param {Object} tok + * @api private + */ + + defer: function(tok){ + this.deferredTokens.push(tok); + }, + + /** + * Lookahead `n` tokens. + * + * @param {Number} n + * @return {Object} + * @api private + */ + + lookahead: function(n){ + var fetch = n - this.stash.length; + while (fetch-- > 0) this.stash.push(this.next()); + return this.stash[--n]; + }, + + /** + * Return the indexOf `start` / `end` delimiters. + * + * @param {String} start + * @param {String} end + * @return {Number} + * @api private + */ + + indexOfDelimiters: function(start, end){ + var str = this.input + , nstart = 0 + , nend = 0 + , pos = 0; + for (var i = 0, len = str.length; i < len; ++i) { + if (start == str.charAt(i)) { + ++nstart; + } else if (end == str.charAt(i)) { + if (++nend == nstart) { + pos = i; + break; + } + } + } + return pos; + }, + + /** + * Stashed token. + */ + + stashed: function() { + return this.stash.length + && this.stash.shift(); + }, + + /** + * Deferred token. + */ + + deferred: function() { + return this.deferredTokens.length + && this.deferredTokens.shift(); + }, + + /** + * end-of-source. + */ + + eos: function() { + if (this.input.length) return; + if (this.indentStack.length) { + this.indentStack.shift(); + return this.tok('outdent'); + } else { + return this.tok('eos'); + } + }, + + /** + * Blank line. + */ + + blank: function() { + var captures; + if (captures = /^\n *\n/.exec(this.input)) { + this.consume(captures[0].length - 1); + if (this.pipeless) return this.tok('text', ''); + return this.next(); + } + }, + + /** + * Comment. + */ + + comment: function() { + var captures; + if (captures = /^ *\/\/(-)?([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('comment', captures[2]); + tok.buffer = '-' != captures[1]; + return tok; + } + }, + + /** + * Interpolated tag. + */ + + interpolation: function() { + var captures; + if (captures = /^#\{(.*?)\}/.exec(this.input)) { + this.consume(captures[0].length); + return this.tok('interpolation', captures[1]); + } + }, + + /** + * Tag. + */ + + tag: function() { + var captures; + if (captures = /^(\w[-:\w]*)(\/?)/.exec(this.input)) { + this.consume(captures[0].length); + var tok, name = captures[1]; + if (':' == name[name.length - 1]) { + name = name.slice(0, -1); + tok = this.tok('tag', name); + this.defer(this.tok(':')); + while (' ' == this.input[0]) this.input = this.input.substr(1); + } else { + tok = this.tok('tag', name); + } + tok.selfClosing = !! captures[2]; + return tok; + } + }, + + /** + * Filter. + */ + + filter: function() { + return this.scan(/^:(\w+)/, 'filter'); + }, + + /** + * Doctype. + */ + + doctype: function() { + return this.scan(/^(?:!!!|doctype) *([^\n]+)?/, 'doctype'); + }, + + /** + * Id. + */ + + id: function() { + return this.scan(/^#([\w-]+)/, 'id'); + }, + + /** + * Class. + */ + + className: function() { + return this.scan(/^\.([\w-]+)/, 'class'); + }, + + /** + * Text. + */ + + text: function() { + return this.scan(/^(?:\| ?| ?)?([^\n]+)/, 'text'); + }, + + /** + * Extends. + */ + + "extends": function() { + return this.scan(/^extends? +([^\n]+)/, 'extends'); + }, + + /** + * Block prepend. + */ + + prepend: function() { + var captures; + if (captures = /^prepend +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = 'prepend' + , name = captures[1] + , tok = this.tok('block', name); + tok.mode = mode; + return tok; + } + }, + + /** + * Block append. + */ + + append: function() { + var captures; + if (captures = /^append +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = 'append' + , name = captures[1] + , tok = this.tok('block', name); + tok.mode = mode; + return tok; + } + }, + + /** + * Block. + */ + + block: function() { + var captures; + if (captures = /^block\b *(?:(prepend|append) +)?([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = captures[1] || 'replace' + , name = captures[2] + , tok = this.tok('block', name); + + tok.mode = mode; + return tok; + } + }, + + /** + * Yield. + */ + + yield: function() { + return this.scan(/^yield */, 'yield'); + }, + + /** + * Include. + */ + + include: function() { + return this.scan(/^include +([^\n]+)/, 'include'); + }, + + /** + * Case. + */ + + "case": function() { + return this.scan(/^case +([^\n]+)/, 'case'); + }, + + /** + * When. + */ + + when: function() { + return this.scan(/^when +([^:\n]+)/, 'when'); + }, + + /** + * Default. + */ + + "default": function() { + return this.scan(/^default */, 'default'); + }, + + /** + * Assignment. + */ + + assignment: function() { + var captures; + if (captures = /^(\w+) += *([^;\n]+)( *;? *)/.exec(this.input)) { + this.consume(captures[0].length); + var name = captures[1] + , val = captures[2]; + return this.tok('code', 'var ' + name + ' = (' + val + ');'); + } + }, + + /** + * Call mixin. + */ + + call: function(){ + var captures; + if (captures = /^\+([-\w]+)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('call', captures[1]); + + // Check for args (not attributes) + if (captures = /^ *\((.*?)\)/.exec(this.input)) { + if (!/^ *[-\w]+ *=/.test(captures[1])) { + this.consume(captures[0].length); + tok.args = captures[1]; + } + } + + return tok; + } + }, + + /** + * Mixin. + */ + + mixin: function(){ + var captures; + if (captures = /^mixin +([-\w]+)(?: *\((.*)\))?/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('mixin', captures[1]); + tok.args = captures[2]; + return tok; + } + }, + + /** + * Conditional. + */ + + conditional: function() { + var captures; + if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var type = captures[1] + , js = captures[2]; + + switch (type) { + case 'if': js = 'if (' + js + ')'; break; + case 'unless': js = 'if (!(' + js + '))'; break; + case 'else if': js = 'else if (' + js + ')'; break; + case 'else': js = 'else'; break; + } + + return this.tok('code', js); + } + }, + + /** + * While. + */ + + "while": function() { + var captures; + if (captures = /^while +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + return this.tok('code', 'while (' + captures[1] + ')'); + } + }, + + /** + * Each. + */ + + each: function() { + var captures; + if (captures = /^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('each', captures[1]); + tok.key = captures[2] || '$index'; + tok.code = captures[3]; + return tok; + } + }, + + /** + * Code. + */ + + code: function() { + var captures; + if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var flags = captures[1]; + captures[1] = captures[2]; + var tok = this.tok('code', captures[1]); + tok.escape = flags[0] === '='; + tok.buffer = flags[0] === '=' || flags[1] === '='; + return tok; + } + }, + + /** + * Attributes. + */ + + attrs: function() { + if ('(' == this.input.charAt(0)) { + var index = this.indexOfDelimiters('(', ')') + , str = this.input.substr(1, index-1) + , tok = this.tok('attrs') + , len = str.length + , colons = this.colons + , states = ['key'] + , escapedAttr + , key = '' + , val = '' + , quote + , c + , p; + + function state(){ + return states[states.length - 1]; + } + + function interpolate(attr) { + return attr.replace(/#\{([^}]+)\}/g, function(_, expr){ + return quote + " + (" + expr + ") + " + quote; + }); + } + + this.consume(index + 1); + tok.attrs = {}; + tok.escaped = {}; + + function parse(c) { + var real = c; + // TODO: remove when people fix ":" + if (colons && ':' == c) c = '='; + switch (c) { + case ',': + case '\n': + switch (state()) { + case 'expr': + case 'array': + case 'string': + case 'object': + val += c; + break; + default: + states.push('key'); + val = val.trim(); + key = key.trim(); + if ('' == key) return; + key = key.replace(/^['"]|['"]$/g, '').replace('!', ''); + tok.escaped[key] = escapedAttr; + tok.attrs[key] = '' == val + ? true + : interpolate(val); + key = val = ''; + } + break; + case '=': + switch (state()) { + case 'key char': + key += real; + break; + case 'val': + case 'expr': + case 'array': + case 'string': + case 'object': + val += real; + break; + default: + escapedAttr = '!' != p; + states.push('val'); + } + break; + case '(': + if ('val' == state() + || 'expr' == state()) states.push('expr'); + val += c; + break; + case ')': + if ('expr' == state() + || 'val' == state()) states.pop(); + val += c; + break; + case '{': + if ('val' == state()) states.push('object'); + val += c; + break; + case '}': + if ('object' == state()) states.pop(); + val += c; + break; + case '[': + if ('val' == state()) states.push('array'); + val += c; + break; + case ']': + if ('array' == state()) states.pop(); + val += c; + break; + case '"': + case "'": + switch (state()) { + case 'key': + states.push('key char'); + break; + case 'key char': + states.pop(); + break; + case 'string': + if (c == quote) states.pop(); + val += c; + break; + default: + states.push('string'); + val += c; + quote = c; + } + break; + case '': + break; + default: + switch (state()) { + case 'key': + case 'key char': + key += c; + break; + default: + val += c; + } + } + p = c; + } + + for (var i = 0; i < len; ++i) { + parse(str.charAt(i)); + } + + parse(','); + + if ('/' == this.input.charAt(0)) { + this.consume(1); + tok.selfClosing = true; + } + + return tok; + } + }, + + /** + * Indent | Outdent | Newline. + */ + + indent: function() { + var captures, re; + + // established regexp + if (this.indentRe) { + captures = this.indentRe.exec(this.input); + // determine regexp + } else { + // tabs + re = /^\n(\t*) */; + captures = re.exec(this.input); + + // spaces + if (captures && !captures[1].length) { + re = /^\n( *)/; + captures = re.exec(this.input); + } + + // established + if (captures && captures[1].length) this.indentRe = re; + } + + if (captures) { + var tok + , indents = captures[1].length; + + ++this.lineno; + this.consume(indents + 1); + + if (' ' == this.input[0] || '\t' == this.input[0]) { + throw new Error('Invalid indentation, you can use tabs or spaces but not both'); + } + + // blank line + if ('\n' == this.input[0]) return this.tok('newline'); + + // outdent + if (this.indentStack.length && indents < this.indentStack[0]) { + while (this.indentStack.length && this.indentStack[0] > indents) { + this.stash.push(this.tok('outdent')); + this.indentStack.shift(); + } + tok = this.stash.pop(); + // indent + } else if (indents && indents != this.indentStack[0]) { + this.indentStack.unshift(indents); + tok = this.tok('indent', indents); + // newline + } else { + tok = this.tok('newline'); + } + + return tok; + } + }, + + /** + * Pipe-less text consumed only when + * pipeless is true; + */ + + pipelessText: function() { + if (this.pipeless) { + if ('\n' == this.input[0]) return; + var i = this.input.indexOf('\n'); + if (-1 == i) i = this.input.length; + var str = this.input.substr(0, i); + this.consume(str.length); + return this.tok('text', str); + } + }, + + /** + * ':' + */ + + colon: function() { + return this.scan(/^: */, ':'); + }, + + /** + * Return the next token object, or those + * previously stashed by lookahead. + * + * @return {Object} + * @api private + */ + + advance: function(){ + return this.stashed() + || this.next(); + }, + + /** + * Return the next token object. + * + * @return {Object} + * @api private + */ + + next: function() { + return this.deferred() + || this.blank() + || this.eos() + || this.pipelessText() + || this.yield() + || this.doctype() + || this.interpolation() + || this["case"]() + || this.when() + || this["default"]() + || this["extends"]() + || this.append() + || this.prepend() + || this.block() + || this.include() + || this.mixin() + || this.call() + || this.conditional() + || this.each() + || this["while"]() + || this.assignment() + || this.tag() + || this.filter() + || this.code() + || this.id() + || this.className() + || this.attrs() + || this.indent() + || this.comment() + || this.colon() + || this.text(); + } +}; + +}); // module: lexer.js + +require.register("nodes/attrs.js", function(module, exports, require){ + +/*! + * Jade - nodes - Attrs + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'), + Block = require('./block'); + +/** + * Initialize a `Attrs` node. + * + * @api public + */ + +var Attrs = module.exports = function Attrs() { + this.attrs = []; +}; + +/** + * Inherit from `Node`. + */ + +Attrs.prototype = new Node; +Attrs.prototype.constructor = Attrs; + + +/** + * Set attribute `name` to `val`, keep in mind these become + * part of a raw js object literal, so to quote a value you must + * '"quote me"', otherwise or example 'user.name' is literal JavaScript. + * + * @param {String} name + * @param {String} val + * @param {Boolean} escaped + * @return {Tag} for chaining + * @api public + */ + +Attrs.prototype.setAttribute = function(name, val, escaped){ + this.attrs.push({ name: name, val: val, escaped: escaped }); + return this; +}; + +/** + * Remove attribute `name` when present. + * + * @param {String} name + * @api public + */ + +Attrs.prototype.removeAttribute = function(name){ + for (var i = 0, len = this.attrs.length; i < len; ++i) { + if (this.attrs[i] && this.attrs[i].name == name) { + delete this.attrs[i]; + } + } +}; + +/** + * Get attribute value by `name`. + * + * @param {String} name + * @return {String} + * @api public + */ + +Attrs.prototype.getAttribute = function(name){ + for (var i = 0, len = this.attrs.length; i < len; ++i) { + if (this.attrs[i] && this.attrs[i].name == name) { + return this.attrs[i].val; + } + } +}; + +}); // module: nodes/attrs.js + +require.register("nodes/block-comment.js", function(module, exports, require){ + +/*! + * Jade - nodes - BlockComment + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `BlockComment` with the given `block`. + * + * @param {String} val + * @param {Block} block + * @param {Boolean} buffer + * @api public + */ + +var BlockComment = module.exports = function BlockComment(val, block, buffer) { + this.block = block; + this.val = val; + this.buffer = buffer; +}; + +/** + * Inherit from `Node`. + */ + +BlockComment.prototype = new Node; +BlockComment.prototype.constructor = BlockComment; + +}); // module: nodes/block-comment.js + +require.register("nodes/block.js", function(module, exports, require){ + +/*! + * Jade - nodes - Block + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a new `Block` with an optional `node`. + * + * @param {Node} node + * @api public + */ + +var Block = module.exports = function Block(node){ + this.nodes = []; + if (node) this.push(node); +}; + +/** + * Inherit from `Node`. + */ + +Block.prototype = new Node; +Block.prototype.constructor = Block; + + +/** + * Block flag. + */ + +Block.prototype.isBlock = true; + +/** + * Replace the nodes in `other` with the nodes + * in `this` block. + * + * @param {Block} other + * @api private + */ + +Block.prototype.replace = function(other){ + other.nodes = this.nodes; +}; + +/** + * Pust the given `node`. + * + * @param {Node} node + * @return {Number} + * @api public + */ + +Block.prototype.push = function(node){ + return this.nodes.push(node); +}; + +/** + * Check if this block is empty. + * + * @return {Boolean} + * @api public + */ + +Block.prototype.isEmpty = function(){ + return 0 == this.nodes.length; +}; + +/** + * Unshift the given `node`. + * + * @param {Node} node + * @return {Number} + * @api public + */ + +Block.prototype.unshift = function(node){ + return this.nodes.unshift(node); +}; + +/** + * Return the "last" block, or the first `yield` node. + * + * @return {Block} + * @api private + */ + +Block.prototype.includeBlock = function(){ + var ret = this + , node; + + for (var i = 0, len = this.nodes.length; i < len; ++i) { + node = this.nodes[i]; + if (node.yield) return node; + else if (node.textOnly) continue; + else if (node.includeBlock) ret = node.includeBlock(); + else if (node.block && !node.block.isEmpty()) ret = node.block.includeBlock(); + } + + return ret; +}; + +/** + * Return a clone of this block. + * + * @return {Block} + * @api private + */ + +Block.prototype.clone = function(){ + var clone = new Block; + for (var i = 0, len = this.nodes.length; i < len; ++i) { + clone.push(this.nodes[i].clone()); + } + return clone; +}; + + +}); // module: nodes/block.js + +require.register("nodes/case.js", function(module, exports, require){ + +/*! + * Jade - nodes - Case + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a new `Case` with `expr`. + * + * @param {String} expr + * @api public + */ + +var Case = exports = module.exports = function Case(expr, block){ + this.expr = expr; + this.block = block; +}; + +/** + * Inherit from `Node`. + */ + +Case.prototype = new Node; +Case.prototype.constructor = Case; + + +var When = exports.When = function When(expr, block){ + this.expr = expr; + this.block = block; + this.debug = false; +}; + +/** + * Inherit from `Node`. + */ + +When.prototype = new Node; +When.prototype.constructor = When; + + + +}); // module: nodes/case.js + +require.register("nodes/code.js", function(module, exports, require){ + +/*! + * Jade - nodes - Code + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Code` node with the given code `val`. + * Code may also be optionally buffered and escaped. + * + * @param {String} val + * @param {Boolean} buffer + * @param {Boolean} escape + * @api public + */ + +var Code = module.exports = function Code(val, buffer, escape) { + this.val = val; + this.buffer = buffer; + this.escape = escape; + if (val.match(/^ *else/)) this.debug = false; +}; + +/** + * Inherit from `Node`. + */ + +Code.prototype = new Node; +Code.prototype.constructor = Code; + +}); // module: nodes/code.js + +require.register("nodes/comment.js", function(module, exports, require){ + +/*! + * Jade - nodes - Comment + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Comment` with the given `val`, optionally `buffer`, + * otherwise the comment may render in the output. + * + * @param {String} val + * @param {Boolean} buffer + * @api public + */ + +var Comment = module.exports = function Comment(val, buffer) { + this.val = val; + this.buffer = buffer; +}; + +/** + * Inherit from `Node`. + */ + +Comment.prototype = new Node; +Comment.prototype.constructor = Comment; + +}); // module: nodes/comment.js + +require.register("nodes/doctype.js", function(module, exports, require){ + +/*! + * Jade - nodes - Doctype + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Doctype` with the given `val`. + * + * @param {String} val + * @api public + */ + +var Doctype = module.exports = function Doctype(val) { + this.val = val; +}; + +/** + * Inherit from `Node`. + */ + +Doctype.prototype = new Node; +Doctype.prototype.constructor = Doctype; + +}); // module: nodes/doctype.js + +require.register("nodes/each.js", function(module, exports, require){ + +/*! + * Jade - nodes - Each + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize an `Each` node, representing iteration + * + * @param {String} obj + * @param {String} val + * @param {String} key + * @param {Block} block + * @api public + */ + +var Each = module.exports = function Each(obj, val, key, block) { + this.obj = obj; + this.val = val; + this.key = key; + this.block = block; +}; + +/** + * Inherit from `Node`. + */ + +Each.prototype = new Node; +Each.prototype.constructor = Each; + +}); // module: nodes/each.js + +require.register("nodes/filter.js", function(module, exports, require){ + +/*! + * Jade - nodes - Filter + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node') + , Block = require('./block'); + +/** + * Initialize a `Filter` node with the given + * filter `name` and `block`. + * + * @param {String} name + * @param {Block|Node} block + * @api public + */ + +var Filter = module.exports = function Filter(name, block, attrs) { + this.name = name; + this.block = block; + this.attrs = attrs; + this.isASTFilter = !block.nodes.every(function(node){ return node.isText }); +}; + +/** + * Inherit from `Node`. + */ + +Filter.prototype = new Node; +Filter.prototype.constructor = Filter; + +}); // module: nodes/filter.js + +require.register("nodes/index.js", function(module, exports, require){ + +/*! + * Jade - nodes + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +exports.Node = require('./node'); +exports.Tag = require('./tag'); +exports.Code = require('./code'); +exports.Each = require('./each'); +exports.Case = require('./case'); +exports.Text = require('./text'); +exports.Block = require('./block'); +exports.Mixin = require('./mixin'); +exports.Filter = require('./filter'); +exports.Comment = require('./comment'); +exports.Literal = require('./literal'); +exports.BlockComment = require('./block-comment'); +exports.Doctype = require('./doctype'); + +}); // module: nodes/index.js + +require.register("nodes/literal.js", function(module, exports, require){ + +/*! + * Jade - nodes - Literal + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Literal` node with the given `str. + * + * @param {String} str + * @api public + */ + +var Literal = module.exports = function Literal(str) { + this.str = str + .replace(/\\/g, "\\\\") + .replace(/\n|\r\n/g, "\\n") + .replace(/'/g, "\\'"); +}; + +/** + * Inherit from `Node`. + */ + +Literal.prototype = new Node; +Literal.prototype.constructor = Literal; + + +}); // module: nodes/literal.js + +require.register("nodes/mixin.js", function(module, exports, require){ + +/*! + * Jade - nodes - Mixin + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Attrs = require('./attrs'); + +/** + * Initialize a new `Mixin` with `name` and `block`. + * + * @param {String} name + * @param {String} args + * @param {Block} block + * @api public + */ + +var Mixin = module.exports = function Mixin(name, args, block, call){ + this.name = name; + this.args = args; + this.block = block; + this.attrs = []; + this.call = call; +}; + +/** + * Inherit from `Attrs`. + */ + +Mixin.prototype = new Attrs; +Mixin.prototype.constructor = Mixin; + + + +}); // module: nodes/mixin.js + +require.register("nodes/node.js", function(module, exports, require){ + +/*! + * Jade - nodes - Node + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Initialize a `Node`. + * + * @api public + */ + +var Node = module.exports = function Node(){}; + +/** + * Clone this node (return itself) + * + * @return {Node} + * @api private + */ + +Node.prototype.clone = function(){ + return this; +}; + +}); // module: nodes/node.js + +require.register("nodes/tag.js", function(module, exports, require){ + +/*! + * Jade - nodes - Tag + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Attrs = require('./attrs'), + Block = require('./block'), + inlineTags = require('../inline-tags'); + +/** + * Initialize a `Tag` node with the given tag `name` and optional `block`. + * + * @param {String} name + * @param {Block} block + * @api public + */ + +var Tag = module.exports = function Tag(name, block) { + this.name = name; + this.attrs = []; + this.block = block || new Block; +}; + +/** + * Inherit from `Attrs`. + */ + +Tag.prototype = new Attrs; +Tag.prototype.constructor = Tag; + + +/** + * Clone this tag. + * + * @return {Tag} + * @api private + */ + +Tag.prototype.clone = function(){ + var clone = new Tag(this.name, this.block.clone()); + clone.line = this.line; + clone.attrs = this.attrs; + clone.textOnly = this.textOnly; + return clone; +}; + +/** + * Check if this tag is an inline tag. + * + * @return {Boolean} + * @api private + */ + +Tag.prototype.isInline = function(){ + return ~inlineTags.indexOf(this.name); +}; + +/** + * Check if this tag's contents can be inlined. Used for pretty printing. + * + * @return {Boolean} + * @api private + */ + +Tag.prototype.canInline = function(){ + var nodes = this.block.nodes; + + function isInline(node){ + // Recurse if the node is a block + if (node.isBlock) return node.nodes.every(isInline); + return node.isText || (node.isInline && node.isInline()); + } + + // Empty tag + if (!nodes.length) return true; + + // Text-only or inline-only tag + if (1 == nodes.length) return isInline(nodes[0]); + + // Multi-line inline-only tag + if (this.block.nodes.every(isInline)) { + for (var i = 1, len = nodes.length; i < len; ++i) { + if (nodes[i-1].isText && nodes[i].isText) + return false; + } + return true; + } + + // Mixed tag + return false; +}; +}); // module: nodes/tag.js + +require.register("nodes/text.js", function(module, exports, require){ + +/*! + * Jade - nodes - Text + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Text` node with optional `line`. + * + * @param {String} line + * @api public + */ + +var Text = module.exports = function Text(line) { + this.val = ''; + if ('string' == typeof line) this.val = line; +}; + +/** + * Inherit from `Node`. + */ + +Text.prototype = new Node; +Text.prototype.constructor = Text; + + +/** + * Flag as text. + */ + +Text.prototype.isText = true; +}); // module: nodes/text.js + +require.register("parser.js", function(module, exports, require){ + +/*! + * Jade - Parser + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Lexer = require('./lexer') + , nodes = require('./nodes'); + +/** + * Initialize `Parser` with the given input `str` and `filename`. + * + * @param {String} str + * @param {String} filename + * @param {Object} options + * @api public + */ + +var Parser = exports = module.exports = function Parser(str, filename, options){ + this.input = str; + this.lexer = new Lexer(str, options); + this.filename = filename; + this.blocks = {}; + this.mixins = {}; + this.options = options; + this.contexts = [this]; +}; + +/** + * Tags that may not contain tags. + */ + +var textOnly = exports.textOnly = ['script', 'style']; + +/** + * Parser prototype. + */ + +Parser.prototype = { + + /** + * Push `parser` onto the context stack, + * or pop and return a `Parser`. + */ + + context: function(parser){ + if (parser) { + this.contexts.push(parser); + } else { + return this.contexts.pop(); + } + }, + + /** + * Return the next token object. + * + * @return {Object} + * @api private + */ + + advance: function(){ + return this.lexer.advance(); + }, + + /** + * Skip `n` tokens. + * + * @param {Number} n + * @api private + */ + + skip: function(n){ + while (n--) this.advance(); + }, + + /** + * Single token lookahead. + * + * @return {Object} + * @api private + */ + + peek: function() { + return this.lookahead(1); + }, + + /** + * Return lexer lineno. + * + * @return {Number} + * @api private + */ + + line: function() { + return this.lexer.lineno; + }, + + /** + * `n` token lookahead. + * + * @param {Number} n + * @return {Object} + * @api private + */ + + lookahead: function(n){ + return this.lexer.lookahead(n); + }, + + /** + * Parse input returning a string of js for evaluation. + * + * @return {String} + * @api public + */ + + parse: function(){ + var block = new nodes.Block, parser; + block.line = this.line(); + + while ('eos' != this.peek().type) { + if ('newline' == this.peek().type) { + this.advance(); + } else { + block.push(this.parseExpr()); + } + } + + if (parser = this.extending) { + this.context(parser); + var ast = parser.parse(); + this.context(); + // hoist mixins + for (var name in this.mixins) + ast.unshift(this.mixins[name]); + return ast; + } + + return block; + }, + + /** + * Expect the given type, or throw an exception. + * + * @param {String} type + * @api private + */ + + expect: function(type){ + if (this.peek().type === type) { + return this.advance(); + } else { + throw new Error('expected "' + type + '", but got "' + this.peek().type + '"'); + } + }, + + /** + * Accept the given `type`. + * + * @param {String} type + * @api private + */ + + accept: function(type){ + if (this.peek().type === type) { + return this.advance(); + } + }, + + /** + * tag + * | doctype + * | mixin + * | include + * | filter + * | comment + * | text + * | each + * | code + * | yield + * | id + * | class + * | interpolation + */ + + parseExpr: function(){ + switch (this.peek().type) { + case 'tag': + return this.parseTag(); + case 'mixin': + return this.parseMixin(); + case 'block': + return this.parseBlock(); + case 'case': + return this.parseCase(); + case 'when': + return this.parseWhen(); + case 'default': + return this.parseDefault(); + case 'extends': + return this.parseExtends(); + case 'include': + return this.parseInclude(); + case 'doctype': + return this.parseDoctype(); + case 'filter': + return this.parseFilter(); + case 'comment': + return this.parseComment(); + case 'text': + return this.parseText(); + case 'each': + return this.parseEach(); + case 'code': + return this.parseCode(); + case 'call': + return this.parseCall(); + case 'interpolation': + return this.parseInterpolation(); + case 'yield': + this.advance(); + var block = new nodes.Block; + block.yield = true; + return block; + case 'id': + case 'class': + var tok = this.advance(); + this.lexer.defer(this.lexer.tok('tag', 'div')); + this.lexer.defer(tok); + return this.parseExpr(); + default: + throw new Error('unexpected token "' + this.peek().type + '"'); + } + }, + + /** + * Text + */ + + parseText: function(){ + var tok = this.expect('text') + , node = new nodes.Text(tok.val); + node.line = this.line(); + return node; + }, + + /** + * ':' expr + * | block + */ + + parseBlockExpansion: function(){ + if (':' == this.peek().type) { + this.advance(); + return new nodes.Block(this.parseExpr()); + } else { + return this.block(); + } + }, + + /** + * case + */ + + parseCase: function(){ + var val = this.expect('case').val + , node = new nodes.Case(val); + node.line = this.line(); + node.block = this.block(); + return node; + }, + + /** + * when + */ + + parseWhen: function(){ + var val = this.expect('when').val + return new nodes.Case.When(val, this.parseBlockExpansion()); + }, + + /** + * default + */ + + parseDefault: function(){ + this.expect('default'); + return new nodes.Case.When('default', this.parseBlockExpansion()); + }, + + /** + * code + */ + + parseCode: function(){ + var tok = this.expect('code') + , node = new nodes.Code(tok.val, tok.buffer, tok.escape) + , block + , i = 1; + node.line = this.line(); + while (this.lookahead(i) && 'newline' == this.lookahead(i).type) ++i; + block = 'indent' == this.lookahead(i).type; + if (block) { + this.skip(i-1); + node.block = this.block(); + } + return node; + }, + + /** + * comment + */ + + parseComment: function(){ + var tok = this.expect('comment') + , node; + + if ('indent' == this.peek().type) { + node = new nodes.BlockComment(tok.val, this.block(), tok.buffer); + } else { + node = new nodes.Comment(tok.val, tok.buffer); + } + + node.line = this.line(); + return node; + }, + + /** + * doctype + */ + + parseDoctype: function(){ + var tok = this.expect('doctype') + , node = new nodes.Doctype(tok.val); + node.line = this.line(); + return node; + }, + + /** + * filter attrs? text-block + */ + + parseFilter: function(){ + var block + , tok = this.expect('filter') + , attrs = this.accept('attrs'); + + this.lexer.pipeless = true; + block = this.parseTextBlock(); + this.lexer.pipeless = false; + + var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs); + node.line = this.line(); + return node; + }, + + /** + * tag ':' attrs? block + */ + + parseASTFilter: function(){ + var block + , tok = this.expect('tag') + , attrs = this.accept('attrs'); + + this.expect(':'); + block = this.block(); + + var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs); + node.line = this.line(); + return node; + }, + + /** + * each block + */ + + parseEach: function(){ + var tok = this.expect('each') + , node = new nodes.Each(tok.code, tok.val, tok.key); + node.line = this.line(); + node.block = this.block(); + return node; + }, + + /** + * 'extends' name + */ + + parseExtends: function(){ + var path = require('path') + , fs = require('fs') + , dirname = path.dirname + , basename = path.basename + , join = path.join; + + if (!this.filename) + throw new Error('the "filename" option is required to extend templates'); + + var path = this.expect('extends').val.trim() + , dir = dirname(this.filename); + + var path = join(dir, path + '.jade') + , str = fs.readFileSync(path, 'utf8') + , parser = new Parser(str, path, this.options); + + parser.blocks = this.blocks; + parser.contexts = this.contexts; + this.extending = parser; + + // TODO: null node + return new nodes.Literal(''); + }, + + /** + * 'block' name block + */ + + parseBlock: function(){ + var block = this.expect('block') + , mode = block.mode + , name = block.val.trim(); + + block = 'indent' == this.peek().type + ? this.block() + : new nodes.Block(new nodes.Literal('')); + + var prev = this.blocks[name]; + + if (prev) { + switch (prev.mode) { + case 'append': + block.nodes = block.nodes.concat(prev.nodes); + prev = block; + break; + case 'prepend': + block.nodes = prev.nodes.concat(block.nodes); + prev = block; + break; + } + } + + block.mode = mode; + return this.blocks[name] = prev || block; + }, + + /** + * include block? + */ + + parseInclude: function(){ + var path = require('path') + , fs = require('fs') + , dirname = path.dirname + , basename = path.basename + , join = path.join; + + var path = this.expect('include').val.trim() + , dir = dirname(this.filename); + + if (!this.filename) + throw new Error('the "filename" option is required to use includes'); + + // no extension + if (!~basename(path).indexOf('.')) { + path += '.jade'; + } + + // non-jade + if ('.jade' != path.substr(-5)) { + var path = join(dir, path) + , str = fs.readFileSync(path, 'utf8'); + return new nodes.Literal(str); + } + + var path = join(dir, path) + , str = fs.readFileSync(path, 'utf8') + , parser = new Parser(str, path, this.options); + parser.blocks = this.blocks; + parser.mixins = this.mixins; + + this.context(parser); + var ast = parser.parse(); + this.context(); + ast.filename = path; + + if ('indent' == this.peek().type) { + ast.includeBlock().push(this.block()); + } + + return ast; + }, + + /** + * call ident block + */ + + parseCall: function(){ + var tok = this.expect('call') + , name = tok.val + , args = tok.args + , mixin = new nodes.Mixin(name, args, new nodes.Block, true); + + this.tag(mixin); + if (mixin.block.isEmpty()) mixin.block = null; + return mixin; + }, + + /** + * mixin block + */ + + parseMixin: function(){ + var tok = this.expect('mixin') + , name = tok.val + , args = tok.args + , mixin; + + // definition + if ('indent' == this.peek().type) { + mixin = new nodes.Mixin(name, args, this.block(), false); + this.mixins[name] = mixin; + return mixin; + // call + } else { + return new nodes.Mixin(name, args, null, true); + } + }, + + /** + * indent (text | newline)* outdent + */ + + parseTextBlock: function(){ + var block = new nodes.Block; + block.line = this.line(); + var spaces = this.expect('indent').val; + if (null == this._spaces) this._spaces = spaces; + var indent = Array(spaces - this._spaces + 1).join(' '); + while ('outdent' != this.peek().type) { + switch (this.peek().type) { + case 'newline': + this.advance(); + break; + case 'indent': + this.parseTextBlock().nodes.forEach(function(node){ + block.push(node); + }); + break; + default: + var text = new nodes.Text(indent + this.advance().val); + text.line = this.line(); + block.push(text); + } + } + + if (spaces == this._spaces) this._spaces = null; + this.expect('outdent'); + return block; + }, + + /** + * indent expr* outdent + */ + + block: function(){ + var block = new nodes.Block; + block.line = this.line(); + this.expect('indent'); + while ('outdent' != this.peek().type) { + if ('newline' == this.peek().type) { + this.advance(); + } else { + block.push(this.parseExpr()); + } + } + this.expect('outdent'); + return block; + }, + + /** + * interpolation (attrs | class | id)* (text | code | ':')? newline* block? + */ + + parseInterpolation: function(){ + var tok = this.advance(); + var tag = new nodes.Tag(tok.val); + tag.buffer = true; + return this.tag(tag); + }, + + /** + * tag (attrs | class | id)* (text | code | ':')? newline* block? + */ + + parseTag: function(){ + // ast-filter look-ahead + var i = 2; + if ('attrs' == this.lookahead(i).type) ++i; + if (':' == this.lookahead(i).type) { + if ('indent' == this.lookahead(++i).type) { + return this.parseASTFilter(); + } + } + + var tok = this.advance() + , tag = new nodes.Tag(tok.val); + + tag.selfClosing = tok.selfClosing; + + return this.tag(tag); + }, + + /** + * Parse tag. + */ + + tag: function(tag){ + var dot; + + tag.line = this.line(); + + // (attrs | class | id)* + out: + while (true) { + switch (this.peek().type) { + case 'id': + case 'class': + var tok = this.advance(); + tag.setAttribute(tok.type, "'" + tok.val + "'"); + continue; + case 'attrs': + var tok = this.advance() + , obj = tok.attrs + , escaped = tok.escaped + , names = Object.keys(obj); + + if (tok.selfClosing) tag.selfClosing = true; + + for (var i = 0, len = names.length; i < len; ++i) { + var name = names[i] + , val = obj[name]; + tag.setAttribute(name, val, escaped[name]); + } + continue; + default: + break out; + } + } + + // check immediate '.' + if ('.' == this.peek().val) { + dot = tag.textOnly = true; + this.advance(); + } + + // (text | code | ':')? + switch (this.peek().type) { + case 'text': + tag.block.push(this.parseText()); + break; + case 'code': + tag.code = this.parseCode(); + break; + case ':': + this.advance(); + tag.block = new nodes.Block; + tag.block.push(this.parseExpr()); + break; + } + + // newline* + while ('newline' == this.peek().type) this.advance(); + + tag.textOnly = tag.textOnly || ~textOnly.indexOf(tag.name); + + // script special-case + if ('script' == tag.name) { + var type = tag.getAttribute('type'); + if (!dot && type && 'text/javascript' != type.replace(/^['"]|['"]$/g, '')) { + tag.textOnly = false; + } + } + + // block? + if ('indent' == this.peek().type) { + if (tag.textOnly) { + this.lexer.pipeless = true; + tag.block = this.parseTextBlock(); + this.lexer.pipeless = false; + } else { + var block = this.block(); + if (tag.block) { + for (var i = 0, len = block.nodes.length; i < len; ++i) { + tag.block.push(block.nodes[i]); + } + } else { + tag.block = block; + } + } + } + + return tag; + } +}; + +}); // module: parser.js + +require.register("runtime.js", function(module, exports, require){ + +/*! + * Jade - runtime + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Lame Array.isArray() polyfill for now. + */ + +if (!Array.isArray) { + Array.isArray = function(arr){ + return '[object Array]' == Object.prototype.toString.call(arr); + }; +} + +/** + * Lame Object.keys() polyfill for now. + */ + +if (!Object.keys) { + Object.keys = function(obj){ + var arr = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + arr.push(key); + } + } + return arr; + } +} + +/** + * Merge two attribute objects giving precedence + * to values in object `b`. Classes are special-cased + * allowing for arrays and merging/joining appropriately + * resulting in a string. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api private + */ + +exports.merge = function merge(a, b) { + var ac = a['class']; + var bc = b['class']; + + if (ac || bc) { + ac = ac || []; + bc = bc || []; + if (!Array.isArray(ac)) ac = [ac]; + if (!Array.isArray(bc)) bc = [bc]; + ac = ac.filter(nulls); + bc = bc.filter(nulls); + a['class'] = ac.concat(bc).join(' '); + } + + for (var key in b) { + if (key != 'class') { + a[key] = b[key]; + } + } + + return a; +}; + +/** + * Filter null `val`s. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function nulls(val) { + return val != null; +} + +/** + * Render the given attributes object. + * + * @param {Object} obj + * @param {Object} escaped + * @return {String} + * @api private + */ + +exports.attrs = function attrs(obj, escaped){ + var buf = [] + , terse = obj.terse; + + delete obj.terse; + var keys = Object.keys(obj) + , len = keys.length; + + if (len) { + buf.push(''); + for (var i = 0; i < len; ++i) { + var key = keys[i] + , val = obj[key]; + + if ('boolean' == typeof val || null == val) { + if (val) { + terse + ? buf.push(key) + : buf.push(key + '="' + key + '"'); + } + } else if (0 == key.indexOf('data') && 'string' != typeof val) { + buf.push(key + "='" + JSON.stringify(val) + "'"); + } else if ('class' == key && Array.isArray(val)) { + buf.push(key + '="' + exports.escape(val.join(' ')) + '"'); + } else if (escaped && escaped[key]) { + buf.push(key + '="' + exports.escape(val) + '"'); + } else { + buf.push(key + '="' + val + '"'); + } + } + } + + return buf.join(' '); +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function escape(html){ + return String(html) + .replace(/&(?!(\w+|\#\d+);)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; + +/** + * Re-throw the given `err` in context to the + * the jade in `filename` at the given `lineno`. + * + * @param {Error} err + * @param {String} filename + * @param {String} lineno + * @api private + */ + +exports.rethrow = function rethrow(err, filename, lineno){ + if (!filename) throw err; + + var context = 3 + , str = require('fs').readFileSync(filename, 'utf8') + , lines = str.split('\n') + , start = Math.max(lineno - context, 0) + , end = Math.min(lines.length, lineno + context); + + // Error context + var context = lines.slice(start, end).map(function(line, i){ + var curr = i + start + 1; + return (curr == lineno ? ' > ' : ' ') + + curr + + '| ' + + line; + }).join('\n'); + + // Alter exception message + err.path = filename; + err.message = (filename || 'Jade') + ':' + lineno + + '\n' + context + '\n\n' + err.message; + throw err; +}; + +}); // module: runtime.js + +require.register("self-closing.js", function(module, exports, require){ + +/*! + * Jade - self closing tags + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = [ + 'meta' + , 'img' + , 'link' + , 'input' + , 'source' + , 'area' + , 'base' + , 'col' + , 'br' + , 'hr' +]; +}); // module: self-closing.js + +require.register("utils.js", function(module, exports, require){ + +/*! + * Jade - utils + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Convert interpolation in the given string to JavaScript. + * + * @param {String} str + * @return {String} + * @api private + */ + +var interpolate = exports.interpolate = function(str){ + return str.replace(/(\\)?([#!]){(.*?)}/g, function(str, escape, flag, code){ + return escape + ? str + : "' + " + + ('!' == flag ? '' : 'escape') + + "((interp = " + code.replace(/\\'/g, "'") + + ") == null ? '' : interp) + '"; + }); +}; + +/** + * Escape single quotes in `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +var escape = exports.escape = function(str) { + return str.replace(/'/g, "\\'"); +}; + +/** + * Interpolate, and escape the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.text = function(str){ + return interpolate(escape(str)); +}; +}); // module: utils.js + +window.jade = require("jade"); +})(); diff --git a/node_modules/mocha/node_modules/jade/jade.md b/node_modules/mocha/node_modules/jade/jade.md new file mode 100644 index 0000000..051dc03 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/jade.md @@ -0,0 +1,510 @@ + +# Jade + + The jade template engine for node.js + +## Synopsis + + jade [-h|--help] [-v|--version] [-o|--obj STR] + [-O|--out DIR] [-p|--path PATH] [-P|--pretty] + [-c|--client] [-D|--no-debug] + +## Examples + + translate jade the templates dir + + $ jade templates + + create {foo,bar}.html + + $ jade {foo,bar}.jade + + jade over stdio + + $ jade < my.jade > my.html + + jade over s + + $ echo "h1 Jade!" | jade + + foo, bar dirs rendering to /tmp + + $ jade foo bar --out /tmp + + compile client-side templates without debugging + instrumentation, making the output javascript + very light-weight. This requires runtime.js + in your projects. + + $ jade --client --no-debug < my.jade + +## Tags + + Tags are simply nested via whitespace, closing + tags defined for you. These indents are called "blocks". + + ul + li + a Foo + li + a Bar + + You may have several tags in one "block": + + ul + li + a Foo + a Bar + a Baz + +## Self-closing Tags + + Some tags are flagged as self-closing by default, such + as `meta`, `link`, and so on. To explicitly self-close + a tag simply append the `/` character: + + foo/ + foo(bar='baz')/ + + Would yield: + + + + +## Attributes + + Tag attributes look similar to HTML, however + the values are regular JavaScript, here are + some examples: + + a(href='google.com') Google + a(class='button', href='google.com') Google + + As mentioned the attribute values are just JavaScript, + this means ternary operations and other JavaScript expressions + work just fine: + + body(class=user.authenticated ? 'authenticated' : 'anonymous') + a(href=user.website || 'http://google.com') + + Multiple lines work too: + + input(type='checkbox', + name='agreement', + checked) + + Multiple lines without the comma work fine: + + input(type='checkbox' + name='agreement' + checked) + + Funky whitespace? fine: + + input( + type='checkbox' + name='agreement' + checked) + +## Boolean attributes + + Boolean attributes are mirrored by Jade, and accept + bools, aka _true_ or _false_. When no value is specified + _true_ is assumed. For example: + + input(type="checkbox", checked) + // => "" + + For example if the checkbox was for an agreement, perhaps `user.agreed` + was _true_ the following would also output 'checked="checked"': + + input(type="checkbox", checked=user.agreed) + +## Class attributes + + The _class_ attribute accepts an array of classes, + this can be handy when generated from a javascript + function etc: + + classes = ['foo', 'bar', 'baz'] + a(class=classes) + // => "" + +## Class literal + + Classes may be defined using a ".CLASSNAME" syntax: + + .button + // => "
        " + + Or chained: + + .large.button + // => "
        " + + The previous defaulted to divs, however you + may also specify the tag type: + + h1.title My Title + // => "

        My Title

        " + +## Id literal + + Much like the class literal there's an id literal: + + #user-1 + // => "
        " + + Again we may specify the tag as well: + + ul#menu + li: a(href='/home') Home + li: a(href='/store') Store + li: a(href='/contact') Contact + + Finally all of these may be used in any combination, + the following are all valid tags: + + a.button#contact(style: 'color: red') Contact + a.button(style: 'color: red')#contact Contact + a(style: 'color: red').button#contact Contact + +## Block expansion + + Jade supports the concept of "block expansion", in which + using a trailing ":" after a tag will inject a block: + + ul + li: a Foo + li: a Bar + li: a Baz + +## Text + + Arbitrary text may follow tags: + + p Welcome to my site + + yields: + +

        Welcome to my site

        + +## Pipe text + + Another form of text is "pipe" text. Pipes act + as the text margin for large bodies of text. + + p + | This is a large + | body of text for + | this tag. + | + | Nothing too + | exciting. + + yields: + +

        This is a large + body of text for + this tag. + + Nothing too + exciting. +

        + + Using pipes we can also specify regular Jade tags + within the text: + + p + | Click to visit + a(href='http://google.com') Google + | if you want. + +## Text only tags + + As an alternative to pipe text you may add + a trailing "." to indicate that the block + contains nothing but plain-text, no tags: + + p. + This is a large + body of text for + this tag. + + Nothing too + exciting. + + Some tags are text-only by default, for example + _script_, _textarea_, and _style_ tags do not + contain nested HTML so Jade implies the trailing ".": + + script + if (foo) { + bar(); + } + + style + body { + padding: 50px; + font: 14px Helvetica; + } + +## Template script tags + + Sometimes it's useful to define HTML in script + tags using Jade, typically for client-side templates. + + To do this simply give the _script_ tag an arbitrary + _type_ attribute such as _text/x-template_: + + script(type='text/template') + h1 Look! + p Jade still works in here! + +## Interpolation + + Both plain-text and piped-text support interpolation, + which comes in two forms, escapes and non-escaped. The + following will output the _user.name_ in the paragraph + but HTML within it will be escaped to prevent XSS attacks: + + p Welcome #{user.name} + + The following syntax is identical however it will _not_ escape + HTML, and should only be used with strings that you trust: + + p Welcome !{user.name} + +## Inline HTML + + Sometimes constructing small inline snippets of HTML + in Jade can be annoying, luckily we can add plain + HTML as well: + + p Welcome #{user.name} + +## Code + + To buffer output with Jade simply use _=_ at the beginning + of a line or after a tag. This method escapes any HTML + present in the string. + + p= user.description + + To buffer output unescaped use the _!=_ variant, but again + be careful of XSS. + + p!= user.description + + The final way to mess with JavaScript code in Jade is the unbuffered + _-_, which can be used for conditionals, defining variables etc: + + - var user = { description: 'foo bar baz' } + #user + - if (user.description) { + h2 Description + p.description= user.description + - } + + When compiled blocks are wrapped in anonymous functions, so the + following is also valid, without braces: + + - var user = { description: 'foo bar baz' } + #user + - if (user.description) + h2 Description + p.description= user.description + + If you really want you could even use `.forEach()` and others: + + - users.forEach(function(user){ + .user + h2= user.name + p User #{user.name} is #{user.age} years old + - }) + + Taking this further Jade provides some syntax for conditionals, + iteration, switch statements etc. Let's look at those next! + +## Assignment + + Jade's first-class assignment is simple, simply use the _=_ + operator and Jade will _var_ it for you. The following are equivalent: + + - var user = { name: 'tobi' } + user = { name: 'tobi' } + +## Conditionals + + Jade's first-class conditional syntax allows for optional + parenthesis, and you may now omit the leading _-_ otherwise + it's identical, still just regular javascript: + + user = { description: 'foo bar baz' } + #user + if user.description + h2 Description + p.description= user.description + + Jade provides the negated version, _unless_ as well, the following + are equivalent: + + - if (!(user.isAnonymous)) + p You're logged in as #{user.name} + + unless user.isAnonymous + p You're logged in as #{user.name} + +## Iteration + + JavaScript's _for_ loops don't look very declarative, so Jade + also provides its own _for_ loop construct, aliased as _each_: + + for user in users + .user + h2= user.name + p user #{user.name} is #{user.age} year old + + As mentioned _each_ is identical: + + each user in users + .user + h2= user.name + + If necessary the index is available as well: + + for user, i in users + .user(class='user-#{i}') + h2= user.name + + Remember, it's just JavaScript: + + ul#letters + for letter in ['a', 'b', 'c'] + li= letter + +## Mixins + + Mixins provide a way to define jade "functions" which "mix in" + their contents when called. This is useful for abstracting + out large fragments of Jade. + + The simplest possible mixin which accepts no arguments might + look like this: + + mixin hello + p Hello + + You use a mixin by placing `+` before the name: + + +hello + + For something a little more dynamic, mixins can take + arguments, the mixin itself is converted to a javascript + function internally: + + mixin hello(user) + p Hello #{user} + + +hello('Tobi') + + Yields: + +

        Hello Tobi

        + + Mixins may optionally take blocks, when a block is passed + its contents becomes the implicit `block` argument. For + example here is a mixin passed a block, and also invoked + without passing a block: + + mixin article(title) + .article + .article-wrapper + h1= title + if block + block + else + p No content provided + + +article('Hello world') + + +article('Hello world') + p This is my + p Amazing article + + yields: + +
        +
        +

        Hello world

        +

        No content provided

        +
        +
        + +
        +
        +

        Hello world

        +

        This is my

        +

        Amazing article

        +
        +
        + + Mixins can even take attributes, just like a tag. When + attributes are passed they become the implicit `attributes` + argument. Individual attributes can be accessed just like + normal object properties: + + mixin centered + .centered(class=attributes.class) + block + + +centered.bold Hello world + + +centered.red + p This is my + p Amazing article + + yields: + +
        Hello world
        +
        +

        This is my

        +

        Amazing article

        +
        + + If you use `attributes` directly, *all* passed attributes + get used: + + mixin link + a.menu(attributes) + block + + +link.highlight(href='#top') Top + +link#sec1.plain(href='#section1') Section 1 + +link#sec2.plain(href='#section2') Section 2 + + yields: + + Top + Section 1 + Section 2 + + If you pass arguments, they must directly follow the mixin: + + mixin list(arr) + if block + .title + block + ul(attributes) + each item in arr + li= item + + +list(['foo', 'bar', 'baz'])(id='myList', class='bold') + + yields: + +
          +
        • foo
        • +
        • bar
        • +
        • baz
        • +
        diff --git a/node_modules/mocha/node_modules/jade/jade.min.js b/node_modules/mocha/node_modules/jade/jade.min.js new file mode 100644 index 0000000..72e4535 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/jade.min.js @@ -0,0 +1,2 @@ +(function(){function require(p){var path=require.resolve(p),mod=require.modules[path];if(!mod)throw new Error('failed to require "'+p+'"');return mod.exports||(mod.exports={},mod.call(mod.exports,mod,mod.exports,require.relative(path))),mod.exports}require.modules={},require.resolve=function(path){var orig=path,reg=path+".js",index=path+"/index.js";return require.modules[reg]&®||require.modules[index]&&index||orig},require.register=function(path,fn){require.modules[path]=fn},require.relative=function(parent){return function(p){if("."!=p.charAt(0))return require(p);var path=parent.split("/"),segs=p.split("/");path.pop();for(var i=0;i",this.doctype=doctype,this.terse="5"==name||"html"==name,this.xml=0==this.doctype.indexOf("1&&!escape&&block.nodes[0].isText&&block.nodes[1].isText&&this.prettyIndent(1,!0);for(var i=0;i0&&!escape&&block.nodes[i].isText&&block.nodes[i-1].isText&&this.prettyIndent(1,!1),this.visit(block.nodes[i]),block.nodes[i+1]&&block.nodes[i].isText&&block.nodes[i+1].isText&&this.buffer("\\n")},visitDoctype:function(doctype){doctype&&(doctype.val||!this.doctype)&&this.setDoctype(doctype.val||"default"),this.doctype&&this.buffer(this.doctype),this.hasCompiledDoctype=!0},visitMixin:function(mixin){var name=mixin.name.replace(/-/g,"_")+"_mixin",args=mixin.args||"",block=mixin.block,attrs=mixin.attrs,pp=this.pp;if(mixin.call){pp&&this.buf.push("__indent.push('"+Array(this.indents+1).join(" ")+"');");if(block||attrs.length){this.buf.push(name+".call({");if(block){this.buf.push("block: function(){"),this.parentIndents++;var _indents=this.indents;this.indents=0,this.visit(mixin.block),this.indents=_indents,this.parentIndents--,attrs.length?this.buf.push("},"):this.buf.push("}")}if(attrs.length){var val=this.attrs(attrs);val.inherits?this.buf.push("attributes: merge({"+val.buf+"}, attributes), escaped: merge("+val.escaped+", escaped, true)"):this.buf.push("attributes: {"+val.buf+"}, escaped: "+val.escaped)}args?this.buf.push("}, "+args+");"):this.buf.push("});")}else this.buf.push(name+"("+args+");");pp&&this.buf.push("__indent.pop();")}else this.buf.push("var "+name+" = function("+args+"){"),this.buf.push("var block = this.block, attributes = this.attributes || {}, escaped = this.escaped || {};"),this.parentIndents++,this.visit(block),this.parentIndents--,this.buf.push("};")},visitTag:function(tag){this.indents++;var name=tag.name,pp=this.pp;tag.buffer&&(name="' + ("+name+") + '"),this.hasCompiledTag||(!this.hasCompiledDoctype&&"html"==name&&this.visitDoctype(),this.hasCompiledTag=!0),pp&&!tag.isInline()&&this.prettyIndent(0,!0),(~selfClosing.indexOf(name)||tag.selfClosing)&&!this.xml?(this.buffer("<"+name),this.visitAttributes(tag.attrs),this.terse?this.buffer(">"):this.buffer("/>")):(tag.attrs.length?(this.buffer("<"+name),tag.attrs.length&&this.visitAttributes(tag.attrs),this.buffer(">")):this.buffer("<"+name+">"),tag.code&&this.visitCode(tag.code),this.escape="pre"==tag.name,this.visit(tag.block),pp&&!tag.isInline()&&"pre"!=tag.name&&!tag.canInline()&&this.prettyIndent(0,!0),this.buffer("")),this.indents--},visitFilter:function(filter){var fn=filters[filter.name];if(!fn)throw filter.isASTFilter?new Error('unknown ast filter "'+filter.name+':"'):new Error('unknown filter ":'+filter.name+'"');if(filter.isASTFilter)this.buf.push(fn(filter.block,this,filter.attrs));else{var text=filter.block.nodes.map(function(node){return node.val}).join("\n");filter.attrs=filter.attrs||{},filter.attrs.filename=this.options.filename,this.buffer(utils.text(fn(text,filter.attrs)))}},visitText:function(text){text=utils.text(text.val.replace(/\\/g,"\\\\")),this.escape&&(text=escape(text)),this.buffer(text)},visitComment:function(comment){if(!comment.buffer)return;this.pp&&this.prettyIndent(1,!0),this.buffer("")},visitBlockComment:function(comment){if(!comment.buffer)return;0==comment.val.trim().indexOf("if")?(this.buffer("")):(this.buffer(""))},visitCode:function(code){if(code.buffer){var val=code.val.trimLeft();this.buf.push("var __val__ = "+val),val='null == __val__ ? "" : __val__',code.escape&&(val="escape("+val+")"),this.buf.push("buf.push("+val+");")}else this.buf.push(code.val);code.block&&(code.buffer||this.buf.push("{"),this.visit(code.block),code.buffer||this.buf.push("}"))},visitEach:function(each){this.buf.push("// iterate "+each.obj+"\n"+";(function(){\n"+" if ('number' == typeof "+each.obj+".length) {\n"+" for (var "+each.key+" = 0, $$l = "+each.obj+".length; "+each.key+" < $$l; "+each.key+"++) {\n"+" var "+each.val+" = "+each.obj+"["+each.key+"];\n"),this.visit(each.block),this.buf.push(" }\n } else {\n for (var "+each.key+" in "+each.obj+") {\n"+" if ("+each.obj+".hasOwnProperty("+each.key+")){"+" var "+each.val+" = "+each.obj+"["+each.key+"];\n"),this.visit(each.block),this.buf.push(" }\n"),this.buf.push(" }\n }\n}).call(this);\n")},visitAttributes:function(attrs){var val=this.attrs(attrs);val.inherits?this.buf.push("buf.push(attrs(merge({ "+val.buf+" }, attributes), merge("+val.escaped+", escaped, true)));"):val.constant?(eval("var buf={"+val.buf+"};"),this.buffer(runtime.attrs(buf,JSON.parse(val.escaped)),!0)):this.buf.push("buf.push(attrs({ "+val.buf+" }, "+val.escaped+"));")},attrs:function(attrs){var buf=[],classes=[],escaped={},constant=attrs.every(function(attr){return isConstant(attr.val)}),inherits=!1;return this.terse&&buf.push("terse: true"),attrs.forEach(function(attr){if(attr.name=="attributes")return inherits=!0;escaped[attr.name]=attr.escaped;if(attr.name=="class")classes.push("("+attr.val+")");else{var pair="'"+attr.name+"':("+attr.val+")";buf.push(pair)}}),classes.length&&(classes=classes.join(" + ' ' + "),buf.push("class: "+classes)),{buf:buf.join(", ").replace("class:",'"class":'),escaped:JSON.stringify(escaped),inherits:inherits,constant:constant}}};function isConstant(val){if(/^ *("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|true|false|null|undefined) *$/i.test(val))return!0;if(!isNaN(Number(val)))return!0;var matches;return(matches=/^ *\[(.*)\] *$/.exec(val))?matches[1].split(",").every(isConstant):!1}function escape(html){return String(html).replace(/&(?!\w+;)/g,"&").replace(//g,">").replace(/"/g,""")}}),require.register("doctypes.js",function(module,exports,require){module.exports={5:"","default":"",xml:'',transitional:'',strict:'',frameset:'',1.1:'',basic:'',mobile:''}}),require.register("filters.js",function(module,exports,require){module.exports={cdata:function(str){return""},sass:function(str){str=str.replace(/\\n/g,"\n");var sass=require("sass").render(str).replace(/\n/g,"\\n");return'"},stylus:function(str,options){var ret;str=str.replace(/\\n/g,"\n");var stylus=require("stylus");return stylus(str,options).render(function(err,css){if(err)throw err;ret=css.replace(/\n/g,"\\n")}),'"},less:function(str){var ret;return str=str.replace(/\\n/g,"\n"),require("less").render(str,function(err,css){if(err)throw err;ret='"}),ret},markdown:function(str){var md;try{md=require("markdown")}catch(err){try{md=require("discount")}catch(err){try{md=require("markdown-js")}catch(err){try{md=require("marked")}catch(err){throw new Error("Cannot find markdown library, install markdown, discount, or marked.")}}}}return str=str.replace(/\\n/g,"\n"),md.parse(str).replace(/\n/g,"\\n").replace(/'/g,"'")},coffeescript:function(str){str=str.replace(/\\n/g,"\n");var js=require("coffee-script").compile(str).replace(/\\/g,"\\\\").replace(/\n/g,"\\n");return'"}}}),require.register("inline-tags.js",function(module,exports,require){module.exports=["a","abbr","acronym","b","br","code","em","font","i","img","ins","kbd","map","samp","small","span","strong","sub","sup"]}),require.register("jade.js",function(module,exports,require){var Parser=require("./parser"),Lexer=require("./lexer"),Compiler=require("./compiler"),runtime=require("./runtime");exports.version="0.26.1",exports.selfClosing=require("./self-closing"),exports.doctypes=require("./doctypes"),exports.filters=require("./filters"),exports.utils=require("./utils"),exports.Compiler=Compiler,exports.Parser=Parser,exports.Lexer=Lexer,exports.nodes=require("./nodes"),exports.runtime=runtime,exports.cache={};function parse(str,options){try{var parser=new Parser(str,options.filename,options),compiler=new(options.compiler||Compiler)(parser.parse(),options),js=compiler.compile();return options.debug&&console.error("\nCompiled Function:\n\n%s",js.replace(/^/gm," ")),"var buf = [];\n"+(options.self?"var self = locals || {};\n"+js:"with (locals || {}) {\n"+js+"\n}\n")+'return buf.join("");'}catch(err){parser=parser.context(),runtime.rethrow(err,parser.filename,parser.lexer.lineno)}}exports.compile=function(str,options){var options=options||{},client=options.client,filename=options.filename?JSON.stringify(options.filename):"undefined",fn;return options.compileDebug!==!1?fn=["var __jade = [{ lineno: 1, filename: "+filename+" }];","try {",parse(String(str),options),"} catch (err) {"," rethrow(err, __jade[0].filename, __jade[0].lineno);","}"].join("\n"):fn=parse(String(str),options),client&&(fn="attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;\n"+fn),fn=new Function("locals, attrs, escape, rethrow, merge",fn),client?fn:function(locals){return fn(locals,runtime.attrs,runtime.escape,runtime.rethrow,runtime.merge)}},exports.render=function(str,options,fn){"function"==typeof options&&(fn=options,options={});if(options.cache&&!options.filename)return fn(new Error('the "filename" option is required for caching'));try{var path=options.filename,tmpl=options.cache?exports.cache[path]||(exports.cache[path]=exports.compile(str,options)):exports.compile(str,options);fn(null,tmpl(options))}catch(err){fn(err)}},exports.renderFile=function(path,options,fn){var key=path+":string";"function"==typeof options&&(fn=options,options={});try{options.filename=path;var str=options.cache?exports.cache[key]||(exports.cache[key]=fs.readFileSync(path,"utf8")):fs.readFileSync(path,"utf8");exports.render(str,options,fn)}catch(err){fn(err)}},exports.__express=exports.renderFile}),require.register("lexer.js",function(module,exports,require){var Lexer=module.exports=function Lexer(str,options){options=options||{},this.input=str.replace(/\r\n|\r/g,"\n"),this.colons=options.colons,this.deferredTokens=[],this.lastIndents=0,this.lineno=1,this.stash=[],this.indentStack=[],this.indentRe=null,this.pipeless=!1};Lexer.prototype={tok:function(type,val){return{type:type,line:this.lineno,val:val}},consume:function(len){this.input=this.input.substr(len)},scan:function(regexp,type){var captures;if(captures=regexp.exec(this.input))return this.consume(captures[0].length),this.tok(type,captures[1])},defer:function(tok){this.deferredTokens.push(tok)},lookahead:function(n){var fetch=n-this.stash.length;while(fetch-->0)this.stash.push(this.next());return this.stash[--n]},indexOfDelimiters:function(start,end){var str=this.input,nstart=0,nend=0,pos=0;for(var i=0,len=str.length;iindents)this.stash.push(this.tok("outdent")),this.indentStack.shift();tok=this.stash.pop()}else indents&&indents!=this.indentStack[0]?(this.indentStack.unshift(indents),tok=this.tok("indent",indents)):tok=this.tok("newline");return tok}},pipelessText:function(){if(this.pipeless){if("\n"==this.input[0])return;var i=this.input.indexOf("\n");-1==i&&(i=this.input.length);var str=this.input.substr(0,i);return this.consume(str.length),this.tok("text",str)}},colon:function(){return this.scan(/^: */,":")},advance:function(){return this.stashed()||this.next()},next:function(){return this.deferred()||this.blank()||this.eos()||this.pipelessText()||this.yield()||this.doctype()||this.interpolation()||this["case"]()||this.when()||this["default"]()||this["extends"]()||this.append()||this.prepend()||this.block()||this.include()||this.mixin()||this.call()||this.conditional()||this.each()||this["while"]()||this.assignment()||this.tag()||this.filter()||this.code()||this.id()||this.className()||this.attrs()||this.indent()||this.comment()||this.colon()||this.text()}}}),require.register("nodes/attrs.js",function(module,exports,require){var Node=require("./node"),Block=require("./block"),Attrs=module.exports=function Attrs(){this.attrs=[]};Attrs.prototype=new Node,Attrs.prototype.constructor=Attrs,Attrs.prototype.setAttribute=function(name,val,escaped){return this.attrs.push({name:name,val:val,escaped:escaped}),this},Attrs.prototype.removeAttribute=function(name){for(var i=0,len=this.attrs.length;i/g,">").replace(/"/g,""")},exports.rethrow=function rethrow(err,filename,lineno){if(!filename)throw err;var context=3,str=require("fs").readFileSync(filename,"utf8"),lines=str.split("\n"),start=Math.max(lineno-context,0),end=Math.min(lines.length,lineno+context),context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" > ":" ")+curr+"| "+line}).join("\n");throw err.path=filename,err.message=(filename||"Jade")+":"+lineno+"\n"+context+"\n\n"+err.message,err}}),require.register("self-closing.js",function(module,exports,require){module.exports=["meta","img","link","input","source","area","base","col","br","hr"]}),require.register("utils.js",function(module,exports,require){var interpolate=exports.interpolate=function(str){return str.replace(/(\\)?([#!]){(.*?)}/g,function(str,escape,flag,code){return escape?str:"' + "+("!"==flag?"":"escape")+"((interp = "+code.replace(/\\'/g,"'")+") == null ? '' : interp) + '"})},escape=exports.escape=function(str){return str.replace(/'/g,"\\'")};exports.text=function(str){return interpolate(escape(str))}}),window.jade=require("jade")})(); \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/lib/compiler.js b/node_modules/mocha/node_modules/jade/lib/compiler.js new file mode 100644 index 0000000..516ac83 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/compiler.js @@ -0,0 +1,642 @@ + +/*! + * Jade - Compiler + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var nodes = require('./nodes') + , filters = require('./filters') + , doctypes = require('./doctypes') + , selfClosing = require('./self-closing') + , runtime = require('./runtime') + , utils = require('./utils'); + +// if browser +// +// if (!Object.keys) { +// Object.keys = function(obj){ +// var arr = []; +// for (var key in obj) { +// if (obj.hasOwnProperty(key)) { +// arr.push(key); +// } +// } +// return arr; +// } +// } +// +// if (!String.prototype.trimLeft) { +// String.prototype.trimLeft = function(){ +// return this.replace(/^\s+/, ''); +// } +// } +// +// end + + +/** + * Initialize `Compiler` with the given `node`. + * + * @param {Node} node + * @param {Object} options + * @api public + */ + +var Compiler = module.exports = function Compiler(node, options) { + this.options = options = options || {}; + this.node = node; + this.hasCompiledDoctype = false; + this.hasCompiledTag = false; + this.pp = options.pretty || false; + this.debug = false !== options.compileDebug; + this.indents = 0; + this.parentIndents = 0; + if (options.doctype) this.setDoctype(options.doctype); +}; + +/** + * Compiler prototype. + */ + +Compiler.prototype = { + + /** + * Compile parse tree to JavaScript. + * + * @api public + */ + + compile: function(){ + this.buf = ['var interp;']; + if (this.pp) this.buf.push("var __indent = [];"); + this.lastBufferedIdx = -1; + this.visit(this.node); + return this.buf.join('\n'); + }, + + /** + * Sets the default doctype `name`. Sets terse mode to `true` when + * html 5 is used, causing self-closing tags to end with ">" vs "/>", + * and boolean attributes are not mirrored. + * + * @param {string} name + * @api public + */ + + setDoctype: function(name){ + var doctype = doctypes[(name || 'default').toLowerCase()]; + doctype = doctype || ''; + this.doctype = doctype; + this.terse = '5' == name || 'html' == name; + this.xml = 0 == this.doctype.indexOf(' 1 && !escape && block.nodes[0].isText && block.nodes[1].isText) + this.prettyIndent(1, true); + + for (var i = 0; i < len; ++i) { + // Pretty print text + if (pp && i > 0 && !escape && block.nodes[i].isText && block.nodes[i-1].isText) + this.prettyIndent(1, false); + + this.visit(block.nodes[i]); + // Multiple text nodes are separated by newlines + if (block.nodes[i+1] && block.nodes[i].isText && block.nodes[i+1].isText) + this.buffer('\\n'); + } + }, + + /** + * Visit `doctype`. Sets terse mode to `true` when html 5 + * is used, causing self-closing tags to end with ">" vs "/>", + * and boolean attributes are not mirrored. + * + * @param {Doctype} doctype + * @api public + */ + + visitDoctype: function(doctype){ + if (doctype && (doctype.val || !this.doctype)) { + this.setDoctype(doctype.val || 'default'); + } + + if (this.doctype) this.buffer(this.doctype); + this.hasCompiledDoctype = true; + }, + + /** + * Visit `mixin`, generating a function that + * may be called within the template. + * + * @param {Mixin} mixin + * @api public + */ + + visitMixin: function(mixin){ + var name = mixin.name.replace(/-/g, '_') + '_mixin' + , args = mixin.args || '' + , block = mixin.block + , attrs = mixin.attrs + , pp = this.pp; + + if (mixin.call) { + if (pp) this.buf.push("__indent.push('" + Array(this.indents + 1).join(' ') + "');") + if (block || attrs.length) { + + this.buf.push(name + '.call({'); + + if (block) { + this.buf.push('block: function(){'); + + // Render block with no indents, dynamically added when rendered + this.parentIndents++; + var _indents = this.indents; + this.indents = 0; + this.visit(mixin.block); + this.indents = _indents; + this.parentIndents--; + + if (attrs.length) { + this.buf.push('},'); + } else { + this.buf.push('}'); + } + } + + if (attrs.length) { + var val = this.attrs(attrs); + if (val.inherits) { + this.buf.push('attributes: merge({' + val.buf + + '}, attributes), escaped: merge(' + val.escaped + ', escaped, true)'); + } else { + this.buf.push('attributes: {' + val.buf + '}, escaped: ' + val.escaped); + } + } + + if (args) { + this.buf.push('}, ' + args + ');'); + } else { + this.buf.push('});'); + } + + } else { + this.buf.push(name + '(' + args + ');'); + } + if (pp) this.buf.push("__indent.pop();") + } else { + this.buf.push('var ' + name + ' = function(' + args + '){'); + this.buf.push('var block = this.block, attributes = this.attributes || {}, escaped = this.escaped || {};'); + this.parentIndents++; + this.visit(block); + this.parentIndents--; + this.buf.push('};'); + } + }, + + /** + * Visit `tag` buffering tag markup, generating + * attributes, visiting the `tag`'s code and block. + * + * @param {Tag} tag + * @api public + */ + + visitTag: function(tag){ + this.indents++; + var name = tag.name + , pp = this.pp; + + if (tag.buffer) name = "' + (" + name + ") + '"; + + if (!this.hasCompiledTag) { + if (!this.hasCompiledDoctype && 'html' == name) { + this.visitDoctype(); + } + this.hasCompiledTag = true; + } + + // pretty print + if (pp && !tag.isInline()) + this.prettyIndent(0, true); + + if ((~selfClosing.indexOf(name) || tag.selfClosing) && !this.xml) { + this.buffer('<' + name); + this.visitAttributes(tag.attrs); + this.terse + ? this.buffer('>') + : this.buffer('/>'); + } else { + // Optimize attributes buffering + if (tag.attrs.length) { + this.buffer('<' + name); + if (tag.attrs.length) this.visitAttributes(tag.attrs); + this.buffer('>'); + } else { + this.buffer('<' + name + '>'); + } + if (tag.code) this.visitCode(tag.code); + this.escape = 'pre' == tag.name; + this.visit(tag.block); + + // pretty print + if (pp && !tag.isInline() && 'pre' != tag.name && !tag.canInline()) + this.prettyIndent(0, true); + + this.buffer(''); + } + this.indents--; + }, + + /** + * Visit `filter`, throwing when the filter does not exist. + * + * @param {Filter} filter + * @api public + */ + + visitFilter: function(filter){ + var fn = filters[filter.name]; + + // unknown filter + if (!fn) { + if (filter.isASTFilter) { + throw new Error('unknown ast filter "' + filter.name + ':"'); + } else { + throw new Error('unknown filter ":' + filter.name + '"'); + } + } + + if (filter.isASTFilter) { + this.buf.push(fn(filter.block, this, filter.attrs)); + } else { + var text = filter.block.nodes.map(function(node){ return node.val }).join('\n'); + filter.attrs = filter.attrs || {}; + filter.attrs.filename = this.options.filename; + this.buffer(utils.text(fn(text, filter.attrs))); + } + }, + + /** + * Visit `text` node. + * + * @param {Text} text + * @api public + */ + + visitText: function(text){ + text = utils.text(text.val.replace(/\\/g, '\\\\')); + if (this.escape) text = escape(text); + this.buffer(text); + }, + + /** + * Visit a `comment`, only buffering when the buffer flag is set. + * + * @param {Comment} comment + * @api public + */ + + visitComment: function(comment){ + if (!comment.buffer) return; + if (this.pp) this.prettyIndent(1, true); + this.buffer(''); + }, + + /** + * Visit a `BlockComment`. + * + * @param {Comment} comment + * @api public + */ + + visitBlockComment: function(comment){ + if (!comment.buffer) return; + if (0 == comment.val.trim().indexOf('if')) { + this.buffer(''); + } else { + this.buffer(''); + } + }, + + /** + * Visit `code`, respecting buffer / escape flags. + * If the code is followed by a block, wrap it in + * a self-calling function. + * + * @param {Code} code + * @api public + */ + + visitCode: function(code){ + // Wrap code blocks with {}. + // we only wrap unbuffered code blocks ATM + // since they are usually flow control + + // Buffer code + if (code.buffer) { + var val = code.val.trimLeft(); + this.buf.push('var __val__ = ' + val); + val = 'null == __val__ ? "" : __val__'; + if (code.escape) val = 'escape(' + val + ')'; + this.buf.push("buf.push(" + val + ");"); + } else { + this.buf.push(code.val); + } + + // Block support + if (code.block) { + if (!code.buffer) this.buf.push('{'); + this.visit(code.block); + if (!code.buffer) this.buf.push('}'); + } + }, + + /** + * Visit `each` block. + * + * @param {Each} each + * @api public + */ + + visitEach: function(each){ + this.buf.push('' + + '// iterate ' + each.obj + '\n' + + ';(function(){\n' + + ' if (\'number\' == typeof ' + each.obj + '.length) {\n' + + ' for (var ' + each.key + ' = 0, $$l = ' + each.obj + '.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n' + + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); + + this.visit(each.block); + + this.buf.push('' + + ' }\n' + + ' } else {\n' + + ' for (var ' + each.key + ' in ' + each.obj + ') {\n' + // if browser + // + ' if (' + each.obj + '.hasOwnProperty(' + each.key + ')){' + // end + + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); + + this.visit(each.block); + + // if browser + // this.buf.push(' }\n'); + // end + + this.buf.push(' }\n }\n}).call(this);\n'); + }, + + /** + * Visit `attrs`. + * + * @param {Array} attrs + * @api public + */ + + visitAttributes: function(attrs){ + var val = this.attrs(attrs); + if (val.inherits) { + this.buf.push("buf.push(attrs(merge({ " + val.buf + + " }, attributes), merge(" + val.escaped + ", escaped, true)));"); + } else if (val.constant) { + eval('var buf={' + val.buf + '};'); + this.buffer(runtime.attrs(buf, JSON.parse(val.escaped)), true); + } else { + this.buf.push("buf.push(attrs({ " + val.buf + " }, " + val.escaped + "));"); + } + }, + + /** + * Compile attributes. + */ + + attrs: function(attrs){ + var buf = [] + , classes = [] + , escaped = {} + , constant = attrs.every(function(attr){ return isConstant(attr.val) }) + , inherits = false; + + if (this.terse) buf.push('terse: true'); + + attrs.forEach(function(attr){ + if (attr.name == 'attributes') return inherits = true; + escaped[attr.name] = attr.escaped; + if (attr.name == 'class') { + classes.push('(' + attr.val + ')'); + } else { + var pair = "'" + attr.name + "':(" + attr.val + ')'; + buf.push(pair); + } + }); + + if (classes.length) { + classes = classes.join(" + ' ' + "); + buf.push("class: " + classes); + } + + return { + buf: buf.join(', ').replace('class:', '"class":'), + escaped: JSON.stringify(escaped), + inherits: inherits, + constant: constant + }; + } +}; + +/** + * Check if expression can be evaluated to a constant + * + * @param {String} expression + * @return {Boolean} + * @api private + */ + +function isConstant(val){ + // Check strings/literals + if (/^ *("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|true|false|null|undefined) *$/i.test(val)) + return true; + + // Check numbers + if (!isNaN(Number(val))) + return true; + + // Check arrays + var matches; + if (matches = /^ *\[(.*)\] *$/.exec(val)) + return matches[1].split(',').every(isConstant); + + return false; +} + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +function escape(html){ + return String(html) + .replace(/&(?!\w+;)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/lib/doctypes.js b/node_modules/mocha/node_modules/jade/lib/doctypes.js new file mode 100644 index 0000000..e87ca1e --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/doctypes.js @@ -0,0 +1,18 @@ + +/*! + * Jade - doctypes + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = { + '5': '' + , 'default': '' + , 'xml': '' + , 'transitional': '' + , 'strict': '' + , 'frameset': '' + , '1.1': '' + , 'basic': '' + , 'mobile': '' +}; \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/lib/filters.js b/node_modules/mocha/node_modules/jade/lib/filters.js new file mode 100644 index 0000000..fdb634c --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/filters.js @@ -0,0 +1,97 @@ + +/*! + * Jade - filters + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = { + + /** + * Wrap text with CDATA block. + */ + + cdata: function(str){ + return ''; + }, + + /** + * Transform sass to css, wrapped in style tags. + */ + + sass: function(str){ + str = str.replace(/\\n/g, '\n'); + var sass = require('sass').render(str).replace(/\n/g, '\\n'); + return ''; + }, + + /** + * Transform stylus to css, wrapped in style tags. + */ + + stylus: function(str, options){ + var ret; + str = str.replace(/\\n/g, '\n'); + var stylus = require('stylus'); + stylus(str, options).render(function(err, css){ + if (err) throw err; + ret = css.replace(/\n/g, '\\n'); + }); + return ''; + }, + + /** + * Transform less to css, wrapped in style tags. + */ + + less: function(str){ + var ret; + str = str.replace(/\\n/g, '\n'); + require('less').render(str, function(err, css){ + if (err) throw err; + ret = ''; + }); + return ret; + }, + + /** + * Transform markdown to html. + */ + + markdown: function(str){ + var md; + + // support markdown / discount + try { + md = require('markdown'); + } catch (err){ + try { + md = require('discount'); + } catch (err) { + try { + md = require('markdown-js'); + } catch (err) { + try { + md = require('marked'); + } catch (err) { + throw new + Error('Cannot find markdown library, install markdown, discount, or marked.'); + } + } + } + } + + str = str.replace(/\\n/g, '\n'); + return md.parse(str).replace(/\n/g, '\\n').replace(/'/g,'''); + }, + + /** + * Transform coffeescript to javascript. + */ + + coffeescript: function(str){ + str = str.replace(/\\n/g, '\n'); + var js = require('coffee-script').compile(str).replace(/\\/g, '\\\\').replace(/\n/g, '\\n'); + return ''; + } +}; diff --git a/node_modules/mocha/node_modules/jade/lib/inline-tags.js b/node_modules/mocha/node_modules/jade/lib/inline-tags.js new file mode 100644 index 0000000..491de0b --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/inline-tags.js @@ -0,0 +1,28 @@ + +/*! + * Jade - inline tags + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = [ + 'a' + , 'abbr' + , 'acronym' + , 'b' + , 'br' + , 'code' + , 'em' + , 'font' + , 'i' + , 'img' + , 'ins' + , 'kbd' + , 'map' + , 'samp' + , 'small' + , 'span' + , 'strong' + , 'sub' + , 'sup' +]; \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/lib/jade.js b/node_modules/mocha/node_modules/jade/lib/jade.js new file mode 100644 index 0000000..00f0abb --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/jade.js @@ -0,0 +1,237 @@ +/*! + * Jade + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Parser = require('./parser') + , Lexer = require('./lexer') + , Compiler = require('./compiler') + , runtime = require('./runtime') +// if node + , fs = require('fs'); +// end + +/** + * Library version. + */ + +exports.version = '0.26.3'; + +/** + * Expose self closing tags. + */ + +exports.selfClosing = require('./self-closing'); + +/** + * Default supported doctypes. + */ + +exports.doctypes = require('./doctypes'); + +/** + * Text filters. + */ + +exports.filters = require('./filters'); + +/** + * Utilities. + */ + +exports.utils = require('./utils'); + +/** + * Expose `Compiler`. + */ + +exports.Compiler = Compiler; + +/** + * Expose `Parser`. + */ + +exports.Parser = Parser; + +/** + * Expose `Lexer`. + */ + +exports.Lexer = Lexer; + +/** + * Nodes. + */ + +exports.nodes = require('./nodes'); + +/** + * Jade runtime helpers. + */ + +exports.runtime = runtime; + +/** + * Template function cache. + */ + +exports.cache = {}; + +/** + * Parse the given `str` of jade and return a function body. + * + * @param {String} str + * @param {Object} options + * @return {String} + * @api private + */ + +function parse(str, options){ + try { + // Parse + var parser = new Parser(str, options.filename, options); + + // Compile + var compiler = new (options.compiler || Compiler)(parser.parse(), options) + , js = compiler.compile(); + + // Debug compiler + if (options.debug) { + console.error('\nCompiled Function:\n\n\033[90m%s\033[0m', js.replace(/^/gm, ' ')); + } + + return '' + + 'var buf = [];\n' + + (options.self + ? 'var self = locals || {};\n' + js + : 'with (locals || {}) {\n' + js + '\n}\n') + + 'return buf.join("");'; + } catch (err) { + parser = parser.context(); + runtime.rethrow(err, parser.filename, parser.lexer.lineno); + } +} + +/** + * Compile a `Function` representation of the given jade `str`. + * + * Options: + * + * - `compileDebug` when `false` debugging code is stripped from the compiled template + * - `client` when `true` the helper functions `escape()` etc will reference `jade.escape()` + * for use with the Jade client-side runtime.js + * + * @param {String} str + * @param {Options} options + * @return {Function} + * @api public + */ + +exports.compile = function(str, options){ + var options = options || {} + , client = options.client + , filename = options.filename + ? JSON.stringify(options.filename) + : 'undefined' + , fn; + + if (options.compileDebug !== false) { + fn = [ + 'var __jade = [{ lineno: 1, filename: ' + filename + ' }];' + , 'try {' + , parse(String(str), options) + , '} catch (err) {' + , ' rethrow(err, __jade[0].filename, __jade[0].lineno);' + , '}' + ].join('\n'); + } else { + fn = parse(String(str), options); + } + + if (client) { + fn = 'attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;\n' + fn; + } + + fn = new Function('locals, attrs, escape, rethrow, merge', fn); + + if (client) return fn; + + return function(locals){ + return fn(locals, runtime.attrs, runtime.escape, runtime.rethrow, runtime.merge); + }; +}; + +/** + * Render the given `str` of jade and invoke + * the callback `fn(err, str)`. + * + * Options: + * + * - `cache` enable template caching + * - `filename` filename required for `include` / `extends` and caching + * + * @param {String} str + * @param {Object|Function} options or fn + * @param {Function} fn + * @api public + */ + +exports.render = function(str, options, fn){ + // swap args + if ('function' == typeof options) { + fn = options, options = {}; + } + + // cache requires .filename + if (options.cache && !options.filename) { + return fn(new Error('the "filename" option is required for caching')); + } + + try { + var path = options.filename; + var tmpl = options.cache + ? exports.cache[path] || (exports.cache[path] = exports.compile(str, options)) + : exports.compile(str, options); + fn(null, tmpl(options)); + } catch (err) { + fn(err); + } +}; + +/** + * Render a Jade file at the given `path` and callback `fn(err, str)`. + * + * @param {String} path + * @param {Object|Function} options or callback + * @param {Function} fn + * @api public + */ + +exports.renderFile = function(path, options, fn){ + var key = path + ':string'; + + if ('function' == typeof options) { + fn = options, options = {}; + } + + try { + options.filename = path; + var str = options.cache + ? exports.cache[key] || (exports.cache[key] = fs.readFileSync(path, 'utf8')) + : fs.readFileSync(path, 'utf8'); + exports.render(str, options, fn); + } catch (err) { + fn(err); + } +}; + +/** + * Express support. + */ + +exports.__express = exports.renderFile; diff --git a/node_modules/mocha/node_modules/jade/lib/lexer.js b/node_modules/mocha/node_modules/jade/lib/lexer.js new file mode 100644 index 0000000..bca314a --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/lexer.js @@ -0,0 +1,771 @@ + +/*! + * Jade - Lexer + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Initialize `Lexer` with the given `str`. + * + * Options: + * + * - `colons` allow colons for attr delimiters + * + * @param {String} str + * @param {Object} options + * @api private + */ + +var Lexer = module.exports = function Lexer(str, options) { + options = options || {}; + this.input = str.replace(/\r\n|\r/g, '\n'); + this.colons = options.colons; + this.deferredTokens = []; + this.lastIndents = 0; + this.lineno = 1; + this.stash = []; + this.indentStack = []; + this.indentRe = null; + this.pipeless = false; +}; + +/** + * Lexer prototype. + */ + +Lexer.prototype = { + + /** + * Construct a token with the given `type` and `val`. + * + * @param {String} type + * @param {String} val + * @return {Object} + * @api private + */ + + tok: function(type, val){ + return { + type: type + , line: this.lineno + , val: val + } + }, + + /** + * Consume the given `len` of input. + * + * @param {Number} len + * @api private + */ + + consume: function(len){ + this.input = this.input.substr(len); + }, + + /** + * Scan for `type` with the given `regexp`. + * + * @param {String} type + * @param {RegExp} regexp + * @return {Object} + * @api private + */ + + scan: function(regexp, type){ + var captures; + if (captures = regexp.exec(this.input)) { + this.consume(captures[0].length); + return this.tok(type, captures[1]); + } + }, + + /** + * Defer the given `tok`. + * + * @param {Object} tok + * @api private + */ + + defer: function(tok){ + this.deferredTokens.push(tok); + }, + + /** + * Lookahead `n` tokens. + * + * @param {Number} n + * @return {Object} + * @api private + */ + + lookahead: function(n){ + var fetch = n - this.stash.length; + while (fetch-- > 0) this.stash.push(this.next()); + return this.stash[--n]; + }, + + /** + * Return the indexOf `start` / `end` delimiters. + * + * @param {String} start + * @param {String} end + * @return {Number} + * @api private + */ + + indexOfDelimiters: function(start, end){ + var str = this.input + , nstart = 0 + , nend = 0 + , pos = 0; + for (var i = 0, len = str.length; i < len; ++i) { + if (start == str.charAt(i)) { + ++nstart; + } else if (end == str.charAt(i)) { + if (++nend == nstart) { + pos = i; + break; + } + } + } + return pos; + }, + + /** + * Stashed token. + */ + + stashed: function() { + return this.stash.length + && this.stash.shift(); + }, + + /** + * Deferred token. + */ + + deferred: function() { + return this.deferredTokens.length + && this.deferredTokens.shift(); + }, + + /** + * end-of-source. + */ + + eos: function() { + if (this.input.length) return; + if (this.indentStack.length) { + this.indentStack.shift(); + return this.tok('outdent'); + } else { + return this.tok('eos'); + } + }, + + /** + * Blank line. + */ + + blank: function() { + var captures; + if (captures = /^\n *\n/.exec(this.input)) { + this.consume(captures[0].length - 1); + if (this.pipeless) return this.tok('text', ''); + return this.next(); + } + }, + + /** + * Comment. + */ + + comment: function() { + var captures; + if (captures = /^ *\/\/(-)?([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('comment', captures[2]); + tok.buffer = '-' != captures[1]; + return tok; + } + }, + + /** + * Interpolated tag. + */ + + interpolation: function() { + var captures; + if (captures = /^#\{(.*?)\}/.exec(this.input)) { + this.consume(captures[0].length); + return this.tok('interpolation', captures[1]); + } + }, + + /** + * Tag. + */ + + tag: function() { + var captures; + if (captures = /^(\w[-:\w]*)(\/?)/.exec(this.input)) { + this.consume(captures[0].length); + var tok, name = captures[1]; + if (':' == name[name.length - 1]) { + name = name.slice(0, -1); + tok = this.tok('tag', name); + this.defer(this.tok(':')); + while (' ' == this.input[0]) this.input = this.input.substr(1); + } else { + tok = this.tok('tag', name); + } + tok.selfClosing = !! captures[2]; + return tok; + } + }, + + /** + * Filter. + */ + + filter: function() { + return this.scan(/^:(\w+)/, 'filter'); + }, + + /** + * Doctype. + */ + + doctype: function() { + return this.scan(/^(?:!!!|doctype) *([^\n]+)?/, 'doctype'); + }, + + /** + * Id. + */ + + id: function() { + return this.scan(/^#([\w-]+)/, 'id'); + }, + + /** + * Class. + */ + + className: function() { + return this.scan(/^\.([\w-]+)/, 'class'); + }, + + /** + * Text. + */ + + text: function() { + return this.scan(/^(?:\| ?| ?)?([^\n]+)/, 'text'); + }, + + /** + * Extends. + */ + + "extends": function() { + return this.scan(/^extends? +([^\n]+)/, 'extends'); + }, + + /** + * Block prepend. + */ + + prepend: function() { + var captures; + if (captures = /^prepend +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = 'prepend' + , name = captures[1] + , tok = this.tok('block', name); + tok.mode = mode; + return tok; + } + }, + + /** + * Block append. + */ + + append: function() { + var captures; + if (captures = /^append +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = 'append' + , name = captures[1] + , tok = this.tok('block', name); + tok.mode = mode; + return tok; + } + }, + + /** + * Block. + */ + + block: function() { + var captures; + if (captures = /^block\b *(?:(prepend|append) +)?([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = captures[1] || 'replace' + , name = captures[2] + , tok = this.tok('block', name); + + tok.mode = mode; + return tok; + } + }, + + /** + * Yield. + */ + + yield: function() { + return this.scan(/^yield */, 'yield'); + }, + + /** + * Include. + */ + + include: function() { + return this.scan(/^include +([^\n]+)/, 'include'); + }, + + /** + * Case. + */ + + "case": function() { + return this.scan(/^case +([^\n]+)/, 'case'); + }, + + /** + * When. + */ + + when: function() { + return this.scan(/^when +([^:\n]+)/, 'when'); + }, + + /** + * Default. + */ + + "default": function() { + return this.scan(/^default */, 'default'); + }, + + /** + * Assignment. + */ + + assignment: function() { + var captures; + if (captures = /^(\w+) += *([^;\n]+)( *;? *)/.exec(this.input)) { + this.consume(captures[0].length); + var name = captures[1] + , val = captures[2]; + return this.tok('code', 'var ' + name + ' = (' + val + ');'); + } + }, + + /** + * Call mixin. + */ + + call: function(){ + var captures; + if (captures = /^\+([-\w]+)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('call', captures[1]); + + // Check for args (not attributes) + if (captures = /^ *\((.*?)\)/.exec(this.input)) { + if (!/^ *[-\w]+ *=/.test(captures[1])) { + this.consume(captures[0].length); + tok.args = captures[1]; + } + } + + return tok; + } + }, + + /** + * Mixin. + */ + + mixin: function(){ + var captures; + if (captures = /^mixin +([-\w]+)(?: *\((.*)\))?/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('mixin', captures[1]); + tok.args = captures[2]; + return tok; + } + }, + + /** + * Conditional. + */ + + conditional: function() { + var captures; + if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var type = captures[1] + , js = captures[2]; + + switch (type) { + case 'if': js = 'if (' + js + ')'; break; + case 'unless': js = 'if (!(' + js + '))'; break; + case 'else if': js = 'else if (' + js + ')'; break; + case 'else': js = 'else'; break; + } + + return this.tok('code', js); + } + }, + + /** + * While. + */ + + "while": function() { + var captures; + if (captures = /^while +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + return this.tok('code', 'while (' + captures[1] + ')'); + } + }, + + /** + * Each. + */ + + each: function() { + var captures; + if (captures = /^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('each', captures[1]); + tok.key = captures[2] || '$index'; + tok.code = captures[3]; + return tok; + } + }, + + /** + * Code. + */ + + code: function() { + var captures; + if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var flags = captures[1]; + captures[1] = captures[2]; + var tok = this.tok('code', captures[1]); + tok.escape = flags[0] === '='; + tok.buffer = flags[0] === '=' || flags[1] === '='; + return tok; + } + }, + + /** + * Attributes. + */ + + attrs: function() { + if ('(' == this.input.charAt(0)) { + var index = this.indexOfDelimiters('(', ')') + , str = this.input.substr(1, index-1) + , tok = this.tok('attrs') + , len = str.length + , colons = this.colons + , states = ['key'] + , escapedAttr + , key = '' + , val = '' + , quote + , c + , p; + + function state(){ + return states[states.length - 1]; + } + + function interpolate(attr) { + return attr.replace(/#\{([^}]+)\}/g, function(_, expr){ + return quote + " + (" + expr + ") + " + quote; + }); + } + + this.consume(index + 1); + tok.attrs = {}; + tok.escaped = {}; + + function parse(c) { + var real = c; + // TODO: remove when people fix ":" + if (colons && ':' == c) c = '='; + switch (c) { + case ',': + case '\n': + switch (state()) { + case 'expr': + case 'array': + case 'string': + case 'object': + val += c; + break; + default: + states.push('key'); + val = val.trim(); + key = key.trim(); + if ('' == key) return; + key = key.replace(/^['"]|['"]$/g, '').replace('!', ''); + tok.escaped[key] = escapedAttr; + tok.attrs[key] = '' == val + ? true + : interpolate(val); + key = val = ''; + } + break; + case '=': + switch (state()) { + case 'key char': + key += real; + break; + case 'val': + case 'expr': + case 'array': + case 'string': + case 'object': + val += real; + break; + default: + escapedAttr = '!' != p; + states.push('val'); + } + break; + case '(': + if ('val' == state() + || 'expr' == state()) states.push('expr'); + val += c; + break; + case ')': + if ('expr' == state() + || 'val' == state()) states.pop(); + val += c; + break; + case '{': + if ('val' == state()) states.push('object'); + val += c; + break; + case '}': + if ('object' == state()) states.pop(); + val += c; + break; + case '[': + if ('val' == state()) states.push('array'); + val += c; + break; + case ']': + if ('array' == state()) states.pop(); + val += c; + break; + case '"': + case "'": + switch (state()) { + case 'key': + states.push('key char'); + break; + case 'key char': + states.pop(); + break; + case 'string': + if (c == quote) states.pop(); + val += c; + break; + default: + states.push('string'); + val += c; + quote = c; + } + break; + case '': + break; + default: + switch (state()) { + case 'key': + case 'key char': + key += c; + break; + default: + val += c; + } + } + p = c; + } + + for (var i = 0; i < len; ++i) { + parse(str.charAt(i)); + } + + parse(','); + + if ('/' == this.input.charAt(0)) { + this.consume(1); + tok.selfClosing = true; + } + + return tok; + } + }, + + /** + * Indent | Outdent | Newline. + */ + + indent: function() { + var captures, re; + + // established regexp + if (this.indentRe) { + captures = this.indentRe.exec(this.input); + // determine regexp + } else { + // tabs + re = /^\n(\t*) */; + captures = re.exec(this.input); + + // spaces + if (captures && !captures[1].length) { + re = /^\n( *)/; + captures = re.exec(this.input); + } + + // established + if (captures && captures[1].length) this.indentRe = re; + } + + if (captures) { + var tok + , indents = captures[1].length; + + ++this.lineno; + this.consume(indents + 1); + + if (' ' == this.input[0] || '\t' == this.input[0]) { + throw new Error('Invalid indentation, you can use tabs or spaces but not both'); + } + + // blank line + if ('\n' == this.input[0]) return this.tok('newline'); + + // outdent + if (this.indentStack.length && indents < this.indentStack[0]) { + while (this.indentStack.length && this.indentStack[0] > indents) { + this.stash.push(this.tok('outdent')); + this.indentStack.shift(); + } + tok = this.stash.pop(); + // indent + } else if (indents && indents != this.indentStack[0]) { + this.indentStack.unshift(indents); + tok = this.tok('indent', indents); + // newline + } else { + tok = this.tok('newline'); + } + + return tok; + } + }, + + /** + * Pipe-less text consumed only when + * pipeless is true; + */ + + pipelessText: function() { + if (this.pipeless) { + if ('\n' == this.input[0]) return; + var i = this.input.indexOf('\n'); + if (-1 == i) i = this.input.length; + var str = this.input.substr(0, i); + this.consume(str.length); + return this.tok('text', str); + } + }, + + /** + * ':' + */ + + colon: function() { + return this.scan(/^: */, ':'); + }, + + /** + * Return the next token object, or those + * previously stashed by lookahead. + * + * @return {Object} + * @api private + */ + + advance: function(){ + return this.stashed() + || this.next(); + }, + + /** + * Return the next token object. + * + * @return {Object} + * @api private + */ + + next: function() { + return this.deferred() + || this.blank() + || this.eos() + || this.pipelessText() + || this.yield() + || this.doctype() + || this.interpolation() + || this["case"]() + || this.when() + || this["default"]() + || this["extends"]() + || this.append() + || this.prepend() + || this.block() + || this.include() + || this.mixin() + || this.call() + || this.conditional() + || this.each() + || this["while"]() + || this.assignment() + || this.tag() + || this.filter() + || this.code() + || this.id() + || this.className() + || this.attrs() + || this.indent() + || this.comment() + || this.colon() + || this.text(); + } +}; diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/attrs.js b/node_modules/mocha/node_modules/jade/lib/nodes/attrs.js new file mode 100644 index 0000000..5de9b59 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/attrs.js @@ -0,0 +1,77 @@ + +/*! + * Jade - nodes - Attrs + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'), + Block = require('./block'); + +/** + * Initialize a `Attrs` node. + * + * @api public + */ + +var Attrs = module.exports = function Attrs() { + this.attrs = []; +}; + +/** + * Inherit from `Node`. + */ + +Attrs.prototype.__proto__ = Node.prototype; + +/** + * Set attribute `name` to `val`, keep in mind these become + * part of a raw js object literal, so to quote a value you must + * '"quote me"', otherwise or example 'user.name' is literal JavaScript. + * + * @param {String} name + * @param {String} val + * @param {Boolean} escaped + * @return {Tag} for chaining + * @api public + */ + +Attrs.prototype.setAttribute = function(name, val, escaped){ + this.attrs.push({ name: name, val: val, escaped: escaped }); + return this; +}; + +/** + * Remove attribute `name` when present. + * + * @param {String} name + * @api public + */ + +Attrs.prototype.removeAttribute = function(name){ + for (var i = 0, len = this.attrs.length; i < len; ++i) { + if (this.attrs[i] && this.attrs[i].name == name) { + delete this.attrs[i]; + } + } +}; + +/** + * Get attribute value by `name`. + * + * @param {String} name + * @return {String} + * @api public + */ + +Attrs.prototype.getAttribute = function(name){ + for (var i = 0, len = this.attrs.length; i < len; ++i) { + if (this.attrs[i] && this.attrs[i].name == name) { + return this.attrs[i].val; + } + } +}; diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/block-comment.js b/node_modules/mocha/node_modules/jade/lib/nodes/block-comment.js new file mode 100644 index 0000000..4f41e4a --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/block-comment.js @@ -0,0 +1,33 @@ + +/*! + * Jade - nodes - BlockComment + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `BlockComment` with the given `block`. + * + * @param {String} val + * @param {Block} block + * @param {Boolean} buffer + * @api public + */ + +var BlockComment = module.exports = function BlockComment(val, block, buffer) { + this.block = block; + this.val = val; + this.buffer = buffer; +}; + +/** + * Inherit from `Node`. + */ + +BlockComment.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/block.js b/node_modules/mocha/node_modules/jade/lib/nodes/block.js new file mode 100644 index 0000000..bb00a1d --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/block.js @@ -0,0 +1,121 @@ + +/*! + * Jade - nodes - Block + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a new `Block` with an optional `node`. + * + * @param {Node} node + * @api public + */ + +var Block = module.exports = function Block(node){ + this.nodes = []; + if (node) this.push(node); +}; + +/** + * Inherit from `Node`. + */ + +Block.prototype.__proto__ = Node.prototype; + +/** + * Block flag. + */ + +Block.prototype.isBlock = true; + +/** + * Replace the nodes in `other` with the nodes + * in `this` block. + * + * @param {Block} other + * @api private + */ + +Block.prototype.replace = function(other){ + other.nodes = this.nodes; +}; + +/** + * Pust the given `node`. + * + * @param {Node} node + * @return {Number} + * @api public + */ + +Block.prototype.push = function(node){ + return this.nodes.push(node); +}; + +/** + * Check if this block is empty. + * + * @return {Boolean} + * @api public + */ + +Block.prototype.isEmpty = function(){ + return 0 == this.nodes.length; +}; + +/** + * Unshift the given `node`. + * + * @param {Node} node + * @return {Number} + * @api public + */ + +Block.prototype.unshift = function(node){ + return this.nodes.unshift(node); +}; + +/** + * Return the "last" block, or the first `yield` node. + * + * @return {Block} + * @api private + */ + +Block.prototype.includeBlock = function(){ + var ret = this + , node; + + for (var i = 0, len = this.nodes.length; i < len; ++i) { + node = this.nodes[i]; + if (node.yield) return node; + else if (node.textOnly) continue; + else if (node.includeBlock) ret = node.includeBlock(); + else if (node.block && !node.block.isEmpty()) ret = node.block.includeBlock(); + } + + return ret; +}; + +/** + * Return a clone of this block. + * + * @return {Block} + * @api private + */ + +Block.prototype.clone = function(){ + var clone = new Block; + for (var i = 0, len = this.nodes.length; i < len; ++i) { + clone.push(this.nodes[i].clone()); + } + return clone; +}; + diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/case.js b/node_modules/mocha/node_modules/jade/lib/nodes/case.js new file mode 100644 index 0000000..08ff033 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/case.js @@ -0,0 +1,43 @@ + +/*! + * Jade - nodes - Case + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a new `Case` with `expr`. + * + * @param {String} expr + * @api public + */ + +var Case = exports = module.exports = function Case(expr, block){ + this.expr = expr; + this.block = block; +}; + +/** + * Inherit from `Node`. + */ + +Case.prototype.__proto__ = Node.prototype; + +var When = exports.When = function When(expr, block){ + this.expr = expr; + this.block = block; + this.debug = false; +}; + +/** + * Inherit from `Node`. + */ + +When.prototype.__proto__ = Node.prototype; + diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/code.js b/node_modules/mocha/node_modules/jade/lib/nodes/code.js new file mode 100644 index 0000000..babc675 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/code.js @@ -0,0 +1,35 @@ + +/*! + * Jade - nodes - Code + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Code` node with the given code `val`. + * Code may also be optionally buffered and escaped. + * + * @param {String} val + * @param {Boolean} buffer + * @param {Boolean} escape + * @api public + */ + +var Code = module.exports = function Code(val, buffer, escape) { + this.val = val; + this.buffer = buffer; + this.escape = escape; + if (val.match(/^ *else/)) this.debug = false; +}; + +/** + * Inherit from `Node`. + */ + +Code.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/comment.js b/node_modules/mocha/node_modules/jade/lib/nodes/comment.js new file mode 100644 index 0000000..2e1469e --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/comment.js @@ -0,0 +1,32 @@ + +/*! + * Jade - nodes - Comment + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Comment` with the given `val`, optionally `buffer`, + * otherwise the comment may render in the output. + * + * @param {String} val + * @param {Boolean} buffer + * @api public + */ + +var Comment = module.exports = function Comment(val, buffer) { + this.val = val; + this.buffer = buffer; +}; + +/** + * Inherit from `Node`. + */ + +Comment.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/doctype.js b/node_modules/mocha/node_modules/jade/lib/nodes/doctype.js new file mode 100644 index 0000000..b8f33e5 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/doctype.js @@ -0,0 +1,29 @@ + +/*! + * Jade - nodes - Doctype + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Doctype` with the given `val`. + * + * @param {String} val + * @api public + */ + +var Doctype = module.exports = function Doctype(val) { + this.val = val; +}; + +/** + * Inherit from `Node`. + */ + +Doctype.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/each.js b/node_modules/mocha/node_modules/jade/lib/nodes/each.js new file mode 100644 index 0000000..f54101f --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/each.js @@ -0,0 +1,35 @@ + +/*! + * Jade - nodes - Each + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize an `Each` node, representing iteration + * + * @param {String} obj + * @param {String} val + * @param {String} key + * @param {Block} block + * @api public + */ + +var Each = module.exports = function Each(obj, val, key, block) { + this.obj = obj; + this.val = val; + this.key = key; + this.block = block; +}; + +/** + * Inherit from `Node`. + */ + +Each.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/filter.js b/node_modules/mocha/node_modules/jade/lib/nodes/filter.js new file mode 100644 index 0000000..851a004 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/filter.js @@ -0,0 +1,35 @@ + +/*! + * Jade - nodes - Filter + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node') + , Block = require('./block'); + +/** + * Initialize a `Filter` node with the given + * filter `name` and `block`. + * + * @param {String} name + * @param {Block|Node} block + * @api public + */ + +var Filter = module.exports = function Filter(name, block, attrs) { + this.name = name; + this.block = block; + this.attrs = attrs; + this.isASTFilter = !block.nodes.every(function(node){ return node.isText }); +}; + +/** + * Inherit from `Node`. + */ + +Filter.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/index.js b/node_modules/mocha/node_modules/jade/lib/nodes/index.js new file mode 100644 index 0000000..386ad2f --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/index.js @@ -0,0 +1,20 @@ + +/*! + * Jade - nodes + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +exports.Node = require('./node'); +exports.Tag = require('./tag'); +exports.Code = require('./code'); +exports.Each = require('./each'); +exports.Case = require('./case'); +exports.Text = require('./text'); +exports.Block = require('./block'); +exports.Mixin = require('./mixin'); +exports.Filter = require('./filter'); +exports.Comment = require('./comment'); +exports.Literal = require('./literal'); +exports.BlockComment = require('./block-comment'); +exports.Doctype = require('./doctype'); diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/literal.js b/node_modules/mocha/node_modules/jade/lib/nodes/literal.js new file mode 100644 index 0000000..fde586b --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/literal.js @@ -0,0 +1,32 @@ + +/*! + * Jade - nodes - Literal + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Literal` node with the given `str. + * + * @param {String} str + * @api public + */ + +var Literal = module.exports = function Literal(str) { + this.str = str + .replace(/\\/g, "\\\\") + .replace(/\n|\r\n/g, "\\n") + .replace(/'/g, "\\'"); +}; + +/** + * Inherit from `Node`. + */ + +Literal.prototype.__proto__ = Node.prototype; diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/mixin.js b/node_modules/mocha/node_modules/jade/lib/nodes/mixin.js new file mode 100644 index 0000000..8407bc7 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/mixin.js @@ -0,0 +1,36 @@ + +/*! + * Jade - nodes - Mixin + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Attrs = require('./attrs'); + +/** + * Initialize a new `Mixin` with `name` and `block`. + * + * @param {String} name + * @param {String} args + * @param {Block} block + * @api public + */ + +var Mixin = module.exports = function Mixin(name, args, block, call){ + this.name = name; + this.args = args; + this.block = block; + this.attrs = []; + this.call = call; +}; + +/** + * Inherit from `Attrs`. + */ + +Mixin.prototype.__proto__ = Attrs.prototype; + diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/node.js b/node_modules/mocha/node_modules/jade/lib/nodes/node.js new file mode 100644 index 0000000..e98f042 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/node.js @@ -0,0 +1,25 @@ + +/*! + * Jade - nodes - Node + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Initialize a `Node`. + * + * @api public + */ + +var Node = module.exports = function Node(){}; + +/** + * Clone this node (return itself) + * + * @return {Node} + * @api private + */ + +Node.prototype.clone = function(){ + return this; +}; diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/tag.js b/node_modules/mocha/node_modules/jade/lib/nodes/tag.js new file mode 100644 index 0000000..4b6728a --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/tag.js @@ -0,0 +1,95 @@ + +/*! + * Jade - nodes - Tag + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Attrs = require('./attrs'), + Block = require('./block'), + inlineTags = require('../inline-tags'); + +/** + * Initialize a `Tag` node with the given tag `name` and optional `block`. + * + * @param {String} name + * @param {Block} block + * @api public + */ + +var Tag = module.exports = function Tag(name, block) { + this.name = name; + this.attrs = []; + this.block = block || new Block; +}; + +/** + * Inherit from `Attrs`. + */ + +Tag.prototype.__proto__ = Attrs.prototype; + +/** + * Clone this tag. + * + * @return {Tag} + * @api private + */ + +Tag.prototype.clone = function(){ + var clone = new Tag(this.name, this.block.clone()); + clone.line = this.line; + clone.attrs = this.attrs; + clone.textOnly = this.textOnly; + return clone; +}; + +/** + * Check if this tag is an inline tag. + * + * @return {Boolean} + * @api private + */ + +Tag.prototype.isInline = function(){ + return ~inlineTags.indexOf(this.name); +}; + +/** + * Check if this tag's contents can be inlined. Used for pretty printing. + * + * @return {Boolean} + * @api private + */ + +Tag.prototype.canInline = function(){ + var nodes = this.block.nodes; + + function isInline(node){ + // Recurse if the node is a block + if (node.isBlock) return node.nodes.every(isInline); + return node.isText || (node.isInline && node.isInline()); + } + + // Empty tag + if (!nodes.length) return true; + + // Text-only or inline-only tag + if (1 == nodes.length) return isInline(nodes[0]); + + // Multi-line inline-only tag + if (this.block.nodes.every(isInline)) { + for (var i = 1, len = nodes.length; i < len; ++i) { + if (nodes[i-1].isText && nodes[i].isText) + return false; + } + return true; + } + + // Mixed tag + return false; +}; \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/lib/nodes/text.js b/node_modules/mocha/node_modules/jade/lib/nodes/text.js new file mode 100644 index 0000000..3b5dd55 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/nodes/text.js @@ -0,0 +1,36 @@ + +/*! + * Jade - nodes - Text + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Text` node with optional `line`. + * + * @param {String} line + * @api public + */ + +var Text = module.exports = function Text(line) { + this.val = ''; + if ('string' == typeof line) this.val = line; +}; + +/** + * Inherit from `Node`. + */ + +Text.prototype.__proto__ = Node.prototype; + +/** + * Flag as text. + */ + +Text.prototype.isText = true; \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/lib/parser.js b/node_modules/mocha/node_modules/jade/lib/parser.js new file mode 100644 index 0000000..92f2af0 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/parser.js @@ -0,0 +1,710 @@ + +/*! + * Jade - Parser + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Lexer = require('./lexer') + , nodes = require('./nodes'); + +/** + * Initialize `Parser` with the given input `str` and `filename`. + * + * @param {String} str + * @param {String} filename + * @param {Object} options + * @api public + */ + +var Parser = exports = module.exports = function Parser(str, filename, options){ + this.input = str; + this.lexer = new Lexer(str, options); + this.filename = filename; + this.blocks = {}; + this.mixins = {}; + this.options = options; + this.contexts = [this]; +}; + +/** + * Tags that may not contain tags. + */ + +var textOnly = exports.textOnly = ['script', 'style']; + +/** + * Parser prototype. + */ + +Parser.prototype = { + + /** + * Push `parser` onto the context stack, + * or pop and return a `Parser`. + */ + + context: function(parser){ + if (parser) { + this.contexts.push(parser); + } else { + return this.contexts.pop(); + } + }, + + /** + * Return the next token object. + * + * @return {Object} + * @api private + */ + + advance: function(){ + return this.lexer.advance(); + }, + + /** + * Skip `n` tokens. + * + * @param {Number} n + * @api private + */ + + skip: function(n){ + while (n--) this.advance(); + }, + + /** + * Single token lookahead. + * + * @return {Object} + * @api private + */ + + peek: function() { + return this.lookahead(1); + }, + + /** + * Return lexer lineno. + * + * @return {Number} + * @api private + */ + + line: function() { + return this.lexer.lineno; + }, + + /** + * `n` token lookahead. + * + * @param {Number} n + * @return {Object} + * @api private + */ + + lookahead: function(n){ + return this.lexer.lookahead(n); + }, + + /** + * Parse input returning a string of js for evaluation. + * + * @return {String} + * @api public + */ + + parse: function(){ + var block = new nodes.Block, parser; + block.line = this.line(); + + while ('eos' != this.peek().type) { + if ('newline' == this.peek().type) { + this.advance(); + } else { + block.push(this.parseExpr()); + } + } + + if (parser = this.extending) { + this.context(parser); + var ast = parser.parse(); + this.context(); + // hoist mixins + for (var name in this.mixins) + ast.unshift(this.mixins[name]); + return ast; + } + + return block; + }, + + /** + * Expect the given type, or throw an exception. + * + * @param {String} type + * @api private + */ + + expect: function(type){ + if (this.peek().type === type) { + return this.advance(); + } else { + throw new Error('expected "' + type + '", but got "' + this.peek().type + '"'); + } + }, + + /** + * Accept the given `type`. + * + * @param {String} type + * @api private + */ + + accept: function(type){ + if (this.peek().type === type) { + return this.advance(); + } + }, + + /** + * tag + * | doctype + * | mixin + * | include + * | filter + * | comment + * | text + * | each + * | code + * | yield + * | id + * | class + * | interpolation + */ + + parseExpr: function(){ + switch (this.peek().type) { + case 'tag': + return this.parseTag(); + case 'mixin': + return this.parseMixin(); + case 'block': + return this.parseBlock(); + case 'case': + return this.parseCase(); + case 'when': + return this.parseWhen(); + case 'default': + return this.parseDefault(); + case 'extends': + return this.parseExtends(); + case 'include': + return this.parseInclude(); + case 'doctype': + return this.parseDoctype(); + case 'filter': + return this.parseFilter(); + case 'comment': + return this.parseComment(); + case 'text': + return this.parseText(); + case 'each': + return this.parseEach(); + case 'code': + return this.parseCode(); + case 'call': + return this.parseCall(); + case 'interpolation': + return this.parseInterpolation(); + case 'yield': + this.advance(); + var block = new nodes.Block; + block.yield = true; + return block; + case 'id': + case 'class': + var tok = this.advance(); + this.lexer.defer(this.lexer.tok('tag', 'div')); + this.lexer.defer(tok); + return this.parseExpr(); + default: + throw new Error('unexpected token "' + this.peek().type + '"'); + } + }, + + /** + * Text + */ + + parseText: function(){ + var tok = this.expect('text') + , node = new nodes.Text(tok.val); + node.line = this.line(); + return node; + }, + + /** + * ':' expr + * | block + */ + + parseBlockExpansion: function(){ + if (':' == this.peek().type) { + this.advance(); + return new nodes.Block(this.parseExpr()); + } else { + return this.block(); + } + }, + + /** + * case + */ + + parseCase: function(){ + var val = this.expect('case').val + , node = new nodes.Case(val); + node.line = this.line(); + node.block = this.block(); + return node; + }, + + /** + * when + */ + + parseWhen: function(){ + var val = this.expect('when').val + return new nodes.Case.When(val, this.parseBlockExpansion()); + }, + + /** + * default + */ + + parseDefault: function(){ + this.expect('default'); + return new nodes.Case.When('default', this.parseBlockExpansion()); + }, + + /** + * code + */ + + parseCode: function(){ + var tok = this.expect('code') + , node = new nodes.Code(tok.val, tok.buffer, tok.escape) + , block + , i = 1; + node.line = this.line(); + while (this.lookahead(i) && 'newline' == this.lookahead(i).type) ++i; + block = 'indent' == this.lookahead(i).type; + if (block) { + this.skip(i-1); + node.block = this.block(); + } + return node; + }, + + /** + * comment + */ + + parseComment: function(){ + var tok = this.expect('comment') + , node; + + if ('indent' == this.peek().type) { + node = new nodes.BlockComment(tok.val, this.block(), tok.buffer); + } else { + node = new nodes.Comment(tok.val, tok.buffer); + } + + node.line = this.line(); + return node; + }, + + /** + * doctype + */ + + parseDoctype: function(){ + var tok = this.expect('doctype') + , node = new nodes.Doctype(tok.val); + node.line = this.line(); + return node; + }, + + /** + * filter attrs? text-block + */ + + parseFilter: function(){ + var block + , tok = this.expect('filter') + , attrs = this.accept('attrs'); + + this.lexer.pipeless = true; + block = this.parseTextBlock(); + this.lexer.pipeless = false; + + var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs); + node.line = this.line(); + return node; + }, + + /** + * tag ':' attrs? block + */ + + parseASTFilter: function(){ + var block + , tok = this.expect('tag') + , attrs = this.accept('attrs'); + + this.expect(':'); + block = this.block(); + + var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs); + node.line = this.line(); + return node; + }, + + /** + * each block + */ + + parseEach: function(){ + var tok = this.expect('each') + , node = new nodes.Each(tok.code, tok.val, tok.key); + node.line = this.line(); + node.block = this.block(); + return node; + }, + + /** + * 'extends' name + */ + + parseExtends: function(){ + var path = require('path') + , fs = require('fs') + , dirname = path.dirname + , basename = path.basename + , join = path.join; + + if (!this.filename) + throw new Error('the "filename" option is required to extend templates'); + + var path = this.expect('extends').val.trim() + , dir = dirname(this.filename); + + var path = join(dir, path + '.jade') + , str = fs.readFileSync(path, 'utf8') + , parser = new Parser(str, path, this.options); + + parser.blocks = this.blocks; + parser.contexts = this.contexts; + this.extending = parser; + + // TODO: null node + return new nodes.Literal(''); + }, + + /** + * 'block' name block + */ + + parseBlock: function(){ + var block = this.expect('block') + , mode = block.mode + , name = block.val.trim(); + + block = 'indent' == this.peek().type + ? this.block() + : new nodes.Block(new nodes.Literal('')); + + var prev = this.blocks[name]; + + if (prev) { + switch (prev.mode) { + case 'append': + block.nodes = block.nodes.concat(prev.nodes); + prev = block; + break; + case 'prepend': + block.nodes = prev.nodes.concat(block.nodes); + prev = block; + break; + } + } + + block.mode = mode; + return this.blocks[name] = prev || block; + }, + + /** + * include block? + */ + + parseInclude: function(){ + var path = require('path') + , fs = require('fs') + , dirname = path.dirname + , basename = path.basename + , join = path.join; + + var path = this.expect('include').val.trim() + , dir = dirname(this.filename); + + if (!this.filename) + throw new Error('the "filename" option is required to use includes'); + + // no extension + if (!~basename(path).indexOf('.')) { + path += '.jade'; + } + + // non-jade + if ('.jade' != path.substr(-5)) { + var path = join(dir, path) + , str = fs.readFileSync(path, 'utf8'); + return new nodes.Literal(str); + } + + var path = join(dir, path) + , str = fs.readFileSync(path, 'utf8') + , parser = new Parser(str, path, this.options); + parser.blocks = this.blocks; + parser.mixins = this.mixins; + + this.context(parser); + var ast = parser.parse(); + this.context(); + ast.filename = path; + + if ('indent' == this.peek().type) { + ast.includeBlock().push(this.block()); + } + + return ast; + }, + + /** + * call ident block + */ + + parseCall: function(){ + var tok = this.expect('call') + , name = tok.val + , args = tok.args + , mixin = new nodes.Mixin(name, args, new nodes.Block, true); + + this.tag(mixin); + if (mixin.block.isEmpty()) mixin.block = null; + return mixin; + }, + + /** + * mixin block + */ + + parseMixin: function(){ + var tok = this.expect('mixin') + , name = tok.val + , args = tok.args + , mixin; + + // definition + if ('indent' == this.peek().type) { + mixin = new nodes.Mixin(name, args, this.block(), false); + this.mixins[name] = mixin; + return mixin; + // call + } else { + return new nodes.Mixin(name, args, null, true); + } + }, + + /** + * indent (text | newline)* outdent + */ + + parseTextBlock: function(){ + var block = new nodes.Block; + block.line = this.line(); + var spaces = this.expect('indent').val; + if (null == this._spaces) this._spaces = spaces; + var indent = Array(spaces - this._spaces + 1).join(' '); + while ('outdent' != this.peek().type) { + switch (this.peek().type) { + case 'newline': + this.advance(); + break; + case 'indent': + this.parseTextBlock().nodes.forEach(function(node){ + block.push(node); + }); + break; + default: + var text = new nodes.Text(indent + this.advance().val); + text.line = this.line(); + block.push(text); + } + } + + if (spaces == this._spaces) this._spaces = null; + this.expect('outdent'); + return block; + }, + + /** + * indent expr* outdent + */ + + block: function(){ + var block = new nodes.Block; + block.line = this.line(); + this.expect('indent'); + while ('outdent' != this.peek().type) { + if ('newline' == this.peek().type) { + this.advance(); + } else { + block.push(this.parseExpr()); + } + } + this.expect('outdent'); + return block; + }, + + /** + * interpolation (attrs | class | id)* (text | code | ':')? newline* block? + */ + + parseInterpolation: function(){ + var tok = this.advance(); + var tag = new nodes.Tag(tok.val); + tag.buffer = true; + return this.tag(tag); + }, + + /** + * tag (attrs | class | id)* (text | code | ':')? newline* block? + */ + + parseTag: function(){ + // ast-filter look-ahead + var i = 2; + if ('attrs' == this.lookahead(i).type) ++i; + if (':' == this.lookahead(i).type) { + if ('indent' == this.lookahead(++i).type) { + return this.parseASTFilter(); + } + } + + var tok = this.advance() + , tag = new nodes.Tag(tok.val); + + tag.selfClosing = tok.selfClosing; + + return this.tag(tag); + }, + + /** + * Parse tag. + */ + + tag: function(tag){ + var dot; + + tag.line = this.line(); + + // (attrs | class | id)* + out: + while (true) { + switch (this.peek().type) { + case 'id': + case 'class': + var tok = this.advance(); + tag.setAttribute(tok.type, "'" + tok.val + "'"); + continue; + case 'attrs': + var tok = this.advance() + , obj = tok.attrs + , escaped = tok.escaped + , names = Object.keys(obj); + + if (tok.selfClosing) tag.selfClosing = true; + + for (var i = 0, len = names.length; i < len; ++i) { + var name = names[i] + , val = obj[name]; + tag.setAttribute(name, val, escaped[name]); + } + continue; + default: + break out; + } + } + + // check immediate '.' + if ('.' == this.peek().val) { + dot = tag.textOnly = true; + this.advance(); + } + + // (text | code | ':')? + switch (this.peek().type) { + case 'text': + tag.block.push(this.parseText()); + break; + case 'code': + tag.code = this.parseCode(); + break; + case ':': + this.advance(); + tag.block = new nodes.Block; + tag.block.push(this.parseExpr()); + break; + } + + // newline* + while ('newline' == this.peek().type) this.advance(); + + tag.textOnly = tag.textOnly || ~textOnly.indexOf(tag.name); + + // script special-case + if ('script' == tag.name) { + var type = tag.getAttribute('type'); + if (!dot && type && 'text/javascript' != type.replace(/^['"]|['"]$/g, '')) { + tag.textOnly = false; + } + } + + // block? + if ('indent' == this.peek().type) { + if (tag.textOnly) { + this.lexer.pipeless = true; + tag.block = this.parseTextBlock(); + this.lexer.pipeless = false; + } else { + var block = this.block(); + if (tag.block) { + for (var i = 0, len = block.nodes.length; i < len; ++i) { + tag.block.push(block.nodes[i]); + } + } else { + tag.block = block; + } + } + } + + return tag; + } +}; diff --git a/node_modules/mocha/node_modules/jade/lib/runtime.js b/node_modules/mocha/node_modules/jade/lib/runtime.js new file mode 100644 index 0000000..fb711f5 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/runtime.js @@ -0,0 +1,174 @@ + +/*! + * Jade - runtime + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Lame Array.isArray() polyfill for now. + */ + +if (!Array.isArray) { + Array.isArray = function(arr){ + return '[object Array]' == Object.prototype.toString.call(arr); + }; +} + +/** + * Lame Object.keys() polyfill for now. + */ + +if (!Object.keys) { + Object.keys = function(obj){ + var arr = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + arr.push(key); + } + } + return arr; + } +} + +/** + * Merge two attribute objects giving precedence + * to values in object `b`. Classes are special-cased + * allowing for arrays and merging/joining appropriately + * resulting in a string. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api private + */ + +exports.merge = function merge(a, b) { + var ac = a['class']; + var bc = b['class']; + + if (ac || bc) { + ac = ac || []; + bc = bc || []; + if (!Array.isArray(ac)) ac = [ac]; + if (!Array.isArray(bc)) bc = [bc]; + ac = ac.filter(nulls); + bc = bc.filter(nulls); + a['class'] = ac.concat(bc).join(' '); + } + + for (var key in b) { + if (key != 'class') { + a[key] = b[key]; + } + } + + return a; +}; + +/** + * Filter null `val`s. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function nulls(val) { + return val != null; +} + +/** + * Render the given attributes object. + * + * @param {Object} obj + * @param {Object} escaped + * @return {String} + * @api private + */ + +exports.attrs = function attrs(obj, escaped){ + var buf = [] + , terse = obj.terse; + + delete obj.terse; + var keys = Object.keys(obj) + , len = keys.length; + + if (len) { + buf.push(''); + for (var i = 0; i < len; ++i) { + var key = keys[i] + , val = obj[key]; + + if ('boolean' == typeof val || null == val) { + if (val) { + terse + ? buf.push(key) + : buf.push(key + '="' + key + '"'); + } + } else if (0 == key.indexOf('data') && 'string' != typeof val) { + buf.push(key + "='" + JSON.stringify(val) + "'"); + } else if ('class' == key && Array.isArray(val)) { + buf.push(key + '="' + exports.escape(val.join(' ')) + '"'); + } else if (escaped && escaped[key]) { + buf.push(key + '="' + exports.escape(val) + '"'); + } else { + buf.push(key + '="' + val + '"'); + } + } + } + + return buf.join(' '); +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function escape(html){ + return String(html) + .replace(/&(?!(\w+|\#\d+);)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; + +/** + * Re-throw the given `err` in context to the + * the jade in `filename` at the given `lineno`. + * + * @param {Error} err + * @param {String} filename + * @param {String} lineno + * @api private + */ + +exports.rethrow = function rethrow(err, filename, lineno){ + if (!filename) throw err; + + var context = 3 + , str = require('fs').readFileSync(filename, 'utf8') + , lines = str.split('\n') + , start = Math.max(lineno - context, 0) + , end = Math.min(lines.length, lineno + context); + + // Error context + var context = lines.slice(start, end).map(function(line, i){ + var curr = i + start + 1; + return (curr == lineno ? ' > ' : ' ') + + curr + + '| ' + + line; + }).join('\n'); + + // Alter exception message + err.path = filename; + err.message = (filename || 'Jade') + ':' + lineno + + '\n' + context + '\n\n' + err.message; + throw err; +}; diff --git a/node_modules/mocha/node_modules/jade/lib/self-closing.js b/node_modules/mocha/node_modules/jade/lib/self-closing.js new file mode 100644 index 0000000..0548771 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/self-closing.js @@ -0,0 +1,19 @@ + +/*! + * Jade - self closing tags + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = [ + 'meta' + , 'img' + , 'link' + , 'input' + , 'source' + , 'area' + , 'base' + , 'col' + , 'br' + , 'hr' +]; \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/lib/utils.js b/node_modules/mocha/node_modules/jade/lib/utils.js new file mode 100644 index 0000000..ff46d02 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/lib/utils.js @@ -0,0 +1,49 @@ + +/*! + * Jade - utils + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Convert interpolation in the given string to JavaScript. + * + * @param {String} str + * @return {String} + * @api private + */ + +var interpolate = exports.interpolate = function(str){ + return str.replace(/(\\)?([#!]){(.*?)}/g, function(str, escape, flag, code){ + return escape + ? str + : "' + " + + ('!' == flag ? '' : 'escape') + + "((interp = " + code.replace(/\\'/g, "'") + + ") == null ? '' : interp) + '"; + }); +}; + +/** + * Escape single quotes in `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +var escape = exports.escape = function(str) { + return str.replace(/'/g, "\\'"); +}; + +/** + * Interpolate, and escape the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.text = function(str){ + return interpolate(escape(str)); +}; \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/node_modules/commander/.npmignore b/node_modules/mocha/node_modules/jade/node_modules/commander/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/commander/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/node_modules/mocha/node_modules/jade/node_modules/commander/.travis.yml b/node_modules/mocha/node_modules/jade/node_modules/commander/.travis.yml new file mode 100644 index 0000000..f1d0f13 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/commander/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.4 + - 0.6 diff --git a/node_modules/mocha/node_modules/jade/node_modules/commander/History.md b/node_modules/mocha/node_modules/jade/node_modules/commander/History.md new file mode 100644 index 0000000..4961d2e --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/commander/History.md @@ -0,0 +1,107 @@ + +0.6.1 / 2012-06-01 +================== + + * Added: append (yes or no) on confirmation + * Added: allow node.js v0.7.x + +0.6.0 / 2012-04-10 +================== + + * Added `.prompt(obj, callback)` support. Closes #49 + * Added default support to .choose(). Closes #41 + * Fixed the choice example + +0.5.1 / 2011-12-20 +================== + + * Fixed `password()` for recent nodes. Closes #36 + +0.5.0 / 2011-12-04 +================== + + * Added sub-command option support [itay] + +0.4.3 / 2011-12-04 +================== + + * Fixed custom help ordering. Closes #32 + +0.4.2 / 2011-11-24 +================== + + * Added travis support + * Fixed: line-buffered input automatically trimmed. Closes #31 + +0.4.1 / 2011-11-18 +================== + + * Removed listening for "close" on --help + +0.4.0 / 2011-11-15 +================== + + * Added support for `--`. Closes #24 + +0.3.3 / 2011-11-14 +================== + + * Fixed: wait for close event when writing help info [Jerry Hamlet] + +0.3.2 / 2011-11-01 +================== + + * Fixed long flag definitions with values [felixge] + +0.3.1 / 2011-10-31 +================== + + * Changed `--version` short flag to `-V` from `-v` + * Changed `.version()` so it's configurable [felixge] + +0.3.0 / 2011-10-31 +================== + + * Added support for long flags only. Closes #18 + +0.2.1 / 2011-10-24 +================== + + * "node": ">= 0.4.x < 0.7.0". Closes #20 + +0.2.0 / 2011-09-26 +================== + + * Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs] + +0.1.0 / 2011-08-24 +================== + + * Added support for custom `--help` output + +0.0.5 / 2011-08-18 +================== + + * Changed: when the user enters nothing prompt for password again + * Fixed issue with passwords beginning with numbers [NuckChorris] + +0.0.4 / 2011-08-15 +================== + + * Fixed `Commander#args` + +0.0.3 / 2011-08-15 +================== + + * Added default option value support + +0.0.2 / 2011-08-15 +================== + + * Added mask support to `Command#password(str[, mask], fn)` + * Added `Command#password(str, fn)` + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/node_modules/mocha/node_modules/jade/node_modules/commander/Makefile b/node_modules/mocha/node_modules/jade/node_modules/commander/Makefile new file mode 100644 index 0000000..0074625 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/commander/Makefile @@ -0,0 +1,7 @@ + +TESTS = $(shell find test/test.*.js) + +test: + @./test/run $(TESTS) + +.PHONY: test \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/node_modules/commander/Readme.md b/node_modules/mocha/node_modules/jade/node_modules/commander/Readme.md new file mode 100644 index 0000000..b8328c3 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/commander/Readme.md @@ -0,0 +1,262 @@ +# Commander.js + + The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander). + + [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js) + +## Installation + + $ npm install commander + +## Option parsing + + Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander'); + +program + .version('0.0.1') + .option('-p, --peppers', 'Add peppers') + .option('-P, --pineapple', 'Add pineapple') + .option('-b, --bbq', 'Add bbq sauce') + .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble') + .parse(process.argv); + +console.log('you ordered a pizza with:'); +if (program.peppers) console.log(' - peppers'); +if (program.pineapple) console.log(' - pineappe'); +if (program.bbq) console.log(' - bbq'); +console.log(' - %s cheese', program.cheese); +``` + + Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. + +## Automated --help + + The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free: + +``` + $ ./examples/pizza --help + + Usage: pizza [options] + + Options: + + -V, --version output the version number + -p, --peppers Add peppers + -P, --pineapple Add pineappe + -b, --bbq Add bbq sauce + -c, --cheese Add the specified type of cheese [marble] + -h, --help output usage information + +``` + +## Coercion + +```js +function range(val) { + return val.split('..').map(Number); +} + +function list(val) { + return val.split(','); +} + +program + .version('0.0.1') + .usage('[options] ') + .option('-i, --integer ', 'An integer argument', parseInt) + .option('-f, --float ', 'A float argument', parseFloat) + .option('-r, --range ..', 'A range', range) + .option('-l, --list ', 'A list', list) + .option('-o, --optional [value]', 'An optional value') + .parse(process.argv); + +console.log(' int: %j', program.integer); +console.log(' float: %j', program.float); +console.log(' optional: %j', program.optional); +program.range = program.range || []; +console.log(' range: %j..%j', program.range[0], program.range[1]); +console.log(' list: %j', program.list); +console.log(' args: %j', program.args); +``` + +## Custom help + + You can display arbitrary `-h, --help` information + by listening for "--help". Commander will automatically + exit once you are done so that the remainder of your program + does not execute causing undesired behaviours, for example + in the following executable "stuff" will not output when + `--help` is used. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('../'); + +function list(val) { + return val.split(',').map(Number); +} + +program + .version('0.0.1') + .option('-f, --foo', 'enable some foo') + .option('-b, --bar', 'enable some bar') + .option('-B, --baz', 'enable some baz'); + +// must be before .parse() since +// node's emit() is immediate + +program.on('--help', function(){ + console.log(' Examples:'); + console.log(''); + console.log(' $ custom-help --help'); + console.log(' $ custom-help -h'); + console.log(''); +}); + +program.parse(process.argv); + +console.log('stuff'); +``` + +yielding the following help output: + +``` + +Usage: custom-help [options] + +Options: + + -h, --help output usage information + -V, --version output the version number + -f, --foo enable some foo + -b, --bar enable some bar + -B, --baz enable some baz + +Examples: + + $ custom-help --help + $ custom-help -h + +``` + +## .prompt(msg, fn) + + Single-line prompt: + +```js +program.prompt('name: ', function(name){ + console.log('hi %s', name); +}); +``` + + Multi-line prompt: + +```js +program.prompt('description:', function(name){ + console.log('hi %s', name); +}); +``` + + Coercion: + +```js +program.prompt('Age: ', Number, function(age){ + console.log('age: %j', age); +}); +``` + +```js +program.prompt('Birthdate: ', Date, function(date){ + console.log('date: %s', date); +}); +``` + +## .password(msg[, mask], fn) + +Prompt for password without echoing: + +```js +program.password('Password: ', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +Prompt for password with mask char "*": + +```js +program.password('Password: ', '*', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +## .confirm(msg, fn) + + Confirm with the given `msg`: + +```js +program.confirm('continue? ', function(ok){ + console.log(' got %j', ok); +}); +``` + +## .choose(list, fn) + + Let the user choose from a `list`: + +```js +var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + +console.log('Choose the coolest pet:'); +program.choose(list, function(i){ + console.log('you chose %d "%s"', i, list[i]); +}); +``` + +## Links + + - [API documentation](http://visionmedia.github.com/commander.js/) + - [ascii tables](https://github.com/LearnBoost/cli-table) + - [progress bars](https://github.com/visionmedia/node-progress) + - [more progress bars](https://github.com/substack/node-multimeter) + - [examples](https://github.com/visionmedia/commander.js/tree/master/examples) + +## License + +(The MIT License) + +Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/node_modules/commander/index.js b/node_modules/mocha/node_modules/jade/node_modules/commander/index.js new file mode 100644 index 0000000..06ec1e4 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/commander/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/commander'); \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/node_modules/commander/lib/commander.js b/node_modules/mocha/node_modules/jade/node_modules/commander/lib/commander.js new file mode 100644 index 0000000..5ba87eb --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/commander/lib/commander.js @@ -0,0 +1,1026 @@ + +/*! + * commander + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , path = require('path') + , tty = require('tty') + , basename = path.basename; + +/** + * Expose the root command. + */ + +exports = module.exports = new Command; + +/** + * Expose `Command`. + */ + +exports.Command = Command; + +/** + * Expose `Option`. + */ + +exports.Option = Option; + +/** + * Initialize a new `Option` with the given `flags` and `description`. + * + * @param {String} flags + * @param {String} description + * @api public + */ + +function Option(flags, description) { + this.flags = flags; + this.required = ~flags.indexOf('<'); + this.optional = ~flags.indexOf('['); + this.bool = !~flags.indexOf('-no-'); + flags = flags.split(/[ ,|]+/); + if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); + this.long = flags.shift(); + this.description = description; +} + +/** + * Return option name. + * + * @return {String} + * @api private + */ + +Option.prototype.name = function(){ + return this.long + .replace('--', '') + .replace('no-', ''); +}; + +/** + * Check if `arg` matches the short or long flag. + * + * @param {String} arg + * @return {Boolean} + * @api private + */ + +Option.prototype.is = function(arg){ + return arg == this.short + || arg == this.long; +}; + +/** + * Initialize a new `Command`. + * + * @param {String} name + * @api public + */ + +function Command(name) { + this.commands = []; + this.options = []; + this.args = []; + this.name = name; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Command.prototype.__proto__ = EventEmitter.prototype; + +/** + * Add command `name`. + * + * The `.action()` callback is invoked when the + * command `name` is specified via __ARGV__, + * and the remaining arguments are applied to the + * function for access. + * + * When the `name` is "*" an un-matched command + * will be passed as the first arg, followed by + * the rest of __ARGV__ remaining. + * + * Examples: + * + * program + * .version('0.0.1') + * .option('-C, --chdir ', 'change the working directory') + * .option('-c, --config ', 'set config path. defaults to ./deploy.conf') + * .option('-T, --no-tests', 'ignore test hook') + * + * program + * .command('setup') + * .description('run remote setup commands') + * .action(function(){ + * console.log('setup'); + * }); + * + * program + * .command('exec ') + * .description('run the given remote command') + * .action(function(cmd){ + * console.log('exec "%s"', cmd); + * }); + * + * program + * .command('*') + * .description('deploy the given env') + * .action(function(env){ + * console.log('deploying "%s"', env); + * }); + * + * program.parse(process.argv); + * + * @param {String} name + * @return {Command} the new command + * @api public + */ + +Command.prototype.command = function(name){ + var args = name.split(/ +/); + var cmd = new Command(args.shift()); + this.commands.push(cmd); + cmd.parseExpectedArgs(args); + cmd.parent = this; + return cmd; +}; + +/** + * Parse expected `args`. + * + * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`. + * + * @param {Array} args + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parseExpectedArgs = function(args){ + if (!args.length) return; + var self = this; + args.forEach(function(arg){ + switch (arg[0]) { + case '<': + self.args.push({ required: true, name: arg.slice(1, -1) }); + break; + case '[': + self.args.push({ required: false, name: arg.slice(1, -1) }); + break; + } + }); + return this; +}; + +/** + * Register callback `fn` for the command. + * + * Examples: + * + * program + * .command('help') + * .description('display verbose help') + * .action(function(){ + * // output help here + * }); + * + * @param {Function} fn + * @return {Command} for chaining + * @api public + */ + +Command.prototype.action = function(fn){ + var self = this; + this.parent.on(this.name, function(args, unknown){ + // Parse any so-far unknown options + unknown = unknown || []; + var parsed = self.parseOptions(unknown); + + // Output help if necessary + outputHelpIfNecessary(self, parsed.unknown); + + // If there are still any unknown options, then we simply + // die, unless someone asked for help, in which case we give it + // to them, and then we die. + if (parsed.unknown.length > 0) { + self.unknownOption(parsed.unknown[0]); + } + + self.args.forEach(function(arg, i){ + if (arg.required && null == args[i]) { + self.missingArgument(arg.name); + } + }); + + // Always append ourselves to the end of the arguments, + // to make sure we match the number of arguments the user + // expects + if (self.args.length) { + args[self.args.length] = self; + } else { + args.push(self); + } + + fn.apply(this, args); + }); + return this; +}; + +/** + * Define option with `flags`, `description` and optional + * coercion `fn`. + * + * The `flags` string should contain both the short and long flags, + * separated by comma, a pipe or space. The following are all valid + * all will output this way when `--help` is used. + * + * "-p, --pepper" + * "-p|--pepper" + * "-p --pepper" + * + * Examples: + * + * // simple boolean defaulting to false + * program.option('-p, --pepper', 'add pepper'); + * + * --pepper + * program.pepper + * // => Boolean + * + * // simple boolean defaulting to false + * program.option('-C, --no-cheese', 'remove cheese'); + * + * program.cheese + * // => true + * + * --no-cheese + * program.cheese + * // => true + * + * // required argument + * program.option('-C, --chdir ', 'change the working directory'); + * + * --chdir /tmp + * program.chdir + * // => "/tmp" + * + * // optional argument + * program.option('-c, --cheese [type]', 'add cheese [marble]'); + * + * @param {String} flags + * @param {String} description + * @param {Function|Mixed} fn or default + * @param {Mixed} defaultValue + * @return {Command} for chaining + * @api public + */ + +Command.prototype.option = function(flags, description, fn, defaultValue){ + var self = this + , option = new Option(flags, description) + , oname = option.name() + , name = camelcase(oname); + + // default as 3rd arg + if ('function' != typeof fn) defaultValue = fn, fn = null; + + // preassign default value only for --no-*, [optional], or + if (false == option.bool || option.optional || option.required) { + // when --no-* we make sure default is true + if (false == option.bool) defaultValue = true; + // preassign only if we have a default + if (undefined !== defaultValue) self[name] = defaultValue; + } + + // register the option + this.options.push(option); + + // when it's passed assign the value + // and conditionally invoke the callback + this.on(oname, function(val){ + // coercion + if (null != val && fn) val = fn(val); + + // unassigned or bool + if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) { + // if no value, bool true, and we have a default, then use it! + if (null == val) { + self[name] = option.bool + ? defaultValue || true + : false; + } else { + self[name] = val; + } + } else if (null !== val) { + // reassign + self[name] = val; + } + }); + + return this; +}; + +/** + * Parse `argv`, settings options and invoking commands when defined. + * + * @param {Array} argv + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parse = function(argv){ + // store raw args + this.rawArgs = argv; + + // guess name + if (!this.name) this.name = basename(argv[1]); + + // process argv + var parsed = this.parseOptions(this.normalize(argv.slice(2))); + this.args = parsed.args; + return this.parseArgs(this.args, parsed.unknown); +}; + +/** + * Normalize `args`, splitting joined short flags. For example + * the arg "-abc" is equivalent to "-a -b -c". + * + * @param {Array} args + * @return {Array} + * @api private + */ + +Command.prototype.normalize = function(args){ + var ret = [] + , arg; + + for (var i = 0, len = args.length; i < len; ++i) { + arg = args[i]; + if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) { + arg.slice(1).split('').forEach(function(c){ + ret.push('-' + c); + }); + } else { + ret.push(arg); + } + } + + return ret; +}; + +/** + * Parse command `args`. + * + * When listener(s) are available those + * callbacks are invoked, otherwise the "*" + * event is emitted and those actions are invoked. + * + * @param {Array} args + * @return {Command} for chaining + * @api private + */ + +Command.prototype.parseArgs = function(args, unknown){ + var cmds = this.commands + , len = cmds.length + , name; + + if (args.length) { + name = args[0]; + if (this.listeners(name).length) { + this.emit(args.shift(), args, unknown); + } else { + this.emit('*', args); + } + } else { + outputHelpIfNecessary(this, unknown); + + // If there were no args and we have unknown options, + // then they are extraneous and we need to error. + if (unknown.length > 0) { + this.unknownOption(unknown[0]); + } + } + + return this; +}; + +/** + * Return an option matching `arg` if any. + * + * @param {String} arg + * @return {Option} + * @api private + */ + +Command.prototype.optionFor = function(arg){ + for (var i = 0, len = this.options.length; i < len; ++i) { + if (this.options[i].is(arg)) { + return this.options[i]; + } + } +}; + +/** + * Parse options from `argv` returning `argv` + * void of these options. + * + * @param {Array} argv + * @return {Array} + * @api public + */ + +Command.prototype.parseOptions = function(argv){ + var args = [] + , len = argv.length + , literal + , option + , arg; + + var unknownOptions = []; + + // parse options + for (var i = 0; i < len; ++i) { + arg = argv[i]; + + // literal args after -- + if ('--' == arg) { + literal = true; + continue; + } + + if (literal) { + args.push(arg); + continue; + } + + // find matching Option + option = this.optionFor(arg); + + // option is defined + if (option) { + // requires arg + if (option.required) { + arg = argv[++i]; + if (null == arg) return this.optionMissingArgument(option); + if ('-' == arg[0]) return this.optionMissingArgument(option, arg); + this.emit(option.name(), arg); + // optional arg + } else if (option.optional) { + arg = argv[i+1]; + if (null == arg || '-' == arg[0]) { + arg = null; + } else { + ++i; + } + this.emit(option.name(), arg); + // bool + } else { + this.emit(option.name()); + } + continue; + } + + // looks like an option + if (arg.length > 1 && '-' == arg[0]) { + unknownOptions.push(arg); + + // If the next argument looks like it might be + // an argument for this option, we pass it on. + // If it isn't, then it'll simply be ignored + if (argv[i+1] && '-' != argv[i+1][0]) { + unknownOptions.push(argv[++i]); + } + continue; + } + + // arg + args.push(arg); + } + + return { args: args, unknown: unknownOptions }; +}; + +/** + * Argument `name` is missing. + * + * @param {String} name + * @api private + */ + +Command.prototype.missingArgument = function(name){ + console.error(); + console.error(" error: missing required argument `%s'", name); + console.error(); + process.exit(1); +}; + +/** + * `Option` is missing an argument, but received `flag` or nothing. + * + * @param {String} option + * @param {String} flag + * @api private + */ + +Command.prototype.optionMissingArgument = function(option, flag){ + console.error(); + if (flag) { + console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag); + } else { + console.error(" error: option `%s' argument missing", option.flags); + } + console.error(); + process.exit(1); +}; + +/** + * Unknown option `flag`. + * + * @param {String} flag + * @api private + */ + +Command.prototype.unknownOption = function(flag){ + console.error(); + console.error(" error: unknown option `%s'", flag); + console.error(); + process.exit(1); +}; + +/** + * Set the program version to `str`. + * + * This method auto-registers the "-V, --version" flag + * which will print the version number when passed. + * + * @param {String} str + * @param {String} flags + * @return {Command} for chaining + * @api public + */ + +Command.prototype.version = function(str, flags){ + if (0 == arguments.length) return this._version; + this._version = str; + flags = flags || '-V, --version'; + this.option(flags, 'output the version number'); + this.on('version', function(){ + console.log(str); + process.exit(0); + }); + return this; +}; + +/** + * Set the description `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.description = function(str){ + if (0 == arguments.length) return this._description; + this._description = str; + return this; +}; + +/** + * Set / get the command usage `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.usage = function(str){ + var args = this.args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }); + + var usage = '[options' + + (this.commands.length ? '] [command' : '') + + ']' + + (this.args.length ? ' ' + args : ''); + if (0 == arguments.length) return this._usage || usage; + this._usage = str; + + return this; +}; + +/** + * Return the largest option length. + * + * @return {Number} + * @api private + */ + +Command.prototype.largestOptionLength = function(){ + return this.options.reduce(function(max, option){ + return Math.max(max, option.flags.length); + }, 0); +}; + +/** + * Return help for options. + * + * @return {String} + * @api private + */ + +Command.prototype.optionHelp = function(){ + var width = this.largestOptionLength(); + + // Prepend the help information + return [pad('-h, --help', width) + ' ' + 'output usage information'] + .concat(this.options.map(function(option){ + return pad(option.flags, width) + + ' ' + option.description; + })) + .join('\n'); +}; + +/** + * Return command help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.commandHelp = function(){ + if (!this.commands.length) return ''; + return [ + '' + , ' Commands:' + , '' + , this.commands.map(function(cmd){ + var args = cmd.args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }).join(' '); + + return cmd.name + + (cmd.options.length + ? ' [options]' + : '') + ' ' + args + + (cmd.description() + ? '\n' + cmd.description() + : ''); + }).join('\n\n').replace(/^/gm, ' ') + , '' + ].join('\n'); +}; + +/** + * Return program help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.helpInformation = function(){ + return [ + '' + , ' Usage: ' + this.name + ' ' + this.usage() + , '' + this.commandHelp() + , ' Options:' + , '' + , '' + this.optionHelp().replace(/^/gm, ' ') + , '' + , '' + ].join('\n'); +}; + +/** + * Prompt for a `Number`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForNumber = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseNumber(val){ + val = Number(val); + if (isNaN(val)) return self.promptSingleLine(str + '(must be a number) ', parseNumber); + fn(val); + }); +}; + +/** + * Prompt for a `Date`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForDate = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseDate(val){ + val = new Date(val); + if (isNaN(val.getTime())) return self.promptSingleLine(str + '(must be a date) ', parseDate); + fn(val); + }); +}; + +/** + * Single-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptSingleLine = function(str, fn){ + if ('function' == typeof arguments[2]) { + return this['promptFor' + (fn.name || fn)](str, arguments[2]); + } + + process.stdout.write(str); + process.stdin.setEncoding('utf8'); + process.stdin.once('data', function(val){ + fn(val.trim()); + }).resume(); +}; + +/** + * Multi-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptMultiLine = function(str, fn){ + var buf = []; + console.log(str); + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function(val){ + if ('\n' == val || '\r\n' == val) { + process.stdin.removeAllListeners('data'); + fn(buf.join('\n')); + } else { + buf.push(val.trimRight()); + } + }).resume(); +}; + +/** + * Prompt `str` and callback `fn(val)` + * + * Commander supports single-line and multi-line prompts. + * To issue a single-line prompt simply add white-space + * to the end of `str`, something like "name: ", whereas + * for a multi-line prompt omit this "description:". + * + * + * Examples: + * + * program.prompt('Username: ', function(name){ + * console.log('hi %s', name); + * }); + * + * program.prompt('Description:', function(desc){ + * console.log('description was "%s"', desc.trim()); + * }); + * + * @param {String|Object} str + * @param {Function} fn + * @api public + */ + +Command.prototype.prompt = function(str, fn){ + var self = this; + + if ('string' == typeof str) { + if (/ $/.test(str)) return this.promptSingleLine.apply(this, arguments); + this.promptMultiLine(str, fn); + } else { + var keys = Object.keys(str) + , obj = {}; + + function next() { + var key = keys.shift() + , label = str[key]; + + if (!key) return fn(obj); + self.prompt(label, function(val){ + obj[key] = val; + next(); + }); + } + + next(); + } +}; + +/** + * Prompt for password with `str`, `mask` char and callback `fn(val)`. + * + * The mask string defaults to '', aka no output is + * written while typing, you may want to use "*" etc. + * + * Examples: + * + * program.password('Password: ', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * program.password('Password: ', '*', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {String} mask + * @param {Function} fn + * @api public + */ + +Command.prototype.password = function(str, mask, fn){ + var self = this + , buf = ''; + + // default mask + if ('function' == typeof mask) { + fn = mask; + mask = ''; + } + + process.stdin.resume(); + tty.setRawMode(true); + process.stdout.write(str); + + // keypress + process.stdin.on('keypress', function(c, key){ + if (key && 'enter' == key.name) { + console.log(); + process.stdin.removeAllListeners('keypress'); + tty.setRawMode(false); + if (!buf.trim().length) return self.password(str, mask, fn); + fn(buf); + return; + } + + if (key && key.ctrl && 'c' == key.name) { + console.log('%s', buf); + process.exit(); + } + + process.stdout.write(mask); + buf += c; + }).resume(); +}; + +/** + * Confirmation prompt with `str` and callback `fn(bool)` + * + * Examples: + * + * program.confirm('continue? ', function(ok){ + * console.log(' got %j', ok); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {Function} fn + * @api public + */ + + +Command.prototype.confirm = function(str, fn, verbose){ + var self = this; + this.prompt(str, function(ok){ + if (!ok.trim()) { + if (!verbose) str += '(yes or no) '; + return self.confirm(str, fn, true); + } + fn(parseBool(ok)); + }); +}; + +/** + * Choice prompt with `list` of items and callback `fn(index, item)` + * + * Examples: + * + * var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + * + * console.log('Choose the coolest pet:'); + * program.choose(list, function(i){ + * console.log('you chose %d "%s"', i, list[i]); + * process.stdin.destroy(); + * }); + * + * @param {Array} list + * @param {Number|Function} index or fn + * @param {Function} fn + * @api public + */ + +Command.prototype.choose = function(list, index, fn){ + var self = this + , hasDefault = 'number' == typeof index; + + if (!hasDefault) { + fn = index; + index = null; + } + + list.forEach(function(item, i){ + if (hasDefault && i == index) { + console.log('* %d) %s', i + 1, item); + } else { + console.log(' %d) %s', i + 1, item); + } + }); + + function again() { + self.prompt(' : ', function(val){ + val = parseInt(val, 10) - 1; + if (hasDefault && isNaN(val)) val = index; + + if (null == list[val]) { + again(); + } else { + fn(val, list[val]); + } + }); + } + + again(); +}; + +/** + * Camel-case the given `flag` + * + * @param {String} flag + * @return {String} + * @api private + */ + +function camelcase(flag) { + return flag.split('-').reduce(function(str, word){ + return str + word[0].toUpperCase() + word.slice(1); + }); +} + +/** + * Parse a boolean `str`. + * + * @param {String} str + * @return {Boolean} + * @api private + */ + +function parseBool(str) { + return /^y|yes|ok|true$/i.test(str); +} + +/** + * Pad `str` to `width`. + * + * @param {String} str + * @param {Number} width + * @return {String} + * @api private + */ + +function pad(str, width) { + var len = Math.max(0, width - str.length); + return str + Array(len + 1).join(' '); +} + +/** + * Output help information if necessary + * + * @param {Command} command to output help for + * @param {Array} array of options to search for -h or --help + * @api private + */ + +function outputHelpIfNecessary(cmd, options) { + options = options || []; + for (var i = 0; i < options.length; i++) { + if (options[i] == '--help' || options[i] == '-h') { + process.stdout.write(cmd.helpInformation()); + cmd.emit('--help'); + process.exit(0); + } + } +} diff --git a/node_modules/mocha/node_modules/jade/node_modules/commander/package.json b/node_modules/mocha/node_modules/jade/node_modules/commander/package.json new file mode 100644 index 0000000..d6ca144 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/commander/package.json @@ -0,0 +1,55 @@ +{ + "name": "commander", + "version": "0.6.1", + "description": "the complete solution for node.js command-line programs", + "keywords": [ + "command", + "option", + "parser", + "prompt", + "stdin" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/commander.js.git" + }, + "dependencies": {}, + "devDependencies": { + "should": ">= 0.0.1" + }, + "scripts": { + "test": "make test" + }, + "main": "index", + "engines": { + "node": ">= 0.4.x" + }, + "_npmUser": { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + "_id": "commander@0.6.1", + "optionalDependencies": {}, + "_engineSupported": true, + "_npmVersion": "1.1.0-3", + "_nodeVersion": "v0.6.12", + "_defaultsLoaded": true, + "dist": { + "shasum": "fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06", + "tarball": "http://registry.npmjs.org/commander/-/commander-0.6.1.tgz" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + } + ], + "directories": {}, + "_shasum": "fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06", + "_resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "_from": "commander@0.6.1" +} diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.gitignore.orig b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.gitignore.orig new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.gitignore.orig @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.gitignore.rej b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.gitignore.rej new file mode 100644 index 0000000..69244ff --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.gitignore.rej @@ -0,0 +1,5 @@ +--- /dev/null ++++ .gitignore +@@ -0,0 +1,2 @@ ++node_modules/ ++npm-debug.log \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.npmignore b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.npmignore new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/LICENSE b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/LICENSE new file mode 100644 index 0000000..432d1ae --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/LICENSE @@ -0,0 +1,21 @@ +Copyright 2010 James Halliday (mail@substack.net) + +This project is free software released under the MIT/X11 license: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/README.markdown b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/README.markdown new file mode 100644 index 0000000..b4dd75f --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/README.markdown @@ -0,0 +1,54 @@ +mkdirp +====== + +Like `mkdir -p`, but in node.js! + +example +======= + +pow.js +------ + var mkdirp = require('mkdirp'); + + mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') + }); + +Output + pow! + +And now /tmp/foo/bar/baz exists, huzzah! + +methods +======= + +var mkdirp = require('mkdirp'); + +mkdirp(dir, mode, cb) +--------------------- + +Create a new directory and any necessary subdirectories at `dir` with octal +permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +mkdirp.sync(dir, mode) +---------------------- + +Synchronously create a new directory and any necessary subdirectories at `dir` +with octal permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +install +======= + +With [npm](http://npmjs.org) do: + + npm install mkdirp + +license +======= + +MIT/X11 diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js new file mode 100644 index 0000000..e692421 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js.orig b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js.orig new file mode 100644 index 0000000..7741462 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js.orig @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', 0755, function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js.rej b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js.rej new file mode 100644 index 0000000..81e7f43 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js.rej @@ -0,0 +1,19 @@ +--- examples/pow.js ++++ examples/pow.js +@@ -1,6 +1,15 @@ +-var mkdirp = require('mkdirp').mkdirp; ++var mkdirp = require('../').mkdirp, ++ mkdirpSync = require('../').mkdirpSync; + + mkdirp('/tmp/foo/bar/baz', 0755, function (err) { + if (err) console.error(err) + else console.log('pow!') + }); ++ ++try { ++ mkdirpSync('/tmp/bar/foo/baz', 0755); ++ console.log('double pow!'); ++} ++catch (ex) { ++ console.log(ex); ++} \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/index.js b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/index.js new file mode 100644 index 0000000..25f43ad --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/index.js @@ -0,0 +1,79 @@ +var path = require('path'); +var fs = require('fs'); + +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; + +function mkdirP (p, mode, f) { + if (typeof mode === 'function' || mode === undefined) { + f = mode; + mode = 0777 & (~process.umask()); + } + + var cb = f || function () {}; + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + fs.mkdir(p, mode, function (er) { + if (!er) return cb(); + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), mode, function (er) { + if (er) cb(er); + else mkdirP(p, mode, cb); + }); + break; + + case 'EEXIST': + fs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original EEXIST be the failure reason. + if (er2 || !stat.isDirectory()) cb(er) + else cb(); + }); + break; + + default: + cb(er); + break; + } + }); +} + +mkdirP.sync = function sync (p, mode) { + if (mode === undefined) { + mode = 0777 & (~process.umask()); + } + + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + try { + fs.mkdirSync(p, mode) + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + var err1 = sync(path.dirname(p), mode) + if (err1) throw err1; + else return sync(p, mode); + break; + + case 'EEXIST' : + var stat; + try { + stat = fs.statSync(p); + } + catch (err1) { + throw err0 + } + if (!stat.isDirectory()) throw err0; + else return null; + break; + default : + throw err0 + break; + } + } + + return null; +}; diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/package.json b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/package.json new file mode 100644 index 0000000..0953b91 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/package.json @@ -0,0 +1,53 @@ +{ + "name": "mkdirp", + "description": "Recursively mkdir, like `mkdir -p`", + "version": "0.3.0", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "main": "./index", + "keywords": [ + "mkdir", + "directory" + ], + "repository": { + "type": "git", + "url": "git://github.com/substack/node-mkdirp.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "devDependencies": { + "tap": "0.0.x" + }, + "license": "MIT/X11", + "engines": { + "node": "*" + }, + "_npmUser": { + "name": "substack", + "email": "mail@substack.net" + }, + "_id": "mkdirp@0.3.0", + "dependencies": {}, + "_engineSupported": true, + "_npmVersion": "1.0.106", + "_nodeVersion": "v0.4.12", + "_defaultsLoaded": true, + "dist": { + "shasum": "1bbf5ab1ba827af23575143490426455f481fe1e", + "tarball": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + }, + "maintainers": [ + { + "name": "substack", + "email": "mail@substack.net" + } + ], + "directories": {}, + "_shasum": "1bbf5ab1ba827af23575143490426455f481fe1e", + "_resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "_from": "mkdirp@0.3.0" +} diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/chmod.js b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/chmod.js new file mode 100644 index 0000000..520dcb8 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/chmod.js @@ -0,0 +1,38 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +test('chmod-pre', function (t) { + var mode = 0744 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.equal(stat && stat.mode & 0777, mode, 'should be 0744'); + t.end(); + }); + }); +}); + +test('chmod', function (t) { + var mode = 0755 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.end(); + }); + }); +}); diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/clobber.js b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/clobber.js new file mode 100644 index 0000000..0eb7099 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/clobber.js @@ -0,0 +1,37 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +// a file in the way +var itw = ps.slice(0, 3).join('/'); + + +test('clobber-pre', function (t) { + console.error("about to write to "+itw) + fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.'); + + fs.stat(itw, function (er, stat) { + t.ifError(er) + t.ok(stat && stat.isFile(), 'should be file') + t.end() + }) +}) + +test('clobber', function (t) { + t.plan(2); + mkdirp(file, 0755, function (err) { + t.ok(err); + t.equal(err.code, 'ENOTDIR'); + t.end(); + }); +}); diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/mkdirp.js b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/mkdirp.js new file mode 100644 index 0000000..b07cd70 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/mkdirp.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('woo', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/perm.js b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/perm.js new file mode 100644 index 0000000..23a7abb --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/perm.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('async perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); + +test('async root perm', function (t) { + mkdirp('/tmp', 0755, function (err) { + if (err) t.fail(err); + t.end(); + }); + t.end(); +}); diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/perm_sync.js b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/perm_sync.js new file mode 100644 index 0000000..f685f60 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/perm_sync.js @@ -0,0 +1,39 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json'; + + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); + +test('sync root perm', function (t) { + t.plan(1); + + var file = '/tmp'; + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/race.js b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/race.js new file mode 100644 index 0000000..96a0447 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/race.js @@ -0,0 +1,41 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('race', function (t) { + t.plan(4); + var ps = [ '', 'tmp' ]; + + for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); + } + var file = ps.join('/'); + + var res = 2; + mk(file, function () { + if (--res === 0) t.end(); + }); + + mk(file, function () { + if (--res === 0) t.end(); + }); + + function mk (file, cb) { + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + if (cb) cb(); + } + }) + }) + }); + } +}); diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/rel.js b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/rel.js new file mode 100644 index 0000000..7985824 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/rel.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('rel', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var cwd = process.cwd(); + process.chdir('/tmp'); + + var file = [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + process.chdir(cwd); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/sync.js b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/sync.js new file mode 100644 index 0000000..e0e389d --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/sync.js @@ -0,0 +1,27 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + var err = mkdirp.sync(file, 0755); + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) +}); diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/umask.js b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/umask.js new file mode 100644 index 0000000..64ccafe --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/umask.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('implicit mode from umask', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0777 & (~process.umask())); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/umask_sync.js b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/umask_sync.js new file mode 100644 index 0000000..83cba56 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/umask_sync.js @@ -0,0 +1,27 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('umask sync modes', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + var err = mkdirp.sync(file); + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, (0777 & (~process.umask()))); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) +}); diff --git a/node_modules/mocha/node_modules/jade/package.json b/node_modules/mocha/node_modules/jade/package.json new file mode 100644 index 0000000..25f493c --- /dev/null +++ b/node_modules/mocha/node_modules/jade/package.json @@ -0,0 +1,56 @@ +{ + "name": "jade", + "description": "Jade template engine", + "version": "0.26.3", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/jade" + }, + "main": "./index.js", + "bin": { + "jade": "./bin/jade" + }, + "man": [ + "./jade.1" + ], + "dependencies": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "devDependencies": { + "mocha": "*", + "markdown": "*", + "stylus": "*", + "uubench": "*", + "should": "*", + "less": "*", + "uglify-js": "*" + }, + "component": { + "scripts": { + "jade": "runtime.js" + } + }, + "scripts": { + "prepublish": "npm prune" + }, + "_id": "jade@0.26.3", + "dist": { + "shasum": "8f10d7977d8d79f2f6ff862a81b0513ccb25686c", + "tarball": "http://registry.npmjs.org/jade/-/jade-0.26.3.tgz" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + } + ], + "directories": {}, + "_shasum": "8f10d7977d8d79f2f6ff862a81b0513ccb25686c", + "_resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "_from": "jade@0.26.3" +} diff --git a/node_modules/mocha/node_modules/jade/runtime.js b/node_modules/mocha/node_modules/jade/runtime.js new file mode 100644 index 0000000..0f54907 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/runtime.js @@ -0,0 +1,179 @@ + +jade = (function(exports){ +/*! + * Jade - runtime + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Lame Array.isArray() polyfill for now. + */ + +if (!Array.isArray) { + Array.isArray = function(arr){ + return '[object Array]' == Object.prototype.toString.call(arr); + }; +} + +/** + * Lame Object.keys() polyfill for now. + */ + +if (!Object.keys) { + Object.keys = function(obj){ + var arr = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + arr.push(key); + } + } + return arr; + } +} + +/** + * Merge two attribute objects giving precedence + * to values in object `b`. Classes are special-cased + * allowing for arrays and merging/joining appropriately + * resulting in a string. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api private + */ + +exports.merge = function merge(a, b) { + var ac = a['class']; + var bc = b['class']; + + if (ac || bc) { + ac = ac || []; + bc = bc || []; + if (!Array.isArray(ac)) ac = [ac]; + if (!Array.isArray(bc)) bc = [bc]; + ac = ac.filter(nulls); + bc = bc.filter(nulls); + a['class'] = ac.concat(bc).join(' '); + } + + for (var key in b) { + if (key != 'class') { + a[key] = b[key]; + } + } + + return a; +}; + +/** + * Filter null `val`s. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function nulls(val) { + return val != null; +} + +/** + * Render the given attributes object. + * + * @param {Object} obj + * @param {Object} escaped + * @return {String} + * @api private + */ + +exports.attrs = function attrs(obj, escaped){ + var buf = [] + , terse = obj.terse; + + delete obj.terse; + var keys = Object.keys(obj) + , len = keys.length; + + if (len) { + buf.push(''); + for (var i = 0; i < len; ++i) { + var key = keys[i] + , val = obj[key]; + + if ('boolean' == typeof val || null == val) { + if (val) { + terse + ? buf.push(key) + : buf.push(key + '="' + key + '"'); + } + } else if (0 == key.indexOf('data') && 'string' != typeof val) { + buf.push(key + "='" + JSON.stringify(val) + "'"); + } else if ('class' == key && Array.isArray(val)) { + buf.push(key + '="' + exports.escape(val.join(' ')) + '"'); + } else if (escaped && escaped[key]) { + buf.push(key + '="' + exports.escape(val) + '"'); + } else { + buf.push(key + '="' + val + '"'); + } + } + } + + return buf.join(' '); +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function escape(html){ + return String(html) + .replace(/&(?!(\w+|\#\d+);)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; + +/** + * Re-throw the given `err` in context to the + * the jade in `filename` at the given `lineno`. + * + * @param {Error} err + * @param {String} filename + * @param {String} lineno + * @api private + */ + +exports.rethrow = function rethrow(err, filename, lineno){ + if (!filename) throw err; + + var context = 3 + , str = require('fs').readFileSync(filename, 'utf8') + , lines = str.split('\n') + , start = Math.max(lineno - context, 0) + , end = Math.min(lines.length, lineno + context); + + // Error context + var context = lines.slice(start, end).map(function(line, i){ + var curr = i + start + 1; + return (curr == lineno ? ' > ' : ' ') + + curr + + '| ' + + line; + }).join('\n'); + + // Alter exception message + err.path = filename; + err.message = (filename || 'Jade') + ':' + lineno + + '\n' + context + '\n\n' + err.message; + throw err; +}; + + return exports; + +})({}); diff --git a/node_modules/mocha/node_modules/jade/runtime.min.js b/node_modules/mocha/node_modules/jade/runtime.min.js new file mode 100644 index 0000000..1714efb --- /dev/null +++ b/node_modules/mocha/node_modules/jade/runtime.min.js @@ -0,0 +1 @@ +jade=function(exports){Array.isArray||(Array.isArray=function(arr){return"[object Array]"==Object.prototype.toString.call(arr)}),Object.keys||(Object.keys=function(obj){var arr=[];for(var key in obj)obj.hasOwnProperty(key)&&arr.push(key);return arr}),exports.merge=function merge(a,b){var ac=a["class"],bc=b["class"];if(ac||bc)ac=ac||[],bc=bc||[],Array.isArray(ac)||(ac=[ac]),Array.isArray(bc)||(bc=[bc]),ac=ac.filter(nulls),bc=bc.filter(nulls),a["class"]=ac.concat(bc).join(" ");for(var key in b)key!="class"&&(a[key]=b[key]);return a};function nulls(val){return val!=null}return exports.attrs=function attrs(obj,escaped){var buf=[],terse=obj.terse;delete obj.terse;var keys=Object.keys(obj),len=keys.length;if(len){buf.push("");for(var i=0;i/g,">").replace(/"/g,""")},exports.rethrow=function rethrow(err,filename,lineno){if(!filename)throw err;var context=3,str=require("fs").readFileSync(filename,"utf8"),lines=str.split("\n"),start=Math.max(lineno-context,0),end=Math.min(lines.length,lineno+context),context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" > ":" ")+curr+"| "+line}).join("\n");throw err.path=filename,err.message=(filename||"Jade")+":"+lineno+"\n"+context+"\n\n"+err.message,err},exports}({}); \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/test.jade b/node_modules/mocha/node_modules/jade/test.jade new file mode 100644 index 0000000..b3a8988 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/test.jade @@ -0,0 +1,7 @@ +p. + This is a large + body of text for + this tag. + + Nothing too + exciting. \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/testing/head.jade b/node_modules/mocha/node_modules/jade/testing/head.jade new file mode 100644 index 0000000..8515406 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/testing/head.jade @@ -0,0 +1,5 @@ +head + script(src='/jquery.js') + yield + if false + script(src='/jquery.ui.js') diff --git a/node_modules/mocha/node_modules/jade/testing/index.jade b/node_modules/mocha/node_modules/jade/testing/index.jade new file mode 100644 index 0000000..1032c5f --- /dev/null +++ b/node_modules/mocha/node_modules/jade/testing/index.jade @@ -0,0 +1,22 @@ + +tag = 'p' +foo = 'bar' + +#{tag} value +#{tag}(foo='bar') value +#{foo ? 'a' : 'li'}(something) here + +mixin item(icon) + li + if attributes.href + a(attributes) + img.icon(src=icon) + block + else + span(attributes) + img.icon(src=icon) + block + +ul + +item('contact') Contact + +item(href='/contact') Contact diff --git a/node_modules/mocha/node_modules/jade/testing/index.js b/node_modules/mocha/node_modules/jade/testing/index.js new file mode 100644 index 0000000..226e8c0 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/testing/index.js @@ -0,0 +1,11 @@ + +/** + * Module dependencies. + */ + +var jade = require('../'); + +jade.renderFile('testing/index.jade', { pretty: true, debug: true, compileDebug: false }, function(err, str){ + if (err) throw err; + console.log(str); +}); \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/testing/layout.jade b/node_modules/mocha/node_modules/jade/testing/layout.jade new file mode 100644 index 0000000..6923cf1 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/testing/layout.jade @@ -0,0 +1,6 @@ +html + include head + script(src='/caustic.js') + script(src='/app.js') + body + block content \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/testing/user.jade b/node_modules/mocha/node_modules/jade/testing/user.jade new file mode 100644 index 0000000..3c636b7 --- /dev/null +++ b/node_modules/mocha/node_modules/jade/testing/user.jade @@ -0,0 +1,7 @@ +h1 Tobi +p Is a ferret + +ul + li: a foo + li: a bar + li: a baz \ No newline at end of file diff --git a/node_modules/mocha/node_modules/jade/testing/user.js b/node_modules/mocha/node_modules/jade/testing/user.js new file mode 100644 index 0000000..2ecc45e --- /dev/null +++ b/node_modules/mocha/node_modules/jade/testing/user.js @@ -0,0 +1,27 @@ +function anonymous(locals, attrs, escape, rethrow) { +var attrs = jade.attrs, escape = jade.escape, rethrow = jade.rethrow; +var __jade = [{ lineno: 1, filename: "testing/user.jade" }]; +try { +var buf = []; +with (locals || {}) { +var interp; +__jade.unshift({ lineno: 1, filename: __jade[0].filename }); +__jade.unshift({ lineno: 1, filename: __jade[0].filename }); +buf.push('

        Tobi'); +__jade.unshift({ lineno: undefined, filename: __jade[0].filename }); +__jade.shift(); +buf.push('

        '); +__jade.shift(); +__jade.unshift({ lineno: 2, filename: __jade[0].filename }); +buf.push('

        Is a ferret'); +__jade.unshift({ lineno: undefined, filename: __jade[0].filename }); +__jade.shift(); +buf.push('

        '); +__jade.shift(); +__jade.shift(); +} +return buf.join(""); +} catch (err) { + rethrow(err, __jade[0].filename, __jade[0].lineno); +} +} \ No newline at end of file diff --git a/node_modules/mocha/node_modules/mkdirp/.npmignore b/node_modules/mocha/node_modules/mkdirp/.npmignore new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/node_modules/mocha/node_modules/mkdirp/.travis.yml b/node_modules/mocha/node_modules/mkdirp/.travis.yml new file mode 100644 index 0000000..c693a93 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.6 + - 0.8 + - "0.10" diff --git a/node_modules/mocha/node_modules/mkdirp/LICENSE b/node_modules/mocha/node_modules/mkdirp/LICENSE new file mode 100644 index 0000000..432d1ae --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/LICENSE @@ -0,0 +1,21 @@ +Copyright 2010 James Halliday (mail@substack.net) + +This project is free software released under the MIT/X11 license: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/mocha/node_modules/mkdirp/bin/cmd.js b/node_modules/mocha/node_modules/mkdirp/bin/cmd.js new file mode 100755 index 0000000..d95de15 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/bin/cmd.js @@ -0,0 +1,33 @@ +#!/usr/bin/env node + +var mkdirp = require('../'); +var minimist = require('minimist'); +var fs = require('fs'); + +var argv = minimist(process.argv.slice(2), { + alias: { m: 'mode', h: 'help' }, + string: [ 'mode' ] +}); +if (argv.help) { + fs.createReadStream(__dirname + '/usage.txt').pipe(process.stdout); + return; +} + +var paths = argv._.slice(); +var mode = argv.mode ? parseInt(argv.mode, 8) : undefined; + +(function next () { + if (paths.length === 0) return; + var p = paths.shift(); + + if (mode === undefined) mkdirp(p, cb) + else mkdirp(p, mode, cb) + + function cb (err) { + if (err) { + console.error(err.message); + process.exit(1); + } + else next(); + } +})(); diff --git a/node_modules/mocha/node_modules/mkdirp/bin/usage.txt b/node_modules/mocha/node_modules/mkdirp/bin/usage.txt new file mode 100644 index 0000000..f952aa2 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/bin/usage.txt @@ -0,0 +1,12 @@ +usage: mkdirp [DIR1,DIR2..] {OPTIONS} + + Create each supplied directory including any necessary parent directories that + don't yet exist. + + If the directory already exists, do nothing. + +OPTIONS are: + + -m, --mode If a directory needs to be created, set the mode as an octal + permission string. + diff --git a/node_modules/mocha/node_modules/mkdirp/examples/pow.js b/node_modules/mocha/node_modules/mkdirp/examples/pow.js new file mode 100644 index 0000000..e692421 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/examples/pow.js @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/node_modules/mocha/node_modules/mkdirp/index.js b/node_modules/mocha/node_modules/mkdirp/index.js new file mode 100644 index 0000000..a1742b2 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/index.js @@ -0,0 +1,97 @@ +var path = require('path'); +var fs = require('fs'); + +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; + +function mkdirP (p, opts, f, made) { + if (typeof opts === 'function') { + f = opts; + opts = {}; + } + else if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = 0777 & (~process.umask()); + } + if (!made) made = null; + + var cb = f || function () {}; + p = path.resolve(p); + + xfs.mkdir(p, mode, function (er) { + if (!er) { + made = made || p; + return cb(null, made); + } + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), opts, function (er, made) { + if (er) cb(er, made); + else mkdirP(p, opts, cb, made); + }); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + xfs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original error be the failure reason. + if (er2 || !stat.isDirectory()) cb(er, made) + else cb(null, made); + }); + break; + } + }); +} + +mkdirP.sync = function sync (p, opts, made) { + if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = 0777 & (~process.umask()); + } + if (!made) made = null; + + p = path.resolve(p); + + try { + xfs.mkdirSync(p, mode); + made = made || p; + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + made = sync(path.dirname(p), opts, made); + sync(p, opts, made); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + var stat; + try { + stat = xfs.statSync(p); + } + catch (err1) { + throw err0; + } + if (!stat.isDirectory()) throw err0; + break; + } + } + + return made; +}; diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/.travis.yml b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/.travis.yml new file mode 100644 index 0000000..cc4dba2 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - "0.8" + - "0.10" diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/LICENSE b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/LICENSE new file mode 100644 index 0000000..ee27ba4 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/LICENSE @@ -0,0 +1,18 @@ +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/example/parse.js b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/example/parse.js new file mode 100644 index 0000000..abff3e8 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/example/parse.js @@ -0,0 +1,2 @@ +var argv = require('../')(process.argv.slice(2)); +console.dir(argv); diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/index.js b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/index.js new file mode 100644 index 0000000..584f551 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/index.js @@ -0,0 +1,187 @@ +module.exports = function (args, opts) { + if (!opts) opts = {}; + + var flags = { bools : {}, strings : {} }; + + [].concat(opts['boolean']).filter(Boolean).forEach(function (key) { + flags.bools[key] = true; + }); + + [].concat(opts.string).filter(Boolean).forEach(function (key) { + flags.strings[key] = true; + }); + + var aliases = {}; + Object.keys(opts.alias || {}).forEach(function (key) { + aliases[key] = [].concat(opts.alias[key]); + aliases[key].forEach(function (x) { + aliases[x] = [key].concat(aliases[key].filter(function (y) { + return x !== y; + })); + }); + }); + + var defaults = opts['default'] || {}; + + var argv = { _ : [] }; + Object.keys(flags.bools).forEach(function (key) { + setArg(key, defaults[key] === undefined ? false : defaults[key]); + }); + + var notFlags = []; + + if (args.indexOf('--') !== -1) { + notFlags = args.slice(args.indexOf('--')+1); + args = args.slice(0, args.indexOf('--')); + } + + function setArg (key, val) { + var value = !flags.strings[key] && isNumber(val) + ? Number(val) : val + ; + setKey(argv, key.split('.'), value); + + (aliases[key] || []).forEach(function (x) { + setKey(argv, x.split('.'), value); + }); + } + + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + + if (/^--.+=/.test(arg)) { + // Using [\s\S] instead of . because js doesn't support the + // 'dotall' regex modifier. See: + // http://stackoverflow.com/a/1068308/13216 + var m = arg.match(/^--([^=]+)=([\s\S]*)$/); + setArg(m[1], m[2]); + } + else if (/^--no-.+/.test(arg)) { + var key = arg.match(/^--no-(.+)/)[1]; + setArg(key, false); + } + else if (/^--.+/.test(arg)) { + var key = arg.match(/^--(.+)/)[1]; + var next = args[i + 1]; + if (next !== undefined && !/^-/.test(next) + && !flags.bools[key] + && (aliases[key] ? !flags.bools[aliases[key]] : true)) { + setArg(key, next); + i++; + } + else if (/^(true|false)$/.test(next)) { + setArg(key, next === 'true'); + i++; + } + else { + setArg(key, flags.strings[key] ? '' : true); + } + } + else if (/^-[^-]+/.test(arg)) { + var letters = arg.slice(1,-1).split(''); + + var broken = false; + for (var j = 0; j < letters.length; j++) { + var next = arg.slice(j+2); + + if (next === '-') { + setArg(letters[j], next) + continue; + } + + if (/[A-Za-z]/.test(letters[j]) + && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { + setArg(letters[j], next); + broken = true; + break; + } + + if (letters[j+1] && letters[j+1].match(/\W/)) { + setArg(letters[j], arg.slice(j+2)); + broken = true; + break; + } + else { + setArg(letters[j], flags.strings[letters[j]] ? '' : true); + } + } + + var key = arg.slice(-1)[0]; + if (!broken && key !== '-') { + if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1]) + && !flags.bools[key] + && (aliases[key] ? !flags.bools[aliases[key]] : true)) { + setArg(key, args[i+1]); + i++; + } + else if (args[i+1] && /true|false/.test(args[i+1])) { + setArg(key, args[i+1] === 'true'); + i++; + } + else { + setArg(key, flags.strings[key] ? '' : true); + } + } + } + else { + argv._.push( + flags.strings['_'] || !isNumber(arg) ? arg : Number(arg) + ); + } + } + + Object.keys(defaults).forEach(function (key) { + if (!hasKey(argv, key.split('.'))) { + setKey(argv, key.split('.'), defaults[key]); + + (aliases[key] || []).forEach(function (x) { + setKey(argv, x.split('.'), defaults[key]); + }); + } + }); + + notFlags.forEach(function(key) { + argv._.push(key); + }); + + return argv; +}; + +function hasKey (obj, keys) { + var o = obj; + keys.slice(0,-1).forEach(function (key) { + o = (o[key] || {}); + }); + + var key = keys[keys.length - 1]; + return key in o; +} + +function setKey (obj, keys, value) { + var o = obj; + keys.slice(0,-1).forEach(function (key) { + if (o[key] === undefined) o[key] = {}; + o = o[key]; + }); + + var key = keys[keys.length - 1]; + if (o[key] === undefined || typeof o[key] === 'boolean') { + o[key] = value; + } + else if (Array.isArray(o[key])) { + o[key].push(value); + } + else { + o[key] = [ o[key], value ]; + } +} + +function isNumber (x) { + if (typeof x === 'number') return true; + if (/^0x[0-9a-f]+$/i.test(x)) return true; + return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x); +} + +function longest (xs) { + return Math.max.apply(null, xs.map(function (x) { return x.length })); +} diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/package.json b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/package.json new file mode 100644 index 0000000..7cd80f4 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/package.json @@ -0,0 +1,66 @@ +{ + "name": "minimist", + "version": "0.0.8", + "description": "parse argument options", + "main": "index.js", + "devDependencies": { + "tape": "~1.0.4", + "tap": "~0.4.0" + }, + "scripts": { + "test": "tap test/*.js" + }, + "testling": { + "files": "test/*.js", + "browsers": [ + "ie/6..latest", + "ff/5", + "firefox/latest", + "chrome/10", + "chrome/latest", + "safari/5.1", + "safari/latest", + "opera/12" + ] + }, + "repository": { + "type": "git", + "url": "git://github.com/substack/minimist.git" + }, + "homepage": "https://github.com/substack/minimist", + "keywords": [ + "argv", + "getopt", + "parser", + "optimist" + ], + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/substack/minimist/issues" + }, + "_id": "minimist@0.0.8", + "dist": { + "shasum": "857fcabfc3397d2625b8228262e86aa7a011b05d", + "tarball": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + }, + "_from": "minimist@0.0.8", + "_npmVersion": "1.4.3", + "_npmUser": { + "name": "substack", + "email": "mail@substack.net" + }, + "maintainers": [ + { + "name": "substack", + "email": "mail@substack.net" + } + ], + "directories": {}, + "_shasum": "857fcabfc3397d2625b8228262e86aa7a011b05d", + "_resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" +} diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/readme.markdown b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/readme.markdown new file mode 100644 index 0000000..c256353 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/readme.markdown @@ -0,0 +1,73 @@ +# minimist + +parse argument options + +This module is the guts of optimist's argument parser without all the +fanciful decoration. + +[![browser support](https://ci.testling.com/substack/minimist.png)](http://ci.testling.com/substack/minimist) + +[![build status](https://secure.travis-ci.org/substack/minimist.png)](http://travis-ci.org/substack/minimist) + +# example + +``` js +var argv = require('minimist')(process.argv.slice(2)); +console.dir(argv); +``` + +``` +$ node example/parse.js -a beep -b boop +{ _: [], a: 'beep', b: 'boop' } +``` + +``` +$ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz +{ _: [ 'foo', 'bar', 'baz' ], + x: 3, + y: 4, + n: 5, + a: true, + b: true, + c: true, + beep: 'boop' } +``` + +# methods + +``` js +var parseArgs = require('minimist') +``` + +## var argv = parseArgs(args, opts={}) + +Return an argument object `argv` populated with the array arguments from `args`. + +`argv._` contains all the arguments that didn't have an option associated with +them. + +Numeric-looking arguments will be returned as numbers unless `opts.string` or +`opts.boolean` is set for that argument name. + +Any arguments after `'--'` will not be parsed and will end up in `argv._`. + +options can be: + +* `opts.string` - a string or array of strings argument names to always treat as +strings +* `opts.boolean` - a string or array of strings to always treat as booleans +* `opts.alias` - an object mapping string names to strings or arrays of string +argument names to use as aliases +* `opts.default` - an object mapping string argument names to default values + +# install + +With [npm](https://npmjs.org) do: + +``` +npm install minimist +``` + +# license + +MIT diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/dash.js b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/dash.js new file mode 100644 index 0000000..8b034b9 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/dash.js @@ -0,0 +1,24 @@ +var parse = require('../'); +var test = require('tape'); + +test('-', function (t) { + t.plan(5); + t.deepEqual(parse([ '-n', '-' ]), { n: '-', _: [] }); + t.deepEqual(parse([ '-' ]), { _: [ '-' ] }); + t.deepEqual(parse([ '-f-' ]), { f: '-', _: [] }); + t.deepEqual( + parse([ '-b', '-' ], { boolean: 'b' }), + { b: true, _: [ '-' ] } + ); + t.deepEqual( + parse([ '-s', '-' ], { string: 's' }), + { s: '-', _: [] } + ); +}); + +test('-a -- b', function (t) { + t.plan(3); + t.deepEqual(parse([ '-a', '--', 'b' ]), { a: true, _: [ 'b' ] }); + t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] }); + t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] }); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/default_bool.js b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/default_bool.js new file mode 100644 index 0000000..f0041ee --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/default_bool.js @@ -0,0 +1,20 @@ +var test = require('tape'); +var parse = require('../'); + +test('boolean default true', function (t) { + var argv = parse([], { + boolean: 'sometrue', + default: { sometrue: true } + }); + t.equal(argv.sometrue, true); + t.end(); +}); + +test('boolean default false', function (t) { + var argv = parse([], { + boolean: 'somefalse', + default: { somefalse: false } + }); + t.equal(argv.somefalse, false); + t.end(); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/dotted.js b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/dotted.js new file mode 100644 index 0000000..ef0ae34 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/dotted.js @@ -0,0 +1,16 @@ +var parse = require('../'); +var test = require('tape'); + +test('dotted alias', function (t) { + var argv = parse(['--a.b', '22'], {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}}); + t.equal(argv.a.b, 22); + t.equal(argv.aa.bb, 22); + t.end(); +}); + +test('dotted default', function (t) { + var argv = parse('', {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}}); + t.equal(argv.a.b, 11); + t.equal(argv.aa.bb, 11); + t.end(); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/long.js b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/long.js new file mode 100644 index 0000000..5d3a1e0 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/long.js @@ -0,0 +1,31 @@ +var test = require('tape'); +var parse = require('../'); + +test('long opts', function (t) { + t.deepEqual( + parse([ '--bool' ]), + { bool : true, _ : [] }, + 'long boolean' + ); + t.deepEqual( + parse([ '--pow', 'xixxle' ]), + { pow : 'xixxle', _ : [] }, + 'long capture sp' + ); + t.deepEqual( + parse([ '--pow=xixxle' ]), + { pow : 'xixxle', _ : [] }, + 'long capture eq' + ); + t.deepEqual( + parse([ '--host', 'localhost', '--port', '555' ]), + { host : 'localhost', port : 555, _ : [] }, + 'long captures sp' + ); + t.deepEqual( + parse([ '--host=localhost', '--port=555' ]), + { host : 'localhost', port : 555, _ : [] }, + 'long captures eq' + ); + t.end(); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/parse.js b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/parse.js new file mode 100644 index 0000000..8a90646 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/parse.js @@ -0,0 +1,318 @@ +var parse = require('../'); +var test = require('tape'); + +test('parse args', function (t) { + t.deepEqual( + parse([ '--no-moo' ]), + { moo : false, _ : [] }, + 'no' + ); + t.deepEqual( + parse([ '-v', 'a', '-v', 'b', '-v', 'c' ]), + { v : ['a','b','c'], _ : [] }, + 'multi' + ); + t.end(); +}); + +test('comprehensive', function (t) { + t.deepEqual( + parse([ + '--name=meowmers', 'bare', '-cats', 'woo', + '-h', 'awesome', '--multi=quux', + '--key', 'value', + '-b', '--bool', '--no-meep', '--multi=baz', + '--', '--not-a-flag', 'eek' + ]), + { + c : true, + a : true, + t : true, + s : 'woo', + h : 'awesome', + b : true, + bool : true, + key : 'value', + multi : [ 'quux', 'baz' ], + meep : false, + name : 'meowmers', + _ : [ 'bare', '--not-a-flag', 'eek' ] + } + ); + t.end(); +}); + +test('nums', function (t) { + var argv = parse([ + '-x', '1234', + '-y', '5.67', + '-z', '1e7', + '-w', '10f', + '--hex', '0xdeadbeef', + '789' + ]); + t.deepEqual(argv, { + x : 1234, + y : 5.67, + z : 1e7, + w : '10f', + hex : 0xdeadbeef, + _ : [ 789 ] + }); + t.deepEqual(typeof argv.x, 'number'); + t.deepEqual(typeof argv.y, 'number'); + t.deepEqual(typeof argv.z, 'number'); + t.deepEqual(typeof argv.w, 'string'); + t.deepEqual(typeof argv.hex, 'number'); + t.deepEqual(typeof argv._[0], 'number'); + t.end(); +}); + +test('flag boolean', function (t) { + var argv = parse([ '-t', 'moo' ], { boolean: 't' }); + t.deepEqual(argv, { t : true, _ : [ 'moo' ] }); + t.deepEqual(typeof argv.t, 'boolean'); + t.end(); +}); + +test('flag boolean value', function (t) { + var argv = parse(['--verbose', 'false', 'moo', '-t', 'true'], { + boolean: [ 't', 'verbose' ], + default: { verbose: true } + }); + + t.deepEqual(argv, { + verbose: false, + t: true, + _: ['moo'] + }); + + t.deepEqual(typeof argv.verbose, 'boolean'); + t.deepEqual(typeof argv.t, 'boolean'); + t.end(); +}); + +test('flag boolean default false', function (t) { + var argv = parse(['moo'], { + boolean: ['t', 'verbose'], + default: { verbose: false, t: false } + }); + + t.deepEqual(argv, { + verbose: false, + t: false, + _: ['moo'] + }); + + t.deepEqual(typeof argv.verbose, 'boolean'); + t.deepEqual(typeof argv.t, 'boolean'); + t.end(); + +}); + +test('boolean groups', function (t) { + var argv = parse([ '-x', '-z', 'one', 'two', 'three' ], { + boolean: ['x','y','z'] + }); + + t.deepEqual(argv, { + x : true, + y : false, + z : true, + _ : [ 'one', 'two', 'three' ] + }); + + t.deepEqual(typeof argv.x, 'boolean'); + t.deepEqual(typeof argv.y, 'boolean'); + t.deepEqual(typeof argv.z, 'boolean'); + t.end(); +}); + +test('newlines in params' , function (t) { + var args = parse([ '-s', "X\nX" ]) + t.deepEqual(args, { _ : [], s : "X\nX" }); + + // reproduce in bash: + // VALUE="new + // line" + // node program.js --s="$VALUE" + args = parse([ "--s=X\nX" ]) + t.deepEqual(args, { _ : [], s : "X\nX" }); + t.end(); +}); + +test('strings' , function (t) { + var s = parse([ '-s', '0001234' ], { string: 's' }).s; + t.equal(s, '0001234'); + t.equal(typeof s, 'string'); + + var x = parse([ '-x', '56' ], { string: 'x' }).x; + t.equal(x, '56'); + t.equal(typeof x, 'string'); + t.end(); +}); + +test('stringArgs', function (t) { + var s = parse([ ' ', ' ' ], { string: '_' })._; + t.same(s.length, 2); + t.same(typeof s[0], 'string'); + t.same(s[0], ' '); + t.same(typeof s[1], 'string'); + t.same(s[1], ' '); + t.end(); +}); + +test('empty strings', function(t) { + var s = parse([ '-s' ], { string: 's' }).s; + t.equal(s, ''); + t.equal(typeof s, 'string'); + + var str = parse([ '--str' ], { string: 'str' }).str; + t.equal(str, ''); + t.equal(typeof str, 'string'); + + var letters = parse([ '-art' ], { + string: [ 'a', 't' ] + }); + + t.equal(letters.a, ''); + t.equal(letters.r, true); + t.equal(letters.t, ''); + + t.end(); +}); + + +test('slashBreak', function (t) { + t.same( + parse([ '-I/foo/bar/baz' ]), + { I : '/foo/bar/baz', _ : [] } + ); + t.same( + parse([ '-xyz/foo/bar/baz' ]), + { x : true, y : true, z : '/foo/bar/baz', _ : [] } + ); + t.end(); +}); + +test('alias', function (t) { + var argv = parse([ '-f', '11', '--zoom', '55' ], { + alias: { z: 'zoom' } + }); + t.equal(argv.zoom, 55); + t.equal(argv.z, argv.zoom); + t.equal(argv.f, 11); + t.end(); +}); + +test('multiAlias', function (t) { + var argv = parse([ '-f', '11', '--zoom', '55' ], { + alias: { z: [ 'zm', 'zoom' ] } + }); + t.equal(argv.zoom, 55); + t.equal(argv.z, argv.zoom); + t.equal(argv.z, argv.zm); + t.equal(argv.f, 11); + t.end(); +}); + +test('nested dotted objects', function (t) { + var argv = parse([ + '--foo.bar', '3', '--foo.baz', '4', + '--foo.quux.quibble', '5', '--foo.quux.o_O', + '--beep.boop' + ]); + + t.same(argv.foo, { + bar : 3, + baz : 4, + quux : { + quibble : 5, + o_O : true + } + }); + t.same(argv.beep, { boop : true }); + t.end(); +}); + +test('boolean and alias with chainable api', function (t) { + var aliased = [ '-h', 'derp' ]; + var regular = [ '--herp', 'derp' ]; + var opts = { + herp: { alias: 'h', boolean: true } + }; + var aliasedArgv = parse(aliased, { + boolean: 'herp', + alias: { h: 'herp' } + }); + var propertyArgv = parse(regular, { + boolean: 'herp', + alias: { h: 'herp' } + }); + var expected = { + herp: true, + h: true, + '_': [ 'derp' ] + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +test('boolean and alias with options hash', function (t) { + var aliased = [ '-h', 'derp' ]; + var regular = [ '--herp', 'derp' ]; + var opts = { + alias: { 'h': 'herp' }, + boolean: 'herp' + }; + var aliasedArgv = parse(aliased, opts); + var propertyArgv = parse(regular, opts); + var expected = { + herp: true, + h: true, + '_': [ 'derp' ] + }; + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +test('boolean and alias using explicit true', function (t) { + var aliased = [ '-h', 'true' ]; + var regular = [ '--herp', 'true' ]; + var opts = { + alias: { h: 'herp' }, + boolean: 'h' + }; + var aliasedArgv = parse(aliased, opts); + var propertyArgv = parse(regular, opts); + var expected = { + herp: true, + h: true, + '_': [ ] + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +// regression, see https://github.com/substack/node-optimist/issues/71 +test('boolean and --x=true', function(t) { + var parsed = parse(['--boool', '--other=true'], { + boolean: 'boool' + }); + + t.same(parsed.boool, true); + t.same(parsed.other, 'true'); + + parsed = parse(['--boool', '--other=false'], { + boolean: 'boool' + }); + + t.same(parsed.boool, true); + t.same(parsed.other, 'false'); + t.end(); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/parse_modified.js b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/parse_modified.js new file mode 100644 index 0000000..21851b0 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/parse_modified.js @@ -0,0 +1,9 @@ +var parse = require('../'); +var test = require('tape'); + +test('parse with modifier functions' , function (t) { + t.plan(1); + + var argv = parse([ '-b', '123' ], { boolean: 'b' }); + t.deepEqual(argv, { b: true, _: ['123'] }); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/short.js b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/short.js new file mode 100644 index 0000000..d513a1c --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/short.js @@ -0,0 +1,67 @@ +var parse = require('../'); +var test = require('tape'); + +test('numeric short args', function (t) { + t.plan(2); + t.deepEqual(parse([ '-n123' ]), { n: 123, _: [] }); + t.deepEqual( + parse([ '-123', '456' ]), + { 1: true, 2: true, 3: 456, _: [] } + ); +}); + +test('short', function (t) { + t.deepEqual( + parse([ '-b' ]), + { b : true, _ : [] }, + 'short boolean' + ); + t.deepEqual( + parse([ 'foo', 'bar', 'baz' ]), + { _ : [ 'foo', 'bar', 'baz' ] }, + 'bare' + ); + t.deepEqual( + parse([ '-cats' ]), + { c : true, a : true, t : true, s : true, _ : [] }, + 'group' + ); + t.deepEqual( + parse([ '-cats', 'meow' ]), + { c : true, a : true, t : true, s : 'meow', _ : [] }, + 'short group next' + ); + t.deepEqual( + parse([ '-h', 'localhost' ]), + { h : 'localhost', _ : [] }, + 'short capture' + ); + t.deepEqual( + parse([ '-h', 'localhost', '-p', '555' ]), + { h : 'localhost', p : 555, _ : [] }, + 'short captures' + ); + t.end(); +}); + +test('mixed short bool and capture', function (t) { + t.same( + parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]), + { + f : true, p : 555, h : 'localhost', + _ : [ 'script.js' ] + } + ); + t.end(); +}); + +test('short and long', function (t) { + t.deepEqual( + parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]), + { + f : true, p : 555, h : 'localhost', + _ : [ 'script.js' ] + } + ); + t.end(); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/whitespace.js b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/whitespace.js new file mode 100644 index 0000000..8a52a58 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/whitespace.js @@ -0,0 +1,8 @@ +var parse = require('../'); +var test = require('tape'); + +test('whitespace should be whitespace' , function (t) { + t.plan(1); + var x = parse([ '-x', '\t' ]).x; + t.equal(x, '\t'); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/package.json b/node_modules/mocha/node_modules/mkdirp/package.json new file mode 100644 index 0000000..a4015ee --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/package.json @@ -0,0 +1,57 @@ +{ + "name": "mkdirp", + "description": "Recursively mkdir, like `mkdir -p`", + "version": "0.5.0", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "main": "./index", + "keywords": [ + "mkdir", + "directory" + ], + "repository": { + "type": "git", + "url": "https://github.com/substack/node-mkdirp.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "dependencies": { + "minimist": "0.0.8" + }, + "devDependencies": { + "tap": "~0.4.0", + "mock-fs": "~2.2.0" + }, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/substack/node-mkdirp/issues" + }, + "homepage": "https://github.com/substack/node-mkdirp", + "_id": "mkdirp@0.5.0", + "dist": { + "shasum": "1d73076a6df986cd9344e15e71fcc05a4c9abf12", + "tarball": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz" + }, + "_from": "mkdirp@0.5.0", + "_npmVersion": "1.4.3", + "_npmUser": { + "name": "substack", + "email": "mail@substack.net" + }, + "maintainers": [ + { + "name": "substack", + "email": "mail@substack.net" + } + ], + "directories": {}, + "_shasum": "1d73076a6df986cd9344e15e71fcc05a4c9abf12", + "_resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz" +} diff --git a/node_modules/mocha/node_modules/mkdirp/readme.markdown b/node_modules/mocha/node_modules/mkdirp/readme.markdown new file mode 100644 index 0000000..3cc1315 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/readme.markdown @@ -0,0 +1,100 @@ +# mkdirp + +Like `mkdir -p`, but in node.js! + +[![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp) + +# example + +## pow.js + +```js +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); +``` + +Output + +``` +pow! +``` + +And now /tmp/foo/bar/baz exists, huzzah! + +# methods + +```js +var mkdirp = require('mkdirp'); +``` + +## mkdirp(dir, opts, cb) + +Create a new directory and any necessary subdirectories at `dir` with octal +permission string `opts.mode`. If `opts` is a non-object, it will be treated as +the `opts.mode`. + +If `opts.mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +`cb(err, made)` fires with the error or the first directory `made` +that had to be created, if any. + +You can optionally pass in an alternate `fs` implementation by passing in +`opts.fs`. Your implementation should have `opts.fs.mkdir(path, mode, cb)` and +`opts.fs.stat(path, cb)`. + +## mkdirp.sync(dir, opts) + +Synchronously create a new directory and any necessary subdirectories at `dir` +with octal permission string `opts.mode`. If `opts` is a non-object, it will be +treated as the `opts.mode`. + +If `opts.mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +Returns the first directory that had to be created, if any. + +You can optionally pass in an alternate `fs` implementation by passing in +`opts.fs`. Your implementation should have `opts.fs.mkdirSync(path, mode)` and +`opts.fs.statSync(path)`. + +# usage + +This package also ships with a `mkdirp` command. + +``` +usage: mkdirp [DIR1,DIR2..] {OPTIONS} + + Create each supplied directory including any necessary parent directories that + don't yet exist. + + If the directory already exists, do nothing. + +OPTIONS are: + + -m, --mode If a directory needs to be created, set the mode as an octal + permission string. + +``` + +# install + +With [npm](http://npmjs.org) do: + +``` +npm install mkdirp +``` + +to get the library, or + +``` +npm install -g mkdirp +``` + +to get the command. + +# license + +MIT diff --git a/node_modules/mocha/node_modules/mkdirp/test/chmod.js b/node_modules/mocha/node_modules/mkdirp/test/chmod.js new file mode 100644 index 0000000..520dcb8 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/chmod.js @@ -0,0 +1,38 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +test('chmod-pre', function (t) { + var mode = 0744 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.equal(stat && stat.mode & 0777, mode, 'should be 0744'); + t.end(); + }); + }); +}); + +test('chmod', function (t) { + var mode = 0755 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.end(); + }); + }); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/clobber.js b/node_modules/mocha/node_modules/mkdirp/test/clobber.js new file mode 100644 index 0000000..0eb7099 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/clobber.js @@ -0,0 +1,37 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +// a file in the way +var itw = ps.slice(0, 3).join('/'); + + +test('clobber-pre', function (t) { + console.error("about to write to "+itw) + fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.'); + + fs.stat(itw, function (er, stat) { + t.ifError(er) + t.ok(stat && stat.isFile(), 'should be file') + t.end() + }) +}) + +test('clobber', function (t) { + t.plan(2); + mkdirp(file, 0755, function (err) { + t.ok(err); + t.equal(err.code, 'ENOTDIR'); + t.end(); + }); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/mkdirp.js b/node_modules/mocha/node_modules/mkdirp/test/mkdirp.js new file mode 100644 index 0000000..3b624dd --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/mkdirp.js @@ -0,0 +1,26 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('woo', function (t) { + t.plan(5); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }) + }) + }); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/opts_fs.js b/node_modules/mocha/node_modules/mkdirp/test/opts_fs.js new file mode 100644 index 0000000..f1fbeca --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/opts_fs.js @@ -0,0 +1,27 @@ +var mkdirp = require('../'); +var path = require('path'); +var test = require('tap').test; +var mockfs = require('mock-fs'); + +test('opts.fs', function (t) { + t.plan(5); + + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/beep/boop/' + [x,y,z].join('/'); + var xfs = mockfs.fs(); + + mkdirp(file, { fs: xfs, mode: 0755 }, function (err) { + t.ifError(err); + xfs.exists(file, function (ex) { + t.ok(ex, 'created file'); + xfs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); + }); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/opts_fs_sync.js b/node_modules/mocha/node_modules/mkdirp/test/opts_fs_sync.js new file mode 100644 index 0000000..224b506 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/opts_fs_sync.js @@ -0,0 +1,25 @@ +var mkdirp = require('../'); +var path = require('path'); +var test = require('tap').test; +var mockfs = require('mock-fs'); + +test('opts.fs sync', function (t) { + t.plan(4); + + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/beep/boop/' + [x,y,z].join('/'); + var xfs = mockfs.fs(); + + mkdirp.sync(file, { fs: xfs, mode: 0755 }); + xfs.exists(file, function (ex) { + t.ok(ex, 'created file'); + xfs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/perm.js b/node_modules/mocha/node_modules/mkdirp/test/perm.js new file mode 100644 index 0000000..2c97590 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/perm.js @@ -0,0 +1,30 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('async perm', function (t) { + t.plan(5); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16); + + mkdirp(file, 0755, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }) + }) + }); +}); + +test('async root perm', function (t) { + mkdirp('/tmp', 0755, function (err) { + if (err) t.fail(err); + t.end(); + }); + t.end(); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/perm_sync.js b/node_modules/mocha/node_modules/mkdirp/test/perm_sync.js new file mode 100644 index 0000000..327e54b --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/perm_sync.js @@ -0,0 +1,34 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('sync perm', function (t) { + t.plan(4); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json'; + + mkdirp.sync(file, 0755); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); +}); + +test('sync root perm', function (t) { + t.plan(3); + + var file = '/tmp'; + mkdirp.sync(file, 0755); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.ok(stat.isDirectory(), 'target not a directory'); + }) + }); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/race.js b/node_modules/mocha/node_modules/mkdirp/test/race.js new file mode 100644 index 0000000..7c295f4 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/race.js @@ -0,0 +1,40 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('race', function (t) { + t.plan(6); + var ps = [ '', 'tmp' ]; + + for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); + } + var file = ps.join('/'); + + var res = 2; + mk(file, function () { + if (--res === 0) t.end(); + }); + + mk(file, function () { + if (--res === 0) t.end(); + }); + + function mk (file, cb) { + mkdirp(file, 0755, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + if (cb) cb(); + }); + }) + }); + } +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/rel.js b/node_modules/mocha/node_modules/mkdirp/test/rel.js new file mode 100644 index 0000000..d1f175c --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/rel.js @@ -0,0 +1,30 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('rel', function (t) { + t.plan(5); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var cwd = process.cwd(); + process.chdir('/tmp'); + + var file = [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + process.chdir(cwd); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }) + }) + }); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/return.js b/node_modules/mocha/node_modules/mkdirp/test/return.js new file mode 100644 index 0000000..bce68e5 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/return.js @@ -0,0 +1,25 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(4); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, '/tmp/' + x); + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, null); + }); + }); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/return_sync.js b/node_modules/mocha/node_modules/mkdirp/test/return_sync.js new file mode 100644 index 0000000..7c222d3 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/return_sync.js @@ -0,0 +1,24 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + // Note that this will throw on failure, which will fail the test. + var made = mkdirp.sync(file); + t.equal(made, '/tmp/' + x); + + // making the same file again should have no effect. + made = mkdirp.sync(file); + t.equal(made, null); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/root.js b/node_modules/mocha/node_modules/mkdirp/test/root.js new file mode 100644 index 0000000..97ad7a2 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/root.js @@ -0,0 +1,18 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('root', function (t) { + // '/' on unix, 'c:/' on windows. + var file = path.resolve('/'); + + mkdirp(file, 0755, function (err) { + if (err) throw err + fs.stat(file, function (er, stat) { + if (er) throw er + t.ok(stat.isDirectory(), 'target is a directory'); + t.end(); + }) + }); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/sync.js b/node_modules/mocha/node_modules/mkdirp/test/sync.js new file mode 100644 index 0000000..88fa432 --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/sync.js @@ -0,0 +1,30 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('sync', function (t) { + t.plan(4); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file, 0755); + } catch (err) { + t.fail(err); + return t.end(); + } + + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/umask.js b/node_modules/mocha/node_modules/mkdirp/test/umask.js new file mode 100644 index 0000000..82c393a --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/umask.js @@ -0,0 +1,26 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('implicit mode from umask', function (t) { + t.plan(5); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0777 & (~process.umask())); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }) + }); +}); diff --git a/node_modules/mocha/node_modules/mkdirp/test/umask_sync.js b/node_modules/mocha/node_modules/mkdirp/test/umask_sync.js new file mode 100644 index 0000000..e537fbe --- /dev/null +++ b/node_modules/mocha/node_modules/mkdirp/test/umask_sync.js @@ -0,0 +1,30 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('umask sync modes', function (t) { + t.plan(4); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file); + } catch (err) { + t.fail(err); + return t.end(); + } + + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, (0777 & (~process.umask()))); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); +}); diff --git a/node_modules/mocha/node_modules/supports-color/cli.js b/node_modules/mocha/node_modules/supports-color/cli.js new file mode 100755 index 0000000..e746987 --- /dev/null +++ b/node_modules/mocha/node_modules/supports-color/cli.js @@ -0,0 +1,29 @@ +#!/usr/bin/env node +'use strict'; +var pkg = require('./package.json'); +var supportsColor = require('./'); +var argv = process.argv.slice(2); + +function help() { + console.log([ + '', + ' ' + pkg.description, + '', + ' Usage', + ' supports-color', + '', + ' Exits with code 0 if color is supported and 1 if not' + ].join('\n')); +} + +if (argv.indexOf('--help') !== -1) { + help(); + return; +} + +if (argv.indexOf('--version') !== -1) { + console.log(pkg.version); + return; +} + +process.exit(supportsColor ? 0 : 1); diff --git a/node_modules/mocha/node_modules/supports-color/index.js b/node_modules/mocha/node_modules/supports-color/index.js new file mode 100644 index 0000000..a2b9784 --- /dev/null +++ b/node_modules/mocha/node_modules/supports-color/index.js @@ -0,0 +1,39 @@ +'use strict'; +var argv = process.argv; + +module.exports = (function () { + if (argv.indexOf('--no-color') !== -1 || + argv.indexOf('--no-colors') !== -1 || + argv.indexOf('--color=false') !== -1) { + return false; + } + + if (argv.indexOf('--color') !== -1 || + argv.indexOf('--colors') !== -1 || + argv.indexOf('--color=true') !== -1 || + argv.indexOf('--color=always') !== -1) { + return true; + } + + if (process.stdout && !process.stdout.isTTY) { + return false; + } + + if (process.platform === 'win32') { + return true; + } + + if ('COLORTERM' in process.env) { + return true; + } + + if (process.env.TERM === 'dumb') { + return false; + } + + if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) { + return true; + } + + return false; +})(); diff --git a/node_modules/mocha/node_modules/supports-color/package.json b/node_modules/mocha/node_modules/supports-color/package.json new file mode 100644 index 0000000..3143796 --- /dev/null +++ b/node_modules/mocha/node_modules/supports-color/package.json @@ -0,0 +1,84 @@ +{ + "name": "supports-color", + "version": "1.2.0", + "description": "Detect whether a terminal supports color", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/sindresorhus/supports-color" + }, + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "http://sindresorhus.com" + }, + "bin": { + "supports-color": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "mocha" + }, + "files": [ + "index.js", + "cli.js" + ], + "keywords": [ + "cli", + "bin", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "ansi", + "styles", + "tty", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "support", + "supports", + "capability", + "detect" + ], + "devDependencies": { + "mocha": "*", + "require-uncached": "^1.0.2" + }, + "gitHead": "e1815a472ebb59612e485096ae31a394e47d3c93", + "bugs": { + "url": "https://github.com/sindresorhus/supports-color/issues" + }, + "homepage": "https://github.com/sindresorhus/supports-color", + "_id": "supports-color@1.2.0", + "_shasum": "ff1ed1e61169d06b3cf2d588e188b18d8847e17e", + "_from": "supports-color@1.2.0", + "_npmVersion": "2.1.2", + "_nodeVersion": "0.10.32", + "_npmUser": { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + } + ], + "dist": { + "shasum": "ff1ed1e61169d06b3cf2d588e188b18d8847e17e", + "tarball": "http://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz" +} diff --git a/node_modules/mocha/node_modules/supports-color/readme.md b/node_modules/mocha/node_modules/supports-color/readme.md new file mode 100644 index 0000000..32d4f46 --- /dev/null +++ b/node_modules/mocha/node_modules/supports-color/readme.md @@ -0,0 +1,44 @@ +# supports-color [![Build Status](https://travis-ci.org/sindresorhus/supports-color.svg?branch=master)](https://travis-ci.org/sindresorhus/supports-color) + +> Detect whether a terminal supports color + + +## Install + +```sh +$ npm install --save supports-color +``` + + +## Usage + +```js +var supportsColor = require('supports-color'); + +if (supportsColor) { + console.log('Terminal supports color'); +} +``` + +It obeys the `--color` and `--no-color` CLI flags. + + +## CLI + +```sh +$ npm install --global supports-color +``` + +``` +$ supports-color --help + + Usage + supports-color + + Exits with code 0 if color is supported and 1 if not +``` + + +## License + +MIT © [Sindre Sorhus](http://sindresorhus.com) diff --git a/node_modules/mocha/package.json b/node_modules/mocha/package.json new file mode 100644 index 0000000..b09ba3a --- /dev/null +++ b/node_modules/mocha/package.json @@ -0,0 +1,1072 @@ +{ + "name": "mocha", + "version": "2.3.4", + "description": "simple, flexible, fun test framework", + "keywords": [ + "mocha", + "test", + "bdd", + "tdd", + "tap" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "contributors": [ + { + "name": "Travis Jeffery", + "email": "tj@travisjeffery.com" + }, + { + "name": "Christopher Hiller", + "email": "boneskull@boneskull.com" + }, + { + "name": "Joshua Appelman", + "email": "jappelman@xebia.com" + }, + { + "name": "Guillermo Rauch", + "email": "rauchg@gmail.com" + }, + { + "name": "David da Silva Contín", + "email": "dasilvacontin@gmail.com" + }, + { + "name": "Daniel St. Jules", + "email": "danielst.jules@gmail.com" + }, + { + "name": "Ariel Mashraki", + "email": "ariel@mashraki.co.il" + }, + { + "name": "Attila Domokos", + "email": "adomokos@gmail.com" + }, + { + "name": "John Firebaugh", + "email": "john.firebaugh@gmail.com" + }, + { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net" + }, + { + "name": "Jo Liss", + "email": "joliss42@gmail.com" + }, + { + "name": "Mike Pennisi", + "email": "mike@mikepennisi.com" + }, + { + "name": "Brendan Nee", + "email": "brendan.nee@gmail.com" + }, + { + "name": "James Carr", + "email": "james.r.carr@gmail.com" + }, + { + "name": "Ryunosuke SATO", + "email": "tricknotes.rs@gmail.com" + }, + { + "name": "Aaron Heckmann", + "email": "aaron.heckmann+github@gmail.com" + }, + { + "name": "Jonathan Ong", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "Forbes Lindesay", + "email": "forbes@lindesay.co.uk" + }, + { + "name": "Raynos", + "email": "raynos2@gmail.com" + }, + { + "name": "Xavier Antoviaque", + "email": "xavier@antoviaque.org" + }, + { + "name": "hokaccha", + "email": "k.hokamura@gmail.com" + }, + { + "name": "Joshua Krall", + "email": "joshuakrall@pobox.com" + }, + { + "name": "Domenic Denicola", + "email": "domenic@domenicdenicola.com" + }, + { + "name": "Glen Mailer", + "email": "glenjamin@gmail.com" + }, + { + "name": "Mathieu Desvé", + "email": "mathieudesve@MacBook-Pro-de-Mathieu.local" + }, + { + "name": "Cory Thomas", + "email": "cory.thomas@bazaarvoice.com" + }, + { + "name": "Fredrik Enestad", + "email": "fredrik@devloop.se" + }, + { + "name": "Ben Bradley", + "email": "ben@bradleyit.com" + }, + { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com" + }, + { + "name": "Jesse Dailey", + "email": "jesse.dailey@gmail.com" + }, + { + "name": "Ben Lindsey", + "email": "ben.lindsey@vungle.com" + }, + { + "name": "Maximilian Antoni", + "email": "mail@maxantoni.de" + }, + { + "name": "Merrick Christensen", + "email": "merrick.christensen@gmail.com" + }, + { + "name": "Michael Demmer", + "email": "demmer@jut.io" + }, + { + "name": "Tyson Tate", + "email": "tyson@tysontate.com" + }, + { + "name": "Valentin Agachi", + "email": "github-com@agachi.name" + }, + { + "name": "Wil Moore III", + "email": "wil.moore@wilmoore.com" + }, + { + "name": "Benjie Gillam", + "email": "benjie@jemjie.com" + }, + { + "name": "Nathan Bowser", + "email": "nathan.bowser@spiderstrategies.com" + }, + { + "name": "eiji.ienaga", + "email": "eiji.ienaga@gmail.com" + }, + { + "name": "fool2fish", + "email": "fool2fish@gmail.com" + }, + { + "name": "Paul Miller", + "email": "paul@paulmillr.com" + }, + { + "name": "Andreas Lind Petersen", + "email": "andreas@one.com" + }, + { + "name": "Timo Tijhof", + "email": "krinklemail@gmail.com" + }, + { + "name": "Nathan Alderson", + "email": "nathan.alderson@adtran.com" + }, + { + "name": "Ian Storm Taylor", + "email": "ian@ianstormtaylor.com" + }, + { + "name": "Arian Stolwijk", + "email": "arian@aryweb.nl" + }, + { + "name": "Rico Sta. Cruz", + "email": "rstacruz@users.noreply.github.com" + }, + { + "name": "domenic", + "email": "domenic@domenicdenicola.com" + }, + { + "name": "Jacob Wejendorp", + "email": "jacob@wejendorp.dk" + }, + { + "name": "fcrisci", + "email": "fabio.crisci@amadeus.com" + }, + { + "name": "Simon Gaeremynck", + "email": "gaeremyncks@gmail.com" + }, + { + "name": "James Nylen", + "email": "jnylen@gmail.com" + }, + { + "name": "Shawn Krisman", + "email": "telaviv@github" + }, + { + "name": "Sean Lang", + "email": "slang800@gmail.com" + }, + { + "name": "David Henderson", + "email": "david.henderson@triggeredmessaging.com" + }, + { + "name": "jsdevel", + "email": "js.developer.undefined@gmail.com" + }, + { + "name": "Alexander Early", + "email": "alexander.early@gmail.com" + }, + { + "name": "Parker Moore", + "email": "parkrmoore@gmail.com" + }, + { + "name": "Paul Armstrong", + "email": "paul@paularmstrongdesigns.com" + }, + { + "name": "monowerker", + "email": "monowerker@gmail.com" + }, + { + "name": "Konstantin Käfer", + "email": "github@kkaefer.com" + }, + { + "name": "Justin DuJardin", + "email": "justin.dujardin@sococo.com" + }, + { + "name": "Juzer Ali", + "email": "er.juzerali@gmail.com" + }, + { + "name": "Dominique Quatravaux", + "email": "dominique@quatravaux.org" + }, + { + "name": "Quang Van", + "email": "quangvvv@gmail.com" + }, + { + "name": "Quanlong He", + "email": "kyan.ql.he@gmail.com" + }, + { + "name": "Vlad Magdalin", + "email": "vlad@webflow.com" + }, + { + "name": "Brian Beck", + "email": "exogen@gmail.com" + }, + { + "name": "Jonas Westerlund", + "email": "jonas.westerlund@me.com" + }, + { + "name": "Michael Riley", + "email": "michael.riley@autodesk.com" + }, + { + "name": "Buck Doyle", + "email": "b@chromatin.ca" + }, + { + "name": "FARKAS Máté", + "email": "mate.farkas@virtual-call-center.eu" + }, + { + "name": "Sune Simonsen", + "email": "sune@we-knowhow.dk" + }, + { + "name": "Keith Cirkel", + "email": "github@keithcirkel.co.uk" + }, + { + "name": "Kent C. Dodds", + "email": "kent+github@doddsfamily.us" + }, + { + "name": "Kevin Conway", + "email": "kevinjacobconway@gmail.com" + }, + { + "name": "Kevin Kirsche", + "email": "Kev.Kirsche+GitHub@gmail.com" + }, + { + "name": "Kirill Korolyov", + "email": "kirill.korolyov@gmail.com" + }, + { + "name": "Koen Punt", + "email": "koen@koenpunt.nl" + }, + { + "name": "Kyle Mitchell", + "email": "kyle@kemitchell.com" + }, + { + "name": "Laszlo Bacsi", + "email": "lackac@lackac.hu" + }, + { + "name": "Liam Newman", + "email": "bitwiseman@gmail.com" + }, + { + "name": "Linus Unnebäck", + "email": "linus@folkdatorn.se" + }, + { + "name": "László Bácsi", + "email": "lackac@lackac.hu" + }, + { + "name": "Maciej Małecki", + "email": "maciej.malecki@notimplemented.org" + }, + { + "name": "Mal Graty", + "email": "mal.graty@googlemail.com" + }, + { + "name": "Marc Kuo", + "email": "kuomarc2@gmail.com" + }, + { + "name": "Marcello Bastea-Forte", + "email": "marcello@cellosoft.com" + }, + { + "name": "Martin Marko", + "email": "marcus@gratex.com" + }, + { + "name": "Matija Marohnić", + "email": "matija.marohnic@gmail.com" + }, + { + "name": "Matt Robenolt", + "email": "matt@ydekproductions.com" + }, + { + "name": "Matt Smith", + "email": "matthewgarysmith@gmail.com" + }, + { + "name": "Matthew Shanley", + "email": "matthewshanley@littlesecretsrecords.com" + }, + { + "name": "Mattias Tidlund", + "email": "mattias.tidlund@learningwell.se" + }, + { + "name": "Michael Jackson", + "email": "mjijackson@gmail.com" + }, + { + "name": "Michael Olson", + "email": "mwolson@member.fsf.org" + }, + { + "name": "Michael Schoonmaker", + "email": "michael.r.schoonmaker@gmail.com" + }, + { + "name": "Michal Charemza", + "email": "michalcharemza@gmail.com" + }, + { + "name": "Moshe Kolodny", + "email": "mkolodny@integralads.com" + }, + { + "name": "Nathan Black", + "email": "nathan@nathanblack.org" + }, + { + "name": "Nick Fitzgerald", + "email": "fitzgen@gmail.com" + }, + { + "name": "Nicolo Taddei", + "email": "taddei.uk@gmail.com" + }, + { + "name": "Noshir Patel", + "email": "nosh@blackpiano.com" + }, + { + "name": "Panu Horsmalahti", + "email": "panu.horsmalahti@iki.fi" + }, + { + "name": "Pete Hawkins", + "email": "pete@petes-imac.frontinternal.net" + }, + { + "name": "Pete Hawkins", + "email": "pete@phawk.co.uk" + }, + { + "name": "Phil Sung", + "email": "psung@dnanexus.com" + }, + { + "name": "R56", + "email": "rviskus@gmail.com" + }, + { + "name": "Raynos", + "email": "=" + }, + { + "name": "Refael Ackermann", + "email": "refael@empeeric.com" + }, + { + "name": "Richard Dingwall", + "email": "rdingwall@gmail.com" + }, + { + "name": "Richard Knop", + "email": "RichardKnop@users.noreply.github.com" + }, + { + "name": "Rob Wu", + "email": "rob@robwu.nl" + }, + { + "name": "Romain Prieto", + "email": "romain.prieto@gmail.com" + }, + { + "name": "Roman Neuhauser", + "email": "rneuhauser@suse.cz" + }, + { + "name": "Roman Shtylman", + "email": "shtylman@gmail.com" + }, + { + "name": "Russ Bradberry", + "email": "devdazed@me.com" + }, + { + "name": "Russell Munson", + "email": "rmunson@github.com" + }, + { + "name": "Rustem Mustafin", + "email": "mustafin@kt-labs.com" + }, + { + "name": "Ryan Hubbard", + "email": "ryanmhubbard@gmail.com" + }, + { + "name": "Salehen Shovon Rahman", + "email": "salehen.rahman@gmail.com" + }, + { + "name": "Sam Mussell", + "email": "smussell@gmail.com" + }, + { + "name": "Sasha Koss", + "email": "koss@nocorp.me" + }, + { + "name": "Seiya Konno", + "email": "nulltask@gmail.com" + }, + { + "name": "Shaine Hatch", + "email": "shaine@squidtree.com" + }, + { + "name": "Simon Goumaz", + "email": "simon@attentif.ch" + }, + { + "name": "Standa Opichal", + "email": "opichals@gmail.com" + }, + { + "name": "Stephen Mathieson", + "email": "smath23@gmail.com" + }, + { + "name": "Steve Mason", + "email": "stevem@brandwatch.com" + }, + { + "name": "Stewart Taylor", + "email": "stewart.taylor1@gmail.com" + }, + { + "name": "Tapiwa Kelvin", + "email": "tapiwa@munzwa.tk" + }, + { + "name": "Teddy Zeenny", + "email": "teddyzeenny@gmail.com" + }, + { + "name": "Tim Ehat", + "email": "timehat@gmail.com" + }, + { + "name": "Todd Agulnick", + "email": "tagulnick@onjack.com" + }, + { + "name": "Tom Coquereau", + "email": "tom@thau.me" + }, + { + "name": "Vadim Nikitin", + "email": "vnikiti@ncsu.edu" + }, + { + "name": "Victor Costan", + "email": "costan@gmail.com" + }, + { + "name": "Will Langstroth", + "email": "william.langstroth@gmail.com" + }, + { + "name": "Yanis Wang", + "email": "yanis.wang@gmail.com" + }, + { + "name": "Yuest Wang", + "email": "yuestwang@gmail.com" + }, + { + "name": "Zsolt Takács", + "email": "zsolt@takacs.cc" + }, + { + "name": "abrkn", + "email": "a@abrkn.com" + }, + { + "name": "airportyh", + "email": "airportyh@gmail.com" + }, + { + "name": "badunk", + "email": "baduncaduncan@gmail.com" + }, + { + "name": "claudyus", + "email": "claudyus@HEX.(none)", + "url": "none" + }, + { + "name": "dasilvacontin", + "email": "daviddasilvacontin@gmail.com" + }, + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + }, + { + "name": "gaye", + "email": "gaye@mozilla.com" + }, + { + "name": "grasGendarme", + "email": "me@grasgendar.me" + }, + { + "name": "klaemo", + "email": "klaemo@fastmail.fm" + }, + { + "name": "lakmeer", + "email": "lakmeerkravid@gmail.com" + }, + { + "name": "lodr", + "email": "salva@unoyunodiez.com" + }, + { + "name": "mrShturman", + "email": "mrshturman@gmail.com" + }, + { + "name": "nishigori", + "email": "Takuya_Nishigori@voyagegroup.com" + }, + { + "name": "omardelarosa", + "email": "thedelarosa@gmail.com" + }, + { + "name": "qiuzuhui", + "email": "qiuzuhui@users.noreply.github.com" + }, + { + "name": "samuel goldszmidt", + "email": "samuel.goldszmidt@gmail.com" + }, + { + "name": "sebv", + "email": "seb.vincent@gmail.com" + }, + { + "name": "slyg", + "email": "syl.faucherand@gmail.com" + }, + { + "name": "startswithaj", + "email": "jake.mc@icloud.com" + }, + { + "name": "tgautier@yahoo.com", + "email": "tgautier@gmail.com" + }, + { + "name": "traleig1", + "email": "darkphoenix739@gmail.com" + }, + { + "name": "vlad", + "email": "iamvlad@gmail.com" + }, + { + "name": "yuitest", + "email": "yuitest@cjhat.net" + }, + { + "name": "zhiyelee", + "email": "zhiyelee@gmail.com" + }, + { + "name": "Adam Crabtree", + "email": "adam.crabtree@redrobotlabs.com" + }, + { + "name": "Adam Gruber", + "email": "talknmime@gmail.com" + }, + { + "name": "Andreas Brekken", + "email": "andreas@opuno.com" + }, + { + "name": "Andrew Nesbitt", + "email": "andrewnez@gmail.com" + }, + { + "name": "Andrey Popp", + "email": "8mayday@gmail.com" + }, + { + "name": "Andrii Shumada", + "email": "eagleeyes91@gmail.com" + }, + { + "name": "Anis Safine", + "email": "anis.safine.ext@francetv.fr" + }, + { + "name": "Arnaud Brousseau", + "email": "arnaud.brousseau@gmail.com" + }, + { + "name": "Atsuya Takagi", + "email": "asoftonight@gmail.com" + }, + { + "name": "Austin Birch", + "email": "mraustinbirch@gmail.com" + }, + { + "name": "Ben Noordhuis", + "email": "info@bnoordhuis.nl" + }, + { + "name": "Benoît Zugmeyer", + "email": "bzugmeyer@gmail.com" + }, + { + "name": "Bjørge Næss", + "email": "bjoerge@origo.no" + }, + { + "name": "Brian Lalor", + "email": "blalor@bravo5.org" + }, + { + "name": "Brian M. Carlson", + "email": "brian.m.carlson@gmail.com" + }, + { + "name": "Brian Moore", + "email": "guardbionic-github@yahoo.com" + }, + { + "name": "Bryan Donovan", + "email": "bdondo@gmail.com" + }, + { + "name": "C. Scott Ananian", + "email": "cscott@cscott.net" + }, + { + "name": "Casey Foster", + "email": "casey@caseywebdev.com" + }, + { + "name": "Chris Buckley", + "email": "chris@cmbuckley.co.uk" + }, + { + "name": "ChrisWren", + "email": "cthewren@gmail.com" + }, + { + "name": "Connor Dunn", + "email": "connorhd@gmail.com" + }, + { + "name": "Corey Butler", + "email": "corey@coreybutler.com" + }, + { + "name": "Daniel Stockman", + "email": "daniel.stockman@gmail.com" + }, + { + "name": "Dave McKenna", + "email": "davemckenna01@gmail.com" + }, + { + "name": "Denis Bardadym", + "email": "bardadymchik@gmail.com" + }, + { + "name": "Devin Weaver", + "email": "suki@tritarget.org" + }, + { + "name": "Di Wu", + "email": "dwu@palantir.com" + }, + { + "name": "Diogo Monteiro", + "email": "diogo.gmt@gmail.com" + }, + { + "name": "Dmitry Shirokov", + "email": "deadrunk@gmail.com" + }, + { + "name": "Dominic Barnes", + "email": "dominic@dbarnes.info" + }, + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Fede Ramirez", + "email": "i@2fd.me" + }, + { + "name": "Fedor Indutny", + "email": "fedor.indutny@gmail.com" + }, + { + "name": "Florian Margaine", + "email": "florian@margaine.com" + }, + { + "name": "Frederico Silva", + "email": "frederico.silva@gmail.com" + }, + { + "name": "Fredrik Lindin", + "email": "fredriklindin@gmail.com" + }, + { + "name": "Gareth Aye", + "email": "gaye@mozilla.com" + }, + { + "name": "Gareth Murphy", + "email": "gareth.cpm@gmail.com" + }, + { + "name": "Gavin Mogan", + "email": "GavinM@airg.com" + }, + { + "name": "Giovanni Bassi", + "email": "giggio@giggio.net" + }, + { + "name": "Glen Huang", + "email": "curvedmark@gmail.com" + }, + { + "name": "Greg Perkins", + "email": "gregperkins@alum.mit.edu" + }, + { + "name": "Harish", + "email": "hyeluri@gmail.com" + }, + { + "name": "Harry Brundage", + "email": "harry.brundage@gmail.com" + }, + { + "name": "Herman Junge", + "email": "herman@geekli.st" + }, + { + "name": "Ian Young", + "email": "ian.greenleaf@gmail.com" + }, + { + "name": "Ian Zamojc", + "email": "ian@thesecretlocation.net" + }, + { + "name": "Ivan", + "email": "ivan@kinvey.com" + }, + { + "name": "JP Bochi", + "email": "jpbochi@gmail.com" + }, + { + "name": "Jaakko Salonen", + "email": "jaakko.salonen@iki.fi" + }, + { + "name": "Jake Craige", + "email": "james.craige@gmail.com" + }, + { + "name": "Jake Marsh", + "email": "jakemmarsh@gmail.com" + }, + { + "name": "Jakub Nešetřil", + "email": "jakub@apiary.io" + }, + { + "name": "James Bowes", + "email": "jbowes@repl.ca" + }, + { + "name": "James Lal", + "email": "james@lightsofapollo.com" + }, + { + "name": "Jan Kopriva", + "email": "jan.kopriva@gooddata.com" + }, + { + "name": "Jason Barry", + "email": "jay@jcbarry.com" + }, + { + "name": "Javier Aranda", + "email": "javierav@javierav.com" + }, + { + "name": "Jean Ponchon", + "email": "gelule@gmail.com" + }, + { + "name": "Jeff Kunkle", + "email": "jeff.kunkle@nearinfinity.com" + }, + { + "name": "Jeff Schilling", + "email": "jeff.schilling@q2ebanking.com" + }, + { + "name": "Jeremy Martin", + "email": "jmar777@gmail.com" + }, + { + "name": "Jimmy Cuadra", + "email": "jimmy@jimmycuadra.com" + }, + { + "name": "John Doty", + "email": "jrhdoty@gmail.com" + }, + { + "name": "Johnathon Sanders", + "email": "outdooricon@gmail.com" + }, + { + "name": "Jonas Dohse", + "email": "jonas@mbr-targeting.com" + }, + { + "name": "Jonathan Creamer", + "email": "matrixhasyou2k4@gmail.com" + }, + { + "name": "Jonathan Delgado", + "email": "jdelgado@rewip.com" + }, + { + "name": "Jonathan Park", + "email": "jpark@daptiv.com" + }, + { + "name": "Jordan Sexton", + "email": "jordan@jordansexton.com" + }, + { + "name": "Jussi Virtanen", + "email": "jussi.k.virtanen@gmail.com" + }, + { + "name": "Katie Gengler", + "email": "katiegengler@gmail.com" + }, + { + "name": "Kazuhito Hokamura", + "email": "k.hokamura@gmail.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git://github.com/mochajs/mocha.git" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "travisjeffery", + "email": "tj@travisjeffery.com" + }, + { + "name": "boneskull", + "email": "boneskull@boneskull.com" + }, + { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + } + ], + "main": "./index", + "bin": { + "mocha": "./bin/mocha", + "_mocha": "./bin/_mocha" + }, + "engines": { + "node": ">= 0.8.x" + }, + "scripts": { + "test": "make test-all" + }, + "dependencies": { + "commander": "2.3.0", + "debug": "2.2.0", + "diff": "1.4.0", + "escape-string-regexp": "1.0.2", + "glob": "3.2.3", + "growl": "1.8.1", + "jade": "0.26.3", + "mkdirp": "0.5.0", + "supports-color": "1.2.0" + }, + "devDependencies": { + "browser-stdout": "^1.2.0", + "browserify": "10.2.4", + "coffee-script": "~1.8.0", + "eslint": "^1.2.1", + "should": "~4.0.0", + "through2": "~0.6.5" + }, + "files": [ + "bin", + "images", + "lib", + "index.js", + "mocha.css", + "mocha.js", + "LICENSE" + ], + "browser": { + "debug": "./lib/browser/debug.js", + "events": "./lib/browser/events.js", + "tty": "./lib/browser/tty.js" + }, + "licenses": [ + { + "type": "MIT", + "url": "https://raw.github.com/mochajs/mocha/master/LICENSE" + } + ], + "gitHead": "c1afbeccb3b4ad27b938649ae464ae1f631533cc", + "bugs": { + "url": "https://github.com/mochajs/mocha/issues" + }, + "homepage": "https://github.com/mochajs/mocha", + "_id": "mocha@2.3.4", + "_shasum": "8629a6fb044f2d225aa4b81a2ae2d001699eb266", + "_from": "mocha@>=1.21.0", + "_npmVersion": "2.14.7", + "_nodeVersion": "4.2.1", + "_npmUser": { + "name": "travisjeffery", + "email": "tj@travisjeffery.com" + }, + "dist": { + "shasum": "8629a6fb044f2d225aa4b81a2ae2d001699eb266", + "tarball": "http://registry.npmjs.org/mocha/-/mocha-2.3.4.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/mocha/-/mocha-2.3.4.tgz" +} diff --git a/node_modules/multer/LICENSE b/node_modules/multer/LICENSE new file mode 100644 index 0000000..6c011b1 --- /dev/null +++ b/node_modules/multer/LICENSE @@ -0,0 +1,17 @@ +Copyright (c) 2014 Hage Yaapa <[http://www.hacksparrow.com](http://www.hacksparrow.com)> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/multer/README.md b/node_modules/multer/README.md new file mode 100644 index 0000000..7286182 --- /dev/null +++ b/node_modules/multer/README.md @@ -0,0 +1,323 @@ +# Multer [![Build Status](https://travis-ci.org/expressjs/multer.svg?branch=master)](https://travis-ci.org/expressjs/multer) [![NPM version](https://badge.fury.io/js/multer.svg)](https://badge.fury.io/js/multer) + +Multer is a node.js middleware for handling `multipart/form-data`. + +It is written on top of [busboy](https://github.com/mscdex/busboy) for maximum efficiency. + +## API + +#### Installation + +`$ npm install multer` + +#### Usage + +```js +var express = require('express') +var multer = require('multer') + +var app = express() +app.use(multer({ dest: './uploads/'})) +``` + +You can access the fields and files in the `request` object: + +```js +console.log(req.body) +console.log(req.files) +``` + +**IMPORTANT**: Multer will not process any form which is not `multipart/form-data`. + +## Multer file object + +A multer file object is a JSON object with the following properties. + +1. `fieldname` - Field name specified in the form +2. `originalname` - Name of the file on the user's computer +3. `name` - Renamed file name +4. `encoding` - Encoding type of the file +5. `mimetype` - Mime type of the file +6. `path` - Location of the uploaded file +7. `extension` - Extension of the file +8. `size` - Size of the file in bytes +9. `truncated` - If the file was truncated due to size limitation +10. `buffer` - Raw data (is null unless the inMemory option is true) + +## Options + +Multer accepts an options object, the most basic of which is the `dest` property, which tells Multer where to upload the files. In case you omit the options object, the file will be renamed and uploaded to the temporary directory of the system. If the `inMemory` option is true, no data is written to disk but data is kept in a buffer accessible in the file object. + +By the default, Multer will rename the files so as to avoid name conflicts. The renaming function can be customized according to your needs. + +The following are the options that can be passed to Multer. + +* `dest` +* `limits` +* `includeEmptyFields` +* `inMemory` +* `rename(fieldname, filename, req, res)` +* `changeDest(dest, req, res)` +* `onFileUploadStart(file, req, res)` +* `onFileUploadData(file, data, req, res)` +* `onFileUploadComplete(file, req, res)` +* `onParseStart()` +* `onParseEnd(req, next)` +* `onError()` +* `onFileSizeLimit(file)` +* `onFilesLimit()` +* `onFieldsLimit()` +* `onPartsLimit()` + +Apart from these, Multer also supports more advanced [busboy options](https://github.com/mscdex/busboy#busboy-methods) like `highWaterMark`, `fileHwm`, and `defCharset`. + +In an average web app, only `dest` and `rename` might be required, and configured as shown in the example. + +```js +app.use(multer({ + dest: './uploads/', + rename: function (fieldname, filename) { + return filename.replace(/\W+/g, '-').toLowerCase() + Date.now() + } +})) +``` + +The details of the properties of the options object is explained in the following sections. + +### dest + +The destination directory for the uploaded files. + +`dest: './uploads/'` + +### limits + +An object specifying the size limits of the following optional properties. This object is passed to busboy directly, and the details of properties can be found on [busboy's page](https://github.com/mscdex/busboy#busboy-methods) + +* `fieldNameSize` - integer - Max field name size (Default: 100 bytes) +* `fieldSize` - integer - Max field value size (Default: 1MB) +* `fields` - integer - Max number of non-file fields (Default: Infinity) +* `fileSize` - integer - For multipart forms, the max file size (in bytes) (Default: Infinity) +* `files` - integer - For multipart forms, the max number of file fields (Default: Infinity) +* `parts` - integer - For multipart forms, the max number of parts (fields + files) (Default: Infinity) +* `headerPairs` - integer - For multipart forms, the max number of header key=>value pairs to parse Default: 2000 (same as node's http). + +```js +limits: { + fieldNameSize: 100, + files: 2, + fields: 5 +} +``` + +Specifying the limits can help protect your site against denial of service (DoS) attacks. + +### includeEmptyFields + +A Boolean value to specify whether empty submitted values should be processed and applied to `req.body`; defaults to `false`; + +```js +includeEmptyFields: true +``` + +### putSingleFilesInArray + +**NOTE** In the next major version, `putSingleFilesInArray` will go away and all `req.files` key-value pairs will point to an array of file objects. Begin migrating your code to use `putSingleFilesInArray: true`. This will become the default in the next version. An explanation follows. + +In the current version `putSingleFilesInArray` is false. Activate it by setting the property to true. + +```js +putSingleFilesInArray: true +``` + +Some applications or libraries, such as Object Modelers, expect `req.files` key-value pairs to always point to arrays. If `putSingleFilesInArray` is true, multer will ensure all values point to an array. + +```js +// the value points to a single file object +req.files['file1'] = [fileObject1] +// the value points to an array of file objects +req.files['file1'] = [fileObject1, fileObject2] +``` + +Contrast this with Multer's default behavior, where `putSingleFilesInArray` is false. If the value for any key in `req.files` is a single file, then the value will equal a single file object. And if the value points to multiple files, then the value will equal an array of file objects. + +```js +// the value points to a single file object +req.files['file1'] = fileObject1 +// the value points to an array of file objects +req.files['file1'] = [fileObject1, fileObject2] +``` + +### inMemory + +If this Boolean value is `true`, the `file.buffer` property holds the data in-memory that Multer would have written to disk. The dest option is still populated and the path property contains the proposed path to save the file. Defaults to `false`. + +```js +inMemory: true +``` + +**WARNING**: Uploading very large files, or relatively small files in large numbers very quickly, can cause your application to run out of memory when `inMemory` is set to `true`. + +### rename(fieldname, filename, req, res) + +Function to rename the uploaded files. Whatever the function returns will become the new name of the uploaded file (extension is not included). The `fieldname` and `filename` of the file will be available in this function, use them if you need to. + +```js +rename: function (fieldname, filename, req, res) { + return fieldname + filename + Date.now() +} +``` + +Note that [req.body Warnings](#reqbody-warnings) applies to this function. + +### changeDest(dest, req, res) + +Function to rename the directory in which to place uploaded files. The `dest` parameter is the default value originally assigned or passed into multer. The `req` and `res` parameters are also passed into the function because they may contain information (eg session data) needed to create the path (eg get userid from the session). + +```js +changeDest: function(dest, req, res) { + return dest + '/user1'; +} +``` + +You might want to check that the subdirectory has been created. Here is a synchronous way to do it. The [mkdirp](https://www.npmjs.com/package/mkdirp) module can be used to automatically create nested child directories. + +```js +changeDest: function(dest, req, res) { + dest += '/user1'; + if (!fs.existsSync(dest)) fs.mkdirSync(dest); + return dest; +} +``` + +Note that [req.body Warnings](#reqbody-warnings) applies to this function. + +### onFileUploadStart(file, req, res) + +Event handler triggered when a file starts to be uploaded. A file object, with the following properties, is available to this function: `fieldname`, `originalname`, `name`, `encoding`, `mimetype`, `path`, and `extension`. + +```js +onFileUploadStart: function (file, req, res) { + console.log(file.fieldname + ' is starting ...') +} +``` + +You can even stop a file from being uploaded - just return `false` from the event handler. The file won't be processed or reach the file system. + +```js +onFileUploadStart: function (file, req, res) { + if (file.originalname == 'virus.exe') return false; +} +``` + +Note that [req.body Warnings](#reqbody-warnings) applies to this function. + +### onFileUploadData(file, data, req, res) + +Event handler triggered when a chunk of buffer is received. A buffer object along with a file object is available to the function. + +```js +onFileUploadData: function (file, data, req, res) { + console.log(data.length + ' of ' + file.fieldname + ' arrived') +} +``` + +Note that [req.body Warnings](#reqbody-warnings) applies to this function. + +### onFileUploadComplete(file, req, res) + +Event handler trigger when a file is completely uploaded. A file object is available to the function. + +```js +onFileUploadComplete: function (file, req, res) { + console.log(file.fieldname + ' uploaded to ' + file.path) +} +``` + +Note that [req.body Warnings](#reqbody-warnings) applies to this function. + +### onParseStart() + +Event handler triggered when the form parsing starts. + +```js +onParseStart: function () { + console.log('Form parsing started at: ', new Date()) +} +``` + +### onParseEnd(req, next) + +Event handler triggered when the form parsing completes. The `request` object and the `next` objects are are passed to the function. + +```js +onParseEnd: function (req, next) { + console.log('Form parsing completed at: ', new Date()); + + // usage example: custom body parse + req.body = require('qs').parse(req.body); + + // call the next middleware + next(); +} +``` + +**Note**: If you have created a `onParseEnd` event listener, you must manually call the `next()` function, else the request will be left hanging. + +### onError() + +Event handler for any errors encountering while processing the form. The `error` object and the `next` object is available to the function. If you are handling errors yourself, make sure to terminate the request or call the `next()` function, else the request will be left hanging. + +```js +onError: function (error, next) { + console.log(error) + next(error) +} +``` + +### onFileSizeLimit() + +Event handler triggered when a file size exceeds the specification in the `limit` object. No more files will be parsed after the limit is reached. + +```js +onFileSizeLimit: function (file) { + console.log('Failed: ', file.originalname) + fs.unlink('./' + file.path) // delete the partially written file +} +``` + +### onFilesLimit() + +Event handler triggered when the number of files exceed the specification in the `limit` object. No more files will be parsed after the limit is reached. + +```js +onFilesLimit: function () { + console.log('Crossed file limit!') +} +``` + +### onFieldsLimit() + +Event handler triggered when the number of fields exceed the specification in the `limit` object. No more fields will be parsed after the limit is reached. + +```js +onFieldsLimit: function () { + console.log('Crossed fields limit!') +} +``` + +### onPartsLimit() + +Event handler triggered when the number of parts exceed the specification in the `limit` object. No more files or fields will be parsed after the limit is reached. + +```js +onPartsLimit: function () { + console.log('Crossed parts limit!') +} +``` + +## req.body Warnings + +**WARNING**: `req.body` is fully parsed after file uploads have finished. Accessing `req.body` prematurely may cause errors. The `req` and `res` parameters are added to some functions to allow the developer to access properties other than `req.body`, such as session variables or socket.io objects. You have been forwarned! :) + +## [MIT Licensed](LICENSE) diff --git a/node_modules/multer/index.js b/node_modules/multer/index.js new file mode 100644 index 0000000..791490f --- /dev/null +++ b/node_modules/multer/index.js @@ -0,0 +1,222 @@ +var os = require('os'); +var fs = require('fs'); +var path = require('path'); +var crypto = require('crypto'); +var Busboy = require('busboy'); +var mkdirp = require('mkdirp'); +var is = require('type-is'); +var qs = require('qs'); + +module.exports = function(options) { + + options = options || {}; + options.includeEmptyFields = options.includeEmptyFields || false; + options.inMemory = options.inMemory || false; + options.putSingleFilesInArray = options.putSingleFilesInArray || false; + + // if the destination directory does not exist then assign uploads to the operating system's temporary directory + var dest; + + if (options.dest) { + dest = options.dest; + } else { + dest = os.tmpdir(); + } + + mkdirp(dest, function(err) { if (err) throw err; }); + + // renaming function for the destination directory + var changeDest = options.changeDest || function(dest, req, res) { + return dest; + }; + + // renaming function for the uploaded file - need not worry about the extension + // ! if you want to keep the original filename, write a renamer function which does that + var rename = options.rename || function(fieldname, filename, req, res) { + var random_string = fieldname + filename + Date.now() + Math.random(); + return crypto.createHash('md5').update(random_string).digest('hex'); + }; + + return function(req, res, next) { + + var readFinished = false; + var fileCount = 0; + + req.body = req.body || {}; + req.files = req.files || {}; + + if (is(req, ['multipart'])) { + if (options.onParseStart) { options.onParseStart(); } + + // add the request headers to the options + options.headers = req.headers; + + var busboy = new Busboy(options); + + // handle text field data + busboy.on('field', function(fieldname, val, valTruncated, keyTruncated) { + + // if includeEmptyFields is false and there is no value then don't + // attach the fields to req.body + if (!options.includeEmptyFields && !val) return; + + if (req.body.hasOwnProperty(fieldname)) { + if (Array.isArray(req.body[fieldname])) { + req.body[fieldname].push(val); + } else { + req.body[fieldname] = [req.body[fieldname], val]; + } + } else { + req.body[fieldname] = val; + } + + }); + + // handle files + busboy.on('file', function(fieldname, fileStream, filename, encoding, mimetype) { + + var ext, newFilename, newFilePath; + + // don't attach to the files object, if there is no file + if (!filename) return fileStream.resume(); + + // defines is processing a new file + fileCount++; + + if (filename.indexOf('.') > 0) { ext = '.' + filename.split('.').slice(-1)[0]; } + else { ext = ''; } + + newFilename = rename(fieldname, filename.replace(ext, ''), req, res) + ext; + newFilePath = path.join(changeDest(dest, req, res), newFilename); + + var file = { + fieldname: fieldname, + originalname: filename, + name: newFilename, + encoding: encoding, + mimetype: mimetype, + path: newFilePath, + extension: (ext === '') ? '' : ext.replace('.', ''), + size: 0, + truncated: null, + buffer: null + }; + + // trigger "file upload start" event + if (options.onFileUploadStart) { + var proceed = options.onFileUploadStart(file, req, res); + // if the onFileUploadStart handler returned null, it means we should proceed further, discard the file! + if (proceed == false) { + fileCount--; + return fileStream.resume(); + } + } + + var bufs = []; + var ws; + + if (!options.inMemory) { + ws = fs.createWriteStream(newFilePath); + fileStream.pipe(ws); + } + + fileStream.on('data', function(data) { + if (data) { + if (options.inMemory) bufs.push(data); + file.size += data.length; + } + // trigger "file data" event + if (options.onFileUploadData) { options.onFileUploadData(file, data, req, res); } + }); + + function onFileStreamEnd() { + file.truncated = fileStream.truncated; + if (!req.files[fieldname]) { req.files[fieldname] = []; } + if (options.inMemory) file.buffer = Buffer.concat(bufs, file.size); + req.files[fieldname].push(file); + + // trigger "file end" event + if (options.onFileUploadComplete) { options.onFileUploadComplete(file, req, res); } + + // defines has completed processing one more file + fileCount--; + onFinish(); + } + + if (options.inMemory) + fileStream.on('end', onFileStreamEnd); + else + ws.on('finish', onFileStreamEnd); + + fileStream.on('error', function(error) { + // trigger "file error" event + if (options.onError) { options.onError(error, next); } + else next(error); + }); + + fileStream.on('limit', function () { + if (options.onFileSizeLimit) { options.onFileSizeLimit(file); } + }); + + function onFileStreamError(error) { + // trigger "file error" event + if (options.onError) { options.onError(error, next); } + else next(error); + } + + if (options.inMemory) + fileStream.on('error', onFileStreamError ); + else + ws.on('error', onFileStreamError ); + + }); + + busboy.on('partsLimit', function() { + if (options.onPartsLimit) { options.onPartsLimit(); } + }); + + busboy.on('filesLimit', function() { + if (options.onFilesLimit) { options.onFilesLimit(); } + }); + + busboy.on('fieldsLimit', function() { + if (options.onFieldsLimit) { options.onFieldsLimit(); } + }); + + busboy.on('finish', function() { + readFinished = true; + onFinish(); + }); + + /** + * Pass the control to the next middleware in stack + * only if the read and write stream are finished + */ + var onFinish = function () { + if (!readFinished || fileCount > 0) return; + + if (!options.putSingleFilesInArray) { + for (var field in req.files) { + if (req.files[field].length === 1) { + req.files[field] = req.files[field][0]; + } + } + } + + // Parse the body and create a best structure + req.body = qs.parse(req.body); + + // when done parsing the form, pass the control to the next middleware in stack + if (options.onParseEnd) { options.onParseEnd(req, next); } + else { next(); } + }; + + req.pipe(busboy); + + } + + else { return next(); } + + } + +} diff --git a/node_modules/multer/node_modules/busboy/.travis.yml b/node_modules/multer/node_modules/busboy/.travis.yml new file mode 100644 index 0000000..28a8b69 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/.travis.yml @@ -0,0 +1,16 @@ +sudo: false +language: cpp +notifications: + email: false +env: + matrix: + - TRAVIS_NODE_VERSION="0.10" + - TRAVIS_NODE_VERSION="0.12" + - TRAVIS_NODE_VERSION="4" + - TRAVIS_NODE_VERSION="5" +install: + - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION + - node --version + - npm --version + - npm install +script: npm test diff --git a/node_modules/multer/node_modules/busboy/LICENSE b/node_modules/multer/node_modules/busboy/LICENSE new file mode 100644 index 0000000..290762e --- /dev/null +++ b/node_modules/multer/node_modules/busboy/LICENSE @@ -0,0 +1,19 @@ +Copyright Brian White. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/README.md b/node_modules/multer/node_modules/busboy/README.md new file mode 100644 index 0000000..696194b --- /dev/null +++ b/node_modules/multer/node_modules/busboy/README.md @@ -0,0 +1,217 @@ +Description +=========== + +A node.js module for parsing incoming HTML form data. + +If you've found this module to be useful and wish to support it, you may do so by visiting this pledgie campaign: +
        Click here to support busboy + + +Requirements +============ + +* [node.js](http://nodejs.org/) -- v0.8.0 or newer + + +Install +======= + + npm install busboy + + +Examples +======== + +* Parsing (multipart) with default options: + +```javascript +var http = require('http'), + inspect = require('util').inspect; + +var Busboy = require('busboy'); + +http.createServer(function(req, res) { + if (req.method === 'POST') { + var busboy = new Busboy({ headers: req.headers }); + busboy.on('file', function(fieldname, file, filename, encoding, mimetype) { + console.log('File [' + fieldname + ']: filename: ' + filename + ', encoding: ' + encoding + ', mimetype: ' + mimetype); + file.on('data', function(data) { + console.log('File [' + fieldname + '] got ' + data.length + ' bytes'); + }); + file.on('end', function() { + console.log('File [' + fieldname + '] Finished'); + }); + }); + busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated, encoding, mimetype) { + console.log('Field [' + fieldname + ']: value: ' + inspect(val)); + }); + busboy.on('finish', function() { + console.log('Done parsing form!'); + res.writeHead(303, { Connection: 'close', Location: '/' }); + res.end(); + }); + req.pipe(busboy); + } else if (req.method === 'GET') { + res.writeHead(200, { Connection: 'close' }); + res.end('\ +
        \ +
        \ +
        \ + \ +
        \ + '); + } +}).listen(8000, function() { + console.log('Listening for requests'); +}); + +// Example output, using http://nodejs.org/images/ryan-speaker.jpg as the file: +// +// Listening for requests +// File [filefield]: filename: ryan-speaker.jpg, encoding: binary +// File [filefield] got 11971 bytes +// Field [textfield]: value: 'testing! :-)' +// File [filefield] Finished +// Done parsing form! +``` + +* Save all incoming files to disk: + +```javascript +var http = require('http'), + path = require('path'), + os = require('os'), + fs = require('fs'); + +var Busboy = require('busboy'); + +http.createServer(function(req, res) { + if (req.method === 'POST') { + var busboy = new Busboy({ headers: req.headers }); + busboy.on('file', function(fieldname, file, filename, encoding, mimetype) { + var saveTo = path.join(os.tmpDir(), path.basename(fieldname)); + file.pipe(fs.createWriteStream(saveTo)); + }); + busboy.on('finish', function() { + res.writeHead(200, { 'Connection': 'close' }); + res.end("That's all folks!"); + }); + return req.pipe(busboy); + } + res.writeHead(404); + res.end(); +}).listen(8000, function() { + console.log('Listening for requests'); +}); +``` + +* Parsing (urlencoded) with default options: + +```javascript +var http = require('http'), + inspect = require('util').inspect; + +var Busboy = require('busboy'); + +http.createServer(function(req, res) { + if (req.method === 'POST') { + var busboy = new Busboy({ headers: req.headers }); + busboy.on('file', function(fieldname, file, filename, encoding, mimetype) { + console.log('File [' + fieldname + ']: filename: ' + filename); + file.on('data', function(data) { + console.log('File [' + fieldname + '] got ' + data.length + ' bytes'); + }); + file.on('end', function() { + console.log('File [' + fieldname + '] Finished'); + }); + }); + busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated) { + console.log('Field [' + fieldname + ']: value: ' + inspect(val)); + }); + busboy.on('finish', function() { + console.log('Done parsing form!'); + res.writeHead(303, { Connection: 'close', Location: '/' }); + res.end(); + }); + req.pipe(busboy); + } else if (req.method === 'GET') { + res.writeHead(200, { Connection: 'close' }); + res.end('\ +
        \ +
        \ +
        \ + Node.js rules!
        \ + \ +
        \ + '); + } +}).listen(8000, function() { + console.log('Listening for requests'); +}); + +// Example output: +// +// Listening for requests +// Field [textfield]: value: 'testing! :-)' +// Field [selectfield]: value: '9001' +// Field [checkfield]: value: 'on' +// Done parsing form! +``` + + +API +=== + +_Busboy_ is a _Writable_ stream + +Busboy (special) events +----------------------- + +* **file**(< _string_ >fieldname, < _ReadableStream_ >stream, < _string_ >filename, < _string_ >transferEncoding, < _string_ >mimeType) - Emitted for each new file form field found. `transferEncoding` contains the 'Content-Transfer-Encoding' value for the file stream. `mimeType` contains the 'Content-Type' value for the file stream. + * Note: if you listen for this event, you should always handle the `stream` no matter if you care about the file contents or not (e.g. you can simply just do `stream.resume();` if you want to discard the contents), otherwise the 'finish' event will never fire on the Busboy instance. However, if you don't care about **any** incoming files, you can simply not listen for the 'file' event at all and any/all files will be automatically and safely discarded (these discarded files do still count towards `files` and `parts` limits). + * If a configured file size limit was reached, `stream` will both have a boolean property `truncated` (best checked at the end of the stream) and emit a 'limit' event to notify you when this happens. + +* **field**(< _string_ >fieldname, < _string_ >value, < _boolean_ >fieldnameTruncated, < _boolean_ >valueTruncated, < _string_ >transferEncoding, < _string_ >mimeType) - Emitted for each new non-file field found. + +* **partsLimit**() - Emitted when specified `parts` limit has been reached. No more 'file' or 'field' events will be emitted. + +* **filesLimit**() - Emitted when specified `files` limit has been reached. No more 'file' events will be emitted. + +* **fieldsLimit**() - Emitted when specified `fields` limit has been reached. No more 'field' events will be emitted. + + +Busboy methods +-------------- + +* **(constructor)**(< _object_ >config) - Creates and returns a new Busboy instance with the following valid `config` settings: + + * **headers** - _object_ - These are the HTTP headers of the incoming request, which are used by individual parsers. + + * **highWaterMark** - _integer_ - highWaterMark to use for this Busboy instance (Default: WritableStream default). + + * **fileHwm** - _integer_ - highWaterMark to use for file streams (Default: ReadableStream default). + + * **defCharset** - _string_ - Default character set to use when one isn't defined (Default: 'utf8'). + + * **preservePath** - _boolean_ - If paths in the multipart 'filename' field shall be preserved. (Default: false). + + * **limits** - _object_ - Various limits on incoming data. Valid properties are: + + * **fieldNameSize** - _integer_ - Max field name size (in bytes) (Default: 100 bytes). + + * **fieldSize** - _integer_ - Max field value size (in bytes) (Default: 1MB). + + * **fields** - _integer_ - Max number of non-file fields (Default: Infinity). + + * **fileSize** - _integer_ - For multipart forms, the max file size (in bytes) (Default: Infinity). + + * **files** - _integer_ - For multipart forms, the max number of file fields (Default: Infinity). + + * **parts** - _integer_ - For multipart forms, the max number of parts (fields + files) (Default: Infinity). + + * **headerPairs** - _integer_ - For multipart forms, the max number of header key=>value pairs to parse **Default:** 2000 (same as node's http). diff --git a/node_modules/multer/node_modules/busboy/deps/encoding/encoding-indexes.js b/node_modules/multer/node_modules/busboy/deps/encoding/encoding-indexes.js new file mode 100644 index 0000000..1ee254d --- /dev/null +++ b/node_modules/multer/node_modules/busboy/deps/encoding/encoding-indexes.js @@ -0,0 +1,73 @@ +/* + Modifications for better node.js integration: + Copyright 2013 Brian White. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ +/* + Copyright 2012 Joshua Bell + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +module.exports = { + "big5":[null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 17392, 19506, 17923, 17830, 17784, 160359, 19831, 17843, 162993, 19682, 163013, 15253, 18230, 18244, 19527, 19520, 148159, 144919, 160594, 159371, 159954, 19543, 172881, 18255, 17882, 19589, 162924, 19719, 19108, 18081, 158499, 29221, 154196, 137827, 146950, 147297, 26189, 22267, null, 32149, 22813, 166841, 15860, 38708, 162799, 23515, 138590, 23204, 13861, 171696, 23249, 23479, 23804, 26478, 34195, 170309, 29793, 29853, 14453, 138579, 145054, 155681, 16108, 153822, 15093, 31484, 40855, 147809, 166157, 143850, 133770, 143966, 17162, 33924, 40854, 37935, 18736, 34323, 22678, 38730, 37400, 31184, 31282, 26208, 27177, 34973, 29772, 31685, 26498, 31276, 21071, 36934, 13542, 29636, 155065, 29894, 40903, 22451, 18735, 21580, 16689, 145038, 22552, 31346, 162661, 35727, 18094, 159368, 16769, 155033, 31662, 140476, 40904, 140481, 140489, 140492, 40905, 34052, 144827, 16564, 40906, 17633, 175615, 25281, 28782, 40907, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 12736, 12737, 12738, 12739, 12740, 131340, 12741, 131281, 131277, 12742, 12743, 131275, 139240, 12744, 131274, 12745, 12746, 12747, 12748, 131342, 12749, 12750, 256, 193, 461, 192, 274, 201, 282, 200, 332, 211, 465, 210, null, 7870, null, 7872, 202, 257, 225, 462, 224, 593, 275, 233, 283, 232, 299, 237, 464, 236, 333, 243, 466, 242, 363, 250, 468, 249, 470, 472, 474, 476, 252, null, 7871, null, 7873, 234, 609, 9178, 9179, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 172969, 135493, null, 25866, null, null, 20029, 28381, 40270, 37343, null, null, 161589, 25745, 20250, 20264, 20392, 20822, 20852, 20892, 20964, 21153, 21160, 21307, 21326, 21457, 21464, 22242, 22768, 22788, 22791, 22834, 22836, 23398, 23454, 23455, 23706, 24198, 24635, 25993, 26622, 26628, 26725, 27982, 28860, 30005, 32420, 32428, 32442, 32455, 32463, 32479, 32518, 32567, 33402, 33487, 33647, 35270, 35774, 35810, 36710, 36711, 36718, 29713, 31996, 32205, 26950, 31433, 21031, null, null, null, null, 37260, 30904, 37214, 32956, null, 36107, 33014, 133607, null, null, 32927, 40647, 19661, 40393, 40460, 19518, 171510, 159758, 40458, 172339, 13761, null, 28314, 33342, 29977, null, 18705, 39532, 39567, 40857, 31111, 164972, 138698, 132560, 142054, 20004, 20097, 20096, 20103, 20159, 20203, 20279, 13388, 20413, 15944, 20483, 20616, 13437, 13459, 13477, 20870, 22789, 20955, 20988, 20997, 20105, 21113, 21136, 21287, 13767, 21417, 13649, 21424, 13651, 21442, 21539, 13677, 13682, 13953, 21651, 21667, 21684, 21689, 21712, 21743, 21784, 21795, 21800, 13720, 21823, 13733, 13759, 21975, 13765, 163204, 21797, null, 134210, 134421, 151851, 21904, 142534, 14828, 131905, 36422, 150968, 169189, 16467, 164030, 30586, 142392, 14900, 18389, 164189, 158194, 151018, 25821, 134524, 135092, 134357, 135412, 25741, 36478, 134806, 134155, 135012, 142505, 164438, 148691, null, 134470, 170573, 164073, 18420, 151207, 142530, 39602, 14951, 169460, 16365, 13574, 152263, 169940, 161992, 142660, 40302, 38933, null, 17369, 155813, 25780, 21731, 142668, 142282, 135287, 14843, 135279, 157402, 157462, 162208, 25834, 151634, 134211, 36456, 139681, 166732, 132913, null, 18443, 131497, 16378, 22643, 142733, null, 148936, 132348, 155799, 134988, 134550, 21881, 16571, 17338, null, 19124, 141926, 135325, 33194, 39157, 134556, 25465, 14846, 141173, 36288, 22177, 25724, 15939, null, 173569, 134665, 142031, 142537, null, 135368, 145858, 14738, 14854, 164507, 13688, 155209, 139463, 22098, 134961, 142514, 169760, 13500, 27709, 151099, null, null, 161140, 142987, 139784, 173659, 167117, 134778, 134196, 157724, 32659, 135375, 141315, 141625, 13819, 152035, 134796, 135053, 134826, 16275, 134960, 134471, 135503, 134732, null, 134827, 134057, 134472, 135360, 135485, 16377, 140950, 25650, 135085, 144372, 161337, 142286, 134526, 134527, 142417, 142421, 14872, 134808, 135367, 134958, 173618, 158544, 167122, 167321, 167114, 38314, 21708, 33476, 21945, null, 171715, 39974, 39606, 161630, 142830, 28992, 33133, 33004, 23580, 157042, 33076, 14231, 21343, 164029, 37302, 134906, 134671, 134775, 134907, 13789, 151019, 13833, 134358, 22191, 141237, 135369, 134672, 134776, 135288, 135496, 164359, 136277, 134777, 151120, 142756, 23124, 135197, 135198, 135413, 135414, 22428, 134673, 161428, 164557, 135093, 134779, 151934, 14083, 135094, 135552, 152280, 172733, 149978, 137274, 147831, 164476, 22681, 21096, 13850, 153405, 31666, 23400, 18432, 19244, 40743, 18919, 39967, 39821, 154484, 143677, 22011, 13810, 22153, 20008, 22786, 138177, 194680, 38737, 131206, 20059, 20155, 13630, 23587, 24401, 24516, 14586, 25164, 25909, 27514, 27701, 27706, 28780, 29227, 20012, 29357, 149737, 32594, 31035, 31993, 32595, 156266, 13505, null, 156491, 32770, 32896, 157202, 158033, 21341, 34916, 35265, 161970, 35744, 36125, 38021, 38264, 38271, 38376, 167439, 38886, 39029, 39118, 39134, 39267, 170000, 40060, 40479, 40644, 27503, 63751, 20023, 131207, 38429, 25143, 38050, null, 20539, 28158, 171123, 40870, 15817, 34959, 147790, 28791, 23797, 19232, 152013, 13657, 154928, 24866, 166450, 36775, 37366, 29073, 26393, 29626, 144001, 172295, 15499, 137600, 19216, 30948, 29698, 20910, 165647, 16393, 27235, 172730, 16931, 34319, 133743, 31274, 170311, 166634, 38741, 28749, 21284, 139390, 37876, 30425, 166371, 40871, 30685, 20131, 20464, 20668, 20015, 20247, 40872, 21556, 32139, 22674, 22736, 138678, 24210, 24217, 24514, 141074, 25995, 144377, 26905, 27203, 146531, 27903, null, 29184, 148741, 29580, 16091, 150035, 23317, 29881, 35715, 154788, 153237, 31379, 31724, 31939, 32364, 33528, 34199, 40873, 34960, 40874, 36537, 40875, 36815, 34143, 39392, 37409, 40876, 167353, 136255, 16497, 17058, 23066, null, null, null, 39016, 26475, 17014, 22333, null, 34262, 149883, 33471, 160013, 19585, 159092, 23931, 158485, 159678, 40877, 40878, 23446, 40879, 26343, 32347, 28247, 31178, 15752, 17603, 143958, 141206, 17306, 17718, null, 23765, 146202, 35577, 23672, 15634, 144721, 23928, 40882, 29015, 17752, 147692, 138787, 19575, 14712, 13386, 131492, 158785, 35532, 20404, 131641, 22975, 33132, 38998, 170234, 24379, 134047, null, 139713, 166253, 16642, 18107, 168057, 16135, 40883, 172469, 16632, 14294, 18167, 158790, 16764, 165554, 160767, 17773, 14548, 152730, 17761, 17691, 19849, 19579, 19830, 17898, 16328, 150287, 13921, 17630, 17597, 16877, 23870, 23880, 23894, 15868, 14351, 23972, 23993, 14368, 14392, 24130, 24253, 24357, 24451, 14600, 14612, 14655, 14669, 24791, 24893, 23781, 14729, 25015, 25017, 25039, 14776, 25132, 25232, 25317, 25368, 14840, 22193, 14851, 25570, 25595, 25607, 25690, 14923, 25792, 23829, 22049, 40863, 14999, 25990, 15037, 26111, 26195, 15090, 26258, 15138, 26390, 15170, 26532, 26624, 15192, 26698, 26756, 15218, 15217, 15227, 26889, 26947, 29276, 26980, 27039, 27013, 15292, 27094, 15325, 27237, 27252, 27249, 27266, 15340, 27289, 15346, 27307, 27317, 27348, 27382, 27521, 27585, 27626, 27765, 27818, 15563, 27906, 27910, 27942, 28033, 15599, 28068, 28081, 28181, 28184, 28201, 28294, 166336, 28347, 28386, 28378, 40831, 28392, 28393, 28452, 28468, 15686, 147265, 28545, 28606, 15722, 15733, 29111, 23705, 15754, 28716, 15761, 28752, 28756, 28783, 28799, 28809, 131877, 17345, 13809, 134872, 147159, 22462, 159443, 28990, 153568, 13902, 27042, 166889, 23412, 31305, 153825, 169177, 31333, 31357, 154028, 31419, 31408, 31426, 31427, 29137, 156813, 16842, 31450, 31453, 31466, 16879, 21682, 154625, 31499, 31573, 31529, 152334, 154878, 31650, 31599, 33692, 154548, 158847, 31696, 33825, 31634, 31672, 154912, 15789, 154725, 33938, 31738, 31750, 31797, 154817, 31812, 31875, 149634, 31910, 26237, 148856, 31945, 31943, 31974, 31860, 31987, 31989, 31950, 32359, 17693, 159300, 32093, 159446, 29837, 32137, 32171, 28981, 32179, 32210, 147543, 155689, 32228, 15635, 32245, 137209, 32229, 164717, 32285, 155937, 155994, 32366, 32402, 17195, 37996, 32295, 32576, 32577, 32583, 31030, 156368, 39393, 32663, 156497, 32675, 136801, 131176, 17756, 145254, 17667, 164666, 32762, 156809, 32773, 32776, 32797, 32808, 32815, 172167, 158915, 32827, 32828, 32865, 141076, 18825, 157222, 146915, 157416, 26405, 32935, 166472, 33031, 33050, 22704, 141046, 27775, 156824, 151480, 25831, 136330, 33304, 137310, 27219, 150117, 150165, 17530, 33321, 133901, 158290, 146814, 20473, 136445, 34018, 33634, 158474, 149927, 144688, 137075, 146936, 33450, 26907, 194964, 16859, 34123, 33488, 33562, 134678, 137140, 14017, 143741, 144730, 33403, 33506, 33560, 147083, 159139, 158469, 158615, 144846, 15807, 33565, 21996, 33669, 17675, 159141, 33708, 33729, 33747, 13438, 159444, 27223, 34138, 13462, 159298, 143087, 33880, 154596, 33905, 15827, 17636, 27303, 33866, 146613, 31064, 33960, 158614, 159351, 159299, 34014, 33807, 33681, 17568, 33939, 34020, 154769, 16960, 154816, 17731, 34100, 23282, 159385, 17703, 34163, 17686, 26559, 34326, 165413, 165435, 34241, 159880, 34306, 136578, 159949, 194994, 17770, 34344, 13896, 137378, 21495, 160666, 34430, 34673, 172280, 34798, 142375, 34737, 34778, 34831, 22113, 34412, 26710, 17935, 34885, 34886, 161248, 146873, 161252, 34910, 34972, 18011, 34996, 34997, 25537, 35013, 30583, 161551, 35207, 35210, 35238, 35241, 35239, 35260, 166437, 35303, 162084, 162493, 35484, 30611, 37374, 35472, 162393, 31465, 162618, 147343, 18195, 162616, 29052, 35596, 35615, 152624, 152933, 35647, 35660, 35661, 35497, 150138, 35728, 35739, 35503, 136927, 17941, 34895, 35995, 163156, 163215, 195028, 14117, 163155, 36054, 163224, 163261, 36114, 36099, 137488, 36059, 28764, 36113, 150729, 16080, 36215, 36265, 163842, 135188, 149898, 15228, 164284, 160012, 31463, 36525, 36534, 36547, 37588, 36633, 36653, 164709, 164882, 36773, 37635, 172703, 133712, 36787, 18730, 166366, 165181, 146875, 24312, 143970, 36857, 172052, 165564, 165121, 140069, 14720, 159447, 36919, 165180, 162494, 36961, 165228, 165387, 37032, 165651, 37060, 165606, 37038, 37117, 37223, 15088, 37289, 37316, 31916, 166195, 138889, 37390, 27807, 37441, 37474, 153017, 37561, 166598, 146587, 166668, 153051, 134449, 37676, 37739, 166625, 166891, 28815, 23235, 166626, 166629, 18789, 37444, 166892, 166969, 166911, 37747, 37979, 36540, 38277, 38310, 37926, 38304, 28662, 17081, 140922, 165592, 135804, 146990, 18911, 27676, 38523, 38550, 16748, 38563, 159445, 25050, 38582, 30965, 166624, 38589, 21452, 18849, 158904, 131700, 156688, 168111, 168165, 150225, 137493, 144138, 38705, 34370, 38710, 18959, 17725, 17797, 150249, 28789, 23361, 38683, 38748, 168405, 38743, 23370, 168427, 38751, 37925, 20688, 143543, 143548, 38793, 38815, 38833, 38846, 38848, 38866, 38880, 152684, 38894, 29724, 169011, 38911, 38901, 168989, 162170, 19153, 38964, 38963, 38987, 39014, 15118, 160117, 15697, 132656, 147804, 153350, 39114, 39095, 39112, 39111, 19199, 159015, 136915, 21936, 39137, 39142, 39148, 37752, 39225, 150057, 19314, 170071, 170245, 39413, 39436, 39483, 39440, 39512, 153381, 14020, 168113, 170965, 39648, 39650, 170757, 39668, 19470, 39700, 39725, 165376, 20532, 39732, 158120, 14531, 143485, 39760, 39744, 171326, 23109, 137315, 39822, 148043, 39938, 39935, 39948, 171624, 40404, 171959, 172434, 172459, 172257, 172323, 172511, 40318, 40323, 172340, 40462, 26760, 40388, 139611, 172435, 172576, 137531, 172595, 40249, 172217, 172724, 40592, 40597, 40606, 40610, 19764, 40618, 40623, 148324, 40641, 15200, 14821, 15645, 20274, 14270, 166955, 40706, 40712, 19350, 37924, 159138, 40727, 40726, 40761, 22175, 22154, 40773, 39352, 168075, 38898, 33919, 40802, 40809, 31452, 40846, 29206, 19390, 149877, 149947, 29047, 150008, 148296, 150097, 29598, 166874, 137466, 31135, 166270, 167478, 37737, 37875, 166468, 37612, 37761, 37835, 166252, 148665, 29207, 16107, 30578, 31299, 28880, 148595, 148472, 29054, 137199, 28835, 137406, 144793, 16071, 137349, 152623, 137208, 14114, 136955, 137273, 14049, 137076, 137425, 155467, 14115, 136896, 22363, 150053, 136190, 135848, 136134, 136374, 34051, 145062, 34051, 33877, 149908, 160101, 146993, 152924, 147195, 159826, 17652, 145134, 170397, 159526, 26617, 14131, 15381, 15847, 22636, 137506, 26640, 16471, 145215, 147681, 147595, 147727, 158753, 21707, 22174, 157361, 22162, 135135, 134056, 134669, 37830, 166675, 37788, 20216, 20779, 14361, 148534, 20156, 132197, 131967, 20299, 20362, 153169, 23144, 131499, 132043, 14745, 131850, 132116, 13365, 20265, 131776, 167603, 131701, 35546, 131596, 20120, 20685, 20749, 20386, 20227, 150030, 147082, 20290, 20526, 20588, 20609, 20428, 20453, 20568, 20732, 20825, 20827, 20829, 20830, 28278, 144789, 147001, 147135, 28018, 137348, 147081, 20904, 20931, 132576, 17629, 132259, 132242, 132241, 36218, 166556, 132878, 21081, 21156, 133235, 21217, 37742, 18042, 29068, 148364, 134176, 149932, 135396, 27089, 134685, 29817, 16094, 29849, 29716, 29782, 29592, 19342, 150204, 147597, 21456, 13700, 29199, 147657, 21940, 131909, 21709, 134086, 22301, 37469, 38644, 37734, 22493, 22413, 22399, 13886, 22731, 23193, 166470, 136954, 137071, 136976, 23084, 22968, 37519, 23166, 23247, 23058, 153926, 137715, 137313, 148117, 14069, 27909, 29763, 23073, 155267, 23169, 166871, 132115, 37856, 29836, 135939, 28933, 18802, 37896, 166395, 37821, 14240, 23582, 23710, 24158, 24136, 137622, 137596, 146158, 24269, 23375, 137475, 137476, 14081, 137376, 14045, 136958, 14035, 33066, 166471, 138682, 144498, 166312, 24332, 24334, 137511, 137131, 23147, 137019, 23364, 34324, 161277, 34912, 24702, 141408, 140843, 24539, 16056, 140719, 140734, 168072, 159603, 25024, 131134, 131142, 140827, 24985, 24984, 24693, 142491, 142599, 149204, 168269, 25713, 149093, 142186, 14889, 142114, 144464, 170218, 142968, 25399, 173147, 25782, 25393, 25553, 149987, 142695, 25252, 142497, 25659, 25963, 26994, 15348, 143502, 144045, 149897, 144043, 21773, 144096, 137433, 169023, 26318, 144009, 143795, 15072, 16784, 152964, 166690, 152975, 136956, 152923, 152613, 30958, 143619, 137258, 143924, 13412, 143887, 143746, 148169, 26254, 159012, 26219, 19347, 26160, 161904, 138731, 26211, 144082, 144097, 26142, 153714, 14545, 145466, 145340, 15257, 145314, 144382, 29904, 15254, 26511, 149034, 26806, 26654, 15300, 27326, 14435, 145365, 148615, 27187, 27218, 27337, 27397, 137490, 25873, 26776, 27212, 15319, 27258, 27479, 147392, 146586, 37792, 37618, 166890, 166603, 37513, 163870, 166364, 37991, 28069, 28427, 149996, 28007, 147327, 15759, 28164, 147516, 23101, 28170, 22599, 27940, 30786, 28987, 148250, 148086, 28913, 29264, 29319, 29332, 149391, 149285, 20857, 150180, 132587, 29818, 147192, 144991, 150090, 149783, 155617, 16134, 16049, 150239, 166947, 147253, 24743, 16115, 29900, 29756, 37767, 29751, 17567, 159210, 17745, 30083, 16227, 150745, 150790, 16216, 30037, 30323, 173510, 15129, 29800, 166604, 149931, 149902, 15099, 15821, 150094, 16127, 149957, 149747, 37370, 22322, 37698, 166627, 137316, 20703, 152097, 152039, 30584, 143922, 30478, 30479, 30587, 149143, 145281, 14942, 149744, 29752, 29851, 16063, 150202, 150215, 16584, 150166, 156078, 37639, 152961, 30750, 30861, 30856, 30930, 29648, 31065, 161601, 153315, 16654, 31131, 33942, 31141, 27181, 147194, 31290, 31220, 16750, 136934, 16690, 37429, 31217, 134476, 149900, 131737, 146874, 137070, 13719, 21867, 13680, 13994, 131540, 134157, 31458, 23129, 141045, 154287, 154268, 23053, 131675, 30960, 23082, 154566, 31486, 16889, 31837, 31853, 16913, 154547, 155324, 155302, 31949, 150009, 137136, 31886, 31868, 31918, 27314, 32220, 32263, 32211, 32590, 156257, 155996, 162632, 32151, 155266, 17002, 158581, 133398, 26582, 131150, 144847, 22468, 156690, 156664, 149858, 32733, 31527, 133164, 154345, 154947, 31500, 155150, 39398, 34373, 39523, 27164, 144447, 14818, 150007, 157101, 39455, 157088, 33920, 160039, 158929, 17642, 33079, 17410, 32966, 33033, 33090, 157620, 39107, 158274, 33378, 33381, 158289, 33875, 159143, 34320, 160283, 23174, 16767, 137280, 23339, 137377, 23268, 137432, 34464, 195004, 146831, 34861, 160802, 23042, 34926, 20293, 34951, 35007, 35046, 35173, 35149, 153219, 35156, 161669, 161668, 166901, 166873, 166812, 166393, 16045, 33955, 18165, 18127, 14322, 35389, 35356, 169032, 24397, 37419, 148100, 26068, 28969, 28868, 137285, 40301, 35999, 36073, 163292, 22938, 30659, 23024, 17262, 14036, 36394, 36519, 150537, 36656, 36682, 17140, 27736, 28603, 140065, 18587, 28537, 28299, 137178, 39913, 14005, 149807, 37051, 37015, 21873, 18694, 37307, 37892, 166475, 16482, 166652, 37927, 166941, 166971, 34021, 35371, 38297, 38311, 38295, 38294, 167220, 29765, 16066, 149759, 150082, 148458, 16103, 143909, 38543, 167655, 167526, 167525, 16076, 149997, 150136, 147438, 29714, 29803, 16124, 38721, 168112, 26695, 18973, 168083, 153567, 38749, 37736, 166281, 166950, 166703, 156606, 37562, 23313, 35689, 18748, 29689, 147995, 38811, 38769, 39224, 134950, 24001, 166853, 150194, 38943, 169178, 37622, 169431, 37349, 17600, 166736, 150119, 166756, 39132, 166469, 16128, 37418, 18725, 33812, 39227, 39245, 162566, 15869, 39323, 19311, 39338, 39516, 166757, 153800, 27279, 39457, 23294, 39471, 170225, 19344, 170312, 39356, 19389, 19351, 37757, 22642, 135938, 22562, 149944, 136424, 30788, 141087, 146872, 26821, 15741, 37976, 14631, 24912, 141185, 141675, 24839, 40015, 40019, 40059, 39989, 39952, 39807, 39887, 171565, 39839, 172533, 172286, 40225, 19630, 147716, 40472, 19632, 40204, 172468, 172269, 172275, 170287, 40357, 33981, 159250, 159711, 158594, 34300, 17715, 159140, 159364, 159216, 33824, 34286, 159232, 145367, 155748, 31202, 144796, 144960, 18733, 149982, 15714, 37851, 37566, 37704, 131775, 30905, 37495, 37965, 20452, 13376, 36964, 152925, 30781, 30804, 30902, 30795, 137047, 143817, 149825, 13978, 20338, 28634, 28633, 28702, 28702, 21524, 147893, 22459, 22771, 22410, 40214, 22487, 28980, 13487, 147884, 29163, 158784, 151447, 23336, 137141, 166473, 24844, 23246, 23051, 17084, 148616, 14124, 19323, 166396, 37819, 37816, 137430, 134941, 33906, 158912, 136211, 148218, 142374, 148417, 22932, 146871, 157505, 32168, 155995, 155812, 149945, 149899, 166394, 37605, 29666, 16105, 29876, 166755, 137375, 16097, 150195, 27352, 29683, 29691, 16086, 150078, 150164, 137177, 150118, 132007, 136228, 149989, 29768, 149782, 28837, 149878, 37508, 29670, 37727, 132350, 37681, 166606, 166422, 37766, 166887, 153045, 18741, 166530, 29035, 149827, 134399, 22180, 132634, 134123, 134328, 21762, 31172, 137210, 32254, 136898, 150096, 137298, 17710, 37889, 14090, 166592, 149933, 22960, 137407, 137347, 160900, 23201, 14050, 146779, 14000, 37471, 23161, 166529, 137314, 37748, 15565, 133812, 19094, 14730, 20724, 15721, 15692, 136092, 29045, 17147, 164376, 28175, 168164, 17643, 27991, 163407, 28775, 27823, 15574, 147437, 146989, 28162, 28428, 15727, 132085, 30033, 14012, 13512, 18048, 16090, 18545, 22980, 37486, 18750, 36673, 166940, 158656, 22546, 22472, 14038, 136274, 28926, 148322, 150129, 143331, 135856, 140221, 26809, 26983, 136088, 144613, 162804, 145119, 166531, 145366, 144378, 150687, 27162, 145069, 158903, 33854, 17631, 17614, 159014, 159057, 158850, 159710, 28439, 160009, 33597, 137018, 33773, 158848, 159827, 137179, 22921, 23170, 137139, 23137, 23153, 137477, 147964, 14125, 23023, 137020, 14023, 29070, 37776, 26266, 148133, 23150, 23083, 148115, 27179, 147193, 161590, 148571, 148170, 28957, 148057, 166369, 20400, 159016, 23746, 148686, 163405, 148413, 27148, 148054, 135940, 28838, 28979, 148457, 15781, 27871, 194597, 150095, 32357, 23019, 23855, 15859, 24412, 150109, 137183, 32164, 33830, 21637, 146170, 144128, 131604, 22398, 133333, 132633, 16357, 139166, 172726, 28675, 168283, 23920, 29583, 31955, 166489, 168992, 20424, 32743, 29389, 29456, 162548, 29496, 29497, 153334, 29505, 29512, 16041, 162584, 36972, 29173, 149746, 29665, 33270, 16074, 30476, 16081, 27810, 22269, 29721, 29726, 29727, 16098, 16112, 16116, 16122, 29907, 16142, 16211, 30018, 30061, 30066, 30093, 16252, 30152, 30172, 16320, 30285, 16343, 30324, 16348, 30330, 151388, 29064, 22051, 35200, 22633, 16413, 30531, 16441, 26465, 16453, 13787, 30616, 16490, 16495, 23646, 30654, 30667, 22770, 30744, 28857, 30748, 16552, 30777, 30791, 30801, 30822, 33864, 152885, 31027, 26627, 31026, 16643, 16649, 31121, 31129, 36795, 31238, 36796, 16743, 31377, 16818, 31420, 33401, 16836, 31439, 31451, 16847, 20001, 31586, 31596, 31611, 31762, 31771, 16992, 17018, 31867, 31900, 17036, 31928, 17044, 31981, 36755, 28864, 134351, 32207, 32212, 32208, 32253, 32686, 32692, 29343, 17303, 32800, 32805, 31545, 32814, 32817, 32852, 15820, 22452, 28832, 32951, 33001, 17389, 33036, 29482, 33038, 33042, 30048, 33044, 17409, 15161, 33110, 33113, 33114, 17427, 22586, 33148, 33156, 17445, 33171, 17453, 33189, 22511, 33217, 33252, 33364, 17551, 33446, 33398, 33482, 33496, 33535, 17584, 33623, 38505, 27018, 33797, 28917, 33892, 24803, 33928, 17668, 33982, 34017, 34040, 34064, 34104, 34130, 17723, 34159, 34160, 34272, 17783, 34418, 34450, 34482, 34543, 38469, 34699, 17926, 17943, 34990, 35071, 35108, 35143, 35217, 162151, 35369, 35384, 35476, 35508, 35921, 36052, 36082, 36124, 18328, 22623, 36291, 18413, 20206, 36410, 21976, 22356, 36465, 22005, 36528, 18487, 36558, 36578, 36580, 36589, 36594, 36791, 36801, 36810, 36812, 36915, 39364, 18605, 39136, 37395, 18718, 37416, 37464, 37483, 37553, 37550, 37567, 37603, 37611, 37619, 37620, 37629, 37699, 37764, 37805, 18757, 18769, 40639, 37911, 21249, 37917, 37933, 37950, 18794, 37972, 38009, 38189, 38306, 18855, 38388, 38451, 18917, 26528, 18980, 38720, 18997, 38834, 38850, 22100, 19172, 24808, 39097, 19225, 39153, 22596, 39182, 39193, 20916, 39196, 39223, 39234, 39261, 39266, 19312, 39365, 19357, 39484, 39695, 31363, 39785, 39809, 39901, 39921, 39924, 19565, 39968, 14191, 138178, 40265, 39994, 40702, 22096, 40339, 40381, 40384, 40444, 38134, 36790, 40571, 40620, 40625, 40637, 40646, 38108, 40674, 40689, 40696, 31432, 40772, 131220, 131767, 132000, 26906, 38083, 22956, 132311, 22592, 38081, 14265, 132565, 132629, 132726, 136890, 22359, 29043, 133826, 133837, 134079, 21610, 194619, 134091, 21662, 134139, 134203, 134227, 134245, 134268, 24807, 134285, 22138, 134325, 134365, 134381, 134511, 134578, 134600, 26965, 39983, 34725, 134660, 134670, 134871, 135056, 134957, 134771, 23584, 135100, 24075, 135260, 135247, 135286, 26398, 135291, 135304, 135318, 13895, 135359, 135379, 135471, 135483, 21348, 33965, 135907, 136053, 135990, 35713, 136567, 136729, 137155, 137159, 20088, 28859, 137261, 137578, 137773, 137797, 138282, 138352, 138412, 138952, 25283, 138965, 139029, 29080, 26709, 139333, 27113, 14024, 139900, 140247, 140282, 141098, 141425, 141647, 33533, 141671, 141715, 142037, 35237, 142056, 36768, 142094, 38840, 142143, 38983, 39613, 142412, null, 142472, 142519, 154600, 142600, 142610, 142775, 142741, 142914, 143220, 143308, 143411, 143462, 144159, 144350, 24497, 26184, 26303, 162425, 144743, 144883, 29185, 149946, 30679, 144922, 145174, 32391, 131910, 22709, 26382, 26904, 146087, 161367, 155618, 146961, 147129, 161278, 139418, 18640, 19128, 147737, 166554, 148206, 148237, 147515, 148276, 148374, 150085, 132554, 20946, 132625, 22943, 138920, 15294, 146687, 148484, 148694, 22408, 149108, 14747, 149295, 165352, 170441, 14178, 139715, 35678, 166734, 39382, 149522, 149755, 150037, 29193, 150208, 134264, 22885, 151205, 151430, 132985, 36570, 151596, 21135, 22335, 29041, 152217, 152601, 147274, 150183, 21948, 152646, 152686, 158546, 37332, 13427, 152895, 161330, 152926, 18200, 152930, 152934, 153543, 149823, 153693, 20582, 13563, 144332, 24798, 153859, 18300, 166216, 154286, 154505, 154630, 138640, 22433, 29009, 28598, 155906, 162834, 36950, 156082, 151450, 35682, 156674, 156746, 23899, 158711, 36662, 156804, 137500, 35562, 150006, 156808, 147439, 156946, 19392, 157119, 157365, 141083, 37989, 153569, 24981, 23079, 194765, 20411, 22201, 148769, 157436, 20074, 149812, 38486, 28047, 158909, 13848, 35191, 157593, 157806, 156689, 157790, 29151, 157895, 31554, 168128, 133649, 157990, 37124, 158009, 31301, 40432, 158202, 39462, 158253, 13919, 156777, 131105, 31107, 158260, 158555, 23852, 144665, 33743, 158621, 18128, 158884, 30011, 34917, 159150, 22710, 14108, 140685, 159819, 160205, 15444, 160384, 160389, 37505, 139642, 160395, 37680, 160486, 149968, 27705, 38047, 160848, 134904, 34855, 35061, 141606, 164979, 137137, 28344, 150058, 137248, 14756, 14009, 23568, 31203, 17727, 26294, 171181, 170148, 35139, 161740, 161880, 22230, 16607, 136714, 14753, 145199, 164072, 136133, 29101, 33638, 162269, 168360, 23143, 19639, 159919, 166315, 162301, 162314, 162571, 163174, 147834, 31555, 31102, 163849, 28597, 172767, 27139, 164632, 21410, 159239, 37823, 26678, 38749, 164207, 163875, 158133, 136173, 143919, 163912, 23941, 166960, 163971, 22293, 38947, 166217, 23979, 149896, 26046, 27093, 21458, 150181, 147329, 15377, 26422, 163984, 164084, 164142, 139169, 164175, 164233, 164271, 164378, 164614, 164655, 164746, 13770, 164968, 165546, 18682, 25574, 166230, 30728, 37461, 166328, 17394, 166375, 17375, 166376, 166726, 166868, 23032, 166921, 36619, 167877, 168172, 31569, 168208, 168252, 15863, 168286, 150218, 36816, 29327, 22155, 169191, 169449, 169392, 169400, 169778, 170193, 170313, 170346, 170435, 170536, 170766, 171354, 171419, 32415, 171768, 171811, 19620, 38215, 172691, 29090, 172799, 19857, 36882, 173515, 19868, 134300, 36798, 21953, 36794, 140464, 36793, 150163, 17673, 32383, 28502, 27313, 20202, 13540, 166700, 161949, 14138, 36480, 137205, 163876, 166764, 166809, 162366, 157359, 15851, 161365, 146615, 153141, 153942, 20122, 155265, 156248, 22207, 134765, 36366, 23405, 147080, 150686, 25566, 25296, 137206, 137339, 25904, 22061, 154698, 21530, 152337, 15814, 171416, 19581, 22050, 22046, 32585, 155352, 22901, 146752, 34672, 19996, 135146, 134473, 145082, 33047, 40286, 36120, 30267, 40005, 30286, 30649, 37701, 21554, 33096, 33527, 22053, 33074, 33816, 32957, 21994, 31074, 22083, 21526, 134813, 13774, 22021, 22001, 26353, 164578, 13869, 30004, 22000, 21946, 21655, 21874, 134209, 134294, 24272, 151880, 134774, 142434, 134818, 40619, 32090, 21982, 135285, 25245, 38765, 21652, 36045, 29174, 37238, 25596, 25529, 25598, 21865, 142147, 40050, 143027, 20890, 13535, 134567, 20903, 21581, 21790, 21779, 30310, 36397, 157834, 30129, 32950, 34820, 34694, 35015, 33206, 33820, 135361, 17644, 29444, 149254, 23440, 33547, 157843, 22139, 141044, 163119, 147875, 163187, 159440, 160438, 37232, 135641, 37384, 146684, 173737, 134828, 134905, 29286, 138402, 18254, 151490, 163833, 135147, 16634, 40029, 25887, 142752, 18675, 149472, 171388, 135148, 134666, 24674, 161187, 135149, null, 155720, 135559, 29091, 32398, 40272, 19994, 19972, 13687, 23309, 27826, 21351, 13996, 14812, 21373, 13989, 149016, 22682, 150382, 33325, 21579, 22442, 154261, 133497, null, 14930, 140389, 29556, 171692, 19721, 39917, 146686, 171824, 19547, 151465, 169374, 171998, 33884, 146870, 160434, 157619, 145184, 25390, 32037, 147191, 146988, 14890, 36872, 21196, 15988, 13946, 17897, 132238, 30272, 23280, 134838, 30842, 163630, 22695, 16575, 22140, 39819, 23924, 30292, 173108, 40581, 19681, 30201, 14331, 24857, 143578, 148466, null, 22109, 135849, 22439, 149859, 171526, 21044, 159918, 13741, 27722, 40316, 31830, 39737, 22494, 137068, 23635, 25811, 169168, 156469, 160100, 34477, 134440, 159010, 150242, 134513, null, 20990, 139023, 23950, 38659, 138705, 40577, 36940, 31519, 39682, 23761, 31651, 25192, 25397, 39679, 31695, 39722, 31870, 39726, 31810, 31878, 39957, 31740, 39689, 40727, 39963, 149822, 40794, 21875, 23491, 20477, 40600, 20466, 21088, 15878, 21201, 22375, 20566, 22967, 24082, 38856, 40363, 36700, 21609, 38836, 39232, 38842, 21292, 24880, 26924, 21466, 39946, 40194, 19515, 38465, 27008, 20646, 30022, 137069, 39386, 21107, null, 37209, 38529, 37212, null, 37201, 167575, 25471, 159011, 27338, 22033, 37262, 30074, 25221, 132092, 29519, 31856, 154657, 146685, null, 149785, 30422, 39837, 20010, 134356, 33726, 34882, null, 23626, 27072, 20717, 22394, 21023, 24053, 20174, 27697, 131570, 20281, 21660, 21722, 21146, 36226, 13822, 24332, 13811, null, 27474, 37244, 40869, 39831, 38958, 39092, 39610, 40616, 40580, 29050, 31508, null, 27642, 34840, 32632, null, 22048, 173642, 36471, 40787, null, 36308, 36431, 40476, 36353, 25218, 164733, 36392, 36469, 31443, 150135, 31294, 30936, 27882, 35431, 30215, 166490, 40742, 27854, 34774, 30147, 172722, 30803, 194624, 36108, 29410, 29553, 35629, 29442, 29937, 36075, 150203, 34351, 24506, 34976, 17591, null, 137275, 159237, null, 35454, 140571, null, 24829, 30311, 39639, 40260, 37742, 39823, 34805, null, 34831, 36087, 29484, 38689, 39856, 13782, 29362, 19463, 31825, 39242, 155993, 24921, 19460, 40598, 24957, null, 22367, 24943, 25254, 25145, 25294, 14940, 25058, 21418, 144373, 25444, 26626, 13778, 23895, 166850, 36826, 167481, null, 20697, 138566, 30982, 21298, 38456, 134971, 16485, null, 30718, null, 31938, 155418, 31962, 31277, 32870, 32867, 32077, 29957, 29938, 35220, 33306, 26380, 32866, 160902, 32859, 29936, 33027, 30500, 35209, 157644, 30035, 159441, 34729, 34766, 33224, 34700, 35401, 36013, 35651, 30507, 29944, 34010, 13877, 27058, 36262, null, 35241, 29800, 28089, 34753, 147473, 29927, 15835, 29046, 24740, 24988, 15569, 29026, 24695, null, 32625, 166701, 29264, 24809, 19326, 21024, 15384, 146631, 155351, 161366, 152881, 137540, 135934, 170243, 159196, 159917, 23745, 156077, 166415, 145015, 131310, 157766, 151310, 17762, 23327, 156492, 40784, 40614, 156267, 12288, 65292, 12289, 12290, 65294, 8231, 65307, 65306, 65311, 65281, 65072, 8230, 8229, 65104, 65105, 65106, 183, 65108, 65109, 65110, 65111, 65372, 8211, 65073, 8212, 65075, 9588, 65076, 65103, 65288, 65289, 65077, 65078, 65371, 65373, 65079, 65080, 12308, 12309, 65081, 65082, 12304, 12305, 65083, 65084, 12298, 12299, 65085, 65086, 12296, 12297, 65087, 65088, 12300, 12301, 65089, 65090, 12302, 12303, 65091, 65092, 65113, 65114, 65115, 65116, 65117, 65118, 8216, 8217, 8220, 8221, 12317, 12318, 8245, 8242, 65283, 65286, 65290, 8251, 167, 12291, 9675, 9679, 9651, 9650, 9678, 9734, 9733, 9671, 9670, 9633, 9632, 9661, 9660, 12963, 8453, 175, 65507, 65343, 717, 65097, 65098, 65101, 65102, 65099, 65100, 65119, 65120, 65121, 65291, 65293, 215, 247, 177, 8730, 65308, 65310, 65309, 8806, 8807, 8800, 8734, 8786, 8801, 65122, 65123, 65124, 65125, 65126, 65374, 8745, 8746, 8869, 8736, 8735, 8895, 13266, 13265, 8747, 8750, 8757, 8756, 9792, 9794, 8853, 8857, 8593, 8595, 8592, 8594, 8598, 8599, 8601, 8600, 8741, 8739, 65295, 65340, 8725, 65128, 65284, 65509, 12306, 65504, 65505, 65285, 65312, 8451, 8457, 65129, 65130, 65131, 13269, 13212, 13213, 13214, 13262, 13217, 13198, 13199, 13252, 176, 20825, 20827, 20830, 20829, 20833, 20835, 21991, 29929, 31950, 9601, 9602, 9603, 9604, 9605, 9606, 9607, 9608, 9615, 9614, 9613, 9612, 9611, 9610, 9609, 9532, 9524, 9516, 9508, 9500, 9620, 9472, 9474, 9621, 9484, 9488, 9492, 9496, 9581, 9582, 9584, 9583, 9552, 9566, 9578, 9569, 9698, 9699, 9701, 9700, 9585, 9586, 9587, 65296, 65297, 65298, 65299, 65300, 65301, 65302, 65303, 65304, 65305, 8544, 8545, 8546, 8547, 8548, 8549, 8550, 8551, 8552, 8553, 12321, 12322, 12323, 12324, 12325, 12326, 12327, 12328, 12329, 21313, 21316, 21317, 65313, 65314, 65315, 65316, 65317, 65318, 65319, 65320, 65321, 65322, 65323, 65324, 65325, 65326, 65327, 65328, 65329, 65330, 65331, 65332, 65333, 65334, 65335, 65336, 65337, 65338, 65345, 65346, 65347, 65348, 65349, 65350, 65351, 65352, 65353, 65354, 65355, 65356, 65357, 65358, 65359, 65360, 65361, 65362, 65363, 65364, 65365, 65366, 65367, 65368, 65369, 65370, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 931, 932, 933, 934, 935, 936, 937, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 963, 964, 965, 966, 967, 968, 969, 12549, 12550, 12551, 12552, 12553, 12554, 12555, 12556, 12557, 12558, 12559, 12560, 12561, 12562, 12563, 12564, 12565, 12566, 12567, 12568, 12569, 12570, 12571, 12572, 12573, 12574, 12575, 12576, 12577, 12578, 12579, 12580, 12581, 12582, 12583, 12584, 12585, 729, 713, 714, 711, 715, 9216, 9217, 9218, 9219, 9220, 9221, 9222, 9223, 9224, 9225, 9226, 9227, 9228, 9229, 9230, 9231, 9232, 9233, 9234, 9235, 9236, 9237, 9238, 9239, 9240, 9241, 9242, 9243, 9244, 9245, 9246, 9247, 9249, 8364, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 19968, 20057, 19969, 19971, 20035, 20061, 20102, 20108, 20154, 20799, 20837, 20843, 20960, 20992, 20993, 21147, 21269, 21313, 21340, 21448, 19977, 19979, 19976, 19978, 20011, 20024, 20961, 20037, 20040, 20063, 20062, 20110, 20129, 20800, 20995, 21242, 21315, 21449, 21475, 22303, 22763, 22805, 22823, 22899, 23376, 23377, 23379, 23544, 23567, 23586, 23608, 23665, 24029, 24037, 24049, 24050, 24051, 24062, 24178, 24318, 24331, 24339, 25165, 19985, 19984, 19981, 20013, 20016, 20025, 20043, 23609, 20104, 20113, 20117, 20114, 20116, 20130, 20161, 20160, 20163, 20166, 20167, 20173, 20170, 20171, 20164, 20803, 20801, 20839, 20845, 20846, 20844, 20887, 20982, 20998, 20999, 21000, 21243, 21246, 21247, 21270, 21305, 21320, 21319, 21317, 21342, 21380, 21451, 21450, 21453, 22764, 22825, 22827, 22826, 22829, 23380, 23569, 23588, 23610, 23663, 24052, 24187, 24319, 24340, 24341, 24515, 25096, 25142, 25163, 25166, 25903, 25991, 26007, 26020, 26041, 26085, 26352, 26376, 26408, 27424, 27490, 27513, 27595, 27604, 27611, 27663, 27700, 28779, 29226, 29238, 29243, 29255, 29273, 29275, 29356, 29579, 19993, 19990, 19989, 19988, 19992, 20027, 20045, 20047, 20046, 20197, 20184, 20180, 20181, 20182, 20183, 20195, 20196, 20185, 20190, 20805, 20804, 20873, 20874, 20908, 20985, 20986, 20984, 21002, 21152, 21151, 21253, 21254, 21271, 21277, 20191, 21322, 21321, 21345, 21344, 21359, 21358, 21435, 21487, 21476, 21491, 21484, 21486, 21481, 21480, 21500, 21496, 21493, 21483, 21478, 21482, 21490, 21489, 21488, 21477, 21485, 21499, 22235, 22234, 22806, 22830, 22833, 22900, 22902, 23381, 23427, 23612, 24040, 24039, 24038, 24066, 24067, 24179, 24188, 24321, 24344, 24343, 24517, 25098, 25171, 25172, 25170, 25169, 26021, 26086, 26414, 26412, 26410, 26411, 26413, 27491, 27597, 27665, 27664, 27704, 27713, 27712, 27710, 29359, 29572, 29577, 29916, 29926, 29976, 29983, 29992, 29993, 30000, 30001, 30002, 30003, 30091, 30333, 30382, 30399, 30446, 30683, 30690, 30707, 31034, 31166, 31348, 31435, 19998, 19999, 20050, 20051, 20073, 20121, 20132, 20134, 20133, 20223, 20233, 20249, 20234, 20245, 20237, 20240, 20241, 20239, 20210, 20214, 20219, 20208, 20211, 20221, 20225, 20235, 20809, 20807, 20806, 20808, 20840, 20849, 20877, 20912, 21015, 21009, 21010, 21006, 21014, 21155, 21256, 21281, 21280, 21360, 21361, 21513, 21519, 21516, 21514, 21520, 21505, 21515, 21508, 21521, 21517, 21512, 21507, 21518, 21510, 21522, 22240, 22238, 22237, 22323, 22320, 22312, 22317, 22316, 22319, 22313, 22809, 22810, 22839, 22840, 22916, 22904, 22915, 22909, 22905, 22914, 22913, 23383, 23384, 23431, 23432, 23429, 23433, 23546, 23574, 23673, 24030, 24070, 24182, 24180, 24335, 24347, 24537, 24534, 25102, 25100, 25101, 25104, 25187, 25179, 25176, 25910, 26089, 26088, 26092, 26093, 26354, 26355, 26377, 26429, 26420, 26417, 26421, 27425, 27492, 27515, 27670, 27741, 27735, 27737, 27743, 27744, 27728, 27733, 27745, 27739, 27725, 27726, 28784, 29279, 29277, 30334, 31481, 31859, 31992, 32566, 32650, 32701, 32769, 32771, 32780, 32786, 32819, 32895, 32905, 32907, 32908, 33251, 33258, 33267, 33276, 33292, 33307, 33311, 33390, 33394, 33406, 34411, 34880, 34892, 34915, 35199, 38433, 20018, 20136, 20301, 20303, 20295, 20311, 20318, 20276, 20315, 20309, 20272, 20304, 20305, 20285, 20282, 20280, 20291, 20308, 20284, 20294, 20323, 20316, 20320, 20271, 20302, 20278, 20313, 20317, 20296, 20314, 20812, 20811, 20813, 20853, 20918, 20919, 21029, 21028, 21033, 21034, 21032, 21163, 21161, 21162, 21164, 21283, 21363, 21365, 21533, 21549, 21534, 21566, 21542, 21582, 21543, 21574, 21571, 21555, 21576, 21570, 21531, 21545, 21578, 21561, 21563, 21560, 21550, 21557, 21558, 21536, 21564, 21568, 21553, 21547, 21535, 21548, 22250, 22256, 22244, 22251, 22346, 22353, 22336, 22349, 22343, 22350, 22334, 22352, 22351, 22331, 22767, 22846, 22941, 22930, 22952, 22942, 22947, 22937, 22934, 22925, 22948, 22931, 22922, 22949, 23389, 23388, 23386, 23387, 23436, 23435, 23439, 23596, 23616, 23617, 23615, 23614, 23696, 23697, 23700, 23692, 24043, 24076, 24207, 24199, 24202, 24311, 24324, 24351, 24420, 24418, 24439, 24441, 24536, 24524, 24535, 24525, 24561, 24555, 24568, 24554, 25106, 25105, 25220, 25239, 25238, 25216, 25206, 25225, 25197, 25226, 25212, 25214, 25209, 25203, 25234, 25199, 25240, 25198, 25237, 25235, 25233, 25222, 25913, 25915, 25912, 26097, 26356, 26463, 26446, 26447, 26448, 26449, 26460, 26454, 26462, 26441, 26438, 26464, 26451, 26455, 27493, 27599, 27714, 27742, 27801, 27777, 27784, 27785, 27781, 27803, 27754, 27770, 27792, 27760, 27788, 27752, 27798, 27794, 27773, 27779, 27762, 27774, 27764, 27782, 27766, 27789, 27796, 27800, 27778, 28790, 28796, 28797, 28792, 29282, 29281, 29280, 29380, 29378, 29590, 29996, 29995, 30007, 30008, 30338, 30447, 30691, 31169, 31168, 31167, 31350, 31995, 32597, 32918, 32915, 32925, 32920, 32923, 32922, 32946, 33391, 33426, 33419, 33421, 35211, 35282, 35328, 35895, 35910, 35925, 35997, 36196, 36208, 36275, 36523, 36554, 36763, 36784, 36802, 36806, 36805, 36804, 24033, 37009, 37026, 37034, 37030, 37027, 37193, 37318, 37324, 38450, 38446, 38449, 38442, 38444, 20006, 20054, 20083, 20107, 20123, 20126, 20139, 20140, 20335, 20381, 20365, 20339, 20351, 20332, 20379, 20363, 20358, 20355, 20336, 20341, 20360, 20329, 20347, 20374, 20350, 20367, 20369, 20346, 20820, 20818, 20821, 20841, 20855, 20854, 20856, 20925, 20989, 21051, 21048, 21047, 21050, 21040, 21038, 21046, 21057, 21182, 21179, 21330, 21332, 21331, 21329, 21350, 21367, 21368, 21369, 21462, 21460, 21463, 21619, 21621, 21654, 21624, 21653, 21632, 21627, 21623, 21636, 21650, 21638, 21628, 21648, 21617, 21622, 21644, 21658, 21602, 21608, 21643, 21629, 21646, 22266, 22403, 22391, 22378, 22377, 22369, 22374, 22372, 22396, 22812, 22857, 22855, 22856, 22852, 22868, 22974, 22971, 22996, 22969, 22958, 22993, 22982, 22992, 22989, 22987, 22995, 22986, 22959, 22963, 22994, 22981, 23391, 23396, 23395, 23447, 23450, 23448, 23452, 23449, 23451, 23578, 23624, 23621, 23622, 23735, 23713, 23736, 23721, 23723, 23729, 23731, 24088, 24090, 24086, 24085, 24091, 24081, 24184, 24218, 24215, 24220, 24213, 24214, 24310, 24358, 24359, 24361, 24448, 24449, 24447, 24444, 24541, 24544, 24573, 24565, 24575, 24591, 24596, 24623, 24629, 24598, 24618, 24597, 24609, 24615, 24617, 24619, 24603, 25110, 25109, 25151, 25150, 25152, 25215, 25289, 25292, 25284, 25279, 25282, 25273, 25298, 25307, 25259, 25299, 25300, 25291, 25288, 25256, 25277, 25276, 25296, 25305, 25287, 25293, 25269, 25306, 25265, 25304, 25302, 25303, 25286, 25260, 25294, 25918, 26023, 26044, 26106, 26132, 26131, 26124, 26118, 26114, 26126, 26112, 26127, 26133, 26122, 26119, 26381, 26379, 26477, 26507, 26517, 26481, 26524, 26483, 26487, 26503, 26525, 26519, 26479, 26480, 26495, 26505, 26494, 26512, 26485, 26522, 26515, 26492, 26474, 26482, 27427, 27494, 27495, 27519, 27667, 27675, 27875, 27880, 27891, 27825, 27852, 27877, 27827, 27837, 27838, 27836, 27874, 27819, 27861, 27859, 27832, 27844, 27833, 27841, 27822, 27863, 27845, 27889, 27839, 27835, 27873, 27867, 27850, 27820, 27887, 27868, 27862, 27872, 28821, 28814, 28818, 28810, 28825, 29228, 29229, 29240, 29256, 29287, 29289, 29376, 29390, 29401, 29399, 29392, 29609, 29608, 29599, 29611, 29605, 30013, 30109, 30105, 30106, 30340, 30402, 30450, 30452, 30693, 30717, 31038, 31040, 31041, 31177, 31176, 31354, 31353, 31482, 31998, 32596, 32652, 32651, 32773, 32954, 32933, 32930, 32945, 32929, 32939, 32937, 32948, 32938, 32943, 33253, 33278, 33293, 33459, 33437, 33433, 33453, 33469, 33439, 33465, 33457, 33452, 33445, 33455, 33464, 33443, 33456, 33470, 33463, 34382, 34417, 21021, 34920, 36555, 36814, 36820, 36817, 37045, 37048, 37041, 37046, 37319, 37329, 38263, 38272, 38428, 38464, 38463, 38459, 38468, 38466, 38585, 38632, 38738, 38750, 20127, 20141, 20142, 20449, 20405, 20399, 20415, 20448, 20433, 20431, 20445, 20419, 20406, 20440, 20447, 20426, 20439, 20398, 20432, 20420, 20418, 20442, 20430, 20446, 20407, 20823, 20882, 20881, 20896, 21070, 21059, 21066, 21069, 21068, 21067, 21063, 21191, 21193, 21187, 21185, 21261, 21335, 21371, 21402, 21467, 21676, 21696, 21672, 21710, 21705, 21688, 21670, 21683, 21703, 21698, 21693, 21674, 21697, 21700, 21704, 21679, 21675, 21681, 21691, 21673, 21671, 21695, 22271, 22402, 22411, 22432, 22435, 22434, 22478, 22446, 22419, 22869, 22865, 22863, 22862, 22864, 23004, 23000, 23039, 23011, 23016, 23043, 23013, 23018, 23002, 23014, 23041, 23035, 23401, 23459, 23462, 23460, 23458, 23461, 23553, 23630, 23631, 23629, 23627, 23769, 23762, 24055, 24093, 24101, 24095, 24189, 24224, 24230, 24314, 24328, 24365, 24421, 24456, 24453, 24458, 24459, 24455, 24460, 24457, 24594, 24605, 24608, 24613, 24590, 24616, 24653, 24688, 24680, 24674, 24646, 24643, 24684, 24683, 24682, 24676, 25153, 25308, 25366, 25353, 25340, 25325, 25345, 25326, 25341, 25351, 25329, 25335, 25327, 25324, 25342, 25332, 25361, 25346, 25919, 25925, 26027, 26045, 26082, 26149, 26157, 26144, 26151, 26159, 26143, 26152, 26161, 26148, 26359, 26623, 26579, 26609, 26580, 26576, 26604, 26550, 26543, 26613, 26601, 26607, 26564, 26577, 26548, 26586, 26597, 26552, 26575, 26590, 26611, 26544, 26585, 26594, 26589, 26578, 27498, 27523, 27526, 27573, 27602, 27607, 27679, 27849, 27915, 27954, 27946, 27969, 27941, 27916, 27953, 27934, 27927, 27963, 27965, 27966, 27958, 27931, 27893, 27961, 27943, 27960, 27945, 27950, 27957, 27918, 27947, 28843, 28858, 28851, 28844, 28847, 28845, 28856, 28846, 28836, 29232, 29298, 29295, 29300, 29417, 29408, 29409, 29623, 29642, 29627, 29618, 29645, 29632, 29619, 29978, 29997, 30031, 30028, 30030, 30027, 30123, 30116, 30117, 30114, 30115, 30328, 30342, 30343, 30344, 30408, 30406, 30403, 30405, 30465, 30457, 30456, 30473, 30475, 30462, 30460, 30471, 30684, 30722, 30740, 30732, 30733, 31046, 31049, 31048, 31047, 31161, 31162, 31185, 31186, 31179, 31359, 31361, 31487, 31485, 31869, 32002, 32005, 32000, 32009, 32007, 32004, 32006, 32568, 32654, 32703, 32772, 32784, 32781, 32785, 32822, 32982, 32997, 32986, 32963, 32964, 32972, 32993, 32987, 32974, 32990, 32996, 32989, 33268, 33314, 33511, 33539, 33541, 33507, 33499, 33510, 33540, 33509, 33538, 33545, 33490, 33495, 33521, 33537, 33500, 33492, 33489, 33502, 33491, 33503, 33519, 33542, 34384, 34425, 34427, 34426, 34893, 34923, 35201, 35284, 35336, 35330, 35331, 35998, 36000, 36212, 36211, 36276, 36557, 36556, 36848, 36838, 36834, 36842, 36837, 36845, 36843, 36836, 36840, 37066, 37070, 37057, 37059, 37195, 37194, 37325, 38274, 38480, 38475, 38476, 38477, 38754, 38761, 38859, 38893, 38899, 38913, 39080, 39131, 39135, 39318, 39321, 20056, 20147, 20492, 20493, 20515, 20463, 20518, 20517, 20472, 20521, 20502, 20486, 20540, 20511, 20506, 20498, 20497, 20474, 20480, 20500, 20520, 20465, 20513, 20491, 20505, 20504, 20467, 20462, 20525, 20522, 20478, 20523, 20489, 20860, 20900, 20901, 20898, 20941, 20940, 20934, 20939, 21078, 21084, 21076, 21083, 21085, 21290, 21375, 21407, 21405, 21471, 21736, 21776, 21761, 21815, 21756, 21733, 21746, 21766, 21754, 21780, 21737, 21741, 21729, 21769, 21742, 21738, 21734, 21799, 21767, 21757, 21775, 22275, 22276, 22466, 22484, 22475, 22467, 22537, 22799, 22871, 22872, 22874, 23057, 23064, 23068, 23071, 23067, 23059, 23020, 23072, 23075, 23081, 23077, 23052, 23049, 23403, 23640, 23472, 23475, 23478, 23476, 23470, 23477, 23481, 23480, 23556, 23633, 23637, 23632, 23789, 23805, 23803, 23786, 23784, 23792, 23798, 23809, 23796, 24046, 24109, 24107, 24235, 24237, 24231, 24369, 24466, 24465, 24464, 24665, 24675, 24677, 24656, 24661, 24685, 24681, 24687, 24708, 24735, 24730, 24717, 24724, 24716, 24709, 24726, 25159, 25331, 25352, 25343, 25422, 25406, 25391, 25429, 25410, 25414, 25423, 25417, 25402, 25424, 25405, 25386, 25387, 25384, 25421, 25420, 25928, 25929, 26009, 26049, 26053, 26178, 26185, 26191, 26179, 26194, 26188, 26181, 26177, 26360, 26388, 26389, 26391, 26657, 26680, 26696, 26694, 26707, 26681, 26690, 26708, 26665, 26803, 26647, 26700, 26705, 26685, 26612, 26704, 26688, 26684, 26691, 26666, 26693, 26643, 26648, 26689, 27530, 27529, 27575, 27683, 27687, 27688, 27686, 27684, 27888, 28010, 28053, 28040, 28039, 28006, 28024, 28023, 27993, 28051, 28012, 28041, 28014, 27994, 28020, 28009, 28044, 28042, 28025, 28037, 28005, 28052, 28874, 28888, 28900, 28889, 28872, 28879, 29241, 29305, 29436, 29433, 29437, 29432, 29431, 29574, 29677, 29705, 29678, 29664, 29674, 29662, 30036, 30045, 30044, 30042, 30041, 30142, 30149, 30151, 30130, 30131, 30141, 30140, 30137, 30146, 30136, 30347, 30384, 30410, 30413, 30414, 30505, 30495, 30496, 30504, 30697, 30768, 30759, 30776, 30749, 30772, 30775, 30757, 30765, 30752, 30751, 30770, 31061, 31056, 31072, 31071, 31062, 31070, 31069, 31063, 31066, 31204, 31203, 31207, 31199, 31206, 31209, 31192, 31364, 31368, 31449, 31494, 31505, 31881, 32033, 32023, 32011, 32010, 32032, 32034, 32020, 32016, 32021, 32026, 32028, 32013, 32025, 32027, 32570, 32607, 32660, 32709, 32705, 32774, 32792, 32789, 32793, 32791, 32829, 32831, 33009, 33026, 33008, 33029, 33005, 33012, 33030, 33016, 33011, 33032, 33021, 33034, 33020, 33007, 33261, 33260, 33280, 33296, 33322, 33323, 33320, 33324, 33467, 33579, 33618, 33620, 33610, 33592, 33616, 33609, 33589, 33588, 33615, 33586, 33593, 33590, 33559, 33600, 33585, 33576, 33603, 34388, 34442, 34474, 34451, 34468, 34473, 34444, 34467, 34460, 34928, 34935, 34945, 34946, 34941, 34937, 35352, 35344, 35342, 35340, 35349, 35338, 35351, 35347, 35350, 35343, 35345, 35912, 35962, 35961, 36001, 36002, 36215, 36524, 36562, 36564, 36559, 36785, 36865, 36870, 36855, 36864, 36858, 36852, 36867, 36861, 36869, 36856, 37013, 37089, 37085, 37090, 37202, 37197, 37196, 37336, 37341, 37335, 37340, 37337, 38275, 38498, 38499, 38497, 38491, 38493, 38500, 38488, 38494, 38587, 39138, 39340, 39592, 39640, 39717, 39730, 39740, 20094, 20602, 20605, 20572, 20551, 20547, 20556, 20570, 20553, 20581, 20598, 20558, 20565, 20597, 20596, 20599, 20559, 20495, 20591, 20589, 20828, 20885, 20976, 21098, 21103, 21202, 21209, 21208, 21205, 21264, 21263, 21273, 21311, 21312, 21310, 21443, 26364, 21830, 21866, 21862, 21828, 21854, 21857, 21827, 21834, 21809, 21846, 21839, 21845, 21807, 21860, 21816, 21806, 21852, 21804, 21859, 21811, 21825, 21847, 22280, 22283, 22281, 22495, 22533, 22538, 22534, 22496, 22500, 22522, 22530, 22581, 22519, 22521, 22816, 22882, 23094, 23105, 23113, 23142, 23146, 23104, 23100, 23138, 23130, 23110, 23114, 23408, 23495, 23493, 23492, 23490, 23487, 23494, 23561, 23560, 23559, 23648, 23644, 23645, 23815, 23814, 23822, 23835, 23830, 23842, 23825, 23849, 23828, 23833, 23844, 23847, 23831, 24034, 24120, 24118, 24115, 24119, 24247, 24248, 24246, 24245, 24254, 24373, 24375, 24407, 24428, 24425, 24427, 24471, 24473, 24478, 24472, 24481, 24480, 24476, 24703, 24739, 24713, 24736, 24744, 24779, 24756, 24806, 24765, 24773, 24763, 24757, 24796, 24764, 24792, 24789, 24774, 24799, 24760, 24794, 24775, 25114, 25115, 25160, 25504, 25511, 25458, 25494, 25506, 25509, 25463, 25447, 25496, 25514, 25457, 25513, 25481, 25475, 25499, 25451, 25512, 25476, 25480, 25497, 25505, 25516, 25490, 25487, 25472, 25467, 25449, 25448, 25466, 25949, 25942, 25937, 25945, 25943, 21855, 25935, 25944, 25941, 25940, 26012, 26011, 26028, 26063, 26059, 26060, 26062, 26205, 26202, 26212, 26216, 26214, 26206, 26361, 21207, 26395, 26753, 26799, 26786, 26771, 26805, 26751, 26742, 26801, 26791, 26775, 26800, 26755, 26820, 26797, 26758, 26757, 26772, 26781, 26792, 26783, 26785, 26754, 27442, 27578, 27627, 27628, 27691, 28046, 28092, 28147, 28121, 28082, 28129, 28108, 28132, 28155, 28154, 28165, 28103, 28107, 28079, 28113, 28078, 28126, 28153, 28088, 28151, 28149, 28101, 28114, 28186, 28085, 28122, 28139, 28120, 28138, 28145, 28142, 28136, 28102, 28100, 28074, 28140, 28095, 28134, 28921, 28937, 28938, 28925, 28911, 29245, 29309, 29313, 29468, 29467, 29462, 29459, 29465, 29575, 29701, 29706, 29699, 29702, 29694, 29709, 29920, 29942, 29943, 29980, 29986, 30053, 30054, 30050, 30064, 30095, 30164, 30165, 30133, 30154, 30157, 30350, 30420, 30418, 30427, 30519, 30526, 30524, 30518, 30520, 30522, 30827, 30787, 30798, 31077, 31080, 31085, 31227, 31378, 31381, 31520, 31528, 31515, 31532, 31526, 31513, 31518, 31534, 31890, 31895, 31893, 32070, 32067, 32113, 32046, 32057, 32060, 32064, 32048, 32051, 32068, 32047, 32066, 32050, 32049, 32573, 32670, 32666, 32716, 32718, 32722, 32796, 32842, 32838, 33071, 33046, 33059, 33067, 33065, 33072, 33060, 33282, 33333, 33335, 33334, 33337, 33678, 33694, 33688, 33656, 33698, 33686, 33725, 33707, 33682, 33674, 33683, 33673, 33696, 33655, 33659, 33660, 33670, 33703, 34389, 24426, 34503, 34496, 34486, 34500, 34485, 34502, 34507, 34481, 34479, 34505, 34899, 34974, 34952, 34987, 34962, 34966, 34957, 34955, 35219, 35215, 35370, 35357, 35363, 35365, 35377, 35373, 35359, 35355, 35362, 35913, 35930, 36009, 36012, 36011, 36008, 36010, 36007, 36199, 36198, 36286, 36282, 36571, 36575, 36889, 36877, 36890, 36887, 36899, 36895, 36893, 36880, 36885, 36894, 36896, 36879, 36898, 36886, 36891, 36884, 37096, 37101, 37117, 37207, 37326, 37365, 37350, 37347, 37351, 37357, 37353, 38281, 38506, 38517, 38515, 38520, 38512, 38516, 38518, 38519, 38508, 38592, 38634, 38633, 31456, 31455, 38914, 38915, 39770, 40165, 40565, 40575, 40613, 40635, 20642, 20621, 20613, 20633, 20625, 20608, 20630, 20632, 20634, 26368, 20977, 21106, 21108, 21109, 21097, 21214, 21213, 21211, 21338, 21413, 21883, 21888, 21927, 21884, 21898, 21917, 21912, 21890, 21916, 21930, 21908, 21895, 21899, 21891, 21939, 21934, 21919, 21822, 21938, 21914, 21947, 21932, 21937, 21886, 21897, 21931, 21913, 22285, 22575, 22570, 22580, 22564, 22576, 22577, 22561, 22557, 22560, 22777, 22778, 22880, 23159, 23194, 23167, 23186, 23195, 23207, 23411, 23409, 23506, 23500, 23507, 23504, 23562, 23563, 23601, 23884, 23888, 23860, 23879, 24061, 24133, 24125, 24128, 24131, 24190, 24266, 24257, 24258, 24260, 24380, 24429, 24489, 24490, 24488, 24785, 24801, 24754, 24758, 24800, 24860, 24867, 24826, 24853, 24816, 24827, 24820, 24936, 24817, 24846, 24822, 24841, 24832, 24850, 25119, 25161, 25507, 25484, 25551, 25536, 25577, 25545, 25542, 25549, 25554, 25571, 25552, 25569, 25558, 25581, 25582, 25462, 25588, 25578, 25563, 25682, 25562, 25593, 25950, 25958, 25954, 25955, 26001, 26000, 26031, 26222, 26224, 26228, 26230, 26223, 26257, 26234, 26238, 26231, 26366, 26367, 26399, 26397, 26874, 26837, 26848, 26840, 26839, 26885, 26847, 26869, 26862, 26855, 26873, 26834, 26866, 26851, 26827, 26829, 26893, 26898, 26894, 26825, 26842, 26990, 26875, 27454, 27450, 27453, 27544, 27542, 27580, 27631, 27694, 27695, 27692, 28207, 28216, 28244, 28193, 28210, 28263, 28234, 28192, 28197, 28195, 28187, 28251, 28248, 28196, 28246, 28270, 28205, 28198, 28271, 28212, 28237, 28218, 28204, 28227, 28189, 28222, 28363, 28297, 28185, 28238, 28259, 28228, 28274, 28265, 28255, 28953, 28954, 28966, 28976, 28961, 28982, 29038, 28956, 29260, 29316, 29312, 29494, 29477, 29492, 29481, 29754, 29738, 29747, 29730, 29733, 29749, 29750, 29748, 29743, 29723, 29734, 29736, 29989, 29990, 30059, 30058, 30178, 30171, 30179, 30169, 30168, 30174, 30176, 30331, 30332, 30358, 30355, 30388, 30428, 30543, 30701, 30813, 30828, 30831, 31245, 31240, 31243, 31237, 31232, 31384, 31383, 31382, 31461, 31459, 31561, 31574, 31558, 31568, 31570, 31572, 31565, 31563, 31567, 31569, 31903, 31909, 32094, 32080, 32104, 32085, 32043, 32110, 32114, 32097, 32102, 32098, 32112, 32115, 21892, 32724, 32725, 32779, 32850, 32901, 33109, 33108, 33099, 33105, 33102, 33081, 33094, 33086, 33100, 33107, 33140, 33298, 33308, 33769, 33795, 33784, 33805, 33760, 33733, 33803, 33729, 33775, 33777, 33780, 33879, 33802, 33776, 33804, 33740, 33789, 33778, 33738, 33848, 33806, 33796, 33756, 33799, 33748, 33759, 34395, 34527, 34521, 34541, 34516, 34523, 34532, 34512, 34526, 34903, 35009, 35010, 34993, 35203, 35222, 35387, 35424, 35413, 35422, 35388, 35393, 35412, 35419, 35408, 35398, 35380, 35386, 35382, 35414, 35937, 35970, 36015, 36028, 36019, 36029, 36033, 36027, 36032, 36020, 36023, 36022, 36031, 36024, 36234, 36229, 36225, 36302, 36317, 36299, 36314, 36305, 36300, 36315, 36294, 36603, 36600, 36604, 36764, 36910, 36917, 36913, 36920, 36914, 36918, 37122, 37109, 37129, 37118, 37219, 37221, 37327, 37396, 37397, 37411, 37385, 37406, 37389, 37392, 37383, 37393, 38292, 38287, 38283, 38289, 38291, 38290, 38286, 38538, 38542, 38539, 38525, 38533, 38534, 38541, 38514, 38532, 38593, 38597, 38596, 38598, 38599, 38639, 38642, 38860, 38917, 38918, 38920, 39143, 39146, 39151, 39145, 39154, 39149, 39342, 39341, 40643, 40653, 40657, 20098, 20653, 20661, 20658, 20659, 20677, 20670, 20652, 20663, 20667, 20655, 20679, 21119, 21111, 21117, 21215, 21222, 21220, 21218, 21219, 21295, 21983, 21992, 21971, 21990, 21966, 21980, 21959, 21969, 21987, 21988, 21999, 21978, 21985, 21957, 21958, 21989, 21961, 22290, 22291, 22622, 22609, 22616, 22615, 22618, 22612, 22635, 22604, 22637, 22602, 22626, 22610, 22603, 22887, 23233, 23241, 23244, 23230, 23229, 23228, 23219, 23234, 23218, 23913, 23919, 24140, 24185, 24265, 24264, 24338, 24409, 24492, 24494, 24858, 24847, 24904, 24863, 24819, 24859, 24825, 24833, 24840, 24910, 24908, 24900, 24909, 24894, 24884, 24871, 24845, 24838, 24887, 25121, 25122, 25619, 25662, 25630, 25642, 25645, 25661, 25644, 25615, 25628, 25620, 25613, 25654, 25622, 25623, 25606, 25964, 26015, 26032, 26263, 26249, 26247, 26248, 26262, 26244, 26264, 26253, 26371, 27028, 26989, 26970, 26999, 26976, 26964, 26997, 26928, 27010, 26954, 26984, 26987, 26974, 26963, 27001, 27014, 26973, 26979, 26971, 27463, 27506, 27584, 27583, 27603, 27645, 28322, 28335, 28371, 28342, 28354, 28304, 28317, 28359, 28357, 28325, 28312, 28348, 28346, 28331, 28369, 28310, 28316, 28356, 28372, 28330, 28327, 28340, 29006, 29017, 29033, 29028, 29001, 29031, 29020, 29036, 29030, 29004, 29029, 29022, 28998, 29032, 29014, 29242, 29266, 29495, 29509, 29503, 29502, 29807, 29786, 29781, 29791, 29790, 29761, 29759, 29785, 29787, 29788, 30070, 30072, 30208, 30192, 30209, 30194, 30193, 30202, 30207, 30196, 30195, 30430, 30431, 30555, 30571, 30566, 30558, 30563, 30585, 30570, 30572, 30556, 30565, 30568, 30562, 30702, 30862, 30896, 30871, 30872, 30860, 30857, 30844, 30865, 30867, 30847, 31098, 31103, 31105, 33836, 31165, 31260, 31258, 31264, 31252, 31263, 31262, 31391, 31392, 31607, 31680, 31584, 31598, 31591, 31921, 31923, 31925, 32147, 32121, 32145, 32129, 32143, 32091, 32622, 32617, 32618, 32626, 32681, 32680, 32676, 32854, 32856, 32902, 32900, 33137, 33136, 33144, 33125, 33134, 33139, 33131, 33145, 33146, 33126, 33285, 33351, 33922, 33911, 33853, 33841, 33909, 33894, 33899, 33865, 33900, 33883, 33852, 33845, 33889, 33891, 33897, 33901, 33862, 34398, 34396, 34399, 34553, 34579, 34568, 34567, 34560, 34558, 34555, 34562, 34563, 34566, 34570, 34905, 35039, 35028, 35033, 35036, 35032, 35037, 35041, 35018, 35029, 35026, 35228, 35299, 35435, 35442, 35443, 35430, 35433, 35440, 35463, 35452, 35427, 35488, 35441, 35461, 35437, 35426, 35438, 35436, 35449, 35451, 35390, 35432, 35938, 35978, 35977, 36042, 36039, 36040, 36036, 36018, 36035, 36034, 36037, 36321, 36319, 36328, 36335, 36339, 36346, 36330, 36324, 36326, 36530, 36611, 36617, 36606, 36618, 36767, 36786, 36939, 36938, 36947, 36930, 36948, 36924, 36949, 36944, 36935, 36943, 36942, 36941, 36945, 36926, 36929, 37138, 37143, 37228, 37226, 37225, 37321, 37431, 37463, 37432, 37437, 37440, 37438, 37467, 37451, 37476, 37457, 37428, 37449, 37453, 37445, 37433, 37439, 37466, 38296, 38552, 38548, 38549, 38605, 38603, 38601, 38602, 38647, 38651, 38649, 38646, 38742, 38772, 38774, 38928, 38929, 38931, 38922, 38930, 38924, 39164, 39156, 39165, 39166, 39347, 39345, 39348, 39649, 40169, 40578, 40718, 40723, 40736, 20711, 20718, 20709, 20694, 20717, 20698, 20693, 20687, 20689, 20721, 20686, 20713, 20834, 20979, 21123, 21122, 21297, 21421, 22014, 22016, 22043, 22039, 22013, 22036, 22022, 22025, 22029, 22030, 22007, 22038, 22047, 22024, 22032, 22006, 22296, 22294, 22645, 22654, 22659, 22675, 22666, 22649, 22661, 22653, 22781, 22821, 22818, 22820, 22890, 22889, 23265, 23270, 23273, 23255, 23254, 23256, 23267, 23413, 23518, 23527, 23521, 23525, 23526, 23528, 23522, 23524, 23519, 23565, 23650, 23940, 23943, 24155, 24163, 24149, 24151, 24148, 24275, 24278, 24330, 24390, 24432, 24505, 24903, 24895, 24907, 24951, 24930, 24931, 24927, 24922, 24920, 24949, 25130, 25735, 25688, 25684, 25764, 25720, 25695, 25722, 25681, 25703, 25652, 25709, 25723, 25970, 26017, 26071, 26070, 26274, 26280, 26269, 27036, 27048, 27029, 27073, 27054, 27091, 27083, 27035, 27063, 27067, 27051, 27060, 27088, 27085, 27053, 27084, 27046, 27075, 27043, 27465, 27468, 27699, 28467, 28436, 28414, 28435, 28404, 28457, 28478, 28448, 28460, 28431, 28418, 28450, 28415, 28399, 28422, 28465, 28472, 28466, 28451, 28437, 28459, 28463, 28552, 28458, 28396, 28417, 28402, 28364, 28407, 29076, 29081, 29053, 29066, 29060, 29074, 29246, 29330, 29334, 29508, 29520, 29796, 29795, 29802, 29808, 29805, 29956, 30097, 30247, 30221, 30219, 30217, 30227, 30433, 30435, 30596, 30589, 30591, 30561, 30913, 30879, 30887, 30899, 30889, 30883, 31118, 31119, 31117, 31278, 31281, 31402, 31401, 31469, 31471, 31649, 31637, 31627, 31605, 31639, 31645, 31636, 31631, 31672, 31623, 31620, 31929, 31933, 31934, 32187, 32176, 32156, 32189, 32190, 32160, 32202, 32180, 32178, 32177, 32186, 32162, 32191, 32181, 32184, 32173, 32210, 32199, 32172, 32624, 32736, 32737, 32735, 32862, 32858, 32903, 33104, 33152, 33167, 33160, 33162, 33151, 33154, 33255, 33274, 33287, 33300, 33310, 33355, 33993, 33983, 33990, 33988, 33945, 33950, 33970, 33948, 33995, 33976, 33984, 34003, 33936, 33980, 34001, 33994, 34623, 34588, 34619, 34594, 34597, 34612, 34584, 34645, 34615, 34601, 35059, 35074, 35060, 35065, 35064, 35069, 35048, 35098, 35055, 35494, 35468, 35486, 35491, 35469, 35489, 35475, 35492, 35498, 35493, 35496, 35480, 35473, 35482, 35495, 35946, 35981, 35980, 36051, 36049, 36050, 36203, 36249, 36245, 36348, 36628, 36626, 36629, 36627, 36771, 36960, 36952, 36956, 36963, 36953, 36958, 36962, 36957, 36955, 37145, 37144, 37150, 37237, 37240, 37239, 37236, 37496, 37504, 37509, 37528, 37526, 37499, 37523, 37532, 37544, 37500, 37521, 38305, 38312, 38313, 38307, 38309, 38308, 38553, 38556, 38555, 38604, 38610, 38656, 38780, 38789, 38902, 38935, 38936, 39087, 39089, 39171, 39173, 39180, 39177, 39361, 39599, 39600, 39654, 39745, 39746, 40180, 40182, 40179, 40636, 40763, 40778, 20740, 20736, 20731, 20725, 20729, 20738, 20744, 20745, 20741, 20956, 21127, 21128, 21129, 21133, 21130, 21232, 21426, 22062, 22075, 22073, 22066, 22079, 22068, 22057, 22099, 22094, 22103, 22132, 22070, 22063, 22064, 22656, 22687, 22686, 22707, 22684, 22702, 22697, 22694, 22893, 23305, 23291, 23307, 23285, 23308, 23304, 23534, 23532, 23529, 23531, 23652, 23653, 23965, 23956, 24162, 24159, 24161, 24290, 24282, 24287, 24285, 24291, 24288, 24392, 24433, 24503, 24501, 24950, 24935, 24942, 24925, 24917, 24962, 24956, 24944, 24939, 24958, 24999, 24976, 25003, 24974, 25004, 24986, 24996, 24980, 25006, 25134, 25705, 25711, 25721, 25758, 25778, 25736, 25744, 25776, 25765, 25747, 25749, 25769, 25746, 25774, 25773, 25771, 25754, 25772, 25753, 25762, 25779, 25973, 25975, 25976, 26286, 26283, 26292, 26289, 27171, 27167, 27112, 27137, 27166, 27161, 27133, 27169, 27155, 27146, 27123, 27138, 27141, 27117, 27153, 27472, 27470, 27556, 27589, 27590, 28479, 28540, 28548, 28497, 28518, 28500, 28550, 28525, 28507, 28536, 28526, 28558, 28538, 28528, 28516, 28567, 28504, 28373, 28527, 28512, 28511, 29087, 29100, 29105, 29096, 29270, 29339, 29518, 29527, 29801, 29835, 29827, 29822, 29824, 30079, 30240, 30249, 30239, 30244, 30246, 30241, 30242, 30362, 30394, 30436, 30606, 30599, 30604, 30609, 30603, 30923, 30917, 30906, 30922, 30910, 30933, 30908, 30928, 31295, 31292, 31296, 31293, 31287, 31291, 31407, 31406, 31661, 31665, 31684, 31668, 31686, 31687, 31681, 31648, 31692, 31946, 32224, 32244, 32239, 32251, 32216, 32236, 32221, 32232, 32227, 32218, 32222, 32233, 32158, 32217, 32242, 32249, 32629, 32631, 32687, 32745, 32806, 33179, 33180, 33181, 33184, 33178, 33176, 34071, 34109, 34074, 34030, 34092, 34093, 34067, 34065, 34083, 34081, 34068, 34028, 34085, 34047, 34054, 34690, 34676, 34678, 34656, 34662, 34680, 34664, 34649, 34647, 34636, 34643, 34907, 34909, 35088, 35079, 35090, 35091, 35093, 35082, 35516, 35538, 35527, 35524, 35477, 35531, 35576, 35506, 35529, 35522, 35519, 35504, 35542, 35533, 35510, 35513, 35547, 35916, 35918, 35948, 36064, 36062, 36070, 36068, 36076, 36077, 36066, 36067, 36060, 36074, 36065, 36205, 36255, 36259, 36395, 36368, 36381, 36386, 36367, 36393, 36383, 36385, 36382, 36538, 36637, 36635, 36639, 36649, 36646, 36650, 36636, 36638, 36645, 36969, 36974, 36968, 36973, 36983, 37168, 37165, 37159, 37169, 37255, 37257, 37259, 37251, 37573, 37563, 37559, 37610, 37548, 37604, 37569, 37555, 37564, 37586, 37575, 37616, 37554, 38317, 38321, 38660, 38662, 38663, 38665, 38752, 38797, 38795, 38799, 38945, 38955, 38940, 39091, 39178, 39187, 39186, 39192, 39389, 39376, 39391, 39387, 39377, 39381, 39378, 39385, 39607, 39662, 39663, 39719, 39749, 39748, 39799, 39791, 40198, 40201, 40195, 40617, 40638, 40654, 22696, 40786, 20754, 20760, 20756, 20752, 20757, 20864, 20906, 20957, 21137, 21139, 21235, 22105, 22123, 22137, 22121, 22116, 22136, 22122, 22120, 22117, 22129, 22127, 22124, 22114, 22134, 22721, 22718, 22727, 22725, 22894, 23325, 23348, 23416, 23536, 23566, 24394, 25010, 24977, 25001, 24970, 25037, 25014, 25022, 25034, 25032, 25136, 25797, 25793, 25803, 25787, 25788, 25818, 25796, 25799, 25794, 25805, 25791, 25810, 25812, 25790, 25972, 26310, 26313, 26297, 26308, 26311, 26296, 27197, 27192, 27194, 27225, 27243, 27224, 27193, 27204, 27234, 27233, 27211, 27207, 27189, 27231, 27208, 27481, 27511, 27653, 28610, 28593, 28577, 28611, 28580, 28609, 28583, 28595, 28608, 28601, 28598, 28582, 28576, 28596, 29118, 29129, 29136, 29138, 29128, 29141, 29113, 29134, 29145, 29148, 29123, 29124, 29544, 29852, 29859, 29848, 29855, 29854, 29922, 29964, 29965, 30260, 30264, 30266, 30439, 30437, 30624, 30622, 30623, 30629, 30952, 30938, 30956, 30951, 31142, 31309, 31310, 31302, 31308, 31307, 31418, 31705, 31761, 31689, 31716, 31707, 31713, 31721, 31718, 31957, 31958, 32266, 32273, 32264, 32283, 32291, 32286, 32285, 32265, 32272, 32633, 32690, 32752, 32753, 32750, 32808, 33203, 33193, 33192, 33275, 33288, 33368, 33369, 34122, 34137, 34120, 34152, 34153, 34115, 34121, 34157, 34154, 34142, 34691, 34719, 34718, 34722, 34701, 34913, 35114, 35122, 35109, 35115, 35105, 35242, 35238, 35558, 35578, 35563, 35569, 35584, 35548, 35559, 35566, 35582, 35585, 35586, 35575, 35565, 35571, 35574, 35580, 35947, 35949, 35987, 36084, 36420, 36401, 36404, 36418, 36409, 36405, 36667, 36655, 36664, 36659, 36776, 36774, 36981, 36980, 36984, 36978, 36988, 36986, 37172, 37266, 37664, 37686, 37624, 37683, 37679, 37666, 37628, 37675, 37636, 37658, 37648, 37670, 37665, 37653, 37678, 37657, 38331, 38567, 38568, 38570, 38613, 38670, 38673, 38678, 38669, 38675, 38671, 38747, 38748, 38758, 38808, 38960, 38968, 38971, 38967, 38957, 38969, 38948, 39184, 39208, 39198, 39195, 39201, 39194, 39405, 39394, 39409, 39608, 39612, 39675, 39661, 39720, 39825, 40213, 40227, 40230, 40232, 40210, 40219, 40664, 40660, 40845, 40860, 20778, 20767, 20769, 20786, 21237, 22158, 22144, 22160, 22149, 22151, 22159, 22741, 22739, 22737, 22734, 23344, 23338, 23332, 23418, 23607, 23656, 23996, 23994, 23997, 23992, 24171, 24396, 24509, 25033, 25026, 25031, 25062, 25035, 25138, 25140, 25806, 25802, 25816, 25824, 25840, 25830, 25836, 25841, 25826, 25837, 25986, 25987, 26329, 26326, 27264, 27284, 27268, 27298, 27292, 27355, 27299, 27262, 27287, 27280, 27296, 27484, 27566, 27610, 27656, 28632, 28657, 28639, 28640, 28635, 28644, 28651, 28655, 28544, 28652, 28641, 28649, 28629, 28654, 28656, 29159, 29151, 29166, 29158, 29157, 29165, 29164, 29172, 29152, 29237, 29254, 29552, 29554, 29865, 29872, 29862, 29864, 30278, 30274, 30284, 30442, 30643, 30634, 30640, 30636, 30631, 30637, 30703, 30967, 30970, 30964, 30959, 30977, 31143, 31146, 31319, 31423, 31751, 31757, 31742, 31735, 31756, 31712, 31968, 31964, 31966, 31970, 31967, 31961, 31965, 32302, 32318, 32326, 32311, 32306, 32323, 32299, 32317, 32305, 32325, 32321, 32308, 32313, 32328, 32309, 32319, 32303, 32580, 32755, 32764, 32881, 32882, 32880, 32879, 32883, 33222, 33219, 33210, 33218, 33216, 33215, 33213, 33225, 33214, 33256, 33289, 33393, 34218, 34180, 34174, 34204, 34193, 34196, 34223, 34203, 34183, 34216, 34186, 34407, 34752, 34769, 34739, 34770, 34758, 34731, 34747, 34746, 34760, 34763, 35131, 35126, 35140, 35128, 35133, 35244, 35598, 35607, 35609, 35611, 35594, 35616, 35613, 35588, 35600, 35905, 35903, 35955, 36090, 36093, 36092, 36088, 36091, 36264, 36425, 36427, 36424, 36426, 36676, 36670, 36674, 36677, 36671, 36991, 36989, 36996, 36993, 36994, 36992, 37177, 37283, 37278, 37276, 37709, 37762, 37672, 37749, 37706, 37733, 37707, 37656, 37758, 37740, 37723, 37744, 37722, 37716, 38346, 38347, 38348, 38344, 38342, 38577, 38584, 38614, 38684, 38686, 38816, 38867, 38982, 39094, 39221, 39425, 39423, 39854, 39851, 39850, 39853, 40251, 40255, 40587, 40655, 40670, 40668, 40669, 40667, 40766, 40779, 21474, 22165, 22190, 22745, 22744, 23352, 24413, 25059, 25139, 25844, 25842, 25854, 25862, 25850, 25851, 25847, 26039, 26332, 26406, 27315, 27308, 27331, 27323, 27320, 27330, 27310, 27311, 27487, 27512, 27567, 28681, 28683, 28670, 28678, 28666, 28689, 28687, 29179, 29180, 29182, 29176, 29559, 29557, 29863, 29887, 29973, 30294, 30296, 30290, 30653, 30655, 30651, 30652, 30990, 31150, 31329, 31330, 31328, 31428, 31429, 31787, 31783, 31786, 31774, 31779, 31777, 31975, 32340, 32341, 32350, 32346, 32353, 32338, 32345, 32584, 32761, 32763, 32887, 32886, 33229, 33231, 33290, 34255, 34217, 34253, 34256, 34249, 34224, 34234, 34233, 34214, 34799, 34796, 34802, 34784, 35206, 35250, 35316, 35624, 35641, 35628, 35627, 35920, 36101, 36441, 36451, 36454, 36452, 36447, 36437, 36544, 36681, 36685, 36999, 36995, 37000, 37291, 37292, 37328, 37780, 37770, 37782, 37794, 37811, 37806, 37804, 37808, 37784, 37786, 37783, 38356, 38358, 38352, 38357, 38626, 38620, 38617, 38619, 38622, 38692, 38819, 38822, 38829, 38905, 38989, 38991, 38988, 38990, 38995, 39098, 39230, 39231, 39229, 39214, 39333, 39438, 39617, 39683, 39686, 39759, 39758, 39757, 39882, 39881, 39933, 39880, 39872, 40273, 40285, 40288, 40672, 40725, 40748, 20787, 22181, 22750, 22751, 22754, 23541, 40848, 24300, 25074, 25079, 25078, 25077, 25856, 25871, 26336, 26333, 27365, 27357, 27354, 27347, 28699, 28703, 28712, 28698, 28701, 28693, 28696, 29190, 29197, 29272, 29346, 29560, 29562, 29885, 29898, 29923, 30087, 30086, 30303, 30305, 30663, 31001, 31153, 31339, 31337, 31806, 31807, 31800, 31805, 31799, 31808, 32363, 32365, 32377, 32361, 32362, 32645, 32371, 32694, 32697, 32696, 33240, 34281, 34269, 34282, 34261, 34276, 34277, 34295, 34811, 34821, 34829, 34809, 34814, 35168, 35167, 35158, 35166, 35649, 35676, 35672, 35657, 35674, 35662, 35663, 35654, 35673, 36104, 36106, 36476, 36466, 36487, 36470, 36460, 36474, 36468, 36692, 36686, 36781, 37002, 37003, 37297, 37294, 37857, 37841, 37855, 37827, 37832, 37852, 37853, 37846, 37858, 37837, 37848, 37860, 37847, 37864, 38364, 38580, 38627, 38698, 38695, 38753, 38876, 38907, 39006, 39000, 39003, 39100, 39237, 39241, 39446, 39449, 39693, 39912, 39911, 39894, 39899, 40329, 40289, 40306, 40298, 40300, 40594, 40599, 40595, 40628, 21240, 22184, 22199, 22198, 22196, 22204, 22756, 23360, 23363, 23421, 23542, 24009, 25080, 25082, 25880, 25876, 25881, 26342, 26407, 27372, 28734, 28720, 28722, 29200, 29563, 29903, 30306, 30309, 31014, 31018, 31020, 31019, 31431, 31478, 31820, 31811, 31821, 31983, 31984, 36782, 32381, 32380, 32386, 32588, 32768, 33242, 33382, 34299, 34297, 34321, 34298, 34310, 34315, 34311, 34314, 34836, 34837, 35172, 35258, 35320, 35696, 35692, 35686, 35695, 35679, 35691, 36111, 36109, 36489, 36481, 36485, 36482, 37300, 37323, 37912, 37891, 37885, 38369, 38704, 39108, 39250, 39249, 39336, 39467, 39472, 39479, 39477, 39955, 39949, 40569, 40629, 40680, 40751, 40799, 40803, 40801, 20791, 20792, 22209, 22208, 22210, 22804, 23660, 24013, 25084, 25086, 25885, 25884, 26005, 26345, 27387, 27396, 27386, 27570, 28748, 29211, 29351, 29910, 29908, 30313, 30675, 31824, 32399, 32396, 32700, 34327, 34349, 34330, 34851, 34850, 34849, 34847, 35178, 35180, 35261, 35700, 35703, 35709, 36115, 36490, 36493, 36491, 36703, 36783, 37306, 37934, 37939, 37941, 37946, 37944, 37938, 37931, 38370, 38712, 38713, 38706, 38911, 39015, 39013, 39255, 39493, 39491, 39488, 39486, 39631, 39764, 39761, 39981, 39973, 40367, 40372, 40386, 40376, 40605, 40687, 40729, 40796, 40806, 40807, 20796, 20795, 22216, 22218, 22217, 23423, 24020, 24018, 24398, 25087, 25892, 27402, 27489, 28753, 28760, 29568, 29924, 30090, 30318, 30316, 31155, 31840, 31839, 32894, 32893, 33247, 35186, 35183, 35324, 35712, 36118, 36119, 36497, 36499, 36705, 37192, 37956, 37969, 37970, 38717, 38718, 38851, 38849, 39019, 39253, 39509, 39501, 39634, 39706, 40009, 39985, 39998, 39995, 40403, 40407, 40756, 40812, 40810, 40852, 22220, 24022, 25088, 25891, 25899, 25898, 26348, 27408, 29914, 31434, 31844, 31843, 31845, 32403, 32406, 32404, 33250, 34360, 34367, 34865, 35722, 37008, 37007, 37987, 37984, 37988, 38760, 39023, 39260, 39514, 39515, 39511, 39635, 39636, 39633, 40020, 40023, 40022, 40421, 40607, 40692, 22225, 22761, 25900, 28766, 30321, 30322, 30679, 32592, 32648, 34870, 34873, 34914, 35731, 35730, 35734, 33399, 36123, 37312, 37994, 38722, 38728, 38724, 38854, 39024, 39519, 39714, 39768, 40031, 40441, 40442, 40572, 40573, 40711, 40823, 40818, 24307, 27414, 28771, 31852, 31854, 34875, 35264, 36513, 37313, 38002, 38000, 39025, 39262, 39638, 39715, 40652, 28772, 30682, 35738, 38007, 38857, 39522, 39525, 32412, 35740, 36522, 37317, 38013, 38014, 38012, 40055, 40056, 40695, 35924, 38015, 40474, 29224, 39530, 39729, 40475, 40478, 31858, 9312, 9313, 9314, 9315, 9316, 9317, 9318, 9319, 9320, 9321, 9332, 9333, 9334, 9335, 9336, 9337, 9338, 9339, 9340, 9341, 8560, 8561, 8562, 8563, 8564, 8565, 8566, 8567, 8568, 8569, 20022, 20031, 20101, 20128, 20866, 20886, 20907, 21241, 21304, 21353, 21430, 22794, 23424, 24027, 12083, 24191, 24308, 24400, 24417, 25908, 26080, 30098, 30326, 36789, 38582, 168, 710, 12541, 12542, 12445, 12446, 12291, 20189, 12293, 12294, 12295, 12540, 65339, 65341, 10045, 12353, 12354, 12355, 12356, 12357, 12358, 12359, 12360, 12361, 12362, 12363, 12364, 12365, 12366, 12367, 12368, 12369, 12370, 12371, 12372, 12373, 12374, 12375, 12376, 12377, 12378, 12379, 12380, 12381, 12382, 12383, 12384, 12385, 12386, 12387, 12388, 12389, 12390, 12391, 12392, 12393, 12394, 12395, 12396, 12397, 12398, 12399, 12400, 12401, 12402, 12403, 12404, 12405, 12406, 12407, 12408, 12409, 12410, 12411, 12412, 12413, 12414, 12415, 12416, 12417, 12418, 12419, 12420, 12421, 12422, 12423, 12424, 12425, 12426, 12427, 12428, 12429, 12430, 12431, 12432, 12433, 12434, 12435, 12449, 12450, 12451, 12452, 12453, 12454, 12455, 12456, 12457, 12458, 12459, 12460, 12461, 12462, 12463, 12464, 12465, 12466, 12467, 12468, 12469, 12470, 12471, 12472, 12473, 12474, 12475, 12476, 12477, 12478, 12479, 12480, 12481, 12482, 12483, 12484, 12485, 12486, 12487, 12488, 12489, 12490, 12491, 12492, 12493, 12494, 12495, 12496, 12497, 12498, 12499, 12500, 12501, 12502, 12503, 12504, 12505, 12506, 12507, 12508, 12509, 12510, 12511, 12512, 12513, 12514, 12515, 12516, 12517, 12518, 12519, 12520, 12521, 12522, 12523, 12524, 12525, 12526, 12527, 12528, 12529, 12530, 12531, 12532, 12533, 12534, 1040, 1041, 1042, 1043, 1044, 1045, 1025, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1105, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 8679, 8632, 8633, 12751, 131276, 20058, 131210, 20994, 17553, 40880, 20872, 40881, 161287, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 65506, 65508, 65287, 65282, 12849, 8470, 8481, 12443, 12444, 11904, 11908, 11910, 11911, 11912, 11914, 11916, 11917, 11925, 11932, 11933, 11941, 11943, 11946, 11948, 11950, 11958, 11964, 11966, 11974, 11978, 11980, 11981, 11983, 11990, 11991, 11998, 12003, null, null, null, 643, 592, 603, 596, 629, 339, 248, 331, 650, 618, 20034, 20060, 20981, 21274, 21378, 19975, 19980, 20039, 20109, 22231, 64012, 23662, 24435, 19983, 20871, 19982, 20014, 20115, 20162, 20169, 20168, 20888, 21244, 21356, 21433, 22304, 22787, 22828, 23568, 24063, 26081, 27571, 27596, 27668, 29247, 20017, 20028, 20200, 20188, 20201, 20193, 20189, 20186, 21004, 21276, 21324, 22306, 22307, 22807, 22831, 23425, 23428, 23570, 23611, 23668, 23667, 24068, 24192, 24194, 24521, 25097, 25168, 27669, 27702, 27715, 27711, 27707, 29358, 29360, 29578, 31160, 32906, 38430, 20238, 20248, 20268, 20213, 20244, 20209, 20224, 20215, 20232, 20253, 20226, 20229, 20258, 20243, 20228, 20212, 20242, 20913, 21011, 21001, 21008, 21158, 21282, 21279, 21325, 21386, 21511, 22241, 22239, 22318, 22314, 22324, 22844, 22912, 22908, 22917, 22907, 22910, 22903, 22911, 23382, 23573, 23589, 23676, 23674, 23675, 23678, 24031, 24181, 24196, 24322, 24346, 24436, 24533, 24532, 24527, 25180, 25182, 25188, 25185, 25190, 25186, 25177, 25184, 25178, 25189, 26095, 26094, 26430, 26425, 26424, 26427, 26426, 26431, 26428, 26419, 27672, 27718, 27730, 27740, 27727, 27722, 27732, 27723, 27724, 28785, 29278, 29364, 29365, 29582, 29994, 30335, 31349, 32593, 33400, 33404, 33408, 33405, 33407, 34381, 35198, 37017, 37015, 37016, 37019, 37012, 38434, 38436, 38432, 38435, 20310, 20283, 20322, 20297, 20307, 20324, 20286, 20327, 20306, 20319, 20289, 20312, 20269, 20275, 20287, 20321, 20879, 20921, 21020, 21022, 21025, 21165, 21166, 21257, 21347, 21362, 21390, 21391, 21552, 21559, 21546, 21588, 21573, 21529, 21532, 21541, 21528, 21565, 21583, 21569, 21544, 21540, 21575, 22254, 22247, 22245, 22337, 22341, 22348, 22345, 22347, 22354, 22790, 22848, 22950, 22936, 22944, 22935, 22926, 22946, 22928, 22927, 22951, 22945, 23438, 23442, 23592, 23594, 23693, 23695, 23688, 23691, 23689, 23698, 23690, 23686, 23699, 23701, 24032, 24074, 24078, 24203, 24201, 24204, 24200, 24205, 24325, 24349, 24440, 24438, 24530, 24529, 24528, 24557, 24552, 24558, 24563, 24545, 24548, 24547, 24570, 24559, 24567, 24571, 24576, 24564, 25146, 25219, 25228, 25230, 25231, 25236, 25223, 25201, 25211, 25210, 25200, 25217, 25224, 25207, 25213, 25202, 25204, 25911, 26096, 26100, 26099, 26098, 26101, 26437, 26439, 26457, 26453, 26444, 26440, 26461, 26445, 26458, 26443, 27600, 27673, 27674, 27768, 27751, 27755, 27780, 27787, 27791, 27761, 27759, 27753, 27802, 27757, 27783, 27797, 27804, 27750, 27763, 27749, 27771, 27790, 28788, 28794, 29283, 29375, 29373, 29379, 29382, 29377, 29370, 29381, 29589, 29591, 29587, 29588, 29586, 30010, 30009, 30100, 30101, 30337, 31037, 32820, 32917, 32921, 32912, 32914, 32924, 33424, 33423, 33413, 33422, 33425, 33427, 33418, 33411, 33412, 35960, 36809, 36799, 37023, 37025, 37029, 37022, 37031, 37024, 38448, 38440, 38447, 38445, 20019, 20376, 20348, 20357, 20349, 20352, 20359, 20342, 20340, 20361, 20356, 20343, 20300, 20375, 20330, 20378, 20345, 20353, 20344, 20368, 20380, 20372, 20382, 20370, 20354, 20373, 20331, 20334, 20894, 20924, 20926, 21045, 21042, 21043, 21062, 21041, 21180, 21258, 21259, 21308, 21394, 21396, 21639, 21631, 21633, 21649, 21634, 21640, 21611, 21626, 21630, 21605, 21612, 21620, 21606, 21645, 21615, 21601, 21600, 21656, 21603, 21607, 21604, 22263, 22265, 22383, 22386, 22381, 22379, 22385, 22384, 22390, 22400, 22389, 22395, 22387, 22388, 22370, 22376, 22397, 22796, 22853, 22965, 22970, 22991, 22990, 22962, 22988, 22977, 22966, 22972, 22979, 22998, 22961, 22973, 22976, 22984, 22964, 22983, 23394, 23397, 23443, 23445, 23620, 23623, 23726, 23716, 23712, 23733, 23727, 23720, 23724, 23711, 23715, 23725, 23714, 23722, 23719, 23709, 23717, 23734, 23728, 23718, 24087, 24084, 24089, 24360, 24354, 24355, 24356, 24404, 24450, 24446, 24445, 24542, 24549, 24621, 24614, 24601, 24626, 24587, 24628, 24586, 24599, 24627, 24602, 24606, 24620, 24610, 24589, 24592, 24622, 24595, 24593, 24588, 24585, 24604, 25108, 25149, 25261, 25268, 25297, 25278, 25258, 25270, 25290, 25262, 25267, 25263, 25275, 25257, 25264, 25272, 25917, 26024, 26043, 26121, 26108, 26116, 26130, 26120, 26107, 26115, 26123, 26125, 26117, 26109, 26129, 26128, 26358, 26378, 26501, 26476, 26510, 26514, 26486, 26491, 26520, 26502, 26500, 26484, 26509, 26508, 26490, 26527, 26513, 26521, 26499, 26493, 26497, 26488, 26489, 26516, 27429, 27520, 27518, 27614, 27677, 27795, 27884, 27883, 27886, 27865, 27830, 27860, 27821, 27879, 27831, 27856, 27842, 27834, 27843, 27846, 27885, 27890, 27858, 27869, 27828, 27786, 27805, 27776, 27870, 27840, 27952, 27853, 27847, 27824, 27897, 27855, 27881, 27857, 28820, 28824, 28805, 28819, 28806, 28804, 28817, 28822, 28802, 28826, 28803, 29290, 29398, 29387, 29400, 29385, 29404, 29394, 29396, 29402, 29388, 29393, 29604, 29601, 29613, 29606, 29602, 29600, 29612, 29597, 29917, 29928, 30015, 30016, 30014, 30092, 30104, 30383, 30451, 30449, 30448, 30453, 30712, 30716, 30713, 30715, 30714, 30711, 31042, 31039, 31173, 31352, 31355, 31483, 31861, 31997, 32821, 32911, 32942, 32931, 32952, 32949, 32941, 33312, 33440, 33472, 33451, 33434, 33432, 33435, 33461, 33447, 33454, 33468, 33438, 33466, 33460, 33448, 33441, 33449, 33474, 33444, 33475, 33462, 33442, 34416, 34415, 34413, 34414, 35926, 36818, 36811, 36819, 36813, 36822, 36821, 36823, 37042, 37044, 37039, 37043, 37040, 38457, 38461, 38460, 38458, 38467, 20429, 20421, 20435, 20402, 20425, 20427, 20417, 20436, 20444, 20441, 20411, 20403, 20443, 20423, 20438, 20410, 20416, 20409, 20460, 21060, 21065, 21184, 21186, 21309, 21372, 21399, 21398, 21401, 21400, 21690, 21665, 21677, 21669, 21711, 21699, 33549, 21687, 21678, 21718, 21686, 21701, 21702, 21664, 21616, 21692, 21666, 21694, 21618, 21726, 21680, 22453, 22430, 22431, 22436, 22412, 22423, 22429, 22427, 22420, 22424, 22415, 22425, 22437, 22426, 22421, 22772, 22797, 22867, 23009, 23006, 23022, 23040, 23025, 23005, 23034, 23037, 23036, 23030, 23012, 23026, 23031, 23003, 23017, 23027, 23029, 23008, 23038, 23028, 23021, 23464, 23628, 23760, 23768, 23756, 23767, 23755, 23771, 23774, 23770, 23753, 23751, 23754, 23766, 23763, 23764, 23759, 23752, 23750, 23758, 23775, 23800, 24057, 24097, 24098, 24099, 24096, 24100, 24240, 24228, 24226, 24219, 24227, 24229, 24327, 24366, 24406, 24454, 24631, 24633, 24660, 24690, 24670, 24645, 24659, 24647, 24649, 24667, 24652, 24640, 24642, 24671, 24612, 24644, 24664, 24678, 24686, 25154, 25155, 25295, 25357, 25355, 25333, 25358, 25347, 25323, 25337, 25359, 25356, 25336, 25334, 25344, 25363, 25364, 25338, 25365, 25339, 25328, 25921, 25923, 26026, 26047, 26166, 26145, 26162, 26165, 26140, 26150, 26146, 26163, 26155, 26170, 26141, 26164, 26169, 26158, 26383, 26384, 26561, 26610, 26568, 26554, 26588, 26555, 26616, 26584, 26560, 26551, 26565, 26603, 26596, 26591, 26549, 26573, 26547, 26615, 26614, 26606, 26595, 26562, 26553, 26574, 26599, 26608, 26546, 26620, 26566, 26605, 26572, 26542, 26598, 26587, 26618, 26569, 26570, 26563, 26602, 26571, 27432, 27522, 27524, 27574, 27606, 27608, 27616, 27680, 27681, 27944, 27956, 27949, 27935, 27964, 27967, 27922, 27914, 27866, 27955, 27908, 27929, 27962, 27930, 27921, 27904, 27933, 27970, 27905, 27928, 27959, 27907, 27919, 27968, 27911, 27936, 27948, 27912, 27938, 27913, 27920, 28855, 28831, 28862, 28849, 28848, 28833, 28852, 28853, 28841, 29249, 29257, 29258, 29292, 29296, 29299, 29294, 29386, 29412, 29416, 29419, 29407, 29418, 29414, 29411, 29573, 29644, 29634, 29640, 29637, 29625, 29622, 29621, 29620, 29675, 29631, 29639, 29630, 29635, 29638, 29624, 29643, 29932, 29934, 29998, 30023, 30024, 30119, 30122, 30329, 30404, 30472, 30467, 30468, 30469, 30474, 30455, 30459, 30458, 30695, 30696, 30726, 30737, 30738, 30725, 30736, 30735, 30734, 30729, 30723, 30739, 31050, 31052, 31051, 31045, 31044, 31189, 31181, 31183, 31190, 31182, 31360, 31358, 31441, 31488, 31489, 31866, 31864, 31865, 31871, 31872, 31873, 32003, 32008, 32001, 32600, 32657, 32653, 32702, 32775, 32782, 32783, 32788, 32823, 32984, 32967, 32992, 32977, 32968, 32962, 32976, 32965, 32995, 32985, 32988, 32970, 32981, 32969, 32975, 32983, 32998, 32973, 33279, 33313, 33428, 33497, 33534, 33529, 33543, 33512, 33536, 33493, 33594, 33515, 33494, 33524, 33516, 33505, 33522, 33525, 33548, 33531, 33526, 33520, 33514, 33508, 33504, 33530, 33523, 33517, 34423, 34420, 34428, 34419, 34881, 34894, 34919, 34922, 34921, 35283, 35332, 35335, 36210, 36835, 36833, 36846, 36832, 37105, 37053, 37055, 37077, 37061, 37054, 37063, 37067, 37064, 37332, 37331, 38484, 38479, 38481, 38483, 38474, 38478, 20510, 20485, 20487, 20499, 20514, 20528, 20507, 20469, 20468, 20531, 20535, 20524, 20470, 20471, 20503, 20508, 20512, 20519, 20533, 20527, 20529, 20494, 20826, 20884, 20883, 20938, 20932, 20933, 20936, 20942, 21089, 21082, 21074, 21086, 21087, 21077, 21090, 21197, 21262, 21406, 21798, 21730, 21783, 21778, 21735, 21747, 21732, 21786, 21759, 21764, 21768, 21739, 21777, 21765, 21745, 21770, 21755, 21751, 21752, 21728, 21774, 21763, 21771, 22273, 22274, 22476, 22578, 22485, 22482, 22458, 22470, 22461, 22460, 22456, 22454, 22463, 22471, 22480, 22457, 22465, 22798, 22858, 23065, 23062, 23085, 23086, 23061, 23055, 23063, 23050, 23070, 23091, 23404, 23463, 23469, 23468, 23555, 23638, 23636, 23788, 23807, 23790, 23793, 23799, 23808, 23801, 24105, 24104, 24232, 24238, 24234, 24236, 24371, 24368, 24423, 24669, 24666, 24679, 24641, 24738, 24712, 24704, 24722, 24705, 24733, 24707, 24725, 24731, 24727, 24711, 24732, 24718, 25113, 25158, 25330, 25360, 25430, 25388, 25412, 25413, 25398, 25411, 25572, 25401, 25419, 25418, 25404, 25385, 25409, 25396, 25432, 25428, 25433, 25389, 25415, 25395, 25434, 25425, 25400, 25431, 25408, 25416, 25930, 25926, 26054, 26051, 26052, 26050, 26186, 26207, 26183, 26193, 26386, 26387, 26655, 26650, 26697, 26674, 26675, 26683, 26699, 26703, 26646, 26673, 26652, 26677, 26667, 26669, 26671, 26702, 26692, 26676, 26653, 26642, 26644, 26662, 26664, 26670, 26701, 26682, 26661, 26656, 27436, 27439, 27437, 27441, 27444, 27501, 32898, 27528, 27622, 27620, 27624, 27619, 27618, 27623, 27685, 28026, 28003, 28004, 28022, 27917, 28001, 28050, 27992, 28002, 28013, 28015, 28049, 28045, 28143, 28031, 28038, 27998, 28007, 28000, 28055, 28016, 28028, 27999, 28034, 28056, 27951, 28008, 28043, 28030, 28032, 28036, 27926, 28035, 28027, 28029, 28021, 28048, 28892, 28883, 28881, 28893, 28875, 32569, 28898, 28887, 28882, 28894, 28896, 28884, 28877, 28869, 28870, 28871, 28890, 28878, 28897, 29250, 29304, 29303, 29302, 29440, 29434, 29428, 29438, 29430, 29427, 29435, 29441, 29651, 29657, 29669, 29654, 29628, 29671, 29667, 29673, 29660, 29650, 29659, 29652, 29661, 29658, 29655, 29656, 29672, 29918, 29919, 29940, 29941, 29985, 30043, 30047, 30128, 30145, 30139, 30148, 30144, 30143, 30134, 30138, 30346, 30409, 30493, 30491, 30480, 30483, 30482, 30499, 30481, 30485, 30489, 30490, 30498, 30503, 30755, 30764, 30754, 30773, 30767, 30760, 30766, 30763, 30753, 30761, 30771, 30762, 30769, 31060, 31067, 31055, 31068, 31059, 31058, 31057, 31211, 31212, 31200, 31214, 31213, 31210, 31196, 31198, 31197, 31366, 31369, 31365, 31371, 31372, 31370, 31367, 31448, 31504, 31492, 31507, 31493, 31503, 31496, 31498, 31502, 31497, 31506, 31876, 31889, 31882, 31884, 31880, 31885, 31877, 32030, 32029, 32017, 32014, 32024, 32022, 32019, 32031, 32018, 32015, 32012, 32604, 32609, 32606, 32608, 32605, 32603, 32662, 32658, 32707, 32706, 32704, 32790, 32830, 32825, 33018, 33010, 33017, 33013, 33025, 33019, 33024, 33281, 33327, 33317, 33587, 33581, 33604, 33561, 33617, 33573, 33622, 33599, 33601, 33574, 33564, 33570, 33602, 33614, 33563, 33578, 33544, 33596, 33613, 33558, 33572, 33568, 33591, 33583, 33577, 33607, 33605, 33612, 33619, 33566, 33580, 33611, 33575, 33608, 34387, 34386, 34466, 34472, 34454, 34445, 34449, 34462, 34439, 34455, 34438, 34443, 34458, 34437, 34469, 34457, 34465, 34471, 34453, 34456, 34446, 34461, 34448, 34452, 34883, 34884, 34925, 34933, 34934, 34930, 34944, 34929, 34943, 34927, 34947, 34942, 34932, 34940, 35346, 35911, 35927, 35963, 36004, 36003, 36214, 36216, 36277, 36279, 36278, 36561, 36563, 36862, 36853, 36866, 36863, 36859, 36868, 36860, 36854, 37078, 37088, 37081, 37082, 37091, 37087, 37093, 37080, 37083, 37079, 37084, 37092, 37200, 37198, 37199, 37333, 37346, 37338, 38492, 38495, 38588, 39139, 39647, 39727, 20095, 20592, 20586, 20577, 20574, 20576, 20563, 20555, 20573, 20594, 20552, 20557, 20545, 20571, 20554, 20578, 20501, 20549, 20575, 20585, 20587, 20579, 20580, 20550, 20544, 20590, 20595, 20567, 20561, 20944, 21099, 21101, 21100, 21102, 21206, 21203, 21293, 21404, 21877, 21878, 21820, 21837, 21840, 21812, 21802, 21841, 21858, 21814, 21813, 21808, 21842, 21829, 21772, 21810, 21861, 21838, 21817, 21832, 21805, 21819, 21824, 21835, 22282, 22279, 22523, 22548, 22498, 22518, 22492, 22516, 22528, 22509, 22525, 22536, 22520, 22539, 22515, 22479, 22535, 22510, 22499, 22514, 22501, 22508, 22497, 22542, 22524, 22544, 22503, 22529, 22540, 22513, 22505, 22512, 22541, 22532, 22876, 23136, 23128, 23125, 23143, 23134, 23096, 23093, 23149, 23120, 23135, 23141, 23148, 23123, 23140, 23127, 23107, 23133, 23122, 23108, 23131, 23112, 23182, 23102, 23117, 23097, 23116, 23152, 23145, 23111, 23121, 23126, 23106, 23132, 23410, 23406, 23489, 23488, 23641, 23838, 23819, 23837, 23834, 23840, 23820, 23848, 23821, 23846, 23845, 23823, 23856, 23826, 23843, 23839, 23854, 24126, 24116, 24241, 24244, 24249, 24242, 24243, 24374, 24376, 24475, 24470, 24479, 24714, 24720, 24710, 24766, 24752, 24762, 24787, 24788, 24783, 24804, 24793, 24797, 24776, 24753, 24795, 24759, 24778, 24767, 24771, 24781, 24768, 25394, 25445, 25482, 25474, 25469, 25533, 25502, 25517, 25501, 25495, 25515, 25486, 25455, 25479, 25488, 25454, 25519, 25461, 25500, 25453, 25518, 25468, 25508, 25403, 25503, 25464, 25477, 25473, 25489, 25485, 25456, 25939, 26061, 26213, 26209, 26203, 26201, 26204, 26210, 26392, 26745, 26759, 26768, 26780, 26733, 26734, 26798, 26795, 26966, 26735, 26787, 26796, 26793, 26741, 26740, 26802, 26767, 26743, 26770, 26748, 26731, 26738, 26794, 26752, 26737, 26750, 26779, 26774, 26763, 26784, 26761, 26788, 26744, 26747, 26769, 26764, 26762, 26749, 27446, 27443, 27447, 27448, 27537, 27535, 27533, 27534, 27532, 27690, 28096, 28075, 28084, 28083, 28276, 28076, 28137, 28130, 28087, 28150, 28116, 28160, 28104, 28128, 28127, 28118, 28094, 28133, 28124, 28125, 28123, 28148, 28106, 28093, 28141, 28144, 28090, 28117, 28098, 28111, 28105, 28112, 28146, 28115, 28157, 28119, 28109, 28131, 28091, 28922, 28941, 28919, 28951, 28916, 28940, 28912, 28932, 28915, 28944, 28924, 28927, 28934, 28947, 28928, 28920, 28918, 28939, 28930, 28942, 29310, 29307, 29308, 29311, 29469, 29463, 29447, 29457, 29464, 29450, 29448, 29439, 29455, 29470, 29576, 29686, 29688, 29685, 29700, 29697, 29693, 29703, 29696, 29690, 29692, 29695, 29708, 29707, 29684, 29704, 30052, 30051, 30158, 30162, 30159, 30155, 30156, 30161, 30160, 30351, 30345, 30419, 30521, 30511, 30509, 30513, 30514, 30516, 30515, 30525, 30501, 30523, 30517, 30792, 30802, 30793, 30797, 30794, 30796, 30758, 30789, 30800, 31076, 31079, 31081, 31082, 31075, 31083, 31073, 31163, 31226, 31224, 31222, 31223, 31375, 31380, 31376, 31541, 31559, 31540, 31525, 31536, 31522, 31524, 31539, 31512, 31530, 31517, 31537, 31531, 31533, 31535, 31538, 31544, 31514, 31523, 31892, 31896, 31894, 31907, 32053, 32061, 32056, 32054, 32058, 32069, 32044, 32041, 32065, 32071, 32062, 32063, 32074, 32059, 32040, 32611, 32661, 32668, 32669, 32667, 32714, 32715, 32717, 32720, 32721, 32711, 32719, 32713, 32799, 32798, 32795, 32839, 32835, 32840, 33048, 33061, 33049, 33051, 33069, 33055, 33068, 33054, 33057, 33045, 33063, 33053, 33058, 33297, 33336, 33331, 33338, 33332, 33330, 33396, 33680, 33699, 33704, 33677, 33658, 33651, 33700, 33652, 33679, 33665, 33685, 33689, 33653, 33684, 33705, 33661, 33667, 33676, 33693, 33691, 33706, 33675, 33662, 33701, 33711, 33672, 33687, 33712, 33663, 33702, 33671, 33710, 33654, 33690, 34393, 34390, 34495, 34487, 34498, 34497, 34501, 34490, 34480, 34504, 34489, 34483, 34488, 34508, 34484, 34491, 34492, 34499, 34493, 34494, 34898, 34953, 34965, 34984, 34978, 34986, 34970, 34961, 34977, 34975, 34968, 34983, 34969, 34971, 34967, 34980, 34988, 34956, 34963, 34958, 35202, 35286, 35289, 35285, 35376, 35367, 35372, 35358, 35897, 35899, 35932, 35933, 35965, 36005, 36221, 36219, 36217, 36284, 36290, 36281, 36287, 36289, 36568, 36574, 36573, 36572, 36567, 36576, 36577, 36900, 36875, 36881, 36892, 36876, 36897, 37103, 37098, 37104, 37108, 37106, 37107, 37076, 37099, 37100, 37097, 37206, 37208, 37210, 37203, 37205, 37356, 37364, 37361, 37363, 37368, 37348, 37369, 37354, 37355, 37367, 37352, 37358, 38266, 38278, 38280, 38524, 38509, 38507, 38513, 38511, 38591, 38762, 38916, 39141, 39319, 20635, 20629, 20628, 20638, 20619, 20643, 20611, 20620, 20622, 20637, 20584, 20636, 20626, 20610, 20615, 20831, 20948, 21266, 21265, 21412, 21415, 21905, 21928, 21925, 21933, 21879, 22085, 21922, 21907, 21896, 21903, 21941, 21889, 21923, 21906, 21924, 21885, 21900, 21926, 21887, 21909, 21921, 21902, 22284, 22569, 22583, 22553, 22558, 22567, 22563, 22568, 22517, 22600, 22565, 22556, 22555, 22579, 22591, 22582, 22574, 22585, 22584, 22573, 22572, 22587, 22881, 23215, 23188, 23199, 23162, 23202, 23198, 23160, 23206, 23164, 23205, 23212, 23189, 23214, 23095, 23172, 23178, 23191, 23171, 23179, 23209, 23163, 23165, 23180, 23196, 23183, 23187, 23197, 23530, 23501, 23499, 23508, 23505, 23498, 23502, 23564, 23600, 23863, 23875, 23915, 23873, 23883, 23871, 23861, 23889, 23886, 23893, 23859, 23866, 23890, 23869, 23857, 23897, 23874, 23865, 23881, 23864, 23868, 23858, 23862, 23872, 23877, 24132, 24129, 24408, 24486, 24485, 24491, 24777, 24761, 24780, 24802, 24782, 24772, 24852, 24818, 24842, 24854, 24837, 24821, 24851, 24824, 24828, 24830, 24769, 24835, 24856, 24861, 24848, 24831, 24836, 24843, 25162, 25492, 25521, 25520, 25550, 25573, 25576, 25583, 25539, 25757, 25587, 25546, 25568, 25590, 25557, 25586, 25589, 25697, 25567, 25534, 25565, 25564, 25540, 25560, 25555, 25538, 25543, 25548, 25547, 25544, 25584, 25559, 25561, 25906, 25959, 25962, 25956, 25948, 25960, 25957, 25996, 26013, 26014, 26030, 26064, 26066, 26236, 26220, 26235, 26240, 26225, 26233, 26218, 26226, 26369, 26892, 26835, 26884, 26844, 26922, 26860, 26858, 26865, 26895, 26838, 26871, 26859, 26852, 26870, 26899, 26896, 26867, 26849, 26887, 26828, 26888, 26992, 26804, 26897, 26863, 26822, 26900, 26872, 26832, 26877, 26876, 26856, 26891, 26890, 26903, 26830, 26824, 26845, 26846, 26854, 26868, 26833, 26886, 26836, 26857, 26901, 26917, 26823, 27449, 27451, 27455, 27452, 27540, 27543, 27545, 27541, 27581, 27632, 27634, 27635, 27696, 28156, 28230, 28231, 28191, 28233, 28296, 28220, 28221, 28229, 28258, 28203, 28223, 28225, 28253, 28275, 28188, 28211, 28235, 28224, 28241, 28219, 28163, 28206, 28254, 28264, 28252, 28257, 28209, 28200, 28256, 28273, 28267, 28217, 28194, 28208, 28243, 28261, 28199, 28280, 28260, 28279, 28245, 28281, 28242, 28262, 28213, 28214, 28250, 28960, 28958, 28975, 28923, 28974, 28977, 28963, 28965, 28962, 28978, 28959, 28968, 28986, 28955, 29259, 29274, 29320, 29321, 29318, 29317, 29323, 29458, 29451, 29488, 29474, 29489, 29491, 29479, 29490, 29485, 29478, 29475, 29493, 29452, 29742, 29740, 29744, 29739, 29718, 29722, 29729, 29741, 29745, 29732, 29731, 29725, 29737, 29728, 29746, 29947, 29999, 30063, 30060, 30183, 30170, 30177, 30182, 30173, 30175, 30180, 30167, 30357, 30354, 30426, 30534, 30535, 30532, 30541, 30533, 30538, 30542, 30539, 30540, 30686, 30700, 30816, 30820, 30821, 30812, 30829, 30833, 30826, 30830, 30832, 30825, 30824, 30814, 30818, 31092, 31091, 31090, 31088, 31234, 31242, 31235, 31244, 31236, 31385, 31462, 31460, 31562, 31547, 31556, 31560, 31564, 31566, 31552, 31576, 31557, 31906, 31902, 31912, 31905, 32088, 32111, 32099, 32083, 32086, 32103, 32106, 32079, 32109, 32092, 32107, 32082, 32084, 32105, 32081, 32095, 32078, 32574, 32575, 32613, 32614, 32674, 32672, 32673, 32727, 32849, 32847, 32848, 33022, 32980, 33091, 33098, 33106, 33103, 33095, 33085, 33101, 33082, 33254, 33262, 33271, 33272, 33273, 33284, 33340, 33341, 33343, 33397, 33595, 33743, 33785, 33827, 33728, 33768, 33810, 33767, 33764, 33788, 33782, 33808, 33734, 33736, 33771, 33763, 33727, 33793, 33757, 33765, 33752, 33791, 33761, 33739, 33742, 33750, 33781, 33737, 33801, 33807, 33758, 33809, 33798, 33730, 33779, 33749, 33786, 33735, 33745, 33770, 33811, 33731, 33772, 33774, 33732, 33787, 33751, 33762, 33819, 33755, 33790, 34520, 34530, 34534, 34515, 34531, 34522, 34538, 34525, 34539, 34524, 34540, 34537, 34519, 34536, 34513, 34888, 34902, 34901, 35002, 35031, 35001, 35000, 35008, 35006, 34998, 35004, 34999, 35005, 34994, 35073, 35017, 35221, 35224, 35223, 35293, 35290, 35291, 35406, 35405, 35385, 35417, 35392, 35415, 35416, 35396, 35397, 35410, 35400, 35409, 35402, 35404, 35407, 35935, 35969, 35968, 36026, 36030, 36016, 36025, 36021, 36228, 36224, 36233, 36312, 36307, 36301, 36295, 36310, 36316, 36303, 36309, 36313, 36296, 36311, 36293, 36591, 36599, 36602, 36601, 36582, 36590, 36581, 36597, 36583, 36584, 36598, 36587, 36593, 36588, 36596, 36585, 36909, 36916, 36911, 37126, 37164, 37124, 37119, 37116, 37128, 37113, 37115, 37121, 37120, 37127, 37125, 37123, 37217, 37220, 37215, 37218, 37216, 37377, 37386, 37413, 37379, 37402, 37414, 37391, 37388, 37376, 37394, 37375, 37373, 37382, 37380, 37415, 37378, 37404, 37412, 37401, 37399, 37381, 37398, 38267, 38285, 38284, 38288, 38535, 38526, 38536, 38537, 38531, 38528, 38594, 38600, 38595, 38641, 38640, 38764, 38768, 38766, 38919, 39081, 39147, 40166, 40697, 20099, 20100, 20150, 20669, 20671, 20678, 20654, 20676, 20682, 20660, 20680, 20674, 20656, 20673, 20666, 20657, 20683, 20681, 20662, 20664, 20951, 21114, 21112, 21115, 21116, 21955, 21979, 21964, 21968, 21963, 21962, 21981, 21952, 21972, 21956, 21993, 21951, 21970, 21901, 21967, 21973, 21986, 21974, 21960, 22002, 21965, 21977, 21954, 22292, 22611, 22632, 22628, 22607, 22605, 22601, 22639, 22613, 22606, 22621, 22617, 22629, 22619, 22589, 22627, 22641, 22780, 23239, 23236, 23243, 23226, 23224, 23217, 23221, 23216, 23231, 23240, 23227, 23238, 23223, 23232, 23242, 23220, 23222, 23245, 23225, 23184, 23510, 23512, 23513, 23583, 23603, 23921, 23907, 23882, 23909, 23922, 23916, 23902, 23912, 23911, 23906, 24048, 24143, 24142, 24138, 24141, 24139, 24261, 24268, 24262, 24267, 24263, 24384, 24495, 24493, 24823, 24905, 24906, 24875, 24901, 24886, 24882, 24878, 24902, 24879, 24911, 24873, 24896, 25120, 37224, 25123, 25125, 25124, 25541, 25585, 25579, 25616, 25618, 25609, 25632, 25636, 25651, 25667, 25631, 25621, 25624, 25657, 25655, 25634, 25635, 25612, 25638, 25648, 25640, 25665, 25653, 25647, 25610, 25626, 25664, 25637, 25639, 25611, 25575, 25627, 25646, 25633, 25614, 25967, 26002, 26067, 26246, 26252, 26261, 26256, 26251, 26250, 26265, 26260, 26232, 26400, 26982, 26975, 26936, 26958, 26978, 26993, 26943, 26949, 26986, 26937, 26946, 26967, 26969, 27002, 26952, 26953, 26933, 26988, 26931, 26941, 26981, 26864, 27000, 26932, 26985, 26944, 26991, 26948, 26998, 26968, 26945, 26996, 26956, 26939, 26955, 26935, 26972, 26959, 26961, 26930, 26962, 26927, 27003, 26940, 27462, 27461, 27459, 27458, 27464, 27457, 27547, 64013, 27643, 27644, 27641, 27639, 27640, 28315, 28374, 28360, 28303, 28352, 28319, 28307, 28308, 28320, 28337, 28345, 28358, 28370, 28349, 28353, 28318, 28361, 28343, 28336, 28365, 28326, 28367, 28338, 28350, 28355, 28380, 28376, 28313, 28306, 28302, 28301, 28324, 28321, 28351, 28339, 28368, 28362, 28311, 28334, 28323, 28999, 29012, 29010, 29027, 29024, 28993, 29021, 29026, 29042, 29048, 29034, 29025, 28994, 29016, 28995, 29003, 29040, 29023, 29008, 29011, 28996, 29005, 29018, 29263, 29325, 29324, 29329, 29328, 29326, 29500, 29506, 29499, 29498, 29504, 29514, 29513, 29764, 29770, 29771, 29778, 29777, 29783, 29760, 29775, 29776, 29774, 29762, 29766, 29773, 29780, 29921, 29951, 29950, 29949, 29981, 30073, 30071, 27011, 30191, 30223, 30211, 30199, 30206, 30204, 30201, 30200, 30224, 30203, 30198, 30189, 30197, 30205, 30361, 30389, 30429, 30549, 30559, 30560, 30546, 30550, 30554, 30569, 30567, 30548, 30553, 30573, 30688, 30855, 30874, 30868, 30863, 30852, 30869, 30853, 30854, 30881, 30851, 30841, 30873, 30848, 30870, 30843, 31100, 31106, 31101, 31097, 31249, 31256, 31257, 31250, 31255, 31253, 31266, 31251, 31259, 31248, 31395, 31394, 31390, 31467, 31590, 31588, 31597, 31604, 31593, 31602, 31589, 31603, 31601, 31600, 31585, 31608, 31606, 31587, 31922, 31924, 31919, 32136, 32134, 32128, 32141, 32127, 32133, 32122, 32142, 32123, 32131, 32124, 32140, 32148, 32132, 32125, 32146, 32621, 32619, 32615, 32616, 32620, 32678, 32677, 32679, 32731, 32732, 32801, 33124, 33120, 33143, 33116, 33129, 33115, 33122, 33138, 26401, 33118, 33142, 33127, 33135, 33092, 33121, 33309, 33353, 33348, 33344, 33346, 33349, 34033, 33855, 33878, 33910, 33913, 33935, 33933, 33893, 33873, 33856, 33926, 33895, 33840, 33869, 33917, 33882, 33881, 33908, 33907, 33885, 34055, 33886, 33847, 33850, 33844, 33914, 33859, 33912, 33842, 33861, 33833, 33753, 33867, 33839, 33858, 33837, 33887, 33904, 33849, 33870, 33868, 33874, 33903, 33989, 33934, 33851, 33863, 33846, 33843, 33896, 33918, 33860, 33835, 33888, 33876, 33902, 33872, 34571, 34564, 34551, 34572, 34554, 34518, 34549, 34637, 34552, 34574, 34569, 34561, 34550, 34573, 34565, 35030, 35019, 35021, 35022, 35038, 35035, 35034, 35020, 35024, 35205, 35227, 35295, 35301, 35300, 35297, 35296, 35298, 35292, 35302, 35446, 35462, 35455, 35425, 35391, 35447, 35458, 35460, 35445, 35459, 35457, 35444, 35450, 35900, 35915, 35914, 35941, 35940, 35942, 35974, 35972, 35973, 36044, 36200, 36201, 36241, 36236, 36238, 36239, 36237, 36243, 36244, 36240, 36242, 36336, 36320, 36332, 36337, 36334, 36304, 36329, 36323, 36322, 36327, 36338, 36331, 36340, 36614, 36607, 36609, 36608, 36613, 36615, 36616, 36610, 36619, 36946, 36927, 36932, 36937, 36925, 37136, 37133, 37135, 37137, 37142, 37140, 37131, 37134, 37230, 37231, 37448, 37458, 37424, 37434, 37478, 37427, 37477, 37470, 37507, 37422, 37450, 37446, 37485, 37484, 37455, 37472, 37479, 37487, 37430, 37473, 37488, 37425, 37460, 37475, 37456, 37490, 37454, 37459, 37452, 37462, 37426, 38303, 38300, 38302, 38299, 38546, 38547, 38545, 38551, 38606, 38650, 38653, 38648, 38645, 38771, 38775, 38776, 38770, 38927, 38925, 38926, 39084, 39158, 39161, 39343, 39346, 39344, 39349, 39597, 39595, 39771, 40170, 40173, 40167, 40576, 40701, 20710, 20692, 20695, 20712, 20723, 20699, 20714, 20701, 20708, 20691, 20716, 20720, 20719, 20707, 20704, 20952, 21120, 21121, 21225, 21227, 21296, 21420, 22055, 22037, 22028, 22034, 22012, 22031, 22044, 22017, 22035, 22018, 22010, 22045, 22020, 22015, 22009, 22665, 22652, 22672, 22680, 22662, 22657, 22655, 22644, 22667, 22650, 22663, 22673, 22670, 22646, 22658, 22664, 22651, 22676, 22671, 22782, 22891, 23260, 23278, 23269, 23253, 23274, 23258, 23277, 23275, 23283, 23266, 23264, 23259, 23276, 23262, 23261, 23257, 23272, 23263, 23415, 23520, 23523, 23651, 23938, 23936, 23933, 23942, 23930, 23937, 23927, 23946, 23945, 23944, 23934, 23932, 23949, 23929, 23935, 24152, 24153, 24147, 24280, 24273, 24279, 24270, 24284, 24277, 24281, 24274, 24276, 24388, 24387, 24431, 24502, 24876, 24872, 24897, 24926, 24945, 24947, 24914, 24915, 24946, 24940, 24960, 24948, 24916, 24954, 24923, 24933, 24891, 24938, 24929, 24918, 25129, 25127, 25131, 25643, 25677, 25691, 25693, 25716, 25718, 25714, 25715, 25725, 25717, 25702, 25766, 25678, 25730, 25694, 25692, 25675, 25683, 25696, 25680, 25727, 25663, 25708, 25707, 25689, 25701, 25719, 25971, 26016, 26273, 26272, 26271, 26373, 26372, 26402, 27057, 27062, 27081, 27040, 27086, 27030, 27056, 27052, 27068, 27025, 27033, 27022, 27047, 27021, 27049, 27070, 27055, 27071, 27076, 27069, 27044, 27092, 27065, 27082, 27034, 27087, 27059, 27027, 27050, 27041, 27038, 27097, 27031, 27024, 27074, 27061, 27045, 27078, 27466, 27469, 27467, 27550, 27551, 27552, 27587, 27588, 27646, 28366, 28405, 28401, 28419, 28453, 28408, 28471, 28411, 28462, 28425, 28494, 28441, 28442, 28455, 28440, 28475, 28434, 28397, 28426, 28470, 28531, 28409, 28398, 28461, 28480, 28464, 28476, 28469, 28395, 28423, 28430, 28483, 28421, 28413, 28406, 28473, 28444, 28412, 28474, 28447, 28429, 28446, 28424, 28449, 29063, 29072, 29065, 29056, 29061, 29058, 29071, 29051, 29062, 29057, 29079, 29252, 29267, 29335, 29333, 29331, 29507, 29517, 29521, 29516, 29794, 29811, 29809, 29813, 29810, 29799, 29806, 29952, 29954, 29955, 30077, 30096, 30230, 30216, 30220, 30229, 30225, 30218, 30228, 30392, 30593, 30588, 30597, 30594, 30574, 30592, 30575, 30590, 30595, 30898, 30890, 30900, 30893, 30888, 30846, 30891, 30878, 30885, 30880, 30892, 30882, 30884, 31128, 31114, 31115, 31126, 31125, 31124, 31123, 31127, 31112, 31122, 31120, 31275, 31306, 31280, 31279, 31272, 31270, 31400, 31403, 31404, 31470, 31624, 31644, 31626, 31633, 31632, 31638, 31629, 31628, 31643, 31630, 31621, 31640, 21124, 31641, 31652, 31618, 31931, 31935, 31932, 31930, 32167, 32183, 32194, 32163, 32170, 32193, 32192, 32197, 32157, 32206, 32196, 32198, 32203, 32204, 32175, 32185, 32150, 32188, 32159, 32166, 32174, 32169, 32161, 32201, 32627, 32738, 32739, 32741, 32734, 32804, 32861, 32860, 33161, 33158, 33155, 33159, 33165, 33164, 33163, 33301, 33943, 33956, 33953, 33951, 33978, 33998, 33986, 33964, 33966, 33963, 33977, 33972, 33985, 33997, 33962, 33946, 33969, 34000, 33949, 33959, 33979, 33954, 33940, 33991, 33996, 33947, 33961, 33967, 33960, 34006, 33944, 33974, 33999, 33952, 34007, 34004, 34002, 34011, 33968, 33937, 34401, 34611, 34595, 34600, 34667, 34624, 34606, 34590, 34593, 34585, 34587, 34627, 34604, 34625, 34622, 34630, 34592, 34610, 34602, 34605, 34620, 34578, 34618, 34609, 34613, 34626, 34598, 34599, 34616, 34596, 34586, 34608, 34577, 35063, 35047, 35057, 35058, 35066, 35070, 35054, 35068, 35062, 35067, 35056, 35052, 35051, 35229, 35233, 35231, 35230, 35305, 35307, 35304, 35499, 35481, 35467, 35474, 35471, 35478, 35901, 35944, 35945, 36053, 36047, 36055, 36246, 36361, 36354, 36351, 36365, 36349, 36362, 36355, 36359, 36358, 36357, 36350, 36352, 36356, 36624, 36625, 36622, 36621, 37155, 37148, 37152, 37154, 37151, 37149, 37146, 37156, 37153, 37147, 37242, 37234, 37241, 37235, 37541, 37540, 37494, 37531, 37498, 37536, 37524, 37546, 37517, 37542, 37530, 37547, 37497, 37527, 37503, 37539, 37614, 37518, 37506, 37525, 37538, 37501, 37512, 37537, 37514, 37510, 37516, 37529, 37543, 37502, 37511, 37545, 37533, 37515, 37421, 38558, 38561, 38655, 38744, 38781, 38778, 38782, 38787, 38784, 38786, 38779, 38788, 38785, 38783, 38862, 38861, 38934, 39085, 39086, 39170, 39168, 39175, 39325, 39324, 39363, 39353, 39355, 39354, 39362, 39357, 39367, 39601, 39651, 39655, 39742, 39743, 39776, 39777, 39775, 40177, 40178, 40181, 40615, 20735, 20739, 20784, 20728, 20742, 20743, 20726, 20734, 20747, 20748, 20733, 20746, 21131, 21132, 21233, 21231, 22088, 22082, 22092, 22069, 22081, 22090, 22089, 22086, 22104, 22106, 22080, 22067, 22077, 22060, 22078, 22072, 22058, 22074, 22298, 22699, 22685, 22705, 22688, 22691, 22703, 22700, 22693, 22689, 22783, 23295, 23284, 23293, 23287, 23286, 23299, 23288, 23298, 23289, 23297, 23303, 23301, 23311, 23655, 23961, 23959, 23967, 23954, 23970, 23955, 23957, 23968, 23964, 23969, 23962, 23966, 24169, 24157, 24160, 24156, 32243, 24283, 24286, 24289, 24393, 24498, 24971, 24963, 24953, 25009, 25008, 24994, 24969, 24987, 24979, 25007, 25005, 24991, 24978, 25002, 24993, 24973, 24934, 25011, 25133, 25710, 25712, 25750, 25760, 25733, 25751, 25756, 25743, 25739, 25738, 25740, 25763, 25759, 25704, 25777, 25752, 25974, 25978, 25977, 25979, 26034, 26035, 26293, 26288, 26281, 26290, 26295, 26282, 26287, 27136, 27142, 27159, 27109, 27128, 27157, 27121, 27108, 27168, 27135, 27116, 27106, 27163, 27165, 27134, 27175, 27122, 27118, 27156, 27127, 27111, 27200, 27144, 27110, 27131, 27149, 27132, 27115, 27145, 27140, 27160, 27173, 27151, 27126, 27174, 27143, 27124, 27158, 27473, 27557, 27555, 27554, 27558, 27649, 27648, 27647, 27650, 28481, 28454, 28542, 28551, 28614, 28562, 28557, 28553, 28556, 28514, 28495, 28549, 28506, 28566, 28534, 28524, 28546, 28501, 28530, 28498, 28496, 28503, 28564, 28563, 28509, 28416, 28513, 28523, 28541, 28519, 28560, 28499, 28555, 28521, 28543, 28565, 28515, 28535, 28522, 28539, 29106, 29103, 29083, 29104, 29088, 29082, 29097, 29109, 29085, 29093, 29086, 29092, 29089, 29098, 29084, 29095, 29107, 29336, 29338, 29528, 29522, 29534, 29535, 29536, 29533, 29531, 29537, 29530, 29529, 29538, 29831, 29833, 29834, 29830, 29825, 29821, 29829, 29832, 29820, 29817, 29960, 29959, 30078, 30245, 30238, 30233, 30237, 30236, 30243, 30234, 30248, 30235, 30364, 30365, 30366, 30363, 30605, 30607, 30601, 30600, 30925, 30907, 30927, 30924, 30929, 30926, 30932, 30920, 30915, 30916, 30921, 31130, 31137, 31136, 31132, 31138, 31131, 27510, 31289, 31410, 31412, 31411, 31671, 31691, 31678, 31660, 31694, 31663, 31673, 31690, 31669, 31941, 31944, 31948, 31947, 32247, 32219, 32234, 32231, 32215, 32225, 32259, 32250, 32230, 32246, 32241, 32240, 32238, 32223, 32630, 32684, 32688, 32685, 32749, 32747, 32746, 32748, 32742, 32744, 32868, 32871, 33187, 33183, 33182, 33173, 33186, 33177, 33175, 33302, 33359, 33363, 33362, 33360, 33358, 33361, 34084, 34107, 34063, 34048, 34089, 34062, 34057, 34061, 34079, 34058, 34087, 34076, 34043, 34091, 34042, 34056, 34060, 34036, 34090, 34034, 34069, 34039, 34027, 34035, 34044, 34066, 34026, 34025, 34070, 34046, 34088, 34077, 34094, 34050, 34045, 34078, 34038, 34097, 34086, 34023, 34024, 34032, 34031, 34041, 34072, 34080, 34096, 34059, 34073, 34095, 34402, 34646, 34659, 34660, 34679, 34785, 34675, 34648, 34644, 34651, 34642, 34657, 34650, 34641, 34654, 34669, 34666, 34640, 34638, 34655, 34653, 34671, 34668, 34682, 34670, 34652, 34661, 34639, 34683, 34677, 34658, 34663, 34665, 34906, 35077, 35084, 35092, 35083, 35095, 35096, 35097, 35078, 35094, 35089, 35086, 35081, 35234, 35236, 35235, 35309, 35312, 35308, 35535, 35526, 35512, 35539, 35537, 35540, 35541, 35515, 35543, 35518, 35520, 35525, 35544, 35523, 35514, 35517, 35545, 35902, 35917, 35983, 36069, 36063, 36057, 36072, 36058, 36061, 36071, 36256, 36252, 36257, 36251, 36384, 36387, 36389, 36388, 36398, 36373, 36379, 36374, 36369, 36377, 36390, 36391, 36372, 36370, 36376, 36371, 36380, 36375, 36378, 36652, 36644, 36632, 36634, 36640, 36643, 36630, 36631, 36979, 36976, 36975, 36967, 36971, 37167, 37163, 37161, 37162, 37170, 37158, 37166, 37253, 37254, 37258, 37249, 37250, 37252, 37248, 37584, 37571, 37572, 37568, 37593, 37558, 37583, 37617, 37599, 37592, 37609, 37591, 37597, 37580, 37615, 37570, 37608, 37578, 37576, 37582, 37606, 37581, 37589, 37577, 37600, 37598, 37607, 37585, 37587, 37557, 37601, 37574, 37556, 38268, 38316, 38315, 38318, 38320, 38564, 38562, 38611, 38661, 38664, 38658, 38746, 38794, 38798, 38792, 38864, 38863, 38942, 38941, 38950, 38953, 38952, 38944, 38939, 38951, 39090, 39176, 39162, 39185, 39188, 39190, 39191, 39189, 39388, 39373, 39375, 39379, 39380, 39374, 39369, 39382, 39384, 39371, 39383, 39372, 39603, 39660, 39659, 39667, 39666, 39665, 39750, 39747, 39783, 39796, 39793, 39782, 39798, 39797, 39792, 39784, 39780, 39788, 40188, 40186, 40189, 40191, 40183, 40199, 40192, 40185, 40187, 40200, 40197, 40196, 40579, 40659, 40719, 40720, 20764, 20755, 20759, 20762, 20753, 20958, 21300, 21473, 22128, 22112, 22126, 22131, 22118, 22115, 22125, 22130, 22110, 22135, 22300, 22299, 22728, 22717, 22729, 22719, 22714, 22722, 22716, 22726, 23319, 23321, 23323, 23329, 23316, 23315, 23312, 23318, 23336, 23322, 23328, 23326, 23535, 23980, 23985, 23977, 23975, 23989, 23984, 23982, 23978, 23976, 23986, 23981, 23983, 23988, 24167, 24168, 24166, 24175, 24297, 24295, 24294, 24296, 24293, 24395, 24508, 24989, 25000, 24982, 25029, 25012, 25030, 25025, 25036, 25018, 25023, 25016, 24972, 25815, 25814, 25808, 25807, 25801, 25789, 25737, 25795, 25819, 25843, 25817, 25907, 25983, 25980, 26018, 26312, 26302, 26304, 26314, 26315, 26319, 26301, 26299, 26298, 26316, 26403, 27188, 27238, 27209, 27239, 27186, 27240, 27198, 27229, 27245, 27254, 27227, 27217, 27176, 27226, 27195, 27199, 27201, 27242, 27236, 27216, 27215, 27220, 27247, 27241, 27232, 27196, 27230, 27222, 27221, 27213, 27214, 27206, 27477, 27476, 27478, 27559, 27562, 27563, 27592, 27591, 27652, 27651, 27654, 28589, 28619, 28579, 28615, 28604, 28622, 28616, 28510, 28612, 28605, 28574, 28618, 28584, 28676, 28581, 28590, 28602, 28588, 28586, 28623, 28607, 28600, 28578, 28617, 28587, 28621, 28591, 28594, 28592, 29125, 29122, 29119, 29112, 29142, 29120, 29121, 29131, 29140, 29130, 29127, 29135, 29117, 29144, 29116, 29126, 29146, 29147, 29341, 29342, 29545, 29542, 29543, 29548, 29541, 29547, 29546, 29823, 29850, 29856, 29844, 29842, 29845, 29857, 29963, 30080, 30255, 30253, 30257, 30269, 30259, 30268, 30261, 30258, 30256, 30395, 30438, 30618, 30621, 30625, 30620, 30619, 30626, 30627, 30613, 30617, 30615, 30941, 30953, 30949, 30954, 30942, 30947, 30939, 30945, 30946, 30957, 30943, 30944, 31140, 31300, 31304, 31303, 31414, 31416, 31413, 31409, 31415, 31710, 31715, 31719, 31709, 31701, 31717, 31706, 31720, 31737, 31700, 31722, 31714, 31708, 31723, 31704, 31711, 31954, 31956, 31959, 31952, 31953, 32274, 32289, 32279, 32268, 32287, 32288, 32275, 32270, 32284, 32277, 32282, 32290, 32267, 32271, 32278, 32269, 32276, 32293, 32292, 32579, 32635, 32636, 32634, 32689, 32751, 32810, 32809, 32876, 33201, 33190, 33198, 33209, 33205, 33195, 33200, 33196, 33204, 33202, 33207, 33191, 33266, 33365, 33366, 33367, 34134, 34117, 34155, 34125, 34131, 34145, 34136, 34112, 34118, 34148, 34113, 34146, 34116, 34129, 34119, 34147, 34110, 34139, 34161, 34126, 34158, 34165, 34133, 34151, 34144, 34188, 34150, 34141, 34132, 34149, 34156, 34403, 34405, 34404, 34715, 34703, 34711, 34707, 34706, 34696, 34689, 34710, 34712, 34681, 34695, 34723, 34693, 34704, 34705, 34717, 34692, 34708, 34716, 34714, 34697, 35102, 35110, 35120, 35117, 35118, 35111, 35121, 35106, 35113, 35107, 35119, 35116, 35103, 35313, 35552, 35554, 35570, 35572, 35573, 35549, 35604, 35556, 35551, 35568, 35528, 35550, 35553, 35560, 35583, 35567, 35579, 35985, 35986, 35984, 36085, 36078, 36081, 36080, 36083, 36204, 36206, 36261, 36263, 36403, 36414, 36408, 36416, 36421, 36406, 36412, 36413, 36417, 36400, 36415, 36541, 36662, 36654, 36661, 36658, 36665, 36663, 36660, 36982, 36985, 36987, 36998, 37114, 37171, 37173, 37174, 37267, 37264, 37265, 37261, 37263, 37671, 37662, 37640, 37663, 37638, 37647, 37754, 37688, 37692, 37659, 37667, 37650, 37633, 37702, 37677, 37646, 37645, 37579, 37661, 37626, 37669, 37651, 37625, 37623, 37684, 37634, 37668, 37631, 37673, 37689, 37685, 37674, 37652, 37644, 37643, 37630, 37641, 37632, 37627, 37654, 38332, 38349, 38334, 38329, 38330, 38326, 38335, 38325, 38333, 38569, 38612, 38667, 38674, 38672, 38809, 38807, 38804, 38896, 38904, 38965, 38959, 38962, 39204, 39199, 39207, 39209, 39326, 39406, 39404, 39397, 39396, 39408, 39395, 39402, 39401, 39399, 39609, 39615, 39604, 39611, 39670, 39674, 39673, 39671, 39731, 39808, 39813, 39815, 39804, 39806, 39803, 39810, 39827, 39826, 39824, 39802, 39829, 39805, 39816, 40229, 40215, 40224, 40222, 40212, 40233, 40221, 40216, 40226, 40208, 40217, 40223, 40584, 40582, 40583, 40622, 40621, 40661, 40662, 40698, 40722, 40765, 20774, 20773, 20770, 20772, 20768, 20777, 21236, 22163, 22156, 22157, 22150, 22148, 22147, 22142, 22146, 22143, 22145, 22742, 22740, 22735, 22738, 23341, 23333, 23346, 23331, 23340, 23335, 23334, 23343, 23342, 23419, 23537, 23538, 23991, 24172, 24170, 24510, 24507, 25027, 25013, 25020, 25063, 25056, 25061, 25060, 25064, 25054, 25839, 25833, 25827, 25835, 25828, 25832, 25985, 25984, 26038, 26074, 26322, 27277, 27286, 27265, 27301, 27273, 27295, 27291, 27297, 27294, 27271, 27283, 27278, 27285, 27267, 27304, 27300, 27281, 27263, 27302, 27290, 27269, 27276, 27282, 27483, 27565, 27657, 28620, 28585, 28660, 28628, 28643, 28636, 28653, 28647, 28646, 28638, 28658, 28637, 28642, 28648, 29153, 29169, 29160, 29170, 29156, 29168, 29154, 29555, 29550, 29551, 29847, 29874, 29867, 29840, 29866, 29869, 29873, 29861, 29871, 29968, 29969, 29970, 29967, 30084, 30275, 30280, 30281, 30279, 30372, 30441, 30645, 30635, 30642, 30647, 30646, 30644, 30641, 30632, 30704, 30963, 30973, 30978, 30971, 30972, 30962, 30981, 30969, 30974, 30980, 31147, 31144, 31324, 31323, 31318, 31320, 31316, 31322, 31422, 31424, 31425, 31749, 31759, 31730, 31744, 31743, 31739, 31758, 31732, 31755, 31731, 31746, 31753, 31747, 31745, 31736, 31741, 31750, 31728, 31729, 31760, 31754, 31976, 32301, 32316, 32322, 32307, 38984, 32312, 32298, 32329, 32320, 32327, 32297, 32332, 32304, 32315, 32310, 32324, 32314, 32581, 32639, 32638, 32637, 32756, 32754, 32812, 33211, 33220, 33228, 33226, 33221, 33223, 33212, 33257, 33371, 33370, 33372, 34179, 34176, 34191, 34215, 34197, 34208, 34187, 34211, 34171, 34212, 34202, 34206, 34167, 34172, 34185, 34209, 34170, 34168, 34135, 34190, 34198, 34182, 34189, 34201, 34205, 34177, 34210, 34178, 34184, 34181, 34169, 34166, 34200, 34192, 34207, 34408, 34750, 34730, 34733, 34757, 34736, 34732, 34745, 34741, 34748, 34734, 34761, 34755, 34754, 34764, 34743, 34735, 34756, 34762, 34740, 34742, 34751, 34744, 34749, 34782, 34738, 35125, 35123, 35132, 35134, 35137, 35154, 35127, 35138, 35245, 35247, 35246, 35314, 35315, 35614, 35608, 35606, 35601, 35589, 35595, 35618, 35599, 35602, 35605, 35591, 35597, 35592, 35590, 35612, 35603, 35610, 35919, 35952, 35954, 35953, 35951, 35989, 35988, 36089, 36207, 36430, 36429, 36435, 36432, 36428, 36423, 36675, 36672, 36997, 36990, 37176, 37274, 37282, 37275, 37273, 37279, 37281, 37277, 37280, 37793, 37763, 37807, 37732, 37718, 37703, 37756, 37720, 37724, 37750, 37705, 37712, 37713, 37728, 37741, 37775, 37708, 37738, 37753, 37719, 37717, 37714, 37711, 37745, 37751, 37755, 37729, 37726, 37731, 37735, 37760, 37710, 37721, 38343, 38336, 38345, 38339, 38341, 38327, 38574, 38576, 38572, 38688, 38687, 38680, 38685, 38681, 38810, 38817, 38812, 38814, 38813, 38869, 38868, 38897, 38977, 38980, 38986, 38985, 38981, 38979, 39205, 39211, 39212, 39210, 39219, 39218, 39215, 39213, 39217, 39216, 39320, 39331, 39329, 39426, 39418, 39412, 39415, 39417, 39416, 39414, 39419, 39421, 39422, 39420, 39427, 39614, 39678, 39677, 39681, 39676, 39752, 39834, 39848, 39838, 39835, 39846, 39841, 39845, 39844, 39814, 39842, 39840, 39855, 40243, 40257, 40295, 40246, 40238, 40239, 40241, 40248, 40240, 40261, 40258, 40259, 40254, 40247, 40256, 40253, 32757, 40237, 40586, 40585, 40589, 40624, 40648, 40666, 40699, 40703, 40740, 40739, 40738, 40788, 40864, 20785, 20781, 20782, 22168, 22172, 22167, 22170, 22173, 22169, 22896, 23356, 23657, 23658, 24000, 24173, 24174, 25048, 25055, 25069, 25070, 25073, 25066, 25072, 25067, 25046, 25065, 25855, 25860, 25853, 25848, 25857, 25859, 25852, 26004, 26075, 26330, 26331, 26328, 27333, 27321, 27325, 27361, 27334, 27322, 27318, 27319, 27335, 27316, 27309, 27486, 27593, 27659, 28679, 28684, 28685, 28673, 28677, 28692, 28686, 28671, 28672, 28667, 28710, 28668, 28663, 28682, 29185, 29183, 29177, 29187, 29181, 29558, 29880, 29888, 29877, 29889, 29886, 29878, 29883, 29890, 29972, 29971, 30300, 30308, 30297, 30288, 30291, 30295, 30298, 30374, 30397, 30444, 30658, 30650, 30975, 30988, 30995, 30996, 30985, 30992, 30994, 30993, 31149, 31148, 31327, 31772, 31785, 31769, 31776, 31775, 31789, 31773, 31782, 31784, 31778, 31781, 31792, 32348, 32336, 32342, 32355, 32344, 32354, 32351, 32337, 32352, 32343, 32339, 32693, 32691, 32759, 32760, 32885, 33233, 33234, 33232, 33375, 33374, 34228, 34246, 34240, 34243, 34242, 34227, 34229, 34237, 34247, 34244, 34239, 34251, 34254, 34248, 34245, 34225, 34230, 34258, 34340, 34232, 34231, 34238, 34409, 34791, 34790, 34786, 34779, 34795, 34794, 34789, 34783, 34803, 34788, 34772, 34780, 34771, 34797, 34776, 34787, 34724, 34775, 34777, 34817, 34804, 34792, 34781, 35155, 35147, 35151, 35148, 35142, 35152, 35153, 35145, 35626, 35623, 35619, 35635, 35632, 35637, 35655, 35631, 35644, 35646, 35633, 35621, 35639, 35622, 35638, 35630, 35620, 35643, 35645, 35642, 35906, 35957, 35993, 35992, 35991, 36094, 36100, 36098, 36096, 36444, 36450, 36448, 36439, 36438, 36446, 36453, 36455, 36443, 36442, 36449, 36445, 36457, 36436, 36678, 36679, 36680, 36683, 37160, 37178, 37179, 37182, 37288, 37285, 37287, 37295, 37290, 37813, 37772, 37778, 37815, 37787, 37789, 37769, 37799, 37774, 37802, 37790, 37798, 37781, 37768, 37785, 37791, 37773, 37809, 37777, 37810, 37796, 37800, 37812, 37795, 37797, 38354, 38355, 38353, 38579, 38615, 38618, 24002, 38623, 38616, 38621, 38691, 38690, 38693, 38828, 38830, 38824, 38827, 38820, 38826, 38818, 38821, 38871, 38873, 38870, 38872, 38906, 38992, 38993, 38994, 39096, 39233, 39228, 39226, 39439, 39435, 39433, 39437, 39428, 39441, 39434, 39429, 39431, 39430, 39616, 39644, 39688, 39684, 39685, 39721, 39733, 39754, 39756, 39755, 39879, 39878, 39875, 39871, 39873, 39861, 39864, 39891, 39862, 39876, 39865, 39869, 40284, 40275, 40271, 40266, 40283, 40267, 40281, 40278, 40268, 40279, 40274, 40276, 40287, 40280, 40282, 40590, 40588, 40671, 40705, 40704, 40726, 40741, 40747, 40746, 40745, 40744, 40780, 40789, 20788, 20789, 21142, 21239, 21428, 22187, 22189, 22182, 22183, 22186, 22188, 22746, 22749, 22747, 22802, 23357, 23358, 23359, 24003, 24176, 24511, 25083, 25863, 25872, 25869, 25865, 25868, 25870, 25988, 26078, 26077, 26334, 27367, 27360, 27340, 27345, 27353, 27339, 27359, 27356, 27344, 27371, 27343, 27341, 27358, 27488, 27568, 27660, 28697, 28711, 28704, 28694, 28715, 28705, 28706, 28707, 28713, 28695, 28708, 28700, 28714, 29196, 29194, 29191, 29186, 29189, 29349, 29350, 29348, 29347, 29345, 29899, 29893, 29879, 29891, 29974, 30304, 30665, 30666, 30660, 30705, 31005, 31003, 31009, 31004, 30999, 31006, 31152, 31335, 31336, 31795, 31804, 31801, 31788, 31803, 31980, 31978, 32374, 32373, 32376, 32368, 32375, 32367, 32378, 32370, 32372, 32360, 32587, 32586, 32643, 32646, 32695, 32765, 32766, 32888, 33239, 33237, 33380, 33377, 33379, 34283, 34289, 34285, 34265, 34273, 34280, 34266, 34263, 34284, 34290, 34296, 34264, 34271, 34275, 34268, 34257, 34288, 34278, 34287, 34270, 34274, 34816, 34810, 34819, 34806, 34807, 34825, 34828, 34827, 34822, 34812, 34824, 34815, 34826, 34818, 35170, 35162, 35163, 35159, 35169, 35164, 35160, 35165, 35161, 35208, 35255, 35254, 35318, 35664, 35656, 35658, 35648, 35667, 35670, 35668, 35659, 35669, 35665, 35650, 35666, 35671, 35907, 35959, 35958, 35994, 36102, 36103, 36105, 36268, 36266, 36269, 36267, 36461, 36472, 36467, 36458, 36463, 36475, 36546, 36690, 36689, 36687, 36688, 36691, 36788, 37184, 37183, 37296, 37293, 37854, 37831, 37839, 37826, 37850, 37840, 37881, 37868, 37836, 37849, 37801, 37862, 37834, 37844, 37870, 37859, 37845, 37828, 37838, 37824, 37842, 37863, 38269, 38362, 38363, 38625, 38697, 38699, 38700, 38696, 38694, 38835, 38839, 38838, 38877, 38878, 38879, 39004, 39001, 39005, 38999, 39103, 39101, 39099, 39102, 39240, 39239, 39235, 39334, 39335, 39450, 39445, 39461, 39453, 39460, 39451, 39458, 39456, 39463, 39459, 39454, 39452, 39444, 39618, 39691, 39690, 39694, 39692, 39735, 39914, 39915, 39904, 39902, 39908, 39910, 39906, 39920, 39892, 39895, 39916, 39900, 39897, 39909, 39893, 39905, 39898, 40311, 40321, 40330, 40324, 40328, 40305, 40320, 40312, 40326, 40331, 40332, 40317, 40299, 40308, 40309, 40304, 40297, 40325, 40307, 40315, 40322, 40303, 40313, 40319, 40327, 40296, 40596, 40593, 40640, 40700, 40749, 40768, 40769, 40781, 40790, 40791, 40792, 21303, 22194, 22197, 22195, 22755, 23365, 24006, 24007, 24302, 24303, 24512, 24513, 25081, 25879, 25878, 25877, 25875, 26079, 26344, 26339, 26340, 27379, 27376, 27370, 27368, 27385, 27377, 27374, 27375, 28732, 28725, 28719, 28727, 28724, 28721, 28738, 28728, 28735, 28730, 28729, 28736, 28731, 28723, 28737, 29203, 29204, 29352, 29565, 29564, 29882, 30379, 30378, 30398, 30445, 30668, 30670, 30671, 30669, 30706, 31013, 31011, 31015, 31016, 31012, 31017, 31154, 31342, 31340, 31341, 31479, 31817, 31816, 31818, 31815, 31813, 31982, 32379, 32382, 32385, 32384, 32698, 32767, 32889, 33243, 33241, 33291, 33384, 33385, 34338, 34303, 34305, 34302, 34331, 34304, 34294, 34308, 34313, 34309, 34316, 34301, 34841, 34832, 34833, 34839, 34835, 34838, 35171, 35174, 35257, 35319, 35680, 35690, 35677, 35688, 35683, 35685, 35687, 35693, 36270, 36486, 36488, 36484, 36697, 36694, 36695, 36693, 36696, 36698, 37005, 37187, 37185, 37303, 37301, 37298, 37299, 37899, 37907, 37883, 37920, 37903, 37908, 37886, 37909, 37904, 37928, 37913, 37901, 37877, 37888, 37879, 37895, 37902, 37910, 37906, 37882, 37897, 37880, 37898, 37887, 37884, 37900, 37878, 37905, 37894, 38366, 38368, 38367, 38702, 38703, 38841, 38843, 38909, 38910, 39008, 39010, 39011, 39007, 39105, 39106, 39248, 39246, 39257, 39244, 39243, 39251, 39474, 39476, 39473, 39468, 39466, 39478, 39465, 39470, 39480, 39469, 39623, 39626, 39622, 39696, 39698, 39697, 39947, 39944, 39927, 39941, 39954, 39928, 40000, 39943, 39950, 39942, 39959, 39956, 39945, 40351, 40345, 40356, 40349, 40338, 40344, 40336, 40347, 40352, 40340, 40348, 40362, 40343, 40353, 40346, 40354, 40360, 40350, 40355, 40383, 40361, 40342, 40358, 40359, 40601, 40603, 40602, 40677, 40676, 40679, 40678, 40752, 40750, 40795, 40800, 40798, 40797, 40793, 40849, 20794, 20793, 21144, 21143, 22211, 22205, 22206, 23368, 23367, 24011, 24015, 24305, 25085, 25883, 27394, 27388, 27395, 27384, 27392, 28739, 28740, 28746, 28744, 28745, 28741, 28742, 29213, 29210, 29209, 29566, 29975, 30314, 30672, 31021, 31025, 31023, 31828, 31827, 31986, 32394, 32391, 32392, 32395, 32390, 32397, 32589, 32699, 32816, 33245, 34328, 34346, 34342, 34335, 34339, 34332, 34329, 34343, 34350, 34337, 34336, 34345, 34334, 34341, 34857, 34845, 34843, 34848, 34852, 34844, 34859, 34890, 35181, 35177, 35182, 35179, 35322, 35705, 35704, 35653, 35706, 35707, 36112, 36116, 36271, 36494, 36492, 36702, 36699, 36701, 37190, 37188, 37189, 37305, 37951, 37947, 37942, 37929, 37949, 37948, 37936, 37945, 37930, 37943, 37932, 37952, 37937, 38373, 38372, 38371, 38709, 38714, 38847, 38881, 39012, 39113, 39110, 39104, 39256, 39254, 39481, 39485, 39494, 39492, 39490, 39489, 39482, 39487, 39629, 39701, 39703, 39704, 39702, 39738, 39762, 39979, 39965, 39964, 39980, 39971, 39976, 39977, 39972, 39969, 40375, 40374, 40380, 40385, 40391, 40394, 40399, 40382, 40389, 40387, 40379, 40373, 40398, 40377, 40378, 40364, 40392, 40369, 40365, 40396, 40371, 40397, 40370, 40570, 40604, 40683, 40686, 40685, 40731, 40728, 40730, 40753, 40782, 40805, 40804, 40850, 20153, 22214, 22213, 22219, 22897, 23371, 23372, 24021, 24017, 24306, 25889, 25888, 25894, 25890, 27403, 27400, 27401, 27661, 28757, 28758, 28759, 28754, 29214, 29215, 29353, 29567, 29912, 29909, 29913, 29911, 30317, 30381, 31029, 31156, 31344, 31345, 31831, 31836, 31833, 31835, 31834, 31988, 31985, 32401, 32591, 32647, 33246, 33387, 34356, 34357, 34355, 34348, 34354, 34358, 34860, 34856, 34854, 34858, 34853, 35185, 35263, 35262, 35323, 35710, 35716, 35714, 35718, 35717, 35711, 36117, 36501, 36500, 36506, 36498, 36496, 36502, 36503, 36704, 36706, 37191, 37964, 37968, 37962, 37963, 37967, 37959, 37957, 37960, 37961, 37958, 38719, 38883, 39018, 39017, 39115, 39252, 39259, 39502, 39507, 39508, 39500, 39503, 39496, 39498, 39497, 39506, 39504, 39632, 39705, 39723, 39739, 39766, 39765, 40006, 40008, 39999, 40004, 39993, 39987, 40001, 39996, 39991, 39988, 39986, 39997, 39990, 40411, 40402, 40414, 40410, 40395, 40400, 40412, 40401, 40415, 40425, 40409, 40408, 40406, 40437, 40405, 40413, 40630, 40688, 40757, 40755, 40754, 40770, 40811, 40853, 40866, 20797, 21145, 22760, 22759, 22898, 23373, 24024, 34863, 24399, 25089, 25091, 25092, 25897, 25893, 26006, 26347, 27409, 27410, 27407, 27594, 28763, 28762, 29218, 29570, 29569, 29571, 30320, 30676, 31847, 31846, 32405, 33388, 34362, 34368, 34361, 34364, 34353, 34363, 34366, 34864, 34866, 34862, 34867, 35190, 35188, 35187, 35326, 35724, 35726, 35723, 35720, 35909, 36121, 36504, 36708, 36707, 37308, 37986, 37973, 37981, 37975, 37982, 38852, 38853, 38912, 39510, 39513, 39710, 39711, 39712, 40018, 40024, 40016, 40010, 40013, 40011, 40021, 40025, 40012, 40014, 40443, 40439, 40431, 40419, 40427, 40440, 40420, 40438, 40417, 40430, 40422, 40434, 40432, 40418, 40428, 40436, 40435, 40424, 40429, 40642, 40656, 40690, 40691, 40710, 40732, 40760, 40759, 40758, 40771, 40783, 40817, 40816, 40814, 40815, 22227, 22221, 23374, 23661, 25901, 26349, 26350, 27411, 28767, 28769, 28765, 28768, 29219, 29915, 29925, 30677, 31032, 31159, 31158, 31850, 32407, 32649, 33389, 34371, 34872, 34871, 34869, 34891, 35732, 35733, 36510, 36511, 36512, 36509, 37310, 37309, 37314, 37995, 37992, 37993, 38629, 38726, 38723, 38727, 38855, 38885, 39518, 39637, 39769, 40035, 40039, 40038, 40034, 40030, 40032, 40450, 40446, 40455, 40451, 40454, 40453, 40448, 40449, 40457, 40447, 40445, 40452, 40608, 40734, 40774, 40820, 40821, 40822, 22228, 25902, 26040, 27416, 27417, 27415, 27418, 28770, 29222, 29354, 30680, 30681, 31033, 31849, 31851, 31990, 32410, 32408, 32411, 32409, 33248, 33249, 34374, 34375, 34376, 35193, 35194, 35196, 35195, 35327, 35736, 35737, 36517, 36516, 36515, 37998, 37997, 37999, 38001, 38003, 38729, 39026, 39263, 40040, 40046, 40045, 40459, 40461, 40464, 40463, 40466, 40465, 40609, 40693, 40713, 40775, 40824, 40827, 40826, 40825, 22302, 28774, 31855, 34876, 36274, 36518, 37315, 38004, 38008, 38006, 38005, 39520, 40052, 40051, 40049, 40053, 40468, 40467, 40694, 40714, 40868, 28776, 28773, 31991, 34410, 34878, 34877, 34879, 35742, 35996, 36521, 36553, 38731, 39027, 39028, 39116, 39265, 39339, 39524, 39526, 39527, 39716, 40469, 40471, 40776, 25095, 27422, 29223, 34380, 36520, 38018, 38016, 38017, 39529, 39528, 39726, 40473, 29225, 34379, 35743, 38019, 40057, 40631, 30325, 39531, 40058, 40477, 28777, 28778, 40612, 40830, 40777, 40856, 30849, 37561, 35023, 22715, 24658, 31911, 23290, 9556, 9574, 9559, 9568, 9580, 9571, 9562, 9577, 9565, 9554, 9572, 9557, 9566, 9578, 9569, 9560, 9575, 9563, 9555, 9573, 9558, 9567, 9579, 9570, 9561, 9576, 9564, 9553, 9552, 9581, 9582, 9584, 9583, 65517, 132423, 37595, 132575, 147397, 34124, 17077, 29679, 20917, 13897, 149826, 166372, 37700, 137691, 33518, 146632, 30780, 26436, 25311, 149811, 166314, 131744, 158643, 135941, 20395, 140525, 20488, 159017, 162436, 144896, 150193, 140563, 20521, 131966, 24484, 131968, 131911, 28379, 132127, 20605, 20737, 13434, 20750, 39020, 14147, 33814, 149924, 132231, 20832, 144308, 20842, 134143, 139516, 131813, 140592, 132494, 143923, 137603, 23426, 34685, 132531, 146585, 20914, 20920, 40244, 20937, 20943, 20945, 15580, 20947, 150182, 20915, 20962, 21314, 20973, 33741, 26942, 145197, 24443, 21003, 21030, 21052, 21173, 21079, 21140, 21177, 21189, 31765, 34114, 21216, 34317, 158483, 21253, 166622, 21833, 28377, 147328, 133460, 147436, 21299, 21316, 134114, 27851, 136998, 26651, 29653, 24650, 16042, 14540, 136936, 29149, 17570, 21357, 21364, 165547, 21374, 21375, 136598, 136723, 30694, 21395, 166555, 21408, 21419, 21422, 29607, 153458, 16217, 29596, 21441, 21445, 27721, 20041, 22526, 21465, 15019, 134031, 21472, 147435, 142755, 21494, 134263, 21523, 28793, 21803, 26199, 27995, 21613, 158547, 134516, 21853, 21647, 21668, 18342, 136973, 134877, 15796, 134477, 166332, 140952, 21831, 19693, 21551, 29719, 21894, 21929, 22021, 137431, 147514, 17746, 148533, 26291, 135348, 22071, 26317, 144010, 26276, 26285, 22093, 22095, 30961, 22257, 38791, 21502, 22272, 22255, 22253, 166758, 13859, 135759, 22342, 147877, 27758, 28811, 22338, 14001, 158846, 22502, 136214, 22531, 136276, 148323, 22566, 150517, 22620, 22698, 13665, 22752, 22748, 135740, 22779, 23551, 22339, 172368, 148088, 37843, 13729, 22815, 26790, 14019, 28249, 136766, 23076, 21843, 136850, 34053, 22985, 134478, 158849, 159018, 137180, 23001, 137211, 137138, 159142, 28017, 137256, 136917, 23033, 159301, 23211, 23139, 14054, 149929, 23159, 14088, 23190, 29797, 23251, 159649, 140628, 15749, 137489, 14130, 136888, 24195, 21200, 23414, 25992, 23420, 162318, 16388, 18525, 131588, 23509, 24928, 137780, 154060, 132517, 23539, 23453, 19728, 23557, 138052, 23571, 29646, 23572, 138405, 158504, 23625, 18653, 23685, 23785, 23791, 23947, 138745, 138807, 23824, 23832, 23878, 138916, 23738, 24023, 33532, 14381, 149761, 139337, 139635, 33415, 14390, 15298, 24110, 27274, 24181, 24186, 148668, 134355, 21414, 20151, 24272, 21416, 137073, 24073, 24308, 164994, 24313, 24315, 14496, 24316, 26686, 37915, 24333, 131521, 194708, 15070, 18606, 135994, 24378, 157832, 140240, 24408, 140401, 24419, 38845, 159342, 24434, 37696, 166454, 24487, 23990, 15711, 152144, 139114, 159992, 140904, 37334, 131742, 166441, 24625, 26245, 137335, 14691, 15815, 13881, 22416, 141236, 31089, 15936, 24734, 24740, 24755, 149890, 149903, 162387, 29860, 20705, 23200, 24932, 33828, 24898, 194726, 159442, 24961, 20980, 132694, 24967, 23466, 147383, 141407, 25043, 166813, 170333, 25040, 14642, 141696, 141505, 24611, 24924, 25886, 25483, 131352, 25285, 137072, 25301, 142861, 25452, 149983, 14871, 25656, 25592, 136078, 137212, 25744, 28554, 142902, 38932, 147596, 153373, 25825, 25829, 38011, 14950, 25658, 14935, 25933, 28438, 150056, 150051, 25989, 25965, 25951, 143486, 26037, 149824, 19255, 26065, 16600, 137257, 26080, 26083, 24543, 144384, 26136, 143863, 143864, 26180, 143780, 143781, 26187, 134773, 26215, 152038, 26227, 26228, 138813, 143921, 165364, 143816, 152339, 30661, 141559, 39332, 26370, 148380, 150049, 15147, 27130, 145346, 26462, 26471, 26466, 147917, 168173, 26583, 17641, 26658, 28240, 37436, 26625, 144358, 159136, 26717, 144495, 27105, 27147, 166623, 26995, 26819, 144845, 26881, 26880, 15666, 14849, 144956, 15232, 26540, 26977, 166474, 17148, 26934, 27032, 15265, 132041, 33635, 20624, 27129, 144985, 139562, 27205, 145155, 27293, 15347, 26545, 27336, 168348, 15373, 27421, 133411, 24798, 27445, 27508, 141261, 28341, 146139, 132021, 137560, 14144, 21537, 146266, 27617, 147196, 27612, 27703, 140427, 149745, 158545, 27738, 33318, 27769, 146876, 17605, 146877, 147876, 149772, 149760, 146633, 14053, 15595, 134450, 39811, 143865, 140433, 32655, 26679, 159013, 159137, 159211, 28054, 27996, 28284, 28420, 149887, 147589, 159346, 34099, 159604, 20935, 27804, 28189, 33838, 166689, 28207, 146991, 29779, 147330, 31180, 28239, 23185, 143435, 28664, 14093, 28573, 146992, 28410, 136343, 147517, 17749, 37872, 28484, 28508, 15694, 28532, 168304, 15675, 28575, 147780, 28627, 147601, 147797, 147513, 147440, 147380, 147775, 20959, 147798, 147799, 147776, 156125, 28747, 28798, 28839, 28801, 28876, 28885, 28886, 28895, 16644, 15848, 29108, 29078, 148087, 28971, 28997, 23176, 29002, 29038, 23708, 148325, 29007, 37730, 148161, 28972, 148570, 150055, 150050, 29114, 166888, 28861, 29198, 37954, 29205, 22801, 37955, 29220, 37697, 153093, 29230, 29248, 149876, 26813, 29269, 29271, 15957, 143428, 26637, 28477, 29314, 29482, 29483, 149539, 165931, 18669, 165892, 29480, 29486, 29647, 29610, 134202, 158254, 29641, 29769, 147938, 136935, 150052, 26147, 14021, 149943, 149901, 150011, 29687, 29717, 26883, 150054, 29753, 132547, 16087, 29788, 141485, 29792, 167602, 29767, 29668, 29814, 33721, 29804, 14128, 29812, 37873, 27180, 29826, 18771, 150156, 147807, 150137, 166799, 23366, 166915, 137374, 29896, 137608, 29966, 29929, 29982, 167641, 137803, 23511, 167596, 37765, 30029, 30026, 30055, 30062, 151426, 16132, 150803, 30094, 29789, 30110, 30132, 30210, 30252, 30289, 30287, 30319, 30326, 156661, 30352, 33263, 14328, 157969, 157966, 30369, 30373, 30391, 30412, 159647, 33890, 151709, 151933, 138780, 30494, 30502, 30528, 25775, 152096, 30552, 144044, 30639, 166244, 166248, 136897, 30708, 30729, 136054, 150034, 26826, 30895, 30919, 30931, 38565, 31022, 153056, 30935, 31028, 30897, 161292, 36792, 34948, 166699, 155779, 140828, 31110, 35072, 26882, 31104, 153687, 31133, 162617, 31036, 31145, 28202, 160038, 16040, 31174, 168205, 31188], + "euc-kr":[44034,44035,44037,44038,44043,44044,44045,44046,44047,44056,44062,44063,44065,44066,44067,44069,44070,44071,44072,44073,44074,44075,44078,44082,44083,44084,44085,44086,44087,44090,44091,44093,44094,44095,44097,44098,44099,44100,44101,44102,44103,44104,44105,44106,44108,44110,44111,44112,44113,44114,44115,44117,44118,44119,44121,44122,44123,44125,44126,44127,44128,44129,44130,44131,44132,44133,44134,44135,44136,44137,44138,44139,44140,44141,44142,44143,44146,44147,44149,44150,44153,44155,44156,44157,44158,44159,44162,44167,44168,44173,44174,44175,44177,44178,44179,44181,44182,44183,44184,44185,44186,44187,44190,44194,44195,44196,44197,44198,44199,44203,44205,44206,44209,44210,44211,44212,44213,44214,44215,44218,44222,44223,44224,44226,44227,44229,44230,44231,44233,44234,44235,44237,44238,44239,44240,44241,44242,44243,44244,44246,44248,44249,44250,44251,44252,44253,44254,44255,44258,44259,44261,44262,44265,44267,44269,44270,44274,44276,44279,44280,44281,44282,44283,44286,44287,44289,44290,44291,44293,44295,44296,44297,44298,44299,44302,44304,44306,44307,44308,44309,44310,44311,44313,44314,44315,44317,44318,44319,44321,44322,44323,44324,44325,44326,44327,44328,44330,44331,44334,44335,44336,44337,44338,44339,44342,44343,44345,44346,44347,44349,44350,44351,44352,44353,44354,44355,44358,44360,44362,44363,44364,44365,44366,44367,44369,44370,44371,44373,44374,44375,44377,44378,44379,44380,44381,44382,44383,44384,44386,44388,44389,44390,44391,44392,44393,44394,44395,44398,44399,44401,44402,44407,44408,44409,44410,44414,44416,44419,44420,44421,44422,44423,44426,44427,44429,44430,44431,44433,44434,44435,44436,44437,44438,44439,44440,44441,44442,44443,44446,44447,44448,44449,44450,44451,44453,44454,44455,44456,44457,44458,44459,44460,44461,44462,44463,44464,44465,44466,44467,44468,44469,44470,44472,44473,44474,44475,44476,44477,44478,44479,44482,44483,44485,44486,44487,44489,44490,44491,44492,44493,44494,44495,44498,44500,44501,44502,44503,44504,44505,44506,44507,44509,44510,44511,44513,44514,44515,44517,44518,44519,44520,44521,44522,44523,44524,44525,44526,44527,44528,44529,44530,44531,44532,44533,44534,44535,44538,44539,44541,44542,44546,44547,44548,44549,44550,44551,44554,44556,44558,44559,44560,44561,44562,44563,44565,44566,44567,44568,44569,44570,44571,44572,44573,44574,44575,44576,44577,44578,44579,44580,44581,44582,44583,44584,44585,44586,44587,44588,44589,44590,44591,44594,44595,44597,44598,44601,44603,44604,44605,44606,44607,44610,44612,44615,44616,44617,44619,44623,44625,44626,44627,44629,44631,44632,44633,44634,44635,44638,44642,44643,44644,44646,44647,44650,44651,44653,44654,44655,44657,44658,44659,44660,44661,44662,44663,44666,44670,44671,44672,44673,44674,44675,44678,44679,44680,44681,44682,44683,44685,44686,44687,44688,44689,44690,44691,44692,44693,44694,44695,44696,44697,44698,44699,44700,44701,44702,44703,44704,44705,44706,44707,44708,44709,44710,44711,44712,44713,44714,44715,44716,44717,44718,44719,44720,44721,44722,44723,44724,44725,44726,44727,44728,44729,44730,44731,44735,44737,44738,44739,44741,44742,44743,44744,44745,44746,44747,44750,44754,44755,44756,44757,44758,44759,44762,44763,44765,44766,44767,44768,44769,44770,44771,44772,44773,44774,44775,44777,44778,44780,44782,44783,44784,44785,44786,44787,44789,44790,44791,44793,44794,44795,44797,44798,44799,44800,44801,44802,44803,44804,44805,44806,44809,44810,44811,44812,44814,44815,44817,44818,44819,44820,44821,44822,44823,44824,44825,44826,44827,44828,44829,44830,44831,44832,44833,44834,44835,44836,44837,44838,44839,44840,44841,44842,44843,44846,44847,44849,44851,44853,44854,44855,44856,44857,44858,44859,44862,44864,44868,44869,44870,44871,44874,44875,44876,44877,44878,44879,44881,44882,44883,44884,44885,44886,44887,44888,44889,44890,44891,44894,44895,44896,44897,44898,44899,44902,44903,44904,44905,44906,44907,44908,44909,44910,44911,44912,44913,44914,44915,44916,44917,44918,44919,44920,44922,44923,44924,44925,44926,44927,44929,44930,44931,44933,44934,44935,44937,44938,44939,44940,44941,44942,44943,44946,44947,44948,44950,44951,44952,44953,44954,44955,44957,44958,44959,44960,44961,44962,44963,44964,44965,44966,44967,44968,44969,44970,44971,44972,44973,44974,44975,44976,44977,44978,44979,44980,44981,44982,44983,44986,44987,44989,44990,44991,44993,44994,44995,44996,44997,44998,45002,45004,45007,45008,45009,45010,45011,45013,45014,45015,45016,45017,45018,45019,45021,45022,45023,45024,45025,45026,45027,45028,45029,45030,45031,45034,45035,45036,45037,45038,45039,45042,45043,45045,45046,45047,45049,45050,45051,45052,45053,45054,45055,45058,45059,45061,45062,45063,45064,45065,45066,45067,45069,45070,45071,45073,45074,45075,45077,45078,45079,45080,45081,45082,45083,45086,45087,45088,45089,45090,45091,45092,45093,45094,45095,45097,45098,45099,45100,45101,45102,45103,45104,45105,45106,45107,45108,45109,45110,45111,45112,45113,45114,45115,45116,45117,45118,45119,45120,45121,45122,45123,45126,45127,45129,45131,45133,45135,45136,45137,45138,45142,45144,45146,45147,45148,45150,45151,45152,45153,45154,45155,45156,45157,45158,45159,45160,45161,45162,45163,45164,45165,45166,45167,45168,45169,45170,45171,45172,45173,45174,45175,45176,45177,45178,45179,45182,45183,45185,45186,45187,45189,45190,45191,45192,45193,45194,45195,45198,45200,45202,45203,45204,45205,45206,45207,45211,45213,45214,45219,45220,45221,45222,45223,45226,45232,45234,45238,45239,45241,45242,45243,45245,45246,45247,45248,45249,45250,45251,45254,45258,45259,45260,45261,45262,45263,45266,45267,45269,45270,45271,45273,45274,45275,45276,45277,45278,45279,45281,45282,45283,45284,45286,45287,45288,45289,45290,45291,45292,45293,45294,45295,45296,45297,45298,45299,45300,45301,45302,45303,45304,45305,45306,45307,45308,45309,45310,45311,45312,45313,45314,45315,45316,45317,45318,45319,45322,45325,45326,45327,45329,45332,45333,45334,45335,45338,45342,45343,45344,45345,45346,45350,45351,45353,45354,45355,45357,45358,45359,45360,45361,45362,45363,45366,45370,45371,45372,45373,45374,45375,45378,45379,45381,45382,45383,45385,45386,45387,45388,45389,45390,45391,45394,45395,45398,45399,45401,45402,45403,45405,45406,45407,45409,45410,45411,45412,45413,45414,45415,45416,45417,45418,45419,45420,45421,45422,45423,45424,45425,45426,45427,45428,45429,45430,45431,45434,45435,45437,45438,45439,45441,45443,45444,45445,45446,45447,45450,45452,45454,45455,45456,45457,45461,45462,45463,45465,45466,45467,45469,45470,45471,45472,45473,45474,45475,45476,45477,45478,45479,45481,45482,45483,45484,45485,45486,45487,45488,45489,45490,45491,45492,45493,45494,45495,45496,45497,45498,45499,45500,45501,45502,45503,45504,45505,45506,45507,45508,45509,45510,45511,45512,45513,45514,45515,45517,45518,45519,45521,45522,45523,45525,45526,45527,45528,45529,45530,45531,45534,45536,45537,45538,45539,45540,45541,45542,45543,45546,45547,45549,45550,45551,45553,45554,45555,45556,45557,45558,45559,45560,45562,45564,45566,45567,45568,45569,45570,45571,45574,45575,45577,45578,45581,45582,45583,45584,45585,45586,45587,45590,45592,45594,45595,45596,45597,45598,45599,45601,45602,45603,45604,45605,45606,45607,45608,45609,45610,45611,45612,45613,45614,45615,45616,45617,45618,45619,45621,45622,45623,45624,45625,45626,45627,45629,45630,45631,45632,45633,45634,45635,45636,45637,45638,45639,45640,45641,45642,45643,45644,45645,45646,45647,45648,45649,45650,45651,45652,45653,45654,45655,45657,45658,45659,45661,45662,45663,45665,45666,45667,45668,45669,45670,45671,45674,45675,45676,45677,45678,45679,45680,45681,45682,45683,45686,45687,45688,45689,45690,45691,45693,45694,45695,45696,45697,45698,45699,45702,45703,45704,45706,45707,45708,45709,45710,45711,45714,45715,45717,45718,45719,45723,45724,45725,45726,45727,45730,45732,45735,45736,45737,45739,45741,45742,45743,45745,45746,45747,45749,45750,45751,45752,45753,45754,45755,45756,45757,45758,45759,45760,45761,45762,45763,45764,45765,45766,45767,45770,45771,45773,45774,45775,45777,45779,45780,45781,45782,45783,45786,45788,45790,45791,45792,45793,45795,45799,45801,45802,45808,45809,45810,45814,45820,45821,45822,45826,45827,45829,45830,45831,45833,45834,45835,45836,45837,45838,45839,45842,45846,45847,45848,45849,45850,45851,45853,45854,45855,45856,45857,45858,45859,45860,45861,45862,45863,45864,45865,45866,45867,45868,45869,45870,45871,45872,45873,45874,45875,45876,45877,45878,45879,45880,45881,45882,45883,45884,45885,45886,45887,45888,45889,45890,45891,45892,45893,45894,45895,45896,45897,45898,45899,45900,45901,45902,45903,45904,45905,45906,45907,45911,45913,45914,45917,45920,45921,45922,45923,45926,45928,45930,45932,45933,45935,45938,45939,45941,45942,45943,45945,45946,45947,45948,45949,45950,45951,45954,45958,45959,45960,45961,45962,45963,45965,45966,45967,45969,45970,45971,45973,45974,45975,45976,45977,45978,45979,45980,45981,45982,45983,45986,45987,45988,45989,45990,45991,45993,45994,45995,45997,45998,45999,46000,46001,46002,46003,46004,46005,46006,46007,46008,46009,46010,46011,46012,46013,46014,46015,46016,46017,46018,46019,46022,46023,46025,46026,46029,46031,46033,46034,46035,46038,46040,46042,46044,46046,46047,46049,46050,46051,46053,46054,46055,46057,46058,46059,46060,46061,46062,46063,46064,46065,46066,46067,46068,46069,46070,46071,46072,46073,46074,46075,46077,46078,46079,46080,46081,46082,46083,46084,46085,46086,46087,46088,46089,46090,46091,46092,46093,46094,46095,46097,46098,46099,46100,46101,46102,46103,46105,46106,46107,46109,46110,46111,46113,46114,46115,46116,46117,46118,46119,46122,46124,46125,46126,46127,46128,46129,46130,46131,46133,46134,46135,46136,46137,46138,46139,46140,46141,46142,46143,46144,46145,46146,46147,46148,46149,46150,46151,46152,46153,46154,46155,46156,46157,46158,46159,46162,46163,46165,46166,46167,46169,46170,46171,46172,46173,46174,46175,46178,46180,46182,46183,46184,46185,46186,46187,46189,46190,46191,46192,46193,46194,46195,46196,46197,46198,46199,46200,46201,46202,46203,46204,46205,46206,46207,46209,46210,46211,46212,46213,46214,46215,46217,46218,46219,46220,46221,46222,46223,46224,46225,46226,46227,46228,46229,46230,46231,46232,46233,46234,46235,46236,46238,46239,46240,46241,46242,46243,46245,46246,46247,46249,46250,46251,46253,46254,46255,46256,46257,46258,46259,46260,46262,46264,46266,46267,46268,46269,46270,46271,46273,46274,46275,46277,46278,46279,46281,46282,46283,46284,46285,46286,46287,46289,46290,46291,46292,46294,46295,46296,46297,46298,46299,46302,46303,46305,46306,46309,46311,46312,46313,46314,46315,46318,46320,46322,46323,46324,46325,46326,46327,46329,46330,46331,46332,46333,46334,46335,46336,46337,46338,46339,46340,46341,46342,46343,46344,46345,46346,46347,46348,46349,46350,46351,46352,46353,46354,46355,46358,46359,46361,46362,46365,46366,46367,46368,46369,46370,46371,46374,46379,46380,46381,46382,46383,46386,46387,46389,46390,46391,46393,46394,46395,46396,46397,46398,46399,46402,46406,46407,46408,46409,46410,46414,46415,46417,46418,46419,46421,46422,46423,46424,46425,46426,46427,46430,46434,46435,46436,46437,46438,46439,46440,46441,46442,46443,46444,46445,46446,46447,46448,46449,46450,46451,46452,46453,46454,46455,46456,46457,46458,46459,46460,46461,46462,46463,46464,46465,46466,46467,46468,46469,46470,46471,46472,46473,46474,46475,46476,46477,46478,46479,46480,46481,46482,46483,46484,46485,46486,46487,46488,46489,46490,46491,46492,46493,46494,46495,46498,46499,46501,46502,46503,46505,46508,46509,46510,46511,46514,46518,46519,46520,46521,46522,46526,46527,46529,46530,46531,46533,46534,46535,46536,46537,46538,46539,46542,46546,46547,46548,46549,46550,46551,46553,46554,46555,46556,46557,46558,46559,46560,46561,46562,46563,46564,46565,46566,46567,46568,46569,46570,46571,46573,46574,46575,46576,46577,46578,46579,46580,46581,46582,46583,46584,46585,46586,46587,46588,46589,46590,46591,46592,46593,46594,46595,46596,46597,46598,46599,46600,46601,46602,46603,46604,46605,46606,46607,46610,46611,46613,46614,46615,46617,46618,46619,46620,46621,46622,46623,46624,46625,46626,46627,46628,46630,46631,46632,46633,46634,46635,46637,46638,46639,46640,46641,46642,46643,46645,46646,46647,46648,46649,46650,46651,46652,46653,46654,46655,46656,46657,46658,46659,46660,46661,46662,46663,46665,46666,46667,46668,46669,46670,46671,46672,46673,46674,46675,46676,46677,46678,46679,46680,46681,46682,46683,46684,46685,46686,46687,46688,46689,46690,46691,46693,46694,46695,46697,46698,46699,46700,46701,46702,46703,46704,46705,46706,46707,46708,46709,46710,46711,46712,46713,46714,46715,46716,46717,46718,46719,46720,46721,46722,46723,46724,46725,46726,46727,46728,46729,46730,46731,46732,46733,46734,46735,46736,46737,46738,46739,46740,46741,46742,46743,46744,46745,46746,46747,46750,46751,46753,46754,46755,46757,46758,46759,46760,46761,46762,46765,46766,46767,46768,46770,46771,46772,46773,46774,46775,46776,46777,46778,46779,46780,46781,46782,46783,46784,46785,46786,46787,46788,46789,46790,46791,46792,46793,46794,46795,46796,46797,46798,46799,46800,46801,46802,46803,46805,46806,46807,46808,46809,46810,46811,46812,46813,46814,46815,46816,46817,46818,46819,46820,46821,46822,46823,46824,46825,46826,46827,46828,46829,46830,46831,46833,46834,46835,46837,46838,46839,46841,46842,46843,46844,46845,46846,46847,46850,46851,46852,46854,46855,46856,46857,46858,46859,46860,46861,46862,46863,46864,46865,46866,46867,46868,46869,46870,46871,46872,46873,46874,46875,46876,46877,46878,46879,46880,46881,46882,46883,46884,46885,46886,46887,46890,46891,46893,46894,46897,46898,46899,46900,46901,46902,46903,46906,46908,46909,46910,46911,46912,46913,46914,46915,46917,46918,46919,46921,46922,46923,46925,46926,46927,46928,46929,46930,46931,46934,46935,46936,46937,46938,46939,46940,46941,46942,46943,46945,46946,46947,46949,46950,46951,46953,46954,46955,46956,46957,46958,46959,46962,46964,46966,46967,46968,46969,46970,46971,46974,46975,46977,46978,46979,46981,46982,46983,46984,46985,46986,46987,46990,46995,46996,46997,47002,47003,47005,47006,47007,47009,47010,47011,47012,47013,47014,47015,47018,47022,47023,47024,47025,47026,47027,47030,47031,47033,47034,47035,47036,47037,47038,47039,47040,47041,47042,47043,47044,47045,47046,47048,47050,47051,47052,47053,47054,47055,47056,47057,47058,47059,47060,47061,47062,47063,47064,47065,47066,47067,47068,47069,47070,47071,47072,47073,47074,47075,47076,47077,47078,47079,47080,47081,47082,47083,47086,47087,47089,47090,47091,47093,47094,47095,47096,47097,47098,47099,47102,47106,47107,47108,47109,47110,47114,47115,47117,47118,47119,47121,47122,47123,47124,47125,47126,47127,47130,47132,47134,47135,47136,47137,47138,47139,47142,47143,47145,47146,47147,47149,47150,47151,47152,47153,47154,47155,47158,47162,47163,47164,47165,47166,47167,47169,47170,47171,47173,47174,47175,47176,47177,47178,47179,47180,47181,47182,47183,47184,47186,47188,47189,47190,47191,47192,47193,47194,47195,47198,47199,47201,47202,47203,47205,47206,47207,47208,47209,47210,47211,47214,47216,47218,47219,47220,47221,47222,47223,47225,47226,47227,47229,47230,47231,47232,47233,47234,47235,47236,47237,47238,47239,47240,47241,47242,47243,47244,47246,47247,47248,47249,47250,47251,47252,47253,47254,47255,47256,47257,47258,47259,47260,47261,47262,47263,47264,47265,47266,47267,47268,47269,47270,47271,47273,47274,47275,47276,47277,47278,47279,47281,47282,47283,47285,47286,47287,47289,47290,47291,47292,47293,47294,47295,47298,47300,47302,47303,47304,47305,47306,47307,47309,47310,47311,47313,47314,47315,47317,47318,47319,47320,47321,47322,47323,47324,47326,47328,47330,47331,47332,47333,47334,47335,47338,47339,47341,47342,47343,47345,47346,47347,47348,47349,47350,47351,47354,47356,47358,47359,47360,47361,47362,47363,47365,47366,47367,47368,47369,47370,47371,47372,47373,47374,47375,47376,47377,47378,47379,47380,47381,47382,47383,47385,47386,47387,47388,47389,47390,47391,47393,47394,47395,47396,47397,47398,47399,47400,47401,47402,47403,47404,47405,47406,47407,47408,47409,47410,47411,47412,47413,47414,47415,47416,47417,47418,47419,47422,47423,47425,47426,47427,47429,47430,47431,47432,47433,47434,47435,47437,47438,47440,47442,47443,47444,47445,47446,47447,47450,47451,47453,47454,47455,47457,47458,47459,47460,47461,47462,47463,47466,47468,47470,47471,47472,47473,47474,47475,47478,47479,47481,47482,47483,47485,47486,47487,47488,47489,47490,47491,47494,47496,47499,47500,47503,47504,47505,47506,47507,47508,47509,47510,47511,47512,47513,47514,47515,47516,47517,47518,47519,47520,47521,47522,47523,47524,47525,47526,47527,47528,47529,47530,47531,47534,47535,47537,47538,47539,47541,47542,47543,47544,47545,47546,47547,47550,47552,47554,47555,47556,47557,47558,47559,47562,47563,47565,47571,47572,47573,47574,47575,47578,47580,47583,47584,47586,47590,47591,47593,47594,47595,47597,47598,47599,47600,47601,47602,47603,47606,47611,47612,47613,47614,47615,47618,47619,47620,47621,47622,47623,47625,47626,47627,47628,47629,47630,47631,47632,47633,47634,47635,47636,47638,47639,47640,47641,47642,47643,47644,47645,47646,47647,47648,47649,47650,47651,47652,47653,47654,47655,47656,47657,47658,47659,47660,47661,47662,47663,47664,47665,47666,47667,47668,47669,47670,47671,47674,47675,47677,47678,47679,47681,47683,47684,47685,47686,47687,47690,47692,47695,47696,47697,47698,47702,47703,47705,47706,47707,47709,47710,47711,47712,47713,47714,47715,47718,47722,47723,47724,47725,47726,47727,47730,47731,47733,47734,47735,47737,47738,47739,47740,47741,47742,47743,47744,47745,47746,47750,47752,47753,47754,47755,47757,47758,47759,47760,47761,47762,47763,47764,47765,47766,47767,47768,47769,47770,47771,47772,47773,47774,47775,47776,47777,47778,47779,47780,47781,47782,47783,47786,47789,47790,47791,47793,47795,47796,47797,47798,47799,47802,47804,47806,47807,47808,47809,47810,47811,47813,47814,47815,47817,47818,47819,47820,47821,47822,47823,47824,47825,47826,47827,47828,47829,47830,47831,47834,47835,47836,47837,47838,47839,47840,47841,47842,47843,47844,47845,47846,47847,47848,47849,47850,47851,47852,47853,47854,47855,47856,47857,47858,47859,47860,47861,47862,47863,47864,47865,47866,47867,47869,47870,47871,47873,47874,47875,47877,47878,47879,47880,47881,47882,47883,47884,47886,47888,47890,47891,47892,47893,47894,47895,47897,47898,47899,47901,47902,47903,47905,47906,47907,47908,47909,47910,47911,47912,47914,47916,47917,47918,47919,47920,47921,47922,47923,47927,47929,47930,47935,47936,47937,47938,47939,47942,47944,47946,47947,47948,47950,47953,47954,47955,47957,47958,47959,47961,47962,47963,47964,47965,47966,47967,47968,47970,47972,47973,47974,47975,47976,47977,47978,47979,47981,47982,47983,47984,47985,47986,47987,47988,47989,47990,47991,47992,47993,47994,47995,47996,47997,47998,47999,48000,48001,48002,48003,48004,48005,48006,48007,48009,48010,48011,48013,48014,48015,48017,48018,48019,48020,48021,48022,48023,48024,48025,48026,48027,48028,48029,48030,48031,48032,48033,48034,48035,48037,48038,48039,48041,48042,48043,48045,48046,48047,48048,48049,48050,48051,48053,48054,48056,48057,48058,48059,48060,48061,48062,48063,48065,48066,48067,48069,48070,48071,48073,48074,48075,48076,48077,48078,48079,48081,48082,48084,48085,48086,48087,48088,48089,48090,48091,48092,48093,48094,48095,48096,48097,48098,48099,48100,48101,48102,48103,48104,48105,48106,48107,48108,48109,48110,48111,48112,48113,48114,48115,48116,48117,48118,48119,48122,48123,48125,48126,48129,48131,48132,48133,48134,48135,48138,48142,48144,48146,48147,48153,48154,48160,48161,48162,48163,48166,48168,48170,48171,48172,48174,48175,48178,48179,48181,48182,48183,48185,48186,48187,48188,48189,48190,48191,48194,48198,48199,48200,48202,48203,48206,48207,48209,48210,48211,48212,48213,48214,48215,48216,48217,48218,48219,48220,48222,48223,48224,48225,48226,48227,48228,48229,48230,48231,48232,48233,48234,48235,48236,48237,48238,48239,48240,48241,48242,48243,48244,48245,48246,48247,48248,48249,48250,48251,48252,48253,48254,48255,48256,48257,48258,48259,48262,48263,48265,48266,48269,48271,48272,48273,48274,48275,48278,48280,48283,48284,48285,48286,48287,48290,48291,48293,48294,48297,48298,48299,48300,48301,48302,48303,48306,48310,48311,48312,48313,48314,48315,48318,48319,48321,48322,48323,48325,48326,48327,48328,48329,48330,48331,48332,48334,48338,48339,48340,48342,48343,48345,48346,48347,48349,48350,48351,48352,48353,48354,48355,48356,48357,48358,48359,48360,48361,48362,48363,48364,48365,48366,48367,48368,48369,48370,48371,48375,48377,48378,48379,48381,48382,48383,48384,48385,48386,48387,48390,48392,48394,48395,48396,48397,48398,48399,48401,48402,48403,48405,48406,48407,48408,48409,48410,48411,48412,48413,48414,48415,48416,48417,48418,48419,48421,48422,48423,48424,48425,48426,48427,48429,48430,48431,48432,48433,48434,48435,48436,48437,48438,48439,48440,48441,48442,48443,48444,48445,48446,48447,48449,48450,48451,48452,48453,48454,48455,48458,48459,48461,48462,48463,48465,48466,48467,48468,48469,48470,48471,48474,48475,48476,48477,48478,48479,48480,48481,48482,48483,48485,48486,48487,48489,48490,48491,48492,48493,48494,48495,48496,48497,48498,48499,48500,48501,48502,48503,48504,48505,48506,48507,48508,48509,48510,48511,48514,48515,48517,48518,48523,48524,48525,48526,48527,48530,48532,48534,48535,48536,48539,48541,48542,48543,48544,48545,48546,48547,48549,48550,48551,48552,48553,48554,48555,48556,48557,48558,48559,48561,48562,48563,48564,48565,48566,48567,48569,48570,48571,48572,48573,48574,48575,48576,48577,48578,48579,48580,48581,48582,48583,48584,48585,48586,48587,48588,48589,48590,48591,48592,48593,48594,48595,48598,48599,48601,48602,48603,48605,48606,48607,48608,48609,48610,48611,48612,48613,48614,48615,48616,48618,48619,48620,48621,48622,48623,48625,48626,48627,48629,48630,48631,48633,48634,48635,48636,48637,48638,48639,48641,48642,48644,48646,48647,48648,48649,48650,48651,48654,48655,48657,48658,48659,48661,48662,48663,48664,48665,48666,48667,48670,48672,48673,48674,48675,48676,48677,48678,48679,48680,48681,48682,48683,48684,48685,48686,48687,48688,48689,48690,48691,48692,48693,48694,48695,48696,48697,48698,48699,48700,48701,48702,48703,48704,48705,48706,48707,48710,48711,48713,48714,48715,48717,48719,48720,48721,48722,48723,48726,48728,48732,48733,48734,48735,48738,48739,48741,48742,48743,48745,48747,48748,48749,48750,48751,48754,48758,48759,48760,48761,48762,48766,48767,48769,48770,48771,48773,48774,48775,48776,48777,48778,48779,48782,48786,48787,48788,48789,48790,48791,48794,48795,48796,48797,48798,48799,48800,48801,48802,48803,48804,48805,48806,48807,48809,48810,48811,48812,48813,48814,48815,48816,48817,48818,48819,48820,48821,48822,48823,48824,48825,48826,48827,48828,48829,48830,48831,48832,48833,48834,48835,48836,48837,48838,48839,48840,48841,48842,48843,48844,48845,48846,48847,48850,48851,48853,48854,48857,48858,48859,48860,48861,48862,48863,48865,48866,48870,48871,48872,48873,48874,48875,48877,48878,48879,48880,48881,48882,48883,48884,48885,48886,48887,48888,48889,48890,48891,48892,48893,48894,48895,48896,48898,48899,48900,48901,48902,48903,48906,48907,48908,48909,48910,48911,48912,48913,48914,48915,48916,48917,48918,48919,48922,48926,48927,48928,48929,48930,48931,48932,48933,48934,48935,48936,48937,48938,48939,48940,48941,48942,48943,48944,48945,48946,48947,48948,48949,48950,48951,48952,48953,48954,48955,48956,48957,48958,48959,48962,48963,48965,48966,48967,48969,48970,48971,48972,48973,48974,48975,48978,48979,48980,48982,48983,48984,48985,48986,48987,48988,48989,48990,48991,48992,48993,48994,48995,48996,48997,48998,48999,49000,49001,49002,49003,49004,49005,49006,49007,49008,49009,49010,49011,49012,49013,49014,49015,49016,49017,49018,49019,49020,49021,49022,49023,49024,49025,49026,49027,49028,49029,49030,49031,49032,49033,49034,49035,49036,49037,49038,49039,49040,49041,49042,49043,49045,49046,49047,49048,49049,49050,49051,49052,49053,49054,49055,49056,49057,49058,49059,49060,49061,49062,49063,49064,49065,49066,49067,49068,49069,49070,49071,49073,49074,49075,49076,49077,49078,49079,49080,49081,49082,49083,49084,49085,49086,49087,49088,49089,49090,49091,49092,49094,49095,49096,49097,49098,49099,49102,49103,49105,49106,49107,49109,49110,49111,49112,49113,49114,49115,49117,49118,49120,49122,49123,49124,49125,49126,49127,49128,49129,49130,49131,49132,49133,49134,49135,49136,49137,49138,49139,49140,49141,49142,49143,49144,49145,49146,49147,49148,49149,49150,49151,49152,49153,49154,49155,49156,49157,49158,49159,49160,49161,49162,49163,49164,49165,49166,49167,49168,49169,49170,49171,49172,49173,49174,49175,49176,49177,49178,49179,49180,49181,49182,49183,49184,49185,49186,49187,49188,49189,49190,49191,49192,49193,49194,49195,49196,49197,49198,49199,49200,49201,49202,49203,49204,49205,49206,49207,49208,49209,49210,49211,49213,49214,49215,49216,49217,49218,49219,49220,49221,49222,49223,49224,49225,49226,49227,49228,49229,49230,49231,49232,49234,49235,49236,49237,49238,49239,49241,49242,49243,49245,49246,49247,49249,49250,49251,49252,49253,49254,49255,49258,49259,49260,49261,49262,49263,49264,49265,49266,49267,49268,49269,49270,49271,49272,49273,49274,49275,49276,49277,49278,49279,49280,49281,49282,49283,49284,49285,49286,49287,49288,49289,49290,49291,49292,49293,49294,49295,49298,49299,49301,49302,49303,49305,49306,49307,49308,49309,49310,49311,49314,49316,49318,49319,49320,49321,49322,49323,49326,49329,49330,49335,49336,49337,49338,49339,49342,49346,49347,49348,49350,49351,49354,49355,49357,49358,49359,49361,49362,49363,49364,49365,49366,49367,49370,49374,49375,49376,49377,49378,49379,49382,49383,49385,49386,49387,49389,49390,49391,49392,49393,49394,49395,49398,49400,49402,49403,49404,49405,49406,49407,49409,49410,49411,49413,49414,49415,49417,49418,49419,49420,49421,49422,49423,49425,49426,49427,49428,49430,49431,49432,49433,49434,49435,49441,49442,49445,49448,49449,49450,49451,49454,49458,49459,49460,49461,49463,49466,49467,49469,49470,49471,49473,49474,49475,49476,49477,49478,49479,49482,49486,49487,49488,49489,49490,49491,49494,49495,49497,49498,49499,49501,49502,49503,49504,49505,49506,49507,49510,49514,49515,49516,49517,49518,49519,49521,49522,49523,49525,49526,49527,49529,49530,49531,49532,49533,49534,49535,49536,49537,49538,49539,49540,49542,49543,49544,49545,49546,49547,49551,49553,49554,49555,49557,49559,49560,49561,49562,49563,49566,49568,49570,49571,49572,49574,49575,49578,49579,49581,49582,49583,49585,49586,49587,49588,49589,49590,49591,49592,49593,49594,49595,49596,49598,49599,49600,49601,49602,49603,49605,49606,49607,49609,49610,49611,49613,49614,49615,49616,49617,49618,49619,49621,49622,49625,49626,49627,49628,49629,49630,49631,49633,49634,49635,49637,49638,49639,49641,49642,49643,49644,49645,49646,49647,49650,49652,49653,49654,49655,49656,49657,49658,49659,49662,49663,49665,49666,49667,49669,49670,49671,49672,49673,49674,49675,49678,49680,49682,49683,49684,49685,49686,49687,49690,49691,49693,49694,49697,49698,49699,49700,49701,49702,49703,49706,49708,49710,49712,49715,49717,49718,49719,49720,49721,49722,49723,49724,49725,49726,49727,49728,49729,49730,49731,49732,49733,49734,49735,49737,49738,49739,49740,49741,49742,49743,49746,49747,49749,49750,49751,49753,49754,49755,49756,49757,49758,49759,49761,49762,49763,49764,49766,49767,49768,49769,49770,49771,49774,49775,49777,49778,49779,49781,49782,49783,49784,49785,49786,49787,49790,49792,49794,49795,49796,49797,49798,49799,49802,49803,49804,49805,49806,49807,49809,49810,49811,49812,49813,49814,49815,49817,49818,49820,49822,49823,49824,49825,49826,49827,49830,49831,49833,49834,49835,49838,49839,49840,49841,49842,49843,49846,49848,49850,49851,49852,49853,49854,49855,49856,49857,49858,49859,49860,49861,49862,49863,49864,49865,49866,49867,49868,49869,49870,49871,49872,49873,49874,49875,49876,49877,49878,49879,49880,49881,49882,49883,49886,49887,49889,49890,49893,49894,49895,49896,49897,49898,49902,49904,49906,49907,49908,49909,49911,49914,49917,49918,49919,49921,49922,49923,49924,49925,49926,49927,49930,49931,49934,49935,49936,49937,49938,49942,49943,49945,49946,49947,49949,49950,49951,49952,49953,49954,49955,49958,49959,49962,49963,49964,49965,49966,49967,49968,49969,49970,49971,49972,49973,49974,49975,49976,49977,49978,49979,49980,49981,49982,49983,49984,49985,49986,49987,49988,49990,49991,49992,49993,49994,49995,49996,49997,49998,49999,50000,50001,50002,50003,50004,50005,50006,50007,50008,50009,50010,50011,50012,50013,50014,50015,50016,50017,50018,50019,50020,50021,50022,50023,50026,50027,50029,50030,50031,50033,50035,50036,50037,50038,50039,50042,50043,50046,50047,50048,50049,50050,50051,50053,50054,50055,50057,50058,50059,50061,50062,50063,50064,50065,50066,50067,50068,50069,50070,50071,50072,50073,50074,50075,50076,50077,50078,50079,50080,50081,50082,50083,50084,50085,50086,50087,50088,50089,50090,50091,50092,50093,50094,50095,50096,50097,50098,50099,50100,50101,50102,50103,50104,50105,50106,50107,50108,50109,50110,50111,50113,50114,50115,50116,50117,50118,50119,50120,50121,50122,50123,50124,50125,50126,50127,50128,50129,50130,50131,50132,50133,50134,50135,50138,50139,50141,50142,50145,50147,50148,50149,50150,50151,50154,50155,50156,50158,50159,50160,50161,50162,50163,50166,50167,50169,50170,50171,50172,50173,50174,50175,50176,50177,50178,50179,50180,50181,50182,50183,50185,50186,50187,50188,50189,50190,50191,50193,50194,50195,50196,50197,50198,50199,50200,50201,50202,50203,50204,50205,50206,50207,50208,50209,50210,50211,50213,50214,50215,50216,50217,50218,50219,50221,50222,50223,50225,50226,50227,50229,50230,50231,50232,50233,50234,50235,50238,50239,50240,50241,50242,50243,50244,50245,50246,50247,50249,50250,50251,50252,50253,50254,50255,50256,50257,50258,50259,50260,50261,50262,50263,50264,50265,50266,50267,50268,50269,50270,50271,50272,50273,50274,50275,50278,50279,50281,50282,50283,50285,50286,50287,50288,50289,50290,50291,50294,50295,50296,50298,50299,50300,50301,50302,50303,50305,50306,50307,50308,50309,50310,50311,50312,50313,50314,50315,50316,50317,50318,50319,50320,50321,50322,50323,50325,50326,50327,50328,50329,50330,50331,50333,50334,50335,50336,50337,50338,50339,50340,50341,50342,50343,50344,50345,50346,50347,50348,50349,50350,50351,50352,50353,50354,50355,50356,50357,50358,50359,50361,50362,50363,50365,50366,50367,50368,50369,50370,50371,50372,50373,50374,50375,50376,50377,50378,50379,50380,50381,50382,50383,50384,50385,50386,50387,50388,50389,50390,50391,50392,50393,50394,50395,50396,50397,50398,50399,50400,50401,50402,50403,50404,50405,50406,50407,50408,50410,50411,50412,50413,50414,50415,50418,50419,50421,50422,50423,50425,50427,50428,50429,50430,50434,50435,50436,50437,50438,50439,50440,50441,50442,50443,50445,50446,50447,50449,50450,50451,50453,50454,50455,50456,50457,50458,50459,50461,50462,50463,50464,50465,50466,50467,50468,50469,50470,50471,50474,50475,50477,50478,50479,50481,50482,50483,50484,50485,50486,50487,50490,50492,50494,50495,50496,50497,50498,50499,50502,50503,50507,50511,50512,50513,50514,50518,50522,50523,50524,50527,50530,50531,50533,50534,50535,50537,50538,50539,50540,50541,50542,50543,50546,50550,50551,50552,50553,50554,50555,50558,50559,50561,50562,50563,50565,50566,50568,50569,50570,50571,50574,50576,50578,50579,50580,50582,50585,50586,50587,50589,50590,50591,50593,50594,50595,50596,50597,50598,50599,50600,50602,50603,50604,50605,50606,50607,50608,50609,50610,50611,50614,50615,50618,50623,50624,50625,50626,50627,50635,50637,50639,50642,50643,50645,50646,50647,50649,50650,50651,50652,50653,50654,50655,50658,50660,50662,50663,50664,50665,50666,50667,50671,50673,50674,50675,50677,50680,50681,50682,50683,50690,50691,50692,50697,50698,50699,50701,50702,50703,50705,50706,50707,50708,50709,50710,50711,50714,50717,50718,50719,50720,50721,50722,50723,50726,50727,50729,50730,50731,50735,50737,50738,50742,50744,50746,50748,50749,50750,50751,50754,50755,50757,50758,50759,50761,50762,50763,50764,50765,50766,50767,50770,50774,50775,50776,50777,50778,50779,50782,50783,50785,50786,50787,50788,50789,50790,50791,50792,50793,50794,50795,50797,50798,50800,50802,50803,50804,50805,50806,50807,50810,50811,50813,50814,50815,50817,50818,50819,50820,50821,50822,50823,50826,50828,50830,50831,50832,50833,50834,50835,50838,50839,50841,50842,50843,50845,50846,50847,50848,50849,50850,50851,50854,50856,50858,50859,50860,50861,50862,50863,50866,50867,50869,50870,50871,50875,50876,50877,50878,50879,50882,50884,50886,50887,50888,50889,50890,50891,50894,50895,50897,50898,50899,50901,50902,50903,50904,50905,50906,50907,50910,50911,50914,50915,50916,50917,50918,50919,50922,50923,50925,50926,50927,50929,50930,50931,50932,50933,50934,50935,50938,50939,50940,50942,50943,50944,50945,50946,50947,50950,50951,50953,50954,50955,50957,50958,50959,50960,50961,50962,50963,50966,50968,50970,50971,50972,50973,50974,50975,50978,50979,50981,50982,50983,50985,50986,50987,50988,50989,50990,50991,50994,50996,50998,51000,51001,51002,51003,51006,51007,51009,51010,51011,51013,51014,51015,51016,51017,51019,51022,51024,51033,51034,51035,51037,51038,51039,51041,51042,51043,51044,51045,51046,51047,51049,51050,51052,51053,51054,51055,51056,51057,51058,51059,51062,51063,51065,51066,51067,51071,51072,51073,51074,51078,51083,51084,51085,51087,51090,51091,51093,51097,51099,51100,51101,51102,51103,51106,51111,51112,51113,51114,51115,51118,51119,51121,51122,51123,51125,51126,51127,51128,51129,51130,51131,51134,51138,51139,51140,51141,51142,51143,51146,51147,51149,51151,51153,51154,51155,51156,51157,51158,51159,51161,51162,51163,51164,51166,51167,51168,51169,51170,51171,51173,51174,51175,51177,51178,51179,51181,51182,51183,51184,51185,51186,51187,51188,51189,51190,51191,51192,51193,51194,51195,51196,51197,51198,51199,51202,51203,51205,51206,51207,51209,51211,51212,51213,51214,51215,51218,51220,51223,51224,51225,51226,51227,51230,51231,51233,51234,51235,51237,51238,51239,51240,51241,51242,51243,51246,51248,51250,51251,51252,51253,51254,51255,51257,51258,51259,51261,51262,51263,51265,51266,51267,51268,51269,51270,51271,51274,51275,51278,51279,51280,51281,51282,51283,51285,51286,51287,51288,51289,51290,51291,51292,51293,51294,51295,51296,51297,51298,51299,51300,51301,51302,51303,51304,51305,51306,51307,51308,51309,51310,51311,51314,51315,51317,51318,51319,51321,51323,51324,51325,51326,51327,51330,51332,51336,51337,51338,51342,51343,51344,51345,51346,51347,51349,51350,51351,51352,51353,51354,51355,51356,51358,51360,51362,51363,51364,51365,51366,51367,51369,51370,51371,51372,51373,51374,51375,51376,51377,51378,51379,51380,51381,51382,51383,51384,51385,51386,51387,51390,51391,51392,51393,51394,51395,51397,51398,51399,51401,51402,51403,51405,51406,51407,51408,51409,51410,51411,51414,51416,51418,51419,51420,51421,51422,51423,51426,51427,51429,51430,51431,51432,51433,51434,51435,51436,51437,51438,51439,51440,51441,51442,51443,51444,51446,51447,51448,51449,51450,51451,51454,51455,51457,51458,51459,51463,51464,51465,51466,51467,51470,12288,12289,12290,183,8229,8230,168,12291,173,8213,8741,65340,8764,8216,8217,8220,8221,12308,12309,12296,12297,12298,12299,12300,12301,12302,12303,12304,12305,177,215,247,8800,8804,8805,8734,8756,176,8242,8243,8451,8491,65504,65505,65509,9794,9792,8736,8869,8978,8706,8711,8801,8786,167,8251,9734,9733,9675,9679,9678,9671,9670,9633,9632,9651,9650,9661,9660,8594,8592,8593,8595,8596,12307,8810,8811,8730,8765,8733,8757,8747,8748,8712,8715,8838,8839,8834,8835,8746,8745,8743,8744,65506,51472,51474,51475,51476,51477,51478,51479,51481,51482,51483,51484,51485,51486,51487,51488,51489,51490,51491,51492,51493,51494,51495,51496,51497,51498,51499,51501,51502,51503,51504,51505,51506,51507,51509,51510,51511,51512,51513,51514,51515,51516,51517,51518,51519,51520,51521,51522,51523,51524,51525,51526,51527,51528,51529,51530,51531,51532,51533,51534,51535,51538,51539,51541,51542,51543,51545,51546,51547,51548,51549,51550,51551,51554,51556,51557,51558,51559,51560,51561,51562,51563,51565,51566,51567,8658,8660,8704,8707,180,65374,711,728,733,730,729,184,731,161,191,720,8750,8721,8719,164,8457,8240,9665,9664,9655,9654,9828,9824,9825,9829,9831,9827,8857,9672,9635,9680,9681,9618,9636,9637,9640,9639,9638,9641,9832,9743,9742,9756,9758,182,8224,8225,8597,8599,8601,8598,8600,9837,9833,9834,9836,12927,12828,8470,13255,8482,13250,13272,8481,8364,174,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,51569,51570,51571,51573,51574,51575,51576,51577,51578,51579,51581,51582,51583,51584,51585,51586,51587,51588,51589,51590,51591,51594,51595,51597,51598,51599,51601,51602,51603,51604,51605,51606,51607,51610,51612,51614,51615,51616,51617,51618,51619,51620,51621,51622,51623,51624,51625,51626,51627,51628,51629,51630,51631,51632,51633,51634,51635,51636,51637,51638,51639,51640,51641,51642,51643,51644,51645,51646,51647,51650,51651,51653,51654,51657,51659,51660,51661,51662,51663,51666,51668,51671,51672,51675,65281,65282,65283,65284,65285,65286,65287,65288,65289,65290,65291,65292,65293,65294,65295,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,65306,65307,65308,65309,65310,65311,65312,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,65339,65510,65341,65342,65343,65344,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,65371,65372,65373,65507,51678,51679,51681,51683,51685,51686,51688,51689,51690,51691,51694,51698,51699,51700,51701,51702,51703,51706,51707,51709,51710,51711,51713,51714,51715,51716,51717,51718,51719,51722,51726,51727,51728,51729,51730,51731,51733,51734,51735,51737,51738,51739,51740,51741,51742,51743,51744,51745,51746,51747,51748,51749,51750,51751,51752,51754,51755,51756,51757,51758,51759,51760,51761,51762,51763,51764,51765,51766,51767,51768,51769,51770,51771,51772,51773,51774,51775,51776,51777,51778,51779,51780,51781,51782,12593,12594,12595,12596,12597,12598,12599,12600,12601,12602,12603,12604,12605,12606,12607,12608,12609,12610,12611,12612,12613,12614,12615,12616,12617,12618,12619,12620,12621,12622,12623,12624,12625,12626,12627,12628,12629,12630,12631,12632,12633,12634,12635,12636,12637,12638,12639,12640,12641,12642,12643,12644,12645,12646,12647,12648,12649,12650,12651,12652,12653,12654,12655,12656,12657,12658,12659,12660,12661,12662,12663,12664,12665,12666,12667,12668,12669,12670,12671,12672,12673,12674,12675,12676,12677,12678,12679,12680,12681,12682,12683,12684,12685,12686,51783,51784,51785,51786,51787,51790,51791,51793,51794,51795,51797,51798,51799,51800,51801,51802,51803,51806,51810,51811,51812,51813,51814,51815,51817,51818,51819,51820,51821,51822,51823,51824,51825,51826,51827,51828,51829,51830,51831,51832,51833,51834,51835,51836,51838,51839,51840,51841,51842,51843,51845,51846,51847,51848,51849,51850,51851,51852,51853,51854,51855,51856,51857,51858,51859,51860,51861,51862,51863,51865,51866,51867,51868,51869,51870,51871,51872,51873,51874,51875,51876,51877,51878,51879,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,null,null,null,null,null,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,null,null,null,null,null,null,null,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,null,null,null,null,null,null,null,null,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,963,964,965,966,967,968,969,null,null,null,null,null,null,51880,51881,51882,51883,51884,51885,51886,51887,51888,51889,51890,51891,51892,51893,51894,51895,51896,51897,51898,51899,51902,51903,51905,51906,51907,51909,51910,51911,51912,51913,51914,51915,51918,51920,51922,51924,51925,51926,51927,51930,51931,51932,51933,51934,51935,51937,51938,51939,51940,51941,51942,51943,51944,51945,51946,51947,51949,51950,51951,51952,51953,51954,51955,51957,51958,51959,51960,51961,51962,51963,51964,51965,51966,51967,51968,51969,51970,51971,51972,51973,51974,51975,51977,51978,9472,9474,9484,9488,9496,9492,9500,9516,9508,9524,9532,9473,9475,9487,9491,9499,9495,9507,9523,9515,9531,9547,9504,9519,9512,9527,9535,9501,9520,9509,9528,9538,9490,9489,9498,9497,9494,9493,9486,9485,9502,9503,9505,9506,9510,9511,9513,9514,9517,9518,9521,9522,9525,9526,9529,9530,9533,9534,9536,9537,9539,9540,9541,9542,9543,9544,9545,9546,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,51979,51980,51981,51982,51983,51985,51986,51987,51989,51990,51991,51993,51994,51995,51996,51997,51998,51999,52002,52003,52004,52005,52006,52007,52008,52009,52010,52011,52012,52013,52014,52015,52016,52017,52018,52019,52020,52021,52022,52023,52024,52025,52026,52027,52028,52029,52030,52031,52032,52034,52035,52036,52037,52038,52039,52042,52043,52045,52046,52047,52049,52050,52051,52052,52053,52054,52055,52058,52059,52060,52062,52063,52064,52065,52066,52067,52069,52070,52071,52072,52073,52074,52075,52076,13205,13206,13207,8467,13208,13252,13219,13220,13221,13222,13209,13210,13211,13212,13213,13214,13215,13216,13217,13218,13258,13197,13198,13199,13263,13192,13193,13256,13223,13224,13232,13233,13234,13235,13236,13237,13238,13239,13240,13241,13184,13185,13186,13187,13188,13242,13243,13244,13245,13246,13247,13200,13201,13202,13203,13204,8486,13248,13249,13194,13195,13196,13270,13253,13229,13230,13231,13275,13225,13226,13227,13228,13277,13264,13267,13251,13257,13276,13254,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52077,52078,52079,52080,52081,52082,52083,52084,52085,52086,52087,52090,52091,52092,52093,52094,52095,52096,52097,52098,52099,52100,52101,52102,52103,52104,52105,52106,52107,52108,52109,52110,52111,52112,52113,52114,52115,52116,52117,52118,52119,52120,52121,52122,52123,52125,52126,52127,52128,52129,52130,52131,52132,52133,52134,52135,52136,52137,52138,52139,52140,52141,52142,52143,52144,52145,52146,52147,52148,52149,52150,52151,52153,52154,52155,52156,52157,52158,52159,52160,52161,52162,52163,52164,198,208,170,294,null,306,null,319,321,216,338,186,222,358,330,null,12896,12897,12898,12899,12900,12901,12902,12903,12904,12905,12906,12907,12908,12909,12910,12911,12912,12913,12914,12915,12916,12917,12918,12919,12920,12921,12922,12923,9424,9425,9426,9427,9428,9429,9430,9431,9432,9433,9434,9435,9436,9437,9438,9439,9440,9441,9442,9443,9444,9445,9446,9447,9448,9449,9312,9313,9314,9315,9316,9317,9318,9319,9320,9321,9322,9323,9324,9325,9326,189,8531,8532,188,190,8539,8540,8541,8542,52165,52166,52167,52168,52169,52170,52171,52172,52173,52174,52175,52176,52177,52178,52179,52181,52182,52183,52184,52185,52186,52187,52188,52189,52190,52191,52192,52193,52194,52195,52197,52198,52200,52202,52203,52204,52205,52206,52207,52208,52209,52210,52211,52212,52213,52214,52215,52216,52217,52218,52219,52220,52221,52222,52223,52224,52225,52226,52227,52228,52229,52230,52231,52232,52233,52234,52235,52238,52239,52241,52242,52243,52245,52246,52247,52248,52249,52250,52251,52254,52255,52256,52259,52260,230,273,240,295,305,307,312,320,322,248,339,223,254,359,331,329,12800,12801,12802,12803,12804,12805,12806,12807,12808,12809,12810,12811,12812,12813,12814,12815,12816,12817,12818,12819,12820,12821,12822,12823,12824,12825,12826,12827,9372,9373,9374,9375,9376,9377,9378,9379,9380,9381,9382,9383,9384,9385,9386,9387,9388,9389,9390,9391,9392,9393,9394,9395,9396,9397,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,9342,9343,9344,9345,9346,185,178,179,8308,8319,8321,8322,8323,8324,52261,52262,52266,52267,52269,52271,52273,52274,52275,52276,52277,52278,52279,52282,52287,52288,52289,52290,52291,52294,52295,52297,52298,52299,52301,52302,52303,52304,52305,52306,52307,52310,52314,52315,52316,52317,52318,52319,52321,52322,52323,52325,52327,52329,52330,52331,52332,52333,52334,52335,52337,52338,52339,52340,52342,52343,52344,52345,52346,52347,52348,52349,52350,52351,52352,52353,52354,52355,52356,52357,52358,52359,52360,52361,52362,52363,52364,52365,52366,52367,52368,52369,52370,52371,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,null,null,null,null,null,null,null,null,null,null,null,52372,52373,52374,52375,52378,52379,52381,52382,52383,52385,52386,52387,52388,52389,52390,52391,52394,52398,52399,52400,52401,52402,52403,52406,52407,52409,52410,52411,52413,52414,52415,52416,52417,52418,52419,52422,52424,52426,52427,52428,52429,52430,52431,52433,52434,52435,52437,52438,52439,52440,52441,52442,52443,52444,52445,52446,52447,52448,52449,52450,52451,52453,52454,52455,52456,52457,52458,52459,52461,52462,52463,52465,52466,52467,52468,52469,52470,52471,52472,52473,52474,52475,52476,52477,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,null,null,null,null,null,null,null,null,52478,52479,52480,52482,52483,52484,52485,52486,52487,52490,52491,52493,52494,52495,52497,52498,52499,52500,52501,52502,52503,52506,52508,52510,52511,52512,52513,52514,52515,52517,52518,52519,52521,52522,52523,52525,52526,52527,52528,52529,52530,52531,52532,52533,52534,52535,52536,52538,52539,52540,52541,52542,52543,52544,52545,52546,52547,52548,52549,52550,52551,52552,52553,52554,52555,52556,52557,52558,52559,52560,52561,52562,52563,52564,52565,52566,52567,52568,52569,52570,52571,52573,52574,52575,1040,1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,null,null,null,null,null,null,null,null,null,null,null,null,null,52577,52578,52579,52581,52582,52583,52584,52585,52586,52587,52590,52592,52594,52595,52596,52597,52598,52599,52601,52602,52603,52604,52605,52606,52607,52608,52609,52610,52611,52612,52613,52614,52615,52617,52618,52619,52620,52621,52622,52623,52624,52625,52626,52627,52630,52631,52633,52634,52635,52637,52638,52639,52640,52641,52642,52643,52646,52648,52650,52651,52652,52653,52654,52655,52657,52658,52659,52660,52661,52662,52663,52664,52665,52666,52667,52668,52669,52670,52671,52672,52673,52674,52675,52677,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52678,52679,52680,52681,52682,52683,52685,52686,52687,52689,52690,52691,52692,52693,52694,52695,52696,52697,52698,52699,52700,52701,52702,52703,52704,52705,52706,52707,52708,52709,52710,52711,52713,52714,52715,52717,52718,52719,52721,52722,52723,52724,52725,52726,52727,52730,52732,52734,52735,52736,52737,52738,52739,52741,52742,52743,52745,52746,52747,52749,52750,52751,52752,52753,52754,52755,52757,52758,52759,52760,52762,52763,52764,52765,52766,52767,52770,52771,52773,52774,52775,52777,52778,52779,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52780,52781,52782,52783,52786,52788,52790,52791,52792,52793,52794,52795,52796,52797,52798,52799,52800,52801,52802,52803,52804,52805,52806,52807,52808,52809,52810,52811,52812,52813,52814,52815,52816,52817,52818,52819,52820,52821,52822,52823,52826,52827,52829,52830,52834,52835,52836,52837,52838,52839,52842,52844,52846,52847,52848,52849,52850,52851,52854,52855,52857,52858,52859,52861,52862,52863,52864,52865,52866,52867,52870,52872,52874,52875,52876,52877,52878,52879,52882,52883,52885,52886,52887,52889,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52890,52891,52892,52893,52894,52895,52898,52902,52903,52904,52905,52906,52907,52910,52911,52912,52913,52914,52915,52916,52917,52918,52919,52920,52921,52922,52923,52924,52925,52926,52927,52928,52930,52931,52932,52933,52934,52935,52936,52937,52938,52939,52940,52941,52942,52943,52944,52945,52946,52947,52948,52949,52950,52951,52952,52953,52954,52955,52956,52957,52958,52959,52960,52961,52962,52963,52966,52967,52969,52970,52973,52974,52975,52976,52977,52978,52979,52982,52986,52987,52988,52989,52990,52991,44032,44033,44036,44039,44040,44041,44042,44048,44049,44050,44051,44052,44053,44054,44055,44057,44058,44059,44060,44061,44064,44068,44076,44077,44079,44080,44081,44088,44089,44092,44096,44107,44109,44116,44120,44124,44144,44145,44148,44151,44152,44154,44160,44161,44163,44164,44165,44166,44169,44170,44171,44172,44176,44180,44188,44189,44191,44192,44193,44200,44201,44202,44204,44207,44208,44216,44217,44219,44220,44221,44225,44228,44232,44236,44245,44247,44256,44257,44260,44263,44264,44266,44268,44271,44272,44273,44275,44277,44278,44284,44285,44288,44292,44294,52994,52995,52997,52998,52999,53001,53002,53003,53004,53005,53006,53007,53010,53012,53014,53015,53016,53017,53018,53019,53021,53022,53023,53025,53026,53027,53029,53030,53031,53032,53033,53034,53035,53038,53042,53043,53044,53045,53046,53047,53049,53050,53051,53052,53053,53054,53055,53056,53057,53058,53059,53060,53061,53062,53063,53064,53065,53066,53067,53068,53069,53070,53071,53072,53073,53074,53075,53078,53079,53081,53082,53083,53085,53086,53087,53088,53089,53090,53091,53094,53096,53098,53099,53100,44300,44301,44303,44305,44312,44316,44320,44329,44332,44333,44340,44341,44344,44348,44356,44357,44359,44361,44368,44372,44376,44385,44387,44396,44397,44400,44403,44404,44405,44406,44411,44412,44413,44415,44417,44418,44424,44425,44428,44432,44444,44445,44452,44471,44480,44481,44484,44488,44496,44497,44499,44508,44512,44516,44536,44537,44540,44543,44544,44545,44552,44553,44555,44557,44564,44592,44593,44596,44599,44600,44602,44608,44609,44611,44613,44614,44618,44620,44621,44622,44624,44628,44630,44636,44637,44639,44640,44641,44645,44648,44649,44652,44656,44664,53101,53102,53103,53106,53107,53109,53110,53111,53113,53114,53115,53116,53117,53118,53119,53121,53122,53123,53124,53126,53127,53128,53129,53130,53131,53133,53134,53135,53136,53137,53138,53139,53140,53141,53142,53143,53144,53145,53146,53147,53148,53149,53150,53151,53152,53154,53155,53156,53157,53158,53159,53161,53162,53163,53164,53165,53166,53167,53169,53170,53171,53172,53173,53174,53175,53176,53177,53178,53179,53180,53181,53182,53183,53184,53185,53186,53187,53189,53190,53191,53192,53193,53194,53195,44665,44667,44668,44669,44676,44677,44684,44732,44733,44734,44736,44740,44748,44749,44751,44752,44753,44760,44761,44764,44776,44779,44781,44788,44792,44796,44807,44808,44813,44816,44844,44845,44848,44850,44852,44860,44861,44863,44865,44866,44867,44872,44873,44880,44892,44893,44900,44901,44921,44928,44932,44936,44944,44945,44949,44956,44984,44985,44988,44992,44999,45000,45001,45003,45005,45006,45012,45020,45032,45033,45040,45041,45044,45048,45056,45057,45060,45068,45072,45076,45084,45085,45096,45124,45125,45128,45130,45132,45134,45139,45140,45141,45143,45145,53196,53197,53198,53199,53200,53201,53202,53203,53204,53205,53206,53207,53208,53209,53210,53211,53212,53213,53214,53215,53218,53219,53221,53222,53223,53225,53226,53227,53228,53229,53230,53231,53234,53236,53238,53239,53240,53241,53242,53243,53245,53246,53247,53249,53250,53251,53253,53254,53255,53256,53257,53258,53259,53260,53261,53262,53263,53264,53266,53267,53268,53269,53270,53271,53273,53274,53275,53276,53277,53278,53279,53280,53281,53282,53283,53284,53285,53286,53287,53288,53289,53290,53291,53292,45149,45180,45181,45184,45188,45196,45197,45199,45201,45208,45209,45210,45212,45215,45216,45217,45218,45224,45225,45227,45228,45229,45230,45231,45233,45235,45236,45237,45240,45244,45252,45253,45255,45256,45257,45264,45265,45268,45272,45280,45285,45320,45321,45323,45324,45328,45330,45331,45336,45337,45339,45340,45341,45347,45348,45349,45352,45356,45364,45365,45367,45368,45369,45376,45377,45380,45384,45392,45393,45396,45397,45400,45404,45408,45432,45433,45436,45440,45442,45448,45449,45451,45453,45458,45459,45460,45464,45468,45480,45516,45520,45524,45532,45533,53294,53295,53296,53297,53298,53299,53302,53303,53305,53306,53307,53309,53310,53311,53312,53313,53314,53315,53318,53320,53322,53323,53324,53325,53326,53327,53329,53330,53331,53333,53334,53335,53337,53338,53339,53340,53341,53342,53343,53345,53346,53347,53348,53349,53350,53351,53352,53353,53354,53355,53358,53359,53361,53362,53363,53365,53366,53367,53368,53369,53370,53371,53374,53375,53376,53378,53379,53380,53381,53382,53383,53384,53385,53386,53387,53388,53389,53390,53391,53392,53393,53394,53395,53396,45535,45544,45545,45548,45552,45561,45563,45565,45572,45573,45576,45579,45580,45588,45589,45591,45593,45600,45620,45628,45656,45660,45664,45672,45673,45684,45685,45692,45700,45701,45705,45712,45713,45716,45720,45721,45722,45728,45729,45731,45733,45734,45738,45740,45744,45748,45768,45769,45772,45776,45778,45784,45785,45787,45789,45794,45796,45797,45798,45800,45803,45804,45805,45806,45807,45811,45812,45813,45815,45816,45817,45818,45819,45823,45824,45825,45828,45832,45840,45841,45843,45844,45845,45852,45908,45909,45910,45912,45915,45916,45918,45919,45924,45925,53397,53398,53399,53400,53401,53402,53403,53404,53405,53406,53407,53408,53409,53410,53411,53414,53415,53417,53418,53419,53421,53422,53423,53424,53425,53426,53427,53430,53432,53434,53435,53436,53437,53438,53439,53442,53443,53445,53446,53447,53450,53451,53452,53453,53454,53455,53458,53462,53463,53464,53465,53466,53467,53470,53471,53473,53474,53475,53477,53478,53479,53480,53481,53482,53483,53486,53490,53491,53492,53493,53494,53495,53497,53498,53499,53500,53501,53502,53503,53504,53505,53506,53507,53508,45927,45929,45931,45934,45936,45937,45940,45944,45952,45953,45955,45956,45957,45964,45968,45972,45984,45985,45992,45996,46020,46021,46024,46027,46028,46030,46032,46036,46037,46039,46041,46043,46045,46048,46052,46056,46076,46096,46104,46108,46112,46120,46121,46123,46132,46160,46161,46164,46168,46176,46177,46179,46181,46188,46208,46216,46237,46244,46248,46252,46261,46263,46265,46272,46276,46280,46288,46293,46300,46301,46304,46307,46308,46310,46316,46317,46319,46321,46328,46356,46357,46360,46363,46364,46372,46373,46375,46376,46377,46378,46384,46385,46388,46392,53509,53510,53511,53512,53513,53514,53515,53516,53518,53519,53520,53521,53522,53523,53524,53525,53526,53527,53528,53529,53530,53531,53532,53533,53534,53535,53536,53537,53538,53539,53540,53541,53542,53543,53544,53545,53546,53547,53548,53549,53550,53551,53554,53555,53557,53558,53559,53561,53563,53564,53565,53566,53567,53570,53574,53575,53576,53577,53578,53579,53582,53583,53585,53586,53587,53589,53590,53591,53592,53593,53594,53595,53598,53600,53602,53603,53604,53605,53606,53607,53609,53610,53611,53613,46400,46401,46403,46404,46405,46411,46412,46413,46416,46420,46428,46429,46431,46432,46433,46496,46497,46500,46504,46506,46507,46512,46513,46515,46516,46517,46523,46524,46525,46528,46532,46540,46541,46543,46544,46545,46552,46572,46608,46609,46612,46616,46629,46636,46644,46664,46692,46696,46748,46749,46752,46756,46763,46764,46769,46804,46832,46836,46840,46848,46849,46853,46888,46889,46892,46895,46896,46904,46905,46907,46916,46920,46924,46932,46933,46944,46948,46952,46960,46961,46963,46965,46972,46973,46976,46980,46988,46989,46991,46992,46993,46994,46998,46999,53614,53615,53616,53617,53618,53619,53620,53621,53622,53623,53624,53625,53626,53627,53629,53630,53631,53632,53633,53634,53635,53637,53638,53639,53641,53642,53643,53644,53645,53646,53647,53648,53649,53650,53651,53652,53653,53654,53655,53656,53657,53658,53659,53660,53661,53662,53663,53666,53667,53669,53670,53671,53673,53674,53675,53676,53677,53678,53679,53682,53684,53686,53687,53688,53689,53691,53693,53694,53695,53697,53698,53699,53700,53701,53702,53703,53704,53705,53706,53707,53708,53709,53710,53711,47000,47001,47004,47008,47016,47017,47019,47020,47021,47028,47029,47032,47047,47049,47084,47085,47088,47092,47100,47101,47103,47104,47105,47111,47112,47113,47116,47120,47128,47129,47131,47133,47140,47141,47144,47148,47156,47157,47159,47160,47161,47168,47172,47185,47187,47196,47197,47200,47204,47212,47213,47215,47217,47224,47228,47245,47272,47280,47284,47288,47296,47297,47299,47301,47308,47312,47316,47325,47327,47329,47336,47337,47340,47344,47352,47353,47355,47357,47364,47384,47392,47420,47421,47424,47428,47436,47439,47441,47448,47449,47452,47456,47464,47465,53712,53713,53714,53715,53716,53717,53718,53719,53721,53722,53723,53724,53725,53726,53727,53728,53729,53730,53731,53732,53733,53734,53735,53736,53737,53738,53739,53740,53741,53742,53743,53744,53745,53746,53747,53749,53750,53751,53753,53754,53755,53756,53757,53758,53759,53760,53761,53762,53763,53764,53765,53766,53768,53770,53771,53772,53773,53774,53775,53777,53778,53779,53780,53781,53782,53783,53784,53785,53786,53787,53788,53789,53790,53791,53792,53793,53794,53795,53796,53797,53798,53799,53800,53801,47467,47469,47476,47477,47480,47484,47492,47493,47495,47497,47498,47501,47502,47532,47533,47536,47540,47548,47549,47551,47553,47560,47561,47564,47566,47567,47568,47569,47570,47576,47577,47579,47581,47582,47585,47587,47588,47589,47592,47596,47604,47605,47607,47608,47609,47610,47616,47617,47624,47637,47672,47673,47676,47680,47682,47688,47689,47691,47693,47694,47699,47700,47701,47704,47708,47716,47717,47719,47720,47721,47728,47729,47732,47736,47747,47748,47749,47751,47756,47784,47785,47787,47788,47792,47794,47800,47801,47803,47805,47812,47816,47832,47833,47868,53802,53803,53806,53807,53809,53810,53811,53813,53814,53815,53816,53817,53818,53819,53822,53824,53826,53827,53828,53829,53830,53831,53833,53834,53835,53836,53837,53838,53839,53840,53841,53842,53843,53844,53845,53846,53847,53848,53849,53850,53851,53853,53854,53855,53856,53857,53858,53859,53861,53862,53863,53864,53865,53866,53867,53868,53869,53870,53871,53872,53873,53874,53875,53876,53877,53878,53879,53880,53881,53882,53883,53884,53885,53886,53887,53890,53891,53893,53894,53895,53897,53898,53899,53900,47872,47876,47885,47887,47889,47896,47900,47904,47913,47915,47924,47925,47926,47928,47931,47932,47933,47934,47940,47941,47943,47945,47949,47951,47952,47956,47960,47969,47971,47980,48008,48012,48016,48036,48040,48044,48052,48055,48064,48068,48072,48080,48083,48120,48121,48124,48127,48128,48130,48136,48137,48139,48140,48141,48143,48145,48148,48149,48150,48151,48152,48155,48156,48157,48158,48159,48164,48165,48167,48169,48173,48176,48177,48180,48184,48192,48193,48195,48196,48197,48201,48204,48205,48208,48221,48260,48261,48264,48267,48268,48270,48276,48277,48279,53901,53902,53903,53906,53907,53908,53910,53911,53912,53913,53914,53915,53917,53918,53919,53921,53922,53923,53925,53926,53927,53928,53929,53930,53931,53933,53934,53935,53936,53938,53939,53940,53941,53942,53943,53946,53947,53949,53950,53953,53955,53956,53957,53958,53959,53962,53964,53965,53966,53967,53968,53969,53970,53971,53973,53974,53975,53977,53978,53979,53981,53982,53983,53984,53985,53986,53987,53990,53991,53992,53993,53994,53995,53996,53997,53998,53999,54002,54003,54005,54006,54007,54009,54010,48281,48282,48288,48289,48292,48295,48296,48304,48305,48307,48308,48309,48316,48317,48320,48324,48333,48335,48336,48337,48341,48344,48348,48372,48373,48374,48376,48380,48388,48389,48391,48393,48400,48404,48420,48428,48448,48456,48457,48460,48464,48472,48473,48484,48488,48512,48513,48516,48519,48520,48521,48522,48528,48529,48531,48533,48537,48538,48540,48548,48560,48568,48596,48597,48600,48604,48617,48624,48628,48632,48640,48643,48645,48652,48653,48656,48660,48668,48669,48671,48708,48709,48712,48716,48718,48724,48725,48727,48729,48730,48731,48736,48737,48740,54011,54012,54013,54014,54015,54018,54020,54022,54023,54024,54025,54026,54027,54031,54033,54034,54035,54037,54039,54040,54041,54042,54043,54046,54050,54051,54052,54054,54055,54058,54059,54061,54062,54063,54065,54066,54067,54068,54069,54070,54071,54074,54078,54079,54080,54081,54082,54083,54086,54087,54088,54089,54090,54091,54092,54093,54094,54095,54096,54097,54098,54099,54100,54101,54102,54103,54104,54105,54106,54107,54108,54109,54110,54111,54112,54113,54114,54115,54116,54117,54118,54119,54120,54121,48744,48746,48752,48753,48755,48756,48757,48763,48764,48765,48768,48772,48780,48781,48783,48784,48785,48792,48793,48808,48848,48849,48852,48855,48856,48864,48867,48868,48869,48876,48897,48904,48905,48920,48921,48923,48924,48925,48960,48961,48964,48968,48976,48977,48981,49044,49072,49093,49100,49101,49104,49108,49116,49119,49121,49212,49233,49240,49244,49248,49256,49257,49296,49297,49300,49304,49312,49313,49315,49317,49324,49325,49327,49328,49331,49332,49333,49334,49340,49341,49343,49344,49345,49349,49352,49353,49356,49360,49368,49369,49371,49372,49373,49380,54122,54123,54124,54125,54126,54127,54128,54129,54130,54131,54132,54133,54134,54135,54136,54137,54138,54139,54142,54143,54145,54146,54147,54149,54150,54151,54152,54153,54154,54155,54158,54162,54163,54164,54165,54166,54167,54170,54171,54173,54174,54175,54177,54178,54179,54180,54181,54182,54183,54186,54188,54190,54191,54192,54193,54194,54195,54197,54198,54199,54201,54202,54203,54205,54206,54207,54208,54209,54210,54211,54214,54215,54218,54219,54220,54221,54222,54223,54225,54226,54227,54228,54229,54230,49381,49384,49388,49396,49397,49399,49401,49408,49412,49416,49424,49429,49436,49437,49438,49439,49440,49443,49444,49446,49447,49452,49453,49455,49456,49457,49462,49464,49465,49468,49472,49480,49481,49483,49484,49485,49492,49493,49496,49500,49508,49509,49511,49512,49513,49520,49524,49528,49541,49548,49549,49550,49552,49556,49558,49564,49565,49567,49569,49573,49576,49577,49580,49584,49597,49604,49608,49612,49620,49623,49624,49632,49636,49640,49648,49649,49651,49660,49661,49664,49668,49676,49677,49679,49681,49688,49689,49692,49695,49696,49704,49705,49707,49709,54231,54233,54234,54235,54236,54237,54238,54239,54240,54242,54244,54245,54246,54247,54248,54249,54250,54251,54254,54255,54257,54258,54259,54261,54262,54263,54264,54265,54266,54267,54270,54272,54274,54275,54276,54277,54278,54279,54281,54282,54283,54284,54285,54286,54287,54288,54289,54290,54291,54292,54293,54294,54295,54296,54297,54298,54299,54300,54302,54303,54304,54305,54306,54307,54308,54309,54310,54311,54312,54313,54314,54315,54316,54317,54318,54319,54320,54321,54322,54323,54324,54325,54326,54327,49711,49713,49714,49716,49736,49744,49745,49748,49752,49760,49765,49772,49773,49776,49780,49788,49789,49791,49793,49800,49801,49808,49816,49819,49821,49828,49829,49832,49836,49837,49844,49845,49847,49849,49884,49885,49888,49891,49892,49899,49900,49901,49903,49905,49910,49912,49913,49915,49916,49920,49928,49929,49932,49933,49939,49940,49941,49944,49948,49956,49957,49960,49961,49989,50024,50025,50028,50032,50034,50040,50041,50044,50045,50052,50056,50060,50112,50136,50137,50140,50143,50144,50146,50152,50153,50157,50164,50165,50168,50184,50192,50212,50220,50224,54328,54329,54330,54331,54332,54333,54334,54335,54337,54338,54339,54341,54342,54343,54344,54345,54346,54347,54348,54349,54350,54351,54352,54353,54354,54355,54356,54357,54358,54359,54360,54361,54362,54363,54365,54366,54367,54369,54370,54371,54373,54374,54375,54376,54377,54378,54379,54380,54382,54384,54385,54386,54387,54388,54389,54390,54391,54394,54395,54397,54398,54401,54403,54404,54405,54406,54407,54410,54412,54414,54415,54416,54417,54418,54419,54421,54422,54423,54424,54425,54426,54427,54428,54429,50228,50236,50237,50248,50276,50277,50280,50284,50292,50293,50297,50304,50324,50332,50360,50364,50409,50416,50417,50420,50424,50426,50431,50432,50433,50444,50448,50452,50460,50472,50473,50476,50480,50488,50489,50491,50493,50500,50501,50504,50505,50506,50508,50509,50510,50515,50516,50517,50519,50520,50521,50525,50526,50528,50529,50532,50536,50544,50545,50547,50548,50549,50556,50557,50560,50564,50567,50572,50573,50575,50577,50581,50583,50584,50588,50592,50601,50612,50613,50616,50617,50619,50620,50621,50622,50628,50629,50630,50631,50632,50633,50634,50636,50638,54430,54431,54432,54433,54434,54435,54436,54437,54438,54439,54440,54442,54443,54444,54445,54446,54447,54448,54449,54450,54451,54452,54453,54454,54455,54456,54457,54458,54459,54460,54461,54462,54463,54464,54465,54466,54467,54468,54469,54470,54471,54472,54473,54474,54475,54477,54478,54479,54481,54482,54483,54485,54486,54487,54488,54489,54490,54491,54493,54494,54496,54497,54498,54499,54500,54501,54502,54503,54505,54506,54507,54509,54510,54511,54513,54514,54515,54516,54517,54518,54519,54521,54522,54524,50640,50641,50644,50648,50656,50657,50659,50661,50668,50669,50670,50672,50676,50678,50679,50684,50685,50686,50687,50688,50689,50693,50694,50695,50696,50700,50704,50712,50713,50715,50716,50724,50725,50728,50732,50733,50734,50736,50739,50740,50741,50743,50745,50747,50752,50753,50756,50760,50768,50769,50771,50772,50773,50780,50781,50784,50796,50799,50801,50808,50809,50812,50816,50824,50825,50827,50829,50836,50837,50840,50844,50852,50853,50855,50857,50864,50865,50868,50872,50873,50874,50880,50881,50883,50885,50892,50893,50896,50900,50908,50909,50912,50913,50920,54526,54527,54528,54529,54530,54531,54533,54534,54535,54537,54538,54539,54541,54542,54543,54544,54545,54546,54547,54550,54552,54553,54554,54555,54556,54557,54558,54559,54560,54561,54562,54563,54564,54565,54566,54567,54568,54569,54570,54571,54572,54573,54574,54575,54576,54577,54578,54579,54580,54581,54582,54583,54584,54585,54586,54587,54590,54591,54593,54594,54595,54597,54598,54599,54600,54601,54602,54603,54606,54608,54610,54611,54612,54613,54614,54615,54618,54619,54621,54622,54623,54625,54626,54627,50921,50924,50928,50936,50937,50941,50948,50949,50952,50956,50964,50965,50967,50969,50976,50977,50980,50984,50992,50993,50995,50997,50999,51004,51005,51008,51012,51018,51020,51021,51023,51025,51026,51027,51028,51029,51030,51031,51032,51036,51040,51048,51051,51060,51061,51064,51068,51069,51070,51075,51076,51077,51079,51080,51081,51082,51086,51088,51089,51092,51094,51095,51096,51098,51104,51105,51107,51108,51109,51110,51116,51117,51120,51124,51132,51133,51135,51136,51137,51144,51145,51148,51150,51152,51160,51165,51172,51176,51180,51200,51201,51204,51208,51210,54628,54630,54631,54634,54636,54638,54639,54640,54641,54642,54643,54646,54647,54649,54650,54651,54653,54654,54655,54656,54657,54658,54659,54662,54666,54667,54668,54669,54670,54671,54673,54674,54675,54676,54677,54678,54679,54680,54681,54682,54683,54684,54685,54686,54687,54688,54689,54690,54691,54692,54694,54695,54696,54697,54698,54699,54700,54701,54702,54703,54704,54705,54706,54707,54708,54709,54710,54711,54712,54713,54714,54715,54716,54717,54718,54719,54720,54721,54722,54723,54724,54725,54726,54727,51216,51217,51219,51221,51222,51228,51229,51232,51236,51244,51245,51247,51249,51256,51260,51264,51272,51273,51276,51277,51284,51312,51313,51316,51320,51322,51328,51329,51331,51333,51334,51335,51339,51340,51341,51348,51357,51359,51361,51368,51388,51389,51396,51400,51404,51412,51413,51415,51417,51424,51425,51428,51445,51452,51453,51456,51460,51461,51462,51468,51469,51471,51473,51480,51500,51508,51536,51537,51540,51544,51552,51553,51555,51564,51568,51572,51580,51592,51593,51596,51600,51608,51609,51611,51613,51648,51649,51652,51655,51656,51658,51664,51665,51667,54730,54731,54733,54734,54735,54737,54739,54740,54741,54742,54743,54746,54748,54750,54751,54752,54753,54754,54755,54758,54759,54761,54762,54763,54765,54766,54767,54768,54769,54770,54771,54774,54776,54778,54779,54780,54781,54782,54783,54786,54787,54789,54790,54791,54793,54794,54795,54796,54797,54798,54799,54802,54806,54807,54808,54809,54810,54811,54813,54814,54815,54817,54818,54819,54821,54822,54823,54824,54825,54826,54827,54828,54830,54831,54832,54833,54834,54835,54836,54837,54838,54839,54842,54843,51669,51670,51673,51674,51676,51677,51680,51682,51684,51687,51692,51693,51695,51696,51697,51704,51705,51708,51712,51720,51721,51723,51724,51725,51732,51736,51753,51788,51789,51792,51796,51804,51805,51807,51808,51809,51816,51837,51844,51864,51900,51901,51904,51908,51916,51917,51919,51921,51923,51928,51929,51936,51948,51956,51976,51984,51988,51992,52000,52001,52033,52040,52041,52044,52048,52056,52057,52061,52068,52088,52089,52124,52152,52180,52196,52199,52201,52236,52237,52240,52244,52252,52253,52257,52258,52263,52264,52265,52268,52270,52272,52280,52281,52283,54845,54846,54847,54849,54850,54851,54852,54854,54855,54858,54860,54862,54863,54864,54866,54867,54870,54871,54873,54874,54875,54877,54878,54879,54880,54881,54882,54883,54884,54885,54886,54888,54890,54891,54892,54893,54894,54895,54898,54899,54901,54902,54903,54904,54905,54906,54907,54908,54909,54910,54911,54912,54913,54914,54916,54918,54919,54920,54921,54922,54923,54926,54927,54929,54930,54931,54933,54934,54935,54936,54937,54938,54939,54940,54942,54944,54946,54947,54948,54949,54950,54951,54953,54954,52284,52285,52286,52292,52293,52296,52300,52308,52309,52311,52312,52313,52320,52324,52326,52328,52336,52341,52376,52377,52380,52384,52392,52393,52395,52396,52397,52404,52405,52408,52412,52420,52421,52423,52425,52432,52436,52452,52460,52464,52481,52488,52489,52492,52496,52504,52505,52507,52509,52516,52520,52524,52537,52572,52576,52580,52588,52589,52591,52593,52600,52616,52628,52629,52632,52636,52644,52645,52647,52649,52656,52676,52684,52688,52712,52716,52720,52728,52729,52731,52733,52740,52744,52748,52756,52761,52768,52769,52772,52776,52784,52785,52787,52789,54955,54957,54958,54959,54961,54962,54963,54964,54965,54966,54967,54968,54970,54972,54973,54974,54975,54976,54977,54978,54979,54982,54983,54985,54986,54987,54989,54990,54991,54992,54994,54995,54997,54998,55000,55002,55003,55004,55005,55006,55007,55009,55010,55011,55013,55014,55015,55017,55018,55019,55020,55021,55022,55023,55025,55026,55027,55028,55030,55031,55032,55033,55034,55035,55038,55039,55041,55042,55043,55045,55046,55047,55048,55049,55050,55051,55052,55053,55054,55055,55056,55058,55059,55060,52824,52825,52828,52831,52832,52833,52840,52841,52843,52845,52852,52853,52856,52860,52868,52869,52871,52873,52880,52881,52884,52888,52896,52897,52899,52900,52901,52908,52909,52929,52964,52965,52968,52971,52972,52980,52981,52983,52984,52985,52992,52993,52996,53000,53008,53009,53011,53013,53020,53024,53028,53036,53037,53039,53040,53041,53048,53076,53077,53080,53084,53092,53093,53095,53097,53104,53105,53108,53112,53120,53125,53132,53153,53160,53168,53188,53216,53217,53220,53224,53232,53233,53235,53237,53244,53248,53252,53265,53272,53293,53300,53301,53304,53308,55061,55062,55063,55066,55067,55069,55070,55071,55073,55074,55075,55076,55077,55078,55079,55082,55084,55086,55087,55088,55089,55090,55091,55094,55095,55097,55098,55099,55101,55102,55103,55104,55105,55106,55107,55109,55110,55112,55114,55115,55116,55117,55118,55119,55122,55123,55125,55130,55131,55132,55133,55134,55135,55138,55140,55142,55143,55144,55146,55147,55149,55150,55151,55153,55154,55155,55157,55158,55159,55160,55161,55162,55163,55166,55167,55168,55170,55171,55172,55173,55174,55175,55178,55179,53316,53317,53319,53321,53328,53332,53336,53344,53356,53357,53360,53364,53372,53373,53377,53412,53413,53416,53420,53428,53429,53431,53433,53440,53441,53444,53448,53449,53456,53457,53459,53460,53461,53468,53469,53472,53476,53484,53485,53487,53488,53489,53496,53517,53552,53553,53556,53560,53562,53568,53569,53571,53572,53573,53580,53581,53584,53588,53596,53597,53599,53601,53608,53612,53628,53636,53640,53664,53665,53668,53672,53680,53681,53683,53685,53690,53692,53696,53720,53748,53752,53767,53769,53776,53804,53805,53808,53812,53820,53821,53823,53825,53832,53852,55181,55182,55183,55185,55186,55187,55188,55189,55190,55191,55194,55196,55198,55199,55200,55201,55202,55203,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,53860,53888,53889,53892,53896,53904,53905,53909,53916,53920,53924,53932,53937,53944,53945,53948,53951,53952,53954,53960,53961,53963,53972,53976,53980,53988,53989,54000,54001,54004,54008,54016,54017,54019,54021,54028,54029,54030,54032,54036,54038,54044,54045,54047,54048,54049,54053,54056,54057,54060,54064,54072,54073,54075,54076,54077,54084,54085,54140,54141,54144,54148,54156,54157,54159,54160,54161,54168,54169,54172,54176,54184,54185,54187,54189,54196,54200,54204,54212,54213,54216,54217,54224,54232,54241,54243,54252,54253,54256,54260,54268,54269,54271,54273,54280,54301,54336,54340,54364,54368,54372,54381,54383,54392,54393,54396,54399,54400,54402,54408,54409,54411,54413,54420,54441,54476,54480,54484,54492,54495,54504,54508,54512,54520,54523,54525,54532,54536,54540,54548,54549,54551,54588,54589,54592,54596,54604,54605,54607,54609,54616,54617,54620,54624,54629,54632,54633,54635,54637,54644,54645,54648,54652,54660,54661,54663,54664,54665,54672,54693,54728,54729,54732,54736,54738,54744,54745,54747,54749,54756,54757,54760,54764,54772,54773,54775,54777,54784,54785,54788,54792,54800,54801,54803,54804,54805,54812,54816,54820,54829,54840,54841,54844,54848,54853,54856,54857,54859,54861,54865,54868,54869,54872,54876,54887,54889,54896,54897,54900,54915,54917,54924,54925,54928,54932,54941,54943,54945,54952,54956,54960,54969,54971,54980,54981,54984,54988,54993,54996,54999,55001,55008,55012,55016,55024,55029,55036,55037,55040,55044,55057,55064,55065,55068,55072,55080,55081,55083,55085,55092,55093,55096,55100,55108,55111,55113,55120,55121,55124,55126,55127,55128,55129,55136,55137,55139,55141,55145,55148,55152,55156,55164,55165,55169,55176,55177,55180,55184,55192,55193,55195,55197,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,20285,20339,20551,20729,21152,21487,21621,21733,22025,23233,23478,26247,26550,26551,26607,27468,29634,30146,31292,33499,33540,34903,34952,35382,36040,36303,36603,36838,39381,21051,21364,21508,24682,24932,27580,29647,33050,35258,35282,38307,20355,21002,22718,22904,23014,24178,24185,25031,25536,26438,26604,26751,28567,30286,30475,30965,31240,31487,31777,32925,33390,33393,35563,38291,20075,21917,26359,28212,30883,31469,33883,35088,34638,38824,21208,22350,22570,23884,24863,25022,25121,25954,26577,27204,28187,29976,30131,30435,30640,32058,37039,37969,37970,40853,21283,23724,30002,32987,37440,38296,21083,22536,23004,23713,23831,24247,24378,24394,24951,27743,30074,30086,31968,32115,32177,32652,33108,33313,34193,35137,35611,37628,38477,40007,20171,20215,20491,20977,22607,24887,24894,24936,25913,27114,28433,30117,30342,30422,31623,33445,33995,63744,37799,38283,21888,23458,22353,63745,31923,32697,37301,20520,21435,23621,24040,25298,25454,25818,25831,28192,28844,31067,36317,36382,63746,36989,37445,37624,20094,20214,20581,24062,24314,24838,26967,33137,34388,36423,37749,39467,20062,20625,26480,26688,20745,21133,21138,27298,30652,37392,40660,21163,24623,36850,20552,25001,25581,25802,26684,27268,28608,33160,35233,38548,22533,29309,29356,29956,32121,32365,32937,35211,35700,36963,40273,25225,27770,28500,32080,32570,35363,20860,24906,31645,35609,37463,37772,20140,20435,20510,20670,20742,21185,21197,21375,22384,22659,24218,24465,24950,25004,25806,25964,26223,26299,26356,26775,28039,28805,28913,29855,29861,29898,30169,30828,30956,31455,31478,32069,32147,32789,32831,33051,33686,35686,36629,36885,37857,38915,38968,39514,39912,20418,21843,22586,22865,23395,23622,24760,25106,26690,26800,26856,28330,30028,30328,30926,31293,31995,32363,32380,35336,35489,35903,38542,40388,21476,21481,21578,21617,22266,22993,23396,23611,24235,25335,25911,25925,25970,26272,26543,27073,27837,30204,30352,30590,31295,32660,32771,32929,33167,33510,33533,33776,34241,34865,34996,35493,63747,36764,37678,38599,39015,39640,40723,21741,26011,26354,26767,31296,35895,40288,22256,22372,23825,26118,26801,26829,28414,29736,34974,39908,27752,63748,39592,20379,20844,20849,21151,23380,24037,24656,24685,25329,25511,25915,29657,31354,34467,36002,38799,20018,23521,25096,26524,29916,31185,33747,35463,35506,36328,36942,37707,38982,24275,27112,34303,37101,63749,20896,23448,23532,24931,26874,27454,28748,29743,29912,31649,32592,33733,35264,36011,38364,39208,21038,24669,25324,36866,20362,20809,21281,22745,24291,26336,27960,28826,29378,29654,31568,33009,37979,21350,25499,32619,20054,20608,22602,22750,24618,24871,25296,27088,39745,23439,32024,32945,36703,20132,20689,21676,21932,23308,23968,24039,25898,25934,26657,27211,29409,30350,30703,32094,32761,33184,34126,34527,36611,36686,37066,39171,39509,39851,19992,20037,20061,20167,20465,20855,21246,21312,21475,21477,21646,22036,22389,22434,23495,23943,24272,25084,25304,25937,26552,26601,27083,27472,27590,27628,27714,28317,28792,29399,29590,29699,30655,30697,31350,32127,32777,33276,33285,33290,33503,34914,35635,36092,36544,36881,37041,37476,37558,39378,39493,40169,40407,40860,22283,23616,33738,38816,38827,40628,21531,31384,32676,35033,36557,37089,22528,23624,25496,31391,23470,24339,31353,31406,33422,36524,20518,21048,21240,21367,22280,25331,25458,27402,28099,30519,21413,29527,34152,36470,38357,26426,27331,28528,35437,36556,39243,63750,26231,27512,36020,39740,63751,21483,22317,22862,25542,27131,29674,30789,31418,31429,31998,33909,35215,36211,36917,38312,21243,22343,30023,31584,33740,37406,63752,27224,20811,21067,21127,25119,26840,26997,38553,20677,21156,21220,25027,26020,26681,27135,29822,31563,33465,33771,35250,35641,36817,39241,63753,20170,22935,25810,26129,27278,29748,31105,31165,33449,34942,34943,35167,63754,37670,20235,21450,24613,25201,27762,32026,32102,20120,20834,30684,32943,20225,20238,20854,20864,21980,22120,22331,22522,22524,22804,22855,22931,23492,23696,23822,24049,24190,24524,25216,26071,26083,26398,26399,26462,26827,26820,27231,27450,27683,27773,27778,28103,29592,29734,29738,29826,29859,30072,30079,30849,30959,31041,31047,31048,31098,31637,32000,32186,32648,32774,32813,32908,35352,35663,35912,36215,37665,37668,39138,39249,39438,39439,39525,40594,32202,20342,21513,25326,26708,37329,21931,20794,63755,63756,23068,25062,63757,25295,25343,63758,63759,63760,63761,63762,63763,37027,63764,63765,63766,63767,63768,35582,63769,63770,63771,63772,26262,63773,29014,63774,63775,38627,63776,25423,25466,21335,63777,26511,26976,28275,63778,30007,63779,63780,63781,32013,63782,63783,34930,22218,23064,63784,63785,63786,63787,63788,20035,63789,20839,22856,26608,32784,63790,22899,24180,25754,31178,24565,24684,25288,25467,23527,23511,21162,63791,22900,24361,24594,63792,63793,63794,29785,63795,63796,63797,63798,63799,63800,39377,63801,63802,63803,63804,63805,63806,63807,63808,63809,63810,63811,28611,63812,63813,33215,36786,24817,63814,63815,33126,63816,63817,23615,63818,63819,63820,63821,63822,63823,63824,63825,23273,35365,26491,32016,63826,63827,63828,63829,63830,63831,33021,63832,63833,23612,27877,21311,28346,22810,33590,20025,20150,20294,21934,22296,22727,24406,26039,26086,27264,27573,28237,30701,31471,31774,32222,34507,34962,37170,37723,25787,28606,29562,30136,36948,21846,22349,25018,25812,26311,28129,28251,28525,28601,30192,32835,33213,34113,35203,35527,35674,37663,27795,30035,31572,36367,36957,21776,22530,22616,24162,25095,25758,26848,30070,31958,34739,40680,20195,22408,22382,22823,23565,23729,24118,24453,25140,25825,29619,33274,34955,36024,38538,40667,23429,24503,24755,20498,20992,21040,22294,22581,22615,23566,23648,23798,23947,24230,24466,24764,25361,25481,25623,26691,26873,27330,28120,28193,28372,28644,29182,30428,30585,31153,31291,33796,35241,36077,36339,36424,36867,36884,36947,37117,37709,38518,38876,27602,28678,29272,29346,29544,30563,31167,31716,32411,35712,22697,24775,25958,26109,26302,27788,28958,29129,35930,38931,20077,31361,20189,20908,20941,21205,21516,24999,26481,26704,26847,27934,28540,30140,30643,31461,33012,33891,37509,20828,26007,26460,26515,30168,31431,33651,63834,35910,36887,38957,23663,33216,33434,36929,36975,37389,24471,23965,27225,29128,30331,31561,34276,35588,37159,39472,21895,25078,63835,30313,32645,34367,34746,35064,37007,63836,27931,28889,29662,32097,33853,63837,37226,39409,63838,20098,21365,27396,27410,28734,29211,34349,40478,21068,36771,23888,25829,25900,27414,28651,31811,32412,34253,35172,35261,25289,33240,34847,24266,26391,28010,29436,29701,29807,34690,37086,20358,23821,24480,33802,20919,25504,30053,20142,20486,20841,20937,26753,27153,31918,31921,31975,33391,35538,36635,37327,20406,20791,21237,21570,24300,24942,25150,26053,27354,28670,31018,34268,34851,38317,39522,39530,40599,40654,21147,26310,27511,28701,31019,36706,38722,24976,25088,25891,28451,29001,29833,32244,32879,34030,36646,36899,37706,20925,21015,21155,27916,28872,35010,24265,25986,27566,28610,31806,29557,20196,20278,22265,63839,23738,23994,24604,29618,31533,32666,32718,32838,36894,37428,38646,38728,38936,40801,20363,28583,31150,37300,38583,21214,63840,25736,25796,27347,28510,28696,29200,30439,32769,34310,34396,36335,36613,38706,39791,40442,40565,30860,31103,32160,33737,37636,40575,40595,35542,22751,24324,26407,28711,29903,31840,32894,20769,28712,29282,30922,36034,36058,36084,38647,20102,20698,23534,24278,26009,29134,30274,30637,32842,34044,36988,39719,40845,22744,23105,23650,27155,28122,28431,30267,32047,32311,34078,35128,37860,38475,21129,26066,26611,27060,27969,28316,28687,29705,29792,30041,30244,30827,35628,39006,20845,25134,38520,20374,20523,23833,28138,32184,36650,24459,24900,26647,63841,38534,21202,32907,20956,20940,26974,31260,32190,33777,38517,20442,21033,21400,21519,21774,23653,24743,26446,26792,28012,29313,29432,29702,29827,63842,30178,31852,32633,32696,33673,35023,35041,37324,37328,38626,39881,21533,28542,29136,29848,34298,36522,38563,40023,40607,26519,28107,29747,33256,38678,30764,31435,31520,31890,25705,29802,30194,30908,30952,39340,39764,40635,23518,24149,28448,33180,33707,37000,19975,21325,23081,24018,24398,24930,25405,26217,26364,28415,28459,28771,30622,33836,34067,34875,36627,39237,39995,21788,25273,26411,27819,33545,35178,38778,20129,22916,24536,24537,26395,32178,32596,33426,33579,33725,36638,37017,22475,22969,23186,23504,26151,26522,26757,27599,29028,32629,36023,36067,36993,39749,33032,35978,38476,39488,40613,23391,27667,29467,30450,30431,33804,20906,35219,20813,20885,21193,26825,27796,30468,30496,32191,32236,38754,40629,28357,34065,20901,21517,21629,26126,26269,26919,28319,30399,30609,33559,33986,34719,37225,37528,40180,34946,20398,20882,21215,22982,24125,24917,25720,25721,26286,26576,27169,27597,27611,29279,29281,29761,30520,30683,32791,33468,33541,35584,35624,35980,26408,27792,29287,30446,30566,31302,40361,27519,27794,22818,26406,33945,21359,22675,22937,24287,25551,26164,26483,28218,29483,31447,33495,37672,21209,24043,25006,25035,25098,25287,25771,26080,26969,27494,27595,28961,29687,30045,32326,33310,33538,34154,35491,36031,38695,40289,22696,40664,20497,21006,21563,21839,25991,27766,32010,32011,32862,34442,38272,38639,21247,27797,29289,21619,23194,23614,23883,24396,24494,26410,26806,26979,28220,28228,30473,31859,32654,34183,35598,36855,38753,40692,23735,24758,24845,25003,25935,26107,26108,27665,27887,29599,29641,32225,38292,23494,34588,35600,21085,21338,25293,25615,25778,26420,27192,27850,29632,29854,31636,31893,32283,33162,33334,34180,36843,38649,39361,20276,21322,21453,21467,25292,25644,25856,26001,27075,27886,28504,29677,30036,30242,30436,30460,30928,30971,31020,32070,33324,34784,36820,38930,39151,21187,25300,25765,28196,28497,30332,36299,37297,37474,39662,39747,20515,20621,22346,22952,23592,24135,24439,25151,25918,26041,26049,26121,26507,27036,28354,30917,32033,32938,33152,33323,33459,33953,34444,35370,35607,37030,38450,40848,20493,20467,63843,22521,24472,25308,25490,26479,28227,28953,30403,32972,32986,35060,35061,35097,36064,36649,37197,38506,20271,20336,24091,26575,26658,30333,30334,39748,24161,27146,29033,29140,30058,63844,32321,34115,34281,39132,20240,31567,32624,38309,20961,24070,26805,27710,27726,27867,29359,31684,33539,27861,29754,20731,21128,22721,25816,27287,29863,30294,30887,34327,38370,38713,63845,21342,24321,35722,36776,36783,37002,21029,30629,40009,40712,19993,20482,20853,23643,24183,26142,26170,26564,26821,28851,29953,30149,31177,31453,36647,39200,39432,20445,22561,22577,23542,26222,27493,27921,28282,28541,29668,29995,33769,35036,35091,35676,36628,20239,20693,21264,21340,23443,24489,26381,31119,33145,33583,34068,35079,35206,36665,36667,39333,39954,26412,20086,20472,22857,23553,23791,23792,25447,26834,28925,29090,29739,32299,34028,34562,36898,37586,40179,19981,20184,20463,20613,21078,21103,21542,21648,22496,22827,23142,23386,23413,23500,24220,63846,25206,25975,26023,28014,28325,29238,31526,31807,32566,33104,33105,33178,33344,33433,33705,35331,36000,36070,36091,36212,36282,37096,37340,38428,38468,39385,40167,21271,20998,21545,22132,22707,22868,22894,24575,24996,25198,26128,27774,28954,30406,31881,31966,32027,33452,36033,38640,63847,20315,24343,24447,25282,23849,26379,26842,30844,32323,40300,19989,20633,21269,21290,21329,22915,23138,24199,24754,24970,25161,25209,26000,26503,27047,27604,27606,27607,27608,27832,63848,29749,30202,30738,30865,31189,31192,31875,32203,32737,32933,33086,33218,33778,34586,35048,35513,35692,36027,37145,38750,39131,40763,22188,23338,24428,25996,27315,27567,27996,28657,28693,29277,29613,36007,36051,38971,24977,27703,32856,39425,20045,20107,20123,20181,20282,20284,20351,20447,20735,21490,21496,21766,21987,22235,22763,22882,23057,23531,23546,23556,24051,24107,24473,24605,25448,26012,26031,26614,26619,26797,27515,27801,27863,28195,28681,29509,30722,31038,31040,31072,31169,31721,32023,32114,32902,33293,33678,34001,34503,35039,35408,35422,35613,36060,36198,36781,37034,39164,39391,40605,21066,63849,26388,63850,20632,21034,23665,25955,27733,29642,29987,30109,31639,33948,37240,38704,20087,25746,27578,29022,34217,19977,63851,26441,26862,28183,33439,34072,34923,25591,28545,37394,39087,19978,20663,20687,20767,21830,21930,22039,23360,23577,23776,24120,24202,24224,24258,24819,26705,27233,28248,29245,29248,29376,30456,31077,31665,32724,35059,35316,35443,35937,36062,38684,22622,29885,36093,21959,63852,31329,32034,33394,29298,29983,29989,63853,31513,22661,22779,23996,24207,24246,24464,24661,25234,25471,25933,26257,26329,26360,26646,26866,29312,29790,31598,32110,32214,32626,32997,33298,34223,35199,35475,36893,37604,40653,40736,22805,22893,24109,24796,26132,26227,26512,27728,28101,28511,30707,30889,33990,37323,37675,20185,20682,20808,21892,23307,23459,25159,25982,26059,28210,29053,29697,29764,29831,29887,30316,31146,32218,32341,32680,33146,33203,33337,34330,34796,35445,36323,36984,37521,37925,39245,39854,21352,23633,26964,27844,27945,28203,33292,34203,35131,35373,35498,38634,40807,21089,26297,27570,32406,34814,36109,38275,38493,25885,28041,29166,63854,22478,22995,23468,24615,24826,25104,26143,26207,29481,29689,30427,30465,31596,32854,32882,33125,35488,37266,19990,21218,27506,27927,31237,31545,32048,63855,36016,21484,22063,22609,23477,23567,23569,24034,25152,25475,25620,26157,26803,27836,28040,28335,28703,28836,29138,29990,30095,30094,30233,31505,31712,31787,32032,32057,34092,34157,34311,35380,36877,36961,37045,37559,38902,39479,20439,23660,26463,28049,31903,32396,35606,36118,36895,23403,24061,25613,33984,36956,39137,29575,23435,24730,26494,28126,35359,35494,36865,38924,21047,63856,28753,30862,37782,34928,37335,20462,21463,22013,22234,22402,22781,23234,23432,23723,23744,24101,24833,25101,25163,25480,25628,25910,25976,27193,27530,27700,27929,28465,29159,29417,29560,29703,29874,30246,30561,31168,31319,31466,31929,32143,32172,32353,32670,33065,33585,33936,34010,34282,34966,35504,35728,36664,36930,36995,37228,37526,37561,38539,38567,38568,38614,38656,38920,39318,39635,39706,21460,22654,22809,23408,23487,28113,28506,29087,29729,29881,32901,33789,24033,24455,24490,24642,26092,26642,26991,27219,27529,27957,28147,29667,30462,30636,31565,32020,33059,33308,33600,34036,34147,35426,35524,37255,37662,38918,39348,25100,34899,36848,37477,23815,23847,23913,29791,33181,34664,28629,25342,32722,35126,35186,19998,20056,20711,21213,21319,25215,26119,32361,34821,38494,20365,21273,22070,22987,23204,23608,23630,23629,24066,24337,24643,26045,26159,26178,26558,26612,29468,30690,31034,32709,33940,33997,35222,35430,35433,35553,35925,35962,22516,23508,24335,24687,25325,26893,27542,28252,29060,31698,34645,35672,36606,39135,39166,20280,20353,20449,21627,23072,23480,24892,26032,26216,29180,30003,31070,32051,33102,33251,33688,34218,34254,34563,35338,36523,36763,63857,36805,22833,23460,23526,24713,23529,23563,24515,27777,63858,28145,28683,29978,33455,35574,20160,21313,63859,38617,27663,20126,20420,20818,21854,23077,23784,25105,29273,33469,33706,34558,34905,35357,38463,38597,39187,40201,40285,22538,23731,23997,24132,24801,24853,25569,27138,28197,37122,37716,38990,39952,40823,23433,23736,25353,26191,26696,30524,38593,38797,38996,39839,26017,35585,36555,38332,21813,23721,24022,24245,26263,30284,33780,38343,22739,25276,29390,40232,20208,22830,24591,26171,27523,31207,40230,21395,21696,22467,23830,24859,26326,28079,30861,33406,38552,38724,21380,25212,25494,28082,32266,33099,38989,27387,32588,40367,40474,20063,20539,20918,22812,24825,25590,26928,29242,32822,63860,37326,24369,63861,63862,32004,33509,33903,33979,34277,36493,63863,20335,63864,63865,22756,23363,24665,25562,25880,25965,26264,63866,26954,27171,27915,28673,29036,30162,30221,31155,31344,63867,32650,63868,35140,63869,35731,37312,38525,63870,39178,22276,24481,26044,28417,30208,31142,35486,39341,39770,40812,20740,25014,25233,27277,33222,20547,22576,24422,28937,35328,35578,23420,34326,20474,20796,22196,22852,25513,28153,23978,26989,20870,20104,20313,63871,63872,63873,22914,63874,63875,27487,27741,63876,29877,30998,63877,33287,33349,33593,36671,36701,63878,39192,63879,63880,63881,20134,63882,22495,24441,26131,63883,63884,30123,32377,35695,63885,36870,39515,22181,22567,23032,23071,23476,63886,24310,63887,63888,25424,25403,63889,26941,27783,27839,28046,28051,28149,28436,63890,28895,28982,29017,63891,29123,29141,63892,30799,30831,63893,31605,32227,63894,32303,63895,34893,36575,63896,63897,63898,37467,63899,40182,63900,63901,63902,24709,28037,63903,29105,63904,63905,38321,21421,63906,63907,63908,26579,63909,28814,28976,29744,33398,33490,63910,38331,39653,40573,26308,63911,29121,33865,63912,63913,22603,63914,63915,23992,24433,63916,26144,26254,27001,27054,27704,27891,28214,28481,28634,28699,28719,29008,29151,29552,63917,29787,63918,29908,30408,31310,32403,63919,63920,33521,35424,36814,63921,37704,63922,38681,63923,63924,20034,20522,63925,21000,21473,26355,27757,28618,29450,30591,31330,33454,34269,34306,63926,35028,35427,35709,35947,63927,37555,63928,38675,38928,20116,20237,20425,20658,21320,21566,21555,21978,22626,22714,22887,23067,23524,24735,63929,25034,25942,26111,26212,26791,27738,28595,28879,29100,29522,31613,34568,35492,39986,40711,23627,27779,29508,29577,37434,28331,29797,30239,31337,32277,34314,20800,22725,25793,29934,29973,30320,32705,37013,38605,39252,28198,29926,31401,31402,33253,34521,34680,35355,23113,23436,23451,26785,26880,28003,29609,29715,29740,30871,32233,32747,33048,33109,33694,35916,38446,38929,26352,24448,26106,26505,27754,29579,20525,23043,27498,30702,22806,23916,24013,29477,30031,63930,63931,20709,20985,22575,22829,22934,23002,23525,63932,63933,23970,25303,25622,25747,25854,63934,26332,63935,27208,63936,29183,29796,63937,31368,31407,32327,32350,32768,33136,63938,34799,35201,35616,36953,63939,36992,39250,24958,27442,28020,32287,35109,36785,20433,20653,20887,21191,22471,22665,23481,24248,24898,27029,28044,28263,28342,29076,29794,29992,29996,32883,33592,33993,36362,37780,37854,63940,20110,20305,20598,20778,21448,21451,21491,23431,23507,23588,24858,24962,26100,29275,29591,29760,30402,31056,31121,31161,32006,32701,33419,34261,34398,36802,36935,37109,37354,38533,38632,38633,21206,24423,26093,26161,26671,29020,31286,37057,38922,20113,63941,27218,27550,28560,29065,32792,33464,34131,36939,38549,38642,38907,34074,39729,20112,29066,38596,20803,21407,21729,22291,22290,22435,23195,23236,23491,24616,24895,25588,27781,27961,28274,28304,29232,29503,29783,33489,34945,36677,36960,63942,38498,39000,40219,26376,36234,37470,20301,20553,20702,21361,22285,22996,23041,23561,24944,26256,28205,29234,29771,32239,32963,33806,33894,34111,34655,34907,35096,35586,36949,38859,39759,20083,20369,20754,20842,63943,21807,21929,23418,23461,24188,24189,24254,24736,24799,24840,24841,25540,25912,26377,63944,26580,26586,63945,26977,26978,27833,27943,63946,28216,63947,28641,29494,29495,63948,29788,30001,63949,30290,63950,63951,32173,33278,33848,35029,35480,35547,35565,36400,36418,36938,36926,36986,37193,37321,37742,63952,63953,22537,63954,27603,32905,32946,63955,63956,20801,22891,23609,63957,63958,28516,29607,32996,36103,63959,37399,38287,63960,63961,63962,63963,32895,25102,28700,32104,34701,63964,22432,24681,24903,27575,35518,37504,38577,20057,21535,28139,34093,38512,38899,39150,25558,27875,37009,20957,25033,33210,40441,20381,20506,20736,23452,24847,25087,25836,26885,27589,30097,30691,32681,33380,34191,34811,34915,35516,35696,37291,20108,20197,20234,63965,63966,22839,23016,63967,24050,24347,24411,24609,63968,63969,63970,63971,29246,29669,63972,30064,30157,63973,31227,63974,32780,32819,32900,33505,33617,63975,63976,36029,36019,36999,63977,63978,39156,39180,63979,63980,28727,30410,32714,32716,32764,35610,20154,20161,20995,21360,63981,21693,22240,23035,23493,24341,24525,28270,63982,63983,32106,33589,63984,34451,35469,63985,38765,38775,63986,63987,19968,20314,20350,22777,26085,28322,36920,37808,39353,20219,22764,22922,23001,24641,63988,63989,31252,63990,33615,36035,20837,21316,63991,63992,63993,20173,21097,23381,33471,20180,21050,21672,22985,23039,23376,23383,23388,24675,24904,28363,28825,29038,29574,29943,30133,30913,32043,32773,33258,33576,34071,34249,35566,36039,38604,20316,21242,22204,26027,26152,28796,28856,29237,32189,33421,37196,38592,40306,23409,26855,27544,28538,30430,23697,26283,28507,31668,31786,34870,38620,19976,20183,21280,22580,22715,22767,22892,23559,24115,24196,24373,25484,26290,26454,27167,27299,27404,28479,29254,63994,29520,29835,31456,31911,33144,33247,33255,33674,33900,34083,34196,34255,35037,36115,37292,38263,38556,20877,21705,22312,23472,25165,26448,26685,26771,28221,28371,28797,32289,35009,36001,36617,40779,40782,29229,31631,35533,37658,20295,20302,20786,21632,22992,24213,25269,26485,26990,27159,27822,28186,29401,29482,30141,31672,32053,33511,33785,33879,34295,35419,36015,36487,36889,37048,38606,40799,21219,21514,23265,23490,25688,25973,28404,29380,63995,30340,31309,31515,31821,32318,32735,33659,35627,36042,36196,36321,36447,36842,36857,36969,37841,20291,20346,20659,20840,20856,21069,21098,22625,22652,22880,23560,23637,24283,24731,25136,26643,27583,27656,28593,29006,29728,30000,30008,30033,30322,31564,31627,31661,31686,32399,35438,36670,36681,37439,37523,37666,37931,38651,39002,39019,39198,20999,25130,25240,27993,30308,31434,31680,32118,21344,23742,24215,28472,28857,31896,38673,39822,40670,25509,25722,34678,19969,20117,20141,20572,20597,21576,22979,23450,24128,24237,24311,24449,24773,25402,25919,25972,26060,26230,26232,26622,26984,27273,27491,27712,28096,28136,28191,28254,28702,28833,29582,29693,30010,30555,30855,31118,31243,31357,31934,32142,33351,35330,35562,35998,37165,37194,37336,37478,37580,37664,38662,38742,38748,38914,40718,21046,21137,21884,22564,24093,24351,24716,25552,26799,28639,31085,31532,33229,34234,35069,35576,36420,37261,38500,38555,38717,38988,40778,20430,20806,20939,21161,22066,24340,24427,25514,25805,26089,26177,26362,26361,26397,26781,26839,27133,28437,28526,29031,29157,29226,29866,30522,31062,31066,31199,31264,31381,31895,31967,32068,32368,32903,34299,34468,35412,35519,36249,36481,36896,36973,37347,38459,38613,40165,26063,31751,36275,37827,23384,23562,21330,25305,29469,20519,23447,24478,24752,24939,26837,28121,29742,31278,32066,32156,32305,33131,36394,36405,37758,37912,20304,22352,24038,24231,25387,32618,20027,20303,20367,20570,23005,32964,21610,21608,22014,22863,23449,24030,24282,26205,26417,26609,26666,27880,27954,28234,28557,28855,29664,30087,31820,32002,32044,32162,33311,34523,35387,35461,36208,36490,36659,36913,37198,37202,37956,39376,31481,31909,20426,20737,20934,22472,23535,23803,26201,27197,27994,28310,28652,28940,30063,31459,34850,36897,36981,38603,39423,33537,20013,20210,34886,37325,21373,27355,26987,27713,33914,22686,24974,26366,25327,28893,29969,30151,32338,33976,35657,36104,20043,21482,21675,22320,22336,24535,25345,25351,25711,25903,26088,26234,26525,26547,27490,27744,27802,28460,30693,30757,31049,31063,32025,32930,33026,33267,33437,33463,34584,35468,63996,36100,36286,36978,30452,31257,31287,32340,32887,21767,21972,22645,25391,25634,26185,26187,26733,27035,27524,27941,28337,29645,29800,29857,30043,30137,30433,30494,30603,31206,32265,32285,33275,34095,34967,35386,36049,36587,36784,36914,37805,38499,38515,38663,20356,21489,23018,23241,24089,26702,29894,30142,31209,31378,33187,34541,36074,36300,36845,26015,26389,63997,22519,28503,32221,36655,37878,38598,24501,25074,28548,19988,20376,20511,21449,21983,23919,24046,27425,27492,30923,31642,63998,36425,36554,36974,25417,25662,30528,31364,37679,38015,40810,25776,28591,29158,29864,29914,31428,31762,32386,31922,32408,35738,36106,38013,39184,39244,21049,23519,25830,26413,32046,20717,21443,22649,24920,24921,25082,26028,31449,35730,35734,20489,20513,21109,21809,23100,24288,24432,24884,25950,26124,26166,26274,27085,28356,28466,29462,30241,31379,33081,33369,33750,33980,20661,22512,23488,23528,24425,25505,30758,32181,33756,34081,37319,37365,20874,26613,31574,36012,20932,22971,24765,34389,20508,63999,21076,23610,24957,25114,25299,25842,26021,28364,30240,33034,36448,38495,38587,20191,21315,21912,22825,24029,25797,27849,28154,29588,31359,33307,34214,36068,36368,36983,37351,38369,38433,38854,20984,21746,21894,24505,25764,28552,32180,36639,36685,37941,20681,23574,27838,28155,29979,30651,31805,31844,35449,35522,22558,22974,24086,25463,29266,30090,30571,35548,36028,36626,24307,26228,28152,32893,33729,35531,38737,39894,64000,21059,26367,28053,28399,32224,35558,36910,36958,39636,21021,21119,21736,24980,25220,25307,26786,26898,26970,27189,28818,28966,30813,30977,30990,31186,31245,32918,33400,33493,33609,34121,35970,36229,37218,37259,37294,20419,22225,29165,30679,34560,35320,23544,24534,26449,37032,21474,22618,23541,24740,24961,25696,32317,32880,34085,37507,25774,20652,23828,26368,22684,25277,25512,26894,27000,27166,28267,30394,31179,33467,33833,35535,36264,36861,37138,37195,37276,37648,37656,37786,38619,39478,39949,19985,30044,31069,31482,31569,31689,32302,33988,36441,36468,36600,36880,26149,26943,29763,20986,26414,40668,20805,24544,27798,34802,34909,34935,24756,33205,33795,36101,21462,21561,22068,23094,23601,28810,32736,32858,33030,33261,36259,37257,39519,40434,20596,20164,21408,24827,28204,23652,20360,20516,21988,23769,24159,24677,26772,27835,28100,29118,30164,30196,30305,31258,31305,32199,32251,32622,33268,34473,36636,38601,39347,40786,21063,21189,39149,35242,19971,26578,28422,20405,23522,26517,27784,28024,29723,30759,37341,37756,34756,31204,31281,24555,20182,21668,21822,22702,22949,24816,25171,25302,26422,26965,33333,38464,39345,39389,20524,21331,21828,22396,64001,25176,64002,25826,26219,26589,28609,28655,29730,29752,35351,37944,21585,22022,22374,24392,24986,27470,28760,28845,32187,35477,22890,33067,25506,30472,32829,36010,22612,25645,27067,23445,24081,28271,64003,34153,20812,21488,22826,24608,24907,27526,27760,27888,31518,32974,33492,36294,37040,39089,64004,25799,28580,25745,25860,20814,21520,22303,35342,24927,26742,64005,30171,31570,32113,36890,22534,27084,33151,35114,36864,38969,20600,22871,22956,25237,36879,39722,24925,29305,38358,22369,23110,24052,25226,25773,25850,26487,27874,27966,29228,29750,30772,32631,33453,36315,38935,21028,22338,26495,29256,29923,36009,36774,37393,38442,20843,21485,25420,20329,21764,24726,25943,27803,28031,29260,29437,31255,35207,35997,24429,28558,28921,33192,24846,20415,20559,25153,29255,31687,32232,32745,36941,38829,39449,36022,22378,24179,26544,33805,35413,21536,23318,24163,24290,24330,25987,32954,34109,38281,38491,20296,21253,21261,21263,21638,21754,22275,24067,24598,25243,25265,25429,64006,27873,28006,30129,30770,32990,33071,33502,33889,33970,34957,35090,36875,37610,39165,39825,24133,26292,26333,28689,29190,64007,20469,21117,24426,24915,26451,27161,28418,29922,31080,34920,35961,39111,39108,39491,21697,31263,26963,35575,35914,39080,39342,24444,25259,30130,30382,34987,36991,38466,21305,24380,24517,27852,29644,30050,30091,31558,33534,39325,20047,36924,19979,20309,21414,22799,24264,26160,27827,29781,33655,34662,36032,36944,38686,39957,22737,23416,34384,35604,40372,23506,24680,24717,26097,27735,28450,28579,28698,32597,32752,38289,38290,38480,38867,21106,36676,20989,21547,21688,21859,21898,27323,28085,32216,33382,37532,38519,40569,21512,21704,30418,34532,38308,38356,38492,20130,20233,23022,23270,24055,24658,25239,26477,26689,27782,28207,32568,32923,33322,64008,64009,38917,20133,20565,21683,22419,22874,23401,23475,25032,26999,28023,28707,34809,35299,35442,35559,36994,39405,39608,21182,26680,20502,24184,26447,33607,34892,20139,21521,22190,29670,37141,38911,39177,39255,39321,22099,22687,34395,35377,25010,27382,29563,36562,27463,38570,39511,22869,29184,36203,38761,20436,23796,24358,25080,26203,27883,28843,29572,29625,29694,30505,30541,32067,32098,32291,33335,34898,64010,36066,37449,39023,23377,31348,34880,38913,23244,20448,21332,22846,23805,25406,28025,29433,33029,33031,33698,37583,38960,20136,20804,21009,22411,24418,27842,28366,28677,28752,28847,29074,29673,29801,33610,34722,34913,36872,37026,37795,39336,20846,24407,24800,24935,26291,34137,36426,37295,38795,20046,20114,21628,22741,22778,22909,23733,24359,25142,25160,26122,26215,27627,28009,28111,28246,28408,28564,28640,28649,28765,29392,29733,29786,29920,30355,31068,31946,32286,32993,33446,33899,33983,34382,34399,34676,35703,35946,37804,38912,39013,24785,25110,37239,23130,26127,28151,28222,29759,39746,24573,24794,31503,21700,24344,27742,27859,27946,28888,32005,34425,35340,40251,21270,21644,23301,27194,28779,30069,31117,31166,33457,33775,35441,35649,36008,38772,64011,25844,25899,30906,30907,31339,20024,21914,22864,23462,24187,24739,25563,27489,26213,26707,28185,29029,29872,32008,36996,39529,39973,27963,28369,29502,35905,38346,20976,24140,24488,24653,24822,24880,24908,26179,26180,27045,27841,28255,28361,28514,29004,29852,30343,31681,31783,33618,34647,36945,38541,40643,21295,22238,24315,24458,24674,24724,25079,26214,26371,27292,28142,28590,28784,29546,32362,33214,33588,34516,35496,36036,21123,29554,23446,27243,37892,21742,22150,23389,25928,25989,26313,26783,28045,28102,29243,32948,37237,39501,20399,20505,21402,21518,21564,21897,21957,24127,24460,26429,29030,29661,36869,21211,21235,22628,22734,28932,29071,29179,34224,35347,26248,34216,21927,26244,29002,33841,21321,21913,27585,24409,24509,25582,26249,28999,35569,36637,40638,20241,25658,28875,30054,34407,24676,35662,40440,20807,20982,21256,27958,33016,40657,26133,27427,28824,30165,21507,23673,32007,35350,27424,27453,27462,21560,24688,27965,32725,33288,20694,20958,21916,22123,22221,23020,23305,24076,24985,24984,25137,26206,26342,29081,29113,29114,29351,31143,31232,32690,35440], + "gbk":[19970, 19972, 19973, 19974, 19983, 19986, 19991, 19999, 20000, 20001, 20003, 20006, 20009, 20014, 20015, 20017, 20019, 20021, 20023, 20028, 20032, 20033, 20034, 20036, 20038, 20042, 20049, 20053, 20055, 20058, 20059, 20066, 20067, 20068, 20069, 20071, 20072, 20074, 20075, 20076, 20077, 20078, 20079, 20082, 20084, 20085, 20086, 20087, 20088, 20089, 20090, 20091, 20092, 20093, 20095, 20096, 20097, 20098, 20099, 20100, 20101, 20103, 20106, 20112, 20118, 20119, 20121, 20124, 20125, 20126, 20131, 20138, 20143, 20144, 20145, 20148, 20150, 20151, 20152, 20153, 20156, 20157, 20158, 20168, 20172, 20175, 20176, 20178, 20186, 20187, 20188, 20192, 20194, 20198, 20199, 20201, 20205, 20206, 20207, 20209, 20212, 20216, 20217, 20218, 20220, 20222, 20224, 20226, 20227, 20228, 20229, 20230, 20231, 20232, 20235, 20236, 20242, 20243, 20244, 20245, 20246, 20252, 20253, 20257, 20259, 20264, 20265, 20268, 20269, 20270, 20273, 20275, 20277, 20279, 20281, 20283, 20286, 20287, 20288, 20289, 20290, 20292, 20293, 20295, 20296, 20297, 20298, 20299, 20300, 20306, 20308, 20310, 20321, 20322, 20326, 20328, 20330, 20331, 20333, 20334, 20337, 20338, 20341, 20343, 20344, 20345, 20346, 20349, 20352, 20353, 20354, 20357, 20358, 20359, 20362, 20364, 20366, 20368, 20370, 20371, 20373, 20374, 20376, 20377, 20378, 20380, 20382, 20383, 20385, 20386, 20388, 20395, 20397, 20400, 20401, 20402, 20403, 20404, 20406, 20407, 20408, 20409, 20410, 20411, 20412, 20413, 20414, 20416, 20417, 20418, 20422, 20423, 20424, 20425, 20427, 20428, 20429, 20434, 20435, 20436, 20437, 20438, 20441, 20443, 20448, 20450, 20452, 20453, 20455, 20459, 20460, 20464, 20466, 20468, 20469, 20470, 20471, 20473, 20475, 20476, 20477, 20479, 20480, 20481, 20482, 20483, 20484, 20485, 20486, 20487, 20488, 20489, 20490, 20491, 20494, 20496, 20497, 20499, 20501, 20502, 20503, 20507, 20509, 20510, 20512, 20514, 20515, 20516, 20519, 20523, 20527, 20528, 20529, 20530, 20531, 20532, 20533, 20534, 20535, 20536, 20537, 20539, 20541, 20543, 20544, 20545, 20546, 20548, 20549, 20550, 20553, 20554, 20555, 20557, 20560, 20561, 20562, 20563, 20564, 20566, 20567, 20568, 20569, 20571, 20573, 20574, 20575, 20576, 20577, 20578, 20579, 20580, 20582, 20583, 20584, 20585, 20586, 20587, 20589, 20590, 20591, 20592, 20593, 20594, 20595, 20596, 20597, 20600, 20601, 20602, 20604, 20605, 20609, 20610, 20611, 20612, 20614, 20615, 20617, 20618, 20619, 20620, 20622, 20623, 20624, 20625, 20626, 20627, 20628, 20629, 20630, 20631, 20632, 20633, 20634, 20635, 20636, 20637, 20638, 20639, 20640, 20641, 20642, 20644, 20646, 20650, 20651, 20653, 20654, 20655, 20656, 20657, 20659, 20660, 20661, 20662, 20663, 20664, 20665, 20668, 20669, 20670, 20671, 20672, 20673, 20674, 20675, 20676, 20677, 20678, 20679, 20680, 20681, 20682, 20683, 20684, 20685, 20686, 20688, 20689, 20690, 20691, 20692, 20693, 20695, 20696, 20697, 20699, 20700, 20701, 20702, 20703, 20704, 20705, 20706, 20707, 20708, 20709, 20712, 20713, 20714, 20715, 20719, 20720, 20721, 20722, 20724, 20726, 20727, 20728, 20729, 20730, 20732, 20733, 20734, 20735, 20736, 20737, 20738, 20739, 20740, 20741, 20744, 20745, 20746, 20748, 20749, 20750, 20751, 20752, 20753, 20755, 20756, 20757, 20758, 20759, 20760, 20761, 20762, 20763, 20764, 20765, 20766, 20767, 20768, 20770, 20771, 20772, 20773, 20774, 20775, 20776, 20777, 20778, 20779, 20780, 20781, 20782, 20783, 20784, 20785, 20786, 20787, 20788, 20789, 20790, 20791, 20792, 20793, 20794, 20795, 20796, 20797, 20798, 20802, 20807, 20810, 20812, 20814, 20815, 20816, 20818, 20819, 20823, 20824, 20825, 20827, 20829, 20830, 20831, 20832, 20833, 20835, 20836, 20838, 20839, 20841, 20842, 20847, 20850, 20858, 20862, 20863, 20867, 20868, 20870, 20871, 20874, 20875, 20878, 20879, 20880, 20881, 20883, 20884, 20888, 20890, 20893, 20894, 20895, 20897, 20899, 20902, 20903, 20904, 20905, 20906, 20909, 20910, 20916, 20920, 20921, 20922, 20926, 20927, 20929, 20930, 20931, 20933, 20936, 20938, 20941, 20942, 20944, 20946, 20947, 20948, 20949, 20950, 20951, 20952, 20953, 20954, 20956, 20958, 20959, 20962, 20963, 20965, 20966, 20967, 20968, 20969, 20970, 20972, 20974, 20977, 20978, 20980, 20983, 20990, 20996, 20997, 21001, 21003, 21004, 21007, 21008, 21011, 21012, 21013, 21020, 21022, 21023, 21025, 21026, 21027, 21029, 21030, 21031, 21034, 21036, 21039, 21041, 21042, 21044, 21045, 21052, 21054, 21060, 21061, 21062, 21063, 21064, 21065, 21067, 21070, 21071, 21074, 21075, 21077, 21079, 21080, 21081, 21082, 21083, 21085, 21087, 21088, 21090, 21091, 21092, 21094, 21096, 21099, 21100, 21101, 21102, 21104, 21105, 21107, 21108, 21109, 21110, 21111, 21112, 21113, 21114, 21115, 21116, 21118, 21120, 21123, 21124, 21125, 21126, 21127, 21129, 21130, 21131, 21132, 21133, 21134, 21135, 21137, 21138, 21140, 21141, 21142, 21143, 21144, 21145, 21146, 21148, 21156, 21157, 21158, 21159, 21166, 21167, 21168, 21172, 21173, 21174, 21175, 21176, 21177, 21178, 21179, 21180, 21181, 21184, 21185, 21186, 21188, 21189, 21190, 21192, 21194, 21196, 21197, 21198, 21199, 21201, 21203, 21204, 21205, 21207, 21209, 21210, 21211, 21212, 21213, 21214, 21216, 21217, 21218, 21219, 21221, 21222, 21223, 21224, 21225, 21226, 21227, 21228, 21229, 21230, 21231, 21233, 21234, 21235, 21236, 21237, 21238, 21239, 21240, 21243, 21244, 21245, 21249, 21250, 21251, 21252, 21255, 21257, 21258, 21259, 21260, 21262, 21265, 21266, 21267, 21268, 21272, 21275, 21276, 21278, 21279, 21282, 21284, 21285, 21287, 21288, 21289, 21291, 21292, 21293, 21295, 21296, 21297, 21298, 21299, 21300, 21301, 21302, 21303, 21304, 21308, 21309, 21312, 21314, 21316, 21318, 21323, 21324, 21325, 21328, 21332, 21336, 21337, 21339, 21341, 21349, 21352, 21354, 21356, 21357, 21362, 21366, 21369, 21371, 21372, 21373, 21374, 21376, 21377, 21379, 21383, 21384, 21386, 21390, 21391, 21392, 21393, 21394, 21395, 21396, 21398, 21399, 21401, 21403, 21404, 21406, 21408, 21409, 21412, 21415, 21418, 21419, 21420, 21421, 21423, 21424, 21425, 21426, 21427, 21428, 21429, 21431, 21432, 21433, 21434, 21436, 21437, 21438, 21440, 21443, 21444, 21445, 21446, 21447, 21454, 21455, 21456, 21458, 21459, 21461, 21466, 21468, 21469, 21470, 21473, 21474, 21479, 21492, 21498, 21502, 21503, 21504, 21506, 21509, 21511, 21515, 21524, 21528, 21529, 21530, 21532, 21538, 21540, 21541, 21546, 21552, 21555, 21558, 21559, 21562, 21565, 21567, 21569, 21570, 21572, 21573, 21575, 21577, 21580, 21581, 21582, 21583, 21585, 21594, 21597, 21598, 21599, 21600, 21601, 21603, 21605, 21607, 21609, 21610, 21611, 21612, 21613, 21614, 21615, 21616, 21620, 21625, 21626, 21630, 21631, 21633, 21635, 21637, 21639, 21640, 21641, 21642, 21645, 21649, 21651, 21655, 21656, 21660, 21662, 21663, 21664, 21665, 21666, 21669, 21678, 21680, 21682, 21685, 21686, 21687, 21689, 21690, 21692, 21694, 21699, 21701, 21706, 21707, 21718, 21720, 21723, 21728, 21729, 21730, 21731, 21732, 21739, 21740, 21743, 21744, 21745, 21748, 21749, 21750, 21751, 21752, 21753, 21755, 21758, 21760, 21762, 21763, 21764, 21765, 21768, 21770, 21771, 21772, 21773, 21774, 21778, 21779, 21781, 21782, 21783, 21784, 21785, 21786, 21788, 21789, 21790, 21791, 21793, 21797, 21798, 21800, 21801, 21803, 21805, 21810, 21812, 21813, 21814, 21816, 21817, 21818, 21819, 21821, 21824, 21826, 21829, 21831, 21832, 21835, 21836, 21837, 21838, 21839, 21841, 21842, 21843, 21844, 21847, 21848, 21849, 21850, 21851, 21853, 21854, 21855, 21856, 21858, 21859, 21864, 21865, 21867, 21871, 21872, 21873, 21874, 21875, 21876, 21881, 21882, 21885, 21887, 21893, 21894, 21900, 21901, 21902, 21904, 21906, 21907, 21909, 21910, 21911, 21914, 21915, 21918, 21920, 21921, 21922, 21923, 21924, 21925, 21926, 21928, 21929, 21930, 21931, 21932, 21933, 21934, 21935, 21936, 21938, 21940, 21942, 21944, 21946, 21948, 21951, 21952, 21953, 21954, 21955, 21958, 21959, 21960, 21962, 21963, 21966, 21967, 21968, 21973, 21975, 21976, 21977, 21978, 21979, 21982, 21984, 21986, 21991, 21993, 21997, 21998, 22000, 22001, 22004, 22006, 22008, 22009, 22010, 22011, 22012, 22015, 22018, 22019, 22020, 22021, 22022, 22023, 22026, 22027, 22029, 22032, 22033, 22034, 22035, 22036, 22037, 22038, 22039, 22041, 22042, 22044, 22045, 22048, 22049, 22050, 22053, 22054, 22056, 22057, 22058, 22059, 22062, 22063, 22064, 22067, 22069, 22071, 22072, 22074, 22076, 22077, 22078, 22080, 22081, 22082, 22083, 22084, 22085, 22086, 22087, 22088, 22089, 22090, 22091, 22095, 22096, 22097, 22098, 22099, 22101, 22102, 22106, 22107, 22109, 22110, 22111, 22112, 22113, 22115, 22117, 22118, 22119, 22125, 22126, 22127, 22128, 22130, 22131, 22132, 22133, 22135, 22136, 22137, 22138, 22141, 22142, 22143, 22144, 22145, 22146, 22147, 22148, 22151, 22152, 22153, 22154, 22155, 22156, 22157, 22160, 22161, 22162, 22164, 22165, 22166, 22167, 22168, 22169, 22170, 22171, 22172, 22173, 22174, 22175, 22176, 22177, 22178, 22180, 22181, 22182, 22183, 22184, 22185, 22186, 22187, 22188, 22189, 22190, 22192, 22193, 22194, 22195, 22196, 22197, 22198, 22200, 22201, 22202, 22203, 22205, 22206, 22207, 22208, 22209, 22210, 22211, 22212, 22213, 22214, 22215, 22216, 22217, 22219, 22220, 22221, 22222, 22223, 22224, 22225, 22226, 22227, 22229, 22230, 22232, 22233, 22236, 22243, 22245, 22246, 22247, 22248, 22249, 22250, 22252, 22254, 22255, 22258, 22259, 22262, 22263, 22264, 22267, 22268, 22272, 22273, 22274, 22277, 22279, 22283, 22284, 22285, 22286, 22287, 22288, 22289, 22290, 22291, 22292, 22293, 22294, 22295, 22296, 22297, 22298, 22299, 22301, 22302, 22304, 22305, 22306, 22308, 22309, 22310, 22311, 22315, 22321, 22322, 22324, 22325, 22326, 22327, 22328, 22332, 22333, 22335, 22337, 22339, 22340, 22341, 22342, 22344, 22345, 22347, 22354, 22355, 22356, 22357, 22358, 22360, 22361, 22370, 22371, 22373, 22375, 22380, 22382, 22384, 22385, 22386, 22388, 22389, 22392, 22393, 22394, 22397, 22398, 22399, 22400, 22401, 22407, 22408, 22409, 22410, 22413, 22414, 22415, 22416, 22417, 22420, 22421, 22422, 22423, 22424, 22425, 22426, 22428, 22429, 22430, 22431, 22437, 22440, 22442, 22444, 22447, 22448, 22449, 22451, 22453, 22454, 22455, 22457, 22458, 22459, 22460, 22461, 22462, 22463, 22464, 22465, 22468, 22469, 22470, 22471, 22472, 22473, 22474, 22476, 22477, 22480, 22481, 22483, 22486, 22487, 22491, 22492, 22494, 22497, 22498, 22499, 22501, 22502, 22503, 22504, 22505, 22506, 22507, 22508, 22510, 22512, 22513, 22514, 22515, 22517, 22518, 22519, 22523, 22524, 22526, 22527, 22529, 22531, 22532, 22533, 22536, 22537, 22538, 22540, 22542, 22543, 22544, 22546, 22547, 22548, 22550, 22551, 22552, 22554, 22555, 22556, 22557, 22559, 22562, 22563, 22565, 22566, 22567, 22568, 22569, 22571, 22572, 22573, 22574, 22575, 22577, 22578, 22579, 22580, 22582, 22583, 22584, 22585, 22586, 22587, 22588, 22589, 22590, 22591, 22592, 22593, 22594, 22595, 22597, 22598, 22599, 22600, 22601, 22602, 22603, 22606, 22607, 22608, 22610, 22611, 22613, 22614, 22615, 22617, 22618, 22619, 22620, 22621, 22623, 22624, 22625, 22626, 22627, 22628, 22630, 22631, 22632, 22633, 22634, 22637, 22638, 22639, 22640, 22641, 22642, 22643, 22644, 22645, 22646, 22647, 22648, 22649, 22650, 22651, 22652, 22653, 22655, 22658, 22660, 22662, 22663, 22664, 22666, 22667, 22668, 22669, 22670, 22671, 22672, 22673, 22676, 22677, 22678, 22679, 22680, 22683, 22684, 22685, 22688, 22689, 22690, 22691, 22692, 22693, 22694, 22695, 22698, 22699, 22700, 22701, 22702, 22703, 22704, 22705, 22706, 22707, 22708, 22709, 22710, 22711, 22712, 22713, 22714, 22715, 22717, 22718, 22719, 22720, 22722, 22723, 22724, 22726, 22727, 22728, 22729, 22730, 22731, 22732, 22733, 22734, 22735, 22736, 22738, 22739, 22740, 22742, 22743, 22744, 22745, 22746, 22747, 22748, 22749, 22750, 22751, 22752, 22753, 22754, 22755, 22757, 22758, 22759, 22760, 22761, 22762, 22765, 22767, 22769, 22770, 22772, 22773, 22775, 22776, 22778, 22779, 22780, 22781, 22782, 22783, 22784, 22785, 22787, 22789, 22790, 22792, 22793, 22794, 22795, 22796, 22798, 22800, 22801, 22802, 22803, 22807, 22808, 22811, 22813, 22814, 22816, 22817, 22818, 22819, 22822, 22824, 22828, 22832, 22834, 22835, 22837, 22838, 22843, 22845, 22846, 22847, 22848, 22851, 22853, 22854, 22858, 22860, 22861, 22864, 22866, 22867, 22873, 22875, 22876, 22877, 22878, 22879, 22881, 22883, 22884, 22886, 22887, 22888, 22889, 22890, 22891, 22892, 22893, 22894, 22895, 22896, 22897, 22898, 22901, 22903, 22906, 22907, 22908, 22910, 22911, 22912, 22917, 22921, 22923, 22924, 22926, 22927, 22928, 22929, 22932, 22933, 22936, 22938, 22939, 22940, 22941, 22943, 22944, 22945, 22946, 22950, 22951, 22956, 22957, 22960, 22961, 22963, 22964, 22965, 22966, 22967, 22968, 22970, 22972, 22973, 22975, 22976, 22977, 22978, 22979, 22980, 22981, 22983, 22984, 22985, 22988, 22989, 22990, 22991, 22997, 22998, 23001, 23003, 23006, 23007, 23008, 23009, 23010, 23012, 23014, 23015, 23017, 23018, 23019, 23021, 23022, 23023, 23024, 23025, 23026, 23027, 23028, 23029, 23030, 23031, 23032, 23034, 23036, 23037, 23038, 23040, 23042, 23050, 23051, 23053, 23054, 23055, 23056, 23058, 23060, 23061, 23062, 23063, 23065, 23066, 23067, 23069, 23070, 23073, 23074, 23076, 23078, 23079, 23080, 23082, 23083, 23084, 23085, 23086, 23087, 23088, 23091, 23093, 23095, 23096, 23097, 23098, 23099, 23101, 23102, 23103, 23105, 23106, 23107, 23108, 23109, 23111, 23112, 23115, 23116, 23117, 23118, 23119, 23120, 23121, 23122, 23123, 23124, 23126, 23127, 23128, 23129, 23131, 23132, 23133, 23134, 23135, 23136, 23137, 23139, 23140, 23141, 23142, 23144, 23145, 23147, 23148, 23149, 23150, 23151, 23152, 23153, 23154, 23155, 23160, 23161, 23163, 23164, 23165, 23166, 23168, 23169, 23170, 23171, 23172, 23173, 23174, 23175, 23176, 23177, 23178, 23179, 23180, 23181, 23182, 23183, 23184, 23185, 23187, 23188, 23189, 23190, 23191, 23192, 23193, 23196, 23197, 23198, 23199, 23200, 23201, 23202, 23203, 23204, 23205, 23206, 23207, 23208, 23209, 23211, 23212, 23213, 23214, 23215, 23216, 23217, 23220, 23222, 23223, 23225, 23226, 23227, 23228, 23229, 23231, 23232, 23235, 23236, 23237, 23238, 23239, 23240, 23242, 23243, 23245, 23246, 23247, 23248, 23249, 23251, 23253, 23255, 23257, 23258, 23259, 23261, 23262, 23263, 23266, 23268, 23269, 23271, 23272, 23274, 23276, 23277, 23278, 23279, 23280, 23282, 23283, 23284, 23285, 23286, 23287, 23288, 23289, 23290, 23291, 23292, 23293, 23294, 23295, 23296, 23297, 23298, 23299, 23300, 23301, 23302, 23303, 23304, 23306, 23307, 23308, 23309, 23310, 23311, 23312, 23313, 23314, 23315, 23316, 23317, 23320, 23321, 23322, 23323, 23324, 23325, 23326, 23327, 23328, 23329, 23330, 23331, 23332, 23333, 23334, 23335, 23336, 23337, 23338, 23339, 23340, 23341, 23342, 23343, 23344, 23345, 23347, 23349, 23350, 23352, 23353, 23354, 23355, 23356, 23357, 23358, 23359, 23361, 23362, 23363, 23364, 23365, 23366, 23367, 23368, 23369, 23370, 23371, 23372, 23373, 23374, 23375, 23378, 23382, 23390, 23392, 23393, 23399, 23400, 23403, 23405, 23406, 23407, 23410, 23412, 23414, 23415, 23416, 23417, 23419, 23420, 23422, 23423, 23426, 23430, 23434, 23437, 23438, 23440, 23441, 23442, 23444, 23446, 23455, 23463, 23464, 23465, 23468, 23469, 23470, 23471, 23473, 23474, 23479, 23482, 23483, 23484, 23488, 23489, 23491, 23496, 23497, 23498, 23499, 23501, 23502, 23503, 23505, 23508, 23509, 23510, 23511, 23512, 23513, 23514, 23515, 23516, 23520, 23522, 23523, 23526, 23527, 23529, 23530, 23531, 23532, 23533, 23535, 23537, 23538, 23539, 23540, 23541, 23542, 23543, 23549, 23550, 23552, 23554, 23555, 23557, 23559, 23560, 23563, 23564, 23565, 23566, 23568, 23570, 23571, 23575, 23577, 23579, 23582, 23583, 23584, 23585, 23587, 23590, 23592, 23593, 23594, 23595, 23597, 23598, 23599, 23600, 23602, 23603, 23605, 23606, 23607, 23619, 23620, 23622, 23623, 23628, 23629, 23634, 23635, 23636, 23638, 23639, 23640, 23642, 23643, 23644, 23645, 23647, 23650, 23652, 23655, 23656, 23657, 23658, 23659, 23660, 23661, 23664, 23666, 23667, 23668, 23669, 23670, 23671, 23672, 23675, 23676, 23677, 23678, 23680, 23683, 23684, 23685, 23686, 23687, 23689, 23690, 23691, 23694, 23695, 23698, 23699, 23701, 23709, 23710, 23711, 23712, 23713, 23716, 23717, 23718, 23719, 23720, 23722, 23726, 23727, 23728, 23730, 23732, 23734, 23737, 23738, 23739, 23740, 23742, 23744, 23746, 23747, 23749, 23750, 23751, 23752, 23753, 23754, 23756, 23757, 23758, 23759, 23760, 23761, 23763, 23764, 23765, 23766, 23767, 23768, 23770, 23771, 23772, 23773, 23774, 23775, 23776, 23778, 23779, 23783, 23785, 23787, 23788, 23790, 23791, 23793, 23794, 23795, 23796, 23797, 23798, 23799, 23800, 23801, 23802, 23804, 23805, 23806, 23807, 23808, 23809, 23812, 23813, 23816, 23817, 23818, 23819, 23820, 23821, 23823, 23824, 23825, 23826, 23827, 23829, 23831, 23832, 23833, 23834, 23836, 23837, 23839, 23840, 23841, 23842, 23843, 23845, 23848, 23850, 23851, 23852, 23855, 23856, 23857, 23858, 23859, 23861, 23862, 23863, 23864, 23865, 23866, 23867, 23868, 23871, 23872, 23873, 23874, 23875, 23876, 23877, 23878, 23880, 23881, 23885, 23886, 23887, 23888, 23889, 23890, 23891, 23892, 23893, 23894, 23895, 23897, 23898, 23900, 23902, 23903, 23904, 23905, 23906, 23907, 23908, 23909, 23910, 23911, 23912, 23914, 23917, 23918, 23920, 23921, 23922, 23923, 23925, 23926, 23927, 23928, 23929, 23930, 23931, 23932, 23933, 23934, 23935, 23936, 23937, 23939, 23940, 23941, 23942, 23943, 23944, 23945, 23946, 23947, 23948, 23949, 23950, 23951, 23952, 23953, 23954, 23955, 23956, 23957, 23958, 23959, 23960, 23962, 23963, 23964, 23966, 23967, 23968, 23969, 23970, 23971, 23972, 23973, 23974, 23975, 23976, 23977, 23978, 23979, 23980, 23981, 23982, 23983, 23984, 23985, 23986, 23987, 23988, 23989, 23990, 23992, 23993, 23994, 23995, 23996, 23997, 23998, 23999, 24000, 24001, 24002, 24003, 24004, 24006, 24007, 24008, 24009, 24010, 24011, 24012, 24014, 24015, 24016, 24017, 24018, 24019, 24020, 24021, 24022, 24023, 24024, 24025, 24026, 24028, 24031, 24032, 24035, 24036, 24042, 24044, 24045, 24048, 24053, 24054, 24056, 24057, 24058, 24059, 24060, 24063, 24064, 24068, 24071, 24073, 24074, 24075, 24077, 24078, 24082, 24083, 24087, 24094, 24095, 24096, 24097, 24098, 24099, 24100, 24101, 24104, 24105, 24106, 24107, 24108, 24111, 24112, 24114, 24115, 24116, 24117, 24118, 24121, 24122, 24126, 24127, 24128, 24129, 24131, 24134, 24135, 24136, 24137, 24138, 24139, 24141, 24142, 24143, 24144, 24145, 24146, 24147, 24150, 24151, 24152, 24153, 24154, 24156, 24157, 24159, 24160, 24163, 24164, 24165, 24166, 24167, 24168, 24169, 24170, 24171, 24172, 24173, 24174, 24175, 24176, 24177, 24181, 24183, 24185, 24190, 24193, 24194, 24195, 24197, 24200, 24201, 24204, 24205, 24206, 24210, 24216, 24219, 24221, 24225, 24226, 24227, 24228, 24232, 24233, 24234, 24235, 24236, 24238, 24239, 24240, 24241, 24242, 24244, 24250, 24251, 24252, 24253, 24255, 24256, 24257, 24258, 24259, 24260, 24261, 24262, 24263, 24264, 24267, 24268, 24269, 24270, 24271, 24272, 24276, 24277, 24279, 24280, 24281, 24282, 24284, 24285, 24286, 24287, 24288, 24289, 24290, 24291, 24292, 24293, 24294, 24295, 24297, 24299, 24300, 24301, 24302, 24303, 24304, 24305, 24306, 24307, 24309, 24312, 24313, 24315, 24316, 24317, 24325, 24326, 24327, 24329, 24332, 24333, 24334, 24336, 24338, 24340, 24342, 24345, 24346, 24348, 24349, 24350, 24353, 24354, 24355, 24356, 24360, 24363, 24364, 24366, 24368, 24370, 24371, 24372, 24373, 24374, 24375, 24376, 24379, 24381, 24382, 24383, 24385, 24386, 24387, 24388, 24389, 24390, 24391, 24392, 24393, 24394, 24395, 24396, 24397, 24398, 24399, 24401, 24404, 24409, 24410, 24411, 24412, 24414, 24415, 24416, 24419, 24421, 24423, 24424, 24427, 24430, 24431, 24434, 24436, 24437, 24438, 24440, 24442, 24445, 24446, 24447, 24451, 24454, 24461, 24462, 24463, 24465, 24467, 24468, 24470, 24474, 24475, 24477, 24478, 24479, 24480, 24482, 24483, 24484, 24485, 24486, 24487, 24489, 24491, 24492, 24495, 24496, 24497, 24498, 24499, 24500, 24502, 24504, 24505, 24506, 24507, 24510, 24511, 24512, 24513, 24514, 24519, 24520, 24522, 24523, 24526, 24531, 24532, 24533, 24538, 24539, 24540, 24542, 24543, 24546, 24547, 24549, 24550, 24552, 24553, 24556, 24559, 24560, 24562, 24563, 24564, 24566, 24567, 24569, 24570, 24572, 24583, 24584, 24585, 24587, 24588, 24592, 24593, 24595, 24599, 24600, 24602, 24606, 24607, 24610, 24611, 24612, 24620, 24621, 24622, 24624, 24625, 24626, 24627, 24628, 24630, 24631, 24632, 24633, 24634, 24637, 24638, 24640, 24644, 24645, 24646, 24647, 24648, 24649, 24650, 24652, 24654, 24655, 24657, 24659, 24660, 24662, 24663, 24664, 24667, 24668, 24670, 24671, 24672, 24673, 24677, 24678, 24686, 24689, 24690, 24692, 24693, 24695, 24702, 24704, 24705, 24706, 24709, 24710, 24711, 24712, 24714, 24715, 24718, 24719, 24720, 24721, 24723, 24725, 24727, 24728, 24729, 24732, 24734, 24737, 24738, 24740, 24741, 24743, 24745, 24746, 24750, 24752, 24755, 24757, 24758, 24759, 24761, 24762, 24765, 24766, 24767, 24768, 24769, 24770, 24771, 24772, 24775, 24776, 24777, 24780, 24781, 24782, 24783, 24784, 24786, 24787, 24788, 24790, 24791, 24793, 24795, 24798, 24801, 24802, 24803, 24804, 24805, 24810, 24817, 24818, 24821, 24823, 24824, 24827, 24828, 24829, 24830, 24831, 24834, 24835, 24836, 24837, 24839, 24842, 24843, 24844, 24848, 24849, 24850, 24851, 24852, 24854, 24855, 24856, 24857, 24859, 24860, 24861, 24862, 24865, 24866, 24869, 24872, 24873, 24874, 24876, 24877, 24878, 24879, 24880, 24881, 24882, 24883, 24884, 24885, 24886, 24887, 24888, 24889, 24890, 24891, 24892, 24893, 24894, 24896, 24897, 24898, 24899, 24900, 24901, 24902, 24903, 24905, 24907, 24909, 24911, 24912, 24914, 24915, 24916, 24918, 24919, 24920, 24921, 24922, 24923, 24924, 24926, 24927, 24928, 24929, 24931, 24932, 24933, 24934, 24937, 24938, 24939, 24940, 24941, 24942, 24943, 24945, 24946, 24947, 24948, 24950, 24952, 24953, 24954, 24955, 24956, 24957, 24958, 24959, 24960, 24961, 24962, 24963, 24964, 24965, 24966, 24967, 24968, 24969, 24970, 24972, 24973, 24975, 24976, 24977, 24978, 24979, 24981, 24982, 24983, 24984, 24985, 24986, 24987, 24988, 24990, 24991, 24992, 24993, 24994, 24995, 24996, 24997, 24998, 25002, 25003, 25005, 25006, 25007, 25008, 25009, 25010, 25011, 25012, 25013, 25014, 25016, 25017, 25018, 25019, 25020, 25021, 25023, 25024, 25025, 25027, 25028, 25029, 25030, 25031, 25033, 25036, 25037, 25038, 25039, 25040, 25043, 25045, 25046, 25047, 25048, 25049, 25050, 25051, 25052, 25053, 25054, 25055, 25056, 25057, 25058, 25059, 25060, 25061, 25063, 25064, 25065, 25066, 25067, 25068, 25069, 25070, 25071, 25072, 25073, 25074, 25075, 25076, 25078, 25079, 25080, 25081, 25082, 25083, 25084, 25085, 25086, 25088, 25089, 25090, 25091, 25092, 25093, 25095, 25097, 25107, 25108, 25113, 25116, 25117, 25118, 25120, 25123, 25126, 25127, 25128, 25129, 25131, 25133, 25135, 25136, 25137, 25138, 25141, 25142, 25144, 25145, 25146, 25147, 25148, 25154, 25156, 25157, 25158, 25162, 25167, 25168, 25173, 25174, 25175, 25177, 25178, 25180, 25181, 25182, 25183, 25184, 25185, 25186, 25188, 25189, 25192, 25201, 25202, 25204, 25205, 25207, 25208, 25210, 25211, 25213, 25217, 25218, 25219, 25221, 25222, 25223, 25224, 25227, 25228, 25229, 25230, 25231, 25232, 25236, 25241, 25244, 25245, 25246, 25251, 25254, 25255, 25257, 25258, 25261, 25262, 25263, 25264, 25266, 25267, 25268, 25270, 25271, 25272, 25274, 25278, 25280, 25281, 25283, 25291, 25295, 25297, 25301, 25309, 25310, 25312, 25313, 25316, 25322, 25323, 25328, 25330, 25333, 25336, 25337, 25338, 25339, 25344, 25347, 25348, 25349, 25350, 25354, 25355, 25356, 25357, 25359, 25360, 25362, 25363, 25364, 25365, 25367, 25368, 25369, 25372, 25382, 25383, 25385, 25388, 25389, 25390, 25392, 25393, 25395, 25396, 25397, 25398, 25399, 25400, 25403, 25404, 25406, 25407, 25408, 25409, 25412, 25415, 25416, 25418, 25425, 25426, 25427, 25428, 25430, 25431, 25432, 25433, 25434, 25435, 25436, 25437, 25440, 25444, 25445, 25446, 25448, 25450, 25451, 25452, 25455, 25456, 25458, 25459, 25460, 25461, 25464, 25465, 25468, 25469, 25470, 25471, 25473, 25475, 25476, 25477, 25478, 25483, 25485, 25489, 25491, 25492, 25493, 25495, 25497, 25498, 25499, 25500, 25501, 25502, 25503, 25505, 25508, 25510, 25515, 25519, 25521, 25522, 25525, 25526, 25529, 25531, 25533, 25535, 25536, 25537, 25538, 25539, 25541, 25543, 25544, 25546, 25547, 25548, 25553, 25555, 25556, 25557, 25559, 25560, 25561, 25562, 25563, 25564, 25565, 25567, 25570, 25572, 25573, 25574, 25575, 25576, 25579, 25580, 25582, 25583, 25584, 25585, 25587, 25589, 25591, 25593, 25594, 25595, 25596, 25598, 25603, 25604, 25606, 25607, 25608, 25609, 25610, 25613, 25614, 25617, 25618, 25621, 25622, 25623, 25624, 25625, 25626, 25629, 25631, 25634, 25635, 25636, 25637, 25639, 25640, 25641, 25643, 25646, 25647, 25648, 25649, 25650, 25651, 25653, 25654, 25655, 25656, 25657, 25659, 25660, 25662, 25664, 25666, 25667, 25673, 25675, 25676, 25677, 25678, 25679, 25680, 25681, 25683, 25685, 25686, 25687, 25689, 25690, 25691, 25692, 25693, 25695, 25696, 25697, 25698, 25699, 25700, 25701, 25702, 25704, 25706, 25707, 25708, 25710, 25711, 25712, 25713, 25714, 25715, 25716, 25717, 25718, 25719, 25723, 25724, 25725, 25726, 25727, 25728, 25729, 25731, 25734, 25736, 25737, 25738, 25739, 25740, 25741, 25742, 25743, 25744, 25747, 25748, 25751, 25752, 25754, 25755, 25756, 25757, 25759, 25760, 25761, 25762, 25763, 25765, 25766, 25767, 25768, 25770, 25771, 25775, 25777, 25778, 25779, 25780, 25782, 25785, 25787, 25789, 25790, 25791, 25793, 25795, 25796, 25798, 25799, 25800, 25801, 25802, 25803, 25804, 25807, 25809, 25811, 25812, 25813, 25814, 25817, 25818, 25819, 25820, 25821, 25823, 25824, 25825, 25827, 25829, 25831, 25832, 25833, 25834, 25835, 25836, 25837, 25838, 25839, 25840, 25841, 25842, 25843, 25844, 25845, 25846, 25847, 25848, 25849, 25850, 25851, 25852, 25853, 25854, 25855, 25857, 25858, 25859, 25860, 25861, 25862, 25863, 25864, 25866, 25867, 25868, 25869, 25870, 25871, 25872, 25873, 25875, 25876, 25877, 25878, 25879, 25881, 25882, 25883, 25884, 25885, 25886, 25887, 25888, 25889, 25890, 25891, 25892, 25894, 25895, 25896, 25897, 25898, 25900, 25901, 25904, 25905, 25906, 25907, 25911, 25914, 25916, 25917, 25920, 25921, 25922, 25923, 25924, 25926, 25927, 25930, 25931, 25933, 25934, 25936, 25938, 25939, 25940, 25943, 25944, 25946, 25948, 25951, 25952, 25953, 25956, 25957, 25959, 25960, 25961, 25962, 25965, 25966, 25967, 25969, 25971, 25973, 25974, 25976, 25977, 25978, 25979, 25980, 25981, 25982, 25983, 25984, 25985, 25986, 25987, 25988, 25989, 25990, 25992, 25993, 25994, 25997, 25998, 25999, 26002, 26004, 26005, 26006, 26008, 26010, 26013, 26014, 26016, 26018, 26019, 26022, 26024, 26026, 26028, 26030, 26033, 26034, 26035, 26036, 26037, 26038, 26039, 26040, 26042, 26043, 26046, 26047, 26048, 26050, 26055, 26056, 26057, 26058, 26061, 26064, 26065, 26067, 26068, 26069, 26072, 26073, 26074, 26075, 26076, 26077, 26078, 26079, 26081, 26083, 26084, 26090, 26091, 26098, 26099, 26100, 26101, 26104, 26105, 26107, 26108, 26109, 26110, 26111, 26113, 26116, 26117, 26119, 26120, 26121, 26123, 26125, 26128, 26129, 26130, 26134, 26135, 26136, 26138, 26139, 26140, 26142, 26145, 26146, 26147, 26148, 26150, 26153, 26154, 26155, 26156, 26158, 26160, 26162, 26163, 26167, 26168, 26169, 26170, 26171, 26173, 26175, 26176, 26178, 26180, 26181, 26182, 26183, 26184, 26185, 26186, 26189, 26190, 26192, 26193, 26200, 26201, 26203, 26204, 26205, 26206, 26208, 26210, 26211, 26213, 26215, 26217, 26218, 26219, 26220, 26221, 26225, 26226, 26227, 26229, 26232, 26233, 26235, 26236, 26237, 26239, 26240, 26241, 26243, 26245, 26246, 26248, 26249, 26250, 26251, 26253, 26254, 26255, 26256, 26258, 26259, 26260, 26261, 26264, 26265, 26266, 26267, 26268, 26270, 26271, 26272, 26273, 26274, 26275, 26276, 26277, 26278, 26281, 26282, 26283, 26284, 26285, 26287, 26288, 26289, 26290, 26291, 26293, 26294, 26295, 26296, 26298, 26299, 26300, 26301, 26303, 26304, 26305, 26306, 26307, 26308, 26309, 26310, 26311, 26312, 26313, 26314, 26315, 26316, 26317, 26318, 26319, 26320, 26321, 26322, 26323, 26324, 26325, 26326, 26327, 26328, 26330, 26334, 26335, 26336, 26337, 26338, 26339, 26340, 26341, 26343, 26344, 26346, 26347, 26348, 26349, 26350, 26351, 26353, 26357, 26358, 26360, 26362, 26363, 26365, 26369, 26370, 26371, 26372, 26373, 26374, 26375, 26380, 26382, 26383, 26385, 26386, 26387, 26390, 26392, 26393, 26394, 26396, 26398, 26400, 26401, 26402, 26403, 26404, 26405, 26407, 26409, 26414, 26416, 26418, 26419, 26422, 26423, 26424, 26425, 26427, 26428, 26430, 26431, 26433, 26436, 26437, 26439, 26442, 26443, 26445, 26450, 26452, 26453, 26455, 26456, 26457, 26458, 26459, 26461, 26466, 26467, 26468, 26470, 26471, 26475, 26476, 26478, 26481, 26484, 26486, 26488, 26489, 26490, 26491, 26493, 26496, 26498, 26499, 26501, 26502, 26504, 26506, 26508, 26509, 26510, 26511, 26513, 26514, 26515, 26516, 26518, 26521, 26523, 26527, 26528, 26529, 26532, 26534, 26537, 26540, 26542, 26545, 26546, 26548, 26553, 26554, 26555, 26556, 26557, 26558, 26559, 26560, 26562, 26565, 26566, 26567, 26568, 26569, 26570, 26571, 26572, 26573, 26574, 26581, 26582, 26583, 26587, 26591, 26593, 26595, 26596, 26598, 26599, 26600, 26602, 26603, 26605, 26606, 26610, 26613, 26614, 26615, 26616, 26617, 26618, 26619, 26620, 26622, 26625, 26626, 26627, 26628, 26630, 26637, 26640, 26642, 26644, 26645, 26648, 26649, 26650, 26651, 26652, 26654, 26655, 26656, 26658, 26659, 26660, 26661, 26662, 26663, 26664, 26667, 26668, 26669, 26670, 26671, 26672, 26673, 26676, 26677, 26678, 26682, 26683, 26687, 26695, 26699, 26701, 26703, 26706, 26710, 26711, 26712, 26713, 26714, 26715, 26716, 26717, 26718, 26719, 26730, 26732, 26733, 26734, 26735, 26736, 26737, 26738, 26739, 26741, 26744, 26745, 26746, 26747, 26748, 26749, 26750, 26751, 26752, 26754, 26756, 26759, 26760, 26761, 26762, 26763, 26764, 26765, 26766, 26768, 26769, 26770, 26772, 26773, 26774, 26776, 26777, 26778, 26779, 26780, 26781, 26782, 26783, 26784, 26785, 26787, 26788, 26789, 26793, 26794, 26795, 26796, 26798, 26801, 26802, 26804, 26806, 26807, 26808, 26809, 26810, 26811, 26812, 26813, 26814, 26815, 26817, 26819, 26820, 26821, 26822, 26823, 26824, 26826, 26828, 26830, 26831, 26832, 26833, 26835, 26836, 26838, 26839, 26841, 26843, 26844, 26845, 26846, 26847, 26849, 26850, 26852, 26853, 26854, 26855, 26856, 26857, 26858, 26859, 26860, 26861, 26863, 26866, 26867, 26868, 26870, 26871, 26872, 26875, 26877, 26878, 26879, 26880, 26882, 26883, 26884, 26886, 26887, 26888, 26889, 26890, 26892, 26895, 26897, 26899, 26900, 26901, 26902, 26903, 26904, 26905, 26906, 26907, 26908, 26909, 26910, 26913, 26914, 26915, 26917, 26918, 26919, 26920, 26921, 26922, 26923, 26924, 26926, 26927, 26929, 26930, 26931, 26933, 26934, 26935, 26936, 26938, 26939, 26940, 26942, 26944, 26945, 26947, 26948, 26949, 26950, 26951, 26952, 26953, 26954, 26955, 26956, 26957, 26958, 26959, 26960, 26961, 26962, 26963, 26965, 26966, 26968, 26969, 26971, 26972, 26975, 26977, 26978, 26980, 26981, 26983, 26984, 26985, 26986, 26988, 26989, 26991, 26992, 26994, 26995, 26996, 26997, 26998, 27002, 27003, 27005, 27006, 27007, 27009, 27011, 27013, 27018, 27019, 27020, 27022, 27023, 27024, 27025, 27026, 27027, 27030, 27031, 27033, 27034, 27037, 27038, 27039, 27040, 27041, 27042, 27043, 27044, 27045, 27046, 27049, 27050, 27052, 27054, 27055, 27056, 27058, 27059, 27061, 27062, 27064, 27065, 27066, 27068, 27069, 27070, 27071, 27072, 27074, 27075, 27076, 27077, 27078, 27079, 27080, 27081, 27083, 27085, 27087, 27089, 27090, 27091, 27093, 27094, 27095, 27096, 27097, 27098, 27100, 27101, 27102, 27105, 27106, 27107, 27108, 27109, 27110, 27111, 27112, 27113, 27114, 27115, 27116, 27118, 27119, 27120, 27121, 27123, 27124, 27125, 27126, 27127, 27128, 27129, 27130, 27131, 27132, 27134, 27136, 27137, 27138, 27139, 27140, 27141, 27142, 27143, 27144, 27145, 27147, 27148, 27149, 27150, 27151, 27152, 27153, 27154, 27155, 27156, 27157, 27158, 27161, 27162, 27163, 27164, 27165, 27166, 27168, 27170, 27171, 27172, 27173, 27174, 27175, 27177, 27179, 27180, 27181, 27182, 27184, 27186, 27187, 27188, 27190, 27191, 27192, 27193, 27194, 27195, 27196, 27199, 27200, 27201, 27202, 27203, 27205, 27206, 27208, 27209, 27210, 27211, 27212, 27213, 27214, 27215, 27217, 27218, 27219, 27220, 27221, 27222, 27223, 27226, 27228, 27229, 27230, 27231, 27232, 27234, 27235, 27236, 27238, 27239, 27240, 27241, 27242, 27243, 27244, 27245, 27246, 27247, 27248, 27250, 27251, 27252, 27253, 27254, 27255, 27256, 27258, 27259, 27261, 27262, 27263, 27265, 27266, 27267, 27269, 27270, 27271, 27272, 27273, 27274, 27275, 27276, 27277, 27279, 27282, 27283, 27284, 27285, 27286, 27288, 27289, 27290, 27291, 27292, 27293, 27294, 27295, 27297, 27298, 27299, 27300, 27301, 27302, 27303, 27304, 27306, 27309, 27310, 27311, 27312, 27313, 27314, 27315, 27316, 27317, 27318, 27319, 27320, 27321, 27322, 27323, 27324, 27325, 27326, 27327, 27328, 27329, 27330, 27331, 27332, 27333, 27334, 27335, 27336, 27337, 27338, 27339, 27340, 27341, 27342, 27343, 27344, 27345, 27346, 27347, 27348, 27349, 27350, 27351, 27352, 27353, 27354, 27355, 27356, 27357, 27358, 27359, 27360, 27361, 27362, 27363, 27364, 27365, 27366, 27367, 27368, 27369, 27370, 27371, 27372, 27373, 27374, 27375, 27376, 27377, 27378, 27379, 27380, 27381, 27382, 27383, 27384, 27385, 27386, 27387, 27388, 27389, 27390, 27391, 27392, 27393, 27394, 27395, 27396, 27397, 27398, 27399, 27400, 27401, 27402, 27403, 27404, 27405, 27406, 27407, 27408, 27409, 27410, 27411, 27412, 27413, 27414, 27415, 27416, 27417, 27418, 27419, 27420, 27421, 27422, 27423, 27429, 27430, 27432, 27433, 27434, 27435, 27436, 27437, 27438, 27439, 27440, 27441, 27443, 27444, 27445, 27446, 27448, 27451, 27452, 27453, 27455, 27456, 27457, 27458, 27460, 27461, 27464, 27466, 27467, 27469, 27470, 27471, 27472, 27473, 27474, 27475, 27476, 27477, 27478, 27479, 27480, 27482, 27483, 27484, 27485, 27486, 27487, 27488, 27489, 27496, 27497, 27499, 27500, 27501, 27502, 27503, 27504, 27505, 27506, 27507, 27508, 27509, 27510, 27511, 27512, 27514, 27517, 27518, 27519, 27520, 27525, 27528, 27532, 27534, 27535, 27536, 27537, 27540, 27541, 27543, 27544, 27545, 27548, 27549, 27550, 27551, 27552, 27554, 27555, 27556, 27557, 27558, 27559, 27560, 27561, 27563, 27564, 27565, 27566, 27567, 27568, 27569, 27570, 27574, 27576, 27577, 27578, 27579, 27580, 27581, 27582, 27584, 27587, 27588, 27590, 27591, 27592, 27593, 27594, 27596, 27598, 27600, 27601, 27608, 27610, 27612, 27613, 27614, 27615, 27616, 27618, 27619, 27620, 27621, 27622, 27623, 27624, 27625, 27628, 27629, 27630, 27632, 27633, 27634, 27636, 27638, 27639, 27640, 27642, 27643, 27644, 27646, 27647, 27648, 27649, 27650, 27651, 27652, 27656, 27657, 27658, 27659, 27660, 27662, 27666, 27671, 27676, 27677, 27678, 27680, 27683, 27685, 27691, 27692, 27693, 27697, 27699, 27702, 27703, 27705, 27706, 27707, 27708, 27710, 27711, 27715, 27716, 27717, 27720, 27723, 27724, 27725, 27726, 27727, 27729, 27730, 27731, 27734, 27736, 27737, 27738, 27746, 27747, 27749, 27750, 27751, 27755, 27756, 27757, 27758, 27759, 27761, 27763, 27765, 27767, 27768, 27770, 27771, 27772, 27775, 27776, 27780, 27783, 27786, 27787, 27789, 27790, 27793, 27794, 27797, 27798, 27799, 27800, 27802, 27804, 27805, 27806, 27808, 27810, 27816, 27820, 27823, 27824, 27828, 27829, 27830, 27831, 27834, 27840, 27841, 27842, 27843, 27846, 27847, 27848, 27851, 27853, 27854, 27855, 27857, 27858, 27864, 27865, 27866, 27868, 27869, 27871, 27876, 27878, 27879, 27881, 27884, 27885, 27890, 27892, 27897, 27903, 27904, 27906, 27907, 27909, 27910, 27912, 27913, 27914, 27917, 27919, 27920, 27921, 27923, 27924, 27925, 27926, 27928, 27932, 27933, 27935, 27936, 27937, 27938, 27939, 27940, 27942, 27944, 27945, 27948, 27949, 27951, 27952, 27956, 27958, 27959, 27960, 27962, 27967, 27968, 27970, 27972, 27977, 27980, 27984, 27989, 27990, 27991, 27992, 27995, 27997, 27999, 28001, 28002, 28004, 28005, 28007, 28008, 28011, 28012, 28013, 28016, 28017, 28018, 28019, 28021, 28022, 28025, 28026, 28027, 28029, 28030, 28031, 28032, 28033, 28035, 28036, 28038, 28039, 28042, 28043, 28045, 28047, 28048, 28050, 28054, 28055, 28056, 28057, 28058, 28060, 28066, 28069, 28076, 28077, 28080, 28081, 28083, 28084, 28086, 28087, 28089, 28090, 28091, 28092, 28093, 28094, 28097, 28098, 28099, 28104, 28105, 28106, 28109, 28110, 28111, 28112, 28114, 28115, 28116, 28117, 28119, 28122, 28123, 28124, 28127, 28130, 28131, 28133, 28135, 28136, 28137, 28138, 28141, 28143, 28144, 28146, 28148, 28149, 28150, 28152, 28154, 28157, 28158, 28159, 28160, 28161, 28162, 28163, 28164, 28166, 28167, 28168, 28169, 28171, 28175, 28178, 28179, 28181, 28184, 28185, 28187, 28188, 28190, 28191, 28194, 28198, 28199, 28200, 28202, 28204, 28206, 28208, 28209, 28211, 28213, 28214, 28215, 28217, 28219, 28220, 28221, 28222, 28223, 28224, 28225, 28226, 28229, 28230, 28231, 28232, 28233, 28234, 28235, 28236, 28239, 28240, 28241, 28242, 28245, 28247, 28249, 28250, 28252, 28253, 28254, 28256, 28257, 28258, 28259, 28260, 28261, 28262, 28263, 28264, 28265, 28266, 28268, 28269, 28271, 28272, 28273, 28274, 28275, 28276, 28277, 28278, 28279, 28280, 28281, 28282, 28283, 28284, 28285, 28288, 28289, 28290, 28292, 28295, 28296, 28298, 28299, 28300, 28301, 28302, 28305, 28306, 28307, 28308, 28309, 28310, 28311, 28313, 28314, 28315, 28317, 28318, 28320, 28321, 28323, 28324, 28326, 28328, 28329, 28331, 28332, 28333, 28334, 28336, 28339, 28341, 28344, 28345, 28348, 28350, 28351, 28352, 28355, 28356, 28357, 28358, 28360, 28361, 28362, 28364, 28365, 28366, 28368, 28370, 28374, 28376, 28377, 28379, 28380, 28381, 28387, 28391, 28394, 28395, 28396, 28397, 28398, 28399, 28400, 28401, 28402, 28403, 28405, 28406, 28407, 28408, 28410, 28411, 28412, 28413, 28414, 28415, 28416, 28417, 28419, 28420, 28421, 28423, 28424, 28426, 28427, 28428, 28429, 28430, 28432, 28433, 28434, 28438, 28439, 28440, 28441, 28442, 28443, 28444, 28445, 28446, 28447, 28449, 28450, 28451, 28453, 28454, 28455, 28456, 28460, 28462, 28464, 28466, 28468, 28469, 28471, 28472, 28473, 28474, 28475, 28476, 28477, 28479, 28480, 28481, 28482, 28483, 28484, 28485, 28488, 28489, 28490, 28492, 28494, 28495, 28496, 28497, 28498, 28499, 28500, 28501, 28502, 28503, 28505, 28506, 28507, 28509, 28511, 28512, 28513, 28515, 28516, 28517, 28519, 28520, 28521, 28522, 28523, 28524, 28527, 28528, 28529, 28531, 28533, 28534, 28535, 28537, 28539, 28541, 28542, 28543, 28544, 28545, 28546, 28547, 28549, 28550, 28551, 28554, 28555, 28559, 28560, 28561, 28562, 28563, 28564, 28565, 28566, 28567, 28568, 28569, 28570, 28571, 28573, 28574, 28575, 28576, 28578, 28579, 28580, 28581, 28582, 28584, 28585, 28586, 28587, 28588, 28589, 28590, 28591, 28592, 28593, 28594, 28596, 28597, 28599, 28600, 28602, 28603, 28604, 28605, 28606, 28607, 28609, 28611, 28612, 28613, 28614, 28615, 28616, 28618, 28619, 28620, 28621, 28622, 28623, 28624, 28627, 28628, 28629, 28630, 28631, 28632, 28633, 28634, 28635, 28636, 28637, 28639, 28642, 28643, 28644, 28645, 28646, 28647, 28648, 28649, 28650, 28651, 28652, 28653, 28656, 28657, 28658, 28659, 28660, 28661, 28662, 28663, 28664, 28665, 28666, 28667, 28668, 28669, 28670, 28671, 28672, 28673, 28674, 28675, 28676, 28677, 28678, 28679, 28680, 28681, 28682, 28683, 28684, 28685, 28686, 28687, 28688, 28690, 28691, 28692, 28693, 28694, 28695, 28696, 28697, 28700, 28701, 28702, 28703, 28704, 28705, 28706, 28708, 28709, 28710, 28711, 28712, 28713, 28714, 28715, 28716, 28717, 28718, 28719, 28720, 28721, 28722, 28723, 28724, 28726, 28727, 28728, 28730, 28731, 28732, 28733, 28734, 28735, 28736, 28737, 28738, 28739, 28740, 28741, 28742, 28743, 28744, 28745, 28746, 28747, 28749, 28750, 28752, 28753, 28754, 28755, 28756, 28757, 28758, 28759, 28760, 28761, 28762, 28763, 28764, 28765, 28767, 28768, 28769, 28770, 28771, 28772, 28773, 28774, 28775, 28776, 28777, 28778, 28782, 28785, 28786, 28787, 28788, 28791, 28793, 28794, 28795, 28797, 28801, 28802, 28803, 28804, 28806, 28807, 28808, 28811, 28812, 28813, 28815, 28816, 28817, 28819, 28823, 28824, 28826, 28827, 28830, 28831, 28832, 28833, 28834, 28835, 28836, 28837, 28838, 28839, 28840, 28841, 28842, 28848, 28850, 28852, 28853, 28854, 28858, 28862, 28863, 28868, 28869, 28870, 28871, 28873, 28875, 28876, 28877, 28878, 28879, 28880, 28881, 28882, 28883, 28884, 28885, 28886, 28887, 28890, 28892, 28893, 28894, 28896, 28897, 28898, 28899, 28901, 28906, 28910, 28912, 28913, 28914, 28915, 28916, 28917, 28918, 28920, 28922, 28923, 28924, 28926, 28927, 28928, 28929, 28930, 28931, 28932, 28933, 28934, 28935, 28936, 28939, 28940, 28941, 28942, 28943, 28945, 28946, 28948, 28951, 28955, 28956, 28957, 28958, 28959, 28960, 28961, 28962, 28963, 28964, 28965, 28967, 28968, 28969, 28970, 28971, 28972, 28973, 28974, 28978, 28979, 28980, 28981, 28983, 28984, 28985, 28986, 28987, 28988, 28989, 28990, 28991, 28992, 28993, 28994, 28995, 28996, 28998, 28999, 29000, 29001, 29003, 29005, 29007, 29008, 29009, 29010, 29011, 29012, 29013, 29014, 29015, 29016, 29017, 29018, 29019, 29021, 29023, 29024, 29025, 29026, 29027, 29029, 29033, 29034, 29035, 29036, 29037, 29039, 29040, 29041, 29044, 29045, 29046, 29047, 29049, 29051, 29052, 29054, 29055, 29056, 29057, 29058, 29059, 29061, 29062, 29063, 29064, 29065, 29067, 29068, 29069, 29070, 29072, 29073, 29074, 29075, 29077, 29078, 29079, 29082, 29083, 29084, 29085, 29086, 29089, 29090, 29091, 29092, 29093, 29094, 29095, 29097, 29098, 29099, 29101, 29102, 29103, 29104, 29105, 29106, 29108, 29110, 29111, 29112, 29114, 29115, 29116, 29117, 29118, 29119, 29120, 29121, 29122, 29124, 29125, 29126, 29127, 29128, 29129, 29130, 29131, 29132, 29133, 29135, 29136, 29137, 29138, 29139, 29142, 29143, 29144, 29145, 29146, 29147, 29148, 29149, 29150, 29151, 29153, 29154, 29155, 29156, 29158, 29160, 29161, 29162, 29163, 29164, 29165, 29167, 29168, 29169, 29170, 29171, 29172, 29173, 29174, 29175, 29176, 29178, 29179, 29180, 29181, 29182, 29183, 29184, 29185, 29186, 29187, 29188, 29189, 29191, 29192, 29193, 29194, 29195, 29196, 29197, 29198, 29199, 29200, 29201, 29202, 29203, 29204, 29205, 29206, 29207, 29208, 29209, 29210, 29211, 29212, 29214, 29215, 29216, 29217, 29218, 29219, 29220, 29221, 29222, 29223, 29225, 29227, 29229, 29230, 29231, 29234, 29235, 29236, 29242, 29244, 29246, 29248, 29249, 29250, 29251, 29252, 29253, 29254, 29257, 29258, 29259, 29262, 29263, 29264, 29265, 29267, 29268, 29269, 29271, 29272, 29274, 29276, 29278, 29280, 29283, 29284, 29285, 29288, 29290, 29291, 29292, 29293, 29296, 29297, 29299, 29300, 29302, 29303, 29304, 29307, 29308, 29309, 29314, 29315, 29317, 29318, 29319, 29320, 29321, 29324, 29326, 29328, 29329, 29331, 29332, 29333, 29334, 29335, 29336, 29337, 29338, 29339, 29340, 29341, 29342, 29344, 29345, 29346, 29347, 29348, 29349, 29350, 29351, 29352, 29353, 29354, 29355, 29358, 29361, 29362, 29363, 29365, 29370, 29371, 29372, 29373, 29374, 29375, 29376, 29381, 29382, 29383, 29385, 29386, 29387, 29388, 29391, 29393, 29395, 29396, 29397, 29398, 29400, 29402, 29403, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 12288, 12289, 12290, 183, 713, 711, 168, 12291, 12293, 8212, 65374, 8214, 8230, 8216, 8217, 8220, 8221, 12308, 12309, 12296, 12297, 12298, 12299, 12300, 12301, 12302, 12303, 12310, 12311, 12304, 12305, 177, 215, 247, 8758, 8743, 8744, 8721, 8719, 8746, 8745, 8712, 8759, 8730, 8869, 8741, 8736, 8978, 8857, 8747, 8750, 8801, 8780, 8776, 8765, 8733, 8800, 8814, 8815, 8804, 8805, 8734, 8757, 8756, 9794, 9792, 176, 8242, 8243, 8451, 65284, 164, 65504, 65505, 8240, 167, 8470, 9734, 9733, 9675, 9679, 9678, 9671, 9670, 9633, 9632, 9651, 9650, 8251, 8594, 8592, 8593, 8595, 12307, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 8560, 8561, 8562, 8563, 8564, 8565, 8566, 8567, 8568, 8569, null, null, null, null, null, null, 9352, 9353, 9354, 9355, 9356, 9357, 9358, 9359, 9360, 9361, 9362, 9363, 9364, 9365, 9366, 9367, 9368, 9369, 9370, 9371, 9332, 9333, 9334, 9335, 9336, 9337, 9338, 9339, 9340, 9341, 9342, 9343, 9344, 9345, 9346, 9347, 9348, 9349, 9350, 9351, 9312, 9313, 9314, 9315, 9316, 9317, 9318, 9319, 9320, 9321, 8364, null, 12832, 12833, 12834, 12835, 12836, 12837, 12838, 12839, 12840, 12841, null, null, 8544, 8545, 8546, 8547, 8548, 8549, 8550, 8551, 8552, 8553, 8554, 8555, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 12288, 65281, 65282, 65283, 65509, 65285, 65286, 65287, 65288, 65289, 65290, 65291, 65292, 65293, 65294, 65295, 65296, 65297, 65298, 65299, 65300, 65301, 65302, 65303, 65304, 65305, 65306, 65307, 65308, 65309, 65310, 65311, 65312, 65313, 65314, 65315, 65316, 65317, 65318, 65319, 65320, 65321, 65322, 65323, 65324, 65325, 65326, 65327, 65328, 65329, 65330, 65331, 65332, 65333, 65334, 65335, 65336, 65337, 65338, 65339, 65340, 65341, 65342, 65343, 65344, 65345, 65346, 65347, 65348, 65349, 65350, 65351, 65352, 65353, 65354, 65355, 65356, 65357, 65358, 65359, 65360, 65361, 65362, 65363, 65364, 65365, 65366, 65367, 65368, 65369, 65370, 65371, 65372, 65373, 65507, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 12353, 12354, 12355, 12356, 12357, 12358, 12359, 12360, 12361, 12362, 12363, 12364, 12365, 12366, 12367, 12368, 12369, 12370, 12371, 12372, 12373, 12374, 12375, 12376, 12377, 12378, 12379, 12380, 12381, 12382, 12383, 12384, 12385, 12386, 12387, 12388, 12389, 12390, 12391, 12392, 12393, 12394, 12395, 12396, 12397, 12398, 12399, 12400, 12401, 12402, 12403, 12404, 12405, 12406, 12407, 12408, 12409, 12410, 12411, 12412, 12413, 12414, 12415, 12416, 12417, 12418, 12419, 12420, 12421, 12422, 12423, 12424, 12425, 12426, 12427, 12428, 12429, 12430, 12431, 12432, 12433, 12434, 12435, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 12449, 12450, 12451, 12452, 12453, 12454, 12455, 12456, 12457, 12458, 12459, 12460, 12461, 12462, 12463, 12464, 12465, 12466, 12467, 12468, 12469, 12470, 12471, 12472, 12473, 12474, 12475, 12476, 12477, 12478, 12479, 12480, 12481, 12482, 12483, 12484, 12485, 12486, 12487, 12488, 12489, 12490, 12491, 12492, 12493, 12494, 12495, 12496, 12497, 12498, 12499, 12500, 12501, 12502, 12503, 12504, 12505, 12506, 12507, 12508, 12509, 12510, 12511, 12512, 12513, 12514, 12515, 12516, 12517, 12518, 12519, 12520, 12521, 12522, 12523, 12524, 12525, 12526, 12527, 12528, 12529, 12530, 12531, 12532, 12533, 12534, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 931, 932, 933, 934, 935, 936, 937, null, null, null, null, null, null, null, null, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 963, 964, 965, 966, 967, 968, 969, null, null, null, null, null, null, null, 65077, 65078, 65081, 65082, 65087, 65088, 65085, 65086, 65089, 65090, 65091, 65092, null, null, 65083, 65084, 65079, 65080, 65073, null, 65075, 65076, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 1040, 1041, 1042, 1043, 1044, 1045, 1025, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 1072, 1073, 1074, 1075, 1076, 1077, 1105, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, null, null, null, null, null, null, null, null, null, null, null, null, null, 714, 715, 729, 8211, 8213, 8229, 8245, 8453, 8457, 8598, 8599, 8600, 8601, 8725, 8735, 8739, 8786, 8806, 8807, 8895, 9552, 9553, 9554, 9555, 9556, 9557, 9558, 9559, 9560, 9561, 9562, 9563, 9564, 9565, 9566, 9567, 9568, 9569, 9570, 9571, 9572, 9573, 9574, 9575, 9576, 9577, 9578, 9579, 9580, 9581, 9582, 9583, 9584, 9585, 9586, 9587, 9601, 9602, 9603, 9604, 9605, 9606, 9607, 9608, 9609, 9610, 9611, 9612, 9613, 9614, 9615, 9619, 9620, 9621, 9660, 9661, 9698, 9699, 9700, 9701, 9737, 8853, 12306, 12317, 12318, null, null, null, null, null, null, null, null, null, null, null, 257, 225, 462, 224, 275, 233, 283, 232, 299, 237, 464, 236, 333, 243, 466, 242, 363, 250, 468, 249, 470, 472, 474, 476, 252, 234, 593, null, 324, 328, 505, 609, null, null, null, null, 12549, 12550, 12551, 12552, 12553, 12554, 12555, 12556, 12557, 12558, 12559, 12560, 12561, 12562, 12563, 12564, 12565, 12566, 12567, 12568, 12569, 12570, 12571, 12572, 12573, 12574, 12575, 12576, 12577, 12578, 12579, 12580, 12581, 12582, 12583, 12584, 12585, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 12321, 12322, 12323, 12324, 12325, 12326, 12327, 12328, 12329, 12963, 13198, 13199, 13212, 13213, 13214, 13217, 13252, 13262, 13265, 13266, 13269, 65072, 65506, 65508, null, 8481, 12849, null, 8208, null, null, null, 12540, 12443, 12444, 12541, 12542, 12294, 12445, 12446, 65097, 65098, 65099, 65100, 65101, 65102, 65103, 65104, 65105, 65106, 65108, 65109, 65110, 65111, 65113, 65114, 65115, 65116, 65117, 65118, 65119, 65120, 65121, 65122, 65123, 65124, 65125, 65126, 65128, 65129, 65130, 65131, 12350, 12272, 12273, 12274, 12275, 12276, 12277, 12278, 12279, 12280, 12281, 12282, 12283, 12295, null, null, null, null, null, null, null, null, null, null, null, null, null, 9472, 9473, 9474, 9475, 9476, 9477, 9478, 9479, 9480, 9481, 9482, 9483, 9484, 9485, 9486, 9487, 9488, 9489, 9490, 9491, 9492, 9493, 9494, 9495, 9496, 9497, 9498, 9499, 9500, 9501, 9502, 9503, 9504, 9505, 9506, 9507, 9508, 9509, 9510, 9511, 9512, 9513, 9514, 9515, 9516, 9517, 9518, 9519, 9520, 9521, 9522, 9523, 9524, 9525, 9526, 9527, 9528, 9529, 9530, 9531, 9532, 9533, 9534, 9535, 9536, 9537, 9538, 9539, 9540, 9541, 9542, 9543, 9544, 9545, 9546, 9547, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 29404, 29405, 29407, 29410, 29411, 29412, 29413, 29414, 29415, 29418, 29419, 29429, 29430, 29433, 29437, 29438, 29439, 29440, 29442, 29444, 29445, 29446, 29447, 29448, 29449, 29451, 29452, 29453, 29455, 29456, 29457, 29458, 29460, 29464, 29465, 29466, 29471, 29472, 29475, 29476, 29478, 29479, 29480, 29485, 29487, 29488, 29490, 29491, 29493, 29494, 29498, 29499, 29500, 29501, 29504, 29505, 29506, 29507, 29508, 29509, 29510, 29511, 29512, 29513, 29514, 29515, 29516, 29518, 29519, 29521, 29523, 29524, 29525, 29526, 29528, 29529, 29530, 29531, 29532, 29533, 29534, 29535, 29537, 29538, 29539, 29540, 29541, 29542, 29543, 29544, 29545, 29546, 29547, 29550, 29552, 29553, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 29554, 29555, 29556, 29557, 29558, 29559, 29560, 29561, 29562, 29563, 29564, 29565, 29567, 29568, 29569, 29570, 29571, 29573, 29574, 29576, 29578, 29580, 29581, 29583, 29584, 29586, 29587, 29588, 29589, 29591, 29592, 29593, 29594, 29596, 29597, 29598, 29600, 29601, 29603, 29604, 29605, 29606, 29607, 29608, 29610, 29612, 29613, 29617, 29620, 29621, 29622, 29624, 29625, 29628, 29629, 29630, 29631, 29633, 29635, 29636, 29637, 29638, 29639, 29643, 29644, 29646, 29650, 29651, 29652, 29653, 29654, 29655, 29656, 29658, 29659, 29660, 29661, 29663, 29665, 29666, 29667, 29668, 29670, 29672, 29674, 29675, 29676, 29678, 29679, 29680, 29681, 29683, 29684, 29685, 29686, 29687, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 29688, 29689, 29690, 29691, 29692, 29693, 29694, 29695, 29696, 29697, 29698, 29700, 29703, 29704, 29707, 29708, 29709, 29710, 29713, 29714, 29715, 29716, 29717, 29718, 29719, 29720, 29721, 29724, 29725, 29726, 29727, 29728, 29729, 29731, 29732, 29735, 29737, 29739, 29741, 29743, 29745, 29746, 29751, 29752, 29753, 29754, 29755, 29757, 29758, 29759, 29760, 29762, 29763, 29764, 29765, 29766, 29767, 29768, 29769, 29770, 29771, 29772, 29773, 29774, 29775, 29776, 29777, 29778, 29779, 29780, 29782, 29784, 29789, 29792, 29793, 29794, 29795, 29796, 29797, 29798, 29799, 29800, 29801, 29802, 29803, 29804, 29806, 29807, 29809, 29810, 29811, 29812, 29813, 29816, 29817, 29818, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 29819, 29820, 29821, 29823, 29826, 29828, 29829, 29830, 29832, 29833, 29834, 29836, 29837, 29839, 29841, 29842, 29843, 29844, 29845, 29846, 29847, 29848, 29849, 29850, 29851, 29853, 29855, 29856, 29857, 29858, 29859, 29860, 29861, 29862, 29866, 29867, 29868, 29869, 29870, 29871, 29872, 29873, 29874, 29875, 29876, 29877, 29878, 29879, 29880, 29881, 29883, 29884, 29885, 29886, 29887, 29888, 29889, 29890, 29891, 29892, 29893, 29894, 29895, 29896, 29897, 29898, 29899, 29900, 29901, 29902, 29903, 29904, 29905, 29907, 29908, 29909, 29910, 29911, 29912, 29913, 29914, 29915, 29917, 29919, 29921, 29925, 29927, 29928, 29929, 29930, 29931, 29932, 29933, 29936, 29937, 29938, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 29939, 29941, 29944, 29945, 29946, 29947, 29948, 29949, 29950, 29952, 29953, 29954, 29955, 29957, 29958, 29959, 29960, 29961, 29962, 29963, 29964, 29966, 29968, 29970, 29972, 29973, 29974, 29975, 29979, 29981, 29982, 29984, 29985, 29986, 29987, 29988, 29990, 29991, 29994, 29998, 30004, 30006, 30009, 30012, 30013, 30015, 30017, 30018, 30019, 30020, 30022, 30023, 30025, 30026, 30029, 30032, 30033, 30034, 30035, 30037, 30038, 30039, 30040, 30045, 30046, 30047, 30048, 30049, 30050, 30051, 30052, 30055, 30056, 30057, 30059, 30060, 30061, 30062, 30063, 30064, 30065, 30067, 30069, 30070, 30071, 30074, 30075, 30076, 30077, 30078, 30080, 30081, 30082, 30084, 30085, 30087, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 30088, 30089, 30090, 30092, 30093, 30094, 30096, 30099, 30101, 30104, 30107, 30108, 30110, 30114, 30118, 30119, 30120, 30121, 30122, 30125, 30134, 30135, 30138, 30139, 30143, 30144, 30145, 30150, 30155, 30156, 30158, 30159, 30160, 30161, 30163, 30167, 30169, 30170, 30172, 30173, 30175, 30176, 30177, 30181, 30185, 30188, 30189, 30190, 30191, 30194, 30195, 30197, 30198, 30199, 30200, 30202, 30203, 30205, 30206, 30210, 30212, 30214, 30215, 30216, 30217, 30219, 30221, 30222, 30223, 30225, 30226, 30227, 30228, 30230, 30234, 30236, 30237, 30238, 30241, 30243, 30247, 30248, 30252, 30254, 30255, 30257, 30258, 30262, 30263, 30265, 30266, 30267, 30269, 30273, 30274, 30276, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 30277, 30278, 30279, 30280, 30281, 30282, 30283, 30286, 30287, 30288, 30289, 30290, 30291, 30293, 30295, 30296, 30297, 30298, 30299, 30301, 30303, 30304, 30305, 30306, 30308, 30309, 30310, 30311, 30312, 30313, 30314, 30316, 30317, 30318, 30320, 30321, 30322, 30323, 30324, 30325, 30326, 30327, 30329, 30330, 30332, 30335, 30336, 30337, 30339, 30341, 30345, 30346, 30348, 30349, 30351, 30352, 30354, 30356, 30357, 30359, 30360, 30362, 30363, 30364, 30365, 30366, 30367, 30368, 30369, 30370, 30371, 30373, 30374, 30375, 30376, 30377, 30378, 30379, 30380, 30381, 30383, 30384, 30387, 30389, 30390, 30391, 30392, 30393, 30394, 30395, 30396, 30397, 30398, 30400, 30401, 30403, 21834, 38463, 22467, 25384, 21710, 21769, 21696, 30353, 30284, 34108, 30702, 33406, 30861, 29233, 38552, 38797, 27688, 23433, 20474, 25353, 26263, 23736, 33018, 26696, 32942, 26114, 30414, 20985, 25942, 29100, 32753, 34948, 20658, 22885, 25034, 28595, 33453, 25420, 25170, 21485, 21543, 31494, 20843, 30116, 24052, 25300, 36299, 38774, 25226, 32793, 22365, 38712, 32610, 29240, 30333, 26575, 30334, 25670, 20336, 36133, 25308, 31255, 26001, 29677, 25644, 25203, 33324, 39041, 26495, 29256, 25198, 25292, 20276, 29923, 21322, 21150, 32458, 37030, 24110, 26758, 27036, 33152, 32465, 26834, 30917, 34444, 38225, 20621, 35876, 33502, 32990, 21253, 35090, 21093, 30404, 30407, 30409, 30411, 30412, 30419, 30421, 30425, 30426, 30428, 30429, 30430, 30432, 30433, 30434, 30435, 30436, 30438, 30439, 30440, 30441, 30442, 30443, 30444, 30445, 30448, 30451, 30453, 30454, 30455, 30458, 30459, 30461, 30463, 30464, 30466, 30467, 30469, 30470, 30474, 30476, 30478, 30479, 30480, 30481, 30482, 30483, 30484, 30485, 30486, 30487, 30488, 30491, 30492, 30493, 30494, 30497, 30499, 30500, 30501, 30503, 30506, 30507, 30508, 30510, 30512, 30513, 30514, 30515, 30516, 30521, 30523, 30525, 30526, 30527, 30530, 30532, 30533, 30534, 30536, 30537, 30538, 30539, 30540, 30541, 30542, 30543, 30546, 30547, 30548, 30549, 30550, 30551, 30552, 30553, 30556, 34180, 38649, 20445, 22561, 39281, 23453, 25265, 25253, 26292, 35961, 40077, 29190, 26479, 30865, 24754, 21329, 21271, 36744, 32972, 36125, 38049, 20493, 29384, 22791, 24811, 28953, 34987, 22868, 33519, 26412, 31528, 23849, 32503, 29997, 27893, 36454, 36856, 36924, 40763, 27604, 37145, 31508, 24444, 30887, 34006, 34109, 27605, 27609, 27606, 24065, 24199, 30201, 38381, 25949, 24330, 24517, 36767, 22721, 33218, 36991, 38491, 38829, 36793, 32534, 36140, 25153, 20415, 21464, 21342, 36776, 36777, 36779, 36941, 26631, 24426, 33176, 34920, 40150, 24971, 21035, 30250, 24428, 25996, 28626, 28392, 23486, 25672, 20853, 20912, 26564, 19993, 31177, 39292, 28851, 30557, 30558, 30559, 30560, 30564, 30567, 30569, 30570, 30573, 30574, 30575, 30576, 30577, 30578, 30579, 30580, 30581, 30582, 30583, 30584, 30586, 30587, 30588, 30593, 30594, 30595, 30598, 30599, 30600, 30601, 30602, 30603, 30607, 30608, 30611, 30612, 30613, 30614, 30615, 30616, 30617, 30618, 30619, 30620, 30621, 30622, 30625, 30627, 30628, 30630, 30632, 30635, 30637, 30638, 30639, 30641, 30642, 30644, 30646, 30647, 30648, 30649, 30650, 30652, 30654, 30656, 30657, 30658, 30659, 30660, 30661, 30662, 30663, 30664, 30665, 30666, 30667, 30668, 30670, 30671, 30672, 30673, 30674, 30675, 30676, 30677, 30678, 30680, 30681, 30682, 30685, 30686, 30687, 30688, 30689, 30692, 30149, 24182, 29627, 33760, 25773, 25320, 38069, 27874, 21338, 21187, 25615, 38082, 31636, 20271, 24091, 33334, 33046, 33162, 28196, 27850, 39539, 25429, 21340, 21754, 34917, 22496, 19981, 24067, 27493, 31807, 37096, 24598, 25830, 29468, 35009, 26448, 25165, 36130, 30572, 36393, 37319, 24425, 33756, 34081, 39184, 21442, 34453, 27531, 24813, 24808, 28799, 33485, 33329, 20179, 27815, 34255, 25805, 31961, 27133, 26361, 33609, 21397, 31574, 20391, 20876, 27979, 23618, 36461, 25554, 21449, 33580, 33590, 26597, 30900, 25661, 23519, 23700, 24046, 35815, 25286, 26612, 35962, 25600, 25530, 34633, 39307, 35863, 32544, 38130, 20135, 38416, 39076, 26124, 29462, 30694, 30696, 30698, 30703, 30704, 30705, 30706, 30708, 30709, 30711, 30713, 30714, 30715, 30716, 30723, 30724, 30725, 30726, 30727, 30728, 30730, 30731, 30734, 30735, 30736, 30739, 30741, 30745, 30747, 30750, 30752, 30753, 30754, 30756, 30760, 30762, 30763, 30766, 30767, 30769, 30770, 30771, 30773, 30774, 30781, 30783, 30785, 30786, 30787, 30788, 30790, 30792, 30793, 30794, 30795, 30797, 30799, 30801, 30803, 30804, 30808, 30809, 30810, 30811, 30812, 30814, 30815, 30816, 30817, 30818, 30819, 30820, 30821, 30822, 30823, 30824, 30825, 30831, 30832, 30833, 30834, 30835, 30836, 30837, 30838, 30840, 30841, 30842, 30843, 30845, 30846, 30847, 30848, 30849, 30850, 30851, 22330, 23581, 24120, 38271, 20607, 32928, 21378, 25950, 30021, 21809, 20513, 36229, 25220, 38046, 26397, 22066, 28526, 24034, 21557, 28818, 36710, 25199, 25764, 25507, 24443, 28552, 37108, 33251, 36784, 23576, 26216, 24561, 27785, 38472, 36225, 34924, 25745, 31216, 22478, 27225, 25104, 21576, 20056, 31243, 24809, 28548, 35802, 25215, 36894, 39563, 31204, 21507, 30196, 25345, 21273, 27744, 36831, 24347, 39536, 32827, 40831, 20360, 23610, 36196, 32709, 26021, 28861, 20805, 20914, 34411, 23815, 23456, 25277, 37228, 30068, 36364, 31264, 24833, 31609, 20167, 32504, 30597, 19985, 33261, 21021, 20986, 27249, 21416, 36487, 38148, 38607, 28353, 38500, 26970, 30852, 30853, 30854, 30856, 30858, 30859, 30863, 30864, 30866, 30868, 30869, 30870, 30873, 30877, 30878, 30880, 30882, 30884, 30886, 30888, 30889, 30890, 30891, 30892, 30893, 30894, 30895, 30901, 30902, 30903, 30904, 30906, 30907, 30908, 30909, 30911, 30912, 30914, 30915, 30916, 30918, 30919, 30920, 30924, 30925, 30926, 30927, 30929, 30930, 30931, 30934, 30935, 30936, 30938, 30939, 30940, 30941, 30942, 30943, 30944, 30945, 30946, 30947, 30948, 30949, 30950, 30951, 30953, 30954, 30955, 30957, 30958, 30959, 30960, 30961, 30963, 30965, 30966, 30968, 30969, 30971, 30972, 30973, 30974, 30975, 30976, 30978, 30979, 30980, 30982, 30983, 30984, 30985, 30986, 30987, 30988, 30784, 20648, 30679, 25616, 35302, 22788, 25571, 24029, 31359, 26941, 20256, 33337, 21912, 20018, 30126, 31383, 24162, 24202, 38383, 21019, 21561, 28810, 25462, 38180, 22402, 26149, 26943, 37255, 21767, 28147, 32431, 34850, 25139, 32496, 30133, 33576, 30913, 38604, 36766, 24904, 29943, 35789, 27492, 21050, 36176, 27425, 32874, 33905, 22257, 21254, 20174, 19995, 20945, 31895, 37259, 31751, 20419, 36479, 31713, 31388, 25703, 23828, 20652, 33030, 30209, 31929, 28140, 32736, 26449, 23384, 23544, 30923, 25774, 25619, 25514, 25387, 38169, 25645, 36798, 31572, 30249, 25171, 22823, 21574, 27513, 20643, 25140, 24102, 27526, 20195, 36151, 34955, 24453, 36910, 30989, 30990, 30991, 30992, 30993, 30994, 30996, 30997, 30998, 30999, 31000, 31001, 31002, 31003, 31004, 31005, 31007, 31008, 31009, 31010, 31011, 31013, 31014, 31015, 31016, 31017, 31018, 31019, 31020, 31021, 31022, 31023, 31024, 31025, 31026, 31027, 31029, 31030, 31031, 31032, 31033, 31037, 31039, 31042, 31043, 31044, 31045, 31047, 31050, 31051, 31052, 31053, 31054, 31055, 31056, 31057, 31058, 31060, 31061, 31064, 31065, 31073, 31075, 31076, 31078, 31081, 31082, 31083, 31084, 31086, 31088, 31089, 31090, 31091, 31092, 31093, 31094, 31097, 31099, 31100, 31101, 31102, 31103, 31106, 31107, 31110, 31111, 31112, 31113, 31115, 31116, 31117, 31118, 31120, 31121, 31122, 24608, 32829, 25285, 20025, 21333, 37112, 25528, 32966, 26086, 27694, 20294, 24814, 28129, 35806, 24377, 34507, 24403, 25377, 20826, 33633, 26723, 20992, 25443, 36424, 20498, 23707, 31095, 23548, 21040, 31291, 24764, 36947, 30423, 24503, 24471, 30340, 36460, 28783, 30331, 31561, 30634, 20979, 37011, 22564, 20302, 28404, 36842, 25932, 31515, 29380, 28068, 32735, 23265, 25269, 24213, 22320, 33922, 31532, 24093, 24351, 36882, 32532, 39072, 25474, 28359, 30872, 28857, 20856, 38747, 22443, 30005, 20291, 30008, 24215, 24806, 22880, 28096, 27583, 30857, 21500, 38613, 20939, 20993, 25481, 21514, 38035, 35843, 36300, 29241, 30879, 34678, 36845, 35853, 21472, 31123, 31124, 31125, 31126, 31127, 31128, 31129, 31131, 31132, 31133, 31134, 31135, 31136, 31137, 31138, 31139, 31140, 31141, 31142, 31144, 31145, 31146, 31147, 31148, 31149, 31150, 31151, 31152, 31153, 31154, 31156, 31157, 31158, 31159, 31160, 31164, 31167, 31170, 31172, 31173, 31175, 31176, 31178, 31180, 31182, 31183, 31184, 31187, 31188, 31190, 31191, 31193, 31194, 31195, 31196, 31197, 31198, 31200, 31201, 31202, 31205, 31208, 31210, 31212, 31214, 31217, 31218, 31219, 31220, 31221, 31222, 31223, 31225, 31226, 31228, 31230, 31231, 31233, 31236, 31237, 31239, 31240, 31241, 31242, 31244, 31247, 31248, 31249, 31250, 31251, 31253, 31254, 31256, 31257, 31259, 31260, 19969, 30447, 21486, 38025, 39030, 40718, 38189, 23450, 35746, 20002, 19996, 20908, 33891, 25026, 21160, 26635, 20375, 24683, 20923, 27934, 20828, 25238, 26007, 38497, 35910, 36887, 30168, 37117, 30563, 27602, 29322, 29420, 35835, 22581, 30585, 36172, 26460, 38208, 32922, 24230, 28193, 22930, 31471, 30701, 38203, 27573, 26029, 32526, 22534, 20817, 38431, 23545, 22697, 21544, 36466, 25958, 39039, 22244, 38045, 30462, 36929, 25479, 21702, 22810, 22842, 22427, 36530, 26421, 36346, 33333, 21057, 24816, 22549, 34558, 23784, 40517, 20420, 39069, 35769, 23077, 24694, 21380, 25212, 36943, 37122, 39295, 24681, 32780, 20799, 32819, 23572, 39285, 27953, 20108, 31261, 31263, 31265, 31266, 31268, 31269, 31270, 31271, 31272, 31273, 31274, 31275, 31276, 31277, 31278, 31279, 31280, 31281, 31282, 31284, 31285, 31286, 31288, 31290, 31294, 31296, 31297, 31298, 31299, 31300, 31301, 31303, 31304, 31305, 31306, 31307, 31308, 31309, 31310, 31311, 31312, 31314, 31315, 31316, 31317, 31318, 31320, 31321, 31322, 31323, 31324, 31325, 31326, 31327, 31328, 31329, 31330, 31331, 31332, 31333, 31334, 31335, 31336, 31337, 31338, 31339, 31340, 31341, 31342, 31343, 31345, 31346, 31347, 31349, 31355, 31356, 31357, 31358, 31362, 31365, 31367, 31369, 31370, 31371, 31372, 31374, 31375, 31376, 31379, 31380, 31385, 31386, 31387, 31390, 31393, 31394, 36144, 21457, 32602, 31567, 20240, 20047, 38400, 27861, 29648, 34281, 24070, 30058, 32763, 27146, 30718, 38034, 32321, 20961, 28902, 21453, 36820, 33539, 36137, 29359, 39277, 27867, 22346, 33459, 26041, 32938, 25151, 38450, 22952, 20223, 35775, 32442, 25918, 33778, 38750, 21857, 39134, 32933, 21290, 35837, 21536, 32954, 24223, 27832, 36153, 33452, 37210, 21545, 27675, 20998, 32439, 22367, 28954, 27774, 31881, 22859, 20221, 24575, 24868, 31914, 20016, 23553, 26539, 34562, 23792, 38155, 39118, 30127, 28925, 36898, 20911, 32541, 35773, 22857, 20964, 20315, 21542, 22827, 25975, 32932, 23413, 25206, 25282, 36752, 24133, 27679, 31526, 20239, 20440, 26381, 31395, 31396, 31399, 31401, 31402, 31403, 31406, 31407, 31408, 31409, 31410, 31412, 31413, 31414, 31415, 31416, 31417, 31418, 31419, 31420, 31421, 31422, 31424, 31425, 31426, 31427, 31428, 31429, 31430, 31431, 31432, 31433, 31434, 31436, 31437, 31438, 31439, 31440, 31441, 31442, 31443, 31444, 31445, 31447, 31448, 31450, 31451, 31452, 31453, 31457, 31458, 31460, 31463, 31464, 31465, 31466, 31467, 31468, 31470, 31472, 31473, 31474, 31475, 31476, 31477, 31478, 31479, 31480, 31483, 31484, 31486, 31488, 31489, 31490, 31493, 31495, 31497, 31500, 31501, 31502, 31504, 31506, 31507, 31510, 31511, 31512, 31514, 31516, 31517, 31519, 31521, 31522, 31523, 31527, 31529, 31533, 28014, 28074, 31119, 34993, 24343, 29995, 25242, 36741, 20463, 37340, 26023, 33071, 33105, 24220, 33104, 36212, 21103, 35206, 36171, 22797, 20613, 20184, 38428, 29238, 33145, 36127, 23500, 35747, 38468, 22919, 32538, 21648, 22134, 22030, 35813, 25913, 27010, 38041, 30422, 28297, 24178, 29976, 26438, 26577, 31487, 32925, 36214, 24863, 31174, 25954, 36195, 20872, 21018, 38050, 32568, 32923, 32434, 23703, 28207, 26464, 31705, 30347, 39640, 33167, 32660, 31957, 25630, 38224, 31295, 21578, 21733, 27468, 25601, 25096, 40509, 33011, 30105, 21106, 38761, 33883, 26684, 34532, 38401, 38548, 38124, 20010, 21508, 32473, 26681, 36319, 32789, 26356, 24218, 32697, 31535, 31536, 31538, 31540, 31541, 31542, 31543, 31545, 31547, 31549, 31551, 31552, 31553, 31554, 31555, 31556, 31558, 31560, 31562, 31565, 31566, 31571, 31573, 31575, 31577, 31580, 31582, 31583, 31585, 31587, 31588, 31589, 31590, 31591, 31592, 31593, 31594, 31595, 31596, 31597, 31599, 31600, 31603, 31604, 31606, 31608, 31610, 31612, 31613, 31615, 31617, 31618, 31619, 31620, 31622, 31623, 31624, 31625, 31626, 31627, 31628, 31630, 31631, 31633, 31634, 31635, 31638, 31640, 31641, 31642, 31643, 31646, 31647, 31648, 31651, 31652, 31653, 31662, 31663, 31664, 31666, 31667, 31669, 31670, 31671, 31673, 31674, 31675, 31676, 31677, 31678, 31679, 31680, 31682, 31683, 31684, 22466, 32831, 26775, 24037, 25915, 21151, 24685, 40858, 20379, 36524, 20844, 23467, 24339, 24041, 27742, 25329, 36129, 20849, 38057, 21246, 27807, 33503, 29399, 22434, 26500, 36141, 22815, 36764, 33735, 21653, 31629, 20272, 27837, 23396, 22993, 40723, 21476, 34506, 39592, 35895, 32929, 25925, 39038, 22266, 38599, 21038, 29916, 21072, 23521, 25346, 35074, 20054, 25296, 24618, 26874, 20851, 23448, 20896, 35266, 31649, 39302, 32592, 24815, 28748, 36143, 20809, 24191, 36891, 29808, 35268, 22317, 30789, 24402, 40863, 38394, 36712, 39740, 35809, 30328, 26690, 26588, 36330, 36149, 21053, 36746, 28378, 26829, 38149, 37101, 22269, 26524, 35065, 36807, 21704, 31685, 31688, 31689, 31690, 31691, 31693, 31694, 31695, 31696, 31698, 31700, 31701, 31702, 31703, 31704, 31707, 31708, 31710, 31711, 31712, 31714, 31715, 31716, 31719, 31720, 31721, 31723, 31724, 31725, 31727, 31728, 31730, 31731, 31732, 31733, 31734, 31736, 31737, 31738, 31739, 31741, 31743, 31744, 31745, 31746, 31747, 31748, 31749, 31750, 31752, 31753, 31754, 31757, 31758, 31760, 31761, 31762, 31763, 31764, 31765, 31767, 31768, 31769, 31770, 31771, 31772, 31773, 31774, 31776, 31777, 31778, 31779, 31780, 31781, 31784, 31785, 31787, 31788, 31789, 31790, 31791, 31792, 31793, 31794, 31795, 31796, 31797, 31798, 31799, 31801, 31802, 31803, 31804, 31805, 31806, 31810, 39608, 23401, 28023, 27686, 20133, 23475, 39559, 37219, 25000, 37039, 38889, 21547, 28085, 23506, 20989, 21898, 32597, 32752, 25788, 25421, 26097, 25022, 24717, 28938, 27735, 27721, 22831, 26477, 33322, 22741, 22158, 35946, 27627, 37085, 22909, 32791, 21495, 28009, 21621, 21917, 33655, 33743, 26680, 31166, 21644, 20309, 21512, 30418, 35977, 38402, 27827, 28088, 36203, 35088, 40548, 36154, 22079, 40657, 30165, 24456, 29408, 24680, 21756, 20136, 27178, 34913, 24658, 36720, 21700, 28888, 34425, 40511, 27946, 23439, 24344, 32418, 21897, 20399, 29492, 21564, 21402, 20505, 21518, 21628, 20046, 24573, 29786, 22774, 33899, 32993, 34676, 29392, 31946, 28246, 31811, 31812, 31813, 31814, 31815, 31816, 31817, 31818, 31819, 31820, 31822, 31823, 31824, 31825, 31826, 31827, 31828, 31829, 31830, 31831, 31832, 31833, 31834, 31835, 31836, 31837, 31838, 31839, 31840, 31841, 31842, 31843, 31844, 31845, 31846, 31847, 31848, 31849, 31850, 31851, 31852, 31853, 31854, 31855, 31856, 31857, 31858, 31861, 31862, 31863, 31864, 31865, 31866, 31870, 31871, 31872, 31873, 31874, 31875, 31876, 31877, 31878, 31879, 31880, 31882, 31883, 31884, 31885, 31886, 31887, 31888, 31891, 31892, 31894, 31897, 31898, 31899, 31904, 31905, 31907, 31910, 31911, 31912, 31913, 31915, 31916, 31917, 31919, 31920, 31924, 31925, 31926, 31927, 31928, 31930, 31931, 24359, 34382, 21804, 25252, 20114, 27818, 25143, 33457, 21719, 21326, 29502, 28369, 30011, 21010, 21270, 35805, 27088, 24458, 24576, 28142, 22351, 27426, 29615, 26707, 36824, 32531, 25442, 24739, 21796, 30186, 35938, 28949, 28067, 23462, 24187, 33618, 24908, 40644, 30970, 34647, 31783, 30343, 20976, 24822, 29004, 26179, 24140, 24653, 35854, 28784, 25381, 36745, 24509, 24674, 34516, 22238, 27585, 24724, 24935, 21321, 24800, 26214, 36159, 31229, 20250, 28905, 27719, 35763, 35826, 32472, 33636, 26127, 23130, 39746, 27985, 28151, 35905, 27963, 20249, 28779, 33719, 25110, 24785, 38669, 36135, 31096, 20987, 22334, 22522, 26426, 30072, 31293, 31215, 31637, 31935, 31936, 31938, 31939, 31940, 31942, 31945, 31947, 31950, 31951, 31952, 31953, 31954, 31955, 31956, 31960, 31962, 31963, 31965, 31966, 31969, 31970, 31971, 31972, 31973, 31974, 31975, 31977, 31978, 31979, 31980, 31981, 31982, 31984, 31985, 31986, 31987, 31988, 31989, 31990, 31991, 31993, 31994, 31996, 31997, 31998, 31999, 32000, 32001, 32002, 32003, 32004, 32005, 32006, 32007, 32008, 32009, 32011, 32012, 32013, 32014, 32015, 32016, 32017, 32018, 32019, 32020, 32021, 32022, 32023, 32024, 32025, 32026, 32027, 32028, 32029, 32030, 32031, 32033, 32035, 32036, 32037, 32038, 32040, 32041, 32042, 32044, 32045, 32046, 32048, 32049, 32050, 32051, 32052, 32053, 32054, 32908, 39269, 36857, 28608, 35749, 40481, 23020, 32489, 32521, 21513, 26497, 26840, 36753, 31821, 38598, 21450, 24613, 30142, 27762, 21363, 23241, 32423, 25380, 20960, 33034, 24049, 34015, 25216, 20864, 23395, 20238, 31085, 21058, 24760, 27982, 23492, 23490, 35745, 35760, 26082, 24524, 38469, 22931, 32487, 32426, 22025, 26551, 22841, 20339, 23478, 21152, 33626, 39050, 36158, 30002, 38078, 20551, 31292, 20215, 26550, 39550, 23233, 27516, 30417, 22362, 23574, 31546, 38388, 29006, 20860, 32937, 33392, 22904, 32516, 33575, 26816, 26604, 30897, 30839, 25315, 25441, 31616, 20461, 21098, 20943, 33616, 27099, 37492, 36341, 36145, 35265, 38190, 31661, 20214, 32055, 32056, 32057, 32058, 32059, 32060, 32061, 32062, 32063, 32064, 32065, 32066, 32067, 32068, 32069, 32070, 32071, 32072, 32073, 32074, 32075, 32076, 32077, 32078, 32079, 32080, 32081, 32082, 32083, 32084, 32085, 32086, 32087, 32088, 32089, 32090, 32091, 32092, 32093, 32094, 32095, 32096, 32097, 32098, 32099, 32100, 32101, 32102, 32103, 32104, 32105, 32106, 32107, 32108, 32109, 32111, 32112, 32113, 32114, 32115, 32116, 32117, 32118, 32120, 32121, 32122, 32123, 32124, 32125, 32126, 32127, 32128, 32129, 32130, 32131, 32132, 32133, 32134, 32135, 32136, 32137, 32138, 32139, 32140, 32141, 32142, 32143, 32144, 32145, 32146, 32147, 32148, 32149, 32150, 32151, 32152, 20581, 33328, 21073, 39279, 28176, 28293, 28071, 24314, 20725, 23004, 23558, 27974, 27743, 30086, 33931, 26728, 22870, 35762, 21280, 37233, 38477, 34121, 26898, 30977, 28966, 33014, 20132, 37066, 27975, 39556, 23047, 22204, 25605, 38128, 30699, 20389, 33050, 29409, 35282, 39290, 32564, 32478, 21119, 25945, 37237, 36735, 36739, 21483, 31382, 25581, 25509, 30342, 31224, 34903, 38454, 25130, 21163, 33410, 26708, 26480, 25463, 30571, 31469, 27905, 32467, 35299, 22992, 25106, 34249, 33445, 30028, 20511, 20171, 30117, 35819, 23626, 24062, 31563, 26020, 37329, 20170, 27941, 35167, 32039, 38182, 20165, 35880, 36827, 38771, 26187, 31105, 36817, 28908, 28024, 32153, 32154, 32155, 32156, 32157, 32158, 32159, 32160, 32161, 32162, 32163, 32164, 32165, 32167, 32168, 32169, 32170, 32171, 32172, 32173, 32175, 32176, 32177, 32178, 32179, 32180, 32181, 32182, 32183, 32184, 32185, 32186, 32187, 32188, 32189, 32190, 32191, 32192, 32193, 32194, 32195, 32196, 32197, 32198, 32199, 32200, 32201, 32202, 32203, 32204, 32205, 32206, 32207, 32208, 32209, 32210, 32211, 32212, 32213, 32214, 32215, 32216, 32217, 32218, 32219, 32220, 32221, 32222, 32223, 32224, 32225, 32226, 32227, 32228, 32229, 32230, 32231, 32232, 32233, 32234, 32235, 32236, 32237, 32238, 32239, 32240, 32241, 32242, 32243, 32244, 32245, 32246, 32247, 32248, 32249, 32250, 23613, 21170, 33606, 20834, 33550, 30555, 26230, 40120, 20140, 24778, 31934, 31923, 32463, 20117, 35686, 26223, 39048, 38745, 22659, 25964, 38236, 24452, 30153, 38742, 31455, 31454, 20928, 28847, 31384, 25578, 31350, 32416, 29590, 38893, 20037, 28792, 20061, 37202, 21417, 25937, 26087, 33276, 33285, 21646, 23601, 30106, 38816, 25304, 29401, 30141, 23621, 39545, 33738, 23616, 21632, 30697, 20030, 27822, 32858, 25298, 25454, 24040, 20855, 36317, 36382, 38191, 20465, 21477, 24807, 28844, 21095, 25424, 40515, 23071, 20518, 30519, 21367, 32482, 25733, 25899, 25225, 25496, 20500, 29237, 35273, 20915, 35776, 32477, 22343, 33740, 38055, 20891, 21531, 23803, 32251, 32252, 32253, 32254, 32255, 32256, 32257, 32258, 32259, 32260, 32261, 32262, 32263, 32264, 32265, 32266, 32267, 32268, 32269, 32270, 32271, 32272, 32273, 32274, 32275, 32276, 32277, 32278, 32279, 32280, 32281, 32282, 32283, 32284, 32285, 32286, 32287, 32288, 32289, 32290, 32291, 32292, 32293, 32294, 32295, 32296, 32297, 32298, 32299, 32300, 32301, 32302, 32303, 32304, 32305, 32306, 32307, 32308, 32309, 32310, 32311, 32312, 32313, 32314, 32316, 32317, 32318, 32319, 32320, 32322, 32323, 32324, 32325, 32326, 32328, 32329, 32330, 32331, 32332, 32333, 32334, 32335, 32336, 32337, 32338, 32339, 32340, 32341, 32342, 32343, 32344, 32345, 32346, 32347, 32348, 32349, 20426, 31459, 27994, 37089, 39567, 21888, 21654, 21345, 21679, 24320, 25577, 26999, 20975, 24936, 21002, 22570, 21208, 22350, 30733, 30475, 24247, 24951, 31968, 25179, 25239, 20130, 28821, 32771, 25335, 28900, 38752, 22391, 33499, 26607, 26869, 30933, 39063, 31185, 22771, 21683, 21487, 28212, 20811, 21051, 23458, 35838, 32943, 21827, 22438, 24691, 22353, 21549, 31354, 24656, 23380, 25511, 25248, 21475, 25187, 23495, 26543, 21741, 31391, 33510, 37239, 24211, 35044, 22840, 22446, 25358, 36328, 33007, 22359, 31607, 20393, 24555, 23485, 27454, 21281, 31568, 29378, 26694, 30719, 30518, 26103, 20917, 20111, 30420, 23743, 31397, 33909, 22862, 39745, 20608, 32350, 32351, 32352, 32353, 32354, 32355, 32356, 32357, 32358, 32359, 32360, 32361, 32362, 32363, 32364, 32365, 32366, 32367, 32368, 32369, 32370, 32371, 32372, 32373, 32374, 32375, 32376, 32377, 32378, 32379, 32380, 32381, 32382, 32383, 32384, 32385, 32387, 32388, 32389, 32390, 32391, 32392, 32393, 32394, 32395, 32396, 32397, 32398, 32399, 32400, 32401, 32402, 32403, 32404, 32405, 32406, 32407, 32408, 32409, 32410, 32412, 32413, 32414, 32430, 32436, 32443, 32444, 32470, 32484, 32492, 32505, 32522, 32528, 32542, 32567, 32569, 32571, 32572, 32573, 32574, 32575, 32576, 32577, 32579, 32582, 32583, 32584, 32585, 32586, 32587, 32588, 32589, 32590, 32591, 32594, 32595, 39304, 24871, 28291, 22372, 26118, 25414, 22256, 25324, 25193, 24275, 38420, 22403, 25289, 21895, 34593, 33098, 36771, 21862, 33713, 26469, 36182, 34013, 23146, 26639, 25318, 31726, 38417, 20848, 28572, 35888, 25597, 35272, 25042, 32518, 28866, 28389, 29701, 27028, 29436, 24266, 37070, 26391, 28010, 25438, 21171, 29282, 32769, 20332, 23013, 37226, 28889, 28061, 21202, 20048, 38647, 38253, 34174, 30922, 32047, 20769, 22418, 25794, 32907, 31867, 27882, 26865, 26974, 20919, 21400, 26792, 29313, 40654, 31729, 29432, 31163, 28435, 29702, 26446, 37324, 40100, 31036, 33673, 33620, 21519, 26647, 20029, 21385, 21169, 30782, 21382, 21033, 20616, 20363, 20432, 32598, 32601, 32603, 32604, 32605, 32606, 32608, 32611, 32612, 32613, 32614, 32615, 32619, 32620, 32621, 32623, 32624, 32627, 32629, 32630, 32631, 32632, 32634, 32635, 32636, 32637, 32639, 32640, 32642, 32643, 32644, 32645, 32646, 32647, 32648, 32649, 32651, 32653, 32655, 32656, 32657, 32658, 32659, 32661, 32662, 32663, 32664, 32665, 32667, 32668, 32672, 32674, 32675, 32677, 32678, 32680, 32681, 32682, 32683, 32684, 32685, 32686, 32689, 32691, 32692, 32693, 32694, 32695, 32698, 32699, 32702, 32704, 32706, 32707, 32708, 32710, 32711, 32712, 32713, 32715, 32717, 32719, 32720, 32721, 32722, 32723, 32726, 32727, 32729, 32730, 32731, 32732, 32733, 32734, 32738, 32739, 30178, 31435, 31890, 27813, 38582, 21147, 29827, 21737, 20457, 32852, 33714, 36830, 38256, 24265, 24604, 28063, 24088, 25947, 33080, 38142, 24651, 28860, 32451, 31918, 20937, 26753, 31921, 33391, 20004, 36742, 37327, 26238, 20142, 35845, 25769, 32842, 20698, 30103, 29134, 23525, 36797, 28518, 20102, 25730, 38243, 24278, 26009, 21015, 35010, 28872, 21155, 29454, 29747, 26519, 30967, 38678, 20020, 37051, 40158, 28107, 20955, 36161, 21533, 25294, 29618, 33777, 38646, 40836, 38083, 20278, 32666, 20940, 28789, 38517, 23725, 39046, 21478, 20196, 28316, 29705, 27060, 30827, 39311, 30041, 21016, 30244, 27969, 26611, 20845, 40857, 32843, 21657, 31548, 31423, 32740, 32743, 32744, 32746, 32747, 32748, 32749, 32751, 32754, 32756, 32757, 32758, 32759, 32760, 32761, 32762, 32765, 32766, 32767, 32770, 32775, 32776, 32777, 32778, 32782, 32783, 32785, 32787, 32794, 32795, 32797, 32798, 32799, 32801, 32803, 32804, 32811, 32812, 32813, 32814, 32815, 32816, 32818, 32820, 32825, 32826, 32828, 32830, 32832, 32833, 32836, 32837, 32839, 32840, 32841, 32846, 32847, 32848, 32849, 32851, 32853, 32854, 32855, 32857, 32859, 32860, 32861, 32862, 32863, 32864, 32865, 32866, 32867, 32868, 32869, 32870, 32871, 32872, 32875, 32876, 32877, 32878, 32879, 32880, 32882, 32883, 32884, 32885, 32886, 32887, 32888, 32889, 32890, 32891, 32892, 32893, 38534, 22404, 25314, 38471, 27004, 23044, 25602, 31699, 28431, 38475, 33446, 21346, 39045, 24208, 28809, 25523, 21348, 34383, 40065, 40595, 30860, 38706, 36335, 36162, 40575, 28510, 31108, 24405, 38470, 25134, 39540, 21525, 38109, 20387, 26053, 23653, 23649, 32533, 34385, 27695, 24459, 29575, 28388, 32511, 23782, 25371, 23402, 28390, 21365, 20081, 25504, 30053, 25249, 36718, 20262, 20177, 27814, 32438, 35770, 33821, 34746, 32599, 36923, 38179, 31657, 39585, 35064, 33853, 27931, 39558, 32476, 22920, 40635, 29595, 30721, 34434, 39532, 39554, 22043, 21527, 22475, 20080, 40614, 21334, 36808, 33033, 30610, 39314, 34542, 28385, 34067, 26364, 24930, 28459, 32894, 32897, 32898, 32901, 32904, 32906, 32909, 32910, 32911, 32912, 32913, 32914, 32916, 32917, 32919, 32921, 32926, 32931, 32934, 32935, 32936, 32940, 32944, 32947, 32949, 32950, 32952, 32953, 32955, 32965, 32967, 32968, 32969, 32970, 32971, 32975, 32976, 32977, 32978, 32979, 32980, 32981, 32984, 32991, 32992, 32994, 32995, 32998, 33006, 33013, 33015, 33017, 33019, 33022, 33023, 33024, 33025, 33027, 33028, 33029, 33031, 33032, 33035, 33036, 33045, 33047, 33049, 33051, 33052, 33053, 33055, 33056, 33057, 33058, 33059, 33060, 33061, 33062, 33063, 33064, 33065, 33066, 33067, 33069, 33070, 33072, 33075, 33076, 33077, 33079, 33081, 33082, 33083, 33084, 33085, 33087, 35881, 33426, 33579, 30450, 27667, 24537, 33725, 29483, 33541, 38170, 27611, 30683, 38086, 21359, 33538, 20882, 24125, 35980, 36152, 20040, 29611, 26522, 26757, 37238, 38665, 29028, 27809, 30473, 23186, 38209, 27599, 32654, 26151, 23504, 22969, 23194, 38376, 38391, 20204, 33804, 33945, 27308, 30431, 38192, 29467, 26790, 23391, 30511, 37274, 38753, 31964, 36855, 35868, 24357, 31859, 31192, 35269, 27852, 34588, 23494, 24130, 26825, 30496, 32501, 20885, 20813, 21193, 23081, 32517, 38754, 33495, 25551, 30596, 34256, 31186, 28218, 24217, 22937, 34065, 28781, 27665, 25279, 30399, 25935, 24751, 38397, 26126, 34719, 40483, 38125, 21517, 21629, 35884, 25720, 33088, 33089, 33090, 33091, 33092, 33093, 33095, 33097, 33101, 33102, 33103, 33106, 33110, 33111, 33112, 33115, 33116, 33117, 33118, 33119, 33121, 33122, 33123, 33124, 33126, 33128, 33130, 33131, 33132, 33135, 33138, 33139, 33141, 33142, 33143, 33144, 33153, 33155, 33156, 33157, 33158, 33159, 33161, 33163, 33164, 33165, 33166, 33168, 33170, 33171, 33172, 33173, 33174, 33175, 33177, 33178, 33182, 33183, 33184, 33185, 33186, 33188, 33189, 33191, 33193, 33195, 33196, 33197, 33198, 33199, 33200, 33201, 33202, 33204, 33205, 33206, 33207, 33208, 33209, 33212, 33213, 33214, 33215, 33220, 33221, 33223, 33224, 33225, 33227, 33229, 33230, 33231, 33232, 33233, 33234, 33235, 25721, 34321, 27169, 33180, 30952, 25705, 39764, 25273, 26411, 33707, 22696, 40664, 27819, 28448, 23518, 38476, 35851, 29279, 26576, 25287, 29281, 20137, 22982, 27597, 22675, 26286, 24149, 21215, 24917, 26408, 30446, 30566, 29287, 31302, 25343, 21738, 21584, 38048, 37027, 23068, 32435, 27670, 20035, 22902, 32784, 22856, 21335, 30007, 38590, 22218, 25376, 33041, 24700, 38393, 28118, 21602, 39297, 20869, 23273, 33021, 22958, 38675, 20522, 27877, 23612, 25311, 20320, 21311, 33147, 36870, 28346, 34091, 25288, 24180, 30910, 25781, 25467, 24565, 23064, 37247, 40479, 23615, 25423, 32834, 23421, 21870, 38218, 38221, 28037, 24744, 26592, 29406, 20957, 23425, 33236, 33237, 33238, 33239, 33240, 33241, 33242, 33243, 33244, 33245, 33246, 33247, 33248, 33249, 33250, 33252, 33253, 33254, 33256, 33257, 33259, 33262, 33263, 33264, 33265, 33266, 33269, 33270, 33271, 33272, 33273, 33274, 33277, 33279, 33283, 33287, 33288, 33289, 33290, 33291, 33294, 33295, 33297, 33299, 33301, 33302, 33303, 33304, 33305, 33306, 33309, 33312, 33316, 33317, 33318, 33319, 33321, 33326, 33330, 33338, 33340, 33341, 33343, 33344, 33345, 33346, 33347, 33349, 33350, 33352, 33354, 33356, 33357, 33358, 33360, 33361, 33362, 33363, 33364, 33365, 33366, 33367, 33369, 33371, 33372, 33373, 33374, 33376, 33377, 33378, 33379, 33380, 33381, 33382, 33383, 33385, 25319, 27870, 29275, 25197, 38062, 32445, 33043, 27987, 20892, 24324, 22900, 21162, 24594, 22899, 26262, 34384, 30111, 25386, 25062, 31983, 35834, 21734, 27431, 40485, 27572, 34261, 21589, 20598, 27812, 21866, 36276, 29228, 24085, 24597, 29750, 25293, 25490, 29260, 24472, 28227, 27966, 25856, 28504, 30424, 30928, 30460, 30036, 21028, 21467, 20051, 24222, 26049, 32810, 32982, 25243, 21638, 21032, 28846, 34957, 36305, 27873, 21624, 32986, 22521, 35060, 36180, 38506, 37197, 20329, 27803, 21943, 30406, 30768, 25256, 28921, 28558, 24429, 34028, 26842, 30844, 31735, 33192, 26379, 40527, 25447, 30896, 22383, 30738, 38713, 25209, 25259, 21128, 29749, 27607, 33386, 33387, 33388, 33389, 33393, 33397, 33398, 33399, 33400, 33403, 33404, 33408, 33409, 33411, 33413, 33414, 33415, 33417, 33420, 33424, 33427, 33428, 33429, 33430, 33434, 33435, 33438, 33440, 33442, 33443, 33447, 33458, 33461, 33462, 33466, 33467, 33468, 33471, 33472, 33474, 33475, 33477, 33478, 33481, 33488, 33494, 33497, 33498, 33501, 33506, 33511, 33512, 33513, 33514, 33516, 33517, 33518, 33520, 33522, 33523, 33525, 33526, 33528, 33530, 33532, 33533, 33534, 33535, 33536, 33546, 33547, 33549, 33552, 33554, 33555, 33558, 33560, 33561, 33565, 33566, 33567, 33568, 33569, 33570, 33571, 33572, 33573, 33574, 33577, 33578, 33582, 33584, 33586, 33591, 33595, 33597, 21860, 33086, 30130, 30382, 21305, 30174, 20731, 23617, 35692, 31687, 20559, 29255, 39575, 39128, 28418, 29922, 31080, 25735, 30629, 25340, 39057, 36139, 21697, 32856, 20050, 22378, 33529, 33805, 24179, 20973, 29942, 35780, 23631, 22369, 27900, 39047, 23110, 30772, 39748, 36843, 31893, 21078, 25169, 38138, 20166, 33670, 33889, 33769, 33970, 22484, 26420, 22275, 26222, 28006, 35889, 26333, 28689, 26399, 27450, 26646, 25114, 22971, 19971, 20932, 28422, 26578, 27791, 20854, 26827, 22855, 27495, 30054, 23822, 33040, 40784, 26071, 31048, 31041, 39569, 36215, 23682, 20062, 20225, 21551, 22865, 30732, 22120, 27668, 36804, 24323, 27773, 27875, 35755, 25488, 33598, 33599, 33601, 33602, 33604, 33605, 33608, 33610, 33611, 33612, 33613, 33614, 33619, 33621, 33622, 33623, 33624, 33625, 33629, 33634, 33648, 33649, 33650, 33651, 33652, 33653, 33654, 33657, 33658, 33662, 33663, 33664, 33665, 33666, 33667, 33668, 33671, 33672, 33674, 33675, 33676, 33677, 33679, 33680, 33681, 33684, 33685, 33686, 33687, 33689, 33690, 33693, 33695, 33697, 33698, 33699, 33700, 33701, 33702, 33703, 33708, 33709, 33710, 33711, 33717, 33723, 33726, 33727, 33730, 33731, 33732, 33734, 33736, 33737, 33739, 33741, 33742, 33744, 33745, 33746, 33747, 33749, 33751, 33753, 33754, 33755, 33758, 33762, 33763, 33764, 33766, 33767, 33768, 33771, 33772, 33773, 24688, 27965, 29301, 25190, 38030, 38085, 21315, 36801, 31614, 20191, 35878, 20094, 40660, 38065, 38067, 21069, 28508, 36963, 27973, 35892, 22545, 23884, 27424, 27465, 26538, 21595, 33108, 32652, 22681, 34103, 24378, 25250, 27207, 38201, 25970, 24708, 26725, 30631, 20052, 20392, 24039, 38808, 25772, 32728, 23789, 20431, 31373, 20999, 33540, 19988, 24623, 31363, 38054, 20405, 20146, 31206, 29748, 21220, 33465, 25810, 31165, 23517, 27777, 38738, 36731, 27682, 20542, 21375, 28165, 25806, 26228, 27696, 24773, 39031, 35831, 24198, 29756, 31351, 31179, 19992, 37041, 29699, 27714, 22234, 37195, 27845, 36235, 21306, 34502, 26354, 36527, 23624, 39537, 28192, 33774, 33775, 33779, 33780, 33781, 33782, 33783, 33786, 33787, 33788, 33790, 33791, 33792, 33794, 33797, 33799, 33800, 33801, 33802, 33808, 33810, 33811, 33812, 33813, 33814, 33815, 33817, 33818, 33819, 33822, 33823, 33824, 33825, 33826, 33827, 33833, 33834, 33835, 33836, 33837, 33838, 33839, 33840, 33842, 33843, 33844, 33845, 33846, 33847, 33849, 33850, 33851, 33854, 33855, 33856, 33857, 33858, 33859, 33860, 33861, 33863, 33864, 33865, 33866, 33867, 33868, 33869, 33870, 33871, 33872, 33874, 33875, 33876, 33877, 33878, 33880, 33885, 33886, 33887, 33888, 33890, 33892, 33893, 33894, 33895, 33896, 33898, 33902, 33903, 33904, 33906, 33908, 33911, 33913, 33915, 33916, 21462, 23094, 40843, 36259, 21435, 22280, 39079, 26435, 37275, 27849, 20840, 30154, 25331, 29356, 21048, 21149, 32570, 28820, 30264, 21364, 40522, 27063, 30830, 38592, 35033, 32676, 28982, 29123, 20873, 26579, 29924, 22756, 25880, 22199, 35753, 39286, 25200, 32469, 24825, 28909, 22764, 20161, 20154, 24525, 38887, 20219, 35748, 20995, 22922, 32427, 25172, 20173, 26085, 25102, 33592, 33993, 33635, 34701, 29076, 28342, 23481, 32466, 20887, 25545, 26580, 32905, 33593, 34837, 20754, 23418, 22914, 36785, 20083, 27741, 20837, 35109, 36719, 38446, 34122, 29790, 38160, 38384, 28070, 33509, 24369, 25746, 27922, 33832, 33134, 40131, 22622, 36187, 19977, 21441, 33917, 33918, 33919, 33920, 33921, 33923, 33924, 33925, 33926, 33930, 33933, 33935, 33936, 33937, 33938, 33939, 33940, 33941, 33942, 33944, 33946, 33947, 33949, 33950, 33951, 33952, 33954, 33955, 33956, 33957, 33958, 33959, 33960, 33961, 33962, 33963, 33964, 33965, 33966, 33968, 33969, 33971, 33973, 33974, 33975, 33979, 33980, 33982, 33984, 33986, 33987, 33989, 33990, 33991, 33992, 33995, 33996, 33998, 33999, 34002, 34004, 34005, 34007, 34008, 34009, 34010, 34011, 34012, 34014, 34017, 34018, 34020, 34023, 34024, 34025, 34026, 34027, 34029, 34030, 34031, 34033, 34034, 34035, 34036, 34037, 34038, 34039, 34040, 34041, 34042, 34043, 34045, 34046, 34048, 34049, 34050, 20254, 25955, 26705, 21971, 20007, 25620, 39578, 25195, 23234, 29791, 33394, 28073, 26862, 20711, 33678, 30722, 26432, 21049, 27801, 32433, 20667, 21861, 29022, 31579, 26194, 29642, 33515, 26441, 23665, 21024, 29053, 34923, 38378, 38485, 25797, 36193, 33203, 21892, 27733, 25159, 32558, 22674, 20260, 21830, 36175, 26188, 19978, 23578, 35059, 26786, 25422, 31245, 28903, 33421, 21242, 38902, 23569, 21736, 37045, 32461, 22882, 36170, 34503, 33292, 33293, 36198, 25668, 23556, 24913, 28041, 31038, 35774, 30775, 30003, 21627, 20280, 36523, 28145, 23072, 32453, 31070, 27784, 23457, 23158, 29978, 32958, 24910, 28183, 22768, 29983, 29989, 29298, 21319, 32499, 34051, 34052, 34053, 34054, 34055, 34056, 34057, 34058, 34059, 34061, 34062, 34063, 34064, 34066, 34068, 34069, 34070, 34072, 34073, 34075, 34076, 34077, 34078, 34080, 34082, 34083, 34084, 34085, 34086, 34087, 34088, 34089, 34090, 34093, 34094, 34095, 34096, 34097, 34098, 34099, 34100, 34101, 34102, 34110, 34111, 34112, 34113, 34114, 34116, 34117, 34118, 34119, 34123, 34124, 34125, 34126, 34127, 34128, 34129, 34130, 34131, 34132, 34133, 34135, 34136, 34138, 34139, 34140, 34141, 34143, 34144, 34145, 34146, 34147, 34149, 34150, 34151, 34153, 34154, 34155, 34156, 34157, 34158, 34159, 34160, 34161, 34163, 34165, 34166, 34167, 34168, 34172, 34173, 34175, 34176, 34177, 30465, 30427, 21097, 32988, 22307, 24072, 22833, 29422, 26045, 28287, 35799, 23608, 34417, 21313, 30707, 25342, 26102, 20160, 39135, 34432, 23454, 35782, 21490, 30690, 20351, 23630, 39542, 22987, 24335, 31034, 22763, 19990, 26623, 20107, 25325, 35475, 36893, 21183, 26159, 21980, 22124, 36866, 20181, 20365, 37322, 39280, 27663, 24066, 24643, 23460, 35270, 35797, 25910, 25163, 39318, 23432, 23551, 25480, 21806, 21463, 30246, 20861, 34092, 26530, 26803, 27530, 25234, 36755, 21460, 33298, 28113, 30095, 20070, 36174, 23408, 29087, 34223, 26257, 26329, 32626, 34560, 40653, 40736, 23646, 26415, 36848, 26641, 26463, 25101, 31446, 22661, 24246, 25968, 28465, 34178, 34179, 34182, 34184, 34185, 34186, 34187, 34188, 34189, 34190, 34192, 34193, 34194, 34195, 34196, 34197, 34198, 34199, 34200, 34201, 34202, 34205, 34206, 34207, 34208, 34209, 34210, 34211, 34213, 34214, 34215, 34217, 34219, 34220, 34221, 34225, 34226, 34227, 34228, 34229, 34230, 34232, 34234, 34235, 34236, 34237, 34238, 34239, 34240, 34242, 34243, 34244, 34245, 34246, 34247, 34248, 34250, 34251, 34252, 34253, 34254, 34257, 34258, 34260, 34262, 34263, 34264, 34265, 34266, 34267, 34269, 34270, 34271, 34272, 34273, 34274, 34275, 34277, 34278, 34279, 34280, 34282, 34283, 34284, 34285, 34286, 34287, 34288, 34289, 34290, 34291, 34292, 34293, 34294, 34295, 34296, 24661, 21047, 32781, 25684, 34928, 29993, 24069, 26643, 25332, 38684, 21452, 29245, 35841, 27700, 30561, 31246, 21550, 30636, 39034, 33308, 35828, 30805, 26388, 28865, 26031, 25749, 22070, 24605, 31169, 21496, 19997, 27515, 32902, 23546, 21987, 22235, 20282, 20284, 39282, 24051, 26494, 32824, 24578, 39042, 36865, 23435, 35772, 35829, 25628, 33368, 25822, 22013, 33487, 37221, 20439, 32032, 36895, 31903, 20723, 22609, 28335, 23487, 35785, 32899, 37240, 33948, 31639, 34429, 38539, 38543, 32485, 39635, 30862, 23681, 31319, 36930, 38567, 31071, 23385, 25439, 31499, 34001, 26797, 21766, 32553, 29712, 32034, 38145, 25152, 22604, 20182, 23427, 22905, 22612, 34297, 34298, 34300, 34301, 34302, 34304, 34305, 34306, 34307, 34308, 34310, 34311, 34312, 34313, 34314, 34315, 34316, 34317, 34318, 34319, 34320, 34322, 34323, 34324, 34325, 34327, 34328, 34329, 34330, 34331, 34332, 34333, 34334, 34335, 34336, 34337, 34338, 34339, 34340, 34341, 34342, 34344, 34346, 34347, 34348, 34349, 34350, 34351, 34352, 34353, 34354, 34355, 34356, 34357, 34358, 34359, 34361, 34362, 34363, 34365, 34366, 34367, 34368, 34369, 34370, 34371, 34372, 34373, 34374, 34375, 34376, 34377, 34378, 34379, 34380, 34386, 34387, 34389, 34390, 34391, 34392, 34393, 34395, 34396, 34397, 34399, 34400, 34401, 34403, 34404, 34405, 34406, 34407, 34408, 34409, 34410, 29549, 25374, 36427, 36367, 32974, 33492, 25260, 21488, 27888, 37214, 22826, 24577, 27760, 22349, 25674, 36138, 30251, 28393, 22363, 27264, 30192, 28525, 35885, 35848, 22374, 27631, 34962, 30899, 25506, 21497, 28845, 27748, 22616, 25642, 22530, 26848, 33179, 21776, 31958, 20504, 36538, 28108, 36255, 28907, 25487, 28059, 28372, 32486, 33796, 26691, 36867, 28120, 38518, 35752, 22871, 29305, 34276, 33150, 30140, 35466, 26799, 21076, 36386, 38161, 25552, 39064, 36420, 21884, 20307, 26367, 22159, 24789, 28053, 21059, 23625, 22825, 28155, 22635, 30000, 29980, 24684, 33300, 33094, 25361, 26465, 36834, 30522, 36339, 36148, 38081, 24086, 21381, 21548, 28867, 34413, 34415, 34416, 34418, 34419, 34420, 34421, 34422, 34423, 34424, 34435, 34436, 34437, 34438, 34439, 34440, 34441, 34446, 34447, 34448, 34449, 34450, 34452, 34454, 34455, 34456, 34457, 34458, 34459, 34462, 34463, 34464, 34465, 34466, 34469, 34470, 34475, 34477, 34478, 34482, 34483, 34487, 34488, 34489, 34491, 34492, 34493, 34494, 34495, 34497, 34498, 34499, 34501, 34504, 34508, 34509, 34514, 34515, 34517, 34518, 34519, 34522, 34524, 34525, 34528, 34529, 34530, 34531, 34533, 34534, 34535, 34536, 34538, 34539, 34540, 34543, 34549, 34550, 34551, 34554, 34555, 34556, 34557, 34559, 34561, 34564, 34565, 34566, 34571, 34572, 34574, 34575, 34576, 34577, 34580, 34582, 27712, 24311, 20572, 20141, 24237, 25402, 33351, 36890, 26704, 37230, 30643, 21516, 38108, 24420, 31461, 26742, 25413, 31570, 32479, 30171, 20599, 25237, 22836, 36879, 20984, 31171, 31361, 22270, 24466, 36884, 28034, 23648, 22303, 21520, 20820, 28237, 22242, 25512, 39059, 33151, 34581, 35114, 36864, 21534, 23663, 33216, 25302, 25176, 33073, 40501, 38464, 39534, 39548, 26925, 22949, 25299, 21822, 25366, 21703, 34521, 27964, 23043, 29926, 34972, 27498, 22806, 35916, 24367, 28286, 29609, 39037, 20024, 28919, 23436, 30871, 25405, 26202, 30358, 24779, 23451, 23113, 19975, 33109, 27754, 29579, 20129, 26505, 32593, 24448, 26106, 26395, 24536, 22916, 23041, 34585, 34587, 34589, 34591, 34592, 34596, 34598, 34599, 34600, 34602, 34603, 34604, 34605, 34607, 34608, 34610, 34611, 34613, 34614, 34616, 34617, 34618, 34620, 34621, 34624, 34625, 34626, 34627, 34628, 34629, 34630, 34634, 34635, 34637, 34639, 34640, 34641, 34642, 34644, 34645, 34646, 34648, 34650, 34651, 34652, 34653, 34654, 34655, 34657, 34658, 34662, 34663, 34664, 34665, 34666, 34667, 34668, 34669, 34671, 34673, 34674, 34675, 34677, 34679, 34680, 34681, 34682, 34687, 34688, 34689, 34692, 34694, 34695, 34697, 34698, 34700, 34702, 34703, 34704, 34705, 34706, 34708, 34709, 34710, 34712, 34713, 34714, 34715, 34716, 34717, 34718, 34720, 34721, 34722, 34723, 34724, 24013, 24494, 21361, 38886, 36829, 26693, 22260, 21807, 24799, 20026, 28493, 32500, 33479, 33806, 22996, 20255, 20266, 23614, 32428, 26410, 34074, 21619, 30031, 32963, 21890, 39759, 20301, 28205, 35859, 23561, 24944, 21355, 30239, 28201, 34442, 25991, 38395, 32441, 21563, 31283, 32010, 38382, 21985, 32705, 29934, 25373, 34583, 28065, 31389, 25105, 26017, 21351, 25569, 27779, 24043, 21596, 38056, 20044, 27745, 35820, 23627, 26080, 33436, 26791, 21566, 21556, 27595, 27494, 20116, 25410, 21320, 33310, 20237, 20398, 22366, 25098, 38654, 26212, 29289, 21247, 21153, 24735, 35823, 26132, 29081, 26512, 35199, 30802, 30717, 26224, 22075, 21560, 38177, 29306, 34725, 34726, 34727, 34729, 34730, 34734, 34736, 34737, 34738, 34740, 34742, 34743, 34744, 34745, 34747, 34748, 34750, 34751, 34753, 34754, 34755, 34756, 34757, 34759, 34760, 34761, 34764, 34765, 34766, 34767, 34768, 34772, 34773, 34774, 34775, 34776, 34777, 34778, 34780, 34781, 34782, 34783, 34785, 34786, 34787, 34788, 34790, 34791, 34792, 34793, 34795, 34796, 34797, 34799, 34800, 34801, 34802, 34803, 34804, 34805, 34806, 34807, 34808, 34810, 34811, 34812, 34813, 34815, 34816, 34817, 34818, 34820, 34821, 34822, 34823, 34824, 34825, 34827, 34828, 34829, 34830, 34831, 34832, 34833, 34834, 34836, 34839, 34840, 34841, 34842, 34844, 34845, 34846, 34847, 34848, 34851, 31232, 24687, 24076, 24713, 33181, 22805, 24796, 29060, 28911, 28330, 27728, 29312, 27268, 34989, 24109, 20064, 23219, 21916, 38115, 27927, 31995, 38553, 25103, 32454, 30606, 34430, 21283, 38686, 36758, 26247, 23777, 20384, 29421, 19979, 21414, 22799, 21523, 25472, 38184, 20808, 20185, 40092, 32420, 21688, 36132, 34900, 33335, 38386, 28046, 24358, 23244, 26174, 38505, 29616, 29486, 21439, 33146, 39301, 32673, 23466, 38519, 38480, 32447, 30456, 21410, 38262, 39321, 31665, 35140, 28248, 20065, 32724, 31077, 35814, 24819, 21709, 20139, 39033, 24055, 27233, 20687, 21521, 35937, 33831, 30813, 38660, 21066, 21742, 22179, 38144, 28040, 23477, 28102, 26195, 34852, 34853, 34854, 34855, 34856, 34857, 34858, 34859, 34860, 34861, 34862, 34863, 34864, 34865, 34867, 34868, 34869, 34870, 34871, 34872, 34874, 34875, 34877, 34878, 34879, 34881, 34882, 34883, 34886, 34887, 34888, 34889, 34890, 34891, 34894, 34895, 34896, 34897, 34898, 34899, 34901, 34902, 34904, 34906, 34907, 34908, 34909, 34910, 34911, 34912, 34918, 34919, 34922, 34925, 34927, 34929, 34931, 34932, 34933, 34934, 34936, 34937, 34938, 34939, 34940, 34944, 34947, 34950, 34951, 34953, 34954, 34956, 34958, 34959, 34960, 34961, 34963, 34964, 34965, 34967, 34968, 34969, 34970, 34971, 34973, 34974, 34975, 34976, 34977, 34979, 34981, 34982, 34983, 34984, 34985, 34986, 23567, 23389, 26657, 32918, 21880, 31505, 25928, 26964, 20123, 27463, 34638, 38795, 21327, 25375, 25658, 37034, 26012, 32961, 35856, 20889, 26800, 21368, 34809, 25032, 27844, 27899, 35874, 23633, 34218, 33455, 38156, 27427, 36763, 26032, 24571, 24515, 20449, 34885, 26143, 33125, 29481, 24826, 20852, 21009, 22411, 24418, 37026, 34892, 37266, 24184, 26447, 24615, 22995, 20804, 20982, 33016, 21256, 27769, 38596, 29066, 20241, 20462, 32670, 26429, 21957, 38152, 31168, 34966, 32483, 22687, 25100, 38656, 34394, 22040, 39035, 24464, 35768, 33988, 37207, 21465, 26093, 24207, 30044, 24676, 32110, 23167, 32490, 32493, 36713, 21927, 23459, 24748, 26059, 29572, 34988, 34990, 34991, 34992, 34994, 34995, 34996, 34997, 34998, 35000, 35001, 35002, 35003, 35005, 35006, 35007, 35008, 35011, 35012, 35015, 35016, 35018, 35019, 35020, 35021, 35023, 35024, 35025, 35027, 35030, 35031, 35034, 35035, 35036, 35037, 35038, 35040, 35041, 35046, 35047, 35049, 35050, 35051, 35052, 35053, 35054, 35055, 35058, 35061, 35062, 35063, 35066, 35067, 35069, 35071, 35072, 35073, 35075, 35076, 35077, 35078, 35079, 35080, 35081, 35083, 35084, 35085, 35086, 35087, 35089, 35092, 35093, 35094, 35095, 35096, 35100, 35101, 35102, 35103, 35104, 35106, 35107, 35108, 35110, 35111, 35112, 35113, 35116, 35117, 35118, 35119, 35121, 35122, 35123, 35125, 35127, 36873, 30307, 30505, 32474, 38772, 34203, 23398, 31348, 38634, 34880, 21195, 29071, 24490, 26092, 35810, 23547, 39535, 24033, 27529, 27739, 35757, 35759, 36874, 36805, 21387, 25276, 40486, 40493, 21568, 20011, 33469, 29273, 34460, 23830, 34905, 28079, 38597, 21713, 20122, 35766, 28937, 21693, 38409, 28895, 28153, 30416, 20005, 30740, 34578, 23721, 24310, 35328, 39068, 38414, 28814, 27839, 22852, 25513, 30524, 34893, 28436, 33395, 22576, 29141, 21388, 30746, 38593, 21761, 24422, 28976, 23476, 35866, 39564, 27523, 22830, 40495, 31207, 26472, 25196, 20335, 30113, 32650, 27915, 38451, 27687, 20208, 30162, 20859, 26679, 28478, 36992, 33136, 22934, 29814, 35128, 35129, 35130, 35131, 35132, 35133, 35134, 35135, 35136, 35138, 35139, 35141, 35142, 35143, 35144, 35145, 35146, 35147, 35148, 35149, 35150, 35151, 35152, 35153, 35154, 35155, 35156, 35157, 35158, 35159, 35160, 35161, 35162, 35163, 35164, 35165, 35168, 35169, 35170, 35171, 35172, 35173, 35175, 35176, 35177, 35178, 35179, 35180, 35181, 35182, 35183, 35184, 35185, 35186, 35187, 35188, 35189, 35190, 35191, 35192, 35193, 35194, 35196, 35197, 35198, 35200, 35202, 35204, 35205, 35207, 35208, 35209, 35210, 35211, 35212, 35213, 35214, 35215, 35216, 35217, 35218, 35219, 35220, 35221, 35222, 35223, 35224, 35225, 35226, 35227, 35228, 35229, 35230, 35231, 35232, 35233, 25671, 23591, 36965, 31377, 35875, 23002, 21676, 33280, 33647, 35201, 32768, 26928, 22094, 32822, 29239, 37326, 20918, 20063, 39029, 25494, 19994, 21494, 26355, 33099, 22812, 28082, 19968, 22777, 21307, 25558, 38129, 20381, 20234, 34915, 39056, 22839, 36951, 31227, 20202, 33008, 30097, 27778, 23452, 23016, 24413, 26885, 34433, 20506, 24050, 20057, 30691, 20197, 33402, 25233, 26131, 37009, 23673, 20159, 24441, 33222, 36920, 32900, 30123, 20134, 35028, 24847, 27589, 24518, 20041, 30410, 28322, 35811, 35758, 35850, 35793, 24322, 32764, 32716, 32462, 33589, 33643, 22240, 27575, 38899, 38452, 23035, 21535, 38134, 28139, 23493, 39278, 23609, 24341, 38544, 35234, 35235, 35236, 35237, 35238, 35239, 35240, 35241, 35242, 35243, 35244, 35245, 35246, 35247, 35248, 35249, 35250, 35251, 35252, 35253, 35254, 35255, 35256, 35257, 35258, 35259, 35260, 35261, 35262, 35263, 35264, 35267, 35277, 35283, 35284, 35285, 35287, 35288, 35289, 35291, 35293, 35295, 35296, 35297, 35298, 35300, 35303, 35304, 35305, 35306, 35308, 35309, 35310, 35312, 35313, 35314, 35316, 35317, 35318, 35319, 35320, 35321, 35322, 35323, 35324, 35325, 35326, 35327, 35329, 35330, 35331, 35332, 35333, 35334, 35336, 35337, 35338, 35339, 35340, 35341, 35342, 35343, 35344, 35345, 35346, 35347, 35348, 35349, 35350, 35351, 35352, 35353, 35354, 35355, 35356, 35357, 21360, 33521, 27185, 23156, 40560, 24212, 32552, 33721, 33828, 33829, 33639, 34631, 36814, 36194, 30408, 24433, 39062, 30828, 26144, 21727, 25317, 20323, 33219, 30152, 24248, 38605, 36362, 34553, 21647, 27891, 28044, 27704, 24703, 21191, 29992, 24189, 20248, 24736, 24551, 23588, 30001, 37038, 38080, 29369, 27833, 28216, 37193, 26377, 21451, 21491, 20305, 37321, 35825, 21448, 24188, 36802, 28132, 20110, 30402, 27014, 34398, 24858, 33286, 20313, 20446, 36926, 40060, 24841, 28189, 28180, 38533, 20104, 23089, 38632, 19982, 23679, 31161, 23431, 35821, 32701, 29577, 22495, 33419, 37057, 21505, 36935, 21947, 23786, 24481, 24840, 27442, 29425, 32946, 35465, 35358, 35359, 35360, 35361, 35362, 35363, 35364, 35365, 35366, 35367, 35368, 35369, 35370, 35371, 35372, 35373, 35374, 35375, 35376, 35377, 35378, 35379, 35380, 35381, 35382, 35383, 35384, 35385, 35386, 35387, 35388, 35389, 35391, 35392, 35393, 35394, 35395, 35396, 35397, 35398, 35399, 35401, 35402, 35403, 35404, 35405, 35406, 35407, 35408, 35409, 35410, 35411, 35412, 35413, 35414, 35415, 35416, 35417, 35418, 35419, 35420, 35421, 35422, 35423, 35424, 35425, 35426, 35427, 35428, 35429, 35430, 35431, 35432, 35433, 35434, 35435, 35436, 35437, 35438, 35439, 35440, 35441, 35442, 35443, 35444, 35445, 35446, 35447, 35448, 35450, 35451, 35452, 35453, 35454, 35455, 35456, 28020, 23507, 35029, 39044, 35947, 39533, 40499, 28170, 20900, 20803, 22435, 34945, 21407, 25588, 36757, 22253, 21592, 22278, 29503, 28304, 32536, 36828, 33489, 24895, 24616, 38498, 26352, 32422, 36234, 36291, 38053, 23731, 31908, 26376, 24742, 38405, 32792, 20113, 37095, 21248, 38504, 20801, 36816, 34164, 37213, 26197, 38901, 23381, 21277, 30776, 26434, 26685, 21705, 28798, 23472, 36733, 20877, 22312, 21681, 25874, 26242, 36190, 36163, 33039, 33900, 36973, 31967, 20991, 34299, 26531, 26089, 28577, 34468, 36481, 22122, 36896, 30338, 28790, 29157, 36131, 25321, 21017, 27901, 36156, 24590, 22686, 24974, 26366, 36192, 25166, 21939, 28195, 26413, 36711, 35457, 35458, 35459, 35460, 35461, 35462, 35463, 35464, 35467, 35468, 35469, 35470, 35471, 35472, 35473, 35474, 35476, 35477, 35478, 35479, 35480, 35481, 35482, 35483, 35484, 35485, 35486, 35487, 35488, 35489, 35490, 35491, 35492, 35493, 35494, 35495, 35496, 35497, 35498, 35499, 35500, 35501, 35502, 35503, 35504, 35505, 35506, 35507, 35508, 35509, 35510, 35511, 35512, 35513, 35514, 35515, 35516, 35517, 35518, 35519, 35520, 35521, 35522, 35523, 35524, 35525, 35526, 35527, 35528, 35529, 35530, 35531, 35532, 35533, 35534, 35535, 35536, 35537, 35538, 35539, 35540, 35541, 35542, 35543, 35544, 35545, 35546, 35547, 35548, 35549, 35550, 35551, 35552, 35553, 35554, 35555, 38113, 38392, 30504, 26629, 27048, 21643, 20045, 28856, 35784, 25688, 25995, 23429, 31364, 20538, 23528, 30651, 27617, 35449, 31896, 27838, 30415, 26025, 36759, 23853, 23637, 34360, 26632, 21344, 25112, 31449, 28251, 32509, 27167, 31456, 24432, 28467, 24352, 25484, 28072, 26454, 19976, 24080, 36134, 20183, 32960, 30260, 38556, 25307, 26157, 25214, 27836, 36213, 29031, 32617, 20806, 32903, 21484, 36974, 25240, 21746, 34544, 36761, 32773, 38167, 34071, 36825, 27993, 29645, 26015, 30495, 29956, 30759, 33275, 36126, 38024, 20390, 26517, 30137, 35786, 38663, 25391, 38215, 38453, 33976, 25379, 30529, 24449, 29424, 20105, 24596, 25972, 25327, 27491, 25919, 35556, 35557, 35558, 35559, 35560, 35561, 35562, 35563, 35564, 35565, 35566, 35567, 35568, 35569, 35570, 35571, 35572, 35573, 35574, 35575, 35576, 35577, 35578, 35579, 35580, 35581, 35582, 35583, 35584, 35585, 35586, 35587, 35588, 35589, 35590, 35592, 35593, 35594, 35595, 35596, 35597, 35598, 35599, 35600, 35601, 35602, 35603, 35604, 35605, 35606, 35607, 35608, 35609, 35610, 35611, 35612, 35613, 35614, 35615, 35616, 35617, 35618, 35619, 35620, 35621, 35623, 35624, 35625, 35626, 35627, 35628, 35629, 35630, 35631, 35632, 35633, 35634, 35635, 35636, 35637, 35638, 35639, 35640, 35641, 35642, 35643, 35644, 35645, 35646, 35647, 35648, 35649, 35650, 35651, 35652, 35653, 24103, 30151, 37073, 35777, 33437, 26525, 25903, 21553, 34584, 30693, 32930, 33026, 27713, 20043, 32455, 32844, 30452, 26893, 27542, 25191, 20540, 20356, 22336, 25351, 27490, 36286, 21482, 26088, 32440, 24535, 25370, 25527, 33267, 33268, 32622, 24092, 23769, 21046, 26234, 31209, 31258, 36136, 28825, 30164, 28382, 27835, 31378, 20013, 30405, 24544, 38047, 34935, 32456, 31181, 32959, 37325, 20210, 20247, 33311, 21608, 24030, 27954, 35788, 31909, 36724, 32920, 24090, 21650, 30385, 23449, 26172, 39588, 29664, 26666, 34523, 26417, 29482, 35832, 35803, 36880, 31481, 28891, 29038, 25284, 30633, 22065, 20027, 33879, 26609, 21161, 34496, 36142, 38136, 31569, 35654, 35655, 35656, 35657, 35658, 35659, 35660, 35661, 35662, 35663, 35664, 35665, 35666, 35667, 35668, 35669, 35670, 35671, 35672, 35673, 35674, 35675, 35676, 35677, 35678, 35679, 35680, 35681, 35682, 35683, 35684, 35685, 35687, 35688, 35689, 35690, 35691, 35693, 35694, 35695, 35696, 35697, 35698, 35699, 35700, 35701, 35702, 35703, 35704, 35705, 35706, 35707, 35708, 35709, 35710, 35711, 35712, 35713, 35714, 35715, 35716, 35717, 35718, 35719, 35720, 35721, 35722, 35723, 35724, 35725, 35726, 35727, 35728, 35729, 35730, 35731, 35732, 35733, 35734, 35735, 35736, 35737, 35738, 35739, 35740, 35741, 35742, 35743, 35756, 35761, 35771, 35783, 35792, 35818, 35849, 35870, 20303, 27880, 31069, 39547, 25235, 29226, 25341, 19987, 30742, 36716, 25776, 36186, 31686, 26729, 24196, 35013, 22918, 25758, 22766, 29366, 26894, 38181, 36861, 36184, 22368, 32512, 35846, 20934, 25417, 25305, 21331, 26700, 29730, 33537, 37196, 21828, 30528, 28796, 27978, 20857, 21672, 36164, 23039, 28363, 28100, 23388, 32043, 20180, 31869, 28371, 23376, 33258, 28173, 23383, 39683, 26837, 36394, 23447, 32508, 24635, 32437, 37049, 36208, 22863, 25549, 31199, 36275, 21330, 26063, 31062, 35781, 38459, 32452, 38075, 32386, 22068, 37257, 26368, 32618, 23562, 36981, 26152, 24038, 20304, 26590, 20570, 20316, 22352, 24231, null, null, null, null, null, 35896, 35897, 35898, 35899, 35900, 35901, 35902, 35903, 35904, 35906, 35907, 35908, 35909, 35912, 35914, 35915, 35917, 35918, 35919, 35920, 35921, 35922, 35923, 35924, 35926, 35927, 35928, 35929, 35931, 35932, 35933, 35934, 35935, 35936, 35939, 35940, 35941, 35942, 35943, 35944, 35945, 35948, 35949, 35950, 35951, 35952, 35953, 35954, 35956, 35957, 35958, 35959, 35963, 35964, 35965, 35966, 35967, 35968, 35969, 35971, 35972, 35974, 35975, 35976, 35979, 35981, 35982, 35983, 35984, 35985, 35986, 35987, 35989, 35990, 35991, 35993, 35994, 35995, 35996, 35997, 35998, 35999, 36000, 36001, 36002, 36003, 36004, 36005, 36006, 36007, 36008, 36009, 36010, 36011, 36012, 36013, 20109, 19980, 20800, 19984, 24319, 21317, 19989, 20120, 19998, 39730, 23404, 22121, 20008, 31162, 20031, 21269, 20039, 22829, 29243, 21358, 27664, 22239, 32996, 39319, 27603, 30590, 40727, 20022, 20127, 40720, 20060, 20073, 20115, 33416, 23387, 21868, 22031, 20164, 21389, 21405, 21411, 21413, 21422, 38757, 36189, 21274, 21493, 21286, 21294, 21310, 36188, 21350, 21347, 20994, 21000, 21006, 21037, 21043, 21055, 21056, 21068, 21086, 21089, 21084, 33967, 21117, 21122, 21121, 21136, 21139, 20866, 32596, 20155, 20163, 20169, 20162, 20200, 20193, 20203, 20190, 20251, 20211, 20258, 20324, 20213, 20261, 20263, 20233, 20267, 20318, 20327, 25912, 20314, 20317, 36014, 36015, 36016, 36017, 36018, 36019, 36020, 36021, 36022, 36023, 36024, 36025, 36026, 36027, 36028, 36029, 36030, 36031, 36032, 36033, 36034, 36035, 36036, 36037, 36038, 36039, 36040, 36041, 36042, 36043, 36044, 36045, 36046, 36047, 36048, 36049, 36050, 36051, 36052, 36053, 36054, 36055, 36056, 36057, 36058, 36059, 36060, 36061, 36062, 36063, 36064, 36065, 36066, 36067, 36068, 36069, 36070, 36071, 36072, 36073, 36074, 36075, 36076, 36077, 36078, 36079, 36080, 36081, 36082, 36083, 36084, 36085, 36086, 36087, 36088, 36089, 36090, 36091, 36092, 36093, 36094, 36095, 36096, 36097, 36098, 36099, 36100, 36101, 36102, 36103, 36104, 36105, 36106, 36107, 36108, 36109, 20319, 20311, 20274, 20285, 20342, 20340, 20369, 20361, 20355, 20367, 20350, 20347, 20394, 20348, 20396, 20372, 20454, 20456, 20458, 20421, 20442, 20451, 20444, 20433, 20447, 20472, 20521, 20556, 20467, 20524, 20495, 20526, 20525, 20478, 20508, 20492, 20517, 20520, 20606, 20547, 20565, 20552, 20558, 20588, 20603, 20645, 20647, 20649, 20666, 20694, 20742, 20717, 20716, 20710, 20718, 20743, 20747, 20189, 27709, 20312, 20325, 20430, 40864, 27718, 31860, 20846, 24061, 40649, 39320, 20865, 22804, 21241, 21261, 35335, 21264, 20971, 22809, 20821, 20128, 20822, 20147, 34926, 34980, 20149, 33044, 35026, 31104, 23348, 34819, 32696, 20907, 20913, 20925, 20924, 36110, 36111, 36112, 36113, 36114, 36115, 36116, 36117, 36118, 36119, 36120, 36121, 36122, 36123, 36124, 36128, 36177, 36178, 36183, 36191, 36197, 36200, 36201, 36202, 36204, 36206, 36207, 36209, 36210, 36216, 36217, 36218, 36219, 36220, 36221, 36222, 36223, 36224, 36226, 36227, 36230, 36231, 36232, 36233, 36236, 36237, 36238, 36239, 36240, 36242, 36243, 36245, 36246, 36247, 36248, 36249, 36250, 36251, 36252, 36253, 36254, 36256, 36257, 36258, 36260, 36261, 36262, 36263, 36264, 36265, 36266, 36267, 36268, 36269, 36270, 36271, 36272, 36274, 36278, 36279, 36281, 36283, 36285, 36288, 36289, 36290, 36293, 36295, 36296, 36297, 36298, 36301, 36304, 36306, 36307, 36308, 20935, 20886, 20898, 20901, 35744, 35750, 35751, 35754, 35764, 35765, 35767, 35778, 35779, 35787, 35791, 35790, 35794, 35795, 35796, 35798, 35800, 35801, 35804, 35807, 35808, 35812, 35816, 35817, 35822, 35824, 35827, 35830, 35833, 35836, 35839, 35840, 35842, 35844, 35847, 35852, 35855, 35857, 35858, 35860, 35861, 35862, 35865, 35867, 35864, 35869, 35871, 35872, 35873, 35877, 35879, 35882, 35883, 35886, 35887, 35890, 35891, 35893, 35894, 21353, 21370, 38429, 38434, 38433, 38449, 38442, 38461, 38460, 38466, 38473, 38484, 38495, 38503, 38508, 38514, 38516, 38536, 38541, 38551, 38576, 37015, 37019, 37021, 37017, 37036, 37025, 37044, 37043, 37046, 37050, 36309, 36312, 36313, 36316, 36320, 36321, 36322, 36325, 36326, 36327, 36329, 36333, 36334, 36336, 36337, 36338, 36340, 36342, 36348, 36350, 36351, 36352, 36353, 36354, 36355, 36356, 36358, 36359, 36360, 36363, 36365, 36366, 36368, 36369, 36370, 36371, 36373, 36374, 36375, 36376, 36377, 36378, 36379, 36380, 36384, 36385, 36388, 36389, 36390, 36391, 36392, 36395, 36397, 36400, 36402, 36403, 36404, 36406, 36407, 36408, 36411, 36412, 36414, 36415, 36419, 36421, 36422, 36428, 36429, 36430, 36431, 36432, 36435, 36436, 36437, 36438, 36439, 36440, 36442, 36443, 36444, 36445, 36446, 36447, 36448, 36449, 36450, 36451, 36452, 36453, 36455, 36456, 36458, 36459, 36462, 36465, 37048, 37040, 37071, 37061, 37054, 37072, 37060, 37063, 37075, 37094, 37090, 37084, 37079, 37083, 37099, 37103, 37118, 37124, 37154, 37150, 37155, 37169, 37167, 37177, 37187, 37190, 21005, 22850, 21154, 21164, 21165, 21182, 21759, 21200, 21206, 21232, 21471, 29166, 30669, 24308, 20981, 20988, 39727, 21430, 24321, 30042, 24047, 22348, 22441, 22433, 22654, 22716, 22725, 22737, 22313, 22316, 22314, 22323, 22329, 22318, 22319, 22364, 22331, 22338, 22377, 22405, 22379, 22406, 22396, 22395, 22376, 22381, 22390, 22387, 22445, 22436, 22412, 22450, 22479, 22439, 22452, 22419, 22432, 22485, 22488, 22490, 22489, 22482, 22456, 22516, 22511, 22520, 22500, 22493, 36467, 36469, 36471, 36472, 36473, 36474, 36475, 36477, 36478, 36480, 36482, 36483, 36484, 36486, 36488, 36489, 36490, 36491, 36492, 36493, 36494, 36497, 36498, 36499, 36501, 36502, 36503, 36504, 36505, 36506, 36507, 36509, 36511, 36512, 36513, 36514, 36515, 36516, 36517, 36518, 36519, 36520, 36521, 36522, 36525, 36526, 36528, 36529, 36531, 36532, 36533, 36534, 36535, 36536, 36537, 36539, 36540, 36541, 36542, 36543, 36544, 36545, 36546, 36547, 36548, 36549, 36550, 36551, 36552, 36553, 36554, 36555, 36556, 36557, 36559, 36560, 36561, 36562, 36563, 36564, 36565, 36566, 36567, 36568, 36569, 36570, 36571, 36572, 36573, 36574, 36575, 36576, 36577, 36578, 36579, 36580, 22539, 22541, 22525, 22509, 22528, 22558, 22553, 22596, 22560, 22629, 22636, 22657, 22665, 22682, 22656, 39336, 40729, 25087, 33401, 33405, 33407, 33423, 33418, 33448, 33412, 33422, 33425, 33431, 33433, 33451, 33464, 33470, 33456, 33480, 33482, 33507, 33432, 33463, 33454, 33483, 33484, 33473, 33449, 33460, 33441, 33450, 33439, 33476, 33486, 33444, 33505, 33545, 33527, 33508, 33551, 33543, 33500, 33524, 33490, 33496, 33548, 33531, 33491, 33553, 33562, 33542, 33556, 33557, 33504, 33493, 33564, 33617, 33627, 33628, 33544, 33682, 33596, 33588, 33585, 33691, 33630, 33583, 33615, 33607, 33603, 33631, 33600, 33559, 33632, 33581, 33594, 33587, 33638, 33637, 36581, 36582, 36583, 36584, 36585, 36586, 36587, 36588, 36589, 36590, 36591, 36592, 36593, 36594, 36595, 36596, 36597, 36598, 36599, 36600, 36601, 36602, 36603, 36604, 36605, 36606, 36607, 36608, 36609, 36610, 36611, 36612, 36613, 36614, 36615, 36616, 36617, 36618, 36619, 36620, 36621, 36622, 36623, 36624, 36625, 36626, 36627, 36628, 36629, 36630, 36631, 36632, 36633, 36634, 36635, 36636, 36637, 36638, 36639, 36640, 36641, 36642, 36643, 36644, 36645, 36646, 36647, 36648, 36649, 36650, 36651, 36652, 36653, 36654, 36655, 36656, 36657, 36658, 36659, 36660, 36661, 36662, 36663, 36664, 36665, 36666, 36667, 36668, 36669, 36670, 36671, 36672, 36673, 36674, 36675, 36676, 33640, 33563, 33641, 33644, 33642, 33645, 33646, 33712, 33656, 33715, 33716, 33696, 33706, 33683, 33692, 33669, 33660, 33718, 33705, 33661, 33720, 33659, 33688, 33694, 33704, 33722, 33724, 33729, 33793, 33765, 33752, 22535, 33816, 33803, 33757, 33789, 33750, 33820, 33848, 33809, 33798, 33748, 33759, 33807, 33795, 33784, 33785, 33770, 33733, 33728, 33830, 33776, 33761, 33884, 33873, 33882, 33881, 33907, 33927, 33928, 33914, 33929, 33912, 33852, 33862, 33897, 33910, 33932, 33934, 33841, 33901, 33985, 33997, 34000, 34022, 33981, 34003, 33994, 33983, 33978, 34016, 33953, 33977, 33972, 33943, 34021, 34019, 34060, 29965, 34104, 34032, 34105, 34079, 34106, 36677, 36678, 36679, 36680, 36681, 36682, 36683, 36684, 36685, 36686, 36687, 36688, 36689, 36690, 36691, 36692, 36693, 36694, 36695, 36696, 36697, 36698, 36699, 36700, 36701, 36702, 36703, 36704, 36705, 36706, 36707, 36708, 36709, 36714, 36736, 36748, 36754, 36765, 36768, 36769, 36770, 36772, 36773, 36774, 36775, 36778, 36780, 36781, 36782, 36783, 36786, 36787, 36788, 36789, 36791, 36792, 36794, 36795, 36796, 36799, 36800, 36803, 36806, 36809, 36810, 36811, 36812, 36813, 36815, 36818, 36822, 36823, 36826, 36832, 36833, 36835, 36839, 36844, 36847, 36849, 36850, 36852, 36853, 36854, 36858, 36859, 36860, 36862, 36863, 36871, 36872, 36876, 36878, 36883, 36885, 36888, 34134, 34107, 34047, 34044, 34137, 34120, 34152, 34148, 34142, 34170, 30626, 34115, 34162, 34171, 34212, 34216, 34183, 34191, 34169, 34222, 34204, 34181, 34233, 34231, 34224, 34259, 34241, 34268, 34303, 34343, 34309, 34345, 34326, 34364, 24318, 24328, 22844, 22849, 32823, 22869, 22874, 22872, 21263, 23586, 23589, 23596, 23604, 25164, 25194, 25247, 25275, 25290, 25306, 25303, 25326, 25378, 25334, 25401, 25419, 25411, 25517, 25590, 25457, 25466, 25486, 25524, 25453, 25516, 25482, 25449, 25518, 25532, 25586, 25592, 25568, 25599, 25540, 25566, 25550, 25682, 25542, 25534, 25669, 25665, 25611, 25627, 25632, 25612, 25638, 25633, 25694, 25732, 25709, 25750, 36889, 36892, 36899, 36900, 36901, 36903, 36904, 36905, 36906, 36907, 36908, 36912, 36913, 36914, 36915, 36916, 36919, 36921, 36922, 36925, 36927, 36928, 36931, 36933, 36934, 36936, 36937, 36938, 36939, 36940, 36942, 36948, 36949, 36950, 36953, 36954, 36956, 36957, 36958, 36959, 36960, 36961, 36964, 36966, 36967, 36969, 36970, 36971, 36972, 36975, 36976, 36977, 36978, 36979, 36982, 36983, 36984, 36985, 36986, 36987, 36988, 36990, 36993, 36996, 36997, 36998, 36999, 37001, 37002, 37004, 37005, 37006, 37007, 37008, 37010, 37012, 37014, 37016, 37018, 37020, 37022, 37023, 37024, 37028, 37029, 37031, 37032, 37033, 37035, 37037, 37042, 37047, 37052, 37053, 37055, 37056, 25722, 25783, 25784, 25753, 25786, 25792, 25808, 25815, 25828, 25826, 25865, 25893, 25902, 24331, 24530, 29977, 24337, 21343, 21489, 21501, 21481, 21480, 21499, 21522, 21526, 21510, 21579, 21586, 21587, 21588, 21590, 21571, 21537, 21591, 21593, 21539, 21554, 21634, 21652, 21623, 21617, 21604, 21658, 21659, 21636, 21622, 21606, 21661, 21712, 21677, 21698, 21684, 21714, 21671, 21670, 21715, 21716, 21618, 21667, 21717, 21691, 21695, 21708, 21721, 21722, 21724, 21673, 21674, 21668, 21725, 21711, 21726, 21787, 21735, 21792, 21757, 21780, 21747, 21794, 21795, 21775, 21777, 21799, 21802, 21863, 21903, 21941, 21833, 21869, 21825, 21845, 21823, 21840, 21820, 37058, 37059, 37062, 37064, 37065, 37067, 37068, 37069, 37074, 37076, 37077, 37078, 37080, 37081, 37082, 37086, 37087, 37088, 37091, 37092, 37093, 37097, 37098, 37100, 37102, 37104, 37105, 37106, 37107, 37109, 37110, 37111, 37113, 37114, 37115, 37116, 37119, 37120, 37121, 37123, 37125, 37126, 37127, 37128, 37129, 37130, 37131, 37132, 37133, 37134, 37135, 37136, 37137, 37138, 37139, 37140, 37141, 37142, 37143, 37144, 37146, 37147, 37148, 37149, 37151, 37152, 37153, 37156, 37157, 37158, 37159, 37160, 37161, 37162, 37163, 37164, 37165, 37166, 37168, 37170, 37171, 37172, 37173, 37174, 37175, 37176, 37178, 37179, 37180, 37181, 37182, 37183, 37184, 37185, 37186, 37188, 21815, 21846, 21877, 21878, 21879, 21811, 21808, 21852, 21899, 21970, 21891, 21937, 21945, 21896, 21889, 21919, 21886, 21974, 21905, 21883, 21983, 21949, 21950, 21908, 21913, 21994, 22007, 21961, 22047, 21969, 21995, 21996, 21972, 21990, 21981, 21956, 21999, 21989, 22002, 22003, 21964, 21965, 21992, 22005, 21988, 36756, 22046, 22024, 22028, 22017, 22052, 22051, 22014, 22016, 22055, 22061, 22104, 22073, 22103, 22060, 22093, 22114, 22105, 22108, 22092, 22100, 22150, 22116, 22129, 22123, 22139, 22140, 22149, 22163, 22191, 22228, 22231, 22237, 22241, 22261, 22251, 22265, 22271, 22276, 22282, 22281, 22300, 24079, 24089, 24084, 24081, 24113, 24123, 24124, 37189, 37191, 37192, 37201, 37203, 37204, 37205, 37206, 37208, 37209, 37211, 37212, 37215, 37216, 37222, 37223, 37224, 37227, 37229, 37235, 37242, 37243, 37244, 37248, 37249, 37250, 37251, 37252, 37254, 37256, 37258, 37262, 37263, 37267, 37268, 37269, 37270, 37271, 37272, 37273, 37276, 37277, 37278, 37279, 37280, 37281, 37284, 37285, 37286, 37287, 37288, 37289, 37291, 37292, 37296, 37297, 37298, 37299, 37302, 37303, 37304, 37305, 37307, 37308, 37309, 37310, 37311, 37312, 37313, 37314, 37315, 37316, 37317, 37318, 37320, 37323, 37328, 37330, 37331, 37332, 37333, 37334, 37335, 37336, 37337, 37338, 37339, 37341, 37342, 37343, 37344, 37345, 37346, 37347, 37348, 37349, 24119, 24132, 24148, 24155, 24158, 24161, 23692, 23674, 23693, 23696, 23702, 23688, 23704, 23705, 23697, 23706, 23708, 23733, 23714, 23741, 23724, 23723, 23729, 23715, 23745, 23735, 23748, 23762, 23780, 23755, 23781, 23810, 23811, 23847, 23846, 23854, 23844, 23838, 23814, 23835, 23896, 23870, 23860, 23869, 23916, 23899, 23919, 23901, 23915, 23883, 23882, 23913, 23924, 23938, 23961, 23965, 35955, 23991, 24005, 24435, 24439, 24450, 24455, 24457, 24460, 24469, 24473, 24476, 24488, 24493, 24501, 24508, 34914, 24417, 29357, 29360, 29364, 29367, 29368, 29379, 29377, 29390, 29389, 29394, 29416, 29423, 29417, 29426, 29428, 29431, 29441, 29427, 29443, 29434, 37350, 37351, 37352, 37353, 37354, 37355, 37356, 37357, 37358, 37359, 37360, 37361, 37362, 37363, 37364, 37365, 37366, 37367, 37368, 37369, 37370, 37371, 37372, 37373, 37374, 37375, 37376, 37377, 37378, 37379, 37380, 37381, 37382, 37383, 37384, 37385, 37386, 37387, 37388, 37389, 37390, 37391, 37392, 37393, 37394, 37395, 37396, 37397, 37398, 37399, 37400, 37401, 37402, 37403, 37404, 37405, 37406, 37407, 37408, 37409, 37410, 37411, 37412, 37413, 37414, 37415, 37416, 37417, 37418, 37419, 37420, 37421, 37422, 37423, 37424, 37425, 37426, 37427, 37428, 37429, 37430, 37431, 37432, 37433, 37434, 37435, 37436, 37437, 37438, 37439, 37440, 37441, 37442, 37443, 37444, 37445, 29435, 29463, 29459, 29473, 29450, 29470, 29469, 29461, 29474, 29497, 29477, 29484, 29496, 29489, 29520, 29517, 29527, 29536, 29548, 29551, 29566, 33307, 22821, 39143, 22820, 22786, 39267, 39271, 39272, 39273, 39274, 39275, 39276, 39284, 39287, 39293, 39296, 39300, 39303, 39306, 39309, 39312, 39313, 39315, 39316, 39317, 24192, 24209, 24203, 24214, 24229, 24224, 24249, 24245, 24254, 24243, 36179, 24274, 24273, 24283, 24296, 24298, 33210, 24516, 24521, 24534, 24527, 24579, 24558, 24580, 24545, 24548, 24574, 24581, 24582, 24554, 24557, 24568, 24601, 24629, 24614, 24603, 24591, 24589, 24617, 24619, 24586, 24639, 24609, 24696, 24697, 24699, 24698, 24642, 37446, 37447, 37448, 37449, 37450, 37451, 37452, 37453, 37454, 37455, 37456, 37457, 37458, 37459, 37460, 37461, 37462, 37463, 37464, 37465, 37466, 37467, 37468, 37469, 37470, 37471, 37472, 37473, 37474, 37475, 37476, 37477, 37478, 37479, 37480, 37481, 37482, 37483, 37484, 37485, 37486, 37487, 37488, 37489, 37490, 37491, 37493, 37494, 37495, 37496, 37497, 37498, 37499, 37500, 37501, 37502, 37503, 37504, 37505, 37506, 37507, 37508, 37509, 37510, 37511, 37512, 37513, 37514, 37515, 37516, 37517, 37519, 37520, 37521, 37522, 37523, 37524, 37525, 37526, 37527, 37528, 37529, 37530, 37531, 37532, 37533, 37534, 37535, 37536, 37537, 37538, 37539, 37540, 37541, 37542, 37543, 24682, 24701, 24726, 24730, 24749, 24733, 24707, 24722, 24716, 24731, 24812, 24763, 24753, 24797, 24792, 24774, 24794, 24756, 24864, 24870, 24853, 24867, 24820, 24832, 24846, 24875, 24906, 24949, 25004, 24980, 24999, 25015, 25044, 25077, 24541, 38579, 38377, 38379, 38385, 38387, 38389, 38390, 38396, 38398, 38403, 38404, 38406, 38408, 38410, 38411, 38412, 38413, 38415, 38418, 38421, 38422, 38423, 38425, 38426, 20012, 29247, 25109, 27701, 27732, 27740, 27722, 27811, 27781, 27792, 27796, 27788, 27752, 27753, 27764, 27766, 27782, 27817, 27856, 27860, 27821, 27895, 27896, 27889, 27863, 27826, 27872, 27862, 27898, 27883, 27886, 27825, 27859, 27887, 27902, 37544, 37545, 37546, 37547, 37548, 37549, 37551, 37552, 37553, 37554, 37555, 37556, 37557, 37558, 37559, 37560, 37561, 37562, 37563, 37564, 37565, 37566, 37567, 37568, 37569, 37570, 37571, 37572, 37573, 37574, 37575, 37577, 37578, 37579, 37580, 37581, 37582, 37583, 37584, 37585, 37586, 37587, 37588, 37589, 37590, 37591, 37592, 37593, 37594, 37595, 37596, 37597, 37598, 37599, 37600, 37601, 37602, 37603, 37604, 37605, 37606, 37607, 37608, 37609, 37610, 37611, 37612, 37613, 37614, 37615, 37616, 37617, 37618, 37619, 37620, 37621, 37622, 37623, 37624, 37625, 37626, 37627, 37628, 37629, 37630, 37631, 37632, 37633, 37634, 37635, 37636, 37637, 37638, 37639, 37640, 37641, 27961, 27943, 27916, 27971, 27976, 27911, 27908, 27929, 27918, 27947, 27981, 27950, 27957, 27930, 27983, 27986, 27988, 27955, 28049, 28015, 28062, 28064, 27998, 28051, 28052, 27996, 28000, 28028, 28003, 28186, 28103, 28101, 28126, 28174, 28095, 28128, 28177, 28134, 28125, 28121, 28182, 28075, 28172, 28078, 28203, 28270, 28238, 28267, 28338, 28255, 28294, 28243, 28244, 28210, 28197, 28228, 28383, 28337, 28312, 28384, 28461, 28386, 28325, 28327, 28349, 28347, 28343, 28375, 28340, 28367, 28303, 28354, 28319, 28514, 28486, 28487, 28452, 28437, 28409, 28463, 28470, 28491, 28532, 28458, 28425, 28457, 28553, 28557, 28556, 28536, 28530, 28540, 28538, 28625, 37642, 37643, 37644, 37645, 37646, 37647, 37648, 37649, 37650, 37651, 37652, 37653, 37654, 37655, 37656, 37657, 37658, 37659, 37660, 37661, 37662, 37663, 37664, 37665, 37666, 37667, 37668, 37669, 37670, 37671, 37672, 37673, 37674, 37675, 37676, 37677, 37678, 37679, 37680, 37681, 37682, 37683, 37684, 37685, 37686, 37687, 37688, 37689, 37690, 37691, 37692, 37693, 37695, 37696, 37697, 37698, 37699, 37700, 37701, 37702, 37703, 37704, 37705, 37706, 37707, 37708, 37709, 37710, 37711, 37712, 37713, 37714, 37715, 37716, 37717, 37718, 37719, 37720, 37721, 37722, 37723, 37724, 37725, 37726, 37727, 37728, 37729, 37730, 37731, 37732, 37733, 37734, 37735, 37736, 37737, 37739, 28617, 28583, 28601, 28598, 28610, 28641, 28654, 28638, 28640, 28655, 28698, 28707, 28699, 28729, 28725, 28751, 28766, 23424, 23428, 23445, 23443, 23461, 23480, 29999, 39582, 25652, 23524, 23534, 35120, 23536, 36423, 35591, 36790, 36819, 36821, 36837, 36846, 36836, 36841, 36838, 36851, 36840, 36869, 36868, 36875, 36902, 36881, 36877, 36886, 36897, 36917, 36918, 36909, 36911, 36932, 36945, 36946, 36944, 36968, 36952, 36962, 36955, 26297, 36980, 36989, 36994, 37000, 36995, 37003, 24400, 24407, 24406, 24408, 23611, 21675, 23632, 23641, 23409, 23651, 23654, 32700, 24362, 24361, 24365, 33396, 24380, 39739, 23662, 22913, 22915, 22925, 22953, 22954, 22947, 37740, 37741, 37742, 37743, 37744, 37745, 37746, 37747, 37748, 37749, 37750, 37751, 37752, 37753, 37754, 37755, 37756, 37757, 37758, 37759, 37760, 37761, 37762, 37763, 37764, 37765, 37766, 37767, 37768, 37769, 37770, 37771, 37772, 37773, 37774, 37776, 37777, 37778, 37779, 37780, 37781, 37782, 37783, 37784, 37785, 37786, 37787, 37788, 37789, 37790, 37791, 37792, 37793, 37794, 37795, 37796, 37797, 37798, 37799, 37800, 37801, 37802, 37803, 37804, 37805, 37806, 37807, 37808, 37809, 37810, 37811, 37812, 37813, 37814, 37815, 37816, 37817, 37818, 37819, 37820, 37821, 37822, 37823, 37824, 37825, 37826, 37827, 37828, 37829, 37830, 37831, 37832, 37833, 37835, 37836, 37837, 22935, 22986, 22955, 22942, 22948, 22994, 22962, 22959, 22999, 22974, 23045, 23046, 23005, 23048, 23011, 23000, 23033, 23052, 23049, 23090, 23092, 23057, 23075, 23059, 23104, 23143, 23114, 23125, 23100, 23138, 23157, 33004, 23210, 23195, 23159, 23162, 23230, 23275, 23218, 23250, 23252, 23224, 23264, 23267, 23281, 23254, 23270, 23256, 23260, 23305, 23319, 23318, 23346, 23351, 23360, 23573, 23580, 23386, 23397, 23411, 23377, 23379, 23394, 39541, 39543, 39544, 39546, 39551, 39549, 39552, 39553, 39557, 39560, 39562, 39568, 39570, 39571, 39574, 39576, 39579, 39580, 39581, 39583, 39584, 39586, 39587, 39589, 39591, 32415, 32417, 32419, 32421, 32424, 32425, 37838, 37839, 37840, 37841, 37842, 37843, 37844, 37845, 37847, 37848, 37849, 37850, 37851, 37852, 37853, 37854, 37855, 37856, 37857, 37858, 37859, 37860, 37861, 37862, 37863, 37864, 37865, 37866, 37867, 37868, 37869, 37870, 37871, 37872, 37873, 37874, 37875, 37876, 37877, 37878, 37879, 37880, 37881, 37882, 37883, 37884, 37885, 37886, 37887, 37888, 37889, 37890, 37891, 37892, 37893, 37894, 37895, 37896, 37897, 37898, 37899, 37900, 37901, 37902, 37903, 37904, 37905, 37906, 37907, 37908, 37909, 37910, 37911, 37912, 37913, 37914, 37915, 37916, 37917, 37918, 37919, 37920, 37921, 37922, 37923, 37924, 37925, 37926, 37927, 37928, 37929, 37930, 37931, 37932, 37933, 37934, 32429, 32432, 32446, 32448, 32449, 32450, 32457, 32459, 32460, 32464, 32468, 32471, 32475, 32480, 32481, 32488, 32491, 32494, 32495, 32497, 32498, 32525, 32502, 32506, 32507, 32510, 32513, 32514, 32515, 32519, 32520, 32523, 32524, 32527, 32529, 32530, 32535, 32537, 32540, 32539, 32543, 32545, 32546, 32547, 32548, 32549, 32550, 32551, 32554, 32555, 32556, 32557, 32559, 32560, 32561, 32562, 32563, 32565, 24186, 30079, 24027, 30014, 37013, 29582, 29585, 29614, 29602, 29599, 29647, 29634, 29649, 29623, 29619, 29632, 29641, 29640, 29669, 29657, 39036, 29706, 29673, 29671, 29662, 29626, 29682, 29711, 29738, 29787, 29734, 29733, 29736, 29744, 29742, 29740, 37935, 37936, 37937, 37938, 37939, 37940, 37941, 37942, 37943, 37944, 37945, 37946, 37947, 37948, 37949, 37951, 37952, 37953, 37954, 37955, 37956, 37957, 37958, 37959, 37960, 37961, 37962, 37963, 37964, 37965, 37966, 37967, 37968, 37969, 37970, 37971, 37972, 37973, 37974, 37975, 37976, 37977, 37978, 37979, 37980, 37981, 37982, 37983, 37984, 37985, 37986, 37987, 37988, 37989, 37990, 37991, 37992, 37993, 37994, 37996, 37997, 37998, 37999, 38000, 38001, 38002, 38003, 38004, 38005, 38006, 38007, 38008, 38009, 38010, 38011, 38012, 38013, 38014, 38015, 38016, 38017, 38018, 38019, 38020, 38033, 38038, 38040, 38087, 38095, 38099, 38100, 38106, 38118, 38139, 38172, 38176, 29723, 29722, 29761, 29788, 29783, 29781, 29785, 29815, 29805, 29822, 29852, 29838, 29824, 29825, 29831, 29835, 29854, 29864, 29865, 29840, 29863, 29906, 29882, 38890, 38891, 38892, 26444, 26451, 26462, 26440, 26473, 26533, 26503, 26474, 26483, 26520, 26535, 26485, 26536, 26526, 26541, 26507, 26487, 26492, 26608, 26633, 26584, 26634, 26601, 26544, 26636, 26585, 26549, 26586, 26547, 26589, 26624, 26563, 26552, 26594, 26638, 26561, 26621, 26674, 26675, 26720, 26721, 26702, 26722, 26692, 26724, 26755, 26653, 26709, 26726, 26689, 26727, 26688, 26686, 26698, 26697, 26665, 26805, 26767, 26740, 26743, 26771, 26731, 26818, 26990, 26876, 26911, 26912, 26873, 38183, 38195, 38205, 38211, 38216, 38219, 38229, 38234, 38240, 38254, 38260, 38261, 38263, 38264, 38265, 38266, 38267, 38268, 38269, 38270, 38272, 38273, 38274, 38275, 38276, 38277, 38278, 38279, 38280, 38281, 38282, 38283, 38284, 38285, 38286, 38287, 38288, 38289, 38290, 38291, 38292, 38293, 38294, 38295, 38296, 38297, 38298, 38299, 38300, 38301, 38302, 38303, 38304, 38305, 38306, 38307, 38308, 38309, 38310, 38311, 38312, 38313, 38314, 38315, 38316, 38317, 38318, 38319, 38320, 38321, 38322, 38323, 38324, 38325, 38326, 38327, 38328, 38329, 38330, 38331, 38332, 38333, 38334, 38335, 38336, 38337, 38338, 38339, 38340, 38341, 38342, 38343, 38344, 38345, 38346, 38347, 26916, 26864, 26891, 26881, 26967, 26851, 26896, 26993, 26937, 26976, 26946, 26973, 27012, 26987, 27008, 27032, 27000, 26932, 27084, 27015, 27016, 27086, 27017, 26982, 26979, 27001, 27035, 27047, 27067, 27051, 27053, 27092, 27057, 27073, 27082, 27103, 27029, 27104, 27021, 27135, 27183, 27117, 27159, 27160, 27237, 27122, 27204, 27198, 27296, 27216, 27227, 27189, 27278, 27257, 27197, 27176, 27224, 27260, 27281, 27280, 27305, 27287, 27307, 29495, 29522, 27521, 27522, 27527, 27524, 27538, 27539, 27533, 27546, 27547, 27553, 27562, 36715, 36717, 36721, 36722, 36723, 36725, 36726, 36728, 36727, 36729, 36730, 36732, 36734, 36737, 36738, 36740, 36743, 36747, 38348, 38349, 38350, 38351, 38352, 38353, 38354, 38355, 38356, 38357, 38358, 38359, 38360, 38361, 38362, 38363, 38364, 38365, 38366, 38367, 38368, 38369, 38370, 38371, 38372, 38373, 38374, 38375, 38380, 38399, 38407, 38419, 38424, 38427, 38430, 38432, 38435, 38436, 38437, 38438, 38439, 38440, 38441, 38443, 38444, 38445, 38447, 38448, 38455, 38456, 38457, 38458, 38462, 38465, 38467, 38474, 38478, 38479, 38481, 38482, 38483, 38486, 38487, 38488, 38489, 38490, 38492, 38493, 38494, 38496, 38499, 38501, 38502, 38507, 38509, 38510, 38511, 38512, 38513, 38515, 38520, 38521, 38522, 38523, 38524, 38525, 38526, 38527, 38528, 38529, 38530, 38531, 38532, 38535, 38537, 38538, 36749, 36750, 36751, 36760, 36762, 36558, 25099, 25111, 25115, 25119, 25122, 25121, 25125, 25124, 25132, 33255, 29935, 29940, 29951, 29967, 29969, 29971, 25908, 26094, 26095, 26096, 26122, 26137, 26482, 26115, 26133, 26112, 28805, 26359, 26141, 26164, 26161, 26166, 26165, 32774, 26207, 26196, 26177, 26191, 26198, 26209, 26199, 26231, 26244, 26252, 26279, 26269, 26302, 26331, 26332, 26342, 26345, 36146, 36147, 36150, 36155, 36157, 36160, 36165, 36166, 36168, 36169, 36167, 36173, 36181, 36185, 35271, 35274, 35275, 35276, 35278, 35279, 35280, 35281, 29294, 29343, 29277, 29286, 29295, 29310, 29311, 29316, 29323, 29325, 29327, 29330, 25352, 25394, 25520, 38540, 38542, 38545, 38546, 38547, 38549, 38550, 38554, 38555, 38557, 38558, 38559, 38560, 38561, 38562, 38563, 38564, 38565, 38566, 38568, 38569, 38570, 38571, 38572, 38573, 38574, 38575, 38577, 38578, 38580, 38581, 38583, 38584, 38586, 38587, 38591, 38594, 38595, 38600, 38602, 38603, 38608, 38609, 38611, 38612, 38614, 38615, 38616, 38617, 38618, 38619, 38620, 38621, 38622, 38623, 38625, 38626, 38627, 38628, 38629, 38630, 38631, 38635, 38636, 38637, 38638, 38640, 38641, 38642, 38644, 38645, 38648, 38650, 38651, 38652, 38653, 38655, 38658, 38659, 38661, 38666, 38667, 38668, 38672, 38673, 38674, 38676, 38677, 38679, 38680, 38681, 38682, 38683, 38685, 38687, 38688, 25663, 25816, 32772, 27626, 27635, 27645, 27637, 27641, 27653, 27655, 27654, 27661, 27669, 27672, 27673, 27674, 27681, 27689, 27684, 27690, 27698, 25909, 25941, 25963, 29261, 29266, 29270, 29232, 34402, 21014, 32927, 32924, 32915, 32956, 26378, 32957, 32945, 32939, 32941, 32948, 32951, 32999, 33000, 33001, 33002, 32987, 32962, 32964, 32985, 32973, 32983, 26384, 32989, 33003, 33009, 33012, 33005, 33037, 33038, 33010, 33020, 26389, 33042, 35930, 33078, 33054, 33068, 33048, 33074, 33096, 33100, 33107, 33140, 33113, 33114, 33137, 33120, 33129, 33148, 33149, 33133, 33127, 22605, 23221, 33160, 33154, 33169, 28373, 33187, 33194, 33228, 26406, 33226, 33211, 38689, 38690, 38691, 38692, 38693, 38694, 38695, 38696, 38697, 38699, 38700, 38702, 38703, 38705, 38707, 38708, 38709, 38710, 38711, 38714, 38715, 38716, 38717, 38719, 38720, 38721, 38722, 38723, 38724, 38725, 38726, 38727, 38728, 38729, 38730, 38731, 38732, 38733, 38734, 38735, 38736, 38737, 38740, 38741, 38743, 38744, 38746, 38748, 38749, 38751, 38755, 38756, 38758, 38759, 38760, 38762, 38763, 38764, 38765, 38766, 38767, 38768, 38769, 38770, 38773, 38775, 38776, 38777, 38778, 38779, 38781, 38782, 38783, 38784, 38785, 38786, 38787, 38788, 38790, 38791, 38792, 38793, 38794, 38796, 38798, 38799, 38800, 38803, 38805, 38806, 38807, 38809, 38810, 38811, 38812, 38813, 33217, 33190, 27428, 27447, 27449, 27459, 27462, 27481, 39121, 39122, 39123, 39125, 39129, 39130, 27571, 24384, 27586, 35315, 26000, 40785, 26003, 26044, 26054, 26052, 26051, 26060, 26062, 26066, 26070, 28800, 28828, 28822, 28829, 28859, 28864, 28855, 28843, 28849, 28904, 28874, 28944, 28947, 28950, 28975, 28977, 29043, 29020, 29032, 28997, 29042, 29002, 29048, 29050, 29080, 29107, 29109, 29096, 29088, 29152, 29140, 29159, 29177, 29213, 29224, 28780, 28952, 29030, 29113, 25150, 25149, 25155, 25160, 25161, 31035, 31040, 31046, 31049, 31067, 31068, 31059, 31066, 31074, 31063, 31072, 31087, 31079, 31098, 31109, 31114, 31130, 31143, 31155, 24529, 24528, 38814, 38815, 38817, 38818, 38820, 38821, 38822, 38823, 38824, 38825, 38826, 38828, 38830, 38832, 38833, 38835, 38837, 38838, 38839, 38840, 38841, 38842, 38843, 38844, 38845, 38846, 38847, 38848, 38849, 38850, 38851, 38852, 38853, 38854, 38855, 38856, 38857, 38858, 38859, 38860, 38861, 38862, 38863, 38864, 38865, 38866, 38867, 38868, 38869, 38870, 38871, 38872, 38873, 38874, 38875, 38876, 38877, 38878, 38879, 38880, 38881, 38882, 38883, 38884, 38885, 38888, 38894, 38895, 38896, 38897, 38898, 38900, 38903, 38904, 38905, 38906, 38907, 38908, 38909, 38910, 38911, 38912, 38913, 38914, 38915, 38916, 38917, 38918, 38919, 38920, 38921, 38922, 38923, 38924, 38925, 38926, 24636, 24669, 24666, 24679, 24641, 24665, 24675, 24747, 24838, 24845, 24925, 25001, 24989, 25035, 25041, 25094, 32896, 32895, 27795, 27894, 28156, 30710, 30712, 30720, 30729, 30743, 30744, 30737, 26027, 30765, 30748, 30749, 30777, 30778, 30779, 30751, 30780, 30757, 30764, 30755, 30761, 30798, 30829, 30806, 30807, 30758, 30800, 30791, 30796, 30826, 30875, 30867, 30874, 30855, 30876, 30881, 30883, 30898, 30905, 30885, 30932, 30937, 30921, 30956, 30962, 30981, 30964, 30995, 31012, 31006, 31028, 40859, 40697, 40699, 40700, 30449, 30468, 30477, 30457, 30471, 30472, 30490, 30498, 30489, 30509, 30502, 30517, 30520, 30544, 30545, 30535, 30531, 30554, 30568, 38927, 38928, 38929, 38930, 38931, 38932, 38933, 38934, 38935, 38936, 38937, 38938, 38939, 38940, 38941, 38942, 38943, 38944, 38945, 38946, 38947, 38948, 38949, 38950, 38951, 38952, 38953, 38954, 38955, 38956, 38957, 38958, 38959, 38960, 38961, 38962, 38963, 38964, 38965, 38966, 38967, 38968, 38969, 38970, 38971, 38972, 38973, 38974, 38975, 38976, 38977, 38978, 38979, 38980, 38981, 38982, 38983, 38984, 38985, 38986, 38987, 38988, 38989, 38990, 38991, 38992, 38993, 38994, 38995, 38996, 38997, 38998, 38999, 39000, 39001, 39002, 39003, 39004, 39005, 39006, 39007, 39008, 39009, 39010, 39011, 39012, 39013, 39014, 39015, 39016, 39017, 39018, 39019, 39020, 39021, 39022, 30562, 30565, 30591, 30605, 30589, 30592, 30604, 30609, 30623, 30624, 30640, 30645, 30653, 30010, 30016, 30030, 30027, 30024, 30043, 30066, 30073, 30083, 32600, 32609, 32607, 35400, 32616, 32628, 32625, 32633, 32641, 32638, 30413, 30437, 34866, 38021, 38022, 38023, 38027, 38026, 38028, 38029, 38031, 38032, 38036, 38039, 38037, 38042, 38043, 38044, 38051, 38052, 38059, 38058, 38061, 38060, 38063, 38064, 38066, 38068, 38070, 38071, 38072, 38073, 38074, 38076, 38077, 38079, 38084, 38088, 38089, 38090, 38091, 38092, 38093, 38094, 38096, 38097, 38098, 38101, 38102, 38103, 38105, 38104, 38107, 38110, 38111, 38112, 38114, 38116, 38117, 38119, 38120, 38122, 39023, 39024, 39025, 39026, 39027, 39028, 39051, 39054, 39058, 39061, 39065, 39075, 39080, 39081, 39082, 39083, 39084, 39085, 39086, 39087, 39088, 39089, 39090, 39091, 39092, 39093, 39094, 39095, 39096, 39097, 39098, 39099, 39100, 39101, 39102, 39103, 39104, 39105, 39106, 39107, 39108, 39109, 39110, 39111, 39112, 39113, 39114, 39115, 39116, 39117, 39119, 39120, 39124, 39126, 39127, 39131, 39132, 39133, 39136, 39137, 39138, 39139, 39140, 39141, 39142, 39145, 39146, 39147, 39148, 39149, 39150, 39151, 39152, 39153, 39154, 39155, 39156, 39157, 39158, 39159, 39160, 39161, 39162, 39163, 39164, 39165, 39166, 39167, 39168, 39169, 39170, 39171, 39172, 39173, 39174, 39175, 38121, 38123, 38126, 38127, 38131, 38132, 38133, 38135, 38137, 38140, 38141, 38143, 38147, 38146, 38150, 38151, 38153, 38154, 38157, 38158, 38159, 38162, 38163, 38164, 38165, 38166, 38168, 38171, 38173, 38174, 38175, 38178, 38186, 38187, 38185, 38188, 38193, 38194, 38196, 38198, 38199, 38200, 38204, 38206, 38207, 38210, 38197, 38212, 38213, 38214, 38217, 38220, 38222, 38223, 38226, 38227, 38228, 38230, 38231, 38232, 38233, 38235, 38238, 38239, 38237, 38241, 38242, 38244, 38245, 38246, 38247, 38248, 38249, 38250, 38251, 38252, 38255, 38257, 38258, 38259, 38202, 30695, 30700, 38601, 31189, 31213, 31203, 31211, 31238, 23879, 31235, 31234, 31262, 31252, 39176, 39177, 39178, 39179, 39180, 39182, 39183, 39185, 39186, 39187, 39188, 39189, 39190, 39191, 39192, 39193, 39194, 39195, 39196, 39197, 39198, 39199, 39200, 39201, 39202, 39203, 39204, 39205, 39206, 39207, 39208, 39209, 39210, 39211, 39212, 39213, 39215, 39216, 39217, 39218, 39219, 39220, 39221, 39222, 39223, 39224, 39225, 39226, 39227, 39228, 39229, 39230, 39231, 39232, 39233, 39234, 39235, 39236, 39237, 39238, 39239, 39240, 39241, 39242, 39243, 39244, 39245, 39246, 39247, 39248, 39249, 39250, 39251, 39254, 39255, 39256, 39257, 39258, 39259, 39260, 39261, 39262, 39263, 39264, 39265, 39266, 39268, 39270, 39283, 39288, 39289, 39291, 39294, 39298, 39299, 39305, 31289, 31287, 31313, 40655, 39333, 31344, 30344, 30350, 30355, 30361, 30372, 29918, 29920, 29996, 40480, 40482, 40488, 40489, 40490, 40491, 40492, 40498, 40497, 40502, 40504, 40503, 40505, 40506, 40510, 40513, 40514, 40516, 40518, 40519, 40520, 40521, 40523, 40524, 40526, 40529, 40533, 40535, 40538, 40539, 40540, 40542, 40547, 40550, 40551, 40552, 40553, 40554, 40555, 40556, 40561, 40557, 40563, 30098, 30100, 30102, 30112, 30109, 30124, 30115, 30131, 30132, 30136, 30148, 30129, 30128, 30147, 30146, 30166, 30157, 30179, 30184, 30182, 30180, 30187, 30183, 30211, 30193, 30204, 30207, 30224, 30208, 30213, 30220, 30231, 30218, 30245, 30232, 30229, 30233, 39308, 39310, 39322, 39323, 39324, 39325, 39326, 39327, 39328, 39329, 39330, 39331, 39332, 39334, 39335, 39337, 39338, 39339, 39340, 39341, 39342, 39343, 39344, 39345, 39346, 39347, 39348, 39349, 39350, 39351, 39352, 39353, 39354, 39355, 39356, 39357, 39358, 39359, 39360, 39361, 39362, 39363, 39364, 39365, 39366, 39367, 39368, 39369, 39370, 39371, 39372, 39373, 39374, 39375, 39376, 39377, 39378, 39379, 39380, 39381, 39382, 39383, 39384, 39385, 39386, 39387, 39388, 39389, 39390, 39391, 39392, 39393, 39394, 39395, 39396, 39397, 39398, 39399, 39400, 39401, 39402, 39403, 39404, 39405, 39406, 39407, 39408, 39409, 39410, 39411, 39412, 39413, 39414, 39415, 39416, 39417, 30235, 30268, 30242, 30240, 30272, 30253, 30256, 30271, 30261, 30275, 30270, 30259, 30285, 30302, 30292, 30300, 30294, 30315, 30319, 32714, 31462, 31352, 31353, 31360, 31366, 31368, 31381, 31398, 31392, 31404, 31400, 31405, 31411, 34916, 34921, 34930, 34941, 34943, 34946, 34978, 35014, 34999, 35004, 35017, 35042, 35022, 35043, 35045, 35057, 35098, 35068, 35048, 35070, 35056, 35105, 35097, 35091, 35099, 35082, 35124, 35115, 35126, 35137, 35174, 35195, 30091, 32997, 30386, 30388, 30684, 32786, 32788, 32790, 32796, 32800, 32802, 32805, 32806, 32807, 32809, 32808, 32817, 32779, 32821, 32835, 32838, 32845, 32850, 32873, 32881, 35203, 39032, 39040, 39043, 39418, 39419, 39420, 39421, 39422, 39423, 39424, 39425, 39426, 39427, 39428, 39429, 39430, 39431, 39432, 39433, 39434, 39435, 39436, 39437, 39438, 39439, 39440, 39441, 39442, 39443, 39444, 39445, 39446, 39447, 39448, 39449, 39450, 39451, 39452, 39453, 39454, 39455, 39456, 39457, 39458, 39459, 39460, 39461, 39462, 39463, 39464, 39465, 39466, 39467, 39468, 39469, 39470, 39471, 39472, 39473, 39474, 39475, 39476, 39477, 39478, 39479, 39480, 39481, 39482, 39483, 39484, 39485, 39486, 39487, 39488, 39489, 39490, 39491, 39492, 39493, 39494, 39495, 39496, 39497, 39498, 39499, 39500, 39501, 39502, 39503, 39504, 39505, 39506, 39507, 39508, 39509, 39510, 39511, 39512, 39513, 39049, 39052, 39053, 39055, 39060, 39066, 39067, 39070, 39071, 39073, 39074, 39077, 39078, 34381, 34388, 34412, 34414, 34431, 34426, 34428, 34427, 34472, 34445, 34443, 34476, 34461, 34471, 34467, 34474, 34451, 34473, 34486, 34500, 34485, 34510, 34480, 34490, 34481, 34479, 34505, 34511, 34484, 34537, 34545, 34546, 34541, 34547, 34512, 34579, 34526, 34548, 34527, 34520, 34513, 34563, 34567, 34552, 34568, 34570, 34573, 34569, 34595, 34619, 34590, 34597, 34606, 34586, 34622, 34632, 34612, 34609, 34601, 34615, 34623, 34690, 34594, 34685, 34686, 34683, 34656, 34672, 34636, 34670, 34699, 34643, 34659, 34684, 34660, 34649, 34661, 34707, 34735, 34728, 34770, 39514, 39515, 39516, 39517, 39518, 39519, 39520, 39521, 39522, 39523, 39524, 39525, 39526, 39527, 39528, 39529, 39530, 39531, 39538, 39555, 39561, 39565, 39566, 39572, 39573, 39577, 39590, 39593, 39594, 39595, 39596, 39597, 39598, 39599, 39602, 39603, 39604, 39605, 39609, 39611, 39613, 39614, 39615, 39619, 39620, 39622, 39623, 39624, 39625, 39626, 39629, 39630, 39631, 39632, 39634, 39636, 39637, 39638, 39639, 39641, 39642, 39643, 39644, 39645, 39646, 39648, 39650, 39651, 39652, 39653, 39655, 39656, 39657, 39658, 39660, 39662, 39664, 39665, 39666, 39667, 39668, 39669, 39670, 39671, 39672, 39674, 39676, 39677, 39678, 39679, 39680, 39681, 39682, 39684, 39685, 39686, 34758, 34696, 34693, 34733, 34711, 34691, 34731, 34789, 34732, 34741, 34739, 34763, 34771, 34749, 34769, 34752, 34762, 34779, 34794, 34784, 34798, 34838, 34835, 34814, 34826, 34843, 34849, 34873, 34876, 32566, 32578, 32580, 32581, 33296, 31482, 31485, 31496, 31491, 31492, 31509, 31498, 31531, 31503, 31559, 31544, 31530, 31513, 31534, 31537, 31520, 31525, 31524, 31539, 31550, 31518, 31576, 31578, 31557, 31605, 31564, 31581, 31584, 31598, 31611, 31586, 31602, 31601, 31632, 31654, 31655, 31672, 31660, 31645, 31656, 31621, 31658, 31644, 31650, 31659, 31668, 31697, 31681, 31692, 31709, 31706, 31717, 31718, 31722, 31756, 31742, 31740, 31759, 31766, 31755, 39687, 39689, 39690, 39691, 39692, 39693, 39694, 39696, 39697, 39698, 39700, 39701, 39702, 39703, 39704, 39705, 39706, 39707, 39708, 39709, 39710, 39712, 39713, 39714, 39716, 39717, 39718, 39719, 39720, 39721, 39722, 39723, 39724, 39725, 39726, 39728, 39729, 39731, 39732, 39733, 39734, 39735, 39736, 39737, 39738, 39741, 39742, 39743, 39744, 39750, 39754, 39755, 39756, 39758, 39760, 39762, 39763, 39765, 39766, 39767, 39768, 39769, 39770, 39771, 39772, 39773, 39774, 39775, 39776, 39777, 39778, 39779, 39780, 39781, 39782, 39783, 39784, 39785, 39786, 39787, 39788, 39789, 39790, 39791, 39792, 39793, 39794, 39795, 39796, 39797, 39798, 39799, 39800, 39801, 39802, 39803, 31775, 31786, 31782, 31800, 31809, 31808, 33278, 33281, 33282, 33284, 33260, 34884, 33313, 33314, 33315, 33325, 33327, 33320, 33323, 33336, 33339, 33331, 33332, 33342, 33348, 33353, 33355, 33359, 33370, 33375, 33384, 34942, 34949, 34952, 35032, 35039, 35166, 32669, 32671, 32679, 32687, 32688, 32690, 31868, 25929, 31889, 31901, 31900, 31902, 31906, 31922, 31932, 31933, 31937, 31943, 31948, 31949, 31944, 31941, 31959, 31976, 33390, 26280, 32703, 32718, 32725, 32741, 32737, 32742, 32745, 32750, 32755, 31992, 32119, 32166, 32174, 32327, 32411, 40632, 40628, 36211, 36228, 36244, 36241, 36273, 36199, 36205, 35911, 35913, 37194, 37200, 37198, 37199, 37220, 39804, 39805, 39806, 39807, 39808, 39809, 39810, 39811, 39812, 39813, 39814, 39815, 39816, 39817, 39818, 39819, 39820, 39821, 39822, 39823, 39824, 39825, 39826, 39827, 39828, 39829, 39830, 39831, 39832, 39833, 39834, 39835, 39836, 39837, 39838, 39839, 39840, 39841, 39842, 39843, 39844, 39845, 39846, 39847, 39848, 39849, 39850, 39851, 39852, 39853, 39854, 39855, 39856, 39857, 39858, 39859, 39860, 39861, 39862, 39863, 39864, 39865, 39866, 39867, 39868, 39869, 39870, 39871, 39872, 39873, 39874, 39875, 39876, 39877, 39878, 39879, 39880, 39881, 39882, 39883, 39884, 39885, 39886, 39887, 39888, 39889, 39890, 39891, 39892, 39893, 39894, 39895, 39896, 39897, 39898, 39899, 37218, 37217, 37232, 37225, 37231, 37245, 37246, 37234, 37236, 37241, 37260, 37253, 37264, 37261, 37265, 37282, 37283, 37290, 37293, 37294, 37295, 37301, 37300, 37306, 35925, 40574, 36280, 36331, 36357, 36441, 36457, 36277, 36287, 36284, 36282, 36292, 36310, 36311, 36314, 36318, 36302, 36303, 36315, 36294, 36332, 36343, 36344, 36323, 36345, 36347, 36324, 36361, 36349, 36372, 36381, 36383, 36396, 36398, 36387, 36399, 36410, 36416, 36409, 36405, 36413, 36401, 36425, 36417, 36418, 36433, 36434, 36426, 36464, 36470, 36476, 36463, 36468, 36485, 36495, 36500, 36496, 36508, 36510, 35960, 35970, 35978, 35973, 35992, 35988, 26011, 35286, 35294, 35290, 35292, 39900, 39901, 39902, 39903, 39904, 39905, 39906, 39907, 39908, 39909, 39910, 39911, 39912, 39913, 39914, 39915, 39916, 39917, 39918, 39919, 39920, 39921, 39922, 39923, 39924, 39925, 39926, 39927, 39928, 39929, 39930, 39931, 39932, 39933, 39934, 39935, 39936, 39937, 39938, 39939, 39940, 39941, 39942, 39943, 39944, 39945, 39946, 39947, 39948, 39949, 39950, 39951, 39952, 39953, 39954, 39955, 39956, 39957, 39958, 39959, 39960, 39961, 39962, 39963, 39964, 39965, 39966, 39967, 39968, 39969, 39970, 39971, 39972, 39973, 39974, 39975, 39976, 39977, 39978, 39979, 39980, 39981, 39982, 39983, 39984, 39985, 39986, 39987, 39988, 39989, 39990, 39991, 39992, 39993, 39994, 39995, 35301, 35307, 35311, 35390, 35622, 38739, 38633, 38643, 38639, 38662, 38657, 38664, 38671, 38670, 38698, 38701, 38704, 38718, 40832, 40835, 40837, 40838, 40839, 40840, 40841, 40842, 40844, 40702, 40715, 40717, 38585, 38588, 38589, 38606, 38610, 30655, 38624, 37518, 37550, 37576, 37694, 37738, 37834, 37775, 37950, 37995, 40063, 40066, 40069, 40070, 40071, 40072, 31267, 40075, 40078, 40080, 40081, 40082, 40084, 40085, 40090, 40091, 40094, 40095, 40096, 40097, 40098, 40099, 40101, 40102, 40103, 40104, 40105, 40107, 40109, 40110, 40112, 40113, 40114, 40115, 40116, 40117, 40118, 40119, 40122, 40123, 40124, 40125, 40132, 40133, 40134, 40135, 40138, 40139, 39996, 39997, 39998, 39999, 40000, 40001, 40002, 40003, 40004, 40005, 40006, 40007, 40008, 40009, 40010, 40011, 40012, 40013, 40014, 40015, 40016, 40017, 40018, 40019, 40020, 40021, 40022, 40023, 40024, 40025, 40026, 40027, 40028, 40029, 40030, 40031, 40032, 40033, 40034, 40035, 40036, 40037, 40038, 40039, 40040, 40041, 40042, 40043, 40044, 40045, 40046, 40047, 40048, 40049, 40050, 40051, 40052, 40053, 40054, 40055, 40056, 40057, 40058, 40059, 40061, 40062, 40064, 40067, 40068, 40073, 40074, 40076, 40079, 40083, 40086, 40087, 40088, 40089, 40093, 40106, 40108, 40111, 40121, 40126, 40127, 40128, 40129, 40130, 40136, 40137, 40145, 40146, 40154, 40155, 40160, 40161, 40140, 40141, 40142, 40143, 40144, 40147, 40148, 40149, 40151, 40152, 40153, 40156, 40157, 40159, 40162, 38780, 38789, 38801, 38802, 38804, 38831, 38827, 38819, 38834, 38836, 39601, 39600, 39607, 40536, 39606, 39610, 39612, 39617, 39616, 39621, 39618, 39627, 39628, 39633, 39749, 39747, 39751, 39753, 39752, 39757, 39761, 39144, 39181, 39214, 39253, 39252, 39647, 39649, 39654, 39663, 39659, 39675, 39661, 39673, 39688, 39695, 39699, 39711, 39715, 40637, 40638, 32315, 40578, 40583, 40584, 40587, 40594, 37846, 40605, 40607, 40667, 40668, 40669, 40672, 40671, 40674, 40681, 40679, 40677, 40682, 40687, 40738, 40748, 40751, 40761, 40759, 40765, 40766, 40772, 40163, 40164, 40165, 40166, 40167, 40168, 40169, 40170, 40171, 40172, 40173, 40174, 40175, 40176, 40177, 40178, 40179, 40180, 40181, 40182, 40183, 40184, 40185, 40186, 40187, 40188, 40189, 40190, 40191, 40192, 40193, 40194, 40195, 40196, 40197, 40198, 40199, 40200, 40201, 40202, 40203, 40204, 40205, 40206, 40207, 40208, 40209, 40210, 40211, 40212, 40213, 40214, 40215, 40216, 40217, 40218, 40219, 40220, 40221, 40222, 40223, 40224, 40225, 40226, 40227, 40228, 40229, 40230, 40231, 40232, 40233, 40234, 40235, 40236, 40237, 40238, 40239, 40240, 40241, 40242, 40243, 40244, 40245, 40246, 40247, 40248, 40249, 40250, 40251, 40252, 40253, 40254, 40255, 40256, 40257, 40258, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 40259, 40260, 40261, 40262, 40263, 40264, 40265, 40266, 40267, 40268, 40269, 40270, 40271, 40272, 40273, 40274, 40275, 40276, 40277, 40278, 40279, 40280, 40281, 40282, 40283, 40284, 40285, 40286, 40287, 40288, 40289, 40290, 40291, 40292, 40293, 40294, 40295, 40296, 40297, 40298, 40299, 40300, 40301, 40302, 40303, 40304, 40305, 40306, 40307, 40308, 40309, 40310, 40311, 40312, 40313, 40314, 40315, 40316, 40317, 40318, 40319, 40320, 40321, 40322, 40323, 40324, 40325, 40326, 40327, 40328, 40329, 40330, 40331, 40332, 40333, 40334, 40335, 40336, 40337, 40338, 40339, 40340, 40341, 40342, 40343, 40344, 40345, 40346, 40347, 40348, 40349, 40350, 40351, 40352, 40353, 40354, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 40355, 40356, 40357, 40358, 40359, 40360, 40361, 40362, 40363, 40364, 40365, 40366, 40367, 40368, 40369, 40370, 40371, 40372, 40373, 40374, 40375, 40376, 40377, 40378, 40379, 40380, 40381, 40382, 40383, 40384, 40385, 40386, 40387, 40388, 40389, 40390, 40391, 40392, 40393, 40394, 40395, 40396, 40397, 40398, 40399, 40400, 40401, 40402, 40403, 40404, 40405, 40406, 40407, 40408, 40409, 40410, 40411, 40412, 40413, 40414, 40415, 40416, 40417, 40418, 40419, 40420, 40421, 40422, 40423, 40424, 40425, 40426, 40427, 40428, 40429, 40430, 40431, 40432, 40433, 40434, 40435, 40436, 40437, 40438, 40439, 40440, 40441, 40442, 40443, 40444, 40445, 40446, 40447, 40448, 40449, 40450, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 40451, 40452, 40453, 40454, 40455, 40456, 40457, 40458, 40459, 40460, 40461, 40462, 40463, 40464, 40465, 40466, 40467, 40468, 40469, 40470, 40471, 40472, 40473, 40474, 40475, 40476, 40477, 40478, 40484, 40487, 40494, 40496, 40500, 40507, 40508, 40512, 40525, 40528, 40530, 40531, 40532, 40534, 40537, 40541, 40543, 40544, 40545, 40546, 40549, 40558, 40559, 40562, 40564, 40565, 40566, 40567, 40568, 40569, 40570, 40571, 40572, 40573, 40576, 40577, 40579, 40580, 40581, 40582, 40585, 40586, 40588, 40589, 40590, 40591, 40592, 40593, 40596, 40597, 40598, 40599, 40600, 40601, 40602, 40603, 40604, 40606, 40608, 40609, 40610, 40611, 40612, 40613, 40615, 40616, 40617, 40618, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 40619, 40620, 40621, 40622, 40623, 40624, 40625, 40626, 40627, 40629, 40630, 40631, 40633, 40634, 40636, 40639, 40640, 40641, 40642, 40643, 40645, 40646, 40647, 40648, 40650, 40651, 40652, 40656, 40658, 40659, 40661, 40662, 40663, 40665, 40666, 40670, 40673, 40675, 40676, 40678, 40680, 40683, 40684, 40685, 40686, 40688, 40689, 40690, 40691, 40692, 40693, 40694, 40695, 40696, 40698, 40701, 40703, 40704, 40705, 40706, 40707, 40708, 40709, 40710, 40711, 40712, 40713, 40714, 40716, 40719, 40721, 40722, 40724, 40725, 40726, 40728, 40730, 40731, 40732, 40733, 40734, 40735, 40737, 40739, 40740, 40741, 40742, 40743, 40744, 40745, 40746, 40747, 40749, 40750, 40752, 40753, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 40754, 40755, 40756, 40757, 40758, 40760, 40762, 40764, 40767, 40768, 40769, 40770, 40771, 40773, 40774, 40775, 40776, 40777, 40778, 40779, 40780, 40781, 40782, 40783, 40786, 40787, 40788, 40789, 40790, 40791, 40792, 40793, 40794, 40795, 40796, 40797, 40798, 40799, 40800, 40801, 40802, 40803, 40804, 40805, 40806, 40807, 40808, 40809, 40810, 40811, 40812, 40813, 40814, 40815, 40816, 40817, 40818, 40819, 40820, 40821, 40822, 40823, 40824, 40825, 40826, 40827, 40828, 40829, 40830, 40833, 40834, 40845, 40846, 40847, 40848, 40849, 40850, 40851, 40852, 40853, 40854, 40855, 40856, 40860, 40861, 40862, 40865, 40866, 40867, 40868, 40869, 63788, 63865, 63893, 63975, 63985, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 64012, 64013, 64014, 64015, 64017, 64019, 64020, 64024, 64031, 64032, 64033, 64035, 64036, 64039, 64040, 64041, 11905, null, null, null, 11908, 13427, 13383, 11912, 11915, null, 13726, 13850, 13838, 11916, 11927, 14702, 14616, null, 14799, 14815, 14963, 14800, null, null, 15182, 15470, 15584, 11943, null, null, 11946, 16470, 16735, 11950, 17207, 11955, 11958, 11959, null, 17329, 17324, 11963, 17373, 17622, 18017, 17996, null, 18211, 18217, 18300, 18317, 11978, 18759, 18810, 18813, 18818, 18819, 18821, 18822, 18847, 18843, 18871, 18870, null, null, 19619, 19615, 19616, 19617, 19575, 19618, 19731, 19732, 19733, 19734, 19735, 19736, 19737, 19886, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], + "gb18030":[[0, 128], [36, 165], [38, 169], [45, 178], [50, 184], [81, 216], [89, 226], [95, 235], [96, 238], [100, 244], [103, 248], [104, 251], [105, 253], [109, 258], [126, 276], [133, 284], [148, 300], [172, 325], [175, 329], [179, 334], [208, 364], [306, 463], [307, 465], [308, 467], [309, 469], [310, 471], [311, 473], [312, 475], [313, 477], [341, 506], [428, 594], [443, 610], [544, 712], [545, 716], [558, 730], [741, 930], [742, 938], [749, 962], [750, 970], [805, 1026], [819, 1104], [820, 1106], [7922, 8209], [7924, 8215], [7925, 8218], [7927, 8222], [7934, 8231], [7943, 8241], [7944, 8244], [7945, 8246], [7950, 8252], [8062, 8365], [8148, 8452], [8149, 8454], [8152, 8458], [8164, 8471], [8174, 8482], [8236, 8556], [8240, 8570], [8262, 8596], [8264, 8602], [8374, 8713], [8380, 8720], [8381, 8722], [8384, 8726], [8388, 8731], [8390, 8737], [8392, 8740], [8393, 8742], [8394, 8748], [8396, 8751], [8401, 8760], [8406, 8766], [8416, 8777], [8419, 8781], [8424, 8787], [8437, 8802], [8439, 8808], [8445, 8816], [8482, 8854], [8485, 8858], [8496, 8870], [8521, 8896], [8603, 8979], [8936, 9322], [8946, 9372], [9046, 9548], [9050, 9588], [9063, 9616], [9066, 9622], [9076, 9634], [9092, 9652], [9100, 9662], [9108, 9672], [9111, 9676], [9113, 9680], [9131, 9702], [9162, 9735], [9164, 9738], [9218, 9793], [9219, 9795], [11329, 11906], [11331, 11909], [11334, 11913], [11336, 11917], [11346, 11928], [11361, 11944], [11363, 11947], [11366, 11951], [11370, 11956], [11372, 11960], [11375, 11964], [11389, 11979], [11682, 12284], [11686, 12292], [11687, 12312], [11692, 12319], [11694, 12330], [11714, 12351], [11716, 12436], [11723, 12447], [11725, 12535], [11730, 12543], [11736, 12586], [11982, 12842], [11989, 12850], [12102, 12964], [12336, 13200], [12348, 13215], [12350, 13218], [12384, 13253], [12393, 13263], [12395, 13267], [12397, 13270], [12510, 13384], [12553, 13428], [12851, 13727], [12962, 13839], [12973, 13851], [13738, 14617], [13823, 14703], [13919, 14801], [13933, 14816], [14080, 14964], [14298, 15183], [14585, 15471], [14698, 15585], [15583, 16471], [15847, 16736], [16318, 17208], [16434, 17325], [16438, 17330], [16481, 17374], [16729, 17623], [17102, 17997], [17122, 18018], [17315, 18212], [17320, 18218], [17402, 18301], [17418, 18318], [17859, 18760], [17909, 18811], [17911, 18814], [17915, 18820], [17916, 18823], [17936, 18844], [17939, 18848], [17961, 18872], [18664, 19576], [18703, 19620], [18814, 19738], [18962, 19887], [19043, 40870], [33469, 59244], [33470, 59336], [33471, 59367], [33484, 59413], [33485, 59417], [33490, 59423], [33497, 59431], [33501, 59437], [33505, 59443], [33513, 59452], [33520, 59460], [33536, 59478], [33550, 59493], [37845, 63789], [37921, 63866], [37948, 63894], [38029, 63976], [38038, 63986], [38064, 64016], [38065, 64018], [38066, 64021], [38069, 64025], [38075, 64034], [38076, 64037], [38078, 64042], [39108, 65074], [39109, 65093], [39113, 65107], [39114, 65112], [39115, 65127], [39116, 65132], [39265, 65375], [39394, 65510], [189000, 65536]], + "jis0208":[12288, 12289, 12290, 65292, 65294, 12539, 65306, 65307, 65311, 65281, 12443, 12444, 180, 65344, 168, 65342, 65507, 65343, 12541, 12542, 12445, 12446, 12291, 20189, 12293, 12294, 12295, 12540, 8213, 8208, 65295, 65340, 65374, 8741, 65372, 8230, 8229, 8216, 8217, 8220, 8221, 65288, 65289, 12308, 12309, 65339, 65341, 65371, 65373, 12296, 12297, 12298, 12299, 12300, 12301, 12302, 12303, 12304, 12305, 65291, 65293, 177, 215, 247, 65309, 8800, 65308, 65310, 8806, 8807, 8734, 8756, 9794, 9792, 176, 8242, 8243, 8451, 65509, 65284, 65504, 65505, 65285, 65283, 65286, 65290, 65312, 167, 9734, 9733, 9675, 9679, 9678, 9671, 9670, 9633, 9632, 9651, 9650, 9661, 9660, 8251, 12306, 8594, 8592, 8593, 8595, 12307, null, null, null, null, null, null, null, null, null, null, null, 8712, 8715, 8838, 8839, 8834, 8835, 8746, 8745, null, null, null, null, null, null, null, null, 8743, 8744, 65506, 8658, 8660, 8704, 8707, null, null, null, null, null, null, null, null, null, null, null, 8736, 8869, 8978, 8706, 8711, 8801, 8786, 8810, 8811, 8730, 8765, 8733, 8757, 8747, 8748, null, null, null, null, null, null, null, 8491, 8240, 9839, 9837, 9834, 8224, 8225, 182, null, null, null, null, 9711, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 65296, 65297, 65298, 65299, 65300, 65301, 65302, 65303, 65304, 65305, null, null, null, null, null, null, null, 65313, 65314, 65315, 65316, 65317, 65318, 65319, 65320, 65321, 65322, 65323, 65324, 65325, 65326, 65327, 65328, 65329, 65330, 65331, 65332, 65333, 65334, 65335, 65336, 65337, 65338, null, null, null, null, null, null, 65345, 65346, 65347, 65348, 65349, 65350, 65351, 65352, 65353, 65354, 65355, 65356, 65357, 65358, 65359, 65360, 65361, 65362, 65363, 65364, 65365, 65366, 65367, 65368, 65369, 65370, null, null, null, null, 12353, 12354, 12355, 12356, 12357, 12358, 12359, 12360, 12361, 12362, 12363, 12364, 12365, 12366, 12367, 12368, 12369, 12370, 12371, 12372, 12373, 12374, 12375, 12376, 12377, 12378, 12379, 12380, 12381, 12382, 12383, 12384, 12385, 12386, 12387, 12388, 12389, 12390, 12391, 12392, 12393, 12394, 12395, 12396, 12397, 12398, 12399, 12400, 12401, 12402, 12403, 12404, 12405, 12406, 12407, 12408, 12409, 12410, 12411, 12412, 12413, 12414, 12415, 12416, 12417, 12418, 12419, 12420, 12421, 12422, 12423, 12424, 12425, 12426, 12427, 12428, 12429, 12430, 12431, 12432, 12433, 12434, 12435, null, null, null, null, null, null, null, null, null, null, null, 12449, 12450, 12451, 12452, 12453, 12454, 12455, 12456, 12457, 12458, 12459, 12460, 12461, 12462, 12463, 12464, 12465, 12466, 12467, 12468, 12469, 12470, 12471, 12472, 12473, 12474, 12475, 12476, 12477, 12478, 12479, 12480, 12481, 12482, 12483, 12484, 12485, 12486, 12487, 12488, 12489, 12490, 12491, 12492, 12493, 12494, 12495, 12496, 12497, 12498, 12499, 12500, 12501, 12502, 12503, 12504, 12505, 12506, 12507, 12508, 12509, 12510, 12511, 12512, 12513, 12514, 12515, 12516, 12517, 12518, 12519, 12520, 12521, 12522, 12523, 12524, 12525, 12526, 12527, 12528, 12529, 12530, 12531, 12532, 12533, 12534, null, null, null, null, null, null, null, null, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 931, 932, 933, 934, 935, 936, 937, null, null, null, null, null, null, null, null, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 963, 964, 965, 966, 967, 968, 969, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 1040, 1041, 1042, 1043, 1044, 1045, 1025, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 1072, 1073, 1074, 1075, 1076, 1077, 1105, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, null, null, null, null, null, null, null, null, null, null, null, null, null, 9472, 9474, 9484, 9488, 9496, 9492, 9500, 9516, 9508, 9524, 9532, 9473, 9475, 9487, 9491, 9499, 9495, 9507, 9523, 9515, 9531, 9547, 9504, 9519, 9512, 9527, 9535, 9501, 9520, 9509, 9528, 9538, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 9312, 9313, 9314, 9315, 9316, 9317, 9318, 9319, 9320, 9321, 9322, 9323, 9324, 9325, 9326, 9327, 9328, 9329, 9330, 9331, 8544, 8545, 8546, 8547, 8548, 8549, 8550, 8551, 8552, 8553, null, 13129, 13076, 13090, 13133, 13080, 13095, 13059, 13110, 13137, 13143, 13069, 13094, 13091, 13099, 13130, 13115, 13212, 13213, 13214, 13198, 13199, 13252, 13217, null, null, null, null, null, null, null, null, 13179, 12317, 12319, 8470, 13261, 8481, 12964, 12965, 12966, 12967, 12968, 12849, 12850, 12857, 13182, 13181, 13180, 8786, 8801, 8747, 8750, 8721, 8730, 8869, 8736, 8735, 8895, 8757, 8745, 8746, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 20124, 21782, 23043, 38463, 21696, 24859, 25384, 23030, 36898, 33909, 33564, 31312, 24746, 25569, 28197, 26093, 33894, 33446, 39925, 26771, 22311, 26017, 25201, 23451, 22992, 34427, 39156, 32098, 32190, 39822, 25110, 31903, 34999, 23433, 24245, 25353, 26263, 26696, 38343, 38797, 26447, 20197, 20234, 20301, 20381, 20553, 22258, 22839, 22996, 23041, 23561, 24799, 24847, 24944, 26131, 26885, 28858, 30031, 30064, 31227, 32173, 32239, 32963, 33806, 34915, 35586, 36949, 36986, 21307, 20117, 20133, 22495, 32946, 37057, 30959, 19968, 22769, 28322, 36920, 31282, 33576, 33419, 39983, 20801, 21360, 21693, 21729, 22240, 23035, 24341, 39154, 28139, 32996, 34093, 38498, 38512, 38560, 38907, 21515, 21491, 23431, 28879, 32701, 36802, 38632, 21359, 40284, 31418, 19985, 30867, 33276, 28198, 22040, 21764, 27421, 34074, 39995, 23013, 21417, 28006, 29916, 38287, 22082, 20113, 36939, 38642, 33615, 39180, 21473, 21942, 23344, 24433, 26144, 26355, 26628, 27704, 27891, 27945, 29787, 30408, 31310, 38964, 33521, 34907, 35424, 37613, 28082, 30123, 30410, 39365, 24742, 35585, 36234, 38322, 27022, 21421, 20870, 22290, 22576, 22852, 23476, 24310, 24616, 25513, 25588, 27839, 28436, 28814, 28948, 29017, 29141, 29503, 32257, 33398, 33489, 34199, 36960, 37467, 40219, 22633, 26044, 27738, 29989, 20985, 22830, 22885, 24448, 24540, 25276, 26106, 27178, 27431, 27572, 29579, 32705, 35158, 40236, 40206, 40644, 23713, 27798, 33659, 20740, 23627, 25014, 33222, 26742, 29281, 20057, 20474, 21368, 24681, 28201, 31311, 38899, 19979, 21270, 20206, 20309, 20285, 20385, 20339, 21152, 21487, 22025, 22799, 23233, 23478, 23521, 31185, 26247, 26524, 26550, 27468, 27827, 28779, 29634, 31117, 31166, 31292, 31623, 33457, 33499, 33540, 33655, 33775, 33747, 34662, 35506, 22057, 36008, 36838, 36942, 38686, 34442, 20420, 23784, 25105, 29273, 30011, 33253, 33469, 34558, 36032, 38597, 39187, 39381, 20171, 20250, 35299, 22238, 22602, 22730, 24315, 24555, 24618, 24724, 24674, 25040, 25106, 25296, 25913, 39745, 26214, 26800, 28023, 28784, 30028, 30342, 32117, 33445, 34809, 38283, 38542, 35997, 20977, 21182, 22806, 21683, 23475, 23830, 24936, 27010, 28079, 30861, 33995, 34903, 35442, 37799, 39608, 28012, 39336, 34521, 22435, 26623, 34510, 37390, 21123, 22151, 21508, 24275, 25313, 25785, 26684, 26680, 27579, 29554, 30906, 31339, 35226, 35282, 36203, 36611, 37101, 38307, 38548, 38761, 23398, 23731, 27005, 38989, 38990, 25499, 31520, 27179, 27263, 26806, 39949, 28511, 21106, 21917, 24688, 25324, 27963, 28167, 28369, 33883, 35088, 36676, 19988, 39993, 21494, 26907, 27194, 38788, 26666, 20828, 31427, 33970, 37340, 37772, 22107, 40232, 26658, 33541, 33841, 31909, 21000, 33477, 29926, 20094, 20355, 20896, 23506, 21002, 21208, 21223, 24059, 21914, 22570, 23014, 23436, 23448, 23515, 24178, 24185, 24739, 24863, 24931, 25022, 25563, 25954, 26577, 26707, 26874, 27454, 27475, 27735, 28450, 28567, 28485, 29872, 29976, 30435, 30475, 31487, 31649, 31777, 32233, 32566, 32752, 32925, 33382, 33694, 35251, 35532, 36011, 36996, 37969, 38291, 38289, 38306, 38501, 38867, 39208, 33304, 20024, 21547, 23736, 24012, 29609, 30284, 30524, 23721, 32747, 36107, 38593, 38929, 38996, 39000, 20225, 20238, 21361, 21916, 22120, 22522, 22855, 23305, 23492, 23696, 24076, 24190, 24524, 25582, 26426, 26071, 26082, 26399, 26827, 26820, 27231, 24112, 27589, 27671, 27773, 30079, 31048, 23395, 31232, 32000, 24509, 35215, 35352, 36020, 36215, 36556, 36637, 39138, 39438, 39740, 20096, 20605, 20736, 22931, 23452, 25135, 25216, 25836, 27450, 29344, 30097, 31047, 32681, 34811, 35516, 35696, 25516, 33738, 38816, 21513, 21507, 21931, 26708, 27224, 35440, 30759, 26485, 40653, 21364, 23458, 33050, 34384, 36870, 19992, 20037, 20167, 20241, 21450, 21560, 23470, 24339, 24613, 25937, 26429, 27714, 27762, 27875, 28792, 29699, 31350, 31406, 31496, 32026, 31998, 32102, 26087, 29275, 21435, 23621, 24040, 25298, 25312, 25369, 28192, 34394, 35377, 36317, 37624, 28417, 31142, 39770, 20136, 20139, 20140, 20379, 20384, 20689, 20807, 31478, 20849, 20982, 21332, 21281, 21375, 21483, 21932, 22659, 23777, 24375, 24394, 24623, 24656, 24685, 25375, 25945, 27211, 27841, 29378, 29421, 30703, 33016, 33029, 33288, 34126, 37111, 37857, 38911, 39255, 39514, 20208, 20957, 23597, 26241, 26989, 23616, 26354, 26997, 29577, 26704, 31873, 20677, 21220, 22343, 24062, 37670, 26020, 27427, 27453, 29748, 31105, 31165, 31563, 32202, 33465, 33740, 34943, 35167, 35641, 36817, 37329, 21535, 37504, 20061, 20534, 21477, 21306, 29399, 29590, 30697, 33510, 36527, 39366, 39368, 39378, 20855, 24858, 34398, 21936, 31354, 20598, 23507, 36935, 38533, 20018, 27355, 37351, 23633, 23624, 25496, 31391, 27795, 38772, 36705, 31402, 29066, 38536, 31874, 26647, 32368, 26705, 37740, 21234, 21531, 34219, 35347, 32676, 36557, 37089, 21350, 34952, 31041, 20418, 20670, 21009, 20804, 21843, 22317, 29674, 22411, 22865, 24418, 24452, 24693, 24950, 24935, 25001, 25522, 25658, 25964, 26223, 26690, 28179, 30054, 31293, 31995, 32076, 32153, 32331, 32619, 33550, 33610, 34509, 35336, 35427, 35686, 36605, 38938, 40335, 33464, 36814, 39912, 21127, 25119, 25731, 28608, 38553, 26689, 20625, 27424, 27770, 28500, 31348, 32080, 34880, 35363, 26376, 20214, 20537, 20518, 20581, 20860, 21048, 21091, 21927, 22287, 22533, 23244, 24314, 25010, 25080, 25331, 25458, 26908, 27177, 29309, 29356, 29486, 30740, 30831, 32121, 30476, 32937, 35211, 35609, 36066, 36562, 36963, 37749, 38522, 38997, 39443, 40568, 20803, 21407, 21427, 24187, 24358, 28187, 28304, 29572, 29694, 32067, 33335, 35328, 35578, 38480, 20046, 20491, 21476, 21628, 22266, 22993, 23396, 24049, 24235, 24359, 25144, 25925, 26543, 28246, 29392, 31946, 34996, 32929, 32993, 33776, 34382, 35463, 36328, 37431, 38599, 39015, 40723, 20116, 20114, 20237, 21320, 21577, 21566, 23087, 24460, 24481, 24735, 26791, 27278, 29786, 30849, 35486, 35492, 35703, 37264, 20062, 39881, 20132, 20348, 20399, 20505, 20502, 20809, 20844, 21151, 21177, 21246, 21402, 21475, 21521, 21518, 21897, 22353, 22434, 22909, 23380, 23389, 23439, 24037, 24039, 24055, 24184, 24195, 24218, 24247, 24344, 24658, 24908, 25239, 25304, 25511, 25915, 26114, 26179, 26356, 26477, 26657, 26775, 27083, 27743, 27946, 28009, 28207, 28317, 30002, 30343, 30828, 31295, 31968, 32005, 32024, 32094, 32177, 32789, 32771, 32943, 32945, 33108, 33167, 33322, 33618, 34892, 34913, 35611, 36002, 36092, 37066, 37237, 37489, 30783, 37628, 38308, 38477, 38917, 39321, 39640, 40251, 21083, 21163, 21495, 21512, 22741, 25335, 28640, 35946, 36703, 40633, 20811, 21051, 21578, 22269, 31296, 37239, 40288, 40658, 29508, 28425, 33136, 29969, 24573, 24794, 39592, 29403, 36796, 27492, 38915, 20170, 22256, 22372, 22718, 23130, 24680, 25031, 26127, 26118, 26681, 26801, 28151, 30165, 32058, 33390, 39746, 20123, 20304, 21449, 21766, 23919, 24038, 24046, 26619, 27801, 29811, 30722, 35408, 37782, 35039, 22352, 24231, 25387, 20661, 20652, 20877, 26368, 21705, 22622, 22971, 23472, 24425, 25165, 25505, 26685, 27507, 28168, 28797, 37319, 29312, 30741, 30758, 31085, 25998, 32048, 33756, 35009, 36617, 38555, 21092, 22312, 26448, 32618, 36001, 20916, 22338, 38442, 22586, 27018, 32948, 21682, 23822, 22524, 30869, 40442, 20316, 21066, 21643, 25662, 26152, 26388, 26613, 31364, 31574, 32034, 37679, 26716, 39853, 31545, 21273, 20874, 21047, 23519, 25334, 25774, 25830, 26413, 27578, 34217, 38609, 30352, 39894, 25420, 37638, 39851, 30399, 26194, 19977, 20632, 21442, 23665, 24808, 25746, 25955, 26719, 29158, 29642, 29987, 31639, 32386, 34453, 35715, 36059, 37240, 39184, 26028, 26283, 27531, 20181, 20180, 20282, 20351, 21050, 21496, 21490, 21987, 22235, 22763, 22987, 22985, 23039, 23376, 23629, 24066, 24107, 24535, 24605, 25351, 25903, 23388, 26031, 26045, 26088, 26525, 27490, 27515, 27663, 29509, 31049, 31169, 31992, 32025, 32043, 32930, 33026, 33267, 35222, 35422, 35433, 35430, 35468, 35566, 36039, 36060, 38604, 39164, 27503, 20107, 20284, 20365, 20816, 23383, 23546, 24904, 25345, 26178, 27425, 28363, 27835, 29246, 29885, 30164, 30913, 31034, 32780, 32819, 33258, 33940, 36766, 27728, 40575, 24335, 35672, 40235, 31482, 36600, 23437, 38635, 19971, 21489, 22519, 22833, 23241, 23460, 24713, 28287, 28422, 30142, 36074, 23455, 34048, 31712, 20594, 26612, 33437, 23649, 34122, 32286, 33294, 20889, 23556, 25448, 36198, 26012, 29038, 31038, 32023, 32773, 35613, 36554, 36974, 34503, 37034, 20511, 21242, 23610, 26451, 28796, 29237, 37196, 37320, 37675, 33509, 23490, 24369, 24825, 20027, 21462, 23432, 25163, 26417, 27530, 29417, 29664, 31278, 33131, 36259, 37202, 39318, 20754, 21463, 21610, 23551, 25480, 27193, 32172, 38656, 22234, 21454, 21608, 23447, 23601, 24030, 20462, 24833, 25342, 27954, 31168, 31179, 32066, 32333, 32722, 33261, 33311, 33936, 34886, 35186, 35728, 36468, 36655, 36913, 37195, 37228, 38598, 37276, 20160, 20303, 20805, 21313, 24467, 25102, 26580, 27713, 28171, 29539, 32294, 37325, 37507, 21460, 22809, 23487, 28113, 31069, 32302, 31899, 22654, 29087, 20986, 34899, 36848, 20426, 23803, 26149, 30636, 31459, 33308, 39423, 20934, 24490, 26092, 26991, 27529, 28147, 28310, 28516, 30462, 32020, 24033, 36981, 37255, 38918, 20966, 21021, 25152, 26257, 26329, 28186, 24246, 32210, 32626, 26360, 34223, 34295, 35576, 21161, 21465, 22899, 24207, 24464, 24661, 37604, 38500, 20663, 20767, 21213, 21280, 21319, 21484, 21736, 21830, 21809, 22039, 22888, 22974, 23100, 23477, 23558, 23567, 23569, 23578, 24196, 24202, 24288, 24432, 25215, 25220, 25307, 25484, 25463, 26119, 26124, 26157, 26230, 26494, 26786, 27167, 27189, 27836, 28040, 28169, 28248, 28988, 28966, 29031, 30151, 30465, 30813, 30977, 31077, 31216, 31456, 31505, 31911, 32057, 32918, 33750, 33931, 34121, 34909, 35059, 35359, 35388, 35412, 35443, 35937, 36062, 37284, 37478, 37758, 37912, 38556, 38808, 19978, 19976, 19998, 20055, 20887, 21104, 22478, 22580, 22732, 23330, 24120, 24773, 25854, 26465, 26454, 27972, 29366, 30067, 31331, 33976, 35698, 37304, 37664, 22065, 22516, 39166, 25325, 26893, 27542, 29165, 32340, 32887, 33394, 35302, 39135, 34645, 36785, 23611, 20280, 20449, 20405, 21767, 23072, 23517, 23529, 24515, 24910, 25391, 26032, 26187, 26862, 27035, 28024, 28145, 30003, 30137, 30495, 31070, 31206, 32051, 33251, 33455, 34218, 35242, 35386, 36523, 36763, 36914, 37341, 38663, 20154, 20161, 20995, 22645, 22764, 23563, 29978, 23613, 33102, 35338, 36805, 38499, 38765, 31525, 35535, 38920, 37218, 22259, 21416, 36887, 21561, 22402, 24101, 25512, 27700, 28810, 30561, 31883, 32736, 34928, 36930, 37204, 37648, 37656, 38543, 29790, 39620, 23815, 23913, 25968, 26530, 36264, 38619, 25454, 26441, 26905, 33733, 38935, 38592, 35070, 28548, 25722, 23544, 19990, 28716, 30045, 26159, 20932, 21046, 21218, 22995, 24449, 24615, 25104, 25919, 25972, 26143, 26228, 26866, 26646, 27491, 28165, 29298, 29983, 30427, 31934, 32854, 22768, 35069, 35199, 35488, 35475, 35531, 36893, 37266, 38738, 38745, 25993, 31246, 33030, 38587, 24109, 24796, 25114, 26021, 26132, 26512, 30707, 31309, 31821, 32318, 33034, 36012, 36196, 36321, 36447, 30889, 20999, 25305, 25509, 25666, 25240, 35373, 31363, 31680, 35500, 38634, 32118, 33292, 34633, 20185, 20808, 21315, 21344, 23459, 23554, 23574, 24029, 25126, 25159, 25776, 26643, 26676, 27849, 27973, 27927, 26579, 28508, 29006, 29053, 26059, 31359, 31661, 32218, 32330, 32680, 33146, 33307, 33337, 34214, 35438, 36046, 36341, 36984, 36983, 37549, 37521, 38275, 39854, 21069, 21892, 28472, 28982, 20840, 31109, 32341, 33203, 31950, 22092, 22609, 23720, 25514, 26366, 26365, 26970, 29401, 30095, 30094, 30990, 31062, 31199, 31895, 32032, 32068, 34311, 35380, 38459, 36961, 40736, 20711, 21109, 21452, 21474, 20489, 21930, 22766, 22863, 29245, 23435, 23652, 21277, 24803, 24819, 25436, 25475, 25407, 25531, 25805, 26089, 26361, 24035, 27085, 27133, 28437, 29157, 20105, 30185, 30456, 31379, 31967, 32207, 32156, 32865, 33609, 33624, 33900, 33980, 34299, 35013, 36208, 36865, 36973, 37783, 38684, 39442, 20687, 22679, 24974, 33235, 34101, 36104, 36896, 20419, 20596, 21063, 21363, 24687, 25417, 26463, 28204, 36275, 36895, 20439, 23646, 36042, 26063, 32154, 21330, 34966, 20854, 25539, 23384, 23403, 23562, 25613, 26449, 36956, 20182, 22810, 22826, 27760, 35409, 21822, 22549, 22949, 24816, 25171, 26561, 33333, 26965, 38464, 39364, 39464, 20307, 22534, 23550, 32784, 23729, 24111, 24453, 24608, 24907, 25140, 26367, 27888, 28382, 32974, 33151, 33492, 34955, 36024, 36864, 36910, 38538, 40667, 39899, 20195, 21488, 22823, 31532, 37261, 38988, 40441, 28381, 28711, 21331, 21828, 23429, 25176, 25246, 25299, 27810, 28655, 29730, 35351, 37944, 28609, 35582, 33592, 20967, 34552, 21482, 21481, 20294, 36948, 36784, 22890, 33073, 24061, 31466, 36799, 26842, 35895, 29432, 40008, 27197, 35504, 20025, 21336, 22022, 22374, 25285, 25506, 26086, 27470, 28129, 28251, 28845, 30701, 31471, 31658, 32187, 32829, 32966, 34507, 35477, 37723, 22243, 22727, 24382, 26029, 26262, 27264, 27573, 30007, 35527, 20516, 30693, 22320, 24347, 24677, 26234, 27744, 30196, 31258, 32622, 33268, 34584, 36933, 39347, 31689, 30044, 31481, 31569, 33988, 36880, 31209, 31378, 33590, 23265, 30528, 20013, 20210, 23449, 24544, 25277, 26172, 26609, 27880, 34411, 34935, 35387, 37198, 37619, 39376, 27159, 28710, 29482, 33511, 33879, 36015, 19969, 20806, 20939, 21899, 23541, 24086, 24115, 24193, 24340, 24373, 24427, 24500, 25074, 25361, 26274, 26397, 28526, 29266, 30010, 30522, 32884, 33081, 33144, 34678, 35519, 35548, 36229, 36339, 37530, 38263, 38914, 40165, 21189, 25431, 30452, 26389, 27784, 29645, 36035, 37806, 38515, 27941, 22684, 26894, 27084, 36861, 37786, 30171, 36890, 22618, 26626, 25524, 27131, 20291, 28460, 26584, 36795, 34086, 32180, 37716, 26943, 28528, 22378, 22775, 23340, 32044, 29226, 21514, 37347, 40372, 20141, 20302, 20572, 20597, 21059, 35998, 21576, 22564, 23450, 24093, 24213, 24237, 24311, 24351, 24716, 25269, 25402, 25552, 26799, 27712, 30855, 31118, 31243, 32224, 33351, 35330, 35558, 36420, 36883, 37048, 37165, 37336, 40718, 27877, 25688, 25826, 25973, 28404, 30340, 31515, 36969, 37841, 28346, 21746, 24505, 25764, 36685, 36845, 37444, 20856, 22635, 22825, 23637, 24215, 28155, 32399, 29980, 36028, 36578, 39003, 28857, 20253, 27583, 28593, 30000, 38651, 20814, 21520, 22581, 22615, 22956, 23648, 24466, 26007, 26460, 28193, 30331, 33759, 36077, 36884, 37117, 37709, 30757, 30778, 21162, 24230, 22303, 22900, 24594, 20498, 20826, 20908, 20941, 20992, 21776, 22612, 22616, 22871, 23445, 23798, 23947, 24764, 25237, 25645, 26481, 26691, 26812, 26847, 30423, 28120, 28271, 28059, 28783, 29128, 24403, 30168, 31095, 31561, 31572, 31570, 31958, 32113, 21040, 33891, 34153, 34276, 35342, 35588, 35910, 36367, 36867, 36879, 37913, 38518, 38957, 39472, 38360, 20685, 21205, 21516, 22530, 23566, 24999, 25758, 27934, 30643, 31461, 33012, 33796, 36947, 37509, 23776, 40199, 21311, 24471, 24499, 28060, 29305, 30563, 31167, 31716, 27602, 29420, 35501, 26627, 27233, 20984, 31361, 26932, 23626, 40182, 33515, 23493, 37193, 28702, 22136, 23663, 24775, 25958, 27788, 35930, 36929, 38931, 21585, 26311, 37389, 22856, 37027, 20869, 20045, 20970, 34201, 35598, 28760, 25466, 37707, 26978, 39348, 32260, 30071, 21335, 26976, 36575, 38627, 27741, 20108, 23612, 24336, 36841, 21250, 36049, 32905, 34425, 24319, 26085, 20083, 20837, 22914, 23615, 38894, 20219, 22922, 24525, 35469, 28641, 31152, 31074, 23527, 33905, 29483, 29105, 24180, 24565, 25467, 25754, 29123, 31896, 20035, 24316, 20043, 22492, 22178, 24745, 28611, 32013, 33021, 33075, 33215, 36786, 35223, 34468, 24052, 25226, 25773, 35207, 26487, 27874, 27966, 29750, 30772, 23110, 32629, 33453, 39340, 20467, 24259, 25309, 25490, 25943, 26479, 30403, 29260, 32972, 32954, 36649, 37197, 20493, 22521, 23186, 26757, 26995, 29028, 29437, 36023, 22770, 36064, 38506, 36889, 34687, 31204, 30695, 33833, 20271, 21093, 21338, 25293, 26575, 27850, 30333, 31636, 31893, 33334, 34180, 36843, 26333, 28448, 29190, 32283, 33707, 39361, 40614, 20989, 31665, 30834, 31672, 32903, 31560, 27368, 24161, 32908, 30033, 30048, 20843, 37474, 28300, 30330, 37271, 39658, 20240, 32624, 25244, 31567, 38309, 40169, 22138, 22617, 34532, 38588, 20276, 21028, 21322, 21453, 21467, 24070, 25644, 26001, 26495, 27710, 27726, 29256, 29359, 29677, 30036, 32321, 33324, 34281, 36009, 31684, 37318, 29033, 38930, 39151, 25405, 26217, 30058, 30436, 30928, 34115, 34542, 21290, 21329, 21542, 22915, 24199, 24444, 24754, 25161, 25209, 25259, 26000, 27604, 27852, 30130, 30382, 30865, 31192, 32203, 32631, 32933, 34987, 35513, 36027, 36991, 38750, 39131, 27147, 31800, 20633, 23614, 24494, 26503, 27608, 29749, 30473, 32654, 40763, 26570, 31255, 21305, 30091, 39661, 24422, 33181, 33777, 32920, 24380, 24517, 30050, 31558, 36924, 26727, 23019, 23195, 32016, 30334, 35628, 20469, 24426, 27161, 27703, 28418, 29922, 31080, 34920, 35413, 35961, 24287, 25551, 30149, 31186, 33495, 37672, 37618, 33948, 34541, 39981, 21697, 24428, 25996, 27996, 28693, 36007, 36051, 38971, 25935, 29942, 19981, 20184, 22496, 22827, 23142, 23500, 20904, 24067, 24220, 24598, 25206, 25975, 26023, 26222, 28014, 29238, 31526, 33104, 33178, 33433, 35676, 36000, 36070, 36212, 38428, 38468, 20398, 25771, 27494, 33310, 33889, 34154, 37096, 23553, 26963, 39080, 33914, 34135, 20239, 21103, 24489, 24133, 26381, 31119, 33145, 35079, 35206, 28149, 24343, 25173, 27832, 20175, 29289, 39826, 20998, 21563, 22132, 22707, 24996, 25198, 28954, 22894, 31881, 31966, 32027, 38640, 25991, 32862, 19993, 20341, 20853, 22592, 24163, 24179, 24330, 26564, 20006, 34109, 38281, 38491, 31859, 38913, 20731, 22721, 30294, 30887, 21029, 30629, 34065, 31622, 20559, 22793, 29255, 31687, 32232, 36794, 36820, 36941, 20415, 21193, 23081, 24321, 38829, 20445, 33303, 37610, 22275, 25429, 27497, 29995, 35036, 36628, 31298, 21215, 22675, 24917, 25098, 26286, 27597, 31807, 33769, 20515, 20472, 21253, 21574, 22577, 22857, 23453, 23792, 23791, 23849, 24214, 25265, 25447, 25918, 26041, 26379, 27861, 27873, 28921, 30770, 32299, 32990, 33459, 33804, 34028, 34562, 35090, 35370, 35914, 37030, 37586, 39165, 40179, 40300, 20047, 20129, 20621, 21078, 22346, 22952, 24125, 24536, 24537, 25151, 26292, 26395, 26576, 26834, 20882, 32033, 32938, 33192, 35584, 35980, 36031, 37502, 38450, 21536, 38956, 21271, 20693, 21340, 22696, 25778, 26420, 29287, 30566, 31302, 37350, 21187, 27809, 27526, 22528, 24140, 22868, 26412, 32763, 20961, 30406, 25705, 30952, 39764, 40635, 22475, 22969, 26151, 26522, 27598, 21737, 27097, 24149, 33180, 26517, 39850, 26622, 40018, 26717, 20134, 20451, 21448, 25273, 26411, 27819, 36804, 20397, 32365, 40639, 19975, 24930, 28288, 28459, 34067, 21619, 26410, 39749, 24051, 31637, 23724, 23494, 34588, 28234, 34001, 31252, 33032, 22937, 31885, 27665, 30496, 21209, 22818, 28961, 29279, 30683, 38695, 40289, 26891, 23167, 23064, 20901, 21517, 21629, 26126, 30431, 36855, 37528, 40180, 23018, 29277, 28357, 20813, 26825, 32191, 32236, 38754, 40634, 25720, 27169, 33538, 22916, 23391, 27611, 29467, 30450, 32178, 32791, 33945, 20786, 26408, 40665, 30446, 26466, 21247, 39173, 23588, 25147, 31870, 36016, 21839, 24758, 32011, 38272, 21249, 20063, 20918, 22812, 29242, 32822, 37326, 24357, 30690, 21380, 24441, 32004, 34220, 35379, 36493, 38742, 26611, 34222, 37971, 24841, 24840, 27833, 30290, 35565, 36664, 21807, 20305, 20778, 21191, 21451, 23461, 24189, 24736, 24962, 25558, 26377, 26586, 28263, 28044, 29494, 29495, 30001, 31056, 35029, 35480, 36938, 37009, 37109, 38596, 34701, 22805, 20104, 20313, 19982, 35465, 36671, 38928, 20653, 24188, 22934, 23481, 24248, 25562, 25594, 25793, 26332, 26954, 27096, 27915, 28342, 29076, 29992, 31407, 32650, 32768, 33865, 33993, 35201, 35617, 36362, 36965, 38525, 39178, 24958, 25233, 27442, 27779, 28020, 32716, 32764, 28096, 32645, 34746, 35064, 26469, 33713, 38972, 38647, 27931, 32097, 33853, 37226, 20081, 21365, 23888, 27396, 28651, 34253, 34349, 35239, 21033, 21519, 23653, 26446, 26792, 29702, 29827, 30178, 35023, 35041, 37324, 38626, 38520, 24459, 29575, 31435, 33870, 25504, 30053, 21129, 27969, 28316, 29705, 30041, 30827, 31890, 38534, 31452, 40845, 20406, 24942, 26053, 34396, 20102, 20142, 20698, 20001, 20940, 23534, 26009, 26753, 28092, 29471, 30274, 30637, 31260, 31975, 33391, 35538, 36988, 37327, 38517, 38936, 21147, 32209, 20523, 21400, 26519, 28107, 29136, 29747, 33256, 36650, 38563, 40023, 40607, 29792, 22593, 28057, 32047, 39006, 20196, 20278, 20363, 20919, 21169, 23994, 24604, 29618, 31036, 33491, 37428, 38583, 38646, 38666, 40599, 40802, 26278, 27508, 21015, 21155, 28872, 35010, 24265, 24651, 24976, 28451, 29001, 31806, 32244, 32879, 34030, 36899, 37676, 21570, 39791, 27347, 28809, 36034, 36335, 38706, 21172, 23105, 24266, 24324, 26391, 27004, 27028, 28010, 28431, 29282, 29436, 31725, 32769, 32894, 34635, 37070, 20845, 40595, 31108, 32907, 37682, 35542, 20525, 21644, 35441, 27498, 36036, 33031, 24785, 26528, 40434, 20121, 20120, 39952, 35435, 34241, 34152, 26880, 28286, 30871, 33109, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 24332, 19984, 19989, 20010, 20017, 20022, 20028, 20031, 20034, 20054, 20056, 20098, 20101, 35947, 20106, 33298, 24333, 20110, 20126, 20127, 20128, 20130, 20144, 20147, 20150, 20174, 20173, 20164, 20166, 20162, 20183, 20190, 20205, 20191, 20215, 20233, 20314, 20272, 20315, 20317, 20311, 20295, 20342, 20360, 20367, 20376, 20347, 20329, 20336, 20369, 20335, 20358, 20374, 20760, 20436, 20447, 20430, 20440, 20443, 20433, 20442, 20432, 20452, 20453, 20506, 20520, 20500, 20522, 20517, 20485, 20252, 20470, 20513, 20521, 20524, 20478, 20463, 20497, 20486, 20547, 20551, 26371, 20565, 20560, 20552, 20570, 20566, 20588, 20600, 20608, 20634, 20613, 20660, 20658, 20681, 20682, 20659, 20674, 20694, 20702, 20709, 20717, 20707, 20718, 20729, 20725, 20745, 20737, 20738, 20758, 20757, 20756, 20762, 20769, 20794, 20791, 20796, 20795, 20799, 20800, 20818, 20812, 20820, 20834, 31480, 20841, 20842, 20846, 20864, 20866, 22232, 20876, 20873, 20879, 20881, 20883, 20885, 20886, 20900, 20902, 20898, 20905, 20906, 20907, 20915, 20913, 20914, 20912, 20917, 20925, 20933, 20937, 20955, 20960, 34389, 20969, 20973, 20976, 20981, 20990, 20996, 21003, 21012, 21006, 21031, 21034, 21038, 21043, 21049, 21071, 21060, 21067, 21068, 21086, 21076, 21098, 21108, 21097, 21107, 21119, 21117, 21133, 21140, 21138, 21105, 21128, 21137, 36776, 36775, 21164, 21165, 21180, 21173, 21185, 21197, 21207, 21214, 21219, 21222, 39149, 21216, 21235, 21237, 21240, 21241, 21254, 21256, 30008, 21261, 21264, 21263, 21269, 21274, 21283, 21295, 21297, 21299, 21304, 21312, 21318, 21317, 19991, 21321, 21325, 20950, 21342, 21353, 21358, 22808, 21371, 21367, 21378, 21398, 21408, 21414, 21413, 21422, 21424, 21430, 21443, 31762, 38617, 21471, 26364, 29166, 21486, 21480, 21485, 21498, 21505, 21565, 21568, 21548, 21549, 21564, 21550, 21558, 21545, 21533, 21582, 21647, 21621, 21646, 21599, 21617, 21623, 21616, 21650, 21627, 21632, 21622, 21636, 21648, 21638, 21703, 21666, 21688, 21669, 21676, 21700, 21704, 21672, 21675, 21698, 21668, 21694, 21692, 21720, 21733, 21734, 21775, 21780, 21757, 21742, 21741, 21754, 21730, 21817, 21824, 21859, 21836, 21806, 21852, 21829, 21846, 21847, 21816, 21811, 21853, 21913, 21888, 21679, 21898, 21919, 21883, 21886, 21912, 21918, 21934, 21884, 21891, 21929, 21895, 21928, 21978, 21957, 21983, 21956, 21980, 21988, 21972, 22036, 22007, 22038, 22014, 22013, 22043, 22009, 22094, 22096, 29151, 22068, 22070, 22066, 22072, 22123, 22116, 22063, 22124, 22122, 22150, 22144, 22154, 22176, 22164, 22159, 22181, 22190, 22198, 22196, 22210, 22204, 22209, 22211, 22208, 22216, 22222, 22225, 22227, 22231, 22254, 22265, 22272, 22271, 22276, 22281, 22280, 22283, 22285, 22291, 22296, 22294, 21959, 22300, 22310, 22327, 22328, 22350, 22331, 22336, 22351, 22377, 22464, 22408, 22369, 22399, 22409, 22419, 22432, 22451, 22436, 22442, 22448, 22467, 22470, 22484, 22482, 22483, 22538, 22486, 22499, 22539, 22553, 22557, 22642, 22561, 22626, 22603, 22640, 27584, 22610, 22589, 22649, 22661, 22713, 22687, 22699, 22714, 22750, 22715, 22712, 22702, 22725, 22739, 22737, 22743, 22745, 22744, 22757, 22748, 22756, 22751, 22767, 22778, 22777, 22779, 22780, 22781, 22786, 22794, 22800, 22811, 26790, 22821, 22828, 22829, 22834, 22840, 22846, 31442, 22869, 22864, 22862, 22874, 22872, 22882, 22880, 22887, 22892, 22889, 22904, 22913, 22941, 20318, 20395, 22947, 22962, 22982, 23016, 23004, 22925, 23001, 23002, 23077, 23071, 23057, 23068, 23049, 23066, 23104, 23148, 23113, 23093, 23094, 23138, 23146, 23194, 23228, 23230, 23243, 23234, 23229, 23267, 23255, 23270, 23273, 23254, 23290, 23291, 23308, 23307, 23318, 23346, 23248, 23338, 23350, 23358, 23363, 23365, 23360, 23377, 23381, 23386, 23387, 23397, 23401, 23408, 23411, 23413, 23416, 25992, 23418, 23424, 23427, 23462, 23480, 23491, 23495, 23497, 23508, 23504, 23524, 23526, 23522, 23518, 23525, 23531, 23536, 23542, 23539, 23557, 23559, 23560, 23565, 23571, 23584, 23586, 23592, 23608, 23609, 23617, 23622, 23630, 23635, 23632, 23631, 23409, 23660, 23662, 20066, 23670, 23673, 23692, 23697, 23700, 22939, 23723, 23739, 23734, 23740, 23735, 23749, 23742, 23751, 23769, 23785, 23805, 23802, 23789, 23948, 23786, 23819, 23829, 23831, 23900, 23839, 23835, 23825, 23828, 23842, 23834, 23833, 23832, 23884, 23890, 23886, 23883, 23916, 23923, 23926, 23943, 23940, 23938, 23970, 23965, 23980, 23982, 23997, 23952, 23991, 23996, 24009, 24013, 24019, 24018, 24022, 24027, 24043, 24050, 24053, 24075, 24090, 24089, 24081, 24091, 24118, 24119, 24132, 24131, 24128, 24142, 24151, 24148, 24159, 24162, 24164, 24135, 24181, 24182, 24186, 40636, 24191, 24224, 24257, 24258, 24264, 24272, 24271, 24278, 24291, 24285, 24282, 24283, 24290, 24289, 24296, 24297, 24300, 24305, 24307, 24304, 24308, 24312, 24318, 24323, 24329, 24413, 24412, 24331, 24337, 24342, 24361, 24365, 24376, 24385, 24392, 24396, 24398, 24367, 24401, 24406, 24407, 24409, 24417, 24429, 24435, 24439, 24451, 24450, 24447, 24458, 24456, 24465, 24455, 24478, 24473, 24472, 24480, 24488, 24493, 24508, 24534, 24571, 24548, 24568, 24561, 24541, 24755, 24575, 24609, 24672, 24601, 24592, 24617, 24590, 24625, 24603, 24597, 24619, 24614, 24591, 24634, 24666, 24641, 24682, 24695, 24671, 24650, 24646, 24653, 24675, 24643, 24676, 24642, 24684, 24683, 24665, 24705, 24717, 24807, 24707, 24730, 24708, 24731, 24726, 24727, 24722, 24743, 24715, 24801, 24760, 24800, 24787, 24756, 24560, 24765, 24774, 24757, 24792, 24909, 24853, 24838, 24822, 24823, 24832, 24820, 24826, 24835, 24865, 24827, 24817, 24845, 24846, 24903, 24894, 24872, 24871, 24906, 24895, 24892, 24876, 24884, 24893, 24898, 24900, 24947, 24951, 24920, 24921, 24922, 24939, 24948, 24943, 24933, 24945, 24927, 24925, 24915, 24949, 24985, 24982, 24967, 25004, 24980, 24986, 24970, 24977, 25003, 25006, 25036, 25034, 25033, 25079, 25032, 25027, 25030, 25018, 25035, 32633, 25037, 25062, 25059, 25078, 25082, 25076, 25087, 25085, 25084, 25086, 25088, 25096, 25097, 25101, 25100, 25108, 25115, 25118, 25121, 25130, 25134, 25136, 25138, 25139, 25153, 25166, 25182, 25187, 25179, 25184, 25192, 25212, 25218, 25225, 25214, 25234, 25235, 25238, 25300, 25219, 25236, 25303, 25297, 25275, 25295, 25343, 25286, 25812, 25288, 25308, 25292, 25290, 25282, 25287, 25243, 25289, 25356, 25326, 25329, 25383, 25346, 25352, 25327, 25333, 25424, 25406, 25421, 25628, 25423, 25494, 25486, 25472, 25515, 25462, 25507, 25487, 25481, 25503, 25525, 25451, 25449, 25534, 25577, 25536, 25542, 25571, 25545, 25554, 25590, 25540, 25622, 25652, 25606, 25619, 25638, 25654, 25885, 25623, 25640, 25615, 25703, 25711, 25718, 25678, 25898, 25749, 25747, 25765, 25769, 25736, 25788, 25818, 25810, 25797, 25799, 25787, 25816, 25794, 25841, 25831, 33289, 25824, 25825, 25260, 25827, 25839, 25900, 25846, 25844, 25842, 25850, 25856, 25853, 25880, 25884, 25861, 25892, 25891, 25899, 25908, 25909, 25911, 25910, 25912, 30027, 25928, 25942, 25941, 25933, 25944, 25950, 25949, 25970, 25976, 25986, 25987, 35722, 26011, 26015, 26027, 26039, 26051, 26054, 26049, 26052, 26060, 26066, 26075, 26073, 26080, 26081, 26097, 26482, 26122, 26115, 26107, 26483, 26165, 26166, 26164, 26140, 26191, 26180, 26185, 26177, 26206, 26205, 26212, 26215, 26216, 26207, 26210, 26224, 26243, 26248, 26254, 26249, 26244, 26264, 26269, 26305, 26297, 26313, 26302, 26300, 26308, 26296, 26326, 26330, 26336, 26175, 26342, 26345, 26352, 26357, 26359, 26383, 26390, 26398, 26406, 26407, 38712, 26414, 26431, 26422, 26433, 26424, 26423, 26438, 26462, 26464, 26457, 26467, 26468, 26505, 26480, 26537, 26492, 26474, 26508, 26507, 26534, 26529, 26501, 26551, 26607, 26548, 26604, 26547, 26601, 26552, 26596, 26590, 26589, 26594, 26606, 26553, 26574, 26566, 26599, 27292, 26654, 26694, 26665, 26688, 26701, 26674, 26702, 26803, 26667, 26713, 26723, 26743, 26751, 26783, 26767, 26797, 26772, 26781, 26779, 26755, 27310, 26809, 26740, 26805, 26784, 26810, 26895, 26765, 26750, 26881, 26826, 26888, 26840, 26914, 26918, 26849, 26892, 26829, 26836, 26855, 26837, 26934, 26898, 26884, 26839, 26851, 26917, 26873, 26848, 26863, 26920, 26922, 26906, 26915, 26913, 26822, 27001, 26999, 26972, 27000, 26987, 26964, 27006, 26990, 26937, 26996, 26941, 26969, 26928, 26977, 26974, 26973, 27009, 26986, 27058, 27054, 27088, 27071, 27073, 27091, 27070, 27086, 23528, 27082, 27101, 27067, 27075, 27047, 27182, 27025, 27040, 27036, 27029, 27060, 27102, 27112, 27138, 27163, 27135, 27402, 27129, 27122, 27111, 27141, 27057, 27166, 27117, 27156, 27115, 27146, 27154, 27329, 27171, 27155, 27204, 27148, 27250, 27190, 27256, 27207, 27234, 27225, 27238, 27208, 27192, 27170, 27280, 27277, 27296, 27268, 27298, 27299, 27287, 34327, 27323, 27331, 27330, 27320, 27315, 27308, 27358, 27345, 27359, 27306, 27354, 27370, 27387, 27397, 34326, 27386, 27410, 27414, 39729, 27423, 27448, 27447, 30428, 27449, 39150, 27463, 27459, 27465, 27472, 27481, 27476, 27483, 27487, 27489, 27512, 27513, 27519, 27520, 27524, 27523, 27533, 27544, 27541, 27550, 27556, 27562, 27563, 27567, 27570, 27569, 27571, 27575, 27580, 27590, 27595, 27603, 27615, 27628, 27627, 27635, 27631, 40638, 27656, 27667, 27668, 27675, 27684, 27683, 27742, 27733, 27746, 27754, 27778, 27789, 27802, 27777, 27803, 27774, 27752, 27763, 27794, 27792, 27844, 27889, 27859, 27837, 27863, 27845, 27869, 27822, 27825, 27838, 27834, 27867, 27887, 27865, 27882, 27935, 34893, 27958, 27947, 27965, 27960, 27929, 27957, 27955, 27922, 27916, 28003, 28051, 28004, 27994, 28025, 27993, 28046, 28053, 28644, 28037, 28153, 28181, 28170, 28085, 28103, 28134, 28088, 28102, 28140, 28126, 28108, 28136, 28114, 28101, 28154, 28121, 28132, 28117, 28138, 28142, 28205, 28270, 28206, 28185, 28274, 28255, 28222, 28195, 28267, 28203, 28278, 28237, 28191, 28227, 28218, 28238, 28196, 28415, 28189, 28216, 28290, 28330, 28312, 28361, 28343, 28371, 28349, 28335, 28356, 28338, 28372, 28373, 28303, 28325, 28354, 28319, 28481, 28433, 28748, 28396, 28408, 28414, 28479, 28402, 28465, 28399, 28466, 28364, 28478, 28435, 28407, 28550, 28538, 28536, 28545, 28544, 28527, 28507, 28659, 28525, 28546, 28540, 28504, 28558, 28561, 28610, 28518, 28595, 28579, 28577, 28580, 28601, 28614, 28586, 28639, 28629, 28652, 28628, 28632, 28657, 28654, 28635, 28681, 28683, 28666, 28689, 28673, 28687, 28670, 28699, 28698, 28532, 28701, 28696, 28703, 28720, 28734, 28722, 28753, 28771, 28825, 28818, 28847, 28913, 28844, 28856, 28851, 28846, 28895, 28875, 28893, 28889, 28937, 28925, 28956, 28953, 29029, 29013, 29064, 29030, 29026, 29004, 29014, 29036, 29071, 29179, 29060, 29077, 29096, 29100, 29143, 29113, 29118, 29138, 29129, 29140, 29134, 29152, 29164, 29159, 29173, 29180, 29177, 29183, 29197, 29200, 29211, 29224, 29229, 29228, 29232, 29234, 29243, 29244, 29247, 29248, 29254, 29259, 29272, 29300, 29310, 29314, 29313, 29319, 29330, 29334, 29346, 29351, 29369, 29362, 29379, 29382, 29380, 29390, 29394, 29410, 29408, 29409, 29433, 29431, 20495, 29463, 29450, 29468, 29462, 29469, 29492, 29487, 29481, 29477, 29502, 29518, 29519, 40664, 29527, 29546, 29544, 29552, 29560, 29557, 29563, 29562, 29640, 29619, 29646, 29627, 29632, 29669, 29678, 29662, 29858, 29701, 29807, 29733, 29688, 29746, 29754, 29781, 29759, 29791, 29785, 29761, 29788, 29801, 29808, 29795, 29802, 29814, 29822, 29835, 29854, 29863, 29898, 29903, 29908, 29681, 29920, 29923, 29927, 29929, 29934, 29938, 29936, 29937, 29944, 29943, 29956, 29955, 29957, 29964, 29966, 29965, 29973, 29971, 29982, 29990, 29996, 30012, 30020, 30029, 30026, 30025, 30043, 30022, 30042, 30057, 30052, 30055, 30059, 30061, 30072, 30070, 30086, 30087, 30068, 30090, 30089, 30082, 30100, 30106, 30109, 30117, 30115, 30146, 30131, 30147, 30133, 30141, 30136, 30140, 30129, 30157, 30154, 30162, 30169, 30179, 30174, 30206, 30207, 30204, 30209, 30192, 30202, 30194, 30195, 30219, 30221, 30217, 30239, 30247, 30240, 30241, 30242, 30244, 30260, 30256, 30267, 30279, 30280, 30278, 30300, 30296, 30305, 30306, 30312, 30313, 30314, 30311, 30316, 30320, 30322, 30326, 30328, 30332, 30336, 30339, 30344, 30347, 30350, 30358, 30355, 30361, 30362, 30384, 30388, 30392, 30393, 30394, 30402, 30413, 30422, 30418, 30430, 30433, 30437, 30439, 30442, 34351, 30459, 30472, 30471, 30468, 30505, 30500, 30494, 30501, 30502, 30491, 30519, 30520, 30535, 30554, 30568, 30571, 30555, 30565, 30591, 30590, 30585, 30606, 30603, 30609, 30624, 30622, 30640, 30646, 30649, 30655, 30652, 30653, 30651, 30663, 30669, 30679, 30682, 30684, 30691, 30702, 30716, 30732, 30738, 31014, 30752, 31018, 30789, 30862, 30836, 30854, 30844, 30874, 30860, 30883, 30901, 30890, 30895, 30929, 30918, 30923, 30932, 30910, 30908, 30917, 30922, 30956, 30951, 30938, 30973, 30964, 30983, 30994, 30993, 31001, 31020, 31019, 31040, 31072, 31063, 31071, 31066, 31061, 31059, 31098, 31103, 31114, 31133, 31143, 40779, 31146, 31150, 31155, 31161, 31162, 31177, 31189, 31207, 31212, 31201, 31203, 31240, 31245, 31256, 31257, 31264, 31263, 31104, 31281, 31291, 31294, 31287, 31299, 31319, 31305, 31329, 31330, 31337, 40861, 31344, 31353, 31357, 31368, 31383, 31381, 31384, 31382, 31401, 31432, 31408, 31414, 31429, 31428, 31423, 36995, 31431, 31434, 31437, 31439, 31445, 31443, 31449, 31450, 31453, 31457, 31458, 31462, 31469, 31472, 31490, 31503, 31498, 31494, 31539, 31512, 31513, 31518, 31541, 31528, 31542, 31568, 31610, 31492, 31565, 31499, 31564, 31557, 31605, 31589, 31604, 31591, 31600, 31601, 31596, 31598, 31645, 31640, 31647, 31629, 31644, 31642, 31627, 31634, 31631, 31581, 31641, 31691, 31681, 31692, 31695, 31668, 31686, 31709, 31721, 31761, 31764, 31718, 31717, 31840, 31744, 31751, 31763, 31731, 31735, 31767, 31757, 31734, 31779, 31783, 31786, 31775, 31799, 31787, 31805, 31820, 31811, 31828, 31823, 31808, 31824, 31832, 31839, 31844, 31830, 31845, 31852, 31861, 31875, 31888, 31908, 31917, 31906, 31915, 31905, 31912, 31923, 31922, 31921, 31918, 31929, 31933, 31936, 31941, 31938, 31960, 31954, 31964, 31970, 39739, 31983, 31986, 31988, 31990, 31994, 32006, 32002, 32028, 32021, 32010, 32069, 32075, 32046, 32050, 32063, 32053, 32070, 32115, 32086, 32078, 32114, 32104, 32110, 32079, 32099, 32147, 32137, 32091, 32143, 32125, 32155, 32186, 32174, 32163, 32181, 32199, 32189, 32171, 32317, 32162, 32175, 32220, 32184, 32159, 32176, 32216, 32221, 32228, 32222, 32251, 32242, 32225, 32261, 32266, 32291, 32289, 32274, 32305, 32287, 32265, 32267, 32290, 32326, 32358, 32315, 32309, 32313, 32323, 32311, 32306, 32314, 32359, 32349, 32342, 32350, 32345, 32346, 32377, 32362, 32361, 32380, 32379, 32387, 32213, 32381, 36782, 32383, 32392, 32393, 32396, 32402, 32400, 32403, 32404, 32406, 32398, 32411, 32412, 32568, 32570, 32581, 32588, 32589, 32590, 32592, 32593, 32597, 32596, 32600, 32607, 32608, 32616, 32617, 32615, 32632, 32642, 32646, 32643, 32648, 32647, 32652, 32660, 32670, 32669, 32666, 32675, 32687, 32690, 32697, 32686, 32694, 32696, 35697, 32709, 32710, 32714, 32725, 32724, 32737, 32742, 32745, 32755, 32761, 39132, 32774, 32772, 32779, 32786, 32792, 32793, 32796, 32801, 32808, 32831, 32827, 32842, 32838, 32850, 32856, 32858, 32863, 32866, 32872, 32883, 32882, 32880, 32886, 32889, 32893, 32895, 32900, 32902, 32901, 32923, 32915, 32922, 32941, 20880, 32940, 32987, 32997, 32985, 32989, 32964, 32986, 32982, 33033, 33007, 33009, 33051, 33065, 33059, 33071, 33099, 38539, 33094, 33086, 33107, 33105, 33020, 33137, 33134, 33125, 33126, 33140, 33155, 33160, 33162, 33152, 33154, 33184, 33173, 33188, 33187, 33119, 33171, 33193, 33200, 33205, 33214, 33208, 33213, 33216, 33218, 33210, 33225, 33229, 33233, 33241, 33240, 33224, 33242, 33247, 33248, 33255, 33274, 33275, 33278, 33281, 33282, 33285, 33287, 33290, 33293, 33296, 33302, 33321, 33323, 33336, 33331, 33344, 33369, 33368, 33373, 33370, 33375, 33380, 33378, 33384, 33386, 33387, 33326, 33393, 33399, 33400, 33406, 33421, 33426, 33451, 33439, 33467, 33452, 33505, 33507, 33503, 33490, 33524, 33523, 33530, 33683, 33539, 33531, 33529, 33502, 33542, 33500, 33545, 33497, 33589, 33588, 33558, 33586, 33585, 33600, 33593, 33616, 33605, 33583, 33579, 33559, 33560, 33669, 33690, 33706, 33695, 33698, 33686, 33571, 33678, 33671, 33674, 33660, 33717, 33651, 33653, 33696, 33673, 33704, 33780, 33811, 33771, 33742, 33789, 33795, 33752, 33803, 33729, 33783, 33799, 33760, 33778, 33805, 33826, 33824, 33725, 33848, 34054, 33787, 33901, 33834, 33852, 34138, 33924, 33911, 33899, 33965, 33902, 33922, 33897, 33862, 33836, 33903, 33913, 33845, 33994, 33890, 33977, 33983, 33951, 34009, 33997, 33979, 34010, 34000, 33985, 33990, 34006, 33953, 34081, 34047, 34036, 34071, 34072, 34092, 34079, 34069, 34068, 34044, 34112, 34147, 34136, 34120, 34113, 34306, 34123, 34133, 34176, 34212, 34184, 34193, 34186, 34216, 34157, 34196, 34203, 34282, 34183, 34204, 34167, 34174, 34192, 34249, 34234, 34255, 34233, 34256, 34261, 34269, 34277, 34268, 34297, 34314, 34323, 34315, 34302, 34298, 34310, 34338, 34330, 34352, 34367, 34381, 20053, 34388, 34399, 34407, 34417, 34451, 34467, 34473, 34474, 34443, 34444, 34486, 34479, 34500, 34502, 34480, 34505, 34851, 34475, 34516, 34526, 34537, 34540, 34527, 34523, 34543, 34578, 34566, 34568, 34560, 34563, 34555, 34577, 34569, 34573, 34553, 34570, 34612, 34623, 34615, 34619, 34597, 34601, 34586, 34656, 34655, 34680, 34636, 34638, 34676, 34647, 34664, 34670, 34649, 34643, 34659, 34666, 34821, 34722, 34719, 34690, 34735, 34763, 34749, 34752, 34768, 38614, 34731, 34756, 34739, 34759, 34758, 34747, 34799, 34802, 34784, 34831, 34829, 34814, 34806, 34807, 34830, 34770, 34833, 34838, 34837, 34850, 34849, 34865, 34870, 34873, 34855, 34875, 34884, 34882, 34898, 34905, 34910, 34914, 34923, 34945, 34942, 34974, 34933, 34941, 34997, 34930, 34946, 34967, 34962, 34990, 34969, 34978, 34957, 34980, 34992, 35007, 34993, 35011, 35012, 35028, 35032, 35033, 35037, 35065, 35074, 35068, 35060, 35048, 35058, 35076, 35084, 35082, 35091, 35139, 35102, 35109, 35114, 35115, 35137, 35140, 35131, 35126, 35128, 35148, 35101, 35168, 35166, 35174, 35172, 35181, 35178, 35183, 35188, 35191, 35198, 35203, 35208, 35210, 35219, 35224, 35233, 35241, 35238, 35244, 35247, 35250, 35258, 35261, 35263, 35264, 35290, 35292, 35293, 35303, 35316, 35320, 35331, 35350, 35344, 35340, 35355, 35357, 35365, 35382, 35393, 35419, 35410, 35398, 35400, 35452, 35437, 35436, 35426, 35461, 35458, 35460, 35496, 35489, 35473, 35493, 35494, 35482, 35491, 35524, 35533, 35522, 35546, 35563, 35571, 35559, 35556, 35569, 35604, 35552, 35554, 35575, 35550, 35547, 35596, 35591, 35610, 35553, 35606, 35600, 35607, 35616, 35635, 38827, 35622, 35627, 35646, 35624, 35649, 35660, 35663, 35662, 35657, 35670, 35675, 35674, 35691, 35679, 35692, 35695, 35700, 35709, 35712, 35724, 35726, 35730, 35731, 35734, 35737, 35738, 35898, 35905, 35903, 35912, 35916, 35918, 35920, 35925, 35938, 35948, 35960, 35962, 35970, 35977, 35973, 35978, 35981, 35982, 35988, 35964, 35992, 25117, 36013, 36010, 36029, 36018, 36019, 36014, 36022, 36040, 36033, 36068, 36067, 36058, 36093, 36090, 36091, 36100, 36101, 36106, 36103, 36111, 36109, 36112, 40782, 36115, 36045, 36116, 36118, 36199, 36205, 36209, 36211, 36225, 36249, 36290, 36286, 36282, 36303, 36314, 36310, 36300, 36315, 36299, 36330, 36331, 36319, 36323, 36348, 36360, 36361, 36351, 36381, 36382, 36368, 36383, 36418, 36405, 36400, 36404, 36426, 36423, 36425, 36428, 36432, 36424, 36441, 36452, 36448, 36394, 36451, 36437, 36470, 36466, 36476, 36481, 36487, 36485, 36484, 36491, 36490, 36499, 36497, 36500, 36505, 36522, 36513, 36524, 36528, 36550, 36529, 36542, 36549, 36552, 36555, 36571, 36579, 36604, 36603, 36587, 36606, 36618, 36613, 36629, 36626, 36633, 36627, 36636, 36639, 36635, 36620, 36646, 36659, 36667, 36665, 36677, 36674, 36670, 36684, 36681, 36678, 36686, 36695, 36700, 36706, 36707, 36708, 36764, 36767, 36771, 36781, 36783, 36791, 36826, 36837, 36834, 36842, 36847, 36999, 36852, 36869, 36857, 36858, 36881, 36885, 36897, 36877, 36894, 36886, 36875, 36903, 36918, 36917, 36921, 36856, 36943, 36944, 36945, 36946, 36878, 36937, 36926, 36950, 36952, 36958, 36968, 36975, 36982, 38568, 36978, 36994, 36989, 36993, 36992, 37002, 37001, 37007, 37032, 37039, 37041, 37045, 37090, 37092, 25160, 37083, 37122, 37138, 37145, 37170, 37168, 37194, 37206, 37208, 37219, 37221, 37225, 37235, 37234, 37259, 37257, 37250, 37282, 37291, 37295, 37290, 37301, 37300, 37306, 37312, 37313, 37321, 37323, 37328, 37334, 37343, 37345, 37339, 37372, 37365, 37366, 37406, 37375, 37396, 37420, 37397, 37393, 37470, 37463, 37445, 37449, 37476, 37448, 37525, 37439, 37451, 37456, 37532, 37526, 37523, 37531, 37466, 37583, 37561, 37559, 37609, 37647, 37626, 37700, 37678, 37657, 37666, 37658, 37667, 37690, 37685, 37691, 37724, 37728, 37756, 37742, 37718, 37808, 37804, 37805, 37780, 37817, 37846, 37847, 37864, 37861, 37848, 37827, 37853, 37840, 37832, 37860, 37914, 37908, 37907, 37891, 37895, 37904, 37942, 37931, 37941, 37921, 37946, 37953, 37970, 37956, 37979, 37984, 37986, 37982, 37994, 37417, 38000, 38005, 38007, 38013, 37978, 38012, 38014, 38017, 38015, 38274, 38279, 38282, 38292, 38294, 38296, 38297, 38304, 38312, 38311, 38317, 38332, 38331, 38329, 38334, 38346, 28662, 38339, 38349, 38348, 38357, 38356, 38358, 38364, 38369, 38373, 38370, 38433, 38440, 38446, 38447, 38466, 38476, 38479, 38475, 38519, 38492, 38494, 38493, 38495, 38502, 38514, 38508, 38541, 38552, 38549, 38551, 38570, 38567, 38577, 38578, 38576, 38580, 38582, 38584, 38585, 38606, 38603, 38601, 38605, 35149, 38620, 38669, 38613, 38649, 38660, 38662, 38664, 38675, 38670, 38673, 38671, 38678, 38681, 38692, 38698, 38704, 38713, 38717, 38718, 38724, 38726, 38728, 38722, 38729, 38748, 38752, 38756, 38758, 38760, 21202, 38763, 38769, 38777, 38789, 38780, 38785, 38778, 38790, 38795, 38799, 38800, 38812, 38824, 38822, 38819, 38835, 38836, 38851, 38854, 38856, 38859, 38876, 38893, 40783, 38898, 31455, 38902, 38901, 38927, 38924, 38968, 38948, 38945, 38967, 38973, 38982, 38991, 38987, 39019, 39023, 39024, 39025, 39028, 39027, 39082, 39087, 39089, 39094, 39108, 39107, 39110, 39145, 39147, 39171, 39177, 39186, 39188, 39192, 39201, 39197, 39198, 39204, 39200, 39212, 39214, 39229, 39230, 39234, 39241, 39237, 39248, 39243, 39249, 39250, 39244, 39253, 39319, 39320, 39333, 39341, 39342, 39356, 39391, 39387, 39389, 39384, 39377, 39405, 39406, 39409, 39410, 39419, 39416, 39425, 39439, 39429, 39394, 39449, 39467, 39479, 39493, 39490, 39488, 39491, 39486, 39509, 39501, 39515, 39511, 39519, 39522, 39525, 39524, 39529, 39531, 39530, 39597, 39600, 39612, 39616, 39631, 39633, 39635, 39636, 39646, 39647, 39650, 39651, 39654, 39663, 39659, 39662, 39668, 39665, 39671, 39675, 39686, 39704, 39706, 39711, 39714, 39715, 39717, 39719, 39720, 39721, 39722, 39726, 39727, 39730, 39748, 39747, 39759, 39757, 39758, 39761, 39768, 39796, 39827, 39811, 39825, 39830, 39831, 39839, 39840, 39848, 39860, 39872, 39882, 39865, 39878, 39887, 39889, 39890, 39907, 39906, 39908, 39892, 39905, 39994, 39922, 39921, 39920, 39957, 39956, 39945, 39955, 39948, 39942, 39944, 39954, 39946, 39940, 39982, 39963, 39973, 39972, 39969, 39984, 40007, 39986, 40006, 39998, 40026, 40032, 40039, 40054, 40056, 40167, 40172, 40176, 40201, 40200, 40171, 40195, 40198, 40234, 40230, 40367, 40227, 40223, 40260, 40213, 40210, 40257, 40255, 40254, 40262, 40264, 40285, 40286, 40292, 40273, 40272, 40281, 40306, 40329, 40327, 40363, 40303, 40314, 40346, 40356, 40361, 40370, 40388, 40385, 40379, 40376, 40378, 40390, 40399, 40386, 40409, 40403, 40440, 40422, 40429, 40431, 40445, 40474, 40475, 40478, 40565, 40569, 40573, 40577, 40584, 40587, 40588, 40594, 40597, 40593, 40605, 40613, 40617, 40632, 40618, 40621, 38753, 40652, 40654, 40655, 40656, 40660, 40668, 40670, 40669, 40672, 40677, 40680, 40687, 40692, 40694, 40695, 40697, 40699, 40700, 40701, 40711, 40712, 30391, 40725, 40737, 40748, 40766, 40778, 40786, 40788, 40803, 40799, 40800, 40801, 40806, 40807, 40812, 40810, 40823, 40818, 40822, 40853, 40860, 40864, 22575, 27079, 36953, 29796, 20956, 29081, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 32394, 35100, 37704, 37512, 34012, 20425, 28859, 26161, 26824, 37625, 26363, 24389, 20008, 20193, 20220, 20224, 20227, 20281, 20310, 20370, 20362, 20378, 20372, 20429, 20544, 20514, 20479, 20510, 20550, 20592, 20546, 20628, 20724, 20696, 20810, 20836, 20893, 20926, 20972, 21013, 21148, 21158, 21184, 21211, 21248, 21255, 21284, 21362, 21395, 21426, 21469, 64014, 21660, 21642, 21673, 21759, 21894, 22361, 22373, 22444, 22472, 22471, 64015, 64016, 22686, 22706, 22795, 22867, 22875, 22877, 22883, 22948, 22970, 23382, 23488, 29999, 23512, 23532, 23582, 23718, 23738, 23797, 23847, 23891, 64017, 23874, 23917, 23992, 23993, 24016, 24353, 24372, 24423, 24503, 24542, 24669, 24709, 24714, 24798, 24789, 24864, 24818, 24849, 24887, 24880, 24984, 25107, 25254, 25589, 25696, 25757, 25806, 25934, 26112, 26133, 26171, 26121, 26158, 26142, 26148, 26213, 26199, 26201, 64018, 26227, 26265, 26272, 26290, 26303, 26362, 26382, 63785, 26470, 26555, 26706, 26560, 26625, 26692, 26831, 64019, 26984, 64020, 27032, 27106, 27184, 27243, 27206, 27251, 27262, 27362, 27364, 27606, 27711, 27740, 27782, 27759, 27866, 27908, 28039, 28015, 28054, 28076, 28111, 28152, 28146, 28156, 28217, 28252, 28199, 28220, 28351, 28552, 28597, 28661, 28677, 28679, 28712, 28805, 28843, 28943, 28932, 29020, 28998, 28999, 64021, 29121, 29182, 29361, 29374, 29476, 64022, 29559, 29629, 29641, 29654, 29667, 29650, 29703, 29685, 29734, 29738, 29737, 29742, 29794, 29833, 29855, 29953, 30063, 30338, 30364, 30366, 30363, 30374, 64023, 30534, 21167, 30753, 30798, 30820, 30842, 31024, 64024, 64025, 64026, 31124, 64027, 31131, 31441, 31463, 64028, 31467, 31646, 64029, 32072, 32092, 32183, 32160, 32214, 32338, 32583, 32673, 64030, 33537, 33634, 33663, 33735, 33782, 33864, 33972, 34131, 34137, 34155, 64031, 34224, 64032, 64033, 34823, 35061, 35346, 35383, 35449, 35495, 35518, 35551, 64034, 35574, 35667, 35711, 36080, 36084, 36114, 36214, 64035, 36559, 64036, 64037, 36967, 37086, 64038, 37141, 37159, 37338, 37335, 37342, 37357, 37358, 37348, 37349, 37382, 37392, 37386, 37434, 37440, 37436, 37454, 37465, 37457, 37433, 37479, 37543, 37495, 37496, 37607, 37591, 37593, 37584, 64039, 37589, 37600, 37587, 37669, 37665, 37627, 64040, 37662, 37631, 37661, 37634, 37744, 37719, 37796, 37830, 37854, 37880, 37937, 37957, 37960, 38290, 63964, 64041, 38557, 38575, 38707, 38715, 38723, 38733, 38735, 38737, 38741, 38999, 39013, 64042, 64043, 39207, 64044, 39326, 39502, 39641, 39644, 39797, 39794, 39823, 39857, 39867, 39936, 40304, 40299, 64045, 40473, 40657, null, null, 8560, 8561, 8562, 8563, 8564, 8565, 8566, 8567, 8568, 8569, 65506, 65508, 65287, 65282, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 8560, 8561, 8562, 8563, 8564, 8565, 8566, 8567, 8568, 8569, 8544, 8545, 8546, 8547, 8548, 8549, 8550, 8551, 8552, 8553, 65506, 65508, 65287, 65282, 12849, 8470, 8481, 8757, 32394, 35100, 37704, 37512, 34012, 20425, 28859, 26161, 26824, 37625, 26363, 24389, 20008, 20193, 20220, 20224, 20227, 20281, 20310, 20370, 20362, 20378, 20372, 20429, 20544, 20514, 20479, 20510, 20550, 20592, 20546, 20628, 20724, 20696, 20810, 20836, 20893, 20926, 20972, 21013, 21148, 21158, 21184, 21211, 21248, 21255, 21284, 21362, 21395, 21426, 21469, 64014, 21660, 21642, 21673, 21759, 21894, 22361, 22373, 22444, 22472, 22471, 64015, 64016, 22686, 22706, 22795, 22867, 22875, 22877, 22883, 22948, 22970, 23382, 23488, 29999, 23512, 23532, 23582, 23718, 23738, 23797, 23847, 23891, 64017, 23874, 23917, 23992, 23993, 24016, 24353, 24372, 24423, 24503, 24542, 24669, 24709, 24714, 24798, 24789, 24864, 24818, 24849, 24887, 24880, 24984, 25107, 25254, 25589, 25696, 25757, 25806, 25934, 26112, 26133, 26171, 26121, 26158, 26142, 26148, 26213, 26199, 26201, 64018, 26227, 26265, 26272, 26290, 26303, 26362, 26382, 63785, 26470, 26555, 26706, 26560, 26625, 26692, 26831, 64019, 26984, 64020, 27032, 27106, 27184, 27243, 27206, 27251, 27262, 27362, 27364, 27606, 27711, 27740, 27782, 27759, 27866, 27908, 28039, 28015, 28054, 28076, 28111, 28152, 28146, 28156, 28217, 28252, 28199, 28220, 28351, 28552, 28597, 28661, 28677, 28679, 28712, 28805, 28843, 28943, 28932, 29020, 28998, 28999, 64021, 29121, 29182, 29361, 29374, 29476, 64022, 29559, 29629, 29641, 29654, 29667, 29650, 29703, 29685, 29734, 29738, 29737, 29742, 29794, 29833, 29855, 29953, 30063, 30338, 30364, 30366, 30363, 30374, 64023, 30534, 21167, 30753, 30798, 30820, 30842, 31024, 64024, 64025, 64026, 31124, 64027, 31131, 31441, 31463, 64028, 31467, 31646, 64029, 32072, 32092, 32183, 32160, 32214, 32338, 32583, 32673, 64030, 33537, 33634, 33663, 33735, 33782, 33864, 33972, 34131, 34137, 34155, 64031, 34224, 64032, 64033, 34823, 35061, 35346, 35383, 35449, 35495, 35518, 35551, 64034, 35574, 35667, 35711, 36080, 36084, 36114, 36214, 64035, 36559, 64036, 64037, 36967, 37086, 64038, 37141, 37159, 37338, 37335, 37342, 37357, 37358, 37348, 37349, 37382, 37392, 37386, 37434, 37440, 37436, 37454, 37465, 37457, 37433, 37479, 37543, 37495, 37496, 37607, 37591, 37593, 37584, 64039, 37589, 37600, 37587, 37669, 37665, 37627, 64040, 37662, 37631, 37661, 37634, 37744, 37719, 37796, 37830, 37854, 37880, 37937, 37957, 37960, 38290, 63964, 64041, 38557, 38575, 38707, 38715, 38723, 38733, 38735, 38737, 38741, 38999, 39013, 64042, 64043, 39207, 64044, 39326, 39502, 39641, 39644, 39797, 39794, 39823, 39857, 39867, 39936, 40304, 40299, 64045, 40473, 40657, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], + "jis0212":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,728,711,184,729,733,175,731,730,65374,900,901,null,null,null,null,null,null,null,null,161,166,191,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,186,170,169,174,8482,164,8470,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,902,904,905,906,938,null,908,null,910,939,null,911,null,null,null,null,940,941,942,943,970,912,972,962,973,971,944,974,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1038,1039,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1118,1119,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,198,272,null,294,null,306,null,321,319,null,330,216,338,null,358,222,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,230,273,240,295,305,307,312,322,320,329,331,248,339,223,359,254,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,193,192,196,194,258,461,256,260,197,195,262,264,268,199,266,270,201,200,203,202,282,278,274,280,null,284,286,290,288,292,205,204,207,206,463,304,298,302,296,308,310,313,317,315,323,327,325,209,211,210,214,212,465,336,332,213,340,344,342,346,348,352,350,356,354,218,217,220,219,364,467,368,362,370,366,360,471,475,473,469,372,221,376,374,377,381,379,null,null,null,null,null,null,null,225,224,228,226,259,462,257,261,229,227,263,265,269,231,267,271,233,232,235,234,283,279,275,281,501,285,287,null,289,293,237,236,239,238,464,null,299,303,297,309,311,314,318,316,324,328,326,241,243,242,246,244,466,337,333,245,341,345,343,347,349,353,351,357,355,250,249,252,251,365,468,369,363,371,367,361,472,476,474,470,373,253,255,375,378,382,380,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,19970,19972,19973,19980,19986,19999,20003,20004,20008,20011,20014,20015,20016,20021,20032,20033,20036,20039,20049,20058,20060,20067,20072,20073,20084,20085,20089,20095,20109,20118,20119,20125,20143,20153,20163,20176,20186,20187,20192,20193,20194,20200,20207,20209,20211,20213,20221,20222,20223,20224,20226,20227,20232,20235,20236,20242,20245,20246,20247,20249,20270,20273,20320,20275,20277,20279,20281,20283,20286,20288,20290,20296,20297,20299,20300,20306,20308,20310,20312,20319,20323,20330,20332,20334,20337,20343,20344,20345,20346,20349,20350,20353,20354,20356,20357,20361,20362,20364,20366,20368,20370,20371,20372,20375,20377,20378,20382,20383,20402,20407,20409,20411,20412,20413,20414,20416,20417,20421,20422,20424,20425,20427,20428,20429,20431,20434,20444,20448,20450,20464,20466,20476,20477,20479,20480,20481,20484,20487,20490,20492,20494,20496,20499,20503,20504,20507,20508,20509,20510,20514,20519,20526,20528,20530,20531,20533,20544,20545,20546,20549,20550,20554,20556,20558,20561,20562,20563,20567,20569,20575,20576,20578,20579,20582,20583,20586,20589,20592,20593,20539,20609,20611,20612,20614,20618,20622,20623,20624,20626,20627,20628,20630,20635,20636,20638,20639,20640,20641,20642,20650,20655,20656,20665,20666,20669,20672,20675,20676,20679,20684,20686,20688,20691,20692,20696,20700,20701,20703,20706,20708,20710,20712,20713,20719,20721,20726,20730,20734,20739,20742,20743,20744,20747,20748,20749,20750,20722,20752,20759,20761,20763,20764,20765,20766,20771,20775,20776,20780,20781,20783,20785,20787,20788,20789,20792,20793,20802,20810,20815,20819,20821,20823,20824,20831,20836,20838,20862,20867,20868,20875,20878,20888,20893,20897,20899,20909,20920,20922,20924,20926,20927,20930,20936,20943,20945,20946,20947,20949,20952,20958,20962,20965,20974,20978,20979,20980,20983,20993,20994,20997,21010,21011,21013,21014,21016,21026,21032,21041,21042,21045,21052,21061,21065,21077,21079,21080,21082,21084,21087,21088,21089,21094,21102,21111,21112,21113,21120,21122,21125,21130,21132,21139,21141,21142,21143,21144,21146,21148,21156,21157,21158,21159,21167,21168,21174,21175,21176,21178,21179,21181,21184,21188,21190,21192,21196,21199,21201,21204,21206,21211,21212,21217,21221,21224,21225,21226,21228,21232,21233,21236,21238,21239,21248,21251,21258,21259,21260,21265,21267,21272,21275,21276,21278,21279,21285,21287,21288,21289,21291,21292,21293,21296,21298,21301,21308,21309,21310,21314,21324,21323,21337,21339,21345,21347,21349,21356,21357,21362,21369,21374,21379,21383,21384,21390,21395,21396,21401,21405,21409,21412,21418,21419,21423,21426,21428,21429,21431,21432,21434,21437,21440,21445,21455,21458,21459,21461,21466,21469,21470,21472,21478,21479,21493,21506,21523,21530,21537,21543,21544,21546,21551,21553,21556,21557,21571,21572,21575,21581,21583,21598,21602,21604,21606,21607,21609,21611,21613,21614,21620,21631,21633,21635,21637,21640,21641,21645,21649,21653,21654,21660,21663,21665,21670,21671,21673,21674,21677,21678,21681,21687,21689,21690,21691,21695,21702,21706,21709,21710,21728,21738,21740,21743,21750,21756,21758,21759,21760,21761,21765,21768,21769,21772,21773,21774,21781,21802,21803,21810,21813,21814,21819,21820,21821,21825,21831,21833,21834,21837,21840,21841,21848,21850,21851,21854,21856,21857,21860,21862,21887,21889,21890,21894,21896,21902,21903,21905,21906,21907,21908,21911,21923,21924,21933,21938,21951,21953,21955,21958,21961,21963,21964,21966,21969,21970,21971,21975,21976,21979,21982,21986,21993,22006,22015,22021,22024,22026,22029,22030,22031,22032,22033,22034,22041,22060,22064,22067,22069,22071,22073,22075,22076,22077,22079,22080,22081,22083,22084,22086,22089,22091,22093,22095,22100,22110,22112,22113,22114,22115,22118,22121,22125,22127,22129,22130,22133,22148,22149,22152,22155,22156,22165,22169,22170,22173,22174,22175,22182,22183,22184,22185,22187,22188,22189,22193,22195,22199,22206,22213,22217,22218,22219,22223,22224,22220,22221,22233,22236,22237,22239,22241,22244,22245,22246,22247,22248,22257,22251,22253,22262,22263,22273,22274,22279,22282,22284,22289,22293,22298,22299,22301,22304,22306,22307,22308,22309,22313,22314,22316,22318,22319,22323,22324,22333,22334,22335,22341,22342,22348,22349,22354,22370,22373,22375,22376,22379,22381,22382,22383,22384,22385,22387,22388,22389,22391,22393,22394,22395,22396,22398,22401,22403,22412,22420,22423,22425,22426,22428,22429,22430,22431,22433,22421,22439,22440,22441,22444,22456,22461,22471,22472,22476,22479,22485,22493,22494,22500,22502,22503,22505,22509,22512,22517,22518,22520,22525,22526,22527,22531,22532,22536,22537,22497,22540,22541,22555,22558,22559,22560,22566,22567,22573,22578,22585,22591,22601,22604,22605,22607,22608,22613,22623,22625,22628,22631,22632,22648,22652,22655,22656,22657,22663,22664,22665,22666,22668,22669,22671,22672,22676,22678,22685,22688,22689,22690,22694,22697,22705,22706,22724,22716,22722,22728,22733,22734,22736,22738,22740,22742,22746,22749,22753,22754,22761,22771,22789,22790,22795,22796,22802,22803,22804,34369,22813,22817,22819,22820,22824,22831,22832,22835,22837,22838,22847,22851,22854,22866,22867,22873,22875,22877,22878,22879,22881,22883,22891,22893,22895,22898,22901,22902,22905,22907,22908,22923,22924,22926,22930,22933,22935,22943,22948,22951,22957,22958,22959,22960,22963,22967,22970,22972,22977,22979,22980,22984,22986,22989,22994,23005,23006,23007,23011,23012,23015,23022,23023,23025,23026,23028,23031,23040,23044,23052,23053,23054,23058,23059,23070,23075,23076,23079,23080,23082,23085,23088,23108,23109,23111,23112,23116,23120,23125,23134,23139,23141,23143,23149,23159,23162,23163,23166,23179,23184,23187,23190,23193,23196,23198,23199,23200,23202,23207,23212,23217,23218,23219,23221,23224,23226,23227,23231,23236,23238,23240,23247,23258,23260,23264,23269,23274,23278,23285,23286,23293,23296,23297,23304,23319,23348,23321,23323,23325,23329,23333,23341,23352,23361,23371,23372,23378,23382,23390,23400,23406,23407,23420,23421,23422,23423,23425,23428,23430,23434,23438,23440,23441,23443,23444,23446,23464,23465,23468,23469,23471,23473,23474,23479,23482,23484,23488,23489,23501,23503,23510,23511,23512,23513,23514,23520,23535,23537,23540,23549,23564,23575,23582,23583,23587,23590,23593,23595,23596,23598,23600,23602,23605,23606,23641,23642,23644,23650,23651,23655,23656,23657,23661,23664,23668,23669,23674,23675,23676,23677,23687,23688,23690,23695,23698,23709,23711,23712,23714,23715,23718,23722,23730,23732,23733,23738,23753,23755,23762,23773,23767,23790,23793,23794,23796,23809,23814,23821,23826,23851,23843,23844,23846,23847,23857,23860,23865,23869,23871,23874,23875,23878,23880,23893,23889,23897,23882,23903,23904,23905,23906,23908,23914,23917,23920,23929,23930,23934,23935,23937,23939,23944,23946,23954,23955,23956,23957,23961,23963,23967,23968,23975,23979,23984,23988,23992,23993,24003,24007,24011,24016,24014,24024,24025,24032,24036,24041,24056,24057,24064,24071,24077,24082,24084,24085,24088,24095,24096,24110,24104,24114,24117,24126,24139,24144,24137,24145,24150,24152,24155,24156,24158,24168,24170,24171,24172,24173,24174,24176,24192,24203,24206,24226,24228,24229,24232,24234,24236,24241,24243,24253,24254,24255,24262,24268,24267,24270,24273,24274,24276,24277,24284,24286,24293,24299,24322,24326,24327,24328,24334,24345,24348,24349,24353,24354,24355,24356,24360,24363,24364,24366,24368,24372,24374,24379,24381,24383,24384,24388,24389,24391,24397,24400,24404,24408,24411,24416,24419,24420,24423,24431,24434,24436,24437,24440,24442,24445,24446,24457,24461,24463,24470,24476,24477,24482,24487,24491,24484,24492,24495,24496,24497,24504,24516,24519,24520,24521,24523,24528,24529,24530,24531,24532,24542,24545,24546,24552,24553,24554,24556,24557,24558,24559,24562,24563,24566,24570,24572,24583,24586,24589,24595,24596,24599,24600,24602,24607,24612,24621,24627,24629,24640,24647,24648,24649,24652,24657,24660,24662,24663,24669,24673,24679,24689,24702,24703,24706,24710,24712,24714,24718,24721,24723,24725,24728,24733,24734,24738,24740,24741,24744,24752,24753,24759,24763,24766,24770,24772,24776,24777,24778,24779,24782,24783,24788,24789,24793,24795,24797,24798,24802,24805,24818,24821,24824,24828,24829,24834,24839,24842,24844,24848,24849,24850,24851,24852,24854,24855,24857,24860,24862,24866,24874,24875,24880,24881,24885,24886,24887,24889,24897,24901,24902,24905,24926,24928,24940,24946,24952,24955,24956,24959,24960,24961,24963,24964,24971,24973,24978,24979,24983,24984,24988,24989,24991,24992,24997,25000,25002,25005,25016,25017,25020,25024,25025,25026,25038,25039,25045,25052,25053,25054,25055,25057,25058,25063,25065,25061,25068,25069,25071,25089,25091,25092,25095,25107,25109,25116,25120,25122,25123,25127,25129,25131,25145,25149,25154,25155,25156,25158,25164,25168,25169,25170,25172,25174,25178,25180,25188,25197,25199,25203,25210,25213,25229,25230,25231,25232,25254,25256,25267,25270,25271,25274,25278,25279,25284,25294,25301,25302,25306,25322,25330,25332,25340,25341,25347,25348,25354,25355,25357,25360,25363,25366,25368,25385,25386,25389,25397,25398,25401,25404,25409,25410,25411,25412,25414,25418,25419,25422,25426,25427,25428,25432,25435,25445,25446,25452,25453,25457,25460,25461,25464,25468,25469,25471,25474,25476,25479,25482,25488,25492,25493,25497,25498,25502,25508,25510,25517,25518,25519,25533,25537,25541,25544,25550,25553,25555,25556,25557,25564,25568,25573,25578,25580,25586,25587,25589,25592,25593,25609,25610,25616,25618,25620,25624,25630,25632,25634,25636,25637,25641,25642,25647,25648,25653,25661,25663,25675,25679,25681,25682,25683,25684,25690,25691,25692,25693,25695,25696,25697,25699,25709,25715,25716,25723,25725,25733,25735,25743,25744,25745,25752,25753,25755,25757,25759,25761,25763,25766,25768,25772,25779,25789,25790,25791,25796,25801,25802,25803,25804,25806,25808,25809,25813,25815,25828,25829,25833,25834,25837,25840,25845,25847,25851,25855,25857,25860,25864,25865,25866,25871,25875,25876,25878,25881,25883,25886,25887,25890,25894,25897,25902,25905,25914,25916,25917,25923,25927,25929,25936,25938,25940,25951,25952,25959,25963,25978,25981,25985,25989,25994,26002,26005,26008,26013,26016,26019,26022,26030,26034,26035,26036,26047,26050,26056,26057,26062,26064,26068,26070,26072,26079,26096,26098,26100,26101,26105,26110,26111,26112,26116,26120,26121,26125,26129,26130,26133,26134,26141,26142,26145,26146,26147,26148,26150,26153,26154,26155,26156,26158,26160,26161,26163,26169,26167,26176,26181,26182,26186,26188,26193,26190,26199,26200,26201,26203,26204,26208,26209,26363,26218,26219,26220,26238,26227,26229,26239,26231,26232,26233,26235,26240,26236,26251,26252,26253,26256,26258,26265,26266,26267,26268,26271,26272,26276,26285,26289,26290,26293,26299,26303,26304,26306,26307,26312,26316,26318,26319,26324,26331,26335,26344,26347,26348,26350,26362,26373,26375,26382,26387,26393,26396,26400,26402,26419,26430,26437,26439,26440,26444,26452,26453,26461,26470,26476,26478,26484,26486,26491,26497,26500,26510,26511,26513,26515,26518,26520,26521,26523,26544,26545,26546,26549,26555,26556,26557,26617,26560,26562,26563,26565,26568,26569,26578,26583,26585,26588,26593,26598,26608,26610,26614,26615,26706,26644,26649,26653,26655,26664,26663,26668,26669,26671,26672,26673,26675,26683,26687,26692,26693,26698,26700,26709,26711,26712,26715,26731,26734,26735,26736,26737,26738,26741,26745,26746,26747,26748,26754,26756,26758,26760,26774,26776,26778,26780,26785,26787,26789,26793,26794,26798,26802,26811,26821,26824,26828,26831,26832,26833,26835,26838,26841,26844,26845,26853,26856,26858,26859,26860,26861,26864,26865,26869,26870,26875,26876,26877,26886,26889,26890,26896,26897,26899,26902,26903,26929,26931,26933,26936,26939,26946,26949,26953,26958,26967,26971,26979,26980,26981,26982,26984,26985,26988,26992,26993,26994,27002,27003,27007,27008,27021,27026,27030,27032,27041,27045,27046,27048,27051,27053,27055,27063,27064,27066,27068,27077,27080,27089,27094,27095,27106,27109,27118,27119,27121,27123,27125,27134,27136,27137,27139,27151,27153,27157,27162,27165,27168,27172,27176,27184,27186,27188,27191,27195,27198,27199,27205,27206,27209,27210,27214,27216,27217,27218,27221,27222,27227,27236,27239,27242,27249,27251,27262,27265,27267,27270,27271,27273,27275,27281,27291,27293,27294,27295,27301,27307,27311,27312,27313,27316,27325,27326,27327,27334,27337,27336,27340,27344,27348,27349,27350,27356,27357,27364,27367,27372,27376,27377,27378,27388,27389,27394,27395,27398,27399,27401,27407,27408,27409,27415,27419,27422,27428,27432,27435,27436,27439,27445,27446,27451,27455,27462,27466,27469,27474,27478,27480,27485,27488,27495,27499,27502,27504,27509,27517,27518,27522,27525,27543,27547,27551,27552,27554,27555,27560,27561,27564,27565,27566,27568,27576,27577,27581,27582,27587,27588,27593,27596,27606,27610,27617,27619,27622,27623,27630,27633,27639,27641,27647,27650,27652,27653,27657,27661,27662,27664,27666,27673,27679,27686,27687,27688,27692,27694,27699,27701,27702,27706,27707,27711,27722,27723,27725,27727,27730,27732,27737,27739,27740,27755,27757,27759,27764,27766,27768,27769,27771,27781,27782,27783,27785,27796,27797,27799,27800,27804,27807,27824,27826,27828,27842,27846,27853,27855,27856,27857,27858,27860,27862,27866,27868,27872,27879,27881,27883,27884,27886,27890,27892,27908,27911,27914,27918,27919,27921,27923,27930,27942,27943,27944,27751,27950,27951,27953,27961,27964,27967,27991,27998,27999,28001,28005,28007,28015,28016,28028,28034,28039,28049,28050,28052,28054,28055,28056,28074,28076,28084,28087,28089,28093,28095,28100,28104,28106,28110,28111,28118,28123,28125,28127,28128,28130,28133,28137,28143,28144,28148,28150,28156,28160,28164,28190,28194,28199,28210,28214,28217,28219,28220,28228,28229,28232,28233,28235,28239,28241,28242,28243,28244,28247,28252,28253,28254,28258,28259,28264,28275,28283,28285,28301,28307,28313,28320,28327,28333,28334,28337,28339,28347,28351,28352,28353,28355,28359,28360,28362,28365,28366,28367,28395,28397,28398,28409,28411,28413,28420,28424,28426,28428,28429,28438,28440,28442,28443,28454,28457,28458,28463,28464,28467,28470,28475,28476,28461,28495,28497,28498,28499,28503,28505,28506,28509,28510,28513,28514,28520,28524,28541,28542,28547,28551,28552,28555,28556,28557,28560,28562,28563,28564,28566,28570,28575,28576,28581,28582,28583,28584,28590,28591,28592,28597,28598,28604,28613,28615,28616,28618,28634,28638,28648,28649,28656,28661,28665,28668,28669,28672,28677,28678,28679,28685,28695,28704,28707,28719,28724,28727,28729,28732,28739,28740,28744,28745,28746,28747,28756,28757,28765,28766,28750,28772,28773,28780,28782,28789,28790,28798,28801,28805,28806,28820,28821,28822,28823,28824,28827,28836,28843,28848,28849,28852,28855,28874,28881,28883,28884,28885,28886,28888,28892,28900,28922,28931,28932,28933,28934,28935,28939,28940,28943,28958,28960,28971,28973,28975,28976,28977,28984,28993,28997,28998,28999,29002,29003,29008,29010,29015,29018,29020,29022,29024,29032,29049,29056,29061,29063,29068,29074,29082,29083,29088,29090,29103,29104,29106,29107,29114,29119,29120,29121,29124,29131,29132,29139,29142,29145,29146,29148,29176,29182,29184,29191,29192,29193,29203,29207,29210,29213,29215,29220,29227,29231,29236,29240,29241,29249,29250,29251,29253,29262,29263,29264,29267,29269,29270,29274,29276,29278,29280,29283,29288,29291,29294,29295,29297,29303,29304,29307,29308,29311,29316,29321,29325,29326,29331,29339,29352,29357,29358,29361,29364,29374,29377,29383,29385,29388,29397,29398,29400,29407,29413,29427,29428,29434,29435,29438,29442,29444,29445,29447,29451,29453,29458,29459,29464,29465,29470,29474,29476,29479,29480,29484,29489,29490,29493,29498,29499,29501,29507,29517,29520,29522,29526,29528,29533,29534,29535,29536,29542,29543,29545,29547,29548,29550,29551,29553,29559,29561,29564,29568,29569,29571,29573,29574,29582,29584,29587,29589,29591,29592,29596,29598,29599,29600,29602,29605,29606,29610,29611,29613,29621,29623,29625,29628,29629,29631,29637,29638,29641,29643,29644,29647,29650,29651,29654,29657,29661,29665,29667,29670,29671,29673,29684,29685,29687,29689,29690,29691,29693,29695,29696,29697,29700,29703,29706,29713,29722,29723,29732,29734,29736,29737,29738,29739,29740,29741,29742,29743,29744,29745,29753,29760,29763,29764,29766,29767,29771,29773,29777,29778,29783,29789,29794,29798,29799,29800,29803,29805,29806,29809,29810,29824,29825,29829,29830,29831,29833,29839,29840,29841,29842,29848,29849,29850,29852,29855,29856,29857,29859,29862,29864,29865,29866,29867,29870,29871,29873,29874,29877,29881,29883,29887,29896,29897,29900,29904,29907,29912,29914,29915,29918,29919,29924,29928,29930,29931,29935,29940,29946,29947,29948,29951,29958,29970,29974,29975,29984,29985,29988,29991,29993,29994,29999,30006,30009,30013,30014,30015,30016,30019,30023,30024,30030,30032,30034,30039,30046,30047,30049,30063,30065,30073,30074,30075,30076,30077,30078,30081,30085,30096,30098,30099,30101,30105,30108,30114,30116,30132,30138,30143,30144,30145,30148,30150,30156,30158,30159,30167,30172,30175,30176,30177,30180,30183,30188,30190,30191,30193,30201,30208,30210,30211,30212,30215,30216,30218,30220,30223,30226,30227,30229,30230,30233,30235,30236,30237,30238,30243,30245,30246,30249,30253,30258,30259,30261,30264,30265,30266,30268,30282,30272,30273,30275,30276,30277,30281,30283,30293,30297,30303,30308,30309,30317,30318,30319,30321,30324,30337,30341,30348,30349,30357,30363,30364,30365,30367,30368,30370,30371,30372,30373,30374,30375,30376,30378,30381,30397,30401,30405,30409,30411,30412,30414,30420,30425,30432,30438,30440,30444,30448,30449,30454,30457,30460,30464,30470,30474,30478,30482,30484,30485,30487,30489,30490,30492,30498,30504,30509,30510,30511,30516,30517,30518,30521,30525,30526,30530,30533,30534,30538,30541,30542,30543,30546,30550,30551,30556,30558,30559,30560,30562,30564,30567,30570,30572,30576,30578,30579,30580,30586,30589,30592,30596,30604,30605,30612,30613,30614,30618,30623,30626,30631,30634,30638,30639,30641,30645,30654,30659,30665,30673,30674,30677,30681,30686,30687,30688,30692,30694,30698,30700,30704,30705,30708,30712,30715,30725,30726,30729,30733,30734,30737,30749,30753,30754,30755,30765,30766,30768,30773,30775,30787,30788,30791,30792,30796,30798,30802,30812,30814,30816,30817,30819,30820,30824,30826,30830,30842,30846,30858,30863,30868,30872,30881,30877,30878,30879,30884,30888,30892,30893,30896,30897,30898,30899,30907,30909,30911,30919,30920,30921,30924,30926,30930,30931,30933,30934,30948,30939,30943,30944,30945,30950,30954,30962,30963,30976,30966,30967,30970,30971,30975,30982,30988,30992,31002,31004,31006,31007,31008,31013,31015,31017,31021,31025,31028,31029,31035,31037,31039,31044,31045,31046,31050,31051,31055,31057,31060,31064,31067,31068,31079,31081,31083,31090,31097,31099,31100,31102,31115,31116,31121,31123,31124,31125,31126,31128,31131,31132,31137,31144,31145,31147,31151,31153,31156,31160,31163,31170,31172,31175,31176,31178,31183,31188,31190,31194,31197,31198,31200,31202,31205,31210,31211,31213,31217,31224,31228,31234,31235,31239,31241,31242,31244,31249,31253,31259,31262,31265,31271,31275,31277,31279,31280,31284,31285,31288,31289,31290,31300,31301,31303,31304,31308,31317,31318,31321,31324,31325,31327,31328,31333,31335,31338,31341,31349,31352,31358,31360,31362,31365,31366,31370,31371,31376,31377,31380,31390,31392,31395,31404,31411,31413,31417,31419,31420,31430,31433,31436,31438,31441,31451,31464,31465,31467,31468,31473,31476,31483,31485,31486,31495,31508,31519,31523,31527,31529,31530,31531,31533,31534,31535,31536,31537,31540,31549,31551,31552,31553,31559,31566,31573,31584,31588,31590,31593,31594,31597,31599,31602,31603,31607,31620,31625,31630,31632,31633,31638,31643,31646,31648,31653,31660,31663,31664,31666,31669,31670,31674,31675,31676,31677,31682,31685,31688,31690,31700,31702,31703,31705,31706,31707,31720,31722,31730,31732,31733,31736,31737,31738,31740,31742,31745,31746,31747,31748,31750,31753,31755,31756,31758,31759,31769,31771,31776,31781,31782,31784,31788,31793,31795,31796,31798,31801,31802,31814,31818,31829,31825,31826,31827,31833,31834,31835,31836,31837,31838,31841,31843,31847,31849,31853,31854,31856,31858,31865,31868,31869,31878,31879,31887,31892,31902,31904,31910,31920,31926,31927,31930,31931,31932,31935,31940,31943,31944,31945,31949,31951,31955,31956,31957,31959,31961,31962,31965,31974,31977,31979,31989,32003,32007,32008,32009,32015,32017,32018,32019,32022,32029,32030,32035,32038,32042,32045,32049,32060,32061,32062,32064,32065,32071,32072,32077,32081,32083,32087,32089,32090,32092,32093,32101,32103,32106,32112,32120,32122,32123,32127,32129,32130,32131,32133,32134,32136,32139,32140,32141,32145,32150,32151,32157,32158,32166,32167,32170,32179,32182,32183,32185,32194,32195,32196,32197,32198,32204,32205,32206,32215,32217,32256,32226,32229,32230,32234,32235,32237,32241,32245,32246,32249,32250,32264,32272,32273,32277,32279,32284,32285,32288,32295,32296,32300,32301,32303,32307,32310,32319,32324,32325,32327,32334,32336,32338,32344,32351,32353,32354,32357,32363,32366,32367,32371,32376,32382,32385,32390,32391,32394,32397,32401,32405,32408,32410,32413,32414,32572,32571,32573,32574,32575,32579,32580,32583,32591,32594,32595,32603,32604,32605,32609,32611,32612,32613,32614,32621,32625,32637,32638,32639,32640,32651,32653,32655,32656,32657,32662,32663,32668,32673,32674,32678,32682,32685,32692,32700,32703,32704,32707,32712,32718,32719,32731,32735,32739,32741,32744,32748,32750,32751,32754,32762,32765,32766,32767,32775,32776,32778,32781,32782,32783,32785,32787,32788,32790,32797,32798,32799,32800,32804,32806,32812,32814,32816,32820,32821,32823,32825,32826,32828,32830,32832,32836,32864,32868,32870,32877,32881,32885,32897,32904,32910,32924,32926,32934,32935,32939,32952,32953,32968,32973,32975,32978,32980,32981,32983,32984,32992,33005,33006,33008,33010,33011,33014,33017,33018,33022,33027,33035,33046,33047,33048,33052,33054,33056,33060,33063,33068,33072,33077,33082,33084,33093,33095,33098,33100,33106,33111,33120,33121,33127,33128,33129,33133,33135,33143,33153,33168,33156,33157,33158,33163,33166,33174,33176,33179,33182,33186,33198,33202,33204,33211,33227,33219,33221,33226,33230,33231,33237,33239,33243,33245,33246,33249,33252,33259,33260,33264,33265,33266,33269,33270,33272,33273,33277,33279,33280,33283,33295,33299,33300,33305,33306,33309,33313,33314,33320,33330,33332,33338,33347,33348,33349,33350,33355,33358,33359,33361,33366,33372,33376,33379,33383,33389,33396,33403,33405,33407,33408,33409,33411,33412,33415,33417,33418,33422,33425,33428,33430,33432,33434,33435,33440,33441,33443,33444,33447,33448,33449,33450,33454,33456,33458,33460,33463,33466,33468,33470,33471,33478,33488,33493,33498,33504,33506,33508,33512,33514,33517,33519,33526,33527,33533,33534,33536,33537,33543,33544,33546,33547,33620,33563,33565,33566,33567,33569,33570,33580,33581,33582,33584,33587,33591,33594,33596,33597,33602,33603,33604,33607,33613,33614,33617,33621,33622,33623,33648,33656,33661,33663,33664,33666,33668,33670,33677,33682,33684,33685,33688,33689,33691,33692,33693,33702,33703,33705,33708,33726,33727,33728,33735,33737,33743,33744,33745,33748,33757,33619,33768,33770,33782,33784,33785,33788,33793,33798,33802,33807,33809,33813,33817,33709,33839,33849,33861,33863,33864,33866,33869,33871,33873,33874,33878,33880,33881,33882,33884,33888,33892,33893,33895,33898,33904,33907,33908,33910,33912,33916,33917,33921,33925,33938,33939,33941,33950,33958,33960,33961,33962,33967,33969,33972,33978,33981,33982,33984,33986,33991,33992,33996,33999,34003,34012,34023,34026,34031,34032,34033,34034,34039,34098,34042,34043,34045,34050,34051,34055,34060,34062,34064,34076,34078,34082,34083,34084,34085,34087,34090,34091,34095,34099,34100,34102,34111,34118,34127,34128,34129,34130,34131,34134,34137,34140,34141,34142,34143,34144,34145,34146,34148,34155,34159,34169,34170,34171,34173,34175,34177,34181,34182,34185,34187,34188,34191,34195,34200,34205,34207,34208,34210,34213,34215,34228,34230,34231,34232,34236,34237,34238,34239,34242,34247,34250,34251,34254,34221,34264,34266,34271,34272,34278,34280,34285,34291,34294,34300,34303,34304,34308,34309,34317,34318,34320,34321,34322,34328,34329,34331,34334,34337,34343,34345,34358,34360,34362,34364,34365,34368,34370,34374,34386,34387,34390,34391,34392,34393,34397,34400,34401,34402,34403,34404,34409,34412,34415,34421,34422,34423,34426,34445,34449,34454,34456,34458,34460,34465,34470,34471,34472,34477,34481,34483,34484,34485,34487,34488,34489,34495,34496,34497,34499,34501,34513,34514,34517,34519,34522,34524,34528,34531,34533,34535,34440,34554,34556,34557,34564,34565,34567,34571,34574,34575,34576,34579,34580,34585,34590,34591,34593,34595,34600,34606,34607,34609,34610,34617,34618,34620,34621,34622,34624,34627,34629,34637,34648,34653,34657,34660,34661,34671,34673,34674,34683,34691,34692,34693,34694,34695,34696,34697,34699,34700,34704,34707,34709,34711,34712,34713,34718,34720,34723,34727,34732,34733,34734,34737,34741,34750,34751,34753,34760,34761,34762,34766,34773,34774,34777,34778,34780,34783,34786,34787,34788,34794,34795,34797,34801,34803,34808,34810,34815,34817,34819,34822,34825,34826,34827,34832,34841,34834,34835,34836,34840,34842,34843,34844,34846,34847,34856,34861,34862,34864,34866,34869,34874,34876,34881,34883,34885,34888,34889,34890,34891,34894,34897,34901,34902,34904,34906,34908,34911,34912,34916,34921,34929,34937,34939,34944,34968,34970,34971,34972,34975,34976,34984,34986,35002,35005,35006,35008,35018,35019,35020,35021,35022,35025,35026,35027,35035,35038,35047,35055,35056,35057,35061,35063,35073,35078,35085,35086,35087,35093,35094,35096,35097,35098,35100,35104,35110,35111,35112,35120,35121,35122,35125,35129,35130,35134,35136,35138,35141,35142,35145,35151,35154,35159,35162,35163,35164,35169,35170,35171,35179,35182,35184,35187,35189,35194,35195,35196,35197,35209,35213,35216,35220,35221,35227,35228,35231,35232,35237,35248,35252,35253,35254,35255,35260,35284,35285,35286,35287,35288,35301,35305,35307,35309,35313,35315,35318,35321,35325,35327,35332,35333,35335,35343,35345,35346,35348,35349,35358,35360,35362,35364,35366,35371,35372,35375,35381,35383,35389,35390,35392,35395,35397,35399,35401,35405,35406,35411,35414,35415,35416,35420,35421,35425,35429,35431,35445,35446,35447,35449,35450,35451,35454,35455,35456,35459,35462,35467,35471,35472,35474,35478,35479,35481,35487,35495,35497,35502,35503,35507,35510,35511,35515,35518,35523,35526,35528,35529,35530,35537,35539,35540,35541,35543,35549,35551,35564,35568,35572,35573,35574,35580,35583,35589,35590,35595,35601,35612,35614,35615,35594,35629,35632,35639,35644,35650,35651,35652,35653,35654,35656,35666,35667,35668,35673,35661,35678,35683,35693,35702,35704,35705,35708,35710,35713,35716,35717,35723,35725,35727,35732,35733,35740,35742,35743,35896,35897,35901,35902,35909,35911,35913,35915,35919,35921,35923,35924,35927,35928,35931,35933,35929,35939,35940,35942,35944,35945,35949,35955,35957,35958,35963,35966,35974,35975,35979,35984,35986,35987,35993,35995,35996,36004,36025,36026,36037,36038,36041,36043,36047,36054,36053,36057,36061,36065,36072,36076,36079,36080,36082,36085,36087,36088,36094,36095,36097,36099,36105,36114,36119,36123,36197,36201,36204,36206,36223,36226,36228,36232,36237,36240,36241,36245,36254,36255,36256,36262,36267,36268,36271,36274,36277,36279,36281,36283,36288,36293,36294,36295,36296,36298,36302,36305,36308,36309,36311,36313,36324,36325,36327,36332,36336,36284,36337,36338,36340,36349,36353,36356,36357,36358,36363,36369,36372,36374,36384,36385,36386,36387,36390,36391,36401,36403,36406,36407,36408,36409,36413,36416,36417,36427,36429,36430,36431,36436,36443,36444,36445,36446,36449,36450,36457,36460,36461,36463,36464,36465,36473,36474,36475,36482,36483,36489,36496,36498,36501,36506,36507,36509,36510,36514,36519,36521,36525,36526,36531,36533,36538,36539,36544,36545,36547,36548,36551,36559,36561,36564,36572,36584,36590,36592,36593,36599,36601,36602,36589,36608,36610,36615,36616,36623,36624,36630,36631,36632,36638,36640,36641,36643,36645,36647,36648,36652,36653,36654,36660,36661,36662,36663,36666,36672,36673,36675,36679,36687,36689,36690,36691,36692,36693,36696,36701,36702,36709,36765,36768,36769,36772,36773,36774,36789,36790,36792,36798,36800,36801,36806,36810,36811,36813,36816,36818,36819,36821,36832,36835,36836,36840,36846,36849,36853,36854,36859,36862,36866,36868,36872,36876,36888,36891,36904,36905,36911,36906,36908,36909,36915,36916,36919,36927,36931,36932,36940,36955,36957,36962,36966,36967,36972,36976,36980,36985,36997,37000,37003,37004,37006,37008,37013,37015,37016,37017,37019,37024,37025,37026,37029,37040,37042,37043,37044,37046,37053,37068,37054,37059,37060,37061,37063,37064,37077,37079,37080,37081,37084,37085,37087,37093,37074,37110,37099,37103,37104,37108,37118,37119,37120,37124,37125,37126,37128,37133,37136,37140,37142,37143,37144,37146,37148,37150,37152,37157,37154,37155,37159,37161,37166,37167,37169,37172,37174,37175,37177,37178,37180,37181,37187,37191,37192,37199,37203,37207,37209,37210,37211,37217,37220,37223,37229,37236,37241,37242,37243,37249,37251,37253,37254,37258,37262,37265,37267,37268,37269,37272,37278,37281,37286,37288,37292,37293,37294,37296,37297,37298,37299,37302,37307,37308,37309,37311,37314,37315,37317,37331,37332,37335,37337,37338,37342,37348,37349,37353,37354,37356,37357,37358,37359,37360,37361,37367,37369,37371,37373,37376,37377,37380,37381,37382,37383,37385,37386,37388,37392,37394,37395,37398,37400,37404,37405,37411,37412,37413,37414,37416,37422,37423,37424,37427,37429,37430,37432,37433,37434,37436,37438,37440,37442,37443,37446,37447,37450,37453,37454,37455,37457,37464,37465,37468,37469,37472,37473,37477,37479,37480,37481,37486,37487,37488,37493,37494,37495,37496,37497,37499,37500,37501,37503,37512,37513,37514,37517,37518,37522,37527,37529,37535,37536,37540,37541,37543,37544,37547,37551,37554,37558,37560,37562,37563,37564,37565,37567,37568,37569,37570,37571,37573,37574,37575,37576,37579,37580,37581,37582,37584,37587,37589,37591,37592,37593,37596,37597,37599,37600,37601,37603,37605,37607,37608,37612,37614,37616,37625,37627,37631,37632,37634,37640,37645,37649,37652,37653,37660,37661,37662,37663,37665,37668,37669,37671,37673,37674,37683,37684,37686,37687,37703,37704,37705,37712,37713,37714,37717,37719,37720,37722,37726,37732,37733,37735,37737,37738,37741,37743,37744,37745,37747,37748,37750,37754,37757,37759,37760,37761,37762,37768,37770,37771,37773,37775,37778,37781,37784,37787,37790,37793,37795,37796,37798,37800,37803,37812,37813,37814,37818,37801,37825,37828,37829,37830,37831,37833,37834,37835,37836,37837,37843,37849,37852,37854,37855,37858,37862,37863,37881,37879,37880,37882,37883,37885,37889,37890,37892,37896,37897,37901,37902,37903,37909,37910,37911,37919,37934,37935,37937,37938,37939,37940,37947,37951,37949,37955,37957,37960,37962,37964,37973,37977,37980,37983,37985,37987,37992,37995,37997,37998,37999,38001,38002,38020,38019,38264,38265,38270,38276,38280,38284,38285,38286,38301,38302,38303,38305,38310,38313,38315,38316,38324,38326,38330,38333,38335,38342,38344,38345,38347,38352,38353,38354,38355,38361,38362,38365,38366,38367,38368,38372,38374,38429,38430,38434,38436,38437,38438,38444,38449,38451,38455,38456,38457,38458,38460,38461,38465,38482,38484,38486,38487,38488,38497,38510,38516,38523,38524,38526,38527,38529,38530,38531,38532,38537,38545,38550,38554,38557,38559,38564,38565,38566,38569,38574,38575,38579,38586,38602,38610,23986,38616,38618,38621,38622,38623,38633,38639,38641,38650,38658,38659,38661,38665,38682,38683,38685,38689,38690,38691,38696,38705,38707,38721,38723,38730,38734,38735,38741,38743,38744,38746,38747,38755,38759,38762,38766,38771,38774,38775,38776,38779,38781,38783,38784,38793,38805,38806,38807,38809,38810,38814,38815,38818,38828,38830,38833,38834,38837,38838,38840,38841,38842,38844,38846,38847,38849,38852,38853,38855,38857,38858,38860,38861,38862,38864,38865,38868,38871,38872,38873,38877,38878,38880,38875,38881,38884,38895,38897,38900,38903,38904,38906,38919,38922,38937,38925,38926,38932,38934,38940,38942,38944,38947,38950,38955,38958,38959,38960,38962,38963,38965,38949,38974,38980,38983,38986,38993,38994,38995,38998,38999,39001,39002,39010,39011,39013,39014,39018,39020,39083,39085,39086,39088,39092,39095,39096,39098,39099,39103,39106,39109,39112,39116,39137,39139,39141,39142,39143,39146,39155,39158,39170,39175,39176,39185,39189,39190,39191,39194,39195,39196,39199,39202,39206,39207,39211,39217,39218,39219,39220,39221,39225,39226,39227,39228,39232,39233,39238,39239,39240,39245,39246,39252,39256,39257,39259,39260,39262,39263,39264,39323,39325,39327,39334,39344,39345,39346,39349,39353,39354,39357,39359,39363,39369,39379,39380,39385,39386,39388,39390,39399,39402,39403,39404,39408,39412,39413,39417,39421,39422,39426,39427,39428,39435,39436,39440,39441,39446,39454,39456,39458,39459,39460,39463,39469,39470,39475,39477,39478,39480,39495,39489,39492,39498,39499,39500,39502,39505,39508,39510,39517,39594,39596,39598,39599,39602,39604,39605,39606,39609,39611,39614,39615,39617,39619,39622,39624,39630,39632,39634,39637,39638,39639,39643,39644,39648,39652,39653,39655,39657,39660,39666,39667,39669,39673,39674,39677,39679,39680,39681,39682,39683,39684,39685,39688,39689,39691,39692,39693,39694,39696,39698,39702,39705,39707,39708,39712,39718,39723,39725,39731,39732,39733,39735,39737,39738,39741,39752,39755,39756,39765,39766,39767,39771,39774,39777,39779,39781,39782,39784,39786,39787,39788,39789,39790,39795,39797,39799,39800,39801,39807,39808,39812,39813,39814,39815,39817,39818,39819,39821,39823,39824,39828,39834,39837,39838,39846,39847,39849,39852,39856,39857,39858,39863,39864,39867,39868,39870,39871,39873,39879,39880,39886,39888,39895,39896,39901,39903,39909,39911,39914,39915,39919,39923,39927,39928,39929,39930,39933,39935,39936,39938,39947,39951,39953,39958,39960,39961,39962,39964,39966,39970,39971,39974,39975,39976,39977,39978,39985,39989,39990,39991,39997,40001,40003,40004,40005,40009,40010,40014,40015,40016,40019,40020,40022,40024,40027,40029,40030,40031,40035,40041,40042,40028,40043,40040,40046,40048,40050,40053,40055,40059,40166,40178,40183,40185,40203,40194,40209,40215,40216,40220,40221,40222,40239,40240,40242,40243,40244,40250,40252,40261,40253,40258,40259,40263,40266,40275,40276,40287,40291,40290,40293,40297,40298,40299,40304,40310,40311,40315,40316,40318,40323,40324,40326,40330,40333,40334,40338,40339,40341,40342,40343,40344,40353,40362,40364,40366,40369,40373,40377,40380,40383,40387,40391,40393,40394,40404,40405,40406,40407,40410,40414,40415,40416,40421,40423,40425,40427,40430,40432,40435,40436,40446,40458,40450,40455,40462,40464,40465,40466,40469,40470,40473,40476,40477,40570,40571,40572,40576,40578,40579,40580,40581,40583,40590,40591,40598,40600,40603,40606,40612,40616,40620,40622,40623,40624,40627,40628,40629,40646,40648,40651,40661,40671,40676,40679,40684,40685,40686,40688,40689,40690,40693,40696,40703,40706,40707,40713,40719,40720,40721,40722,40724,40726,40727,40729,40730,40731,40735,40738,40742,40746,40747,40751,40753,40754,40756,40759,40761,40762,40764,40765,40767,40769,40771,40772,40773,40774,40775,40787,40789,40790,40791,40792,40794,40797,40798,40808,40809,40813,40814,40815,40816,40817,40819,40821,40826,40829,40847,40848,40849,40850,40852,40854,40855,40862,40865,40866,40867,40869,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null], + "ibm864":[176,183,8729,8730,9618,9472,9474,9532,9508,9516,9500,9524,9488,9484,9492,9496,946,8734,966,177,189,188,8776,171,187,65271,65272,155,156,65275,65276,159,160,173,65154,163,164,65156,null,null,65166,65167,65173,65177,1548,65181,65185,65189,1632,1633,1634,1635,1636,1637,1638,1639,1640,1641,65233,1563,65201,65205,65209,1567,162,65152,65153,65155,65157,65226,65163,65165,65169,65171,65175,65179,65183,65187,65191,65193,65195,65197,65199,65203,65207,65211,65215,65217,65221,65227,65231,166,172,247,215,65225,1600,65235,65239,65243,65247,65251,65255,65259,65261,65263,65267,65213,65228,65230,65229,65249,65149,1617,65253,65257,65260,65264,65266,65232,65237,65269,65270,65245,65241,65265,9632,null], + "ibm866":[1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,9617,9618,9619,9474,9508,9569,9570,9558,9557,9571,9553,9559,9565,9564,9563,9488,9492,9524,9516,9500,9472,9532,9566,9567,9562,9556,9577,9574,9568,9552,9580,9575,9576,9572,9573,9561,9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1025,1105,1028,1108,1031,1111,1038,1118,176,8729,183,8730,8470,164,9632,160], + "iso-8859-2":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,728,321,164,317,346,167,168,352,350,356,377,173,381,379,176,261,731,322,180,318,347,711,184,353,351,357,378,733,382,380,340,193,194,258,196,313,262,199,268,201,280,203,282,205,206,270,272,323,327,211,212,336,214,215,344,366,218,368,220,221,354,223,341,225,226,259,228,314,263,231,269,233,281,235,283,237,238,271,273,324,328,243,244,337,246,247,345,367,250,369,252,253,355,729], + "iso-8859-3":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,294,728,163,164,null,292,167,168,304,350,286,308,173,null,379,176,295,178,179,180,181,293,183,184,305,351,287,309,189,null,380,192,193,194,null,196,266,264,199,200,201,202,203,204,205,206,207,null,209,210,211,212,288,214,215,284,217,218,219,220,364,348,223,224,225,226,null,228,267,265,231,232,233,234,235,236,237,238,239,null,241,242,243,244,289,246,247,285,249,250,251,252,365,349,729], + "iso-8859-4":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,312,342,164,296,315,167,168,352,274,290,358,173,381,175,176,261,731,343,180,297,316,711,184,353,275,291,359,330,382,331,256,193,194,195,196,197,198,302,268,201,280,203,278,205,206,298,272,325,332,310,212,213,214,215,216,370,218,219,220,360,362,223,257,225,226,227,228,229,230,303,269,233,281,235,279,237,238,299,273,326,333,311,244,245,246,247,248,371,250,251,252,361,363,729], + "iso-8859-5":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,173,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,8470,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,167,1118,1119], + "iso-8859-6":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,null,null,null,164,null,null,null,null,null,null,null,1548,173,null,null,null,null,null,null,null,null,null,null,null,null,null,1563,null,null,null,1567,null,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,null,null,null,null,null,1600,1601,1602,1603,1604,1605,1606,1607,1608,1609,1610,1611,1612,1613,1614,1615,1616,1617,1618,null,null,null,null,null,null,null,null,null,null,null,null,null], + "iso-8859-7":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,8216,8217,163,8364,8367,166,167,168,169,890,171,172,173,null,8213,176,177,178,179,900,901,902,183,904,905,906,187,908,189,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,null,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,null], + "iso-8859-8":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,null,162,163,164,165,166,167,168,169,215,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,247,187,188,189,190,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,8215,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,null,null,8206,8207,null], + "iso-8859-10":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,274,290,298,296,310,167,315,272,352,358,381,173,362,330,176,261,275,291,299,297,311,183,316,273,353,359,382,8213,363,331,256,193,194,195,196,197,198,302,268,201,280,203,278,205,206,207,208,325,332,211,212,213,214,360,216,370,218,219,220,221,222,223,257,225,226,227,228,229,230,303,269,233,281,235,279,237,238,239,240,326,333,243,244,245,246,361,248,371,250,251,252,253,254,312], + "iso-8859-13":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,8221,162,163,164,8222,166,167,216,169,342,171,172,173,174,198,176,177,178,179,8220,181,182,183,248,185,343,187,188,189,190,230,260,302,256,262,196,197,280,274,268,201,377,278,290,310,298,315,352,323,325,211,332,213,214,215,370,321,346,362,220,379,381,223,261,303,257,263,228,229,281,275,269,233,378,279,291,311,299,316,353,324,326,243,333,245,246,247,371,322,347,363,252,380,382,8217], + "iso-8859-14":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,7682,7683,163,266,267,7690,167,7808,169,7810,7691,7922,173,174,376,7710,7711,288,289,7744,7745,182,7766,7809,7767,7811,7776,7923,7812,7813,7777,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,372,209,210,211,212,213,214,7786,216,217,218,219,220,221,374,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,373,241,242,243,244,245,246,7787,248,249,250,251,252,253,375,255], + "iso-8859-15":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,8364,165,352,167,353,169,170,171,172,173,174,175,176,177,178,179,381,181,182,183,382,185,186,187,338,339,376,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255], + "iso-8859-16":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,261,321,8364,8222,352,167,353,169,536,171,377,173,378,379,176,177,268,322,381,8221,182,183,382,269,537,187,338,339,376,380,192,193,194,258,196,262,198,199,200,201,202,203,204,205,206,207,272,323,210,211,212,336,214,346,368,217,218,219,220,280,538,223,224,225,226,259,228,263,230,231,232,233,234,235,236,237,238,239,273,324,242,243,244,337,246,347,369,249,250,251,252,281,539,255], + "koi8-r":[9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9600,9604,9608,9612,9616,9617,9618,9619,8992,9632,8729,8730,8776,8804,8805,160,8993,176,178,183,247,9552,9553,9554,1105,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,1025,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,169,1102,1072,1073,1094,1076,1077,1092,1075,1093,1080,1081,1082,1083,1084,1085,1086,1087,1103,1088,1089,1090,1091,1078,1074,1100,1099,1079,1096,1101,1097,1095,1098,1070,1040,1041,1062,1044,1045,1060,1043,1061,1048,1049,1050,1051,1052,1053,1054,1055,1071,1056,1057,1058,1059,1046,1042,1068,1067,1047,1064,1069,1065,1063,1066], + "koi8-u":[9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9600,9604,9608,9612,9616,9617,9618,9619,8992,9632,8729,8730,8776,8804,8805,160,8993,176,178,183,247,9552,9553,9554,1105,1108,9556,1110,1111,9559,9560,9561,9562,9563,1169,9565,9566,9567,9568,9569,1025,1028,9571,1030,1031,9574,9575,9576,9577,9578,1168,9580,169,1102,1072,1073,1094,1076,1077,1092,1075,1093,1080,1081,1082,1083,1084,1085,1086,1087,1103,1088,1089,1090,1091,1078,1074,1100,1099,1079,1096,1101,1097,1095,1098,1070,1040,1041,1062,1044,1045,1060,1043,1061,1048,1049,1050,1051,1052,1053,1054,1055,1071,1056,1057,1058,1059,1046,1042,1068,1067,1047,1064,1069,1065,1063,1066], + "macintosh":[196,197,199,201,209,214,220,225,224,226,228,227,229,231,233,232,234,235,237,236,238,239,241,243,242,244,246,245,250,249,251,252,8224,176,162,163,167,8226,182,223,174,169,8482,180,168,8800,198,216,8734,177,8804,8805,165,181,8706,8721,8719,960,8747,170,186,937,230,248,191,161,172,8730,402,8776,8710,171,187,8230,160,192,195,213,338,339,8211,8212,8220,8221,8216,8217,247,9674,255,376,8260,8364,8249,8250,64257,64258,8225,183,8218,8222,8240,194,202,193,203,200,205,206,207,204,211,212,63743,210,218,219,217,305,710,732,175,728,729,730,184,733,731,711], + "windows-874":[8364,129,130,131,132,8230,134,135,136,137,138,139,140,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,152,153,154,155,156,157,158,159,160,3585,3586,3587,3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608,3609,3610,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,3630,3631,3632,3633,3634,3635,3636,3637,3638,3639,3640,3641,3642,null,null,null,null,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656,3657,3658,3659,3660,3661,3662,3663,3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675,null,null,null,null], + "windows-1250":[8364,129,8218,131,8222,8230,8224,8225,136,8240,352,8249,346,356,381,377,144,8216,8217,8220,8221,8226,8211,8212,152,8482,353,8250,347,357,382,378,160,711,728,321,164,260,166,167,168,169,350,171,172,173,174,379,176,177,731,322,180,181,182,183,184,261,351,187,317,733,318,380,340,193,194,258,196,313,262,199,268,201,280,203,282,205,206,270,272,323,327,211,212,336,214,215,344,366,218,368,220,221,354,223,341,225,226,259,228,314,263,231,269,233,281,235,283,237,238,271,273,324,328,243,244,337,246,247,345,367,250,369,252,253,355,729], + "windows-1251":[1026,1027,8218,1107,8222,8230,8224,8225,8364,8240,1033,8249,1034,1036,1035,1039,1106,8216,8217,8220,8221,8226,8211,8212,152,8482,1113,8250,1114,1116,1115,1119,160,1038,1118,1032,164,1168,166,167,1025,169,1028,171,172,173,174,1031,176,177,1030,1110,1169,181,182,183,1105,8470,1108,187,1112,1029,1109,1111,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103], + "windows-1252":[8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,381,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,382,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255], + "windows-1253":[8364,129,8218,402,8222,8230,8224,8225,136,8240,138,8249,140,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,152,8482,154,8250,156,157,158,159,160,901,902,163,164,165,166,167,168,169,null,171,172,173,174,8213,176,177,178,179,900,181,182,183,904,905,906,187,908,189,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,null,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,null], + "windows-1254":[8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,158,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,286,209,210,211,212,213,214,215,216,217,218,219,220,304,350,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,287,241,242,243,244,245,246,247,248,249,250,251,252,305,351,255], + "windows-1255":[8364,129,8218,402,8222,8230,8224,8225,710,8240,138,8249,140,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,154,8250,156,157,158,159,160,161,162,163,8362,165,166,167,168,169,215,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,247,187,188,189,190,191,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,null,1467,1468,1469,1470,1471,1472,1473,1474,1475,1520,1521,1522,1523,1524,null,null,null,null,null,null,null,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,null,null,8206,8207,null], + "windows-1256":[8364,1662,8218,402,8222,8230,8224,8225,710,8240,1657,8249,338,1670,1688,1672,1711,8216,8217,8220,8221,8226,8211,8212,1705,8482,1681,8250,339,8204,8205,1722,160,1548,162,163,164,165,166,167,168,169,1726,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,1563,187,188,189,190,1567,1729,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,1588,1589,1590,215,1591,1592,1593,1594,1600,1601,1602,1603,224,1604,226,1605,1606,1607,1608,231,232,233,234,235,1609,1610,238,239,1611,1612,1613,1614,244,1615,1616,247,1617,249,1618,251,252,8206,8207,1746], + "windows-1257":[8364,129,8218,131,8222,8230,8224,8225,136,8240,138,8249,140,168,711,184,144,8216,8217,8220,8221,8226,8211,8212,152,8482,154,8250,156,175,731,159,160,null,162,163,164,null,166,167,216,169,342,171,172,173,174,198,176,177,178,179,180,181,182,183,248,185,343,187,188,189,190,230,260,302,256,262,196,197,280,274,268,201,377,278,290,310,298,315,352,323,325,211,332,213,214,215,370,321,346,362,220,379,381,223,261,303,257,263,228,229,281,275,269,233,378,279,291,311,299,316,353,324,326,243,333,245,246,247,371,322,347,363,252,380,382,729], + "windows-1258":[8364,129,8218,402,8222,8230,8224,8225,710,8240,138,8249,338,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,154,8250,339,157,158,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,258,196,197,198,199,200,201,202,203,768,205,206,207,272,209,777,211,212,416,214,215,216,217,218,219,220,431,771,223,224,225,226,259,228,229,230,231,232,233,234,235,769,237,238,239,273,241,803,243,244,417,246,247,248,249,250,251,252,432,8363,255], + "x-mac-cyrillic":[1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,8224,176,1168,163,167,8226,182,1030,174,169,8482,1026,1106,8800,1027,1107,8734,177,8804,8805,1110,181,1169,1032,1028,1108,1031,1111,1033,1113,1034,1114,1112,1029,172,8730,402,8776,8710,171,187,8230,160,1035,1115,1036,1116,1109,8211,8212,8220,8221,8216,8217,247,8222,1038,1118,1039,1119,8470,1025,1105,1103,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,8364] +}; diff --git a/node_modules/multer/node_modules/busboy/deps/encoding/encoding.js b/node_modules/multer/node_modules/busboy/deps/encoding/encoding.js new file mode 100644 index 0000000..e3bc0a7 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/deps/encoding/encoding.js @@ -0,0 +1,2391 @@ +/* + Modifications for better node.js integration: + Copyright 2014 Brian White. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ +/* + Original source code: + Copyright 2014 Joshua Bell + + 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. +*/ + +// +// Utilities +// + +/** + * @param {number} a The number to test. + * @param {number} min The minimum value in the range, inclusive. + * @param {number} max The maximum value in the range, inclusive. + * @return {boolean} True if a >= min and a <= max. + */ +function inRange(a, min, max) { + return min <= a && a <= max; +} + +/** + * @param {number} n The numerator. + * @param {number} d The denominator. + * @return {number} The result of the integer division of n by d. + */ +function div(n, d) { + return Math.floor(n / d); +} + + +// +// Implementation of Encoding specification +// http://dvcs.w3.org/hg/encoding/raw-file/tip/Overview.html +// + +// +// 3. Terminology +// + +// +// 4. Encodings +// + +/** @const */ var EOF_byte = -1; +/** @const */ var EOF_code_point = -1; + +/** + * @constructor + * @param {Buffer} bytes Array of bytes that provide the stream. + */ +function ByteInputStream(bytes) { + /** @type {number} */ + var pos = 0; + + /** + * @this {ByteInputStream} + * @return {number} Get the next byte from the stream. + */ + this.get = function() { + return (pos >= bytes.length) ? EOF_byte : Number(bytes[pos]); + }; + + /** @param {number} n Number (positive or negative) by which to + * offset the byte pointer. */ + this.offset = function(n) { + pos += n; + if (pos < 0) { + throw new Error('Seeking past start of the buffer'); + } + if (pos > bytes.length) { + throw new Error('Seeking past EOF'); + } + }; + + /** + * @param {Array.} test Array of bytes to compare against. + * @return {boolean} True if the start of the stream matches the test + * bytes. + */ + this.match = function(test) { + if (test.length > pos + bytes.length) { + return false; + } + var i; + for (i = 0; i < test.length; i += 1) { + if (Number(bytes[pos + i]) !== test[i]) { + return false; + } + } + return true; + }; +} + +/** + * @constructor + * @param {Array.} bytes The array to write bytes into. + */ +function ByteOutputStream(bytes) { + /** @type {number} */ + var pos = 0; + + /** + * @param {...number} var_args The byte or bytes to emit into the stream. + * @return {number} The last byte emitted. + */ + this.emit = function(var_args) { + /** @type {number} */ + var last = EOF_byte; + var i; + for (i = 0; i < arguments.length; ++i) { + last = Number(arguments[i]); + bytes[pos++] = last; + } + return last; + }; +} + +/** + * @constructor + * @param {string} string The source of code units for the stream. + */ +function CodePointInputStream(string) { + /** + * @param {string} string Input string of UTF-16 code units. + * @return {Array.} Code points. + */ + function stringToCodePoints(string) { + /** @type {Array.} */ + var cps = []; + // Based on http://www.w3.org/TR/WebIDL/#idl-DOMString + var i = 0, n = string.length; + while (i < string.length) { + var c = string.charCodeAt(i); + if (!inRange(c, 0xD800, 0xDFFF)) { + cps.push(c); + } else if (inRange(c, 0xDC00, 0xDFFF)) { + cps.push(0xFFFD); + } else { // (inRange(cu, 0xD800, 0xDBFF)) + if (i === n - 1) { + cps.push(0xFFFD); + } else { + var d = string.charCodeAt(i + 1); + if (inRange(d, 0xDC00, 0xDFFF)) { + var a = c & 0x3FF; + var b = d & 0x3FF; + i += 1; + cps.push(0x10000 + (a << 10) + b); + } else { + cps.push(0xFFFD); + } + } + } + i += 1; + } + return cps; + } + + /** @type {number} */ + var pos = 0; + /** @type {Array.} */ + var cps = stringToCodePoints(string); + + /** @param {number} n The number of bytes (positive or negative) + * to advance the code point pointer by.*/ + this.offset = function(n) { + pos += n; + if (pos < 0) { + throw new Error('Seeking past start of the buffer'); + } + if (pos > cps.length) { + throw new Error('Seeking past EOF'); + } + }; + + + /** @return {number} Get the next code point from the stream. */ + this.get = function() { + if (pos >= cps.length) { + return EOF_code_point; + } + return cps[pos]; + }; +} + +/** + * @constructor + */ +function CodePointOutputStream() { + /** @type {string} */ + var string = ''; + + /** @return {string} The accumulated string. */ + this.string = function() { + return string; + }; + + /** @param {number} c The code point to encode into the stream. */ + this.emit = function(c) { + if (c <= 0xFFFF) { + string += String.fromCharCode(c); + } else { + c -= 0x10000; + string += String.fromCharCode(0xD800 + ((c >> 10) & 0x3ff)); + string += String.fromCharCode(0xDC00 + (c & 0x3ff)); + } + }; +} + +/** + * @constructor + * @param {string} message Description of the error. + */ +function EncodingError(message) { + this.name = 'EncodingError'; + this.message = message; + this.code = 0; +} +EncodingError.prototype = Error.prototype; + +/** + * @param {boolean} fatal If true, decoding errors raise an exception. + * @param {number=} opt_code_point Override the standard fallback code point. + * @return {number} The code point to insert on a decoding error. + */ +function decoderError(fatal, opt_code_point) { + if (fatal) { + throw new EncodingError('Decoder error'); + } + return opt_code_point || 0xFFFD; +} + +/** + * @param {number} code_point The code point that could not be encoded. + * @return {number} Always throws, no value is actually returned. + */ +function encoderError(code_point) { + throw new EncodingError('The code point ' + code_point + + ' could not be encoded.'); +} + +/** + * @param {string} label The encoding label. + * @return {?{name:string,labels:Array.}} + */ +function getEncoding(label) { + label = String(label).trim().toLowerCase(); + if (Object.prototype.hasOwnProperty.call(label_to_encoding, label)) { + return label_to_encoding[label]; + } + return null; +} + +/** @type {Array.<{encodings: Array.<{name:string,labels:Array.}>, + * heading: string}>} */ +var encodings = [ + { + "encodings": [ + { + "labels": [ + "unicode-1-1-utf-8", + "utf-8", + "utf8" + ], + "name": "utf-8" + } + ], + "heading": "The Encoding" + }, + { + "encodings": [ + { + "labels": [ + "864", + "cp864", + "csibm864", + "ibm864" + ], + "name": "ibm864" + }, + { + "labels": [ + "866", + "cp866", + "csibm866", + "ibm866" + ], + "name": "ibm866" + }, + { + "labels": [ + "csisolatin2", + "iso-8859-2", + "iso-ir-101", + "iso8859-2", + "iso88592", + "iso_8859-2", + "iso_8859-2:1987", + "l2", + "latin2" + ], + "name": "iso-8859-2" + }, + { + "labels": [ + "csisolatin3", + "iso-8859-3", + "iso-ir-109", + "iso8859-3", + "iso88593", + "iso_8859-3", + "iso_8859-3:1988", + "l3", + "latin3" + ], + "name": "iso-8859-3" + }, + { + "labels": [ + "csisolatin4", + "iso-8859-4", + "iso-ir-110", + "iso8859-4", + "iso88594", + "iso_8859-4", + "iso_8859-4:1988", + "l4", + "latin4" + ], + "name": "iso-8859-4" + }, + { + "labels": [ + "csisolatincyrillic", + "cyrillic", + "iso-8859-5", + "iso-ir-144", + "iso8859-5", + "iso88595", + "iso_8859-5", + "iso_8859-5:1988" + ], + "name": "iso-8859-5" + }, + { + "labels": [ + "arabic", + "asmo-708", + "csiso88596e", + "csiso88596i", + "csisolatinarabic", + "ecma-114", + "iso-8859-6", + "iso-8859-6-e", + "iso-8859-6-i", + "iso-ir-127", + "iso8859-6", + "iso88596", + "iso_8859-6", + "iso_8859-6:1987" + ], + "name": "iso-8859-6" + }, + { + "labels": [ + "csisolatingreek", + "ecma-118", + "elot_928", + "greek", + "greek8", + "iso-8859-7", + "iso-ir-126", + "iso8859-7", + "iso88597", + "iso_8859-7", + "iso_8859-7:1987", + "sun_eu_greek" + ], + "name": "iso-8859-7" + }, + { + "labels": [ + "csiso88598e", + "csisolatinhebrew", + "hebrew", + "iso-8859-8", + "iso-8859-8-e", + "iso-ir-138", + "iso8859-8", + "iso88598", + "iso_8859-8", + "iso_8859-8:1988", + "visual" + ], + "name": "iso-8859-8" + }, + { + "labels": [ + "csiso88598i", + "iso-8859-8-i", + "logical" + ], + "name": "iso-8859-8-i" + }, + { + "labels": [ + "csisolatin6", + "iso-8859-10", + "iso-ir-157", + "iso8859-10", + "iso885910", + "l6", + "latin6" + ], + "name": "iso-8859-10" + }, + { + "labels": [ + "iso-8859-13", + "iso8859-13", + "iso885913" + ], + "name": "iso-8859-13" + }, + { + "labels": [ + "iso-8859-14", + "iso8859-14", + "iso885914" + ], + "name": "iso-8859-14" + }, + { + "labels": [ + "csisolatin9", + "iso-8859-15", + "iso8859-15", + "iso885915", + "iso_8859-15", + "l9" + ], + "name": "iso-8859-15" + }, + { + "labels": [ + "iso-8859-16" + ], + "name": "iso-8859-16" + }, + { + "labels": [ + "cskoi8r", + "koi", + "koi8", + "koi8-r", + "koi8_r" + ], + "name": "koi8-r" + }, + { + "labels": [ + "koi8-u" + ], + "name": "koi8-u" + }, + { + "labels": [ + "csmacintosh", + "mac", + "macintosh", + "x-mac-roman" + ], + "name": "macintosh" + }, + { + "labels": [ + "dos-874", + "iso-8859-11", + "iso8859-11", + "iso885911", + "tis-620", + "windows-874" + ], + "name": "windows-874" + }, + { + "labels": [ + "cp1250", + "windows-1250", + "x-cp1250" + ], + "name": "windows-1250" + }, + { + "labels": [ + "cp1251", + "windows-1251", + "x-cp1251" + ], + "name": "windows-1251" + }, + { + "labels": [ + "ansi_x3.4-1968", + "ascii", + "cp1252", + "cp819", + "csisolatin1", + "ibm819", + "iso-8859-1", + "iso-ir-100", + "iso8859-1", + "iso88591", + "iso_8859-1", + "iso_8859-1:1987", + "l1", + "latin1", + "us-ascii", + "windows-1252", + "x-cp1252" + ], + "name": "windows-1252" + }, + { + "labels": [ + "cp1253", + "windows-1253", + "x-cp1253" + ], + "name": "windows-1253" + }, + { + "labels": [ + "cp1254", + "csisolatin5", + "iso-8859-9", + "iso-ir-148", + "iso8859-9", + "iso88599", + "iso_8859-9", + "iso_8859-9:1989", + "l5", + "latin5", + "windows-1254", + "x-cp1254" + ], + "name": "windows-1254" + }, + { + "labels": [ + "cp1255", + "windows-1255", + "x-cp1255" + ], + "name": "windows-1255" + }, + { + "labels": [ + "cp1256", + "windows-1256", + "x-cp1256" + ], + "name": "windows-1256" + }, + { + "labels": [ + "cp1257", + "windows-1257", + "x-cp1257" + ], + "name": "windows-1257" + }, + { + "labels": [ + "cp1258", + "windows-1258", + "x-cp1258" + ], + "name": "windows-1258" + }, + { + "labels": [ + "x-mac-cyrillic", + "x-mac-ukrainian" + ], + "name": "x-mac-cyrillic" + } + ], + "heading": "Legacy single-byte encodings" + }, + { + "encodings": [ + { + "labels": [ + "chinese", + "csgb2312", + "csiso58gb231280", + "gb2312", + "gb_2312", + "gb_2312-80", + "gbk", + "iso-ir-58", + "x-gbk" + ], + "name": "gbk" + }, + { + "labels": [ + "gb18030" + ], + "name": "gb18030" + }, + { + "labels": [ + "hz-gb-2312" + ], + "name": "hz-gb-2312" + } + ], + "heading": "Legacy multi-byte Chinese (simplified) encodings" + }, + { + "encodings": [ + { + "labels": [ + "big5", + "big5-hkscs", + "cn-big5", + "csbig5", + "x-x-big5" + ], + "name": "big5" + } + ], + "heading": "Legacy multi-byte Chinese (traditional) encodings" + }, + { + "encodings": [ + { + "labels": [ + "cseucpkdfmtjapanese", + "euc-jp", + "x-euc-jp" + ], + "name": "euc-jp" + }, + { + "labels": [ + "csiso2022jp", + "iso-2022-jp" + ], + "name": "iso-2022-jp" + }, + { + "labels": [ + "csshiftjis", + "ms_kanji", + "shift-jis", + "shift_jis", + "sjis", + "windows-31j", + "x-sjis" + ], + "name": "shift_jis" + } + ], + "heading": "Legacy multi-byte Japanese encodings" + }, + { + "encodings": [ + { + "labels": [ + "cseuckr", + "csksc56011987", + "euc-kr", + "iso-ir-149", + "korean", + "ks_c_5601-1987", + "ks_c_5601-1989", + "ksc5601", + "ksc_5601", + "windows-949" + ], + "name": "euc-kr" + } + ], + "heading": "Legacy multi-byte Korean encodings" + }, + { + "encodings": [ + { + "labels": [ + "csiso2022kr", + "iso-2022-cn", + "iso-2022-cn-ext", + "iso-2022-kr" + ], + "name": "replacement" + }, + { + "labels": [ + "utf-16be" + ], + "name": "utf-16be" + }, + { + "labels": [ + "utf-16", + "utf-16le" + ], + "name": "utf-16le" + }, + { + "labels": [ + "x-user-defined" + ], + "name": "x-user-defined" + } + ], + "heading": "Legacy miscellaneous encodings" + } +]; + +var name_to_encoding = {}; +var label_to_encoding = {}; +encodings.forEach(function(category) { + category.encodings.forEach(function(encoding) { + name_to_encoding[encoding.name] = encoding; + encoding.labels.forEach(function(label) { + label_to_encoding[label] = encoding; + }); + }); +}); + +// +// 5. Indexes +// + +/** + * @param {number} pointer The |pointer| to search for. + * @param {Array.|undefined} index The |index| to search within. + * @return {?number} The code point corresponding to |pointer| in |index|, + * or null if |code point| is not in |index|. + */ +function indexCodePointFor(pointer, index) { + if (!index) return null; + return index[pointer] || null; +} + +/** + * @param {number} code_point The |code point| to search for. + * @param {Array.} index The |index| to search within. + * @return {?number} The first pointer corresponding to |code point| in + * |index|, or null if |code point| is not in |index|. + */ +function indexPointerFor(code_point, index) { + var pointer = index.indexOf(code_point); + return pointer === -1 ? null : pointer; +} + +/** @type {Object.|Array.>)>} */ +var indexes = require('./encoding-indexes'); + +/** + * @param {number} pointer The |pointer| to search for in the gb18030 index. + * @return {?number} The code point corresponding to |pointer| in |index|, + * or null if |code point| is not in the gb18030 index. + */ +function indexGB18030CodePointFor(pointer) { + if ((pointer > 39419 && pointer < 189000) || (pointer > 1237575)) { + return null; + } + var /** @type {number} */ offset = 0, + /** @type {number} */ code_point_offset = 0, + /** @type {Array.>} */ idx = indexes['gb18030']; + var i; + for (i = 0; i < idx.length; ++i) { + var entry = idx[i]; + if (entry[0] <= pointer) { + offset = entry[0]; + code_point_offset = entry[1]; + } else { + break; + } + } + return code_point_offset + pointer - offset; +} + +/** + * @param {number} code_point The |code point| to locate in the gb18030 index. + * @return {number} The first pointer corresponding to |code point| in the + * gb18030 index. + */ +function indexGB18030PointerFor(code_point) { + var /** @type {number} */ offset = 0, + /** @type {number} */ pointer_offset = 0, + /** @type {Array.>} */ idx = indexes['gb18030']; + var i; + for (i = 0; i < idx.length; ++i) { + var entry = idx[i]; + if (entry[1] <= code_point) { + offset = entry[1]; + pointer_offset = entry[0]; + } else { + break; + } + } + return pointer_offset + code_point - offset; +} + + +// +// 7. API +// + +/** @const */ var DEFAULT_ENCODING = 'utf-8'; + +// 7.1 Interface TextDecoder + +/** + * @constructor + * @param {string=} opt_encoding The label of the encoding; + * defaults to 'utf-8'. + * @param {{fatal: boolean}=} options + */ +function TextDecoder(opt_encoding, options) { + if (!(this instanceof TextDecoder)) { + return new TextDecoder(opt_encoding, options); + } + opt_encoding = opt_encoding ? String(opt_encoding) : DEFAULT_ENCODING; + options = Object(options); + /** @private */ + this._encoding = getEncoding(opt_encoding); + if (this._encoding === null || this._encoding.name === 'replacement') + throw new TypeError('Unknown encoding: ' + opt_encoding); + + /** @private @type {boolean} */ + this._streaming = false; + /** @private @type {boolean} */ + this._BOMseen = false; + /** @private */ + this._decoder = null; + /** @private @type {{fatal: boolean}=} */ + this._options = { fatal: Boolean(options.fatal) }; + + if (Object.defineProperty) { + Object.defineProperty( + this, 'encoding', + { get: function() { return this._encoding.name; } }); + } else { + this.encoding = this._encoding.name; + } + + return this; +} + +// TODO: Issue if input byte stream is offset by decoder +// TODO: BOM detection will not work if stream header spans multiple calls +// (last N bytes of previous stream may need to be retained?) +TextDecoder.prototype = { + /** + * @param {Buffer=} bytes The buffer of bytes to decode. + * @param {{stream: boolean}=} options + */ + decode: function decode(bytes, options) { + options = Object(options); + + if (!this._streaming) { + this._decoder = this._encoding.getDecoder(this._options); + this._BOMseen = false; + } + this._streaming = Boolean(options.stream); + + var input_stream = new ByteInputStream(bytes); + + var output_stream = new CodePointOutputStream(); + + /** @type {number} */ + var code_point; + + while (input_stream.get() !== EOF_byte) { + code_point = this._decoder.decode(input_stream); + if (code_point !== null && code_point !== EOF_code_point) { + output_stream.emit(code_point); + } + } + if (!this._streaming) { + do { + code_point = this._decoder.decode(input_stream); + if (code_point !== null && code_point !== EOF_code_point) { + output_stream.emit(code_point); + } + } while (code_point !== EOF_code_point && + input_stream.get() != EOF_byte); + this._decoder = null; + } + + var result = output_stream.string(); + if (!this._BOMseen && result.length) { + this._BOMseen = true; + if (UTFs.indexOf(this.encoding) !== -1 && + result.charCodeAt(0) === 0xFEFF) { + result = result.substring(1); + } + } + + return result; + } +}; + +var UTFs = ['utf-8', 'utf-16le', 'utf-16be']; + +// 7.2 Interface TextEncoder + +/** + * @constructor + * @param {string=} opt_encoding The label of the encoding; + * defaults to 'utf-8'. + * @param {{fatal: boolean}=} options + */ +function TextEncoder(opt_encoding, options) { + if (!(this instanceof TextEncoder)) { + return new TextEncoder(opt_encoding, options); + } + opt_encoding = opt_encoding ? String(opt_encoding) : DEFAULT_ENCODING; + options = Object(options); + /** @private */ + this._encoding = getEncoding(opt_encoding); + if (this._encoding === null || (this._encoding.name !== 'utf-8' && + this._encoding.name !== 'utf-16le' && + this._encoding.name !== 'utf-16be')) + throw new TypeError('Unknown encoding: ' + opt_encoding); + /** @private @type {boolean} */ + this._streaming = false; + /** @private */ + this._encoder = null; + /** @private @type {{fatal: boolean}=} */ + this._options = { fatal: Boolean(options.fatal) }; + + if (Object.defineProperty) { + Object.defineProperty( + this, 'encoding', + { get: function() { return this._encoding.name; } }); + } else { + this.encoding = this._encoding.name; + } + + return this; +} + +TextEncoder.prototype = { + /** + * @param {string=} opt_string The string to encode. + * @param {{stream: boolean}=} options + */ + encode: function encode(opt_string, options) { + opt_string = opt_string ? String(opt_string) : ''; + options = Object(options); + // TODO: any options? + if (!this._streaming) { + this._encoder = this._encoding.getEncoder(this._options); + } + this._streaming = Boolean(options.stream); + + var bytes = []; + var output_stream = new ByteOutputStream(bytes); + var input_stream = new CodePointInputStream(opt_string); + while (input_stream.get() !== EOF_code_point) { + this._encoder.encode(output_stream, input_stream); + } + if (!this._streaming) { + /** @type {number} */ + var last_byte; + do { + last_byte = this._encoder.encode(output_stream, input_stream); + } while (last_byte !== EOF_byte); + this._encoder = null; + } + return new Buffer(bytes); + } +}; + + +// +// 8. The encoding +// + +// 8.1 utf-8 + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function UTF8Decoder(options) { + var fatal = options.fatal; + var /** @type {number} */ utf8_code_point = 0, + /** @type {number} */ utf8_bytes_needed = 0, + /** @type {number} */ utf8_bytes_seen = 0, + /** @type {number} */ utf8_lower_boundary = 0; + + /** + * @param {ByteInputStream} byte_pointer The byte stream to decode. + * @return {?number} The next code point decoded, or null if not enough + * data exists in the input stream to decode a complete code point. + */ + this.decode = function(byte_pointer) { + var bite = byte_pointer.get(); + if (bite === EOF_byte) { + if (utf8_bytes_needed !== 0) { + return decoderError(fatal); + } + return EOF_code_point; + } + byte_pointer.offset(1); + + if (utf8_bytes_needed === 0) { + if (inRange(bite, 0x00, 0x7F)) { + return bite; + } + if (inRange(bite, 0xC2, 0xDF)) { + utf8_bytes_needed = 1; + utf8_lower_boundary = 0x80; + utf8_code_point = bite - 0xC0; + } else if (inRange(bite, 0xE0, 0xEF)) { + utf8_bytes_needed = 2; + utf8_lower_boundary = 0x800; + utf8_code_point = bite - 0xE0; + } else if (inRange(bite, 0xF0, 0xF4)) { + utf8_bytes_needed = 3; + utf8_lower_boundary = 0x10000; + utf8_code_point = bite - 0xF0; + } else { + return decoderError(fatal); + } + utf8_code_point = utf8_code_point * Math.pow(64, utf8_bytes_needed); + return null; + } + if (!inRange(bite, 0x80, 0xBF)) { + utf8_code_point = 0; + utf8_bytes_needed = 0; + utf8_bytes_seen = 0; + utf8_lower_boundary = 0; + byte_pointer.offset(-1); + return decoderError(fatal); + } + utf8_bytes_seen += 1; + utf8_code_point = utf8_code_point + (bite - 0x80) * + Math.pow(64, utf8_bytes_needed - utf8_bytes_seen); + if (utf8_bytes_seen !== utf8_bytes_needed) { + return null; + } + var code_point = utf8_code_point; + var lower_boundary = utf8_lower_boundary; + utf8_code_point = 0; + utf8_bytes_needed = 0; + utf8_bytes_seen = 0; + utf8_lower_boundary = 0; + if (inRange(code_point, lower_boundary, 0x10FFFF) && + !inRange(code_point, 0xD800, 0xDFFF)) { + return code_point; + } + return decoderError(fatal); + }; +} + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function UTF8Encoder(options) { + var fatal = options.fatal; + /** + * @param {ByteOutputStream} output_byte_stream Output byte stream. + * @param {CodePointInputStream} code_point_pointer Input stream. + * @return {number} The last byte emitted. + */ + this.encode = function(output_byte_stream, code_point_pointer) { + /** @type {number} */ + var code_point = code_point_pointer.get(); + if (code_point === EOF_code_point) { + return EOF_byte; + } + code_point_pointer.offset(1); + if (inRange(code_point, 0xD800, 0xDFFF)) { + return encoderError(code_point); + } + if (inRange(code_point, 0x0000, 0x007f)) { + return output_byte_stream.emit(code_point); + } + var count, offset; + if (inRange(code_point, 0x0080, 0x07FF)) { + count = 1; + offset = 0xC0; + } else if (inRange(code_point, 0x0800, 0xFFFF)) { + count = 2; + offset = 0xE0; + } else if (inRange(code_point, 0x10000, 0x10FFFF)) { + count = 3; + offset = 0xF0; + } + var result = output_byte_stream.emit( + div(code_point, Math.pow(64, count)) + offset); + while (count > 0) { + var temp = div(code_point, Math.pow(64, count - 1)); + result = output_byte_stream.emit(0x80 + (temp % 64)); + count -= 1; + } + return result; + }; +} + +/** @param {{fatal: boolean}} options */ +name_to_encoding['utf-8'].getEncoder = function(options) { + return new UTF8Encoder(options); +}; +/** @param {{fatal: boolean}} options */ +name_to_encoding['utf-8'].getDecoder = function(options) { + return new UTF8Decoder(options); +}; + +// +// 9. Legacy single-byte encodings +// + +/** + * @constructor + * @param {Array.} index The encoding index. + * @param {{fatal: boolean}} options + */ +function SingleByteDecoder(index, options) { + var fatal = options.fatal; + /** + * @param {ByteInputStream} byte_pointer The byte stream to decode. + * @return {?number} The next code point decoded, or null if not enough + * data exists in the input stream to decode a complete code point. + */ + this.decode = function(byte_pointer) { + var bite = byte_pointer.get(); + if (bite === EOF_byte) { + return EOF_code_point; + } + byte_pointer.offset(1); + if (inRange(bite, 0x00, 0x7F)) { + return bite; + } + var code_point = index[bite - 0x80]; + if (code_point === null) { + return decoderError(fatal); + } + return code_point; + }; +} + +/** + * @constructor + * @param {Array.} index The encoding index. + * @param {{fatal: boolean}} options + */ +function SingleByteEncoder(index, options) { + var fatal = options.fatal; + /** + * @param {ByteOutputStream} output_byte_stream Output byte stream. + * @param {CodePointInputStream} code_point_pointer Input stream. + * @return {number} The last byte emitted. + */ + this.encode = function(output_byte_stream, code_point_pointer) { + var code_point = code_point_pointer.get(); + if (code_point === EOF_code_point) { + return EOF_byte; + } + code_point_pointer.offset(1); + if (inRange(code_point, 0x0000, 0x007F)) { + return output_byte_stream.emit(code_point); + } + var pointer = indexPointerFor(code_point, index); + if (pointer === null) { + encoderError(code_point); + } + return output_byte_stream.emit(pointer + 0x80); + }; +} + +(function() { + encodings.forEach(function(category) { + if (category.heading !== 'Legacy single-byte encodings') + return; + category.encodings.forEach(function(encoding) { + var idx = indexes[encoding.name]; + /** @param {{fatal: boolean}} options */ + encoding.getDecoder = function(options) { + return new SingleByteDecoder(idx, options); + }; + /** @param {{fatal: boolean}} options */ + encoding.getEncoder = function(options) { + return new SingleByteEncoder(idx, options); + }; + }); + }); +}()); + +// +// 10. Legacy multi-byte Chinese (simplified) encodings +// + +// 9.1 gbk + +/** + * @constructor + * @param {boolean} gb18030 True if decoding gb18030, false otherwise. + * @param {{fatal: boolean}} options + */ +function GBKDecoder(gb18030, options) { + var fatal = options.fatal; + var /** @type {number} */ gbk_first = 0x00, + /** @type {number} */ gbk_second = 0x00, + /** @type {number} */ gbk_third = 0x00; + /** + * @param {ByteInputStream} byte_pointer The byte stream to decode. + * @return {?number} The next code point decoded, or null if not enough + * data exists in the input stream to decode a complete code point. + */ + this.decode = function(byte_pointer) { + var bite = byte_pointer.get(); + if (bite === EOF_byte && gbk_first === 0x00 && + gbk_second === 0x00 && gbk_third === 0x00) { + return EOF_code_point; + } + if (bite === EOF_byte && + (gbk_first !== 0x00 || gbk_second !== 0x00 || gbk_third !== 0x00)) { + gbk_first = 0x00; + gbk_second = 0x00; + gbk_third = 0x00; + decoderError(fatal); + } + byte_pointer.offset(1); + var code_point; + if (gbk_third !== 0x00) { + code_point = null; + if (inRange(bite, 0x30, 0x39)) { + code_point = indexGB18030CodePointFor( + (((gbk_first - 0x81) * 10 + (gbk_second - 0x30)) * 126 + + (gbk_third - 0x81)) * 10 + bite - 0x30); + } + gbk_first = 0x00; + gbk_second = 0x00; + gbk_third = 0x00; + if (code_point === null) { + byte_pointer.offset(-3); + return decoderError(fatal); + } + return code_point; + } + if (gbk_second !== 0x00) { + if (inRange(bite, 0x81, 0xFE)) { + gbk_third = bite; + return null; + } + byte_pointer.offset(-2); + gbk_first = 0x00; + gbk_second = 0x00; + return decoderError(fatal); + } + if (gbk_first !== 0x00) { + if (inRange(bite, 0x30, 0x39) && gb18030) { + gbk_second = bite; + return null; + } + var lead = gbk_first; + var pointer = null; + gbk_first = 0x00; + var offset = bite < 0x7F ? 0x40 : 0x41; + if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFE)) { + pointer = (lead - 0x81) * 190 + (bite - offset); + } + code_point = pointer === null ? null : + indexCodePointFor(pointer, indexes['gbk']); + if (pointer === null) { + byte_pointer.offset(-1); + } + if (code_point === null) { + return decoderError(fatal); + } + return code_point; + } + if (inRange(bite, 0x00, 0x7F)) { + return bite; + } + if (bite === 0x80) { + return 0x20AC; + } + if (inRange(bite, 0x81, 0xFE)) { + gbk_first = bite; + return null; + } + return decoderError(fatal); + }; +} + +/** + * @constructor + * @param {boolean} gb18030 True if decoding gb18030, false otherwise. + * @param {{fatal: boolean}} options + */ +function GBKEncoder(gb18030, options) { + var fatal = options.fatal; + /** + * @param {ByteOutputStream} output_byte_stream Output byte stream. + * @param {CodePointInputStream} code_point_pointer Input stream. + * @return {number} The last byte emitted. + */ + this.encode = function(output_byte_stream, code_point_pointer) { + var code_point = code_point_pointer.get(); + if (code_point === EOF_code_point) { + return EOF_byte; + } + code_point_pointer.offset(1); + if (inRange(code_point, 0x0000, 0x007F)) { + return output_byte_stream.emit(code_point); + } + var pointer = indexPointerFor(code_point, indexes['gbk']); + if (pointer !== null) { + var lead = div(pointer, 190) + 0x81; + var trail = pointer % 190; + var offset = trail < 0x3F ? 0x40 : 0x41; + return output_byte_stream.emit(lead, trail + offset); + } + if (pointer === null && !gb18030) { + return encoderError(code_point); + } + pointer = indexGB18030PointerFor(code_point); + var byte1 = div(div(div(pointer, 10), 126), 10); + pointer = pointer - byte1 * 10 * 126 * 10; + var byte2 = div(div(pointer, 10), 126); + pointer = pointer - byte2 * 10 * 126; + var byte3 = div(pointer, 10); + var byte4 = pointer - byte3 * 10; + return output_byte_stream.emit(byte1 + 0x81, + byte2 + 0x30, + byte3 + 0x81, + byte4 + 0x30); + }; +} + +name_to_encoding['gbk'].getEncoder = function(options) { + return new GBKEncoder(false, options); +}; +name_to_encoding['gbk'].getDecoder = function(options) { + return new GBKDecoder(false, options); +}; + +// 9.2 gb18030 +name_to_encoding['gb18030'].getEncoder = function(options) { + return new GBKEncoder(true, options); +}; +name_to_encoding['gb18030'].getDecoder = function(options) { + return new GBKDecoder(true, options); +}; + +// 10.2 hz-gb-2312 + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function HZGB2312Decoder(options) { + var fatal = options.fatal; + var /** @type {boolean} */ hzgb2312 = false, + /** @type {number} */ hzgb2312_lead = 0x00; + /** + * @param {ByteInputStream} byte_pointer The byte stream to decode. + * @return {?number} The next code point decoded, or null if not enough + * data exists in the input stream to decode a complete code point. + */ + this.decode = function(byte_pointer) { + var bite = byte_pointer.get(); + if (bite === EOF_byte && hzgb2312_lead === 0x00) { + return EOF_code_point; + } + if (bite === EOF_byte && hzgb2312_lead !== 0x00) { + hzgb2312_lead = 0x00; + return decoderError(fatal); + } + byte_pointer.offset(1); + if (hzgb2312_lead === 0x7E) { + hzgb2312_lead = 0x00; + if (bite === 0x7B) { + hzgb2312 = true; + return null; + } + if (bite === 0x7D) { + hzgb2312 = false; + return null; + } + if (bite === 0x7E) { + return 0x007E; + } + if (bite === 0x0A) { + return null; + } + byte_pointer.offset(-1); + return decoderError(fatal); + } + if (hzgb2312_lead !== 0x00) { + var lead = hzgb2312_lead; + hzgb2312_lead = 0x00; + var code_point = null; + if (inRange(bite, 0x21, 0x7E)) { + code_point = indexCodePointFor((lead - 1) * 190 + + (bite + 0x3F), indexes['gbk']); + } + if (bite === 0x0A) { + hzgb2312 = false; + } + if (code_point === null) { + return decoderError(fatal); + } + return code_point; + } + if (bite === 0x7E) { + hzgb2312_lead = 0x7E; + return null; + } + if (hzgb2312) { + if (inRange(bite, 0x20, 0x7F)) { + hzgb2312_lead = bite; + return null; + } + if (bite === 0x0A) { + hzgb2312 = false; + } + return decoderError(fatal); + } + if (inRange(bite, 0x00, 0x7F)) { + return bite; + } + return decoderError(fatal); + }; +} + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function HZGB2312Encoder(options) { + var fatal = options.fatal; + /** @type {boolean} */ + var hzgb2312 = false; + /** + * @param {ByteOutputStream} output_byte_stream Output byte stream. + * @param {CodePointInputStream} code_point_pointer Input stream. + * @return {number} The last byte emitted. + */ + this.encode = function(output_byte_stream, code_point_pointer) { + var code_point = code_point_pointer.get(); + if (code_point === EOF_code_point) { + return EOF_byte; + } + code_point_pointer.offset(1); + if (inRange(code_point, 0x0000, 0x007F) && hzgb2312) { + code_point_pointer.offset(-1); + hzgb2312 = false; + return output_byte_stream.emit(0x7E, 0x7D); + } + if (code_point === 0x007E) { + return output_byte_stream.emit(0x7E, 0x7E); + } + if (inRange(code_point, 0x0000, 0x007F)) { + return output_byte_stream.emit(code_point); + } + if (!hzgb2312) { + code_point_pointer.offset(-1); + hzgb2312 = true; + return output_byte_stream.emit(0x7E, 0x7B); + } + var pointer = indexPointerFor(code_point, indexes['gbk']); + if (pointer === null) { + return encoderError(code_point); + } + var lead = div(pointer, 190) + 1; + var trail = pointer % 190 - 0x3F; + if (!inRange(lead, 0x21, 0x7E) || !inRange(trail, 0x21, 0x7E)) { + return encoderError(code_point); + } + return output_byte_stream.emit(lead, trail); + }; +} + +/** @param {{fatal: boolean}} options */ +name_to_encoding['hz-gb-2312'].getEncoder = function(options) { + return new HZGB2312Encoder(options); +}; +/** @param {{fatal: boolean}} options */ +name_to_encoding['hz-gb-2312'].getDecoder = function(options) { + return new HZGB2312Decoder(options); +}; + +// +// 11. Legacy multi-byte Chinese (traditional) encodings +// + +// 11.1 big5 + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function Big5Decoder(options) { + var fatal = options.fatal; + var /** @type {number} */ big5_lead = 0x00, + /** @type {?number} */ big5_pending = null; + + /** + * @param {ByteInputStream} byte_pointer The byte steram to decode. + * @return {?number} The next code point decoded, or null if not enough + * data exists in the input stream to decode a complete code point. + */ + this.decode = function(byte_pointer) { + // NOTE: Hack to support emitting two code points + if (big5_pending !== null) { + var pending = big5_pending; + big5_pending = null; + return pending; + } + var bite = byte_pointer.get(); + if (bite === EOF_byte && big5_lead === 0x00) { + return EOF_code_point; + } + if (bite === EOF_byte && big5_lead !== 0x00) { + big5_lead = 0x00; + return decoderError(fatal); + } + byte_pointer.offset(1); + if (big5_lead !== 0x00) { + var lead = big5_lead; + var pointer = null; + big5_lead = 0x00; + var offset = bite < 0x7F ? 0x40 : 0x62; + if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0xA1, 0xFE)) { + pointer = (lead - 0x81) * 157 + (bite - offset); + } + if (pointer === 1133) { + big5_pending = 0x0304; + return 0x00CA; + } + if (pointer === 1135) { + big5_pending = 0x030C; + return 0x00CA; + } + if (pointer === 1164) { + big5_pending = 0x0304; + return 0x00EA; + } + if (pointer === 1166) { + big5_pending = 0x030C; + return 0x00EA; + } + var code_point = (pointer === null) ? null : + indexCodePointFor(pointer, indexes['big5']); + if (pointer === null) { + byte_pointer.offset(-1); + } + if (code_point === null) { + return decoderError(fatal); + } + return code_point; + } + if (inRange(bite, 0x00, 0x7F)) { + return bite; + } + if (inRange(bite, 0x81, 0xFE)) { + big5_lead = bite; + return null; + } + return decoderError(fatal); + }; +} + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function Big5Encoder(options) { + var fatal = options.fatal; + /** + * @param {ByteOutputStream} output_byte_stream Output byte stream. + * @param {CodePointInputStream} code_point_pointer Input stream. + * @return {number} The last byte emitted. + */ + this.encode = function(output_byte_stream, code_point_pointer) { + var code_point = code_point_pointer.get(); + if (code_point === EOF_code_point) { + return EOF_byte; + } + code_point_pointer.offset(1); + if (inRange(code_point, 0x0000, 0x007F)) { + return output_byte_stream.emit(code_point); + } + var pointer = indexPointerFor(code_point, indexes['big5']); + if (pointer === null) { + return encoderError(code_point); + } + var lead = div(pointer, 157) + 0x81; + //if (lead < 0xA1) { + // return encoderError(code_point); + //} + var trail = pointer % 157; + var offset = trail < 0x3F ? 0x40 : 0x62; + return output_byte_stream.emit(lead, trail + offset); + }; +} + +/** @param {{fatal: boolean}} options */ +name_to_encoding['big5'].getEncoder = function(options) { + return new Big5Encoder(options); +}; +/** @param {{fatal: boolean}} options */ +name_to_encoding['big5'].getDecoder = function(options) { + return new Big5Decoder(options); +}; + + +// +// 12. Legacy multi-byte Japanese encodings +// + +// 12.1 euc.jp + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function EUCJPDecoder(options) { + var fatal = options.fatal; + var /** @type {number} */ eucjp_first = 0x00, + /** @type {number} */ eucjp_second = 0x00; + /** + * @param {ByteInputStream} byte_pointer The byte stream to decode. + * @return {?number} The next code point decoded, or null if not enough + * data exists in the input stream to decode a complete code point. + */ + this.decode = function(byte_pointer) { + var bite = byte_pointer.get(); + if (bite === EOF_byte) { + if (eucjp_first === 0x00 && eucjp_second === 0x00) { + return EOF_code_point; + } + eucjp_first = 0x00; + eucjp_second = 0x00; + return decoderError(fatal); + } + byte_pointer.offset(1); + + var lead, code_point; + if (eucjp_second !== 0x00) { + lead = eucjp_second; + eucjp_second = 0x00; + code_point = null; + if (inRange(lead, 0xA1, 0xFE) && inRange(bite, 0xA1, 0xFE)) { + code_point = indexCodePointFor((lead - 0xA1) * 94 + bite - 0xA1, + indexes['jis0212']); + } + if (!inRange(bite, 0xA1, 0xFE)) { + byte_pointer.offset(-1); + } + if (code_point === null) { + return decoderError(fatal); + } + return code_point; + } + if (eucjp_first === 0x8E && inRange(bite, 0xA1, 0xDF)) { + eucjp_first = 0x00; + return 0xFF61 + bite - 0xA1; + } + if (eucjp_first === 0x8F && inRange(bite, 0xA1, 0xFE)) { + eucjp_first = 0x00; + eucjp_second = bite; + return null; + } + if (eucjp_first !== 0x00) { + lead = eucjp_first; + eucjp_first = 0x00; + code_point = null; + if (inRange(lead, 0xA1, 0xFE) && inRange(bite, 0xA1, 0xFE)) { + code_point = indexCodePointFor((lead - 0xA1) * 94 + bite - 0xA1, + indexes['jis0208']); + } + if (!inRange(bite, 0xA1, 0xFE)) { + byte_pointer.offset(-1); + } + if (code_point === null) { + return decoderError(fatal); + } + return code_point; + } + if (inRange(bite, 0x00, 0x7F)) { + return bite; + } + if (bite === 0x8E || bite === 0x8F || (inRange(bite, 0xA1, 0xFE))) { + eucjp_first = bite; + return null; + } + return decoderError(fatal); + }; +} + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function EUCJPEncoder(options) { + var fatal = options.fatal; + /** + * @param {ByteOutputStream} output_byte_stream Output byte stream. + * @param {CodePointInputStream} code_point_pointer Input stream. + * @return {number} The last byte emitted. + */ + this.encode = function(output_byte_stream, code_point_pointer) { + var code_point = code_point_pointer.get(); + if (code_point === EOF_code_point) { + return EOF_byte; + } + code_point_pointer.offset(1); + if (inRange(code_point, 0x0000, 0x007F)) { + return output_byte_stream.emit(code_point); + } + if (code_point === 0x00A5) { + return output_byte_stream.emit(0x5C); + } + if (code_point === 0x203E) { + return output_byte_stream.emit(0x7E); + } + if (inRange(code_point, 0xFF61, 0xFF9F)) { + return output_byte_stream.emit(0x8E, code_point - 0xFF61 + 0xA1); + } + + var pointer = indexPointerFor(code_point, indexes['jis0208']); + if (pointer === null) { + return encoderError(code_point); + } + var lead = div(pointer, 94) + 0xA1; + var trail = pointer % 94 + 0xA1; + return output_byte_stream.emit(lead, trail); + }; +} + +/** @param {{fatal: boolean}} options */ +name_to_encoding['euc-jp'].getEncoder = function(options) { + return new EUCJPEncoder(options); +}; +/** @param {{fatal: boolean}} options */ +name_to_encoding['euc-jp'].getDecoder = function(options) { + return new EUCJPDecoder(options); +}; + +// 12.2 iso-2022-jp + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function ISO2022JPDecoder(options) { + var fatal = options.fatal; + /** @enum */ + var state = { + ASCII: 0, + escape_start: 1, + escape_middle: 2, + escape_final: 3, + lead: 4, + trail: 5, + Katakana: 6 + }; + var /** @type {number} */ iso2022jp_state = state.ASCII, + /** @type {boolean} */ iso2022jp_jis0212 = false, + /** @type {number} */ iso2022jp_lead = 0x00; + /** + * @param {ByteInputStream} byte_pointer The byte stream to decode. + * @return {?number} The next code point decoded, or null if not enough + * data exists in the input stream to decode a complete code point. + */ + this.decode = function(byte_pointer) { + var bite = byte_pointer.get(); + if (bite !== EOF_byte) { + byte_pointer.offset(1); + } + switch (iso2022jp_state) { + default: + case state.ASCII: + if (bite === 0x1B) { + iso2022jp_state = state.escape_start; + return null; + } + if (inRange(bite, 0x00, 0x7F)) { + return bite; + } + if (bite === EOF_byte) { + return EOF_code_point; + } + return decoderError(fatal); + + case state.escape_start: + if (bite === 0x24 || bite === 0x28) { + iso2022jp_lead = bite; + iso2022jp_state = state.escape_middle; + return null; + } + if (bite !== EOF_byte) { + byte_pointer.offset(-1); + } + iso2022jp_state = state.ASCII; + return decoderError(fatal); + + case state.escape_middle: + var lead = iso2022jp_lead; + iso2022jp_lead = 0x00; + if (lead === 0x24 && (bite === 0x40 || bite === 0x42)) { + iso2022jp_jis0212 = false; + iso2022jp_state = state.lead; + return null; + } + if (lead === 0x24 && bite === 0x28) { + iso2022jp_state = state.escape_final; + return null; + } + if (lead === 0x28 && (bite === 0x42 || bite === 0x4A)) { + iso2022jp_state = state.ASCII; + return null; + } + if (lead === 0x28 && bite === 0x49) { + iso2022jp_state = state.Katakana; + return null; + } + if (bite === EOF_byte) { + byte_pointer.offset(-1); + } else { + byte_pointer.offset(-2); + } + iso2022jp_state = state.ASCII; + return decoderError(fatal); + + case state.escape_final: + if (bite === 0x44) { + iso2022jp_jis0212 = true; + iso2022jp_state = state.lead; + return null; + } + if (bite === EOF_byte) { + byte_pointer.offset(-2); + } else { + byte_pointer.offset(-3); + } + iso2022jp_state = state.ASCII; + return decoderError(fatal); + + case state.lead: + if (bite === 0x0A) { + iso2022jp_state = state.ASCII; + return decoderError(fatal, 0x000A); + } + if (bite === 0x1B) { + iso2022jp_state = state.escape_start; + return null; + } + if (bite === EOF_byte) { + return EOF_code_point; + } + iso2022jp_lead = bite; + iso2022jp_state = state.trail; + return null; + + case state.trail: + iso2022jp_state = state.lead; + if (bite === EOF_byte) { + return decoderError(fatal); + } + var code_point = null; + var pointer = (iso2022jp_lead - 0x21) * 94 + bite - 0x21; + if (inRange(iso2022jp_lead, 0x21, 0x7E) && + inRange(bite, 0x21, 0x7E)) { + code_point = (iso2022jp_jis0212 === false) ? + indexCodePointFor(pointer, indexes['jis0208']) : + indexCodePointFor(pointer, indexes['jis0212']); + } + if (code_point === null) { + return decoderError(fatal); + } + return code_point; + + case state.Katakana: + if (bite === 0x1B) { + iso2022jp_state = state.escape_start; + return null; + } + if (inRange(bite, 0x21, 0x5F)) { + return 0xFF61 + bite - 0x21; + } + if (bite === EOF_byte) { + return EOF_code_point; + } + return decoderError(fatal); + } + }; +} + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function ISO2022JPEncoder(options) { + var fatal = options.fatal; + /** @enum */ + var state = { + ASCII: 0, + lead: 1, + Katakana: 2 + }; + var /** @type {number} */ iso2022jp_state = state.ASCII; + /** + * @param {ByteOutputStream} output_byte_stream Output byte stream. + * @param {CodePointInputStream} code_point_pointer Input stream. + * @return {number} The last byte emitted. + */ + this.encode = function(output_byte_stream, code_point_pointer) { + var code_point = code_point_pointer.get(); + if (code_point === EOF_code_point) { + return EOF_byte; + } + code_point_pointer.offset(1); + if ((inRange(code_point, 0x0000, 0x007F) || + code_point === 0x00A5 || code_point === 0x203E) && + iso2022jp_state !== state.ASCII) { + code_point_pointer.offset(-1); + iso2022jp_state = state.ASCII; + return output_byte_stream.emit(0x1B, 0x28, 0x42); + } + if (inRange(code_point, 0x0000, 0x007F)) { + return output_byte_stream.emit(code_point); + } + if (code_point === 0x00A5) { + return output_byte_stream.emit(0x5C); + } + if (code_point === 0x203E) { + return output_byte_stream.emit(0x7E); + } + if (inRange(code_point, 0xFF61, 0xFF9F) && + iso2022jp_state !== state.Katakana) { + code_point_pointer.offset(-1); + iso2022jp_state = state.Katakana; + return output_byte_stream.emit(0x1B, 0x28, 0x49); + } + if (inRange(code_point, 0xFF61, 0xFF9F)) { + return output_byte_stream.emit(code_point - 0xFF61 - 0x21); + } + if (iso2022jp_state !== state.lead) { + code_point_pointer.offset(-1); + iso2022jp_state = state.lead; + return output_byte_stream.emit(0x1B, 0x24, 0x42); + } + var pointer = indexPointerFor(code_point, indexes['jis0208']); + if (pointer === null) { + return encoderError(code_point); + } + var lead = div(pointer, 94) + 0x21; + var trail = pointer % 94 + 0x21; + return output_byte_stream.emit(lead, trail); + }; +} + +/** @param {{fatal: boolean}} options */ +name_to_encoding['iso-2022-jp'].getEncoder = function(options) { + return new ISO2022JPEncoder(options); +}; +/** @param {{fatal: boolean}} options */ +name_to_encoding['iso-2022-jp'].getDecoder = function(options) { + return new ISO2022JPDecoder(options); +}; + +// 12.3 shift_jis + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function ShiftJISDecoder(options) { + var fatal = options.fatal; + var /** @type {number} */ shiftjis_lead = 0x00; + /** + * @param {ByteInputStream} byte_pointer The byte stream to decode. + * @return {?number} The next code point decoded, or null if not enough + * data exists in the input stream to decode a complete code point. + */ + this.decode = function(byte_pointer) { + var bite = byte_pointer.get(); + if (bite === EOF_byte && shiftjis_lead === 0x00) { + return EOF_code_point; + } + if (bite === EOF_byte && shiftjis_lead !== 0x00) { + shiftjis_lead = 0x00; + return decoderError(fatal); + } + byte_pointer.offset(1); + if (shiftjis_lead !== 0x00) { + var lead = shiftjis_lead; + shiftjis_lead = 0x00; + if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFC)) { + var offset = (bite < 0x7F) ? 0x40 : 0x41; + var lead_offset = (lead < 0xA0) ? 0x81 : 0xC1; + var code_point = indexCodePointFor((lead - lead_offset) * 188 + + bite - offset, indexes['jis0208']); + if (code_point === null) { + return decoderError(fatal); + } + return code_point; + } + byte_pointer.offset(-1); + return decoderError(fatal); + } + if (inRange(bite, 0x00, 0x80)) { + return bite; + } + if (inRange(bite, 0xA1, 0xDF)) { + return 0xFF61 + bite - 0xA1; + } + if (inRange(bite, 0x81, 0x9F) || inRange(bite, 0xE0, 0xFC)) { + shiftjis_lead = bite; + return null; + } + return decoderError(fatal); + }; +} + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function ShiftJISEncoder(options) { + var fatal = options.fatal; + /** + * @param {ByteOutputStream} output_byte_stream Output byte stream. + * @param {CodePointInputStream} code_point_pointer Input stream. + * @return {number} The last byte emitted. + */ + this.encode = function(output_byte_stream, code_point_pointer) { + var code_point = code_point_pointer.get(); + if (code_point === EOF_code_point) { + return EOF_byte; + } + code_point_pointer.offset(1); + if (inRange(code_point, 0x0000, 0x0080)) { + return output_byte_stream.emit(code_point); + } + if (code_point === 0x00A5) { + return output_byte_stream.emit(0x5C); + } + if (code_point === 0x203E) { + return output_byte_stream.emit(0x7E); + } + if (inRange(code_point, 0xFF61, 0xFF9F)) { + return output_byte_stream.emit(code_point - 0xFF61 + 0xA1); + } + var pointer = indexPointerFor(code_point, indexes['jis0208']); + if (pointer === null) { + return encoderError(code_point); + } + var lead = div(pointer, 188); + var lead_offset = lead < 0x1F ? 0x81 : 0xC1; + var trail = pointer % 188; + var offset = trail < 0x3F ? 0x40 : 0x41; + return output_byte_stream.emit(lead + lead_offset, trail + offset); + }; +} + +/** @param {{fatal: boolean}} options */ +name_to_encoding['shift_jis'].getEncoder = function(options) { + return new ShiftJISEncoder(options); +}; +/** @param {{fatal: boolean}} options */ +name_to_encoding['shift_jis'].getDecoder = function(options) { + return new ShiftJISDecoder(options); +}; + +// +// 13. Legacy multi-byte Korean encodings +// + +// 13.1 euc-kr + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function EUCKRDecoder(options) { + var fatal = options.fatal; + var /** @type {number} */ euckr_lead = 0x00; + /** + * @param {ByteInputStream} byte_pointer The byte stream to decode. + * @return {?number} The next code point decoded, or null if not enough + * data exists in the input stream to decode a complete code point. + */ + this.decode = function(byte_pointer) { + var bite = byte_pointer.get(); + if (bite === EOF_byte && euckr_lead === 0) { + return EOF_code_point; + } + if (bite === EOF_byte && euckr_lead !== 0) { + euckr_lead = 0x00; + return decoderError(fatal); + } + byte_pointer.offset(1); + if (euckr_lead !== 0x00) { + var lead = euckr_lead; + var pointer = null; + euckr_lead = 0x00; + + if (inRange(lead, 0x81, 0xC6)) { + var temp = (26 + 26 + 126) * (lead - 0x81); + if (inRange(bite, 0x41, 0x5A)) { + pointer = temp + bite - 0x41; + } else if (inRange(bite, 0x61, 0x7A)) { + pointer = temp + 26 + bite - 0x61; + } else if (inRange(bite, 0x81, 0xFE)) { + pointer = temp + 26 + 26 + bite - 0x81; + } + } + + if (inRange(lead, 0xC7, 0xFD) && inRange(bite, 0xA1, 0xFE)) { + pointer = (26 + 26 + 126) * (0xC7 - 0x81) + (lead - 0xC7) * 94 + + (bite - 0xA1); + } + + var code_point = (pointer === null) ? null : + indexCodePointFor(pointer, indexes['euc-kr']); + if (pointer === null) { + byte_pointer.offset(-1); + } + if (code_point === null) { + return decoderError(fatal); + } + return code_point; + } + + if (inRange(bite, 0x00, 0x7F)) { + return bite; + } + + if (inRange(bite, 0x81, 0xFD)) { + euckr_lead = bite; + return null; + } + + return decoderError(fatal); + }; +} + +/** + * @constructor + * @param {{fatal: boolean}} options + */ +function EUCKREncoder(options) { + var fatal = options.fatal; + /** + * @param {ByteOutputStream} output_byte_stream Output byte stream. + * @param {CodePointInputStream} code_point_pointer Input stream. + * @return {number} The last byte emitted. + */ + this.encode = function(output_byte_stream, code_point_pointer) { + var code_point = code_point_pointer.get(); + if (code_point === EOF_code_point) { + return EOF_byte; + } + code_point_pointer.offset(1); + if (inRange(code_point, 0x0000, 0x007F)) { + return output_byte_stream.emit(code_point); + } + var pointer = indexPointerFor(code_point, indexes['euc-kr']); + if (pointer === null) { + return encoderError(code_point); + } + var lead, trail; + if (pointer < ((26 + 26 + 126) * (0xC7 - 0x81))) { + lead = div(pointer, (26 + 26 + 126)) + 0x81; + trail = pointer % (26 + 26 + 126); + var offset = trail < 26 ? 0x41 : trail < 26 + 26 ? 0x47 : 0x4D; + return output_byte_stream.emit(lead, trail + offset); + } + pointer = pointer - (26 + 26 + 126) * (0xC7 - 0x81); + lead = div(pointer, 94) + 0xC7; + trail = pointer % 94 + 0xA1; + return output_byte_stream.emit(lead, trail); + }; +} + +/** @param {{fatal: boolean}} options */ +name_to_encoding['euc-kr'].getEncoder = function(options) { + return new EUCKREncoder(options); +}; +/** @param {{fatal: boolean}} options */ +name_to_encoding['euc-kr'].getDecoder = function(options) { + return new EUCKRDecoder(options); +}; + + +// +// 14. Legacy miscellaneous encodings +// + +// 14.1 replacement + +// Not needed - API throws TypeError + +// 14.2 utf-16 + +/** + * @constructor + * @param {boolean} utf16_be True if big-endian, false if little-endian. + * @param {{fatal: boolean}} options + */ +function UTF16Decoder(utf16_be, options) { + var fatal = options.fatal; + var /** @type {?number} */ utf16_lead_byte = null, + /** @type {?number} */ utf16_lead_surrogate = null; + /** + * @param {ByteInputStream} byte_pointer The byte stream to decode. + * @return {?number} The next code point decoded, or null if not enough + * data exists in the input stream to decode a complete code point. + */ + this.decode = function(byte_pointer) { + var bite = byte_pointer.get(); + if (bite === EOF_byte && utf16_lead_byte === null && + utf16_lead_surrogate === null) { + return EOF_code_point; + } + if (bite === EOF_byte && (utf16_lead_byte !== null || + utf16_lead_surrogate !== null)) { + return decoderError(fatal); + } + byte_pointer.offset(1); + if (utf16_lead_byte === null) { + utf16_lead_byte = bite; + return null; + } + var code_point; + if (utf16_be) { + code_point = (utf16_lead_byte << 8) + bite; + } else { + code_point = (bite << 8) + utf16_lead_byte; + } + utf16_lead_byte = null; + if (utf16_lead_surrogate !== null) { + var lead_surrogate = utf16_lead_surrogate; + utf16_lead_surrogate = null; + if (inRange(code_point, 0xDC00, 0xDFFF)) { + return 0x10000 + (lead_surrogate - 0xD800) * 0x400 + + (code_point - 0xDC00); + } + byte_pointer.offset(-2); + return decoderError(fatal); + } + if (inRange(code_point, 0xD800, 0xDBFF)) { + utf16_lead_surrogate = code_point; + return null; + } + if (inRange(code_point, 0xDC00, 0xDFFF)) { + return decoderError(fatal); + } + return code_point; + }; +} + +/** + * @constructor + * @param {boolean} utf16_be True if big-endian, false if little-endian. + * @param {{fatal: boolean}} options + */ +function UTF16Encoder(utf16_be, options) { + var fatal = options.fatal; + /** + * @param {ByteOutputStream} output_byte_stream Output byte stream. + * @param {CodePointInputStream} code_point_pointer Input stream. + * @return {number} The last byte emitted. + */ + this.encode = function(output_byte_stream, code_point_pointer) { + /** + * @param {number} code_unit + * @return {number} last byte emitted + */ + function convert_to_bytes(code_unit) { + var byte1 = code_unit >> 8; + var byte2 = code_unit & 0x00FF; + if (utf16_be) { + return output_byte_stream.emit(byte1, byte2); + } + return output_byte_stream.emit(byte2, byte1); + } + var code_point = code_point_pointer.get(); + if (code_point === EOF_code_point) { + return EOF_byte; + } + code_point_pointer.offset(1); + if (inRange(code_point, 0xD800, 0xDFFF)) { + encoderError(code_point); + } + if (code_point <= 0xFFFF) { + return convert_to_bytes(code_point); + } + var lead = div((code_point - 0x10000), 0x400) + 0xD800; + var trail = ((code_point - 0x10000) % 0x400) + 0xDC00; + convert_to_bytes(lead); + return convert_to_bytes(trail); + }; +} + +// 14.3 utf-16be +/** @param {{fatal: boolean}} options */ +name_to_encoding['utf-16be'].getEncoder = function(options) { + return new UTF16Encoder(true, options); +}; +/** @param {{fatal: boolean}} options */ +name_to_encoding['utf-16be'].getDecoder = function(options) { + return new UTF16Decoder(true, options); +}; + +// 14.4 utf-16le +/** @param {{fatal: boolean}} options */ +name_to_encoding['utf-16le'].getEncoder = function(options) { + return new UTF16Encoder(false, options); +}; +/** @param {{fatal: boolean}} options */ +name_to_encoding['utf-16le'].getDecoder = function(options) { + return new UTF16Decoder(false, options); +}; + +// 14.5 x-user-defined +// TODO: Implement this encoding. + +// NOTE: currently unused +/** + * @param {string} label The encoding label. + * @param {ByteInputStream} input_stream The byte stream to test. + */ +function detectEncoding(label, input_stream) { + if (input_stream.match([0xFF, 0xFE])) { + input_stream.offset(2); + return 'utf-16le'; + } + if (input_stream.match([0xFE, 0xFF])) { + input_stream.offset(2); + return 'utf-16be'; + } + if (input_stream.match([0xEF, 0xBB, 0xBF])) { + input_stream.offset(3); + return 'utf-8'; + } + return label; +} + +exports.TextEncoder = TextEncoder; +exports.TextDecoder = TextDecoder; +exports.encodingExists = getEncoding; diff --git a/node_modules/multer/node_modules/busboy/lib/main.js b/node_modules/multer/node_modules/busboy/lib/main.js new file mode 100644 index 0000000..6c630f2 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/lib/main.js @@ -0,0 +1,88 @@ +var fs = require('fs'), + WritableStream = require('stream').Writable + || require('readable-stream').Writable, + inherits = require('util').inherits; + +var parseParams = require('./utils').parseParams; + +function Busboy(opts) { + if (!(this instanceof Busboy)) + return new Busboy(opts); + if (opts.highWaterMark !== undefined) + WritableStream.call(this, { highWaterMark: opts.highWaterMark }); + else + WritableStream.call(this); + + this._done = false; + this._parser = undefined; + + this.opts = opts; + if (opts.headers && typeof opts.headers['content-type'] === 'string') + this.parseHeaders(opts.headers); + else + throw new Error('Missing Content-Type'); +} +inherits(Busboy, WritableStream); + +Busboy.prototype.emit = function(ev) { + if (ev === 'finish' && !this._done) + this._parser && this._parser.end(); + else + WritableStream.prototype.emit.apply(this, arguments); +}; + +Busboy.prototype.parseHeaders = function(headers) { + this._parser = undefined; + if (headers['content-type']) { + var parsed = parseParams(headers['content-type']), + matched, type; + for (var i = 0; i < TYPES_LEN; ++i) { + type = TYPES[i]; + if (typeof type.detect === 'function') + matched = type.detect(parsed); + else + matched = type.detect.test(parsed[0]); + if (matched) + break; + } + if (matched) { + var cfg = { + limits: this.opts.limits, + headers: headers, + parsedConType: parsed, + highWaterMark: undefined, + fileHwm: undefined, + defCharset: undefined, + preservePath: false + }; + if (this.opts.highWaterMark) + cfg.highWaterMark = this.opts.highWaterMark; + if (this.opts.fileHwm) + cfg.fileHwm = this.opts.fileHwm; + cfg.defCharset = this.opts.defCharset; + cfg.preservePath = this.opts.preservePath; + this._parser = type(this, cfg); + return; + } + } + throw new Error('Unsupported content type: ' + headers['content-type']); +}; + +Busboy.prototype._write = function(chunk, encoding, cb) { + if (!this._parser) + return cb(new Error('Not ready to parse. Missing Content-Type?')); + this._parser.write(chunk, cb); +}; + +var TYPES = [], TYPES_LEN = 0; +fs.readdirSync(__dirname + '/types').forEach(function(type) { + if (!/\.js$/.test(type)) + return; + var typemod = require(__dirname + '/types/' + type); + if (typemod.detect) { + TYPES.push(typemod); + ++TYPES_LEN; + } +}); + +module.exports = Busboy; diff --git a/node_modules/multer/node_modules/busboy/lib/types/multipart.js b/node_modules/multer/node_modules/busboy/lib/types/multipart.js new file mode 100644 index 0000000..4afe54e --- /dev/null +++ b/node_modules/multer/node_modules/busboy/lib/types/multipart.js @@ -0,0 +1,319 @@ +// TODO: +// * support 1 nested multipart level +// (see second multipart example here: +// http://www.w3.org/TR/html401/interact/forms.html#didx-multipartform-data) +// * support limits.fieldNameSize +// -- this will require modifications to utils.parseParams + +var ReadableStream = require('stream').Readable || require('readable-stream'), + inherits = require('util').inherits; + +var Dicer = require('dicer'); + +var parseParams = require('../utils').parseParams, + decodeText = require('../utils').decodeText, + basename = require('../utils').basename; + +var RE_BOUNDARY = /^boundary$/i, + RE_FIELD = /^form-data$/i, + RE_CHARSET = /^charset$/i, + RE_FILENAME = /^filename$/i, + RE_NAME = /^name$/i; + +Multipart.detect = /^multipart\/form-data/i; +function Multipart(boy, cfg) { + if (!(this instanceof Multipart)) + return new Multipart(boy, cfg); + var i, + len, + self = this, + boundary, + limits = cfg.limits, + parsedConType = cfg.parsedConType || [], + defCharset = cfg.defCharset || 'utf8', + preservePath = cfg.preservePath, + fileopts = (typeof cfg.fileHwm === 'number' + ? { highWaterMark: cfg.fileHwm } + : {}); + + for (i = 0, len = parsedConType.length; i < len; ++i) { + if (Array.isArray(parsedConType[i]) + && RE_BOUNDARY.test(parsedConType[i][0])) { + boundary = parsedConType[i][1]; + break; + } + } + + function checkFinished() { + if (nends === 0 && finished && !boy._done) { + finished = false; + process.nextTick(function() { + boy._done = true; + boy.emit('finish'); + }); + } + } + + if (typeof boundary !== 'string') + throw new Error('Multipart: Boundary not found'); + + var fieldSizeLimit = (limits && typeof limits.fieldSize === 'number' + ? limits.fieldSize + : 1 * 1024 * 1024), + fileSizeLimit = (limits && typeof limits.fileSize === 'number' + ? limits.fileSize + : Infinity), + filesLimit = (limits && typeof limits.files === 'number' + ? limits.files + : Infinity), + fieldsLimit = (limits && typeof limits.fields === 'number' + ? limits.fields + : Infinity), + partsLimit = (limits && typeof limits.parts === 'number' + ? limits.parts + : Infinity); + + var nfiles = 0, + nfields = 0, + nends = 0, + curFile, + curField, + finished = false; + + this._needDrain = false; + this._pause = false; + this._cb = undefined; + this._nparts = 0; + this._boy = boy; + + var parserCfg = { + boundary: boundary, + maxHeaderPairs: (limits && limits.headerPairs) + }; + if (fileopts.highWaterMark) + parserCfg.partHwm = fileopts.highWaterMark; + if (cfg.highWaterMark) + parserCfg.highWaterMark = cfg.highWaterMark; + + this.parser = new Dicer(parserCfg); + this.parser.on('drain', function() { + self._needDrain = false; + if (self._cb && !self._pause) { + var cb = self._cb; + self._cb = undefined; + cb(); + } + }).on('part', function onPart(part) { + if (++self._nparts > partsLimit) { + self.parser.removeListener('part', onPart); + self.parser.on('part', skipPart); + boy.hitPartsLimit = true; + boy.emit('partsLimit'); + return skipPart(part); + } + + // hack because streams2 _always_ doesn't emit 'end' until nextTick, so let + // us emit 'end' early since we know the part has ended if we are already + // seeing the next part + if (curField) { + var field = curField; + field.emit('end'); + field.removeAllListeners('end'); + } + + part.on('header', function(header) { + var contype, + fieldname, + parsed, + charset, + encoding, + filename, + nsize = 0; + + if (header['content-type']) { + parsed = parseParams(header['content-type'][0]); + if (parsed[0]) { + contype = parsed[0].toLowerCase(); + for (i = 0, len = parsed.length; i < len; ++i) { + if (RE_CHARSET.test(parsed[i][0])) { + charset = parsed[i][1].toLowerCase(); + break; + } + } + } + } + + if (contype === undefined) + contype = 'text/plain'; + if (charset === undefined) + charset = defCharset; + + if (header['content-disposition']) { + parsed = parseParams(header['content-disposition'][0]); + if (!RE_FIELD.test(parsed[0])) + return skipPart(part); + for (i = 0, len = parsed.length; i < len; ++i) { + if (RE_NAME.test(parsed[i][0])) { + fieldname = decodeText(parsed[i][1], 'binary', 'utf8'); + } else if (RE_FILENAME.test(parsed[i][0])) { + filename = decodeText(parsed[i][1], 'binary', 'utf8'); + if (!preservePath) + filename = basename(filename); + } + } + } else + return skipPart(part); + + if (header['content-transfer-encoding']) + encoding = header['content-transfer-encoding'][0].toLowerCase(); + else + encoding = '7bit'; + + var onData, + onEnd; + if (contype === 'application/octet-stream' || filename !== undefined) { + // file/binary field + if (nfiles === filesLimit) { + if (!boy.hitFilesLimit) { + boy.hitFilesLimit = true; + boy.emit('filesLimit'); + } + return skipPart(part); + } + + ++nfiles; + + if (!boy._events.file) { + self.parser._ignore(); + return; + } + + ++nends; + var file = new FileStream(fileopts); + curFile = file; + file.on('end', function() { + --nends; + checkFinished(); + }); + file._read = function(n) { + if (!self._pause) + return; + self._pause = false; + if (self._cb && !self._needDrain) { + var cb = self._cb; + self._cb = undefined; + cb(); + } + }; + boy.emit('file', fieldname, file, filename, encoding, contype); + + onData = function(data) { + if ((nsize += data.length) > fileSizeLimit) { + var extralen = (fileSizeLimit - (nsize - data.length)); + if (extralen > 0) + file.push(data.slice(0, extralen)); + file.emit('limit'); + file.truncated = true; + part.removeAllListeners('data'); + } else if (!file.push(data)) + self._pause = true; + }; + + onEnd = function() { + curFile = undefined; + file.push(null); + }; + } else { + // non-file field + if (nfields === fieldsLimit) { + if (!boy.hitFieldsLimit) { + boy.hitFieldsLimit = true; + boy.emit('fieldsLimit'); + } + return skipPart(part); + } + + ++nfields; + ++nends; + var buffer = '', + truncated = false; + curField = part; + + onData = function(data) { + if ((nsize += data.length) > fieldSizeLimit) { + var extralen = (fieldSizeLimit - (nsize - data.length)); + buffer += data.toString('binary', 0, extralen); + truncated = true; + part.removeAllListeners('data'); + } else + buffer += data.toString('binary'); + }; + + onEnd = function() { + curField = undefined; + if (buffer.length) + buffer = decodeText(buffer, 'binary', charset); + boy.emit('field', fieldname, buffer, false, truncated, encoding, contype); + --nends; + checkFinished(); + }; + } + + /* As of node@2efe4ab761666 (v0.10.29+/v0.11.14+), busboy had become + broken. Streams2/streams3 is a huge black box of confusion, but + somehow overriding the sync state seems to fix things again (and still + seems to work for previous node versions). + */ + part._readableState.sync = false; + + part.on('data', onData); + part.on('end', onEnd); + }).on('error', function(err) { + if (curFile) + curFile.emit('error', err); + }); + }).on('error', function(err) { + boy.emit('error', err); + }).on('finish', function() { + finished = true; + checkFinished(); + }); +} + +Multipart.prototype.write = function(chunk, cb) { + var r; + if ((r = this.parser.write(chunk)) && !this._pause) + cb(); + else { + this._needDrain = !r; + this._cb = cb; + } +}; + +Multipart.prototype.end = function() { + var self = this; + if (this._nparts === 0 && !self._boy._done) { + process.nextTick(function() { + self._boy._done = true; + self._boy.emit('finish'); + }); + } else if (this.parser.writable) + this.parser.end(); +}; + +function skipPart(part) { + part.resume(); +} + +function FileStream(opts) { + if (!(this instanceof FileStream)) + return new FileStream(opts); + ReadableStream.call(this, opts); + + this.truncated = false; +} +inherits(FileStream, ReadableStream); + +FileStream.prototype._read = function(n) {}; + +module.exports = Multipart; diff --git a/node_modules/multer/node_modules/busboy/lib/types/urlencoded.js b/node_modules/multer/node_modules/busboy/lib/types/urlencoded.js new file mode 100644 index 0000000..361c804 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/lib/types/urlencoded.js @@ -0,0 +1,214 @@ +var Decoder = require('../utils').Decoder, + decodeText = require('../utils').decodeText; + +var RE_CHARSET = /^charset$/i; + +UrlEncoded.detect = /^application\/x-www-form-urlencoded/i; +function UrlEncoded(boy, cfg) { + if (!(this instanceof UrlEncoded)) + return new UrlEncoded(boy, cfg); + var limits = cfg.limits, + headers = cfg.headers, + parsedConType = cfg.parsedConType; + this.boy = boy; + + this.fieldSizeLimit = (limits && typeof limits.fieldSize === 'number' + ? limits.fieldSize + : 1 * 1024 * 1024); + this.fieldNameSizeLimit = (limits && typeof limits.fieldNameSize === 'number' + ? limits.fieldNameSize + : 100); + this.fieldsLimit = (limits && typeof limits.fields === 'number' + ? limits.fields + : Infinity); + + var charset; + for (var i = 0, len = parsedConType.length; i < len; ++i) { + if (Array.isArray(parsedConType[i]) + && RE_CHARSET.test(parsedConType[i][0])) { + charset = parsedConType[i][1].toLowerCase(); + break; + } + } + + if (charset === undefined) + charset = cfg.defCharset || 'utf8'; + + this.decoder = new Decoder(); + this.charset = charset; + this._fields = 0; + this._state = 'key'; + this._checkingBytes = true; + this._bytesKey = 0; + this._bytesVal = 0; + this._key = ''; + this._val = ''; + this._keyTrunc = false; + this._valTrunc = false; + this._hitlimit = false; +} + +UrlEncoded.prototype.write = function(data, cb) { + if (this._fields === this.fieldsLimit) { + if (!this.boy.hitFieldsLimit) { + this.boy.hitFieldsLimit = true; + this.boy.emit('fieldsLimit'); + } + return cb(); + } + + var idxeq, idxamp, i, p = 0, len = data.length; + + while (p < len) { + if (this._state === 'key') { + idxeq = idxamp = undefined; + for (i = p; i < len; ++i) { + if (!this._checkingBytes) + ++p; + if (data[i] === 0x3D/*=*/) { + idxeq = i; + break; + } else if (data[i] === 0x26/*&*/) { + idxamp = i; + break; + } + if (this._checkingBytes && this._bytesKey === this.fieldNameSizeLimit) { + this._hitLimit = true; + break; + } else if (this._checkingBytes) + ++this._bytesKey; + } + + if (idxeq !== undefined) { + // key with assignment + if (idxeq > p) + this._key += this.decoder.write(data.toString('binary', p, idxeq)); + this._state = 'val'; + + this._hitLimit = false; + this._checkingBytes = true; + this._val = ''; + this._bytesVal = 0; + this._valTrunc = false; + this.decoder.reset(); + + p = idxeq + 1; + } else if (idxamp !== undefined) { + // key with no assignment + ++this._fields; + var key, keyTrunc = this._keyTrunc; + if (idxamp > p) + key = (this._key += this.decoder.write(data.toString('binary', p, idxamp))); + else + key = this._key; + + this._hitLimit = false; + this._checkingBytes = true; + this._key = ''; + this._bytesKey = 0; + this._keyTrunc = false; + this.decoder.reset(); + + if (key.length) { + this.boy.emit('field', decodeText(key, 'binary', this.charset), + '', + keyTrunc, + false); + } + + p = idxamp + 1; + if (this._fields === this.fieldsLimit) + return cb(); + } else if (this._hitLimit) { + // we may not have hit the actual limit if there are encoded bytes... + if (i > p) + this._key += this.decoder.write(data.toString('binary', p, i)); + p = i; + if ((this._bytesKey = this._key.length) === this.fieldNameSizeLimit) { + // yep, we actually did hit the limit + this._checkingBytes = false; + this._keyTrunc = true; + } + } else { + if (p < len) + this._key += this.decoder.write(data.toString('binary', p)); + p = len; + } + } else { + idxamp = undefined; + for (i = p; i < len; ++i) { + if (!this._checkingBytes) + ++p; + if (data[i] === 0x26/*&*/) { + idxamp = i; + break; + } + if (this._checkingBytes && this._bytesVal === this.fieldSizeLimit) { + this._hitLimit = true; + break; + } + else if (this._checkingBytes) + ++this._bytesVal; + } + + if (idxamp !== undefined) { + ++this._fields; + if (idxamp > p) + this._val += this.decoder.write(data.toString('binary', p, idxamp)); + this.boy.emit('field', decodeText(this._key, 'binary', this.charset), + decodeText(this._val, 'binary', this.charset), + this._keyTrunc, + this._valTrunc); + this._state = 'key'; + + this._hitLimit = false; + this._checkingBytes = true; + this._key = ''; + this._bytesKey = 0; + this._keyTrunc = false; + this.decoder.reset(); + + p = idxamp + 1; + if (this._fields === this.fieldsLimit) + return cb(); + } else if (this._hitLimit) { + // we may not have hit the actual limit if there are encoded bytes... + if (i > p) + this._val += this.decoder.write(data.toString('binary', p, i)); + p = i; + if ((this._val === '' && this.fieldSizeLimit === 0) + || (this._bytesVal = this._val.length) === this.fieldSizeLimit) { + // yep, we actually did hit the limit + this._checkingBytes = false; + this._valTrunc = true; + } + } else { + if (p < len) + this._val += this.decoder.write(data.toString('binary', p)); + p = len; + } + } + } + cb(); +}; + +UrlEncoded.prototype.end = function() { + if (this.boy._done) + return; + + if (this._state === 'key' && this._key.length > 0) { + this.boy.emit('field', decodeText(this._key, 'binary', this.charset), + '', + this._keyTrunc, + false); + } else if (this._state === 'val') { + this.boy.emit('field', decodeText(this._key, 'binary', this.charset), + decodeText(this._val, 'binary', this.charset), + this._keyTrunc, + this._valTrunc); + } + this.boy._done = true; + this.boy.emit('finish'); +}; + +module.exports = UrlEncoded; diff --git a/node_modules/multer/node_modules/busboy/lib/utils.js b/node_modules/multer/node_modules/busboy/lib/utils.js new file mode 100644 index 0000000..ed3129e --- /dev/null +++ b/node_modules/multer/node_modules/busboy/lib/utils.js @@ -0,0 +1,186 @@ +var jsencoding = require('../deps/encoding/encoding'); + +var RE_ENCODED = /%([a-fA-F0-9]{2})/g; +function encodedReplacer(match, byte) { + return String.fromCharCode(parseInt(byte, 16)); +} +function parseParams(str) { + var res = [], + state = 'key', + charset = '', + inquote = false, + escaping = false, + p = 0, + tmp = ''; + + for (var i = 0, len = str.length; i < len; ++i) { + if (str[i] === '\\' && inquote) { + if (escaping) + escaping = false; + else { + escaping = true; + continue; + } + } else if (str[i] === '"') { + if (!escaping) { + if (inquote) { + inquote = false; + state = 'key'; + } else + inquote = true; + continue; + } else + escaping = false; + } else { + if (escaping && inquote) + tmp += '\\'; + escaping = false; + if ((state === 'charset' || state === 'lang') && str[i] === "'") { + if (state === 'charset') { + state = 'lang'; + charset = tmp.substring(1); + } else + state = 'value'; + tmp = ''; + continue; + } else if (state === 'key' + && (str[i] === '*' || str[i] === '=') + && res.length) { + if (str[i] === '*') + state = 'charset'; + else + state = 'value'; + res[p] = [tmp, undefined]; + tmp = ''; + continue; + } else if (!inquote && str[i] === ';') { + state = 'key'; + if (charset) { + if (tmp.length) { + tmp = decodeText(tmp.replace(RE_ENCODED, encodedReplacer), + 'binary', + charset); + } + charset = ''; + } + if (res[p] === undefined) + res[p] = tmp; + else + res[p][1] = tmp; + tmp = ''; + ++p; + continue; + } else if (!inquote && (str[i] === ' ' || str[i] === '\t')) + continue; + } + tmp += str[i]; + } + if (charset && tmp.length) { + tmp = decodeText(tmp.replace(RE_ENCODED, encodedReplacer), + 'binary', + charset); + } + + if (res[p] === undefined) { + if (tmp) + res[p] = tmp; + } else + res[p][1] = tmp; + + return res; +}; +exports.parseParams = parseParams; + + +function decodeText(text, textEncoding, destEncoding) { + var ret; + if (text && jsencoding.encodingExists(destEncoding)) { + try { + ret = jsencoding.TextDecoder(destEncoding) + .decode(new Buffer(text, textEncoding)); + } catch(e) {} + } + return (typeof ret === 'string' ? ret : text); +} +exports.decodeText = decodeText; + + +var HEX = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +], RE_PLUS = /\+/g; +function Decoder() { + this.buffer = undefined; +} +Decoder.prototype.write = function(str) { + // Replace '+' with ' ' before decoding + str = str.replace(RE_PLUS, ' '); + var res = ''; + var i = 0, p = 0, len = str.length; + for (; i < len; ++i) { + if (this.buffer !== undefined) { + if (!HEX[str.charCodeAt(i)]) { + res += '%' + this.buffer; + this.buffer = undefined; + --i; // retry character + } else { + this.buffer += str[i]; + ++p; + if (this.buffer.length === 2) { + res += String.fromCharCode(parseInt(this.buffer, 16)); + this.buffer = undefined; + } + } + } else if (str[i] === '%') { + if (i > p) { + res += str.substring(p, i); + p = i; + } + this.buffer = ''; + ++p; + } + } + if (p < len && this.buffer === undefined) + res += str.substring(p); + return res; +}; +Decoder.prototype.reset = function() { + this.buffer = undefined; +}; +exports.Decoder = Decoder; + + +var RE_SPLIT_POSIX = + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/, + RE_SPLIT_DEVICE = + /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/, + RE_SPLIT_WINDOWS = + /^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/; +function splitPathPosix(filename) { + return RE_SPLIT_POSIX.exec(filename).slice(1); +} +function splitPathWindows(filename) { + // Separate device+slash from tail + var result = RE_SPLIT_DEVICE.exec(filename), + device = (result[1] || '') + (result[2] || ''), + tail = result[3] || ''; + // Split the tail into dir, basename and extension + var result2 = RE_SPLIT_WINDOWS.exec(tail), + dir = result2[1], + basename = result2[2], + ext = result2[3]; + return [device, dir, basename, ext]; +} +function basename(path) { + var f = splitPathPosix(path)[2]; + if (f === path) + f = splitPathWindows(path)[2]; + return f; +} +exports.basename = basename; \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/.travis.yml b/node_modules/multer/node_modules/busboy/node_modules/dicer/.travis.yml new file mode 100644 index 0000000..28a8b69 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/.travis.yml @@ -0,0 +1,16 @@ +sudo: false +language: cpp +notifications: + email: false +env: + matrix: + - TRAVIS_NODE_VERSION="0.10" + - TRAVIS_NODE_VERSION="0.12" + - TRAVIS_NODE_VERSION="4" + - TRAVIS_NODE_VERSION="5" +install: + - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION + - node --version + - npm --version + - npm install +script: npm test diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/LICENSE b/node_modules/multer/node_modules/busboy/node_modules/dicer/LICENSE new file mode 100644 index 0000000..290762e --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/LICENSE @@ -0,0 +1,19 @@ +Copyright Brian White. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/README.md b/node_modules/multer/node_modules/busboy/node_modules/dicer/README.md new file mode 100644 index 0000000..5e71528 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/README.md @@ -0,0 +1,122 @@ + +Description +=========== + +A very fast streaming multipart parser for node.js. + +Benchmarks can be found [here](https://github.com/mscdex/dicer/wiki/Benchmarks). + + +Requirements +============ + +* [node.js](http://nodejs.org/) -- v0.8.0 or newer + + +Install +============ + + npm install dicer + + +Examples +======== + +* Parse an HTTP form upload + +```javascript +var inspect = require('util').inspect, + http = require('http'); + +var Dicer = require('dicer'); + + // quick and dirty way to parse multipart boundary +var RE_BOUNDARY = /^multipart\/.+?(?:; boundary=(?:(?:"(.+)")|(?:([^\s]+))))$/i, + HTML = new Buffer('\ +
        \ +
        \ +
        \ + \ +
        \ + '), + PORT = 8080; + +http.createServer(function(req, res) { + var m; + if (req.method === 'POST' + && req.headers['content-type'] + && (m = RE_BOUNDARY.exec(req.headers['content-type']))) { + var d = new Dicer({ boundary: m[1] || m[2] }); + + d.on('part', function(p) { + console.log('New part!'); + p.on('header', function(header) { + for (var h in header) { + console.log('Part header: k: ' + inspect(h) + + ', v: ' + inspect(header[h])); + } + }); + p.on('data', function(data) { + console.log('Part data: ' + inspect(data.toString())); + }); + p.on('end', function() { + console.log('End of part\n'); + }); + }); + d.on('finish', function() { + console.log('End of parts'); + res.writeHead(200); + res.end('Form submission successful!'); + }); + req.pipe(d); + } else if (req.method === 'GET' && req.url === '/') { + res.writeHead(200); + res.end(HTML); + } else { + res.writeHead(404); + res.end(); + } +}).listen(PORT, function() { + console.log('Listening for requests on port ' + PORT); +}); +``` + + +API +=== + +_Dicer_ is a _WritableStream_ + +Dicer (special) events +---------------------- + +* **finish**() - Emitted when all parts have been parsed and the Dicer instance has been ended. + +* **part**(< _PartStream_ >stream) - Emitted when a new part has been found. + +* **preamble**(< _PartStream_ >stream) - Emitted for preamble if you should happen to need it (can usually be ignored). + +* **trailer**(< _Buffer_ >data) - Emitted when trailing data was found after the terminating boundary (as with the preamble, this can usually be ignored too). + + +Dicer methods +------------- + +* **(constructor)**(< _object_ >config) - Creates and returns a new Dicer instance with the following valid `config` settings: + + * **boundary** - _string_ - This is the boundary used to detect the beginning of a new part. + + * **headerFirst** - _boolean_ - If true, preamble header parsing will be performed first. + + * **maxHeaderPairs** - _integer_ - The maximum number of header key=>value pairs to parse **Default:** 2000 (same as node's http). + +* **setBoundary**(< _string_ >boundary) - _(void)_ - Sets the boundary to use for parsing and performs some initialization needed for parsing. You should only need to use this if you set `headerFirst` to true in the constructor and are parsing the boundary from the preamble header. + + + +_PartStream_ is a _ReadableStream_ + +PartStream (special) events +--------------------------- + +* **header**(< _object_ >header) - An object containing the header for this particular part. Each property value is an _array_ of one or more string values. diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/dicer-bench-multipart-parser.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/dicer-bench-multipart-parser.js new file mode 100644 index 0000000..a418cb4 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/dicer-bench-multipart-parser.js @@ -0,0 +1,63 @@ +var assert = require('assert'); +var Dicer = require('..'), + boundary = '-----------------------------168072824752491622650073', + d = new Dicer({ boundary: boundary }), + mb = 100, + buffer = createMultipartBuffer(boundary, mb * 1024 * 1024), + callbacks = + { partBegin: -1, + partEnd: -1, + headerField: -1, + headerValue: -1, + partData: -1, + end: -1, + }; + + +d.on('part', function(p) { + callbacks.partBegin++; + p.on('header', function(header) { + /*for (var h in header) + console.log('Part header: k: ' + inspect(h) + ', v: ' + inspect(header[h]));*/ + }); + p.on('data', function(data) { + callbacks.partData++; + //console.log('Part data: ' + inspect(data.toString())); + }); + p.on('end', function() { + //console.log('End of part\n'); + callbacks.partEnd++; + }); +}); +d.on('end', function() { + //console.log('End of parts'); + callbacks.end++; +}); + +var start = +new Date(), + nparsed = d.write(buffer), + duration = +new Date - start, + mbPerSec = (mb / (duration / 1000)).toFixed(2); + +console.log(mbPerSec+' mb/sec'); + +//assert.equal(nparsed, buffer.length); + +function createMultipartBuffer(boundary, size) { + var head = + '--'+boundary+'\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + , tail = '\r\n--'+boundary+'--\r\n' + , buffer = new Buffer(size); + + buffer.write(head, 'ascii', 0); + buffer.write(tail, 'ascii', buffer.length - tail.length); + return buffer; +} + +process.on('exit', function() { + /*for (var k in callbacks) { + assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]); + }*/ +}); diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/formidable-bench-multipart-parser.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/formidable-bench-multipart-parser.js new file mode 100644 index 0000000..faa6aaf --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/formidable-bench-multipart-parser.js @@ -0,0 +1,70 @@ +var assert = require('assert'); +require('../node_modules/formidable/test/common'); +var multipartParser = require('../node_modules/formidable/lib/multipart_parser'), + MultipartParser = multipartParser.MultipartParser, + parser = new MultipartParser(), + boundary = '-----------------------------168072824752491622650073', + mb = 100, + buffer = createMultipartBuffer(boundary, mb * 1024 * 1024), + callbacks = + { partBegin: -1, + partEnd: -1, + headerField: -1, + headerValue: -1, + partData: -1, + end: -1, + }; + + +parser.initWithBoundary(boundary); +parser.onHeaderField = function() { + callbacks.headerField++; +}; + +parser.onHeaderValue = function() { + callbacks.headerValue++; +}; + +parser.onPartBegin = function() { + callbacks.partBegin++; +}; + +parser.onPartData = function() { + callbacks.partData++; +}; + +parser.onPartEnd = function() { + callbacks.partEnd++; +}; + +parser.onEnd = function() { + callbacks.end++; +}; + +var start = +new Date(), + nparsed = parser.write(buffer), + duration = +new Date - start, + mbPerSec = (mb / (duration / 1000)).toFixed(2); + +console.log(mbPerSec+' mb/sec'); + +//assert.equal(nparsed, buffer.length); + +function createMultipartBuffer(boundary, size) { + var head = + '--'+boundary+'\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + , tail = '\r\n--'+boundary+'--\r\n' + , buffer = new Buffer(size); + + buffer.write(head, 'ascii', 0); + buffer.write(tail, 'ascii', buffer.length - tail.length); + return buffer; +} + +process.on('exit', function() { + /*for (var k in callbacks) { + assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]); + }*/ +}); diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/multipartser-bench-multipart-parser.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/multipartser-bench-multipart-parser.js new file mode 100644 index 0000000..912b996 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/multipartser-bench-multipart-parser.js @@ -0,0 +1,56 @@ +var assert = require('assert'); +var multipartser = require('multipartser'), + boundary = '-----------------------------168072824752491622650073', + parser = multipartser(), + mb = 100, + buffer = createMultipartBuffer(boundary, mb * 1024 * 1024), + callbacks = + { partBegin: -1, + partEnd: -1, + headerField: -1, + headerValue: -1, + partData: -1, + end: -1, + }; + +parser.boundary( boundary ); + +parser.on( 'part', function ( part ) { +}); + +parser.on( 'end', function () { + //console.log( 'completed parsing' ); +}); + +parser.on( 'error', function ( error ) { + console.error( error ); +}); + +var start = +new Date(), + nparsed = parser.data(buffer), + nend = parser.end(), + duration = +new Date - start, + mbPerSec = (mb / (duration / 1000)).toFixed(2); + +console.log(mbPerSec+' mb/sec'); + +//assert.equal(nparsed, buffer.length); + +function createMultipartBuffer(boundary, size) { + var head = + '--'+boundary+'\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + , tail = '\r\n--'+boundary+'--\r\n' + , buffer = new Buffer(size); + + buffer.write(head, 'ascii', 0); + buffer.write(tail, 'ascii', buffer.length - tail.length); + return buffer; +} + +process.on('exit', function() { + /*for (var k in callbacks) { + assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]); + }*/ +}); diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/multiparty-bench-multipart-parser.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/multiparty-bench-multipart-parser.js new file mode 100644 index 0000000..dd6ad5a --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/multiparty-bench-multipart-parser.js @@ -0,0 +1,76 @@ +var assert = require('assert'), + Form = require('multiparty').Form, + boundary = '-----------------------------168072824752491622650073', + mb = 100, + buffer = createMultipartBuffer(boundary, mb * 1024 * 1024), + callbacks = + { partBegin: -1, + partEnd: -1, + headerField: -1, + headerValue: -1, + partData: -1, + end: -1, + }; + +var form = new Form({ boundary: boundary }); + +hijack('onParseHeaderField', function() { + callbacks.headerField++; +}); + +hijack('onParseHeaderValue', function() { + callbacks.headerValue++; +}); + +hijack('onParsePartBegin', function() { + callbacks.partBegin++; +}); + +hijack('onParsePartData', function() { + callbacks.partData++; +}); + +hijack('onParsePartEnd', function() { + callbacks.partEnd++; +}); + +form.on('finish', function() { + callbacks.end++; +}); + +var start = new Date(); +form.write(buffer, function(err) { + var duration = new Date() - start; + assert.ifError(err); + var mbPerSec = (mb / (duration / 1000)).toFixed(2); + console.log(mbPerSec+' mb/sec'); +}); + +//assert.equal(nparsed, buffer.length); + +function createMultipartBuffer(boundary, size) { + var head = + '--'+boundary+'\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + , tail = '\r\n--'+boundary+'--\r\n' + , buffer = new Buffer(size); + + buffer.write(head, 'ascii', 0); + buffer.write(tail, 'ascii', buffer.length - tail.length); + return buffer; +} + +process.on('exit', function() { + /*for (var k in callbacks) { + assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]); + }*/ +}); + +function hijack(name, fn) { + var oldFn = form[name]; + form[name] = function() { + fn(); + return oldFn.apply(this, arguments); + }; +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/parted-bench-multipart-parser.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/parted-bench-multipart-parser.js new file mode 100644 index 0000000..cf79ba9 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/parted-bench-multipart-parser.js @@ -0,0 +1,63 @@ +// A special, edited version of the multipart parser from parted is needed here +// because otherwise it attempts to do some things above and beyond just parsing +// -- like saving to disk and whatnot + +var assert = require('assert'); +var Parser = require('./parted-multipart'), + boundary = '-----------------------------168072824752491622650073', + parser = new Parser('boundary=' + boundary), + mb = 100, + buffer = createMultipartBuffer(boundary, mb * 1024 * 1024), + callbacks = + { partBegin: -1, + partEnd: -1, + headerField: -1, + headerValue: -1, + partData: -1, + end: -1, + }; + + +parser.on('header', function() { + //callbacks.headerField++; +}); + +parser.on('data', function() { + //callbacks.partBegin++; +}); + +parser.on('part', function() { + +}); + +parser.on('end', function() { + //callbacks.end++; +}); + +var start = +new Date(), + nparsed = parser.write(buffer), + duration = +new Date - start, + mbPerSec = (mb / (duration / 1000)).toFixed(2); + +console.log(mbPerSec+' mb/sec'); + +//assert.equal(nparsed, buffer.length); + +function createMultipartBuffer(boundary, size) { + var head = + '--'+boundary+'\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + , tail = '\r\n--'+boundary+'--\r\n' + , buffer = new Buffer(size); + + buffer.write(head, 'ascii', 0); + buffer.write(tail, 'ascii', buffer.length - tail.length); + return buffer; +} + +process.on('exit', function() { + /*for (var k in callbacks) { + assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]); + }*/ +}); diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/parted-multipart.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/parted-multipart.js new file mode 100644 index 0000000..759445f --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/bench/parted-multipart.js @@ -0,0 +1,485 @@ +/** + * Parted (https://github.com/chjj/parted) + * A streaming multipart state parser. + * Copyright (c) 2011, Christopher Jeffrey. (MIT Licensed) + */ + +var fs = require('fs') + , path = require('path') + , EventEmitter = require('events').EventEmitter + , StringDecoder = require('string_decoder').StringDecoder + , set = require('qs').set + , each = Array.prototype.forEach; + +/** + * Character Constants + */ + +var DASH = '-'.charCodeAt(0) + , CR = '\r'.charCodeAt(0) + , LF = '\n'.charCodeAt(0) + , COLON = ':'.charCodeAt(0) + , SPACE = ' '.charCodeAt(0); + +/** + * Parser + */ + +var Parser = function(type, options) { + if (!(this instanceof Parser)) { + return new Parser(type, options); + } + + EventEmitter.call(this); + + this.writable = true; + this.readable = true; + + this.options = options || {}; + + var key = grab(type, 'boundary'); + if (!key) { + return this._error('No boundary key found.'); + } + + this.key = new Buffer('\r\n--' + key); + + this._key = {}; + each.call(this.key, function(ch) { + this._key[ch] = true; + }, this); + + this.state = 'start'; + this.pending = 0; + this.written = 0; + this.writtenDisk = 0; + this.buff = new Buffer(200); + + this.preamble = true; + this.epilogue = false; + + this._reset(); +}; + +Parser.prototype.__proto__ = EventEmitter.prototype; + +/** + * Parsing + */ + +Parser.prototype.write = function(data) { + if (!this.writable + || this.epilogue) return; + + try { + this._parse(data); + } catch (e) { + this._error(e); + } + + return true; +}; + +Parser.prototype.end = function(data) { + if (!this.writable) return; + + if (data) this.write(data); + + if (!this.epilogue) { + return this._error('Message underflow.'); + } + + return true; +}; + +Parser.prototype._parse = function(data) { + var i = 0 + , len = data.length + , buff = this.buff + , key = this.key + , ch + , val + , j; + + for (; i < len; i++) { + if (this.pos >= 200) { + return this._error('Potential buffer overflow.'); + } + + ch = data[i]; + + switch (this.state) { + case 'start': + switch (ch) { + case DASH: + this.pos = 3; + this.state = 'key'; + break; + default: + break; + } + break; + case 'key': + if (this.pos === key.length) { + this.state = 'key_end'; + i--; + } else if (ch !== key[this.pos]) { + if (this.preamble) { + this.state = 'start'; + i--; + } else { + this.state = 'body'; + val = this.pos - i; + if (val > 0) { + this._write(key.slice(0, val)); + } + i--; + } + } else { + this.pos++; + } + break; + case 'key_end': + switch (ch) { + case CR: + this.state = 'key_line_end'; + break; + case DASH: + this.state = 'key_dash_end'; + break; + default: + return this._error('Expected CR or DASH.'); + } + break; + case 'key_line_end': + switch (ch) { + case LF: + if (this.preamble) { + this.preamble = false; + } else { + this._finish(); + } + this.state = 'header_name'; + this.pos = 0; + break; + default: + return this._error('Expected CR.'); + } + break; + case 'key_dash_end': + switch (ch) { + case DASH: + this.epilogue = true; + this._finish(); + return; + default: + return this._error('Expected DASH.'); + } + break; + case 'header_name': + switch (ch) { + case COLON: + this.header = buff.toString('ascii', 0, this.pos); + this.pos = 0; + this.state = 'header_val'; + break; + default: + buff[this.pos++] = ch | 32; + break; + } + break; + case 'header_val': + switch (ch) { + case CR: + this.state = 'header_val_end'; + break; + case SPACE: + if (this.pos === 0) { + break; + } + ; // FALL-THROUGH + default: + buff[this.pos++] = ch; + break; + } + break; + case 'header_val_end': + switch (ch) { + case LF: + val = buff.toString('ascii', 0, this.pos); + this._header(this.header, val); + this.pos = 0; + this.state = 'header_end'; + break; + default: + return this._error('Expected LF.'); + } + break; + case 'header_end': + switch (ch) { + case CR: + this.state = 'head_end'; + break; + default: + this.state = 'header_name'; + i--; + break; + } + break; + case 'head_end': + switch (ch) { + case LF: + this.state = 'body'; + i++; + if (i >= len) return; + data = data.slice(i); + i = -1; + len = data.length; + break; + default: + return this._error('Expected LF.'); + } + break; + case 'body': + switch (ch) { + case CR: + if (i > 0) { + this._write(data.slice(0, i)); + } + this.pos = 1; + this.state = 'key'; + data = data.slice(i); + i = 0; + len = data.length; + break; + default: + // boyer-moore-like algorithm + // at felixge's suggestion + while ((j = i + key.length - 1) < len) { + if (this._key[data[j]]) break; + i = j; + } + break; + } + break; + } + } + + if (this.state === 'body') { + this._write(data); + } +}; + +Parser.prototype._header = function(name, val) { + /*if (name === 'content-disposition') { + this.field = grab(val, 'name'); + this.file = grab(val, 'filename'); + + if (this.file) { + this.data = stream(this.file, this.options.path); + } else { + this.decode = new StringDecoder('utf8'); + this.data = ''; + } + }*/ + + return this.emit('header', name, val); +}; + +Parser.prototype._write = function(data) { + /*if (this.data == null) { + return this._error('No disposition.'); + } + + if (this.file) { + this.data.write(data); + this.writtenDisk += data.length; + } else { + this.data += this.decode.write(data); + this.written += data.length; + }*/ + + this.emit('data', data); +}; + +Parser.prototype._reset = function() { + this.pos = 0; + this.decode = null; + this.field = null; + this.data = null; + this.file = null; + this.header = null; +}; + +Parser.prototype._error = function(err) { + this.destroy(); + this.emit('error', typeof err === 'string' + ? new Error(err) + : err); +}; + +Parser.prototype.destroy = function(err) { + this.writable = false; + this.readable = false; + this._reset(); +}; + +Parser.prototype._finish = function() { + var self = this + , field = this.field + , data = this.data + , file = this.file + , part; + + this.pending++; + + this._reset(); + + if (data && data.path) { + part = data.path; + data.end(next); + } else { + part = data; + next(); + } + + function next() { + if (!self.readable) return; + + self.pending--; + + self.emit('part', field, part); + + if (data && data.path) { + self.emit('file', field, part, file); + } + + if (self.epilogue && !self.pending) { + self.emit('end'); + self.destroy(); + } + } +}; + +/** + * Uploads + */ + +Parser.root = process.platform === 'win32' + ? 'C:/Temp' + : '/tmp'; + +/** + * Middleware + */ + +Parser.middleware = function(options) { + options = options || {}; + return function(req, res, next) { + if (options.ensureBody) { + req.body = {}; + } + + if (req.method === 'GET' + || req.method === 'HEAD' + || req._multipart) return next(); + + req._multipart = true; + + var type = req.headers['content-type']; + + if (type) type = type.split(';')[0].trim().toLowerCase(); + + if (type === 'multipart/form-data') { + Parser.handle(req, res, next, options); + } else { + next(); + } + }; +}; + +/** + * Handler + */ + +Parser.handle = function(req, res, next, options) { + var parser = new Parser(req.headers['content-type'], options) + , diskLimit = options.diskLimit + , limit = options.limit + , parts = {} + , files = {}; + + parser.on('error', function(err) { + req.destroy(); + next(err); + }); + + parser.on('part', function(field, part) { + set(parts, field, part); + }); + + parser.on('file', function(field, path, name) { + set(files, field, { + path: path, + name: name, + toString: function() { + return path; + } + }); + }); + + parser.on('data', function() { + if (this.writtenDisk > diskLimit || this.written > limit) { + this.emit('error', new Error('Overflow.')); + this.destroy(); + } + }); + + parser.on('end', next); + + req.body = parts; + req.files = files; + req.pipe(parser); +}; + +/** + * Helpers + */ + +var isWindows = process.platform === 'win32'; + +var stream = function(name, dir) { + var ext = path.extname(name) || '' + , name = path.basename(name, ext) || '' + , dir = dir || Parser.root + , tag; + + tag = Math.random().toString(36).substring(2); + + name = name.substring(0, 200) + '.' + tag; + name = path.join(dir, name) + ext.substring(0, 6); + name = name.replace(/\0/g, ''); + + if (isWindows) { + name = name.replace(/[:*<>|"?]/g, ''); + } + + return fs.createWriteStream(name); +}; + +var grab = function(str, name) { + if (!str) return; + + var rx = new RegExp('\\b' + name + '\\s*=\\s*("[^"]+"|\'[^\']+\'|[^;,]+)', 'i') + , cap = rx.exec(str); + + if (cap) { + return cap[1].trim().replace(/^['"]|['"]$/g, ''); + } +}; + +/** + * Expose + */ + +module.exports = Parser; \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/lib/Dicer.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/lib/Dicer.js new file mode 100644 index 0000000..246b3ea --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/lib/Dicer.js @@ -0,0 +1,240 @@ +var WritableStream = require('stream').Writable + || require('readable-stream').Writable, + inherits = require('util').inherits; + +var StreamSearch = require('streamsearch'); + +var PartStream = require('./PartStream'), + HeaderParser = require('./HeaderParser'); + +var DASH = 45, + B_ONEDASH = new Buffer('-'), + B_CRLF = new Buffer('\r\n'), + EMPTY_FN = function() {}; + +function Dicer(cfg) { + if (!(this instanceof Dicer)) + return new Dicer(cfg); + WritableStream.call(this, cfg); + + if (!cfg || (!cfg.headerFirst && typeof cfg.boundary !== 'string')) + throw new TypeError('Boundary required'); + + if (typeof cfg.boundary === 'string') + this.setBoundary(cfg.boundary); + else + this._bparser = undefined; + + this._headerFirst = cfg.headerFirst; + + var self = this; + + this._dashes = 0; + this._parts = 0; + this._finished = false; + this._realFinish = false; + this._isPreamble = true; + this._justMatched = false; + this._firstWrite = true; + this._inHeader = true; + this._part = undefined; + this._cb = undefined; + this._ignoreData = false; + this._partOpts = (typeof cfg.partHwm === 'number' + ? { highWaterMark: cfg.partHwm } + : {}); + this._pause = false; + + this._hparser = new HeaderParser(cfg); + this._hparser.on('header', function(header) { + self._inHeader = false; + self._part.emit('header', header); + }); + +} +inherits(Dicer, WritableStream); + +Dicer.prototype.emit = function(ev) { + if (ev === 'finish' && !this._realFinish) { + if (!this._finished) { + var self = this; + process.nextTick(function() { + self.emit('error', new Error('Unexpected end of multipart data')); + if (self._part && !self._ignoreData) { + var type = (self._isPreamble ? 'Preamble' : 'Part'); + self._part.emit('error', new Error(type + ' terminated early due to unexpected end of multipart data')); + self._part.push(null); + process.nextTick(function() { + self._realFinish = true; + self.emit('finish'); + self._realFinish = false; + }); + return; + } + self._realFinish = true; + self.emit('finish'); + self._realFinish = false; + }); + } + } else + WritableStream.prototype.emit.apply(this, arguments); +}; + +Dicer.prototype._write = function(data, encoding, cb) { + // ignore unexpected data (e.g. extra trailer data after finished) + if (!this._hparser && !this._bparser) + return cb(); + + if (this._headerFirst && this._isPreamble) { + if (!this._part) { + this._part = new PartStream(this._partOpts); + if (this._events.preamble) + this.emit('preamble', this._part); + else + this._ignore(); + } + var r = this._hparser.push(data); + if (!this._inHeader && r !== undefined && r < data.length) + data = data.slice(r); + else + return cb(); + } + + // allows for "easier" testing + if (this._firstWrite) { + this._bparser.push(B_CRLF); + this._firstWrite = false; + } + + this._bparser.push(data); + + if (this._pause) + this._cb = cb; + else + cb(); +}; + +Dicer.prototype.reset = function() { + this._part = undefined; + this._bparser = undefined; + this._hparser = undefined; +}; + +Dicer.prototype.setBoundary = function(boundary) { + var self = this; + this._bparser = new StreamSearch('\r\n--' + boundary); + this._bparser.on('info', function(isMatch, data, start, end) { + self._oninfo(isMatch, data, start, end); + }); +}; + +Dicer.prototype._ignore = function() { + if (this._part && !this._ignoreData) { + this._ignoreData = true; + this._part.on('error', EMPTY_FN); + // we must perform some kind of read on the stream even though we are + // ignoring the data, otherwise node's Readable stream will not emit 'end' + // after pushing null to the stream + this._part.resume(); + } +}; + +Dicer.prototype._oninfo = function(isMatch, data, start, end) { + var buf, self = this, i = 0, r, ev, shouldWriteMore = true; + + if (!this._part && this._justMatched && data) { + while (this._dashes < 2 && (start + i) < end) { + if (data[start + i] === DASH) { + ++i; + ++this._dashes; + } else { + if (this._dashes) + buf = B_ONEDASH; + this._dashes = 0; + break; + } + } + if (this._dashes === 2) { + if ((start + i) < end && this._events.trailer) + this.emit('trailer', data.slice(start + i, end)); + this.reset(); + this._finished = true; + // no more parts will be added + if (self._parts === 0) { + self._realFinish = true; + self.emit('finish'); + self._realFinish = false; + } + } + if (this._dashes) + return; + } + if (this._justMatched) + this._justMatched = false; + if (!this._part) { + this._part = new PartStream(this._partOpts); + this._part._read = function(n) { + self._unpause(); + }; + ev = this._isPreamble ? 'preamble' : 'part'; + if (this._events[ev]) + this.emit(ev, this._part); + else + this._ignore(); + if (!this._isPreamble) + this._inHeader = true; + } + if (data && start < end && !this._ignoreData) { + if (this._isPreamble || !this._inHeader) { + if (buf) + shouldWriteMore = this._part.push(buf); + shouldWriteMore = this._part.push(data.slice(start, end)); + if (!shouldWriteMore) + this._pause = true; + } else if (!this._isPreamble && this._inHeader) { + if (buf) + this._hparser.push(buf); + r = this._hparser.push(data.slice(start, end)); + if (!this._inHeader && r !== undefined && r < end) + this._oninfo(false, data, start + r, end); + } + } + if (isMatch) { + this._hparser.reset(); + if (this._isPreamble) + this._isPreamble = false; + else { + ++this._parts; + this._part.on('end', function() { + if (--self._parts === 0) { + if (self._finished) { + self._realFinish = true; + self.emit('finish'); + self._realFinish = false; + } else { + self._unpause(); + } + } + }); + } + this._part.push(null); + this._part = undefined; + this._ignoreData = false; + this._justMatched = true; + this._dashes = 0; + } +}; + +Dicer.prototype._unpause = function() { + if (!this._pause) + return; + + this._pause = false; + if (this._cb) { + var cb = this._cb; + this._cb = undefined; + cb(); + } +}; + +module.exports = Dicer; diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/lib/HeaderParser.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/lib/HeaderParser.js new file mode 100644 index 0000000..6ae913c --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/lib/HeaderParser.js @@ -0,0 +1,110 @@ +var EventEmitter = require('events').EventEmitter, + inherits = require('util').inherits; + +var StreamSearch = require('streamsearch'); + +var B_DCRLF = new Buffer('\r\n\r\n'), + RE_CRLF = /\r\n/g, + RE_HDR = /^([^:]+):[ \t]?([\x00-\xFF]+)?$/, + MAX_HEADER_PAIRS = 2000, // from node's http.js + MAX_HEADER_SIZE = 80 * 1024; // from node's http_parser + +function HeaderParser(cfg) { + EventEmitter.call(this); + + var self = this; + this.nread = 0; + this.maxed = false; + this.npairs = 0; + this.maxHeaderPairs = (cfg && typeof cfg.maxHeaderPairs === 'number' + ? cfg.maxHeaderPairs + : MAX_HEADER_PAIRS); + this.buffer = ''; + this.header = {}; + this.finished = false; + this.ss = new StreamSearch(B_DCRLF); + this.ss.on('info', function(isMatch, data, start, end) { + if (data && !self.maxed) { + if (self.nread + (end - start) > MAX_HEADER_SIZE) { + end = (MAX_HEADER_SIZE - self.nread); + self.nread = MAX_HEADER_SIZE; + } else + self.nread += (end - start); + + if (self.nread === MAX_HEADER_SIZE) + self.maxed = true; + + self.buffer += data.toString('binary', start, end); + } + if (isMatch) + self._finish(); + }); +} +inherits(HeaderParser, EventEmitter); + +HeaderParser.prototype.push = function(data) { + var r = this.ss.push(data); + if (this.finished) + return r; +}; + +HeaderParser.prototype.reset = function() { + this.finished = false; + this.buffer = ''; + this.header = {}; + this.ss.reset(); +}; + +HeaderParser.prototype._finish = function() { + if (this.buffer) + this._parseHeader(); + this.ss.matches = this.ss.maxMatches; + var header = this.header; + this.header = {}; + this.buffer = ''; + this.finished = true; + this.nread = this.npairs = 0; + this.maxed = false; + this.emit('header', header); +}; + +HeaderParser.prototype._parseHeader = function() { + if (this.npairs === this.maxHeaderPairs) + return; + + var lines = this.buffer.split(RE_CRLF), len = lines.length, m, h, + modded = false; + + for (var i = 0; i < len; ++i) { + if (lines[i].length === 0) + continue; + if (lines[i][0] === '\t' || lines[i][0] === ' ') { + // folded header content + // RFC2822 says to just remove the CRLF and not the whitespace following + // it, so we follow the RFC and include the leading whitespace ... + this.header[h][this.header[h].length - 1] += lines[i]; + } else { + m = RE_HDR.exec(lines[i]); + if (m) { + h = m[1].toLowerCase(); + if (m[2]) { + if (this.header[h] === undefined) + this.header[h] = [m[2]]; + else + this.header[h].push(m[2]); + } else + this.header[h] = ['']; + if (++this.npairs === this.maxHeaderPairs) + break; + } else { + this.buffer = lines[i]; + modded = true; + break; + } + } + } + if (!modded) + this.buffer = ''; +}; + +module.exports = HeaderParser; diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/lib/PartStream.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/lib/PartStream.js new file mode 100644 index 0000000..9eea84b --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/lib/PartStream.js @@ -0,0 +1,11 @@ +var inherits = require('util').inherits, + ReadableStream = require('stream').Readable || require('readable-stream'); + +function PartStream(opts) { + ReadableStream.call(this, opts); +} +inherits(PartStream, ReadableStream); + +PartStream.prototype._read = function(n) {}; + +module.exports = PartStream; diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/node_modules/streamsearch/LICENSE b/node_modules/multer/node_modules/busboy/node_modules/dicer/node_modules/streamsearch/LICENSE new file mode 100644 index 0000000..290762e --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/node_modules/streamsearch/LICENSE @@ -0,0 +1,19 @@ +Copyright Brian White. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/node_modules/streamsearch/README.md b/node_modules/multer/node_modules/busboy/node_modules/dicer/node_modules/streamsearch/README.md new file mode 100644 index 0000000..6310c20 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/node_modules/streamsearch/README.md @@ -0,0 +1,87 @@ +Description +=========== + +streamsearch is a module for [node.js](http://nodejs.org/) that allows searching a stream using the Boyer-Moore-Horspool algorithm. + +This module is based heavily on the Streaming Boyer-Moore-Horspool C++ implementation by Hongli Lai [here](https://github.com/FooBarWidget/boyer-moore-horspool). + + +Requirements +============ + +* [node.js](http://nodejs.org/) -- v0.8.0 or newer + + +Installation +============ + + npm install streamsearch + +Example +======= + +```javascript + var StreamSearch = require('streamsearch'), + inspect = require('util').inspect; + + var needle = new Buffer([13, 10]), // CRLF + s = new StreamSearch(needle), + chunks = [ + new Buffer('foo'), + new Buffer(' bar'), + new Buffer('\r'), + new Buffer('\n'), + new Buffer('baz, hello\r'), + new Buffer('\n world.'), + new Buffer('\r\n Node.JS rules!!\r\n\r\n') + ]; + s.on('info', function(isMatch, data, start, end) { + if (data) + console.log('data: ' + inspect(data.toString('ascii', start, end))); + if (isMatch) + console.log('match!'); + }); + for (var i = 0, len = chunks.length; i < len; ++i) + s.push(chunks[i]); + + // output: + // + // data: 'foo' + // data: ' bar' + // match! + // data: 'baz, hello' + // match! + // data: ' world.' + // match! + // data: ' Node.JS rules!!' + // match! + // data: '' + // match! +``` + + +API +=== + +Events +------ + +* **info**(< _boolean_ >isMatch[, < _Buffer_ >chunk, < _integer_ >start, < _integer_ >end]) - A match _may_ or _may not_ have been made. In either case, a preceding `chunk` of data _may_ be available that did not match the needle. Data (if available) is in `chunk` between `start` (inclusive) and `end` (exclusive). + + +Properties +---------- + +* **maxMatches** - < _integer_ > - The maximum number of matches. Defaults to Infinity. + +* **matches** - < _integer_ > - The current match count. + + +Functions +--------- + +* **(constructor)**(< _mixed_ >needle) - Creates and returns a new instance for searching for a _Buffer_ or _string_ `needle`. + +* **push**(< _Buffer_ >chunk) - _integer_ - Processes `chunk`. The return value is the last processed index in `chunk` + 1. + +* **reset**() - _(void)_ - Resets internal state. Useful for when you wish to start searching a new/different stream for example. diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/node_modules/streamsearch/lib/sbmh.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/node_modules/streamsearch/lib/sbmh.js new file mode 100644 index 0000000..dbefbc1 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/node_modules/streamsearch/lib/sbmh.js @@ -0,0 +1,213 @@ +/* + Based heavily on the Streaming Boyer-Moore-Horspool C++ implementation + by Hongli Lai at: https://github.com/FooBarWidget/boyer-moore-horspool +*/ +var EventEmitter = require('events').EventEmitter, + inherits = require('util').inherits; + +function jsmemcmp(buf1, pos1, buf2, pos2, num) { + for (var i = 0; i < num; ++i, ++pos1, ++pos2) + if (buf1[pos1] !== buf2[pos2]) + return false; + return true; +} + +function SBMH(needle) { + if (typeof needle === 'string') + needle = new Buffer(needle); + var i, j, needle_len = needle.length; + + this.maxMatches = Infinity; + this.matches = 0; + + this._occ = new Array(256); + this._lookbehind_size = 0; + this._needle = needle; + this._bufpos = 0; + + this._lookbehind = new Buffer(needle_len); + + // Initialize occurrence table. + for (j = 0; j < 256; ++j) + this._occ[j] = needle_len; + + // Populate occurrence table with analysis of the needle, + // ignoring last letter. + if (needle_len >= 1) { + for (i = 0; i < needle_len - 1; ++i) + this._occ[needle[i]] = needle_len - 1 - i; + } +} +inherits(SBMH, EventEmitter); + +SBMH.prototype.reset = function() { + this._lookbehind_size = 0; + this.matches = 0; + this._bufpos = 0; +}; + +SBMH.prototype.push = function(chunk, pos) { + var r, chlen; + if (!Buffer.isBuffer(chunk)) + chunk = new Buffer(chunk, 'binary'); + chlen = chunk.length; + this._bufpos = pos || 0; + while (r !== chlen && this.matches < this.maxMatches) + r = this._sbmh_feed(chunk); + return r; +}; + +SBMH.prototype._sbmh_feed = function(data) { + var len = data.length, needle = this._needle, needle_len = needle.length; + + // Positive: points to a position in `data` + // pos == 3 points to data[3] + // Negative: points to a position in the lookbehind buffer + // pos == -2 points to lookbehind[lookbehind_size - 2] + var pos = -this._lookbehind_size, + last_needle_char = needle[needle_len - 1], + occ = this._occ, + lookbehind = this._lookbehind; + + if (pos < 0) { + // Lookbehind buffer is not empty. Perform Boyer-Moore-Horspool + // search with character lookup code that considers both the + // lookbehind buffer and the current round's haystack data. + // + // Loop until + // there is a match. + // or until + // we've moved past the position that requires the + // lookbehind buffer. In this case we switch to the + // optimized loop. + // or until + // the character to look at lies outside the haystack. + while (pos < 0 && pos <= len - needle_len) { + var ch = this._sbmh_lookup_char(data, pos + needle_len - 1); + + if (ch === last_needle_char + && this._sbmh_memcmp(data, pos, needle_len - 1)) { + this._lookbehind_size = 0; + ++this.matches; + if (pos > -this._lookbehind_size) + this.emit('info', true, lookbehind, 0, this._lookbehind_size + pos); + else + this.emit('info', true); + + this._bufpos = pos + needle_len; + return pos + needle_len; + } else + pos += occ[ch]; + } + + // No match. + + if (pos < 0) { + // There's too few data for Boyer-Moore-Horspool to run, + // so let's use a different algorithm to skip as much as + // we can. + // Forward pos until + // the trailing part of lookbehind + data + // looks like the beginning of the needle + // or until + // pos == 0 + while (pos < 0 && !this._sbmh_memcmp(data, pos, len - pos)) + pos++; + } + + if (pos >= 0) { + // Discard lookbehind buffer. + this.emit('info', false, lookbehind, 0, this._lookbehind_size); + this._lookbehind_size = 0; + } else { + // Cut off part of the lookbehind buffer that has + // been processed and append the entire haystack + // into it. + var bytesToCutOff = this._lookbehind_size + pos; + + if (bytesToCutOff > 0) { + // The cut off data is guaranteed not to contain the needle. + this.emit('info', false, lookbehind, 0, bytesToCutOff); + } + + lookbehind.copy(lookbehind, 0, bytesToCutOff, + this._lookbehind_size - bytesToCutOff); + this._lookbehind_size -= bytesToCutOff; + + data.copy(lookbehind, this._lookbehind_size); + this._lookbehind_size += len; + + this._bufpos = len; + return len; + } + } + + if (pos >= 0) + pos += this._bufpos; + + // Lookbehind buffer is now empty. Perform Boyer-Moore-Horspool + // search with optimized character lookup code that only considers + // the current round's haystack data. + while (pos <= len - needle_len) { + var ch = data[pos + needle_len - 1]; + + if (ch === last_needle_char + && data[pos] === needle[0] + && jsmemcmp(needle, 0, data, pos, needle_len - 1)) { + ++this.matches; + if (pos > 0) + this.emit('info', true, data, this._bufpos, pos); + else + this.emit('info', true); + + this._bufpos = pos + needle_len; + return pos + needle_len; + } else + pos += occ[ch]; + } + + // There was no match. If there's trailing haystack data that we cannot + // match yet using the Boyer-Moore-Horspool algorithm (because the trailing + // data is less than the needle size) then match using a modified + // algorithm that starts matching from the beginning instead of the end. + // Whatever trailing data is left after running this algorithm is added to + // the lookbehind buffer. + if (pos < len) { + while (pos < len && (data[pos] !== needle[0] + || !jsmemcmp(data, pos, needle, 0, len - pos))) { + ++pos; + } + if (pos < len) { + data.copy(lookbehind, 0, pos, pos + (len - pos)); + this._lookbehind_size = len - pos; + } + } + + // Everything until pos is guaranteed not to contain needle data. + if (pos > 0) + this.emit('info', false, data, this._bufpos, pos < len ? pos : len); + + this._bufpos = len; + return len; +}; + +SBMH.prototype._sbmh_lookup_char = function(data, pos) { + if (pos < 0) + return this._lookbehind[this._lookbehind_size + pos]; + else + return data[pos]; +} + +SBMH.prototype._sbmh_memcmp = function(data, pos, len) { + var i = 0; + + while (i < len) { + if (this._sbmh_lookup_char(data, pos + i) === this._needle[i]) + ++i; + else + return false; + } + return true; +} + +module.exports = SBMH; diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/node_modules/streamsearch/package.json b/node_modules/multer/node_modules/busboy/node_modules/dicer/node_modules/streamsearch/package.json new file mode 100644 index 0000000..ebc41d5 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/node_modules/streamsearch/package.json @@ -0,0 +1,53 @@ +{ + "name": "streamsearch", + "version": "0.1.2", + "author": { + "name": "Brian White", + "email": "mscdex@mscdex.net" + }, + "description": "Streaming Boyer-Moore-Horspool searching for node.js", + "main": "./lib/sbmh", + "engines": { + "node": ">=0.8.0" + }, + "keywords": [ + "stream", + "horspool", + "boyer-moore-horspool", + "boyer-moore", + "search" + ], + "licenses": [ + { + "type": "MIT", + "url": "http://github.com/mscdex/streamsearch/raw/master/LICENSE" + } + ], + "repository": { + "type": "git", + "url": "http://github.com/mscdex/streamsearch.git" + }, + "readme": "Description\n===========\n\nstreamsearch is a module for [node.js](http://nodejs.org/) that allows searching a stream using the Boyer-Moore-Horspool algorithm.\n\nThis module is based heavily on the Streaming Boyer-Moore-Horspool C++ implementation by Hongli Lai [here](https://github.com/FooBarWidget/boyer-moore-horspool).\n\n\nRequirements\n============\n\n* [node.js](http://nodejs.org/) -- v0.8.0 or newer\n\n\nInstallation\n============\n\n npm install streamsearch\n\nExample\n=======\n\n```javascript\n var StreamSearch = require('streamsearch'),\n inspect = require('util').inspect;\n\n var needle = new Buffer([13, 10]), // CRLF\n s = new StreamSearch(needle),\n chunks = [\n new Buffer('foo'),\n new Buffer(' bar'),\n new Buffer('\\r'),\n new Buffer('\\n'),\n new Buffer('baz, hello\\r'),\n new Buffer('\\n world.'),\n new Buffer('\\r\\n Node.JS rules!!\\r\\n\\r\\n')\n ];\n s.on('info', function(isMatch, data, start, end) {\n if (data)\n console.log('data: ' + inspect(data.toString('ascii', start, end)));\n if (isMatch)\n console.log('match!');\n });\n for (var i = 0, len = chunks.length; i < len; ++i)\n s.push(chunks[i]);\n\n // output:\n //\n // data: 'foo'\n // data: ' bar'\n // match!\n // data: 'baz, hello'\n // match!\n // data: ' world.'\n // match!\n // data: ' Node.JS rules!!'\n // match!\n // data: ''\n // match!\n```\n\n\nAPI\n===\n\nEvents\n------\n\n* **info**(< _boolean_ >isMatch[, < _Buffer_ >chunk, < _integer_ >start, < _integer_ >end]) - A match _may_ or _may not_ have been made. In either case, a preceding `chunk` of data _may_ be available that did not match the needle. Data (if available) is in `chunk` between `start` (inclusive) and `end` (exclusive).\n\n\nProperties\n----------\n\n* **maxMatches** - < _integer_ > - The maximum number of matches. Defaults to Infinity.\n\n* **matches** - < _integer_ > - The current match count.\n\n\nFunctions\n---------\n\n* **(constructor)**(< _mixed_ >needle) - Creates and returns a new instance for searching for a _Buffer_ or _string_ `needle`.\n\n* **push**(< _Buffer_ >chunk) - _integer_ - Processes `chunk`. The return value is the last processed index in `chunk` + 1.\n\n* **reset**() - _(void)_ - Resets internal state. Useful for when you wish to start searching a new/different stream for example.\n", + "readmeFilename": "README.md", + "_id": "streamsearch@0.1.2", + "dist": { + "shasum": "808b9d0e56fc273d809ba57338e929919a1a9f1a", + "tarball": "http://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz" + }, + "_from": "streamsearch@0.1.2", + "_resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "scripts": {}, + "_npmVersion": "1.2.18", + "_npmUser": { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + "maintainers": [ + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + } + ], + "directories": {}, + "_shasum": "808b9d0e56fc273d809ba57338e929919a1a9f1a" +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/package.json b/node_modules/multer/node_modules/busboy/node_modules/dicer/package.json new file mode 100644 index 0000000..84a93f9 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/package.json @@ -0,0 +1,63 @@ +{ + "name": "dicer", + "version": "0.2.5", + "author": { + "name": "Brian White", + "email": "mscdex@mscdex.net" + }, + "description": "A very fast streaming multipart parser for node.js", + "main": "./lib/Dicer", + "dependencies": { + "streamsearch": "0.1.2", + "readable-stream": "1.1.x" + }, + "scripts": { + "test": "node test/test.js" + }, + "engines": { + "node": ">=0.8.0" + }, + "keywords": [ + "parser", + "parse", + "parsing", + "multipart", + "form-data", + "streaming" + ], + "licenses": [ + { + "type": "MIT", + "url": "http://github.com/mscdex/dicer/raw/master/LICENSE" + } + ], + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/mscdex/dicer.git" + }, + "bugs": { + "url": "https://github.com/mscdex/dicer/issues" + }, + "homepage": "https://github.com/mscdex/dicer#readme", + "_id": "dicer@0.2.5", + "_shasum": "5996c086bb33218c812c090bddc09cd12facb70f", + "_resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "_from": "dicer@0.2.5", + "_npmVersion": "3.3.6", + "_nodeVersion": "5.0.0", + "_npmUser": { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + "maintainers": [ + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + } + ], + "dist": { + "shasum": "5996c086bb33218c812c090bddc09cd12facb70f", + "tarball": "http://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz" + }, + "directories": {} +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/original b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/original new file mode 100644 index 0000000..ad9f0cc --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/original @@ -0,0 +1,31 @@ +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="_method" + +put +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[blog]" + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[public_email]" + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[interests]" + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[bio]" + +hello + +"quote" +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="commit" + +Save +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="media"; filename="" +Content-Type: application/octet-stream + + diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part1 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part1 new file mode 100644 index 0000000..a232311 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part1 @@ -0,0 +1 @@ +put \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part1.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part1.header new file mode 100644 index 0000000..5e6bbe5 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part1.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"_method\""]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part2 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part2 new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part2.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part2.header new file mode 100644 index 0000000..5b53966 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part2.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"profile[blog]\""]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part3 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part3 new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part3.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part3.header new file mode 100644 index 0000000..579e16e --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part3.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"profile[public_email]\""]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part4 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part4 new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part4.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part4.header new file mode 100644 index 0000000..b41be09 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part4.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"profile[interests]\""]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part5 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part5 new file mode 100644 index 0000000..f2bb979 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part5 @@ -0,0 +1,3 @@ +hello + +"quote" \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part5.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part5.header new file mode 100644 index 0000000..92e417f --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part5.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"profile[bio]\""]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part6 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part6 new file mode 100644 index 0000000..f0f5479 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part6 @@ -0,0 +1 @@ +Save \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part6.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part6.header new file mode 100644 index 0000000..65a68a9 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part6.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"commit\""]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part7.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part7.header new file mode 100644 index 0000000..25171e8 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-noend/part7.header @@ -0,0 +1,2 @@ +{"content-disposition": ["form-data; name=\"media\"; filename=\"\""], + "content-type": ["application/octet-stream"]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-wrongboundary/original b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-wrongboundary/original new file mode 100644 index 0000000..859770c --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-wrongboundary/original @@ -0,0 +1,32 @@ +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="_method" + +put +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[blog]" + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[public_email]" + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[interests]" + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[bio]" + +hello + +"quote" +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="media"; filename="" +Content-Type: application/octet-stream + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="commit" + +Save +------WebKitFormBoundaryWLHCs9qmcJJoyjKR-- \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-wrongboundary/preamble b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-wrongboundary/preamble new file mode 100644 index 0000000..6e4bcc6 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-wrongboundary/preamble @@ -0,0 +1,33 @@ + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="_method" + +put +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[blog]" + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[public_email]" + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[interests]" + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[bio]" + +hello + +"quote" +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="media"; filename="" +Content-Type: application/octet-stream + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="commit" + +Save +------WebKitFormBoundaryWLHCs9qmcJJoyjKR-- \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-wrongboundary/preamble.error b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-wrongboundary/preamble.error new file mode 100644 index 0000000..15f4c89 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many-wrongboundary/preamble.error @@ -0,0 +1 @@ +Preamble terminated early due to unexpected end of multipart data \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/original b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/original new file mode 100644 index 0000000..859770c --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/original @@ -0,0 +1,32 @@ +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="_method" + +put +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[blog]" + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[public_email]" + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[interests]" + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="profile[bio]" + +hello + +"quote" +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="media"; filename="" +Content-Type: application/octet-stream + + +------WebKitFormBoundaryWLHCs9qmcJJoyjKR +Content-Disposition: form-data; name="commit" + +Save +------WebKitFormBoundaryWLHCs9qmcJJoyjKR-- \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part1 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part1 new file mode 100644 index 0000000..a232311 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part1 @@ -0,0 +1 @@ +put \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part1.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part1.header new file mode 100644 index 0000000..5e6bbe5 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part1.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"_method\""]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part2 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part2 new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part2.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part2.header new file mode 100644 index 0000000..5b53966 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part2.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"profile[blog]\""]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part3 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part3 new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part3.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part3.header new file mode 100644 index 0000000..579e16e --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part3.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"profile[public_email]\""]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part4 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part4 new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part4.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part4.header new file mode 100644 index 0000000..b41be09 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part4.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"profile[interests]\""]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part5 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part5 new file mode 100644 index 0000000..f2bb979 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part5 @@ -0,0 +1,3 @@ +hello + +"quote" \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part5.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part5.header new file mode 100644 index 0000000..92e417f --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part5.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"profile[bio]\""]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part6 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part6 new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part6.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part6.header new file mode 100644 index 0000000..25171e8 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part6.header @@ -0,0 +1,2 @@ +{"content-disposition": ["form-data; name=\"media\"; filename=\"\""], + "content-type": ["application/octet-stream"]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part7 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part7 new file mode 100644 index 0000000..f0f5479 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part7 @@ -0,0 +1 @@ +Save \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part7.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part7.header new file mode 100644 index 0000000..65a68a9 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/many/part7.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"commit\""]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/original b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/original new file mode 100644 index 0000000..3044550 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/original @@ -0,0 +1,24 @@ +User-Agent: foo bar baz +Content-Type: multipart/form-data; boundary=AaB03x + +--AaB03x +Content-Disposition: form-data; name="foo" + +bar +--AaB03x +Content-Disposition: form-data; name="files" +Content-Type: multipart/mixed, boundary=BbC04y + +--BbC04y +Content-Disposition: attachment; filename="file.txt" +Content-Type: text/plain + +contents +--BbC04y +Content-Disposition: attachment; filename="flowers.jpg" +Content-Type: image/jpeg +Content-Transfer-Encoding: binary + +contents +--BbC04y-- +--AaB03x-- \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/part1 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/part1 new file mode 100644 index 0000000..ba0e162 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/part1 @@ -0,0 +1 @@ +bar \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/part1.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/part1.header new file mode 100644 index 0000000..03bd093 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/part1.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"foo\""]} diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/part2 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/part2 new file mode 100644 index 0000000..2d4deb5 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/part2 @@ -0,0 +1,12 @@ +--BbC04y +Content-Disposition: attachment; filename="file.txt" +Content-Type: text/plain + +contents +--BbC04y +Content-Disposition: attachment; filename="flowers.jpg" +Content-Type: image/jpeg +Content-Transfer-Encoding: binary + +contents +--BbC04y-- \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/part2.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/part2.header new file mode 100644 index 0000000..bbe4513 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/part2.header @@ -0,0 +1,2 @@ +{"content-disposition": ["form-data; name=\"files\""], + "content-type": ["multipart/mixed, boundary=BbC04y"]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/preamble.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/preamble.header new file mode 100644 index 0000000..2815341 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested-full/preamble.header @@ -0,0 +1,2 @@ +{"user-agent": ["foo bar baz"], + "content-type": ["multipart/form-data; boundary=AaB03x"]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/original b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/original new file mode 100644 index 0000000..380f451 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/original @@ -0,0 +1,21 @@ +--AaB03x +Content-Disposition: form-data; name="foo" + +bar +--AaB03x +Content-Disposition: form-data; name="files" +Content-Type: multipart/mixed, boundary=BbC04y + +--BbC04y +Content-Disposition: attachment; filename="file.txt" +Content-Type: text/plain + +contents +--BbC04y +Content-Disposition: attachment; filename="flowers.jpg" +Content-Type: image/jpeg +Content-Transfer-Encoding: binary + +contents +--BbC04y-- +--AaB03x-- \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/part1 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/part1 new file mode 100644 index 0000000..ba0e162 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/part1 @@ -0,0 +1 @@ +bar \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/part1.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/part1.header new file mode 100644 index 0000000..03bd093 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/part1.header @@ -0,0 +1 @@ +{"content-disposition": ["form-data; name=\"foo\""]} diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/part2 b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/part2 new file mode 100644 index 0000000..2d4deb5 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/part2 @@ -0,0 +1,12 @@ +--BbC04y +Content-Disposition: attachment; filename="file.txt" +Content-Type: text/plain + +contents +--BbC04y +Content-Disposition: attachment; filename="flowers.jpg" +Content-Type: image/jpeg +Content-Transfer-Encoding: binary + +contents +--BbC04y-- \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/part2.header b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/part2.header new file mode 100644 index 0000000..bbe4513 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/fixtures/nested/part2.header @@ -0,0 +1,2 @@ +{"content-disposition": ["form-data; name=\"files\""], + "content-type": ["multipart/mixed, boundary=BbC04y"]} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-endfinish.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-endfinish.js new file mode 100644 index 0000000..0ad3925 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-endfinish.js @@ -0,0 +1,87 @@ +var Dicer = require('..'); +var assert = require('assert'); + +var CRLF = '\r\n'; +var boundary = 'boundary'; + +var writeSep = '--' + boundary; + +var writePart = [ + writeSep, + 'Content-Type: text/plain', + 'Content-Length: 0' + ].join(CRLF) + + CRLF + CRLF + + 'some data' + CRLF; + +var writeEnd = '--' + CRLF; + +var firedEnd = false; +var firedFinish = false; + +var dicer = new Dicer({boundary: boundary}); +dicer.on('part', partListener); +dicer.on('finish', finishListener); +dicer.write(writePart+writeSep); + +function partListener(partReadStream) { + partReadStream.on('data', function(){}); + partReadStream.on('end', partEndListener); +} +function partEndListener() { + firedEnd = true; + setImmediate(afterEnd); +} +function afterEnd() { + dicer.end(writeEnd); + setImmediate(afterWrite); +} +function finishListener() { + assert(firedEnd, 'Failed to end before finishing'); + firedFinish = true; + test2(); +} +function afterWrite() { + assert(firedFinish, 'Failed to finish'); +} + +var isPausePush = true; + +var firedPauseCallback = false; +var firedPauseFinish = false; + +var dicer2 = null; + +function test2() { + dicer2 = new Dicer({boundary: boundary}); + dicer2.on('part', pausePartListener); + dicer2.on('finish', pauseFinish); + dicer2.write(writePart+writeSep, 'utf8', pausePartCallback); + setImmediate(pauseAfterWrite); +} +function pausePartListener(partReadStream) { + partReadStream.on('data', function(){}); + partReadStream.on('end', function(){}); + var realPush = partReadStream.push; + partReadStream.push = function fakePush() { + realPush.apply(partReadStream, arguments); + if (!isPausePush) + return true; + isPausePush = false; + return false; + }; +} +function pauseAfterWrite() { + dicer2.end(writeEnd); + setImmediate(pauseAfterEnd); +} +function pauseAfterEnd() { + assert(firedPauseCallback, 'Failed to call callback after pause'); + assert(firedPauseFinish, 'Failed to finish after pause'); +} +function pauseFinish() { + firedPauseFinish = true; +} +function pausePartCallback() { + firedPauseCallback = true; +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-headerparser.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-headerparser.js new file mode 100644 index 0000000..bfe72ca --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-headerparser.js @@ -0,0 +1,68 @@ +var assert = require('assert'), + path = require('path'); + +var HeaderParser = require('../lib/HeaderParser'); + +var DCRLF = '\r\n\r\n', + MAXED_BUFFER = new Buffer(128 * 1024); +MAXED_BUFFER.fill(0x41); // 'A' + +var group = path.basename(__filename, '.js') + '/'; + +[ + { source: DCRLF, + expected: {}, + what: 'No header' + }, + { source: ['Content-Type:\t text/plain', + 'Content-Length:0' + ].join('\r\n') + DCRLF, + expected: {'content-type': [' text/plain'], 'content-length': ['0']}, + what: 'Value spacing' + }, + { source: ['Content-Type:\r\n text/plain', + 'Foo:\r\n bar\r\n baz', + ].join('\r\n') + DCRLF, + expected: {'content-type': [' text/plain'], 'foo': [' bar baz']}, + what: 'Folded values' + }, + { source: ['Content-Type:', + 'Foo: ', + ].join('\r\n') + DCRLF, + expected: {'content-type': [''], 'foo': ['']}, + what: 'Empty values' + }, + { source: MAXED_BUFFER.toString('ascii') + DCRLF, + expected: {}, + what: 'Max header size (single chunk)' + }, + { source: ['ABCDEFGHIJ', MAXED_BUFFER.toString('ascii'), DCRLF], + expected: {}, + what: 'Max header size (multiple chunks #1)' + }, + { source: [MAXED_BUFFER.toString('ascii'), MAXED_BUFFER.toString('ascii'), DCRLF], + expected: {}, + what: 'Max header size (multiple chunk #2)' + }, +].forEach(function(v) { + var parser = new HeaderParser(), + fired = false; + + parser.on('header', function(header) { + assert(!fired, makeMsg(v.what, 'Header event fired more than once')); + fired = true; + assert.deepEqual(header, + v.expected, + makeMsg(v.what, 'Parsed result mismatch')); + }); + if (!Array.isArray(v.source)) + v.source = [v.source]; + v.source.forEach(function(s) { + parser.push(s); + }); + assert(fired, makeMsg(v.what, 'Did not receive header from parser')); +}); + +function makeMsg(what, msg) { + return '[' + group + what + ']: ' + msg; +} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-multipart-extra-trailer.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-multipart-extra-trailer.js new file mode 100644 index 0000000..39d0440 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-multipart-extra-trailer.js @@ -0,0 +1,148 @@ +var Dicer = require('..'); +var assert = require('assert'), + fs = require('fs'), + path = require('path'), + inspect = require('util').inspect; + +var FIXTURES_ROOT = __dirname + '/fixtures/'; + +var t = 0, + group = path.basename(__filename, '.js') + '/'; + +var tests = [ + { source: 'many', + opts: { boundary: '----WebKitFormBoundaryWLHCs9qmcJJoyjKR' }, + chsize: 16, + nparts: 7, + what: 'Extra trailer data pushed after finished' + }, +]; + +function next() { + if (t === tests.length) + return; + var v = tests[t], + fixtureBase = FIXTURES_ROOT + v.source, + fd, + n = 0, + buffer = new Buffer(v.chsize), + state = { parts: [] }; + + fd = fs.openSync(fixtureBase + '/original', 'r'); + + var dicer = new Dicer(v.opts), + error, + partErrors = 0, + finishes = 0; + + dicer.on('part', function(p) { + var part = { + body: undefined, + bodylen: 0, + error: undefined, + header: undefined + }; + + p.on('header', function(h) { + part.header = h; + }).on('data', function(data) { + // make a copy because we are using readSync which re-uses a buffer ... + var copy = new Buffer(data.length); + data.copy(copy); + data = copy; + if (!part.body) + part.body = [ data ]; + else + part.body.push(data); + part.bodylen += data.length; + }).on('error', function(err) { + part.error = err; + ++partErrors; + }).on('end', function() { + if (part.body) + part.body = Buffer.concat(part.body, part.bodylen); + state.parts.push(part); + }); + }).on('error', function(err) { + error = err; + }).on('finish', function() { + assert(finishes++ === 0, makeMsg(v.what, 'finish emitted multiple times')); + + if (v.dicerError) + assert(error !== undefined, makeMsg(v.what, 'Expected error')); + else + assert(error === undefined, makeMsg(v.what, 'Unexpected error')); + + if (v.events && v.events.indexOf('part') > -1) { + assert.equal(state.parts.length, + v.nparts, + makeMsg(v.what, + 'Part count mismatch:\nActual: ' + + state.parts.length + + '\nExpected: ' + + v.nparts)); + + if (!v.npartErrors) + v.npartErrors = 0; + assert.equal(partErrors, + v.npartErrors, + makeMsg(v.what, + 'Part errors mismatch:\nActual: ' + + partErrors + + '\nExpected: ' + + v.npartErrors)); + + for (var i = 0, header, body; i < v.nparts; ++i) { + if (fs.existsSync(fixtureBase + '/part' + (i+1))) { + body = fs.readFileSync(fixtureBase + '/part' + (i+1)); + if (body.length === 0) + body = undefined; + } else + body = undefined; + assert.deepEqual(state.parts[i].body, + body, + makeMsg(v.what, + 'Part #' + (i+1) + ' body mismatch')); + if (fs.existsSync(fixtureBase + '/part' + (i+1) + '.header')) { + header = fs.readFileSync(fixtureBase + + '/part' + (i+1) + '.header', 'binary'); + header = JSON.parse(header); + } else + header = undefined; + assert.deepEqual(state.parts[i].header, + header, + makeMsg(v.what, + 'Part #' + (i+1) + + ' parsed header mismatch:\nActual: ' + + inspect(state.parts[i].header) + + '\nExpected: ' + + inspect(header))); + } + } + ++t; + next(); + }); + + while (true) { + n = fs.readSync(fd, buffer, 0, buffer.length, null); + if (n === 0) { + setTimeout(function() { + dicer.write('\r\n\r\n\r\n'); + dicer.end(); + }, 50); + break; + } + dicer.write(n === buffer.length ? buffer : buffer.slice(0, n)); + } + fs.closeSync(fd); +} +next(); + +function makeMsg(what, msg) { + return '[' + group + what + ']: ' + msg; +} + +process.on('exit', function() { + assert(t === tests.length, + makeMsg('_exit', 'Only ran ' + t + '/' + tests.length + ' tests')); +}); \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-multipart-nolisteners.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-multipart-nolisteners.js new file mode 100644 index 0000000..07bb606 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-multipart-nolisteners.js @@ -0,0 +1,228 @@ +var Dicer = require('..'); +var assert = require('assert'), + fs = require('fs'), + path = require('path'), + inspect = require('util').inspect; + +var FIXTURES_ROOT = __dirname + '/fixtures/'; + +var t = 0, + group = path.basename(__filename, '.js') + '/'; + +var tests = [ + { source: 'many', + opts: { boundary: '----WebKitFormBoundaryWLHCs9qmcJJoyjKR' }, + chsize: 16, + nparts: 0, + what: 'No preamble or part listeners' + }, +]; + +function next() { + if (t === tests.length) + return; + var v = tests[t], + fixtureBase = FIXTURES_ROOT + v.source, + fd, + n = 0, + buffer = new Buffer(v.chsize), + state = { done: false, parts: [], preamble: undefined }; + + fd = fs.openSync(fixtureBase + '/original', 'r'); + + var dicer = new Dicer(v.opts), + error, + partErrors = 0, + finishes = 0; + + if (v.events && v.events.indexOf('preamble') > -1) { + dicer.on('preamble', function(p) { + var preamble = { + body: undefined, + bodylen: 0, + error: undefined, + header: undefined + }; + + p.on('header', function(h) { + preamble.header = h; + }).on('data', function(data) { + // make a copy because we are using readSync which re-uses a buffer ... + var copy = new Buffer(data.length); + data.copy(copy); + data = copy; + if (!preamble.body) + preamble.body = [ data ]; + else + preamble.body.push(data); + preamble.bodylen += data.length; + }).on('error', function(err) { + preamble.error = err; + }).on('end', function() { + if (preamble.body) + preamble.body = Buffer.concat(preamble.body, preamble.bodylen); + if (preamble.body || preamble.header) + state.preamble = preamble; + }); + }); + } + if (v.events && v.events.indexOf('part') > -1) { + dicer.on('part', function(p) { + var part = { + body: undefined, + bodylen: 0, + error: undefined, + header: undefined + }; + + p.on('header', function(h) { + part.header = h; + }).on('data', function(data) { + // make a copy because we are using readSync which re-uses a buffer ... + var copy = new Buffer(data.length); + data.copy(copy); + data = copy; + if (!part.body) + part.body = [ data ]; + else + part.body.push(data); + part.bodylen += data.length; + }).on('error', function(err) { + part.error = err; + ++partErrors; + }).on('end', function() { + if (part.body) + part.body = Buffer.concat(part.body, part.bodylen); + state.parts.push(part); + }); + }); + } + dicer.on('error', function(err) { + error = err; + }).on('finish', function() { + assert(finishes++ === 0, makeMsg(v.what, 'finish emitted multiple times')); + + if (v.dicerError) + assert(error !== undefined, makeMsg(v.what, 'Expected error')); + else + assert(error === undefined, makeMsg(v.what, 'Unexpected error')); + + if (v.events && v.events.indexOf('preamble') > -1) { + var preamble; + if (fs.existsSync(fixtureBase + '/preamble')) { + var prebody = fs.readFileSync(fixtureBase + '/preamble'); + if (prebody.length) { + preamble = { + body: prebody, + bodylen: prebody.length, + error: undefined, + header: undefined + }; + } + } + if (fs.existsSync(fixtureBase + '/preamble.header')) { + var prehead = JSON.parse(fs.readFileSync(fixtureBase + + '/preamble.header', 'binary')); + if (!preamble) { + preamble = { + body: undefined, + bodylen: 0, + error: undefined, + header: prehead + }; + } else + preamble.header = prehead; + } + if (fs.existsSync(fixtureBase + '/preamble.error')) { + var err = new Error(fs.readFileSync(fixtureBase + + '/preamble.error', 'binary')); + if (!preamble) { + preamble = { + body: undefined, + bodylen: 0, + error: err, + header: undefined + }; + } else + preamble.error = err; + } + + assert.deepEqual(state.preamble, + preamble, + makeMsg(v.what, + 'Preamble mismatch:\nActual:' + + inspect(state.preamble) + + '\nExpected: ' + + inspect(preamble))); + } + + if (v.events && v.events.indexOf('part') > -1) { + assert.equal(state.parts.length, + v.nparts, + makeMsg(v.what, + 'Part count mismatch:\nActual: ' + + state.parts.length + + '\nExpected: ' + + v.nparts)); + + if (!v.npartErrors) + v.npartErrors = 0; + assert.equal(partErrors, + v.npartErrors, + makeMsg(v.what, + 'Part errors mismatch:\nActual: ' + + partErrors + + '\nExpected: ' + + v.npartErrors)); + + for (var i = 0, header, body; i < v.nparts; ++i) { + if (fs.existsSync(fixtureBase + '/part' + (i+1))) { + body = fs.readFileSync(fixtureBase + '/part' + (i+1)); + if (body.length === 0) + body = undefined; + } else + body = undefined; + assert.deepEqual(state.parts[i].body, + body, + makeMsg(v.what, + 'Part #' + (i+1) + ' body mismatch')); + if (fs.existsSync(fixtureBase + '/part' + (i+1) + '.header')) { + header = fs.readFileSync(fixtureBase + + '/part' + (i+1) + '.header', 'binary'); + header = JSON.parse(header); + } else + header = undefined; + assert.deepEqual(state.parts[i].header, + header, + makeMsg(v.what, + 'Part #' + (i+1) + + ' parsed header mismatch:\nActual: ' + + inspect(state.parts[i].header) + + '\nExpected: ' + + inspect(header))); + } + } + ++t; + next(); + }); + + while (true) { + n = fs.readSync(fd, buffer, 0, buffer.length, null); + if (n === 0) { + dicer.end(); + break; + } + dicer.write(n === buffer.length ? buffer : buffer.slice(0, n)); + } + fs.closeSync(fd); +} +next(); + +function makeMsg(what, msg) { + return '[' + group + what + ']: ' + msg; +} + +process.on('exit', function() { + assert(t === tests.length, + makeMsg('_exit', 'Only ran ' + t + '/' + tests.length + ' tests')); +}); \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-multipart.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-multipart.js new file mode 100644 index 0000000..66e8c77 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test-multipart.js @@ -0,0 +1,240 @@ +var Dicer = require('..'); +var assert = require('assert'), + fs = require('fs'), + path = require('path'), + inspect = require('util').inspect; + +var FIXTURES_ROOT = __dirname + '/fixtures/'; + +var t = 0, + group = path.basename(__filename, '.js') + '/'; + +var tests = [ + { source: 'nested', + opts: { boundary: 'AaB03x' }, + chsize: 32, + nparts: 2, + what: 'One nested multipart' + }, + { source: 'many', + opts: { boundary: '----WebKitFormBoundaryWLHCs9qmcJJoyjKR' }, + chsize: 16, + nparts: 7, + what: 'Many parts' + }, + { source: 'many-wrongboundary', + opts: { boundary: 'LOLOLOL' }, + chsize: 8, + nparts: 0, + dicerError: true, + what: 'Many parts, wrong boundary' + }, + { source: 'many-noend', + opts: { boundary: '----WebKitFormBoundaryWLHCs9qmcJJoyjKR' }, + chsize: 16, + nparts: 7, + npartErrors: 1, + dicerError: true, + what: 'Many parts, end boundary missing, 1 file open' + }, + { source: 'nested-full', + opts: { boundary: 'AaB03x', headerFirst: true }, + chsize: 32, + nparts: 2, + what: 'One nested multipart with preceding header' + }, + { source: 'nested-full', + opts: { headerFirst: true }, + chsize: 32, + nparts: 2, + setBoundary: 'AaB03x', + what: 'One nested multipart with preceding header, using setBoundary' + }, +]; + +function next() { + if (t === tests.length) + return; + var v = tests[t], + fixtureBase = FIXTURES_ROOT + v.source, + n = 0, + buffer = new Buffer(v.chsize), + state = { parts: [], preamble: undefined }; + + var dicer = new Dicer(v.opts), + error, + partErrors = 0, + finishes = 0; + + dicer.on('preamble', function(p) { + var preamble = { + body: undefined, + bodylen: 0, + error: undefined, + header: undefined + }; + + p.on('header', function(h) { + preamble.header = h; + if (v.setBoundary) + dicer.setBoundary(v.setBoundary); + }).on('data', function(data) { + // make a copy because we are using readSync which re-uses a buffer ... + var copy = new Buffer(data.length); + data.copy(copy); + data = copy; + if (!preamble.body) + preamble.body = [ data ]; + else + preamble.body.push(data); + preamble.bodylen += data.length; + }).on('error', function(err) { + preamble.error = err; + }).on('end', function() { + if (preamble.body) + preamble.body = Buffer.concat(preamble.body, preamble.bodylen); + if (preamble.body || preamble.header) + state.preamble = preamble; + }); + }); + dicer.on('part', function(p) { + var part = { + body: undefined, + bodylen: 0, + error: undefined, + header: undefined + }; + + p.on('header', function(h) { + part.header = h; + }).on('data', function(data) { + if (!part.body) + part.body = [ data ]; + else + part.body.push(data); + part.bodylen += data.length; + }).on('error', function(err) { + part.error = err; + ++partErrors; + }).on('end', function() { + if (part.body) + part.body = Buffer.concat(part.body, part.bodylen); + state.parts.push(part); + }); + }).on('error', function(err) { + error = err; + }).on('finish', function() { + assert(finishes++ === 0, makeMsg(v.what, 'finish emitted multiple times')); + + if (v.dicerError) + assert(error !== undefined, makeMsg(v.what, 'Expected error')); + else + assert(error === undefined, makeMsg(v.what, 'Unexpected error: ' + error)); + + var preamble; + if (fs.existsSync(fixtureBase + '/preamble')) { + var prebody = fs.readFileSync(fixtureBase + '/preamble'); + if (prebody.length) { + preamble = { + body: prebody, + bodylen: prebody.length, + error: undefined, + header: undefined + }; + } + } + if (fs.existsSync(fixtureBase + '/preamble.header')) { + var prehead = JSON.parse(fs.readFileSync(fixtureBase + + '/preamble.header', 'binary')); + if (!preamble) { + preamble = { + body: undefined, + bodylen: 0, + error: undefined, + header: prehead + }; + } else + preamble.header = prehead; + } + if (fs.existsSync(fixtureBase + '/preamble.error')) { + var err = new Error(fs.readFileSync(fixtureBase + + '/preamble.error', 'binary')); + if (!preamble) { + preamble = { + body: undefined, + bodylen: 0, + error: err, + header: undefined + }; + } else + preamble.error = err; + } + + assert.deepEqual(state.preamble, + preamble, + makeMsg(v.what, + 'Preamble mismatch:\nActual:' + + inspect(state.preamble) + + '\nExpected: ' + + inspect(preamble))); + + assert.equal(state.parts.length, + v.nparts, + makeMsg(v.what, + 'Part count mismatch:\nActual: ' + + state.parts.length + + '\nExpected: ' + + v.nparts)); + + if (!v.npartErrors) + v.npartErrors = 0; + assert.equal(partErrors, + v.npartErrors, + makeMsg(v.what, + 'Part errors mismatch:\nActual: ' + + partErrors + + '\nExpected: ' + + v.npartErrors)); + + for (var i = 0, header, body; i < v.nparts; ++i) { + if (fs.existsSync(fixtureBase + '/part' + (i+1))) { + body = fs.readFileSync(fixtureBase + '/part' + (i+1)); + if (body.length === 0) + body = undefined; + } else + body = undefined; + assert.deepEqual(state.parts[i].body, + body, + makeMsg(v.what, + 'Part #' + (i+1) + ' body mismatch')); + if (fs.existsSync(fixtureBase + '/part' + (i+1) + '.header')) { + header = fs.readFileSync(fixtureBase + + '/part' + (i+1) + '.header', 'binary'); + header = JSON.parse(header); + } else + header = undefined; + assert.deepEqual(state.parts[i].header, + header, + makeMsg(v.what, + 'Part #' + (i+1) + + ' parsed header mismatch:\nActual: ' + + inspect(state.parts[i].header) + + '\nExpected: ' + + inspect(header))); + } + ++t; + next(); + }); + + fs.createReadStream(fixtureBase + '/original').pipe(dicer); +} +next(); + +function makeMsg(what, msg) { + return '[' + group + what + ']: ' + msg; +} + +process.on('exit', function() { + assert(t === tests.length, + makeMsg('_exit', 'Only ran ' + t + '/' + tests.length + ' tests')); +}); \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test.js b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test.js new file mode 100644 index 0000000..3383f27 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/dicer/test/test.js @@ -0,0 +1,4 @@ +require('fs').readdirSync(__dirname).forEach(function(f) { + if (f.substr(0, 5) === 'test-') + require('./' + f); +}); \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/.npmignore b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/.npmignore new file mode 100644 index 0000000..38344f8 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/.npmignore @@ -0,0 +1,5 @@ +build/ +test/ +examples/ +fs.js +zlib.js \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/LICENSE b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/LICENSE new file mode 100644 index 0000000..e3d4e69 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/LICENSE @@ -0,0 +1,18 @@ +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/README.md b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/README.md new file mode 100644 index 0000000..e46b823 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/README.md @@ -0,0 +1,15 @@ +# readable-stream + +***Node-core streams for userland*** + +[![NPM](https://nodei.co/npm/readable-stream.png?downloads=true&downloadRank=true)](https://nodei.co/npm/readable-stream/) +[![NPM](https://nodei.co/npm-dl/readable-stream.png&months=6&height=3)](https://nodei.co/npm/readable-stream/) + +This package is a mirror of the Streams2 and Streams3 implementations in Node-core. + +If you want to guarantee a stable streams base, regardless of what version of Node you, or the users of your libraries are using, use **readable-stream** *only* and avoid the *"stream"* module in Node-core. + +**readable-stream** comes in two major versions, v1.0.x and v1.1.x. The former tracks the Streams2 implementation in Node 0.10, including bug-fixes and minor improvements as they are added. The latter tracks Streams3 as it develops in Node 0.11; we will likely see a v1.2.x branch for Node 0.12. + +**readable-stream** uses proper patch-level versioning so if you pin to `"~1.0.0"` you’ll get the latest Node 0.10 Streams2 implementation, including any fixes and minor non-breaking improvements. The patch-level versions of 1.0.x and 1.1.x should mirror the patch-level versions of Node-core releases. You should prefer the **1.0.x** releases for now and when you’re ready to start using Streams3, pin to `"~1.1.0"` + diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/duplex.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/duplex.js new file mode 100644 index 0000000..ca807af --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/duplex.js @@ -0,0 +1 @@ +module.exports = require("./lib/_stream_duplex.js") diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/float.patch b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/float.patch new file mode 100644 index 0000000..b984607 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/float.patch @@ -0,0 +1,923 @@ +diff --git a/lib/_stream_duplex.js b/lib/_stream_duplex.js +index c5a741c..a2e0d8e 100644 +--- a/lib/_stream_duplex.js ++++ b/lib/_stream_duplex.js +@@ -26,8 +26,8 @@ + + module.exports = Duplex; + var util = require('util'); +-var Readable = require('_stream_readable'); +-var Writable = require('_stream_writable'); ++var Readable = require('./_stream_readable'); ++var Writable = require('./_stream_writable'); + + util.inherits(Duplex, Readable); + +diff --git a/lib/_stream_passthrough.js b/lib/_stream_passthrough.js +index a5e9864..330c247 100644 +--- a/lib/_stream_passthrough.js ++++ b/lib/_stream_passthrough.js +@@ -25,7 +25,7 @@ + + module.exports = PassThrough; + +-var Transform = require('_stream_transform'); ++var Transform = require('./_stream_transform'); + var util = require('util'); + util.inherits(PassThrough, Transform); + +diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js +index 0c3fe3e..90a8298 100644 +--- a/lib/_stream_readable.js ++++ b/lib/_stream_readable.js +@@ -23,10 +23,34 @@ module.exports = Readable; + Readable.ReadableState = ReadableState; + + var EE = require('events').EventEmitter; ++if (!EE.listenerCount) EE.listenerCount = function(emitter, type) { ++ return emitter.listeners(type).length; ++}; ++ ++if (!global.setImmediate) global.setImmediate = function setImmediate(fn) { ++ return setTimeout(fn, 0); ++}; ++if (!global.clearImmediate) global.clearImmediate = function clearImmediate(i) { ++ return clearTimeout(i); ++}; ++ + var Stream = require('stream'); + var util = require('util'); ++if (!util.isUndefined) { ++ var utilIs = require('core-util-is'); ++ for (var f in utilIs) { ++ util[f] = utilIs[f]; ++ } ++} + var StringDecoder; +-var debug = util.debuglog('stream'); ++var debug; ++if (util.debuglog) ++ debug = util.debuglog('stream'); ++else try { ++ debug = require('debuglog')('stream'); ++} catch (er) { ++ debug = function() {}; ++} + + util.inherits(Readable, Stream); + +@@ -380,7 +404,7 @@ function chunkInvalid(state, chunk) { + + + function onEofChunk(stream, state) { +- if (state.decoder && !state.ended) { ++ if (state.decoder && !state.ended && state.decoder.end) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) { + state.buffer.push(chunk); +diff --git a/lib/_stream_transform.js b/lib/_stream_transform.js +index b1f9fcc..b0caf57 100644 +--- a/lib/_stream_transform.js ++++ b/lib/_stream_transform.js +@@ -64,8 +64,14 @@ + + module.exports = Transform; + +-var Duplex = require('_stream_duplex'); ++var Duplex = require('./_stream_duplex'); + var util = require('util'); ++if (!util.isUndefined) { ++ var utilIs = require('core-util-is'); ++ for (var f in utilIs) { ++ util[f] = utilIs[f]; ++ } ++} + util.inherits(Transform, Duplex); + + +diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js +index ba2e920..f49288b 100644 +--- a/lib/_stream_writable.js ++++ b/lib/_stream_writable.js +@@ -27,6 +27,12 @@ module.exports = Writable; + Writable.WritableState = WritableState; + + var util = require('util'); ++if (!util.isUndefined) { ++ var utilIs = require('core-util-is'); ++ for (var f in utilIs) { ++ util[f] = utilIs[f]; ++ } ++} + var Stream = require('stream'); + + util.inherits(Writable, Stream); +@@ -119,7 +125,7 @@ function WritableState(options, stream) { + function Writable(options) { + // Writable ctor is applied to Duplexes, though they're not + // instanceof Writable, they're instanceof Readable. +- if (!(this instanceof Writable) && !(this instanceof Stream.Duplex)) ++ if (!(this instanceof Writable) && !(this instanceof require('./_stream_duplex'))) + return new Writable(options); + + this._writableState = new WritableState(options, this); +diff --git a/test/simple/test-stream-big-push.js b/test/simple/test-stream-big-push.js +index e3787e4..8cd2127 100644 +--- a/test/simple/test-stream-big-push.js ++++ b/test/simple/test-stream-big-push.js +@@ -21,7 +21,7 @@ + + var common = require('../common'); + var assert = require('assert'); +-var stream = require('stream'); ++var stream = require('../../'); + var str = 'asdfasdfasdfasdfasdf'; + + var r = new stream.Readable({ +diff --git a/test/simple/test-stream-end-paused.js b/test/simple/test-stream-end-paused.js +index bb73777..d40efc7 100644 +--- a/test/simple/test-stream-end-paused.js ++++ b/test/simple/test-stream-end-paused.js +@@ -25,7 +25,7 @@ var gotEnd = false; + + // Make sure we don't miss the end event for paused 0-length streams + +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + var stream = new Readable(); + var calledRead = false; + stream._read = function() { +diff --git a/test/simple/test-stream-pipe-after-end.js b/test/simple/test-stream-pipe-after-end.js +index b46ee90..0be8366 100644 +--- a/test/simple/test-stream-pipe-after-end.js ++++ b/test/simple/test-stream-pipe-after-end.js +@@ -22,8 +22,8 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Readable = require('_stream_readable'); +-var Writable = require('_stream_writable'); ++var Readable = require('../../lib/_stream_readable'); ++var Writable = require('../../lib/_stream_writable'); + var util = require('util'); + + util.inherits(TestReadable, Readable); +diff --git a/test/simple/test-stream-pipe-cleanup.js b/test/simple/test-stream-pipe-cleanup.js +deleted file mode 100644 +index f689358..0000000 +--- a/test/simple/test-stream-pipe-cleanup.js ++++ /dev/null +@@ -1,122 +0,0 @@ +-// Copyright Joyent, Inc. and other Node contributors. +-// +-// Permission is hereby granted, free of charge, to any person obtaining a +-// copy of this software and associated documentation files (the +-// "Software"), to deal in the Software without restriction, including +-// without limitation the rights to use, copy, modify, merge, publish, +-// distribute, sublicense, and/or sell copies of the Software, and to permit +-// persons to whom the Software is furnished to do so, subject to the +-// following conditions: +-// +-// The above copyright notice and this permission notice shall be included +-// in all copies or substantial portions of the Software. +-// +-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +-// USE OR OTHER DEALINGS IN THE SOFTWARE. +- +-// This test asserts that Stream.prototype.pipe does not leave listeners +-// hanging on the source or dest. +- +-var common = require('../common'); +-var stream = require('stream'); +-var assert = require('assert'); +-var util = require('util'); +- +-function Writable() { +- this.writable = true; +- this.endCalls = 0; +- stream.Stream.call(this); +-} +-util.inherits(Writable, stream.Stream); +-Writable.prototype.end = function() { +- this.endCalls++; +-}; +- +-Writable.prototype.destroy = function() { +- this.endCalls++; +-}; +- +-function Readable() { +- this.readable = true; +- stream.Stream.call(this); +-} +-util.inherits(Readable, stream.Stream); +- +-function Duplex() { +- this.readable = true; +- Writable.call(this); +-} +-util.inherits(Duplex, Writable); +- +-var i = 0; +-var limit = 100; +- +-var w = new Writable(); +- +-var r; +- +-for (i = 0; i < limit; i++) { +- r = new Readable(); +- r.pipe(w); +- r.emit('end'); +-} +-assert.equal(0, r.listeners('end').length); +-assert.equal(limit, w.endCalls); +- +-w.endCalls = 0; +- +-for (i = 0; i < limit; i++) { +- r = new Readable(); +- r.pipe(w); +- r.emit('close'); +-} +-assert.equal(0, r.listeners('close').length); +-assert.equal(limit, w.endCalls); +- +-w.endCalls = 0; +- +-r = new Readable(); +- +-for (i = 0; i < limit; i++) { +- w = new Writable(); +- r.pipe(w); +- w.emit('close'); +-} +-assert.equal(0, w.listeners('close').length); +- +-r = new Readable(); +-w = new Writable(); +-var d = new Duplex(); +-r.pipe(d); // pipeline A +-d.pipe(w); // pipeline B +-assert.equal(r.listeners('end').length, 2); // A.onend, A.cleanup +-assert.equal(r.listeners('close').length, 2); // A.onclose, A.cleanup +-assert.equal(d.listeners('end').length, 2); // B.onend, B.cleanup +-assert.equal(d.listeners('close').length, 3); // A.cleanup, B.onclose, B.cleanup +-assert.equal(w.listeners('end').length, 0); +-assert.equal(w.listeners('close').length, 1); // B.cleanup +- +-r.emit('end'); +-assert.equal(d.endCalls, 1); +-assert.equal(w.endCalls, 0); +-assert.equal(r.listeners('end').length, 0); +-assert.equal(r.listeners('close').length, 0); +-assert.equal(d.listeners('end').length, 2); // B.onend, B.cleanup +-assert.equal(d.listeners('close').length, 2); // B.onclose, B.cleanup +-assert.equal(w.listeners('end').length, 0); +-assert.equal(w.listeners('close').length, 1); // B.cleanup +- +-d.emit('end'); +-assert.equal(d.endCalls, 1); +-assert.equal(w.endCalls, 1); +-assert.equal(r.listeners('end').length, 0); +-assert.equal(r.listeners('close').length, 0); +-assert.equal(d.listeners('end').length, 0); +-assert.equal(d.listeners('close').length, 0); +-assert.equal(w.listeners('end').length, 0); +-assert.equal(w.listeners('close').length, 0); +diff --git a/test/simple/test-stream-pipe-error-handling.js b/test/simple/test-stream-pipe-error-handling.js +index c5d724b..c7d6b7d 100644 +--- a/test/simple/test-stream-pipe-error-handling.js ++++ b/test/simple/test-stream-pipe-error-handling.js +@@ -21,7 +21,7 @@ + + var common = require('../common'); + var assert = require('assert'); +-var Stream = require('stream').Stream; ++var Stream = require('../../').Stream; + + (function testErrorListenerCatches() { + var source = new Stream(); +diff --git a/test/simple/test-stream-pipe-event.js b/test/simple/test-stream-pipe-event.js +index cb9d5fe..56f8d61 100644 +--- a/test/simple/test-stream-pipe-event.js ++++ b/test/simple/test-stream-pipe-event.js +@@ -20,7 +20,7 @@ + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var common = require('../common'); +-var stream = require('stream'); ++var stream = require('../../'); + var assert = require('assert'); + var util = require('util'); + +diff --git a/test/simple/test-stream-push-order.js b/test/simple/test-stream-push-order.js +index f2e6ec2..a5c9bf9 100644 +--- a/test/simple/test-stream-push-order.js ++++ b/test/simple/test-stream-push-order.js +@@ -20,7 +20,7 @@ + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var common = require('../common.js'); +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + var assert = require('assert'); + + var s = new Readable({ +diff --git a/test/simple/test-stream-push-strings.js b/test/simple/test-stream-push-strings.js +index 06f43dc..1701a9a 100644 +--- a/test/simple/test-stream-push-strings.js ++++ b/test/simple/test-stream-push-strings.js +@@ -22,7 +22,7 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + var util = require('util'); + + util.inherits(MyStream, Readable); +diff --git a/test/simple/test-stream-readable-event.js b/test/simple/test-stream-readable-event.js +index ba6a577..a8e6f7b 100644 +--- a/test/simple/test-stream-readable-event.js ++++ b/test/simple/test-stream-readable-event.js +@@ -22,7 +22,7 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + + (function first() { + // First test, not reading when the readable is added. +diff --git a/test/simple/test-stream-readable-flow-recursion.js b/test/simple/test-stream-readable-flow-recursion.js +index 2891ad6..11689ba 100644 +--- a/test/simple/test-stream-readable-flow-recursion.js ++++ b/test/simple/test-stream-readable-flow-recursion.js +@@ -27,7 +27,7 @@ var assert = require('assert'); + // more data continuously, but without triggering a nextTick + // warning or RangeError. + +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + + // throw an error if we trigger a nextTick warning. + process.throwDeprecation = true; +diff --git a/test/simple/test-stream-unshift-empty-chunk.js b/test/simple/test-stream-unshift-empty-chunk.js +index 0c96476..7827538 100644 +--- a/test/simple/test-stream-unshift-empty-chunk.js ++++ b/test/simple/test-stream-unshift-empty-chunk.js +@@ -24,7 +24,7 @@ var assert = require('assert'); + + // This test verifies that stream.unshift(Buffer(0)) or + // stream.unshift('') does not set state.reading=false. +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + + var r = new Readable(); + var nChunks = 10; +diff --git a/test/simple/test-stream-unshift-read-race.js b/test/simple/test-stream-unshift-read-race.js +index 83fd9fa..17c18aa 100644 +--- a/test/simple/test-stream-unshift-read-race.js ++++ b/test/simple/test-stream-unshift-read-race.js +@@ -29,7 +29,7 @@ var assert = require('assert'); + // 3. push() after the EOF signaling null is an error. + // 4. _read() is not called after pushing the EOF null chunk. + +-var stream = require('stream'); ++var stream = require('../../'); + var hwm = 10; + var r = stream.Readable({ highWaterMark: hwm }); + var chunks = 10; +@@ -51,7 +51,14 @@ r._read = function(n) { + + function push(fast) { + assert(!pushedNull, 'push() after null push'); +- var c = pos >= data.length ? null : data.slice(pos, pos + n); ++ var c; ++ if (pos >= data.length) ++ c = null; ++ else { ++ if (n + pos > data.length) ++ n = data.length - pos; ++ c = data.slice(pos, pos + n); ++ } + pushedNull = c === null; + if (fast) { + pos += n; +diff --git a/test/simple/test-stream-writev.js b/test/simple/test-stream-writev.js +index 5b49e6e..b5321f3 100644 +--- a/test/simple/test-stream-writev.js ++++ b/test/simple/test-stream-writev.js +@@ -22,7 +22,7 @@ + var common = require('../common'); + var assert = require('assert'); + +-var stream = require('stream'); ++var stream = require('../../'); + + var queue = []; + for (var decode = 0; decode < 2; decode++) { +diff --git a/test/simple/test-stream2-basic.js b/test/simple/test-stream2-basic.js +index 3814bf0..248c1be 100644 +--- a/test/simple/test-stream2-basic.js ++++ b/test/simple/test-stream2-basic.js +@@ -21,7 +21,7 @@ + + + var common = require('../common.js'); +-var R = require('_stream_readable'); ++var R = require('../../lib/_stream_readable'); + var assert = require('assert'); + + var util = require('util'); +diff --git a/test/simple/test-stream2-compatibility.js b/test/simple/test-stream2-compatibility.js +index 6cdd4e9..f0fa84b 100644 +--- a/test/simple/test-stream2-compatibility.js ++++ b/test/simple/test-stream2-compatibility.js +@@ -21,7 +21,7 @@ + + + var common = require('../common.js'); +-var R = require('_stream_readable'); ++var R = require('../../lib/_stream_readable'); + var assert = require('assert'); + + var util = require('util'); +diff --git a/test/simple/test-stream2-finish-pipe.js b/test/simple/test-stream2-finish-pipe.js +index 39b274f..006a19b 100644 +--- a/test/simple/test-stream2-finish-pipe.js ++++ b/test/simple/test-stream2-finish-pipe.js +@@ -20,7 +20,7 @@ + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var common = require('../common.js'); +-var stream = require('stream'); ++var stream = require('../../'); + var Buffer = require('buffer').Buffer; + + var r = new stream.Readable(); +diff --git a/test/simple/test-stream2-fs.js b/test/simple/test-stream2-fs.js +deleted file mode 100644 +index e162406..0000000 +--- a/test/simple/test-stream2-fs.js ++++ /dev/null +@@ -1,72 +0,0 @@ +-// Copyright Joyent, Inc. and other Node contributors. +-// +-// Permission is hereby granted, free of charge, to any person obtaining a +-// copy of this software and associated documentation files (the +-// "Software"), to deal in the Software without restriction, including +-// without limitation the rights to use, copy, modify, merge, publish, +-// distribute, sublicense, and/or sell copies of the Software, and to permit +-// persons to whom the Software is furnished to do so, subject to the +-// following conditions: +-// +-// The above copyright notice and this permission notice shall be included +-// in all copies or substantial portions of the Software. +-// +-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +-// USE OR OTHER DEALINGS IN THE SOFTWARE. +- +- +-var common = require('../common.js'); +-var R = require('_stream_readable'); +-var assert = require('assert'); +- +-var fs = require('fs'); +-var FSReadable = fs.ReadStream; +- +-var path = require('path'); +-var file = path.resolve(common.fixturesDir, 'x1024.txt'); +- +-var size = fs.statSync(file).size; +- +-var expectLengths = [1024]; +- +-var util = require('util'); +-var Stream = require('stream'); +- +-util.inherits(TestWriter, Stream); +- +-function TestWriter() { +- Stream.apply(this); +- this.buffer = []; +- this.length = 0; +-} +- +-TestWriter.prototype.write = function(c) { +- this.buffer.push(c.toString()); +- this.length += c.length; +- return true; +-}; +- +-TestWriter.prototype.end = function(c) { +- if (c) this.buffer.push(c.toString()); +- this.emit('results', this.buffer); +-} +- +-var r = new FSReadable(file); +-var w = new TestWriter(); +- +-w.on('results', function(res) { +- console.error(res, w.length); +- assert.equal(w.length, size); +- var l = 0; +- assert.deepEqual(res.map(function (c) { +- return c.length; +- }), expectLengths); +- console.log('ok'); +-}); +- +-r.pipe(w); +diff --git a/test/simple/test-stream2-httpclient-response-end.js b/test/simple/test-stream2-httpclient-response-end.js +deleted file mode 100644 +index 15cffc2..0000000 +--- a/test/simple/test-stream2-httpclient-response-end.js ++++ /dev/null +@@ -1,52 +0,0 @@ +-// Copyright Joyent, Inc. and other Node contributors. +-// +-// Permission is hereby granted, free of charge, to any person obtaining a +-// copy of this software and associated documentation files (the +-// "Software"), to deal in the Software without restriction, including +-// without limitation the rights to use, copy, modify, merge, publish, +-// distribute, sublicense, and/or sell copies of the Software, and to permit +-// persons to whom the Software is furnished to do so, subject to the +-// following conditions: +-// +-// The above copyright notice and this permission notice shall be included +-// in all copies or substantial portions of the Software. +-// +-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +-// USE OR OTHER DEALINGS IN THE SOFTWARE. +- +-var common = require('../common.js'); +-var assert = require('assert'); +-var http = require('http'); +-var msg = 'Hello'; +-var readable_event = false; +-var end_event = false; +-var server = http.createServer(function(req, res) { +- res.writeHead(200, {'Content-Type': 'text/plain'}); +- res.end(msg); +-}).listen(common.PORT, function() { +- http.get({port: common.PORT}, function(res) { +- var data = ''; +- res.on('readable', function() { +- console.log('readable event'); +- readable_event = true; +- data += res.read(); +- }); +- res.on('end', function() { +- console.log('end event'); +- end_event = true; +- assert.strictEqual(msg, data); +- server.close(); +- }); +- }); +-}); +- +-process.on('exit', function() { +- assert(readable_event); +- assert(end_event); +-}); +- +diff --git a/test/simple/test-stream2-large-read-stall.js b/test/simple/test-stream2-large-read-stall.js +index 2fbfbca..667985b 100644 +--- a/test/simple/test-stream2-large-read-stall.js ++++ b/test/simple/test-stream2-large-read-stall.js +@@ -30,7 +30,7 @@ var PUSHSIZE = 20; + var PUSHCOUNT = 1000; + var HWM = 50; + +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + var r = new Readable({ + highWaterMark: HWM + }); +@@ -39,23 +39,23 @@ var rs = r._readableState; + r._read = push; + + r.on('readable', function() { +- console.error('>> readable'); ++ //console.error('>> readable'); + do { +- console.error(' > read(%d)', READSIZE); ++ //console.error(' > read(%d)', READSIZE); + var ret = r.read(READSIZE); +- console.error(' < %j (%d remain)', ret && ret.length, rs.length); ++ //console.error(' < %j (%d remain)', ret && ret.length, rs.length); + } while (ret && ret.length === READSIZE); + +- console.error('<< after read()', +- ret && ret.length, +- rs.needReadable, +- rs.length); ++ //console.error('<< after read()', ++ // ret && ret.length, ++ // rs.needReadable, ++ // rs.length); + }); + + var endEmitted = false; + r.on('end', function() { + endEmitted = true; +- console.error('end'); ++ //console.error('end'); + }); + + var pushes = 0; +@@ -64,11 +64,11 @@ function push() { + return; + + if (pushes++ === PUSHCOUNT) { +- console.error(' push(EOF)'); ++ //console.error(' push(EOF)'); + return r.push(null); + } + +- console.error(' push #%d', pushes); ++ //console.error(' push #%d', pushes); + if (r.push(new Buffer(PUSHSIZE))) + setTimeout(push); + } +diff --git a/test/simple/test-stream2-objects.js b/test/simple/test-stream2-objects.js +index 3e6931d..ff47d89 100644 +--- a/test/simple/test-stream2-objects.js ++++ b/test/simple/test-stream2-objects.js +@@ -21,8 +21,8 @@ + + + var common = require('../common.js'); +-var Readable = require('_stream_readable'); +-var Writable = require('_stream_writable'); ++var Readable = require('../../lib/_stream_readable'); ++var Writable = require('../../lib/_stream_writable'); + var assert = require('assert'); + + // tiny node-tap lookalike. +diff --git a/test/simple/test-stream2-pipe-error-handling.js b/test/simple/test-stream2-pipe-error-handling.js +index cf7531c..e3f3e4e 100644 +--- a/test/simple/test-stream2-pipe-error-handling.js ++++ b/test/simple/test-stream2-pipe-error-handling.js +@@ -21,7 +21,7 @@ + + var common = require('../common'); + var assert = require('assert'); +-var stream = require('stream'); ++var stream = require('../../'); + + (function testErrorListenerCatches() { + var count = 1000; +diff --git a/test/simple/test-stream2-pipe-error-once-listener.js b/test/simple/test-stream2-pipe-error-once-listener.js +index 5e8e3cb..53b2616 100755 +--- a/test/simple/test-stream2-pipe-error-once-listener.js ++++ b/test/simple/test-stream2-pipe-error-once-listener.js +@@ -24,7 +24,7 @@ var common = require('../common.js'); + var assert = require('assert'); + + var util = require('util'); +-var stream = require('stream'); ++var stream = require('../../'); + + + var Read = function() { +diff --git a/test/simple/test-stream2-push.js b/test/simple/test-stream2-push.js +index b63edc3..eb2b0e9 100644 +--- a/test/simple/test-stream2-push.js ++++ b/test/simple/test-stream2-push.js +@@ -20,7 +20,7 @@ + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var common = require('../common.js'); +-var stream = require('stream'); ++var stream = require('../../'); + var Readable = stream.Readable; + var Writable = stream.Writable; + var assert = require('assert'); +diff --git a/test/simple/test-stream2-read-sync-stack.js b/test/simple/test-stream2-read-sync-stack.js +index e8a7305..9740a47 100644 +--- a/test/simple/test-stream2-read-sync-stack.js ++++ b/test/simple/test-stream2-read-sync-stack.js +@@ -21,7 +21,7 @@ + + var common = require('../common'); + var assert = require('assert'); +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + var r = new Readable(); + var N = 256 * 1024; + +diff --git a/test/simple/test-stream2-readable-empty-buffer-no-eof.js b/test/simple/test-stream2-readable-empty-buffer-no-eof.js +index cd30178..4b1659d 100644 +--- a/test/simple/test-stream2-readable-empty-buffer-no-eof.js ++++ b/test/simple/test-stream2-readable-empty-buffer-no-eof.js +@@ -22,10 +22,9 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + + test1(); +-test2(); + + function test1() { + var r = new Readable(); +@@ -88,31 +87,3 @@ function test1() { + console.log('ok'); + }); + } +- +-function test2() { +- var r = new Readable({ encoding: 'base64' }); +- var reads = 5; +- r._read = function(n) { +- if (!reads--) +- return r.push(null); // EOF +- else +- return r.push(new Buffer('x')); +- }; +- +- var results = []; +- function flow() { +- var chunk; +- while (null !== (chunk = r.read())) +- results.push(chunk + ''); +- } +- r.on('readable', flow); +- r.on('end', function() { +- results.push('EOF'); +- }); +- flow(); +- +- process.on('exit', function() { +- assert.deepEqual(results, [ 'eHh4', 'eHg=', 'EOF' ]); +- console.log('ok'); +- }); +-} +diff --git a/test/simple/test-stream2-readable-from-list.js b/test/simple/test-stream2-readable-from-list.js +index 7c96ffe..04a96f5 100644 +--- a/test/simple/test-stream2-readable-from-list.js ++++ b/test/simple/test-stream2-readable-from-list.js +@@ -21,7 +21,7 @@ + + var assert = require('assert'); + var common = require('../common.js'); +-var fromList = require('_stream_readable')._fromList; ++var fromList = require('../../lib/_stream_readable')._fromList; + + // tiny node-tap lookalike. + var tests = []; +diff --git a/test/simple/test-stream2-readable-legacy-drain.js b/test/simple/test-stream2-readable-legacy-drain.js +index 675da8e..51fd3d5 100644 +--- a/test/simple/test-stream2-readable-legacy-drain.js ++++ b/test/simple/test-stream2-readable-legacy-drain.js +@@ -22,7 +22,7 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Stream = require('stream'); ++var Stream = require('../../'); + var Readable = Stream.Readable; + + var r = new Readable(); +diff --git a/test/simple/test-stream2-readable-non-empty-end.js b/test/simple/test-stream2-readable-non-empty-end.js +index 7314ae7..c971898 100644 +--- a/test/simple/test-stream2-readable-non-empty-end.js ++++ b/test/simple/test-stream2-readable-non-empty-end.js +@@ -21,7 +21,7 @@ + + var assert = require('assert'); + var common = require('../common.js'); +-var Readable = require('_stream_readable'); ++var Readable = require('../../lib/_stream_readable'); + + var len = 0; + var chunks = new Array(10); +diff --git a/test/simple/test-stream2-readable-wrap-empty.js b/test/simple/test-stream2-readable-wrap-empty.js +index 2e5cf25..fd8a3dc 100644 +--- a/test/simple/test-stream2-readable-wrap-empty.js ++++ b/test/simple/test-stream2-readable-wrap-empty.js +@@ -22,7 +22,7 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Readable = require('_stream_readable'); ++var Readable = require('../../lib/_stream_readable'); + var EE = require('events').EventEmitter; + + var oldStream = new EE(); +diff --git a/test/simple/test-stream2-readable-wrap.js b/test/simple/test-stream2-readable-wrap.js +index 90eea01..6b177f7 100644 +--- a/test/simple/test-stream2-readable-wrap.js ++++ b/test/simple/test-stream2-readable-wrap.js +@@ -22,8 +22,8 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Readable = require('_stream_readable'); +-var Writable = require('_stream_writable'); ++var Readable = require('../../lib/_stream_readable'); ++var Writable = require('../../lib/_stream_writable'); + var EE = require('events').EventEmitter; + + var testRuns = 0, completedRuns = 0; +diff --git a/test/simple/test-stream2-set-encoding.js b/test/simple/test-stream2-set-encoding.js +index 5d2c32a..685531b 100644 +--- a/test/simple/test-stream2-set-encoding.js ++++ b/test/simple/test-stream2-set-encoding.js +@@ -22,7 +22,7 @@ + + var common = require('../common.js'); + var assert = require('assert'); +-var R = require('_stream_readable'); ++var R = require('../../lib/_stream_readable'); + var util = require('util'); + + // tiny node-tap lookalike. +diff --git a/test/simple/test-stream2-transform.js b/test/simple/test-stream2-transform.js +index 9c9ddd8..a0cacc6 100644 +--- a/test/simple/test-stream2-transform.js ++++ b/test/simple/test-stream2-transform.js +@@ -21,8 +21,8 @@ + + var assert = require('assert'); + var common = require('../common.js'); +-var PassThrough = require('_stream_passthrough'); +-var Transform = require('_stream_transform'); ++var PassThrough = require('../../').PassThrough; ++var Transform = require('../../').Transform; + + // tiny node-tap lookalike. + var tests = []; +diff --git a/test/simple/test-stream2-unpipe-drain.js b/test/simple/test-stream2-unpipe-drain.js +index d66dc3c..365b327 100644 +--- a/test/simple/test-stream2-unpipe-drain.js ++++ b/test/simple/test-stream2-unpipe-drain.js +@@ -22,7 +22,7 @@ + + var common = require('../common.js'); + var assert = require('assert'); +-var stream = require('stream'); ++var stream = require('../../'); + var crypto = require('crypto'); + + var util = require('util'); +diff --git a/test/simple/test-stream2-unpipe-leak.js b/test/simple/test-stream2-unpipe-leak.js +index 99f8746..17c92ae 100644 +--- a/test/simple/test-stream2-unpipe-leak.js ++++ b/test/simple/test-stream2-unpipe-leak.js +@@ -22,7 +22,7 @@ + + var common = require('../common.js'); + var assert = require('assert'); +-var stream = require('stream'); ++var stream = require('../../'); + + var chunk = new Buffer('hallo'); + +diff --git a/test/simple/test-stream2-writable.js b/test/simple/test-stream2-writable.js +index 704100c..209c3a6 100644 +--- a/test/simple/test-stream2-writable.js ++++ b/test/simple/test-stream2-writable.js +@@ -20,8 +20,8 @@ + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var common = require('../common.js'); +-var W = require('_stream_writable'); +-var D = require('_stream_duplex'); ++var W = require('../../').Writable; ++var D = require('../../').Duplex; + var assert = require('assert'); + + var util = require('util'); +diff --git a/test/simple/test-stream3-pause-then-read.js b/test/simple/test-stream3-pause-then-read.js +index b91bde3..2f72c15 100644 +--- a/test/simple/test-stream3-pause-then-read.js ++++ b/test/simple/test-stream3-pause-then-read.js +@@ -22,7 +22,7 @@ + var common = require('../common'); + var assert = require('assert'); + +-var stream = require('stream'); ++var stream = require('../../'); + var Readable = stream.Readable; + var Writable = stream.Writable; + diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_duplex.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_duplex.js new file mode 100644 index 0000000..b513d61 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_duplex.js @@ -0,0 +1,89 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a duplex stream is just a stream that is both readable and writable. +// Since JS doesn't have multiple prototypal inheritance, this class +// prototypally inherits from Readable, and then parasitically from +// Writable. + +module.exports = Duplex; + +/**/ +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) keys.push(key); + return keys; +} +/**/ + + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +var Readable = require('./_stream_readable'); +var Writable = require('./_stream_writable'); + +util.inherits(Duplex, Readable); + +forEach(objectKeys(Writable.prototype), function(method) { + if (!Duplex.prototype[method]) + Duplex.prototype[method] = Writable.prototype[method]; +}); + +function Duplex(options) { + if (!(this instanceof Duplex)) + return new Duplex(options); + + Readable.call(this, options); + Writable.call(this, options); + + if (options && options.readable === false) + this.readable = false; + + if (options && options.writable === false) + this.writable = false; + + this.allowHalfOpen = true; + if (options && options.allowHalfOpen === false) + this.allowHalfOpen = false; + + this.once('end', onend); +} + +// the no-half-open enforcer +function onend() { + // if we allow half-open state, or if the writable side ended, + // then we're ok. + if (this.allowHalfOpen || this._writableState.ended) + return; + + // no more data can be written. + // But allow more writes to happen in this tick. + process.nextTick(this.end.bind(this)); +} + +function forEach (xs, f) { + for (var i = 0, l = xs.length; i < l; i++) { + f(xs[i], i); + } +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_passthrough.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_passthrough.js new file mode 100644 index 0000000..895ca50 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_passthrough.js @@ -0,0 +1,46 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a passthrough stream. +// basically just the most minimal sort of Transform stream. +// Every written chunk gets output as-is. + +module.exports = PassThrough; + +var Transform = require('./_stream_transform'); + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +util.inherits(PassThrough, Transform); + +function PassThrough(options) { + if (!(this instanceof PassThrough)) + return new PassThrough(options); + + Transform.call(this, options); +} + +PassThrough.prototype._transform = function(chunk, encoding, cb) { + cb(null, chunk); +}; diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_readable.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_readable.js new file mode 100644 index 0000000..19ab358 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_readable.js @@ -0,0 +1,951 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +module.exports = Readable; + +/**/ +var isArray = require('isarray'); +/**/ + + +/**/ +var Buffer = require('buffer').Buffer; +/**/ + +Readable.ReadableState = ReadableState; + +var EE = require('events').EventEmitter; + +/**/ +if (!EE.listenerCount) EE.listenerCount = function(emitter, type) { + return emitter.listeners(type).length; +}; +/**/ + +var Stream = require('stream'); + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +var StringDecoder; + + +/**/ +var debug = require('util'); +if (debug && debug.debuglog) { + debug = debug.debuglog('stream'); +} else { + debug = function () {}; +} +/**/ + + +util.inherits(Readable, Stream); + +function ReadableState(options, stream) { + var Duplex = require('./_stream_duplex'); + + options = options || {}; + + // the point at which it stops calling _read() to fill the buffer + // Note: 0 is a valid value, means "don't call _read preemptively ever" + var hwm = options.highWaterMark; + var defaultHwm = options.objectMode ? 16 : 16 * 1024; + this.highWaterMark = (hwm || hwm === 0) ? hwm : defaultHwm; + + // cast to ints. + this.highWaterMark = ~~this.highWaterMark; + + this.buffer = []; + this.length = 0; + this.pipes = null; + this.pipesCount = 0; + this.flowing = null; + this.ended = false; + this.endEmitted = false; + this.reading = false; + + // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + this.sync = true; + + // whenever we return null, then we set a flag to say + // that we're awaiting a 'readable' event emission. + this.needReadable = false; + this.emittedReadable = false; + this.readableListening = false; + + + // object stream flag. Used to make read(n) ignore n and to + // make all the buffer merging and length checks go away + this.objectMode = !!options.objectMode; + + if (stream instanceof Duplex) + this.objectMode = this.objectMode || !!options.readableObjectMode; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; + + // when piping, we only care about 'readable' events that happen + // after read()ing all the bytes and not getting any pushback. + this.ranOut = false; + + // the number of writers that are awaiting a drain event in .pipe()s + this.awaitDrain = 0; + + // if true, a maybeReadMore has been scheduled + this.readingMore = false; + + this.decoder = null; + this.encoding = null; + if (options.encoding) { + if (!StringDecoder) + StringDecoder = require('string_decoder/').StringDecoder; + this.decoder = new StringDecoder(options.encoding); + this.encoding = options.encoding; + } +} + +function Readable(options) { + var Duplex = require('./_stream_duplex'); + + if (!(this instanceof Readable)) + return new Readable(options); + + this._readableState = new ReadableState(options, this); + + // legacy + this.readable = true; + + Stream.call(this); +} + +// Manually shove something into the read() buffer. +// This returns true if the highWaterMark has not been hit yet, +// similar to how Writable.write() returns true if you should +// write() some more. +Readable.prototype.push = function(chunk, encoding) { + var state = this._readableState; + + if (util.isString(chunk) && !state.objectMode) { + encoding = encoding || state.defaultEncoding; + if (encoding !== state.encoding) { + chunk = new Buffer(chunk, encoding); + encoding = ''; + } + } + + return readableAddChunk(this, state, chunk, encoding, false); +}; + +// Unshift should *always* be something directly out of read() +Readable.prototype.unshift = function(chunk) { + var state = this._readableState; + return readableAddChunk(this, state, chunk, '', true); +}; + +function readableAddChunk(stream, state, chunk, encoding, addToFront) { + var er = chunkInvalid(state, chunk); + if (er) { + stream.emit('error', er); + } else if (util.isNullOrUndefined(chunk)) { + state.reading = false; + if (!state.ended) + onEofChunk(stream, state); + } else if (state.objectMode || chunk && chunk.length > 0) { + if (state.ended && !addToFront) { + var e = new Error('stream.push() after EOF'); + stream.emit('error', e); + } else if (state.endEmitted && addToFront) { + var e = new Error('stream.unshift() after end event'); + stream.emit('error', e); + } else { + if (state.decoder && !addToFront && !encoding) + chunk = state.decoder.write(chunk); + + if (!addToFront) + state.reading = false; + + // if we want the data now, just emit it. + if (state.flowing && state.length === 0 && !state.sync) { + stream.emit('data', chunk); + stream.read(0); + } else { + // update the buffer info. + state.length += state.objectMode ? 1 : chunk.length; + if (addToFront) + state.buffer.unshift(chunk); + else + state.buffer.push(chunk); + + if (state.needReadable) + emitReadable(stream); + } + + maybeReadMore(stream, state); + } + } else if (!addToFront) { + state.reading = false; + } + + return needMoreData(state); +} + + + +// if it's past the high water mark, we can push in some more. +// Also, if we have no data yet, we can stand some +// more bytes. This is to work around cases where hwm=0, +// such as the repl. Also, if the push() triggered a +// readable event, and the user called read(largeNumber) such that +// needReadable was set, then we ought to push more, so that another +// 'readable' event will be triggered. +function needMoreData(state) { + return !state.ended && + (state.needReadable || + state.length < state.highWaterMark || + state.length === 0); +} + +// backwards compatibility. +Readable.prototype.setEncoding = function(enc) { + if (!StringDecoder) + StringDecoder = require('string_decoder/').StringDecoder; + this._readableState.decoder = new StringDecoder(enc); + this._readableState.encoding = enc; + return this; +}; + +// Don't raise the hwm > 128MB +var MAX_HWM = 0x800000; +function roundUpToNextPowerOf2(n) { + if (n >= MAX_HWM) { + n = MAX_HWM; + } else { + // Get the next highest power of 2 + n--; + for (var p = 1; p < 32; p <<= 1) n |= n >> p; + n++; + } + return n; +} + +function howMuchToRead(n, state) { + if (state.length === 0 && state.ended) + return 0; + + if (state.objectMode) + return n === 0 ? 0 : 1; + + if (isNaN(n) || util.isNull(n)) { + // only flow one buffer at a time + if (state.flowing && state.buffer.length) + return state.buffer[0].length; + else + return state.length; + } + + if (n <= 0) + return 0; + + // If we're asking for more than the target buffer level, + // then raise the water mark. Bump up to the next highest + // power of 2, to prevent increasing it excessively in tiny + // amounts. + if (n > state.highWaterMark) + state.highWaterMark = roundUpToNextPowerOf2(n); + + // don't have that much. return null, unless we've ended. + if (n > state.length) { + if (!state.ended) { + state.needReadable = true; + return 0; + } else + return state.length; + } + + return n; +} + +// you can override either this method, or the async _read(n) below. +Readable.prototype.read = function(n) { + debug('read', n); + var state = this._readableState; + var nOrig = n; + + if (!util.isNumber(n) || n > 0) + state.emittedReadable = false; + + // if we're doing read(0) to trigger a readable event, but we + // already have a bunch of data in the buffer, then just trigger + // the 'readable' event and move on. + if (n === 0 && + state.needReadable && + (state.length >= state.highWaterMark || state.ended)) { + debug('read: emitReadable', state.length, state.ended); + if (state.length === 0 && state.ended) + endReadable(this); + else + emitReadable(this); + return null; + } + + n = howMuchToRead(n, state); + + // if we've ended, and we're now clear, then finish it up. + if (n === 0 && state.ended) { + if (state.length === 0) + endReadable(this); + return null; + } + + // All the actual chunk generation logic needs to be + // *below* the call to _read. The reason is that in certain + // synthetic stream cases, such as passthrough streams, _read + // may be a completely synchronous operation which may change + // the state of the read buffer, providing enough data when + // before there was *not* enough. + // + // So, the steps are: + // 1. Figure out what the state of things will be after we do + // a read from the buffer. + // + // 2. If that resulting state will trigger a _read, then call _read. + // Note that this may be asynchronous, or synchronous. Yes, it is + // deeply ugly to write APIs this way, but that still doesn't mean + // that the Readable class should behave improperly, as streams are + // designed to be sync/async agnostic. + // Take note if the _read call is sync or async (ie, if the read call + // has returned yet), so that we know whether or not it's safe to emit + // 'readable' etc. + // + // 3. Actually pull the requested chunks out of the buffer and return. + + // if we need a readable event, then we need to do some reading. + var doRead = state.needReadable; + debug('need readable', doRead); + + // if we currently have less than the highWaterMark, then also read some + if (state.length === 0 || state.length - n < state.highWaterMark) { + doRead = true; + debug('length less than watermark', doRead); + } + + // however, if we've ended, then there's no point, and if we're already + // reading, then it's unnecessary. + if (state.ended || state.reading) { + doRead = false; + debug('reading or ended', doRead); + } + + if (doRead) { + debug('do read'); + state.reading = true; + state.sync = true; + // if the length is currently zero, then we *need* a readable event. + if (state.length === 0) + state.needReadable = true; + // call internal read method + this._read(state.highWaterMark); + state.sync = false; + } + + // If _read pushed data synchronously, then `reading` will be false, + // and we need to re-evaluate how much data we can return to the user. + if (doRead && !state.reading) + n = howMuchToRead(nOrig, state); + + var ret; + if (n > 0) + ret = fromList(n, state); + else + ret = null; + + if (util.isNull(ret)) { + state.needReadable = true; + n = 0; + } + + state.length -= n; + + // If we have nothing in the buffer, then we want to know + // as soon as we *do* get something into the buffer. + if (state.length === 0 && !state.ended) + state.needReadable = true; + + // If we tried to read() past the EOF, then emit end on the next tick. + if (nOrig !== n && state.ended && state.length === 0) + endReadable(this); + + if (!util.isNull(ret)) + this.emit('data', ret); + + return ret; +}; + +function chunkInvalid(state, chunk) { + var er = null; + if (!util.isBuffer(chunk) && + !util.isString(chunk) && + !util.isNullOrUndefined(chunk) && + !state.objectMode) { + er = new TypeError('Invalid non-string/buffer chunk'); + } + return er; +} + + +function onEofChunk(stream, state) { + if (state.decoder && !state.ended) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) { + state.buffer.push(chunk); + state.length += state.objectMode ? 1 : chunk.length; + } + } + state.ended = true; + + // emit 'readable' now to make sure it gets picked up. + emitReadable(stream); +} + +// Don't emit readable right away in sync mode, because this can trigger +// another read() call => stack overflow. This way, it might trigger +// a nextTick recursion warning, but that's not so bad. +function emitReadable(stream) { + var state = stream._readableState; + state.needReadable = false; + if (!state.emittedReadable) { + debug('emitReadable', state.flowing); + state.emittedReadable = true; + if (state.sync) + process.nextTick(function() { + emitReadable_(stream); + }); + else + emitReadable_(stream); + } +} + +function emitReadable_(stream) { + debug('emit readable'); + stream.emit('readable'); + flow(stream); +} + + +// at this point, the user has presumably seen the 'readable' event, +// and called read() to consume some data. that may have triggered +// in turn another _read(n) call, in which case reading = true if +// it's in progress. +// However, if we're not ended, or reading, and the length < hwm, +// then go ahead and try to read some more preemptively. +function maybeReadMore(stream, state) { + if (!state.readingMore) { + state.readingMore = true; + process.nextTick(function() { + maybeReadMore_(stream, state); + }); + } +} + +function maybeReadMore_(stream, state) { + var len = state.length; + while (!state.reading && !state.flowing && !state.ended && + state.length < state.highWaterMark) { + debug('maybeReadMore read 0'); + stream.read(0); + if (len === state.length) + // didn't get any data, stop spinning. + break; + else + len = state.length; + } + state.readingMore = false; +} + +// abstract method. to be overridden in specific implementation classes. +// call cb(er, data) where data is <= n in length. +// for virtual (non-string, non-buffer) streams, "length" is somewhat +// arbitrary, and perhaps not very meaningful. +Readable.prototype._read = function(n) { + this.emit('error', new Error('not implemented')); +}; + +Readable.prototype.pipe = function(dest, pipeOpts) { + var src = this; + var state = this._readableState; + + switch (state.pipesCount) { + case 0: + state.pipes = dest; + break; + case 1: + state.pipes = [state.pipes, dest]; + break; + default: + state.pipes.push(dest); + break; + } + state.pipesCount += 1; + debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); + + var doEnd = (!pipeOpts || pipeOpts.end !== false) && + dest !== process.stdout && + dest !== process.stderr; + + var endFn = doEnd ? onend : cleanup; + if (state.endEmitted) + process.nextTick(endFn); + else + src.once('end', endFn); + + dest.on('unpipe', onunpipe); + function onunpipe(readable) { + debug('onunpipe'); + if (readable === src) { + cleanup(); + } + } + + function onend() { + debug('onend'); + dest.end(); + } + + // when the dest drains, it reduces the awaitDrain counter + // on the source. This would be more elegant with a .once() + // handler in flow(), but adding and removing repeatedly is + // too slow. + var ondrain = pipeOnDrain(src); + dest.on('drain', ondrain); + + function cleanup() { + debug('cleanup'); + // cleanup event handlers once the pipe is broken + dest.removeListener('close', onclose); + dest.removeListener('finish', onfinish); + dest.removeListener('drain', ondrain); + dest.removeListener('error', onerror); + dest.removeListener('unpipe', onunpipe); + src.removeListener('end', onend); + src.removeListener('end', cleanup); + src.removeListener('data', ondata); + + // if the reader is waiting for a drain event from this + // specific writer, then it would cause it to never start + // flowing again. + // So, if this is awaiting a drain, then we just call it now. + // If we don't know, then assume that we are waiting for one. + if (state.awaitDrain && + (!dest._writableState || dest._writableState.needDrain)) + ondrain(); + } + + src.on('data', ondata); + function ondata(chunk) { + debug('ondata'); + var ret = dest.write(chunk); + if (false === ret) { + debug('false write response, pause', + src._readableState.awaitDrain); + src._readableState.awaitDrain++; + src.pause(); + } + } + + // if the dest has an error, then stop piping into it. + // however, don't suppress the throwing behavior for this. + function onerror(er) { + debug('onerror', er); + unpipe(); + dest.removeListener('error', onerror); + if (EE.listenerCount(dest, 'error') === 0) + dest.emit('error', er); + } + // This is a brutally ugly hack to make sure that our error handler + // is attached before any userland ones. NEVER DO THIS. + if (!dest._events || !dest._events.error) + dest.on('error', onerror); + else if (isArray(dest._events.error)) + dest._events.error.unshift(onerror); + else + dest._events.error = [onerror, dest._events.error]; + + + + // Both close and finish should trigger unpipe, but only once. + function onclose() { + dest.removeListener('finish', onfinish); + unpipe(); + } + dest.once('close', onclose); + function onfinish() { + debug('onfinish'); + dest.removeListener('close', onclose); + unpipe(); + } + dest.once('finish', onfinish); + + function unpipe() { + debug('unpipe'); + src.unpipe(dest); + } + + // tell the dest that it's being piped to + dest.emit('pipe', src); + + // start the flow if it hasn't been started already. + if (!state.flowing) { + debug('pipe resume'); + src.resume(); + } + + return dest; +}; + +function pipeOnDrain(src) { + return function() { + var state = src._readableState; + debug('pipeOnDrain', state.awaitDrain); + if (state.awaitDrain) + state.awaitDrain--; + if (state.awaitDrain === 0 && EE.listenerCount(src, 'data')) { + state.flowing = true; + flow(src); + } + }; +} + + +Readable.prototype.unpipe = function(dest) { + var state = this._readableState; + + // if we're not piping anywhere, then do nothing. + if (state.pipesCount === 0) + return this; + + // just one destination. most common case. + if (state.pipesCount === 1) { + // passed in one, but it's not the right one. + if (dest && dest !== state.pipes) + return this; + + if (!dest) + dest = state.pipes; + + // got a match. + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + if (dest) + dest.emit('unpipe', this); + return this; + } + + // slow case. multiple pipe destinations. + + if (!dest) { + // remove all. + var dests = state.pipes; + var len = state.pipesCount; + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + + for (var i = 0; i < len; i++) + dests[i].emit('unpipe', this); + return this; + } + + // try to find the right one. + var i = indexOf(state.pipes, dest); + if (i === -1) + return this; + + state.pipes.splice(i, 1); + state.pipesCount -= 1; + if (state.pipesCount === 1) + state.pipes = state.pipes[0]; + + dest.emit('unpipe', this); + + return this; +}; + +// set up data events if they are asked for +// Ensure readable listeners eventually get something +Readable.prototype.on = function(ev, fn) { + var res = Stream.prototype.on.call(this, ev, fn); + + // If listening to data, and it has not explicitly been paused, + // then call resume to start the flow of data on the next tick. + if (ev === 'data' && false !== this._readableState.flowing) { + this.resume(); + } + + if (ev === 'readable' && this.readable) { + var state = this._readableState; + if (!state.readableListening) { + state.readableListening = true; + state.emittedReadable = false; + state.needReadable = true; + if (!state.reading) { + var self = this; + process.nextTick(function() { + debug('readable nexttick read 0'); + self.read(0); + }); + } else if (state.length) { + emitReadable(this, state); + } + } + } + + return res; +}; +Readable.prototype.addListener = Readable.prototype.on; + +// pause() and resume() are remnants of the legacy readable stream API +// If the user uses them, then switch into old mode. +Readable.prototype.resume = function() { + var state = this._readableState; + if (!state.flowing) { + debug('resume'); + state.flowing = true; + if (!state.reading) { + debug('resume read 0'); + this.read(0); + } + resume(this, state); + } + return this; +}; + +function resume(stream, state) { + if (!state.resumeScheduled) { + state.resumeScheduled = true; + process.nextTick(function() { + resume_(stream, state); + }); + } +} + +function resume_(stream, state) { + state.resumeScheduled = false; + stream.emit('resume'); + flow(stream); + if (state.flowing && !state.reading) + stream.read(0); +} + +Readable.prototype.pause = function() { + debug('call pause flowing=%j', this._readableState.flowing); + if (false !== this._readableState.flowing) { + debug('pause'); + this._readableState.flowing = false; + this.emit('pause'); + } + return this; +}; + +function flow(stream) { + var state = stream._readableState; + debug('flow', state.flowing); + if (state.flowing) { + do { + var chunk = stream.read(); + } while (null !== chunk && state.flowing); + } +} + +// wrap an old-style stream as the async data source. +// This is *not* part of the readable stream interface. +// It is an ugly unfortunate mess of history. +Readable.prototype.wrap = function(stream) { + var state = this._readableState; + var paused = false; + + var self = this; + stream.on('end', function() { + debug('wrapped end'); + if (state.decoder && !state.ended) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) + self.push(chunk); + } + + self.push(null); + }); + + stream.on('data', function(chunk) { + debug('wrapped data'); + if (state.decoder) + chunk = state.decoder.write(chunk); + if (!chunk || !state.objectMode && !chunk.length) + return; + + var ret = self.push(chunk); + if (!ret) { + paused = true; + stream.pause(); + } + }); + + // proxy all the other methods. + // important when wrapping filters and duplexes. + for (var i in stream) { + if (util.isFunction(stream[i]) && util.isUndefined(this[i])) { + this[i] = function(method) { return function() { + return stream[method].apply(stream, arguments); + }}(i); + } + } + + // proxy certain important events. + var events = ['error', 'close', 'destroy', 'pause', 'resume']; + forEach(events, function(ev) { + stream.on(ev, self.emit.bind(self, ev)); + }); + + // when we try to consume some more bytes, simply unpause the + // underlying stream. + self._read = function(n) { + debug('wrapped _read', n); + if (paused) { + paused = false; + stream.resume(); + } + }; + + return self; +}; + + + +// exposed for testing purposes only. +Readable._fromList = fromList; + +// Pluck off n bytes from an array of buffers. +// Length is the combined lengths of all the buffers in the list. +function fromList(n, state) { + var list = state.buffer; + var length = state.length; + var stringMode = !!state.decoder; + var objectMode = !!state.objectMode; + var ret; + + // nothing in the list, definitely empty. + if (list.length === 0) + return null; + + if (length === 0) + ret = null; + else if (objectMode) + ret = list.shift(); + else if (!n || n >= length) { + // read it all, truncate the array. + if (stringMode) + ret = list.join(''); + else + ret = Buffer.concat(list, length); + list.length = 0; + } else { + // read just some of it. + if (n < list[0].length) { + // just take a part of the first list item. + // slice is the same for buffers and strings. + var buf = list[0]; + ret = buf.slice(0, n); + list[0] = buf.slice(n); + } else if (n === list[0].length) { + // first list is a perfect match + ret = list.shift(); + } else { + // complex case. + // we have enough to cover it, but it spans past the first buffer. + if (stringMode) + ret = ''; + else + ret = new Buffer(n); + + var c = 0; + for (var i = 0, l = list.length; i < l && c < n; i++) { + var buf = list[0]; + var cpy = Math.min(n - c, buf.length); + + if (stringMode) + ret += buf.slice(0, cpy); + else + buf.copy(ret, c, 0, cpy); + + if (cpy < buf.length) + list[0] = buf.slice(cpy); + else + list.shift(); + + c += cpy; + } + } + } + + return ret; +} + +function endReadable(stream) { + var state = stream._readableState; + + // If we get here before consuming all the bytes, then that is a + // bug in node. Should never happen. + if (state.length > 0) + throw new Error('endReadable called on non-empty stream'); + + if (!state.endEmitted) { + state.ended = true; + process.nextTick(function() { + // Check that we didn't get one last unshift. + if (!state.endEmitted && state.length === 0) { + state.endEmitted = true; + stream.readable = false; + stream.emit('end'); + } + }); + } +} + +function forEach (xs, f) { + for (var i = 0, l = xs.length; i < l; i++) { + f(xs[i], i); + } +} + +function indexOf (xs, x) { + for (var i = 0, l = xs.length; i < l; i++) { + if (xs[i] === x) return i; + } + return -1; +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_transform.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_transform.js new file mode 100644 index 0000000..905c5e4 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_transform.js @@ -0,0 +1,209 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +// a transform stream is a readable/writable stream where you do +// something with the data. Sometimes it's called a "filter", +// but that's not a great name for it, since that implies a thing where +// some bits pass through, and others are simply ignored. (That would +// be a valid example of a transform, of course.) +// +// While the output is causally related to the input, it's not a +// necessarily symmetric or synchronous transformation. For example, +// a zlib stream might take multiple plain-text writes(), and then +// emit a single compressed chunk some time in the future. +// +// Here's how this works: +// +// The Transform stream has all the aspects of the readable and writable +// stream classes. When you write(chunk), that calls _write(chunk,cb) +// internally, and returns false if there's a lot of pending writes +// buffered up. When you call read(), that calls _read(n) until +// there's enough pending readable data buffered up. +// +// In a transform stream, the written data is placed in a buffer. When +// _read(n) is called, it transforms the queued up data, calling the +// buffered _write cb's as it consumes chunks. If consuming a single +// written chunk would result in multiple output chunks, then the first +// outputted bit calls the readcb, and subsequent chunks just go into +// the read buffer, and will cause it to emit 'readable' if necessary. +// +// This way, back-pressure is actually determined by the reading side, +// since _read has to be called to start processing a new chunk. However, +// a pathological inflate type of transform can cause excessive buffering +// here. For example, imagine a stream where every byte of input is +// interpreted as an integer from 0-255, and then results in that many +// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in +// 1kb of data being output. In this case, you could write a very small +// amount of input, and end up with a very large amount of output. In +// such a pathological inflating mechanism, there'd be no way to tell +// the system to stop doing the transform. A single 4MB write could +// cause the system to run out of memory. +// +// However, even in such a pathological case, only a single written chunk +// would be consumed, and then the rest would wait (un-transformed) until +// the results of the previous transformed chunk were consumed. + +module.exports = Transform; + +var Duplex = require('./_stream_duplex'); + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +util.inherits(Transform, Duplex); + + +function TransformState(options, stream) { + this.afterTransform = function(er, data) { + return afterTransform(stream, er, data); + }; + + this.needTransform = false; + this.transforming = false; + this.writecb = null; + this.writechunk = null; +} + +function afterTransform(stream, er, data) { + var ts = stream._transformState; + ts.transforming = false; + + var cb = ts.writecb; + + if (!cb) + return stream.emit('error', new Error('no writecb in Transform class')); + + ts.writechunk = null; + ts.writecb = null; + + if (!util.isNullOrUndefined(data)) + stream.push(data); + + if (cb) + cb(er); + + var rs = stream._readableState; + rs.reading = false; + if (rs.needReadable || rs.length < rs.highWaterMark) { + stream._read(rs.highWaterMark); + } +} + + +function Transform(options) { + if (!(this instanceof Transform)) + return new Transform(options); + + Duplex.call(this, options); + + this._transformState = new TransformState(options, this); + + // when the writable side finishes, then flush out anything remaining. + var stream = this; + + // start out asking for a readable event once data is transformed. + this._readableState.needReadable = true; + + // we have implemented the _read method, and done the other things + // that Readable wants before the first _read call, so unset the + // sync guard flag. + this._readableState.sync = false; + + this.once('prefinish', function() { + if (util.isFunction(this._flush)) + this._flush(function(er) { + done(stream, er); + }); + else + done(stream); + }); +} + +Transform.prototype.push = function(chunk, encoding) { + this._transformState.needTransform = false; + return Duplex.prototype.push.call(this, chunk, encoding); +}; + +// This is the part where you do stuff! +// override this function in implementation classes. +// 'chunk' is an input chunk. +// +// Call `push(newChunk)` to pass along transformed output +// to the readable side. You may call 'push' zero or more times. +// +// Call `cb(err)` when you are done with this chunk. If you pass +// an error, then that'll put the hurt on the whole operation. If you +// never call cb(), then you'll never get another chunk. +Transform.prototype._transform = function(chunk, encoding, cb) { + throw new Error('not implemented'); +}; + +Transform.prototype._write = function(chunk, encoding, cb) { + var ts = this._transformState; + ts.writecb = cb; + ts.writechunk = chunk; + ts.writeencoding = encoding; + if (!ts.transforming) { + var rs = this._readableState; + if (ts.needTransform || + rs.needReadable || + rs.length < rs.highWaterMark) + this._read(rs.highWaterMark); + } +}; + +// Doesn't matter what the args are here. +// _transform does all the work. +// That we got here means that the readable side wants more data. +Transform.prototype._read = function(n) { + var ts = this._transformState; + + if (!util.isNull(ts.writechunk) && ts.writecb && !ts.transforming) { + ts.transforming = true; + this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); + } else { + // mark that we need a transform, so that any data that comes in + // will get processed, now that we've asked for it. + ts.needTransform = true; + } +}; + + +function done(stream, er) { + if (er) + return stream.emit('error', er); + + // if there's nothing in the write buffer, then that means + // that nothing more will ever be provided + var ws = stream._writableState; + var ts = stream._transformState; + + if (ws.length) + throw new Error('calling transform done when ws.length != 0'); + + if (ts.transforming) + throw new Error('calling transform done when still transforming'); + + return stream.push(null); +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_writable.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_writable.js new file mode 100644 index 0000000..db8539c --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/lib/_stream_writable.js @@ -0,0 +1,477 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// A bit simpler than readable streams. +// Implement an async ._write(chunk, cb), and it'll handle all +// the drain event emission and buffering. + +module.exports = Writable; + +/**/ +var Buffer = require('buffer').Buffer; +/**/ + +Writable.WritableState = WritableState; + + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +var Stream = require('stream'); + +util.inherits(Writable, Stream); + +function WriteReq(chunk, encoding, cb) { + this.chunk = chunk; + this.encoding = encoding; + this.callback = cb; +} + +function WritableState(options, stream) { + var Duplex = require('./_stream_duplex'); + + options = options || {}; + + // the point at which write() starts returning false + // Note: 0 is a valid value, means that we always return false if + // the entire buffer is not flushed immediately on write() + var hwm = options.highWaterMark; + var defaultHwm = options.objectMode ? 16 : 16 * 1024; + this.highWaterMark = (hwm || hwm === 0) ? hwm : defaultHwm; + + // object stream flag to indicate whether or not this stream + // contains buffers or objects. + this.objectMode = !!options.objectMode; + + if (stream instanceof Duplex) + this.objectMode = this.objectMode || !!options.writableObjectMode; + + // cast to ints. + this.highWaterMark = ~~this.highWaterMark; + + this.needDrain = false; + // at the start of calling end() + this.ending = false; + // when end() has been called, and returned + this.ended = false; + // when 'finish' is emitted + this.finished = false; + + // should we decode strings into buffers before passing to _write? + // this is here so that some node-core streams can optimize string + // handling at a lower level. + var noDecode = options.decodeStrings === false; + this.decodeStrings = !noDecode; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; + + // not an actual buffer we keep track of, but a measurement + // of how much we're waiting to get pushed to some underlying + // socket or file. + this.length = 0; + + // a flag to see when we're in the middle of a write. + this.writing = false; + + // when true all writes will be buffered until .uncork() call + this.corked = 0; + + // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + this.sync = true; + + // a flag to know if we're processing previously buffered items, which + // may call the _write() callback in the same tick, so that we don't + // end up in an overlapped onwrite situation. + this.bufferProcessing = false; + + // the callback that's passed to _write(chunk,cb) + this.onwrite = function(er) { + onwrite(stream, er); + }; + + // the callback that the user supplies to write(chunk,encoding,cb) + this.writecb = null; + + // the amount that is being written when _write is called. + this.writelen = 0; + + this.buffer = []; + + // number of pending user-supplied write callbacks + // this must be 0 before 'finish' can be emitted + this.pendingcb = 0; + + // emit prefinish if the only thing we're waiting for is _write cbs + // This is relevant for synchronous Transform streams + this.prefinished = false; + + // True if the error was already emitted and should not be thrown again + this.errorEmitted = false; +} + +function Writable(options) { + var Duplex = require('./_stream_duplex'); + + // Writable ctor is applied to Duplexes, though they're not + // instanceof Writable, they're instanceof Readable. + if (!(this instanceof Writable) && !(this instanceof Duplex)) + return new Writable(options); + + this._writableState = new WritableState(options, this); + + // legacy. + this.writable = true; + + Stream.call(this); +} + +// Otherwise people can pipe Writable streams, which is just wrong. +Writable.prototype.pipe = function() { + this.emit('error', new Error('Cannot pipe. Not readable.')); +}; + + +function writeAfterEnd(stream, state, cb) { + var er = new Error('write after end'); + // TODO: defer error events consistently everywhere, not just the cb + stream.emit('error', er); + process.nextTick(function() { + cb(er); + }); +} + +// If we get something that is not a buffer, string, null, or undefined, +// and we're not in objectMode, then that's an error. +// Otherwise stream chunks are all considered to be of length=1, and the +// watermarks determine how many objects to keep in the buffer, rather than +// how many bytes or characters. +function validChunk(stream, state, chunk, cb) { + var valid = true; + if (!util.isBuffer(chunk) && + !util.isString(chunk) && + !util.isNullOrUndefined(chunk) && + !state.objectMode) { + var er = new TypeError('Invalid non-string/buffer chunk'); + stream.emit('error', er); + process.nextTick(function() { + cb(er); + }); + valid = false; + } + return valid; +} + +Writable.prototype.write = function(chunk, encoding, cb) { + var state = this._writableState; + var ret = false; + + if (util.isFunction(encoding)) { + cb = encoding; + encoding = null; + } + + if (util.isBuffer(chunk)) + encoding = 'buffer'; + else if (!encoding) + encoding = state.defaultEncoding; + + if (!util.isFunction(cb)) + cb = function() {}; + + if (state.ended) + writeAfterEnd(this, state, cb); + else if (validChunk(this, state, chunk, cb)) { + state.pendingcb++; + ret = writeOrBuffer(this, state, chunk, encoding, cb); + } + + return ret; +}; + +Writable.prototype.cork = function() { + var state = this._writableState; + + state.corked++; +}; + +Writable.prototype.uncork = function() { + var state = this._writableState; + + if (state.corked) { + state.corked--; + + if (!state.writing && + !state.corked && + !state.finished && + !state.bufferProcessing && + state.buffer.length) + clearBuffer(this, state); + } +}; + +function decodeChunk(state, chunk, encoding) { + if (!state.objectMode && + state.decodeStrings !== false && + util.isString(chunk)) { + chunk = new Buffer(chunk, encoding); + } + return chunk; +} + +// if we're already writing something, then just put this +// in the queue, and wait our turn. Otherwise, call _write +// If we return false, then we need a drain event, so set that flag. +function writeOrBuffer(stream, state, chunk, encoding, cb) { + chunk = decodeChunk(state, chunk, encoding); + if (util.isBuffer(chunk)) + encoding = 'buffer'; + var len = state.objectMode ? 1 : chunk.length; + + state.length += len; + + var ret = state.length < state.highWaterMark; + // we must ensure that previous needDrain will not be reset to false. + if (!ret) + state.needDrain = true; + + if (state.writing || state.corked) + state.buffer.push(new WriteReq(chunk, encoding, cb)); + else + doWrite(stream, state, false, len, chunk, encoding, cb); + + return ret; +} + +function doWrite(stream, state, writev, len, chunk, encoding, cb) { + state.writelen = len; + state.writecb = cb; + state.writing = true; + state.sync = true; + if (writev) + stream._writev(chunk, state.onwrite); + else + stream._write(chunk, encoding, state.onwrite); + state.sync = false; +} + +function onwriteError(stream, state, sync, er, cb) { + if (sync) + process.nextTick(function() { + state.pendingcb--; + cb(er); + }); + else { + state.pendingcb--; + cb(er); + } + + stream._writableState.errorEmitted = true; + stream.emit('error', er); +} + +function onwriteStateUpdate(state) { + state.writing = false; + state.writecb = null; + state.length -= state.writelen; + state.writelen = 0; +} + +function onwrite(stream, er) { + var state = stream._writableState; + var sync = state.sync; + var cb = state.writecb; + + onwriteStateUpdate(state); + + if (er) + onwriteError(stream, state, sync, er, cb); + else { + // Check if we're actually ready to finish, but don't emit yet + var finished = needFinish(stream, state); + + if (!finished && + !state.corked && + !state.bufferProcessing && + state.buffer.length) { + clearBuffer(stream, state); + } + + if (sync) { + process.nextTick(function() { + afterWrite(stream, state, finished, cb); + }); + } else { + afterWrite(stream, state, finished, cb); + } + } +} + +function afterWrite(stream, state, finished, cb) { + if (!finished) + onwriteDrain(stream, state); + state.pendingcb--; + cb(); + finishMaybe(stream, state); +} + +// Must force callback to be called on nextTick, so that we don't +// emit 'drain' before the write() consumer gets the 'false' return +// value, and has a chance to attach a 'drain' listener. +function onwriteDrain(stream, state) { + if (state.length === 0 && state.needDrain) { + state.needDrain = false; + stream.emit('drain'); + } +} + + +// if there's something in the buffer waiting, then process it +function clearBuffer(stream, state) { + state.bufferProcessing = true; + + if (stream._writev && state.buffer.length > 1) { + // Fast case, write everything using _writev() + var cbs = []; + for (var c = 0; c < state.buffer.length; c++) + cbs.push(state.buffer[c].callback); + + // count the one we are adding, as well. + // TODO(isaacs) clean this up + state.pendingcb++; + doWrite(stream, state, true, state.length, state.buffer, '', function(err) { + for (var i = 0; i < cbs.length; i++) { + state.pendingcb--; + cbs[i](err); + } + }); + + // Clear buffer + state.buffer = []; + } else { + // Slow case, write chunks one-by-one + for (var c = 0; c < state.buffer.length; c++) { + var entry = state.buffer[c]; + var chunk = entry.chunk; + var encoding = entry.encoding; + var cb = entry.callback; + var len = state.objectMode ? 1 : chunk.length; + + doWrite(stream, state, false, len, chunk, encoding, cb); + + // if we didn't call the onwrite immediately, then + // it means that we need to wait until it does. + // also, that means that the chunk and cb are currently + // being processed, so move the buffer counter past them. + if (state.writing) { + c++; + break; + } + } + + if (c < state.buffer.length) + state.buffer = state.buffer.slice(c); + else + state.buffer.length = 0; + } + + state.bufferProcessing = false; +} + +Writable.prototype._write = function(chunk, encoding, cb) { + cb(new Error('not implemented')); + +}; + +Writable.prototype._writev = null; + +Writable.prototype.end = function(chunk, encoding, cb) { + var state = this._writableState; + + if (util.isFunction(chunk)) { + cb = chunk; + chunk = null; + encoding = null; + } else if (util.isFunction(encoding)) { + cb = encoding; + encoding = null; + } + + if (!util.isNullOrUndefined(chunk)) + this.write(chunk, encoding); + + // .end() fully uncorks + if (state.corked) { + state.corked = 1; + this.uncork(); + } + + // ignore unnecessary end() calls. + if (!state.ending && !state.finished) + endWritable(this, state, cb); +}; + + +function needFinish(stream, state) { + return (state.ending && + state.length === 0 && + !state.finished && + !state.writing); +} + +function prefinish(stream, state) { + if (!state.prefinished) { + state.prefinished = true; + stream.emit('prefinish'); + } +} + +function finishMaybe(stream, state) { + var need = needFinish(stream, state); + if (need) { + if (state.pendingcb === 0) { + prefinish(stream, state); + state.finished = true; + stream.emit('finish'); + } else + prefinish(stream, state); + } + return need; +} + +function endWritable(stream, state, cb) { + state.ending = true; + finishMaybe(stream, state); + if (cb) { + if (state.finished) + process.nextTick(cb); + else + stream.once('finish', cb); + } + state.ended = true; +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/LICENSE b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/LICENSE new file mode 100644 index 0000000..d8d7f94 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/LICENSE @@ -0,0 +1,19 @@ +Copyright Node.js contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/README.md b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/README.md new file mode 100644 index 0000000..5a76b41 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/README.md @@ -0,0 +1,3 @@ +# core-util-is + +The `util.is*` functions introduced in Node v0.12. diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/float.patch b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/float.patch new file mode 100644 index 0000000..a06d5c0 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/float.patch @@ -0,0 +1,604 @@ +diff --git a/lib/util.js b/lib/util.js +index a03e874..9074e8e 100644 +--- a/lib/util.js ++++ b/lib/util.js +@@ -19,430 +19,6 @@ + // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + // USE OR OTHER DEALINGS IN THE SOFTWARE. + +-var formatRegExp = /%[sdj%]/g; +-exports.format = function(f) { +- if (!isString(f)) { +- var objects = []; +- for (var i = 0; i < arguments.length; i++) { +- objects.push(inspect(arguments[i])); +- } +- return objects.join(' '); +- } +- +- var i = 1; +- var args = arguments; +- var len = args.length; +- var str = String(f).replace(formatRegExp, function(x) { +- if (x === '%%') return '%'; +- if (i >= len) return x; +- switch (x) { +- case '%s': return String(args[i++]); +- case '%d': return Number(args[i++]); +- case '%j': +- try { +- return JSON.stringify(args[i++]); +- } catch (_) { +- return '[Circular]'; +- } +- default: +- return x; +- } +- }); +- for (var x = args[i]; i < len; x = args[++i]) { +- if (isNull(x) || !isObject(x)) { +- str += ' ' + x; +- } else { +- str += ' ' + inspect(x); +- } +- } +- return str; +-}; +- +- +-// Mark that a method should not be used. +-// Returns a modified function which warns once by default. +-// If --no-deprecation is set, then it is a no-op. +-exports.deprecate = function(fn, msg) { +- // Allow for deprecating things in the process of starting up. +- if (isUndefined(global.process)) { +- return function() { +- return exports.deprecate(fn, msg).apply(this, arguments); +- }; +- } +- +- if (process.noDeprecation === true) { +- return fn; +- } +- +- var warned = false; +- function deprecated() { +- if (!warned) { +- if (process.throwDeprecation) { +- throw new Error(msg); +- } else if (process.traceDeprecation) { +- console.trace(msg); +- } else { +- console.error(msg); +- } +- warned = true; +- } +- return fn.apply(this, arguments); +- } +- +- return deprecated; +-}; +- +- +-var debugs = {}; +-var debugEnviron; +-exports.debuglog = function(set) { +- if (isUndefined(debugEnviron)) +- debugEnviron = process.env.NODE_DEBUG || ''; +- set = set.toUpperCase(); +- if (!debugs[set]) { +- if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { +- var pid = process.pid; +- debugs[set] = function() { +- var msg = exports.format.apply(exports, arguments); +- console.error('%s %d: %s', set, pid, msg); +- }; +- } else { +- debugs[set] = function() {}; +- } +- } +- return debugs[set]; +-}; +- +- +-/** +- * Echos the value of a value. Trys to print the value out +- * in the best way possible given the different types. +- * +- * @param {Object} obj The object to print out. +- * @param {Object} opts Optional options object that alters the output. +- */ +-/* legacy: obj, showHidden, depth, colors*/ +-function inspect(obj, opts) { +- // default options +- var ctx = { +- seen: [], +- stylize: stylizeNoColor +- }; +- // legacy... +- if (arguments.length >= 3) ctx.depth = arguments[2]; +- if (arguments.length >= 4) ctx.colors = arguments[3]; +- if (isBoolean(opts)) { +- // legacy... +- ctx.showHidden = opts; +- } else if (opts) { +- // got an "options" object +- exports._extend(ctx, opts); +- } +- // set default options +- if (isUndefined(ctx.showHidden)) ctx.showHidden = false; +- if (isUndefined(ctx.depth)) ctx.depth = 2; +- if (isUndefined(ctx.colors)) ctx.colors = false; +- if (isUndefined(ctx.customInspect)) ctx.customInspect = true; +- if (ctx.colors) ctx.stylize = stylizeWithColor; +- return formatValue(ctx, obj, ctx.depth); +-} +-exports.inspect = inspect; +- +- +-// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +-inspect.colors = { +- 'bold' : [1, 22], +- 'italic' : [3, 23], +- 'underline' : [4, 24], +- 'inverse' : [7, 27], +- 'white' : [37, 39], +- 'grey' : [90, 39], +- 'black' : [30, 39], +- 'blue' : [34, 39], +- 'cyan' : [36, 39], +- 'green' : [32, 39], +- 'magenta' : [35, 39], +- 'red' : [31, 39], +- 'yellow' : [33, 39] +-}; +- +-// Don't use 'blue' not visible on cmd.exe +-inspect.styles = { +- 'special': 'cyan', +- 'number': 'yellow', +- 'boolean': 'yellow', +- 'undefined': 'grey', +- 'null': 'bold', +- 'string': 'green', +- 'date': 'magenta', +- // "name": intentionally not styling +- 'regexp': 'red' +-}; +- +- +-function stylizeWithColor(str, styleType) { +- var style = inspect.styles[styleType]; +- +- if (style) { +- return '\u001b[' + inspect.colors[style][0] + 'm' + str + +- '\u001b[' + inspect.colors[style][1] + 'm'; +- } else { +- return str; +- } +-} +- +- +-function stylizeNoColor(str, styleType) { +- return str; +-} +- +- +-function arrayToHash(array) { +- var hash = {}; +- +- array.forEach(function(val, idx) { +- hash[val] = true; +- }); +- +- return hash; +-} +- +- +-function formatValue(ctx, value, recurseTimes) { +- // Provide a hook for user-specified inspect functions. +- // Check that value is an object with an inspect function on it +- if (ctx.customInspect && +- value && +- isFunction(value.inspect) && +- // Filter out the util module, it's inspect function is special +- value.inspect !== exports.inspect && +- // Also filter out any prototype objects using the circular check. +- !(value.constructor && value.constructor.prototype === value)) { +- var ret = value.inspect(recurseTimes, ctx); +- if (!isString(ret)) { +- ret = formatValue(ctx, ret, recurseTimes); +- } +- return ret; +- } +- +- // Primitive types cannot have properties +- var primitive = formatPrimitive(ctx, value); +- if (primitive) { +- return primitive; +- } +- +- // Look up the keys of the object. +- var keys = Object.keys(value); +- var visibleKeys = arrayToHash(keys); +- +- if (ctx.showHidden) { +- keys = Object.getOwnPropertyNames(value); +- } +- +- // Some type of object without properties can be shortcutted. +- if (keys.length === 0) { +- if (isFunction(value)) { +- var name = value.name ? ': ' + value.name : ''; +- return ctx.stylize('[Function' + name + ']', 'special'); +- } +- if (isRegExp(value)) { +- return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); +- } +- if (isDate(value)) { +- return ctx.stylize(Date.prototype.toString.call(value), 'date'); +- } +- if (isError(value)) { +- return formatError(value); +- } +- } +- +- var base = '', array = false, braces = ['{', '}']; +- +- // Make Array say that they are Array +- if (isArray(value)) { +- array = true; +- braces = ['[', ']']; +- } +- +- // Make functions say that they are functions +- if (isFunction(value)) { +- var n = value.name ? ': ' + value.name : ''; +- base = ' [Function' + n + ']'; +- } +- +- // Make RegExps say that they are RegExps +- if (isRegExp(value)) { +- base = ' ' + RegExp.prototype.toString.call(value); +- } +- +- // Make dates with properties first say the date +- if (isDate(value)) { +- base = ' ' + Date.prototype.toUTCString.call(value); +- } +- +- // Make error with message first say the error +- if (isError(value)) { +- base = ' ' + formatError(value); +- } +- +- if (keys.length === 0 && (!array || value.length == 0)) { +- return braces[0] + base + braces[1]; +- } +- +- if (recurseTimes < 0) { +- if (isRegExp(value)) { +- return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); +- } else { +- return ctx.stylize('[Object]', 'special'); +- } +- } +- +- ctx.seen.push(value); +- +- var output; +- if (array) { +- output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); +- } else { +- output = keys.map(function(key) { +- return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); +- }); +- } +- +- ctx.seen.pop(); +- +- return reduceToSingleString(output, base, braces); +-} +- +- +-function formatPrimitive(ctx, value) { +- if (isUndefined(value)) +- return ctx.stylize('undefined', 'undefined'); +- if (isString(value)) { +- var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') +- .replace(/'/g, "\\'") +- .replace(/\\"/g, '"') + '\''; +- return ctx.stylize(simple, 'string'); +- } +- if (isNumber(value)) { +- // Format -0 as '-0'. Strict equality won't distinguish 0 from -0, +- // so instead we use the fact that 1 / -0 < 0 whereas 1 / 0 > 0 . +- if (value === 0 && 1 / value < 0) +- return ctx.stylize('-0', 'number'); +- return ctx.stylize('' + value, 'number'); +- } +- if (isBoolean(value)) +- return ctx.stylize('' + value, 'boolean'); +- // For some reason typeof null is "object", so special case here. +- if (isNull(value)) +- return ctx.stylize('null', 'null'); +-} +- +- +-function formatError(value) { +- return '[' + Error.prototype.toString.call(value) + ']'; +-} +- +- +-function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { +- var output = []; +- for (var i = 0, l = value.length; i < l; ++i) { +- if (hasOwnProperty(value, String(i))) { +- output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, +- String(i), true)); +- } else { +- output.push(''); +- } +- } +- keys.forEach(function(key) { +- if (!key.match(/^\d+$/)) { +- output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, +- key, true)); +- } +- }); +- return output; +-} +- +- +-function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { +- var name, str, desc; +- desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; +- if (desc.get) { +- if (desc.set) { +- str = ctx.stylize('[Getter/Setter]', 'special'); +- } else { +- str = ctx.stylize('[Getter]', 'special'); +- } +- } else { +- if (desc.set) { +- str = ctx.stylize('[Setter]', 'special'); +- } +- } +- if (!hasOwnProperty(visibleKeys, key)) { +- name = '[' + key + ']'; +- } +- if (!str) { +- if (ctx.seen.indexOf(desc.value) < 0) { +- if (isNull(recurseTimes)) { +- str = formatValue(ctx, desc.value, null); +- } else { +- str = formatValue(ctx, desc.value, recurseTimes - 1); +- } +- if (str.indexOf('\n') > -1) { +- if (array) { +- str = str.split('\n').map(function(line) { +- return ' ' + line; +- }).join('\n').substr(2); +- } else { +- str = '\n' + str.split('\n').map(function(line) { +- return ' ' + line; +- }).join('\n'); +- } +- } +- } else { +- str = ctx.stylize('[Circular]', 'special'); +- } +- } +- if (isUndefined(name)) { +- if (array && key.match(/^\d+$/)) { +- return str; +- } +- name = JSON.stringify('' + key); +- if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { +- name = name.substr(1, name.length - 2); +- name = ctx.stylize(name, 'name'); +- } else { +- name = name.replace(/'/g, "\\'") +- .replace(/\\"/g, '"') +- .replace(/(^"|"$)/g, "'"); +- name = ctx.stylize(name, 'string'); +- } +- } +- +- return name + ': ' + str; +-} +- +- +-function reduceToSingleString(output, base, braces) { +- var numLinesEst = 0; +- var length = output.reduce(function(prev, cur) { +- numLinesEst++; +- if (cur.indexOf('\n') >= 0) numLinesEst++; +- return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; +- }, 0); +- +- if (length > 60) { +- return braces[0] + +- (base === '' ? '' : base + '\n ') + +- ' ' + +- output.join(',\n ') + +- ' ' + +- braces[1]; +- } +- +- return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +-} +- +- + // NOTE: These type checking functions intentionally don't use `instanceof` + // because it is fragile and can be easily faked with `Object.create()`. + function isArray(ar) { +@@ -522,166 +98,10 @@ function isPrimitive(arg) { + exports.isPrimitive = isPrimitive; + + function isBuffer(arg) { +- return arg instanceof Buffer; ++ return Buffer.isBuffer(arg); + } + exports.isBuffer = isBuffer; + + function objectToString(o) { + return Object.prototype.toString.call(o); +-} +- +- +-function pad(n) { +- return n < 10 ? '0' + n.toString(10) : n.toString(10); +-} +- +- +-var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', +- 'Oct', 'Nov', 'Dec']; +- +-// 26 Feb 16:19:34 +-function timestamp() { +- var d = new Date(); +- var time = [pad(d.getHours()), +- pad(d.getMinutes()), +- pad(d.getSeconds())].join(':'); +- return [d.getDate(), months[d.getMonth()], time].join(' '); +-} +- +- +-// log is just a thin wrapper to console.log that prepends a timestamp +-exports.log = function() { +- console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +-}; +- +- +-/** +- * Inherit the prototype methods from one constructor into another. +- * +- * The Function.prototype.inherits from lang.js rewritten as a standalone +- * function (not on Function.prototype). NOTE: If this file is to be loaded +- * during bootstrapping this function needs to be rewritten using some native +- * functions as prototype setup using normal JavaScript does not work as +- * expected during bootstrapping (see mirror.js in r114903). +- * +- * @param {function} ctor Constructor function which needs to inherit the +- * prototype. +- * @param {function} superCtor Constructor function to inherit prototype from. +- */ +-exports.inherits = function(ctor, superCtor) { +- ctor.super_ = superCtor; +- ctor.prototype = Object.create(superCtor.prototype, { +- constructor: { +- value: ctor, +- enumerable: false, +- writable: true, +- configurable: true +- } +- }); +-}; +- +-exports._extend = function(origin, add) { +- // Don't do anything if add isn't an object +- if (!add || !isObject(add)) return origin; +- +- var keys = Object.keys(add); +- var i = keys.length; +- while (i--) { +- origin[keys[i]] = add[keys[i]]; +- } +- return origin; +-}; +- +-function hasOwnProperty(obj, prop) { +- return Object.prototype.hasOwnProperty.call(obj, prop); +-} +- +- +-// Deprecated old stuff. +- +-exports.p = exports.deprecate(function() { +- for (var i = 0, len = arguments.length; i < len; ++i) { +- console.error(exports.inspect(arguments[i])); +- } +-}, 'util.p: Use console.error() instead'); +- +- +-exports.exec = exports.deprecate(function() { +- return require('child_process').exec.apply(this, arguments); +-}, 'util.exec is now called `child_process.exec`.'); +- +- +-exports.print = exports.deprecate(function() { +- for (var i = 0, len = arguments.length; i < len; ++i) { +- process.stdout.write(String(arguments[i])); +- } +-}, 'util.print: Use console.log instead'); +- +- +-exports.puts = exports.deprecate(function() { +- for (var i = 0, len = arguments.length; i < len; ++i) { +- process.stdout.write(arguments[i] + '\n'); +- } +-}, 'util.puts: Use console.log instead'); +- +- +-exports.debug = exports.deprecate(function(x) { +- process.stderr.write('DEBUG: ' + x + '\n'); +-}, 'util.debug: Use console.error instead'); +- +- +-exports.error = exports.deprecate(function(x) { +- for (var i = 0, len = arguments.length; i < len; ++i) { +- process.stderr.write(arguments[i] + '\n'); +- } +-}, 'util.error: Use console.error instead'); +- +- +-exports.pump = exports.deprecate(function(readStream, writeStream, callback) { +- var callbackCalled = false; +- +- function call(a, b, c) { +- if (callback && !callbackCalled) { +- callback(a, b, c); +- callbackCalled = true; +- } +- } +- +- readStream.addListener('data', function(chunk) { +- if (writeStream.write(chunk) === false) readStream.pause(); +- }); +- +- writeStream.addListener('drain', function() { +- readStream.resume(); +- }); +- +- readStream.addListener('end', function() { +- writeStream.end(); +- }); +- +- readStream.addListener('close', function() { +- call(); +- }); +- +- readStream.addListener('error', function(err) { +- writeStream.end(); +- call(err); +- }); +- +- writeStream.addListener('error', function(err) { +- readStream.destroy(); +- call(err); +- }); +-}, 'util.pump(): Use readableStream.pipe() instead'); +- +- +-var uv; +-exports._errnoException = function(err, syscall) { +- if (isUndefined(uv)) uv = process.binding('uv'); +- var errname = uv.errname(err); +- var e = new Error(syscall + ' ' + errname); +- e.code = errname; +- e.errno = errname; +- e.syscall = syscall; +- return e; +-}; ++} \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/lib/util.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/lib/util.js new file mode 100644 index 0000000..ff4c851 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/lib/util.js @@ -0,0 +1,107 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. + +function isArray(arg) { + if (Array.isArray) { + return Array.isArray(arg); + } + return objectToString(arg) === '[object Array]'; +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = Buffer.isBuffer; + +function objectToString(o) { + return Object.prototype.toString.call(o); +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/package.json b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/package.json new file mode 100644 index 0000000..0d9cbea --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/package.json @@ -0,0 +1,59 @@ +{ + "name": "core-util-is", + "version": "1.0.2", + "description": "The `util.is*` functions introduced in Node v0.12.", + "main": "lib/util.js", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/core-util-is.git" + }, + "keywords": [ + "util", + "isBuffer", + "isArray", + "isNumber", + "isString", + "isRegExp", + "isThis", + "isThat", + "polyfill" + ], + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/isaacs/core-util-is/issues" + }, + "scripts": { + "test": "tap test.js" + }, + "devDependencies": { + "tap": "^2.3.0" + }, + "gitHead": "a177da234df5638b363ddc15fa324619a38577c8", + "homepage": "https://github.com/isaacs/core-util-is#readme", + "_id": "core-util-is@1.0.2", + "_shasum": "b5fd54220aa2bc5ab57aab7140c940754503c1a7", + "_from": "core-util-is@>=1.0.0 <1.1.0", + "_npmVersion": "3.3.2", + "_nodeVersion": "4.0.0", + "_npmUser": { + "name": "isaacs", + "email": "i@izs.me" + }, + "dist": { + "shasum": "b5fd54220aa2bc5ab57aab7140c940754503c1a7", + "tarball": "http://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + } + ], + "directories": {}, + "_resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/test.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/test.js new file mode 100644 index 0000000..1a490c6 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/core-util-is/test.js @@ -0,0 +1,68 @@ +var assert = require('tap'); + +var t = require('./lib/util'); + +assert.equal(t.isArray([]), true); +assert.equal(t.isArray({}), false); + +assert.equal(t.isBoolean(null), false); +assert.equal(t.isBoolean(true), true); +assert.equal(t.isBoolean(false), true); + +assert.equal(t.isNull(null), true); +assert.equal(t.isNull(undefined), false); +assert.equal(t.isNull(false), false); +assert.equal(t.isNull(), false); + +assert.equal(t.isNullOrUndefined(null), true); +assert.equal(t.isNullOrUndefined(undefined), true); +assert.equal(t.isNullOrUndefined(false), false); +assert.equal(t.isNullOrUndefined(), true); + +assert.equal(t.isNumber(null), false); +assert.equal(t.isNumber('1'), false); +assert.equal(t.isNumber(1), true); + +assert.equal(t.isString(null), false); +assert.equal(t.isString('1'), true); +assert.equal(t.isString(1), false); + +assert.equal(t.isSymbol(null), false); +assert.equal(t.isSymbol('1'), false); +assert.equal(t.isSymbol(1), false); +assert.equal(t.isSymbol(Symbol()), true); + +assert.equal(t.isUndefined(null), false); +assert.equal(t.isUndefined(undefined), true); +assert.equal(t.isUndefined(false), false); +assert.equal(t.isUndefined(), true); + +assert.equal(t.isRegExp(null), false); +assert.equal(t.isRegExp('1'), false); +assert.equal(t.isRegExp(new RegExp()), true); + +assert.equal(t.isObject({}), true); +assert.equal(t.isObject([]), true); +assert.equal(t.isObject(new RegExp()), true); +assert.equal(t.isObject(new Date()), true); + +assert.equal(t.isDate(null), false); +assert.equal(t.isDate('1'), false); +assert.equal(t.isDate(new Date()), true); + +assert.equal(t.isError(null), false); +assert.equal(t.isError({ err: true }), false); +assert.equal(t.isError(new Error()), true); + +assert.equal(t.isFunction(null), false); +assert.equal(t.isFunction({ }), false); +assert.equal(t.isFunction(function() {}), true); + +assert.equal(t.isPrimitive(null), true); +assert.equal(t.isPrimitive(''), true); +assert.equal(t.isPrimitive(0), true); +assert.equal(t.isPrimitive(new Date()), false); + +assert.equal(t.isBuffer(null), false); +assert.equal(t.isBuffer({}), false); +assert.equal(t.isBuffer(new Buffer(0)), true); diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/LICENSE b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/LICENSE new file mode 100644 index 0000000..dea3013 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/LICENSE @@ -0,0 +1,16 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/README.md b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/README.md new file mode 100644 index 0000000..b1c5665 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/README.md @@ -0,0 +1,42 @@ +Browser-friendly inheritance fully compatible with standard node.js +[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor). + +This package exports standard `inherits` from node.js `util` module in +node environment, but also provides alternative browser-friendly +implementation through [browser +field](https://gist.github.com/shtylman/4339901). Alternative +implementation is a literal copy of standard one located in standalone +module to avoid requiring of `util`. It also has a shim for old +browsers with no `Object.create` support. + +While keeping you sure you are using standard `inherits` +implementation in node.js environment, it allows bundlers such as +[browserify](https://github.com/substack/node-browserify) to not +include full `util` package to your client code if all you need is +just `inherits` function. It worth, because browser shim for `util` +package is large and `inherits` is often the single function you need +from it. + +It's recommended to use this package instead of +`require('util').inherits` for any code that has chances to be used +not only in node.js but in browser too. + +## usage + +```js +var inherits = require('inherits'); +// then use exactly as the standard one +``` + +## note on version ~1.0 + +Version ~1.0 had completely different motivation and is not compatible +neither with 2.0 nor with standard node.js `inherits`. + +If you are using version ~1.0 and planning to switch to ~2.0, be +careful: + +* new version uses `super_` instead of `super` for referencing + superclass +* new version overwrites current prototype while old one preserves any + existing fields on it diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/inherits.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/inherits.js new file mode 100644 index 0000000..29f5e24 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/inherits.js @@ -0,0 +1 @@ +module.exports = require('util').inherits diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/inherits_browser.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/inherits_browser.js new file mode 100644 index 0000000..c1e78a7 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/inherits_browser.js @@ -0,0 +1,23 @@ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/package.json b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/package.json new file mode 100644 index 0000000..93d5078 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/package.json @@ -0,0 +1,50 @@ +{ + "name": "inherits", + "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()", + "version": "2.0.1", + "keywords": [ + "inheritance", + "class", + "klass", + "oop", + "object-oriented", + "inherits", + "browser", + "browserify" + ], + "main": "./inherits.js", + "browser": "./inherits_browser.js", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/inherits.git" + }, + "license": "ISC", + "scripts": { + "test": "node test" + }, + "bugs": { + "url": "https://github.com/isaacs/inherits/issues" + }, + "_id": "inherits@2.0.1", + "dist": { + "shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1", + "tarball": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "_from": "inherits@>=2.0.1 <2.1.0", + "_npmVersion": "1.3.8", + "_npmUser": { + "name": "isaacs", + "email": "i@izs.me" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + } + ], + "directories": {}, + "_shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1", + "_resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "readme": "ERROR: No README data found!", + "homepage": "https://github.com/isaacs/inherits#readme" +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/test.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/test.js new file mode 100644 index 0000000..fc53012 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/inherits/test.js @@ -0,0 +1,25 @@ +var inherits = require('./inherits.js') +var assert = require('assert') + +function test(c) { + assert(c.constructor === Child) + assert(c.constructor.super_ === Parent) + assert(Object.getPrototypeOf(c) === Child.prototype) + assert(Object.getPrototypeOf(Object.getPrototypeOf(c)) === Parent.prototype) + assert(c instanceof Child) + assert(c instanceof Parent) +} + +function Child() { + Parent.call(this) + test(this) +} + +function Parent() {} + +inherits(Child, Parent) + +var c = new Child +test(c) + +console.log('ok') diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/README.md b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/README.md new file mode 100644 index 0000000..052a62b --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/README.md @@ -0,0 +1,54 @@ + +# isarray + +`Array#isArray` for older browsers. + +## Usage + +```js +var isArray = require('isarray'); + +console.log(isArray([])); // => true +console.log(isArray({})); // => false +``` + +## Installation + +With [npm](http://npmjs.org) do + +```bash +$ npm install isarray +``` + +Then bundle for the browser with +[browserify](https://github.com/substack/browserify). + +With [component](http://component.io) do + +```bash +$ component install juliangruber/isarray +``` + +## License + +(MIT) + +Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/build/build.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/build/build.js new file mode 100644 index 0000000..ec58596 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/build/build.js @@ -0,0 +1,209 @@ + +/** + * Require the given path. + * + * @param {String} path + * @return {Object} exports + * @api public + */ + +function require(path, parent, orig) { + var resolved = require.resolve(path); + + // lookup failed + if (null == resolved) { + orig = orig || path; + parent = parent || 'root'; + var err = new Error('Failed to require "' + orig + '" from "' + parent + '"'); + err.path = orig; + err.parent = parent; + err.require = true; + throw err; + } + + var module = require.modules[resolved]; + + // perform real require() + // by invoking the module's + // registered function + if (!module.exports) { + module.exports = {}; + module.client = module.component = true; + module.call(this, module.exports, require.relative(resolved), module); + } + + return module.exports; +} + +/** + * Registered modules. + */ + +require.modules = {}; + +/** + * Registered aliases. + */ + +require.aliases = {}; + +/** + * Resolve `path`. + * + * Lookup: + * + * - PATH/index.js + * - PATH.js + * - PATH + * + * @param {String} path + * @return {String} path or null + * @api private + */ + +require.resolve = function(path) { + if (path.charAt(0) === '/') path = path.slice(1); + var index = path + '/index.js'; + + var paths = [ + path, + path + '.js', + path + '.json', + path + '/index.js', + path + '/index.json' + ]; + + for (var i = 0; i < paths.length; i++) { + var path = paths[i]; + if (require.modules.hasOwnProperty(path)) return path; + } + + if (require.aliases.hasOwnProperty(index)) { + return require.aliases[index]; + } +}; + +/** + * Normalize `path` relative to the current path. + * + * @param {String} curr + * @param {String} path + * @return {String} + * @api private + */ + +require.normalize = function(curr, path) { + var segs = []; + + if ('.' != path.charAt(0)) return path; + + curr = curr.split('/'); + path = path.split('/'); + + for (var i = 0; i < path.length; ++i) { + if ('..' == path[i]) { + curr.pop(); + } else if ('.' != path[i] && '' != path[i]) { + segs.push(path[i]); + } + } + + return curr.concat(segs).join('/'); +}; + +/** + * Register module at `path` with callback `definition`. + * + * @param {String} path + * @param {Function} definition + * @api private + */ + +require.register = function(path, definition) { + require.modules[path] = definition; +}; + +/** + * Alias a module definition. + * + * @param {String} from + * @param {String} to + * @api private + */ + +require.alias = function(from, to) { + if (!require.modules.hasOwnProperty(from)) { + throw new Error('Failed to alias "' + from + '", it does not exist'); + } + require.aliases[to] = from; +}; + +/** + * Return a require function relative to the `parent` path. + * + * @param {String} parent + * @return {Function} + * @api private + */ + +require.relative = function(parent) { + var p = require.normalize(parent, '..'); + + /** + * lastIndexOf helper. + */ + + function lastIndexOf(arr, obj) { + var i = arr.length; + while (i--) { + if (arr[i] === obj) return i; + } + return -1; + } + + /** + * The relative require() itself. + */ + + function localRequire(path) { + var resolved = localRequire.resolve(path); + return require(resolved, parent, path); + } + + /** + * Resolve relative to the parent. + */ + + localRequire.resolve = function(path) { + var c = path.charAt(0); + if ('/' == c) return path.slice(1); + if ('.' == c) return require.normalize(p, path); + + // resolve deps by returning + // the dep in the nearest "deps" + // directory + var segs = parent.split('/'); + var i = lastIndexOf(segs, 'deps') + 1; + if (!i) i = 0; + path = segs.slice(0, i + 1).join('/') + '/deps/' + path; + return path; + }; + + /** + * Check if module is defined at `path`. + */ + + localRequire.exists = function(path) { + return require.modules.hasOwnProperty(localRequire.resolve(path)); + }; + + return localRequire; +}; +require.register("isarray/index.js", function(exports, require, module){ +module.exports = Array.isArray || function (arr) { + return Object.prototype.toString.call(arr) == '[object Array]'; +}; + +}); +require.alias("isarray/index.js", "isarray/index.js"); + diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/component.json b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/component.json new file mode 100644 index 0000000..9e31b68 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/component.json @@ -0,0 +1,19 @@ +{ + "name" : "isarray", + "description" : "Array#isArray for older browsers", + "version" : "0.0.1", + "repository" : "juliangruber/isarray", + "homepage": "https://github.com/juliangruber/isarray", + "main" : "index.js", + "scripts" : [ + "index.js" + ], + "dependencies" : {}, + "keywords": ["browser","isarray","array"], + "author": { + "name": "Julian Gruber", + "email": "mail@juliangruber.com", + "url": "http://juliangruber.com" + }, + "license": "MIT" +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/index.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/index.js new file mode 100644 index 0000000..5f5ad45 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/index.js @@ -0,0 +1,3 @@ +module.exports = Array.isArray || function (arr) { + return Object.prototype.toString.call(arr) == '[object Array]'; +}; diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/package.json b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/package.json new file mode 100644 index 0000000..85aec01 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/isarray/package.json @@ -0,0 +1,49 @@ +{ + "name": "isarray", + "description": "Array#isArray for older browsers", + "version": "0.0.1", + "repository": { + "type": "git", + "url": "git://github.com/juliangruber/isarray.git" + }, + "homepage": "https://github.com/juliangruber/isarray", + "main": "index.js", + "scripts": { + "test": "tap test/*.js" + }, + "dependencies": {}, + "devDependencies": { + "tap": "*" + }, + "keywords": [ + "browser", + "isarray", + "array" + ], + "author": { + "name": "Julian Gruber", + "email": "mail@juliangruber.com", + "url": "http://juliangruber.com" + }, + "license": "MIT", + "_id": "isarray@0.0.1", + "dist": { + "shasum": "8a18acfca9a8f4177e09abfc6038939b05d1eedf", + "tarball": "http://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "_from": "isarray@0.0.1", + "_npmVersion": "1.2.18", + "_npmUser": { + "name": "juliangruber", + "email": "julian@juliangruber.com" + }, + "maintainers": [ + { + "name": "juliangruber", + "email": "julian@juliangruber.com" + } + ], + "directories": {}, + "_shasum": "8a18acfca9a8f4177e09abfc6038939b05d1eedf", + "_resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/.npmignore b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/.npmignore new file mode 100644 index 0000000..206320c --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/.npmignore @@ -0,0 +1,2 @@ +build +test diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/LICENSE b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/LICENSE new file mode 100644 index 0000000..6de584a --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/LICENSE @@ -0,0 +1,20 @@ +Copyright Joyent, Inc. and other Node contributors. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/README.md b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/README.md new file mode 100644 index 0000000..4d2aa00 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/README.md @@ -0,0 +1,7 @@ +**string_decoder.js** (`require('string_decoder')`) from Node.js core + +Copyright Joyent, Inc. and other Node contributors. See LICENCE file for details. + +Version numbers match the versions found in Node core, e.g. 0.10.24 matches Node 0.10.24, likewise 0.11.10 matches Node 0.11.10. **Prefer the stable version over the unstable.** + +The *build/* directory contains a build script that will scrape the source from the [joyent/node](https://github.com/joyent/node) repo given a specific Node version. \ No newline at end of file diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/index.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/index.js new file mode 100644 index 0000000..b00e54f --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/index.js @@ -0,0 +1,221 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var Buffer = require('buffer').Buffer; + +var isBufferEncoding = Buffer.isEncoding + || function(encoding) { + switch (encoding && encoding.toLowerCase()) { + case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': case 'raw': return true; + default: return false; + } + } + + +function assertEncoding(encoding) { + if (encoding && !isBufferEncoding(encoding)) { + throw new Error('Unknown encoding: ' + encoding); + } +} + +// StringDecoder provides an interface for efficiently splitting a series of +// buffers into a series of JS strings without breaking apart multi-byte +// characters. CESU-8 is handled as part of the UTF-8 encoding. +// +// @TODO Handling all encodings inside a single object makes it very difficult +// to reason about this code, so it should be split up in the future. +// @TODO There should be a utf8-strict encoding that rejects invalid UTF-8 code +// points as used by CESU-8. +var StringDecoder = exports.StringDecoder = function(encoding) { + this.encoding = (encoding || 'utf8').toLowerCase().replace(/[-_]/, ''); + assertEncoding(encoding); + switch (this.encoding) { + case 'utf8': + // CESU-8 represents each of Surrogate Pair by 3-bytes + this.surrogateSize = 3; + break; + case 'ucs2': + case 'utf16le': + // UTF-16 represents each of Surrogate Pair by 2-bytes + this.surrogateSize = 2; + this.detectIncompleteChar = utf16DetectIncompleteChar; + break; + case 'base64': + // Base-64 stores 3 bytes in 4 chars, and pads the remainder. + this.surrogateSize = 3; + this.detectIncompleteChar = base64DetectIncompleteChar; + break; + default: + this.write = passThroughWrite; + return; + } + + // Enough space to store all bytes of a single character. UTF-8 needs 4 + // bytes, but CESU-8 may require up to 6 (3 bytes per surrogate). + this.charBuffer = new Buffer(6); + // Number of bytes received for the current incomplete multi-byte character. + this.charReceived = 0; + // Number of bytes expected for the current incomplete multi-byte character. + this.charLength = 0; +}; + + +// write decodes the given buffer and returns it as JS string that is +// guaranteed to not contain any partial multi-byte characters. Any partial +// character found at the end of the buffer is buffered up, and will be +// returned when calling write again with the remaining bytes. +// +// Note: Converting a Buffer containing an orphan surrogate to a String +// currently works, but converting a String to a Buffer (via `new Buffer`, or +// Buffer#write) will replace incomplete surrogates with the unicode +// replacement character. See https://codereview.chromium.org/121173009/ . +StringDecoder.prototype.write = function(buffer) { + var charStr = ''; + // if our last write ended with an incomplete multibyte character + while (this.charLength) { + // determine how many remaining bytes this buffer has to offer for this char + var available = (buffer.length >= this.charLength - this.charReceived) ? + this.charLength - this.charReceived : + buffer.length; + + // add the new bytes to the char buffer + buffer.copy(this.charBuffer, this.charReceived, 0, available); + this.charReceived += available; + + if (this.charReceived < this.charLength) { + // still not enough chars in this buffer? wait for more ... + return ''; + } + + // remove bytes belonging to the current character from the buffer + buffer = buffer.slice(available, buffer.length); + + // get the character that was split + charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding); + + // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character + var charCode = charStr.charCodeAt(charStr.length - 1); + if (charCode >= 0xD800 && charCode <= 0xDBFF) { + this.charLength += this.surrogateSize; + charStr = ''; + continue; + } + this.charReceived = this.charLength = 0; + + // if there are no more bytes in this buffer, just emit our char + if (buffer.length === 0) { + return charStr; + } + break; + } + + // determine and set charLength / charReceived + this.detectIncompleteChar(buffer); + + var end = buffer.length; + if (this.charLength) { + // buffer the incomplete character bytes we got + buffer.copy(this.charBuffer, 0, buffer.length - this.charReceived, end); + end -= this.charReceived; + } + + charStr += buffer.toString(this.encoding, 0, end); + + var end = charStr.length - 1; + var charCode = charStr.charCodeAt(end); + // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character + if (charCode >= 0xD800 && charCode <= 0xDBFF) { + var size = this.surrogateSize; + this.charLength += size; + this.charReceived += size; + this.charBuffer.copy(this.charBuffer, size, 0, size); + buffer.copy(this.charBuffer, 0, 0, size); + return charStr.substring(0, end); + } + + // or just emit the charStr + return charStr; +}; + +// detectIncompleteChar determines if there is an incomplete UTF-8 character at +// the end of the given buffer. If so, it sets this.charLength to the byte +// length that character, and sets this.charReceived to the number of bytes +// that are available for this character. +StringDecoder.prototype.detectIncompleteChar = function(buffer) { + // determine how many bytes we have to check at the end of this buffer + var i = (buffer.length >= 3) ? 3 : buffer.length; + + // Figure out if one of the last i bytes of our buffer announces an + // incomplete char. + for (; i > 0; i--) { + var c = buffer[buffer.length - i]; + + // See http://en.wikipedia.org/wiki/UTF-8#Description + + // 110XXXXX + if (i == 1 && c >> 5 == 0x06) { + this.charLength = 2; + break; + } + + // 1110XXXX + if (i <= 2 && c >> 4 == 0x0E) { + this.charLength = 3; + break; + } + + // 11110XXX + if (i <= 3 && c >> 3 == 0x1E) { + this.charLength = 4; + break; + } + } + this.charReceived = i; +}; + +StringDecoder.prototype.end = function(buffer) { + var res = ''; + if (buffer && buffer.length) + res = this.write(buffer); + + if (this.charReceived) { + var cr = this.charReceived; + var buf = this.charBuffer; + var enc = this.encoding; + res += buf.slice(0, cr).toString(enc); + } + + return res; +}; + +function passThroughWrite(buffer) { + return buffer.toString(this.encoding); +} + +function utf16DetectIncompleteChar(buffer) { + this.charReceived = buffer.length % 2; + this.charLength = this.charReceived ? 2 : 0; +} + +function base64DetectIncompleteChar(buffer) { + this.charReceived = buffer.length % 3; + this.charLength = this.charReceived ? 3 : 0; +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/package.json b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/package.json new file mode 100644 index 0000000..21c9cd5 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/node_modules/string_decoder/package.json @@ -0,0 +1,53 @@ +{ + "name": "string_decoder", + "version": "0.10.31", + "description": "The string_decoder module from Node core", + "main": "index.js", + "dependencies": {}, + "devDependencies": { + "tap": "~0.4.8" + }, + "scripts": { + "test": "tap test/simple/*.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/rvagg/string_decoder.git" + }, + "homepage": "https://github.com/rvagg/string_decoder", + "keywords": [ + "string", + "decoder", + "browser", + "browserify" + ], + "license": "MIT", + "gitHead": "d46d4fd87cf1d06e031c23f1ba170ca7d4ade9a0", + "bugs": { + "url": "https://github.com/rvagg/string_decoder/issues" + }, + "_id": "string_decoder@0.10.31", + "_shasum": "62e203bc41766c6c28c9fc84301dab1c5310fa94", + "_from": "string_decoder@>=0.10.0 <0.11.0", + "_npmVersion": "1.4.23", + "_npmUser": { + "name": "rvagg", + "email": "rod@vagg.org" + }, + "maintainers": [ + { + "name": "substack", + "email": "mail@substack.net" + }, + { + "name": "rvagg", + "email": "rod@vagg.org" + } + ], + "dist": { + "shasum": "62e203bc41766c6c28c9fc84301dab1c5310fa94", + "tarball": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/package.json b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/package.json new file mode 100644 index 0000000..8cf6cc2 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/package.json @@ -0,0 +1,69 @@ +{ + "name": "readable-stream", + "version": "1.1.13", + "description": "Streams3, a user-land copy of the stream library from Node.js v0.11.x", + "main": "readable.js", + "dependencies": { + "core-util-is": "~1.0.0", + "isarray": "0.0.1", + "string_decoder": "~0.10.x", + "inherits": "~2.0.1" + }, + "devDependencies": { + "tap": "~0.2.6" + }, + "scripts": { + "test": "tap test/simple/*.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/isaacs/readable-stream" + }, + "keywords": [ + "readable", + "stream", + "pipe" + ], + "browser": { + "util": false + }, + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "license": "MIT", + "gitHead": "3b672fd7ae92acf5b4ffdbabf74b372a0a56b051", + "bugs": { + "url": "https://github.com/isaacs/readable-stream/issues" + }, + "homepage": "https://github.com/isaacs/readable-stream", + "_id": "readable-stream@1.1.13", + "_shasum": "f6eef764f514c89e2b9e23146a75ba106756d23e", + "_from": "readable-stream@>=1.1.0 <1.2.0", + "_npmVersion": "1.4.23", + "_npmUser": { + "name": "rvagg", + "email": "rod@vagg.org" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + }, + { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + }, + { + "name": "rvagg", + "email": "rod@vagg.org" + } + ], + "dist": { + "shasum": "f6eef764f514c89e2b9e23146a75ba106756d23e", + "tarball": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz" +} diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/passthrough.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/passthrough.js new file mode 100644 index 0000000..27e8d8a --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/passthrough.js @@ -0,0 +1 @@ +module.exports = require("./lib/_stream_passthrough.js") diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/readable.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/readable.js new file mode 100644 index 0000000..09b8bf5 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/readable.js @@ -0,0 +1,7 @@ +exports = module.exports = require('./lib/_stream_readable.js'); +exports.Stream = require('stream'); +exports.Readable = exports; +exports.Writable = require('./lib/_stream_writable.js'); +exports.Duplex = require('./lib/_stream_duplex.js'); +exports.Transform = require('./lib/_stream_transform.js'); +exports.PassThrough = require('./lib/_stream_passthrough.js'); diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/transform.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/transform.js new file mode 100644 index 0000000..5d482f0 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/transform.js @@ -0,0 +1 @@ +module.exports = require("./lib/_stream_transform.js") diff --git a/node_modules/multer/node_modules/busboy/node_modules/readable-stream/writable.js b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/writable.js new file mode 100644 index 0000000..e1e9efd --- /dev/null +++ b/node_modules/multer/node_modules/busboy/node_modules/readable-stream/writable.js @@ -0,0 +1 @@ +module.exports = require("./lib/_stream_writable.js") diff --git a/node_modules/multer/node_modules/busboy/package.json b/node_modules/multer/node_modules/busboy/package.json new file mode 100644 index 0000000..1d06d38 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/package.json @@ -0,0 +1,61 @@ +{ + "name": "busboy", + "version": "0.2.12", + "author": { + "name": "Brian White", + "email": "mscdex@mscdex.net" + }, + "description": "A streaming parser for HTML form data for node.js", + "main": "./lib/main", + "dependencies": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "scripts": { + "test": "node test/test.js" + }, + "engines": { + "node": ">=0.8.0" + }, + "keywords": [ + "uploads", + "forms", + "multipart", + "form-data" + ], + "licenses": [ + { + "type": "MIT", + "url": "http://github.com/mscdex/busboy/raw/master/LICENSE" + } + ], + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/mscdex/busboy.git" + }, + "bugs": { + "url": "https://github.com/mscdex/busboy/issues" + }, + "homepage": "https://github.com/mscdex/busboy#readme", + "_id": "busboy@0.2.12", + "_shasum": "bf3f080dede87c72a028a3938081f3b1adf0b3ba", + "_resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.12.tgz", + "_from": "busboy@>=0.2.9 <0.3.0", + "_npmVersion": "3.3.6", + "_nodeVersion": "5.0.0", + "_npmUser": { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + "maintainers": [ + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + } + ], + "dist": { + "shasum": "bf3f080dede87c72a028a3938081f3b1adf0b3ba", + "tarball": "http://registry.npmjs.org/busboy/-/busboy-0.2.12.tgz" + }, + "directories": {} +} diff --git a/node_modules/multer/node_modules/busboy/test/test-types-multipart.js b/node_modules/multer/node_modules/busboy/test/test-types-multipart.js new file mode 100644 index 0000000..9ce6051 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/test/test-types-multipart.js @@ -0,0 +1,336 @@ +var Busboy = require('..'); + +var path = require('path'), + inspect = require('util').inspect, + assert = require('assert'); + +var EMPTY_FN = function() {}; + +var t = 0, + group = path.basename(__filename, '.js') + '/'; +var tests = [ + { source: [ + ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="file_name_0"', + '', + 'super alpha file', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="file_name_1"', + '', + 'super beta file', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="upload_file_0"; filename="1k_a.dat"', + 'Content-Type: application/octet-stream', + '', + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="upload_file_1"; filename="1k_b.dat"', + 'Content-Type: application/octet-stream', + '', + 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--' + ].join('\r\n') + ], + boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + expected: [ + ['field', 'file_name_0', 'super alpha file', false, false, '7bit', 'text/plain'], + ['field', 'file_name_1', 'super beta file', false, false, '7bit', 'text/plain'], + ['file', 'upload_file_0', 1023, 0, '1k_a.dat', '7bit', 'application/octet-stream'], + ['file', 'upload_file_1', 1023, 0, '1k_b.dat', '7bit', 'application/octet-stream'] + ], + what: 'Fields and files' + }, + { source: [ + ['------WebKitFormBoundaryTB2MiQ36fnSJlrhY', + 'Content-Disposition: form-data; name="cont"', + '', + 'some random content', + '------WebKitFormBoundaryTB2MiQ36fnSJlrhY', + 'Content-Disposition: form-data; name="pass"', + '', + 'some random pass', + '------WebKitFormBoundaryTB2MiQ36fnSJlrhY', + 'Content-Disposition: form-data; name="bit"', + '', + '2', + '------WebKitFormBoundaryTB2MiQ36fnSJlrhY--' + ].join('\r\n') + ], + boundary: '----WebKitFormBoundaryTB2MiQ36fnSJlrhY', + expected: [ + ['field', 'cont', 'some random content', false, false, '7bit', 'text/plain'], + ['field', 'pass', 'some random pass', false, false, '7bit', 'text/plain'], + ['field', 'bit', '2', false, false, '7bit', 'text/plain'] + ], + what: 'Fields only' + }, + { source: [ + '' + ], + boundary: '----WebKitFormBoundaryTB2MiQ36fnSJlrhY', + expected: [], + what: 'No fields and no files' + }, + { source: [ + ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="file_name_0"', + '', + 'super alpha file', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="upload_file_0"; filename="1k_a.dat"', + 'Content-Type: application/octet-stream', + '', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--' + ].join('\r\n') + ], + boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + limits: { + fileSize: 13, + fieldSize: 5 + }, + expected: [ + ['field', 'file_name_0', 'super', false, true, '7bit', 'text/plain'], + ['file', 'upload_file_0', 13, 2, '1k_a.dat', '7bit', 'application/octet-stream'] + ], + what: 'Fields and files (limits)' + }, + { source: [ + ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="file_name_0"', + '', + 'super alpha file', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="upload_file_0"; filename="1k_a.dat"', + 'Content-Type: application/octet-stream', + '', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--' + ].join('\r\n') + ], + boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + limits: { + files: 0 + }, + expected: [ + ['field', 'file_name_0', 'super alpha file', false, false, '7bit', 'text/plain'] + ], + what: 'Fields and files (limits: 0 files)' + }, + { source: [ + ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="file_name_0"', + '', + 'super alpha file', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="file_name_1"', + '', + 'super beta file', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="upload_file_0"; filename="1k_a.dat"', + 'Content-Type: application/octet-stream', + '', + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="upload_file_1"; filename="1k_b.dat"', + 'Content-Type: application/octet-stream', + '', + 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--' + ].join('\r\n') + ], + boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + expected: [ + ['field', 'file_name_0', 'super alpha file', false, false, '7bit', 'text/plain'], + ['field', 'file_name_1', 'super beta file', false, false, '7bit', 'text/plain'], + ], + events: ['field'], + what: 'Fields and (ignored) files' + }, + { source: [ + ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="upload_file_0"; filename="/tmp/1k_a.dat"', + 'Content-Type: application/octet-stream', + '', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="upload_file_1"; filename="C:\\files\\1k_b.dat"', + 'Content-Type: application/octet-stream', + '', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="upload_file_2"; filename="relative/1k_c.dat"', + 'Content-Type: application/octet-stream', + '', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--' + ].join('\r\n') + ], + boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + expected: [ + ['file', 'upload_file_0', 26, 0, '1k_a.dat', '7bit', 'application/octet-stream'], + ['file', 'upload_file_1', 26, 0, '1k_b.dat', '7bit', 'application/octet-stream'], + ['file', 'upload_file_2', 26, 0, '1k_c.dat', '7bit', 'application/octet-stream'] + ], + what: 'Files with filenames containing paths' + }, + { source: [ + ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="upload_file_0"; filename="/absolute/1k_a.dat"', + 'Content-Type: application/octet-stream', + '', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="upload_file_1"; filename="C:\\absolute\\1k_b.dat"', + 'Content-Type: application/octet-stream', + '', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + 'Content-Disposition: form-data; name="upload_file_2"; filename="relative/1k_c.dat"', + 'Content-Type: application/octet-stream', + '', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--' + ].join('\r\n') + ], + boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', + preservePath: true, + expected: [ + ['file', 'upload_file_0', 26, 0, '/absolute/1k_a.dat', '7bit', 'application/octet-stream'], + ['file', 'upload_file_1', 26, 0, 'C:\\absolute\\1k_b.dat', '7bit', 'application/octet-stream'], + ['file', 'upload_file_2', 26, 0, 'relative/1k_c.dat', '7bit', 'application/octet-stream'] + ], + what: 'Paths to be preserved through the preservePath option' + }, + { source: [ + ['------WebKitFormBoundaryTB2MiQ36fnSJlrhY', + 'Content-Disposition: form-data; name="cont"', + 'Content-Type: ', + '', + 'some random content', + '------WebKitFormBoundaryTB2MiQ36fnSJlrhY', + 'Content-Disposition: ', + '', + 'some random pass', + '------WebKitFormBoundaryTB2MiQ36fnSJlrhY--' + ].join('\r\n') + ], + boundary: '----WebKitFormBoundaryTB2MiQ36fnSJlrhY', + expected: [ + ['field', 'cont', 'some random content', false, false, '7bit', 'text/plain'] + ], + what: 'Empty content-type and empty content-disposition' + }, + { source: [ + ['--asdasdasdasd\r\n', + 'Content-Type: text/plain\r\n', + 'Content-Disposition: form-data; name="foo"\r\n', + '\r\n', + 'asd\r\n', + '--asdasdasdasd--' + ].join(':)') + ], + boundary: 'asdasdasdasd', + expected: [], + shouldError: 'Unexpected end of multipart data', + what: 'Stopped mid-header' + }, + { source: [ + ['------WebKitFormBoundaryTB2MiQ36fnSJlrhY', + 'Content-Disposition: form-data; name="cont"', + 'Content-Type: application/json', + '', + '{}', + '------WebKitFormBoundaryTB2MiQ36fnSJlrhY--', + ].join('\r\n') + ], + boundary: '----WebKitFormBoundaryTB2MiQ36fnSJlrhY', + expected: [ + ['field', 'cont', '{}', false, false, '7bit', 'application/json'] + ], + what: 'content-type for fields' + } +]; + +function next() { + if (t === tests.length) + return; + + var v = tests[t]; + + var busboy = new Busboy({ + limits: v.limits, + preservePath: v.preservePath, + headers: { + 'content-type': 'multipart/form-data; boundary=' + v.boundary + } + }), + finishes = 0, + results = []; + + if (v.events === undefined || v.events.indexOf('field') > -1) { + busboy.on('field', function(key, val, keyTrunc, valTrunc, encoding, contype) { + results.push(['field', key, val, keyTrunc, valTrunc, encoding, contype]); + }); + } + if (v.events === undefined || v.events.indexOf('file') > -1) { + busboy.on('file', function(fieldname, stream, filename, encoding, mimeType) { + var nb = 0, + info = ['file', + fieldname, + nb, + 0, + filename, + encoding, + mimeType]; + results.push(info); + stream.on('data', function(d) { + nb += d.length; + }).on('limit', function() { + ++info[3]; + }).on('end', function() { + info[2] = nb; + if (stream.truncated) + ++info[3]; + }); + }); + } + busboy.on('finish', function() { + assert(finishes++ === 0, makeMsg(v.what, 'finish emitted multiple times')); + assert.deepEqual(results.length, + v.expected.length, + makeMsg(v.what, 'Parsed result count mismatch. Saw ' + + results.length + + '. Expected: ' + v.expected.length)); + + results.forEach(function(result, i) { + assert.deepEqual(result, + v.expected[i], + makeMsg(v.what, + 'Result mismatch:\nParsed: ' + inspect(result) + + '\nExpected: ' + inspect(v.expected[i])) + ); + }); + ++t; + next(); + }).on('error', function(err) { + if (!v.shouldError || v.shouldError !== err.message) + assert(false, makeMsg(v.what, 'Unexpected error: ' + err)); + }); + + v.source.forEach(function(s) { + busboy.write(new Buffer(s, 'utf8'), EMPTY_FN); + }); + busboy.end(); +} +next(); + +function makeMsg(what, msg) { + return '[' + group + what + ']: ' + msg; +} + +process.on('exit', function() { + assert(t === tests.length, + makeMsg('_exit', + 'Only finished ' + t + '/' + tests.length + ' tests')); +}); diff --git a/node_modules/multer/node_modules/busboy/test/test-types-urlencoded.js b/node_modules/multer/node_modules/busboy/test/test-types-urlencoded.js new file mode 100644 index 0000000..eac61ab --- /dev/null +++ b/node_modules/multer/node_modules/busboy/test/test-types-urlencoded.js @@ -0,0 +1,183 @@ +var Busboy = require('..'); + +var path = require('path'), + inspect = require('util').inspect, + assert = require('assert'); + +var EMPTY_FN = function() {}; + +var t = 0, + group = path.basename(__filename, '.js') + '/'; + +var tests = [ + { source: ['foo'], + expected: [['foo', '', false, false]], + what: 'Unassigned value' + }, + { source: ['foo=bar'], + expected: [['foo', 'bar', false, false]], + what: 'Assigned value' + }, + { source: ['foo&bar=baz'], + expected: [['foo', '', false, false], + ['bar', 'baz', false, false]], + what: 'Unassigned and assigned value' + }, + { source: ['foo=bar&baz'], + expected: [['foo', 'bar', false, false], + ['baz', '', false, false]], + what: 'Assigned and unassigned value' + }, + { source: ['foo=bar&baz=bla'], + expected: [['foo', 'bar', false, false], + ['baz', 'bla', false, false]], + what: 'Two assigned values' + }, + { source: ['foo&bar'], + expected: [['foo', '', false, false], + ['bar', '', false, false]], + what: 'Two unassigned values' + }, + { source: ['foo&bar&'], + expected: [['foo', '', false, false], + ['bar', '', false, false]], + what: 'Two unassigned values and ampersand' + }, + { source: ['foo=bar+baz%2Bquux'], + expected: [['foo', 'bar baz+quux', false, false]], + what: 'Assigned value with (plus) space' + }, + { source: ['foo=bar%20baz%21'], + expected: [['foo', 'bar baz!', false, false]], + what: 'Assigned value with encoded bytes' + }, + { source: ['foo%20bar=baz%20bla%21'], + expected: [['foo bar', 'baz bla!', false, false]], + what: 'Assigned value with encoded bytes #2' + }, + { source: ['foo=bar%20baz%21&num=1000'], + expected: [['foo', 'bar baz!', false, false], + ['num', '1000', false, false]], + what: 'Two assigned values, one with encoded bytes' + }, + { source: ['foo=bar&baz=bla'], + expected: [], + what: 'Limits: zero fields', + limits: { fields: 0 } + }, + { source: ['foo=bar&baz=bla'], + expected: [['foo', 'bar', false, false]], + what: 'Limits: one field', + limits: { fields: 1 } + }, + { source: ['foo=bar&baz=bla'], + expected: [['foo', 'bar', false, false], + ['baz', 'bla', false, false]], + what: 'Limits: field part lengths match limits', + limits: { fieldNameSize: 3, fieldSize: 3 } + }, + { source: ['foo=bar&baz=bla'], + expected: [['fo', 'bar', true, false], + ['ba', 'bla', true, false]], + what: 'Limits: truncated field name', + limits: { fieldNameSize: 2 } + }, + { source: ['foo=bar&baz=bla'], + expected: [['foo', 'ba', false, true], + ['baz', 'bl', false, true]], + what: 'Limits: truncated field value', + limits: { fieldSize: 2 } + }, + { source: ['foo=bar&baz=bla'], + expected: [['fo', 'ba', true, true], + ['ba', 'bl', true, true]], + what: 'Limits: truncated field name and value', + limits: { fieldNameSize: 2, fieldSize: 2 } + }, + { source: ['foo=bar&baz=bla'], + expected: [['fo', '', true, true], + ['ba', '', true, true]], + what: 'Limits: truncated field name and zero value limit', + limits: { fieldNameSize: 2, fieldSize: 0 } + }, + { source: ['foo=bar&baz=bla'], + expected: [['', '', true, true], + ['', '', true, true]], + what: 'Limits: truncated zero field name and zero value limit', + limits: { fieldNameSize: 0, fieldSize: 0 } + }, + { source: ['&'], + expected: [], + what: 'Ampersand' + }, + { source: ['&&&&&'], + expected: [], + what: 'Many ampersands' + }, + { source: ['='], + expected: [['', '', false, false]], + what: 'Assigned value, empty name and value' + }, + { source: [''], + expected: [], + what: 'Nothing' + }, +]; + +function next() { + if (t === tests.length) + return; + + var v = tests[t]; + + var busboy = new Busboy({ + limits: v.limits, + headers: { + 'content-type': 'application/x-www-form-urlencoded; charset=utf-8' + } + }), + finishes = 0, + results = []; + + busboy.on('field', function(key, val, keyTrunc, valTrunc) { + results.push([key, val, keyTrunc, valTrunc]); + }); + busboy.on('file', function() { + throw new Error(makeMsg(v.what, 'Unexpected file')); + }); + busboy.on('finish', function() { + assert(finishes++ === 0, makeMsg(v.what, 'finish emitted multiple times')); + assert.deepEqual(results.length, + v.expected.length, + makeMsg(v.what, 'Parsed result count mismatch. Saw ' + + results.length + + '. Expected: ' + v.expected.length)); + + var i = 0; + results.forEach(function(result) { + assert.deepEqual(result, + v.expected[i], + makeMsg(v.what, + 'Result mismatch:\nParsed: ' + inspect(result) + + '\nExpected: ' + inspect(v.expected[i])) + ); + ++i; + }); + ++t; + next(); + }); + + v.source.forEach(function(s) { + busboy.write(new Buffer(s, 'utf8'), EMPTY_FN); + }); + busboy.end(); +} +next(); + +function makeMsg(what, msg) { + return '[' + group + what + ']: ' + msg; +} + +process.on('exit', function() { + assert(t === tests.length, makeMsg('_exit', 'Only finished ' + t + '/' + tests.length + ' tests')); +}); diff --git a/node_modules/multer/node_modules/busboy/test/test-utils-decoder.js b/node_modules/multer/node_modules/busboy/test/test-utils-decoder.js new file mode 100644 index 0000000..780bf44 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/test/test-utils-decoder.js @@ -0,0 +1,66 @@ +var Decoder = require('../lib/utils').Decoder; + +var path = require('path'), + assert = require('assert'); + +var group = path.basename(__filename, '.js') + '/'; + +[ + { source: ['Hello world'], + expected: 'Hello world', + what: 'No encoded bytes' + }, + { source: ['Hello%20world'], + expected: 'Hello world', + what: 'One full encoded byte' + }, + { source: ['Hello%20world%21'], + expected: 'Hello world!', + what: 'Two full encoded bytes' + }, + { source: ['Hello%', '20world'], + expected: 'Hello world', + what: 'One full encoded byte split #1' + }, + { source: ['Hello%2', '0world'], + expected: 'Hello world', + what: 'One full encoded byte split #2' + }, + { source: ['Hello%20', 'world'], + expected: 'Hello world', + what: 'One full encoded byte (concat)' + }, + { source: ['Hello%2Qworld'], + expected: 'Hello%2Qworld', + what: 'Malformed encoded byte #1' + }, + { source: ['Hello%world'], + expected: 'Hello%world', + what: 'Malformed encoded byte #2' + }, + { source: ['Hello+world'], + expected: 'Hello world', + what: 'Plus to space' + }, + { source: ['Hello+world%21'], + expected: 'Hello world!', + what: 'Plus and encoded byte' + }, + { source: ['5%2B5%3D10'], + expected: '5+5=10', + what: 'Encoded plus' + }, + { source: ['5+%2B+5+%3D+10'], + expected: '5 + 5 = 10', + what: 'Spaces and encoded plus' + }, +].forEach(function(v) { + var dec = new Decoder(), result = ''; + v.source.forEach(function(s) { + result += dec.write(s); + }); + var msg = '[' + group + v.what + ']: decoded string mismatch.\n' + + 'Saw: ' + result + '\n' + + 'Expected: ' + v.expected; + assert.deepEqual(result, v.expected, msg); +}); diff --git a/node_modules/multer/node_modules/busboy/test/test-utils-parse-params.js b/node_modules/multer/node_modules/busboy/test/test-utils-parse-params.js new file mode 100644 index 0000000..c85c300 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/test/test-utils-parse-params.js @@ -0,0 +1,96 @@ +var parseParams = require('../lib/utils').parseParams; + +var path = require('path'), + assert = require('assert'), + inspect = require('util').inspect; + +var group = path.basename(__filename, '.js') + '/'; + +[ + { source: 'video/ogg', + expected: ['video/ogg'], + what: 'No parameters' + }, + { source: 'video/ogg;', + expected: ['video/ogg'], + what: 'No parameters (with separator)' + }, + { source: 'video/ogg; ', + expected: ['video/ogg'], + what: 'No parameters (with separator followed by whitespace)' + }, + { source: ';video/ogg', + expected: ['', 'video/ogg'], + what: 'Empty parameter' + }, + { source: 'video/*', + expected: ['video/*'], + what: 'Subtype with asterisk' + }, + { source: 'text/plain; encoding=utf8', + expected: ['text/plain', ['encoding', 'utf8']], + what: 'Unquoted' + }, + { source: 'text/plain; encoding=', + expected: ['text/plain', ['encoding', '']], + what: 'Unquoted empty string' + }, + { source: 'text/plain; encoding="utf8"', + expected: ['text/plain', ['encoding', 'utf8']], + what: 'Quoted' + }, + { source: 'text/plain; greeting="hello \\"world\\""', + expected: ['text/plain', ['greeting', 'hello "world"']], + what: 'Quotes within quoted' + }, + { source: 'text/plain; encoding=""', + expected: ['text/plain', ['encoding', '']], + what: 'Quoted empty string' + }, + { source: 'text/plain; encoding="utf8";\t foo=bar;test', + expected: ['text/plain', ['encoding', 'utf8'], ['foo', 'bar'], 'test'], + what: 'Multiple params with various spacing' + }, + { source: "text/plain; filename*=iso-8859-1'en'%A3%20rates", + expected: ['text/plain', ['filename', '£ rates']], + what: 'Extended parameter (RFC 5987) with language' + }, + { source: "text/plain; filename*=utf-8''%c2%a3%20and%20%e2%82%ac%20rates", + expected: ['text/plain', ['filename', '£ and € rates']], + what: 'Extended parameter (RFC 5987) without language' + }, + { source: "text/plain; filename*=utf-8''%E6%B5%8B%E8%AF%95%E6%96%87%E6%A1%A3", + expected: ['text/plain', ['filename', '测试文档']], + what: 'Extended parameter (RFC 5987) without language #2' + }, + { source: "text/plain; filename*=iso-8859-1'en'%A3%20rates; altfilename*=utf-8''%c2%a3%20and%20%e2%82%ac%20rates", + expected: ['text/plain', ['filename', '£ rates'], ['altfilename', '£ and € rates']], + what: 'Multiple extended parameters (RFC 5987) with mixed charsets' + }, + { source: "text/plain; filename*=iso-8859-1'en'%A3%20rates; altfilename=\"foobarbaz\"", + expected: ['text/plain', ['filename', '£ rates'], ['altfilename', 'foobarbaz']], + what: 'Mixed regular and extended parameters (RFC 5987)' + }, + { source: "text/plain; filename=\"foobarbaz\"; altfilename*=iso-8859-1'en'%A3%20rates", + expected: ['text/plain', ['filename', 'foobarbaz'], ['altfilename', '£ rates']], + what: 'Mixed regular and extended parameters (RFC 5987) #2' + }, + { source: 'text/plain; filename="C:\\folder\\test.png"', + expected: ['text/plain', ['filename', 'C:\\folder\\test.png']], + what: 'Unescaped backslashes should be considered backslashes' + }, + { source: 'text/plain; filename="John \\"Magic\\" Smith.png"', + expected: ['text/plain', ['filename', 'John "Magic" Smith.png']], + what: 'Escaped double-quotes should be considered double-quotes' + }, + { source: 'multipart/form-data; charset=utf-8; boundary=0xKhTmLbOuNdArY', + expected: ['multipart/form-data', ['charset', 'utf-8'], ['boundary', '0xKhTmLbOuNdArY']], + what: 'Multiple non-quoted parameters' + }, +].forEach(function(v) { + var result = parseParams(v.source), + msg = '[' + group + v.what + ']: parsed parameters mismatch.\n' + + 'Saw: ' + inspect(result) + '\n' + + 'Expected: ' + inspect(v.expected); + assert.deepEqual(result, v.expected, msg); +}); diff --git a/node_modules/multer/node_modules/busboy/test/test.js b/node_modules/multer/node_modules/busboy/test/test.js new file mode 100644 index 0000000..3383f27 --- /dev/null +++ b/node_modules/multer/node_modules/busboy/test/test.js @@ -0,0 +1,4 @@ +require('fs').readdirSync(__dirname).forEach(function(f) { + if (f.substr(0, 5) === 'test-') + require('./' + f); +}); \ No newline at end of file diff --git a/node_modules/multer/node_modules/mkdirp/.npmignore b/node_modules/multer/node_modules/mkdirp/.npmignore new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/node_modules/multer/node_modules/mkdirp/.travis.yml b/node_modules/multer/node_modules/mkdirp/.travis.yml new file mode 100644 index 0000000..84fd7ca --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.6 + - 0.8 + - 0.9 diff --git a/node_modules/multer/node_modules/mkdirp/LICENSE b/node_modules/multer/node_modules/mkdirp/LICENSE new file mode 100644 index 0000000..432d1ae --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/LICENSE @@ -0,0 +1,21 @@ +Copyright 2010 James Halliday (mail@substack.net) + +This project is free software released under the MIT/X11 license: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/multer/node_modules/mkdirp/examples/pow.js b/node_modules/multer/node_modules/mkdirp/examples/pow.js new file mode 100644 index 0000000..e692421 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/examples/pow.js @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/node_modules/multer/node_modules/mkdirp/index.js b/node_modules/multer/node_modules/mkdirp/index.js new file mode 100644 index 0000000..fda6de8 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/index.js @@ -0,0 +1,82 @@ +var path = require('path'); +var fs = require('fs'); + +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; + +function mkdirP (p, mode, f, made) { + if (typeof mode === 'function' || mode === undefined) { + f = mode; + mode = 0777 & (~process.umask()); + } + if (!made) made = null; + + var cb = f || function () {}; + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + fs.mkdir(p, mode, function (er) { + if (!er) { + made = made || p; + return cb(null, made); + } + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), mode, function (er, made) { + if (er) cb(er, made); + else mkdirP(p, mode, cb, made); + }); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + fs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original error be the failure reason. + if (er2 || !stat.isDirectory()) cb(er, made) + else cb(null, made); + }); + break; + } + }); +} + +mkdirP.sync = function sync (p, mode, made) { + if (mode === undefined) { + mode = 0777 & (~process.umask()); + } + if (!made) made = null; + + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + try { + fs.mkdirSync(p, mode); + made = made || p; + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + made = sync(path.dirname(p), mode, made); + sync(p, mode, made); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + var stat; + try { + stat = fs.statSync(p); + } + catch (err1) { + throw err0; + } + if (!stat.isDirectory()) throw err0; + break; + } + } + + return made; +}; diff --git a/node_modules/multer/node_modules/mkdirp/package.json b/node_modules/multer/node_modules/mkdirp/package.json new file mode 100644 index 0000000..cc7b329 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/package.json @@ -0,0 +1,46 @@ +{ + "name": "mkdirp", + "description": "Recursively mkdir, like `mkdir -p`", + "version": "0.3.5", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "main": "./index", + "keywords": [ + "mkdir", + "directory" + ], + "repository": { + "type": "git", + "url": "http://github.com/substack/node-mkdirp.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "devDependencies": { + "tap": "~0.4.0" + }, + "license": "MIT", + "_id": "mkdirp@0.3.5", + "dist": { + "shasum": "de3e5f8961c88c787ee1368df849ac4413eca8d7", + "tarball": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + }, + "_from": "mkdirp@>=0.3.5 <0.4.0", + "_npmVersion": "1.2.2", + "_npmUser": { + "name": "substack", + "email": "mail@substack.net" + }, + "maintainers": [ + { + "name": "substack", + "email": "mail@substack.net" + } + ], + "directories": {}, + "_shasum": "de3e5f8961c88c787ee1368df849ac4413eca8d7", + "_resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" +} diff --git a/node_modules/multer/node_modules/mkdirp/readme.markdown b/node_modules/multer/node_modules/mkdirp/readme.markdown new file mode 100644 index 0000000..83b0216 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/readme.markdown @@ -0,0 +1,63 @@ +# mkdirp + +Like `mkdir -p`, but in node.js! + +[![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp) + +# example + +## pow.js + +```js +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); +``` + +Output + +``` +pow! +``` + +And now /tmp/foo/bar/baz exists, huzzah! + +# methods + +```js +var mkdirp = require('mkdirp'); +``` + +## mkdirp(dir, mode, cb) + +Create a new directory and any necessary subdirectories at `dir` with octal +permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +`cb(err, made)` fires with the error or the first directory `made` +that had to be created, if any. + +## mkdirp.sync(dir, mode) + +Synchronously create a new directory and any necessary subdirectories at `dir` +with octal permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +Returns the first directory that had to be created, if any. + +# install + +With [npm](http://npmjs.org) do: + +``` +npm install mkdirp +``` + +# license + +MIT diff --git a/node_modules/multer/node_modules/mkdirp/test/chmod.js b/node_modules/multer/node_modules/mkdirp/test/chmod.js new file mode 100644 index 0000000..520dcb8 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/test/chmod.js @@ -0,0 +1,38 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +test('chmod-pre', function (t) { + var mode = 0744 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.equal(stat && stat.mode & 0777, mode, 'should be 0744'); + t.end(); + }); + }); +}); + +test('chmod', function (t) { + var mode = 0755 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.end(); + }); + }); +}); diff --git a/node_modules/multer/node_modules/mkdirp/test/clobber.js b/node_modules/multer/node_modules/mkdirp/test/clobber.js new file mode 100644 index 0000000..0eb7099 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/test/clobber.js @@ -0,0 +1,37 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +// a file in the way +var itw = ps.slice(0, 3).join('/'); + + +test('clobber-pre', function (t) { + console.error("about to write to "+itw) + fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.'); + + fs.stat(itw, function (er, stat) { + t.ifError(er) + t.ok(stat && stat.isFile(), 'should be file') + t.end() + }) +}) + +test('clobber', function (t) { + t.plan(2); + mkdirp(file, 0755, function (err) { + t.ok(err); + t.equal(err.code, 'ENOTDIR'); + t.end(); + }); +}); diff --git a/node_modules/multer/node_modules/mkdirp/test/mkdirp.js b/node_modules/multer/node_modules/mkdirp/test/mkdirp.js new file mode 100644 index 0000000..b07cd70 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/test/mkdirp.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('woo', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/node_modules/multer/node_modules/mkdirp/test/perm.js b/node_modules/multer/node_modules/mkdirp/test/perm.js new file mode 100644 index 0000000..23a7abb --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/test/perm.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('async perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); + +test('async root perm', function (t) { + mkdirp('/tmp', 0755, function (err) { + if (err) t.fail(err); + t.end(); + }); + t.end(); +}); diff --git a/node_modules/multer/node_modules/mkdirp/test/perm_sync.js b/node_modules/multer/node_modules/mkdirp/test/perm_sync.js new file mode 100644 index 0000000..f685f60 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/test/perm_sync.js @@ -0,0 +1,39 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json'; + + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); + +test('sync root perm', function (t) { + t.plan(1); + + var file = '/tmp'; + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); diff --git a/node_modules/multer/node_modules/mkdirp/test/race.js b/node_modules/multer/node_modules/mkdirp/test/race.js new file mode 100644 index 0000000..96a0447 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/test/race.js @@ -0,0 +1,41 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('race', function (t) { + t.plan(4); + var ps = [ '', 'tmp' ]; + + for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); + } + var file = ps.join('/'); + + var res = 2; + mk(file, function () { + if (--res === 0) t.end(); + }); + + mk(file, function () { + if (--res === 0) t.end(); + }); + + function mk (file, cb) { + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + if (cb) cb(); + } + }) + }) + }); + } +}); diff --git a/node_modules/multer/node_modules/mkdirp/test/rel.js b/node_modules/multer/node_modules/mkdirp/test/rel.js new file mode 100644 index 0000000..7985824 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/test/rel.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('rel', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var cwd = process.cwd(); + process.chdir('/tmp'); + + var file = [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + process.chdir(cwd); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/node_modules/multer/node_modules/mkdirp/test/return.js b/node_modules/multer/node_modules/mkdirp/test/return.js new file mode 100644 index 0000000..bce68e5 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/test/return.js @@ -0,0 +1,25 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(4); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, '/tmp/' + x); + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, null); + }); + }); +}); diff --git a/node_modules/multer/node_modules/mkdirp/test/return_sync.js b/node_modules/multer/node_modules/mkdirp/test/return_sync.js new file mode 100644 index 0000000..7c222d3 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/test/return_sync.js @@ -0,0 +1,24 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + // Note that this will throw on failure, which will fail the test. + var made = mkdirp.sync(file); + t.equal(made, '/tmp/' + x); + + // making the same file again should have no effect. + made = mkdirp.sync(file); + t.equal(made, null); +}); diff --git a/node_modules/multer/node_modules/mkdirp/test/root.js b/node_modules/multer/node_modules/mkdirp/test/root.js new file mode 100644 index 0000000..97ad7a2 --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/test/root.js @@ -0,0 +1,18 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('root', function (t) { + // '/' on unix, 'c:/' on windows. + var file = path.resolve('/'); + + mkdirp(file, 0755, function (err) { + if (err) throw err + fs.stat(file, function (er, stat) { + if (er) throw er + t.ok(stat.isDirectory(), 'target is a directory'); + t.end(); + }) + }); +}); diff --git a/node_modules/multer/node_modules/mkdirp/test/sync.js b/node_modules/multer/node_modules/mkdirp/test/sync.js new file mode 100644 index 0000000..7530cad --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/test/sync.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file, 0755); + } catch (err) { + t.fail(err); + return t.end(); + } + + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }); + }); +}); diff --git a/node_modules/multer/node_modules/mkdirp/test/umask.js b/node_modules/multer/node_modules/mkdirp/test/umask.js new file mode 100644 index 0000000..64ccafe --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/test/umask.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('implicit mode from umask', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0777 & (~process.umask())); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/node_modules/multer/node_modules/mkdirp/test/umask_sync.js b/node_modules/multer/node_modules/mkdirp/test/umask_sync.js new file mode 100644 index 0000000..35bd5cb --- /dev/null +++ b/node_modules/multer/node_modules/mkdirp/test/umask_sync.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('umask sync modes', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file); + } catch (err) { + t.fail(err); + return t.end(); + } + + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, (0777 & (~process.umask()))); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }); + }); +}); diff --git a/node_modules/multer/node_modules/qs/.jshintignore b/node_modules/multer/node_modules/qs/.jshintignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/node_modules/multer/node_modules/qs/.jshintignore @@ -0,0 +1 @@ +node_modules diff --git a/node_modules/multer/node_modules/qs/.jshintrc b/node_modules/multer/node_modules/qs/.jshintrc new file mode 100644 index 0000000..997b3f7 --- /dev/null +++ b/node_modules/multer/node_modules/qs/.jshintrc @@ -0,0 +1,10 @@ +{ + "node": true, + + "curly": true, + "latedef": true, + "quotmark": true, + "undef": true, + "unused": true, + "trailing": true +} diff --git a/node_modules/multer/node_modules/qs/.npmignore b/node_modules/multer/node_modules/qs/.npmignore new file mode 100644 index 0000000..7e1574d --- /dev/null +++ b/node_modules/multer/node_modules/qs/.npmignore @@ -0,0 +1,18 @@ +.idea +*.iml +npm-debug.log +dump.rdb +node_modules +results.tap +results.xml +npm-shrinkwrap.json +config.json +.DS_Store +*/.DS_Store +*/*/.DS_Store +._* +*/._* +*/*/._* +coverage.* +lib-cov +complexity.md diff --git a/node_modules/multer/node_modules/qs/.travis.yml b/node_modules/multer/node_modules/qs/.travis.yml new file mode 100644 index 0000000..c891dd0 --- /dev/null +++ b/node_modules/multer/node_modules/qs/.travis.yml @@ -0,0 +1,4 @@ +language: node_js + +node_js: + - 0.10 \ No newline at end of file diff --git a/node_modules/multer/node_modules/qs/CONTRIBUTING.md b/node_modules/multer/node_modules/qs/CONTRIBUTING.md new file mode 100644 index 0000000..8928361 --- /dev/null +++ b/node_modules/multer/node_modules/qs/CONTRIBUTING.md @@ -0,0 +1 @@ +Please view our [hapijs contributing guide](https://github.com/hapijs/hapi/blob/master/CONTRIBUTING.md). diff --git a/node_modules/multer/node_modules/qs/LICENSE b/node_modules/multer/node_modules/qs/LICENSE new file mode 100755 index 0000000..d456948 --- /dev/null +++ b/node_modules/multer/node_modules/qs/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2014 Nathan LaFreniere and other contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of any contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * * * + +The complete list of contributors can be found at: https://github.com/hapijs/qs/graphs/contributors diff --git a/node_modules/multer/node_modules/qs/Makefile b/node_modules/multer/node_modules/qs/Makefile new file mode 100644 index 0000000..600a700 --- /dev/null +++ b/node_modules/multer/node_modules/qs/Makefile @@ -0,0 +1,8 @@ +test: + @node node_modules/lab/bin/lab +test-cov: + @node node_modules/lab/bin/lab -t 100 +test-cov-html: + @node node_modules/lab/bin/lab -r html -o coverage.html + +.PHONY: test test-cov test-cov-html \ No newline at end of file diff --git a/node_modules/multer/node_modules/qs/README.md b/node_modules/multer/node_modules/qs/README.md new file mode 100755 index 0000000..b861887 --- /dev/null +++ b/node_modules/multer/node_modules/qs/README.md @@ -0,0 +1,192 @@ +# qs + +A querystring parsing and stringifying library with some added security. + +[![Build Status](https://secure.travis-ci.org/hapijs/qs.svg)](http://travis-ci.org/hapijs/qs) + +Lead Maintainer: [Nathan LaFreniere](https://github.com/nlf) + +The **qs** module was originally created and maintained by [TJ Holowaychuk](https://github.com/visionmedia/node-querystring). + +## Usage + +```javascript +var Qs = require('qs'); + +var obj = Qs.parse('a=c'); // { a: 'c' } +var str = Qs.stringify(obj); // 'a=c' +``` + +### Parsing Objects + +```javascript +Qs.parse(string, [depth], [delimiter]); +``` + +**qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`. +For example, the string `'foo[bar]=baz'` converts to: + +```javascript +{ + foo: { + bar: 'baz' + } +} +``` + +URI encoded strings work too: + +```javascript +Qs.parse('a%5Bb%5D=c'); +// { a: { b: 'c' } } +``` + +You can also nest your objects, like `'foo[bar][baz]=foobarbaz'`: + +```javascript +{ + foo: { + bar: { + baz: 'foobarbaz' + } + } +} +``` + +By default, when nesting objects **qs** will only parse up to 5 children deep. This means if you attempt to parse a string like +`'a[b][c][d][e][f][g][h][i]=j'` your resulting object will be: + +```javascript +{ + a: { + b: { + c: { + d: { + e: { + f: { + '[g][h][i]': 'j' + } + } + } + } + } + } +} +``` + +This depth can be overridden by passing a `depth` option to `Qs.parse(string, depth)`: + +```javascript +Qs.parse('a[b][c][d][e][f][g][h][i]=j', 1); +// { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } } +``` + +The depth limit mitigate abuse when **qs** is used to parse user input, and it is recommended to keep it a reasonably small number. + +An optional delimiter can also be passed: + +```javascript +Qs.parse('a=b;c=d', ';'); +// { a: 'b', c: 'd' } +``` + +### Parsing Arrays + +**qs** can also parse arrays using a similar `[]` notation: + +```javascript +Qs.parse('a[]=b&a[]=c'); +// { a: ['b', 'c'] } +``` + +You may specify an index as well: + +```javascript +Qs.parse('a[1]=c&a[0]=b'); +// { a: ['b', 'c'] } +``` + +Note that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number +to create an array. When creating arrays with specific indices, **qs** will compact a sparse array to only the existing values preserving +their order: + +```javascript +Qs.parse('a[1]=b&a[15]=c'); +// { a: ['b', 'c'] } +``` + +Note that an empty string is also a value, and will be preserved: + +```javascript +Qs.parse('a[]=&a[]=b'); +// { a: ['', 'b'] } +Qs.parse('a[0]=b&a[1]=&a[2]=c'); +// { a: ['b', '', 'c'] } +``` + +**qs** will also limit specifying indices in an array to a maximum index of `20`. Any array members with an index of greater than `20` will +instead be converted to an object with the index as the key: + +```javascript +Qs.parse('a[100]=b'); +// { a: { '100': 'b' } } +``` + +If you mix notations, **qs** will merge the two items into an object: + +```javascript +Qs.parse('a[0]=b&a[b]=c'); +// { a: { '0': 'b', b: 'c' } } +``` + +You can also create arrays of objects: + +```javascript +Qs.parse('a[][b]=c'); +// { a: [{ b: 'c' }] } +``` + +### Stringifying + +```javascript +Qs.stringify(object, [delimiter]); +``` + +When stringifying, **qs** always URI encodes output. Objects are stringified as you would expect: + +```javascript +Qs.stringify({ a: 'b' }); +// 'a=b' +Qs.stringify({ a: { b: 'c' } }); +// 'a%5Bb%5D=c' +``` + +Examples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases *will* be URI encoded during real usage. + +When arrays are stringified, they are always given explicit indices: + +```javascript +Qs.stringify({ a: ['b', 'c', 'd'] }); +// 'a[0]=b&a[1]=c&a[2]=d' +``` + +Empty strings and null values will omit the value, but the equals sign (=) remains in place: + +```javascript +Qs.stringify({ a: '' }); +// 'a=' +``` + +Properties that are set to `undefined` will be omitted entirely: + +```javascript +Qs.stringify({ a: null, b: undefined }); +// 'a=' +``` + +The delimiter may be overridden with stringify as well: + +```javascript +Qs.stringify({ a: 'b', c: 'd' }, ';'); +// 'a=b;c=d' +``` diff --git a/node_modules/multer/node_modules/qs/index.js b/node_modules/multer/node_modules/qs/index.js new file mode 100644 index 0000000..bb0a047 --- /dev/null +++ b/node_modules/multer/node_modules/qs/index.js @@ -0,0 +1 @@ +module.exports = require('./lib'); diff --git a/node_modules/multer/node_modules/qs/lib/index.js b/node_modules/multer/node_modules/qs/lib/index.js new file mode 100755 index 0000000..0e09493 --- /dev/null +++ b/node_modules/multer/node_modules/qs/lib/index.js @@ -0,0 +1,15 @@ +// Load modules + +var Stringify = require('./stringify'); +var Parse = require('./parse'); + + +// Declare internals + +var internals = {}; + + +module.exports = { + stringify: Stringify, + parse: Parse +}; diff --git a/node_modules/multer/node_modules/qs/lib/parse.js b/node_modules/multer/node_modules/qs/lib/parse.js new file mode 100755 index 0000000..4a3fdd9 --- /dev/null +++ b/node_modules/multer/node_modules/qs/lib/parse.js @@ -0,0 +1,155 @@ +// Load modules + +var Utils = require('./utils'); + + +// Declare internals + +var internals = { + delimiter: '&', + depth: 5, + arrayLimit: 20, + parametersLimit: 1000 +}; + + +internals.parseValues = function (str, delimiter) { + + delimiter = typeof delimiter === 'string' ? delimiter : internals.delimiter; + + var obj = {}; + var parts = str.split(delimiter, internals.parametersLimit); + + for (var i = 0, il = parts.length; i < il; ++i) { + var part = parts[i]; + var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1; + + if (pos === -1) { + obj[Utils.decode(part)] = ''; + } + else { + var key = Utils.decode(part.slice(0, pos)); + var val = Utils.decode(part.slice(pos + 1)); + + if (!obj[key]) { + obj[key] = val; + } + else { + obj[key] = [].concat(obj[key]).concat(val); + } + } + } + + return obj; +}; + + +internals.parseObject = function (chain, val) { + + if (!chain.length) { + return val; + } + + var root = chain.shift(); + + var obj = {}; + if (root === '[]') { + obj = []; + obj = obj.concat(internals.parseObject(chain, val)); + } + else { + var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root; + var index = parseInt(cleanRoot, 10); + if (!isNaN(index) && + root !== cleanRoot && + index <= internals.arrayLimit) { + + obj = []; + obj[index] = internals.parseObject(chain, val); + } + else { + obj[cleanRoot] = internals.parseObject(chain, val); + } + } + + return obj; +}; + + +internals.parseKeys = function (key, val, depth) { + + if (!key) { + return; + } + + // The regex chunks + + var parent = /^([^\[\]]*)/; + var child = /(\[[^\[\]]*\])/g; + + // Get the parent + + var segment = parent.exec(key); + + // Don't allow them to overwrite object prototype properties + + if (Object.prototype.hasOwnProperty(segment[1])) { + return; + } + + // Stash the parent if it exists + + var keys = []; + if (segment[1]) { + keys.push(segment[1]); + } + + // Loop through children appending to the array until we hit depth + + var i = 0; + while ((segment = child.exec(key)) !== null && i < depth) { + + ++i; + if (!Object.prototype.hasOwnProperty(segment[1].replace(/\[|\]/g, ''))) { + keys.push(segment[1]); + } + } + + // If there's a remainder, just add whatever is left + + if (segment) { + keys.push('[' + key.slice(segment.index) + ']'); + } + + return internals.parseObject(keys, val); +}; + + +module.exports = function (str, depth, delimiter) { + + if (str === '' || + str === null || + typeof str === 'undefined') { + + return {}; + } + + if (typeof depth !== 'number') { + delimiter = depth; + depth = internals.depth; + } + + var tempObj = typeof str === 'string' ? internals.parseValues(str, delimiter) : Utils.clone(str); + var obj = {}; + + // Iterate over the keys and setup the new object + // + for (var key in tempObj) { + if (tempObj.hasOwnProperty(key)) { + var newObj = internals.parseKeys(key, tempObj[key], depth); + obj = Utils.merge(obj, newObj); + } + } + + return Utils.compact(obj); +}; diff --git a/node_modules/multer/node_modules/qs/lib/stringify.js b/node_modules/multer/node_modules/qs/lib/stringify.js new file mode 100755 index 0000000..1cc3df9 --- /dev/null +++ b/node_modules/multer/node_modules/qs/lib/stringify.js @@ -0,0 +1,55 @@ +// Load modules + + +// Declare internals + +var internals = { + delimiter: '&' +}; + + +internals.stringify = function (obj, prefix) { + + if (Buffer.isBuffer(obj)) { + obj = obj.toString(); + } + else if (obj instanceof Date) { + obj = obj.toISOString(); + } + else if (obj === null) { + obj = ''; + } + + if (typeof obj === 'string' || + typeof obj === 'number' || + typeof obj === 'boolean') { + + return [encodeURIComponent(prefix) + '=' + encodeURIComponent(obj)]; + } + + var values = []; + + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']')); + } + } + + return values; +}; + + +module.exports = function (obj, delimiter) { + + delimiter = typeof delimiter === 'undefined' ? internals.delimiter : delimiter; + + var keys = []; + + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + keys = keys.concat(internals.stringify(obj[key], key)); + } + } + + return keys.join(delimiter); +}; diff --git a/node_modules/multer/node_modules/qs/lib/utils.js b/node_modules/multer/node_modules/qs/lib/utils.js new file mode 100755 index 0000000..3f5c149 --- /dev/null +++ b/node_modules/multer/node_modules/qs/lib/utils.js @@ -0,0 +1,133 @@ +// Load modules + + +// Declare internals + +var internals = {}; + + +exports.arrayToObject = function (source) { + + var obj = {}; + for (var i = 0, il = source.length; i < il; ++i) { + if (typeof source[i] !== 'undefined') { + + obj[i] = source[i]; + } + } + + return obj; +}; + + +exports.clone = function (source) { + + if (typeof source !== 'object' || + source === null) { + + return source; + } + + if (Buffer.isBuffer(source)) { + return source.toString(); + } + + var obj = Array.isArray(source) ? [] : {}; + for (var i in source) { + if (source.hasOwnProperty(i)) { + obj[i] = exports.clone(source[i]); + } + } + + return obj; +}; + + +exports.merge = function (target, source) { + + if (!source) { + return target; + } + + var obj = exports.clone(target); + + if (Array.isArray(source)) { + for (var i = 0, il = source.length; i < il; ++i) { + if (typeof source[i] !== 'undefined') { + if (typeof obj[i] === 'object') { + obj[i] = exports.merge(obj[i], source[i]); + } + else { + obj[i] = source[i]; + } + } + } + + return obj; + } + + if (Array.isArray(obj)) { + obj = exports.arrayToObject(obj); + } + + var keys = Object.keys(source); + for (var k = 0, kl = keys.length; k < kl; ++k) { + var key = keys[k]; + var value = source[key]; + + if (value && + typeof value === 'object') { + + if (!obj[key]) { + obj[key] = exports.clone(value); + } + else { + obj[key] = exports.merge(obj[key], value); + } + } + else { + obj[key] = value; + } + } + + return obj; +}; + + +exports.decode = function (str) { + + try { + return decodeURIComponent(str.replace(/\+/g, ' ')); + } catch (e) { + return str; + } +}; + + +exports.compact = function (obj) { + + if (typeof obj !== 'object' || obj === null) { + return obj; + } + + var compacted = {}; + + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + if (Array.isArray(obj[key])) { + compacted[key] = []; + + for (var i = 0, l = obj[key].length; i < l; i++) { + if (typeof obj[key][i] !== 'undefined') { + compacted[key].push(obj[key][i]); + } + } + } + else { + compacted[key] = exports.compact(obj[key]); + } + } + } + + return compacted; +}; diff --git a/node_modules/multer/node_modules/qs/package.json b/node_modules/multer/node_modules/qs/package.json new file mode 100644 index 0000000..579dd22 --- /dev/null +++ b/node_modules/multer/node_modules/qs/package.json @@ -0,0 +1,60 @@ +{ + "name": "qs", + "version": "1.2.2", + "description": "A querystring parser that supports nesting and arrays, with a depth limit", + "homepage": "https://github.com/hapijs/qs", + "main": "index.js", + "dependencies": {}, + "devDependencies": { + "lab": "3.x.x" + }, + "scripts": { + "test": "make test-cov" + }, + "repository": { + "type": "git", + "url": "https://github.com/hapijs/qs.git" + }, + "keywords": [ + "querystring", + "qs" + ], + "author": { + "name": "Nathan LaFreniere", + "email": "quitlahok@gmail.com" + }, + "licenses": [ + { + "type": "BSD", + "url": "http://github.com/hapijs/qs/raw/master/LICENSE" + } + ], + "gitHead": "bd9455fea88d1c51a80dbf57ef0f99b4e553177d", + "bugs": { + "url": "https://github.com/hapijs/qs/issues" + }, + "_id": "qs@1.2.2", + "_shasum": "19b57ff24dc2a99ce1f8bdf6afcda59f8ef61f88", + "_from": "qs@>=1.2.2 <1.3.0", + "_npmVersion": "1.4.21", + "_npmUser": { + "name": "hueniverse", + "email": "eran@hueniverse.com" + }, + "maintainers": [ + { + "name": "nlf", + "email": "quitlahok@gmail.com" + }, + { + "name": "hueniverse", + "email": "eran@hueniverse.com" + } + ], + "dist": { + "shasum": "19b57ff24dc2a99ce1f8bdf6afcda59f8ef61f88", + "tarball": "http://registry.npmjs.org/qs/-/qs-1.2.2.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/qs/-/qs-1.2.2.tgz" +} diff --git a/node_modules/multer/node_modules/qs/test/parse.js b/node_modules/multer/node_modules/qs/test/parse.js new file mode 100755 index 0000000..c00e7be --- /dev/null +++ b/node_modules/multer/node_modules/qs/test/parse.js @@ -0,0 +1,301 @@ +// Load modules + +var Lab = require('lab'); +var Qs = require('../'); + + +// Declare internals + +var internals = {}; + + +// Test shortcuts + +var expect = Lab.expect; +var before = Lab.before; +var after = Lab.after; +var describe = Lab.experiment; +var it = Lab.test; + + +describe('#parse', function () { + + it('parses a simple string', function (done) { + + expect(Qs.parse('0=foo')).to.deep.equal({ '0': 'foo' }); + expect(Qs.parse('foo=c++')).to.deep.equal({ foo: 'c ' }); + expect(Qs.parse('a[>=]=23')).to.deep.equal({ a: { '>=': '23' } }); + expect(Qs.parse('a[<=>]==23')).to.deep.equal({ a: { '<=>': '=23' } }); + expect(Qs.parse('a[==]=23')).to.deep.equal({ a: { '==': '23' } }); + expect(Qs.parse('foo')).to.deep.equal({ foo: '' }); + expect(Qs.parse('foo=bar')).to.deep.equal({ foo: 'bar' }); + expect(Qs.parse(' foo = bar = baz ')).to.deep.equal({ ' foo ': ' bar = baz ' }); + expect(Qs.parse('foo=bar=baz')).to.deep.equal({ foo: 'bar=baz' }); + expect(Qs.parse('foo=bar&bar=baz')).to.deep.equal({ foo: 'bar', bar: 'baz' }); + expect(Qs.parse('foo=bar&baz')).to.deep.equal({ foo: 'bar', baz: '' }); + expect(Qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World')).to.deep.equal({ + cht: 'p3', + chd: 't:60,40', + chs: '250x100', + chl: 'Hello|World' + }); + done(); + }); + + it('parses a single nested string', function (done) { + + expect(Qs.parse('a[b]=c')).to.deep.equal({ a: { b: 'c' } }); + done(); + }); + + it('parses a double nested string', function (done) { + + expect(Qs.parse('a[b][c]=d')).to.deep.equal({ a: { b: { c: 'd' } } }); + done(); + }); + + it('defaults to a depth of 5', function (done) { + + expect(Qs.parse('a[b][c][d][e][f][g][h]=i')).to.deep.equal({ a: { b: { c: { d: { e: { f: { '[g][h]': 'i' } } } } } } }); + done(); + }); + + it('only parses one level when depth = 1', function (done) { + + expect(Qs.parse('a[b][c]=d', 1)).to.deep.equal({ a: { b: { '[c]': 'd' } } }); + expect(Qs.parse('a[b][c][d]=e', 1)).to.deep.equal({ a: { b: { '[c][d]': 'e' } } }); + done(); + }); + + it('parses a simple array', function (done) { + + expect(Qs.parse('a=b&a=c')).to.deep.equal({ a: ['b', 'c'] }); + done(); + }); + + it('parses an explicit array', function (done) { + + expect(Qs.parse('a[]=b')).to.deep.equal({ a: ['b'] }); + expect(Qs.parse('a[]=b&a[]=c')).to.deep.equal({ a: ['b', 'c'] }); + expect(Qs.parse('a[]=b&a[]=c&a[]=d')).to.deep.equal({ a: ['b', 'c', 'd'] }); + done(); + }); + + it('parses a nested array', function (done) { + + expect(Qs.parse('a[b][]=c&a[b][]=d')).to.deep.equal({ a: { b: ['c', 'd'] } }); + expect(Qs.parse('a[>=]=25')).to.deep.equal({ a: { '>=': '25' } }); + done(); + }); + + it('allows to specify array indices', function (done) { + + expect(Qs.parse('a[1]=c&a[0]=b&a[2]=d')).to.deep.equal({ a: ['b', 'c', 'd'] }); + expect(Qs.parse('a[1]=c&a[0]=b')).to.deep.equal({ a: ['b', 'c'] }); + expect(Qs.parse('a[1]=c')).to.deep.equal({ a: ['c'] }); + done(); + }); + + it('limits specific array indices to 20', function (done) { + + expect(Qs.parse('a[20]=a')).to.deep.equal({ a: ['a'] }); + expect(Qs.parse('a[21]=a')).to.deep.equal({ a: { '21': 'a' } }); + done(); + }); + + it('supports encoded = signs', function (done) { + + expect(Qs.parse('he%3Dllo=th%3Dere')).to.deep.equal({ 'he=llo': 'th=ere' }); + done(); + }); + + it('is ok with url encoded strings', function (done) { + + expect(Qs.parse('a[b%20c]=d')).to.deep.equal({ a: { 'b c': 'd' } }); + expect(Qs.parse('a[b]=c%20d')).to.deep.equal({ a: { b: 'c d' } }); + done(); + }); + + it('allows brackets in the value', function (done) { + + expect(Qs.parse('pets=["tobi"]')).to.deep.equal({ pets: '["tobi"]' }); + expect(Qs.parse('operators=[">=", "<="]')).to.deep.equal({ operators: '[">=", "<="]' }); + done(); + }); + + it('allows empty values', function (done) { + + expect(Qs.parse('')).to.deep.equal({}); + expect(Qs.parse(null)).to.deep.equal({}); + expect(Qs.parse(undefined)).to.deep.equal({}); + done(); + }); + + it('transforms arrays to objects', function (done) { + + expect(Qs.parse('foo[0]=bar&foo[bad]=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } }); + expect(Qs.parse('foo[bad]=baz&foo[0]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } }); + expect(Qs.parse('foo[bad]=baz&foo[]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } }); + expect(Qs.parse('foo[]=bar&foo[bad]=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } }); + expect(Qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } }); + expect(Qs.parse('foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb')).to.deep.equal({foo: [ {a: 'a', b: 'b'}, {a: 'aa', b: 'bb'} ]}); + done(); + }); + + it('correctly prunes undefined values when converting an array to an object', function (done) { + + expect(Qs.parse('a[2]=b&a[99999999]=c')).to.deep.equal({ a: { '2': 'b', '99999999': 'c' } }); + done(); + }); + + it('supports malformed uri characters', function (done) { + + expect(Qs.parse('{%:%}')).to.deep.equal({ '{%:%}': '' }); + expect(Qs.parse('foo=%:%}')).to.deep.equal({ foo: '%:%}' }); + done(); + }); + + it('doesn\'t produce empty keys', function (done) { + + expect(Qs.parse('_r=1&')).to.deep.equal({ '_r': '1' }); + done(); + }); + + it('cannot override prototypes', function (done) { + + var obj = Qs.parse('toString=bad&bad[toString]=bad&constructor=bad'); + expect(typeof obj.toString).to.equal('function'); + expect(typeof obj.bad.toString).to.equal('function'); + expect(typeof obj.constructor).to.equal('function'); + done(); + }); + + it('cannot access Object prototype', function (done) { + + Qs.parse('constructor[prototype][bad]=bad'); + Qs.parse('bad[constructor][prototype][bad]=bad'); + expect(typeof Object.prototype.bad).to.equal('undefined'); + done(); + }); + + it('parses arrays of objects', function (done) { + + expect(Qs.parse('a[][b]=c')).to.deep.equal({ a: [{ b: 'c' }] }); + expect(Qs.parse('a[0][b]=c')).to.deep.equal({ a: [{ b: 'c' }] }); + done(); + }); + + it('allows for empty strings in arrays', function (done) { + + expect(Qs.parse('a[]=b&a[]=&a[]=c')).to.deep.equal({ a: ['b', '', 'c'] }); + expect(Qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]=')).to.deep.equal({ a: ['b', '', 'c', ''] }); + done(); + }); + + it('compacts sparse arrays', function (done) { + + expect(Qs.parse('a[10]=1&a[2]=2')).to.deep.equal({ a: ['2', '1'] }); + done(); + }); + + it('parses semi-parsed strings', function (done) { + + expect(Qs.parse({ 'a[b]': 'c' })).to.deep.equal({ a: { b: 'c' } }); + expect(Qs.parse({ 'a[b]': 'c', 'a[d]': 'e' })).to.deep.equal({ a: { b: 'c', d: 'e' } }); + done(); + }); + + it('parses buffers to strings', function (done) { + + var b = new Buffer('test'); + expect(Qs.parse({ a: b })).to.deep.equal({ a: b.toString() }); + done(); + }); + + it('continues parsing when no parent is found', function (done) { + + expect(Qs.parse('[]&a=b')).to.deep.equal({ '0': '', a: 'b' }); + expect(Qs.parse('[foo]=bar')).to.deep.equal({ foo: 'bar' }); + done(); + }); + + it('does not error when parsing a very long array', function (done) { + + var str = 'a[]=a'; + while (Buffer.byteLength(str) < 128 * 1024) { + str += '&' + str; + } + + expect(function () { + + Qs.parse(str); + }).to.not.throw(); + + done(); + }); + + it('should not throw when a native prototype has an enumerable property', { parallel: false }, function (done) { + + Object.prototype.crash = ''; + Array.prototype.crash = ''; + expect(Qs.parse.bind(null, 'a=b')).to.not.throw(); + expect(Qs.parse('a=b')).to.deep.equal({ a: 'b' }); + expect(Qs.parse.bind(null, 'a[][b]=c')).to.not.throw(); + expect(Qs.parse('a[][b]=c')).to.deep.equal({ a: [{ b: 'c' }] }); + delete Object.prototype.crash; + delete Array.prototype.crash; + done(); + }); + + it('parses a string with an alternative delimiter', function (done) { + + expect(Qs.parse('a=b;c=d', ';')).to.deep.equal({ a: 'b', c: 'd' }); + done(); + }); + + it('does not use non-string objects as delimiters', function (done) { + + expect(Qs.parse('a=b&c=d', {})).to.deep.equal({ a: 'b', c: 'd' }); + done(); + }); + + it('parses an object', function (done) { + + var input = { + "user[name]": {"pop[bob]": 3}, + "user[email]": null + }; + + var expected = { + "user": { + "name": {"pop[bob]": 3}, + "email": null + } + }; + + var result = Qs.parse(input); + + expect(result).to.deep.equal(expected); + done(); + }); + + it('parses an object and not child values', function (done) { + + var input = { + "user[name]": {"pop[bob]": { "test": 3 }}, + "user[email]": null + }; + + var expected = { + "user": { + "name": {"pop[bob]": { "test": 3 }}, + "email": null + } + }; + + var result = Qs.parse(input); + + expect(result).to.deep.equal(expected); + done(); + }); +}); diff --git a/node_modules/multer/node_modules/qs/test/stringify.js b/node_modules/multer/node_modules/qs/test/stringify.js new file mode 100755 index 0000000..7bf1df4 --- /dev/null +++ b/node_modules/multer/node_modules/qs/test/stringify.js @@ -0,0 +1,129 @@ +// Load modules + +var Lab = require('lab'); +var Qs = require('../'); + + +// Declare internals + +var internals = {}; + + +// Test shortcuts + +var expect = Lab.expect; +var before = Lab.before; +var after = Lab.after; +var describe = Lab.experiment; +var it = Lab.test; + + +describe('#stringify', function () { + + it('stringifies a querystring object', function (done) { + + expect(Qs.stringify({ a: 'b' })).to.equal('a=b'); + expect(Qs.stringify({ a: 1 })).to.equal('a=1'); + expect(Qs.stringify({ a: 1, b: 2 })).to.equal('a=1&b=2'); + done(); + }); + + it('stringifies a nested object', function (done) { + + expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c'); + expect(Qs.stringify({ a: { b: { c: { d: 'e' } } } })).to.equal('a%5Bb%5D%5Bc%5D%5Bd%5D=e'); + done(); + }); + + it('stringifies an array value', function (done) { + + expect(Qs.stringify({ a: ['b', 'c', 'd'] })).to.equal('a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d'); + done(); + }); + + it('stringifies a nested array value', function (done) { + + expect(Qs.stringify({ a: { b: ['c', 'd'] } })).to.equal('a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d'); + done(); + }); + + it('stringifies an object inside an array', function (done) { + + expect(Qs.stringify({ a: [{ b: 'c' }] })).to.equal('a%5B0%5D%5Bb%5D=c'); + expect(Qs.stringify({ a: [{ b: { c: [1] } }] })).to.equal('a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1'); + done(); + }); + + it('stringifies a complicated object', function (done) { + + expect(Qs.stringify({ a: { b: 'c', d: 'e' } })).to.equal('a%5Bb%5D=c&a%5Bd%5D=e'); + done(); + }); + + it('stringifies an empty value', function (done) { + + expect(Qs.stringify({ a: '' })).to.equal('a='); + expect(Qs.stringify({ a: '', b: '' })).to.equal('a=&b='); + expect(Qs.stringify({ a: null })).to.equal('a='); + expect(Qs.stringify({ a: { b: null } })).to.equal('a%5Bb%5D='); + done(); + }); + + it('drops keys with a value of undefined', function (done) { + + expect(Qs.stringify({ a: undefined })).to.equal(''); + expect(Qs.stringify({ a: { b: undefined, c: null } })).to.equal('a%5Bc%5D='); + done(); + }); + + it('url encodes values', function (done) { + + expect(Qs.stringify({ a: 'b c' })).to.equal('a=b%20c'); + done(); + }); + + it('stringifies a date', function (done) { + + var now = new Date(); + var str = 'a=' + encodeURIComponent(now.toISOString()); + expect(Qs.stringify({ a: now })).to.equal(str); + done(); + }); + + it('stringifies the weird object from qs', function (done) { + + expect(Qs.stringify({ 'my weird field': 'q1!2"\'w$5&7/z8)?' })).to.equal('my%20weird%20field=q1!2%22\'w%245%267%2Fz8)%3F'); + done(); + }); + + it('skips properties that are part of the object prototype', function (done) { + + Object.prototype.crash = 'test'; + expect(Qs.stringify({ a: 'b'})).to.equal('a=b'); + expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c'); + delete Object.prototype.crash; + done(); + }); + + it('stringifies boolean values', function (done) { + + expect(Qs.stringify({ a: true })).to.equal('a=true'); + expect(Qs.stringify({ a: { b: true } })).to.equal('a%5Bb%5D=true'); + expect(Qs.stringify({ b: false })).to.equal('b=false'); + expect(Qs.stringify({ b: { c: false } })).to.equal('b%5Bc%5D=false'); + done(); + }); + + it('stringifies buffer values', function (done) { + + expect(Qs.stringify({ a: new Buffer('test') })).to.equal('a=test'); + expect(Qs.stringify({ a: { b: new Buffer('test') } })).to.equal('a%5Bb%5D=test'); + done(); + }); + + it('stringifies an object using an alternative delimiter', function (done) { + + expect(Qs.stringify({ a: 'b', c: 'd' }, ';')).to.equal('a=b;c=d'); + done(); + }); +}); diff --git a/node_modules/multer/node_modules/type-is/HISTORY.md b/node_modules/multer/node_modules/type-is/HISTORY.md new file mode 100644 index 0000000..e3cab75 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/HISTORY.md @@ -0,0 +1,115 @@ +1.5.7 / 2015-02-09 +================== + + * fix argument reassignment + * deps: mime-types@~2.0.9 + - Add new mime types + +1.5.6 / 2015-01-29 +================== + + * deps: mime-types@~2.0.8 + - Add new mime types + +1.5.5 / 2014-12-30 +================== + + * deps: mime-types@~2.0.7 + - Add new mime types + - Fix missing extensions + - Fix various invalid MIME type entries + - Remove example template MIME types + - deps: mime-db@~1.5.0 + +1.5.4 / 2014-12-10 +================== + + * deps: mime-types@~2.0.4 + - Add new mime types + - deps: mime-db@~1.3.0 + +1.5.3 / 2014-11-09 +================== + + * deps: mime-types@~2.0.3 + - Add new mime types + - deps: mime-db@~1.2.0 + +1.5.2 / 2014-09-28 +================== + + * deps: mime-types@~2.0.2 + - Add new mime types + - deps: mime-db@~1.1.0 + +1.5.1 / 2014-09-07 +================== + + * Support Node.js 0.6 + * deps: media-typer@0.3.0 + * deps: mime-types@~2.0.1 + - Support Node.js 0.6 + +1.5.0 / 2014-09-05 +================== + + * fix `hasbody` to be true for `content-length: 0` + +1.4.0 / 2014-09-02 +================== + + * update mime-types + +1.3.2 / 2014-06-24 +================== + + * use `~` range on mime-types + +1.3.1 / 2014-06-19 +================== + + * fix global variable leak + +1.3.0 / 2014-06-19 +================== + + * improve type parsing + + - invalid media type never matches + - media type not case-sensitive + - extra LWS does not affect results + +1.2.2 / 2014-06-19 +================== + + * fix behavior on unknown type argument + +1.2.1 / 2014-06-03 +================== + + * switch dependency from `mime` to `mime-types@1.0.0` + +1.2.0 / 2014-05-11 +================== + + * support suffix matching: + + - `+json` matches `application/vnd+json` + - `*/vnd+json` matches `application/vnd+json` + - `application/*+json` matches `application/vnd+json` + +1.1.0 / 2014-04-12 +================== + + * add non-array values support + * expose internal utilities: + + - `.is()` + - `.hasBody()` + - `.normalize()` + - `.match()` + +1.0.1 / 2014-03-30 +================== + + * add `multipart` as a shorthand diff --git a/node_modules/multer/node_modules/type-is/LICENSE b/node_modules/multer/node_modules/type-is/LICENSE new file mode 100644 index 0000000..4164d08 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/multer/node_modules/type-is/README.md b/node_modules/multer/node_modules/type-is/README.md new file mode 100644 index 0000000..0beeed8 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/README.md @@ -0,0 +1,117 @@ +# type-is + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Infer the content-type of a request. + +### Install + +```sh +$ npm install type-is +``` + +## API + +```js +var http = require('http') +var is = require('type-is') + +http.createServer(function (req, res) { + var istext = is(req, ['text/*']) + res.end('you ' + (istext ? 'sent' : 'did not send') + ' me text') +}) +``` + +### type = is(request, types) + +`request` is the node HTTP request. `types` is an array of types. + +```js +// req.headers.content-type = 'application/json' + +is(req, ['json']) // 'json' +is(req, ['html', 'json']) // 'json' +is(req, ['application/*']) // 'application/json' +is(req, ['application/json']) // 'application/json' + +is(req, ['html']) // false +``` + +### type = is.is(mediaType, types) + +`mediaType` is the [media type](https://tools.ietf.org/html/rfc6838) string. `types` is an array of types. + +```js +var mediaType = 'application/json' + +is.is(mediaType, ['json']) // 'json' +is.is(mediaType, ['html', 'json']) // 'json' +is.is(mediaType, ['application/*']) // 'application/json' +is.is(mediaType, ['application/json']) // 'application/json' + +is.is(mediaType, ['html']) // false +``` + +### Each type can be: + +- An extension name such as `json`. This name will be returned if matched. +- A mime type such as `application/json`. +- A mime type with a wildcard such as `*/json` or `application/*`. The full mime type will be returned if matched +- A suffix such as `+json`. This can be combined with a wildcard such as `*/vnd+json` or `application/*+json`. The full mime type will be returned if matched. + +`false` will be returned if no type matches. + +`null` will be returned if the request does not have a body. + +## Examples + +#### Example body parser + +```js +var is = require('type-is'); + +function bodyParser(req, res, next) { + if (!is.hasBody(req)) { + return next() + } + + switch (is(req, ['urlencoded', 'json', 'multipart'])) { + case 'urlencoded': + // parse urlencoded body + throw new Error('implement urlencoded body parsing') + break + case 'json': + // parse json body + throw new Error('implement json body parsing') + break + case 'multipart': + // parse multipart body + throw new Error('implement multipart body parsing') + break + default: + // 415 error code + res.statusCode = 415 + res.end() + return + } +} +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/type-is.svg?style=flat +[npm-url]: https://npmjs.org/package/type-is +[node-version-image]: https://img.shields.io/node/v/type-is.svg?style=flat +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/type-is.svg?style=flat +[travis-url]: https://travis-ci.org/jshttp/type-is +[coveralls-image]: https://img.shields.io/coveralls/jshttp/type-is.svg?style=flat +[coveralls-url]: https://coveralls.io/r/jshttp/type-is?branch=master +[downloads-image]: https://img.shields.io/npm/dm/type-is.svg?style=flat +[downloads-url]: https://npmjs.org/package/type-is diff --git a/node_modules/multer/node_modules/type-is/index.js b/node_modules/multer/node_modules/type-is/index.js new file mode 100644 index 0000000..73e885a --- /dev/null +++ b/node_modules/multer/node_modules/type-is/index.js @@ -0,0 +1,228 @@ + +var typer = require('media-typer') +var mime = require('mime-types') + +module.exports = typeofrequest; +typeofrequest.is = typeis; +typeofrequest.hasBody = hasbody; +typeofrequest.normalize = normalize; +typeofrequest.match = mimeMatch; + +/** + * Compare a `value` content-type with `types`. + * Each `type` can be an extension like `html`, + * a special shortcut like `multipart` or `urlencoded`, + * or a mime type. + * + * If no types match, `false` is returned. + * Otherwise, the first `type` that matches is returned. + * + * @param {String} value + * @param {Array} types + * @return String + */ + +function typeis(value, types_) { + var i + var types = types_ + + // remove parameters and normalize + var val = typenormalize(value) + + // no type or invalid + if (!val) { + return false + } + + // support flattened arguments + if (types && !Array.isArray(types)) { + types = new Array(arguments.length - 1) + for (i = 0; i < types.length; i++) { + types[i] = arguments[i + 1] + } + } + + // no types, return the content type + if (!types || !types.length) { + return val + } + + var type + for (i = 0; i < types.length; i++) { + if (mimeMatch(normalize(type = types[i]), val)) { + return type[0] === '+' || ~type.indexOf('*') + ? val + : type + } + } + + // no matches + return false; +} + +/** + * Check if a request has a request body. + * A request with a body __must__ either have `transfer-encoding` + * or `content-length` headers set. + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3 + * + * @param {Object} request + * @return {Boolean} + * @api public + */ + +function hasbody(req) { + var headers = req.headers; + if ('transfer-encoding' in headers) return true; + return !isNaN(headers['content-length']); +} + +/** + * Check if the incoming request contains the "Content-Type" + * header field, and it contains any of the give mime `type`s. + * If there is no request body, `null` is returned. + * If there is no content type, `false` is returned. + * Otherwise, it returns the first `type` that matches. + * + * Examples: + * + * // With Content-Type: text/html; charset=utf-8 + * this.is('html'); // => 'html' + * this.is('text/html'); // => 'text/html' + * this.is('text/*', 'application/json'); // => 'text/html' + * + * // When Content-Type is application/json + * this.is('json', 'urlencoded'); // => 'json' + * this.is('application/json'); // => 'application/json' + * this.is('html', 'application/*'); // => 'application/json' + * + * this.is('html'); // => false + * + * @param {String|Array} types... + * @return {String|false|null} + * @api public + */ + +function typeofrequest(req, types_) { + var types = types_ + + // no body + if (!hasbody(req)) { + return null + } + + // support flattened arguments + if (arguments.length > 2) { + types = new Array(arguments.length - 1) + for (var i = 0; i < types.length; i++) { + types[i] = arguments[i + 1] + } + } + + // request content type + var value = req.headers['content-type'] + + return typeis(value, types); +} + +/** + * Normalize a mime type. + * If it's a shorthand, expand it to a valid mime type. + * + * In general, you probably want: + * + * var type = is(req, ['urlencoded', 'json', 'multipart']); + * + * Then use the appropriate body parsers. + * These three are the most common request body types + * and are thus ensured to work. + * + * @param {String} type + * @api private + */ + +function normalize(type) { + switch (type) { + case 'urlencoded': return 'application/x-www-form-urlencoded'; + case 'multipart': + type = 'multipart/*'; + break; + } + + return type[0] === '+' || ~type.indexOf('/') + ? type + : mime.lookup(type) +} + +/** + * Check if `exected` mime type + * matches `actual` mime type with + * wildcard and +suffix support. + * + * @param {String} expected + * @param {String} actual + * @return {Boolean} + * @api private + */ + +function mimeMatch(expected, actual) { + // invalid type + if (expected === false) { + return false + } + + // exact match + if (expected === actual) { + return true + } + + actual = actual.split('/'); + + if (expected[0] === '+') { + // support +suffix + return Boolean(actual[1]) + && expected.length <= actual[1].length + && expected === actual[1].substr(0 - expected.length) + } + + if (!~expected.indexOf('*')) return false; + + expected = expected.split('/'); + + if (expected[0] === '*') { + // support */yyy + return expected[1] === actual[1] + } + + if (expected[1] === '*') { + // support xxx/* + return expected[0] === actual[0] + } + + if (expected[1][0] === '*' && expected[1][1] === '+') { + // support xxx/*+zzz + return expected[0] === actual[0] + && expected[1].length <= actual[1].length + 1 + && expected[1].substr(1) === actual[1].substr(1 - expected[1].length) + } + + return false +} + +/** + * Normalize a type and remove parameters. + * + * @param {string} value + * @return {string} + * @api private + */ + +function typenormalize(value) { + try { + var type = typer.parse(value) + delete type.parameters + return typer.format(type) + } catch (err) { + return null + } +} diff --git a/node_modules/multer/node_modules/type-is/node_modules/media-typer/HISTORY.md b/node_modules/multer/node_modules/type-is/node_modules/media-typer/HISTORY.md new file mode 100644 index 0000000..62c2003 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/media-typer/HISTORY.md @@ -0,0 +1,22 @@ +0.3.0 / 2014-09-07 +================== + + * Support Node.js 0.6 + * Throw error when parameter format invalid on parse + +0.2.0 / 2014-06-18 +================== + + * Add `typer.format()` to format media types + +0.1.0 / 2014-06-17 +================== + + * Accept `req` as argument to `parse` + * Accept `res` as argument to `parse` + * Parse media type with extra LWS between type and first parameter + +0.0.0 / 2014-06-13 +================== + + * Initial implementation diff --git a/node_modules/multer/node_modules/type-is/node_modules/media-typer/LICENSE b/node_modules/multer/node_modules/type-is/node_modules/media-typer/LICENSE new file mode 100644 index 0000000..b7dce6c --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/media-typer/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2014 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/multer/node_modules/type-is/node_modules/media-typer/README.md b/node_modules/multer/node_modules/type-is/node_modules/media-typer/README.md new file mode 100644 index 0000000..d8df623 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/media-typer/README.md @@ -0,0 +1,81 @@ +# media-typer + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Simple RFC 6838 media type parser + +## Installation + +```sh +$ npm install media-typer +``` + +## API + +```js +var typer = require('media-typer') +``` + +### typer.parse(string) + +```js +var obj = typer.parse('image/svg+xml; charset=utf-8') +``` + +Parse a media type string. This will return an object with the following +properties (examples are shown for the string `'image/svg+xml; charset=utf-8'`): + + - `type`: The type of the media type (always lower case). Example: `'image'` + + - `subtype`: The subtype of the media type (always lower case). Example: `'svg'` + + - `suffix`: The suffix of the media type (always lower case). Example: `'xml'` + + - `parameters`: An object of the parameters in the media type (name of parameter always lower case). Example: `{charset: 'utf-8'}` + +### typer.parse(req) + +```js +var obj = typer.parse(req) +``` + +Parse the `content-type` header from the given `req`. Short-cut for +`typer.parse(req.headers['content-type'])`. + +### typer.parse(res) + +```js +var obj = typer.parse(res) +``` + +Parse the `content-type` header set on the given `res`. Short-cut for +`typer.parse(res.getHeader('content-type'))`. + +### typer.format(obj) + +```js +var obj = typer.format({type: 'image', subtype: 'svg', suffix: 'xml'}) +``` + +Format an object into a media type string. This will return a string of the +mime type for the given object. For the properties of the object, see the +documentation for `typer.parse(string)`. + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/media-typer.svg?style=flat +[npm-url]: https://npmjs.org/package/media-typer +[node-version-image]: https://img.shields.io/badge/node.js-%3E%3D_0.6-brightgreen.svg?style=flat +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/media-typer.svg?style=flat +[travis-url]: https://travis-ci.org/jshttp/media-typer +[coveralls-image]: https://img.shields.io/coveralls/jshttp/media-typer.svg?style=flat +[coveralls-url]: https://coveralls.io/r/jshttp/media-typer +[downloads-image]: https://img.shields.io/npm/dm/media-typer.svg?style=flat +[downloads-url]: https://npmjs.org/package/media-typer diff --git a/node_modules/multer/node_modules/type-is/node_modules/media-typer/index.js b/node_modules/multer/node_modules/type-is/node_modules/media-typer/index.js new file mode 100644 index 0000000..07f7295 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/media-typer/index.js @@ -0,0 +1,270 @@ +/*! + * media-typer + * Copyright(c) 2014 Douglas Christopher Wilson + * MIT Licensed + */ + +/** + * RegExp to match *( ";" parameter ) in RFC 2616 sec 3.7 + * + * parameter = token "=" ( token | quoted-string ) + * token = 1* + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) + * qdtext = > + * quoted-pair = "\" CHAR + * CHAR = + * TEXT = + * LWS = [CRLF] 1*( SP | HT ) + * CRLF = CR LF + * CR = + * LF = + * SP = + * SHT = + * CTL = + * OCTET = + */ +var paramRegExp = /; *([!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+) *= *("(?:[ !\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u0020-\u007e])*"|[!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+) */g; +var textRegExp = /^[\u0020-\u007e\u0080-\u00ff]+$/ +var tokenRegExp = /^[!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+$/ + +/** + * RegExp to match quoted-pair in RFC 2616 + * + * quoted-pair = "\" CHAR + * CHAR = + */ +var qescRegExp = /\\([\u0000-\u007f])/g; + +/** + * RegExp to match chars that must be quoted-pair in RFC 2616 + */ +var quoteRegExp = /([\\"])/g; + +/** + * RegExp to match type in RFC 6838 + * + * type-name = restricted-name + * subtype-name = restricted-name + * restricted-name = restricted-name-first *126restricted-name-chars + * restricted-name-first = ALPHA / DIGIT + * restricted-name-chars = ALPHA / DIGIT / "!" / "#" / + * "$" / "&" / "-" / "^" / "_" + * restricted-name-chars =/ "." ; Characters before first dot always + * ; specify a facet name + * restricted-name-chars =/ "+" ; Characters after last plus always + * ; specify a structured syntax suffix + * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z + * DIGIT = %x30-39 ; 0-9 + */ +var subtypeNameRegExp = /^[A-Za-z0-9][A-Za-z0-9!#$&^_.-]{0,126}$/ +var typeNameRegExp = /^[A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126}$/ +var typeRegExp = /^ *([A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126})\/([A-Za-z0-9][A-Za-z0-9!#$&^_.+-]{0,126}) *$/; + +/** + * Module exports. + */ + +exports.format = format +exports.parse = parse + +/** + * Format object to media type. + * + * @param {object} obj + * @return {string} + * @api public + */ + +function format(obj) { + if (!obj || typeof obj !== 'object') { + throw new TypeError('argument obj is required') + } + + var parameters = obj.parameters + var subtype = obj.subtype + var suffix = obj.suffix + var type = obj.type + + if (!type || !typeNameRegExp.test(type)) { + throw new TypeError('invalid type') + } + + if (!subtype || !subtypeNameRegExp.test(subtype)) { + throw new TypeError('invalid subtype') + } + + // format as type/subtype + var string = type + '/' + subtype + + // append +suffix + if (suffix) { + if (!typeNameRegExp.test(suffix)) { + throw new TypeError('invalid suffix') + } + + string += '+' + suffix + } + + // append parameters + if (parameters && typeof parameters === 'object') { + var param + var params = Object.keys(parameters).sort() + + for (var i = 0; i < params.length; i++) { + param = params[i] + + if (!tokenRegExp.test(param)) { + throw new TypeError('invalid parameter name') + } + + string += '; ' + param + '=' + qstring(parameters[param]) + } + } + + return string +} + +/** + * Parse media type to object. + * + * @param {string|object} string + * @return {Object} + * @api public + */ + +function parse(string) { + if (!string) { + throw new TypeError('argument string is required') + } + + // support req/res-like objects as argument + if (typeof string === 'object') { + string = getcontenttype(string) + } + + if (typeof string !== 'string') { + throw new TypeError('argument string is required to be a string') + } + + var index = string.indexOf(';') + var type = index !== -1 + ? string.substr(0, index) + : string + + var key + var match + var obj = splitType(type) + var params = {} + var value + + paramRegExp.lastIndex = index + + while (match = paramRegExp.exec(string)) { + if (match.index !== index) { + throw new TypeError('invalid parameter format') + } + + index += match[0].length + key = match[1].toLowerCase() + value = match[2] + + if (value[0] === '"') { + // remove quotes and escapes + value = value + .substr(1, value.length - 2) + .replace(qescRegExp, '$1') + } + + params[key] = value + } + + if (index !== -1 && index !== string.length) { + throw new TypeError('invalid parameter format') + } + + obj.parameters = params + + return obj +} + +/** + * Get content-type from req/res objects. + * + * @param {object} + * @return {Object} + * @api private + */ + +function getcontenttype(obj) { + if (typeof obj.getHeader === 'function') { + // res-like + return obj.getHeader('content-type') + } + + if (typeof obj.headers === 'object') { + // req-like + return obj.headers && obj.headers['content-type'] + } +} + +/** + * Quote a string if necessary. + * + * @param {string} val + * @return {string} + * @api private + */ + +function qstring(val) { + var str = String(val) + + // no need to quote tokens + if (tokenRegExp.test(str)) { + return str + } + + if (str.length > 0 && !textRegExp.test(str)) { + throw new TypeError('invalid parameter value') + } + + return '"' + str.replace(quoteRegExp, '\\$1') + '"' +} + +/** + * Simply "type/subtype+siffx" into parts. + * + * @param {string} string + * @return {Object} + * @api private + */ + +function splitType(string) { + var match = typeRegExp.exec(string.toLowerCase()) + + if (!match) { + throw new TypeError('invalid media type') + } + + var type = match[1] + var subtype = match[2] + var suffix + + // suffix after last + + var index = subtype.lastIndexOf('+') + if (index !== -1) { + suffix = subtype.substr(index + 1) + subtype = subtype.substr(0, index) + } + + var obj = { + type: type, + subtype: subtype, + suffix: suffix + } + + return obj +} diff --git a/node_modules/multer/node_modules/type-is/node_modules/media-typer/package.json b/node_modules/multer/node_modules/type-is/node_modules/media-typer/package.json new file mode 100644 index 0000000..88bb0a8 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/media-typer/package.json @@ -0,0 +1,57 @@ +{ + "name": "media-typer", + "description": "Simple RFC 6838 media type parser and formatter", + "version": "0.3.0", + "author": { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/jshttp/media-typer" + }, + "devDependencies": { + "istanbul": "0.3.2", + "mocha": "~1.21.4", + "should": "~4.0.4" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec --check-leaks --bail test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "d49d41ffd0bb5a0655fa44a59df2ec0bfc835b16", + "bugs": { + "url": "https://github.com/jshttp/media-typer/issues" + }, + "homepage": "https://github.com/jshttp/media-typer", + "_id": "media-typer@0.3.0", + "_shasum": "8710d7af0aa626f8fffa1ce00168545263255748", + "_from": "media-typer@0.3.0", + "_npmVersion": "1.4.21", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "8710d7af0aa626f8fffa1ce00168545263255748", + "tarball": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" +} diff --git a/node_modules/multer/node_modules/type-is/node_modules/mime-types/HISTORY.md b/node_modules/multer/node_modules/type-is/node_modules/mime-types/HISTORY.md new file mode 100644 index 0000000..fb90c27 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/mime-types/HISTORY.md @@ -0,0 +1,115 @@ +2.0.14 / 2015-06-06 +=================== + + * deps: mime-db@~1.12.0 + - Add new mime types + +2.0.13 / 2015-05-31 +=================== + + * deps: mime-db@~1.11.0 + - Add new mime types + +2.0.12 / 2015-05-19 +=================== + + * deps: mime-db@~1.10.0 + - Add new mime types + +2.0.11 / 2015-05-05 +=================== + + * deps: mime-db@~1.9.1 + - Add new mime types + +2.0.10 / 2015-03-13 +=================== + + * deps: mime-db@~1.8.0 + - Add new mime types + +2.0.9 / 2015-02-09 +================== + + * deps: mime-db@~1.7.0 + - Add new mime types + - Community extensions ownership transferred from `node-mime` + +2.0.8 / 2015-01-29 +================== + + * deps: mime-db@~1.6.0 + - Add new mime types + +2.0.7 / 2014-12-30 +================== + + * deps: mime-db@~1.5.0 + - Add new mime types + - Fix various invalid MIME type entries + +2.0.6 / 2014-12-30 +================== + + * deps: mime-db@~1.4.0 + - Add new mime types + - Fix various invalid MIME type entries + - Remove example template MIME types + +2.0.5 / 2014-12-29 +================== + + * deps: mime-db@~1.3.1 + - Fix missing extensions + +2.0.4 / 2014-12-10 +================== + + * deps: mime-db@~1.3.0 + - Add new mime types + +2.0.3 / 2014-11-09 +================== + + * deps: mime-db@~1.2.0 + - Add new mime types + +2.0.2 / 2014-09-28 +================== + + * deps: mime-db@~1.1.0 + - Add new mime types + - Add additional compressible + - Update charsets + +2.0.1 / 2014-09-07 +================== + + * Support Node.js 0.6 + +2.0.0 / 2014-09-02 +================== + + * Use `mime-db` + * Remove `.define()` + +1.0.2 / 2014-08-04 +================== + + * Set charset=utf-8 for `text/javascript` + +1.0.1 / 2014-06-24 +================== + + * Add `text/jsx` type + +1.0.0 / 2014-05-12 +================== + + * Return `false` for unknown types + * Set charset=utf-8 for `application/json` + +0.1.0 / 2014-05-02 +================== + + * Initial release diff --git a/node_modules/multer/node_modules/type-is/node_modules/mime-types/LICENSE b/node_modules/multer/node_modules/type-is/node_modules/mime-types/LICENSE new file mode 100644 index 0000000..a7ae8ee --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/mime-types/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/multer/node_modules/type-is/node_modules/mime-types/README.md b/node_modules/multer/node_modules/type-is/node_modules/mime-types/README.md new file mode 100644 index 0000000..3727493 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/mime-types/README.md @@ -0,0 +1,102 @@ +# mime-types + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +The ultimate javascript content-type utility. + +Similar to [node-mime](https://github.com/broofa/node-mime), except: + +- __No fallbacks.__ Instead of naively returning the first available type, `mime-types` simply returns `false`, + so do `var type = mime.lookup('unrecognized') || 'application/octet-stream'`. +- No `new Mime()` business, so you could do `var lookup = require('mime-types').lookup`. +- Additional mime types are added such as jade and stylus via [mime-db](https://github.com/jshttp/mime-db) +- No `.define()` functionality + +Otherwise, the API is compatible. + +## Install + +```sh +$ npm install mime-types +``` + +## Adding Types + +All mime types are based on [mime-db](https://github.com/jshttp/mime-db), +so open a PR there if you'd like to add mime types. + +## API + +```js +var mime = require('mime-types') +``` + +All functions return `false` if input is invalid or not found. + +### mime.lookup(path) + +Lookup the content-type associated with a file. + +```js +mime.lookup('json') // 'application/json' +mime.lookup('.md') // 'text/x-markdown' +mime.lookup('file.html') // 'text/html' +mime.lookup('folder/file.js') // 'application/javascript' + +mime.lookup('cats') // false +``` + +### mime.contentType(type) + +Create a full content-type header given a content-type or extension. + +```js +mime.contentType('markdown') // 'text/x-markdown; charset=utf-8' +mime.contentType('file.json') // 'application/json; charset=utf-8' + +// from a full path +mime.contentType(path.extname('/path/to/file.json')) // 'application/json; charset=utf-8' +``` + +### mime.extension(type) + +Get the default extension for a content-type. + +```js +mime.extension('application/octet-stream') // 'bin' +``` + +### mime.charset(type) + +Lookup the implied default charset of a content-type. + +```js +mime.charset('text/x-markdown') // 'UTF-8' +``` + +### var type = mime.types[extension] + +A map of content-types by extension. + +### [extensions...] = mime.extensions[type] + +A map of extensions by content-type. + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/mime-types.svg +[npm-url]: https://npmjs.org/package/mime-types +[node-version-image]: https://img.shields.io/node/v/mime-types.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/mime-types/master.svg +[travis-url]: https://travis-ci.org/jshttp/mime-types +[coveralls-image]: https://img.shields.io/coveralls/jshttp/mime-types/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/mime-types +[downloads-image]: https://img.shields.io/npm/dm/mime-types.svg +[downloads-url]: https://npmjs.org/package/mime-types diff --git a/node_modules/multer/node_modules/type-is/node_modules/mime-types/index.js b/node_modules/multer/node_modules/type-is/node_modules/mime-types/index.js new file mode 100644 index 0000000..b46a202 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/mime-types/index.js @@ -0,0 +1,63 @@ + +var db = require('mime-db') + +// types[extension] = type +exports.types = Object.create(null) +// extensions[type] = [extensions] +exports.extensions = Object.create(null) + +Object.keys(db).forEach(function (name) { + var mime = db[name] + var exts = mime.extensions + if (!exts || !exts.length) return + exports.extensions[name] = exts + exts.forEach(function (ext) { + exports.types[ext] = name + }) +}) + +exports.lookup = function (string) { + if (!string || typeof string !== "string") return false + // remove any leading paths, though we should just use path.basename + string = string.replace(/.*[\.\/\\]/, '').toLowerCase() + if (!string) return false + return exports.types[string] || false +} + +exports.extension = function (type) { + if (!type || typeof type !== "string") return false + // to do: use media-typer + type = type.match(/^\s*([^;\s]*)(?:;|\s|$)/) + if (!type) return false + var exts = exports.extensions[type[1].toLowerCase()] + if (!exts || !exts.length) return false + return exts[0] +} + +// type has to be an exact mime type +exports.charset = function (type) { + var mime = db[type] + if (mime && mime.charset) return mime.charset + + // default text/* to utf-8 + if (/^text\//.test(type)) return 'UTF-8' + + return false +} + +// backwards compatibility +exports.charsets = { + lookup: exports.charset +} + +// to do: maybe use set-type module or something +exports.contentType = function (type) { + if (!type || typeof type !== "string") return false + if (!~type.indexOf('/')) type = exports.lookup(type) + if (!type) return false + if (!~type.indexOf('charset')) { + var charset = exports.charset(type) + if (charset) type += '; charset=' + charset.toLowerCase() + } + return type +} diff --git a/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/HISTORY.md b/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/HISTORY.md new file mode 100644 index 0000000..d559c26 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/HISTORY.md @@ -0,0 +1,212 @@ +1.12.0 / 2015-06-05 +=================== + + * Add `application/bdoc` + * Add `application/vnd.hyperdrive+json` + * Add `application/x-bdoc` + * Add extension `.rtf` to `text/rtf` + +1.11.0 / 2015-05-31 +=================== + + * Add `audio/wav` + * Add `audio/wave` + * Add extension `.litcoffee` to `text/coffeescript` + * Add extension `.sfd-hdstx` to `application/vnd.hydrostatix.sof-data` + * Add extension `.n-gage` to `application/vnd.nokia.n-gage.symbian.install` + +1.10.0 / 2015-05-19 +=================== + + * Add `application/vnd.balsamiq.bmpr` + * Add `application/vnd.microsoft.portable-executable` + * Add `application/x-ns-proxy-autoconfig` + +1.9.1 / 2015-04-19 +================== + + * Remove `.json` extension from `application/manifest+json` + - This is causing bugs downstream + +1.9.0 / 2015-04-19 +================== + + * Add `application/manifest+json` + * Add `application/vnd.micro+json` + * Add `image/vnd.zbrush.pcx` + * Add `image/x-ms-bmp` + +1.8.0 / 2015-03-13 +================== + + * Add `application/vnd.citationstyles.style+xml` + * Add `application/vnd.fastcopy-disk-image` + * Add `application/vnd.gov.sk.xmldatacontainer+xml` + * Add extension `.jsonld` to `application/ld+json` + +1.7.0 / 2015-02-08 +================== + + * Add `application/vnd.gerber` + * Add `application/vnd.msa-disk-image` + +1.6.1 / 2015-02-05 +================== + + * Community extensions ownership transferred from `node-mime` + +1.6.0 / 2015-01-29 +================== + + * Add `application/jose` + * Add `application/jose+json` + * Add `application/json-seq` + * Add `application/jwk+json` + * Add `application/jwk-set+json` + * Add `application/jwt` + * Add `application/rdap+json` + * Add `application/vnd.gov.sk.e-form+xml` + * Add `application/vnd.ims.imsccv1p3` + +1.5.0 / 2014-12-30 +================== + + * Add `application/vnd.oracle.resource+json` + * Fix various invalid MIME type entries + - `application/mbox+xml` + - `application/oscp-response` + - `application/vwg-multiplexed` + - `audio/g721` + +1.4.0 / 2014-12-21 +================== + + * Add `application/vnd.ims.imsccv1p2` + * Fix various invalid MIME type entries + - `application/vnd-acucobol` + - `application/vnd-curl` + - `application/vnd-dart` + - `application/vnd-dxr` + - `application/vnd-fdf` + - `application/vnd-mif` + - `application/vnd-sema` + - `application/vnd-wap-wmlc` + - `application/vnd.adobe.flash-movie` + - `application/vnd.dece-zip` + - `application/vnd.dvb_service` + - `application/vnd.micrografx-igx` + - `application/vnd.sealed-doc` + - `application/vnd.sealed-eml` + - `application/vnd.sealed-mht` + - `application/vnd.sealed-ppt` + - `application/vnd.sealed-tiff` + - `application/vnd.sealed-xls` + - `application/vnd.sealedmedia.softseal-html` + - `application/vnd.sealedmedia.softseal-pdf` + - `application/vnd.wap-slc` + - `application/vnd.wap-wbxml` + - `audio/vnd.sealedmedia.softseal-mpeg` + - `image/vnd-djvu` + - `image/vnd-svf` + - `image/vnd-wap-wbmp` + - `image/vnd.sealed-png` + - `image/vnd.sealedmedia.softseal-gif` + - `image/vnd.sealedmedia.softseal-jpg` + - `model/vnd-dwf` + - `model/vnd.parasolid.transmit-binary` + - `model/vnd.parasolid.transmit-text` + - `text/vnd-a` + - `text/vnd-curl` + - `text/vnd.wap-wml` + * Remove example template MIME types + - `application/example` + - `audio/example` + - `image/example` + - `message/example` + - `model/example` + - `multipart/example` + - `text/example` + - `video/example` + +1.3.1 / 2014-12-16 +================== + + * Fix missing extensions + - `application/json5` + - `text/hjson` + +1.3.0 / 2014-12-07 +================== + + * Add `application/a2l` + * Add `application/aml` + * Add `application/atfx` + * Add `application/atxml` + * Add `application/cdfx+xml` + * Add `application/dii` + * Add `application/json5` + * Add `application/lxf` + * Add `application/mf4` + * Add `application/vnd.apache.thrift.compact` + * Add `application/vnd.apache.thrift.json` + * Add `application/vnd.coffeescript` + * Add `application/vnd.enphase.envoy` + * Add `application/vnd.ims.imsccv1p1` + * Add `text/csv-schema` + * Add `text/hjson` + * Add `text/markdown` + * Add `text/yaml` + +1.2.0 / 2014-11-09 +================== + + * Add `application/cea` + * Add `application/dit` + * Add `application/vnd.gov.sk.e-form+zip` + * Add `application/vnd.tmd.mediaflex.api+xml` + * Type `application/epub+zip` is now IANA-registered + +1.1.2 / 2014-10-23 +================== + + * Rebuild database for `application/x-www-form-urlencoded` change + +1.1.1 / 2014-10-20 +================== + + * Mark `application/x-www-form-urlencoded` as compressible. + +1.1.0 / 2014-09-28 +================== + + * Add `application/font-woff2` + +1.0.3 / 2014-09-25 +================== + + * Fix engine requirement in package + +1.0.2 / 2014-09-25 +================== + + * Add `application/coap-group+json` + * Add `application/dcd` + * Add `application/vnd.apache.thrift.binary` + * Add `image/vnd.tencent.tap` + * Mark all JSON-derived types as compressible + * Update `text/vtt` data + +1.0.1 / 2014-08-30 +================== + + * Fix extension ordering + +1.0.0 / 2014-08-30 +================== + + * Add `application/atf` + * Add `application/merge-patch+json` + * Add `multipart/x-mixed-replace` + * Add `source: 'apache'` metadata + * Add `source: 'iana'` metadata + * Remove badly-assumed charset data diff --git a/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/LICENSE b/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/LICENSE new file mode 100644 index 0000000..a7ae8ee --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/README.md b/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/README.md new file mode 100644 index 0000000..2c54bb4 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/README.md @@ -0,0 +1,76 @@ +# mime-db + +[![NPM Version][npm-version-image]][npm-url] +[![NPM Downloads][npm-downloads-image]][npm-url] +[![Node.js Version][node-image]][node-url] +[![Build Status][travis-image]][travis-url] +[![Coverage Status][coveralls-image]][coveralls-url] + +This is a database of all mime types. +It consists of a single, public JSON file and does not include any logic, +allowing it to remain as un-opinionated as possible with an API. +It aggregates data from the following sources: + +- http://www.iana.org/assignments/media-types/media-types.xhtml +- http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types + +## Installation + +```bash +npm install mime-db +``` + +If you're crazy enough to use this in the browser, +you can just grab the JSON file: + +``` +https://cdn.rawgit.com/jshttp/mime-db/master/db.json +``` + +## Usage + +```js +var db = require('mime-db'); + +// grab data on .js files +var data = db['application/javascript']; +``` + +## Data Structure + +The JSON file is a map lookup for lowercased mime types. +Each mime type has the following properties: + +- `.source` - where the mime type is defined. + If not set, it's probably a custom media type. + - `apache` - [Apache common media types](http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types) + - `iana` - [IANA-defined media types](http://www.iana.org/assignments/media-types/media-types.xhtml) +- `.extensions[]` - known extensions associated with this mime type. +- `.compressible` - whether a file of this type is can be gzipped. +- `.charset` - the default charset associated with this type, if any. + +If unknown, every property could be `undefined`. + +## Contributing + +To edit the database, only make PRs against `src/custom.json` or +`src/custom-suffix.json`. + +To update the build, run `npm run update`. + +## Adding Custom Media Types + +The best way to get new media types included in this library is to register +them with the IANA. The community registration procedure is outlined in +[RFC 6838 section 5](http://tools.ietf.org/html/rfc6838#section-5). Types +registered with the IANA are automatically pulled into this library. + +[npm-version-image]: https://img.shields.io/npm/v/mime-db.svg +[npm-downloads-image]: https://img.shields.io/npm/dm/mime-db.svg +[npm-url]: https://npmjs.org/package/mime-db +[travis-image]: https://img.shields.io/travis/jshttp/mime-db/master.svg +[travis-url]: https://travis-ci.org/jshttp/mime-db +[coveralls-image]: https://img.shields.io/coveralls/jshttp/mime-db/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/mime-db?branch=master +[node-image]: https://img.shields.io/node/v/mime-db.svg +[node-url]: http://nodejs.org/download/ diff --git a/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/db.json b/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/db.json new file mode 100644 index 0000000..5f111be --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/db.json @@ -0,0 +1,6359 @@ +{ + "application/1d-interleaved-parityfec": { + "source": "iana" + }, + "application/3gpdash-qoe-report+xml": { + "source": "iana" + }, + "application/3gpp-ims+xml": { + "source": "iana" + }, + "application/a2l": { + "source": "iana" + }, + "application/activemessage": { + "source": "iana" + }, + "application/alto-costmap+json": { + "source": "iana", + "compressible": true + }, + "application/alto-costmapfilter+json": { + "source": "iana", + "compressible": true + }, + "application/alto-directory+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointcost+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointcostparams+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointprop+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointpropparams+json": { + "source": "iana", + "compressible": true + }, + "application/alto-error+json": { + "source": "iana", + "compressible": true + }, + "application/alto-networkmap+json": { + "source": "iana", + "compressible": true + }, + "application/alto-networkmapfilter+json": { + "source": "iana", + "compressible": true + }, + "application/aml": { + "source": "iana" + }, + "application/andrew-inset": { + "source": "iana", + "extensions": ["ez"] + }, + "application/applefile": { + "source": "iana" + }, + "application/applixware": { + "source": "apache", + "extensions": ["aw"] + }, + "application/atf": { + "source": "iana" + }, + "application/atfx": { + "source": "iana" + }, + "application/atom+xml": { + "source": "iana", + "compressible": true, + "extensions": ["atom"] + }, + "application/atomcat+xml": { + "source": "iana", + "extensions": ["atomcat"] + }, + "application/atomdeleted+xml": { + "source": "iana" + }, + "application/atomicmail": { + "source": "iana" + }, + "application/atomsvc+xml": { + "source": "iana", + "extensions": ["atomsvc"] + }, + "application/atxml": { + "source": "iana" + }, + "application/auth-policy+xml": { + "source": "iana" + }, + "application/bacnet-xdd+zip": { + "source": "iana" + }, + "application/batch-smtp": { + "source": "iana" + }, + "application/bdoc": { + "compressible": false, + "extensions": ["bdoc"] + }, + "application/beep+xml": { + "source": "iana" + }, + "application/calendar+json": { + "source": "iana", + "compressible": true + }, + "application/calendar+xml": { + "source": "iana" + }, + "application/call-completion": { + "source": "iana" + }, + "application/cals-1840": { + "source": "iana" + }, + "application/cbor": { + "source": "iana" + }, + "application/ccmp+xml": { + "source": "iana" + }, + "application/ccxml+xml": { + "source": "iana", + "extensions": ["ccxml"] + }, + "application/cdfx+xml": { + "source": "iana" + }, + "application/cdmi-capability": { + "source": "iana", + "extensions": ["cdmia"] + }, + "application/cdmi-container": { + "source": "iana", + "extensions": ["cdmic"] + }, + "application/cdmi-domain": { + "source": "iana", + "extensions": ["cdmid"] + }, + "application/cdmi-object": { + "source": "iana", + "extensions": ["cdmio"] + }, + "application/cdmi-queue": { + "source": "iana", + "extensions": ["cdmiq"] + }, + "application/cea": { + "source": "iana" + }, + "application/cea-2018+xml": { + "source": "iana" + }, + "application/cellml+xml": { + "source": "iana" + }, + "application/cfw": { + "source": "iana" + }, + "application/cms": { + "source": "iana" + }, + "application/cnrp+xml": { + "source": "iana" + }, + "application/coap-group+json": { + "source": "iana", + "compressible": true + }, + "application/commonground": { + "source": "iana" + }, + "application/conference-info+xml": { + "source": "iana" + }, + "application/cpl+xml": { + "source": "iana" + }, + "application/csrattrs": { + "source": "iana" + }, + "application/csta+xml": { + "source": "iana" + }, + "application/cstadata+xml": { + "source": "iana" + }, + "application/cu-seeme": { + "source": "apache", + "extensions": ["cu"] + }, + "application/cybercash": { + "source": "iana" + }, + "application/dart": { + "compressible": true + }, + "application/dash+xml": { + "source": "iana", + "extensions": ["mdp"] + }, + "application/dashdelta": { + "source": "iana" + }, + "application/davmount+xml": { + "source": "iana", + "extensions": ["davmount"] + }, + "application/dca-rft": { + "source": "iana" + }, + "application/dcd": { + "source": "iana" + }, + "application/dec-dx": { + "source": "iana" + }, + "application/dialog-info+xml": { + "source": "iana" + }, + "application/dicom": { + "source": "iana" + }, + "application/dii": { + "source": "iana" + }, + "application/dit": { + "source": "iana" + }, + "application/dns": { + "source": "iana" + }, + "application/docbook+xml": { + "source": "apache", + "extensions": ["dbk"] + }, + "application/dskpp+xml": { + "source": "iana" + }, + "application/dssc+der": { + "source": "iana", + "extensions": ["dssc"] + }, + "application/dssc+xml": { + "source": "iana", + "extensions": ["xdssc"] + }, + "application/dvcs": { + "source": "iana" + }, + "application/ecmascript": { + "source": "iana", + "compressible": true, + "extensions": ["ecma"] + }, + "application/edi-consent": { + "source": "iana" + }, + "application/edi-x12": { + "source": "iana", + "compressible": false + }, + "application/edifact": { + "source": "iana", + "compressible": false + }, + "application/emma+xml": { + "source": "iana", + "extensions": ["emma"] + }, + "application/emotionml+xml": { + "source": "iana" + }, + "application/encaprtp": { + "source": "iana" + }, + "application/epp+xml": { + "source": "iana" + }, + "application/epub+zip": { + "source": "iana", + "extensions": ["epub"] + }, + "application/eshop": { + "source": "iana" + }, + "application/exi": { + "source": "iana", + "extensions": ["exi"] + }, + "application/fastinfoset": { + "source": "iana" + }, + "application/fastsoap": { + "source": "iana" + }, + "application/fdt+xml": { + "source": "iana" + }, + "application/fits": { + "source": "iana" + }, + "application/font-sfnt": { + "source": "iana" + }, + "application/font-tdpfr": { + "source": "iana", + "extensions": ["pfr"] + }, + "application/font-woff": { + "source": "iana", + "compressible": false, + "extensions": ["woff"] + }, + "application/font-woff2": { + "compressible": false, + "extensions": ["woff2"] + }, + "application/framework-attributes+xml": { + "source": "iana" + }, + "application/gml+xml": { + "source": "apache", + "extensions": ["gml"] + }, + "application/gpx+xml": { + "source": "apache", + "extensions": ["gpx"] + }, + "application/gxf": { + "source": "apache", + "extensions": ["gxf"] + }, + "application/gzip": { + "source": "iana", + "compressible": false + }, + "application/h224": { + "source": "iana" + }, + "application/held+xml": { + "source": "iana" + }, + "application/http": { + "source": "iana" + }, + "application/hyperstudio": { + "source": "iana", + "extensions": ["stk"] + }, + "application/ibe-key-request+xml": { + "source": "iana" + }, + "application/ibe-pkg-reply+xml": { + "source": "iana" + }, + "application/ibe-pp-data": { + "source": "iana" + }, + "application/iges": { + "source": "iana" + }, + "application/im-iscomposing+xml": { + "source": "iana" + }, + "application/index": { + "source": "iana" + }, + "application/index.cmd": { + "source": "iana" + }, + "application/index.obj": { + "source": "iana" + }, + "application/index.response": { + "source": "iana" + }, + "application/index.vnd": { + "source": "iana" + }, + "application/inkml+xml": { + "source": "iana", + "extensions": ["ink","inkml"] + }, + "application/iotp": { + "source": "iana" + }, + "application/ipfix": { + "source": "iana", + "extensions": ["ipfix"] + }, + "application/ipp": { + "source": "iana" + }, + "application/isup": { + "source": "iana" + }, + "application/its+xml": { + "source": "iana" + }, + "application/java-archive": { + "source": "apache", + "compressible": false, + "extensions": ["jar"] + }, + "application/java-serialized-object": { + "source": "apache", + "compressible": false, + "extensions": ["ser"] + }, + "application/java-vm": { + "source": "apache", + "compressible": false, + "extensions": ["class"] + }, + "application/javascript": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["js"] + }, + "application/jose": { + "source": "iana" + }, + "application/jose+json": { + "source": "iana", + "compressible": true + }, + "application/jrd+json": { + "source": "iana", + "compressible": true + }, + "application/json": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["json","map"] + }, + "application/json-patch+json": { + "source": "iana", + "compressible": true + }, + "application/json-seq": { + "source": "iana" + }, + "application/json5": { + "extensions": ["json5"] + }, + "application/jsonml+json": { + "source": "apache", + "compressible": true, + "extensions": ["jsonml"] + }, + "application/jwk+json": { + "source": "iana", + "compressible": true + }, + "application/jwk-set+json": { + "source": "iana", + "compressible": true + }, + "application/jwt": { + "source": "iana" + }, + "application/kpml-request+xml": { + "source": "iana" + }, + "application/kpml-response+xml": { + "source": "iana" + }, + "application/ld+json": { + "source": "iana", + "compressible": true, + "extensions": ["jsonld"] + }, + "application/link-format": { + "source": "iana" + }, + "application/load-control+xml": { + "source": "iana" + }, + "application/lost+xml": { + "source": "iana", + "extensions": ["lostxml"] + }, + "application/lostsync+xml": { + "source": "iana" + }, + "application/lxf": { + "source": "iana" + }, + "application/mac-binhex40": { + "source": "iana", + "extensions": ["hqx"] + }, + "application/mac-compactpro": { + "source": "apache", + "extensions": ["cpt"] + }, + "application/macwriteii": { + "source": "iana" + }, + "application/mads+xml": { + "source": "iana", + "extensions": ["mads"] + }, + "application/manifest+json": { + "charset": "UTF-8", + "compressible": true, + "extensions": ["webmanifest"] + }, + "application/marc": { + "source": "iana", + "extensions": ["mrc"] + }, + "application/marcxml+xml": { + "source": "iana", + "extensions": ["mrcx"] + }, + "application/mathematica": { + "source": "iana", + "extensions": ["ma","nb","mb"] + }, + "application/mathml+xml": { + "source": "iana", + "extensions": ["mathml"] + }, + "application/mathml-content+xml": { + "source": "iana" + }, + "application/mathml-presentation+xml": { + "source": "iana" + }, + "application/mbms-associated-procedure-description+xml": { + "source": "iana" + }, + "application/mbms-deregister+xml": { + "source": "iana" + }, + "application/mbms-envelope+xml": { + "source": "iana" + }, + "application/mbms-msk+xml": { + "source": "iana" + }, + "application/mbms-msk-response+xml": { + "source": "iana" + }, + "application/mbms-protection-description+xml": { + "source": "iana" + }, + "application/mbms-reception-report+xml": { + "source": "iana" + }, + "application/mbms-register+xml": { + "source": "iana" + }, + "application/mbms-register-response+xml": { + "source": "iana" + }, + "application/mbms-schedule+xml": { + "source": "iana" + }, + "application/mbms-user-service-description+xml": { + "source": "iana" + }, + "application/mbox": { + "source": "iana", + "extensions": ["mbox"] + }, + "application/media-policy-dataset+xml": { + "source": "iana" + }, + "application/media_control+xml": { + "source": "iana" + }, + "application/mediaservercontrol+xml": { + "source": "iana", + "extensions": ["mscml"] + }, + "application/merge-patch+json": { + "source": "iana", + "compressible": true + }, + "application/metalink+xml": { + "source": "apache", + "extensions": ["metalink"] + }, + "application/metalink4+xml": { + "source": "iana", + "extensions": ["meta4"] + }, + "application/mets+xml": { + "source": "iana", + "extensions": ["mets"] + }, + "application/mf4": { + "source": "iana" + }, + "application/mikey": { + "source": "iana" + }, + "application/mods+xml": { + "source": "iana", + "extensions": ["mods"] + }, + "application/moss-keys": { + "source": "iana" + }, + "application/moss-signature": { + "source": "iana" + }, + "application/mosskey-data": { + "source": "iana" + }, + "application/mosskey-request": { + "source": "iana" + }, + "application/mp21": { + "source": "iana", + "extensions": ["m21","mp21"] + }, + "application/mp4": { + "source": "iana", + "extensions": ["mp4s","m4p"] + }, + "application/mpeg4-generic": { + "source": "iana" + }, + "application/mpeg4-iod": { + "source": "iana" + }, + "application/mpeg4-iod-xmt": { + "source": "iana" + }, + "application/mrb-consumer+xml": { + "source": "iana" + }, + "application/mrb-publish+xml": { + "source": "iana" + }, + "application/msc-ivr+xml": { + "source": "iana" + }, + "application/msc-mixer+xml": { + "source": "iana" + }, + "application/msword": { + "source": "iana", + "compressible": false, + "extensions": ["doc","dot"] + }, + "application/mxf": { + "source": "iana", + "extensions": ["mxf"] + }, + "application/nasdata": { + "source": "iana" + }, + "application/news-checkgroups": { + "source": "iana" + }, + "application/news-groupinfo": { + "source": "iana" + }, + "application/news-transmission": { + "source": "iana" + }, + "application/nlsml+xml": { + "source": "iana" + }, + "application/nss": { + "source": "iana" + }, + "application/ocsp-request": { + "source": "iana" + }, + "application/ocsp-response": { + "source": "iana" + }, + "application/octet-stream": { + "source": "iana", + "compressible": false, + "extensions": ["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","buffer"] + }, + "application/oda": { + "source": "iana", + "extensions": ["oda"] + }, + "application/odx": { + "source": "iana" + }, + "application/oebps-package+xml": { + "source": "iana", + "extensions": ["opf"] + }, + "application/ogg": { + "source": "iana", + "compressible": false, + "extensions": ["ogx"] + }, + "application/omdoc+xml": { + "source": "apache", + "extensions": ["omdoc"] + }, + "application/onenote": { + "source": "apache", + "extensions": ["onetoc","onetoc2","onetmp","onepkg"] + }, + "application/oxps": { + "source": "iana", + "extensions": ["oxps"] + }, + "application/p2p-overlay+xml": { + "source": "iana" + }, + "application/parityfec": { + "source": "iana" + }, + "application/patch-ops-error+xml": { + "source": "iana", + "extensions": ["xer"] + }, + "application/pdf": { + "source": "iana", + "compressible": false, + "extensions": ["pdf"] + }, + "application/pdx": { + "source": "iana" + }, + "application/pgp-encrypted": { + "source": "iana", + "compressible": false, + "extensions": ["pgp"] + }, + "application/pgp-keys": { + "source": "iana" + }, + "application/pgp-signature": { + "source": "iana", + "extensions": ["asc","sig"] + }, + "application/pics-rules": { + "source": "apache", + "extensions": ["prf"] + }, + "application/pidf+xml": { + "source": "iana" + }, + "application/pidf-diff+xml": { + "source": "iana" + }, + "application/pkcs10": { + "source": "iana", + "extensions": ["p10"] + }, + "application/pkcs7-mime": { + "source": "iana", + "extensions": ["p7m","p7c"] + }, + "application/pkcs7-signature": { + "source": "iana", + "extensions": ["p7s"] + }, + "application/pkcs8": { + "source": "iana", + "extensions": ["p8"] + }, + "application/pkix-attr-cert": { + "source": "iana", + "extensions": ["ac"] + }, + "application/pkix-cert": { + "source": "iana", + "extensions": ["cer"] + }, + "application/pkix-crl": { + "source": "iana", + "extensions": ["crl"] + }, + "application/pkix-pkipath": { + "source": "iana", + "extensions": ["pkipath"] + }, + "application/pkixcmp": { + "source": "iana", + "extensions": ["pki"] + }, + "application/pls+xml": { + "source": "iana", + "extensions": ["pls"] + }, + "application/poc-settings+xml": { + "source": "iana" + }, + "application/postscript": { + "source": "iana", + "compressible": true, + "extensions": ["ai","eps","ps"] + }, + "application/provenance+xml": { + "source": "iana" + }, + "application/prs.alvestrand.titrax-sheet": { + "source": "iana" + }, + "application/prs.cww": { + "source": "iana", + "extensions": ["cww"] + }, + "application/prs.hpub+zip": { + "source": "iana" + }, + "application/prs.nprend": { + "source": "iana" + }, + "application/prs.plucker": { + "source": "iana" + }, + "application/prs.rdf-xml-crypt": { + "source": "iana" + }, + "application/prs.xsf+xml": { + "source": "iana" + }, + "application/pskc+xml": { + "source": "iana", + "extensions": ["pskcxml"] + }, + "application/qsig": { + "source": "iana" + }, + "application/raptorfec": { + "source": "iana" + }, + "application/rdap+json": { + "source": "iana", + "compressible": true + }, + "application/rdf+xml": { + "source": "iana", + "compressible": true, + "extensions": ["rdf"] + }, + "application/reginfo+xml": { + "source": "iana", + "extensions": ["rif"] + }, + "application/relax-ng-compact-syntax": { + "source": "iana", + "extensions": ["rnc"] + }, + "application/remote-printing": { + "source": "iana" + }, + "application/reputon+json": { + "source": "iana", + "compressible": true + }, + "application/resource-lists+xml": { + "source": "iana", + "extensions": ["rl"] + }, + "application/resource-lists-diff+xml": { + "source": "iana", + "extensions": ["rld"] + }, + "application/riscos": { + "source": "iana" + }, + "application/rlmi+xml": { + "source": "iana" + }, + "application/rls-services+xml": { + "source": "iana", + "extensions": ["rs"] + }, + "application/rpki-ghostbusters": { + "source": "iana", + "extensions": ["gbr"] + }, + "application/rpki-manifest": { + "source": "iana", + "extensions": ["mft"] + }, + "application/rpki-roa": { + "source": "iana", + "extensions": ["roa"] + }, + "application/rpki-updown": { + "source": "iana" + }, + "application/rsd+xml": { + "source": "apache", + "extensions": ["rsd"] + }, + "application/rss+xml": { + "source": "apache", + "compressible": true, + "extensions": ["rss"] + }, + "application/rtf": { + "source": "iana", + "compressible": true, + "extensions": ["rtf"] + }, + "application/rtploopback": { + "source": "iana" + }, + "application/rtx": { + "source": "iana" + }, + "application/samlassertion+xml": { + "source": "iana" + }, + "application/samlmetadata+xml": { + "source": "iana" + }, + "application/sbml+xml": { + "source": "iana", + "extensions": ["sbml"] + }, + "application/scaip+xml": { + "source": "iana" + }, + "application/scvp-cv-request": { + "source": "iana", + "extensions": ["scq"] + }, + "application/scvp-cv-response": { + "source": "iana", + "extensions": ["scs"] + }, + "application/scvp-vp-request": { + "source": "iana", + "extensions": ["spq"] + }, + "application/scvp-vp-response": { + "source": "iana", + "extensions": ["spp"] + }, + "application/sdp": { + "source": "iana", + "extensions": ["sdp"] + }, + "application/sep+xml": { + "source": "iana" + }, + "application/sep-exi": { + "source": "iana" + }, + "application/session-info": { + "source": "iana" + }, + "application/set-payment": { + "source": "iana" + }, + "application/set-payment-initiation": { + "source": "iana", + "extensions": ["setpay"] + }, + "application/set-registration": { + "source": "iana" + }, + "application/set-registration-initiation": { + "source": "iana", + "extensions": ["setreg"] + }, + "application/sgml": { + "source": "iana" + }, + "application/sgml-open-catalog": { + "source": "iana" + }, + "application/shf+xml": { + "source": "iana", + "extensions": ["shf"] + }, + "application/sieve": { + "source": "iana" + }, + "application/simple-filter+xml": { + "source": "iana" + }, + "application/simple-message-summary": { + "source": "iana" + }, + "application/simplesymbolcontainer": { + "source": "iana" + }, + "application/slate": { + "source": "iana" + }, + "application/smil": { + "source": "iana" + }, + "application/smil+xml": { + "source": "iana", + "extensions": ["smi","smil"] + }, + "application/smpte336m": { + "source": "iana" + }, + "application/soap+fastinfoset": { + "source": "iana" + }, + "application/soap+xml": { + "source": "iana", + "compressible": true + }, + "application/sparql-query": { + "source": "iana", + "extensions": ["rq"] + }, + "application/sparql-results+xml": { + "source": "iana", + "extensions": ["srx"] + }, + "application/spirits-event+xml": { + "source": "iana" + }, + "application/sql": { + "source": "iana" + }, + "application/srgs": { + "source": "iana", + "extensions": ["gram"] + }, + "application/srgs+xml": { + "source": "iana", + "extensions": ["grxml"] + }, + "application/sru+xml": { + "source": "iana", + "extensions": ["sru"] + }, + "application/ssdl+xml": { + "source": "apache", + "extensions": ["ssdl"] + }, + "application/ssml+xml": { + "source": "iana", + "extensions": ["ssml"] + }, + "application/tamp-apex-update": { + "source": "iana" + }, + "application/tamp-apex-update-confirm": { + "source": "iana" + }, + "application/tamp-community-update": { + "source": "iana" + }, + "application/tamp-community-update-confirm": { + "source": "iana" + }, + "application/tamp-error": { + "source": "iana" + }, + "application/tamp-sequence-adjust": { + "source": "iana" + }, + "application/tamp-sequence-adjust-confirm": { + "source": "iana" + }, + "application/tamp-status-query": { + "source": "iana" + }, + "application/tamp-status-response": { + "source": "iana" + }, + "application/tamp-update": { + "source": "iana" + }, + "application/tamp-update-confirm": { + "source": "iana" + }, + "application/tar": { + "compressible": true + }, + "application/tei+xml": { + "source": "iana", + "extensions": ["tei","teicorpus"] + }, + "application/thraud+xml": { + "source": "iana", + "extensions": ["tfi"] + }, + "application/timestamp-query": { + "source": "iana" + }, + "application/timestamp-reply": { + "source": "iana" + }, + "application/timestamped-data": { + "source": "iana", + "extensions": ["tsd"] + }, + "application/ttml+xml": { + "source": "iana" + }, + "application/tve-trigger": { + "source": "iana" + }, + "application/ulpfec": { + "source": "iana" + }, + "application/urc-grpsheet+xml": { + "source": "iana" + }, + "application/urc-ressheet+xml": { + "source": "iana" + }, + "application/urc-targetdesc+xml": { + "source": "iana" + }, + "application/urc-uisocketdesc+xml": { + "source": "iana" + }, + "application/vcard+json": { + "source": "iana", + "compressible": true + }, + "application/vcard+xml": { + "source": "iana" + }, + "application/vemmi": { + "source": "iana" + }, + "application/vividence.scriptfile": { + "source": "apache" + }, + "application/vnd.3gpp.bsf+xml": { + "source": "iana" + }, + "application/vnd.3gpp.pic-bw-large": { + "source": "iana", + "extensions": ["plb"] + }, + "application/vnd.3gpp.pic-bw-small": { + "source": "iana", + "extensions": ["psb"] + }, + "application/vnd.3gpp.pic-bw-var": { + "source": "iana", + "extensions": ["pvb"] + }, + "application/vnd.3gpp.sms": { + "source": "iana" + }, + "application/vnd.3gpp2.bcmcsinfo+xml": { + "source": "iana" + }, + "application/vnd.3gpp2.sms": { + "source": "iana" + }, + "application/vnd.3gpp2.tcap": { + "source": "iana", + "extensions": ["tcap"] + }, + "application/vnd.3m.post-it-notes": { + "source": "iana", + "extensions": ["pwn"] + }, + "application/vnd.accpac.simply.aso": { + "source": "iana", + "extensions": ["aso"] + }, + "application/vnd.accpac.simply.imp": { + "source": "iana", + "extensions": ["imp"] + }, + "application/vnd.acucobol": { + "source": "iana", + "extensions": ["acu"] + }, + "application/vnd.acucorp": { + "source": "iana", + "extensions": ["atc","acutc"] + }, + "application/vnd.adobe.air-application-installer-package+zip": { + "source": "apache", + "extensions": ["air"] + }, + "application/vnd.adobe.flash.movie": { + "source": "iana" + }, + "application/vnd.adobe.formscentral.fcdt": { + "source": "iana", + "extensions": ["fcdt"] + }, + "application/vnd.adobe.fxp": { + "source": "iana", + "extensions": ["fxp","fxpl"] + }, + "application/vnd.adobe.partial-upload": { + "source": "iana" + }, + "application/vnd.adobe.xdp+xml": { + "source": "iana", + "extensions": ["xdp"] + }, + "application/vnd.adobe.xfdf": { + "source": "iana", + "extensions": ["xfdf"] + }, + "application/vnd.aether.imp": { + "source": "iana" + }, + "application/vnd.ah-barcode": { + "source": "iana" + }, + "application/vnd.ahead.space": { + "source": "iana", + "extensions": ["ahead"] + }, + "application/vnd.airzip.filesecure.azf": { + "source": "iana", + "extensions": ["azf"] + }, + "application/vnd.airzip.filesecure.azs": { + "source": "iana", + "extensions": ["azs"] + }, + "application/vnd.amazon.ebook": { + "source": "apache", + "extensions": ["azw"] + }, + "application/vnd.americandynamics.acc": { + "source": "iana", + "extensions": ["acc"] + }, + "application/vnd.amiga.ami": { + "source": "iana", + "extensions": ["ami"] + }, + "application/vnd.amundsen.maze+xml": { + "source": "iana" + }, + "application/vnd.android.package-archive": { + "source": "apache", + "compressible": false, + "extensions": ["apk"] + }, + "application/vnd.anser-web-certificate-issue-initiation": { + "source": "iana", + "extensions": ["cii"] + }, + "application/vnd.anser-web-funds-transfer-initiation": { + "source": "apache", + "extensions": ["fti"] + }, + "application/vnd.antix.game-component": { + "source": "iana", + "extensions": ["atx"] + }, + "application/vnd.apache.thrift.binary": { + "source": "iana" + }, + "application/vnd.apache.thrift.compact": { + "source": "iana" + }, + "application/vnd.apache.thrift.json": { + "source": "iana" + }, + "application/vnd.api+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.apple.installer+xml": { + "source": "iana", + "extensions": ["mpkg"] + }, + "application/vnd.apple.mpegurl": { + "source": "iana", + "extensions": ["m3u8"] + }, + "application/vnd.arastra.swi": { + "source": "iana" + }, + "application/vnd.aristanetworks.swi": { + "source": "iana", + "extensions": ["swi"] + }, + "application/vnd.artsquare": { + "source": "iana" + }, + "application/vnd.astraea-software.iota": { + "source": "iana", + "extensions": ["iota"] + }, + "application/vnd.audiograph": { + "source": "iana", + "extensions": ["aep"] + }, + "application/vnd.autopackage": { + "source": "iana" + }, + "application/vnd.avistar+xml": { + "source": "iana" + }, + "application/vnd.balsamiq.bmml+xml": { + "source": "iana" + }, + "application/vnd.balsamiq.bmpr": { + "source": "iana" + }, + "application/vnd.bekitzur-stech+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.blueice.multipass": { + "source": "iana", + "extensions": ["mpm"] + }, + "application/vnd.bluetooth.ep.oob": { + "source": "iana" + }, + "application/vnd.bluetooth.le.oob": { + "source": "iana" + }, + "application/vnd.bmi": { + "source": "iana", + "extensions": ["bmi"] + }, + "application/vnd.businessobjects": { + "source": "iana", + "extensions": ["rep"] + }, + "application/vnd.cab-jscript": { + "source": "iana" + }, + "application/vnd.canon-cpdl": { + "source": "iana" + }, + "application/vnd.canon-lips": { + "source": "iana" + }, + "application/vnd.cendio.thinlinc.clientconf": { + "source": "iana" + }, + "application/vnd.century-systems.tcp_stream": { + "source": "iana" + }, + "application/vnd.chemdraw+xml": { + "source": "iana", + "extensions": ["cdxml"] + }, + "application/vnd.chipnuts.karaoke-mmd": { + "source": "iana", + "extensions": ["mmd"] + }, + "application/vnd.cinderella": { + "source": "iana", + "extensions": ["cdy"] + }, + "application/vnd.cirpack.isdn-ext": { + "source": "iana" + }, + "application/vnd.citationstyles.style+xml": { + "source": "iana" + }, + "application/vnd.claymore": { + "source": "iana", + "extensions": ["cla"] + }, + "application/vnd.cloanto.rp9": { + "source": "iana", + "extensions": ["rp9"] + }, + "application/vnd.clonk.c4group": { + "source": "iana", + "extensions": ["c4g","c4d","c4f","c4p","c4u"] + }, + "application/vnd.cluetrust.cartomobile-config": { + "source": "iana", + "extensions": ["c11amc"] + }, + "application/vnd.cluetrust.cartomobile-config-pkg": { + "source": "iana", + "extensions": ["c11amz"] + }, + "application/vnd.coffeescript": { + "source": "iana" + }, + "application/vnd.collection+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.collection.doc+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.collection.next+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.commerce-battelle": { + "source": "iana" + }, + "application/vnd.commonspace": { + "source": "iana", + "extensions": ["csp"] + }, + "application/vnd.contact.cmsg": { + "source": "iana", + "extensions": ["cdbcmsg"] + }, + "application/vnd.cosmocaller": { + "source": "iana", + "extensions": ["cmc"] + }, + "application/vnd.crick.clicker": { + "source": "iana", + "extensions": ["clkx"] + }, + "application/vnd.crick.clicker.keyboard": { + "source": "iana", + "extensions": ["clkk"] + }, + "application/vnd.crick.clicker.palette": { + "source": "iana", + "extensions": ["clkp"] + }, + "application/vnd.crick.clicker.template": { + "source": "iana", + "extensions": ["clkt"] + }, + "application/vnd.crick.clicker.wordbank": { + "source": "iana", + "extensions": ["clkw"] + }, + "application/vnd.criticaltools.wbs+xml": { + "source": "iana", + "extensions": ["wbs"] + }, + "application/vnd.ctc-posml": { + "source": "iana", + "extensions": ["pml"] + }, + "application/vnd.ctct.ws+xml": { + "source": "iana" + }, + "application/vnd.cups-pdf": { + "source": "iana" + }, + "application/vnd.cups-postscript": { + "source": "iana" + }, + "application/vnd.cups-ppd": { + "source": "iana", + "extensions": ["ppd"] + }, + "application/vnd.cups-raster": { + "source": "iana" + }, + "application/vnd.cups-raw": { + "source": "iana" + }, + "application/vnd.curl": { + "source": "iana" + }, + "application/vnd.curl.car": { + "source": "apache", + "extensions": ["car"] + }, + "application/vnd.curl.pcurl": { + "source": "apache", + "extensions": ["pcurl"] + }, + "application/vnd.cyan.dean.root+xml": { + "source": "iana" + }, + "application/vnd.cybank": { + "source": "iana" + }, + "application/vnd.dart": { + "source": "iana", + "compressible": true, + "extensions": ["dart"] + }, + "application/vnd.data-vision.rdz": { + "source": "iana", + "extensions": ["rdz"] + }, + "application/vnd.debian.binary-package": { + "source": "iana" + }, + "application/vnd.dece.data": { + "source": "iana", + "extensions": ["uvf","uvvf","uvd","uvvd"] + }, + "application/vnd.dece.ttml+xml": { + "source": "iana", + "extensions": ["uvt","uvvt"] + }, + "application/vnd.dece.unspecified": { + "source": "iana", + "extensions": ["uvx","uvvx"] + }, + "application/vnd.dece.zip": { + "source": "iana", + "extensions": ["uvz","uvvz"] + }, + "application/vnd.denovo.fcselayout-link": { + "source": "iana", + "extensions": ["fe_launch"] + }, + "application/vnd.desmume-movie": { + "source": "iana" + }, + "application/vnd.dir-bi.plate-dl-nosuffix": { + "source": "iana" + }, + "application/vnd.dm.delegation+xml": { + "source": "iana" + }, + "application/vnd.dna": { + "source": "iana", + "extensions": ["dna"] + }, + "application/vnd.document+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.dolby.mlp": { + "source": "apache", + "extensions": ["mlp"] + }, + "application/vnd.dolby.mobile.1": { + "source": "iana" + }, + "application/vnd.dolby.mobile.2": { + "source": "iana" + }, + "application/vnd.doremir.scorecloud-binary-document": { + "source": "iana" + }, + "application/vnd.dpgraph": { + "source": "iana", + "extensions": ["dpg"] + }, + "application/vnd.dreamfactory": { + "source": "iana", + "extensions": ["dfac"] + }, + "application/vnd.ds-keypoint": { + "source": "apache", + "extensions": ["kpxx"] + }, + "application/vnd.dtg.local": { + "source": "iana" + }, + "application/vnd.dtg.local.flash": { + "source": "iana" + }, + "application/vnd.dtg.local.html": { + "source": "iana" + }, + "application/vnd.dvb.ait": { + "source": "iana", + "extensions": ["ait"] + }, + "application/vnd.dvb.dvbj": { + "source": "iana" + }, + "application/vnd.dvb.esgcontainer": { + "source": "iana" + }, + "application/vnd.dvb.ipdcdftnotifaccess": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgaccess": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgaccess2": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgpdd": { + "source": "iana" + }, + "application/vnd.dvb.ipdcroaming": { + "source": "iana" + }, + "application/vnd.dvb.iptv.alfec-base": { + "source": "iana" + }, + "application/vnd.dvb.iptv.alfec-enhancement": { + "source": "iana" + }, + "application/vnd.dvb.notif-aggregate-root+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-container+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-generic+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-ia-msglist+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-ia-registration-request+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-ia-registration-response+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-init+xml": { + "source": "iana" + }, + "application/vnd.dvb.pfr": { + "source": "iana" + }, + "application/vnd.dvb.service": { + "source": "iana", + "extensions": ["svc"] + }, + "application/vnd.dxr": { + "source": "iana" + }, + "application/vnd.dynageo": { + "source": "iana", + "extensions": ["geo"] + }, + "application/vnd.dzr": { + "source": "iana" + }, + "application/vnd.easykaraoke.cdgdownload": { + "source": "iana" + }, + "application/vnd.ecdis-update": { + "source": "iana" + }, + "application/vnd.ecowin.chart": { + "source": "iana", + "extensions": ["mag"] + }, + "application/vnd.ecowin.filerequest": { + "source": "iana" + }, + "application/vnd.ecowin.fileupdate": { + "source": "iana" + }, + "application/vnd.ecowin.series": { + "source": "iana" + }, + "application/vnd.ecowin.seriesrequest": { + "source": "iana" + }, + "application/vnd.ecowin.seriesupdate": { + "source": "iana" + }, + "application/vnd.emclient.accessrequest+xml": { + "source": "iana" + }, + "application/vnd.enliven": { + "source": "iana", + "extensions": ["nml"] + }, + "application/vnd.enphase.envoy": { + "source": "iana" + }, + "application/vnd.eprints.data+xml": { + "source": "iana" + }, + "application/vnd.epson.esf": { + "source": "iana", + "extensions": ["esf"] + }, + "application/vnd.epson.msf": { + "source": "iana", + "extensions": ["msf"] + }, + "application/vnd.epson.quickanime": { + "source": "iana", + "extensions": ["qam"] + }, + "application/vnd.epson.salt": { + "source": "iana", + "extensions": ["slt"] + }, + "application/vnd.epson.ssf": { + "source": "iana", + "extensions": ["ssf"] + }, + "application/vnd.ericsson.quickcall": { + "source": "iana" + }, + "application/vnd.eszigno3+xml": { + "source": "iana", + "extensions": ["es3","et3"] + }, + "application/vnd.etsi.aoc+xml": { + "source": "iana" + }, + "application/vnd.etsi.asic-e+zip": { + "source": "iana" + }, + "application/vnd.etsi.asic-s+zip": { + "source": "iana" + }, + "application/vnd.etsi.cug+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvcommand+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvdiscovery+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvprofile+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsad-bc+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsad-cod+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsad-npvr+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvservice+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsync+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvueprofile+xml": { + "source": "iana" + }, + "application/vnd.etsi.mcid+xml": { + "source": "iana" + }, + "application/vnd.etsi.mheg5": { + "source": "iana" + }, + "application/vnd.etsi.overload-control-policy-dataset+xml": { + "source": "iana" + }, + "application/vnd.etsi.pstn+xml": { + "source": "iana" + }, + "application/vnd.etsi.sci+xml": { + "source": "iana" + }, + "application/vnd.etsi.simservs+xml": { + "source": "iana" + }, + "application/vnd.etsi.timestamp-token": { + "source": "iana" + }, + "application/vnd.etsi.tsl+xml": { + "source": "iana" + }, + "application/vnd.etsi.tsl.der": { + "source": "iana" + }, + "application/vnd.eudora.data": { + "source": "iana" + }, + "application/vnd.ezpix-album": { + "source": "iana", + "extensions": ["ez2"] + }, + "application/vnd.ezpix-package": { + "source": "iana", + "extensions": ["ez3"] + }, + "application/vnd.f-secure.mobile": { + "source": "iana" + }, + "application/vnd.fastcopy-disk-image": { + "source": "iana" + }, + "application/vnd.fdf": { + "source": "iana", + "extensions": ["fdf"] + }, + "application/vnd.fdsn.mseed": { + "source": "iana", + "extensions": ["mseed"] + }, + "application/vnd.fdsn.seed": { + "source": "iana", + "extensions": ["seed","dataless"] + }, + "application/vnd.ffsns": { + "source": "iana" + }, + "application/vnd.fints": { + "source": "iana" + }, + "application/vnd.flographit": { + "source": "iana", + "extensions": ["gph"] + }, + "application/vnd.fluxtime.clip": { + "source": "iana", + "extensions": ["ftc"] + }, + "application/vnd.font-fontforge-sfd": { + "source": "iana" + }, + "application/vnd.framemaker": { + "source": "iana", + "extensions": ["fm","frame","maker","book"] + }, + "application/vnd.frogans.fnc": { + "source": "iana", + "extensions": ["fnc"] + }, + "application/vnd.frogans.ltf": { + "source": "iana", + "extensions": ["ltf"] + }, + "application/vnd.fsc.weblaunch": { + "source": "iana", + "extensions": ["fsc"] + }, + "application/vnd.fujitsu.oasys": { + "source": "iana", + "extensions": ["oas"] + }, + "application/vnd.fujitsu.oasys2": { + "source": "iana", + "extensions": ["oa2"] + }, + "application/vnd.fujitsu.oasys3": { + "source": "iana", + "extensions": ["oa3"] + }, + "application/vnd.fujitsu.oasysgp": { + "source": "iana", + "extensions": ["fg5"] + }, + "application/vnd.fujitsu.oasysprs": { + "source": "iana", + "extensions": ["bh2"] + }, + "application/vnd.fujixerox.art-ex": { + "source": "iana" + }, + "application/vnd.fujixerox.art4": { + "source": "iana" + }, + "application/vnd.fujixerox.ddd": { + "source": "iana", + "extensions": ["ddd"] + }, + "application/vnd.fujixerox.docuworks": { + "source": "iana", + "extensions": ["xdw"] + }, + "application/vnd.fujixerox.docuworks.binder": { + "source": "iana", + "extensions": ["xbd"] + }, + "application/vnd.fujixerox.docuworks.container": { + "source": "iana" + }, + "application/vnd.fujixerox.hbpl": { + "source": "iana" + }, + "application/vnd.fut-misnet": { + "source": "iana" + }, + "application/vnd.fuzzysheet": { + "source": "iana", + "extensions": ["fzs"] + }, + "application/vnd.genomatix.tuxedo": { + "source": "iana", + "extensions": ["txd"] + }, + "application/vnd.geo+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.geocube+xml": { + "source": "iana" + }, + "application/vnd.geogebra.file": { + "source": "iana", + "extensions": ["ggb"] + }, + "application/vnd.geogebra.tool": { + "source": "iana", + "extensions": ["ggt"] + }, + "application/vnd.geometry-explorer": { + "source": "iana", + "extensions": ["gex","gre"] + }, + "application/vnd.geonext": { + "source": "iana", + "extensions": ["gxt"] + }, + "application/vnd.geoplan": { + "source": "iana", + "extensions": ["g2w"] + }, + "application/vnd.geospace": { + "source": "iana", + "extensions": ["g3w"] + }, + "application/vnd.gerber": { + "source": "iana" + }, + "application/vnd.globalplatform.card-content-mgt": { + "source": "iana" + }, + "application/vnd.globalplatform.card-content-mgt-response": { + "source": "iana" + }, + "application/vnd.gmx": { + "source": "iana", + "extensions": ["gmx"] + }, + "application/vnd.google-earth.kml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["kml"] + }, + "application/vnd.google-earth.kmz": { + "source": "iana", + "compressible": false, + "extensions": ["kmz"] + }, + "application/vnd.gov.sk.e-form+xml": { + "source": "iana" + }, + "application/vnd.gov.sk.e-form+zip": { + "source": "iana" + }, + "application/vnd.gov.sk.xmldatacontainer+xml": { + "source": "iana" + }, + "application/vnd.grafeq": { + "source": "iana", + "extensions": ["gqf","gqs"] + }, + "application/vnd.gridmp": { + "source": "iana" + }, + "application/vnd.groove-account": { + "source": "iana", + "extensions": ["gac"] + }, + "application/vnd.groove-help": { + "source": "iana", + "extensions": ["ghf"] + }, + "application/vnd.groove-identity-message": { + "source": "iana", + "extensions": ["gim"] + }, + "application/vnd.groove-injector": { + "source": "iana", + "extensions": ["grv"] + }, + "application/vnd.groove-tool-message": { + "source": "iana", + "extensions": ["gtm"] + }, + "application/vnd.groove-tool-template": { + "source": "iana", + "extensions": ["tpl"] + }, + "application/vnd.groove-vcard": { + "source": "iana", + "extensions": ["vcg"] + }, + "application/vnd.hal+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hal+xml": { + "source": "iana", + "extensions": ["hal"] + }, + "application/vnd.handheld-entertainment+xml": { + "source": "iana", + "extensions": ["zmm"] + }, + "application/vnd.hbci": { + "source": "iana", + "extensions": ["hbci"] + }, + "application/vnd.hcl-bireports": { + "source": "iana" + }, + "application/vnd.heroku+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hhe.lesson-player": { + "source": "iana", + "extensions": ["les"] + }, + "application/vnd.hp-hpgl": { + "source": "iana", + "extensions": ["hpgl"] + }, + "application/vnd.hp-hpid": { + "source": "iana", + "extensions": ["hpid"] + }, + "application/vnd.hp-hps": { + "source": "iana", + "extensions": ["hps"] + }, + "application/vnd.hp-jlyt": { + "source": "iana", + "extensions": ["jlt"] + }, + "application/vnd.hp-pcl": { + "source": "iana", + "extensions": ["pcl"] + }, + "application/vnd.hp-pclxl": { + "source": "iana", + "extensions": ["pclxl"] + }, + "application/vnd.httphone": { + "source": "iana" + }, + "application/vnd.hydrostatix.sof-data": { + "source": "iana", + "extensions": ["sfd-hdstx"] + }, + "application/vnd.hyperdrive+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hzn-3d-crossword": { + "source": "iana" + }, + "application/vnd.ibm.afplinedata": { + "source": "iana" + }, + "application/vnd.ibm.electronic-media": { + "source": "iana" + }, + "application/vnd.ibm.minipay": { + "source": "iana", + "extensions": ["mpy"] + }, + "application/vnd.ibm.modcap": { + "source": "iana", + "extensions": ["afp","listafp","list3820"] + }, + "application/vnd.ibm.rights-management": { + "source": "iana", + "extensions": ["irm"] + }, + "application/vnd.ibm.secure-container": { + "source": "iana", + "extensions": ["sc"] + }, + "application/vnd.iccprofile": { + "source": "iana", + "extensions": ["icc","icm"] + }, + "application/vnd.ieee.1905": { + "source": "iana" + }, + "application/vnd.igloader": { + "source": "iana", + "extensions": ["igl"] + }, + "application/vnd.immervision-ivp": { + "source": "iana", + "extensions": ["ivp"] + }, + "application/vnd.immervision-ivu": { + "source": "iana", + "extensions": ["ivu"] + }, + "application/vnd.ims.imsccv1p1": { + "source": "iana" + }, + "application/vnd.ims.imsccv1p2": { + "source": "iana" + }, + "application/vnd.ims.imsccv1p3": { + "source": "iana" + }, + "application/vnd.ims.lis.v2.result+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolconsumerprofile+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolproxy+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolproxy.id+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolsettings+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolsettings.simple+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.informedcontrol.rms+xml": { + "source": "iana" + }, + "application/vnd.informix-visionary": { + "source": "iana" + }, + "application/vnd.infotech.project": { + "source": "iana" + }, + "application/vnd.infotech.project+xml": { + "source": "iana" + }, + "application/vnd.innopath.wamp.notification": { + "source": "iana" + }, + "application/vnd.insors.igm": { + "source": "iana", + "extensions": ["igm"] + }, + "application/vnd.intercon.formnet": { + "source": "iana", + "extensions": ["xpw","xpx"] + }, + "application/vnd.intergeo": { + "source": "iana", + "extensions": ["i2g"] + }, + "application/vnd.intertrust.digibox": { + "source": "iana" + }, + "application/vnd.intertrust.nncp": { + "source": "iana" + }, + "application/vnd.intu.qbo": { + "source": "iana", + "extensions": ["qbo"] + }, + "application/vnd.intu.qfx": { + "source": "iana", + "extensions": ["qfx"] + }, + "application/vnd.iptc.g2.catalogitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.conceptitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.knowledgeitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.newsitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.newsmessage+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.packageitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.planningitem+xml": { + "source": "iana" + }, + "application/vnd.ipunplugged.rcprofile": { + "source": "iana", + "extensions": ["rcprofile"] + }, + "application/vnd.irepository.package+xml": { + "source": "iana", + "extensions": ["irp"] + }, + "application/vnd.is-xpr": { + "source": "iana", + "extensions": ["xpr"] + }, + "application/vnd.isac.fcs": { + "source": "iana", + "extensions": ["fcs"] + }, + "application/vnd.jam": { + "source": "iana", + "extensions": ["jam"] + }, + "application/vnd.japannet-directory-service": { + "source": "iana" + }, + "application/vnd.japannet-jpnstore-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-payment-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-registration": { + "source": "iana" + }, + "application/vnd.japannet-registration-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-setstore-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-verification": { + "source": "iana" + }, + "application/vnd.japannet-verification-wakeup": { + "source": "iana" + }, + "application/vnd.jcp.javame.midlet-rms": { + "source": "iana", + "extensions": ["rms"] + }, + "application/vnd.jisp": { + "source": "iana", + "extensions": ["jisp"] + }, + "application/vnd.joost.joda-archive": { + "source": "iana", + "extensions": ["joda"] + }, + "application/vnd.jsk.isdn-ngn": { + "source": "iana" + }, + "application/vnd.kahootz": { + "source": "iana", + "extensions": ["ktz","ktr"] + }, + "application/vnd.kde.karbon": { + "source": "iana", + "extensions": ["karbon"] + }, + "application/vnd.kde.kchart": { + "source": "iana", + "extensions": ["chrt"] + }, + "application/vnd.kde.kformula": { + "source": "iana", + "extensions": ["kfo"] + }, + "application/vnd.kde.kivio": { + "source": "iana", + "extensions": ["flw"] + }, + "application/vnd.kde.kontour": { + "source": "iana", + "extensions": ["kon"] + }, + "application/vnd.kde.kpresenter": { + "source": "iana", + "extensions": ["kpr","kpt"] + }, + "application/vnd.kde.kspread": { + "source": "iana", + "extensions": ["ksp"] + }, + "application/vnd.kde.kword": { + "source": "iana", + "extensions": ["kwd","kwt"] + }, + "application/vnd.kenameaapp": { + "source": "iana", + "extensions": ["htke"] + }, + "application/vnd.kidspiration": { + "source": "iana", + "extensions": ["kia"] + }, + "application/vnd.kinar": { + "source": "iana", + "extensions": ["kne","knp"] + }, + "application/vnd.koan": { + "source": "iana", + "extensions": ["skp","skd","skt","skm"] + }, + "application/vnd.kodak-descriptor": { + "source": "iana", + "extensions": ["sse"] + }, + "application/vnd.las.las+xml": { + "source": "iana", + "extensions": ["lasxml"] + }, + "application/vnd.liberty-request+xml": { + "source": "iana" + }, + "application/vnd.llamagraphics.life-balance.desktop": { + "source": "iana", + "extensions": ["lbd"] + }, + "application/vnd.llamagraphics.life-balance.exchange+xml": { + "source": "iana", + "extensions": ["lbe"] + }, + "application/vnd.lotus-1-2-3": { + "source": "iana", + "extensions": ["123"] + }, + "application/vnd.lotus-approach": { + "source": "iana", + "extensions": ["apr"] + }, + "application/vnd.lotus-freelance": { + "source": "iana", + "extensions": ["pre"] + }, + "application/vnd.lotus-notes": { + "source": "iana", + "extensions": ["nsf"] + }, + "application/vnd.lotus-organizer": { + "source": "iana", + "extensions": ["org"] + }, + "application/vnd.lotus-screencam": { + "source": "iana", + "extensions": ["scm"] + }, + "application/vnd.lotus-wordpro": { + "source": "iana", + "extensions": ["lwp"] + }, + "application/vnd.macports.portpkg": { + "source": "iana", + "extensions": ["portpkg"] + }, + "application/vnd.marlin.drm.actiontoken+xml": { + "source": "iana" + }, + "application/vnd.marlin.drm.conftoken+xml": { + "source": "iana" + }, + "application/vnd.marlin.drm.license+xml": { + "source": "iana" + }, + "application/vnd.marlin.drm.mdcf": { + "source": "iana" + }, + "application/vnd.mason+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.maxmind.maxmind-db": { + "source": "iana" + }, + "application/vnd.mcd": { + "source": "iana", + "extensions": ["mcd"] + }, + "application/vnd.medcalcdata": { + "source": "iana", + "extensions": ["mc1"] + }, + "application/vnd.mediastation.cdkey": { + "source": "iana", + "extensions": ["cdkey"] + }, + "application/vnd.meridian-slingshot": { + "source": "iana" + }, + "application/vnd.mfer": { + "source": "iana", + "extensions": ["mwf"] + }, + "application/vnd.mfmp": { + "source": "iana", + "extensions": ["mfm"] + }, + "application/vnd.micro+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.micrografx.flo": { + "source": "iana", + "extensions": ["flo"] + }, + "application/vnd.micrografx.igx": { + "source": "iana", + "extensions": ["igx"] + }, + "application/vnd.microsoft.portable-executable": { + "source": "iana" + }, + "application/vnd.miele+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.mif": { + "source": "iana", + "extensions": ["mif"] + }, + "application/vnd.minisoft-hp3000-save": { + "source": "iana" + }, + "application/vnd.mitsubishi.misty-guard.trustweb": { + "source": "iana" + }, + "application/vnd.mobius.daf": { + "source": "iana", + "extensions": ["daf"] + }, + "application/vnd.mobius.dis": { + "source": "iana", + "extensions": ["dis"] + }, + "application/vnd.mobius.mbk": { + "source": "iana", + "extensions": ["mbk"] + }, + "application/vnd.mobius.mqy": { + "source": "iana", + "extensions": ["mqy"] + }, + "application/vnd.mobius.msl": { + "source": "iana", + "extensions": ["msl"] + }, + "application/vnd.mobius.plc": { + "source": "iana", + "extensions": ["plc"] + }, + "application/vnd.mobius.txf": { + "source": "iana", + "extensions": ["txf"] + }, + "application/vnd.mophun.application": { + "source": "iana", + "extensions": ["mpn"] + }, + "application/vnd.mophun.certificate": { + "source": "iana", + "extensions": ["mpc"] + }, + "application/vnd.motorola.flexsuite": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.adsi": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.fis": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.gotap": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.kmr": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.ttc": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.wem": { + "source": "iana" + }, + "application/vnd.motorola.iprm": { + "source": "iana" + }, + "application/vnd.mozilla.xul+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xul"] + }, + "application/vnd.ms-3mfdocument": { + "source": "iana" + }, + "application/vnd.ms-artgalry": { + "source": "iana", + "extensions": ["cil"] + }, + "application/vnd.ms-asf": { + "source": "iana" + }, + "application/vnd.ms-cab-compressed": { + "source": "iana", + "extensions": ["cab"] + }, + "application/vnd.ms-color.iccprofile": { + "source": "apache" + }, + "application/vnd.ms-excel": { + "source": "iana", + "compressible": false, + "extensions": ["xls","xlm","xla","xlc","xlt","xlw"] + }, + "application/vnd.ms-excel.addin.macroenabled.12": { + "source": "iana", + "extensions": ["xlam"] + }, + "application/vnd.ms-excel.sheet.binary.macroenabled.12": { + "source": "iana", + "extensions": ["xlsb"] + }, + "application/vnd.ms-excel.sheet.macroenabled.12": { + "source": "iana", + "extensions": ["xlsm"] + }, + "application/vnd.ms-excel.template.macroenabled.12": { + "source": "iana", + "extensions": ["xltm"] + }, + "application/vnd.ms-fontobject": { + "source": "iana", + "compressible": true, + "extensions": ["eot"] + }, + "application/vnd.ms-htmlhelp": { + "source": "iana", + "extensions": ["chm"] + }, + "application/vnd.ms-ims": { + "source": "iana", + "extensions": ["ims"] + }, + "application/vnd.ms-lrm": { + "source": "iana", + "extensions": ["lrm"] + }, + "application/vnd.ms-office.activex+xml": { + "source": "iana" + }, + "application/vnd.ms-officetheme": { + "source": "iana", + "extensions": ["thmx"] + }, + "application/vnd.ms-opentype": { + "source": "apache", + "compressible": true + }, + "application/vnd.ms-package.obfuscated-opentype": { + "source": "apache" + }, + "application/vnd.ms-pki.seccat": { + "source": "apache", + "extensions": ["cat"] + }, + "application/vnd.ms-pki.stl": { + "source": "apache", + "extensions": ["stl"] + }, + "application/vnd.ms-playready.initiator+xml": { + "source": "iana" + }, + "application/vnd.ms-powerpoint": { + "source": "iana", + "compressible": false, + "extensions": ["ppt","pps","pot"] + }, + "application/vnd.ms-powerpoint.addin.macroenabled.12": { + "source": "iana", + "extensions": ["ppam"] + }, + "application/vnd.ms-powerpoint.presentation.macroenabled.12": { + "source": "iana", + "extensions": ["pptm"] + }, + "application/vnd.ms-powerpoint.slide.macroenabled.12": { + "source": "iana", + "extensions": ["sldm"] + }, + "application/vnd.ms-powerpoint.slideshow.macroenabled.12": { + "source": "iana", + "extensions": ["ppsm"] + }, + "application/vnd.ms-powerpoint.template.macroenabled.12": { + "source": "iana", + "extensions": ["potm"] + }, + "application/vnd.ms-printing.printticket+xml": { + "source": "apache" + }, + "application/vnd.ms-project": { + "source": "iana", + "extensions": ["mpp","mpt"] + }, + "application/vnd.ms-tnef": { + "source": "iana" + }, + "application/vnd.ms-windows.printerpairing": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.lic-chlg-req": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.lic-resp": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.meter-chlg-req": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.meter-resp": { + "source": "iana" + }, + "application/vnd.ms-word.document.macroenabled.12": { + "source": "iana", + "extensions": ["docm"] + }, + "application/vnd.ms-word.template.macroenabled.12": { + "source": "iana", + "extensions": ["dotm"] + }, + "application/vnd.ms-works": { + "source": "iana", + "extensions": ["wps","wks","wcm","wdb"] + }, + "application/vnd.ms-wpl": { + "source": "iana", + "extensions": ["wpl"] + }, + "application/vnd.ms-xpsdocument": { + "source": "iana", + "compressible": false, + "extensions": ["xps"] + }, + "application/vnd.msa-disk-image": { + "source": "iana" + }, + "application/vnd.mseq": { + "source": "iana", + "extensions": ["mseq"] + }, + "application/vnd.msign": { + "source": "iana" + }, + "application/vnd.multiad.creator": { + "source": "iana" + }, + "application/vnd.multiad.creator.cif": { + "source": "iana" + }, + "application/vnd.music-niff": { + "source": "iana" + }, + "application/vnd.musician": { + "source": "iana", + "extensions": ["mus"] + }, + "application/vnd.muvee.style": { + "source": "iana", + "extensions": ["msty"] + }, + "application/vnd.mynfc": { + "source": "iana", + "extensions": ["taglet"] + }, + "application/vnd.ncd.control": { + "source": "iana" + }, + "application/vnd.ncd.reference": { + "source": "iana" + }, + "application/vnd.nervana": { + "source": "iana" + }, + "application/vnd.netfpx": { + "source": "iana" + }, + "application/vnd.neurolanguage.nlu": { + "source": "iana", + "extensions": ["nlu"] + }, + "application/vnd.nintendo.nitro.rom": { + "source": "iana" + }, + "application/vnd.nintendo.snes.rom": { + "source": "iana" + }, + "application/vnd.nitf": { + "source": "iana", + "extensions": ["ntf","nitf"] + }, + "application/vnd.noblenet-directory": { + "source": "iana", + "extensions": ["nnd"] + }, + "application/vnd.noblenet-sealer": { + "source": "iana", + "extensions": ["nns"] + }, + "application/vnd.noblenet-web": { + "source": "iana", + "extensions": ["nnw"] + }, + "application/vnd.nokia.catalogs": { + "source": "iana" + }, + "application/vnd.nokia.conml+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.conml+xml": { + "source": "iana" + }, + "application/vnd.nokia.iptv.config+xml": { + "source": "iana" + }, + "application/vnd.nokia.isds-radio-presets": { + "source": "iana" + }, + "application/vnd.nokia.landmark+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.landmark+xml": { + "source": "iana" + }, + "application/vnd.nokia.landmarkcollection+xml": { + "source": "iana" + }, + "application/vnd.nokia.n-gage.ac+xml": { + "source": "iana" + }, + "application/vnd.nokia.n-gage.data": { + "source": "iana", + "extensions": ["ngdat"] + }, + "application/vnd.nokia.n-gage.symbian.install": { + "source": "iana", + "extensions": ["n-gage"] + }, + "application/vnd.nokia.ncd": { + "source": "iana" + }, + "application/vnd.nokia.pcd+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.pcd+xml": { + "source": "iana" + }, + "application/vnd.nokia.radio-preset": { + "source": "iana", + "extensions": ["rpst"] + }, + "application/vnd.nokia.radio-presets": { + "source": "iana", + "extensions": ["rpss"] + }, + "application/vnd.novadigm.edm": { + "source": "iana", + "extensions": ["edm"] + }, + "application/vnd.novadigm.edx": { + "source": "iana", + "extensions": ["edx"] + }, + "application/vnd.novadigm.ext": { + "source": "iana", + "extensions": ["ext"] + }, + "application/vnd.ntt-local.content-share": { + "source": "iana" + }, + "application/vnd.ntt-local.file-transfer": { + "source": "iana" + }, + "application/vnd.ntt-local.ogw_remote-access": { + "source": "iana" + }, + "application/vnd.ntt-local.sip-ta_remote": { + "source": "iana" + }, + "application/vnd.ntt-local.sip-ta_tcp_stream": { + "source": "iana" + }, + "application/vnd.oasis.opendocument.chart": { + "source": "iana", + "extensions": ["odc"] + }, + "application/vnd.oasis.opendocument.chart-template": { + "source": "iana", + "extensions": ["otc"] + }, + "application/vnd.oasis.opendocument.database": { + "source": "iana", + "extensions": ["odb"] + }, + "application/vnd.oasis.opendocument.formula": { + "source": "iana", + "extensions": ["odf"] + }, + "application/vnd.oasis.opendocument.formula-template": { + "source": "iana", + "extensions": ["odft"] + }, + "application/vnd.oasis.opendocument.graphics": { + "source": "iana", + "compressible": false, + "extensions": ["odg"] + }, + "application/vnd.oasis.opendocument.graphics-template": { + "source": "iana", + "extensions": ["otg"] + }, + "application/vnd.oasis.opendocument.image": { + "source": "iana", + "extensions": ["odi"] + }, + "application/vnd.oasis.opendocument.image-template": { + "source": "iana", + "extensions": ["oti"] + }, + "application/vnd.oasis.opendocument.presentation": { + "source": "iana", + "compressible": false, + "extensions": ["odp"] + }, + "application/vnd.oasis.opendocument.presentation-template": { + "source": "iana", + "extensions": ["otp"] + }, + "application/vnd.oasis.opendocument.spreadsheet": { + "source": "iana", + "compressible": false, + "extensions": ["ods"] + }, + "application/vnd.oasis.opendocument.spreadsheet-template": { + "source": "iana", + "extensions": ["ots"] + }, + "application/vnd.oasis.opendocument.text": { + "source": "iana", + "compressible": false, + "extensions": ["odt"] + }, + "application/vnd.oasis.opendocument.text-master": { + "source": "iana", + "extensions": ["odm"] + }, + "application/vnd.oasis.opendocument.text-template": { + "source": "iana", + "extensions": ["ott"] + }, + "application/vnd.oasis.opendocument.text-web": { + "source": "iana", + "extensions": ["oth"] + }, + "application/vnd.obn": { + "source": "iana" + }, + "application/vnd.oftn.l10n+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.oipf.contentaccessdownload+xml": { + "source": "iana" + }, + "application/vnd.oipf.contentaccessstreaming+xml": { + "source": "iana" + }, + "application/vnd.oipf.cspg-hexbinary": { + "source": "iana" + }, + "application/vnd.oipf.dae.svg+xml": { + "source": "iana" + }, + "application/vnd.oipf.dae.xhtml+xml": { + "source": "iana" + }, + "application/vnd.oipf.mippvcontrolmessage+xml": { + "source": "iana" + }, + "application/vnd.oipf.pae.gem": { + "source": "iana" + }, + "application/vnd.oipf.spdiscovery+xml": { + "source": "iana" + }, + "application/vnd.oipf.spdlist+xml": { + "source": "iana" + }, + "application/vnd.oipf.ueprofile+xml": { + "source": "iana" + }, + "application/vnd.oipf.userprofile+xml": { + "source": "iana" + }, + "application/vnd.olpc-sugar": { + "source": "iana", + "extensions": ["xo"] + }, + "application/vnd.oma-scws-config": { + "source": "iana" + }, + "application/vnd.oma-scws-http-request": { + "source": "iana" + }, + "application/vnd.oma-scws-http-response": { + "source": "iana" + }, + "application/vnd.oma.bcast.associated-procedure-parameter+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.drm-trigger+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.imd+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.ltkm": { + "source": "iana" + }, + "application/vnd.oma.bcast.notification+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.provisioningtrigger": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgboot": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgdd+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgdu": { + "source": "iana" + }, + "application/vnd.oma.bcast.simple-symbol-container": { + "source": "iana" + }, + "application/vnd.oma.bcast.smartcard-trigger+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.sprov+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.stkm": { + "source": "iana" + }, + "application/vnd.oma.cab-address-book+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-feature-handler+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-pcc+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-subs-invite+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-user-prefs+xml": { + "source": "iana" + }, + "application/vnd.oma.dcd": { + "source": "iana" + }, + "application/vnd.oma.dcdc": { + "source": "iana" + }, + "application/vnd.oma.dd2+xml": { + "source": "iana", + "extensions": ["dd2"] + }, + "application/vnd.oma.drm.risd+xml": { + "source": "iana" + }, + "application/vnd.oma.group-usage-list+xml": { + "source": "iana" + }, + "application/vnd.oma.pal+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.detailed-progress-report+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.final-report+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.groups+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.invocation-descriptor+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.optimized-progress-report+xml": { + "source": "iana" + }, + "application/vnd.oma.push": { + "source": "iana" + }, + "application/vnd.oma.scidm.messages+xml": { + "source": "iana" + }, + "application/vnd.oma.xcap-directory+xml": { + "source": "iana" + }, + "application/vnd.omads-email+xml": { + "source": "iana" + }, + "application/vnd.omads-file+xml": { + "source": "iana" + }, + "application/vnd.omads-folder+xml": { + "source": "iana" + }, + "application/vnd.omaloc-supl-init": { + "source": "iana" + }, + "application/vnd.openeye.oeb": { + "source": "iana" + }, + "application/vnd.openofficeorg.extension": { + "source": "apache", + "extensions": ["oxt"] + }, + "application/vnd.openxmlformats-officedocument.custom-properties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.customxmlproperties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawing+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.chart+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.extended-properties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml-template": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.comments+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.presentation": { + "source": "iana", + "compressible": false, + "extensions": ["pptx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.presprops+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slide": { + "source": "iana", + "extensions": ["sldx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.slide+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideshow": { + "source": "iana", + "extensions": ["ppsx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.tags+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.template": { + "source": "apache", + "extensions": ["potx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml-template": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "source": "iana", + "compressible": false, + "extensions": ["xlsx"] + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.template": { + "source": "apache", + "extensions": ["xltx"] + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.theme+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.themeoverride+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.vmldrawing": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml-template": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document": { + "source": "iana", + "compressible": false, + "extensions": ["docx"] + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.template": { + "source": "apache", + "extensions": ["dotx"] + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-package.core-properties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-package.relationships+xml": { + "source": "iana" + }, + "application/vnd.oracle.resource+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.orange.indata": { + "source": "iana" + }, + "application/vnd.osa.netdeploy": { + "source": "iana" + }, + "application/vnd.osgeo.mapguide.package": { + "source": "iana", + "extensions": ["mgp"] + }, + "application/vnd.osgi.bundle": { + "source": "iana" + }, + "application/vnd.osgi.dp": { + "source": "iana", + "extensions": ["dp"] + }, + "application/vnd.osgi.subsystem": { + "source": "iana", + "extensions": ["esa"] + }, + "application/vnd.otps.ct-kip+xml": { + "source": "iana" + }, + "application/vnd.palm": { + "source": "iana", + "extensions": ["pdb","pqa","oprc"] + }, + "application/vnd.panoply": { + "source": "iana" + }, + "application/vnd.paos+xml": { + "source": "iana" + }, + "application/vnd.paos.xml": { + "source": "apache" + }, + "application/vnd.pawaafile": { + "source": "iana", + "extensions": ["paw"] + }, + "application/vnd.pcos": { + "source": "iana" + }, + "application/vnd.pg.format": { + "source": "iana", + "extensions": ["str"] + }, + "application/vnd.pg.osasli": { + "source": "iana", + "extensions": ["ei6"] + }, + "application/vnd.piaccess.application-licence": { + "source": "iana" + }, + "application/vnd.picsel": { + "source": "iana", + "extensions": ["efif"] + }, + "application/vnd.pmi.widget": { + "source": "iana", + "extensions": ["wg"] + }, + "application/vnd.poc.group-advertisement+xml": { + "source": "iana" + }, + "application/vnd.pocketlearn": { + "source": "iana", + "extensions": ["plf"] + }, + "application/vnd.powerbuilder6": { + "source": "iana", + "extensions": ["pbd"] + }, + "application/vnd.powerbuilder6-s": { + "source": "iana" + }, + "application/vnd.powerbuilder7": { + "source": "iana" + }, + "application/vnd.powerbuilder7-s": { + "source": "iana" + }, + "application/vnd.powerbuilder75": { + "source": "iana" + }, + "application/vnd.powerbuilder75-s": { + "source": "iana" + }, + "application/vnd.preminet": { + "source": "iana" + }, + "application/vnd.previewsystems.box": { + "source": "iana", + "extensions": ["box"] + }, + "application/vnd.proteus.magazine": { + "source": "iana", + "extensions": ["mgz"] + }, + "application/vnd.publishare-delta-tree": { + "source": "iana", + "extensions": ["qps"] + }, + "application/vnd.pvi.ptid1": { + "source": "iana", + "extensions": ["ptid"] + }, + "application/vnd.pwg-multiplexed": { + "source": "iana" + }, + "application/vnd.pwg-xhtml-print+xml": { + "source": "iana" + }, + "application/vnd.qualcomm.brew-app-res": { + "source": "iana" + }, + "application/vnd.quark.quarkxpress": { + "source": "iana", + "extensions": ["qxd","qxt","qwd","qwt","qxl","qxb"] + }, + "application/vnd.quobject-quoxdocument": { + "source": "iana" + }, + "application/vnd.radisys.moml+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-conf+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-conn+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-dialog+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-stream+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-conf+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-base+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-fax-detect+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-fax-sendrecv+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-group+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-speech+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-transform+xml": { + "source": "iana" + }, + "application/vnd.rainstor.data": { + "source": "iana" + }, + "application/vnd.rapid": { + "source": "iana" + }, + "application/vnd.realvnc.bed": { + "source": "iana", + "extensions": ["bed"] + }, + "application/vnd.recordare.musicxml": { + "source": "iana", + "extensions": ["mxl"] + }, + "application/vnd.recordare.musicxml+xml": { + "source": "iana", + "extensions": ["musicxml"] + }, + "application/vnd.renlearn.rlprint": { + "source": "iana" + }, + "application/vnd.rig.cryptonote": { + "source": "iana", + "extensions": ["cryptonote"] + }, + "application/vnd.rim.cod": { + "source": "apache", + "extensions": ["cod"] + }, + "application/vnd.rn-realmedia": { + "source": "apache", + "extensions": ["rm"] + }, + "application/vnd.rn-realmedia-vbr": { + "source": "apache", + "extensions": ["rmvb"] + }, + "application/vnd.route66.link66+xml": { + "source": "iana", + "extensions": ["link66"] + }, + "application/vnd.rs-274x": { + "source": "iana" + }, + "application/vnd.ruckus.download": { + "source": "iana" + }, + "application/vnd.s3sms": { + "source": "iana" + }, + "application/vnd.sailingtracker.track": { + "source": "iana", + "extensions": ["st"] + }, + "application/vnd.sbm.cid": { + "source": "iana" + }, + "application/vnd.sbm.mid2": { + "source": "iana" + }, + "application/vnd.scribus": { + "source": "iana" + }, + "application/vnd.sealed.3df": { + "source": "iana" + }, + "application/vnd.sealed.csf": { + "source": "iana" + }, + "application/vnd.sealed.doc": { + "source": "iana" + }, + "application/vnd.sealed.eml": { + "source": "iana" + }, + "application/vnd.sealed.mht": { + "source": "iana" + }, + "application/vnd.sealed.net": { + "source": "iana" + }, + "application/vnd.sealed.ppt": { + "source": "iana" + }, + "application/vnd.sealed.tiff": { + "source": "iana" + }, + "application/vnd.sealed.xls": { + "source": "iana" + }, + "application/vnd.sealedmedia.softseal.html": { + "source": "iana" + }, + "application/vnd.sealedmedia.softseal.pdf": { + "source": "iana" + }, + "application/vnd.seemail": { + "source": "iana", + "extensions": ["see"] + }, + "application/vnd.sema": { + "source": "iana", + "extensions": ["sema"] + }, + "application/vnd.semd": { + "source": "iana", + "extensions": ["semd"] + }, + "application/vnd.semf": { + "source": "iana", + "extensions": ["semf"] + }, + "application/vnd.shana.informed.formdata": { + "source": "iana", + "extensions": ["ifm"] + }, + "application/vnd.shana.informed.formtemplate": { + "source": "iana", + "extensions": ["itp"] + }, + "application/vnd.shana.informed.interchange": { + "source": "iana", + "extensions": ["iif"] + }, + "application/vnd.shana.informed.package": { + "source": "iana", + "extensions": ["ipk"] + }, + "application/vnd.simtech-mindmapper": { + "source": "iana", + "extensions": ["twd","twds"] + }, + "application/vnd.siren+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.smaf": { + "source": "iana", + "extensions": ["mmf"] + }, + "application/vnd.smart.notebook": { + "source": "iana" + }, + "application/vnd.smart.teacher": { + "source": "iana", + "extensions": ["teacher"] + }, + "application/vnd.software602.filler.form+xml": { + "source": "iana" + }, + "application/vnd.software602.filler.form-xml-zip": { + "source": "iana" + }, + "application/vnd.solent.sdkm+xml": { + "source": "iana", + "extensions": ["sdkm","sdkd"] + }, + "application/vnd.spotfire.dxp": { + "source": "iana", + "extensions": ["dxp"] + }, + "application/vnd.spotfire.sfs": { + "source": "iana", + "extensions": ["sfs"] + }, + "application/vnd.sss-cod": { + "source": "iana" + }, + "application/vnd.sss-dtf": { + "source": "iana" + }, + "application/vnd.sss-ntf": { + "source": "iana" + }, + "application/vnd.stardivision.calc": { + "source": "apache", + "extensions": ["sdc"] + }, + "application/vnd.stardivision.draw": { + "source": "apache", + "extensions": ["sda"] + }, + "application/vnd.stardivision.impress": { + "source": "apache", + "extensions": ["sdd"] + }, + "application/vnd.stardivision.math": { + "source": "apache", + "extensions": ["smf"] + }, + "application/vnd.stardivision.writer": { + "source": "apache", + "extensions": ["sdw","vor"] + }, + "application/vnd.stardivision.writer-global": { + "source": "apache", + "extensions": ["sgl"] + }, + "application/vnd.stepmania.package": { + "source": "iana", + "extensions": ["smzip"] + }, + "application/vnd.stepmania.stepchart": { + "source": "iana", + "extensions": ["sm"] + }, + "application/vnd.street-stream": { + "source": "iana" + }, + "application/vnd.sun.wadl+xml": { + "source": "iana" + }, + "application/vnd.sun.xml.calc": { + "source": "apache", + "extensions": ["sxc"] + }, + "application/vnd.sun.xml.calc.template": { + "source": "apache", + "extensions": ["stc"] + }, + "application/vnd.sun.xml.draw": { + "source": "apache", + "extensions": ["sxd"] + }, + "application/vnd.sun.xml.draw.template": { + "source": "apache", + "extensions": ["std"] + }, + "application/vnd.sun.xml.impress": { + "source": "apache", + "extensions": ["sxi"] + }, + "application/vnd.sun.xml.impress.template": { + "source": "apache", + "extensions": ["sti"] + }, + "application/vnd.sun.xml.math": { + "source": "apache", + "extensions": ["sxm"] + }, + "application/vnd.sun.xml.writer": { + "source": "apache", + "extensions": ["sxw"] + }, + "application/vnd.sun.xml.writer.global": { + "source": "apache", + "extensions": ["sxg"] + }, + "application/vnd.sun.xml.writer.template": { + "source": "apache", + "extensions": ["stw"] + }, + "application/vnd.sus-calendar": { + "source": "iana", + "extensions": ["sus","susp"] + }, + "application/vnd.svd": { + "source": "iana", + "extensions": ["svd"] + }, + "application/vnd.swiftview-ics": { + "source": "iana" + }, + "application/vnd.symbian.install": { + "source": "apache", + "extensions": ["sis","sisx"] + }, + "application/vnd.syncml+xml": { + "source": "iana", + "extensions": ["xsm"] + }, + "application/vnd.syncml.dm+wbxml": { + "source": "iana", + "extensions": ["bdm"] + }, + "application/vnd.syncml.dm+xml": { + "source": "iana", + "extensions": ["xdm"] + }, + "application/vnd.syncml.dm.notification": { + "source": "iana" + }, + "application/vnd.syncml.dmddf+wbxml": { + "source": "iana" + }, + "application/vnd.syncml.dmddf+xml": { + "source": "iana" + }, + "application/vnd.syncml.dmtnds+wbxml": { + "source": "iana" + }, + "application/vnd.syncml.dmtnds+xml": { + "source": "iana" + }, + "application/vnd.syncml.ds.notification": { + "source": "iana" + }, + "application/vnd.tao.intent-module-archive": { + "source": "iana", + "extensions": ["tao"] + }, + "application/vnd.tcpdump.pcap": { + "source": "iana", + "extensions": ["pcap","cap","dmp"] + }, + "application/vnd.tmd.mediaflex.api+xml": { + "source": "iana" + }, + "application/vnd.tmobile-livetv": { + "source": "iana", + "extensions": ["tmo"] + }, + "application/vnd.trid.tpt": { + "source": "iana", + "extensions": ["tpt"] + }, + "application/vnd.triscape.mxs": { + "source": "iana", + "extensions": ["mxs"] + }, + "application/vnd.trueapp": { + "source": "iana", + "extensions": ["tra"] + }, + "application/vnd.truedoc": { + "source": "iana" + }, + "application/vnd.ubisoft.webplayer": { + "source": "iana" + }, + "application/vnd.ufdl": { + "source": "iana", + "extensions": ["ufd","ufdl"] + }, + "application/vnd.uiq.theme": { + "source": "iana", + "extensions": ["utz"] + }, + "application/vnd.umajin": { + "source": "iana", + "extensions": ["umj"] + }, + "application/vnd.unity": { + "source": "iana", + "extensions": ["unityweb"] + }, + "application/vnd.uoml+xml": { + "source": "iana", + "extensions": ["uoml"] + }, + "application/vnd.uplanet.alert": { + "source": "iana" + }, + "application/vnd.uplanet.alert-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.bearer-choice": { + "source": "iana" + }, + "application/vnd.uplanet.bearer-choice-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.cacheop": { + "source": "iana" + }, + "application/vnd.uplanet.cacheop-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.channel": { + "source": "iana" + }, + "application/vnd.uplanet.channel-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.list": { + "source": "iana" + }, + "application/vnd.uplanet.list-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.listcmd": { + "source": "iana" + }, + "application/vnd.uplanet.listcmd-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.signal": { + "source": "iana" + }, + "application/vnd.valve.source.material": { + "source": "iana" + }, + "application/vnd.vcx": { + "source": "iana", + "extensions": ["vcx"] + }, + "application/vnd.vd-study": { + "source": "iana" + }, + "application/vnd.vectorworks": { + "source": "iana" + }, + "application/vnd.verimatrix.vcas": { + "source": "iana" + }, + "application/vnd.vidsoft.vidconference": { + "source": "iana" + }, + "application/vnd.visio": { + "source": "iana", + "extensions": ["vsd","vst","vss","vsw"] + }, + "application/vnd.visionary": { + "source": "iana", + "extensions": ["vis"] + }, + "application/vnd.vividence.scriptfile": { + "source": "iana" + }, + "application/vnd.vsf": { + "source": "iana", + "extensions": ["vsf"] + }, + "application/vnd.wap.sic": { + "source": "iana" + }, + "application/vnd.wap.slc": { + "source": "iana" + }, + "application/vnd.wap.wbxml": { + "source": "iana", + "extensions": ["wbxml"] + }, + "application/vnd.wap.wmlc": { + "source": "iana", + "extensions": ["wmlc"] + }, + "application/vnd.wap.wmlscriptc": { + "source": "iana", + "extensions": ["wmlsc"] + }, + "application/vnd.webturbo": { + "source": "iana", + "extensions": ["wtb"] + }, + "application/vnd.wfa.p2p": { + "source": "iana" + }, + "application/vnd.wfa.wsc": { + "source": "iana" + }, + "application/vnd.windows.devicepairing": { + "source": "iana" + }, + "application/vnd.wmc": { + "source": "iana" + }, + "application/vnd.wmf.bootstrap": { + "source": "iana" + }, + "application/vnd.wolfram.mathematica": { + "source": "iana" + }, + "application/vnd.wolfram.mathematica.package": { + "source": "iana" + }, + "application/vnd.wolfram.player": { + "source": "iana", + "extensions": ["nbp"] + }, + "application/vnd.wordperfect": { + "source": "iana", + "extensions": ["wpd"] + }, + "application/vnd.wqd": { + "source": "iana", + "extensions": ["wqd"] + }, + "application/vnd.wrq-hp3000-labelled": { + "source": "iana" + }, + "application/vnd.wt.stf": { + "source": "iana", + "extensions": ["stf"] + }, + "application/vnd.wv.csp+wbxml": { + "source": "iana" + }, + "application/vnd.wv.csp+xml": { + "source": "iana" + }, + "application/vnd.wv.ssp+xml": { + "source": "iana" + }, + "application/vnd.xacml+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.xara": { + "source": "iana", + "extensions": ["xar"] + }, + "application/vnd.xfdl": { + "source": "iana", + "extensions": ["xfdl"] + }, + "application/vnd.xfdl.webform": { + "source": "iana" + }, + "application/vnd.xmi+xml": { + "source": "iana" + }, + "application/vnd.xmpie.cpkg": { + "source": "iana" + }, + "application/vnd.xmpie.dpkg": { + "source": "iana" + }, + "application/vnd.xmpie.plan": { + "source": "iana" + }, + "application/vnd.xmpie.ppkg": { + "source": "iana" + }, + "application/vnd.xmpie.xlim": { + "source": "iana" + }, + "application/vnd.yamaha.hv-dic": { + "source": "iana", + "extensions": ["hvd"] + }, + "application/vnd.yamaha.hv-script": { + "source": "iana", + "extensions": ["hvs"] + }, + "application/vnd.yamaha.hv-voice": { + "source": "iana", + "extensions": ["hvp"] + }, + "application/vnd.yamaha.openscoreformat": { + "source": "iana", + "extensions": ["osf"] + }, + "application/vnd.yamaha.openscoreformat.osfpvg+xml": { + "source": "iana", + "extensions": ["osfpvg"] + }, + "application/vnd.yamaha.remote-setup": { + "source": "iana" + }, + "application/vnd.yamaha.smaf-audio": { + "source": "iana", + "extensions": ["saf"] + }, + "application/vnd.yamaha.smaf-phrase": { + "source": "iana", + "extensions": ["spf"] + }, + "application/vnd.yamaha.through-ngn": { + "source": "iana" + }, + "application/vnd.yamaha.tunnel-udpencap": { + "source": "iana" + }, + "application/vnd.yaoweme": { + "source": "iana" + }, + "application/vnd.yellowriver-custom-menu": { + "source": "iana", + "extensions": ["cmp"] + }, + "application/vnd.zul": { + "source": "iana", + "extensions": ["zir","zirz"] + }, + "application/vnd.zzazz.deck+xml": { + "source": "iana", + "extensions": ["zaz"] + }, + "application/voicexml+xml": { + "source": "iana", + "extensions": ["vxml"] + }, + "application/vq-rtcpxr": { + "source": "iana" + }, + "application/watcherinfo+xml": { + "source": "iana" + }, + "application/whoispp-query": { + "source": "iana" + }, + "application/whoispp-response": { + "source": "iana" + }, + "application/widget": { + "source": "iana", + "extensions": ["wgt"] + }, + "application/winhlp": { + "source": "apache", + "extensions": ["hlp"] + }, + "application/wita": { + "source": "iana" + }, + "application/wordperfect5.1": { + "source": "iana" + }, + "application/wsdl+xml": { + "source": "iana", + "extensions": ["wsdl"] + }, + "application/wspolicy+xml": { + "source": "iana", + "extensions": ["wspolicy"] + }, + "application/x-7z-compressed": { + "source": "apache", + "compressible": false, + "extensions": ["7z"] + }, + "application/x-abiword": { + "source": "apache", + "extensions": ["abw"] + }, + "application/x-ace-compressed": { + "source": "apache", + "extensions": ["ace"] + }, + "application/x-amf": { + "source": "apache" + }, + "application/x-apple-diskimage": { + "source": "apache", + "extensions": ["dmg"] + }, + "application/x-authorware-bin": { + "source": "apache", + "extensions": ["aab","x32","u32","vox"] + }, + "application/x-authorware-map": { + "source": "apache", + "extensions": ["aam"] + }, + "application/x-authorware-seg": { + "source": "apache", + "extensions": ["aas"] + }, + "application/x-bcpio": { + "source": "apache", + "extensions": ["bcpio"] + }, + "application/x-bdoc": { + "compressible": false, + "extensions": ["bdoc"] + }, + "application/x-bittorrent": { + "source": "apache", + "extensions": ["torrent"] + }, + "application/x-blorb": { + "source": "apache", + "extensions": ["blb","blorb"] + }, + "application/x-bzip": { + "source": "apache", + "compressible": false, + "extensions": ["bz"] + }, + "application/x-bzip2": { + "source": "apache", + "compressible": false, + "extensions": ["bz2","boz"] + }, + "application/x-cbr": { + "source": "apache", + "extensions": ["cbr","cba","cbt","cbz","cb7"] + }, + "application/x-cdlink": { + "source": "apache", + "extensions": ["vcd"] + }, + "application/x-cfs-compressed": { + "source": "apache", + "extensions": ["cfs"] + }, + "application/x-chat": { + "source": "apache", + "extensions": ["chat"] + }, + "application/x-chess-pgn": { + "source": "apache", + "extensions": ["pgn"] + }, + "application/x-chrome-extension": { + "extensions": ["crx"] + }, + "application/x-compress": { + "source": "apache" + }, + "application/x-conference": { + "source": "apache", + "extensions": ["nsc"] + }, + "application/x-cpio": { + "source": "apache", + "extensions": ["cpio"] + }, + "application/x-csh": { + "source": "apache", + "extensions": ["csh"] + }, + "application/x-deb": { + "compressible": false + }, + "application/x-debian-package": { + "source": "apache", + "extensions": ["deb","udeb"] + }, + "application/x-dgc-compressed": { + "source": "apache", + "extensions": ["dgc"] + }, + "application/x-director": { + "source": "apache", + "extensions": ["dir","dcr","dxr","cst","cct","cxt","w3d","fgd","swa"] + }, + "application/x-doom": { + "source": "apache", + "extensions": ["wad"] + }, + "application/x-dtbncx+xml": { + "source": "apache", + "extensions": ["ncx"] + }, + "application/x-dtbook+xml": { + "source": "apache", + "extensions": ["dtb"] + }, + "application/x-dtbresource+xml": { + "source": "apache", + "extensions": ["res"] + }, + "application/x-dvi": { + "source": "apache", + "compressible": false, + "extensions": ["dvi"] + }, + "application/x-envoy": { + "source": "apache", + "extensions": ["evy"] + }, + "application/x-eva": { + "source": "apache", + "extensions": ["eva"] + }, + "application/x-font-bdf": { + "source": "apache", + "extensions": ["bdf"] + }, + "application/x-font-dos": { + "source": "apache" + }, + "application/x-font-framemaker": { + "source": "apache" + }, + "application/x-font-ghostscript": { + "source": "apache", + "extensions": ["gsf"] + }, + "application/x-font-libgrx": { + "source": "apache" + }, + "application/x-font-linux-psf": { + "source": "apache", + "extensions": ["psf"] + }, + "application/x-font-otf": { + "source": "apache", + "compressible": true, + "extensions": ["otf"] + }, + "application/x-font-pcf": { + "source": "apache", + "extensions": ["pcf"] + }, + "application/x-font-snf": { + "source": "apache", + "extensions": ["snf"] + }, + "application/x-font-speedo": { + "source": "apache" + }, + "application/x-font-sunos-news": { + "source": "apache" + }, + "application/x-font-ttf": { + "source": "apache", + "compressible": true, + "extensions": ["ttf","ttc"] + }, + "application/x-font-type1": { + "source": "apache", + "extensions": ["pfa","pfb","pfm","afm"] + }, + "application/x-font-vfont": { + "source": "apache" + }, + "application/x-freearc": { + "source": "apache", + "extensions": ["arc"] + }, + "application/x-futuresplash": { + "source": "apache", + "extensions": ["spl"] + }, + "application/x-gca-compressed": { + "source": "apache", + "extensions": ["gca"] + }, + "application/x-glulx": { + "source": "apache", + "extensions": ["ulx"] + }, + "application/x-gnumeric": { + "source": "apache", + "extensions": ["gnumeric"] + }, + "application/x-gramps-xml": { + "source": "apache", + "extensions": ["gramps"] + }, + "application/x-gtar": { + "source": "apache", + "extensions": ["gtar"] + }, + "application/x-gzip": { + "source": "apache" + }, + "application/x-hdf": { + "source": "apache", + "extensions": ["hdf"] + }, + "application/x-install-instructions": { + "source": "apache", + "extensions": ["install"] + }, + "application/x-iso9660-image": { + "source": "apache", + "extensions": ["iso"] + }, + "application/x-java-jnlp-file": { + "source": "apache", + "compressible": false, + "extensions": ["jnlp"] + }, + "application/x-javascript": { + "compressible": true + }, + "application/x-latex": { + "source": "apache", + "compressible": false, + "extensions": ["latex"] + }, + "application/x-lua-bytecode": { + "extensions": ["luac"] + }, + "application/x-lzh-compressed": { + "source": "apache", + "extensions": ["lzh","lha"] + }, + "application/x-mie": { + "source": "apache", + "extensions": ["mie"] + }, + "application/x-mobipocket-ebook": { + "source": "apache", + "extensions": ["prc","mobi"] + }, + "application/x-mpegurl": { + "compressible": false + }, + "application/x-ms-application": { + "source": "apache", + "extensions": ["application"] + }, + "application/x-ms-shortcut": { + "source": "apache", + "extensions": ["lnk"] + }, + "application/x-ms-wmd": { + "source": "apache", + "extensions": ["wmd"] + }, + "application/x-ms-wmz": { + "source": "apache", + "extensions": ["wmz"] + }, + "application/x-ms-xbap": { + "source": "apache", + "extensions": ["xbap"] + }, + "application/x-msaccess": { + "source": "apache", + "extensions": ["mdb"] + }, + "application/x-msbinder": { + "source": "apache", + "extensions": ["obd"] + }, + "application/x-mscardfile": { + "source": "apache", + "extensions": ["crd"] + }, + "application/x-msclip": { + "source": "apache", + "extensions": ["clp"] + }, + "application/x-msdownload": { + "source": "apache", + "extensions": ["exe","dll","com","bat","msi"] + }, + "application/x-msmediaview": { + "source": "apache", + "extensions": ["mvb","m13","m14"] + }, + "application/x-msmetafile": { + "source": "apache", + "extensions": ["wmf","wmz","emf","emz"] + }, + "application/x-msmoney": { + "source": "apache", + "extensions": ["mny"] + }, + "application/x-mspublisher": { + "source": "apache", + "extensions": ["pub"] + }, + "application/x-msschedule": { + "source": "apache", + "extensions": ["scd"] + }, + "application/x-msterminal": { + "source": "apache", + "extensions": ["trm"] + }, + "application/x-mswrite": { + "source": "apache", + "extensions": ["wri"] + }, + "application/x-netcdf": { + "source": "apache", + "extensions": ["nc","cdf"] + }, + "application/x-ns-proxy-autoconfig": { + "compressible": true, + "extensions": ["pac"] + }, + "application/x-nzb": { + "source": "apache", + "extensions": ["nzb"] + }, + "application/x-pkcs12": { + "source": "apache", + "compressible": false, + "extensions": ["p12","pfx"] + }, + "application/x-pkcs7-certificates": { + "source": "apache", + "extensions": ["p7b","spc"] + }, + "application/x-pkcs7-certreqresp": { + "source": "apache", + "extensions": ["p7r"] + }, + "application/x-rar-compressed": { + "source": "apache", + "compressible": false, + "extensions": ["rar"] + }, + "application/x-research-info-systems": { + "source": "apache", + "extensions": ["ris"] + }, + "application/x-sh": { + "source": "apache", + "compressible": true, + "extensions": ["sh"] + }, + "application/x-shar": { + "source": "apache", + "extensions": ["shar"] + }, + "application/x-shockwave-flash": { + "source": "apache", + "compressible": false, + "extensions": ["swf"] + }, + "application/x-silverlight-app": { + "source": "apache", + "extensions": ["xap"] + }, + "application/x-sql": { + "source": "apache", + "extensions": ["sql"] + }, + "application/x-stuffit": { + "source": "apache", + "compressible": false, + "extensions": ["sit"] + }, + "application/x-stuffitx": { + "source": "apache", + "extensions": ["sitx"] + }, + "application/x-subrip": { + "source": "apache", + "extensions": ["srt"] + }, + "application/x-sv4cpio": { + "source": "apache", + "extensions": ["sv4cpio"] + }, + "application/x-sv4crc": { + "source": "apache", + "extensions": ["sv4crc"] + }, + "application/x-t3vm-image": { + "source": "apache", + "extensions": ["t3"] + }, + "application/x-tads": { + "source": "apache", + "extensions": ["gam"] + }, + "application/x-tar": { + "source": "apache", + "compressible": true, + "extensions": ["tar"] + }, + "application/x-tcl": { + "source": "apache", + "extensions": ["tcl"] + }, + "application/x-tex": { + "source": "apache", + "extensions": ["tex"] + }, + "application/x-tex-tfm": { + "source": "apache", + "extensions": ["tfm"] + }, + "application/x-texinfo": { + "source": "apache", + "extensions": ["texinfo","texi"] + }, + "application/x-tgif": { + "source": "apache", + "extensions": ["obj"] + }, + "application/x-ustar": { + "source": "apache", + "extensions": ["ustar"] + }, + "application/x-wais-source": { + "source": "apache", + "extensions": ["src"] + }, + "application/x-web-app-manifest+json": { + "compressible": true, + "extensions": ["webapp"] + }, + "application/x-www-form-urlencoded": { + "source": "iana", + "compressible": true + }, + "application/x-x509-ca-cert": { + "source": "apache", + "extensions": ["der","crt"] + }, + "application/x-xfig": { + "source": "apache", + "extensions": ["fig"] + }, + "application/x-xliff+xml": { + "source": "apache", + "extensions": ["xlf"] + }, + "application/x-xpinstall": { + "source": "apache", + "compressible": false, + "extensions": ["xpi"] + }, + "application/x-xz": { + "source": "apache", + "extensions": ["xz"] + }, + "application/x-zmachine": { + "source": "apache", + "extensions": ["z1","z2","z3","z4","z5","z6","z7","z8"] + }, + "application/x400-bp": { + "source": "iana" + }, + "application/xacml+xml": { + "source": "iana" + }, + "application/xaml+xml": { + "source": "apache", + "extensions": ["xaml"] + }, + "application/xcap-att+xml": { + "source": "iana" + }, + "application/xcap-caps+xml": { + "source": "iana" + }, + "application/xcap-diff+xml": { + "source": "iana", + "extensions": ["xdf"] + }, + "application/xcap-el+xml": { + "source": "iana" + }, + "application/xcap-error+xml": { + "source": "iana" + }, + "application/xcap-ns+xml": { + "source": "iana" + }, + "application/xcon-conference-info+xml": { + "source": "iana" + }, + "application/xcon-conference-info-diff+xml": { + "source": "iana" + }, + "application/xenc+xml": { + "source": "iana", + "extensions": ["xenc"] + }, + "application/xhtml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xhtml","xht"] + }, + "application/xhtml-voice+xml": { + "source": "apache" + }, + "application/xml": { + "source": "iana", + "compressible": true, + "extensions": ["xml","xsl","xsd"] + }, + "application/xml-dtd": { + "source": "iana", + "compressible": true, + "extensions": ["dtd"] + }, + "application/xml-external-parsed-entity": { + "source": "iana" + }, + "application/xml-patch+xml": { + "source": "iana" + }, + "application/xmpp+xml": { + "source": "iana" + }, + "application/xop+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xop"] + }, + "application/xproc+xml": { + "source": "apache", + "extensions": ["xpl"] + }, + "application/xslt+xml": { + "source": "iana", + "extensions": ["xslt"] + }, + "application/xspf+xml": { + "source": "apache", + "extensions": ["xspf"] + }, + "application/xv+xml": { + "source": "iana", + "extensions": ["mxml","xhvml","xvml","xvm"] + }, + "application/yang": { + "source": "iana", + "extensions": ["yang"] + }, + "application/yin+xml": { + "source": "iana", + "extensions": ["yin"] + }, + "application/zip": { + "source": "iana", + "compressible": false, + "extensions": ["zip"] + }, + "application/zlib": { + "source": "iana" + }, + "audio/1d-interleaved-parityfec": { + "source": "iana" + }, + "audio/32kadpcm": { + "source": "iana" + }, + "audio/3gpp": { + "source": "iana" + }, + "audio/3gpp2": { + "source": "iana" + }, + "audio/ac3": { + "source": "iana" + }, + "audio/adpcm": { + "source": "apache", + "extensions": ["adp"] + }, + "audio/amr": { + "source": "iana" + }, + "audio/amr-wb": { + "source": "iana" + }, + "audio/amr-wb+": { + "source": "iana" + }, + "audio/aptx": { + "source": "iana" + }, + "audio/asc": { + "source": "iana" + }, + "audio/atrac-advanced-lossless": { + "source": "iana" + }, + "audio/atrac-x": { + "source": "iana" + }, + "audio/atrac3": { + "source": "iana" + }, + "audio/basic": { + "source": "iana", + "compressible": false, + "extensions": ["au","snd"] + }, + "audio/bv16": { + "source": "iana" + }, + "audio/bv32": { + "source": "iana" + }, + "audio/clearmode": { + "source": "iana" + }, + "audio/cn": { + "source": "iana" + }, + "audio/dat12": { + "source": "iana" + }, + "audio/dls": { + "source": "iana" + }, + "audio/dsr-es201108": { + "source": "iana" + }, + "audio/dsr-es202050": { + "source": "iana" + }, + "audio/dsr-es202211": { + "source": "iana" + }, + "audio/dsr-es202212": { + "source": "iana" + }, + "audio/dv": { + "source": "iana" + }, + "audio/dvi4": { + "source": "iana" + }, + "audio/eac3": { + "source": "iana" + }, + "audio/encaprtp": { + "source": "iana" + }, + "audio/evrc": { + "source": "iana" + }, + "audio/evrc-qcp": { + "source": "iana" + }, + "audio/evrc0": { + "source": "iana" + }, + "audio/evrc1": { + "source": "iana" + }, + "audio/evrcb": { + "source": "iana" + }, + "audio/evrcb0": { + "source": "iana" + }, + "audio/evrcb1": { + "source": "iana" + }, + "audio/evrcnw": { + "source": "iana" + }, + "audio/evrcnw0": { + "source": "iana" + }, + "audio/evrcnw1": { + "source": "iana" + }, + "audio/evrcwb": { + "source": "iana" + }, + "audio/evrcwb0": { + "source": "iana" + }, + "audio/evrcwb1": { + "source": "iana" + }, + "audio/fwdred": { + "source": "iana" + }, + "audio/g719": { + "source": "iana" + }, + "audio/g722": { + "source": "iana" + }, + "audio/g7221": { + "source": "iana" + }, + "audio/g723": { + "source": "iana" + }, + "audio/g726-16": { + "source": "iana" + }, + "audio/g726-24": { + "source": "iana" + }, + "audio/g726-32": { + "source": "iana" + }, + "audio/g726-40": { + "source": "iana" + }, + "audio/g728": { + "source": "iana" + }, + "audio/g729": { + "source": "iana" + }, + "audio/g7291": { + "source": "iana" + }, + "audio/g729d": { + "source": "iana" + }, + "audio/g729e": { + "source": "iana" + }, + "audio/gsm": { + "source": "iana" + }, + "audio/gsm-efr": { + "source": "iana" + }, + "audio/gsm-hr-08": { + "source": "iana" + }, + "audio/ilbc": { + "source": "iana" + }, + "audio/ip-mr_v2.5": { + "source": "iana" + }, + "audio/isac": { + "source": "apache" + }, + "audio/l16": { + "source": "iana" + }, + "audio/l20": { + "source": "iana" + }, + "audio/l24": { + "source": "iana", + "compressible": false + }, + "audio/l8": { + "source": "iana" + }, + "audio/lpc": { + "source": "iana" + }, + "audio/midi": { + "source": "apache", + "extensions": ["mid","midi","kar","rmi"] + }, + "audio/mobile-xmf": { + "source": "iana" + }, + "audio/mp4": { + "source": "iana", + "compressible": false, + "extensions": ["mp4a","m4a"] + }, + "audio/mp4a-latm": { + "source": "iana" + }, + "audio/mpa": { + "source": "iana" + }, + "audio/mpa-robust": { + "source": "iana" + }, + "audio/mpeg": { + "source": "iana", + "compressible": false, + "extensions": ["mpga","mp2","mp2a","mp3","m2a","m3a"] + }, + "audio/mpeg4-generic": { + "source": "iana" + }, + "audio/musepack": { + "source": "apache" + }, + "audio/ogg": { + "source": "iana", + "compressible": false, + "extensions": ["oga","ogg","spx"] + }, + "audio/opus": { + "source": "iana" + }, + "audio/parityfec": { + "source": "iana" + }, + "audio/pcma": { + "source": "iana" + }, + "audio/pcma-wb": { + "source": "iana" + }, + "audio/pcmu": { + "source": "iana" + }, + "audio/pcmu-wb": { + "source": "iana" + }, + "audio/prs.sid": { + "source": "iana" + }, + "audio/qcelp": { + "source": "iana" + }, + "audio/raptorfec": { + "source": "iana" + }, + "audio/red": { + "source": "iana" + }, + "audio/rtp-enc-aescm128": { + "source": "iana" + }, + "audio/rtp-midi": { + "source": "iana" + }, + "audio/rtploopback": { + "source": "iana" + }, + "audio/rtx": { + "source": "iana" + }, + "audio/s3m": { + "source": "apache", + "extensions": ["s3m"] + }, + "audio/silk": { + "source": "apache", + "extensions": ["sil"] + }, + "audio/smv": { + "source": "iana" + }, + "audio/smv-qcp": { + "source": "iana" + }, + "audio/smv0": { + "source": "iana" + }, + "audio/sp-midi": { + "source": "iana" + }, + "audio/speex": { + "source": "iana" + }, + "audio/t140c": { + "source": "iana" + }, + "audio/t38": { + "source": "iana" + }, + "audio/telephone-event": { + "source": "iana" + }, + "audio/tone": { + "source": "iana" + }, + "audio/uemclip": { + "source": "iana" + }, + "audio/ulpfec": { + "source": "iana" + }, + "audio/vdvi": { + "source": "iana" + }, + "audio/vmr-wb": { + "source": "iana" + }, + "audio/vnd.3gpp.iufp": { + "source": "iana" + }, + "audio/vnd.4sb": { + "source": "iana" + }, + "audio/vnd.audiokoz": { + "source": "iana" + }, + "audio/vnd.celp": { + "source": "iana" + }, + "audio/vnd.cisco.nse": { + "source": "iana" + }, + "audio/vnd.cmles.radio-events": { + "source": "iana" + }, + "audio/vnd.cns.anp1": { + "source": "iana" + }, + "audio/vnd.cns.inf1": { + "source": "iana" + }, + "audio/vnd.dece.audio": { + "source": "iana", + "extensions": ["uva","uvva"] + }, + "audio/vnd.digital-winds": { + "source": "iana", + "extensions": ["eol"] + }, + "audio/vnd.dlna.adts": { + "source": "iana" + }, + "audio/vnd.dolby.heaac.1": { + "source": "iana" + }, + "audio/vnd.dolby.heaac.2": { + "source": "iana" + }, + "audio/vnd.dolby.mlp": { + "source": "iana" + }, + "audio/vnd.dolby.mps": { + "source": "iana" + }, + "audio/vnd.dolby.pl2": { + "source": "iana" + }, + "audio/vnd.dolby.pl2x": { + "source": "iana" + }, + "audio/vnd.dolby.pl2z": { + "source": "iana" + }, + "audio/vnd.dolby.pulse.1": { + "source": "iana" + }, + "audio/vnd.dra": { + "source": "iana", + "extensions": ["dra"] + }, + "audio/vnd.dts": { + "source": "iana", + "extensions": ["dts"] + }, + "audio/vnd.dts.hd": { + "source": "iana", + "extensions": ["dtshd"] + }, + "audio/vnd.dvb.file": { + "source": "iana" + }, + "audio/vnd.everad.plj": { + "source": "iana" + }, + "audio/vnd.hns.audio": { + "source": "iana" + }, + "audio/vnd.lucent.voice": { + "source": "iana", + "extensions": ["lvp"] + }, + "audio/vnd.ms-playready.media.pya": { + "source": "iana", + "extensions": ["pya"] + }, + "audio/vnd.nokia.mobile-xmf": { + "source": "iana" + }, + "audio/vnd.nortel.vbk": { + "source": "iana" + }, + "audio/vnd.nuera.ecelp4800": { + "source": "iana", + "extensions": ["ecelp4800"] + }, + "audio/vnd.nuera.ecelp7470": { + "source": "iana", + "extensions": ["ecelp7470"] + }, + "audio/vnd.nuera.ecelp9600": { + "source": "iana", + "extensions": ["ecelp9600"] + }, + "audio/vnd.octel.sbc": { + "source": "iana" + }, + "audio/vnd.qcelp": { + "source": "iana" + }, + "audio/vnd.rhetorex.32kadpcm": { + "source": "iana" + }, + "audio/vnd.rip": { + "source": "iana", + "extensions": ["rip"] + }, + "audio/vnd.rn-realaudio": { + "compressible": false + }, + "audio/vnd.sealedmedia.softseal.mpeg": { + "source": "iana" + }, + "audio/vnd.vmx.cvsd": { + "source": "iana" + }, + "audio/vnd.wave": { + "compressible": false + }, + "audio/vorbis": { + "source": "iana", + "compressible": false + }, + "audio/vorbis-config": { + "source": "iana" + }, + "audio/wav": { + "compressible": false, + "extensions": ["wav"] + }, + "audio/wave": { + "compressible": false, + "extensions": ["wav"] + }, + "audio/webm": { + "source": "apache", + "compressible": false, + "extensions": ["weba"] + }, + "audio/x-aac": { + "source": "apache", + "compressible": false, + "extensions": ["aac"] + }, + "audio/x-aiff": { + "source": "apache", + "extensions": ["aif","aiff","aifc"] + }, + "audio/x-caf": { + "source": "apache", + "compressible": false, + "extensions": ["caf"] + }, + "audio/x-flac": { + "source": "apache", + "extensions": ["flac"] + }, + "audio/x-matroska": { + "source": "apache", + "extensions": ["mka"] + }, + "audio/x-mpegurl": { + "source": "apache", + "extensions": ["m3u"] + }, + "audio/x-ms-wax": { + "source": "apache", + "extensions": ["wax"] + }, + "audio/x-ms-wma": { + "source": "apache", + "extensions": ["wma"] + }, + "audio/x-pn-realaudio": { + "source": "apache", + "extensions": ["ram","ra"] + }, + "audio/x-pn-realaudio-plugin": { + "source": "apache", + "extensions": ["rmp"] + }, + "audio/x-tta": { + "source": "apache" + }, + "audio/x-wav": { + "source": "apache", + "extensions": ["wav"] + }, + "audio/xm": { + "source": "apache", + "extensions": ["xm"] + }, + "chemical/x-cdx": { + "source": "apache", + "extensions": ["cdx"] + }, + "chemical/x-cif": { + "source": "apache", + "extensions": ["cif"] + }, + "chemical/x-cmdf": { + "source": "apache", + "extensions": ["cmdf"] + }, + "chemical/x-cml": { + "source": "apache", + "extensions": ["cml"] + }, + "chemical/x-csml": { + "source": "apache", + "extensions": ["csml"] + }, + "chemical/x-pdb": { + "source": "apache" + }, + "chemical/x-xyz": { + "source": "apache", + "extensions": ["xyz"] + }, + "font/opentype": { + "compressible": true, + "extensions": ["otf"] + }, + "image/bmp": { + "source": "apache", + "compressible": true, + "extensions": ["bmp"] + }, + "image/cgm": { + "source": "iana", + "extensions": ["cgm"] + }, + "image/fits": { + "source": "iana" + }, + "image/g3fax": { + "source": "iana", + "extensions": ["g3"] + }, + "image/gif": { + "source": "iana", + "compressible": false, + "extensions": ["gif"] + }, + "image/ief": { + "source": "iana", + "extensions": ["ief"] + }, + "image/jp2": { + "source": "iana" + }, + "image/jpeg": { + "source": "iana", + "compressible": false, + "extensions": ["jpeg","jpg","jpe"] + }, + "image/jpm": { + "source": "iana" + }, + "image/jpx": { + "source": "iana" + }, + "image/ktx": { + "source": "iana", + "extensions": ["ktx"] + }, + "image/naplps": { + "source": "iana" + }, + "image/pjpeg": { + "compressible": false + }, + "image/png": { + "source": "iana", + "compressible": false, + "extensions": ["png"] + }, + "image/prs.btif": { + "source": "iana", + "extensions": ["btif"] + }, + "image/prs.pti": { + "source": "iana" + }, + "image/pwg-raster": { + "source": "iana" + }, + "image/sgi": { + "source": "apache", + "extensions": ["sgi"] + }, + "image/svg+xml": { + "source": "iana", + "compressible": true, + "extensions": ["svg","svgz"] + }, + "image/t38": { + "source": "iana" + }, + "image/tiff": { + "source": "iana", + "compressible": false, + "extensions": ["tiff","tif"] + }, + "image/tiff-fx": { + "source": "iana" + }, + "image/vnd.adobe.photoshop": { + "source": "iana", + "compressible": true, + "extensions": ["psd"] + }, + "image/vnd.airzip.accelerator.azv": { + "source": "iana" + }, + "image/vnd.cns.inf2": { + "source": "iana" + }, + "image/vnd.dece.graphic": { + "source": "iana", + "extensions": ["uvi","uvvi","uvg","uvvg"] + }, + "image/vnd.djvu": { + "source": "iana", + "extensions": ["djvu","djv"] + }, + "image/vnd.dvb.subtitle": { + "source": "iana", + "extensions": ["sub"] + }, + "image/vnd.dwg": { + "source": "iana", + "extensions": ["dwg"] + }, + "image/vnd.dxf": { + "source": "iana", + "extensions": ["dxf"] + }, + "image/vnd.fastbidsheet": { + "source": "iana", + "extensions": ["fbs"] + }, + "image/vnd.fpx": { + "source": "iana", + "extensions": ["fpx"] + }, + "image/vnd.fst": { + "source": "iana", + "extensions": ["fst"] + }, + "image/vnd.fujixerox.edmics-mmr": { + "source": "iana", + "extensions": ["mmr"] + }, + "image/vnd.fujixerox.edmics-rlc": { + "source": "iana", + "extensions": ["rlc"] + }, + "image/vnd.globalgraphics.pgb": { + "source": "iana" + }, + "image/vnd.microsoft.icon": { + "source": "iana" + }, + "image/vnd.mix": { + "source": "iana" + }, + "image/vnd.ms-modi": { + "source": "iana", + "extensions": ["mdi"] + }, + "image/vnd.ms-photo": { + "source": "apache", + "extensions": ["wdp"] + }, + "image/vnd.net-fpx": { + "source": "iana", + "extensions": ["npx"] + }, + "image/vnd.radiance": { + "source": "iana" + }, + "image/vnd.sealed.png": { + "source": "iana" + }, + "image/vnd.sealedmedia.softseal.gif": { + "source": "iana" + }, + "image/vnd.sealedmedia.softseal.jpg": { + "source": "iana" + }, + "image/vnd.svf": { + "source": "iana" + }, + "image/vnd.tencent.tap": { + "source": "iana" + }, + "image/vnd.valve.source.texture": { + "source": "iana" + }, + "image/vnd.wap.wbmp": { + "source": "iana", + "extensions": ["wbmp"] + }, + "image/vnd.xiff": { + "source": "iana", + "extensions": ["xif"] + }, + "image/vnd.zbrush.pcx": { + "source": "iana" + }, + "image/webp": { + "source": "apache", + "extensions": ["webp"] + }, + "image/x-3ds": { + "source": "apache", + "extensions": ["3ds"] + }, + "image/x-cmu-raster": { + "source": "apache", + "extensions": ["ras"] + }, + "image/x-cmx": { + "source": "apache", + "extensions": ["cmx"] + }, + "image/x-freehand": { + "source": "apache", + "extensions": ["fh","fhc","fh4","fh5","fh7"] + }, + "image/x-icon": { + "source": "apache", + "compressible": true, + "extensions": ["ico"] + }, + "image/x-mrsid-image": { + "source": "apache", + "extensions": ["sid"] + }, + "image/x-ms-bmp": { + "compressible": true, + "extensions": ["bmp"] + }, + "image/x-pcx": { + "source": "apache", + "extensions": ["pcx"] + }, + "image/x-pict": { + "source": "apache", + "extensions": ["pic","pct"] + }, + "image/x-portable-anymap": { + "source": "apache", + "extensions": ["pnm"] + }, + "image/x-portable-bitmap": { + "source": "apache", + "extensions": ["pbm"] + }, + "image/x-portable-graymap": { + "source": "apache", + "extensions": ["pgm"] + }, + "image/x-portable-pixmap": { + "source": "apache", + "extensions": ["ppm"] + }, + "image/x-rgb": { + "source": "apache", + "extensions": ["rgb"] + }, + "image/x-tga": { + "source": "apache", + "extensions": ["tga"] + }, + "image/x-xbitmap": { + "source": "apache", + "extensions": ["xbm"] + }, + "image/x-xcf": { + "compressible": false + }, + "image/x-xpixmap": { + "source": "apache", + "extensions": ["xpm"] + }, + "image/x-xwindowdump": { + "source": "apache", + "extensions": ["xwd"] + }, + "message/cpim": { + "source": "iana" + }, + "message/delivery-status": { + "source": "iana" + }, + "message/disposition-notification": { + "source": "iana" + }, + "message/external-body": { + "source": "iana" + }, + "message/feedback-report": { + "source": "iana" + }, + "message/global": { + "source": "iana" + }, + "message/global-delivery-status": { + "source": "iana" + }, + "message/global-disposition-notification": { + "source": "iana" + }, + "message/global-headers": { + "source": "iana" + }, + "message/http": { + "source": "iana", + "compressible": false + }, + "message/imdn+xml": { + "source": "iana", + "compressible": true + }, + "message/news": { + "source": "iana" + }, + "message/partial": { + "source": "iana", + "compressible": false + }, + "message/rfc822": { + "source": "iana", + "compressible": true, + "extensions": ["eml","mime"] + }, + "message/s-http": { + "source": "iana" + }, + "message/sip": { + "source": "iana" + }, + "message/sipfrag": { + "source": "iana" + }, + "message/tracking-status": { + "source": "iana" + }, + "message/vnd.si.simp": { + "source": "iana" + }, + "message/vnd.wfa.wsc": { + "source": "iana" + }, + "model/iges": { + "source": "iana", + "compressible": false, + "extensions": ["igs","iges"] + }, + "model/mesh": { + "source": "iana", + "compressible": false, + "extensions": ["msh","mesh","silo"] + }, + "model/vnd.collada+xml": { + "source": "iana", + "extensions": ["dae"] + }, + "model/vnd.dwf": { + "source": "iana", + "extensions": ["dwf"] + }, + "model/vnd.flatland.3dml": { + "source": "iana" + }, + "model/vnd.gdl": { + "source": "iana", + "extensions": ["gdl"] + }, + "model/vnd.gs-gdl": { + "source": "apache" + }, + "model/vnd.gs.gdl": { + "source": "iana" + }, + "model/vnd.gtw": { + "source": "iana", + "extensions": ["gtw"] + }, + "model/vnd.moml+xml": { + "source": "iana" + }, + "model/vnd.mts": { + "source": "iana", + "extensions": ["mts"] + }, + "model/vnd.opengex": { + "source": "iana" + }, + "model/vnd.parasolid.transmit.binary": { + "source": "iana" + }, + "model/vnd.parasolid.transmit.text": { + "source": "iana" + }, + "model/vnd.valve.source.compiled-map": { + "source": "iana" + }, + "model/vnd.vtu": { + "source": "iana", + "extensions": ["vtu"] + }, + "model/vrml": { + "source": "iana", + "compressible": false, + "extensions": ["wrl","vrml"] + }, + "model/x3d+binary": { + "source": "apache", + "compressible": false, + "extensions": ["x3db","x3dbz"] + }, + "model/x3d+fastinfoset": { + "source": "iana" + }, + "model/x3d+vrml": { + "source": "apache", + "compressible": false, + "extensions": ["x3dv","x3dvz"] + }, + "model/x3d+xml": { + "source": "iana", + "compressible": true, + "extensions": ["x3d","x3dz"] + }, + "model/x3d-vrml": { + "source": "iana" + }, + "multipart/alternative": { + "source": "iana", + "compressible": false + }, + "multipart/appledouble": { + "source": "iana" + }, + "multipart/byteranges": { + "source": "iana" + }, + "multipart/digest": { + "source": "iana" + }, + "multipart/encrypted": { + "source": "iana", + "compressible": false + }, + "multipart/form-data": { + "source": "iana", + "compressible": false + }, + "multipart/header-set": { + "source": "iana" + }, + "multipart/mixed": { + "source": "iana", + "compressible": false + }, + "multipart/parallel": { + "source": "iana" + }, + "multipart/related": { + "source": "iana", + "compressible": false + }, + "multipart/report": { + "source": "iana" + }, + "multipart/signed": { + "source": "iana", + "compressible": false + }, + "multipart/voice-message": { + "source": "iana" + }, + "multipart/x-mixed-replace": { + "source": "iana" + }, + "text/1d-interleaved-parityfec": { + "source": "iana" + }, + "text/cache-manifest": { + "source": "iana", + "compressible": true, + "extensions": ["appcache","manifest"] + }, + "text/calendar": { + "source": "iana", + "extensions": ["ics","ifb"] + }, + "text/calender": { + "compressible": true + }, + "text/cmd": { + "compressible": true + }, + "text/coffeescript": { + "extensions": ["coffee","litcoffee"] + }, + "text/css": { + "source": "iana", + "compressible": true, + "extensions": ["css"] + }, + "text/csv": { + "source": "iana", + "compressible": true, + "extensions": ["csv"] + }, + "text/csv-schema": { + "source": "iana" + }, + "text/directory": { + "source": "iana" + }, + "text/dns": { + "source": "iana" + }, + "text/ecmascript": { + "source": "iana" + }, + "text/encaprtp": { + "source": "iana" + }, + "text/enriched": { + "source": "iana" + }, + "text/fwdred": { + "source": "iana" + }, + "text/grammar-ref-list": { + "source": "iana" + }, + "text/hjson": { + "extensions": ["hjson"] + }, + "text/html": { + "source": "iana", + "compressible": true, + "extensions": ["html","htm"] + }, + "text/jade": { + "extensions": ["jade"] + }, + "text/javascript": { + "source": "iana", + "compressible": true + }, + "text/jcr-cnd": { + "source": "iana" + }, + "text/jsx": { + "compressible": true, + "extensions": ["jsx"] + }, + "text/less": { + "extensions": ["less"] + }, + "text/markdown": { + "source": "iana" + }, + "text/mizar": { + "source": "iana" + }, + "text/n3": { + "source": "iana", + "compressible": true, + "extensions": ["n3"] + }, + "text/parameters": { + "source": "iana" + }, + "text/parityfec": { + "source": "iana" + }, + "text/plain": { + "source": "iana", + "compressible": true, + "extensions": ["txt","text","conf","def","list","log","in","ini"] + }, + "text/provenance-notation": { + "source": "iana" + }, + "text/prs.fallenstein.rst": { + "source": "iana" + }, + "text/prs.lines.tag": { + "source": "iana", + "extensions": ["dsc"] + }, + "text/raptorfec": { + "source": "iana" + }, + "text/red": { + "source": "iana" + }, + "text/rfc822-headers": { + "source": "iana" + }, + "text/richtext": { + "source": "iana", + "compressible": true, + "extensions": ["rtx"] + }, + "text/rtf": { + "source": "iana", + "compressible": true, + "extensions": ["rtf"] + }, + "text/rtp-enc-aescm128": { + "source": "iana" + }, + "text/rtploopback": { + "source": "iana" + }, + "text/rtx": { + "source": "iana" + }, + "text/sgml": { + "source": "iana", + "extensions": ["sgml","sgm"] + }, + "text/stylus": { + "extensions": ["stylus","styl"] + }, + "text/t140": { + "source": "iana" + }, + "text/tab-separated-values": { + "source": "iana", + "compressible": true, + "extensions": ["tsv"] + }, + "text/troff": { + "source": "iana", + "extensions": ["t","tr","roff","man","me","ms"] + }, + "text/turtle": { + "source": "iana", + "extensions": ["ttl"] + }, + "text/ulpfec": { + "source": "iana" + }, + "text/uri-list": { + "source": "iana", + "compressible": true, + "extensions": ["uri","uris","urls"] + }, + "text/vcard": { + "source": "iana", + "compressible": true, + "extensions": ["vcard"] + }, + "text/vnd.a": { + "source": "iana" + }, + "text/vnd.abc": { + "source": "iana" + }, + "text/vnd.curl": { + "source": "iana", + "extensions": ["curl"] + }, + "text/vnd.curl.dcurl": { + "source": "apache", + "extensions": ["dcurl"] + }, + "text/vnd.curl.mcurl": { + "source": "apache", + "extensions": ["mcurl"] + }, + "text/vnd.curl.scurl": { + "source": "apache", + "extensions": ["scurl"] + }, + "text/vnd.debian.copyright": { + "source": "iana" + }, + "text/vnd.dmclientscript": { + "source": "iana" + }, + "text/vnd.dvb.subtitle": { + "source": "iana", + "extensions": ["sub"] + }, + "text/vnd.esmertec.theme-descriptor": { + "source": "iana" + }, + "text/vnd.fly": { + "source": "iana", + "extensions": ["fly"] + }, + "text/vnd.fmi.flexstor": { + "source": "iana", + "extensions": ["flx"] + }, + "text/vnd.graphviz": { + "source": "iana", + "extensions": ["gv"] + }, + "text/vnd.in3d.3dml": { + "source": "iana", + "extensions": ["3dml"] + }, + "text/vnd.in3d.spot": { + "source": "iana", + "extensions": ["spot"] + }, + "text/vnd.iptc.newsml": { + "source": "iana" + }, + "text/vnd.iptc.nitf": { + "source": "iana" + }, + "text/vnd.latex-z": { + "source": "iana" + }, + "text/vnd.motorola.reflex": { + "source": "iana" + }, + "text/vnd.ms-mediapackage": { + "source": "iana" + }, + "text/vnd.net2phone.commcenter.command": { + "source": "iana" + }, + "text/vnd.radisys.msml-basic-layout": { + "source": "iana" + }, + "text/vnd.si.uricatalogue": { + "source": "iana" + }, + "text/vnd.sun.j2me.app-descriptor": { + "source": "iana", + "extensions": ["jad"] + }, + "text/vnd.trolltech.linguist": { + "source": "iana" + }, + "text/vnd.wap.si": { + "source": "iana" + }, + "text/vnd.wap.sl": { + "source": "iana" + }, + "text/vnd.wap.wml": { + "source": "iana", + "extensions": ["wml"] + }, + "text/vnd.wap.wmlscript": { + "source": "iana", + "extensions": ["wmls"] + }, + "text/vtt": { + "charset": "UTF-8", + "compressible": true, + "extensions": ["vtt"] + }, + "text/x-asm": { + "source": "apache", + "extensions": ["s","asm"] + }, + "text/x-c": { + "source": "apache", + "extensions": ["c","cc","cxx","cpp","h","hh","dic"] + }, + "text/x-component": { + "extensions": ["htc"] + }, + "text/x-fortran": { + "source": "apache", + "extensions": ["f","for","f77","f90"] + }, + "text/x-gwt-rpc": { + "compressible": true + }, + "text/x-handlebars-template": { + "extensions": ["hbs"] + }, + "text/x-java-source": { + "source": "apache", + "extensions": ["java"] + }, + "text/x-jquery-tmpl": { + "compressible": true + }, + "text/x-lua": { + "extensions": ["lua"] + }, + "text/x-markdown": { + "compressible": true, + "extensions": ["markdown","md","mkd"] + }, + "text/x-nfo": { + "source": "apache", + "extensions": ["nfo"] + }, + "text/x-opml": { + "source": "apache", + "extensions": ["opml"] + }, + "text/x-pascal": { + "source": "apache", + "extensions": ["p","pas"] + }, + "text/x-sass": { + "extensions": ["sass"] + }, + "text/x-scss": { + "extensions": ["scss"] + }, + "text/x-setext": { + "source": "apache", + "extensions": ["etx"] + }, + "text/x-sfv": { + "source": "apache", + "extensions": ["sfv"] + }, + "text/x-uuencode": { + "source": "apache", + "extensions": ["uu"] + }, + "text/x-vcalendar": { + "source": "apache", + "extensions": ["vcs"] + }, + "text/x-vcard": { + "source": "apache", + "extensions": ["vcf"] + }, + "text/xml": { + "source": "iana", + "compressible": true + }, + "text/xml-external-parsed-entity": { + "source": "iana" + }, + "text/yaml": { + "extensions": ["yaml","yml"] + }, + "video/1d-interleaved-parityfec": { + "source": "apache" + }, + "video/3gpp": { + "source": "apache", + "extensions": ["3gp"] + }, + "video/3gpp-tt": { + "source": "apache" + }, + "video/3gpp2": { + "source": "apache", + "extensions": ["3g2"] + }, + "video/bmpeg": { + "source": "apache" + }, + "video/bt656": { + "source": "apache" + }, + "video/celb": { + "source": "apache" + }, + "video/dv": { + "source": "apache" + }, + "video/h261": { + "source": "apache", + "extensions": ["h261"] + }, + "video/h263": { + "source": "apache", + "extensions": ["h263"] + }, + "video/h263-1998": { + "source": "apache" + }, + "video/h263-2000": { + "source": "apache" + }, + "video/h264": { + "source": "apache", + "extensions": ["h264"] + }, + "video/h264-rcdo": { + "source": "apache" + }, + "video/h264-svc": { + "source": "apache" + }, + "video/jpeg": { + "source": "apache", + "extensions": ["jpgv"] + }, + "video/jpeg2000": { + "source": "apache" + }, + "video/jpm": { + "source": "apache", + "extensions": ["jpm","jpgm"] + }, + "video/mj2": { + "source": "apache", + "extensions": ["mj2","mjp2"] + }, + "video/mp1s": { + "source": "apache" + }, + "video/mp2p": { + "source": "apache" + }, + "video/mp2t": { + "source": "apache", + "extensions": ["ts"] + }, + "video/mp4": { + "source": "apache", + "compressible": false, + "extensions": ["mp4","mp4v","mpg4"] + }, + "video/mp4v-es": { + "source": "apache" + }, + "video/mpeg": { + "source": "apache", + "compressible": false, + "extensions": ["mpeg","mpg","mpe","m1v","m2v"] + }, + "video/mpeg4-generic": { + "source": "apache" + }, + "video/mpv": { + "source": "apache" + }, + "video/nv": { + "source": "apache" + }, + "video/ogg": { + "source": "apache", + "compressible": false, + "extensions": ["ogv"] + }, + "video/parityfec": { + "source": "apache" + }, + "video/pointer": { + "source": "apache" + }, + "video/quicktime": { + "source": "apache", + "compressible": false, + "extensions": ["qt","mov"] + }, + "video/raw": { + "source": "apache" + }, + "video/rtp-enc-aescm128": { + "source": "apache" + }, + "video/rtx": { + "source": "apache" + }, + "video/smpte292m": { + "source": "apache" + }, + "video/ulpfec": { + "source": "apache" + }, + "video/vc1": { + "source": "apache" + }, + "video/vnd.cctv": { + "source": "apache" + }, + "video/vnd.dece.hd": { + "source": "apache", + "extensions": ["uvh","uvvh"] + }, + "video/vnd.dece.mobile": { + "source": "apache", + "extensions": ["uvm","uvvm"] + }, + "video/vnd.dece.mp4": { + "source": "apache" + }, + "video/vnd.dece.pd": { + "source": "apache", + "extensions": ["uvp","uvvp"] + }, + "video/vnd.dece.sd": { + "source": "apache", + "extensions": ["uvs","uvvs"] + }, + "video/vnd.dece.video": { + "source": "apache", + "extensions": ["uvv","uvvv"] + }, + "video/vnd.directv.mpeg": { + "source": "apache" + }, + "video/vnd.directv.mpeg-tts": { + "source": "apache" + }, + "video/vnd.dlna.mpeg-tts": { + "source": "apache" + }, + "video/vnd.dvb.file": { + "source": "apache", + "extensions": ["dvb"] + }, + "video/vnd.fvt": { + "source": "apache", + "extensions": ["fvt"] + }, + "video/vnd.hns.video": { + "source": "apache" + }, + "video/vnd.iptvforum.1dparityfec-1010": { + "source": "apache" + }, + "video/vnd.iptvforum.1dparityfec-2005": { + "source": "apache" + }, + "video/vnd.iptvforum.2dparityfec-1010": { + "source": "apache" + }, + "video/vnd.iptvforum.2dparityfec-2005": { + "source": "apache" + }, + "video/vnd.iptvforum.ttsavc": { + "source": "apache" + }, + "video/vnd.iptvforum.ttsmpeg2": { + "source": "apache" + }, + "video/vnd.motorola.video": { + "source": "apache" + }, + "video/vnd.motorola.videop": { + "source": "apache" + }, + "video/vnd.mpegurl": { + "source": "apache", + "extensions": ["mxu","m4u"] + }, + "video/vnd.ms-playready.media.pyv": { + "source": "apache", + "extensions": ["pyv"] + }, + "video/vnd.nokia.interleaved-multimedia": { + "source": "apache" + }, + "video/vnd.nokia.videovoip": { + "source": "apache" + }, + "video/vnd.objectvideo": { + "source": "apache" + }, + "video/vnd.sealed.mpeg1": { + "source": "apache" + }, + "video/vnd.sealed.mpeg4": { + "source": "apache" + }, + "video/vnd.sealed.swf": { + "source": "apache" + }, + "video/vnd.sealedmedia.softseal.mov": { + "source": "apache" + }, + "video/vnd.uvvu.mp4": { + "source": "apache", + "extensions": ["uvu","uvvu"] + }, + "video/vnd.vivo": { + "source": "apache", + "extensions": ["viv"] + }, + "video/webm": { + "source": "apache", + "compressible": false, + "extensions": ["webm"] + }, + "video/x-f4v": { + "source": "apache", + "extensions": ["f4v"] + }, + "video/x-fli": { + "source": "apache", + "extensions": ["fli"] + }, + "video/x-flv": { + "source": "apache", + "compressible": false, + "extensions": ["flv"] + }, + "video/x-m4v": { + "source": "apache", + "extensions": ["m4v"] + }, + "video/x-matroska": { + "source": "apache", + "compressible": false, + "extensions": ["mkv","mk3d","mks"] + }, + "video/x-mng": { + "source": "apache", + "extensions": ["mng"] + }, + "video/x-ms-asf": { + "source": "apache", + "extensions": ["asf","asx"] + }, + "video/x-ms-vob": { + "source": "apache", + "extensions": ["vob"] + }, + "video/x-ms-wm": { + "source": "apache", + "extensions": ["wm"] + }, + "video/x-ms-wmv": { + "source": "apache", + "compressible": false, + "extensions": ["wmv"] + }, + "video/x-ms-wmx": { + "source": "apache", + "extensions": ["wmx"] + }, + "video/x-ms-wvx": { + "source": "apache", + "extensions": ["wvx"] + }, + "video/x-msvideo": { + "source": "apache", + "extensions": ["avi"] + }, + "video/x-sgi-movie": { + "source": "apache", + "extensions": ["movie"] + }, + "video/x-smv": { + "source": "apache", + "extensions": ["smv"] + }, + "x-conference/x-cooltalk": { + "source": "apache", + "extensions": ["ice"] + }, + "x-shader/x-fragment": { + "compressible": true + }, + "x-shader/x-vertex": { + "compressible": true + } +} diff --git a/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/index.js b/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/index.js new file mode 100644 index 0000000..551031f --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/index.js @@ -0,0 +1,11 @@ +/*! + * mime-db + * Copyright(c) 2014 Jonathan Ong + * MIT Licensed + */ + +/** + * Module exports. + */ + +module.exports = require('./db.json') diff --git a/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/package.json b/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/package.json new file mode 100644 index 0000000..e9e5b9c --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/mime-types/node_modules/mime-db/package.json @@ -0,0 +1,93 @@ +{ + "name": "mime-db", + "description": "Media Type Database", + "version": "1.12.0", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + { + "name": "Robert Kieffer", + "email": "robert@broofa.com", + "url": "http://github.com/broofa" + } + ], + "license": "MIT", + "keywords": [ + "mime", + "db", + "type", + "types", + "database", + "charset", + "charsets" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/mime-db" + }, + "devDependencies": { + "bluebird": "2.9.27", + "co": "4.5.4", + "cogent": "1.0.1", + "csv-parse": "0.1.2", + "gnode": "0.1.1", + "istanbul": "0.3.9", + "mocha": "1.21.5", + "raw-body": "2.1.0", + "stream-to-array": "2" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "README.md", + "db.json", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "build": "node scripts/build", + "fetch": "gnode scripts/extensions && gnode scripts/types", + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/", + "update": "npm run fetch && npm run build" + }, + "gitHead": "cf35cbba6b22f4a3b3eef9a32129ea5b7f0f91ee", + "bugs": { + "url": "https://github.com/jshttp/mime-db/issues" + }, + "homepage": "https://github.com/jshttp/mime-db", + "_id": "mime-db@1.12.0", + "_shasum": "3d0c63180f458eb10d325aaa37d7c58ae312e9d7", + "_from": "mime-db@>=1.12.0 <1.13.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "3d0c63180f458eb10d325aaa37d7c58ae312e9d7", + "tarball": "http://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz" +} diff --git a/node_modules/multer/node_modules/type-is/node_modules/mime-types/package.json b/node_modules/multer/node_modules/type-is/node_modules/mime-types/package.json new file mode 100644 index 0000000..247f1a8 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/node_modules/mime-types/package.json @@ -0,0 +1,83 @@ +{ + "name": "mime-types", + "description": "The ultimate javascript content-type utility.", + "version": "2.0.14", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jeremiah Senkpiel", + "email": "fishrock123@rocketmail.com", + "url": "https://searchbeam.jit.su" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + } + ], + "license": "MIT", + "keywords": [ + "mime", + "types" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/mime-types" + }, + "dependencies": { + "mime-db": "~1.12.0" + }, + "devDependencies": { + "istanbul": "0.3.9", + "mocha": "~1.21.5" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec test/test.js", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot test/test.js", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter dot test/test.js" + }, + "gitHead": "7d53a3351581eb3d7ae1e846ea860037bce6fe3f", + "bugs": { + "url": "https://github.com/jshttp/mime-types/issues" + }, + "homepage": "https://github.com/jshttp/mime-types", + "_id": "mime-types@2.0.14", + "_shasum": "310e159db23e077f8bb22b748dabfa4957140aa6", + "_from": "mime-types@>=2.0.9 <2.1.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "310e159db23e077f8bb22b748dabfa4957140aa6", + "tarball": "http://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz" +} diff --git a/node_modules/multer/node_modules/type-is/package.json b/node_modules/multer/node_modules/type-is/package.json new file mode 100644 index 0000000..50cab07 --- /dev/null +++ b/node_modules/multer/node_modules/type-is/package.json @@ -0,0 +1,92 @@ +{ + "name": "type-is", + "description": "Infer the content-type of a request.", + "version": "1.5.7", + "author": { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/jshttp/type-is" + }, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.0.9" + }, + "devDependencies": { + "istanbul": "0.3.5", + "mocha": "~1.21.5" + }, + "engines": { + "node": ">= 0.6" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "index.js" + ], + "scripts": { + "test": "mocha --reporter spec --check-leaks --bail test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "keywords": [ + "content", + "type", + "checking" + ], + "gitHead": "f4335cc563a98ee80366f04f67c50cef089ae803", + "bugs": { + "url": "https://github.com/jshttp/type-is/issues" + }, + "homepage": "https://github.com/jshttp/type-is", + "_id": "type-is@1.5.7", + "_shasum": "b9368a593cc6ef7d0645e78b2f4c64cbecd05e90", + "_from": "type-is@>=1.5.2 <1.6.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "shtylman", + "email": "shtylman@gmail.com" + }, + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + } + ], + "dist": { + "shasum": "b9368a593cc6ef7d0645e78b2f4c64cbecd05e90", + "tarball": "http://registry.npmjs.org/type-is/-/type-is-1.5.7.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/type-is/-/type-is-1.5.7.tgz" +} diff --git a/node_modules/multer/package.json b/node_modules/multer/package.json new file mode 100644 index 0000000..2c59494 --- /dev/null +++ b/node_modules/multer/package.json @@ -0,0 +1,84 @@ +{ + "name": "multer", + "description": "Middleware for handling `multipart/form-data`.", + "version": "0.1.8", + "contributors": [ + { + "name": "Hage Yaapa", + "email": "captain@hacksparrow.com", + "url": "http://www.hacksparrow.com" + }, + { + "name": "Jaret Pfluger", + "email": "https://github.com/jpfluger" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/expressjs/multer" + }, + "keywords": [ + "form", + "post", + "multipart", + "form-data", + "formdata", + "express", + "middleware" + ], + "dependencies": { + "busboy": "~0.2.9", + "mkdirp": "~0.3.5", + "qs": "~1.2.2", + "type-is": "~1.5.2" + }, + "devDependencies": { + "chai": "^1.9.1", + "co": "^3.0.6", + "express": "*", + "mocha": "*", + "rimraf": "^2.2.8", + "supertest": "^0.13.0" + }, + "engines": { + "node": ">= 0.10.0" + }, + "files": [ + "LICENSE", + "index.js" + ], + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks" + }, + "gitHead": "58ee463ab42accf3e910ceb6e095a90de6b41ae7", + "bugs": { + "url": "https://github.com/expressjs/multer/issues" + }, + "homepage": "https://github.com/expressjs/multer", + "_id": "multer@0.1.8", + "_shasum": "551b8a6015093701bcacc964916b1ae06578f37b", + "_from": "multer@>=0.1.7 <0.2.0", + "_npmVersion": "2.5.1", + "_nodeVersion": "0.10.32", + "_npmUser": { + "name": "hacksparrow", + "email": "captain@hacksparrow.com" + }, + "maintainers": [ + { + "name": "hacksparrow", + "email": "captain@hacksparrow.com" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + } + ], + "dist": { + "shasum": "551b8a6015093701bcacc964916b1ae06578f37b", + "tarball": "http://registry.npmjs.org/multer/-/multer-0.1.8.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/multer/-/multer-0.1.8.tgz" +} diff --git a/node_modules/promises-aplus-tests/LICENSE.txt b/node_modules/promises-aplus-tests/LICENSE.txt new file mode 100644 index 0000000..6000885 --- /dev/null +++ b/node_modules/promises-aplus-tests/LICENSE.txt @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2012 Domenic Denicola + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/node_modules/promises-aplus-tests/README.md b/node_modules/promises-aplus-tests/README.md new file mode 100644 index 0000000..c84148b --- /dev/null +++ b/node_modules/promises-aplus-tests/README.md @@ -0,0 +1,94 @@ + + Promises/A+ logo + + +# Promises/A+ Compliance Test Suite + +This suite tests compliance of a promise implementation with the [Promises/A+ specification][]. + +[Promises/A+ specification]: https://github.com/promises-aplus/promises-spec + +Passing the tests in this repo means that you have a Promises/A+ compliant implementation of the `then()` method, and you can display the Promises/A+ logo in your README. You can also [send a pull request](https://github.com/promises-aplus/promises-spec) to have your implementation listed on the [implementations page](https://promisesaplus.com/implementations). + +## How To Run + +The tests can run in either a Node.js environment or, if you set things up correctly, in the browser. + +### Adapters + +In order to test your promise library, you must expose a very minimal adapter interface. These are written as Node.js +modules with a few well-known exports: + +- `resolved(value)`: creates a promise that is resolved with `value`. +- `rejected(reason)`: creates a promise that is already rejected with `reason`. +- `deferred()`: creates an object consisting of `{ promise, resolve, reject }`: + - `promise` is a promise that is currently in the pending state. + - `resolve(value)` resolves the promise with `value`. + - `reject(reason)` moves the promise from the pending state to the rejected state, with rejection reason `reason`. + +The `resolved` and `rejected` exports are actually optional, and will be automatically created by the test runner using +`deferred` if they are not present. But, if your promise library has the capability to create already-resolved or +already-rejected promises, then you should include these exports, so that the test runner can provide you with better +code coverage and uncover any bugs in those methods. + +Note that the tests will never pass a promise or a thenable as a resolution. That means that we never use the promise- +or thenable-accepting forms of the resolve operation directly, and instead only use the direct fulfillment operation, +since fulfill and resolve are equivalent when not given a thenable. + +Finally, note that none of these functions, including `deferred().resolve` and `deferred().reject`, should throw +exceptions. The tests are not structured to deal with that, and if your implementation has the potential to throw +exceptions—e.g., perhaps it throws when trying to resolve an already-resolved promise—you should wrap direct calls to +your implementation in `try`/`catch` when writing the adapter. + +### From the CLI + +This package comes with a command-line interface that can be used either by installing it globally with +`npm install promises-aplus-tests -g` or by including it in your `package.json`'s `devDependencies` and using npm's +`scripts` feature. In the latter case, your setup might look something like + +```json +{ + "devDependencies": { + "promises-aplus-tests": "*" + }, + "scripts": { + "test": "run-my-own-tests && promises-aplus-tests test/my-adapter" + } +} +``` + +The CLI takes as its first argument the filename of your adapter file, relative to the current working directory. It +tries to pass through any subsequent options to Mocha, so you can use e.g. `--reporter spec` or `--grep 2.2.4`. + +### Programmatically + +The main export of this package is a function that allows you to run the tests against an adapter: + +```js +var promisesAplusTests = require("promises-aplus-tests"); + +promisesAplusTests(adapter, function (err) { + // All done; output is in the console. Or check `err` for number of failures. +}); +``` + +You can also pass any Mocha options as the second parameter, e.g. + +```js +promisesAplusTests(adapter, { reporter: "dot" }, function (err) { + // As before. +}); +``` + +### Within an Existing Mocha Test Suite + +If you already have a Mocha test suite and want to include these tests in it, you can do: + +```js +describe("Promises/A+ Tests", function () { + require("promises-aplus-tests").mocha(adapter); +}); +``` + +This also works in the browser, if you have your Mocha tests running there, as long as you use [browserify](http://browserify.org/). diff --git a/node_modules/promises-aplus-tests/lib/cli.js b/node_modules/promises-aplus-tests/lib/cli.js new file mode 100755 index 0000000..bb3cee7 --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/cli.js @@ -0,0 +1,34 @@ +#!/usr/bin/env node +"use strict"; + +var path = require("path"); +var getMochaOpts = require("./getMochaOpts"); +var programmaticRunner = require("./programmaticRunner"); + +var filePath = getAdapterFilePath(); +var adapter = adapterObjectFromFilePath(filePath); +var mochaOpts = getMochaOpts(process.argv.slice(3)); +programmaticRunner(adapter, mochaOpts, function (err) { + if (err) { + process.exit(err.failures || -1); + } +}); + +function getAdapterFilePath() { + if (process.argv[2]) { + return path.join(process.cwd(), process.argv[2]); + } else { + throw new Error("Specify your adapter file as an argument."); + } +} + +function adapterObjectFromFilePath(filePath) { + try { + return require(filePath); + } catch (e) { + var error = new Error("Error `require`ing adapter file " + filePath + "\n\n" + e); + error.cause = e; + + throw error; + } +} diff --git a/node_modules/promises-aplus-tests/lib/getMochaOpts.js b/node_modules/promises-aplus-tests/lib/getMochaOpts.js new file mode 100644 index 0000000..d059ac8 --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/getMochaOpts.js @@ -0,0 +1,19 @@ +"use strict"; + +module.exports = function getMochaOpts(args) { + var rawOpts = args; + var opts = {}; + + rawOpts.join(" ").split("--").forEach(function (opt) { + var optSplit = opt.split(" "); + + var key = optSplit[0]; + var value = optSplit[1] || true; + + if (key) { + opts[key] = value; + } + }); + + return opts; +}; diff --git a/node_modules/promises-aplus-tests/lib/programmaticRunner.js b/node_modules/promises-aplus-tests/lib/programmaticRunner.js new file mode 100644 index 0000000..f563f8d --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/programmaticRunner.js @@ -0,0 +1,76 @@ +"use strict"; + +var Mocha = require("mocha"); +var path = require("path"); +var fs = require("fs"); +var _ = require("underscore"); + +var testsDir = path.resolve(__dirname, "tests"); + +function normalizeAdapter(adapter) { + if (!adapter.resolved) { + adapter.resolved = function (value) { + var d = adapter.deferred(); + d.resolve(value); + return d.promise; + }; + } + + if (!adapter.rejected) { + adapter.rejected = function (reason) { + var d = adapter.deferred(); + d.reject(reason); + return d.promise; + }; + } +} + +module.exports = function (adapter, mochaOpts, cb) { + if (typeof mochaOpts === "function") { + cb = mochaOpts; + mochaOpts = {}; + } + if (typeof cb !== "function") { + cb = function () { }; + } + + normalizeAdapter(adapter); + mochaOpts = _.defaults(mochaOpts, { timeout: 200, slow: Infinity }); + + fs.readdir(testsDir, function (err, testFileNames) { + if (err) { + cb(err); + return; + } + + var mocha = new Mocha(mochaOpts); + testFileNames.forEach(function (testFileName) { + if (path.extname(testFileName) === ".js") { + var testFilePath = path.resolve(testsDir, testFileName); + mocha.addFile(testFilePath); + } + }); + + global.adapter = adapter; + mocha.run(function (failures) { + delete global.adapter; + if (failures > 0) { + var err = new Error("Test suite failed with " + failures + " failures."); + err.failures = failures; + cb(err); + } else { + cb(null); + } + }); + }); +}; + +module.exports.mocha = function (adapter) { + normalizeAdapter(adapter); + + global.adapter = adapter; + + require("./testFiles"); + + delete global.adapter; +}; diff --git a/node_modules/promises-aplus-tests/lib/testFiles.js b/node_modules/promises-aplus-tests/lib/testFiles.js new file mode 100644 index 0000000..237c8ae --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/testFiles.js @@ -0,0 +1,13 @@ +require("./tests/2.1.2"); +require("./tests/2.1.3"); +require("./tests/2.2.1"); +require("./tests/2.2.2"); +require("./tests/2.2.3"); +require("./tests/2.2.4"); +require("./tests/2.2.5"); +require("./tests/2.2.6"); +require("./tests/2.2.7"); +require("./tests/2.3.1"); +require("./tests/2.3.2"); +require("./tests/2.3.3"); +require("./tests/2.3.4"); diff --git a/node_modules/promises-aplus-tests/lib/tests/2.1.2.js b/node_modules/promises-aplus-tests/lib/tests/2.1.2.js new file mode 100644 index 0000000..b60e5d5 --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/2.1.2.js @@ -0,0 +1,76 @@ +"use strict"; + +var assert = require("assert"); +var testFulfilled = require("./helpers/testThreeCases").testFulfilled; + +var adapter = global.adapter; +var deferred = adapter.deferred; + +var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it + +describe("2.1.2.1: When fulfilled, a promise: must not transition to any other state.", function () { + testFulfilled(dummy, function (promise, done) { + var onFulfilledCalled = false; + + promise.then(function onFulfilled() { + onFulfilledCalled = true; + }, function onRejected() { + assert.strictEqual(onFulfilledCalled, false); + done(); + }); + + setTimeout(done, 100); + }); + + specify("trying to fulfill then immediately reject", function (done) { + var d = deferred(); + var onFulfilledCalled = false; + + d.promise.then(function onFulfilled() { + onFulfilledCalled = true; + }, function onRejected() { + assert.strictEqual(onFulfilledCalled, false); + done(); + }); + + d.resolve(dummy); + d.reject(dummy); + setTimeout(done, 100); + }); + + specify("trying to fulfill then reject, delayed", function (done) { + var d = deferred(); + var onFulfilledCalled = false; + + d.promise.then(function onFulfilled() { + onFulfilledCalled = true; + }, function onRejected() { + assert.strictEqual(onFulfilledCalled, false); + done(); + }); + + setTimeout(function () { + d.resolve(dummy); + d.reject(dummy); + }, 50); + setTimeout(done, 100); + }); + + specify("trying to fulfill immediately then reject delayed", function (done) { + var d = deferred(); + var onFulfilledCalled = false; + + d.promise.then(function onFulfilled() { + onFulfilledCalled = true; + }, function onRejected() { + assert.strictEqual(onFulfilledCalled, false); + done(); + }); + + d.resolve(dummy); + setTimeout(function () { + d.reject(dummy); + }, 50); + setTimeout(done, 100); + }); +}); diff --git a/node_modules/promises-aplus-tests/lib/tests/2.1.3.js b/node_modules/promises-aplus-tests/lib/tests/2.1.3.js new file mode 100644 index 0000000..019a198 --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/2.1.3.js @@ -0,0 +1,76 @@ +"use strict"; + +var assert = require("assert"); +var testRejected = require("./helpers/testThreeCases").testRejected; + +var adapter = global.adapter; +var deferred = adapter.deferred; + +var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it + +describe("2.1.3.1: When rejected, a promise: must not transition to any other state.", function () { + testRejected(dummy, function (promise, done) { + var onRejectedCalled = false; + + promise.then(function onFulfilled() { + assert.strictEqual(onRejectedCalled, false); + done(); + }, function onRejected() { + onRejectedCalled = true; + }); + + setTimeout(done, 100); + }); + + specify("trying to reject then immediately fulfill", function (done) { + var d = deferred(); + var onRejectedCalled = false; + + d.promise.then(function onFulfilled() { + assert.strictEqual(onRejectedCalled, false); + done(); + }, function onRejected() { + onRejectedCalled = true; + }); + + d.reject(dummy); + d.resolve(dummy); + setTimeout(done, 100); + }); + + specify("trying to reject then fulfill, delayed", function (done) { + var d = deferred(); + var onRejectedCalled = false; + + d.promise.then(function onFulfilled() { + assert.strictEqual(onRejectedCalled, false); + done(); + }, function onRejected() { + onRejectedCalled = true; + }); + + setTimeout(function () { + d.reject(dummy); + d.resolve(dummy); + }, 50); + setTimeout(done, 100); + }); + + specify("trying to reject immediately then fulfill delayed", function (done) { + var d = deferred(); + var onRejectedCalled = false; + + d.promise.then(function onFulfilled() { + assert.strictEqual(onRejectedCalled, false); + done(); + }, function onRejected() { + onRejectedCalled = true; + }); + + d.reject(dummy); + setTimeout(function () { + d.resolve(dummy); + }, 50); + setTimeout(done, 100); + }); +}); diff --git a/node_modules/promises-aplus-tests/lib/tests/2.2.1.js b/node_modules/promises-aplus-tests/lib/tests/2.2.1.js new file mode 100644 index 0000000..8d91988 --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/2.2.1.js @@ -0,0 +1,77 @@ +"use strict"; + +var adapter = global.adapter; +var resolved = adapter.resolved; +var rejected = adapter.rejected; + +var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it + +describe("2.2.1: Both `onFulfilled` and `onRejected` are optional arguments.", function () { + describe("2.2.1.1: If `onFulfilled` is not a function, it must be ignored.", function () { + describe("applied to a directly-rejected promise", function () { + function testNonFunction(nonFunction, stringRepresentation) { + specify("`onFulfilled` is " + stringRepresentation, function (done) { + rejected(dummy).then(nonFunction, function () { + done(); + }); + }); + } + + testNonFunction(undefined, "`undefined`"); + testNonFunction(null, "`null`"); + testNonFunction(false, "`false`"); + testNonFunction(5, "`5`"); + testNonFunction({}, "an object"); + }); + + describe("applied to a promise rejected and then chained off of", function () { + function testNonFunction(nonFunction, stringRepresentation) { + specify("`onFulfilled` is " + stringRepresentation, function (done) { + rejected(dummy).then(function () { }, undefined).then(nonFunction, function () { + done(); + }); + }); + } + + testNonFunction(undefined, "`undefined`"); + testNonFunction(null, "`null`"); + testNonFunction(false, "`false`"); + testNonFunction(5, "`5`"); + testNonFunction({}, "an object"); + }); + }); + + describe("2.2.1.2: If `onRejected` is not a function, it must be ignored.", function () { + describe("applied to a directly-fulfilled promise", function () { + function testNonFunction(nonFunction, stringRepresentation) { + specify("`onRejected` is " + stringRepresentation, function (done) { + resolved(dummy).then(function () { + done(); + }, nonFunction); + }); + } + + testNonFunction(undefined, "`undefined`"); + testNonFunction(null, "`null`"); + testNonFunction(false, "`false`"); + testNonFunction(5, "`5`"); + testNonFunction({}, "an object"); + }); + + describe("applied to a promise fulfilled and then chained off of", function () { + function testNonFunction(nonFunction, stringRepresentation) { + specify("`onFulfilled` is " + stringRepresentation, function (done) { + resolved(dummy).then(undefined, function () { }).then(function () { + done(); + }, nonFunction); + }); + } + + testNonFunction(undefined, "`undefined`"); + testNonFunction(null, "`null`"); + testNonFunction(false, "`false`"); + testNonFunction(5, "`5`"); + testNonFunction({}, "an object"); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/lib/tests/2.2.2.js b/node_modules/promises-aplus-tests/lib/tests/2.2.2.js new file mode 100644 index 0000000..f706ce9 --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/2.2.2.js @@ -0,0 +1,151 @@ +"use strict"; + +var assert = require("assert"); +var testFulfilled = require("./helpers/testThreeCases").testFulfilled; + +var adapter = global.adapter; +var resolved = adapter.resolved; +var deferred = adapter.deferred; + +var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it +var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality + +describe("2.2.2: If `onFulfilled` is a function,", function () { + describe("2.2.2.1: it must be called after `promise` is fulfilled, with `promise`’s fulfillment value as its " + + "first argument.", function () { + testFulfilled(sentinel, function (promise, done) { + promise.then(function onFulfilled(value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + + describe("2.2.2.2: it must not be called before `promise` is fulfilled", function () { + specify("fulfilled after a delay", function (done) { + var d = deferred(); + var isFulfilled = false; + + d.promise.then(function onFulfilled() { + assert.strictEqual(isFulfilled, true); + done(); + }); + + setTimeout(function () { + d.resolve(dummy); + isFulfilled = true; + }, 50); + }); + + specify("never fulfilled", function (done) { + var d = deferred(); + var onFulfilledCalled = false; + + d.promise.then(function onFulfilled() { + onFulfilledCalled = true; + done(); + }); + + setTimeout(function () { + assert.strictEqual(onFulfilledCalled, false); + done(); + }, 150); + }); + }); + + describe("2.2.2.3: it must not be called more than once.", function () { + specify("already-fulfilled", function (done) { + var timesCalled = 0; + + resolved(dummy).then(function onFulfilled() { + assert.strictEqual(++timesCalled, 1); + done(); + }); + }); + + specify("trying to fulfill a pending promise more than once, immediately", function (done) { + var d = deferred(); + var timesCalled = 0; + + d.promise.then(function onFulfilled() { + assert.strictEqual(++timesCalled, 1); + done(); + }); + + d.resolve(dummy); + d.resolve(dummy); + }); + + specify("trying to fulfill a pending promise more than once, delayed", function (done) { + var d = deferred(); + var timesCalled = 0; + + d.promise.then(function onFulfilled() { + assert.strictEqual(++timesCalled, 1); + done(); + }); + + setTimeout(function () { + d.resolve(dummy); + d.resolve(dummy); + }, 50); + }); + + specify("trying to fulfill a pending promise more than once, immediately then delayed", function (done) { + var d = deferred(); + var timesCalled = 0; + + d.promise.then(function onFulfilled() { + assert.strictEqual(++timesCalled, 1); + done(); + }); + + d.resolve(dummy); + setTimeout(function () { + d.resolve(dummy); + }, 50); + }); + + specify("when multiple `then` calls are made, spaced apart in time", function (done) { + var d = deferred(); + var timesCalled = [0, 0, 0]; + + d.promise.then(function onFulfilled() { + assert.strictEqual(++timesCalled[0], 1); + }); + + setTimeout(function () { + d.promise.then(function onFulfilled() { + assert.strictEqual(++timesCalled[1], 1); + }); + }, 50); + + setTimeout(function () { + d.promise.then(function onFulfilled() { + assert.strictEqual(++timesCalled[2], 1); + done(); + }); + }, 100); + + setTimeout(function () { + d.resolve(dummy); + }, 150); + }); + + specify("when `then` is interleaved with fulfillment", function (done) { + var d = deferred(); + var timesCalled = [0, 0]; + + d.promise.then(function onFulfilled() { + assert.strictEqual(++timesCalled[0], 1); + }); + + d.resolve(dummy); + + d.promise.then(function onFulfilled() { + assert.strictEqual(++timesCalled[1], 1); + done(); + }); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/lib/tests/2.2.3.js b/node_modules/promises-aplus-tests/lib/tests/2.2.3.js new file mode 100644 index 0000000..8f0b14f --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/2.2.3.js @@ -0,0 +1,151 @@ +"use strict"; + +var assert = require("assert"); +var testRejected = require("./helpers/testThreeCases").testRejected; + +var adapter = global.adapter; +var rejected = adapter.rejected; +var deferred = adapter.deferred; + +var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it +var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality + +describe("2.2.3: If `onRejected` is a function,", function () { + describe("2.2.3.1: it must be called after `promise` is rejected, with `promise`’s rejection reason as its " + + "first argument.", function () { + testRejected(sentinel, function (promise, done) { + promise.then(null, function onRejected(reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("2.2.3.2: it must not be called before `promise` is rejected", function () { + specify("rejected after a delay", function (done) { + var d = deferred(); + var isRejected = false; + + d.promise.then(null, function onRejected() { + assert.strictEqual(isRejected, true); + done(); + }); + + setTimeout(function () { + d.reject(dummy); + isRejected = true; + }, 50); + }); + + specify("never rejected", function (done) { + var d = deferred(); + var onRejectedCalled = false; + + d.promise.then(null, function onRejected() { + onRejectedCalled = true; + done(); + }); + + setTimeout(function () { + assert.strictEqual(onRejectedCalled, false); + done(); + }, 150); + }); + }); + + describe("2.2.3.3: it must not be called more than once.", function () { + specify("already-rejected", function (done) { + var timesCalled = 0; + + rejected(dummy).then(null, function onRejected() { + assert.strictEqual(++timesCalled, 1); + done(); + }); + }); + + specify("trying to reject a pending promise more than once, immediately", function (done) { + var d = deferred(); + var timesCalled = 0; + + d.promise.then(null, function onRejected() { + assert.strictEqual(++timesCalled, 1); + done(); + }); + + d.reject(dummy); + d.reject(dummy); + }); + + specify("trying to reject a pending promise more than once, delayed", function (done) { + var d = deferred(); + var timesCalled = 0; + + d.promise.then(null, function onRejected() { + assert.strictEqual(++timesCalled, 1); + done(); + }); + + setTimeout(function () { + d.reject(dummy); + d.reject(dummy); + }, 50); + }); + + specify("trying to reject a pending promise more than once, immediately then delayed", function (done) { + var d = deferred(); + var timesCalled = 0; + + d.promise.then(null, function onRejected() { + assert.strictEqual(++timesCalled, 1); + done(); + }); + + d.reject(dummy); + setTimeout(function () { + d.reject(dummy); + }, 50); + }); + + specify("when multiple `then` calls are made, spaced apart in time", function (done) { + var d = deferred(); + var timesCalled = [0, 0, 0]; + + d.promise.then(null, function onRejected() { + assert.strictEqual(++timesCalled[0], 1); + }); + + setTimeout(function () { + d.promise.then(null, function onRejected() { + assert.strictEqual(++timesCalled[1], 1); + }); + }, 50); + + setTimeout(function () { + d.promise.then(null, function onRejected() { + assert.strictEqual(++timesCalled[2], 1); + done(); + }); + }, 100); + + setTimeout(function () { + d.reject(dummy); + }, 150); + }); + + specify("when `then` is interleaved with rejection", function (done) { + var d = deferred(); + var timesCalled = [0, 0]; + + d.promise.then(null, function onRejected() { + assert.strictEqual(++timesCalled[0], 1); + }); + + d.reject(dummy); + + d.promise.then(null, function onRejected() { + assert.strictEqual(++timesCalled[1], 1); + done(); + }); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/lib/tests/2.2.4.js b/node_modules/promises-aplus-tests/lib/tests/2.2.4.js new file mode 100644 index 0000000..63591d2 --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/2.2.4.js @@ -0,0 +1,182 @@ +"use strict"; + +var assert = require("assert"); +var testFulfilled = require("./helpers/testThreeCases").testFulfilled; +var testRejected = require("./helpers/testThreeCases").testRejected; + +var adapter = global.adapter; +var resolved = adapter.resolved; +var rejected = adapter.rejected; +var deferred = adapter.deferred; + +var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it + +describe("2.2.4: `onFulfilled` or `onRejected` must not be called until the execution context stack contains only " + + "platform code.", function () { + describe("`then` returns before the promise becomes fulfilled or rejected", function () { + testFulfilled(dummy, function (promise, done) { + var thenHasReturned = false; + + promise.then(function onFulfilled() { + assert.strictEqual(thenHasReturned, true); + done(); + }); + + thenHasReturned = true; + }); + testRejected(dummy, function (promise, done) { + var thenHasReturned = false; + + promise.then(null, function onRejected() { + assert.strictEqual(thenHasReturned, true); + done(); + }); + + thenHasReturned = true; + }); + }); + + describe("Clean-stack execution ordering tests (fulfillment case)", function () { + specify("when `onFulfilled` is added immediately before the promise is fulfilled", + function () { + var d = deferred(); + var onFulfilledCalled = false; + + d.promise.then(function onFulfilled() { + onFulfilledCalled = true; + }); + + d.resolve(dummy); + + assert.strictEqual(onFulfilledCalled, false); + }); + + specify("when `onFulfilled` is added immediately after the promise is fulfilled", + function () { + var d = deferred(); + var onFulfilledCalled = false; + + d.resolve(dummy); + + d.promise.then(function onFulfilled() { + onFulfilledCalled = true; + }); + + assert.strictEqual(onFulfilledCalled, false); + }); + + specify("when one `onFulfilled` is added inside another `onFulfilled`", function (done) { + var promise = resolved(); + var firstOnFulfilledFinished = false; + + promise.then(function () { + promise.then(function () { + assert.strictEqual(firstOnFulfilledFinished, true); + done(); + }); + firstOnFulfilledFinished = true; + }); + }); + + specify("when `onFulfilled` is added inside an `onRejected`", function (done) { + var promise = rejected(); + var promise2 = resolved(); + var firstOnRejectedFinished = false; + + promise.then(null, function () { + promise2.then(function () { + assert.strictEqual(firstOnRejectedFinished, true); + done(); + }); + firstOnRejectedFinished = true; + }); + }); + + specify("when the promise is fulfilled asynchronously", function (done) { + var d = deferred(); + var firstStackFinished = false; + + setTimeout(function () { + d.resolve(dummy); + firstStackFinished = true; + }, 0); + + d.promise.then(function () { + assert.strictEqual(firstStackFinished, true); + done(); + }); + }); + }); + + describe("Clean-stack execution ordering tests (rejection case)", function () { + specify("when `onRejected` is added immediately before the promise is rejected", + function () { + var d = deferred(); + var onRejectedCalled = false; + + d.promise.then(null, function onRejected() { + onRejectedCalled = true; + }); + + d.reject(dummy); + + assert.strictEqual(onRejectedCalled, false); + }); + + specify("when `onRejected` is added immediately after the promise is rejected", + function () { + var d = deferred(); + var onRejectedCalled = false; + + d.reject(dummy); + + d.promise.then(null, function onRejected() { + onRejectedCalled = true; + }); + + assert.strictEqual(onRejectedCalled, false); + }); + + specify("when `onRejected` is added inside an `onFulfilled`", function (done) { + var promise = resolved(); + var promise2 = rejected(); + var firstOnFulfilledFinished = false; + + promise.then(function () { + promise2.then(null, function () { + assert.strictEqual(firstOnFulfilledFinished, true); + done(); + }); + firstOnFulfilledFinished = true; + }); + }); + + specify("when one `onRejected` is added inside another `onRejected`", function (done) { + var promise = rejected(); + var firstOnRejectedFinished = false; + + promise.then(null, function () { + promise.then(null, function () { + assert.strictEqual(firstOnRejectedFinished, true); + done(); + }); + firstOnRejectedFinished = true; + }); + }); + + specify("when the promise is rejected asynchronously", function (done) { + var d = deferred(); + var firstStackFinished = false; + + setTimeout(function () { + d.reject(dummy); + firstStackFinished = true; + }, 0); + + d.promise.then(null, function () { + assert.strictEqual(firstStackFinished, true); + done(); + }); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/lib/tests/2.2.5.js b/node_modules/promises-aplus-tests/lib/tests/2.2.5.js new file mode 100644 index 0000000..d966004 --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/2.2.5.js @@ -0,0 +1,47 @@ +/*jshint strict: false */ + +var assert = require("assert"); + +var adapter = global.adapter; +var resolved = adapter.resolved; +var rejected = adapter.rejected; + +var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it + +describe("2.2.5 `onFulfilled` and `onRejected` must be called as functions (i.e. with no `this` value).", function () { + describe("strict mode", function () { + specify("fulfilled", function (done) { + resolved(dummy).then(function onFulfilled() { + "use strict"; + + assert.strictEqual(this, undefined); + done(); + }); + }); + + specify("rejected", function (done) { + rejected(dummy).then(null, function onRejected() { + "use strict"; + + assert.strictEqual(this, undefined); + done(); + }); + }); + }); + + describe("sloppy mode", function () { + specify("fulfilled", function (done) { + resolved(dummy).then(function onFulfilled() { + assert.strictEqual(this, global); + done(); + }); + }); + + specify("rejected", function (done) { + rejected(dummy).then(null, function onRejected() { + assert.strictEqual(this, global); + done(); + }); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/lib/tests/2.2.6.js b/node_modules/promises-aplus-tests/lib/tests/2.2.6.js new file mode 100644 index 0000000..67ecf06 --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/2.2.6.js @@ -0,0 +1,257 @@ +"use strict"; + +var assert = require("assert"); +var sinon = require("sinon"); +var testFulfilled = require("./helpers/testThreeCases").testFulfilled; +var testRejected = require("./helpers/testThreeCases").testRejected; + +var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it +var other = { other: "other" }; // a value we don't want to be strict equal to +var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality +var sentinel2 = { sentinel2: "sentinel2" }; +var sentinel3 = { sentinel3: "sentinel3" }; + +function callbackAggregator(times, ultimateCallback) { + var soFar = 0; + return function () { + if (++soFar === times) { + ultimateCallback(); + } + }; +} + +describe("2.2.6: `then` may be called multiple times on the same promise.", function () { + describe("2.2.6.1: If/when `promise` is fulfilled, all respective `onFulfilled` callbacks must execute in the " + + "order of their originating calls to `then`.", function () { + describe("multiple boring fulfillment handlers", function () { + testFulfilled(sentinel, function (promise, done) { + var handler1 = sinon.stub().returns(other); + var handler2 = sinon.stub().returns(other); + var handler3 = sinon.stub().returns(other); + + var spy = sinon.spy(); + promise.then(handler1, spy); + promise.then(handler2, spy); + promise.then(handler3, spy); + + promise.then(function (value) { + assert.strictEqual(value, sentinel); + + sinon.assert.calledWith(handler1, sinon.match.same(sentinel)); + sinon.assert.calledWith(handler2, sinon.match.same(sentinel)); + sinon.assert.calledWith(handler3, sinon.match.same(sentinel)); + sinon.assert.notCalled(spy); + + done(); + }); + }); + }); + + describe("multiple fulfillment handlers, one of which throws", function () { + testFulfilled(sentinel, function (promise, done) { + var handler1 = sinon.stub().returns(other); + var handler2 = sinon.stub().throws(other); + var handler3 = sinon.stub().returns(other); + + var spy = sinon.spy(); + promise.then(handler1, spy); + promise.then(handler2, spy); + promise.then(handler3, spy); + + promise.then(function (value) { + assert.strictEqual(value, sentinel); + + sinon.assert.calledWith(handler1, sinon.match.same(sentinel)); + sinon.assert.calledWith(handler2, sinon.match.same(sentinel)); + sinon.assert.calledWith(handler3, sinon.match.same(sentinel)); + sinon.assert.notCalled(spy); + + done(); + }); + }); + }); + + describe("results in multiple branching chains with their own fulfillment values", function () { + testFulfilled(dummy, function (promise, done) { + var semiDone = callbackAggregator(3, done); + + promise.then(function () { + return sentinel; + }).then(function (value) { + assert.strictEqual(value, sentinel); + semiDone(); + }); + + promise.then(function () { + throw sentinel2; + }).then(null, function (reason) { + assert.strictEqual(reason, sentinel2); + semiDone(); + }); + + promise.then(function () { + return sentinel3; + }).then(function (value) { + assert.strictEqual(value, sentinel3); + semiDone(); + }); + }); + }); + + describe("`onFulfilled` handlers are called in the original order", function () { + testFulfilled(dummy, function (promise, done) { + var handler1 = sinon.spy(function handler1() {}); + var handler2 = sinon.spy(function handler2() {}); + var handler3 = sinon.spy(function handler3() {}); + + promise.then(handler1); + promise.then(handler2); + promise.then(handler3); + + promise.then(function () { + sinon.assert.callOrder(handler1, handler2, handler3); + done(); + }); + }); + + describe("even when one handler is added inside another handler", function () { + testFulfilled(dummy, function (promise, done) { + var handler1 = sinon.spy(function handler1() {}); + var handler2 = sinon.spy(function handler2() {}); + var handler3 = sinon.spy(function handler3() {}); + + promise.then(function () { + handler1(); + promise.then(handler3); + }); + promise.then(handler2); + + promise.then(function () { + // Give implementations a bit of extra time to flush their internal queue, if necessary. + setTimeout(function () { + sinon.assert.callOrder(handler1, handler2, handler3); + done(); + }, 15); + }); + }); + }); + }); + }); + + describe("2.2.6.2: If/when `promise` is rejected, all respective `onRejected` callbacks must execute in the " + + "order of their originating calls to `then`.", function () { + describe("multiple boring rejection handlers", function () { + testRejected(sentinel, function (promise, done) { + var handler1 = sinon.stub().returns(other); + var handler2 = sinon.stub().returns(other); + var handler3 = sinon.stub().returns(other); + + var spy = sinon.spy(); + promise.then(spy, handler1); + promise.then(spy, handler2); + promise.then(spy, handler3); + + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + + sinon.assert.calledWith(handler1, sinon.match.same(sentinel)); + sinon.assert.calledWith(handler2, sinon.match.same(sentinel)); + sinon.assert.calledWith(handler3, sinon.match.same(sentinel)); + sinon.assert.notCalled(spy); + + done(); + }); + }); + }); + + describe("multiple rejection handlers, one of which throws", function () { + testRejected(sentinel, function (promise, done) { + var handler1 = sinon.stub().returns(other); + var handler2 = sinon.stub().throws(other); + var handler3 = sinon.stub().returns(other); + + var spy = sinon.spy(); + promise.then(spy, handler1); + promise.then(spy, handler2); + promise.then(spy, handler3); + + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + + sinon.assert.calledWith(handler1, sinon.match.same(sentinel)); + sinon.assert.calledWith(handler2, sinon.match.same(sentinel)); + sinon.assert.calledWith(handler3, sinon.match.same(sentinel)); + sinon.assert.notCalled(spy); + + done(); + }); + }); + }); + + describe("results in multiple branching chains with their own fulfillment values", function () { + testRejected(sentinel, function (promise, done) { + var semiDone = callbackAggregator(3, done); + + promise.then(null, function () { + return sentinel; + }).then(function (value) { + assert.strictEqual(value, sentinel); + semiDone(); + }); + + promise.then(null, function () { + throw sentinel2; + }).then(null, function (reason) { + assert.strictEqual(reason, sentinel2); + semiDone(); + }); + + promise.then(null, function () { + return sentinel3; + }).then(function (value) { + assert.strictEqual(value, sentinel3); + semiDone(); + }); + }); + }); + + describe("`onRejected` handlers are called in the original order", function () { + testRejected(dummy, function (promise, done) { + var handler1 = sinon.spy(function handler1() {}); + var handler2 = sinon.spy(function handler2() {}); + var handler3 = sinon.spy(function handler3() {}); + + promise.then(null, handler1); + promise.then(null, handler2); + promise.then(null, handler3); + + promise.then(null, function () { + sinon.assert.callOrder(handler1, handler2, handler3); + done(); + }); + }); + + describe("even when one handler is added inside another handler", function () { + testRejected(dummy, function (promise, done) { + var handler1 = sinon.spy(function handler1() {}); + var handler2 = sinon.spy(function handler2() {}); + var handler3 = sinon.spy(function handler3() {}); + + promise.then(null, function () { + handler1(); + promise.then(null, handler3); + }); + promise.then(null, handler2); + + promise.then(null, function () { + // Give implementations a bit of extra time to flush their internal queue, if necessary. + setTimeout(function () { + sinon.assert.callOrder(handler1, handler2, handler3); + done(); + }, 15); + }); + }); + }); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/lib/tests/2.2.7.js b/node_modules/promises-aplus-tests/lib/tests/2.2.7.js new file mode 100644 index 0000000..7665089 --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/2.2.7.js @@ -0,0 +1,109 @@ +"use strict"; + +var assert = require("assert"); +var testFulfilled = require("./helpers/testThreeCases").testFulfilled; +var testRejected = require("./helpers/testThreeCases").testRejected; +var reasons = require("./helpers/reasons"); + +var adapter = global.adapter; +var deferred = adapter.deferred; + +var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it +var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality +var other = { other: "other" }; // a value we don't want to be strict equal to + +describe("2.2.7: `then` must return a promise: `promise2 = promise1.then(onFulfilled, onRejected)`", function () { + specify("is a promise", function () { + var promise1 = deferred().promise; + var promise2 = promise1.then(); + + assert(typeof promise2 === "object" || typeof promise2 === "function"); + assert.notStrictEqual(promise2, null); + assert.strictEqual(typeof promise2.then, "function"); + }); + + describe("2.2.7.1: If either `onFulfilled` or `onRejected` returns a value `x`, run the Promise Resolution " + + "Procedure `[[Resolve]](promise2, x)`", function () { + specify("see separate 3.3 tests", function () { }); + }); + + describe("2.2.7.2: If either `onFulfilled` or `onRejected` throws an exception `e`, `promise2` must be rejected " + + "with `e` as the reason.", function () { + function testReason(expectedReason, stringRepresentation) { + describe("The reason is " + stringRepresentation, function () { + testFulfilled(dummy, function (promise1, done) { + var promise2 = promise1.then(function onFulfilled() { + throw expectedReason; + }); + + promise2.then(null, function onPromise2Rejected(actualReason) { + assert.strictEqual(actualReason, expectedReason); + done(); + }); + }); + testRejected(dummy, function (promise1, done) { + var promise2 = promise1.then(null, function onRejected() { + throw expectedReason; + }); + + promise2.then(null, function onPromise2Rejected(actualReason) { + assert.strictEqual(actualReason, expectedReason); + done(); + }); + }); + }); + } + + Object.keys(reasons).forEach(function (stringRepresentation) { + testReason(reasons[stringRepresentation](), stringRepresentation); + }); + }); + + describe("2.2.7.3: If `onFulfilled` is not a function and `promise1` is fulfilled, `promise2` must be fulfilled " + + "with the same value.", function () { + + function testNonFunction(nonFunction, stringRepresentation) { + describe("`onFulfilled` is " + stringRepresentation, function () { + testFulfilled(sentinel, function (promise1, done) { + var promise2 = promise1.then(nonFunction); + + promise2.then(function onPromise2Fulfilled(value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + } + + testNonFunction(undefined, "`undefined`"); + testNonFunction(null, "`null`"); + testNonFunction(false, "`false`"); + testNonFunction(5, "`5`"); + testNonFunction({}, "an object"); + testNonFunction([function () { return other; }], "an array containing a function"); + }); + + describe("2.2.7.4: If `onRejected` is not a function and `promise1` is rejected, `promise2` must be rejected " + + "with the same reason.", function () { + + function testNonFunction(nonFunction, stringRepresentation) { + describe("`onRejected` is " + stringRepresentation, function () { + testRejected(sentinel, function (promise1, done) { + var promise2 = promise1.then(null, nonFunction); + + promise2.then(null, function onPromise2Rejected(reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + } + + testNonFunction(undefined, "`undefined`"); + testNonFunction(null, "`null`"); + testNonFunction(false, "`false`"); + testNonFunction(5, "`5`"); + testNonFunction({}, "an object"); + testNonFunction([function () { return other; }], "an array containing a function"); + }); +}); diff --git a/node_modules/promises-aplus-tests/lib/tests/2.3.1.js b/node_modules/promises-aplus-tests/lib/tests/2.3.1.js new file mode 100644 index 0000000..e70bc4c --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/2.3.1.js @@ -0,0 +1,34 @@ +"use strict"; + +var assert = require("assert"); + +var adapter = global.adapter; +var resolved = adapter.resolved; +var rejected = adapter.rejected; + +var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it + +describe("2.3.1: If `promise` and `x` refer to the same object, reject `promise` with a `TypeError' as the reason.", + function () { + specify("via return from a fulfilled promise", function (done) { + var promise = resolved(dummy).then(function () { + return promise; + }); + + promise.then(null, function (reason) { + assert(reason instanceof TypeError); + done(); + }); + }); + + specify("via return from a rejected promise", function (done) { + var promise = rejected(dummy).then(null, function () { + return promise; + }); + + promise.then(null, function (reason) { + assert(reason instanceof TypeError); + done(); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/lib/tests/2.3.2.js b/node_modules/promises-aplus-tests/lib/tests/2.3.2.js new file mode 100644 index 0000000..13d0889 --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/2.3.2.js @@ -0,0 +1,126 @@ +"use strict"; + +var assert = require("assert"); + +var adapter = global.adapter; +var resolved = adapter.resolved; +var rejected = adapter.rejected; +var deferred = adapter.deferred; + +var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it +var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality + +function testPromiseResolution(xFactory, test) { + specify("via return from a fulfilled promise", function (done) { + var promise = resolved(dummy).then(function onBasePromiseFulfilled() { + return xFactory(); + }); + + test(promise, done); + }); + + specify("via return from a rejected promise", function (done) { + var promise = rejected(dummy).then(null, function onBasePromiseRejected() { + return xFactory(); + }); + + test(promise, done); + }); +} + +describe("2.3.2: If `x` is a promise, adopt its state", function () { + describe("2.3.2.1: If `x` is pending, `promise` must remain pending until `x` is fulfilled or rejected.", + function () { + function xFactory() { + return deferred().promise; + } + + testPromiseResolution(xFactory, function (promise, done) { + var wasFulfilled = false; + var wasRejected = false; + + promise.then( + function onPromiseFulfilled() { + wasFulfilled = true; + }, + function onPromiseRejected() { + wasRejected = true; + } + ); + + setTimeout(function () { + assert.strictEqual(wasFulfilled, false); + assert.strictEqual(wasRejected, false); + done(); + }, 100); + }); + }); + + describe("2.3.2.2: If/when `x` is fulfilled, fulfill `promise` with the same value.", function () { + describe("`x` is already-fulfilled", function () { + function xFactory() { + return resolved(sentinel); + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function onPromiseFulfilled(value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + + describe("`x` is eventually-fulfilled", function () { + var d = null; + + function xFactory() { + d = deferred(); + setTimeout(function () { + d.resolve(sentinel); + }, 50); + return d.promise; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function onPromiseFulfilled(value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + }); + + describe("2.3.2.3: If/when `x` is rejected, reject `promise` with the same reason.", function () { + describe("`x` is already-rejected", function () { + function xFactory() { + return rejected(sentinel); + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function onPromiseRejected(reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("`x` is eventually-rejected", function () { + var d = null; + + function xFactory() { + d = deferred(); + setTimeout(function () { + d.reject(sentinel); + }, 50); + return d.promise; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function onPromiseRejected(reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/lib/tests/2.3.3.js b/node_modules/promises-aplus-tests/lib/tests/2.3.3.js new file mode 100644 index 0000000..f15de34 --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/2.3.3.js @@ -0,0 +1,969 @@ +"use strict"; + +var assert = require("assert"); +var thenables = require("./helpers/thenables"); +var reasons = require("./helpers/reasons"); + +var adapter = global.adapter; +var resolved = adapter.resolved; +var rejected = adapter.rejected; +var deferred = adapter.deferred; + +var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it +var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality +var other = { other: "other" }; // a value we don't want to be strict equal to +var sentinelArray = [sentinel]; // a sentinel fulfillment value to test when we need an array + +function testPromiseResolution(xFactory, test) { + specify("via return from a fulfilled promise", function (done) { + var promise = resolved(dummy).then(function onBasePromiseFulfilled() { + return xFactory(); + }); + + test(promise, done); + }); + + specify("via return from a rejected promise", function (done) { + var promise = rejected(dummy).then(null, function onBasePromiseRejected() { + return xFactory(); + }); + + test(promise, done); + }); +} + +function testCallingResolvePromise(yFactory, stringRepresentation, test) { + describe("`y` is " + stringRepresentation, function () { + describe("`then` calls `resolvePromise` synchronously", function () { + function xFactory() { + return { + then: function (resolvePromise) { + resolvePromise(yFactory()); + } + }; + } + + testPromiseResolution(xFactory, test); + }); + + describe("`then` calls `resolvePromise` asynchronously", function () { + function xFactory() { + return { + then: function (resolvePromise) { + setTimeout(function () { + resolvePromise(yFactory()); + }, 0); + } + }; + } + + testPromiseResolution(xFactory, test); + }); + }); +} + +function testCallingRejectPromise(r, stringRepresentation, test) { + describe("`r` is " + stringRepresentation, function () { + describe("`then` calls `rejectPromise` synchronously", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + rejectPromise(r); + } + }; + } + + testPromiseResolution(xFactory, test); + }); + + describe("`then` calls `rejectPromise` asynchronously", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + setTimeout(function () { + rejectPromise(r); + }, 0); + } + }; + } + + testPromiseResolution(xFactory, test); + }); + }); +} + +function testCallingResolvePromiseFulfillsWith(yFactory, stringRepresentation, fulfillmentValue) { + testCallingResolvePromise(yFactory, stringRepresentation, function (promise, done) { + promise.then(function onPromiseFulfilled(value) { + assert.strictEqual(value, fulfillmentValue); + done(); + }); + }); +} + +function testCallingResolvePromiseRejectsWith(yFactory, stringRepresentation, rejectionReason) { + testCallingResolvePromise(yFactory, stringRepresentation, function (promise, done) { + promise.then(null, function onPromiseRejected(reason) { + assert.strictEqual(reason, rejectionReason); + done(); + }); + }); +} + +function testCallingRejectPromiseRejectsWith(reason, stringRepresentation) { + testCallingRejectPromise(reason, stringRepresentation, function (promise, done) { + promise.then(null, function onPromiseRejected(rejectionReason) { + assert.strictEqual(rejectionReason, reason); + done(); + }); + }); +} + +describe("2.3.3: Otherwise, if `x` is an object or function,", function () { + describe("2.3.3.1: Let `then` be `x.then`", function () { + describe("`x` is an object with null prototype", function () { + var numberOfTimesThenWasRetrieved = null; + + beforeEach(function () { + numberOfTimesThenWasRetrieved = 0; + }); + + function xFactory() { + return Object.create(null, { + then: { + get: function () { + ++numberOfTimesThenWasRetrieved; + return function thenMethodForX(onFulfilled) { + onFulfilled(); + }; + } + } + }); + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function () { + assert.strictEqual(numberOfTimesThenWasRetrieved, 1); + done(); + }); + }); + }); + + describe("`x` is an object with normal Object.prototype", function () { + var numberOfTimesThenWasRetrieved = null; + + beforeEach(function () { + numberOfTimesThenWasRetrieved = 0; + }); + + function xFactory() { + return Object.create(Object.prototype, { + then: { + get: function () { + ++numberOfTimesThenWasRetrieved; + return function thenMethodForX(onFulfilled) { + onFulfilled(); + }; + } + } + }); + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function () { + assert.strictEqual(numberOfTimesThenWasRetrieved, 1); + done(); + }); + }); + }); + + describe("`x` is a function", function () { + var numberOfTimesThenWasRetrieved = null; + + beforeEach(function () { + numberOfTimesThenWasRetrieved = 0; + }); + + function xFactory() { + function x() { } + + Object.defineProperty(x, "then", { + get: function () { + ++numberOfTimesThenWasRetrieved; + return function thenMethodForX(onFulfilled) { + onFulfilled(); + }; + } + }); + + return x; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function () { + assert.strictEqual(numberOfTimesThenWasRetrieved, 1); + done(); + }); + }); + }); + }); + + describe("2.3.3.2: If retrieving the property `x.then` results in a thrown exception `e`, reject `promise` with " + + "`e` as the reason.", function () { + function testRejectionViaThrowingGetter(e, stringRepresentation) { + function xFactory() { + return Object.create(Object.prototype, { + then: { + get: function () { + throw e; + } + } + }); + } + + describe("`e` is " + stringRepresentation, function () { + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, e); + done(); + }); + }); + }); + } + + Object.keys(reasons).forEach(function (stringRepresentation) { + testRejectionViaThrowingGetter(reasons[stringRepresentation], stringRepresentation); + }); + }); + + describe("2.3.3.3: If `then` is a function, call it with `x` as `this`, first argument `resolvePromise`, and " + + "second argument `rejectPromise`", function () { + describe("Calls with `x` as `this` and two function arguments", function () { + function xFactory() { + var x = { + then: function (onFulfilled, onRejected) { + assert.strictEqual(this, x); + assert.strictEqual(typeof onFulfilled, "function"); + assert.strictEqual(typeof onRejected, "function"); + onFulfilled(); + } + }; + return x; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function () { + done(); + }); + }); + }); + + describe("Uses the original value of `then`", function () { + var numberOfTimesThenWasRetrieved = null; + + beforeEach(function () { + numberOfTimesThenWasRetrieved = 0; + }); + + function xFactory() { + return Object.create(Object.prototype, { + then: { + get: function () { + if (numberOfTimesThenWasRetrieved === 0) { + return function (onFulfilled) { + onFulfilled(); + }; + } + return null; + } + } + }); + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function () { + done(); + }); + }); + }); + + describe("2.3.3.3.1: If/when `resolvePromise` is called with value `y`, run `[[Resolve]](promise, y)`", + function () { + describe("`y` is not a thenable", function () { + testCallingResolvePromiseFulfillsWith(function () { return undefined; }, "`undefined`", undefined); + testCallingResolvePromiseFulfillsWith(function () { return null; }, "`null`", null); + testCallingResolvePromiseFulfillsWith(function () { return false; }, "`false`", false); + testCallingResolvePromiseFulfillsWith(function () { return 5; }, "`5`", 5); + testCallingResolvePromiseFulfillsWith(function () { return sentinel; }, "an object", sentinel); + testCallingResolvePromiseFulfillsWith(function () { return sentinelArray; }, "an array", sentinelArray); + }); + + describe("`y` is a thenable", function () { + Object.keys(thenables.fulfilled).forEach(function (stringRepresentation) { + function yFactory() { + return thenables.fulfilled[stringRepresentation](sentinel); + } + + testCallingResolvePromiseFulfillsWith(yFactory, stringRepresentation, sentinel); + }); + + Object.keys(thenables.rejected).forEach(function (stringRepresentation) { + function yFactory() { + return thenables.rejected[stringRepresentation](sentinel); + } + + testCallingResolvePromiseRejectsWith(yFactory, stringRepresentation, sentinel); + }); + }); + + describe("`y` is a thenable for a thenable", function () { + Object.keys(thenables.fulfilled).forEach(function (outerStringRepresentation) { + var outerThenableFactory = thenables.fulfilled[outerStringRepresentation]; + + Object.keys(thenables.fulfilled).forEach(function (innerStringRepresentation) { + var innerThenableFactory = thenables.fulfilled[innerStringRepresentation]; + + var stringRepresentation = outerStringRepresentation + " for " + innerStringRepresentation; + + function yFactory() { + return outerThenableFactory(innerThenableFactory(sentinel)); + } + + testCallingResolvePromiseFulfillsWith(yFactory, stringRepresentation, sentinel); + }); + + Object.keys(thenables.rejected).forEach(function (innerStringRepresentation) { + var innerThenableFactory = thenables.rejected[innerStringRepresentation]; + + var stringRepresentation = outerStringRepresentation + " for " + innerStringRepresentation; + + function yFactory() { + return outerThenableFactory(innerThenableFactory(sentinel)); + } + + testCallingResolvePromiseRejectsWith(yFactory, stringRepresentation, sentinel); + }); + }); + }); + }); + + describe("2.3.3.3.2: If/when `rejectPromise` is called with reason `r`, reject `promise` with `r`", + function () { + Object.keys(reasons).forEach(function (stringRepresentation) { + testCallingRejectPromiseRejectsWith(reasons[stringRepresentation](), stringRepresentation); + }); + }); + + describe("2.3.3.3.3: If both `resolvePromise` and `rejectPromise` are called, or multiple calls to the same " + + "argument are made, the first call takes precedence, and any further calls are ignored.", + function () { + describe("calling `resolvePromise` then `rejectPromise`, both synchronously", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + resolvePromise(sentinel); + rejectPromise(other); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function (value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + + describe("calling `resolvePromise` synchronously then `rejectPromise` asynchronously", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + resolvePromise(sentinel); + + setTimeout(function () { + rejectPromise(other); + }, 0); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function (value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + + describe("calling `resolvePromise` then `rejectPromise`, both asynchronously", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + setTimeout(function () { + resolvePromise(sentinel); + }, 0); + + setTimeout(function () { + rejectPromise(other); + }, 0); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function (value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + + describe("calling `resolvePromise` with an asynchronously-fulfilled promise, then calling " + + "`rejectPromise`, both synchronously", function () { + function xFactory() { + var d = deferred(); + setTimeout(function () { + d.resolve(sentinel); + }, 50); + + return { + then: function (resolvePromise, rejectPromise) { + resolvePromise(d.promise); + rejectPromise(other); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function (value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + + describe("calling `resolvePromise` with an asynchronously-rejected promise, then calling " + + "`rejectPromise`, both synchronously", function () { + function xFactory() { + var d = deferred(); + setTimeout(function () { + d.reject(sentinel); + }, 50); + + return { + then: function (resolvePromise, rejectPromise) { + resolvePromise(d.promise); + rejectPromise(other); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("calling `rejectPromise` then `resolvePromise`, both synchronously", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + rejectPromise(sentinel); + resolvePromise(other); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("calling `rejectPromise` synchronously then `resolvePromise` asynchronously", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + rejectPromise(sentinel); + + setTimeout(function () { + resolvePromise(other); + }, 0); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("calling `rejectPromise` then `resolvePromise`, both asynchronously", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + setTimeout(function () { + rejectPromise(sentinel); + }, 0); + + setTimeout(function () { + resolvePromise(other); + }, 0); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("calling `resolvePromise` twice synchronously", function () { + function xFactory() { + return { + then: function (resolvePromise) { + resolvePromise(sentinel); + resolvePromise(other); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function (value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + + describe("calling `resolvePromise` twice, first synchronously then asynchronously", function () { + function xFactory() { + return { + then: function (resolvePromise) { + resolvePromise(sentinel); + + setTimeout(function () { + resolvePromise(other); + }, 0); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function (value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + + describe("calling `resolvePromise` twice, both times asynchronously", function () { + function xFactory() { + return { + then: function (resolvePromise) { + setTimeout(function () { + resolvePromise(sentinel); + }, 0); + + setTimeout(function () { + resolvePromise(other); + }, 0); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function (value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + + describe("calling `resolvePromise` with an asynchronously-fulfilled promise, then calling it again, both " + + "times synchronously", function () { + function xFactory() { + var d = deferred(); + setTimeout(function () { + d.resolve(sentinel); + }, 50); + + return { + then: function (resolvePromise) { + resolvePromise(d.promise); + resolvePromise(other); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function (value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + + describe("calling `resolvePromise` with an asynchronously-rejected promise, then calling it again, both " + + "times synchronously", function () { + function xFactory() { + var d = deferred(); + setTimeout(function () { + d.reject(sentinel); + }, 50); + + return { + then: function (resolvePromise) { + resolvePromise(d.promise); + resolvePromise(other); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("calling `rejectPromise` twice synchronously", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + rejectPromise(sentinel); + rejectPromise(other); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("calling `rejectPromise` twice, first synchronously then asynchronously", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + rejectPromise(sentinel); + + setTimeout(function () { + rejectPromise(other); + }, 0); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("calling `rejectPromise` twice, both times asynchronously", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + setTimeout(function () { + rejectPromise(sentinel); + }, 0); + + setTimeout(function () { + rejectPromise(other); + }, 0); + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("saving and abusing `resolvePromise` and `rejectPromise`", function () { + var savedResolvePromise, savedRejectPromise; + + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + savedResolvePromise = resolvePromise; + savedRejectPromise = rejectPromise; + } + }; + } + + beforeEach(function () { + savedResolvePromise = null; + savedRejectPromise = null; + }); + + testPromiseResolution(xFactory, function (promise, done) { + var timesFulfilled = 0; + var timesRejected = 0; + + promise.then( + function () { + ++timesFulfilled; + }, + function () { + ++timesRejected; + } + ); + + if (savedResolvePromise && savedRejectPromise) { + savedResolvePromise(dummy); + savedResolvePromise(dummy); + savedRejectPromise(dummy); + savedRejectPromise(dummy); + } + + setTimeout(function () { + savedResolvePromise(dummy); + savedResolvePromise(dummy); + savedRejectPromise(dummy); + savedRejectPromise(dummy); + }, 50); + + setTimeout(function () { + assert.strictEqual(timesFulfilled, 1); + assert.strictEqual(timesRejected, 0); + done(); + }, 100); + }); + }); + }); + + describe("2.3.3.3.4: If calling `then` throws an exception `e`,", function () { + describe("2.3.3.3.4.1: If `resolvePromise` or `rejectPromise` have been called, ignore it.", function () { + describe("`resolvePromise` was called with a non-thenable", function () { + function xFactory() { + return { + then: function (resolvePromise) { + resolvePromise(sentinel); + throw other; + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function (value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + + describe("`resolvePromise` was called with an asynchronously-fulfilled promise", function () { + function xFactory() { + var d = deferred(); + setTimeout(function () { + d.resolve(sentinel); + }, 50); + + return { + then: function (resolvePromise) { + resolvePromise(d.promise); + throw other; + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function (value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + + describe("`resolvePromise` was called with an asynchronously-rejected promise", function () { + function xFactory() { + var d = deferred(); + setTimeout(function () { + d.reject(sentinel); + }, 50); + + return { + then: function (resolvePromise) { + resolvePromise(d.promise); + throw other; + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("`rejectPromise` was called", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + rejectPromise(sentinel); + throw other; + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("`resolvePromise` then `rejectPromise` were called", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + resolvePromise(sentinel); + rejectPromise(other); + throw other; + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function (value) { + assert.strictEqual(value, sentinel); + done(); + }); + }); + }); + + describe("`rejectPromise` then `resolvePromise` were called", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + rejectPromise(sentinel); + resolvePromise(other); + throw other; + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + }); + + describe("2.3.3.3.4.2: Otherwise, reject `promise` with `e` as the reason.", function () { + describe("straightforward case", function () { + function xFactory() { + return { + then: function () { + throw sentinel; + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("`resolvePromise` is called asynchronously before the `throw`", function () { + function xFactory() { + return { + then: function (resolvePromise) { + setTimeout(function () { + resolvePromise(other); + }, 0); + throw sentinel; + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + + describe("`rejectPromise` is called asynchronously before the `throw`", function () { + function xFactory() { + return { + then: function (resolvePromise, rejectPromise) { + setTimeout(function () { + rejectPromise(other); + }, 0); + throw sentinel; + } + }; + } + + testPromiseResolution(xFactory, function (promise, done) { + promise.then(null, function (reason) { + assert.strictEqual(reason, sentinel); + done(); + }); + }); + }); + }); + }); + }); + + describe("2.3.3.4: If `then` is not a function, fulfill promise with `x`", function () { + function testFulfillViaNonFunction(then, stringRepresentation) { + var x = null; + + beforeEach(function () { + x = { then: then }; + }); + + function xFactory() { + return x; + } + + describe("`then` is " + stringRepresentation, function () { + testPromiseResolution(xFactory, function (promise, done) { + promise.then(function (value) { + assert.strictEqual(value, x); + done(); + }); + }); + }); + } + + testFulfillViaNonFunction(5, "`5`"); + testFulfillViaNonFunction({}, "an object"); + testFulfillViaNonFunction([function () { }], "an array containing a function"); + testFulfillViaNonFunction(/a-b/i, "a regular expression"); + testFulfillViaNonFunction(Object.create(Function.prototype), "an object inheriting from `Function.prototype`"); + }); +}); diff --git a/node_modules/promises-aplus-tests/lib/tests/2.3.4.js b/node_modules/promises-aplus-tests/lib/tests/2.3.4.js new file mode 100644 index 0000000..e421eea --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/2.3.4.js @@ -0,0 +1,69 @@ +"use strict"; + +var assert = require("assert"); +var testFulfilled = require("./helpers/testThreeCases").testFulfilled; +var testRejected = require("./helpers/testThreeCases").testRejected; + +var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it + +describe("2.3.4: If `x` is not an object or function, fulfill `promise` with `x`", function () { + function testValue(expectedValue, stringRepresentation, beforeEachHook, afterEachHook) { + describe("The value is " + stringRepresentation, function () { + if (typeof beforeEachHook === "function") { + beforeEach(beforeEachHook); + } + if (typeof afterEachHook === "function") { + afterEach(afterEachHook); + } + + testFulfilled(dummy, function (promise1, done) { + var promise2 = promise1.then(function onFulfilled() { + return expectedValue; + }); + + promise2.then(function onPromise2Fulfilled(actualValue) { + assert.strictEqual(actualValue, expectedValue); + done(); + }); + }); + testRejected(dummy, function (promise1, done) { + var promise2 = promise1.then(null, function onRejected() { + return expectedValue; + }); + + promise2.then(function onPromise2Fulfilled(actualValue) { + assert.strictEqual(actualValue, expectedValue); + done(); + }); + }); + }); + } + + testValue(undefined, "`undefined`"); + testValue(null, "`null`"); + testValue(false, "`false`"); + testValue(true, "`true`"); + testValue(0, "`0`"); + + testValue( + true, + "`true` with `Boolean.prototype` modified to have a `then` method", + function () { + Boolean.prototype.then = function () {}; + }, + function () { + delete Boolean.prototype.then; + } + ); + + testValue( + 1, + "`1` with `Number.prototype` modified to have a `then` method", + function () { + Number.prototype.then = function () {}; + }, + function () { + delete Number.prototype.then; + } + ); +}); diff --git a/node_modules/promises-aplus-tests/lib/tests/helpers/reasons.js b/node_modules/promises-aplus-tests/lib/tests/helpers/reasons.js new file mode 100644 index 0000000..54d670e --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/helpers/reasons.js @@ -0,0 +1,56 @@ +"use strict"; + +// This module exports some valid rejection reason factories, keyed by human-readable versions of their names. + +var adapter = global.adapter; +var resolved = adapter.resolved; +var rejected = adapter.rejected; + +var dummy = { dummy: "dummy" }; + +exports["`undefined`"] = function () { + return undefined; +}; + +exports["`null`"] = function () { + return null; +}; + +exports["`false`"] = function () { + return false; +}; + +exports["`0`"] = function () { + return 0; +}; + +exports["an error"] = function () { + return new Error(); +}; + +exports["an error without a stack"] = function () { + var error = new Error(); + delete error.stack; + + return error; +}; + +exports["a date"] = function () { + return new Date(); +}; + +exports["an object"] = function () { + return {}; +}; + +exports["an always-pending thenable"] = function () { + return { then: function () { } }; +}; + +exports["a fulfilled promise"] = function () { + return resolved(dummy); +}; + +exports["a rejected promise"] = function () { + return rejected(dummy); +}; diff --git a/node_modules/promises-aplus-tests/lib/tests/helpers/testThreeCases.js b/node_modules/promises-aplus-tests/lib/tests/helpers/testThreeCases.js new file mode 100644 index 0000000..bb37668 --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/helpers/testThreeCases.js @@ -0,0 +1,46 @@ +"use strict"; + +var adapter = global.adapter; +var resolved = adapter.resolved; +var rejected = adapter.rejected; +var deferred = adapter.deferred; + +exports.testFulfilled = function (value, test) { + specify("already-fulfilled", function (done) { + test(resolved(value), done); + }); + + specify("immediately-fulfilled", function (done) { + var d = deferred(); + test(d.promise, done); + d.resolve(value); + }); + + specify("eventually-fulfilled", function (done) { + var d = deferred(); + test(d.promise, done); + setTimeout(function () { + d.resolve(value); + }, 50); + }); +}; + +exports.testRejected = function (reason, test) { + specify("already-rejected", function (done) { + test(rejected(reason), done); + }); + + specify("immediately-rejected", function (done) { + var d = deferred(); + test(d.promise, done); + d.reject(reason); + }); + + specify("eventually-rejected", function (done) { + var d = deferred(); + test(d.promise, done); + setTimeout(function () { + d.reject(reason); + }, 50); + }); +}; diff --git a/node_modules/promises-aplus-tests/lib/tests/helpers/thenables.js b/node_modules/promises-aplus-tests/lib/tests/helpers/thenables.js new file mode 100644 index 0000000..8f74d2e --- /dev/null +++ b/node_modules/promises-aplus-tests/lib/tests/helpers/thenables.js @@ -0,0 +1,142 @@ +"use strict"; + +var adapter = global.adapter; +var resolved = adapter.resolved; +var rejected = adapter.rejected; +var deferred = adapter.deferred; + +var other = { other: "other" }; // a value we don't want to be strict equal to + +exports.fulfilled = { + "a synchronously-fulfilled custom thenable": function (value) { + return { + then: function (onFulfilled) { + onFulfilled(value); + } + }; + }, + + "an asynchronously-fulfilled custom thenable": function (value) { + return { + then: function (onFulfilled) { + setTimeout(function () { + onFulfilled(value); + }, 0); + } + }; + }, + + "a synchronously-fulfilled one-time thenable": function (value) { + var numberOfTimesThenRetrieved = 0; + return Object.create(null, { + then: { + get: function () { + if (numberOfTimesThenRetrieved === 0) { + ++numberOfTimesThenRetrieved; + return function (onFulfilled) { + onFulfilled(value); + }; + } + return null; + } + } + }); + }, + + "a thenable that tries to fulfill twice": function (value) { + return { + then: function (onFulfilled) { + onFulfilled(value); + onFulfilled(other); + } + }; + }, + + "a thenable that fulfills but then throws": function (value) { + return { + then: function (onFulfilled) { + onFulfilled(value); + throw other; + } + }; + }, + + "an already-fulfilled promise": function (value) { + return resolved(value); + }, + + "an eventually-fulfilled promise": function (value) { + var d = deferred(); + setTimeout(function () { + d.resolve(value); + }, 50); + return d.promise; + } +}; + +exports.rejected = { + "a synchronously-rejected custom thenable": function (reason) { + return { + then: function (onFulfilled, onRejected) { + onRejected(reason); + } + }; + }, + + "an asynchronously-rejected custom thenable": function (reason) { + return { + then: function (onFulfilled, onRejected) { + setTimeout(function () { + onRejected(reason); + }, 0); + } + }; + }, + + "a synchronously-rejected one-time thenable": function (reason) { + var numberOfTimesThenRetrieved = 0; + return Object.create(null, { + then: { + get: function () { + if (numberOfTimesThenRetrieved === 0) { + ++numberOfTimesThenRetrieved; + return function (onFulfilled, onRejected) { + onRejected(reason); + }; + } + return null; + } + } + }); + }, + + "a thenable that immediately throws in `then`": function (reason) { + return { + then: function () { + throw reason; + } + }; + }, + + "an object with a throwing `then` accessor": function (reason) { + return Object.create(null, { + then: { + get: function () { + throw reason; + } + } + }); + }, + + "an already-rejected promise": function (reason) { + return rejected(reason); + }, + + "an eventually-rejected promise": function (reason) { + var d = deferred(); + setTimeout(function () { + d.reject(reason); + }, 50); + return d.promise; + } +}; diff --git a/node_modules/promises-aplus-tests/node_modules/.bin/_mocha b/node_modules/promises-aplus-tests/node_modules/.bin/_mocha new file mode 120000 index 0000000..f2a54ff --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/.bin/_mocha @@ -0,0 +1 @@ +../mocha/bin/_mocha \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/.bin/mocha b/node_modules/promises-aplus-tests/node_modules/.bin/mocha new file mode 120000 index 0000000..43c668d --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/.bin/mocha @@ -0,0 +1 @@ +../mocha/bin/mocha \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/LICENSE b/node_modules/promises-aplus-tests/node_modules/mocha/LICENSE new file mode 100644 index 0000000..1c5d7fa --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2011-2014 TJ Holowaychuk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/Readme.md b/node_modules/promises-aplus-tests/node_modules/mocha/Readme.md new file mode 100644 index 0000000..b84bb05 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/Readme.md @@ -0,0 +1,203 @@ + [![Build Status](https://secure.travis-ci.org/visionmedia/mocha.png)](http://travis-ci.org/visionmedia/mocha) + + [![Mocha test framework](http://f.cl.ly/items/3l1k0n2A1U3M1I1L210p/Screen%20Shot%202012-02-24%20at%202.21.43%20PM.png)](http://visionmedia.github.io/mocha) + + Mocha is a simple, flexible, fun JavaScript test framework for node.js and the browser. For more information view the [documentation](http://visionmedia.github.io/mocha). + +## Contributors + +``` + + project : mocha + repo age : 2 years, 11 months + active : 433 days + commits : 1424 + files : 143 + authors : + 588 TJ Holowaychuk 41.3% + 389 Tj Holowaychuk 27.3% + 98 Travis Jeffery 6.9% + 31 Guillermo Rauch 2.2% + 13 Attila Domokos 0.9% + 10 John Firebaugh 0.7% + 8 Jo Liss 0.6% + 7 Joshua Appelman 0.5% + 7 Nathan Rajlich 0.5% + 6 Brendan Nee 0.4% + 6 James Carr 0.4% + 6 Mike Pennisi 0.4% + 5 Raynos 0.4% + 5 Aaron Heckmann 0.4% + 5 Ryunosuke SATO 0.4% + 4 Forbes Lindesay 0.3% + 4 Domenic Denicola 0.3% + 4 Xavier Antoviaque 0.3% + 4 hokaccha 0.3% + 4 Jonathan Ong 0.3% + 4 Joshua Krall 0.3% + 3 Ben Lindsey 0.2% + 3 Benjie Gillam 0.2% + 3 Fredrik Enestad 0.2% + 3 Sindre Sorhus 0.2% + 3 Cory Thomas 0.2% + 3 Mathieu Desvé 0.2% + 3 Tyson Tate 0.2% + 3 Valentin Agachi 0.2% + 3 Wil Moore III 0.2% + 3 Jesse Dailey 0.2% + 3 Merrick Christensen 0.2% + 3 eiji.ienaga 0.2% + 3 fool2fish 0.2% + 3 Nathan Bowser 0.2% + 3 Paul Miller 0.2% + 2 FARKAS Máté 0.1% + 2 Shawn Krisman 0.1% + 2 Jacob Wejendorp 0.1% + 2 Jonas Westerlund 0.1% + 2 Paul Armstrong 0.1% + 2 Konstantin Käfer 0.1% + 2 Michael Riley 0.1% + 2 Michael Schoonmaker 0.1% + 2 Andreas Lind Petersen 0.1% + 2 domenic 0.1% + 2 Quang Van 0.1% + 2 fcrisci 0.1% + 2 Nathan Alderson 0.1% + 2 travis jeffery 0.1% + 2 Juzer Ali 0.1% + 2 Pete Hawkins 0.1% + 2 Justin DuJardin 0.1% + 2 David Henderson 0.1% + 2 jsdevel 0.1% + 2 Timo Tijhof 0.1% + 2 Brian Beck 0.1% + 2 Simon Gaeremynck 0.1% + 2 Ian Storm Taylor 0.1% + 2 Arian Stolwijk 0.1% + 2 Alexander Early 0.1% + 2 Ben Bradley 0.1% + 2 Glen Mailer 0.1% + 1 Maciej Małecki 0.1% + 1 Mal Graty 0.1% + 1 Marc Kuo 0.1% + 1 Matija Marohnić 0.1% + 1 Matt Robenolt 0.1% + 1 Matt Smith 0.1% + 1 Matthew Shanley 0.1% + 1 Mattias Tidlund 0.1% + 1 Michael Jackson 0.1% + 1 Nathan Black 0.1% + 1 Nick Fitzgerald 0.1% + 1 Noshir Patel 0.1% + 1 Panu Horsmalahti 0.1% + 1 Phil Sung 0.1% + 1 R56 0.1% + 1 Refael Ackermann 0.1% + 1 Richard Dingwall 0.1% + 1 Romain Prieto 0.1% + 1 Roman Neuhauser 0.1% + 1 Roman Shtylman 0.1% + 1 Russ Bradberry 0.1% + 1 Russell Munson 0.1% + 1 Rustem Mustafin 0.1% + 1 Salehen Shovon Rahman 0.1% + 1 Sasha Koss 0.1% + 1 Seiya Konno 0.1% + 1 Shaine Hatch 0.1% + 1 Simon Goumaz 0.1% + 1 Standa Opichal 0.1% + 1 Stephen Mathieson 0.1% + 1 Steve Mason 0.1% + 1 Tapiwa Kelvin 0.1% + 1 Teddy Zeenny 0.1% + 1 Tim Ehat 0.1% + 1 Vadim Nikitin 0.1% + 1 Victor Costan 0.1% + 1 Will Langstroth 0.1% + 1 Yanis Wang 0.1% + 1 Yuest Wang 0.1% + 1 Zsolt Takács 0.1% + 1 abrkn 0.1% + 1 airportyh 0.1% + 1 badunk 0.1% + 1 claudyus 0.1% + 1 fengmk2 0.1% + 1 gaye 0.1% + 1 grasGendarme 0.1% + 1 lakmeer 0.1% + 1 lodr 0.1% + 1 qiuzuhui 0.1% + 1 sebv 0.1% + 1 tgautier@yahoo.com 0.1% + 1 traleig1 0.1% + 1 vlad 0.1% + 1 yuitest 0.1% + 1 Adam Crabtree 0.1% + 1 Andreas Brekken 0.1% + 1 Andrew Nesbitt 0.1% + 1 Andrey Popp 0.1% + 1 Arnaud Brousseau 0.1% + 1 Atsuya Takagi 0.1% + 1 Austin Birch 0.1% + 1 Ben Noordhuis 0.1% + 1 Bjørge Næss 0.1% + 1 Brian Lalor 0.1% + 1 Brian M. Carlson 0.1% + 1 Brian Moore 0.1% + 1 Bryan Donovan 0.1% + 1 Casey Foster 0.1% + 1 ChrisWren 0.1% + 1 Christopher Hiller 0.1% + 1 Corey Butler 0.1% + 1 Daniel Stockman 0.1% + 1 Dave McKenna 0.1% + 1 Denis Bardadym 0.1% + 1 Devin Weaver 0.1% + 1 Di Wu 0.1% + 1 Dmitry Shirokov 0.1% + 1 Fedor Indutny 0.1% + 1 Florian Margaine 0.1% + 1 Frederico Silva 0.1% + 1 Fredrik Lindin 0.1% + 1 Gareth Aye 0.1% + 1 Gareth Murphy 0.1% + 1 Gavin Mogan 0.1% + 1 Giovanni Bassi 0.1% + 1 Glen Huang 0.1% + 1 Greg Perkins 0.1% + 1 Harish 0.1% + 1 Harry Brundage 0.1% + 1 Herman Junge 0.1% + 1 Ian Young 0.1% + 1 Ivan 0.1% + 1 JP Bochi 0.1% + 1 Jaakko Salonen 0.1% + 1 Jakub Nešetřil 0.1% + 1 James Bowes 0.1% + 1 James Lal 0.1% + 1 Jan Kopriva 0.1% + 1 Jason Barry 0.1% + 1 Javier Aranda 0.1% + 1 Jean Ponchon 0.1% + 1 Jeff Kunkle 0.1% + 1 Jeremy Martin 0.1% + 1 Jimmy Cuadra 0.1% + 1 John Doty 0.1% + 1 Jonathan Creamer 0.1% + 1 Jonathan Park 0.1% + 1 Jussi Virtanen 0.1% + 1 Katie Gengler 0.1% + 1 Kazuhito Hokamura 0.1% + 1 Kirill Korolyov 0.1% + 1 Koen Punt 0.1% + 1 Laszlo Bacsi 0.1% + 1 Liam Newman 0.1% + 1 Linus Unnebäck 0.1% + 1 László Bácsi 0.1% +``` + +## Links + + - [Google Group](http://groups.google.com/group/mochajs) + - [Wiki](https://github.com/visionmedia/mocha/wiki) + - Mocha [Extensions and reporters](https://github.com/visionmedia/mocha/wiki) diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/bin/_mocha b/node_modules/promises-aplus-tests/node_modules/mocha/bin/_mocha new file mode 100755 index 0000000..3032690 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/bin/_mocha @@ -0,0 +1,454 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander') + , sprintf = require('util').format + , path = require('path') + , fs = require('fs') + , glob = require('glob') + , resolve = path.resolve + , exists = fs.existsSync || path.existsSync + , Mocha = require('../') + , utils = Mocha.utils + , interfaces = Mocha.interfaces + , join = path.join + , basename = path.basename + , cwd = process.cwd() + , mocha = new Mocha; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Files. + */ + +var files = []; + +/** + * Globals. + */ + +var globals = []; + +/** + * Requires. + */ + +var requires = []; + +/** + * Images. + */ + +var images = { + fail: __dirname + '/../images/error.png' + , pass: __dirname + '/../images/ok.png' +}; + +// options + +program + .version(JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version) + .usage('[debug] [options] [files]') + .option('-A, --async-only', "force all tests to take a callback (async)") + .option('-c, --colors', 'force enabling of colors') + .option('-C, --no-colors', 'force disabling of colors') + .option('-G, --growl', 'enable growl notification support') + .option('-R, --reporter ', 'specify the reporter to use', 'spec') + .option('-S, --sort', "sort test files") + .option('-b, --bail', "bail after first test failure") + .option('-d, --debug', "enable node's debugger, synonym for node --debug") + .option('-g, --grep ', 'only run tests matching ') + .option('-gc', '--expose-gc', 'expose gc extension') + .option('-i, --invert', 'inverts --grep matches') + .option('-r, --require ', 'require the given module') + .option('-s, --slow ', '"slow" test threshold in milliseconds [75]') + .option('-t, --timeout ', 'set test-case timeout in milliseconds [2000]') + .option('-u, --ui ', 'specify user-interface (bdd|tdd|exports)', 'bdd') + .option('-w, --watch', 'watch files for changes') + .option('--check-leaks', 'check for global variable leaks') + .option('--compilers :,...', 'use the given module(s) to compile files', list, []) + .option('--debug-brk', "enable node's debugger breaking on the first line") + .option('--globals ', 'allow the given comma-delimited global [names]', list, []) + .option('--harmony', 'enable all harmony features (except typeof)') + .option('--harmony-collections', 'enable harmony collections (sets, maps, and weak maps)') + .option('--harmony-generators', 'enable harmony generators') + .option('--harmony-proxies', 'enable harmony proxies') + .option('--inline-diffs', 'display actual/expected differences inline within each string') + .option('--interfaces', 'display available interfaces') + .option('--no-deprecation', 'silence deprecation warnings') + .option('--no-exit', 'require a clean shutdown of the event loop: mocha will not call process.exit') + .option('--no-timeouts', 'disables timeouts, given implicitly with --debug') + .option('--opts ', 'specify opts path', 'test/mocha.opts') + .option('--prof', 'log statistical profiling information') + .option('--recursive', 'include sub directories') + .option('--reporters', 'display available reporters') + .option('--throw-deprecation', 'throw an exception anytime a deprecated function is used') + .option('--trace', 'trace function calls') + .option('--trace-deprecation', 'show stack traces on deprecations') + .option('--watch-extensions ,...', 'additional extensions to monitor with --watch', list, []) + +program.name = 'mocha'; + +// init command + +program + .command('init ') + .description('initialize a client-side mocha setup at ') + .action(function(path){ + var mkdir = require('mkdirp'); + mkdir.sync(path); + var css = fs.readFileSync(join(__dirname, '..', 'mocha.css')); + var js = fs.readFileSync(join(__dirname, '..', 'mocha.js')); + var tmpl = fs.readFileSync(join(__dirname, '..', 'lib/template.html')); + fs.writeFileSync(join(path, 'mocha.css'), css); + fs.writeFileSync(join(path, 'mocha.js'), js); + fs.writeFileSync(join(path, 'tests.js'), ''); + fs.writeFileSync(join(path, 'index.html'), tmpl); + process.exit(0); + }); + +// --globals + +program.on('globals', function(val){ + globals = globals.concat(list(val)); +}); + +// --reporters + +program.on('reporters', function(){ + console.log(); + console.log(' dot - dot matrix'); + console.log(' doc - html documentation'); + console.log(' spec - hierarchical spec list'); + console.log(' json - single json object'); + console.log(' progress - progress bar'); + console.log(' list - spec-style listing'); + console.log(' tap - test-anything-protocol'); + console.log(' landing - unicode landing strip'); + console.log(' xunit - xunit reporter'); + console.log(' html-cov - HTML test coverage'); + console.log(' json-cov - JSON test coverage'); + console.log(' min - minimal reporter (great with --watch)'); + console.log(' json-stream - newline delimited json events'); + console.log(' markdown - markdown documentation (github flavour)'); + console.log(' nyan - nyan cat!'); + console.log(); + process.exit(); +}); + +// --interfaces + +program.on('interfaces', function(){ + console.log(''); + console.log(' bdd'); + console.log(' tdd'); + console.log(' qunit'); + console.log(' exports'); + console.log(''); + process.exit(); +}); + +// -r, --require + +module.paths.push(cwd, join(cwd, 'node_modules')); + +program.on('require', function(mod){ + var abs = exists(mod) || exists(mod + '.js'); + if (abs) mod = resolve(mod); + requires.push(mod); +}); + +// --opts + +var optsPath = process.argv.indexOf('--opts') !== -1 + ? process.argv[process.argv.indexOf('--opts') + 1] + : 'test/mocha.opts'; + +try { + var opts = fs.readFileSync(optsPath, 'utf8') + .trim() + .split(/\s+/); + + process.argv = process.argv + .slice(0, 2) + .concat(opts.concat(process.argv.slice(2))); +} catch (err) { + // ignore +} + +// parse args + +program.parse(process.argv); + +// infinite stack traces + +Error.stackTraceLimit = Infinity; // TODO: config + +// reporter + +mocha.reporter(program.reporter); + +// interface + +mocha.ui(program.ui); + +// load reporter + +try { + Reporter = require('../lib/reporters/' + program.reporter); +} catch (err) { + try { + Reporter = require(program.reporter); + } catch (err) { + throw new Error('reporter "' + program.reporter + '" does not exist'); + } +} + +// --no-colors + +if (!program.colors) mocha.useColors(false); + +// --colors + +if (~process.argv.indexOf('--colors') || + ~process.argv.indexOf('-c')) { + mocha.useColors(true); +} + +// --inline-diffs + +if (program.inlineDiffs) mocha.useInlineDiffs(true); + +// --slow + +if (program.slow) mocha.suite.slow(program.slow); + +// --no-timeouts + +if (!program.timeouts) mocha.enableTimeouts(false); + +// --timeout + +if (program.timeout) mocha.suite.timeout(program.timeout); + +// --bail + +mocha.suite.bail(program.bail); + +// --grep + +if (program.grep) mocha.grep(new RegExp(program.grep)); + +// --invert + +if (program.invert) mocha.invert(); + +// --check-leaks + +if (program.checkLeaks) mocha.checkLeaks(); + +// --growl + +if (program.growl) mocha.growl(); + +// --async-only + +if (program.asyncOnly) mocha.asyncOnly(); + +// --globals + +mocha.globals(globals); + +// custom compiler support + +var extensions = ['js']; +program.compilers.forEach(function(c) { + var compiler = c.split(':') + , ext = compiler[0] + , mod = compiler[1]; + + if (mod[0] == '.') mod = join(process.cwd(), mod); + require(mod); + extensions.push(ext); +}); + +// requires + +requires.forEach(function(mod) { + require(mod); +}); + +//args + +var args = program.args; + +// default files to test/*.{js,coffee} + +if (!args.length) args.push('test'); + +args.forEach(function(arg){ + files = files.concat(utils.lookupFiles(arg, extensions, program.recursive)); +}); + +// resolve + +files = files.map(function(path){ + return resolve(path); +}); + +if (program.sort) { + files.sort(); +} + +// --watch + +var runner; +if (program.watch) { + console.log(); + hideCursor(); + process.on('SIGINT', function(){ + showCursor(); + console.log('\n'); + process.exit(); + }); + + + var watchFiles = utils.files(cwd, [ 'js', 'coffee', 'litcoffee', 'coffee.md' ].concat(program.watchExtensions)); + var runAgain = false; + + function loadAndRun() { + try { + mocha.files = files; + runAgain = false; + runner = mocha.run(function(){ + runner = null; + if (runAgain) { + rerun(); + } + }); + } catch(e) { + console.log(e.stack); + } + } + + function purge() { + watchFiles.forEach(function(file){ + delete require.cache[file]; + }); + } + + loadAndRun(); + + function rerun() { + purge(); + stop() + if (!program.grep) + mocha.grep(null); + mocha.suite = mocha.suite.clone(); + mocha.suite.ctx = new Mocha.Context; + mocha.ui(program.ui); + loadAndRun(); + } + + utils.watch(watchFiles, function(){ + runAgain = true; + if (runner) { + runner.abort(); + } else { + rerun(); + } + }); + + return; +} + +// load + +mocha.files = files; +runner = mocha.run(program.exit ? process.exit : exitLater); + +function exitLater(code) { + process.on('exit', function() { process.exit(code) }) +} + +process.on('SIGINT', function() { runner.abort(); }) + +// enable growl notifications + +function growl(runner, reporter) { + var notify = require('growl'); + + runner.on('end', function(){ + var stats = reporter.stats; + if (stats.failures) { + var msg = stats.failures + ' of ' + runner.total + ' tests failed'; + notify(msg, { name: 'mocha', title: 'Failed', image: images.fail }); + } else { + notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { + name: 'mocha' + , title: 'Passed' + , image: images.pass + }); + } + }); +} + +/** + * Parse list. + */ + +function list(str) { + return str.split(/ *, */); +} + +/** + * Hide the cursor. + */ + +function hideCursor(){ + process.stdout.write('\u001b[?25l'); +}; + +/** + * Show the cursor. + */ + +function showCursor(){ + process.stdout.write('\u001b[?25h'); +}; + +/** + * Stop play()ing. + */ + +function stop() { + process.stdout.write('\u001b[2K'); + clearInterval(play.timer); +} + +/** + * Play the given array of strings. + */ + +function play(arr, interval) { + var len = arr.length + , interval = interval || 100 + , i = 0; + + play.timer = setInterval(function(){ + var str = arr[i++ % len]; + process.stdout.write('\u001b[0G' + str); + }, interval); +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/bin/mocha b/node_modules/promises-aplus-tests/node_modules/mocha/bin/mocha new file mode 100755 index 0000000..b5dbc72 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/bin/mocha @@ -0,0 +1,56 @@ +#!/usr/bin/env node + +/** + * This tiny wrapper file checks for known node flags and appends them + * when found, before invoking the "real" _mocha(1) executable. + */ + +var spawn = require('child_process').spawn + , args = [ __dirname + '/_mocha' ]; + +process.argv.slice(2).forEach(function(arg){ + var flag = arg.split('=')[0]; + + switch (flag) { + case '-d': + args.unshift('--debug'); + args.push('--no-timeouts'); + break; + case 'debug': + case '--debug': + case '--debug-brk': + args.unshift(arg); + args.push('--no-timeouts'); + break; + case '-gc': + case '--expose-gc': + args.unshift('--expose-gc'); + break; + case '--gc-global': + case '--harmony': + case '--harmony-proxies': + case '--harmony-collections': + case '--harmony-generators': + case '--no-deprecation': + case '--prof': + case '--throw-deprecation': + case '--trace-deprecation': + args.unshift(arg); + break; + default: + if (0 == arg.indexOf('--trace')) args.unshift(arg); + else args.push(arg); + break; + } +}); + +var proc = spawn(process.argv[0], args, { customFds: [0,1,2] }); +proc.on('exit', function (code, signal) { + process.on('exit', function(){ + if (signal) { + process.kill(process.pid, signal); + } else { + process.exit(code); + } + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/images/error.png b/node_modules/promises-aplus-tests/node_modules/mocha/images/error.png new file mode 100644 index 0000000..a07a1ba Binary files /dev/null and b/node_modules/promises-aplus-tests/node_modules/mocha/images/error.png differ diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/images/ok.png b/node_modules/promises-aplus-tests/node_modules/mocha/images/ok.png new file mode 100644 index 0000000..b3623a5 Binary files /dev/null and b/node_modules/promises-aplus-tests/node_modules/mocha/images/ok.png differ diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/index.js b/node_modules/promises-aplus-tests/node_modules/mocha/index.js new file mode 100644 index 0000000..507566f --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/index.js @@ -0,0 +1,4 @@ + +module.exports = process.env.COV + ? require('./lib-cov/mocha') + : require('./lib/mocha'); \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/debug.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/debug.js new file mode 100644 index 0000000..03cf592 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/debug.js @@ -0,0 +1,5 @@ + +module.exports = function(type){ + return function(){ + } +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/diff.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/diff.js new file mode 100644 index 0000000..c084609 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/diff.js @@ -0,0 +1,369 @@ +/* See LICENSE file for terms of use */ + +/* + * Text diff implementation. + * + * This library supports the following APIS: + * JsDiff.diffChars: Character by character diff + * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace + * JsDiff.diffLines: Line based diff + * + * JsDiff.diffCss: Diff targeted at CSS content + * + * These methods are based on the implementation proposed in + * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 + */ +var JsDiff = (function() { + /*jshint maxparams: 5*/ + function clonePath(path) { + return { newPos: path.newPos, components: path.components.slice(0) }; + } + function removeEmpty(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + if (array[i]) { + ret.push(array[i]); + } + } + return ret; + } + function escapeHTML(s) { + var n = s; + n = n.replace(/&/g, '&'); + n = n.replace(//g, '>'); + n = n.replace(/"/g, '"'); + + return n; + } + + var Diff = function(ignoreWhitespace) { + this.ignoreWhitespace = ignoreWhitespace; + }; + Diff.prototype = { + diff: function(oldString, newString) { + // Handle the identity case (this is due to unrolling editLength == 0 + if (newString === oldString) { + return [{ value: newString }]; + } + if (!newString) { + return [{ value: oldString, removed: true }]; + } + if (!oldString) { + return [{ value: newString, added: true }]; + } + + newString = this.tokenize(newString); + oldString = this.tokenize(oldString); + + var newLen = newString.length, oldLen = oldString.length; + var maxEditLength = newLen + oldLen; + var bestPath = [{ newPos: -1, components: [] }]; + + // Seed editLength = 0 + var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); + if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) { + return bestPath[0].components; + } + + for (var editLength = 1; editLength <= maxEditLength; editLength++) { + for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) { + var basePath; + var addPath = bestPath[diagonalPath-1], + removePath = bestPath[diagonalPath+1]; + oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; + if (addPath) { + // No one else is going to attempt to use this value, clear it + bestPath[diagonalPath-1] = undefined; + } + + var canAdd = addPath && addPath.newPos+1 < newLen; + var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; + if (!canAdd && !canRemove) { + bestPath[diagonalPath] = undefined; + continue; + } + + // Select the diagonal that we want to branch from. We select the prior + // path whose position in the new string is the farthest from the origin + // and does not pass the bounds of the diff graph + if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { + basePath = clonePath(removePath); + this.pushComponent(basePath.components, oldString[oldPos], undefined, true); + } else { + basePath = clonePath(addPath); + basePath.newPos++; + this.pushComponent(basePath.components, newString[basePath.newPos], true, undefined); + } + + var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath); + + if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) { + return basePath.components; + } else { + bestPath[diagonalPath] = basePath; + } + } + } + }, + + pushComponent: function(components, value, added, removed) { + var last = components[components.length-1]; + if (last && last.added === added && last.removed === removed) { + // We need to clone here as the component clone operation is just + // as shallow array clone + components[components.length-1] = + {value: this.join(last.value, value), added: added, removed: removed }; + } else { + components.push({value: value, added: added, removed: removed }); + } + }, + extractCommon: function(basePath, newString, oldString, diagonalPath) { + var newLen = newString.length, + oldLen = oldString.length, + newPos = basePath.newPos, + oldPos = newPos - diagonalPath; + while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) { + newPos++; + oldPos++; + + this.pushComponent(basePath.components, newString[newPos], undefined, undefined); + } + basePath.newPos = newPos; + return oldPos; + }, + + equals: function(left, right) { + var reWhitespace = /\S/; + if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) { + return true; + } else { + return left === right; + } + }, + join: function(left, right) { + return left + right; + }, + tokenize: function(value) { + return value; + } + }; + + var CharDiff = new Diff(); + + var WordDiff = new Diff(true); + var WordWithSpaceDiff = new Diff(); + WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { + return removeEmpty(value.split(/(\s+|\b)/)); + }; + + var CssDiff = new Diff(true); + CssDiff.tokenize = function(value) { + return removeEmpty(value.split(/([{}:;,]|\s+)/)); + }; + + var LineDiff = new Diff(); + LineDiff.tokenize = function(value) { + var retLines = [], + lines = value.split(/^/m); + + for(var i = 0; i < lines.length; i++) { + var line = lines[i], + lastLine = lines[i - 1]; + + // Merge lines that may contain windows new lines + if (line == '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') { + retLines[retLines.length - 1] += '\n'; + } else if (line) { + retLines.push(line); + } + } + + return retLines; + }; + + return { + Diff: Diff, + + diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); }, + diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); }, + diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); }, + diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); }, + + diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); }, + + createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { + var ret = []; + + ret.push('Index: ' + fileName); + ret.push('==================================================================='); + ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); + ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); + + var diff = LineDiff.diff(oldStr, newStr); + if (!diff[diff.length-1].value) { + diff.pop(); // Remove trailing newline add + } + diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier + + function contextLines(lines) { + return lines.map(function(entry) { return ' ' + entry; }); + } + function eofNL(curRange, i, current) { + var last = diff[diff.length-2], + isLast = i === diff.length-2, + isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed); + + // Figure out if this is the last line for the given file and missing NL + if (!/\n$/.test(current.value) && (isLast || isLastOfType)) { + curRange.push('\\ No newline at end of file'); + } + } + + var oldRangeStart = 0, newRangeStart = 0, curRange = [], + oldLine = 1, newLine = 1; + for (var i = 0; i < diff.length; i++) { + var current = diff[i], + lines = current.lines || current.value.replace(/\n$/, '').split('\n'); + current.lines = lines; + + if (current.added || current.removed) { + if (!oldRangeStart) { + var prev = diff[i-1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + + if (prev) { + curRange = contextLines(prev.lines.slice(-4)); + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; + } + } + curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; })); + eofNL(curRange, i, current); + + if (current.added) { + newLine += lines.length; + } else { + oldLine += lines.length; + } + } else { + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= 8 && i < diff.length-2) { + // Overlapping + curRange.push.apply(curRange, contextLines(lines)); + } else { + // end the range and output + var contextSize = Math.min(lines.length, 4); + ret.push( + '@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize) + + ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize) + + ' @@'); + ret.push.apply(ret, curRange); + ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); + if (lines.length <= 4) { + eofNL(ret, i, current); + } + + oldRangeStart = 0; newRangeStart = 0; curRange = []; + } + } + oldLine += lines.length; + newLine += lines.length; + } + } + + return ret.join('\n') + '\n'; + }, + + applyPatch: function(oldStr, uniDiff) { + var diffstr = uniDiff.split('\n'); + var diff = []; + var remEOFNL = false, + addEOFNL = false; + + for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) { + if(diffstr[i][0] === '@') { + var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); + diff.unshift({ + start:meh[3], + oldlength:meh[2], + oldlines:[], + newlength:meh[4], + newlines:[] + }); + } else if(diffstr[i][0] === '+') { + diff[0].newlines.push(diffstr[i].substr(1)); + } else if(diffstr[i][0] === '-') { + diff[0].oldlines.push(diffstr[i].substr(1)); + } else if(diffstr[i][0] === ' ') { + diff[0].newlines.push(diffstr[i].substr(1)); + diff[0].oldlines.push(diffstr[i].substr(1)); + } else if(diffstr[i][0] === '\\') { + if (diffstr[i-1][0] === '+') { + remEOFNL = true; + } else if(diffstr[i-1][0] === '-') { + addEOFNL = true; + } + } + } + + var str = oldStr.split('\n'); + for (var i = diff.length - 1; i >= 0; i--) { + var d = diff[i]; + for (var j = 0; j < d.oldlength; j++) { + if(str[d.start-1+j] !== d.oldlines[j]) { + return false; + } + } + Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines)); + } + + if (remEOFNL) { + while (!str[str.length-1]) { + str.pop(); + } + } else if (addEOFNL) { + str.push(''); + } + return str.join('\n'); + }, + + convertChangesToXML: function(changes){ + var ret = []; + for ( var i = 0; i < changes.length; i++) { + var change = changes[i]; + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + + ret.push(escapeHTML(change.value)); + + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + } + return ret.join(''); + }, + + // See: http://code.google.com/p/google-diff-match-patch/wiki/API + convertChangesToDMP: function(changes){ + var ret = [], change; + for ( var i = 0; i < changes.length; i++) { + change = changes[i]; + ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]); + } + return ret; + } + }; +})(); + +if (typeof module !== 'undefined') { + module.exports = JsDiff; +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/escape-string-regexp.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/escape-string-regexp.js new file mode 100644 index 0000000..ac6572c --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/escape-string-regexp.js @@ -0,0 +1,11 @@ +'use strict'; + +var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; + +module.exports = function (str) { + if (typeof str !== 'string') { + throw new TypeError('Expected a string'); + } + + return str.replace(matchOperatorsRe, '\\$&'); +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/events.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/events.js new file mode 100644 index 0000000..cfbd072 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/events.js @@ -0,0 +1,178 @@ + +/** + * Module exports. + */ + +exports.EventEmitter = EventEmitter; + +/** + * Check if `obj` is an array. + */ + +function isArray(obj) { + return '[object Array]' == {}.toString.call(obj); +} + +/** + * Event emitter constructor. + * + * @api public + */ + +function EventEmitter(){}; + +/** + * Adds a listener. + * + * @api public + */ + +EventEmitter.prototype.on = function (name, fn) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = fn; + } else if (isArray(this.$events[name])) { + this.$events[name].push(fn); + } else { + this.$events[name] = [this.$events[name], fn]; + } + + return this; +}; + +EventEmitter.prototype.addListener = EventEmitter.prototype.on; + +/** + * Adds a volatile listener. + * + * @api public + */ + +EventEmitter.prototype.once = function (name, fn) { + var self = this; + + function on () { + self.removeListener(name, on); + fn.apply(this, arguments); + }; + + on.listener = fn; + this.on(name, on); + + return this; +}; + +/** + * Removes a listener. + * + * @api public + */ + +EventEmitter.prototype.removeListener = function (name, fn) { + if (this.$events && this.$events[name]) { + var list = this.$events[name]; + + if (isArray(list)) { + var pos = -1; + + for (var i = 0, l = list.length; i < l; i++) { + if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { + pos = i; + break; + } + } + + if (pos < 0) { + return this; + } + + list.splice(pos, 1); + + if (!list.length) { + delete this.$events[name]; + } + } else if (list === fn || (list.listener && list.listener === fn)) { + delete this.$events[name]; + } + } + + return this; +}; + +/** + * Removes all listeners for an event. + * + * @api public + */ + +EventEmitter.prototype.removeAllListeners = function (name) { + if (name === undefined) { + this.$events = {}; + return this; + } + + if (this.$events && this.$events[name]) { + this.$events[name] = null; + } + + return this; +}; + +/** + * Gets all listeners for a certain event. + * + * @api public + */ + +EventEmitter.prototype.listeners = function (name) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = []; + } + + if (!isArray(this.$events[name])) { + this.$events[name] = [this.$events[name]]; + } + + return this.$events[name]; +}; + +/** + * Emits an event. + * + * @api public + */ + +EventEmitter.prototype.emit = function (name) { + if (!this.$events) { + return false; + } + + var handler = this.$events[name]; + + if (!handler) { + return false; + } + + var args = [].slice.call(arguments, 1); + + if ('function' == typeof handler) { + handler.apply(this, args); + } else if (isArray(handler)) { + var listeners = handler.slice(); + + for (var i = 0, l = listeners.length; i < l; i++) { + listeners[i].apply(this, args); + } + } else { + return false; + } + + return true; +}; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/fs.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/fs.js new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/glob.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/glob.js new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/path.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/path.js new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/progress.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/progress.js new file mode 100644 index 0000000..90526f7 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/progress.js @@ -0,0 +1,125 @@ +/** + * Expose `Progress`. + */ + +module.exports = Progress; + +/** + * Initialize a new `Progress` indicator. + */ + +function Progress() { + this.percent = 0; + this.size(0); + this.fontSize(11); + this.font('helvetica, arial, sans-serif'); +} + +/** + * Set progress size to `n`. + * + * @param {Number} n + * @return {Progress} for chaining + * @api public + */ + +Progress.prototype.size = function(n){ + this._size = n; + return this; +}; + +/** + * Set text to `str`. + * + * @param {String} str + * @return {Progress} for chaining + * @api public + */ + +Progress.prototype.text = function(str){ + this._text = str; + return this; +}; + +/** + * Set font size to `n`. + * + * @param {Number} n + * @return {Progress} for chaining + * @api public + */ + +Progress.prototype.fontSize = function(n){ + this._fontSize = n; + return this; +}; + +/** + * Set font `family`. + * + * @param {String} family + * @return {Progress} for chaining + */ + +Progress.prototype.font = function(family){ + this._font = family; + return this; +}; + +/** + * Update percentage to `n`. + * + * @param {Number} n + * @return {Progress} for chaining + */ + +Progress.prototype.update = function(n){ + this.percent = n; + return this; +}; + +/** + * Draw on `ctx`. + * + * @param {CanvasRenderingContext2d} ctx + * @return {Progress} for chaining + */ + +Progress.prototype.draw = function(ctx){ + try { + var percent = Math.min(this.percent, 100) + , size = this._size + , half = size / 2 + , x = half + , y = half + , rad = half - 1 + , fontSize = this._fontSize; + + ctx.font = fontSize + 'px ' + this._font; + + var angle = Math.PI * 2 * (percent / 100); + ctx.clearRect(0, 0, size, size); + + // outer circle + ctx.strokeStyle = '#9f9f9f'; + ctx.beginPath(); + ctx.arc(x, y, rad, 0, angle, false); + ctx.stroke(); + + // inner circle + ctx.strokeStyle = '#eee'; + ctx.beginPath(); + ctx.arc(x, y, rad - 1, 0, angle, true); + ctx.stroke(); + + // text + var text = this._text || (percent | 0) + '%' + , w = ctx.measureText(text).width; + + ctx.fillText( + text + , x - w / 2 + 1 + , y + fontSize / 2 - 1); + } catch (ex) {} //don't fail if we can't render progress + return this; +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/tty.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/tty.js new file mode 100644 index 0000000..6f5f079 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/browser/tty.js @@ -0,0 +1,13 @@ + +exports.isatty = function(){ + return true; +}; + +exports.getWindowSize = function(){ + if ('innerHeight' in global) { + return [global.innerHeight, global.innerWidth]; + } else { + // In a Web Worker, the DOM Window is not available. + return [640, 480]; + } +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/context.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/context.js new file mode 100644 index 0000000..84440be --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/context.js @@ -0,0 +1,84 @@ + +/** + * Expose `Context`. + */ + +module.exports = Context; + +/** + * Initialize a new `Context`. + * + * @api private + */ + +function Context(){} + +/** + * Set or get the context `Runnable` to `runnable`. + * + * @param {Runnable} runnable + * @return {Context} + * @api private + */ + +Context.prototype.runnable = function(runnable){ + if (0 == arguments.length) return this._runnable; + this.test = this._runnable = runnable; + return this; +}; + +/** + * Set test timeout `ms`. + * + * @param {Number} ms + * @return {Context} self + * @api private + */ + +Context.prototype.timeout = function(ms){ + if (arguments.length === 0) return this.runnable().timeout(); + this.runnable().timeout(ms); + return this; +}; + +/** + * Set test timeout `enabled`. + * + * @param {Boolean} enabled + * @return {Context} self + * @api private + */ + +Context.prototype.enableTimeouts = function (enabled) { + this.runnable().enableTimeouts(enabled); + return this; +}; + + +/** + * Set test slowness threshold `ms`. + * + * @param {Number} ms + * @return {Context} self + * @api private + */ + +Context.prototype.slow = function(ms){ + this.runnable().slow(ms); + return this; +}; + +/** + * Inspect the context void of `._runnable`. + * + * @return {String} + * @api private + */ + +Context.prototype.inspect = function(){ + return JSON.stringify(this, function(key, val){ + if ('_runnable' == key) return; + if ('test' == key) return; + return val; + }, 2); +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/hook.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/hook.js new file mode 100644 index 0000000..814e7b6 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/hook.js @@ -0,0 +1,49 @@ + +/** + * Module dependencies. + */ + +var Runnable = require('./runnable'); + +/** + * Expose `Hook`. + */ + +module.exports = Hook; + +/** + * Initialize a new `Hook` with the given `title` and callback `fn`. + * + * @param {String} title + * @param {Function} fn + * @api private + */ + +function Hook(title, fn) { + Runnable.call(this, title, fn); + this.type = 'hook'; +} + +/** + * Inherit from `Runnable.prototype`. + */ + +Hook.prototype.__proto__ = Runnable.prototype; + +/** + * Get or set the test `err`. + * + * @param {Error} err + * @return {Error} + * @api public + */ + +Hook.prototype.error = function(err){ + if (0 == arguments.length) { + var err = this._error; + this._error = null; + return err; + } + + this._error = err; +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/bdd.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/bdd.js new file mode 100644 index 0000000..ac16d72 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/bdd.js @@ -0,0 +1,140 @@ + +/** + * Module dependencies. + */ + +var Suite = require('../suite') + , Test = require('../test') + , utils = require('../utils') + , escapeRe = require('escape-string-regexp'); + +/** + * BDD-style interface: + * + * describe('Array', function(){ + * describe('#indexOf()', function(){ + * it('should return -1 when not present', function(){ + * + * }); + * + * it('should return the index when present', function(){ + * + * }); + * }); + * }); + * + */ + +module.exports = function(suite){ + var suites = [suite]; + + suite.on('pre-require', function(context, file, mocha){ + + /** + * Execute before running tests. + */ + + context.before = function(name, fn){ + suites[0].beforeAll(name, fn); + }; + + /** + * Execute after running tests. + */ + + context.after = function(name, fn){ + suites[0].afterAll(name, fn); + }; + + /** + * Execute before each test case. + */ + + context.beforeEach = function(name, fn){ + suites[0].beforeEach(name, fn); + }; + + /** + * Execute after each test case. + */ + + context.afterEach = function(name, fn){ + suites[0].afterEach(name, fn); + }; + + /** + * Describe a "suite" with the given `title` + * and callback `fn` containing nested suites + * and/or tests. + */ + + context.describe = context.context = function(title, fn){ + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + return suite; + }; + + /** + * Pending describe. + */ + + context.xdescribe = + context.xcontext = + context.describe.skip = function(title, fn){ + var suite = Suite.create(suites[0], title); + suite.pending = true; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + }; + + /** + * Exclusive suite. + */ + + context.describe.only = function(title, fn){ + var suite = context.describe(title, fn); + mocha.grep(suite.fullTitle()); + return suite; + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.it = context.specify = function(title, fn){ + var suite = suites[0]; + if (suite.pending) fn = null; + var test = new Test(title, fn); + test.file = file; + suite.addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.it.only = function(title, fn){ + var test = context.it(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + return test; + }; + + /** + * Pending test case. + */ + + context.xit = + context.xspecify = + context.it.skip = function(title){ + context.it(title); + }; + }); +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/exports.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/exports.js new file mode 100644 index 0000000..cedb905 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/exports.js @@ -0,0 +1,62 @@ + +/** + * Module dependencies. + */ + +var Suite = require('../suite') + , Test = require('../test'); + +/** + * TDD-style interface: + * + * exports.Array = { + * '#indexOf()': { + * 'should return -1 when the value is not present': function(){ + * + * }, + * + * 'should return the correct index when the value is present': function(){ + * + * } + * } + * }; + * + */ + +module.exports = function(suite){ + var suites = [suite]; + + suite.on('require', visit); + + function visit(obj, file) { + var suite; + for (var key in obj) { + if ('function' == typeof obj[key]) { + var fn = obj[key]; + switch (key) { + case 'before': + suites[0].beforeAll(fn); + break; + case 'after': + suites[0].afterAll(fn); + break; + case 'beforeEach': + suites[0].beforeEach(fn); + break; + case 'afterEach': + suites[0].afterEach(fn); + break; + default: + var test = new Test(key, fn); + test.file = file; + suites[0].addTest(test); + } + } else { + suite = Suite.create(suites[0], key); + suites.unshift(suite); + visit(obj[key]); + suites.shift(); + } + } + } +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/index.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/index.js new file mode 100644 index 0000000..f7b2655 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/index.js @@ -0,0 +1,5 @@ + +exports.bdd = require('./bdd'); +exports.tdd = require('./tdd'); +exports.qunit = require('./qunit'); +exports.exports = require('./exports'); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/qunit.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/qunit.js new file mode 100644 index 0000000..0a22641 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/qunit.js @@ -0,0 +1,125 @@ + +/** + * Module dependencies. + */ + +var Suite = require('../suite') + , Test = require('../test') + , escapeRe = require('escape-string-regexp') + , utils = require('../utils'); + +/** + * QUnit-style interface: + * + * suite('Array'); + * + * test('#length', function(){ + * var arr = [1,2,3]; + * ok(arr.length == 3); + * }); + * + * test('#indexOf()', function(){ + * var arr = [1,2,3]; + * ok(arr.indexOf(1) == 0); + * ok(arr.indexOf(2) == 1); + * ok(arr.indexOf(3) == 2); + * }); + * + * suite('String'); + * + * test('#length', function(){ + * ok('foo'.length == 3); + * }); + * + */ + +module.exports = function(suite){ + var suites = [suite]; + + suite.on('pre-require', function(context, file, mocha){ + + /** + * Execute before running tests. + */ + + context.before = function(name, fn){ + suites[0].beforeAll(name, fn); + }; + + /** + * Execute after running tests. + */ + + context.after = function(name, fn){ + suites[0].afterAll(name, fn); + }; + + /** + * Execute before each test case. + */ + + context.beforeEach = function(name, fn){ + suites[0].beforeEach(name, fn); + }; + + /** + * Execute after each test case. + */ + + context.afterEach = function(name, fn){ + suites[0].afterEach(name, fn); + }; + + /** + * Describe a "suite" with the given `title`. + */ + + context.suite = function(title){ + if (suites.length > 1) suites.shift(); + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + return suite; + }; + + /** + * Exclusive test-case. + */ + + context.suite.only = function(title, fn){ + var suite = context.suite(title, fn); + mocha.grep(suite.fullTitle()); + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.test = function(title, fn){ + var test = new Test(title, fn); + test.file = file; + suites[0].addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn){ + var test = context.test(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + }; + + /** + * Pending test case. + */ + + context.test.skip = function(title){ + context.test(title); + }; + }); +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/tdd.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/tdd.js new file mode 100644 index 0000000..dc43e41 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/interfaces/tdd.js @@ -0,0 +1,141 @@ + +/** + * Module dependencies. + */ + +var Suite = require('../suite') + , Test = require('../test') + , escapeRe = require('escape-string-regexp') + , utils = require('../utils'); + +/** + * TDD-style interface: + * + * suite('Array', function(){ + * suite('#indexOf()', function(){ + * suiteSetup(function(){ + * + * }); + * + * test('should return -1 when not present', function(){ + * + * }); + * + * test('should return the index when present', function(){ + * + * }); + * + * suiteTeardown(function(){ + * + * }); + * }); + * }); + * + */ + +module.exports = function(suite){ + var suites = [suite]; + + suite.on('pre-require', function(context, file, mocha){ + + /** + * Execute before each test case. + */ + + context.setup = function(name, fn){ + suites[0].beforeEach(name, fn); + }; + + /** + * Execute after each test case. + */ + + context.teardown = function(name, fn){ + suites[0].afterEach(name, fn); + }; + + /** + * Execute before the suite. + */ + + context.suiteSetup = function(name, fn){ + suites[0].beforeAll(name, fn); + }; + + /** + * Execute after the suite. + */ + + context.suiteTeardown = function(name, fn){ + suites[0].afterAll(name, fn); + }; + + /** + * Describe a "suite" with the given `title` + * and callback `fn` containing nested suites + * and/or tests. + */ + + context.suite = function(title, fn){ + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + return suite; + }; + + /** + * Pending suite. + */ + context.suite.skip = function(title, fn) { + var suite = Suite.create(suites[0], title); + suite.pending = true; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + }; + + /** + * Exclusive test-case. + */ + + context.suite.only = function(title, fn){ + var suite = context.suite(title, fn); + mocha.grep(suite.fullTitle()); + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.test = function(title, fn){ + var suite = suites[0]; + if (suite.pending) fn = null; + var test = new Test(title, fn); + test.file = file; + suite.addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn){ + var test = context.test(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + }; + + /** + * Pending test case. + */ + + context.test.skip = function(title){ + context.test(title); + }; + }); +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/mocha.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/mocha.js new file mode 100644 index 0000000..73bdaf9 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/mocha.js @@ -0,0 +1,407 @@ +/*! + * mocha + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var path = require('path') + , escapeRe = require('escape-string-regexp') + , utils = require('./utils'); + +/** + * Expose `Mocha`. + */ + +exports = module.exports = Mocha; + +/** + * To require local UIs and reporters when running in node. + */ + +if (typeof process !== 'undefined' && typeof process.cwd === 'function') { + var join = path.join + , cwd = process.cwd(); + module.paths.push(cwd, join(cwd, 'node_modules')); +} + +/** + * Expose internals. + */ + +exports.utils = utils; +exports.interfaces = require('./interfaces'); +exports.reporters = require('./reporters'); +exports.Runnable = require('./runnable'); +exports.Context = require('./context'); +exports.Runner = require('./runner'); +exports.Suite = require('./suite'); +exports.Hook = require('./hook'); +exports.Test = require('./test'); + +/** + * Return image `name` path. + * + * @param {String} name + * @return {String} + * @api private + */ + +function image(name) { + return __dirname + '/../images/' + name + '.png'; +} + +/** + * Setup mocha with `options`. + * + * Options: + * + * - `ui` name "bdd", "tdd", "exports" etc + * - `reporter` reporter instance, defaults to `mocha.reporters.spec` + * - `globals` array of accepted globals + * - `timeout` timeout in milliseconds + * - `bail` bail on the first test failure + * - `slow` milliseconds to wait before considering a test slow + * - `ignoreLeaks` ignore global leaks + * - `grep` string or regexp to filter tests with + * + * @param {Object} options + * @api public + */ + +function Mocha(options) { + options = options || {}; + this.files = []; + this.options = options; + this.grep(options.grep); + this.suite = new exports.Suite('', new exports.Context); + this.ui(options.ui); + this.bail(options.bail); + this.reporter(options.reporter); + if (null != options.timeout) this.timeout(options.timeout); + this.useColors(options.useColors) + if (options.enableTimeouts !== null) this.enableTimeouts(options.enableTimeouts); + if (options.slow) this.slow(options.slow); + + this.suite.on('pre-require', function (context) { + exports.afterEach = context.afterEach || context.teardown; + exports.after = context.after || context.suiteTeardown; + exports.beforeEach = context.beforeEach || context.setup; + exports.before = context.before || context.suiteSetup; + exports.describe = context.describe || context.suite; + exports.it = context.it || context.test; + exports.setup = context.setup || context.beforeEach; + exports.suiteSetup = context.suiteSetup || context.before; + exports.suiteTeardown = context.suiteTeardown || context.after; + exports.suite = context.suite || context.describe; + exports.teardown = context.teardown || context.afterEach; + exports.test = context.test || context.it; + }); +} + +/** + * Enable or disable bailing on the first failure. + * + * @param {Boolean} [bail] + * @api public + */ + +Mocha.prototype.bail = function(bail){ + if (0 == arguments.length) bail = true; + this.suite.bail(bail); + return this; +}; + +/** + * Add test `file`. + * + * @param {String} file + * @api public + */ + +Mocha.prototype.addFile = function(file){ + this.files.push(file); + return this; +}; + +/** + * Set reporter to `reporter`, defaults to "spec". + * + * @param {String|Function} reporter name or constructor + * @api public + */ + +Mocha.prototype.reporter = function(reporter){ + if ('function' == typeof reporter) { + this._reporter = reporter; + } else { + reporter = reporter || 'spec'; + var _reporter; + try { _reporter = require('./reporters/' + reporter); } catch (err) {}; + if (!_reporter) try { _reporter = require(reporter); } catch (err) {}; + if (!_reporter && reporter === 'teamcity') + console.warn('The Teamcity reporter was moved to a package named ' + + 'mocha-teamcity-reporter ' + + '(https://npmjs.org/package/mocha-teamcity-reporter).'); + if (!_reporter) throw new Error('invalid reporter "' + reporter + '"'); + this._reporter = _reporter; + } + return this; +}; + +/** + * Set test UI `name`, defaults to "bdd". + * + * @param {String} bdd + * @api public + */ + +Mocha.prototype.ui = function(name){ + name = name || 'bdd'; + this._ui = exports.interfaces[name]; + if (!this._ui) try { this._ui = require(name); } catch (err) {}; + if (!this._ui) throw new Error('invalid interface "' + name + '"'); + this._ui = this._ui(this.suite); + return this; +}; + +/** + * Load registered files. + * + * @api private + */ + +Mocha.prototype.loadFiles = function(fn){ + var self = this; + var suite = this.suite; + var pending = this.files.length; + this.files.forEach(function(file){ + file = path.resolve(file); + suite.emit('pre-require', global, file, self); + suite.emit('require', require(file), file, self); + suite.emit('post-require', global, file, self); + --pending || (fn && fn()); + }); +}; + +/** + * Enable growl support. + * + * @api private + */ + +Mocha.prototype._growl = function(runner, reporter) { + var notify = require('growl'); + + runner.on('end', function(){ + var stats = reporter.stats; + if (stats.failures) { + var msg = stats.failures + ' of ' + runner.total + ' tests failed'; + notify(msg, { name: 'mocha', title: 'Failed', image: image('error') }); + } else { + notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { + name: 'mocha' + , title: 'Passed' + , image: image('ok') + }); + } + }); +}; + +/** + * Add regexp to grep, if `re` is a string it is escaped. + * + * @param {RegExp|String} re + * @return {Mocha} + * @api public + */ + +Mocha.prototype.grep = function(re){ + this.options.grep = 'string' == typeof re + ? new RegExp(escapeRe(re)) + : re; + return this; +}; + +/** + * Invert `.grep()` matches. + * + * @return {Mocha} + * @api public + */ + +Mocha.prototype.invert = function(){ + this.options.invert = true; + return this; +}; + +/** + * Ignore global leaks. + * + * @param {Boolean} ignore + * @return {Mocha} + * @api public + */ + +Mocha.prototype.ignoreLeaks = function(ignore){ + this.options.ignoreLeaks = !!ignore; + return this; +}; + +/** + * Enable global leak checking. + * + * @return {Mocha} + * @api public + */ + +Mocha.prototype.checkLeaks = function(){ + this.options.ignoreLeaks = false; + return this; +}; + +/** + * Enable growl support. + * + * @return {Mocha} + * @api public + */ + +Mocha.prototype.growl = function(){ + this.options.growl = true; + return this; +}; + +/** + * Ignore `globals` array or string. + * + * @param {Array|String} globals + * @return {Mocha} + * @api public + */ + +Mocha.prototype.globals = function(globals){ + this.options.globals = (this.options.globals || []).concat(globals); + return this; +}; + +/** + * Emit color output. + * + * @param {Boolean} colors + * @return {Mocha} + * @api public + */ + +Mocha.prototype.useColors = function(colors){ + this.options.useColors = arguments.length && colors != undefined + ? colors + : true; + return this; +}; + +/** + * Use inline diffs rather than +/-. + * + * @param {Boolean} inlineDiffs + * @return {Mocha} + * @api public + */ + +Mocha.prototype.useInlineDiffs = function(inlineDiffs) { + this.options.useInlineDiffs = arguments.length && inlineDiffs != undefined + ? inlineDiffs + : false; + return this; +}; + +/** + * Set the timeout in milliseconds. + * + * @param {Number} timeout + * @return {Mocha} + * @api public + */ + +Mocha.prototype.timeout = function(timeout){ + this.suite.timeout(timeout); + return this; +}; + +/** + * Set slowness threshold in milliseconds. + * + * @param {Number} slow + * @return {Mocha} + * @api public + */ + +Mocha.prototype.slow = function(slow){ + this.suite.slow(slow); + return this; +}; + +/** + * Enable timeouts. + * + * @param {Boolean} enabled + * @return {Mocha} + * @api public + */ + +Mocha.prototype.enableTimeouts = function(enabled) { + this.suite.enableTimeouts(arguments.length && enabled !== undefined + ? enabled + : true); + return this +}; + +/** + * Makes all tests async (accepting a callback) + * + * @return {Mocha} + * @api public + */ + +Mocha.prototype.asyncOnly = function(){ + this.options.asyncOnly = true; + return this; +}; + +/** + * Disable syntax highlighting (in browser). + * @returns {Mocha} + * @api public + */ +Mocha.prototype.noHighlighting = function() { + this.options.noHighlighting = true; + return this; +}; + +/** + * Run tests and invoke `fn()` when complete. + * + * @param {Function} fn + * @return {Runner} + * @api public + */ + +Mocha.prototype.run = function(fn){ + if (this.files.length) this.loadFiles(); + var suite = this.suite; + var options = this.options; + options.files = this.files; + var runner = new exports.Runner(suite); + var reporter = new this._reporter(runner, options); + runner.ignoreLeaks = false !== options.ignoreLeaks; + runner.asyncOnly = options.asyncOnly; + if (options.grep) runner.grep(options.grep, options.invert); + if (options.globals) runner.globals(options.globals); + if (options.growl) this._growl(runner, reporter); + exports.reporters.Base.useColors = options.useColors; + exports.reporters.Base.inlineDiffs = options.useInlineDiffs; + return runner.run(fn); +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/ms.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/ms.js new file mode 100644 index 0000000..ba451fa --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/ms.js @@ -0,0 +1,109 @@ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} options + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options){ + options = options || {}; + if ('string' == typeof val) return parse(val); + return options['long'] ? longFormat(val) : shortFormat(val); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str); + if (!match) return; + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 's': + return n * s; + case 'ms': + return n; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function shortFormat(ms) { + if (ms >= d) return Math.round(ms / d) + 'd'; + if (ms >= h) return Math.round(ms / h) + 'h'; + if (ms >= m) return Math.round(ms / m) + 'm'; + if (ms >= s) return Math.round(ms / s) + 's'; + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function longFormat(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) return; + if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; + return Math.ceil(ms / n) + ' ' + name + 's'; +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/base.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/base.js new file mode 100644 index 0000000..00c94de --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/base.js @@ -0,0 +1,459 @@ + +/** + * Module dependencies. + */ + +var tty = require('tty') + , diff = require('diff') + , ms = require('../ms') + , utils = require('../utils'); + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Check if both stdio streams are associated with a tty. + */ + +var isatty = tty.isatty(1) && tty.isatty(2); + +/** + * Expose `Base`. + */ + +exports = module.exports = Base; + +/** + * Enable coloring by default. + */ + +exports.useColors = isatty || (process.env.MOCHA_COLORS !== undefined); + +/** + * Inline diffs instead of +/- + */ + +exports.inlineDiffs = false; + +/** + * Default color map. + */ + +exports.colors = { + 'pass': 90 + , 'fail': 31 + , 'bright pass': 92 + , 'bright fail': 91 + , 'bright yellow': 93 + , 'pending': 36 + , 'suite': 0 + , 'error title': 0 + , 'error message': 31 + , 'error stack': 90 + , 'checkmark': 32 + , 'fast': 90 + , 'medium': 33 + , 'slow': 31 + , 'green': 32 + , 'light': 90 + , 'diff gutter': 90 + , 'diff added': 42 + , 'diff removed': 41 +}; + +/** + * Default symbol map. + */ + +exports.symbols = { + ok: '✓', + err: '✖', + dot: '․' +}; + +// With node.js on Windows: use symbols available in terminal default fonts +if ('win32' == process.platform) { + exports.symbols.ok = '\u221A'; + exports.symbols.err = '\u00D7'; + exports.symbols.dot = '.'; +} + +/** + * Color `str` with the given `type`, + * allowing colors to be disabled, + * as well as user-defined color + * schemes. + * + * @param {String} type + * @param {String} str + * @return {String} + * @api private + */ + +var color = exports.color = function(type, str) { + if (!exports.useColors) return str; + return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; +}; + +/** + * Expose term window size, with some + * defaults for when stderr is not a tty. + */ + +exports.window = { + width: isatty + ? process.stdout.getWindowSize + ? process.stdout.getWindowSize(1)[0] + : tty.getWindowSize()[1] + : 75 +}; + +/** + * Expose some basic cursor interactions + * that are common among reporters. + */ + +exports.cursor = { + hide: function(){ + isatty && process.stdout.write('\u001b[?25l'); + }, + + show: function(){ + isatty && process.stdout.write('\u001b[?25h'); + }, + + deleteLine: function(){ + isatty && process.stdout.write('\u001b[2K'); + }, + + beginningOfLine: function(){ + isatty && process.stdout.write('\u001b[0G'); + }, + + CR: function(){ + if (isatty) { + exports.cursor.deleteLine(); + exports.cursor.beginningOfLine(); + } else { + process.stdout.write('\r'); + } + } +}; + +/** + * Outut the given `failures` as a list. + * + * @param {Array} failures + * @api public + */ + +exports.list = function(failures){ + console.error(); + failures.forEach(function(test, i){ + // format + var fmt = color('error title', ' %s) %s:\n') + + color('error message', ' %s') + + color('error stack', '\n%s\n'); + + // msg + var err = test.err + , message = err.message || '' + , stack = err.stack || message + , index = stack.indexOf(message) + message.length + , msg = stack.slice(0, index) + , actual = err.actual + , expected = err.expected + , escape = true; + + // uncaught + if (err.uncaught) { + msg = 'Uncaught ' + msg; + } + + // explicitly show diff + if (err.showDiff && sameType(actual, expected)) { + escape = false; + err.actual = actual = utils.stringify(actual); + err.expected = expected = utils.stringify(expected); + } + + // actual / expected diff + if (err.showDiff && 'string' == typeof actual && 'string' == typeof expected) { + fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n'); + var match = message.match(/^([^:]+): expected/); + msg = '\n ' + color('error message', match ? match[1] : msg); + + if (exports.inlineDiffs) { + msg += inlineDiff(err, escape); + } else { + msg += unifiedDiff(err, escape); + } + } + + // indent stack trace without msg + stack = stack.slice(index ? index + 1 : index) + .replace(/^/gm, ' '); + + console.error(fmt, (i + 1), test.fullTitle(), msg, stack); + }); +}; + +/** + * Initialize a new `Base` reporter. + * + * All other reporters generally + * inherit from this reporter, providing + * stats such as test duration, number + * of tests passed / failed etc. + * + * @param {Runner} runner + * @api public + */ + +function Base(runner) { + var self = this + , stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 } + , failures = this.failures = []; + + if (!runner) return; + this.runner = runner; + + runner.stats = stats; + + runner.on('start', function(){ + stats.start = new Date; + }); + + runner.on('suite', function(suite){ + stats.suites = stats.suites || 0; + suite.root || stats.suites++; + }); + + runner.on('test end', function(test){ + stats.tests = stats.tests || 0; + stats.tests++; + }); + + runner.on('pass', function(test){ + stats.passes = stats.passes || 0; + + var medium = test.slow() / 2; + test.speed = test.duration > test.slow() + ? 'slow' + : test.duration > medium + ? 'medium' + : 'fast'; + + stats.passes++; + }); + + runner.on('fail', function(test, err){ + stats.failures = stats.failures || 0; + stats.failures++; + test.err = err; + failures.push(test); + }); + + runner.on('end', function(){ + stats.end = new Date; + stats.duration = new Date - stats.start; + }); + + runner.on('pending', function(){ + stats.pending++; + }); +} + +/** + * Output common epilogue used by many of + * the bundled reporters. + * + * @api public + */ + +Base.prototype.epilogue = function(){ + var stats = this.stats; + var tests; + var fmt; + + console.log(); + + // passes + fmt = color('bright pass', ' ') + + color('green', ' %d passing') + + color('light', ' (%s)'); + + console.log(fmt, + stats.passes || 0, + ms(stats.duration)); + + // pending + if (stats.pending) { + fmt = color('pending', ' ') + + color('pending', ' %d pending'); + + console.log(fmt, stats.pending); + } + + // failures + if (stats.failures) { + fmt = color('fail', ' %d failing'); + + console.error(fmt, + stats.failures); + + Base.list(this.failures); + console.error(); + } + + console.log(); +}; + +/** + * Pad the given `str` to `len`. + * + * @param {String} str + * @param {String} len + * @return {String} + * @api private + */ + +function pad(str, len) { + str = String(str); + return Array(len - str.length + 1).join(' ') + str; +} + + +/** + * Returns an inline diff between 2 strings with coloured ANSI output + * + * @param {Error} Error with actual/expected + * @return {String} Diff + * @api private + */ + +function inlineDiff(err, escape) { + var msg = errorDiff(err, 'WordsWithSpace', escape); + + // linenos + var lines = msg.split('\n'); + if (lines.length > 4) { + var width = String(lines.length).length; + msg = lines.map(function(str, i){ + return pad(++i, width) + ' |' + ' ' + str; + }).join('\n'); + } + + // legend + msg = '\n' + + color('diff removed', 'actual') + + ' ' + + color('diff added', 'expected') + + '\n\n' + + msg + + '\n'; + + // indent + msg = msg.replace(/^/gm, ' '); + return msg; +} + +/** + * Returns a unified diff between 2 strings + * + * @param {Error} Error with actual/expected + * @return {String} Diff + * @api private + */ + +function unifiedDiff(err, escape) { + var indent = ' '; + function cleanUp(line) { + if (escape) { + line = escapeInvisibles(line); + } + if (line[0] === '+') return indent + colorLines('diff added', line); + if (line[0] === '-') return indent + colorLines('diff removed', line); + if (line.match(/\@\@/)) return null; + if (line.match(/\\ No newline/)) return null; + else return indent + line; + } + function notBlank(line) { + return line != null; + } + msg = diff.createPatch('string', err.actual, err.expected); + var lines = msg.split('\n').splice(4); + return '\n ' + + colorLines('diff added', '+ expected') + ' ' + + colorLines('diff removed', '- actual') + + '\n\n' + + lines.map(cleanUp).filter(notBlank).join('\n'); +} + +/** + * Return a character diff for `err`. + * + * @param {Error} err + * @return {String} + * @api private + */ + +function errorDiff(err, type, escape) { + var actual = escape ? escapeInvisibles(err.actual) : err.actual; + var expected = escape ? escapeInvisibles(err.expected) : err.expected; + return diff['diff' + type](actual, expected).map(function(str){ + if (str.added) return colorLines('diff added', str.value); + if (str.removed) return colorLines('diff removed', str.value); + return str.value; + }).join(''); +} + +/** + * Returns a string with all invisible characters in plain text + * + * @param {String} line + * @return {String} + * @api private + */ +function escapeInvisibles(line) { + return line.replace(/\t/g, '') + .replace(/\r/g, '') + .replace(/\n/g, '\n'); +} + +/** + * Color lines for `str`, using the color `name`. + * + * @param {String} name + * @param {String} str + * @return {String} + * @api private + */ + +function colorLines(name, str) { + return str.split('\n').map(function(str){ + return color(name, str); + }).join('\n'); +} + +/** + * Check that a / b have the same type. + * + * @param {Object} a + * @param {Object} b + * @return {Boolean} + * @api private + */ + +function sameType(a, b) { + a = Object.prototype.toString.call(a); + b = Object.prototype.toString.call(b); + return a == b; +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/doc.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/doc.js new file mode 100644 index 0000000..eab72e5 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/doc.js @@ -0,0 +1,63 @@ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , utils = require('../utils'); + +/** + * Expose `Doc`. + */ + +exports = module.exports = Doc; + +/** + * Initialize a new `Doc` reporter. + * + * @param {Runner} runner + * @api public + */ + +function Doc(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , total = runner.total + , indents = 2; + + function indent() { + return Array(indents).join(' '); + } + + runner.on('suite', function(suite){ + if (suite.root) return; + ++indents; + console.log('%s
        ', indent()); + ++indents; + console.log('%s

        %s

        ', indent(), utils.escape(suite.title)); + console.log('%s
        ', indent()); + }); + + runner.on('suite end', function(suite){ + if (suite.root) return; + console.log('%s
        ', indent()); + --indents; + console.log('%s
        ', indent()); + --indents; + }); + + runner.on('pass', function(test){ + console.log('%s
        %s
        ', indent(), utils.escape(test.title)); + var code = utils.escape(utils.clean(test.fn.toString())); + console.log('%s
        %s
        ', indent(), code); + }); + + runner.on('fail', function(test, err){ + console.log('%s
        %s
        ', indent(), utils.escape(test.title)); + var code = utils.escape(utils.clean(test.fn.toString())); + console.log('%s
        %s
        ', indent(), code); + console.log('%s
        %s
        ', indent(), utils.escape(err)); + }); +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/dot.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/dot.js new file mode 100644 index 0000000..e200468 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/dot.js @@ -0,0 +1,63 @@ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , color = Base.color; + +/** + * Expose `Dot`. + */ + +exports = module.exports = Dot; + +/** + * Initialize a new `Dot` matrix test reporter. + * + * @param {Runner} runner + * @api public + */ + +function Dot(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , width = Base.window.width * .75 | 0 + , n = -1; + + runner.on('start', function(){ + process.stdout.write('\n '); + }); + + runner.on('pending', function(test){ + if (++n % width == 0) process.stdout.write('\n '); + process.stdout.write(color('pending', Base.symbols.dot)); + }); + + runner.on('pass', function(test){ + if (++n % width == 0) process.stdout.write('\n '); + if ('slow' == test.speed) { + process.stdout.write(color('bright yellow', Base.symbols.dot)); + } else { + process.stdout.write(color(test.speed, Base.symbols.dot)); + } + }); + + runner.on('fail', function(test, err){ + if (++n % width == 0) process.stdout.write('\n '); + process.stdout.write(color('fail', Base.symbols.dot)); + }); + + runner.on('end', function(){ + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ + +Dot.prototype.__proto__ = Base.prototype; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/html-cov.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/html-cov.js new file mode 100644 index 0000000..bfb27ff --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/html-cov.js @@ -0,0 +1,51 @@ + +/** + * Module dependencies. + */ + +var JSONCov = require('./json-cov') + , fs = require('fs'); + +/** + * Expose `HTMLCov`. + */ + +exports = module.exports = HTMLCov; + +/** + * Initialize a new `JsCoverage` reporter. + * + * @param {Runner} runner + * @api public + */ + +function HTMLCov(runner) { + var jade = require('jade') + , file = __dirname + '/templates/coverage.jade' + , str = fs.readFileSync(file, 'utf8') + , fn = jade.compile(str, { filename: file }) + , self = this; + + JSONCov.call(this, runner, false); + + runner.on('end', function(){ + process.stdout.write(fn({ + cov: self.cov + , coverageClass: coverageClass + })); + }); +} + +/** + * Return coverage class for `n`. + * + * @return {String} + * @api private + */ + +function coverageClass(n) { + if (n >= 75) return 'high'; + if (n >= 50) return 'medium'; + if (n >= 25) return 'low'; + return 'terrible'; +} \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/html.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/html.js new file mode 100644 index 0000000..8b44e3e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/html.js @@ -0,0 +1,273 @@ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , utils = require('../utils') + , Progress = require('../browser/progress') + , escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Expose `HTML`. + */ + +exports = module.exports = HTML; + +/** + * Stats template. + */ + +var statsTemplate = ''; + +/** + * Initialize a new `HTML` reporter. + * + * @param {Runner} runner + * @api public + */ + +function HTML(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , total = runner.total + , stat = fragment(statsTemplate) + , items = stat.getElementsByTagName('li') + , passes = items[1].getElementsByTagName('em')[0] + , passesLink = items[1].getElementsByTagName('a')[0] + , failures = items[2].getElementsByTagName('em')[0] + , failuresLink = items[2].getElementsByTagName('a')[0] + , duration = items[3].getElementsByTagName('em')[0] + , canvas = stat.getElementsByTagName('canvas')[0] + , report = fragment('
          ') + , stack = [report] + , progress + , ctx + , root = document.getElementById('mocha'); + + if (canvas.getContext) { + var ratio = window.devicePixelRatio || 1; + canvas.style.width = canvas.width; + canvas.style.height = canvas.height; + canvas.width *= ratio; + canvas.height *= ratio; + ctx = canvas.getContext('2d'); + ctx.scale(ratio, ratio); + progress = new Progress; + } + + if (!root) return error('#mocha div missing, add it to your document'); + + // pass toggle + on(passesLink, 'click', function(){ + unhide(); + var name = /pass/.test(report.className) ? '' : ' pass'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) hideSuitesWithout('test pass'); + }); + + // failure toggle + on(failuresLink, 'click', function(){ + unhide(); + var name = /fail/.test(report.className) ? '' : ' fail'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) hideSuitesWithout('test fail'); + }); + + root.appendChild(stat); + root.appendChild(report); + + if (progress) progress.size(40); + + runner.on('suite', function(suite){ + if (suite.root) return; + + // suite + var url = self.suiteURL(suite); + var el = fragment('
        • %s

        • ', url, escape(suite.title)); + + // container + stack[0].appendChild(el); + stack.unshift(document.createElement('ul')); + el.appendChild(stack[0]); + }); + + runner.on('suite end', function(suite){ + if (suite.root) return; + stack.shift(); + }); + + runner.on('fail', function(test, err){ + if ('hook' == test.type) runner.emit('test end', test); + }); + + runner.on('test end', function(test){ + // TODO: add to stats + var percent = stats.tests / this.total * 100 | 0; + if (progress) progress.update(percent).draw(ctx); + + // update stats + var ms = new Date - stats.start; + text(passes, stats.passes); + text(failures, stats.failures); + text(duration, (ms / 1000).toFixed(2)); + + // test + if ('passed' == test.state) { + var url = self.testURL(test); + var el = fragment('
        • %e%ems

        • ', test.speed, test.title, test.duration, url); + } else if (test.pending) { + var el = fragment('
        • %e

        • ', test.title); + } else { + var el = fragment('
        • %e

        • ', test.title, encodeURIComponent(test.fullTitle())); + var str = test.err.stack || test.err.toString(); + + // FF / Opera do not add the message + if (!~str.indexOf(test.err.message)) { + str = test.err.message + '\n' + str; + } + + // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we + // check for the result of the stringifying. + if ('[object Error]' == str) str = test.err.message; + + // Safari doesn't give you a stack. Let's at least provide a source line. + if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) { + str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")"; + } + + el.appendChild(fragment('
          %e
          ', str)); + } + + // toggle code + // TODO: defer + if (!test.pending) { + var h2 = el.getElementsByTagName('h2')[0]; + + on(h2, 'click', function(){ + pre.style.display = 'none' == pre.style.display + ? 'block' + : 'none'; + }); + + var pre = fragment('
          %e
          ', utils.clean(test.fn.toString())); + el.appendChild(pre); + pre.style.display = 'none'; + } + + // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. + if (stack[0]) stack[0].appendChild(el); + }); +} + +/** + * Provide suite URL + * + * @param {Object} [suite] + */ + +HTML.prototype.suiteURL = function(suite){ + return '?grep=' + encodeURIComponent(suite.fullTitle()); +}; + +/** + * Provide test URL + * + * @param {Object} [test] + */ + +HTML.prototype.testURL = function(test){ + return '?grep=' + encodeURIComponent(test.fullTitle()); +}; + +/** + * Display error `msg`. + */ + +function error(msg) { + document.body.appendChild(fragment('
          %s
          ', msg)); +} + +/** + * Return a DOM fragment from `html`. + */ + +function fragment(html) { + var args = arguments + , div = document.createElement('div') + , i = 1; + + div.innerHTML = html.replace(/%([se])/g, function(_, type){ + switch (type) { + case 's': return String(args[i++]); + case 'e': return escape(args[i++]); + } + }); + + return div.firstChild; +} + +/** + * Check for suites that do not have elements + * with `classname`, and hide them. + */ + +function hideSuitesWithout(classname) { + var suites = document.getElementsByClassName('suite'); + for (var i = 0; i < suites.length; i++) { + var els = suites[i].getElementsByClassName(classname); + if (0 == els.length) suites[i].className += ' hidden'; + } +} + +/** + * Unhide .hidden suites. + */ + +function unhide() { + var els = document.getElementsByClassName('suite hidden'); + for (var i = 0; i < els.length; ++i) { + els[i].className = els[i].className.replace('suite hidden', 'suite'); + } +} + +/** + * Set `el` text to `str`. + */ + +function text(el, str) { + if (el.textContent) { + el.textContent = str; + } else { + el.innerText = str; + } +} + +/** + * Listen on `event` with callback `fn`. + */ + +function on(el, event, fn) { + if (el.addEventListener) { + el.addEventListener(event, fn, false); + } else { + el.attachEvent('on' + event, fn); + } +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/index.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/index.js new file mode 100644 index 0000000..1c4fccf --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/index.js @@ -0,0 +1,18 @@ + +exports.Base = require('./base'); +exports.Dot = require('./dot'); +exports.Doc = require('./doc'); +exports.TAP = require('./tap'); +exports.JSON = require('./json'); +exports.HTML = require('./html'); +exports.List = require('./list'); +exports.Min = require('./min'); +exports.Spec = require('./spec'); +exports.Nyan = require('./nyan'); +exports.XUnit = require('./xunit'); +exports.Markdown = require('./markdown'); +exports.Progress = require('./progress'); +exports.Landing = require('./landing'); +exports.JSONCov = require('./json-cov'); +exports.HTMLCov = require('./html-cov'); +exports.JSONStream = require('./json-stream'); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/json-cov.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/json-cov.js new file mode 100644 index 0000000..83e57f4 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/json-cov.js @@ -0,0 +1,153 @@ + +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `JSONCov`. + */ + +exports = module.exports = JSONCov; + +/** + * Initialize a new `JsCoverage` reporter. + * + * @param {Runner} runner + * @param {Boolean} output + * @api public + */ + +function JSONCov(runner, output) { + var self = this + , output = 1 == arguments.length ? true : output; + + Base.call(this, runner); + + var tests = [] + , failures = [] + , passes = []; + + runner.on('test end', function(test){ + tests.push(test); + }); + + runner.on('pass', function(test){ + passes.push(test); + }); + + runner.on('fail', function(test){ + failures.push(test); + }); + + runner.on('end', function(){ + var cov = global._$jscoverage || {}; + var result = self.cov = map(cov); + result.stats = self.stats; + result.tests = tests.map(clean); + result.failures = failures.map(clean); + result.passes = passes.map(clean); + if (!output) return; + process.stdout.write(JSON.stringify(result, null, 2 )); + }); +} + +/** + * Map jscoverage data to a JSON structure + * suitable for reporting. + * + * @param {Object} cov + * @return {Object} + * @api private + */ + +function map(cov) { + var ret = { + instrumentation: 'node-jscoverage' + , sloc: 0 + , hits: 0 + , misses: 0 + , coverage: 0 + , files: [] + }; + + for (var filename in cov) { + var data = coverage(filename, cov[filename]); + ret.files.push(data); + ret.hits += data.hits; + ret.misses += data.misses; + ret.sloc += data.sloc; + } + + ret.files.sort(function(a, b) { + return a.filename.localeCompare(b.filename); + }); + + if (ret.sloc > 0) { + ret.coverage = (ret.hits / ret.sloc) * 100; + } + + return ret; +} + +/** + * Map jscoverage data for a single source file + * to a JSON structure suitable for reporting. + * + * @param {String} filename name of the source file + * @param {Object} data jscoverage coverage data + * @return {Object} + * @api private + */ + +function coverage(filename, data) { + var ret = { + filename: filename, + coverage: 0, + hits: 0, + misses: 0, + sloc: 0, + source: {} + }; + + data.source.forEach(function(line, num){ + num++; + + if (data[num] === 0) { + ret.misses++; + ret.sloc++; + } else if (data[num] !== undefined) { + ret.hits++; + ret.sloc++; + } + + ret.source[num] = { + source: line + , coverage: data[num] === undefined + ? '' + : data[num] + }; + }); + + ret.coverage = ret.hits / ret.sloc * 100; + + return ret; +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @param {Object} test + * @return {Object} + * @api private + */ + +function clean(test) { + return { + title: test.title + , fullTitle: test.fullTitle() + , duration: test.duration + } +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/json-stream.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/json-stream.js new file mode 100644 index 0000000..7cb8fbe --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/json-stream.js @@ -0,0 +1,61 @@ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , color = Base.color; + +/** + * Expose `List`. + */ + +exports = module.exports = List; + +/** + * Initialize a new `List` test reporter. + * + * @param {Runner} runner + * @api public + */ + +function List(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , total = runner.total; + + runner.on('start', function(){ + console.log(JSON.stringify(['start', { total: total }])); + }); + + runner.on('pass', function(test){ + console.log(JSON.stringify(['pass', clean(test)])); + }); + + runner.on('fail', function(test, err){ + console.log(JSON.stringify(['fail', clean(test)])); + }); + + runner.on('end', function(){ + process.stdout.write(JSON.stringify(['end', self.stats])); + }); +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @param {Object} test + * @return {Object} + * @api private + */ + +function clean(test) { + return { + title: test.title + , fullTitle: test.fullTitle() + , duration: test.duration + } +} \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/json.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/json.js new file mode 100644 index 0000000..4ec9e12 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/json.js @@ -0,0 +1,93 @@ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `JSON`. + */ + +exports = module.exports = JSONReporter; + +/** + * Initialize a new `JSON` reporter. + * + * @param {Runner} runner + * @api public + */ + +function JSONReporter(runner) { + var self = this; + Base.call(this, runner); + + var tests = [] + , pending = [] + , failures = [] + , passes = []; + + runner.on('test end', function(test){ + tests.push(test); + }); + + runner.on('pass', function(test){ + passes.push(test); + }); + + runner.on('fail', function(test){ + failures.push(test); + }); + + runner.on('pending', function(test){ + pending.push(test); + }); + + runner.on('end', function(){ + var obj = { + stats: self.stats, + tests: tests.map(clean), + pending: pending.map(clean), + failures: failures.map(clean), + passes: passes.map(clean) + }; + + runner.testResults = obj; + + process.stdout.write(JSON.stringify(obj, null, 2)); + }); +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @param {Object} test + * @return {Object} + * @api private + */ + +function clean(test) { + return { + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration, + err: errorJSON(test.err || {}) + } +} + +/** + * Transform `error` into a JSON object. + * @param {Error} err + * @return {Object} + */ + +function errorJSON(err) { + var res = {}; + Object.getOwnPropertyNames(err).forEach(function(key) { + res[key] = err[key]; + }, err); + return res; +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/landing.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/landing.js new file mode 100644 index 0000000..bf064f6 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/landing.js @@ -0,0 +1,97 @@ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `Landing`. + */ + +exports = module.exports = Landing; + +/** + * Airplane color. + */ + +Base.colors.plane = 0; + +/** + * Airplane crash color. + */ + +Base.colors['plane crash'] = 31; + +/** + * Runway color. + */ + +Base.colors.runway = 90; + +/** + * Initialize a new `Landing` reporter. + * + * @param {Runner} runner + * @api public + */ + +function Landing(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , width = Base.window.width * .75 | 0 + , total = runner.total + , stream = process.stdout + , plane = color('plane', '✈') + , crashed = -1 + , n = 0; + + function runway() { + var buf = Array(width).join('-'); + return ' ' + color('runway', buf); + } + + runner.on('start', function(){ + stream.write('\n '); + cursor.hide(); + }); + + runner.on('test end', function(test){ + // check if the plane crashed + var col = -1 == crashed + ? width * ++n / total | 0 + : crashed; + + // show the crash + if ('failed' == test.state) { + plane = color('plane crash', '✈'); + crashed = col; + } + + // render landing strip + stream.write('\u001b[4F\n\n'); + stream.write(runway()); + stream.write('\n '); + stream.write(color('runway', Array(col).join('⋅'))); + stream.write(plane) + stream.write(color('runway', Array(width - col).join('⋅') + '\n')); + stream.write(runway()); + stream.write('\u001b[0m'); + }); + + runner.on('end', function(){ + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ + +Landing.prototype.__proto__ = Base.prototype; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/list.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/list.js new file mode 100644 index 0000000..3328e15 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/list.js @@ -0,0 +1,64 @@ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `List`. + */ + +exports = module.exports = List; + +/** + * Initialize a new `List` test reporter. + * + * @param {Runner} runner + * @api public + */ + +function List(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , n = 0; + + runner.on('start', function(){ + console.log(); + }); + + runner.on('test', function(test){ + process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); + }); + + runner.on('pending', function(test){ + var fmt = color('checkmark', ' -') + + color('pending', ' %s'); + console.log(fmt, test.fullTitle()); + }); + + runner.on('pass', function(test){ + var fmt = color('checkmark', ' '+Base.symbols.dot) + + color('pass', ' %s: ') + + color(test.speed, '%dms'); + cursor.CR(); + console.log(fmt, test.fullTitle(), test.duration); + }); + + runner.on('fail', function(test, err){ + cursor.CR(); + console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); + }); + + runner.on('end', self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ + +List.prototype.__proto__ = Base.prototype; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/markdown.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/markdown.js new file mode 100644 index 0000000..6383a64 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/markdown.js @@ -0,0 +1,91 @@ +/** + * Module dependencies. + */ + +var Base = require('./base') + , utils = require('../utils'); + +/** + * Expose `Markdown`. + */ + +exports = module.exports = Markdown; + +/** + * Initialize a new `Markdown` reporter. + * + * @param {Runner} runner + * @api public + */ + +function Markdown(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , level = 0 + , buf = ''; + + function title(str) { + return Array(level).join('#') + ' ' + str; + } + + function indent() { + return Array(level).join(' '); + } + + function mapTOC(suite, obj) { + var ret = obj; + obj = obj[suite.title] = obj[suite.title] || { suite: suite }; + suite.suites.forEach(function(suite){ + mapTOC(suite, obj); + }); + return ret; + } + + function stringifyTOC(obj, level) { + ++level; + var buf = ''; + var link; + for (var key in obj) { + if ('suite' == key) continue; + if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; + if (key) buf += Array(level).join(' ') + link; + buf += stringifyTOC(obj[key], level); + } + --level; + return buf; + } + + function generateTOC(suite) { + var obj = mapTOC(suite, {}); + return stringifyTOC(obj, 0); + } + + generateTOC(runner.suite); + + runner.on('suite', function(suite){ + ++level; + var slug = utils.slug(suite.fullTitle()); + buf += '' + '\n'; + buf += title(suite.title) + '\n'; + }); + + runner.on('suite end', function(suite){ + --level; + }); + + runner.on('pass', function(test){ + var code = utils.clean(test.fn.toString()); + buf += test.title + '.\n'; + buf += '\n```js\n'; + buf += code + '\n'; + buf += '```\n\n'; + }); + + runner.on('end', function(){ + process.stdout.write('# TOC\n'); + process.stdout.write(generateTOC(runner.suite)); + process.stdout.write(buf); + }); +} \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/min.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/min.js new file mode 100644 index 0000000..1b6117d --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/min.js @@ -0,0 +1,38 @@ + +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `Min`. + */ + +exports = module.exports = Min; + +/** + * Initialize a new `Min` minimal test reporter (best used with --watch). + * + * @param {Runner} runner + * @api public + */ + +function Min(runner) { + Base.call(this, runner); + + runner.on('start', function(){ + // clear screen + process.stdout.write('\u001b[2J'); + // set cursor position + process.stdout.write('\u001b[1;3H'); + }); + + runner.on('end', this.epilogue.bind(this)); +} + +/** + * Inherit from `Base.prototype`. + */ + +Min.prototype.__proto__ = Base.prototype; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/nyan.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/nyan.js new file mode 100644 index 0000000..a8d43bf --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/nyan.js @@ -0,0 +1,260 @@ +/** + * Module dependencies. + */ + +var Base = require('./base') + , color = Base.color; + +/** + * Expose `Dot`. + */ + +exports = module.exports = NyanCat; + +/** + * Initialize a new `Dot` matrix test reporter. + * + * @param {Runner} runner + * @api public + */ + +function NyanCat(runner) { + Base.call(this, runner); + var self = this + , stats = this.stats + , width = Base.window.width * .75 | 0 + , rainbowColors = this.rainbowColors = self.generateColors() + , colorIndex = this.colorIndex = 0 + , numerOfLines = this.numberOfLines = 4 + , trajectories = this.trajectories = [[], [], [], []] + , nyanCatWidth = this.nyanCatWidth = 11 + , trajectoryWidthMax = this.trajectoryWidthMax = (width - nyanCatWidth) + , scoreboardWidth = this.scoreboardWidth = 5 + , tick = this.tick = 0 + , n = 0; + + runner.on('start', function(){ + Base.cursor.hide(); + self.draw(); + }); + + runner.on('pending', function(test){ + self.draw(); + }); + + runner.on('pass', function(test){ + self.draw(); + }); + + runner.on('fail', function(test, err){ + self.draw(); + }); + + runner.on('end', function(){ + Base.cursor.show(); + for (var i = 0; i < self.numberOfLines; i++) write('\n'); + self.epilogue(); + }); +} + +/** + * Draw the nyan cat + * + * @api private + */ + +NyanCat.prototype.draw = function(){ + this.appendRainbow(); + this.drawScoreboard(); + this.drawRainbow(); + this.drawNyanCat(); + this.tick = !this.tick; +}; + +/** + * Draw the "scoreboard" showing the number + * of passes, failures and pending tests. + * + * @api private + */ + +NyanCat.prototype.drawScoreboard = function(){ + var stats = this.stats; + var colors = Base.colors; + + function draw(color, n) { + write(' '); + write('\u001b[' + color + 'm' + n + '\u001b[0m'); + write('\n'); + } + + draw(colors.green, stats.passes); + draw(colors.fail, stats.failures); + draw(colors.pending, stats.pending); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Append the rainbow. + * + * @api private + */ + +NyanCat.prototype.appendRainbow = function(){ + var segment = this.tick ? '_' : '-'; + var rainbowified = this.rainbowify(segment); + + for (var index = 0; index < this.numberOfLines; index++) { + var trajectory = this.trajectories[index]; + if (trajectory.length >= this.trajectoryWidthMax) trajectory.shift(); + trajectory.push(rainbowified); + } +}; + +/** + * Draw the rainbow. + * + * @api private + */ + +NyanCat.prototype.drawRainbow = function(){ + var self = this; + + this.trajectories.forEach(function(line, index) { + write('\u001b[' + self.scoreboardWidth + 'C'); + write(line.join('')); + write('\n'); + }); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw the nyan cat + * + * @api private + */ + +NyanCat.prototype.drawNyanCat = function() { + var self = this; + var startWidth = this.scoreboardWidth + this.trajectories[0].length; + var color = '\u001b[' + startWidth + 'C'; + var padding = ''; + + write(color); + write('_,------,'); + write('\n'); + + write(color); + padding = self.tick ? ' ' : ' '; + write('_|' + padding + '/\\_/\\ '); + write('\n'); + + write(color); + padding = self.tick ? '_' : '__'; + var tail = self.tick ? '~' : '^'; + var face; + write(tail + '|' + padding + this.face() + ' '); + write('\n'); + + write(color); + padding = self.tick ? ' ' : ' '; + write(padding + '"" "" '); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw nyan cat face. + * + * @return {String} + * @api private + */ + +NyanCat.prototype.face = function() { + var stats = this.stats; + if (stats.failures) { + return '( x .x)'; + } else if (stats.pending) { + return '( o .o)'; + } else if(stats.passes) { + return '( ^ .^)'; + } else { + return '( - .-)'; + } +}; + +/** + * Move cursor up `n`. + * + * @param {Number} n + * @api private + */ + +NyanCat.prototype.cursorUp = function(n) { + write('\u001b[' + n + 'A'); +}; + +/** + * Move cursor down `n`. + * + * @param {Number} n + * @api private + */ + +NyanCat.prototype.cursorDown = function(n) { + write('\u001b[' + n + 'B'); +}; + +/** + * Generate rainbow colors. + * + * @return {Array} + * @api private + */ + +NyanCat.prototype.generateColors = function(){ + var colors = []; + + for (var i = 0; i < (6 * 7); i++) { + var pi3 = Math.floor(Math.PI / 3); + var n = (i * (1.0 / 6)); + var r = Math.floor(3 * Math.sin(n) + 3); + var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); + var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); + colors.push(36 * r + 6 * g + b + 16); + } + + return colors; +}; + +/** + * Apply rainbow to the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +NyanCat.prototype.rainbowify = function(str){ + var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; + this.colorIndex += 1; + return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; +}; + +/** + * Stdout helper. + */ + +function write(string) { + process.stdout.write(string); +} + +/** + * Inherit from `Base.prototype`. + */ + +NyanCat.prototype.__proto__ = Base.prototype; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/progress.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/progress.js new file mode 100644 index 0000000..2debb94 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/progress.js @@ -0,0 +1,92 @@ +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `Progress`. + */ + +exports = module.exports = Progress; + +/** + * General progress bar color. + */ + +Base.colors.progress = 90; + +/** + * Initialize a new `Progress` bar test reporter. + * + * @param {Runner} runner + * @param {Object} options + * @api public + */ + +function Progress(runner, options) { + Base.call(this, runner); + + var self = this + , options = options || {} + , stats = this.stats + , width = Base.window.width * .50 | 0 + , total = runner.total + , complete = 0 + , max = Math.max + , lastN = -1; + + // default chars + options.open = options.open || '['; + options.complete = options.complete || '▬'; + options.incomplete = options.incomplete || Base.symbols.dot; + options.close = options.close || ']'; + options.verbose = false; + + // tests started + runner.on('start', function(){ + console.log(); + cursor.hide(); + }); + + // tests complete + runner.on('test end', function(){ + complete++; + var incomplete = total - complete + , percent = complete / total + , n = width * percent | 0 + , i = width - n; + + if (lastN === n && !options.verbose) { + // Don't re-render the line if it hasn't changed + return; + } + lastN = n; + + cursor.CR(); + process.stdout.write('\u001b[J'); + process.stdout.write(color('progress', ' ' + options.open)); + process.stdout.write(Array(n).join(options.complete)); + process.stdout.write(Array(i).join(options.incomplete)); + process.stdout.write(color('progress', options.close)); + if (options.verbose) { + process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); + } + }); + + // tests are complete, output some stats + // and the failures if any + runner.on('end', function(){ + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ + +Progress.prototype.__proto__ = Base.prototype; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/spec.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/spec.js new file mode 100644 index 0000000..ada25c3 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/spec.js @@ -0,0 +1,83 @@ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `Spec`. + */ + +exports = module.exports = Spec; + +/** + * Initialize a new `Spec` test reporter. + * + * @param {Runner} runner + * @api public + */ + +function Spec(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , indents = 0 + , n = 0; + + function indent() { + return Array(indents).join(' ') + } + + runner.on('start', function(){ + console.log(); + }); + + runner.on('suite', function(suite){ + ++indents; + console.log(color('suite', '%s%s'), indent(), suite.title); + }); + + runner.on('suite end', function(suite){ + --indents; + if (1 == indents) console.log(); + }); + + runner.on('pending', function(test){ + var fmt = indent() + color('pending', ' - %s'); + console.log(fmt, test.title); + }); + + runner.on('pass', function(test){ + if ('fast' == test.speed) { + var fmt = indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s '); + cursor.CR(); + console.log(fmt, test.title); + } else { + var fmt = indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s ') + + color(test.speed, '(%dms)'); + cursor.CR(); + console.log(fmt, test.title, test.duration); + } + }); + + runner.on('fail', function(test, err){ + cursor.CR(); + console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); + }); + + runner.on('end', self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ + +Spec.prototype.__proto__ = Base.prototype; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/tap.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/tap.js new file mode 100644 index 0000000..2bcd995 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/tap.js @@ -0,0 +1,73 @@ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `TAP`. + */ + +exports = module.exports = TAP; + +/** + * Initialize a new `TAP` reporter. + * + * @param {Runner} runner + * @api public + */ + +function TAP(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , n = 1 + , passes = 0 + , failures = 0; + + runner.on('start', function(){ + var total = runner.grepTotal(runner.suite); + console.log('%d..%d', 1, total); + }); + + runner.on('test end', function(){ + ++n; + }); + + runner.on('pending', function(test){ + console.log('ok %d %s # SKIP -', n, title(test)); + }); + + runner.on('pass', function(test){ + passes++; + console.log('ok %d %s', n, title(test)); + }); + + runner.on('fail', function(test, err){ + failures++; + console.log('not ok %d %s', n, title(test)); + if (err.stack) console.log(err.stack.replace(/^/gm, ' ')); + }); + + runner.on('end', function(){ + console.log('# tests ' + (passes + failures)); + console.log('# pass ' + passes); + console.log('# fail ' + failures); + }); +} + +/** + * Return a TAP-safe title of `test` + * + * @param {Object} test + * @return {String} + * @api private + */ + +function title(test) { + return test.fullTitle().replace(/#/g, ''); +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/templates/coverage.jade b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/templates/coverage.jade new file mode 100644 index 0000000..edd59d8 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/templates/coverage.jade @@ -0,0 +1,51 @@ +doctype html +html + head + title Coverage + meta(charset='utf-8') + include script.html + include style.html + body + #coverage + h1#overview Coverage + include menu + + #stats(class=coverageClass(cov.coverage)) + .percentage #{cov.coverage | 0}% + .sloc= cov.sloc + .hits= cov.hits + .misses= cov.misses + + #files + for file in cov.files + .file + h2(id=file.filename)= file.filename + #stats(class=coverageClass(file.coverage)) + .percentage #{file.coverage | 0}% + .sloc= file.sloc + .hits= file.hits + .misses= file.misses + + table#source + thead + tr + th Line + th Hits + th Source + tbody + for line, number in file.source + if line.coverage > 0 + tr.hit + td.line= number + td.hits= line.coverage + td.source= line.source + else if 0 === line.coverage + tr.miss + td.line= number + td.hits 0 + td.source= line.source + else + tr + td.line= number + td.hits + td.source= line.source || ' ' diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/templates/menu.jade b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/templates/menu.jade new file mode 100644 index 0000000..e9ba464 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/templates/menu.jade @@ -0,0 +1,13 @@ +#menu + li + a(href='#overview') overview + for file in cov.files + li + span.cov(class=coverageClass(file.coverage)) #{file.coverage | 0} + a(href='##{file.filename}') + segments = file.filename.split('/') + basename = segments.pop() + if segments.length + span.dirname= segments.join('/') + '/' + span.basename= basename + a#logo(href='http://visionmedia.github.io/mocha/') m diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/templates/script.html b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/templates/script.html new file mode 100644 index 0000000..073cf79 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/templates/script.html @@ -0,0 +1,34 @@ + diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/templates/style.html b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/templates/style.html new file mode 100644 index 0000000..643c0ab --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/templates/style.html @@ -0,0 +1,320 @@ + \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/xunit.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/xunit.js new file mode 100644 index 0000000..3506a07 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/reporters/xunit.js @@ -0,0 +1,118 @@ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , utils = require('../utils') + , escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Expose `XUnit`. + */ + +exports = module.exports = XUnit; + +/** + * Initialize a new `XUnit` reporter. + * + * @param {Runner} runner + * @api public + */ + +function XUnit(runner) { + Base.call(this, runner); + var stats = this.stats + , tests = [] + , self = this; + + runner.on('pending', function(test){ + tests.push(test); + }); + + runner.on('pass', function(test){ + tests.push(test); + }); + + runner.on('fail', function(test){ + tests.push(test); + }); + + runner.on('end', function(){ + console.log(tag('testsuite', { + name: 'Mocha Tests' + , tests: stats.tests + , failures: stats.failures + , errors: stats.failures + , skipped: stats.tests - stats.failures - stats.passes + , timestamp: (new Date).toUTCString() + , time: (stats.duration / 1000) || 0 + }, false)); + + tests.forEach(test); + console.log(''); + }); +} + +/** + * Inherit from `Base.prototype`. + */ + +XUnit.prototype.__proto__ = Base.prototype; + +/** + * Output tag for the given `test.` + */ + +function test(test) { + var attrs = { + classname: test.parent.fullTitle() + , name: test.title + , time: (test.duration / 1000) || 0 + }; + + if ('failed' == test.state) { + var err = test.err; + console.log(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack)))); + } else if (test.pending) { + console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); + } else { + console.log(tag('testcase', attrs, true) ); + } +} + +/** + * HTML tag helper. + */ + +function tag(name, attrs, close, content) { + var end = close ? '/>' : '>' + , pairs = [] + , tag; + + for (var key in attrs) { + pairs.push(key + '="' + escape(attrs[key]) + '"'); + } + + tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; + if (content) tag += content + ''; +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/runnable.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/runnable.js new file mode 100644 index 0000000..9409007 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/runnable.js @@ -0,0 +1,264 @@ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , debug = require('debug')('mocha:runnable') + , milliseconds = require('./ms'); + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Object#toString(). + */ + +var toString = Object.prototype.toString; + +/** + * Expose `Runnable`. + */ + +module.exports = Runnable; + +/** + * Initialize a new `Runnable` with the given `title` and callback `fn`. + * + * @param {String} title + * @param {Function} fn + * @api private + */ + +function Runnable(title, fn) { + this.title = title; + this.fn = fn; + this.async = fn && fn.length; + this.sync = ! this.async; + this._timeout = 2000; + this._slow = 75; + this._enableTimeouts = true; + this.timedOut = false; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Runnable.prototype.__proto__ = EventEmitter.prototype; + +/** + * Set & get timeout `ms`. + * + * @param {Number|String} ms + * @return {Runnable|Number} ms or self + * @api private + */ + +Runnable.prototype.timeout = function(ms){ + if (0 == arguments.length) return this._timeout; + if (ms === 0) this._enableTimeouts = false; + if ('string' == typeof ms) ms = milliseconds(ms); + debug('timeout %d', ms); + this._timeout = ms; + if (this.timer) this.resetTimeout(); + return this; +}; + +/** + * Set & get slow `ms`. + * + * @param {Number|String} ms + * @return {Runnable|Number} ms or self + * @api private + */ + +Runnable.prototype.slow = function(ms){ + if (0 === arguments.length) return this._slow; + if ('string' == typeof ms) ms = milliseconds(ms); + debug('timeout %d', ms); + this._slow = ms; + return this; +}; + +/** + * Set and & get timeout `enabled`. + * + * @param {Boolean} enabled + * @return {Runnable|Boolean} enabled or self + * @api private + */ + +Runnable.prototype.enableTimeouts = function(enabled){ + if (arguments.length === 0) return this._enableTimeouts; + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Return the full title generated by recursively + * concatenating the parent's full title. + * + * @return {String} + * @api public + */ + +Runnable.prototype.fullTitle = function(){ + return this.parent.fullTitle() + ' ' + this.title; +}; + +/** + * Clear the timeout. + * + * @api private + */ + +Runnable.prototype.clearTimeout = function(){ + clearTimeout(this.timer); +}; + +/** + * Inspect the runnable void of private properties. + * + * @return {String} + * @api private + */ + +Runnable.prototype.inspect = function(){ + return JSON.stringify(this, function(key, val){ + if ('_' == key[0]) return; + if ('parent' == key) return '#'; + if ('ctx' == key) return '#'; + return val; + }, 2); +}; + +/** + * Reset the timeout. + * + * @api private + */ + +Runnable.prototype.resetTimeout = function(){ + var self = this; + var ms = this.timeout() || 1e9; + + if (!this._enableTimeouts) return; + this.clearTimeout(); + this.timer = setTimeout(function(){ + if (!self._enableTimeouts) return; + self.callback(new Error('timeout of ' + ms + 'ms exceeded')); + self.timedOut = true; + }, ms); +}; + +/** + * Whitelist these globals for this test run + * + * @api private + */ +Runnable.prototype.globals = function(arr){ + var self = this; + this._allowedGlobals = arr; +}; + +/** + * Run the test and invoke `fn(err)`. + * + * @param {Function} fn + * @api private + */ + +Runnable.prototype.run = function(fn){ + var self = this + , start = new Date + , ctx = this.ctx + , finished + , emitted; + + // Some times the ctx exists but it is not runnable + if (ctx && ctx.runnable) ctx.runnable(this); + + // called multiple times + function multiple(err) { + if (emitted) return; + emitted = true; + self.emit('error', err || new Error('done() called multiple times')); + } + + // finished + function done(err) { + var ms = self.timeout(); + if (self.timedOut) return; + if (finished) return multiple(err); + self.clearTimeout(); + self.duration = new Date - start; + finished = true; + if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded'); + fn(err); + } + + // for .resetTimeout() + this.callback = done; + + // explicit async with `done` argument + if (this.async) { + this.resetTimeout(); + + try { + this.fn.call(ctx, function(err){ + if (err instanceof Error || toString.call(err) === "[object Error]") return done(err); + if (null != err) { + if (Object.prototype.toString.call(err) === '[object Object]') { + return done(new Error('done() invoked with non-Error: ' + JSON.stringify(err))); + } else { + return done(new Error('done() invoked with non-Error: ' + err)); + } + } + done(); + }); + } catch (err) { + done(err); + } + return; + } + + if (this.asyncOnly) { + return done(new Error('--async-only option in use without declaring `done()`')); + } + + // sync or promise-returning + try { + if (this.pending) { + done(); + } else { + callFn(this.fn); + } + } catch (err) { + done(err); + } + + function callFn(fn) { + var result = fn.call(ctx); + if (result && typeof result.then === 'function') { + self.resetTimeout(); + result + .then(function() { + done() + }, + function(reason) { + done(reason || new Error('Promise rejected with no or falsy reason')) + }); + } else { + done(); + } + } +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/runner.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/runner.js new file mode 100644 index 0000000..b5a6d49 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/runner.js @@ -0,0 +1,673 @@ +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , debug = require('debug')('mocha:runner') + , Test = require('./test') + , utils = require('./utils') + , filter = utils.filter + , keys = utils.keys; + +/** + * Non-enumerable globals. + */ + +var globals = [ + 'setTimeout', + 'clearTimeout', + 'setInterval', + 'clearInterval', + 'XMLHttpRequest', + 'Date' +]; + +/** + * Expose `Runner`. + */ + +module.exports = Runner; + +/** + * Initialize a `Runner` for the given `suite`. + * + * Events: + * + * - `start` execution started + * - `end` execution complete + * - `suite` (suite) test suite execution started + * - `suite end` (suite) all tests (and sub-suites) have finished + * - `test` (test) test execution started + * - `test end` (test) test completed + * - `hook` (hook) hook execution started + * - `hook end` (hook) hook complete + * - `pass` (test) test passed + * - `fail` (test, err) test failed + * - `pending` (test) test pending + * + * @api public + */ + +function Runner(suite) { + var self = this; + this._globals = []; + this._abort = false; + this.suite = suite; + this.total = suite.total(); + this.failures = 0; + this.on('test end', function(test){ self.checkGlobals(test); }); + this.on('hook end', function(hook){ self.checkGlobals(hook); }); + this.grep(/.*/); + this.globals(this.globalProps().concat(extraGlobals())); +} + +/** + * Wrapper for setImmediate, process.nextTick, or browser polyfill. + * + * @param {Function} fn + * @api private + */ + +Runner.immediately = global.setImmediate || process.nextTick; + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Runner.prototype.__proto__ = EventEmitter.prototype; + +/** + * Run tests with full titles matching `re`. Updates runner.total + * with number of tests matched. + * + * @param {RegExp} re + * @param {Boolean} invert + * @return {Runner} for chaining + * @api public + */ + +Runner.prototype.grep = function(re, invert){ + debug('grep %s', re); + this._grep = re; + this._invert = invert; + this.total = this.grepTotal(this.suite); + return this; +}; + +/** + * Returns the number of tests matching the grep search for the + * given suite. + * + * @param {Suite} suite + * @return {Number} + * @api public + */ + +Runner.prototype.grepTotal = function(suite) { + var self = this; + var total = 0; + + suite.eachTest(function(test){ + var match = self._grep.test(test.fullTitle()); + if (self._invert) match = !match; + if (match) total++; + }); + + return total; +}; + +/** + * Return a list of global properties. + * + * @return {Array} + * @api private + */ + +Runner.prototype.globalProps = function() { + var props = utils.keys(global); + + // non-enumerables + for (var i = 0; i < globals.length; ++i) { + if (~utils.indexOf(props, globals[i])) continue; + props.push(globals[i]); + } + + return props; +}; + +/** + * Allow the given `arr` of globals. + * + * @param {Array} arr + * @return {Runner} for chaining + * @api public + */ + +Runner.prototype.globals = function(arr){ + if (0 == arguments.length) return this._globals; + debug('globals %j', arr); + this._globals = this._globals.concat(arr); + return this; +}; + +/** + * Check for global variable leaks. + * + * @api private + */ + +Runner.prototype.checkGlobals = function(test){ + if (this.ignoreLeaks) return; + var ok = this._globals; + + var globals = this.globalProps(); + var leaks; + + if (test) { + ok = ok.concat(test._allowedGlobals || []); + } + + if(this.prevGlobalsLength == globals.length) return; + this.prevGlobalsLength = globals.length; + + leaks = filterLeaks(ok, globals); + this._globals = this._globals.concat(leaks); + + if (leaks.length > 1) { + this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + '')); + } else if (leaks.length) { + this.fail(test, new Error('global leak detected: ' + leaks[0])); + } +}; + +/** + * Fail the given `test`. + * + * @param {Test} test + * @param {Error} err + * @api private + */ + +Runner.prototype.fail = function(test, err){ + ++this.failures; + test.state = 'failed'; + + if ('string' == typeof err) { + err = new Error('the string "' + err + '" was thrown, throw an Error :)'); + } + + this.emit('fail', test, err); +}; + +/** + * Fail the given `hook` with `err`. + * + * Hook failures work in the following pattern: + * - If bail, then exit + * - Failed `before` hook skips all tests in a suite and subsuites, + * but jumps to corresponding `after` hook + * - Failed `before each` hook skips remaining tests in a + * suite and jumps to corresponding `after each` hook, + * which is run only once + * - Failed `after` hook does not alter + * execution order + * - Failed `after each` hook skips remaining tests in a + * suite and subsuites, but executes other `after each` + * hooks + * + * @param {Hook} hook + * @param {Error} err + * @api private + */ + +Runner.prototype.failHook = function(hook, err){ + this.fail(hook, err); + if (this.suite.bail()) { + this.emit('end'); + } +}; + +/** + * Run hook `name` callbacks and then invoke `fn()`. + * + * @param {String} name + * @param {Function} function + * @api private + */ + +Runner.prototype.hook = function(name, fn){ + var suite = this.suite + , hooks = suite['_' + name] + , self = this + , timer; + + function next(i) { + var hook = hooks[i]; + if (!hook) return fn(); + if (self.failures && suite.bail()) return fn(); + self.currentRunnable = hook; + + hook.ctx.currentTest = self.test; + + self.emit('hook', hook); + + hook.on('error', function(err){ + self.failHook(hook, err); + }); + + hook.run(function(err){ + hook.removeAllListeners('error'); + var testError = hook.error(); + if (testError) self.fail(self.test, testError); + if (err) { + self.failHook(hook, err); + + // stop executing hooks, notify callee of hook err + return fn(err); + } + self.emit('hook end', hook); + delete hook.ctx.currentTest; + next(++i); + }); + } + + Runner.immediately(function(){ + next(0); + }); +}; + +/** + * Run hook `name` for the given array of `suites` + * in order, and callback `fn(err, errSuite)`. + * + * @param {String} name + * @param {Array} suites + * @param {Function} fn + * @api private + */ + +Runner.prototype.hooks = function(name, suites, fn){ + var self = this + , orig = this.suite; + + function next(suite) { + self.suite = suite; + + if (!suite) { + self.suite = orig; + return fn(); + } + + self.hook(name, function(err){ + if (err) { + var errSuite = self.suite; + self.suite = orig; + return fn(err, errSuite); + } + + next(suites.pop()); + }); + } + + next(suites.pop()); +}; + +/** + * Run hooks from the top level down. + * + * @param {String} name + * @param {Function} fn + * @api private + */ + +Runner.prototype.hookUp = function(name, fn){ + var suites = [this.suite].concat(this.parents()).reverse(); + this.hooks(name, suites, fn); +}; + +/** + * Run hooks from the bottom up. + * + * @param {String} name + * @param {Function} fn + * @api private + */ + +Runner.prototype.hookDown = function(name, fn){ + var suites = [this.suite].concat(this.parents()); + this.hooks(name, suites, fn); +}; + +/** + * Return an array of parent Suites from + * closest to furthest. + * + * @return {Array} + * @api private + */ + +Runner.prototype.parents = function(){ + var suite = this.suite + , suites = []; + while (suite = suite.parent) suites.push(suite); + return suites; +}; + +/** + * Run the current test and callback `fn(err)`. + * + * @param {Function} fn + * @api private + */ + +Runner.prototype.runTest = function(fn){ + var test = this.test + , self = this; + + if (this.asyncOnly) test.asyncOnly = true; + + try { + test.on('error', function(err){ + self.fail(test, err); + }); + test.run(fn); + } catch (err) { + fn(err); + } +}; + +/** + * Run tests in the given `suite` and invoke + * the callback `fn()` when complete. + * + * @param {Suite} suite + * @param {Function} fn + * @api private + */ + +Runner.prototype.runTests = function(suite, fn){ + var self = this + , tests = suite.tests.slice() + , test; + + + function hookErr(err, errSuite, after) { + // before/after Each hook for errSuite failed: + var orig = self.suite; + + // for failed 'after each' hook start from errSuite parent, + // otherwise start from errSuite itself + self.suite = after ? errSuite.parent : errSuite; + + if (self.suite) { + // call hookUp afterEach + self.hookUp('afterEach', function(err2, errSuite2) { + self.suite = orig; + // some hooks may fail even now + if (err2) return hookErr(err2, errSuite2, true); + // report error suite + fn(errSuite); + }); + } else { + // there is no need calling other 'after each' hooks + self.suite = orig; + fn(errSuite); + } + } + + function next(err, errSuite) { + // if we bail after first err + if (self.failures && suite._bail) return fn(); + + if (self._abort) return fn(); + + if (err) return hookErr(err, errSuite, true); + + // next test + test = tests.shift(); + + // all done + if (!test) return fn(); + + // grep + var match = self._grep.test(test.fullTitle()); + if (self._invert) match = !match; + if (!match) return next(); + + // pending + if (test.pending) { + self.emit('pending', test); + self.emit('test end', test); + return next(); + } + + // execute test and hook(s) + self.emit('test', self.test = test); + self.hookDown('beforeEach', function(err, errSuite){ + + if (err) return hookErr(err, errSuite, false); + + self.currentRunnable = self.test; + self.runTest(function(err){ + test = self.test; + + if (err) { + self.fail(test, err); + self.emit('test end', test); + return self.hookUp('afterEach', next); + } + + test.state = 'passed'; + self.emit('pass', test); + self.emit('test end', test); + self.hookUp('afterEach', next); + }); + }); + } + + this.next = next; + next(); +}; + +/** + * Run the given `suite` and invoke the + * callback `fn()` when complete. + * + * @param {Suite} suite + * @param {Function} fn + * @api private + */ + +Runner.prototype.runSuite = function(suite, fn){ + var total = this.grepTotal(suite) + , self = this + , i = 0; + + debug('run suite %s', suite.fullTitle()); + + if (!total) return fn(); + + this.emit('suite', this.suite = suite); + + function next(errSuite) { + if (errSuite) { + // current suite failed on a hook from errSuite + if (errSuite == suite) { + // if errSuite is current suite + // continue to the next sibling suite + return done(); + } else { + // errSuite is among the parents of current suite + // stop execution of errSuite and all sub-suites + return done(errSuite); + } + } + + if (self._abort) return done(); + + var curr = suite.suites[i++]; + if (!curr) return done(); + self.runSuite(curr, next); + } + + function done(errSuite) { + self.suite = suite; + self.hook('afterAll', function(){ + self.emit('suite end', suite); + fn(errSuite); + }); + } + + this.hook('beforeAll', function(err){ + if (err) return done(); + self.runTests(suite, next); + }); +}; + +/** + * Handle uncaught exceptions. + * + * @param {Error} err + * @api private + */ + +Runner.prototype.uncaught = function(err){ + if (err) { + debug('uncaught exception %s', err !== function () { + return this; + }.call(err) ? err : ( err.message || err )); + } else { + debug('uncaught undefined exception'); + err = new Error('Caught undefined error, did you throw without specifying what?'); + } + err.uncaught = true; + + var runnable = this.currentRunnable; + if (!runnable) return; + + var wasAlreadyDone = runnable.state; + this.fail(runnable, err); + + runnable.clearTimeout(); + + if (wasAlreadyDone) return; + + // recover from test + if ('test' == runnable.type) { + this.emit('test end', runnable); + this.hookUp('afterEach', this.next); + return; + } + + // bail on hooks + this.emit('end'); +}; + +/** + * Run the root suite and invoke `fn(failures)` + * on completion. + * + * @param {Function} fn + * @return {Runner} for chaining + * @api public + */ + +Runner.prototype.run = function(fn){ + var self = this + , fn = fn || function(){}; + + function uncaught(err){ + self.uncaught(err); + } + + debug('start'); + + // callback + this.on('end', function(){ + debug('end'); + process.removeListener('uncaughtException', uncaught); + fn(self.failures); + }); + + // run suites + this.emit('start'); + this.runSuite(this.suite, function(){ + debug('finished running'); + self.emit('end'); + }); + + // uncaught exception + process.on('uncaughtException', uncaught); + + return this; +}; + +/** + * Cleanly abort execution + * + * @return {Runner} for chaining + * @api public + */ +Runner.prototype.abort = function(){ + debug('aborting'); + this._abort = true; +}; + +/** + * Filter leaks with the given globals flagged as `ok`. + * + * @param {Array} ok + * @param {Array} globals + * @return {Array} + * @api private + */ + +function filterLeaks(ok, globals) { + return filter(globals, function(key){ + // Firefox and Chrome exposes iframes as index inside the window object + if (/^d+/.test(key)) return false; + + // in firefox + // if runner runs in an iframe, this iframe's window.getInterface method not init at first + // it is assigned in some seconds + if (global.navigator && /^getInterface/.test(key)) return false; + + // an iframe could be approached by window[iframeIndex] + // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak + if (global.navigator && /^\d+/.test(key)) return false; + + // Opera and IE expose global variables for HTML element IDs (issue #243) + if (/^mocha-/.test(key)) return false; + + var matched = filter(ok, function(ok){ + if (~ok.indexOf('*')) return 0 == key.indexOf(ok.split('*')[0]); + return key == ok; + }); + return matched.length == 0 && (!global.navigator || 'onerror' !== key); + }); +} + +/** + * Array of globals dependent on the environment. + * + * @return {Array} + * @api private + */ + + function extraGlobals() { + if (typeof(process) === 'object' && + typeof(process.version) === 'string') { + + var nodeVersion = process.version.split('.').reduce(function(a, v) { + return a << 8 | v; + }); + + // 'errno' was renamed to process._errno in v0.9.11. + + if (nodeVersion < 0x00090B) { + return ['errno']; + } + } + + return []; + } diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/suite.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/suite.js new file mode 100644 index 0000000..e8696f4 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/suite.js @@ -0,0 +1,346 @@ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , debug = require('debug')('mocha:suite') + , milliseconds = require('./ms') + , utils = require('./utils') + , Hook = require('./hook'); + +/** + * Expose `Suite`. + */ + +exports = module.exports = Suite; + +/** + * Create a new `Suite` with the given `title` + * and parent `Suite`. When a suite with the + * same title is already present, that suite + * is returned to provide nicer reporter + * and more flexible meta-testing. + * + * @param {Suite} parent + * @param {String} title + * @return {Suite} + * @api public + */ + +exports.create = function(parent, title){ + var suite = new Suite(title, parent.ctx); + suite.parent = parent; + if (parent.pending) suite.pending = true; + title = suite.fullTitle(); + parent.addSuite(suite); + return suite; +}; + +/** + * Initialize a new `Suite` with the given + * `title` and `ctx`. + * + * @param {String} title + * @param {Context} ctx + * @api private + */ + +function Suite(title, parentContext) { + this.title = title; + var context = function() {}; + context.prototype = parentContext; + this.ctx = new context(); + this.suites = []; + this.tests = []; + this.pending = false; + this._beforeEach = []; + this._beforeAll = []; + this._afterEach = []; + this._afterAll = []; + this.root = !title; + this._timeout = 2000; + this._enableTimeouts = true; + this._slow = 75; + this._bail = false; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Suite.prototype.__proto__ = EventEmitter.prototype; + +/** + * Return a clone of this `Suite`. + * + * @return {Suite} + * @api private + */ + +Suite.prototype.clone = function(){ + var suite = new Suite(this.title); + debug('clone'); + suite.ctx = this.ctx; + suite.timeout(this.timeout()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + return suite; +}; + +/** + * Set timeout `ms` or short-hand such as "2s". + * + * @param {Number|String} ms + * @return {Suite|Number} for chaining + * @api private + */ + +Suite.prototype.timeout = function(ms){ + if (0 == arguments.length) return this._timeout; + if (ms === 0) this._enableTimeouts = false; + if ('string' == typeof ms) ms = milliseconds(ms); + debug('timeout %d', ms); + this._timeout = parseInt(ms, 10); + return this; +}; + +/** + * Set timeout `enabled`. + * + * @param {Boolean} enabled + * @return {Suite|Boolean} self or enabled + * @api private + */ + +Suite.prototype.enableTimeouts = function(enabled){ + if (arguments.length === 0) return this._enableTimeouts; + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Set slow `ms` or short-hand such as "2s". + * + * @param {Number|String} ms + * @return {Suite|Number} for chaining + * @api private + */ + +Suite.prototype.slow = function(ms){ + if (0 === arguments.length) return this._slow; + if ('string' == typeof ms) ms = milliseconds(ms); + debug('slow %d', ms); + this._slow = ms; + return this; +}; + +/** + * Sets whether to bail after first error. + * + * @parma {Boolean} bail + * @return {Suite|Number} for chaining + * @api private + */ + +Suite.prototype.bail = function(bail){ + if (0 == arguments.length) return this._bail; + debug('bail %s', bail); + this._bail = bail; + return this; +}; + +/** + * Run `fn(test[, done])` before running tests. + * + * @param {Function} fn + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.beforeAll = function(title, fn){ + if (this.pending) return this; + if ('function' === typeof title) { + fn = title; + title = fn.name; + } + title = '"before all" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._beforeAll.push(hook); + this.emit('beforeAll', hook); + return this; +}; + +/** + * Run `fn(test[, done])` after running tests. + * + * @param {Function} fn + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.afterAll = function(title, fn){ + if (this.pending) return this; + if ('function' === typeof title) { + fn = title; + title = fn.name; + } + title = '"after all" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._afterAll.push(hook); + this.emit('afterAll', hook); + return this; +}; + +/** + * Run `fn(test[, done])` before each test case. + * + * @param {Function} fn + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.beforeEach = function(title, fn){ + if (this.pending) return this; + if ('function' === typeof title) { + fn = title; + title = fn.name; + } + title = '"before each" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._beforeEach.push(hook); + this.emit('beforeEach', hook); + return this; +}; + +/** + * Run `fn(test[, done])` after each test case. + * + * @param {Function} fn + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.afterEach = function(title, fn){ + if (this.pending) return this; + if ('function' === typeof title) { + fn = title; + title = fn.name; + } + title = '"after each" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._afterEach.push(hook); + this.emit('afterEach', hook); + return this; +}; + +/** + * Add a test `suite`. + * + * @param {Suite} suite + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.addSuite = function(suite){ + suite.parent = this; + suite.timeout(this.timeout()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + this.suites.push(suite); + this.emit('suite', suite); + return this; +}; + +/** + * Add a `test` to this suite. + * + * @param {Test} test + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.addTest = function(test){ + test.parent = this; + test.timeout(this.timeout()); + test.enableTimeouts(this.enableTimeouts()); + test.slow(this.slow()); + test.ctx = this.ctx; + this.tests.push(test); + this.emit('test', test); + return this; +}; + +/** + * Return the full title generated by recursively + * concatenating the parent's full title. + * + * @return {String} + * @api public + */ + +Suite.prototype.fullTitle = function(){ + if (this.parent) { + var full = this.parent.fullTitle(); + if (full) return full + ' ' + this.title; + } + return this.title; +}; + +/** + * Return the total number of tests. + * + * @return {Number} + * @api public + */ + +Suite.prototype.total = function(){ + return utils.reduce(this.suites, function(sum, suite){ + return sum + suite.total(); + }, 0) + this.tests.length; +}; + +/** + * Iterates through each suite recursively to find + * all tests. Applies a function in the format + * `fn(test)`. + * + * @param {Function} fn + * @return {Suite} + * @api private + */ + +Suite.prototype.eachTest = function(fn){ + utils.forEach(this.tests, fn); + utils.forEach(this.suites, function(suite){ + suite.eachTest(fn); + }); + return this; +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/template.html b/node_modules/promises-aplus-tests/node_modules/mocha/lib/template.html new file mode 100644 index 0000000..0590d4a --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/template.html @@ -0,0 +1,18 @@ + + + + Mocha + + + + + +
          + + + + + + diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/test.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/test.js new file mode 100644 index 0000000..11773e0 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/test.js @@ -0,0 +1,32 @@ + +/** + * Module dependencies. + */ + +var Runnable = require('./runnable'); + +/** + * Expose `Test`. + */ + +module.exports = Test; + +/** + * Initialize a new `Test` with the given `title` and callback `fn`. + * + * @param {String} title + * @param {Function} fn + * @api private + */ + +function Test(title, fn) { + Runnable.call(this, title, fn); + this.pending = !fn; + this.type = 'test'; +} + +/** + * Inherit from `Runnable.prototype`. + */ + +Test.prototype.__proto__ = Runnable.prototype; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/lib/utils.js b/node_modules/promises-aplus-tests/node_modules/mocha/lib/utils.js new file mode 100644 index 0000000..ecc0a14 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/lib/utils.js @@ -0,0 +1,388 @@ +/** + * Module dependencies. + */ + +var fs = require('fs') + , path = require('path') + , basename = path.basename + , exists = fs.existsSync || path.existsSync + , glob = require('glob') + , join = path.join + , debug = require('debug')('mocha:watch'); + +/** + * Ignored directories. + */ + +var ignore = ['node_modules', '.git']; + +/** + * Escape special characters in the given string of html. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function(html){ + return String(html) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); +}; + +/** + * Array#forEach (<=IE8) + * + * @param {Array} array + * @param {Function} fn + * @param {Object} scope + * @api private + */ + +exports.forEach = function(arr, fn, scope){ + for (var i = 0, l = arr.length; i < l; i++) + fn.call(scope, arr[i], i); +}; + +/** + * Array#map (<=IE8) + * + * @param {Array} array + * @param {Function} fn + * @param {Object} scope + * @api private + */ + +exports.map = function(arr, fn, scope){ + var result = []; + for (var i = 0, l = arr.length; i < l; i++) + result.push(fn.call(scope, arr[i], i)); + return result; +}; + +/** + * Array#indexOf (<=IE8) + * + * @parma {Array} arr + * @param {Object} obj to find index of + * @param {Number} start + * @api private + */ + +exports.indexOf = function(arr, obj, start){ + for (var i = start || 0, l = arr.length; i < l; i++) { + if (arr[i] === obj) + return i; + } + return -1; +}; + +/** + * Array#reduce (<=IE8) + * + * @param {Array} array + * @param {Function} fn + * @param {Object} initial value + * @api private + */ + +exports.reduce = function(arr, fn, val){ + var rval = val; + + for (var i = 0, l = arr.length; i < l; i++) { + rval = fn(rval, arr[i], i, arr); + } + + return rval; +}; + +/** + * Array#filter (<=IE8) + * + * @param {Array} array + * @param {Function} fn + * @api private + */ + +exports.filter = function(arr, fn){ + var ret = []; + + for (var i = 0, l = arr.length; i < l; i++) { + var val = arr[i]; + if (fn(val, i, arr)) ret.push(val); + } + + return ret; +}; + +/** + * Object.keys (<=IE8) + * + * @param {Object} obj + * @return {Array} keys + * @api private + */ + +exports.keys = Object.keys || function(obj) { + var keys = [] + , has = Object.prototype.hasOwnProperty // for `window` on <=IE8 + + for (var key in obj) { + if (has.call(obj, key)) { + keys.push(key); + } + } + + return keys; +}; + +/** + * Watch the given `files` for changes + * and invoke `fn(file)` on modification. + * + * @param {Array} files + * @param {Function} fn + * @api private + */ + +exports.watch = function(files, fn){ + var options = { interval: 100 }; + files.forEach(function(file){ + debug('file %s', file); + fs.watchFile(file, options, function(curr, prev){ + if (prev.mtime < curr.mtime) fn(file); + }); + }); +}; + +/** + * Ignored files. + */ + +function ignored(path){ + return !~ignore.indexOf(path); +} + +/** + * Lookup files in the given `dir`. + * + * @return {Array} + * @api private + */ + +exports.files = function(dir, ext, ret){ + ret = ret || []; + ext = ext || ['js']; + + var re = new RegExp('\\.(' + ext.join('|') + ')$'); + + fs.readdirSync(dir) + .filter(ignored) + .forEach(function(path){ + path = join(dir, path); + if (fs.statSync(path).isDirectory()) { + exports.files(path, ext, ret); + } else if (path.match(re)) { + ret.push(path); + } + }); + + return ret; +}; + +/** + * Compute a slug from the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.slug = function(str){ + return str + .toLowerCase() + .replace(/ +/g, '-') + .replace(/[^-\w]/g, ''); +}; + +/** + * Strip the function definition from `str`, + * and re-indent for pre whitespace. + */ + +exports.clean = function(str) { + str = str + .replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '') + .replace(/^function *\(.*\) *{|\(.*\) *=> *{?/, '') + .replace(/\s+\}$/, ''); + + var spaces = str.match(/^\n?( *)/)[1].length + , tabs = str.match(/^\n?(\t*)/)[1].length + , re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs ? tabs : spaces) + '}', 'gm'); + + str = str.replace(re, ''); + + return exports.trim(str); +}; + +/** + * Trim the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.trim = function(str){ + return str.replace(/^\s+|\s+$/g, ''); +}; + +/** + * Parse the given `qs`. + * + * @param {String} qs + * @return {Object} + * @api private + */ + +exports.parseQuery = function(qs){ + return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){ + var i = pair.indexOf('=') + , key = pair.slice(0, i) + , val = pair.slice(++i); + + obj[key] = decodeURIComponent(val); + return obj; + }, {}); +}; + +/** + * Highlight the given string of `js`. + * + * @param {String} js + * @return {String} + * @api private + */ + +function highlight(js) { + return js + .replace(//g, '>') + .replace(/\/\/(.*)/gm, '//$1') + .replace(/('.*?')/gm, '$1') + .replace(/(\d+\.\d+)/gm, '$1') + .replace(/(\d+)/gm, '$1') + .replace(/\bnew[ \t]+(\w+)/gm, 'new $1') + .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1') +} + +/** + * Highlight the contents of tag `name`. + * + * @param {String} name + * @api private + */ + +exports.highlightTags = function(name) { + var code = document.getElementById('mocha').getElementsByTagName(name); + for (var i = 0, len = code.length; i < len; ++i) { + code[i].innerHTML = highlight(code[i].innerHTML); + } +}; + + +/** + * Stringify `obj`. + * + * @param {Object} obj + * @return {String} + * @api private + */ + +exports.stringify = function(obj) { + if (obj instanceof RegExp) return obj.toString(); + return JSON.stringify(exports.canonicalize(obj), null, 2).replace(/,(\n|$)/g, '$1'); +}; + +/** + * Return a new object that has the keys in sorted order. + * @param {Object} obj + * @param {Array} [stack] + * @return {Object} + * @api private + */ + +exports.canonicalize = function(obj, stack) { + stack = stack || []; + + if (exports.indexOf(stack, obj) !== -1) return '[Circular]'; + + var canonicalizedObj; + + if ({}.toString.call(obj) === '[object Array]') { + stack.push(obj); + canonicalizedObj = exports.map(obj, function (item) { + return exports.canonicalize(item, stack); + }); + stack.pop(); + } else if (typeof obj === 'object' && obj !== null) { + stack.push(obj); + canonicalizedObj = {}; + exports.forEach(exports.keys(obj).sort(), function (key) { + canonicalizedObj[key] = exports.canonicalize(obj[key], stack); + }); + stack.pop(); + } else { + canonicalizedObj = obj; + } + + return canonicalizedObj; + }; + +/** + * Lookup file names at the given `path`. + */ +exports.lookupFiles = function lookupFiles(path, extensions, recursive) { + var files = []; + var re = new RegExp('\\.(' + extensions.join('|') + ')$'); + + if (!exists(path)) { + if (exists(path + '.js')) { + path += '.js'; + } else { + files = glob.sync(path); + if (!files.length) throw new Error("cannot resolve path (or pattern) '" + path + "'"); + return files; + } + } + + try { + var stat = fs.statSync(path); + if (stat.isFile()) return path; + } + catch (ignored) { + return; + } + + fs.readdirSync(path).forEach(function(file){ + file = join(path, file); + try { + var stat = fs.statSync(file); + if (stat.isDirectory()) { + if (recursive) { + files = files.concat(lookupFiles(file, extensions, recursive)); + } + return; + } + } + catch (ignored) { + return; + } + if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') return; + files.push(file); + }); + + return files; +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/mocha.css b/node_modules/promises-aplus-tests/node_modules/mocha/mocha.css new file mode 100644 index 0000000..42b9798 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/mocha.css @@ -0,0 +1,270 @@ +@charset "utf-8"; + +body { + margin:0; +} + +#mocha { + font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; + margin: 60px 50px; +} + +#mocha ul, +#mocha li { + margin: 0; + padding: 0; +} + +#mocha ul { + list-style: none; +} + +#mocha h1, +#mocha h2 { + margin: 0; +} + +#mocha h1 { + margin-top: 15px; + font-size: 1em; + font-weight: 200; +} + +#mocha h1 a { + text-decoration: none; + color: inherit; +} + +#mocha h1 a:hover { + text-decoration: underline; +} + +#mocha .suite .suite h1 { + margin-top: 0; + font-size: .8em; +} + +#mocha .hidden { + display: none; +} + +#mocha h2 { + font-size: 12px; + font-weight: normal; + cursor: pointer; +} + +#mocha .suite { + margin-left: 15px; +} + +#mocha .test { + margin-left: 15px; + overflow: hidden; +} + +#mocha .test.pending:hover h2::after { + content: '(pending)'; + font-family: arial, sans-serif; +} + +#mocha .test.pass.medium .duration { + background: #c09853; +} + +#mocha .test.pass.slow .duration { + background: #b94a48; +} + +#mocha .test.pass::before { + content: '✓'; + font-size: 12px; + display: block; + float: left; + margin-right: 5px; + color: #00d6b2; +} + +#mocha .test.pass .duration { + font-size: 9px; + margin-left: 5px; + padding: 2px 5px; + color: #fff; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); + -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); + box-shadow: inset 0 1px 1px rgba(0,0,0,.2); + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + -ms-border-radius: 5px; + -o-border-radius: 5px; + border-radius: 5px; +} + +#mocha .test.pass.fast .duration { + display: none; +} + +#mocha .test.pending { + color: #0b97c4; +} + +#mocha .test.pending::before { + content: '◦'; + color: #0b97c4; +} + +#mocha .test.fail { + color: #c00; +} + +#mocha .test.fail pre { + color: black; +} + +#mocha .test.fail::before { + content: '✖'; + font-size: 12px; + display: block; + float: left; + margin-right: 5px; + color: #c00; +} + +#mocha .test pre.error { + color: #c00; + max-height: 300px; + overflow: auto; +} + +/** + * (1): approximate for browsers not supporting calc + * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) + * ^^ seriously + */ +#mocha .test pre { + display: block; + float: left; + clear: left; + font: 12px/1.5 monaco, monospace; + margin: 5px; + padding: 15px; + border: 1px solid #eee; + max-width: 85%; /*(1)*/ + max-width: calc(100% - 42px); /*(2)*/ + word-wrap: break-word; + border-bottom-color: #ddd; + -webkit-border-radius: 3px; + -webkit-box-shadow: 0 1px 3px #eee; + -moz-border-radius: 3px; + -moz-box-shadow: 0 1px 3px #eee; + border-radius: 3px; +} + +#mocha .test h2 { + position: relative; +} + +#mocha .test a.replay { + position: absolute; + top: 3px; + right: 0; + text-decoration: none; + vertical-align: middle; + display: block; + width: 15px; + height: 15px; + line-height: 15px; + text-align: center; + background: #eee; + font-size: 15px; + -moz-border-radius: 15px; + border-radius: 15px; + -webkit-transition: opacity 200ms; + -moz-transition: opacity 200ms; + transition: opacity 200ms; + opacity: 0.3; + color: #888; +} + +#mocha .test:hover a.replay { + opacity: 1; +} + +#mocha-report.pass .test.fail { + display: none; +} + +#mocha-report.fail .test.pass { + display: none; +} + +#mocha-report.pending .test.pass, +#mocha-report.pending .test.fail { + display: none; +} +#mocha-report.pending .test.pass.pending { + display: block; +} + +#mocha-error { + color: #c00; + font-size: 1.5em; + font-weight: 100; + letter-spacing: 1px; +} + +#mocha-stats { + position: fixed; + top: 15px; + right: 10px; + font-size: 12px; + margin: 0; + color: #888; + z-index: 1; +} + +#mocha-stats .progress { + float: right; + padding-top: 0; +} + +#mocha-stats em { + color: black; +} + +#mocha-stats a { + text-decoration: none; + color: inherit; +} + +#mocha-stats a:hover { + border-bottom: 1px solid #eee; +} + +#mocha-stats li { + display: inline-block; + margin: 0 5px; + list-style: none; + padding-top: 11px; +} + +#mocha-stats canvas { + width: 40px; + height: 40px; +} + +#mocha code .comment { color: #ddd; } +#mocha code .init { color: #2f6fad; } +#mocha code .string { color: #5890ad; } +#mocha code .keyword { color: #8a6343; } +#mocha code .number { color: #2f6fad; } + +@media screen and (max-device-width: 480px) { + #mocha { + margin: 60px 0px; + } + + #mocha #stats { + position: absolute; + } +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/mocha.js b/node_modules/promises-aplus-tests/node_modules/mocha/mocha.js new file mode 100644 index 0000000..e8bee79 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/mocha.js @@ -0,0 +1,6095 @@ +;(function(){ + +// CommonJS require() + +function require(p){ + var path = require.resolve(p) + , mod = require.modules[path]; + if (!mod) throw new Error('failed to require "' + p + '"'); + if (!mod.exports) { + mod.exports = {}; + mod.call(mod.exports, mod, mod.exports, require.relative(path)); + } + return mod.exports; + } + +require.modules = {}; + +require.resolve = function (path){ + var orig = path + , reg = path + '.js' + , index = path + '/index.js'; + return require.modules[reg] && reg + || require.modules[index] && index + || orig; + }; + +require.register = function (path, fn){ + require.modules[path] = fn; + }; + +require.relative = function (parent) { + return function(p){ + if ('.' != p.charAt(0)) return require(p); + + var path = parent.split('/') + , segs = p.split('/'); + path.pop(); + + for (var i = 0; i < segs.length; i++) { + var seg = segs[i]; + if ('..' == seg) path.pop(); + else if ('.' != seg) path.push(seg); + } + + return require(path.join('/')); + }; + }; + + +require.register("browser/debug.js", function(module, exports, require){ + +module.exports = function(type){ + return function(){ + } +}; + +}); // module: browser/debug.js + +require.register("browser/diff.js", function(module, exports, require){ +/* See LICENSE file for terms of use */ + +/* + * Text diff implementation. + * + * This library supports the following APIS: + * JsDiff.diffChars: Character by character diff + * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace + * JsDiff.diffLines: Line based diff + * + * JsDiff.diffCss: Diff targeted at CSS content + * + * These methods are based on the implementation proposed in + * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 + */ +var JsDiff = (function() { + /*jshint maxparams: 5*/ + function clonePath(path) { + return { newPos: path.newPos, components: path.components.slice(0) }; + } + function removeEmpty(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + if (array[i]) { + ret.push(array[i]); + } + } + return ret; + } + function escapeHTML(s) { + var n = s; + n = n.replace(/&/g, '&'); + n = n.replace(//g, '>'); + n = n.replace(/"/g, '"'); + + return n; + } + + var Diff = function(ignoreWhitespace) { + this.ignoreWhitespace = ignoreWhitespace; + }; + Diff.prototype = { + diff: function(oldString, newString) { + // Handle the identity case (this is due to unrolling editLength == 0 + if (newString === oldString) { + return [{ value: newString }]; + } + if (!newString) { + return [{ value: oldString, removed: true }]; + } + if (!oldString) { + return [{ value: newString, added: true }]; + } + + newString = this.tokenize(newString); + oldString = this.tokenize(oldString); + + var newLen = newString.length, oldLen = oldString.length; + var maxEditLength = newLen + oldLen; + var bestPath = [{ newPos: -1, components: [] }]; + + // Seed editLength = 0 + var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); + if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) { + return bestPath[0].components; + } + + for (var editLength = 1; editLength <= maxEditLength; editLength++) { + for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) { + var basePath; + var addPath = bestPath[diagonalPath-1], + removePath = bestPath[diagonalPath+1]; + oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; + if (addPath) { + // No one else is going to attempt to use this value, clear it + bestPath[diagonalPath-1] = undefined; + } + + var canAdd = addPath && addPath.newPos+1 < newLen; + var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; + if (!canAdd && !canRemove) { + bestPath[diagonalPath] = undefined; + continue; + } + + // Select the diagonal that we want to branch from. We select the prior + // path whose position in the new string is the farthest from the origin + // and does not pass the bounds of the diff graph + if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { + basePath = clonePath(removePath); + this.pushComponent(basePath.components, oldString[oldPos], undefined, true); + } else { + basePath = clonePath(addPath); + basePath.newPos++; + this.pushComponent(basePath.components, newString[basePath.newPos], true, undefined); + } + + var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath); + + if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) { + return basePath.components; + } else { + bestPath[diagonalPath] = basePath; + } + } + } + }, + + pushComponent: function(components, value, added, removed) { + var last = components[components.length-1]; + if (last && last.added === added && last.removed === removed) { + // We need to clone here as the component clone operation is just + // as shallow array clone + components[components.length-1] = + {value: this.join(last.value, value), added: added, removed: removed }; + } else { + components.push({value: value, added: added, removed: removed }); + } + }, + extractCommon: function(basePath, newString, oldString, diagonalPath) { + var newLen = newString.length, + oldLen = oldString.length, + newPos = basePath.newPos, + oldPos = newPos - diagonalPath; + while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) { + newPos++; + oldPos++; + + this.pushComponent(basePath.components, newString[newPos], undefined, undefined); + } + basePath.newPos = newPos; + return oldPos; + }, + + equals: function(left, right) { + var reWhitespace = /\S/; + if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) { + return true; + } else { + return left === right; + } + }, + join: function(left, right) { + return left + right; + }, + tokenize: function(value) { + return value; + } + }; + + var CharDiff = new Diff(); + + var WordDiff = new Diff(true); + var WordWithSpaceDiff = new Diff(); + WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { + return removeEmpty(value.split(/(\s+|\b)/)); + }; + + var CssDiff = new Diff(true); + CssDiff.tokenize = function(value) { + return removeEmpty(value.split(/([{}:;,]|\s+)/)); + }; + + var LineDiff = new Diff(); + LineDiff.tokenize = function(value) { + var retLines = [], + lines = value.split(/^/m); + + for(var i = 0; i < lines.length; i++) { + var line = lines[i], + lastLine = lines[i - 1]; + + // Merge lines that may contain windows new lines + if (line == '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') { + retLines[retLines.length - 1] += '\n'; + } else if (line) { + retLines.push(line); + } + } + + return retLines; + }; + + return { + Diff: Diff, + + diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); }, + diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); }, + diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); }, + diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); }, + + diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); }, + + createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { + var ret = []; + + ret.push('Index: ' + fileName); + ret.push('==================================================================='); + ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); + ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); + + var diff = LineDiff.diff(oldStr, newStr); + if (!diff[diff.length-1].value) { + diff.pop(); // Remove trailing newline add + } + diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier + + function contextLines(lines) { + return lines.map(function(entry) { return ' ' + entry; }); + } + function eofNL(curRange, i, current) { + var last = diff[diff.length-2], + isLast = i === diff.length-2, + isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed); + + // Figure out if this is the last line for the given file and missing NL + if (!/\n$/.test(current.value) && (isLast || isLastOfType)) { + curRange.push('\\ No newline at end of file'); + } + } + + var oldRangeStart = 0, newRangeStart = 0, curRange = [], + oldLine = 1, newLine = 1; + for (var i = 0; i < diff.length; i++) { + var current = diff[i], + lines = current.lines || current.value.replace(/\n$/, '').split('\n'); + current.lines = lines; + + if (current.added || current.removed) { + if (!oldRangeStart) { + var prev = diff[i-1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + + if (prev) { + curRange = contextLines(prev.lines.slice(-4)); + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; + } + } + curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; })); + eofNL(curRange, i, current); + + if (current.added) { + newLine += lines.length; + } else { + oldLine += lines.length; + } + } else { + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= 8 && i < diff.length-2) { + // Overlapping + curRange.push.apply(curRange, contextLines(lines)); + } else { + // end the range and output + var contextSize = Math.min(lines.length, 4); + ret.push( + '@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize) + + ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize) + + ' @@'); + ret.push.apply(ret, curRange); + ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); + if (lines.length <= 4) { + eofNL(ret, i, current); + } + + oldRangeStart = 0; newRangeStart = 0; curRange = []; + } + } + oldLine += lines.length; + newLine += lines.length; + } + } + + return ret.join('\n') + '\n'; + }, + + applyPatch: function(oldStr, uniDiff) { + var diffstr = uniDiff.split('\n'); + var diff = []; + var remEOFNL = false, + addEOFNL = false; + + for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) { + if(diffstr[i][0] === '@') { + var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); + diff.unshift({ + start:meh[3], + oldlength:meh[2], + oldlines:[], + newlength:meh[4], + newlines:[] + }); + } else if(diffstr[i][0] === '+') { + diff[0].newlines.push(diffstr[i].substr(1)); + } else if(diffstr[i][0] === '-') { + diff[0].oldlines.push(diffstr[i].substr(1)); + } else if(diffstr[i][0] === ' ') { + diff[0].newlines.push(diffstr[i].substr(1)); + diff[0].oldlines.push(diffstr[i].substr(1)); + } else if(diffstr[i][0] === '\\') { + if (diffstr[i-1][0] === '+') { + remEOFNL = true; + } else if(diffstr[i-1][0] === '-') { + addEOFNL = true; + } + } + } + + var str = oldStr.split('\n'); + for (var i = diff.length - 1; i >= 0; i--) { + var d = diff[i]; + for (var j = 0; j < d.oldlength; j++) { + if(str[d.start-1+j] !== d.oldlines[j]) { + return false; + } + } + Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines)); + } + + if (remEOFNL) { + while (!str[str.length-1]) { + str.pop(); + } + } else if (addEOFNL) { + str.push(''); + } + return str.join('\n'); + }, + + convertChangesToXML: function(changes){ + var ret = []; + for ( var i = 0; i < changes.length; i++) { + var change = changes[i]; + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + + ret.push(escapeHTML(change.value)); + + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + } + return ret.join(''); + }, + + // See: http://code.google.com/p/google-diff-match-patch/wiki/API + convertChangesToDMP: function(changes){ + var ret = [], change; + for ( var i = 0; i < changes.length; i++) { + change = changes[i]; + ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]); + } + return ret; + } + }; +})(); + +if (typeof module !== 'undefined') { + module.exports = JsDiff; +} + +}); // module: browser/diff.js + +require.register("browser/escape-string-regexp.js", function(module, exports, require){ +'use strict'; + +var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; + +module.exports = function (str) { + if (typeof str !== 'string') { + throw new TypeError('Expected a string'); + } + + return str.replace(matchOperatorsRe, '\\$&'); +}; + +}); // module: browser/escape-string-regexp.js + +require.register("browser/events.js", function(module, exports, require){ + +/** + * Module exports. + */ + +exports.EventEmitter = EventEmitter; + +/** + * Check if `obj` is an array. + */ + +function isArray(obj) { + return '[object Array]' == {}.toString.call(obj); +} + +/** + * Event emitter constructor. + * + * @api public + */ + +function EventEmitter(){}; + +/** + * Adds a listener. + * + * @api public + */ + +EventEmitter.prototype.on = function (name, fn) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = fn; + } else if (isArray(this.$events[name])) { + this.$events[name].push(fn); + } else { + this.$events[name] = [this.$events[name], fn]; + } + + return this; +}; + +EventEmitter.prototype.addListener = EventEmitter.prototype.on; + +/** + * Adds a volatile listener. + * + * @api public + */ + +EventEmitter.prototype.once = function (name, fn) { + var self = this; + + function on () { + self.removeListener(name, on); + fn.apply(this, arguments); + }; + + on.listener = fn; + this.on(name, on); + + return this; +}; + +/** + * Removes a listener. + * + * @api public + */ + +EventEmitter.prototype.removeListener = function (name, fn) { + if (this.$events && this.$events[name]) { + var list = this.$events[name]; + + if (isArray(list)) { + var pos = -1; + + for (var i = 0, l = list.length; i < l; i++) { + if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { + pos = i; + break; + } + } + + if (pos < 0) { + return this; + } + + list.splice(pos, 1); + + if (!list.length) { + delete this.$events[name]; + } + } else if (list === fn || (list.listener && list.listener === fn)) { + delete this.$events[name]; + } + } + + return this; +}; + +/** + * Removes all listeners for an event. + * + * @api public + */ + +EventEmitter.prototype.removeAllListeners = function (name) { + if (name === undefined) { + this.$events = {}; + return this; + } + + if (this.$events && this.$events[name]) { + this.$events[name] = null; + } + + return this; +}; + +/** + * Gets all listeners for a certain event. + * + * @api public + */ + +EventEmitter.prototype.listeners = function (name) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = []; + } + + if (!isArray(this.$events[name])) { + this.$events[name] = [this.$events[name]]; + } + + return this.$events[name]; +}; + +/** + * Emits an event. + * + * @api public + */ + +EventEmitter.prototype.emit = function (name) { + if (!this.$events) { + return false; + } + + var handler = this.$events[name]; + + if (!handler) { + return false; + } + + var args = [].slice.call(arguments, 1); + + if ('function' == typeof handler) { + handler.apply(this, args); + } else if (isArray(handler)) { + var listeners = handler.slice(); + + for (var i = 0, l = listeners.length; i < l; i++) { + listeners[i].apply(this, args); + } + } else { + return false; + } + + return true; +}; +}); // module: browser/events.js + +require.register("browser/fs.js", function(module, exports, require){ + +}); // module: browser/fs.js + +require.register("browser/glob.js", function(module, exports, require){ + +}); // module: browser/glob.js + +require.register("browser/path.js", function(module, exports, require){ + +}); // module: browser/path.js + +require.register("browser/progress.js", function(module, exports, require){ +/** + * Expose `Progress`. + */ + +module.exports = Progress; + +/** + * Initialize a new `Progress` indicator. + */ + +function Progress() { + this.percent = 0; + this.size(0); + this.fontSize(11); + this.font('helvetica, arial, sans-serif'); +} + +/** + * Set progress size to `n`. + * + * @param {Number} n + * @return {Progress} for chaining + * @api public + */ + +Progress.prototype.size = function(n){ + this._size = n; + return this; +}; + +/** + * Set text to `str`. + * + * @param {String} str + * @return {Progress} for chaining + * @api public + */ + +Progress.prototype.text = function(str){ + this._text = str; + return this; +}; + +/** + * Set font size to `n`. + * + * @param {Number} n + * @return {Progress} for chaining + * @api public + */ + +Progress.prototype.fontSize = function(n){ + this._fontSize = n; + return this; +}; + +/** + * Set font `family`. + * + * @param {String} family + * @return {Progress} for chaining + */ + +Progress.prototype.font = function(family){ + this._font = family; + return this; +}; + +/** + * Update percentage to `n`. + * + * @param {Number} n + * @return {Progress} for chaining + */ + +Progress.prototype.update = function(n){ + this.percent = n; + return this; +}; + +/** + * Draw on `ctx`. + * + * @param {CanvasRenderingContext2d} ctx + * @return {Progress} for chaining + */ + +Progress.prototype.draw = function(ctx){ + try { + var percent = Math.min(this.percent, 100) + , size = this._size + , half = size / 2 + , x = half + , y = half + , rad = half - 1 + , fontSize = this._fontSize; + + ctx.font = fontSize + 'px ' + this._font; + + var angle = Math.PI * 2 * (percent / 100); + ctx.clearRect(0, 0, size, size); + + // outer circle + ctx.strokeStyle = '#9f9f9f'; + ctx.beginPath(); + ctx.arc(x, y, rad, 0, angle, false); + ctx.stroke(); + + // inner circle + ctx.strokeStyle = '#eee'; + ctx.beginPath(); + ctx.arc(x, y, rad - 1, 0, angle, true); + ctx.stroke(); + + // text + var text = this._text || (percent | 0) + '%' + , w = ctx.measureText(text).width; + + ctx.fillText( + text + , x - w / 2 + 1 + , y + fontSize / 2 - 1); + } catch (ex) {} //don't fail if we can't render progress + return this; +}; + +}); // module: browser/progress.js + +require.register("browser/tty.js", function(module, exports, require){ + +exports.isatty = function(){ + return true; +}; + +exports.getWindowSize = function(){ + if ('innerHeight' in global) { + return [global.innerHeight, global.innerWidth]; + } else { + // In a Web Worker, the DOM Window is not available. + return [640, 480]; + } +}; + +}); // module: browser/tty.js + +require.register("context.js", function(module, exports, require){ + +/** + * Expose `Context`. + */ + +module.exports = Context; + +/** + * Initialize a new `Context`. + * + * @api private + */ + +function Context(){} + +/** + * Set or get the context `Runnable` to `runnable`. + * + * @param {Runnable} runnable + * @return {Context} + * @api private + */ + +Context.prototype.runnable = function(runnable){ + if (0 == arguments.length) return this._runnable; + this.test = this._runnable = runnable; + return this; +}; + +/** + * Set test timeout `ms`. + * + * @param {Number} ms + * @return {Context} self + * @api private + */ + +Context.prototype.timeout = function(ms){ + if (arguments.length === 0) return this.runnable().timeout(); + this.runnable().timeout(ms); + return this; +}; + +/** + * Set test timeout `enabled`. + * + * @param {Boolean} enabled + * @return {Context} self + * @api private + */ + +Context.prototype.enableTimeouts = function (enabled) { + this.runnable().enableTimeouts(enabled); + return this; +}; + + +/** + * Set test slowness threshold `ms`. + * + * @param {Number} ms + * @return {Context} self + * @api private + */ + +Context.prototype.slow = function(ms){ + this.runnable().slow(ms); + return this; +}; + +/** + * Inspect the context void of `._runnable`. + * + * @return {String} + * @api private + */ + +Context.prototype.inspect = function(){ + return JSON.stringify(this, function(key, val){ + if ('_runnable' == key) return; + if ('test' == key) return; + return val; + }, 2); +}; + +}); // module: context.js + +require.register("hook.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Runnable = require('./runnable'); + +/** + * Expose `Hook`. + */ + +module.exports = Hook; + +/** + * Initialize a new `Hook` with the given `title` and callback `fn`. + * + * @param {String} title + * @param {Function} fn + * @api private + */ + +function Hook(title, fn) { + Runnable.call(this, title, fn); + this.type = 'hook'; +} + +/** + * Inherit from `Runnable.prototype`. + */ + +function F(){}; +F.prototype = Runnable.prototype; +Hook.prototype = new F; +Hook.prototype.constructor = Hook; + + +/** + * Get or set the test `err`. + * + * @param {Error} err + * @return {Error} + * @api public + */ + +Hook.prototype.error = function(err){ + if (0 == arguments.length) { + var err = this._error; + this._error = null; + return err; + } + + this._error = err; +}; + +}); // module: hook.js + +require.register("interfaces/bdd.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Suite = require('../suite') + , Test = require('../test') + , utils = require('../utils') + , escapeRe = require('browser/escape-string-regexp'); + +/** + * BDD-style interface: + * + * describe('Array', function(){ + * describe('#indexOf()', function(){ + * it('should return -1 when not present', function(){ + * + * }); + * + * it('should return the index when present', function(){ + * + * }); + * }); + * }); + * + */ + +module.exports = function(suite){ + var suites = [suite]; + + suite.on('pre-require', function(context, file, mocha){ + + /** + * Execute before running tests. + */ + + context.before = function(name, fn){ + suites[0].beforeAll(name, fn); + }; + + /** + * Execute after running tests. + */ + + context.after = function(name, fn){ + suites[0].afterAll(name, fn); + }; + + /** + * Execute before each test case. + */ + + context.beforeEach = function(name, fn){ + suites[0].beforeEach(name, fn); + }; + + /** + * Execute after each test case. + */ + + context.afterEach = function(name, fn){ + suites[0].afterEach(name, fn); + }; + + /** + * Describe a "suite" with the given `title` + * and callback `fn` containing nested suites + * and/or tests. + */ + + context.describe = context.context = function(title, fn){ + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + return suite; + }; + + /** + * Pending describe. + */ + + context.xdescribe = + context.xcontext = + context.describe.skip = function(title, fn){ + var suite = Suite.create(suites[0], title); + suite.pending = true; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + }; + + /** + * Exclusive suite. + */ + + context.describe.only = function(title, fn){ + var suite = context.describe(title, fn); + mocha.grep(suite.fullTitle()); + return suite; + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.it = context.specify = function(title, fn){ + var suite = suites[0]; + if (suite.pending) fn = null; + var test = new Test(title, fn); + test.file = file; + suite.addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.it.only = function(title, fn){ + var test = context.it(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + return test; + }; + + /** + * Pending test case. + */ + + context.xit = + context.xspecify = + context.it.skip = function(title){ + context.it(title); + }; + }); +}; + +}); // module: interfaces/bdd.js + +require.register("interfaces/exports.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Suite = require('../suite') + , Test = require('../test'); + +/** + * TDD-style interface: + * + * exports.Array = { + * '#indexOf()': { + * 'should return -1 when the value is not present': function(){ + * + * }, + * + * 'should return the correct index when the value is present': function(){ + * + * } + * } + * }; + * + */ + +module.exports = function(suite){ + var suites = [suite]; + + suite.on('require', visit); + + function visit(obj, file) { + var suite; + for (var key in obj) { + if ('function' == typeof obj[key]) { + var fn = obj[key]; + switch (key) { + case 'before': + suites[0].beforeAll(fn); + break; + case 'after': + suites[0].afterAll(fn); + break; + case 'beforeEach': + suites[0].beforeEach(fn); + break; + case 'afterEach': + suites[0].afterEach(fn); + break; + default: + var test = new Test(key, fn); + test.file = file; + suites[0].addTest(test); + } + } else { + suite = Suite.create(suites[0], key); + suites.unshift(suite); + visit(obj[key]); + suites.shift(); + } + } + } +}; + +}); // module: interfaces/exports.js + +require.register("interfaces/index.js", function(module, exports, require){ + +exports.bdd = require('./bdd'); +exports.tdd = require('./tdd'); +exports.qunit = require('./qunit'); +exports.exports = require('./exports'); + +}); // module: interfaces/index.js + +require.register("interfaces/qunit.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Suite = require('../suite') + , Test = require('../test') + , escapeRe = require('browser/escape-string-regexp') + , utils = require('../utils'); + +/** + * QUnit-style interface: + * + * suite('Array'); + * + * test('#length', function(){ + * var arr = [1,2,3]; + * ok(arr.length == 3); + * }); + * + * test('#indexOf()', function(){ + * var arr = [1,2,3]; + * ok(arr.indexOf(1) == 0); + * ok(arr.indexOf(2) == 1); + * ok(arr.indexOf(3) == 2); + * }); + * + * suite('String'); + * + * test('#length', function(){ + * ok('foo'.length == 3); + * }); + * + */ + +module.exports = function(suite){ + var suites = [suite]; + + suite.on('pre-require', function(context, file, mocha){ + + /** + * Execute before running tests. + */ + + context.before = function(name, fn){ + suites[0].beforeAll(name, fn); + }; + + /** + * Execute after running tests. + */ + + context.after = function(name, fn){ + suites[0].afterAll(name, fn); + }; + + /** + * Execute before each test case. + */ + + context.beforeEach = function(name, fn){ + suites[0].beforeEach(name, fn); + }; + + /** + * Execute after each test case. + */ + + context.afterEach = function(name, fn){ + suites[0].afterEach(name, fn); + }; + + /** + * Describe a "suite" with the given `title`. + */ + + context.suite = function(title){ + if (suites.length > 1) suites.shift(); + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + return suite; + }; + + /** + * Exclusive test-case. + */ + + context.suite.only = function(title, fn){ + var suite = context.suite(title, fn); + mocha.grep(suite.fullTitle()); + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.test = function(title, fn){ + var test = new Test(title, fn); + test.file = file; + suites[0].addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn){ + var test = context.test(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + }; + + /** + * Pending test case. + */ + + context.test.skip = function(title){ + context.test(title); + }; + }); +}; + +}); // module: interfaces/qunit.js + +require.register("interfaces/tdd.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Suite = require('../suite') + , Test = require('../test') + , escapeRe = require('browser/escape-string-regexp') + , utils = require('../utils'); + +/** + * TDD-style interface: + * + * suite('Array', function(){ + * suite('#indexOf()', function(){ + * suiteSetup(function(){ + * + * }); + * + * test('should return -1 when not present', function(){ + * + * }); + * + * test('should return the index when present', function(){ + * + * }); + * + * suiteTeardown(function(){ + * + * }); + * }); + * }); + * + */ + +module.exports = function(suite){ + var suites = [suite]; + + suite.on('pre-require', function(context, file, mocha){ + + /** + * Execute before each test case. + */ + + context.setup = function(name, fn){ + suites[0].beforeEach(name, fn); + }; + + /** + * Execute after each test case. + */ + + context.teardown = function(name, fn){ + suites[0].afterEach(name, fn); + }; + + /** + * Execute before the suite. + */ + + context.suiteSetup = function(name, fn){ + suites[0].beforeAll(name, fn); + }; + + /** + * Execute after the suite. + */ + + context.suiteTeardown = function(name, fn){ + suites[0].afterAll(name, fn); + }; + + /** + * Describe a "suite" with the given `title` + * and callback `fn` containing nested suites + * and/or tests. + */ + + context.suite = function(title, fn){ + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + return suite; + }; + + /** + * Pending suite. + */ + context.suite.skip = function(title, fn) { + var suite = Suite.create(suites[0], title); + suite.pending = true; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + }; + + /** + * Exclusive test-case. + */ + + context.suite.only = function(title, fn){ + var suite = context.suite(title, fn); + mocha.grep(suite.fullTitle()); + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.test = function(title, fn){ + var suite = suites[0]; + if (suite.pending) fn = null; + var test = new Test(title, fn); + test.file = file; + suite.addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn){ + var test = context.test(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + }; + + /** + * Pending test case. + */ + + context.test.skip = function(title){ + context.test(title); + }; + }); +}; + +}); // module: interfaces/tdd.js + +require.register("mocha.js", function(module, exports, require){ +/*! + * mocha + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var path = require('browser/path') + , escapeRe = require('browser/escape-string-regexp') + , utils = require('./utils'); + +/** + * Expose `Mocha`. + */ + +exports = module.exports = Mocha; + +/** + * To require local UIs and reporters when running in node. + */ + +if (typeof process !== 'undefined' && typeof process.cwd === 'function') { + var join = path.join + , cwd = process.cwd(); + module.paths.push(cwd, join(cwd, 'node_modules')); +} + +/** + * Expose internals. + */ + +exports.utils = utils; +exports.interfaces = require('./interfaces'); +exports.reporters = require('./reporters'); +exports.Runnable = require('./runnable'); +exports.Context = require('./context'); +exports.Runner = require('./runner'); +exports.Suite = require('./suite'); +exports.Hook = require('./hook'); +exports.Test = require('./test'); + +/** + * Return image `name` path. + * + * @param {String} name + * @return {String} + * @api private + */ + +function image(name) { + return __dirname + '/../images/' + name + '.png'; +} + +/** + * Setup mocha with `options`. + * + * Options: + * + * - `ui` name "bdd", "tdd", "exports" etc + * - `reporter` reporter instance, defaults to `mocha.reporters.spec` + * - `globals` array of accepted globals + * - `timeout` timeout in milliseconds + * - `bail` bail on the first test failure + * - `slow` milliseconds to wait before considering a test slow + * - `ignoreLeaks` ignore global leaks + * - `grep` string or regexp to filter tests with + * + * @param {Object} options + * @api public + */ + +function Mocha(options) { + options = options || {}; + this.files = []; + this.options = options; + this.grep(options.grep); + this.suite = new exports.Suite('', new exports.Context); + this.ui(options.ui); + this.bail(options.bail); + this.reporter(options.reporter); + if (null != options.timeout) this.timeout(options.timeout); + this.useColors(options.useColors) + if (options.enableTimeouts !== null) this.enableTimeouts(options.enableTimeouts); + if (options.slow) this.slow(options.slow); + + this.suite.on('pre-require', function (context) { + exports.afterEach = context.afterEach || context.teardown; + exports.after = context.after || context.suiteTeardown; + exports.beforeEach = context.beforeEach || context.setup; + exports.before = context.before || context.suiteSetup; + exports.describe = context.describe || context.suite; + exports.it = context.it || context.test; + exports.setup = context.setup || context.beforeEach; + exports.suiteSetup = context.suiteSetup || context.before; + exports.suiteTeardown = context.suiteTeardown || context.after; + exports.suite = context.suite || context.describe; + exports.teardown = context.teardown || context.afterEach; + exports.test = context.test || context.it; + }); +} + +/** + * Enable or disable bailing on the first failure. + * + * @param {Boolean} [bail] + * @api public + */ + +Mocha.prototype.bail = function(bail){ + if (0 == arguments.length) bail = true; + this.suite.bail(bail); + return this; +}; + +/** + * Add test `file`. + * + * @param {String} file + * @api public + */ + +Mocha.prototype.addFile = function(file){ + this.files.push(file); + return this; +}; + +/** + * Set reporter to `reporter`, defaults to "spec". + * + * @param {String|Function} reporter name or constructor + * @api public + */ + +Mocha.prototype.reporter = function(reporter){ + if ('function' == typeof reporter) { + this._reporter = reporter; + } else { + reporter = reporter || 'spec'; + var _reporter; + try { _reporter = require('./reporters/' + reporter); } catch (err) {}; + if (!_reporter) try { _reporter = require(reporter); } catch (err) {}; + if (!_reporter && reporter === 'teamcity') + console.warn('The Teamcity reporter was moved to a package named ' + + 'mocha-teamcity-reporter ' + + '(https://npmjs.org/package/mocha-teamcity-reporter).'); + if (!_reporter) throw new Error('invalid reporter "' + reporter + '"'); + this._reporter = _reporter; + } + return this; +}; + +/** + * Set test UI `name`, defaults to "bdd". + * + * @param {String} bdd + * @api public + */ + +Mocha.prototype.ui = function(name){ + name = name || 'bdd'; + this._ui = exports.interfaces[name]; + if (!this._ui) try { this._ui = require(name); } catch (err) {}; + if (!this._ui) throw new Error('invalid interface "' + name + '"'); + this._ui = this._ui(this.suite); + return this; +}; + +/** + * Load registered files. + * + * @api private + */ + +Mocha.prototype.loadFiles = function(fn){ + var self = this; + var suite = this.suite; + var pending = this.files.length; + this.files.forEach(function(file){ + file = path.resolve(file); + suite.emit('pre-require', global, file, self); + suite.emit('require', require(file), file, self); + suite.emit('post-require', global, file, self); + --pending || (fn && fn()); + }); +}; + +/** + * Enable growl support. + * + * @api private + */ + +Mocha.prototype._growl = function(runner, reporter) { + var notify = require('growl'); + + runner.on('end', function(){ + var stats = reporter.stats; + if (stats.failures) { + var msg = stats.failures + ' of ' + runner.total + ' tests failed'; + notify(msg, { name: 'mocha', title: 'Failed', image: image('error') }); + } else { + notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { + name: 'mocha' + , title: 'Passed' + , image: image('ok') + }); + } + }); +}; + +/** + * Add regexp to grep, if `re` is a string it is escaped. + * + * @param {RegExp|String} re + * @return {Mocha} + * @api public + */ + +Mocha.prototype.grep = function(re){ + this.options.grep = 'string' == typeof re + ? new RegExp(escapeRe(re)) + : re; + return this; +}; + +/** + * Invert `.grep()` matches. + * + * @return {Mocha} + * @api public + */ + +Mocha.prototype.invert = function(){ + this.options.invert = true; + return this; +}; + +/** + * Ignore global leaks. + * + * @param {Boolean} ignore + * @return {Mocha} + * @api public + */ + +Mocha.prototype.ignoreLeaks = function(ignore){ + this.options.ignoreLeaks = !!ignore; + return this; +}; + +/** + * Enable global leak checking. + * + * @return {Mocha} + * @api public + */ + +Mocha.prototype.checkLeaks = function(){ + this.options.ignoreLeaks = false; + return this; +}; + +/** + * Enable growl support. + * + * @return {Mocha} + * @api public + */ + +Mocha.prototype.growl = function(){ + this.options.growl = true; + return this; +}; + +/** + * Ignore `globals` array or string. + * + * @param {Array|String} globals + * @return {Mocha} + * @api public + */ + +Mocha.prototype.globals = function(globals){ + this.options.globals = (this.options.globals || []).concat(globals); + return this; +}; + +/** + * Emit color output. + * + * @param {Boolean} colors + * @return {Mocha} + * @api public + */ + +Mocha.prototype.useColors = function(colors){ + this.options.useColors = arguments.length && colors != undefined + ? colors + : true; + return this; +}; + +/** + * Use inline diffs rather than +/-. + * + * @param {Boolean} inlineDiffs + * @return {Mocha} + * @api public + */ + +Mocha.prototype.useInlineDiffs = function(inlineDiffs) { + this.options.useInlineDiffs = arguments.length && inlineDiffs != undefined + ? inlineDiffs + : false; + return this; +}; + +/** + * Set the timeout in milliseconds. + * + * @param {Number} timeout + * @return {Mocha} + * @api public + */ + +Mocha.prototype.timeout = function(timeout){ + this.suite.timeout(timeout); + return this; +}; + +/** + * Set slowness threshold in milliseconds. + * + * @param {Number} slow + * @return {Mocha} + * @api public + */ + +Mocha.prototype.slow = function(slow){ + this.suite.slow(slow); + return this; +}; + +/** + * Enable timeouts. + * + * @param {Boolean} enabled + * @return {Mocha} + * @api public + */ + +Mocha.prototype.enableTimeouts = function(enabled) { + this.suite.enableTimeouts(arguments.length && enabled !== undefined + ? enabled + : true); + return this +}; + +/** + * Makes all tests async (accepting a callback) + * + * @return {Mocha} + * @api public + */ + +Mocha.prototype.asyncOnly = function(){ + this.options.asyncOnly = true; + return this; +}; + +/** + * Disable syntax highlighting (in browser). + * @returns {Mocha} + * @api public + */ +Mocha.prototype.noHighlighting = function() { + this.options.noHighlighting = true; + return this; +}; + +/** + * Run tests and invoke `fn()` when complete. + * + * @param {Function} fn + * @return {Runner} + * @api public + */ + +Mocha.prototype.run = function(fn){ + if (this.files.length) this.loadFiles(); + var suite = this.suite; + var options = this.options; + options.files = this.files; + var runner = new exports.Runner(suite); + var reporter = new this._reporter(runner, options); + runner.ignoreLeaks = false !== options.ignoreLeaks; + runner.asyncOnly = options.asyncOnly; + if (options.grep) runner.grep(options.grep, options.invert); + if (options.globals) runner.globals(options.globals); + if (options.growl) this._growl(runner, reporter); + exports.reporters.Base.useColors = options.useColors; + exports.reporters.Base.inlineDiffs = options.useInlineDiffs; + return runner.run(fn); +}; + +}); // module: mocha.js + +require.register("ms.js", function(module, exports, require){ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} options + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options){ + options = options || {}; + if ('string' == typeof val) return parse(val); + return options['long'] ? longFormat(val) : shortFormat(val); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str); + if (!match) return; + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 's': + return n * s; + case 'ms': + return n; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function shortFormat(ms) { + if (ms >= d) return Math.round(ms / d) + 'd'; + if (ms >= h) return Math.round(ms / h) + 'h'; + if (ms >= m) return Math.round(ms / m) + 'm'; + if (ms >= s) return Math.round(ms / s) + 's'; + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function longFormat(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) return; + if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; + return Math.ceil(ms / n) + ' ' + name + 's'; +} + +}); // module: ms.js + +require.register("reporters/base.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var tty = require('browser/tty') + , diff = require('browser/diff') + , ms = require('../ms') + , utils = require('../utils'); + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Check if both stdio streams are associated with a tty. + */ + +var isatty = tty.isatty(1) && tty.isatty(2); + +/** + * Expose `Base`. + */ + +exports = module.exports = Base; + +/** + * Enable coloring by default. + */ + +exports.useColors = isatty || (process.env.MOCHA_COLORS !== undefined); + +/** + * Inline diffs instead of +/- + */ + +exports.inlineDiffs = false; + +/** + * Default color map. + */ + +exports.colors = { + 'pass': 90 + , 'fail': 31 + , 'bright pass': 92 + , 'bright fail': 91 + , 'bright yellow': 93 + , 'pending': 36 + , 'suite': 0 + , 'error title': 0 + , 'error message': 31 + , 'error stack': 90 + , 'checkmark': 32 + , 'fast': 90 + , 'medium': 33 + , 'slow': 31 + , 'green': 32 + , 'light': 90 + , 'diff gutter': 90 + , 'diff added': 42 + , 'diff removed': 41 +}; + +/** + * Default symbol map. + */ + +exports.symbols = { + ok: '✓', + err: '✖', + dot: '․' +}; + +// With node.js on Windows: use symbols available in terminal default fonts +if ('win32' == process.platform) { + exports.symbols.ok = '\u221A'; + exports.symbols.err = '\u00D7'; + exports.symbols.dot = '.'; +} + +/** + * Color `str` with the given `type`, + * allowing colors to be disabled, + * as well as user-defined color + * schemes. + * + * @param {String} type + * @param {String} str + * @return {String} + * @api private + */ + +var color = exports.color = function(type, str) { + if (!exports.useColors) return str; + return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; +}; + +/** + * Expose term window size, with some + * defaults for when stderr is not a tty. + */ + +exports.window = { + width: isatty + ? process.stdout.getWindowSize + ? process.stdout.getWindowSize(1)[0] + : tty.getWindowSize()[1] + : 75 +}; + +/** + * Expose some basic cursor interactions + * that are common among reporters. + */ + +exports.cursor = { + hide: function(){ + isatty && process.stdout.write('\u001b[?25l'); + }, + + show: function(){ + isatty && process.stdout.write('\u001b[?25h'); + }, + + deleteLine: function(){ + isatty && process.stdout.write('\u001b[2K'); + }, + + beginningOfLine: function(){ + isatty && process.stdout.write('\u001b[0G'); + }, + + CR: function(){ + if (isatty) { + exports.cursor.deleteLine(); + exports.cursor.beginningOfLine(); + } else { + process.stdout.write('\r'); + } + } +}; + +/** + * Outut the given `failures` as a list. + * + * @param {Array} failures + * @api public + */ + +exports.list = function(failures){ + console.error(); + failures.forEach(function(test, i){ + // format + var fmt = color('error title', ' %s) %s:\n') + + color('error message', ' %s') + + color('error stack', '\n%s\n'); + + // msg + var err = test.err + , message = err.message || '' + , stack = err.stack || message + , index = stack.indexOf(message) + message.length + , msg = stack.slice(0, index) + , actual = err.actual + , expected = err.expected + , escape = true; + + // uncaught + if (err.uncaught) { + msg = 'Uncaught ' + msg; + } + + // explicitly show diff + if (err.showDiff && sameType(actual, expected)) { + escape = false; + err.actual = actual = utils.stringify(actual); + err.expected = expected = utils.stringify(expected); + } + + // actual / expected diff + if (err.showDiff && 'string' == typeof actual && 'string' == typeof expected) { + fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n'); + var match = message.match(/^([^:]+): expected/); + msg = '\n ' + color('error message', match ? match[1] : msg); + + if (exports.inlineDiffs) { + msg += inlineDiff(err, escape); + } else { + msg += unifiedDiff(err, escape); + } + } + + // indent stack trace without msg + stack = stack.slice(index ? index + 1 : index) + .replace(/^/gm, ' '); + + console.error(fmt, (i + 1), test.fullTitle(), msg, stack); + }); +}; + +/** + * Initialize a new `Base` reporter. + * + * All other reporters generally + * inherit from this reporter, providing + * stats such as test duration, number + * of tests passed / failed etc. + * + * @param {Runner} runner + * @api public + */ + +function Base(runner) { + var self = this + , stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 } + , failures = this.failures = []; + + if (!runner) return; + this.runner = runner; + + runner.stats = stats; + + runner.on('start', function(){ + stats.start = new Date; + }); + + runner.on('suite', function(suite){ + stats.suites = stats.suites || 0; + suite.root || stats.suites++; + }); + + runner.on('test end', function(test){ + stats.tests = stats.tests || 0; + stats.tests++; + }); + + runner.on('pass', function(test){ + stats.passes = stats.passes || 0; + + var medium = test.slow() / 2; + test.speed = test.duration > test.slow() + ? 'slow' + : test.duration > medium + ? 'medium' + : 'fast'; + + stats.passes++; + }); + + runner.on('fail', function(test, err){ + stats.failures = stats.failures || 0; + stats.failures++; + test.err = err; + failures.push(test); + }); + + runner.on('end', function(){ + stats.end = new Date; + stats.duration = new Date - stats.start; + }); + + runner.on('pending', function(){ + stats.pending++; + }); +} + +/** + * Output common epilogue used by many of + * the bundled reporters. + * + * @api public + */ + +Base.prototype.epilogue = function(){ + var stats = this.stats; + var tests; + var fmt; + + console.log(); + + // passes + fmt = color('bright pass', ' ') + + color('green', ' %d passing') + + color('light', ' (%s)'); + + console.log(fmt, + stats.passes || 0, + ms(stats.duration)); + + // pending + if (stats.pending) { + fmt = color('pending', ' ') + + color('pending', ' %d pending'); + + console.log(fmt, stats.pending); + } + + // failures + if (stats.failures) { + fmt = color('fail', ' %d failing'); + + console.error(fmt, + stats.failures); + + Base.list(this.failures); + console.error(); + } + + console.log(); +}; + +/** + * Pad the given `str` to `len`. + * + * @param {String} str + * @param {String} len + * @return {String} + * @api private + */ + +function pad(str, len) { + str = String(str); + return Array(len - str.length + 1).join(' ') + str; +} + + +/** + * Returns an inline diff between 2 strings with coloured ANSI output + * + * @param {Error} Error with actual/expected + * @return {String} Diff + * @api private + */ + +function inlineDiff(err, escape) { + var msg = errorDiff(err, 'WordsWithSpace', escape); + + // linenos + var lines = msg.split('\n'); + if (lines.length > 4) { + var width = String(lines.length).length; + msg = lines.map(function(str, i){ + return pad(++i, width) + ' |' + ' ' + str; + }).join('\n'); + } + + // legend + msg = '\n' + + color('diff removed', 'actual') + + ' ' + + color('diff added', 'expected') + + '\n\n' + + msg + + '\n'; + + // indent + msg = msg.replace(/^/gm, ' '); + return msg; +} + +/** + * Returns a unified diff between 2 strings + * + * @param {Error} Error with actual/expected + * @return {String} Diff + * @api private + */ + +function unifiedDiff(err, escape) { + var indent = ' '; + function cleanUp(line) { + if (escape) { + line = escapeInvisibles(line); + } + if (line[0] === '+') return indent + colorLines('diff added', line); + if (line[0] === '-') return indent + colorLines('diff removed', line); + if (line.match(/\@\@/)) return null; + if (line.match(/\\ No newline/)) return null; + else return indent + line; + } + function notBlank(line) { + return line != null; + } + msg = diff.createPatch('string', err.actual, err.expected); + var lines = msg.split('\n').splice(4); + return '\n ' + + colorLines('diff added', '+ expected') + ' ' + + colorLines('diff removed', '- actual') + + '\n\n' + + lines.map(cleanUp).filter(notBlank).join('\n'); +} + +/** + * Return a character diff for `err`. + * + * @param {Error} err + * @return {String} + * @api private + */ + +function errorDiff(err, type, escape) { + var actual = escape ? escapeInvisibles(err.actual) : err.actual; + var expected = escape ? escapeInvisibles(err.expected) : err.expected; + return diff['diff' + type](actual, expected).map(function(str){ + if (str.added) return colorLines('diff added', str.value); + if (str.removed) return colorLines('diff removed', str.value); + return str.value; + }).join(''); +} + +/** + * Returns a string with all invisible characters in plain text + * + * @param {String} line + * @return {String} + * @api private + */ +function escapeInvisibles(line) { + return line.replace(/\t/g, '') + .replace(/\r/g, '') + .replace(/\n/g, '\n'); +} + +/** + * Color lines for `str`, using the color `name`. + * + * @param {String} name + * @param {String} str + * @return {String} + * @api private + */ + +function colorLines(name, str) { + return str.split('\n').map(function(str){ + return color(name, str); + }).join('\n'); +} + +/** + * Check that a / b have the same type. + * + * @param {Object} a + * @param {Object} b + * @return {Boolean} + * @api private + */ + +function sameType(a, b) { + a = Object.prototype.toString.call(a); + b = Object.prototype.toString.call(b); + return a == b; +} + +}); // module: reporters/base.js + +require.register("reporters/doc.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , utils = require('../utils'); + +/** + * Expose `Doc`. + */ + +exports = module.exports = Doc; + +/** + * Initialize a new `Doc` reporter. + * + * @param {Runner} runner + * @api public + */ + +function Doc(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , total = runner.total + , indents = 2; + + function indent() { + return Array(indents).join(' '); + } + + runner.on('suite', function(suite){ + if (suite.root) return; + ++indents; + console.log('%s
          ', indent()); + ++indents; + console.log('%s

          %s

          ', indent(), utils.escape(suite.title)); + console.log('%s
          ', indent()); + }); + + runner.on('suite end', function(suite){ + if (suite.root) return; + console.log('%s
          ', indent()); + --indents; + console.log('%s
          ', indent()); + --indents; + }); + + runner.on('pass', function(test){ + console.log('%s
          %s
          ', indent(), utils.escape(test.title)); + var code = utils.escape(utils.clean(test.fn.toString())); + console.log('%s
          %s
          ', indent(), code); + }); + + runner.on('fail', function(test, err){ + console.log('%s
          %s
          ', indent(), utils.escape(test.title)); + var code = utils.escape(utils.clean(test.fn.toString())); + console.log('%s
          %s
          ', indent(), code); + console.log('%s
          %s
          ', indent(), utils.escape(err)); + }); +} + +}); // module: reporters/doc.js + +require.register("reporters/dot.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , color = Base.color; + +/** + * Expose `Dot`. + */ + +exports = module.exports = Dot; + +/** + * Initialize a new `Dot` matrix test reporter. + * + * @param {Runner} runner + * @api public + */ + +function Dot(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , width = Base.window.width * .75 | 0 + , n = -1; + + runner.on('start', function(){ + process.stdout.write('\n '); + }); + + runner.on('pending', function(test){ + if (++n % width == 0) process.stdout.write('\n '); + process.stdout.write(color('pending', Base.symbols.dot)); + }); + + runner.on('pass', function(test){ + if (++n % width == 0) process.stdout.write('\n '); + if ('slow' == test.speed) { + process.stdout.write(color('bright yellow', Base.symbols.dot)); + } else { + process.stdout.write(color(test.speed, Base.symbols.dot)); + } + }); + + runner.on('fail', function(test, err){ + if (++n % width == 0) process.stdout.write('\n '); + process.stdout.write(color('fail', Base.symbols.dot)); + }); + + runner.on('end', function(){ + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +Dot.prototype = new F; +Dot.prototype.constructor = Dot; + + +}); // module: reporters/dot.js + +require.register("reporters/html-cov.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var JSONCov = require('./json-cov') + , fs = require('browser/fs'); + +/** + * Expose `HTMLCov`. + */ + +exports = module.exports = HTMLCov; + +/** + * Initialize a new `JsCoverage` reporter. + * + * @param {Runner} runner + * @api public + */ + +function HTMLCov(runner) { + var jade = require('jade') + , file = __dirname + '/templates/coverage.jade' + , str = fs.readFileSync(file, 'utf8') + , fn = jade.compile(str, { filename: file }) + , self = this; + + JSONCov.call(this, runner, false); + + runner.on('end', function(){ + process.stdout.write(fn({ + cov: self.cov + , coverageClass: coverageClass + })); + }); +} + +/** + * Return coverage class for `n`. + * + * @return {String} + * @api private + */ + +function coverageClass(n) { + if (n >= 75) return 'high'; + if (n >= 50) return 'medium'; + if (n >= 25) return 'low'; + return 'terrible'; +} +}); // module: reporters/html-cov.js + +require.register("reporters/html.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , utils = require('../utils') + , Progress = require('../browser/progress') + , escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Expose `HTML`. + */ + +exports = module.exports = HTML; + +/** + * Stats template. + */ + +var statsTemplate = ''; + +/** + * Initialize a new `HTML` reporter. + * + * @param {Runner} runner + * @api public + */ + +function HTML(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , total = runner.total + , stat = fragment(statsTemplate) + , items = stat.getElementsByTagName('li') + , passes = items[1].getElementsByTagName('em')[0] + , passesLink = items[1].getElementsByTagName('a')[0] + , failures = items[2].getElementsByTagName('em')[0] + , failuresLink = items[2].getElementsByTagName('a')[0] + , duration = items[3].getElementsByTagName('em')[0] + , canvas = stat.getElementsByTagName('canvas')[0] + , report = fragment('
            ') + , stack = [report] + , progress + , ctx + , root = document.getElementById('mocha'); + + if (canvas.getContext) { + var ratio = window.devicePixelRatio || 1; + canvas.style.width = canvas.width; + canvas.style.height = canvas.height; + canvas.width *= ratio; + canvas.height *= ratio; + ctx = canvas.getContext('2d'); + ctx.scale(ratio, ratio); + progress = new Progress; + } + + if (!root) return error('#mocha div missing, add it to your document'); + + // pass toggle + on(passesLink, 'click', function(){ + unhide(); + var name = /pass/.test(report.className) ? '' : ' pass'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) hideSuitesWithout('test pass'); + }); + + // failure toggle + on(failuresLink, 'click', function(){ + unhide(); + var name = /fail/.test(report.className) ? '' : ' fail'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) hideSuitesWithout('test fail'); + }); + + root.appendChild(stat); + root.appendChild(report); + + if (progress) progress.size(40); + + runner.on('suite', function(suite){ + if (suite.root) return; + + // suite + var url = self.suiteURL(suite); + var el = fragment('
          • %s

          • ', url, escape(suite.title)); + + // container + stack[0].appendChild(el); + stack.unshift(document.createElement('ul')); + el.appendChild(stack[0]); + }); + + runner.on('suite end', function(suite){ + if (suite.root) return; + stack.shift(); + }); + + runner.on('fail', function(test, err){ + if ('hook' == test.type) runner.emit('test end', test); + }); + + runner.on('test end', function(test){ + // TODO: add to stats + var percent = stats.tests / this.total * 100 | 0; + if (progress) progress.update(percent).draw(ctx); + + // update stats + var ms = new Date - stats.start; + text(passes, stats.passes); + text(failures, stats.failures); + text(duration, (ms / 1000).toFixed(2)); + + // test + if ('passed' == test.state) { + var url = self.testURL(test); + var el = fragment('
          • %e%ems

          • ', test.speed, test.title, test.duration, url); + } else if (test.pending) { + var el = fragment('
          • %e

          • ', test.title); + } else { + var el = fragment('
          • %e

          • ', test.title, encodeURIComponent(test.fullTitle())); + var str = test.err.stack || test.err.toString(); + + // FF / Opera do not add the message + if (!~str.indexOf(test.err.message)) { + str = test.err.message + '\n' + str; + } + + // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we + // check for the result of the stringifying. + if ('[object Error]' == str) str = test.err.message; + + // Safari doesn't give you a stack. Let's at least provide a source line. + if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) { + str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")"; + } + + el.appendChild(fragment('
            %e
            ', str)); + } + + // toggle code + // TODO: defer + if (!test.pending) { + var h2 = el.getElementsByTagName('h2')[0]; + + on(h2, 'click', function(){ + pre.style.display = 'none' == pre.style.display + ? 'block' + : 'none'; + }); + + var pre = fragment('
            %e
            ', utils.clean(test.fn.toString())); + el.appendChild(pre); + pre.style.display = 'none'; + } + + // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. + if (stack[0]) stack[0].appendChild(el); + }); +} + +/** + * Provide suite URL + * + * @param {Object} [suite] + */ + +HTML.prototype.suiteURL = function(suite){ + return '?grep=' + encodeURIComponent(suite.fullTitle()); +}; + +/** + * Provide test URL + * + * @param {Object} [test] + */ + +HTML.prototype.testURL = function(test){ + return '?grep=' + encodeURIComponent(test.fullTitle()); +}; + +/** + * Display error `msg`. + */ + +function error(msg) { + document.body.appendChild(fragment('
            %s
            ', msg)); +} + +/** + * Return a DOM fragment from `html`. + */ + +function fragment(html) { + var args = arguments + , div = document.createElement('div') + , i = 1; + + div.innerHTML = html.replace(/%([se])/g, function(_, type){ + switch (type) { + case 's': return String(args[i++]); + case 'e': return escape(args[i++]); + } + }); + + return div.firstChild; +} + +/** + * Check for suites that do not have elements + * with `classname`, and hide them. + */ + +function hideSuitesWithout(classname) { + var suites = document.getElementsByClassName('suite'); + for (var i = 0; i < suites.length; i++) { + var els = suites[i].getElementsByClassName(classname); + if (0 == els.length) suites[i].className += ' hidden'; + } +} + +/** + * Unhide .hidden suites. + */ + +function unhide() { + var els = document.getElementsByClassName('suite hidden'); + for (var i = 0; i < els.length; ++i) { + els[i].className = els[i].className.replace('suite hidden', 'suite'); + } +} + +/** + * Set `el` text to `str`. + */ + +function text(el, str) { + if (el.textContent) { + el.textContent = str; + } else { + el.innerText = str; + } +} + +/** + * Listen on `event` with callback `fn`. + */ + +function on(el, event, fn) { + if (el.addEventListener) { + el.addEventListener(event, fn, false); + } else { + el.attachEvent('on' + event, fn); + } +} + +}); // module: reporters/html.js + +require.register("reporters/index.js", function(module, exports, require){ + +exports.Base = require('./base'); +exports.Dot = require('./dot'); +exports.Doc = require('./doc'); +exports.TAP = require('./tap'); +exports.JSON = require('./json'); +exports.HTML = require('./html'); +exports.List = require('./list'); +exports.Min = require('./min'); +exports.Spec = require('./spec'); +exports.Nyan = require('./nyan'); +exports.XUnit = require('./xunit'); +exports.Markdown = require('./markdown'); +exports.Progress = require('./progress'); +exports.Landing = require('./landing'); +exports.JSONCov = require('./json-cov'); +exports.HTMLCov = require('./html-cov'); +exports.JSONStream = require('./json-stream'); + +}); // module: reporters/index.js + +require.register("reporters/json-cov.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `JSONCov`. + */ + +exports = module.exports = JSONCov; + +/** + * Initialize a new `JsCoverage` reporter. + * + * @param {Runner} runner + * @param {Boolean} output + * @api public + */ + +function JSONCov(runner, output) { + var self = this + , output = 1 == arguments.length ? true : output; + + Base.call(this, runner); + + var tests = [] + , failures = [] + , passes = []; + + runner.on('test end', function(test){ + tests.push(test); + }); + + runner.on('pass', function(test){ + passes.push(test); + }); + + runner.on('fail', function(test){ + failures.push(test); + }); + + runner.on('end', function(){ + var cov = global._$jscoverage || {}; + var result = self.cov = map(cov); + result.stats = self.stats; + result.tests = tests.map(clean); + result.failures = failures.map(clean); + result.passes = passes.map(clean); + if (!output) return; + process.stdout.write(JSON.stringify(result, null, 2 )); + }); +} + +/** + * Map jscoverage data to a JSON structure + * suitable for reporting. + * + * @param {Object} cov + * @return {Object} + * @api private + */ + +function map(cov) { + var ret = { + instrumentation: 'node-jscoverage' + , sloc: 0 + , hits: 0 + , misses: 0 + , coverage: 0 + , files: [] + }; + + for (var filename in cov) { + var data = coverage(filename, cov[filename]); + ret.files.push(data); + ret.hits += data.hits; + ret.misses += data.misses; + ret.sloc += data.sloc; + } + + ret.files.sort(function(a, b) { + return a.filename.localeCompare(b.filename); + }); + + if (ret.sloc > 0) { + ret.coverage = (ret.hits / ret.sloc) * 100; + } + + return ret; +} + +/** + * Map jscoverage data for a single source file + * to a JSON structure suitable for reporting. + * + * @param {String} filename name of the source file + * @param {Object} data jscoverage coverage data + * @return {Object} + * @api private + */ + +function coverage(filename, data) { + var ret = { + filename: filename, + coverage: 0, + hits: 0, + misses: 0, + sloc: 0, + source: {} + }; + + data.source.forEach(function(line, num){ + num++; + + if (data[num] === 0) { + ret.misses++; + ret.sloc++; + } else if (data[num] !== undefined) { + ret.hits++; + ret.sloc++; + } + + ret.source[num] = { + source: line + , coverage: data[num] === undefined + ? '' + : data[num] + }; + }); + + ret.coverage = ret.hits / ret.sloc * 100; + + return ret; +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @param {Object} test + * @return {Object} + * @api private + */ + +function clean(test) { + return { + title: test.title + , fullTitle: test.fullTitle() + , duration: test.duration + } +} + +}); // module: reporters/json-cov.js + +require.register("reporters/json-stream.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , color = Base.color; + +/** + * Expose `List`. + */ + +exports = module.exports = List; + +/** + * Initialize a new `List` test reporter. + * + * @param {Runner} runner + * @api public + */ + +function List(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , total = runner.total; + + runner.on('start', function(){ + console.log(JSON.stringify(['start', { total: total }])); + }); + + runner.on('pass', function(test){ + console.log(JSON.stringify(['pass', clean(test)])); + }); + + runner.on('fail', function(test, err){ + console.log(JSON.stringify(['fail', clean(test)])); + }); + + runner.on('end', function(){ + process.stdout.write(JSON.stringify(['end', self.stats])); + }); +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @param {Object} test + * @return {Object} + * @api private + */ + +function clean(test) { + return { + title: test.title + , fullTitle: test.fullTitle() + , duration: test.duration + } +} +}); // module: reporters/json-stream.js + +require.register("reporters/json.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `JSON`. + */ + +exports = module.exports = JSONReporter; + +/** + * Initialize a new `JSON` reporter. + * + * @param {Runner} runner + * @api public + */ + +function JSONReporter(runner) { + var self = this; + Base.call(this, runner); + + var tests = [] + , pending = [] + , failures = [] + , passes = []; + + runner.on('test end', function(test){ + tests.push(test); + }); + + runner.on('pass', function(test){ + passes.push(test); + }); + + runner.on('fail', function(test){ + failures.push(test); + }); + + runner.on('pending', function(test){ + pending.push(test); + }); + + runner.on('end', function(){ + var obj = { + stats: self.stats, + tests: tests.map(clean), + pending: pending.map(clean), + failures: failures.map(clean), + passes: passes.map(clean) + }; + + runner.testResults = obj; + + process.stdout.write(JSON.stringify(obj, null, 2)); + }); +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @param {Object} test + * @return {Object} + * @api private + */ + +function clean(test) { + return { + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration, + err: errorJSON(test.err || {}) + } +} + +/** + * Transform `error` into a JSON object. + * @param {Error} err + * @return {Object} + */ + +function errorJSON(err) { + var res = {}; + Object.getOwnPropertyNames(err).forEach(function(key) { + res[key] = err[key]; + }, err); + return res; +} + +}); // module: reporters/json.js + +require.register("reporters/landing.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `Landing`. + */ + +exports = module.exports = Landing; + +/** + * Airplane color. + */ + +Base.colors.plane = 0; + +/** + * Airplane crash color. + */ + +Base.colors['plane crash'] = 31; + +/** + * Runway color. + */ + +Base.colors.runway = 90; + +/** + * Initialize a new `Landing` reporter. + * + * @param {Runner} runner + * @api public + */ + +function Landing(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , width = Base.window.width * .75 | 0 + , total = runner.total + , stream = process.stdout + , plane = color('plane', '✈') + , crashed = -1 + , n = 0; + + function runway() { + var buf = Array(width).join('-'); + return ' ' + color('runway', buf); + } + + runner.on('start', function(){ + stream.write('\n '); + cursor.hide(); + }); + + runner.on('test end', function(test){ + // check if the plane crashed + var col = -1 == crashed + ? width * ++n / total | 0 + : crashed; + + // show the crash + if ('failed' == test.state) { + plane = color('plane crash', '✈'); + crashed = col; + } + + // render landing strip + stream.write('\u001b[4F\n\n'); + stream.write(runway()); + stream.write('\n '); + stream.write(color('runway', Array(col).join('⋅'))); + stream.write(plane) + stream.write(color('runway', Array(width - col).join('⋅') + '\n')); + stream.write(runway()); + stream.write('\u001b[0m'); + }); + + runner.on('end', function(){ + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +Landing.prototype = new F; +Landing.prototype.constructor = Landing; + +}); // module: reporters/landing.js + +require.register("reporters/list.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `List`. + */ + +exports = module.exports = List; + +/** + * Initialize a new `List` test reporter. + * + * @param {Runner} runner + * @api public + */ + +function List(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , n = 0; + + runner.on('start', function(){ + console.log(); + }); + + runner.on('test', function(test){ + process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); + }); + + runner.on('pending', function(test){ + var fmt = color('checkmark', ' -') + + color('pending', ' %s'); + console.log(fmt, test.fullTitle()); + }); + + runner.on('pass', function(test){ + var fmt = color('checkmark', ' '+Base.symbols.dot) + + color('pass', ' %s: ') + + color(test.speed, '%dms'); + cursor.CR(); + console.log(fmt, test.fullTitle(), test.duration); + }); + + runner.on('fail', function(test, err){ + cursor.CR(); + console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); + }); + + runner.on('end', self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +List.prototype = new F; +List.prototype.constructor = List; + + +}); // module: reporters/list.js + +require.register("reporters/markdown.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , utils = require('../utils'); + +/** + * Expose `Markdown`. + */ + +exports = module.exports = Markdown; + +/** + * Initialize a new `Markdown` reporter. + * + * @param {Runner} runner + * @api public + */ + +function Markdown(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , level = 0 + , buf = ''; + + function title(str) { + return Array(level).join('#') + ' ' + str; + } + + function indent() { + return Array(level).join(' '); + } + + function mapTOC(suite, obj) { + var ret = obj; + obj = obj[suite.title] = obj[suite.title] || { suite: suite }; + suite.suites.forEach(function(suite){ + mapTOC(suite, obj); + }); + return ret; + } + + function stringifyTOC(obj, level) { + ++level; + var buf = ''; + var link; + for (var key in obj) { + if ('suite' == key) continue; + if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; + if (key) buf += Array(level).join(' ') + link; + buf += stringifyTOC(obj[key], level); + } + --level; + return buf; + } + + function generateTOC(suite) { + var obj = mapTOC(suite, {}); + return stringifyTOC(obj, 0); + } + + generateTOC(runner.suite); + + runner.on('suite', function(suite){ + ++level; + var slug = utils.slug(suite.fullTitle()); + buf += '' + '\n'; + buf += title(suite.title) + '\n'; + }); + + runner.on('suite end', function(suite){ + --level; + }); + + runner.on('pass', function(test){ + var code = utils.clean(test.fn.toString()); + buf += test.title + '.\n'; + buf += '\n```js\n'; + buf += code + '\n'; + buf += '```\n\n'; + }); + + runner.on('end', function(){ + process.stdout.write('# TOC\n'); + process.stdout.write(generateTOC(runner.suite)); + process.stdout.write(buf); + }); +} +}); // module: reporters/markdown.js + +require.register("reporters/min.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `Min`. + */ + +exports = module.exports = Min; + +/** + * Initialize a new `Min` minimal test reporter (best used with --watch). + * + * @param {Runner} runner + * @api public + */ + +function Min(runner) { + Base.call(this, runner); + + runner.on('start', function(){ + // clear screen + process.stdout.write('\u001b[2J'); + // set cursor position + process.stdout.write('\u001b[1;3H'); + }); + + runner.on('end', this.epilogue.bind(this)); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +Min.prototype = new F; +Min.prototype.constructor = Min; + + +}); // module: reporters/min.js + +require.register("reporters/nyan.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , color = Base.color; + +/** + * Expose `Dot`. + */ + +exports = module.exports = NyanCat; + +/** + * Initialize a new `Dot` matrix test reporter. + * + * @param {Runner} runner + * @api public + */ + +function NyanCat(runner) { + Base.call(this, runner); + var self = this + , stats = this.stats + , width = Base.window.width * .75 | 0 + , rainbowColors = this.rainbowColors = self.generateColors() + , colorIndex = this.colorIndex = 0 + , numerOfLines = this.numberOfLines = 4 + , trajectories = this.trajectories = [[], [], [], []] + , nyanCatWidth = this.nyanCatWidth = 11 + , trajectoryWidthMax = this.trajectoryWidthMax = (width - nyanCatWidth) + , scoreboardWidth = this.scoreboardWidth = 5 + , tick = this.tick = 0 + , n = 0; + + runner.on('start', function(){ + Base.cursor.hide(); + self.draw(); + }); + + runner.on('pending', function(test){ + self.draw(); + }); + + runner.on('pass', function(test){ + self.draw(); + }); + + runner.on('fail', function(test, err){ + self.draw(); + }); + + runner.on('end', function(){ + Base.cursor.show(); + for (var i = 0; i < self.numberOfLines; i++) write('\n'); + self.epilogue(); + }); +} + +/** + * Draw the nyan cat + * + * @api private + */ + +NyanCat.prototype.draw = function(){ + this.appendRainbow(); + this.drawScoreboard(); + this.drawRainbow(); + this.drawNyanCat(); + this.tick = !this.tick; +}; + +/** + * Draw the "scoreboard" showing the number + * of passes, failures and pending tests. + * + * @api private + */ + +NyanCat.prototype.drawScoreboard = function(){ + var stats = this.stats; + var colors = Base.colors; + + function draw(color, n) { + write(' '); + write('\u001b[' + color + 'm' + n + '\u001b[0m'); + write('\n'); + } + + draw(colors.green, stats.passes); + draw(colors.fail, stats.failures); + draw(colors.pending, stats.pending); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Append the rainbow. + * + * @api private + */ + +NyanCat.prototype.appendRainbow = function(){ + var segment = this.tick ? '_' : '-'; + var rainbowified = this.rainbowify(segment); + + for (var index = 0; index < this.numberOfLines; index++) { + var trajectory = this.trajectories[index]; + if (trajectory.length >= this.trajectoryWidthMax) trajectory.shift(); + trajectory.push(rainbowified); + } +}; + +/** + * Draw the rainbow. + * + * @api private + */ + +NyanCat.prototype.drawRainbow = function(){ + var self = this; + + this.trajectories.forEach(function(line, index) { + write('\u001b[' + self.scoreboardWidth + 'C'); + write(line.join('')); + write('\n'); + }); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw the nyan cat + * + * @api private + */ + +NyanCat.prototype.drawNyanCat = function() { + var self = this; + var startWidth = this.scoreboardWidth + this.trajectories[0].length; + var color = '\u001b[' + startWidth + 'C'; + var padding = ''; + + write(color); + write('_,------,'); + write('\n'); + + write(color); + padding = self.tick ? ' ' : ' '; + write('_|' + padding + '/\\_/\\ '); + write('\n'); + + write(color); + padding = self.tick ? '_' : '__'; + var tail = self.tick ? '~' : '^'; + var face; + write(tail + '|' + padding + this.face() + ' '); + write('\n'); + + write(color); + padding = self.tick ? ' ' : ' '; + write(padding + '"" "" '); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw nyan cat face. + * + * @return {String} + * @api private + */ + +NyanCat.prototype.face = function() { + var stats = this.stats; + if (stats.failures) { + return '( x .x)'; + } else if (stats.pending) { + return '( o .o)'; + } else if(stats.passes) { + return '( ^ .^)'; + } else { + return '( - .-)'; + } +}; + +/** + * Move cursor up `n`. + * + * @param {Number} n + * @api private + */ + +NyanCat.prototype.cursorUp = function(n) { + write('\u001b[' + n + 'A'); +}; + +/** + * Move cursor down `n`. + * + * @param {Number} n + * @api private + */ + +NyanCat.prototype.cursorDown = function(n) { + write('\u001b[' + n + 'B'); +}; + +/** + * Generate rainbow colors. + * + * @return {Array} + * @api private + */ + +NyanCat.prototype.generateColors = function(){ + var colors = []; + + for (var i = 0; i < (6 * 7); i++) { + var pi3 = Math.floor(Math.PI / 3); + var n = (i * (1.0 / 6)); + var r = Math.floor(3 * Math.sin(n) + 3); + var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); + var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); + colors.push(36 * r + 6 * g + b + 16); + } + + return colors; +}; + +/** + * Apply rainbow to the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +NyanCat.prototype.rainbowify = function(str){ + var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; + this.colorIndex += 1; + return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; +}; + +/** + * Stdout helper. + */ + +function write(string) { + process.stdout.write(string); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +NyanCat.prototype = new F; +NyanCat.prototype.constructor = NyanCat; + + +}); // module: reporters/nyan.js + +require.register("reporters/progress.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `Progress`. + */ + +exports = module.exports = Progress; + +/** + * General progress bar color. + */ + +Base.colors.progress = 90; + +/** + * Initialize a new `Progress` bar test reporter. + * + * @param {Runner} runner + * @param {Object} options + * @api public + */ + +function Progress(runner, options) { + Base.call(this, runner); + + var self = this + , options = options || {} + , stats = this.stats + , width = Base.window.width * .50 | 0 + , total = runner.total + , complete = 0 + , max = Math.max + , lastN = -1; + + // default chars + options.open = options.open || '['; + options.complete = options.complete || '▬'; + options.incomplete = options.incomplete || Base.symbols.dot; + options.close = options.close || ']'; + options.verbose = false; + + // tests started + runner.on('start', function(){ + console.log(); + cursor.hide(); + }); + + // tests complete + runner.on('test end', function(){ + complete++; + var incomplete = total - complete + , percent = complete / total + , n = width * percent | 0 + , i = width - n; + + if (lastN === n && !options.verbose) { + // Don't re-render the line if it hasn't changed + return; + } + lastN = n; + + cursor.CR(); + process.stdout.write('\u001b[J'); + process.stdout.write(color('progress', ' ' + options.open)); + process.stdout.write(Array(n).join(options.complete)); + process.stdout.write(Array(i).join(options.incomplete)); + process.stdout.write(color('progress', options.close)); + if (options.verbose) { + process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); + } + }); + + // tests are complete, output some stats + // and the failures if any + runner.on('end', function(){ + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +Progress.prototype = new F; +Progress.prototype.constructor = Progress; + + +}); // module: reporters/progress.js + +require.register("reporters/spec.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `Spec`. + */ + +exports = module.exports = Spec; + +/** + * Initialize a new `Spec` test reporter. + * + * @param {Runner} runner + * @api public + */ + +function Spec(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , indents = 0 + , n = 0; + + function indent() { + return Array(indents).join(' ') + } + + runner.on('start', function(){ + console.log(); + }); + + runner.on('suite', function(suite){ + ++indents; + console.log(color('suite', '%s%s'), indent(), suite.title); + }); + + runner.on('suite end', function(suite){ + --indents; + if (1 == indents) console.log(); + }); + + runner.on('pending', function(test){ + var fmt = indent() + color('pending', ' - %s'); + console.log(fmt, test.title); + }); + + runner.on('pass', function(test){ + if ('fast' == test.speed) { + var fmt = indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s '); + cursor.CR(); + console.log(fmt, test.title); + } else { + var fmt = indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s ') + + color(test.speed, '(%dms)'); + cursor.CR(); + console.log(fmt, test.title, test.duration); + } + }); + + runner.on('fail', function(test, err){ + cursor.CR(); + console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); + }); + + runner.on('end', self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +Spec.prototype = new F; +Spec.prototype.constructor = Spec; + + +}); // module: reporters/spec.js + +require.register("reporters/tap.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `TAP`. + */ + +exports = module.exports = TAP; + +/** + * Initialize a new `TAP` reporter. + * + * @param {Runner} runner + * @api public + */ + +function TAP(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , n = 1 + , passes = 0 + , failures = 0; + + runner.on('start', function(){ + var total = runner.grepTotal(runner.suite); + console.log('%d..%d', 1, total); + }); + + runner.on('test end', function(){ + ++n; + }); + + runner.on('pending', function(test){ + console.log('ok %d %s # SKIP -', n, title(test)); + }); + + runner.on('pass', function(test){ + passes++; + console.log('ok %d %s', n, title(test)); + }); + + runner.on('fail', function(test, err){ + failures++; + console.log('not ok %d %s', n, title(test)); + if (err.stack) console.log(err.stack.replace(/^/gm, ' ')); + }); + + runner.on('end', function(){ + console.log('# tests ' + (passes + failures)); + console.log('# pass ' + passes); + console.log('# fail ' + failures); + }); +} + +/** + * Return a TAP-safe title of `test` + * + * @param {Object} test + * @return {String} + * @api private + */ + +function title(test) { + return test.fullTitle().replace(/#/g, ''); +} + +}); // module: reporters/tap.js + +require.register("reporters/xunit.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Base = require('./base') + , utils = require('../utils') + , escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Expose `XUnit`. + */ + +exports = module.exports = XUnit; + +/** + * Initialize a new `XUnit` reporter. + * + * @param {Runner} runner + * @api public + */ + +function XUnit(runner) { + Base.call(this, runner); + var stats = this.stats + , tests = [] + , self = this; + + runner.on('pending', function(test){ + tests.push(test); + }); + + runner.on('pass', function(test){ + tests.push(test); + }); + + runner.on('fail', function(test){ + tests.push(test); + }); + + runner.on('end', function(){ + console.log(tag('testsuite', { + name: 'Mocha Tests' + , tests: stats.tests + , failures: stats.failures + , errors: stats.failures + , skipped: stats.tests - stats.failures - stats.passes + , timestamp: (new Date).toUTCString() + , time: (stats.duration / 1000) || 0 + }, false)); + + tests.forEach(test); + console.log(''); + }); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +XUnit.prototype = new F; +XUnit.prototype.constructor = XUnit; + + +/** + * Output tag for the given `test.` + */ + +function test(test) { + var attrs = { + classname: test.parent.fullTitle() + , name: test.title + , time: (test.duration / 1000) || 0 + }; + + if ('failed' == test.state) { + var err = test.err; + console.log(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack)))); + } else if (test.pending) { + console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); + } else { + console.log(tag('testcase', attrs, true) ); + } +} + +/** + * HTML tag helper. + */ + +function tag(name, attrs, close, content) { + var end = close ? '/>' : '>' + , pairs = [] + , tag; + + for (var key in attrs) { + pairs.push(key + '="' + escape(attrs[key]) + '"'); + } + + tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; + if (content) tag += content + ''; +} + +}); // module: reporters/xunit.js + +require.register("runnable.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var EventEmitter = require('browser/events').EventEmitter + , debug = require('browser/debug')('mocha:runnable') + , milliseconds = require('./ms'); + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Object#toString(). + */ + +var toString = Object.prototype.toString; + +/** + * Expose `Runnable`. + */ + +module.exports = Runnable; + +/** + * Initialize a new `Runnable` with the given `title` and callback `fn`. + * + * @param {String} title + * @param {Function} fn + * @api private + */ + +function Runnable(title, fn) { + this.title = title; + this.fn = fn; + this.async = fn && fn.length; + this.sync = ! this.async; + this._timeout = 2000; + this._slow = 75; + this._enableTimeouts = true; + this.timedOut = false; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +function F(){}; +F.prototype = EventEmitter.prototype; +Runnable.prototype = new F; +Runnable.prototype.constructor = Runnable; + + +/** + * Set & get timeout `ms`. + * + * @param {Number|String} ms + * @return {Runnable|Number} ms or self + * @api private + */ + +Runnable.prototype.timeout = function(ms){ + if (0 == arguments.length) return this._timeout; + if (ms === 0) this._enableTimeouts = false; + if ('string' == typeof ms) ms = milliseconds(ms); + debug('timeout %d', ms); + this._timeout = ms; + if (this.timer) this.resetTimeout(); + return this; +}; + +/** + * Set & get slow `ms`. + * + * @param {Number|String} ms + * @return {Runnable|Number} ms or self + * @api private + */ + +Runnable.prototype.slow = function(ms){ + if (0 === arguments.length) return this._slow; + if ('string' == typeof ms) ms = milliseconds(ms); + debug('timeout %d', ms); + this._slow = ms; + return this; +}; + +/** + * Set and & get timeout `enabled`. + * + * @param {Boolean} enabled + * @return {Runnable|Boolean} enabled or self + * @api private + */ + +Runnable.prototype.enableTimeouts = function(enabled){ + if (arguments.length === 0) return this._enableTimeouts; + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Return the full title generated by recursively + * concatenating the parent's full title. + * + * @return {String} + * @api public + */ + +Runnable.prototype.fullTitle = function(){ + return this.parent.fullTitle() + ' ' + this.title; +}; + +/** + * Clear the timeout. + * + * @api private + */ + +Runnable.prototype.clearTimeout = function(){ + clearTimeout(this.timer); +}; + +/** + * Inspect the runnable void of private properties. + * + * @return {String} + * @api private + */ + +Runnable.prototype.inspect = function(){ + return JSON.stringify(this, function(key, val){ + if ('_' == key[0]) return; + if ('parent' == key) return '#'; + if ('ctx' == key) return '#'; + return val; + }, 2); +}; + +/** + * Reset the timeout. + * + * @api private + */ + +Runnable.prototype.resetTimeout = function(){ + var self = this; + var ms = this.timeout() || 1e9; + + if (!this._enableTimeouts) return; + this.clearTimeout(); + this.timer = setTimeout(function(){ + if (!self._enableTimeouts) return; + self.callback(new Error('timeout of ' + ms + 'ms exceeded')); + self.timedOut = true; + }, ms); +}; + +/** + * Whitelist these globals for this test run + * + * @api private + */ +Runnable.prototype.globals = function(arr){ + var self = this; + this._allowedGlobals = arr; +}; + +/** + * Run the test and invoke `fn(err)`. + * + * @param {Function} fn + * @api private + */ + +Runnable.prototype.run = function(fn){ + var self = this + , start = new Date + , ctx = this.ctx + , finished + , emitted; + + // Some times the ctx exists but it is not runnable + if (ctx && ctx.runnable) ctx.runnable(this); + + // called multiple times + function multiple(err) { + if (emitted) return; + emitted = true; + self.emit('error', err || new Error('done() called multiple times')); + } + + // finished + function done(err) { + var ms = self.timeout(); + if (self.timedOut) return; + if (finished) return multiple(err); + self.clearTimeout(); + self.duration = new Date - start; + finished = true; + if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded'); + fn(err); + } + + // for .resetTimeout() + this.callback = done; + + // explicit async with `done` argument + if (this.async) { + this.resetTimeout(); + + try { + this.fn.call(ctx, function(err){ + if (err instanceof Error || toString.call(err) === "[object Error]") return done(err); + if (null != err) { + if (Object.prototype.toString.call(err) === '[object Object]') { + return done(new Error('done() invoked with non-Error: ' + JSON.stringify(err))); + } else { + return done(new Error('done() invoked with non-Error: ' + err)); + } + } + done(); + }); + } catch (err) { + done(err); + } + return; + } + + if (this.asyncOnly) { + return done(new Error('--async-only option in use without declaring `done()`')); + } + + // sync or promise-returning + try { + if (this.pending) { + done(); + } else { + callFn(this.fn); + } + } catch (err) { + done(err); + } + + function callFn(fn) { + var result = fn.call(ctx); + if (result && typeof result.then === 'function') { + self.resetTimeout(); + result + .then(function() { + done() + }, + function(reason) { + done(reason || new Error('Promise rejected with no or falsy reason')) + }); + } else { + done(); + } + } +}; + +}); // module: runnable.js + +require.register("runner.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var EventEmitter = require('browser/events').EventEmitter + , debug = require('browser/debug')('mocha:runner') + , Test = require('./test') + , utils = require('./utils') + , filter = utils.filter + , keys = utils.keys; + +/** + * Non-enumerable globals. + */ + +var globals = [ + 'setTimeout', + 'clearTimeout', + 'setInterval', + 'clearInterval', + 'XMLHttpRequest', + 'Date' +]; + +/** + * Expose `Runner`. + */ + +module.exports = Runner; + +/** + * Initialize a `Runner` for the given `suite`. + * + * Events: + * + * - `start` execution started + * - `end` execution complete + * - `suite` (suite) test suite execution started + * - `suite end` (suite) all tests (and sub-suites) have finished + * - `test` (test) test execution started + * - `test end` (test) test completed + * - `hook` (hook) hook execution started + * - `hook end` (hook) hook complete + * - `pass` (test) test passed + * - `fail` (test, err) test failed + * - `pending` (test) test pending + * + * @api public + */ + +function Runner(suite) { + var self = this; + this._globals = []; + this._abort = false; + this.suite = suite; + this.total = suite.total(); + this.failures = 0; + this.on('test end', function(test){ self.checkGlobals(test); }); + this.on('hook end', function(hook){ self.checkGlobals(hook); }); + this.grep(/.*/); + this.globals(this.globalProps().concat(extraGlobals())); +} + +/** + * Wrapper for setImmediate, process.nextTick, or browser polyfill. + * + * @param {Function} fn + * @api private + */ + +Runner.immediately = global.setImmediate || process.nextTick; + +/** + * Inherit from `EventEmitter.prototype`. + */ + +function F(){}; +F.prototype = EventEmitter.prototype; +Runner.prototype = new F; +Runner.prototype.constructor = Runner; + + +/** + * Run tests with full titles matching `re`. Updates runner.total + * with number of tests matched. + * + * @param {RegExp} re + * @param {Boolean} invert + * @return {Runner} for chaining + * @api public + */ + +Runner.prototype.grep = function(re, invert){ + debug('grep %s', re); + this._grep = re; + this._invert = invert; + this.total = this.grepTotal(this.suite); + return this; +}; + +/** + * Returns the number of tests matching the grep search for the + * given suite. + * + * @param {Suite} suite + * @return {Number} + * @api public + */ + +Runner.prototype.grepTotal = function(suite) { + var self = this; + var total = 0; + + suite.eachTest(function(test){ + var match = self._grep.test(test.fullTitle()); + if (self._invert) match = !match; + if (match) total++; + }); + + return total; +}; + +/** + * Return a list of global properties. + * + * @return {Array} + * @api private + */ + +Runner.prototype.globalProps = function() { + var props = utils.keys(global); + + // non-enumerables + for (var i = 0; i < globals.length; ++i) { + if (~utils.indexOf(props, globals[i])) continue; + props.push(globals[i]); + } + + return props; +}; + +/** + * Allow the given `arr` of globals. + * + * @param {Array} arr + * @return {Runner} for chaining + * @api public + */ + +Runner.prototype.globals = function(arr){ + if (0 == arguments.length) return this._globals; + debug('globals %j', arr); + this._globals = this._globals.concat(arr); + return this; +}; + +/** + * Check for global variable leaks. + * + * @api private + */ + +Runner.prototype.checkGlobals = function(test){ + if (this.ignoreLeaks) return; + var ok = this._globals; + + var globals = this.globalProps(); + var leaks; + + if (test) { + ok = ok.concat(test._allowedGlobals || []); + } + + if(this.prevGlobalsLength == globals.length) return; + this.prevGlobalsLength = globals.length; + + leaks = filterLeaks(ok, globals); + this._globals = this._globals.concat(leaks); + + if (leaks.length > 1) { + this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + '')); + } else if (leaks.length) { + this.fail(test, new Error('global leak detected: ' + leaks[0])); + } +}; + +/** + * Fail the given `test`. + * + * @param {Test} test + * @param {Error} err + * @api private + */ + +Runner.prototype.fail = function(test, err){ + ++this.failures; + test.state = 'failed'; + + if ('string' == typeof err) { + err = new Error('the string "' + err + '" was thrown, throw an Error :)'); + } + + this.emit('fail', test, err); +}; + +/** + * Fail the given `hook` with `err`. + * + * Hook failures work in the following pattern: + * - If bail, then exit + * - Failed `before` hook skips all tests in a suite and subsuites, + * but jumps to corresponding `after` hook + * - Failed `before each` hook skips remaining tests in a + * suite and jumps to corresponding `after each` hook, + * which is run only once + * - Failed `after` hook does not alter + * execution order + * - Failed `after each` hook skips remaining tests in a + * suite and subsuites, but executes other `after each` + * hooks + * + * @param {Hook} hook + * @param {Error} err + * @api private + */ + +Runner.prototype.failHook = function(hook, err){ + this.fail(hook, err); + if (this.suite.bail()) { + this.emit('end'); + } +}; + +/** + * Run hook `name` callbacks and then invoke `fn()`. + * + * @param {String} name + * @param {Function} function + * @api private + */ + +Runner.prototype.hook = function(name, fn){ + var suite = this.suite + , hooks = suite['_' + name] + , self = this + , timer; + + function next(i) { + var hook = hooks[i]; + if (!hook) return fn(); + if (self.failures && suite.bail()) return fn(); + self.currentRunnable = hook; + + hook.ctx.currentTest = self.test; + + self.emit('hook', hook); + + hook.on('error', function(err){ + self.failHook(hook, err); + }); + + hook.run(function(err){ + hook.removeAllListeners('error'); + var testError = hook.error(); + if (testError) self.fail(self.test, testError); + if (err) { + self.failHook(hook, err); + + // stop executing hooks, notify callee of hook err + return fn(err); + } + self.emit('hook end', hook); + delete hook.ctx.currentTest; + next(++i); + }); + } + + Runner.immediately(function(){ + next(0); + }); +}; + +/** + * Run hook `name` for the given array of `suites` + * in order, and callback `fn(err, errSuite)`. + * + * @param {String} name + * @param {Array} suites + * @param {Function} fn + * @api private + */ + +Runner.prototype.hooks = function(name, suites, fn){ + var self = this + , orig = this.suite; + + function next(suite) { + self.suite = suite; + + if (!suite) { + self.suite = orig; + return fn(); + } + + self.hook(name, function(err){ + if (err) { + var errSuite = self.suite; + self.suite = orig; + return fn(err, errSuite); + } + + next(suites.pop()); + }); + } + + next(suites.pop()); +}; + +/** + * Run hooks from the top level down. + * + * @param {String} name + * @param {Function} fn + * @api private + */ + +Runner.prototype.hookUp = function(name, fn){ + var suites = [this.suite].concat(this.parents()).reverse(); + this.hooks(name, suites, fn); +}; + +/** + * Run hooks from the bottom up. + * + * @param {String} name + * @param {Function} fn + * @api private + */ + +Runner.prototype.hookDown = function(name, fn){ + var suites = [this.suite].concat(this.parents()); + this.hooks(name, suites, fn); +}; + +/** + * Return an array of parent Suites from + * closest to furthest. + * + * @return {Array} + * @api private + */ + +Runner.prototype.parents = function(){ + var suite = this.suite + , suites = []; + while (suite = suite.parent) suites.push(suite); + return suites; +}; + +/** + * Run the current test and callback `fn(err)`. + * + * @param {Function} fn + * @api private + */ + +Runner.prototype.runTest = function(fn){ + var test = this.test + , self = this; + + if (this.asyncOnly) test.asyncOnly = true; + + try { + test.on('error', function(err){ + self.fail(test, err); + }); + test.run(fn); + } catch (err) { + fn(err); + } +}; + +/** + * Run tests in the given `suite` and invoke + * the callback `fn()` when complete. + * + * @param {Suite} suite + * @param {Function} fn + * @api private + */ + +Runner.prototype.runTests = function(suite, fn){ + var self = this + , tests = suite.tests.slice() + , test; + + + function hookErr(err, errSuite, after) { + // before/after Each hook for errSuite failed: + var orig = self.suite; + + // for failed 'after each' hook start from errSuite parent, + // otherwise start from errSuite itself + self.suite = after ? errSuite.parent : errSuite; + + if (self.suite) { + // call hookUp afterEach + self.hookUp('afterEach', function(err2, errSuite2) { + self.suite = orig; + // some hooks may fail even now + if (err2) return hookErr(err2, errSuite2, true); + // report error suite + fn(errSuite); + }); + } else { + // there is no need calling other 'after each' hooks + self.suite = orig; + fn(errSuite); + } + } + + function next(err, errSuite) { + // if we bail after first err + if (self.failures && suite._bail) return fn(); + + if (self._abort) return fn(); + + if (err) return hookErr(err, errSuite, true); + + // next test + test = tests.shift(); + + // all done + if (!test) return fn(); + + // grep + var match = self._grep.test(test.fullTitle()); + if (self._invert) match = !match; + if (!match) return next(); + + // pending + if (test.pending) { + self.emit('pending', test); + self.emit('test end', test); + return next(); + } + + // execute test and hook(s) + self.emit('test', self.test = test); + self.hookDown('beforeEach', function(err, errSuite){ + + if (err) return hookErr(err, errSuite, false); + + self.currentRunnable = self.test; + self.runTest(function(err){ + test = self.test; + + if (err) { + self.fail(test, err); + self.emit('test end', test); + return self.hookUp('afterEach', next); + } + + test.state = 'passed'; + self.emit('pass', test); + self.emit('test end', test); + self.hookUp('afterEach', next); + }); + }); + } + + this.next = next; + next(); +}; + +/** + * Run the given `suite` and invoke the + * callback `fn()` when complete. + * + * @param {Suite} suite + * @param {Function} fn + * @api private + */ + +Runner.prototype.runSuite = function(suite, fn){ + var total = this.grepTotal(suite) + , self = this + , i = 0; + + debug('run suite %s', suite.fullTitle()); + + if (!total) return fn(); + + this.emit('suite', this.suite = suite); + + function next(errSuite) { + if (errSuite) { + // current suite failed on a hook from errSuite + if (errSuite == suite) { + // if errSuite is current suite + // continue to the next sibling suite + return done(); + } else { + // errSuite is among the parents of current suite + // stop execution of errSuite and all sub-suites + return done(errSuite); + } + } + + if (self._abort) return done(); + + var curr = suite.suites[i++]; + if (!curr) return done(); + self.runSuite(curr, next); + } + + function done(errSuite) { + self.suite = suite; + self.hook('afterAll', function(){ + self.emit('suite end', suite); + fn(errSuite); + }); + } + + this.hook('beforeAll', function(err){ + if (err) return done(); + self.runTests(suite, next); + }); +}; + +/** + * Handle uncaught exceptions. + * + * @param {Error} err + * @api private + */ + +Runner.prototype.uncaught = function(err){ + if (err) { + debug('uncaught exception %s', err !== function () { + return this; + }.call(err) ? err : ( err.message || err )); + } else { + debug('uncaught undefined exception'); + err = new Error('Caught undefined error, did you throw without specifying what?'); + } + err.uncaught = true; + + var runnable = this.currentRunnable; + if (!runnable) return; + + var wasAlreadyDone = runnable.state; + this.fail(runnable, err); + + runnable.clearTimeout(); + + if (wasAlreadyDone) return; + + // recover from test + if ('test' == runnable.type) { + this.emit('test end', runnable); + this.hookUp('afterEach', this.next); + return; + } + + // bail on hooks + this.emit('end'); +}; + +/** + * Run the root suite and invoke `fn(failures)` + * on completion. + * + * @param {Function} fn + * @return {Runner} for chaining + * @api public + */ + +Runner.prototype.run = function(fn){ + var self = this + , fn = fn || function(){}; + + function uncaught(err){ + self.uncaught(err); + } + + debug('start'); + + // callback + this.on('end', function(){ + debug('end'); + process.removeListener('uncaughtException', uncaught); + fn(self.failures); + }); + + // run suites + this.emit('start'); + this.runSuite(this.suite, function(){ + debug('finished running'); + self.emit('end'); + }); + + // uncaught exception + process.on('uncaughtException', uncaught); + + return this; +}; + +/** + * Cleanly abort execution + * + * @return {Runner} for chaining + * @api public + */ +Runner.prototype.abort = function(){ + debug('aborting'); + this._abort = true; +}; + +/** + * Filter leaks with the given globals flagged as `ok`. + * + * @param {Array} ok + * @param {Array} globals + * @return {Array} + * @api private + */ + +function filterLeaks(ok, globals) { + return filter(globals, function(key){ + // Firefox and Chrome exposes iframes as index inside the window object + if (/^d+/.test(key)) return false; + + // in firefox + // if runner runs in an iframe, this iframe's window.getInterface method not init at first + // it is assigned in some seconds + if (global.navigator && /^getInterface/.test(key)) return false; + + // an iframe could be approached by window[iframeIndex] + // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak + if (global.navigator && /^\d+/.test(key)) return false; + + // Opera and IE expose global variables for HTML element IDs (issue #243) + if (/^mocha-/.test(key)) return false; + + var matched = filter(ok, function(ok){ + if (~ok.indexOf('*')) return 0 == key.indexOf(ok.split('*')[0]); + return key == ok; + }); + return matched.length == 0 && (!global.navigator || 'onerror' !== key); + }); +} + +/** + * Array of globals dependent on the environment. + * + * @return {Array} + * @api private + */ + + function extraGlobals() { + if (typeof(process) === 'object' && + typeof(process.version) === 'string') { + + var nodeVersion = process.version.split('.').reduce(function(a, v) { + return a << 8 | v; + }); + + // 'errno' was renamed to process._errno in v0.9.11. + + if (nodeVersion < 0x00090B) { + return ['errno']; + } + } + + return []; + } + +}); // module: runner.js + +require.register("suite.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var EventEmitter = require('browser/events').EventEmitter + , debug = require('browser/debug')('mocha:suite') + , milliseconds = require('./ms') + , utils = require('./utils') + , Hook = require('./hook'); + +/** + * Expose `Suite`. + */ + +exports = module.exports = Suite; + +/** + * Create a new `Suite` with the given `title` + * and parent `Suite`. When a suite with the + * same title is already present, that suite + * is returned to provide nicer reporter + * and more flexible meta-testing. + * + * @param {Suite} parent + * @param {String} title + * @return {Suite} + * @api public + */ + +exports.create = function(parent, title){ + var suite = new Suite(title, parent.ctx); + suite.parent = parent; + if (parent.pending) suite.pending = true; + title = suite.fullTitle(); + parent.addSuite(suite); + return suite; +}; + +/** + * Initialize a new `Suite` with the given + * `title` and `ctx`. + * + * @param {String} title + * @param {Context} ctx + * @api private + */ + +function Suite(title, parentContext) { + this.title = title; + var context = function() {}; + context.prototype = parentContext; + this.ctx = new context(); + this.suites = []; + this.tests = []; + this.pending = false; + this._beforeEach = []; + this._beforeAll = []; + this._afterEach = []; + this._afterAll = []; + this.root = !title; + this._timeout = 2000; + this._enableTimeouts = true; + this._slow = 75; + this._bail = false; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +function F(){}; +F.prototype = EventEmitter.prototype; +Suite.prototype = new F; +Suite.prototype.constructor = Suite; + + +/** + * Return a clone of this `Suite`. + * + * @return {Suite} + * @api private + */ + +Suite.prototype.clone = function(){ + var suite = new Suite(this.title); + debug('clone'); + suite.ctx = this.ctx; + suite.timeout(this.timeout()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + return suite; +}; + +/** + * Set timeout `ms` or short-hand such as "2s". + * + * @param {Number|String} ms + * @return {Suite|Number} for chaining + * @api private + */ + +Suite.prototype.timeout = function(ms){ + if (0 == arguments.length) return this._timeout; + if (ms === 0) this._enableTimeouts = false; + if ('string' == typeof ms) ms = milliseconds(ms); + debug('timeout %d', ms); + this._timeout = parseInt(ms, 10); + return this; +}; + +/** + * Set timeout `enabled`. + * + * @param {Boolean} enabled + * @return {Suite|Boolean} self or enabled + * @api private + */ + +Suite.prototype.enableTimeouts = function(enabled){ + if (arguments.length === 0) return this._enableTimeouts; + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Set slow `ms` or short-hand such as "2s". + * + * @param {Number|String} ms + * @return {Suite|Number} for chaining + * @api private + */ + +Suite.prototype.slow = function(ms){ + if (0 === arguments.length) return this._slow; + if ('string' == typeof ms) ms = milliseconds(ms); + debug('slow %d', ms); + this._slow = ms; + return this; +}; + +/** + * Sets whether to bail after first error. + * + * @parma {Boolean} bail + * @return {Suite|Number} for chaining + * @api private + */ + +Suite.prototype.bail = function(bail){ + if (0 == arguments.length) return this._bail; + debug('bail %s', bail); + this._bail = bail; + return this; +}; + +/** + * Run `fn(test[, done])` before running tests. + * + * @param {Function} fn + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.beforeAll = function(title, fn){ + if (this.pending) return this; + if ('function' === typeof title) { + fn = title; + title = fn.name; + } + title = '"before all" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._beforeAll.push(hook); + this.emit('beforeAll', hook); + return this; +}; + +/** + * Run `fn(test[, done])` after running tests. + * + * @param {Function} fn + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.afterAll = function(title, fn){ + if (this.pending) return this; + if ('function' === typeof title) { + fn = title; + title = fn.name; + } + title = '"after all" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._afterAll.push(hook); + this.emit('afterAll', hook); + return this; +}; + +/** + * Run `fn(test[, done])` before each test case. + * + * @param {Function} fn + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.beforeEach = function(title, fn){ + if (this.pending) return this; + if ('function' === typeof title) { + fn = title; + title = fn.name; + } + title = '"before each" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._beforeEach.push(hook); + this.emit('beforeEach', hook); + return this; +}; + +/** + * Run `fn(test[, done])` after each test case. + * + * @param {Function} fn + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.afterEach = function(title, fn){ + if (this.pending) return this; + if ('function' === typeof title) { + fn = title; + title = fn.name; + } + title = '"after each" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._afterEach.push(hook); + this.emit('afterEach', hook); + return this; +}; + +/** + * Add a test `suite`. + * + * @param {Suite} suite + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.addSuite = function(suite){ + suite.parent = this; + suite.timeout(this.timeout()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + this.suites.push(suite); + this.emit('suite', suite); + return this; +}; + +/** + * Add a `test` to this suite. + * + * @param {Test} test + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.addTest = function(test){ + test.parent = this; + test.timeout(this.timeout()); + test.enableTimeouts(this.enableTimeouts()); + test.slow(this.slow()); + test.ctx = this.ctx; + this.tests.push(test); + this.emit('test', test); + return this; +}; + +/** + * Return the full title generated by recursively + * concatenating the parent's full title. + * + * @return {String} + * @api public + */ + +Suite.prototype.fullTitle = function(){ + if (this.parent) { + var full = this.parent.fullTitle(); + if (full) return full + ' ' + this.title; + } + return this.title; +}; + +/** + * Return the total number of tests. + * + * @return {Number} + * @api public + */ + +Suite.prototype.total = function(){ + return utils.reduce(this.suites, function(sum, suite){ + return sum + suite.total(); + }, 0) + this.tests.length; +}; + +/** + * Iterates through each suite recursively to find + * all tests. Applies a function in the format + * `fn(test)`. + * + * @param {Function} fn + * @return {Suite} + * @api private + */ + +Suite.prototype.eachTest = function(fn){ + utils.forEach(this.tests, fn); + utils.forEach(this.suites, function(suite){ + suite.eachTest(fn); + }); + return this; +}; + +}); // module: suite.js + +require.register("test.js", function(module, exports, require){ + +/** + * Module dependencies. + */ + +var Runnable = require('./runnable'); + +/** + * Expose `Test`. + */ + +module.exports = Test; + +/** + * Initialize a new `Test` with the given `title` and callback `fn`. + * + * @param {String} title + * @param {Function} fn + * @api private + */ + +function Test(title, fn) { + Runnable.call(this, title, fn); + this.pending = !fn; + this.type = 'test'; +} + +/** + * Inherit from `Runnable.prototype`. + */ + +function F(){}; +F.prototype = Runnable.prototype; +Test.prototype = new F; +Test.prototype.constructor = Test; + + +}); // module: test.js + +require.register("utils.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var fs = require('browser/fs') + , path = require('browser/path') + , basename = path.basename + , exists = fs.existsSync || path.existsSync + , glob = require('browser/glob') + , join = path.join + , debug = require('browser/debug')('mocha:watch'); + +/** + * Ignored directories. + */ + +var ignore = ['node_modules', '.git']; + +/** + * Escape special characters in the given string of html. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function(html){ + return String(html) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); +}; + +/** + * Array#forEach (<=IE8) + * + * @param {Array} array + * @param {Function} fn + * @param {Object} scope + * @api private + */ + +exports.forEach = function(arr, fn, scope){ + for (var i = 0, l = arr.length; i < l; i++) + fn.call(scope, arr[i], i); +}; + +/** + * Array#map (<=IE8) + * + * @param {Array} array + * @param {Function} fn + * @param {Object} scope + * @api private + */ + +exports.map = function(arr, fn, scope){ + var result = []; + for (var i = 0, l = arr.length; i < l; i++) + result.push(fn.call(scope, arr[i], i)); + return result; +}; + +/** + * Array#indexOf (<=IE8) + * + * @parma {Array} arr + * @param {Object} obj to find index of + * @param {Number} start + * @api private + */ + +exports.indexOf = function(arr, obj, start){ + for (var i = start || 0, l = arr.length; i < l; i++) { + if (arr[i] === obj) + return i; + } + return -1; +}; + +/** + * Array#reduce (<=IE8) + * + * @param {Array} array + * @param {Function} fn + * @param {Object} initial value + * @api private + */ + +exports.reduce = function(arr, fn, val){ + var rval = val; + + for (var i = 0, l = arr.length; i < l; i++) { + rval = fn(rval, arr[i], i, arr); + } + + return rval; +}; + +/** + * Array#filter (<=IE8) + * + * @param {Array} array + * @param {Function} fn + * @api private + */ + +exports.filter = function(arr, fn){ + var ret = []; + + for (var i = 0, l = arr.length; i < l; i++) { + var val = arr[i]; + if (fn(val, i, arr)) ret.push(val); + } + + return ret; +}; + +/** + * Object.keys (<=IE8) + * + * @param {Object} obj + * @return {Array} keys + * @api private + */ + +exports.keys = Object.keys || function(obj) { + var keys = [] + , has = Object.prototype.hasOwnProperty // for `window` on <=IE8 + + for (var key in obj) { + if (has.call(obj, key)) { + keys.push(key); + } + } + + return keys; +}; + +/** + * Watch the given `files` for changes + * and invoke `fn(file)` on modification. + * + * @param {Array} files + * @param {Function} fn + * @api private + */ + +exports.watch = function(files, fn){ + var options = { interval: 100 }; + files.forEach(function(file){ + debug('file %s', file); + fs.watchFile(file, options, function(curr, prev){ + if (prev.mtime < curr.mtime) fn(file); + }); + }); +}; + +/** + * Ignored files. + */ + +function ignored(path){ + return !~ignore.indexOf(path); +} + +/** + * Lookup files in the given `dir`. + * + * @return {Array} + * @api private + */ + +exports.files = function(dir, ext, ret){ + ret = ret || []; + ext = ext || ['js']; + + var re = new RegExp('\\.(' + ext.join('|') + ')$'); + + fs.readdirSync(dir) + .filter(ignored) + .forEach(function(path){ + path = join(dir, path); + if (fs.statSync(path).isDirectory()) { + exports.files(path, ext, ret); + } else if (path.match(re)) { + ret.push(path); + } + }); + + return ret; +}; + +/** + * Compute a slug from the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.slug = function(str){ + return str + .toLowerCase() + .replace(/ +/g, '-') + .replace(/[^-\w]/g, ''); +}; + +/** + * Strip the function definition from `str`, + * and re-indent for pre whitespace. + */ + +exports.clean = function(str) { + str = str + .replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '') + .replace(/^function *\(.*\) *{|\(.*\) *=> *{?/, '') + .replace(/\s+\}$/, ''); + + var spaces = str.match(/^\n?( *)/)[1].length + , tabs = str.match(/^\n?(\t*)/)[1].length + , re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs ? tabs : spaces) + '}', 'gm'); + + str = str.replace(re, ''); + + return exports.trim(str); +}; + +/** + * Trim the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.trim = function(str){ + return str.replace(/^\s+|\s+$/g, ''); +}; + +/** + * Parse the given `qs`. + * + * @param {String} qs + * @return {Object} + * @api private + */ + +exports.parseQuery = function(qs){ + return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){ + var i = pair.indexOf('=') + , key = pair.slice(0, i) + , val = pair.slice(++i); + + obj[key] = decodeURIComponent(val); + return obj; + }, {}); +}; + +/** + * Highlight the given string of `js`. + * + * @param {String} js + * @return {String} + * @api private + */ + +function highlight(js) { + return js + .replace(//g, '>') + .replace(/\/\/(.*)/gm, '//$1') + .replace(/('.*?')/gm, '$1') + .replace(/(\d+\.\d+)/gm, '$1') + .replace(/(\d+)/gm, '$1') + .replace(/\bnew[ \t]+(\w+)/gm, 'new $1') + .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1') +} + +/** + * Highlight the contents of tag `name`. + * + * @param {String} name + * @api private + */ + +exports.highlightTags = function(name) { + var code = document.getElementById('mocha').getElementsByTagName(name); + for (var i = 0, len = code.length; i < len; ++i) { + code[i].innerHTML = highlight(code[i].innerHTML); + } +}; + + +/** + * Stringify `obj`. + * + * @param {Object} obj + * @return {String} + * @api private + */ + +exports.stringify = function(obj) { + if (obj instanceof RegExp) return obj.toString(); + return JSON.stringify(exports.canonicalize(obj), null, 2).replace(/,(\n|$)/g, '$1'); +}; + +/** + * Return a new object that has the keys in sorted order. + * @param {Object} obj + * @param {Array} [stack] + * @return {Object} + * @api private + */ + +exports.canonicalize = function(obj, stack) { + stack = stack || []; + + if (exports.indexOf(stack, obj) !== -1) return '[Circular]'; + + var canonicalizedObj; + + if ({}.toString.call(obj) === '[object Array]') { + stack.push(obj); + canonicalizedObj = exports.map(obj, function (item) { + return exports.canonicalize(item, stack); + }); + stack.pop(); + } else if (typeof obj === 'object' && obj !== null) { + stack.push(obj); + canonicalizedObj = {}; + exports.forEach(exports.keys(obj).sort(), function (key) { + canonicalizedObj[key] = exports.canonicalize(obj[key], stack); + }); + stack.pop(); + } else { + canonicalizedObj = obj; + } + + return canonicalizedObj; + }; + +/** + * Lookup file names at the given `path`. + */ +exports.lookupFiles = function lookupFiles(path, extensions, recursive) { + var files = []; + var re = new RegExp('\\.(' + extensions.join('|') + ')$'); + + if (!exists(path)) { + if (exists(path + '.js')) { + path += '.js'; + } else { + files = glob.sync(path); + if (!files.length) throw new Error("cannot resolve path (or pattern) '" + path + "'"); + return files; + } + } + + try { + var stat = fs.statSync(path); + if (stat.isFile()) return path; + } + catch (ignored) { + return; + } + + fs.readdirSync(path).forEach(function(file){ + file = join(path, file); + try { + var stat = fs.statSync(file); + if (stat.isDirectory()) { + if (recursive) { + files = files.concat(lookupFiles(file, extensions, recursive)); + } + return; + } + } + catch (ignored) { + return; + } + if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') return; + files.push(file); + }); + + return files; +}; + +}); // module: utils.js +// The global object is "self" in Web Workers. +var global = (function() { return this; })(); + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date; +var setTimeout = global.setTimeout; +var setInterval = global.setInterval; +var clearTimeout = global.clearTimeout; +var clearInterval = global.clearInterval; + +/** + * Node shims. + * + * These are meant only to allow + * mocha.js to run untouched, not + * to allow running node code in + * the browser. + */ + +var process = {}; +process.exit = function(status){}; +process.stdout = {}; + +var uncaughtExceptionHandlers = []; + +var originalOnerrorHandler = global.onerror; + +/** + * Remove uncaughtException listener. + * Revert to original onerror handler if previously defined. + */ + +process.removeListener = function(e, fn){ + if ('uncaughtException' == e) { + if (originalOnerrorHandler) { + global.onerror = originalOnerrorHandler; + } else { + global.onerror = function() {}; + } + var i = Mocha.utils.indexOf(uncaughtExceptionHandlers, fn); + if (i != -1) { uncaughtExceptionHandlers.splice(i, 1); } + } +}; + +/** + * Implements uncaughtException listener. + */ + +process.on = function(e, fn){ + if ('uncaughtException' == e) { + global.onerror = function(err, url, line){ + fn(new Error(err + ' (' + url + ':' + line + ')')); + return true; + }; + uncaughtExceptionHandlers.push(fn); + } +}; + +/** + * Expose mocha. + */ + +var Mocha = global.Mocha = require('mocha'), + mocha = global.mocha = new Mocha({ reporter: 'html' }); + +// The BDD UI is registered by default, but no UI will be functional in the +// browser without an explicit call to the overridden `mocha.ui` (see below). +// Ensure that this default UI does not expose its methods to the global scope. +mocha.suite.removeAllListeners('pre-require'); + +var immediateQueue = [] + , immediateTimeout; + +function timeslice() { + var immediateStart = new Date().getTime(); + while (immediateQueue.length && (new Date().getTime() - immediateStart) < 100) { + immediateQueue.shift()(); + } + if (immediateQueue.length) { + immediateTimeout = setTimeout(timeslice, 0); + } else { + immediateTimeout = null; + } +} + +/** + * High-performance override of Runner.immediately. + */ + +Mocha.Runner.immediately = function(callback) { + immediateQueue.push(callback); + if (!immediateTimeout) { + immediateTimeout = setTimeout(timeslice, 0); + } +}; + +/** + * Function to allow assertion libraries to throw errors directly into mocha. + * This is useful when running tests in a browser because window.onerror will + * only receive the 'message' attribute of the Error. + */ +mocha.throwError = function(err) { + Mocha.utils.forEach(uncaughtExceptionHandlers, function (fn) { + fn(err); + }); + throw err; +}; + +/** + * Override ui to ensure that the ui functions are initialized. + * Normally this would happen in Mocha.prototype.loadFiles. + */ + +mocha.ui = function(ui){ + Mocha.prototype.ui.call(this, ui); + this.suite.emit('pre-require', global, null, this); + return this; +}; + +/** + * Setup mocha with the given setting options. + */ + +mocha.setup = function(opts){ + if ('string' == typeof opts) opts = { ui: opts }; + for (var opt in opts) this[opt](opts[opt]); + return this; +}; + +/** + * Run mocha, returning the Runner. + */ + +mocha.run = function(fn){ + var options = mocha.options; + mocha.globals('location'); + + var query = Mocha.utils.parseQuery(global.location.search || ''); + if (query.grep) mocha.grep(query.grep); + if (query.invert) mocha.invert(); + + return Mocha.prototype.run.call(mocha, function(err){ + // The DOM Document is not available in Web Workers. + var document = global.document; + if (document && document.getElementById('mocha') && options.noHighlighting !== true) { + Mocha.utils.highlightTags('code'); + } + if (fn) fn(err); + }); +}; + +/** + * Expose the process shim. + */ + +Mocha.process = process; +})(); \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/.bin/jade b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/.bin/jade new file mode 120000 index 0000000..571fae7 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/.bin/jade @@ -0,0 +1 @@ +../jade/bin/jade \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/.bin/mkdirp b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/.bin/mkdirp new file mode 120000 index 0000000..017896c --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/.bin/mkdirp @@ -0,0 +1 @@ +../mkdirp/bin/cmd.js \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/commander/Readme.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/commander/Readme.md new file mode 100644 index 0000000..7bb60b2 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/commander/Readme.md @@ -0,0 +1,208 @@ +# Commander.js + + The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander). + + [![Build Status](https://api.travis-ci.org/visionmedia/commander.js.svg)](http://travis-ci.org/visionmedia/commander.js) + +## Installation + + $ npm install commander + +## Option parsing + + Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander'); + +program + .version('0.0.1') + .option('-p, --peppers', 'Add peppers') + .option('-P, --pineapple', 'Add pineapple') + .option('-b, --bbq', 'Add bbq sauce') + .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble') + .parse(process.argv); + +console.log('you ordered a pizza with:'); +if (program.peppers) console.log(' - peppers'); +if (program.pineapple) console.log(' - pineapple'); +if (program.bbq) console.log(' - bbq'); +console.log(' - %s cheese', program.cheese); +``` + + Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. + +## Automated --help + + The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free: + +``` + $ ./examples/pizza --help + + Usage: pizza [options] + + Options: + + -V, --version output the version number + -p, --peppers Add peppers + -P, --pineapple Add pineapple + -b, --bbq Add bbq sauce + -c, --cheese Add the specified type of cheese [marble] + -h, --help output usage information + +``` + +## Coercion + +```js +function range(val) { + return val.split('..').map(Number); +} + +function list(val) { + return val.split(','); +} + +function collect(val, memo) { + memo.push(val); + return memo; +} + +function increaseVerbosity(v, total) { + return total + 1; +} + +program + .version('0.0.1') + .usage('[options] ') + .option('-i, --integer ', 'An integer argument', parseInt) + .option('-f, --float ', 'A float argument', parseFloat) + .option('-r, --range ..', 'A range', range) + .option('-l, --list ', 'A list', list) + .option('-o, --optional [value]', 'An optional value') + .option('-c, --collect [value]', 'A repeatable value', collect, []) + .option('-v, --verbose', 'A value that can be increased', increaseVerbosity, 0) + .parse(process.argv); + +console.log(' int: %j', program.integer); +console.log(' float: %j', program.float); +console.log(' optional: %j', program.optional); +program.range = program.range || []; +console.log(' range: %j..%j', program.range[0], program.range[1]); +console.log(' list: %j', program.list); +console.log(' collect: %j', program.collect); +console.log(' verbosity: %j', program.verbose); +console.log(' args: %j', program.args); +``` + +## Custom help + + You can display arbitrary `-h, --help` information + by listening for "--help". Commander will automatically + exit once you are done so that the remainder of your program + does not execute causing undesired behaviours, for example + in the following executable "stuff" will not output when + `--help` is used. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('../'); + +function list(val) { + return val.split(',').map(Number); +} + +program + .version('0.0.1') + .option('-f, --foo', 'enable some foo') + .option('-b, --bar', 'enable some bar') + .option('-B, --baz', 'enable some baz'); + +// must be before .parse() since +// node's emit() is immediate + +program.on('--help', function(){ + console.log(' Examples:'); + console.log(''); + console.log(' $ custom-help --help'); + console.log(' $ custom-help -h'); + console.log(''); +}); + +program.parse(process.argv); + +console.log('stuff'); +``` + +yielding the following help output: + +``` + +Usage: custom-help [options] + +Options: + + -h, --help output usage information + -V, --version output the version number + -f, --foo enable some foo + -b, --bar enable some bar + -B, --baz enable some baz + +Examples: + + $ custom-help --help + $ custom-help -h + +``` + +## .outputHelp() + + Output help information without exiting. + +## .help() + + Output help information and exit immediately. + +## Links + + - [API documentation](http://visionmedia.github.com/commander.js/) + - [ascii tables](https://github.com/LearnBoost/cli-table) + - [progress bars](https://github.com/visionmedia/node-progress) + - [more progress bars](https://github.com/substack/node-multimeter) + - [examples](https://github.com/visionmedia/commander.js/tree/master/examples) + +## License + +(The MIT License) + +Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/commander/index.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/commander/index.js new file mode 100644 index 0000000..8378d19 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/commander/index.js @@ -0,0 +1,876 @@ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter; +var spawn = require('child_process').spawn; +var path = require('path'); +var dirname = path.dirname; +var basename = path.basename; + +/** + * Expose the root command. + */ + +exports = module.exports = new Command; + +/** + * Expose `Command`. + */ + +exports.Command = Command; + +/** + * Expose `Option`. + */ + +exports.Option = Option; + +/** + * Initialize a new `Option` with the given `flags` and `description`. + * + * @param {String} flags + * @param {String} description + * @api public + */ + +function Option(flags, description) { + this.flags = flags; + this.required = ~flags.indexOf('<'); + this.optional = ~flags.indexOf('['); + this.bool = !~flags.indexOf('-no-'); + flags = flags.split(/[ ,|]+/); + if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); + this.long = flags.shift(); + this.description = description || ''; +} + +/** + * Return option name. + * + * @return {String} + * @api private + */ + +Option.prototype.name = function(){ + return this.long + .replace('--', '') + .replace('no-', ''); +}; + +/** + * Check if `arg` matches the short or long flag. + * + * @param {String} arg + * @return {Boolean} + * @api private + */ + +Option.prototype.is = function(arg){ + return arg == this.short + || arg == this.long; +}; + +/** + * Initialize a new `Command`. + * + * @param {String} name + * @api public + */ + +function Command(name) { + this.commands = []; + this.options = []; + this._execs = []; + this._args = []; + this._name = name; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Command.prototype.__proto__ = EventEmitter.prototype; + +/** + * Add command `name`. + * + * The `.action()` callback is invoked when the + * command `name` is specified via __ARGV__, + * and the remaining arguments are applied to the + * function for access. + * + * When the `name` is "*" an un-matched command + * will be passed as the first arg, followed by + * the rest of __ARGV__ remaining. + * + * Examples: + * + * program + * .version('0.0.1') + * .option('-C, --chdir ', 'change the working directory') + * .option('-c, --config ', 'set config path. defaults to ./deploy.conf') + * .option('-T, --no-tests', 'ignore test hook') + * + * program + * .command('setup') + * .description('run remote setup commands') + * .action(function(){ + * console.log('setup'); + * }); + * + * program + * .command('exec ') + * .description('run the given remote command') + * .action(function(cmd){ + * console.log('exec "%s"', cmd); + * }); + * + * program + * .command('*') + * .description('deploy the given env') + * .action(function(env){ + * console.log('deploying "%s"', env); + * }); + * + * program.parse(process.argv); + * + * @param {String} name + * @param {String} [desc] + * @return {Command} the new command + * @api public + */ + +Command.prototype.command = function(name, desc) { + var args = name.split(/ +/); + var cmd = new Command(args.shift()); + if (desc) cmd.description(desc); + if (desc) this.executables = true; + if (desc) this._execs[cmd._name] = true; + this.commands.push(cmd); + cmd.parseExpectedArgs(args); + cmd.parent = this; + if (desc) return this; + return cmd; +}; + +/** + * Add an implicit `help [cmd]` subcommand + * which invokes `--help` for the given command. + * + * @api private + */ + +Command.prototype.addImplicitHelpCommand = function() { + this.command('help [cmd]', 'display help for [cmd]'); +}; + +/** + * Parse expected `args`. + * + * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`. + * + * @param {Array} args + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parseExpectedArgs = function(args){ + if (!args.length) return; + var self = this; + args.forEach(function(arg){ + switch (arg[0]) { + case '<': + self._args.push({ required: true, name: arg.slice(1, -1) }); + break; + case '[': + self._args.push({ required: false, name: arg.slice(1, -1) }); + break; + } + }); + return this; +}; + +/** + * Register callback `fn` for the command. + * + * Examples: + * + * program + * .command('help') + * .description('display verbose help') + * .action(function(){ + * // output help here + * }); + * + * @param {Function} fn + * @return {Command} for chaining + * @api public + */ + +Command.prototype.action = function(fn){ + var self = this; + var listener = function(args, unknown){ + // Parse any so-far unknown options + args = args || []; + unknown = unknown || []; + + var parsed = self.parseOptions(unknown); + + // Output help if necessary + outputHelpIfNecessary(self, parsed.unknown); + + // If there are still any unknown options, then we simply + // die, unless someone asked for help, in which case we give it + // to them, and then we die. + if (parsed.unknown.length > 0) { + self.unknownOption(parsed.unknown[0]); + } + + // Leftover arguments need to be pushed back. Fixes issue #56 + if (parsed.args.length) args = parsed.args.concat(args); + + self._args.forEach(function(arg, i){ + if (arg.required && null == args[i]) { + self.missingArgument(arg.name); + } + }); + + // Always append ourselves to the end of the arguments, + // to make sure we match the number of arguments the user + // expects + if (self._args.length) { + args[self._args.length] = self; + } else { + args.push(self); + } + + fn.apply(this, args); + }; + this.parent.on(this._name, listener); + if (this._alias) this.parent.on(this._alias, listener); + return this; +}; + +/** + * Define option with `flags`, `description` and optional + * coercion `fn`. + * + * The `flags` string should contain both the short and long flags, + * separated by comma, a pipe or space. The following are all valid + * all will output this way when `--help` is used. + * + * "-p, --pepper" + * "-p|--pepper" + * "-p --pepper" + * + * Examples: + * + * // simple boolean defaulting to false + * program.option('-p, --pepper', 'add pepper'); + * + * --pepper + * program.pepper + * // => Boolean + * + * // simple boolean defaulting to true + * program.option('-C, --no-cheese', 'remove cheese'); + * + * program.cheese + * // => true + * + * --no-cheese + * program.cheese + * // => false + * + * // required argument + * program.option('-C, --chdir ', 'change the working directory'); + * + * --chdir /tmp + * program.chdir + * // => "/tmp" + * + * // optional argument + * program.option('-c, --cheese [type]', 'add cheese [marble]'); + * + * @param {String} flags + * @param {String} description + * @param {Function|Mixed} fn or default + * @param {Mixed} defaultValue + * @return {Command} for chaining + * @api public + */ + +Command.prototype.option = function(flags, description, fn, defaultValue){ + var self = this + , option = new Option(flags, description) + , oname = option.name() + , name = camelcase(oname); + + // default as 3rd arg + if ('function' != typeof fn) defaultValue = fn, fn = null; + + // preassign default value only for --no-*, [optional], or + if (false == option.bool || option.optional || option.required) { + // when --no-* we make sure default is true + if (false == option.bool) defaultValue = true; + // preassign only if we have a default + if (undefined !== defaultValue) self[name] = defaultValue; + } + + // register the option + this.options.push(option); + + // when it's passed assign the value + // and conditionally invoke the callback + this.on(oname, function(val){ + // coercion + if (null !== val && fn) val = fn(val, undefined === self[name] ? defaultValue : self[name]); + + // unassigned or bool + if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) { + // if no value, bool true, and we have a default, then use it! + if (null == val) { + self[name] = option.bool + ? defaultValue || true + : false; + } else { + self[name] = val; + } + } else if (null !== val) { + // reassign + self[name] = val; + } + }); + + return this; +}; + +/** + * Parse `argv`, settings options and invoking commands when defined. + * + * @param {Array} argv + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parse = function(argv){ + // implicit help + if (this.executables) this.addImplicitHelpCommand(); + + // store raw args + this.rawArgs = argv; + + // guess name + this._name = this._name || basename(argv[1], '.js'); + + // process argv + var parsed = this.parseOptions(this.normalize(argv.slice(2))); + var args = this.args = parsed.args; + + var result = this.parseArgs(this.args, parsed.unknown); + + // executable sub-commands + var name = result.args[0]; + if (this._execs[name]) return this.executeSubCommand(argv, args, parsed.unknown); + + return result; +}; + +/** + * Execute a sub-command executable. + * + * @param {Array} argv + * @param {Array} args + * @param {Array} unknown + * @api private + */ + +Command.prototype.executeSubCommand = function(argv, args, unknown) { + args = args.concat(unknown); + + if (!args.length) this.help(); + if ('help' == args[0] && 1 == args.length) this.help(); + + // --help + if ('help' == args[0]) { + args[0] = args[1]; + args[1] = '--help'; + } + + // executable + var dir = dirname(argv[1]); + var bin = basename(argv[1], '.js') + '-' + args[0]; + + // check for ./ first + var local = path.join(dir, bin); + + // run it + args = args.slice(1); + args.unshift(local); + var proc = spawn('node', args, { stdio: 'inherit', customFds: [0, 1, 2] }); + proc.on('error', function(err){ + if (err.code == "ENOENT") { + console.error('\n %s(1) does not exist, try --help\n', bin); + } else if (err.code == "EACCES") { + console.error('\n %s(1) not executable. try chmod or run with root\n', bin); + } + }); + + this.runningCommand = proc; +}; + +/** + * Normalize `args`, splitting joined short flags. For example + * the arg "-abc" is equivalent to "-a -b -c". + * This also normalizes equal sign and splits "--abc=def" into "--abc def". + * + * @param {Array} args + * @return {Array} + * @api private + */ + +Command.prototype.normalize = function(args){ + var ret = [] + , arg + , lastOpt + , index; + + for (var i = 0, len = args.length; i < len; ++i) { + arg = args[i]; + i > 0 && (lastOpt = this.optionFor(args[i-1])); + + if (lastOpt && lastOpt.required) { + ret.push(arg); + } else if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) { + arg.slice(1).split('').forEach(function(c){ + ret.push('-' + c); + }); + } else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) { + ret.push(arg.slice(0, index), arg.slice(index + 1)); + } else { + ret.push(arg); + } + } + + return ret; +}; + +/** + * Parse command `args`. + * + * When listener(s) are available those + * callbacks are invoked, otherwise the "*" + * event is emitted and those actions are invoked. + * + * @param {Array} args + * @return {Command} for chaining + * @api private + */ + +Command.prototype.parseArgs = function(args, unknown){ + var cmds = this.commands + , len = cmds.length + , name; + + if (args.length) { + name = args[0]; + if (this.listeners(name).length) { + this.emit(args.shift(), args, unknown); + } else { + this.emit('*', args); + } + } else { + outputHelpIfNecessary(this, unknown); + + // If there were no args and we have unknown options, + // then they are extraneous and we need to error. + if (unknown.length > 0) { + this.unknownOption(unknown[0]); + } + } + + return this; +}; + +/** + * Return an option matching `arg` if any. + * + * @param {String} arg + * @return {Option} + * @api private + */ + +Command.prototype.optionFor = function(arg){ + for (var i = 0, len = this.options.length; i < len; ++i) { + if (this.options[i].is(arg)) { + return this.options[i]; + } + } +}; + +/** + * Parse options from `argv` returning `argv` + * void of these options. + * + * @param {Array} argv + * @return {Array} + * @api public + */ + +Command.prototype.parseOptions = function(argv){ + var args = [] + , len = argv.length + , literal + , option + , arg; + + var unknownOptions = []; + + // parse options + for (var i = 0; i < len; ++i) { + arg = argv[i]; + + // literal args after -- + if ('--' == arg) { + literal = true; + continue; + } + + if (literal) { + args.push(arg); + continue; + } + + // find matching Option + option = this.optionFor(arg); + + // option is defined + if (option) { + // requires arg + if (option.required) { + arg = argv[++i]; + if (null == arg) return this.optionMissingArgument(option); + this.emit(option.name(), arg); + // optional arg + } else if (option.optional) { + arg = argv[i+1]; + if (null == arg || ('-' == arg[0] && '-' != arg)) { + arg = null; + } else { + ++i; + } + this.emit(option.name(), arg); + // bool + } else { + this.emit(option.name()); + } + continue; + } + + // looks like an option + if (arg.length > 1 && '-' == arg[0]) { + unknownOptions.push(arg); + + // If the next argument looks like it might be + // an argument for this option, we pass it on. + // If it isn't, then it'll simply be ignored + if (argv[i+1] && '-' != argv[i+1][0]) { + unknownOptions.push(argv[++i]); + } + continue; + } + + // arg + args.push(arg); + } + + return { args: args, unknown: unknownOptions }; +}; + +/** + * Argument `name` is missing. + * + * @param {String} name + * @api private + */ + +Command.prototype.missingArgument = function(name){ + console.error(); + console.error(" error: missing required argument `%s'", name); + console.error(); + process.exit(1); +}; + +/** + * `Option` is missing an argument, but received `flag` or nothing. + * + * @param {String} option + * @param {String} flag + * @api private + */ + +Command.prototype.optionMissingArgument = function(option, flag){ + console.error(); + if (flag) { + console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag); + } else { + console.error(" error: option `%s' argument missing", option.flags); + } + console.error(); + process.exit(1); +}; + +/** + * Unknown option `flag`. + * + * @param {String} flag + * @api private + */ + +Command.prototype.unknownOption = function(flag){ + console.error(); + console.error(" error: unknown option `%s'", flag); + console.error(); + process.exit(1); +}; + + +/** + * Set the program version to `str`. + * + * This method auto-registers the "-V, --version" flag + * which will print the version number when passed. + * + * @param {String} str + * @param {String} flags + * @return {Command} for chaining + * @api public + */ + +Command.prototype.version = function(str, flags){ + if (0 == arguments.length) return this._version; + this._version = str; + flags = flags || '-V, --version'; + this.option(flags, 'output the version number'); + this.on('version', function(){ + console.log(str); + process.exit(0); + }); + return this; +}; + +/** + * Set the description `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.description = function(str){ + if (0 == arguments.length) return this._description; + this._description = str; + return this; +}; + +/** + * Set an alias for the command + * + * @param {String} alias + * @return {String|Command} + * @api public + */ + +Command.prototype.alias = function(alias){ + if (0 == arguments.length) return this._alias; + this._alias = alias; + return this; +}; + +/** + * Set / get the command usage `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.usage = function(str){ + var args = this._args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }); + + var usage = '[options' + + (this.commands.length ? '] [command' : '') + + ']' + + (this._args.length ? ' ' + args : ''); + + if (0 == arguments.length) return this._usage || usage; + this._usage = str; + + return this; +}; + +/** + * Return the largest option length. + * + * @return {Number} + * @api private + */ + +Command.prototype.largestOptionLength = function(){ + return this.options.reduce(function(max, option){ + return Math.max(max, option.flags.length); + }, 0); +}; + +/** + * Return help for options. + * + * @return {String} + * @api private + */ + +Command.prototype.optionHelp = function(){ + var width = this.largestOptionLength(); + + // Prepend the help information + return [pad('-h, --help', width) + ' ' + 'output usage information'] + .concat(this.options.map(function(option){ + return pad(option.flags, width) + + ' ' + option.description; + })) + .join('\n'); +}; + +/** + * Return command help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.commandHelp = function(){ + if (!this.commands.length) return ''; + return [ + '' + , ' Commands:' + , '' + , this.commands.map(function(cmd){ + var args = cmd._args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }).join(' '); + + return cmd._name + + (cmd._alias + ? '|' + cmd._alias + : '') + + (cmd.options.length + ? ' [options]' + : '') + ' ' + args + + (cmd.description() + ? '\n ' + cmd.description() + : '') + + '\n'; + }).join('\n').replace(/^/gm, ' ') + , '' + ].join('\n'); +}; + +/** + * Return program help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.helpInformation = function(){ + return [ + '' + , ' Usage: ' + this._name + + (this._alias + ? '|' + this._alias + : '') + + ' ' + this.usage() + , '' + this.commandHelp() + , ' Options:' + , '' + , '' + this.optionHelp().replace(/^/gm, ' ') + , '' + , '' + ].join('\n'); +}; + +/** + * Output help information for this command + * + * @api public + */ + +Command.prototype.outputHelp = function(){ + process.stdout.write(this.helpInformation()); + this.emit('--help'); +}; + +/** + * Output help information and exit. + * + * @api public + */ + +Command.prototype.help = function(){ + this.outputHelp(); + process.exit(); +}; + +/** + * Camel-case the given `flag` + * + * @param {String} flag + * @return {String} + * @api private + */ + +function camelcase(flag) { + return flag.split('-').reduce(function(str, word){ + return str + word[0].toUpperCase() + word.slice(1); + }); +} + +/** + * Pad `str` to `width`. + * + * @param {String} str + * @param {Number} width + * @return {String} + * @api private + */ + +function pad(str, width) { + var len = Math.max(0, width - str.length); + return str + Array(len + 1).join(' '); +} + +/** + * Output help information if necessary + * + * @param {Command} command to output help for + * @param {Array} array of options to search for -h or --help + * @api private + */ + +function outputHelpIfNecessary(cmd, options) { + options = options || []; + for (var i = 0; i < options.length; i++) { + if (options[i] == '--help' || options[i] == '-h') { + cmd.outputHelp(); + process.exit(0); + } + } +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/commander/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/commander/package.json new file mode 100644 index 0000000..6edde3b --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/commander/package.json @@ -0,0 +1,71 @@ +{ + "name": "commander", + "version": "2.3.0", + "description": "the complete solution for node.js command-line programs", + "keywords": [ + "command", + "option", + "parser", + "prompt", + "stdin" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/visionmedia/commander.js.git" + }, + "devDependencies": { + "should": ">= 0.0.1" + }, + "scripts": { + "test": "make test" + }, + "main": "index", + "engines": { + "node": ">= 0.6.x" + }, + "files": [ + "index.js" + ], + "gitHead": "7e9f407ec03d4371a478c2fe417db4998ecb6169", + "bugs": { + "url": "https://github.com/visionmedia/commander.js/issues" + }, + "homepage": "https://github.com/visionmedia/commander.js", + "_id": "commander@2.3.0", + "_shasum": "fd430e889832ec353b9acd1de217c11cb3eef873", + "_from": "commander@2.3.0", + "_npmVersion": "1.4.21", + "_npmUser": { + "name": "somekittens", + "email": "rkoutnik@gmail.com" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "somekittens", + "email": "rkoutnik@gmail.com" + }, + { + "name": "zhiyelee", + "email": "zhiyelee@gmail.com" + }, + { + "name": "thethomaseffect", + "email": "thethomaseffect@gmail.com" + } + ], + "dist": { + "shasum": "fd430e889832ec353b9acd1de217c11cb3eef873", + "tarball": "http://registry.npmjs.org/commander/-/commander-2.3.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/.jshintrc b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/.jshintrc new file mode 100644 index 0000000..299877f --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/.jshintrc @@ -0,0 +1,3 @@ +{ + "laxbreak": true +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/.npmignore b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/.npmignore new file mode 100644 index 0000000..7e6163d --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/.npmignore @@ -0,0 +1,6 @@ +support +test +examples +example +*.sock +dist diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/History.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/History.md new file mode 100644 index 0000000..79429ff --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/History.md @@ -0,0 +1,150 @@ + +2.0.0 / 2014-09-01 +================== + + * package: update "browserify" to v5.11.0 + * node: use stderr rather than stdout for logging (#29, @stephenmathieson) + +1.0.4 / 2014-07-15 +================== + + * dist: recompile + * example: remove `console.info()` log usage + * example: add "Content-Type" UTF-8 header to browser example + * browser: place %c marker after the space character + * browser: reset the "content" color via `color: inherit` + * browser: add colors support for Firefox >= v31 + * debug: prefer an instance `log()` function over the global one (#119) + * Readme: update documentation about styled console logs for FF v31 (#116, @wryk) + +1.0.3 / 2014-07-09 +================== + + * Add support for multiple wildcards in namespaces (#122, @seegno) + * browser: fix lint + +1.0.2 / 2014-06-10 +================== + + * browser: update color palette (#113, @gscottolson) + * common: make console logging function configurable (#108, @timoxley) + * node: fix %o colors on old node <= 0.8.x + * Makefile: find node path using shell/which (#109, @timoxley) + +1.0.1 / 2014-06-06 +================== + + * browser: use `removeItem()` to clear localStorage + * browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777) + * package: add "contributors" section + * node: fix comment typo + * README: list authors + +1.0.0 / 2014-06-04 +================== + + * make ms diff be global, not be scope + * debug: ignore empty strings in enable() + * node: make DEBUG_COLORS able to disable coloring + * *: export the `colors` array + * npmignore: don't publish the `dist` dir + * Makefile: refactor to use browserify + * package: add "browserify" as a dev dependency + * Readme: add Web Inspector Colors section + * node: reset terminal color for the debug content + * node: map "%o" to `util.inspect()` + * browser: map "%j" to `JSON.stringify()` + * debug: add custom "formatters" + * debug: use "ms" module for humanizing the diff + * Readme: add "bash" syntax highlighting + * browser: add Firebug color support + * browser: add colors for WebKit browsers + * node: apply log to `console` + * rewrite: abstract common logic for Node & browsers + * add .jshintrc file + +0.8.1 / 2014-04-14 +================== + + * package: re-add the "component" section + +0.8.0 / 2014-03-30 +================== + + * add `enable()` method for nodejs. Closes #27 + * change from stderr to stdout + * remove unnecessary index.js file + +0.7.4 / 2013-11-13 +================== + + * remove "browserify" key from package.json (fixes something in browserify) + +0.7.3 / 2013-10-30 +================== + + * fix: catch localStorage security error when cookies are blocked (Chrome) + * add debug(err) support. Closes #46 + * add .browser prop to package.json. Closes #42 + +0.7.2 / 2013-02-06 +================== + + * fix package.json + * fix: Mobile Safari (private mode) is broken with debug + * fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript + +0.7.1 / 2013-02-05 +================== + + * add repository URL to package.json + * add DEBUG_COLORED to force colored output + * add browserify support + * fix component. Closes #24 + +0.7.0 / 2012-05-04 +================== + + * Added .component to package.json + * Added debug.component.js build + +0.6.0 / 2012-03-16 +================== + + * Added support for "-" prefix in DEBUG [Vinay Pulim] + * Added `.enabled` flag to the node version [TooTallNate] + +0.5.0 / 2012-02-02 +================== + + * Added: humanize diffs. Closes #8 + * Added `debug.disable()` to the CS variant + * Removed padding. Closes #10 + * Fixed: persist client-side variant again. Closes #9 + +0.4.0 / 2012-02-01 +================== + + * Added browser variant support for older browsers [TooTallNate] + * Added `debug.enable('project:*')` to browser variant [TooTallNate] + * Added padding to diff (moved it to the right) + +0.3.0 / 2012-01-26 +================== + + * Added millisecond diff when isatty, otherwise UTC string + +0.2.0 / 2012-01-22 +================== + + * Added wildcard support + +0.1.0 / 2011-12-02 +================== + + * Added: remove colors unless stderr isatty [TooTallNate] + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/Makefile b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/Makefile new file mode 100644 index 0000000..b0bde6e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/Makefile @@ -0,0 +1,33 @@ + +# get Makefile directory name: http://stackoverflow.com/a/5982798/376773 +THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd) + +# BIN directory +BIN := $(THIS_DIR)/node_modules/.bin + +# applications +NODE ?= $(shell which node) +NPM ?= $(NODE) $(shell which npm) +BROWSERIFY ?= $(NODE) $(BIN)/browserify + +all: dist/debug.js + +install: node_modules + +clean: + @rm -rf node_modules dist + +dist: + @mkdir -p $@ + +dist/debug.js: node_modules browser.js debug.js dist + @$(BROWSERIFY) \ + --standalone debug \ + . > $@ + +node_modules: package.json + @NODE_ENV= $(NPM) install + @touch node_modules + +.PHONY: all install clean diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/Readme.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/Readme.md new file mode 100644 index 0000000..e59b9ad --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/Readme.md @@ -0,0 +1,156 @@ +# debug + + tiny node.js debugging utility modelled after node core's debugging technique. + +## Installation + +```bash +$ npm install debug +``` + +## Usage + + With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility. + +Example _app.js_: + +```js +var debug = require('debug')('http') + , http = require('http') + , name = 'My App'; + +// fake app + +debug('booting %s', name); + +http.createServer(function(req, res){ + debug(req.method + ' ' + req.url); + res.end('hello\n'); +}).listen(3000, function(){ + debug('listening'); +}); + +// fake worker of some kind + +require('./worker'); +``` + +Example _worker.js_: + +```js +var debug = require('debug')('worker'); + +setInterval(function(){ + debug('doing some work'); +}, 1000); +``` + + The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples: + + ![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png) + + ![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png) + +## Millisecond diff + + When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls. + + ![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png) + + When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below: + + ![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png) + +## Conventions + + If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". + +## Wildcards + + The `*` character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect.compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`. + + You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with "connect:". + +## Browser support + + Debug works in the browser as well, currently persisted by `localStorage`. For example if you have `worker:a` and `worker:b` as shown below, and wish to debug both type `debug.enable('worker:*')` in the console and refresh the page, this will remain until you disable with `debug.disable()`. + +```js +a = debug('worker:a'); +b = debug('worker:b'); + +setInterval(function(){ + a('doing some work'); +}, 1000); + +setInterval(function(){ + b('doing some work'); +}, 1200); +``` + +#### Web Inspector Colors + + Colors are also enabled on "Web Inspectors" that understand the `%c` formatting + option. These are WebKit web inspectors, Firefox ([since version + 31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/)) + and the Firebug plugin for Firefox (any version). + + Colored output looks something like: + + ![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png) + +### stderr vs stdout + +You can set an alternative logging method per-namespace by overriding the `log` method on a per-namespace or globally: + +Example _stderr.js_: + +```js +var debug = require('../'); +var log = debug('app:log'); + +// by default console.log is used +log('goes to stdout!'); + +var error = debug('app:error'); +// set this namespace to log via console.error +error.log = console.error.bind(console); // don't forget to bind to console! +error('goes to stderr'); +log('still goes to stdout!'); + +// set all output to go via console.warn +// overrides all per-namespace log settings +debug.log = console.warn.bind(console); +log('now goes to stderr via console.warn'); +error('still goes to stderr, but via console.warn now'); +``` + +## Authors + + - TJ Holowaychuk + - Nathan Rajlich + +## License + +(The MIT License) + +Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/browser.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/browser.js new file mode 100644 index 0000000..ce6369f --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/browser.js @@ -0,0 +1,147 @@ + +/** + * This is the web browser implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = require('./debug'); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; + +/** + * Colors. + */ + +exports.colors = [ + 'lightseagreen', + 'forestgreen', + 'goldenrod', + 'dodgerblue', + 'darkorchid', + 'crimson' +]; + +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + +function useColors() { + // is webkit? http://stackoverflow.com/a/16459606/376773 + return ('WebkitAppearance' in document.documentElement.style) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (window.console && (console.firebug || (console.exception && console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); +} + +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +exports.formatters.j = function(v) { + return JSON.stringify(v); +}; + + +/** + * Colorize log arguments if enabled. + * + * @api public + */ + +function formatArgs() { + var args = arguments; + var useColors = this.useColors; + + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); + + if (!useColors) return args; + + var c = 'color: ' + this.color; + args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); + + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); + return args; +} + +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + +function log() { + // This hackery is required for IE8, + // where the `console.log` function doesn't have 'apply' + return 'object' == typeof console + && 'function' == typeof console.log + && Function.prototype.apply.call(console.log, console, arguments); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + try { + if (null == namespaces) { + localStorage.removeItem('debug'); + } else { + localStorage.debug = namespaces; + } + } catch(e) {} +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + var r; + try { + r = localStorage.debug; + } catch(e) {} + return r; +} + +/** + * Enable namespaces listed in `localStorage.debug` initially. + */ + +exports.enable(load()); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/component.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/component.json new file mode 100644 index 0000000..db1ceed --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/component.json @@ -0,0 +1,19 @@ +{ + "name": "debug", + "repo": "visionmedia/debug", + "description": "small debugging utility", + "version": "2.0.0", + "keywords": [ + "debug", + "log", + "debugger" + ], + "main": "browser.js", + "scripts": [ + "browser.js", + "debug.js" + ], + "dependencies": { + "guille/ms.js": "0.6.1" + } +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/debug.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/debug.js new file mode 100644 index 0000000..7571a86 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/debug.js @@ -0,0 +1,197 @@ + +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = debug; +exports.coerce = coerce; +exports.disable = disable; +exports.enable = enable; +exports.enabled = enabled; +exports.humanize = require('ms'); + +/** + * The currently active debug mode names, and names to skip. + */ + +exports.names = []; +exports.skips = []; + +/** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lowercased letter, i.e. "n". + */ + +exports.formatters = {}; + +/** + * Previously assigned color. + */ + +var prevColor = 0; + +/** + * Previous log timestamp. + */ + +var prevTime; + +/** + * Select a color. + * + * @return {Number} + * @api private + */ + +function selectColor() { + return exports.colors[prevColor++ % exports.colors.length]; +} + +/** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + +function debug(namespace) { + + // define the `disabled` version + function disabled() { + } + disabled.enabled = false; + + // define the `enabled` version + function enabled() { + + var self = enabled; + + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + // add the `color` if not set + if (null == self.useColors) self.useColors = exports.useColors(); + if (null == self.color && self.useColors) self.color = selectColor(); + + var args = Array.prototype.slice.call(arguments); + + args[0] = exports.coerce(args[0]); + + if ('string' !== typeof args[0]) { + // anything else let's inspect with %o + args = ['%o'].concat(args); + } + + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); + + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + if ('function' === typeof exports.formatArgs) { + args = exports.formatArgs.apply(self, args); + } + var logFn = enabled.log || exports.log || console.log.bind(console); + logFn.apply(self, args); + } + enabled.enabled = true; + + var fn = exports.enabled(namespace) ? enabled : disabled; + + fn.namespace = namespace; + + return fn; +} + +/** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + +function enable(namespaces) { + exports.save(namespaces); + + var split = (namespaces || '').split(/[\s,]+/); + var len = split.length; + + for (var i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } + } +} + +/** + * Disable debug output. + * + * @api public + */ + +function disable() { + exports.enable(''); +} + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +function enabled(name) { + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; + } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; + } + } + return false; +} + +/** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node.js new file mode 100644 index 0000000..db86f64 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node.js @@ -0,0 +1,129 @@ + +/** + * Module dependencies. + */ + +var tty = require('tty'); +var util = require('util'); + +/** + * This is the Node.js implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = require('./debug'); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; + +/** + * Colors. + */ + +exports.colors = [6, 2, 3, 4, 5, 1]; + +/** + * Is stdout a TTY? Colored output is enabled when `true`. + */ + +function useColors() { + var debugColors = (process.env.DEBUG_COLORS || '').trim().toLowerCase(); + if (0 === debugColors.length) { + return tty.isatty(1); + } else { + return '0' !== debugColors + && 'no' !== debugColors + && 'false' !== debugColors + && 'disabled' !== debugColors; + } +} + +/** + * Map %o to `util.inspect()`, since Node doesn't do that out of the box. + */ + +var inspect = (4 === util.inspect.length ? + // node <= 0.8.x + function (v, colors) { + return util.inspect(v, void 0, void 0, colors); + } : + // node > 0.8.x + function (v, colors) { + return util.inspect(v, { colors: colors }); + } +); + +exports.formatters.o = function(v) { + return inspect(v, this.useColors) + .replace(/\s*\n\s*/g, ' '); +}; + +/** + * Adds ANSI color escape codes if enabled. + * + * @api public + */ + +function formatArgs() { + var args = arguments; + var useColors = this.useColors; + var name = this.namespace; + + if (useColors) { + var c = this.color; + + args[0] = ' \u001b[9' + c + 'm' + name + ' ' + + '\u001b[0m' + + args[0] + '\u001b[3' + c + 'm' + + ' +' + exports.humanize(this.diff) + '\u001b[0m'; + } else { + args[0] = new Date().toUTCString() + + ' ' + name + ' ' + args[0]; + } + return args; +} + +/** + * Invokes `console.error()` with the specified arguments. + */ + +function log() { + return console.error.apply(console, arguments); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + if (null == namespaces) { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } else { + process.env.DEBUG = namespaces; + } +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + return process.env.DEBUG; +} + +/** + * Enable namespaces listed in `process.env.DEBUG` initially. + */ + +exports.enable(load()); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node_modules/ms/.npmignore b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node_modules/ms/.npmignore new file mode 100644 index 0000000..d1aa0ce --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node_modules/ms/.npmignore @@ -0,0 +1,5 @@ +node_modules +test +History.md +Makefile +component.json diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node_modules/ms/README.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node_modules/ms/README.md new file mode 100644 index 0000000..d4ab12a --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node_modules/ms/README.md @@ -0,0 +1,33 @@ +# ms.js: miliseconds conversion utility + +```js +ms('1d') // 86400000 +ms('10h') // 36000000 +ms('2h') // 7200000 +ms('1m') // 60000 +ms('5s') // 5000 +ms('100') // 100 +``` + +```js +ms(60000) // "1m" +ms(2 * 60000) // "2m" +ms(ms('10 hours')) // "10h" +``` + +```js +ms(60000, { long: true }) // "1 minute" +ms(2 * 60000, { long: true }) // "2 minutes" +ms(ms('10 hours', { long: true })) // "10 hours" +``` + +- Node/Browser compatible. Published as `ms` in NPM. +- If a number is supplied to `ms`, a string with a unit is returned. +- If a string that contains the number is supplied, it returns it as +a number (e.g: it returns `100` for `'100'`). +- If you pass a string with a number and a valid unit, the number of +equivalent ms is returned. + +## License + +MIT \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node_modules/ms/index.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node_modules/ms/index.js new file mode 100644 index 0000000..c5847f8 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node_modules/ms/index.js @@ -0,0 +1,111 @@ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} options + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options){ + options = options || {}; + if ('string' == typeof val) return parse(val); + return options.long + ? long(val) + : short(val); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str); + if (!match) return; + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 's': + return n * s; + case 'ms': + return n; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function short(ms) { + if (ms >= d) return Math.round(ms / d) + 'd'; + if (ms >= h) return Math.round(ms / h) + 'h'; + if (ms >= m) return Math.round(ms / m) + 'm'; + if (ms >= s) return Math.round(ms / s) + 's'; + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function long(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) return; + if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; + return Math.ceil(ms / n) + ' ' + name + 's'; +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node_modules/ms/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node_modules/ms/package.json new file mode 100644 index 0000000..8334411 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/node_modules/ms/package.json @@ -0,0 +1,43 @@ +{ + "name": "ms", + "version": "0.6.2", + "description": "Tiny ms conversion utility", + "repository": { + "type": "git", + "url": "git://github.com/guille/ms.js.git" + }, + "main": "./index", + "devDependencies": { + "mocha": "*", + "expect.js": "*", + "serve": "*" + }, + "component": { + "scripts": { + "ms/index.js": "index.js" + } + }, + "bugs": { + "url": "https://github.com/guille/ms.js/issues" + }, + "_id": "ms@0.6.2", + "dist": { + "shasum": "d89c2124c6fdc1353d65a8b77bf1aac4b193708c", + "tarball": "http://registry.npmjs.org/ms/-/ms-0.6.2.tgz" + }, + "_from": "ms@0.6.2", + "_npmVersion": "1.2.30", + "_npmUser": { + "name": "rauchg", + "email": "rauchg@gmail.com" + }, + "maintainers": [ + { + "name": "rauchg", + "email": "rauchg@gmail.com" + } + ], + "directories": {}, + "_shasum": "d89c2124c6fdc1353d65a8b77bf1aac4b193708c", + "_resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/package.json new file mode 100644 index 0000000..e3c906e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/debug/package.json @@ -0,0 +1,70 @@ +{ + "name": "debug", + "version": "2.0.0", + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/debug.git" + }, + "description": "small debugging utility", + "keywords": [ + "debug", + "log", + "debugger" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "contributors": [ + { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net", + "url": "http://n8.io" + } + ], + "dependencies": { + "ms": "0.6.2" + }, + "devDependencies": { + "browserify": "5.11.0", + "mocha": "*" + }, + "main": "./node.js", + "browser": "./browser.js", + "component": { + "scripts": { + "debug/index.js": "browser.js", + "debug/debug.js": "debug.js" + } + }, + "gitHead": "c61ae82bde19c6fdedfc6684817ff7eb541ff029", + "bugs": { + "url": "https://github.com/visionmedia/debug/issues" + }, + "homepage": "https://github.com/visionmedia/debug", + "_id": "debug@2.0.0", + "scripts": {}, + "_shasum": "89bd9df6732b51256bc6705342bba02ed12131ef", + "_from": "debug@2.0.0", + "_npmVersion": "1.4.21", + "_npmUser": { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + } + ], + "dist": { + "shasum": "89bd9df6732b51256bc6705342bba02ed12131ef", + "tarball": "http://registry.npmjs.org/debug/-/debug-2.0.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/debug/-/debug-2.0.0.tgz" +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/diff/README.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/diff/README.md new file mode 100644 index 0000000..1bc32d5 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/diff/README.md @@ -0,0 +1,154 @@ +# jsdiff + +[![Build Status](https://secure.travis-ci.org/kpdecker/jsdiff.png)](http://travis-ci.org/kpdecker/jsdiff) + +A javascript text differencing implementation. + +Based on the algorithm proposed in +["An O(ND) Difference Algorithm and its Variations" (Myers, 1986)](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927). + +## Installation + + npm install diff + +or + + git clone git://github.com/kpdecker/jsdiff.git + +## API + +* `JsDiff.diffChars(oldStr, newStr)` - diffs two blocks of text, comparing character by character. + + Returns a list of change objects (See below). + +* `JsDiff.diffWords(oldStr, newStr)` - diffs two blocks of text, comparing word by word. + + Returns a list of change objects (See below). + +* `JsDiff.diffLines(oldStr, newStr)` - diffs two blocks of text, comparing line by line. + + Returns a list of change objects (See below). + +* `JsDiff.diffCss(oldStr, newStr)` - diffs two blocks of text, comparing CSS tokens. + + Returns a list of change objects (See below). + +* `JsDiff.createPatch(fileName, oldStr, newStr, oldHeader, newHeader)` - creates a unified diff patch. + + Parameters: + * `fileName` : String to be output in the filename sections of the patch + * `oldStr` : Original string value + * `newStr` : New string value + * `oldHeader` : Additional information to include in the old file header + * `newHeader` : Additional information to include in thew new file header + +* `JsDiff.applyPatch(oldStr, diffStr)` - applies a unified diff patch. + + Return a string containing new version of provided data. + +* `convertChangesToXML(changes)` - converts a list of changes to a serialized XML format + +### Change Objects +Many of the methods above return change objects. These objects are consist of the following fields: + +* `value`: Text content +* `added`: True if the value was inserted into the new string +* `removed`: True of the value was removed from the old string + +Note that some cases may omit a particular flag field. Comparison on the flag fields should always be done in a truthy or falsy manner. + +## Examples + +Basic example in Node + +```js +require('colors') +var jsdiff = require('diff'); + +var one = 'beep boop'; +var other = 'beep boob blah'; + +var diff = jsdiff.diffChars(one, other); + +diff.forEach(function(part){ + // green for additions, red for deletions + // grey for common parts + var color = part.added ? 'green' : + part.removed ? 'red' : 'grey'; + process.stderr.write(part.value[color]); +}); + +console.log() +``` +Running the above program should yield + +Node Example + +Basic example in a web page + +```html +
            
            +
            +
            +```
            +
            +Open the above .html file in a browser and you should see
            +
            +Node Example
            +
            +**[Full online demo](http://kpdecker.github.com/jsdiff)**
            +
            +## License
            +
            +Software License Agreement (BSD License)
            +
            +Copyright (c) 2009-2011, Kevin Decker kpdecker@gmail.com
            +
            +All rights reserved.
            +
            +Redistribution and use of this software in source and binary forms, with or without modification,
            +are permitted provided that the following conditions are met:
            +
            +* Redistributions of source code must retain the above
            +  copyright notice, this list of conditions and the
            +  following disclaimer.
            +
            +* Redistributions in binary form must reproduce the above
            +  copyright notice, this list of conditions and the
            +  following disclaimer in the documentation and/or other
            +  materials provided with the distribution.
            +
            +* Neither the name of Kevin Decker nor the names of its
            +  contributors may be used to endorse or promote products
            +  derived from this software without specific prior
            +  written permission.
            +
            +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
            +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
            +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
            +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
            +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
            +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
            +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
            +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
            +
            +
            +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/kpdecker/jsdiff/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
            +
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/diff/diff.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/diff/diff.js
            new file mode 100644
            index 0000000..c084609
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/diff/diff.js
            @@ -0,0 +1,369 @@
            +/* See LICENSE file for terms of use */
            +
            +/*
            + * Text diff implementation.
            + *
            + * This library supports the following APIS:
            + * JsDiff.diffChars: Character by character diff
            + * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace
            + * JsDiff.diffLines: Line based diff
            + *
            + * JsDiff.diffCss: Diff targeted at CSS content
            + *
            + * These methods are based on the implementation proposed in
            + * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986).
            + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927
            + */
            +var JsDiff = (function() {
            +  /*jshint maxparams: 5*/
            +  function clonePath(path) {
            +    return { newPos: path.newPos, components: path.components.slice(0) };
            +  }
            +  function removeEmpty(array) {
            +    var ret = [];
            +    for (var i = 0; i < array.length; i++) {
            +      if (array[i]) {
            +        ret.push(array[i]);
            +      }
            +    }
            +    return ret;
            +  }
            +  function escapeHTML(s) {
            +    var n = s;
            +    n = n.replace(/&/g, '&');
            +    n = n.replace(//g, '>');
            +    n = n.replace(/"/g, '"');
            +
            +    return n;
            +  }
            +
            +  var Diff = function(ignoreWhitespace) {
            +    this.ignoreWhitespace = ignoreWhitespace;
            +  };
            +  Diff.prototype = {
            +      diff: function(oldString, newString) {
            +        // Handle the identity case (this is due to unrolling editLength == 0
            +        if (newString === oldString) {
            +          return [{ value: newString }];
            +        }
            +        if (!newString) {
            +          return [{ value: oldString, removed: true }];
            +        }
            +        if (!oldString) {
            +          return [{ value: newString, added: true }];
            +        }
            +
            +        newString = this.tokenize(newString);
            +        oldString = this.tokenize(oldString);
            +
            +        var newLen = newString.length, oldLen = oldString.length;
            +        var maxEditLength = newLen + oldLen;
            +        var bestPath = [{ newPos: -1, components: [] }];
            +
            +        // Seed editLength = 0
            +        var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0);
            +        if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) {
            +          return bestPath[0].components;
            +        }
            +
            +        for (var editLength = 1; editLength <= maxEditLength; editLength++) {
            +          for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) {
            +            var basePath;
            +            var addPath = bestPath[diagonalPath-1],
            +                removePath = bestPath[diagonalPath+1];
            +            oldPos = (removePath ? removePath.newPos : 0) - diagonalPath;
            +            if (addPath) {
            +              // No one else is going to attempt to use this value, clear it
            +              bestPath[diagonalPath-1] = undefined;
            +            }
            +
            +            var canAdd = addPath && addPath.newPos+1 < newLen;
            +            var canRemove = removePath && 0 <= oldPos && oldPos < oldLen;
            +            if (!canAdd && !canRemove) {
            +              bestPath[diagonalPath] = undefined;
            +              continue;
            +            }
            +
            +            // Select the diagonal that we want to branch from. We select the prior
            +            // path whose position in the new string is the farthest from the origin
            +            // and does not pass the bounds of the diff graph
            +            if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) {
            +              basePath = clonePath(removePath);
            +              this.pushComponent(basePath.components, oldString[oldPos], undefined, true);
            +            } else {
            +              basePath = clonePath(addPath);
            +              basePath.newPos++;
            +              this.pushComponent(basePath.components, newString[basePath.newPos], true, undefined);
            +            }
            +
            +            var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath);
            +
            +            if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) {
            +              return basePath.components;
            +            } else {
            +              bestPath[diagonalPath] = basePath;
            +            }
            +          }
            +        }
            +      },
            +
            +      pushComponent: function(components, value, added, removed) {
            +        var last = components[components.length-1];
            +        if (last && last.added === added && last.removed === removed) {
            +          // We need to clone here as the component clone operation is just
            +          // as shallow array clone
            +          components[components.length-1] =
            +            {value: this.join(last.value, value), added: added, removed: removed };
            +        } else {
            +          components.push({value: value, added: added, removed: removed });
            +        }
            +      },
            +      extractCommon: function(basePath, newString, oldString, diagonalPath) {
            +        var newLen = newString.length,
            +            oldLen = oldString.length,
            +            newPos = basePath.newPos,
            +            oldPos = newPos - diagonalPath;
            +        while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) {
            +          newPos++;
            +          oldPos++;
            +
            +          this.pushComponent(basePath.components, newString[newPos], undefined, undefined);
            +        }
            +        basePath.newPos = newPos;
            +        return oldPos;
            +      },
            +
            +      equals: function(left, right) {
            +        var reWhitespace = /\S/;
            +        if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) {
            +          return true;
            +        } else {
            +          return left === right;
            +        }
            +      },
            +      join: function(left, right) {
            +        return left + right;
            +      },
            +      tokenize: function(value) {
            +        return value;
            +      }
            +  };
            +
            +  var CharDiff = new Diff();
            +
            +  var WordDiff = new Diff(true);
            +  var WordWithSpaceDiff = new Diff();
            +  WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) {
            +    return removeEmpty(value.split(/(\s+|\b)/));
            +  };
            +
            +  var CssDiff = new Diff(true);
            +  CssDiff.tokenize = function(value) {
            +    return removeEmpty(value.split(/([{}:;,]|\s+)/));
            +  };
            +
            +  var LineDiff = new Diff();
            +  LineDiff.tokenize = function(value) {
            +    var retLines = [],
            +        lines = value.split(/^/m);
            +
            +    for(var i = 0; i < lines.length; i++) {
            +      var line = lines[i],
            +          lastLine = lines[i - 1];
            +
            +      // Merge lines that may contain windows new lines
            +      if (line == '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') {
            +        retLines[retLines.length - 1] += '\n';
            +      } else if (line) {
            +        retLines.push(line);
            +      }
            +    }
            +
            +    return retLines;
            +  };
            +
            +  return {
            +    Diff: Diff,
            +
            +    diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); },
            +    diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); },
            +    diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); },
            +    diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); },
            +
            +    diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); },
            +
            +    createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) {
            +      var ret = [];
            +
            +      ret.push('Index: ' + fileName);
            +      ret.push('===================================================================');
            +      ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader));
            +      ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader));
            +
            +      var diff = LineDiff.diff(oldStr, newStr);
            +      if (!diff[diff.length-1].value) {
            +        diff.pop();   // Remove trailing newline add
            +      }
            +      diff.push({value: '', lines: []});   // Append an empty value to make cleanup easier
            +
            +      function contextLines(lines) {
            +        return lines.map(function(entry) { return ' ' + entry; });
            +      }
            +      function eofNL(curRange, i, current) {
            +        var last = diff[diff.length-2],
            +            isLast = i === diff.length-2,
            +            isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed);
            +
            +        // Figure out if this is the last line for the given file and missing NL
            +        if (!/\n$/.test(current.value) && (isLast || isLastOfType)) {
            +          curRange.push('\\ No newline at end of file');
            +        }
            +      }
            +
            +      var oldRangeStart = 0, newRangeStart = 0, curRange = [],
            +          oldLine = 1, newLine = 1;
            +      for (var i = 0; i < diff.length; i++) {
            +        var current = diff[i],
            +            lines = current.lines || current.value.replace(/\n$/, '').split('\n');
            +        current.lines = lines;
            +
            +        if (current.added || current.removed) {
            +          if (!oldRangeStart) {
            +            var prev = diff[i-1];
            +            oldRangeStart = oldLine;
            +            newRangeStart = newLine;
            +
            +            if (prev) {
            +              curRange = contextLines(prev.lines.slice(-4));
            +              oldRangeStart -= curRange.length;
            +              newRangeStart -= curRange.length;
            +            }
            +          }
            +          curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; }));
            +          eofNL(curRange, i, current);
            +
            +          if (current.added) {
            +            newLine += lines.length;
            +          } else {
            +            oldLine += lines.length;
            +          }
            +        } else {
            +          if (oldRangeStart) {
            +            // Close out any changes that have been output (or join overlapping)
            +            if (lines.length <= 8 && i < diff.length-2) {
            +              // Overlapping
            +              curRange.push.apply(curRange, contextLines(lines));
            +            } else {
            +              // end the range and output
            +              var contextSize = Math.min(lines.length, 4);
            +              ret.push(
            +                  '@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize)
            +                  + ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize)
            +                  + ' @@');
            +              ret.push.apply(ret, curRange);
            +              ret.push.apply(ret, contextLines(lines.slice(0, contextSize)));
            +              if (lines.length <= 4) {
            +                eofNL(ret, i, current);
            +              }
            +
            +              oldRangeStart = 0;  newRangeStart = 0; curRange = [];
            +            }
            +          }
            +          oldLine += lines.length;
            +          newLine += lines.length;
            +        }
            +      }
            +
            +      return ret.join('\n') + '\n';
            +    },
            +
            +    applyPatch: function(oldStr, uniDiff) {
            +      var diffstr = uniDiff.split('\n');
            +      var diff = [];
            +      var remEOFNL = false,
            +          addEOFNL = false;
            +
            +      for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) {
            +        if(diffstr[i][0] === '@') {
            +          var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/);
            +          diff.unshift({
            +            start:meh[3],
            +            oldlength:meh[2],
            +            oldlines:[],
            +            newlength:meh[4],
            +            newlines:[]
            +          });
            +        } else if(diffstr[i][0] === '+') {
            +          diff[0].newlines.push(diffstr[i].substr(1));
            +        } else if(diffstr[i][0] === '-') {
            +          diff[0].oldlines.push(diffstr[i].substr(1));
            +        } else if(diffstr[i][0] === ' ') {
            +          diff[0].newlines.push(diffstr[i].substr(1));
            +          diff[0].oldlines.push(diffstr[i].substr(1));
            +        } else if(diffstr[i][0] === '\\') {
            +          if (diffstr[i-1][0] === '+') {
            +            remEOFNL = true;
            +          } else if(diffstr[i-1][0] === '-') {
            +            addEOFNL = true;
            +          }
            +        }
            +      }
            +
            +      var str = oldStr.split('\n');
            +      for (var i = diff.length - 1; i >= 0; i--) {
            +        var d = diff[i];
            +        for (var j = 0; j < d.oldlength; j++) {
            +          if(str[d.start-1+j] !== d.oldlines[j]) {
            +            return false;
            +          }
            +        }
            +        Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines));
            +      }
            +
            +      if (remEOFNL) {
            +        while (!str[str.length-1]) {
            +          str.pop();
            +        }
            +      } else if (addEOFNL) {
            +        str.push('');
            +      }
            +      return str.join('\n');
            +    },
            +
            +    convertChangesToXML: function(changes){
            +      var ret = [];
            +      for ( var i = 0; i < changes.length; i++) {
            +        var change = changes[i];
            +        if (change.added) {
            +          ret.push('');
            +        } else if (change.removed) {
            +          ret.push('');
            +        }
            +
            +        ret.push(escapeHTML(change.value));
            +
            +        if (change.added) {
            +          ret.push('');
            +        } else if (change.removed) {
            +          ret.push('');
            +        }
            +      }
            +      return ret.join('');
            +    },
            +
            +    // See: http://code.google.com/p/google-diff-match-patch/wiki/API
            +    convertChangesToDMP: function(changes){
            +      var ret = [], change;
            +      for ( var i = 0; i < changes.length; i++) {
            +        change = changes[i];
            +        ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]);
            +      }
            +      return ret;
            +    }
            +  };
            +})();
            +
            +if (typeof module !== 'undefined') {
            +    module.exports = JsDiff;
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/diff/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/diff/package.json
            new file mode 100644
            index 0000000..4b90cde
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/diff/package.json
            @@ -0,0 +1,60 @@
            +{
            +  "name": "diff",
            +  "version": "1.0.8",
            +  "description": "A javascript text diff implementation.",
            +  "keywords": [
            +    "diff",
            +    "javascript"
            +  ],
            +  "maintainers": [
            +    {
            +      "name": "kpdecker",
            +      "email": "kpdecker@gmail.com"
            +    }
            +  ],
            +  "bugs": {
            +    "url": "http://github.com/kpdecker/jsdiff/issues",
            +    "email": "kpdecker@gmail.com"
            +  },
            +  "licenses": [
            +    {
            +      "type": "BSD",
            +      "url": "http://github.com/kpdecker/jsdiff/blob/master/LICENSE"
            +    }
            +  ],
            +  "repository": {
            +    "type": "git",
            +    "url": "git://github.com/kpdecker/jsdiff.git"
            +  },
            +  "engines": {
            +    "node": ">=0.3.1"
            +  },
            +  "main": "./diff",
            +  "scripts": {
            +    "test": "node_modules/.bin/mocha test/*.js"
            +  },
            +  "dependencies": {},
            +  "devDependencies": {
            +    "mocha": "~1.6",
            +    "should": "~1.2",
            +    "colors": "~0.6.2"
            +  },
            +  "optionalDependencies": {},
            +  "files": [
            +    "diff.js"
            +  ],
            +  "_id": "diff@1.0.8",
            +  "dist": {
            +    "shasum": "343276308ec991b7bc82267ed55bc1411f971666",
            +    "tarball": "http://registry.npmjs.org/diff/-/diff-1.0.8.tgz"
            +  },
            +  "_from": "diff@1.0.8",
            +  "_npmVersion": "1.3.11",
            +  "_npmUser": {
            +    "name": "kpdecker",
            +    "email": "kpdecker@gmail.com"
            +  },
            +  "directories": {},
            +  "_shasum": "343276308ec991b7bc82267ed55bc1411f971666",
            +  "_resolved": "https://registry.npmjs.org/diff/-/diff-1.0.8.tgz"
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/escape-string-regexp/index.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/escape-string-regexp/index.js
            new file mode 100644
            index 0000000..ac6572c
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/escape-string-regexp/index.js
            @@ -0,0 +1,11 @@
            +'use strict';
            +
            +var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
            +
            +module.exports = function (str) {
            +	if (typeof str !== 'string') {
            +		throw new TypeError('Expected a string');
            +	}
            +
            +	return str.replace(matchOperatorsRe,  '\\$&');
            +};
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/escape-string-regexp/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/escape-string-regexp/package.json
            new file mode 100644
            index 0000000..714b098
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/escape-string-regexp/package.json
            @@ -0,0 +1,69 @@
            +{
            +  "name": "escape-string-regexp",
            +  "version": "1.0.2",
            +  "description": "Escape RegExp special characters",
            +  "license": "MIT",
            +  "repository": {
            +    "type": "git",
            +    "url": "git+https://github.com/sindresorhus/escape-string-regexp.git"
            +  },
            +  "author": {
            +    "name": "Sindre Sorhus",
            +    "email": "sindresorhus@gmail.com",
            +    "url": "http://sindresorhus.com"
            +  },
            +  "engines": {
            +    "node": ">=0.8.0"
            +  },
            +  "scripts": {
            +    "test": "mocha"
            +  },
            +  "files": [
            +    "index.js"
            +  ],
            +  "keywords": [
            +    "regex",
            +    "regexp",
            +    "re",
            +    "regular",
            +    "expression",
            +    "escape",
            +    "string",
            +    "str",
            +    "special",
            +    "characters"
            +  ],
            +  "devDependencies": {
            +    "mocha": "*"
            +  },
            +  "gitHead": "0587ee0ee03ea3fcbfa3c15cf67b47f214e20987",
            +  "bugs": {
            +    "url": "https://github.com/sindresorhus/escape-string-regexp/issues"
            +  },
            +  "homepage": "https://github.com/sindresorhus/escape-string-regexp",
            +  "_id": "escape-string-regexp@1.0.2",
            +  "_shasum": "4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1",
            +  "_from": "escape-string-regexp@1.0.2",
            +  "_npmVersion": "1.4.23",
            +  "_npmUser": {
            +    "name": "jbnicolai",
            +    "email": "jappelman@xebia.com"
            +  },
            +  "maintainers": [
            +    {
            +      "name": "sindresorhus",
            +      "email": "sindresorhus@gmail.com"
            +    },
            +    {
            +      "name": "jbnicolai",
            +      "email": "jappelman@xebia.com"
            +    }
            +  ],
            +  "dist": {
            +    "shasum": "4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1",
            +    "tarball": "http://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz"
            +  },
            +  "directories": {},
            +  "_resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz",
            +  "readme": "ERROR: No README data found!"
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/escape-string-regexp/readme.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/escape-string-regexp/readme.md
            new file mode 100644
            index 0000000..808a963
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/escape-string-regexp/readme.md
            @@ -0,0 +1,27 @@
            +# escape-string-regexp [![Build Status](https://travis-ci.org/sindresorhus/escape-string-regexp.svg?branch=master)](https://travis-ci.org/sindresorhus/escape-string-regexp)
            +
            +> Escape RegExp special characters
            +
            +
            +## Install
            +
            +```sh
            +$ npm install --save escape-string-regexp
            +```
            +
            +
            +## Usage
            +
            +```js
            +var escapeStringRegexp = require('escape-string-regexp');
            +
            +var escapedString = escapeStringRegexp('how much $ for a unicorn?');
            +//=> how much \$ for a unicorn\?
            +
            +new RegExp(escapedString);
            +```
            +
            +
            +## License
            +
            +MIT © [Sindre Sorhus](http://sindresorhus.com)
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/.npmignore b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/.npmignore
            new file mode 100644
            index 0000000..2af4b71
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/.npmignore
            @@ -0,0 +1,2 @@
            +.*.swp
            +test/a/
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/.travis.yml b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/.travis.yml
            new file mode 100644
            index 0000000..baa0031
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/.travis.yml
            @@ -0,0 +1,3 @@
            +language: node_js
            +node_js:
            +  - 0.8
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/LICENSE b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/LICENSE
            new file mode 100644
            index 0000000..0c44ae7
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/LICENSE
            @@ -0,0 +1,27 @@
            +Copyright (c) Isaac Z. Schlueter ("Author")
            +All rights reserved.
            +
            +The BSD License
            +
            +Redistribution and use in source and binary forms, with or without
            +modification, are permitted provided that the following conditions
            +are met:
            +
            +1. Redistributions of source code must retain the above copyright
            +   notice, this list of conditions and the following disclaimer.
            +
            +2. Redistributions in binary form must reproduce the above copyright
            +   notice, this list of conditions and the following disclaimer in the
            +   documentation and/or other materials provided with the distribution.
            +
            +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
            +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
            +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
            +PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
            +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
            +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
            +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
            +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
            +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
            +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
            +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/README.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/README.md
            new file mode 100644
            index 0000000..cc69164
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/README.md
            @@ -0,0 +1,250 @@
            +# Glob
            +
            +Match files using the patterns the shell uses, like stars and stuff.
            +
            +This is a glob implementation in JavaScript.  It uses the `minimatch`
            +library to do its matching.
            +
            +## Attention: node-glob users!
            +
            +The API has changed dramatically between 2.x and 3.x. This library is
            +now 100% JavaScript, and the integer flags have been replaced with an
            +options object.
            +
            +Also, there's an event emitter class, proper tests, and all the other
            +things you've come to expect from node modules.
            +
            +And best of all, no compilation!
            +
            +## Usage
            +
            +```javascript
            +var glob = require("glob")
            +
            +// options is optional
            +glob("**/*.js", options, function (er, files) {
            +  // files is an array of filenames.
            +  // If the `nonull` option is set, and nothing
            +  // was found, then files is ["**/*.js"]
            +  // er is an error object or null.
            +})
            +```
            +
            +## Features
            +
            +Please see the [minimatch
            +documentation](https://github.com/isaacs/minimatch) for more details.
            +
            +Supports these glob features:
            +
            +* Brace Expansion
            +* Extended glob matching
            +* "Globstar" `**` matching
            +
            +See:
            +
            +* `man sh`
            +* `man bash`
            +* `man 3 fnmatch`
            +* `man 5 gitignore`
            +* [minimatch documentation](https://github.com/isaacs/minimatch)
            +
            +## glob(pattern, [options], cb)
            +
            +* `pattern` {String} Pattern to be matched
            +* `options` {Object}
            +* `cb` {Function}
            +  * `err` {Error | null}
            +  * `matches` {Array} filenames found matching the pattern
            +
            +Perform an asynchronous glob search.
            +
            +## glob.sync(pattern, [options])
            +
            +* `pattern` {String} Pattern to be matched
            +* `options` {Object}
            +* return: {Array} filenames found matching the pattern
            +
            +Perform a synchronous glob search.
            +
            +## Class: glob.Glob
            +
            +Create a Glob object by instanting the `glob.Glob` class.
            +
            +```javascript
            +var Glob = require("glob").Glob
            +var mg = new Glob(pattern, options, cb)
            +```
            +
            +It's an EventEmitter, and starts walking the filesystem to find matches
            +immediately.
            +
            +### new glob.Glob(pattern, [options], [cb])
            +
            +* `pattern` {String} pattern to search for
            +* `options` {Object}
            +* `cb` {Function} Called when an error occurs, or matches are found
            +  * `err` {Error | null}
            +  * `matches` {Array} filenames found matching the pattern
            +
            +Note that if the `sync` flag is set in the options, then matches will
            +be immediately available on the `g.found` member.
            +
            +### Properties
            +
            +* `minimatch` The minimatch object that the glob uses.
            +* `options` The options object passed in.
            +* `error` The error encountered.  When an error is encountered, the
            +  glob object is in an undefined state, and should be discarded.
            +* `aborted` Boolean which is set to true when calling `abort()`.  There
            +  is no way at this time to continue a glob search after aborting, but
            +  you can re-use the statCache to avoid having to duplicate syscalls.
            +* `statCache` Collection of all the stat results the glob search
            +  performed.
            +* `cache` Convenience object.  Each field has the following possible
            +  values:
            +  * `false` - Path does not exist
            +  * `true` - Path exists
            +  * `1` - Path exists, and is not a directory
            +  * `2` - Path exists, and is a directory
            +  * `[file, entries, ...]` - Path exists, is a directory, and the
            +    array value is the results of `fs.readdir`
            +
            +### Events
            +
            +* `end` When the matching is finished, this is emitted with all the
            +  matches found.  If the `nonull` option is set, and no match was found,
            +  then the `matches` list contains the original pattern.  The matches
            +  are sorted, unless the `nosort` flag is set.
            +* `match` Every time a match is found, this is emitted with the matched.
            +* `error` Emitted when an unexpected error is encountered, or whenever
            +  any fs error occurs if `options.strict` is set.
            +* `abort` When `abort()` is called, this event is raised.
            +
            +### Methods
            +
            +* `abort` Stop the search.
            +
            +### Options
            +
            +All the options that can be passed to Minimatch can also be passed to
            +Glob to change pattern matching behavior.  Also, some have been added,
            +or have glob-specific ramifications.
            +
            +All options are false by default, unless otherwise noted.
            +
            +All options are added to the glob object, as well.
            +
            +* `cwd` The current working directory in which to search.  Defaults
            +  to `process.cwd()`.
            +* `root` The place where patterns starting with `/` will be mounted
            +  onto.  Defaults to `path.resolve(options.cwd, "/")` (`/` on Unix
            +  systems, and `C:\` or some such on Windows.)
            +* `dot` Include `.dot` files in normal matches and `globstar` matches.
            +  Note that an explicit dot in a portion of the pattern will always
            +  match dot files.
            +* `nomount` By default, a pattern starting with a forward-slash will be
            +  "mounted" onto the root setting, so that a valid filesystem path is
            +  returned.  Set this flag to disable that behavior.
            +* `mark` Add a `/` character to directory matches.  Note that this
            +  requires additional stat calls.
            +* `nosort` Don't sort the results.
            +* `stat` Set to true to stat *all* results.  This reduces performance
            +  somewhat, and is completely unnecessary, unless `readdir` is presumed
            +  to be an untrustworthy indicator of file existence.  It will cause
            +  ELOOP to be triggered one level sooner in the case of cyclical
            +  symbolic links.
            +* `silent` When an unusual error is encountered
            +  when attempting to read a directory, a warning will be printed to
            +  stderr.  Set the `silent` option to true to suppress these warnings.
            +* `strict` When an unusual error is encountered
            +  when attempting to read a directory, the process will just continue on
            +  in search of other matches.  Set the `strict` option to raise an error
            +  in these cases.
            +* `cache` See `cache` property above.  Pass in a previously generated
            +  cache object to save some fs calls.
            +* `statCache` A cache of results of filesystem information, to prevent
            +  unnecessary stat calls.  While it should not normally be necessary to
            +  set this, you may pass the statCache from one glob() call to the
            +  options object of another, if you know that the filesystem will not
            +  change between calls.  (See "Race Conditions" below.)
            +* `sync` Perform a synchronous glob search.
            +* `nounique` In some cases, brace-expanded patterns can result in the
            +  same file showing up multiple times in the result set.  By default,
            +  this implementation prevents duplicates in the result set.
            +  Set this flag to disable that behavior.
            +* `nonull` Set to never return an empty set, instead returning a set
            +  containing the pattern itself.  This is the default in glob(3).
            +* `nocase` Perform a case-insensitive match.  Note that case-insensitive
            +  filesystems will sometimes result in glob returning results that are
            +  case-insensitively matched anyway, since readdir and stat will not
            +  raise an error.
            +* `debug` Set to enable debug logging in minimatch and glob.
            +* `globDebug` Set to enable debug logging in glob, but not minimatch.
            +
            +## Comparisons to other fnmatch/glob implementations
            +
            +While strict compliance with the existing standards is a worthwhile
            +goal, some discrepancies exist between node-glob and other
            +implementations, and are intentional.
            +
            +If the pattern starts with a `!` character, then it is negated.  Set the
            +`nonegate` flag to suppress this behavior, and treat leading `!`
            +characters normally.  This is perhaps relevant if you wish to start the
            +pattern with a negative extglob pattern like `!(a|B)`.  Multiple `!`
            +characters at the start of a pattern will negate the pattern multiple
            +times.
            +
            +If a pattern starts with `#`, then it is treated as a comment, and
            +will not match anything.  Use `\#` to match a literal `#` at the
            +start of a line, or set the `nocomment` flag to suppress this behavior.
            +
            +The double-star character `**` is supported by default, unless the
            +`noglobstar` flag is set.  This is supported in the manner of bsdglob
            +and bash 4.1, where `**` only has special significance if it is the only
            +thing in a path part.  That is, `a/**/b` will match `a/x/y/b`, but
            +`a/**b` will not.
            +
            +If an escaped pattern has no matches, and the `nonull` flag is set,
            +then glob returns the pattern as-provided, rather than
            +interpreting the character escapes.  For example,
            +`glob.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
            +`"*a?"`.  This is akin to setting the `nullglob` option in bash, except
            +that it does not resolve escaped pattern characters.
            +
            +If brace expansion is not disabled, then it is performed before any
            +other interpretation of the glob pattern.  Thus, a pattern like
            +`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
            +**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
            +checked for validity.  Since those two are valid, matching proceeds.
            +
            +## Windows
            +
            +**Please only use forward-slashes in glob expressions.**
            +
            +Though windows uses either `/` or `\` as its path separator, only `/`
            +characters are used by this glob implementation.  You must use
            +forward-slashes **only** in glob expressions.  Back-slashes will always
            +be interpreted as escape characters, not path separators.
            +
            +Results from absolute patterns such as `/foo/*` are mounted onto the
            +root setting using `path.join`.  On windows, this will by default result
            +in `/foo/*` matching `C:\foo\bar.txt`.
            +
            +## Race Conditions
            +
            +Glob searching, by its very nature, is susceptible to race conditions,
            +since it relies on directory walking and such.
            +
            +As a result, it is possible that a file that exists when glob looks for
            +it may have been deleted or modified by the time it returns the result.
            +
            +As part of its internal implementation, this program caches all stat
            +and readdir calls that it makes, in order to cut down on system
            +overhead.  However, this also makes it even more susceptible to races,
            +especially if the cache or statCache objects are reused between glob
            +calls.
            +
            +Users are thus advised not to use a glob result as a guarantee of
            +filesystem state in the face of rapid changes.  For the vast majority
            +of operations, this is never a problem.
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/examples/g.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/examples/g.js
            new file mode 100644
            index 0000000..be122df
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/examples/g.js
            @@ -0,0 +1,9 @@
            +var Glob = require("../").Glob
            +
            +var pattern = "test/a/**/[cg]/../[cg]"
            +console.log(pattern)
            +
            +var mg = new Glob(pattern, {mark: true, sync:true}, function (er, matches) {
            +  console.log("matches", matches)
            +})
            +console.log("after")
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/examples/usr-local.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/examples/usr-local.js
            new file mode 100644
            index 0000000..327a425
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/examples/usr-local.js
            @@ -0,0 +1,9 @@
            +var Glob = require("../").Glob
            +
            +var pattern = "{./*/*,/*,/usr/local/*}"
            +console.log(pattern)
            +
            +var mg = new Glob(pattern, {mark: true}, function (er, matches) {
            +  console.log("matches", matches)
            +})
            +console.log("after")
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/glob.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/glob.js
            new file mode 100644
            index 0000000..f0118a4
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/glob.js
            @@ -0,0 +1,675 @@
            +// Approach:
            +//
            +// 1. Get the minimatch set
            +// 2. For each pattern in the set, PROCESS(pattern)
            +// 3. Store matches per-set, then uniq them
            +//
            +// PROCESS(pattern)
            +// Get the first [n] items from pattern that are all strings
            +// Join these together.  This is PREFIX.
            +//   If there is no more remaining, then stat(PREFIX) and
            +//   add to matches if it succeeds.  END.
            +// readdir(PREFIX) as ENTRIES
            +//   If fails, END
            +//   If pattern[n] is GLOBSTAR
            +//     // handle the case where the globstar match is empty
            +//     // by pruning it out, and testing the resulting pattern
            +//     PROCESS(pattern[0..n] + pattern[n+1 .. $])
            +//     // handle other cases.
            +//     for ENTRY in ENTRIES (not dotfiles)
            +//       // attach globstar + tail onto the entry
            +//       PROCESS(pattern[0..n] + ENTRY + pattern[n .. $])
            +//
            +//   else // not globstar
            +//     for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot)
            +//       Test ENTRY against pattern[n]
            +//       If fails, continue
            +//       If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $])
            +//
            +// Caveat:
            +//   Cache all stats and readdirs results to minimize syscall.  Since all
            +//   we ever care about is existence and directory-ness, we can just keep
            +//   `true` for files, and [children,...] for directories, or `false` for
            +//   things that don't exist.
            +
            +
            +
            +module.exports = glob
            +
            +var fs = require("graceful-fs")
            +, minimatch = require("minimatch")
            +, Minimatch = minimatch.Minimatch
            +, inherits = require("inherits")
            +, EE = require("events").EventEmitter
            +, path = require("path")
            +, isDir = {}
            +, assert = require("assert").ok
            +
            +function glob (pattern, options, cb) {
            +  if (typeof options === "function") cb = options, options = {}
            +  if (!options) options = {}
            +
            +  if (typeof options === "number") {
            +    deprecated()
            +    return
            +  }
            +
            +  var g = new Glob(pattern, options, cb)
            +  return g.sync ? g.found : g
            +}
            +
            +glob.fnmatch = deprecated
            +
            +function deprecated () {
            +  throw new Error("glob's interface has changed. Please see the docs.")
            +}
            +
            +glob.sync = globSync
            +function globSync (pattern, options) {
            +  if (typeof options === "number") {
            +    deprecated()
            +    return
            +  }
            +
            +  options = options || {}
            +  options.sync = true
            +  return glob(pattern, options)
            +}
            +
            +
            +glob.Glob = Glob
            +inherits(Glob, EE)
            +function Glob (pattern, options, cb) {
            +  if (!(this instanceof Glob)) {
            +    return new Glob(pattern, options, cb)
            +  }
            +
            +  if (typeof cb === "function") {
            +    this.on("error", cb)
            +    this.on("end", function (matches) {
            +      cb(null, matches)
            +    })
            +  }
            +
            +  options = options || {}
            +
            +  this.EOF = {}
            +  this._emitQueue = []
            +
            +  this.maxDepth = options.maxDepth || 1000
            +  this.maxLength = options.maxLength || Infinity
            +  this.cache = options.cache || {}
            +  this.statCache = options.statCache || {}
            +
            +  this.changedCwd = false
            +  var cwd = process.cwd()
            +  if (!options.hasOwnProperty("cwd")) this.cwd = cwd
            +  else {
            +    this.cwd = options.cwd
            +    this.changedCwd = path.resolve(options.cwd) !== cwd
            +  }
            +
            +  this.root = options.root || path.resolve(this.cwd, "/")
            +  this.root = path.resolve(this.root)
            +  if (process.platform === "win32")
            +    this.root = this.root.replace(/\\/g, "/")
            +
            +  this.nomount = !!options.nomount
            +
            +  if (!pattern) {
            +    throw new Error("must provide pattern")
            +  }
            +
            +  // base-matching: just use globstar for that.
            +  if (options.matchBase && -1 === pattern.indexOf("/")) {
            +    if (options.noglobstar) {
            +      throw new Error("base matching requires globstar")
            +    }
            +    pattern = "**/" + pattern
            +  }
            +
            +  this.strict = options.strict !== false
            +  this.dot = !!options.dot
            +  this.mark = !!options.mark
            +  this.sync = !!options.sync
            +  this.nounique = !!options.nounique
            +  this.nonull = !!options.nonull
            +  this.nosort = !!options.nosort
            +  this.nocase = !!options.nocase
            +  this.stat = !!options.stat
            +
            +  this.debug = !!options.debug || !!options.globDebug
            +  if (this.debug)
            +    this.log = console.error
            +
            +  this.silent = !!options.silent
            +
            +  var mm = this.minimatch = new Minimatch(pattern, options)
            +  this.options = mm.options
            +  pattern = this.pattern = mm.pattern
            +
            +  this.error = null
            +  this.aborted = false
            +
            +  // list of all the patterns that ** has resolved do, so
            +  // we can avoid visiting multiple times.
            +  this._globstars = {}
            +
            +  EE.call(this)
            +
            +  // process each pattern in the minimatch set
            +  var n = this.minimatch.set.length
            +
            +  // The matches are stored as {: true,...} so that
            +  // duplicates are automagically pruned.
            +  // Later, we do an Object.keys() on these.
            +  // Keep them as a list so we can fill in when nonull is set.
            +  this.matches = new Array(n)
            +
            +  this.minimatch.set.forEach(iterator.bind(this))
            +  function iterator (pattern, i, set) {
            +    this._process(pattern, 0, i, function (er) {
            +      if (er) this.emit("error", er)
            +      if (-- n <= 0) this._finish()
            +    })
            +  }
            +}
            +
            +Glob.prototype.log = function () {}
            +
            +Glob.prototype._finish = function () {
            +  assert(this instanceof Glob)
            +
            +  var nou = this.nounique
            +  , all = nou ? [] : {}
            +
            +  for (var i = 0, l = this.matches.length; i < l; i ++) {
            +    var matches = this.matches[i]
            +    this.log("matches[%d] =", i, matches)
            +    // do like the shell, and spit out the literal glob
            +    if (!matches) {
            +      if (this.nonull) {
            +        var literal = this.minimatch.globSet[i]
            +        if (nou) all.push(literal)
            +        else all[literal] = true
            +      }
            +    } else {
            +      // had matches
            +      var m = Object.keys(matches)
            +      if (nou) all.push.apply(all, m)
            +      else m.forEach(function (m) {
            +        all[m] = true
            +      })
            +    }
            +  }
            +
            +  if (!nou) all = Object.keys(all)
            +
            +  if (!this.nosort) {
            +    all = all.sort(this.nocase ? alphasorti : alphasort)
            +  }
            +
            +  if (this.mark) {
            +    // at *some* point we statted all of these
            +    all = all.map(function (m) {
            +      var sc = this.cache[m]
            +      if (!sc)
            +        return m
            +      var isDir = (Array.isArray(sc) || sc === 2)
            +      if (isDir && m.slice(-1) !== "/") {
            +        return m + "/"
            +      }
            +      if (!isDir && m.slice(-1) === "/") {
            +        return m.replace(/\/+$/, "")
            +      }
            +      return m
            +    }, this)
            +  }
            +
            +  this.log("emitting end", all)
            +
            +  this.EOF = this.found = all
            +  this.emitMatch(this.EOF)
            +}
            +
            +function alphasorti (a, b) {
            +  a = a.toLowerCase()
            +  b = b.toLowerCase()
            +  return alphasort(a, b)
            +}
            +
            +function alphasort (a, b) {
            +  return a > b ? 1 : a < b ? -1 : 0
            +}
            +
            +Glob.prototype.abort = function () {
            +  this.aborted = true
            +  this.emit("abort")
            +}
            +
            +Glob.prototype.pause = function () {
            +  if (this.paused) return
            +  if (this.sync)
            +    this.emit("error", new Error("Can't pause/resume sync glob"))
            +  this.paused = true
            +  this.emit("pause")
            +}
            +
            +Glob.prototype.resume = function () {
            +  if (!this.paused) return
            +  if (this.sync)
            +    this.emit("error", new Error("Can't pause/resume sync glob"))
            +  this.paused = false
            +  this.emit("resume")
            +  this._processEmitQueue()
            +  //process.nextTick(this.emit.bind(this, "resume"))
            +}
            +
            +Glob.prototype.emitMatch = function (m) {
            +  if (!this.stat || this.statCache[m] || m === this.EOF) {
            +    this._emitQueue.push(m)
            +    this._processEmitQueue()
            +  } else {
            +    this._stat(m, function(exists, isDir) {
            +      if (exists) {
            +        this._emitQueue.push(m)
            +        this._processEmitQueue()
            +      }
            +    })
            +  }
            +}
            +
            +Glob.prototype._processEmitQueue = function (m) {
            +  while (!this._processingEmitQueue &&
            +         !this.paused) {
            +    this._processingEmitQueue = true
            +    var m = this._emitQueue.shift()
            +    if (!m) {
            +      this._processingEmitQueue = false
            +      break
            +    }
            +
            +    this.log('emit!', m === this.EOF ? "end" : "match")
            +
            +    this.emit(m === this.EOF ? "end" : "match", m)
            +    this._processingEmitQueue = false
            +  }
            +}
            +
            +Glob.prototype._process = function (pattern, depth, index, cb_) {
            +  assert(this instanceof Glob)
            +
            +  var cb = function cb (er, res) {
            +    assert(this instanceof Glob)
            +    if (this.paused) {
            +      if (!this._processQueue) {
            +        this._processQueue = []
            +        this.once("resume", function () {
            +          var q = this._processQueue
            +          this._processQueue = null
            +          q.forEach(function (cb) { cb() })
            +        })
            +      }
            +      this._processQueue.push(cb_.bind(this, er, res))
            +    } else {
            +      cb_.call(this, er, res)
            +    }
            +  }.bind(this)
            +
            +  if (this.aborted) return cb()
            +
            +  if (depth > this.maxDepth) return cb()
            +
            +  // Get the first [n] parts of pattern that are all strings.
            +  var n = 0
            +  while (typeof pattern[n] === "string") {
            +    n ++
            +  }
            +  // now n is the index of the first one that is *not* a string.
            +
            +  // see if there's anything else
            +  var prefix
            +  switch (n) {
            +    // if not, then this is rather simple
            +    case pattern.length:
            +      prefix = pattern.join("/")
            +      this._stat(prefix, function (exists, isDir) {
            +        // either it's there, or it isn't.
            +        // nothing more to do, either way.
            +        if (exists) {
            +          if (prefix && isAbsolute(prefix) && !this.nomount) {
            +            if (prefix.charAt(0) === "/") {
            +              prefix = path.join(this.root, prefix)
            +            } else {
            +              prefix = path.resolve(this.root, prefix)
            +            }
            +          }
            +
            +          if (process.platform === "win32")
            +            prefix = prefix.replace(/\\/g, "/")
            +
            +          this.matches[index] = this.matches[index] || {}
            +          this.matches[index][prefix] = true
            +          this.emitMatch(prefix)
            +        }
            +        return cb()
            +      })
            +      return
            +
            +    case 0:
            +      // pattern *starts* with some non-trivial item.
            +      // going to readdir(cwd), but not include the prefix in matches.
            +      prefix = null
            +      break
            +
            +    default:
            +      // pattern has some string bits in the front.
            +      // whatever it starts with, whether that's "absolute" like /foo/bar,
            +      // or "relative" like "../baz"
            +      prefix = pattern.slice(0, n)
            +      prefix = prefix.join("/")
            +      break
            +  }
            +
            +  // get the list of entries.
            +  var read
            +  if (prefix === null) read = "."
            +  else if (isAbsolute(prefix) || isAbsolute(pattern.join("/"))) {
            +    if (!prefix || !isAbsolute(prefix)) {
            +      prefix = path.join("/", prefix)
            +    }
            +    read = prefix = path.resolve(prefix)
            +
            +    // if (process.platform === "win32")
            +    //   read = prefix = prefix.replace(/^[a-zA-Z]:|\\/g, "/")
            +
            +    this.log('absolute: ', prefix, this.root, pattern, read)
            +  } else {
            +    read = prefix
            +  }
            +
            +  this.log('readdir(%j)', read, this.cwd, this.root)
            +
            +  return this._readdir(read, function (er, entries) {
            +    if (er) {
            +      // not a directory!
            +      // this means that, whatever else comes after this, it can never match
            +      return cb()
            +    }
            +
            +    // globstar is special
            +    if (pattern[n] === minimatch.GLOBSTAR) {
            +      // test without the globstar, and with every child both below
            +      // and replacing the globstar.
            +      var s = [ pattern.slice(0, n).concat(pattern.slice(n + 1)) ]
            +      entries.forEach(function (e) {
            +        if (e.charAt(0) === "." && !this.dot) return
            +        // instead of the globstar
            +        s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1)))
            +        // below the globstar
            +        s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n)))
            +      }, this)
            +
            +      s = s.filter(function (pattern) {
            +        var key = gsKey(pattern)
            +        var seen = !this._globstars[key]
            +        this._globstars[key] = true
            +        return seen
            +      }, this)
            +
            +      if (!s.length)
            +        return cb()
            +
            +      // now asyncForEach over this
            +      var l = s.length
            +      , errState = null
            +      s.forEach(function (gsPattern) {
            +        this._process(gsPattern, depth + 1, index, function (er) {
            +          if (errState) return
            +          if (er) return cb(errState = er)
            +          if (--l <= 0) return cb()
            +        })
            +      }, this)
            +
            +      return
            +    }
            +
            +    // not a globstar
            +    // It will only match dot entries if it starts with a dot, or if
            +    // dot is set.  Stuff like @(.foo|.bar) isn't allowed.
            +    var pn = pattern[n]
            +    var rawGlob = pattern[n]._glob
            +    , dotOk = this.dot || rawGlob.charAt(0) === "."
            +
            +    entries = entries.filter(function (e) {
            +      return (e.charAt(0) !== "." || dotOk) &&
            +             e.match(pattern[n])
            +    })
            +
            +    // If n === pattern.length - 1, then there's no need for the extra stat
            +    // *unless* the user has specified "mark" or "stat" explicitly.
            +    // We know that they exist, since the readdir returned them.
            +    if (n === pattern.length - 1 &&
            +        !this.mark &&
            +        !this.stat) {
            +      entries.forEach(function (e) {
            +        if (prefix) {
            +          if (prefix !== "/") e = prefix + "/" + e
            +          else e = prefix + e
            +        }
            +        if (e.charAt(0) === "/" && !this.nomount) {
            +          e = path.join(this.root, e)
            +        }
            +
            +        if (process.platform === "win32")
            +          e = e.replace(/\\/g, "/")
            +
            +        this.matches[index] = this.matches[index] || {}
            +        this.matches[index][e] = true
            +        this.emitMatch(e)
            +      }, this)
            +      return cb.call(this)
            +    }
            +
            +
            +    // now test all the remaining entries as stand-ins for that part
            +    // of the pattern.
            +    var l = entries.length
            +    , errState = null
            +    if (l === 0) return cb() // no matches possible
            +    entries.forEach(function (e) {
            +      var p = pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1))
            +      this._process(p, depth + 1, index, function (er) {
            +        if (errState) return
            +        if (er) return cb(errState = er)
            +        if (--l === 0) return cb.call(this)
            +      })
            +    }, this)
            +  })
            +
            +}
            +
            +function gsKey (pattern) {
            +  return '**' + pattern.map(function (p) {
            +    return (p === minimatch.GLOBSTAR) ? '**' : (''+p)
            +  }).join('/')
            +}
            +
            +Glob.prototype._stat = function (f, cb) {
            +  assert(this instanceof Glob)
            +  var abs = f
            +  if (f.charAt(0) === "/") {
            +    abs = path.join(this.root, f)
            +  } else if (this.changedCwd) {
            +    abs = path.resolve(this.cwd, f)
            +  }
            +
            +  if (f.length > this.maxLength) {
            +    var er = new Error("Path name too long")
            +    er.code = "ENAMETOOLONG"
            +    er.path = f
            +    return this._afterStat(f, abs, cb, er)
            +  }
            +
            +  this.log('stat', [this.cwd, f, '=', abs])
            +
            +  if (!this.stat && this.cache.hasOwnProperty(f)) {
            +    var exists = this.cache[f]
            +    , isDir = exists && (Array.isArray(exists) || exists === 2)
            +    if (this.sync) return cb.call(this, !!exists, isDir)
            +    return process.nextTick(cb.bind(this, !!exists, isDir))
            +  }
            +
            +  var stat = this.statCache[abs]
            +  if (this.sync || stat) {
            +    var er
            +    try {
            +      stat = fs.statSync(abs)
            +    } catch (e) {
            +      er = e
            +    }
            +    this._afterStat(f, abs, cb, er, stat)
            +  } else {
            +    fs.stat(abs, this._afterStat.bind(this, f, abs, cb))
            +  }
            +}
            +
            +Glob.prototype._afterStat = function (f, abs, cb, er, stat) {
            +  var exists
            +  assert(this instanceof Glob)
            +
            +  if (abs.slice(-1) === "/" && stat && !stat.isDirectory()) {
            +    this.log("should be ENOTDIR, fake it")
            +
            +    er = new Error("ENOTDIR, not a directory '" + abs + "'")
            +    er.path = abs
            +    er.code = "ENOTDIR"
            +    stat = null
            +  }
            +
            +  var emit = !this.statCache[abs]
            +  this.statCache[abs] = stat
            +
            +  if (er || !stat) {
            +    exists = false
            +  } else {
            +    exists = stat.isDirectory() ? 2 : 1
            +    if (emit)
            +      this.emit('stat', f, stat)
            +  }
            +  this.cache[f] = this.cache[f] || exists
            +  cb.call(this, !!exists, exists === 2)
            +}
            +
            +Glob.prototype._readdir = function (f, cb) {
            +  assert(this instanceof Glob)
            +  var abs = f
            +  if (f.charAt(0) === "/") {
            +    abs = path.join(this.root, f)
            +  } else if (isAbsolute(f)) {
            +    abs = f
            +  } else if (this.changedCwd) {
            +    abs = path.resolve(this.cwd, f)
            +  }
            +
            +  if (f.length > this.maxLength) {
            +    var er = new Error("Path name too long")
            +    er.code = "ENAMETOOLONG"
            +    er.path = f
            +    return this._afterReaddir(f, abs, cb, er)
            +  }
            +
            +  this.log('readdir', [this.cwd, f, abs])
            +  if (this.cache.hasOwnProperty(f)) {
            +    var c = this.cache[f]
            +    if (Array.isArray(c)) {
            +      if (this.sync) return cb.call(this, null, c)
            +      return process.nextTick(cb.bind(this, null, c))
            +    }
            +
            +    if (!c || c === 1) {
            +      // either ENOENT or ENOTDIR
            +      var code = c ? "ENOTDIR" : "ENOENT"
            +      , er = new Error((c ? "Not a directory" : "Not found") + ": " + f)
            +      er.path = f
            +      er.code = code
            +      this.log(f, er)
            +      if (this.sync) return cb.call(this, er)
            +      return process.nextTick(cb.bind(this, er))
            +    }
            +
            +    // at this point, c === 2, meaning it's a dir, but we haven't
            +    // had to read it yet, or c === true, meaning it's *something*
            +    // but we don't have any idea what.  Need to read it, either way.
            +  }
            +
            +  if (this.sync) {
            +    var er, entries
            +    try {
            +      entries = fs.readdirSync(abs)
            +    } catch (e) {
            +      er = e
            +    }
            +    return this._afterReaddir(f, abs, cb, er, entries)
            +  }
            +
            +  fs.readdir(abs, this._afterReaddir.bind(this, f, abs, cb))
            +}
            +
            +Glob.prototype._afterReaddir = function (f, abs, cb, er, entries) {
            +  assert(this instanceof Glob)
            +  if (entries && !er) {
            +    this.cache[f] = entries
            +    // if we haven't asked to stat everything for suresies, then just
            +    // assume that everything in there exists, so we can avoid
            +    // having to stat it a second time.  This also gets us one step
            +    // further into ELOOP territory.
            +    if (!this.mark && !this.stat) {
            +      entries.forEach(function (e) {
            +        if (f === "/") e = f + e
            +        else e = f + "/" + e
            +        this.cache[e] = true
            +      }, this)
            +    }
            +
            +    return cb.call(this, er, entries)
            +  }
            +
            +  // now handle errors, and cache the information
            +  if (er) switch (er.code) {
            +    case "ENOTDIR": // totally normal. means it *does* exist.
            +      this.cache[f] = 1
            +      return cb.call(this, er)
            +    case "ENOENT": // not terribly unusual
            +    case "ELOOP":
            +    case "ENAMETOOLONG":
            +    case "UNKNOWN":
            +      this.cache[f] = false
            +      return cb.call(this, er)
            +    default: // some unusual error.  Treat as failure.
            +      this.cache[f] = false
            +      if (this.strict) this.emit("error", er)
            +      if (!this.silent) console.error("glob error", er)
            +      return cb.call(this, er)
            +  }
            +}
            +
            +var isAbsolute = process.platform === "win32" ? absWin : absUnix
            +
            +function absWin (p) {
            +  if (absUnix(p)) return true
            +  // pull off the device/UNC bit from a windows path.
            +  // from node's lib/path.js
            +  var splitDeviceRe =
            +      /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/
            +    , result = splitDeviceRe.exec(p)
            +    , device = result[1] || ''
            +    , isUnc = device && device.charAt(1) !== ':'
            +    , isAbsolute = !!result[2] || isUnc // UNC paths are always absolute
            +
            +  return isAbsolute
            +}
            +
            +function absUnix (p) {
            +  return p.charAt(0) === "/" || p === ""
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/.npmignore b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/.npmignore
            new file mode 100644
            index 0000000..c2658d7
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/.npmignore
            @@ -0,0 +1 @@
            +node_modules/
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/LICENSE b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/LICENSE
            new file mode 100644
            index 0000000..0c44ae7
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/LICENSE
            @@ -0,0 +1,27 @@
            +Copyright (c) Isaac Z. Schlueter ("Author")
            +All rights reserved.
            +
            +The BSD License
            +
            +Redistribution and use in source and binary forms, with or without
            +modification, are permitted provided that the following conditions
            +are met:
            +
            +1. Redistributions of source code must retain the above copyright
            +   notice, this list of conditions and the following disclaimer.
            +
            +2. Redistributions in binary form must reproduce the above copyright
            +   notice, this list of conditions and the following disclaimer in the
            +   documentation and/or other materials provided with the distribution.
            +
            +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
            +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
            +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
            +PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
            +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
            +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
            +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
            +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
            +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
            +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
            +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/README.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/README.md
            new file mode 100644
            index 0000000..eb1a109
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/README.md
            @@ -0,0 +1,26 @@
            +# graceful-fs
            +
            +graceful-fs functions as a drop-in replacement for the fs module,
            +making various improvements.
            +
            +The improvements are meant to normalize behavior across different
            +platforms and environments, and to make filesystem access more
            +resilient to errors.
            +
            +## Improvements over fs module
            +
            +graceful-fs:
            +
            +* Queues up `open` and `readdir` calls, and retries them once
            +  something closes if there is an EMFILE error from too many file
            +  descriptors.
            +* fixes `lchmod` for Node versions prior to 0.6.2.
            +* implements `fs.lutimes` if possible. Otherwise it becomes a noop.
            +* ignores `EINVAL` and `EPERM` errors in `chown`, `fchown` or
            +  `lchown` if the user isn't root.
            +* makes `lchmod` and `lchown` become noops, if not available.
            +* retries reading a file if `read` results in EAGAIN error.
            +
            +On Windows, it retries renaming a file for up to one second if `EACCESS`
            +or `EPERM` error occurs, likely because antivirus software has locked
            +the directory.
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/graceful-fs.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/graceful-fs.js
            new file mode 100644
            index 0000000..c84db91
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/graceful-fs.js
            @@ -0,0 +1,160 @@
            +// Monkey-patching the fs module.
            +// It's ugly, but there is simply no other way to do this.
            +var fs = module.exports = require('fs')
            +
            +var assert = require('assert')
            +
            +// fix up some busted stuff, mostly on windows and old nodes
            +require('./polyfills.js')
            +
            +// The EMFILE enqueuing stuff
            +
            +var util = require('util')
            +
            +function noop () {}
            +
            +var debug = noop
            +if (util.debuglog)
            +  debug = util.debuglog('gfs')
            +else if (/\bgfs\b/i.test(process.env.NODE_DEBUG || ''))
            +  debug = function() {
            +    var m = util.format.apply(util, arguments)
            +    m = 'GFS: ' + m.split(/\n/).join('\nGFS: ')
            +    console.error(m)
            +  }
            +
            +if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) {
            +  process.on('exit', function() {
            +    debug('fds', fds)
            +    debug(queue)
            +    assert.equal(queue.length, 0)
            +  })
            +}
            +
            +
            +var originalOpen = fs.open
            +fs.open = open
            +
            +function open(path, flags, mode, cb) {
            +  if (typeof mode === "function") cb = mode, mode = null
            +  if (typeof cb !== "function") cb = noop
            +  new OpenReq(path, flags, mode, cb)
            +}
            +
            +function OpenReq(path, flags, mode, cb) {
            +  this.path = path
            +  this.flags = flags
            +  this.mode = mode
            +  this.cb = cb
            +  Req.call(this)
            +}
            +
            +util.inherits(OpenReq, Req)
            +
            +OpenReq.prototype.process = function() {
            +  originalOpen.call(fs, this.path, this.flags, this.mode, this.done)
            +}
            +
            +var fds = {}
            +OpenReq.prototype.done = function(er, fd) {
            +  debug('open done', er, fd)
            +  if (fd)
            +    fds['fd' + fd] = this.path
            +  Req.prototype.done.call(this, er, fd)
            +}
            +
            +
            +var originalReaddir = fs.readdir
            +fs.readdir = readdir
            +
            +function readdir(path, cb) {
            +  if (typeof cb !== "function") cb = noop
            +  new ReaddirReq(path, cb)
            +}
            +
            +function ReaddirReq(path, cb) {
            +  this.path = path
            +  this.cb = cb
            +  Req.call(this)
            +}
            +
            +util.inherits(ReaddirReq, Req)
            +
            +ReaddirReq.prototype.process = function() {
            +  originalReaddir.call(fs, this.path, this.done)
            +}
            +
            +ReaddirReq.prototype.done = function(er, files) {
            +  if (files && files.sort)
            +    files = files.sort()
            +  Req.prototype.done.call(this, er, files)
            +  onclose()
            +}
            +
            +
            +var originalClose = fs.close
            +fs.close = close
            +
            +function close (fd, cb) {
            +  debug('close', fd)
            +  if (typeof cb !== "function") cb = noop
            +  delete fds['fd' + fd]
            +  originalClose.call(fs, fd, function(er) {
            +    onclose()
            +    cb(er)
            +  })
            +}
            +
            +
            +var originalCloseSync = fs.closeSync
            +fs.closeSync = closeSync
            +
            +function closeSync (fd) {
            +  try {
            +    return originalCloseSync(fd)
            +  } finally {
            +    onclose()
            +  }
            +}
            +
            +
            +// Req class
            +function Req () {
            +  // start processing
            +  this.done = this.done.bind(this)
            +  this.failures = 0
            +  this.process()
            +}
            +
            +Req.prototype.done = function (er, result) {
            +  var tryAgain = false
            +  if (er) {
            +    var code = er.code
            +    var tryAgain = code === "EMFILE"
            +    if (process.platform === "win32")
            +      tryAgain = tryAgain || code === "OK"
            +  }
            +
            +  if (tryAgain) {
            +    this.failures ++
            +    enqueue(this)
            +  } else {
            +    var cb = this.cb
            +    cb(er, result)
            +  }
            +}
            +
            +var queue = []
            +
            +function enqueue(req) {
            +  queue.push(req)
            +  debug('enqueue %d %s', queue.length, req.constructor.name, req)
            +}
            +
            +function onclose() {
            +  var req = queue.shift()
            +  if (req) {
            +    debug('process', req.constructor.name, req)
            +    req.process()
            +  }
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/package.json
            new file mode 100644
            index 0000000..7ee49d4
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/package.json
            @@ -0,0 +1,65 @@
            +{
            +  "author": {
            +    "name": "Isaac Z. Schlueter",
            +    "email": "i@izs.me",
            +    "url": "http://blog.izs.me"
            +  },
            +  "name": "graceful-fs",
            +  "description": "A drop-in replacement for fs, making various improvements.",
            +  "version": "2.0.3",
            +  "repository": {
            +    "type": "git",
            +    "url": "git://github.com/isaacs/node-graceful-fs.git"
            +  },
            +  "main": "graceful-fs.js",
            +  "engines": {
            +    "node": ">=0.4.0"
            +  },
            +  "directories": {
            +    "test": "test"
            +  },
            +  "scripts": {
            +    "test": "tap test/*.js"
            +  },
            +  "keywords": [
            +    "fs",
            +    "module",
            +    "reading",
            +    "retry",
            +    "retries",
            +    "queue",
            +    "error",
            +    "errors",
            +    "handling",
            +    "EMFILE",
            +    "EAGAIN",
            +    "EINVAL",
            +    "EPERM",
            +    "EACCESS"
            +  ],
            +  "license": "BSD",
            +  "bugs": {
            +    "url": "https://github.com/isaacs/node-graceful-fs/issues"
            +  },
            +  "homepage": "https://github.com/isaacs/node-graceful-fs",
            +  "_id": "graceful-fs@2.0.3",
            +  "dist": {
            +    "shasum": "7cd2cdb228a4a3f36e95efa6cc142de7d1a136d0",
            +    "tarball": "http://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz"
            +  },
            +  "_from": "graceful-fs@>=2.0.0 <2.1.0",
            +  "_npmVersion": "1.4.6",
            +  "_npmUser": {
            +    "name": "isaacs",
            +    "email": "i@izs.me"
            +  },
            +  "maintainers": [
            +    {
            +      "name": "isaacs",
            +      "email": "i@izs.me"
            +    }
            +  ],
            +  "_shasum": "7cd2cdb228a4a3f36e95efa6cc142de7d1a136d0",
            +  "_resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz",
            +  "readme": "ERROR: No README data found!"
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/polyfills.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/polyfills.js
            new file mode 100644
            index 0000000..afc83b3
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/polyfills.js
            @@ -0,0 +1,228 @@
            +var fs = require('fs')
            +var constants = require('constants')
            +
            +var origCwd = process.cwd
            +var cwd = null
            +process.cwd = function() {
            +  if (!cwd)
            +    cwd = origCwd.call(process)
            +  return cwd
            +}
            +var chdir = process.chdir
            +process.chdir = function(d) {
            +  cwd = null
            +  chdir.call(process, d)
            +}
            +
            +// (re-)implement some things that are known busted or missing.
            +
            +// lchmod, broken prior to 0.6.2
            +// back-port the fix here.
            +if (constants.hasOwnProperty('O_SYMLINK') &&
            +    process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
            +  fs.lchmod = function (path, mode, callback) {
            +    callback = callback || noop
            +    fs.open( path
            +           , constants.O_WRONLY | constants.O_SYMLINK
            +           , mode
            +           , function (err, fd) {
            +      if (err) {
            +        callback(err)
            +        return
            +      }
            +      // prefer to return the chmod error, if one occurs,
            +      // but still try to close, and report closing errors if they occur.
            +      fs.fchmod(fd, mode, function (err) {
            +        fs.close(fd, function(err2) {
            +          callback(err || err2)
            +        })
            +      })
            +    })
            +  }
            +
            +  fs.lchmodSync = function (path, mode) {
            +    var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode)
            +
            +    // prefer to return the chmod error, if one occurs,
            +    // but still try to close, and report closing errors if they occur.
            +    var err, err2
            +    try {
            +      var ret = fs.fchmodSync(fd, mode)
            +    } catch (er) {
            +      err = er
            +    }
            +    try {
            +      fs.closeSync(fd)
            +    } catch (er) {
            +      err2 = er
            +    }
            +    if (err || err2) throw (err || err2)
            +    return ret
            +  }
            +}
            +
            +
            +// lutimes implementation, or no-op
            +if (!fs.lutimes) {
            +  if (constants.hasOwnProperty("O_SYMLINK")) {
            +    fs.lutimes = function (path, at, mt, cb) {
            +      fs.open(path, constants.O_SYMLINK, function (er, fd) {
            +        cb = cb || noop
            +        if (er) return cb(er)
            +        fs.futimes(fd, at, mt, function (er) {
            +          fs.close(fd, function (er2) {
            +            return cb(er || er2)
            +          })
            +        })
            +      })
            +    }
            +
            +    fs.lutimesSync = function (path, at, mt) {
            +      var fd = fs.openSync(path, constants.O_SYMLINK)
            +        , err
            +        , err2
            +        , ret
            +
            +      try {
            +        var ret = fs.futimesSync(fd, at, mt)
            +      } catch (er) {
            +        err = er
            +      }
            +      try {
            +        fs.closeSync(fd)
            +      } catch (er) {
            +        err2 = er
            +      }
            +      if (err || err2) throw (err || err2)
            +      return ret
            +    }
            +
            +  } else if (fs.utimensat && constants.hasOwnProperty("AT_SYMLINK_NOFOLLOW")) {
            +    // maybe utimensat will be bound soonish?
            +    fs.lutimes = function (path, at, mt, cb) {
            +      fs.utimensat(path, at, mt, constants.AT_SYMLINK_NOFOLLOW, cb)
            +    }
            +
            +    fs.lutimesSync = function (path, at, mt) {
            +      return fs.utimensatSync(path, at, mt, constants.AT_SYMLINK_NOFOLLOW)
            +    }
            +
            +  } else {
            +    fs.lutimes = function (_a, _b, _c, cb) { process.nextTick(cb) }
            +    fs.lutimesSync = function () {}
            +  }
            +}
            +
            +
            +// https://github.com/isaacs/node-graceful-fs/issues/4
            +// Chown should not fail on einval or eperm if non-root.
            +
            +fs.chown = chownFix(fs.chown)
            +fs.fchown = chownFix(fs.fchown)
            +fs.lchown = chownFix(fs.lchown)
            +
            +fs.chownSync = chownFixSync(fs.chownSync)
            +fs.fchownSync = chownFixSync(fs.fchownSync)
            +fs.lchownSync = chownFixSync(fs.lchownSync)
            +
            +function chownFix (orig) {
            +  if (!orig) return orig
            +  return function (target, uid, gid, cb) {
            +    return orig.call(fs, target, uid, gid, function (er, res) {
            +      if (chownErOk(er)) er = null
            +      cb(er, res)
            +    })
            +  }
            +}
            +
            +function chownFixSync (orig) {
            +  if (!orig) return orig
            +  return function (target, uid, gid) {
            +    try {
            +      return orig.call(fs, target, uid, gid)
            +    } catch (er) {
            +      if (!chownErOk(er)) throw er
            +    }
            +  }
            +}
            +
            +function chownErOk (er) {
            +  // if there's no getuid, or if getuid() is something other than 0,
            +  // and the error is EINVAL or EPERM, then just ignore it.
            +  // This specific case is a silent failure in cp, install, tar,
            +  // and most other unix tools that manage permissions.
            +  // When running as root, or if other types of errors are encountered,
            +  // then it's strict.
            +  if (!er || (!process.getuid || process.getuid() !== 0)
            +      && (er.code === "EINVAL" || er.code === "EPERM")) return true
            +}
            +
            +
            +// if lchmod/lchown do not exist, then make them no-ops
            +if (!fs.lchmod) {
            +  fs.lchmod = function (path, mode, cb) {
            +    process.nextTick(cb)
            +  }
            +  fs.lchmodSync = function () {}
            +}
            +if (!fs.lchown) {
            +  fs.lchown = function (path, uid, gid, cb) {
            +    process.nextTick(cb)
            +  }
            +  fs.lchownSync = function () {}
            +}
            +
            +
            +
            +// on Windows, A/V software can lock the directory, causing this
            +// to fail with an EACCES or EPERM if the directory contains newly
            +// created files.  Try again on failure, for up to 1 second.
            +if (process.platform === "win32") {
            +  var rename_ = fs.rename
            +  fs.rename = function rename (from, to, cb) {
            +    var start = Date.now()
            +    rename_(from, to, function CB (er) {
            +      if (er
            +          && (er.code === "EACCES" || er.code === "EPERM")
            +          && Date.now() - start < 1000) {
            +        return rename_(from, to, CB)
            +      }
            +      cb(er)
            +    })
            +  }
            +}
            +
            +
            +// if read() returns EAGAIN, then just try it again.
            +var read = fs.read
            +fs.read = function (fd, buffer, offset, length, position, callback_) {
            +  var callback
            +  if (callback_ && typeof callback_ === 'function') {
            +    var eagCounter = 0
            +    callback = function (er, _, __) {
            +      if (er && er.code === 'EAGAIN' && eagCounter < 10) {
            +        eagCounter ++
            +        return read.call(fs, fd, buffer, offset, length, position, callback)
            +      }
            +      callback_.apply(this, arguments)
            +    }
            +  }
            +  return read.call(fs, fd, buffer, offset, length, position, callback)
            +}
            +
            +var readSync = fs.readSync
            +fs.readSync = function (fd, buffer, offset, length, position) {
            +  var eagCounter = 0
            +  while (true) {
            +    try {
            +      return readSync.call(fs, fd, buffer, offset, length, position)
            +    } catch (er) {
            +      if (er.code === 'EAGAIN' && eagCounter < 10) {
            +        eagCounter ++
            +        continue
            +      }
            +      throw er
            +    }
            +  }
            +}
            +
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/test/open.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/test/open.js
            new file mode 100644
            index 0000000..104f36b
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/test/open.js
            @@ -0,0 +1,39 @@
            +var test = require('tap').test
            +var fs = require('../graceful-fs.js')
            +
            +test('graceful fs is monkeypatched fs', function (t) {
            +  t.equal(fs, require('fs'))
            +  t.end()
            +})
            +
            +test('open an existing file works', function (t) {
            +  var fd = fs.openSync(__filename, 'r')
            +  fs.closeSync(fd)
            +  fs.open(__filename, 'r', function (er, fd) {
            +    if (er) throw er
            +    fs.close(fd, function (er) {
            +      if (er) throw er
            +      t.pass('works')
            +      t.end()
            +    })
            +  })
            +})
            +
            +test('open a non-existing file throws', function (t) {
            +  var er
            +  try {
            +    var fd = fs.openSync('this file does not exist', 'r')
            +  } catch (x) {
            +    er = x
            +  }
            +  t.ok(er, 'should throw')
            +  t.notOk(fd, 'should not get an fd')
            +  t.equal(er.code, 'ENOENT')
            +
            +  fs.open('neither does this file', 'r', function (er, fd) {
            +    t.ok(er, 'should throw')
            +    t.notOk(fd, 'should not get an fd')
            +    t.equal(er.code, 'ENOENT')
            +    t.end()
            +  })
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/test/readdir-sort.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/test/readdir-sort.js
            new file mode 100644
            index 0000000..aeaedf1
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/graceful-fs/test/readdir-sort.js
            @@ -0,0 +1,21 @@
            +var test = require("tap").test
            +var fs = require("fs")
            +
            +var readdir = fs.readdir
            +fs.readdir = function(path, cb) {
            +  process.nextTick(function() {
            +    cb(null, ["b", "z", "a"])
            +  })
            +}
            +
            +var g = require("../")
            +
            +test("readdir reorder", function (t) {
            +  g.readdir("whatevers", function (er, files) {
            +    if (er)
            +      throw er
            +    console.error(files)
            +    t.same(files, [ "a", "b", "z" ])
            +    t.end()
            +  })
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/LICENSE b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/LICENSE
            new file mode 100644
            index 0000000..dea3013
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/LICENSE
            @@ -0,0 +1,16 @@
            +The ISC License
            +
            +Copyright (c) Isaac Z. Schlueter
            +
            +Permission to use, copy, modify, and/or distribute this software for any
            +purpose with or without fee is hereby granted, provided that the above
            +copyright notice and this permission notice appear in all copies.
            +
            +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
            +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
            +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
            +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
            +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
            +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
            +PERFORMANCE OF THIS SOFTWARE.
            +
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/README.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/README.md
            new file mode 100644
            index 0000000..b1c5665
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/README.md
            @@ -0,0 +1,42 @@
            +Browser-friendly inheritance fully compatible with standard node.js
            +[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor).
            +
            +This package exports standard `inherits` from node.js `util` module in
            +node environment, but also provides alternative browser-friendly
            +implementation through [browser
            +field](https://gist.github.com/shtylman/4339901). Alternative
            +implementation is a literal copy of standard one located in standalone
            +module to avoid requiring of `util`. It also has a shim for old
            +browsers with no `Object.create` support.
            +
            +While keeping you sure you are using standard `inherits`
            +implementation in node.js environment, it allows bundlers such as
            +[browserify](https://github.com/substack/node-browserify) to not
            +include full `util` package to your client code if all you need is
            +just `inherits` function. It worth, because browser shim for `util`
            +package is large and `inherits` is often the single function you need
            +from it.
            +
            +It's recommended to use this package instead of
            +`require('util').inherits` for any code that has chances to be used
            +not only in node.js but in browser too.
            +
            +## usage
            +
            +```js
            +var inherits = require('inherits');
            +// then use exactly as the standard one
            +```
            +
            +## note on version ~1.0
            +
            +Version ~1.0 had completely different motivation and is not compatible
            +neither with 2.0 nor with standard node.js `inherits`.
            +
            +If you are using version ~1.0 and planning to switch to ~2.0, be
            +careful:
            +
            +* new version uses `super_` instead of `super` for referencing
            +  superclass
            +* new version overwrites current prototype while old one preserves any
            +  existing fields on it
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/inherits.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/inherits.js
            new file mode 100644
            index 0000000..29f5e24
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/inherits.js
            @@ -0,0 +1 @@
            +module.exports = require('util').inherits
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/inherits_browser.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/inherits_browser.js
            new file mode 100644
            index 0000000..c1e78a7
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/inherits_browser.js
            @@ -0,0 +1,23 @@
            +if (typeof Object.create === 'function') {
            +  // implementation from standard node.js 'util' module
            +  module.exports = function inherits(ctor, superCtor) {
            +    ctor.super_ = superCtor
            +    ctor.prototype = Object.create(superCtor.prototype, {
            +      constructor: {
            +        value: ctor,
            +        enumerable: false,
            +        writable: true,
            +        configurable: true
            +      }
            +    });
            +  };
            +} else {
            +  // old school shim for old browsers
            +  module.exports = function inherits(ctor, superCtor) {
            +    ctor.super_ = superCtor
            +    var TempCtor = function () {}
            +    TempCtor.prototype = superCtor.prototype
            +    ctor.prototype = new TempCtor()
            +    ctor.prototype.constructor = ctor
            +  }
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/package.json
            new file mode 100644
            index 0000000..bb245d2
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/package.json
            @@ -0,0 +1,50 @@
            +{
            +  "name": "inherits",
            +  "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()",
            +  "version": "2.0.1",
            +  "keywords": [
            +    "inheritance",
            +    "class",
            +    "klass",
            +    "oop",
            +    "object-oriented",
            +    "inherits",
            +    "browser",
            +    "browserify"
            +  ],
            +  "main": "./inherits.js",
            +  "browser": "./inherits_browser.js",
            +  "repository": {
            +    "type": "git",
            +    "url": "git://github.com/isaacs/inherits.git"
            +  },
            +  "license": "ISC",
            +  "scripts": {
            +    "test": "node test"
            +  },
            +  "bugs": {
            +    "url": "https://github.com/isaacs/inherits/issues"
            +  },
            +  "_id": "inherits@2.0.1",
            +  "dist": {
            +    "shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1",
            +    "tarball": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
            +  },
            +  "_from": "inherits@>=2.0.0 <3.0.0",
            +  "_npmVersion": "1.3.8",
            +  "_npmUser": {
            +    "name": "isaacs",
            +    "email": "i@izs.me"
            +  },
            +  "maintainers": [
            +    {
            +      "name": "isaacs",
            +      "email": "i@izs.me"
            +    }
            +  ],
            +  "directories": {},
            +  "_shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1",
            +  "_resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
            +  "readme": "ERROR: No README data found!",
            +  "homepage": "https://github.com/isaacs/inherits#readme"
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/test.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/test.js
            new file mode 100644
            index 0000000..fc53012
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/inherits/test.js
            @@ -0,0 +1,25 @@
            +var inherits = require('./inherits.js')
            +var assert = require('assert')
            +
            +function test(c) {
            +  assert(c.constructor === Child)
            +  assert(c.constructor.super_ === Parent)
            +  assert(Object.getPrototypeOf(c) === Child.prototype)
            +  assert(Object.getPrototypeOf(Object.getPrototypeOf(c)) === Parent.prototype)
            +  assert(c instanceof Child)
            +  assert(c instanceof Parent)
            +}
            +
            +function Child() {
            +  Parent.call(this)
            +  test(this)
            +}
            +
            +function Parent() {}
            +
            +inherits(Child, Parent)
            +
            +var c = new Child
            +test(c)
            +
            +console.log('ok')
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/.npmignore b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/.npmignore
            new file mode 100644
            index 0000000..3c3629e
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/.npmignore
            @@ -0,0 +1 @@
            +node_modules
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/LICENSE b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/LICENSE
            new file mode 100644
            index 0000000..05a4010
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/LICENSE
            @@ -0,0 +1,23 @@
            +Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
            +All rights reserved.
            +
            +Permission is hereby granted, free of charge, to any person
            +obtaining a copy of this software and associated documentation
            +files (the "Software"), to deal in the Software without
            +restriction, including without limitation the rights to use,
            +copy, modify, merge, publish, distribute, sublicense, and/or sell
            +copies of the Software, and to permit persons to whom the
            +Software is furnished to do so, subject to the following
            +conditions:
            +
            +The above copyright notice and this permission notice shall be
            +included in all copies or substantial portions of the Software.
            +
            +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
            +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
            +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
            +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
            +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
            +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
            +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
            +OTHER DEALINGS IN THE SOFTWARE.
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/README.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/README.md
            new file mode 100644
            index 0000000..978268e
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/README.md
            @@ -0,0 +1,218 @@
            +# minimatch
            +
            +A minimal matching utility.
            +
            +[![Build Status](https://secure.travis-ci.org/isaacs/minimatch.png)](http://travis-ci.org/isaacs/minimatch)
            +
            +
            +This is the matching library used internally by npm.
            +
            +Eventually, it will replace the C binding in node-glob.
            +
            +It works by converting glob expressions into JavaScript `RegExp`
            +objects.
            +
            +## Usage
            +
            +```javascript
            +var minimatch = require("minimatch")
            +
            +minimatch("bar.foo", "*.foo") // true!
            +minimatch("bar.foo", "*.bar") // false!
            +minimatch("bar.foo", "*.+(bar|foo)", { debug: true }) // true, and noisy!
            +```
            +
            +## Features
            +
            +Supports these glob features:
            +
            +* Brace Expansion
            +* Extended glob matching
            +* "Globstar" `**` matching
            +
            +See:
            +
            +* `man sh`
            +* `man bash`
            +* `man 3 fnmatch`
            +* `man 5 gitignore`
            +
            +## Minimatch Class
            +
            +Create a minimatch object by instanting the `minimatch.Minimatch` class.
            +
            +```javascript
            +var Minimatch = require("minimatch").Minimatch
            +var mm = new Minimatch(pattern, options)
            +```
            +
            +### Properties
            +
            +* `pattern` The original pattern the minimatch object represents.
            +* `options` The options supplied to the constructor.
            +* `set` A 2-dimensional array of regexp or string expressions.
            +  Each row in the
            +  array corresponds to a brace-expanded pattern.  Each item in the row
            +  corresponds to a single path-part.  For example, the pattern
            +  `{a,b/c}/d` would expand to a set of patterns like:
            +
            +        [ [ a, d ]
            +        , [ b, c, d ] ]
            +
            +    If a portion of the pattern doesn't have any "magic" in it
            +    (that is, it's something like `"foo"` rather than `fo*o?`), then it
            +    will be left as a string rather than converted to a regular
            +    expression.
            +
            +* `regexp` Created by the `makeRe` method.  A single regular expression
            +  expressing the entire pattern.  This is useful in cases where you wish
            +  to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled.
            +* `negate` True if the pattern is negated.
            +* `comment` True if the pattern is a comment.
            +* `empty` True if the pattern is `""`.
            +
            +### Methods
            +
            +* `makeRe` Generate the `regexp` member if necessary, and return it.
            +  Will return `false` if the pattern is invalid.
            +* `match(fname)` Return true if the filename matches the pattern, or
            +  false otherwise.
            +* `matchOne(fileArray, patternArray, partial)` Take a `/`-split
            +  filename, and match it against a single row in the `regExpSet`.  This
            +  method is mainly for internal use, but is exposed so that it can be
            +  used by a glob-walker that needs to avoid excessive filesystem calls.
            +
            +All other methods are internal, and will be called as necessary.
            +
            +## Functions
            +
            +The top-level exported function has a `cache` property, which is an LRU
            +cache set to store 100 items.  So, calling these methods repeatedly
            +with the same pattern and options will use the same Minimatch object,
            +saving the cost of parsing it multiple times.
            +
            +### minimatch(path, pattern, options)
            +
            +Main export.  Tests a path against the pattern using the options.
            +
            +```javascript
            +var isJS = minimatch(file, "*.js", { matchBase: true })
            +```
            +
            +### minimatch.filter(pattern, options)
            +
            +Returns a function that tests its
            +supplied argument, suitable for use with `Array.filter`.  Example:
            +
            +```javascript
            +var javascripts = fileList.filter(minimatch.filter("*.js", {matchBase: true}))
            +```
            +
            +### minimatch.match(list, pattern, options)
            +
            +Match against the list of
            +files, in the style of fnmatch or glob.  If nothing is matched, and
            +options.nonull is set, then return a list containing the pattern itself.
            +
            +```javascript
            +var javascripts = minimatch.match(fileList, "*.js", {matchBase: true}))
            +```
            +
            +### minimatch.makeRe(pattern, options)
            +
            +Make a regular expression object from the pattern.
            +
            +## Options
            +
            +All options are `false` by default.
            +
            +### debug
            +
            +Dump a ton of stuff to stderr.
            +
            +### nobrace
            +
            +Do not expand `{a,b}` and `{1..3}` brace sets.
            +
            +### noglobstar
            +
            +Disable `**` matching against multiple folder names.
            +
            +### dot
            +
            +Allow patterns to match filenames starting with a period, even if
            +the pattern does not explicitly have a period in that spot.
            +
            +Note that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot`
            +is set.
            +
            +### noext
            +
            +Disable "extglob" style patterns like `+(a|b)`.
            +
            +### nocase
            +
            +Perform a case-insensitive match.
            +
            +### nonull
            +
            +When a match is not found by `minimatch.match`, return a list containing
            +the pattern itself.  When set, an empty list is returned if there are
            +no matches.
            +
            +### matchBase
            +
            +If set, then patterns without slashes will be matched
            +against the basename of the path if it contains slashes.  For example,
            +`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`.
            +
            +### nocomment
            +
            +Suppress the behavior of treating `#` at the start of a pattern as a
            +comment.
            +
            +### nonegate
            +
            +Suppress the behavior of treating a leading `!` character as negation.
            +
            +### flipNegate
            +
            +Returns from negate expressions the same as if they were not negated.
            +(Ie, true on a hit, false on a miss.)
            +
            +
            +## Comparisons to other fnmatch/glob implementations
            +
            +While strict compliance with the existing standards is a worthwhile
            +goal, some discrepancies exist between minimatch and other
            +implementations, and are intentional.
            +
            +If the pattern starts with a `!` character, then it is negated.  Set the
            +`nonegate` flag to suppress this behavior, and treat leading `!`
            +characters normally.  This is perhaps relevant if you wish to start the
            +pattern with a negative extglob pattern like `!(a|B)`.  Multiple `!`
            +characters at the start of a pattern will negate the pattern multiple
            +times.
            +
            +If a pattern starts with `#`, then it is treated as a comment, and
            +will not match anything.  Use `\#` to match a literal `#` at the
            +start of a line, or set the `nocomment` flag to suppress this behavior.
            +
            +The double-star character `**` is supported by default, unless the
            +`noglobstar` flag is set.  This is supported in the manner of bsdglob
            +and bash 4.1, where `**` only has special significance if it is the only
            +thing in a path part.  That is, `a/**/b` will match `a/x/y/b`, but
            +`a/**b` will not.
            +
            +If an escaped pattern has no matches, and the `nonull` flag is set,
            +then minimatch.match returns the pattern as-provided, rather than
            +interpreting the character escapes.  For example,
            +`minimatch.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
            +`"*a?"`.  This is akin to setting the `nullglob` option in bash, except
            +that it does not resolve escaped pattern characters.
            +
            +If brace expansion is not disabled, then it is performed before any
            +other interpretation of the glob pattern.  Thus, a pattern like
            +`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
            +**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
            +checked for validity.  Since those two are valid, matching proceeds.
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/minimatch.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/minimatch.js
            new file mode 100644
            index 0000000..c633f89
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/minimatch.js
            @@ -0,0 +1,1055 @@
            +;(function (require, exports, module, platform) {
            +
            +if (module) module.exports = minimatch
            +else exports.minimatch = minimatch
            +
            +if (!require) {
            +  require = function (id) {
            +    switch (id) {
            +      case "sigmund": return function sigmund (obj) {
            +        return JSON.stringify(obj)
            +      }
            +      case "path": return { basename: function (f) {
            +        f = f.split(/[\/\\]/)
            +        var e = f.pop()
            +        if (!e) e = f.pop()
            +        return e
            +      }}
            +      case "lru-cache": return function LRUCache () {
            +        // not quite an LRU, but still space-limited.
            +        var cache = {}
            +        var cnt = 0
            +        this.set = function (k, v) {
            +          cnt ++
            +          if (cnt >= 100) cache = {}
            +          cache[k] = v
            +        }
            +        this.get = function (k) { return cache[k] }
            +      }
            +    }
            +  }
            +}
            +
            +minimatch.Minimatch = Minimatch
            +
            +var LRU = require("lru-cache")
            +  , cache = minimatch.cache = new LRU({max: 100})
            +  , GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {}
            +  , sigmund = require("sigmund")
            +
            +var path = require("path")
            +  // any single thing other than /
            +  // don't need to escape / when using new RegExp()
            +  , qmark = "[^/]"
            +
            +  // * => any number of characters
            +  , star = qmark + "*?"
            +
            +  // ** when dots are allowed.  Anything goes, except .. and .
            +  // not (^ or / followed by one or two dots followed by $ or /),
            +  // followed by anything, any number of times.
            +  , twoStarDot = "(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?"
            +
            +  // not a ^ or / followed by a dot,
            +  // followed by anything, any number of times.
            +  , twoStarNoDot = "(?:(?!(?:\\\/|^)\\.).)*?"
            +
            +  // characters that need to be escaped in RegExp.
            +  , reSpecials = charSet("().*{}+?[]^$\\!")
            +
            +// "abc" -> { a:true, b:true, c:true }
            +function charSet (s) {
            +  return s.split("").reduce(function (set, c) {
            +    set[c] = true
            +    return set
            +  }, {})
            +}
            +
            +// normalizes slashes.
            +var slashSplit = /\/+/
            +
            +minimatch.filter = filter
            +function filter (pattern, options) {
            +  options = options || {}
            +  return function (p, i, list) {
            +    return minimatch(p, pattern, options)
            +  }
            +}
            +
            +function ext (a, b) {
            +  a = a || {}
            +  b = b || {}
            +  var t = {}
            +  Object.keys(b).forEach(function (k) {
            +    t[k] = b[k]
            +  })
            +  Object.keys(a).forEach(function (k) {
            +    t[k] = a[k]
            +  })
            +  return t
            +}
            +
            +minimatch.defaults = function (def) {
            +  if (!def || !Object.keys(def).length) return minimatch
            +
            +  var orig = minimatch
            +
            +  var m = function minimatch (p, pattern, options) {
            +    return orig.minimatch(p, pattern, ext(def, options))
            +  }
            +
            +  m.Minimatch = function Minimatch (pattern, options) {
            +    return new orig.Minimatch(pattern, ext(def, options))
            +  }
            +
            +  return m
            +}
            +
            +Minimatch.defaults = function (def) {
            +  if (!def || !Object.keys(def).length) return Minimatch
            +  return minimatch.defaults(def).Minimatch
            +}
            +
            +
            +function minimatch (p, pattern, options) {
            +  if (typeof pattern !== "string") {
            +    throw new TypeError("glob pattern string required")
            +  }
            +
            +  if (!options) options = {}
            +
            +  // shortcut: comments match nothing.
            +  if (!options.nocomment && pattern.charAt(0) === "#") {
            +    return false
            +  }
            +
            +  // "" only matches ""
            +  if (pattern.trim() === "") return p === ""
            +
            +  return new Minimatch(pattern, options).match(p)
            +}
            +
            +function Minimatch (pattern, options) {
            +  if (!(this instanceof Minimatch)) {
            +    return new Minimatch(pattern, options, cache)
            +  }
            +
            +  if (typeof pattern !== "string") {
            +    throw new TypeError("glob pattern string required")
            +  }
            +
            +  if (!options) options = {}
            +  pattern = pattern.trim()
            +
            +  // windows: need to use /, not \
            +  // On other platforms, \ is a valid (albeit bad) filename char.
            +  if (platform === "win32") {
            +    pattern = pattern.split("\\").join("/")
            +  }
            +
            +  // lru storage.
            +  // these things aren't particularly big, but walking down the string
            +  // and turning it into a regexp can get pretty costly.
            +  var cacheKey = pattern + "\n" + sigmund(options)
            +  var cached = minimatch.cache.get(cacheKey)
            +  if (cached) return cached
            +  minimatch.cache.set(cacheKey, this)
            +
            +  this.options = options
            +  this.set = []
            +  this.pattern = pattern
            +  this.regexp = null
            +  this.negate = false
            +  this.comment = false
            +  this.empty = false
            +
            +  // make the set of regexps etc.
            +  this.make()
            +}
            +
            +Minimatch.prototype.debug = function() {}
            +
            +Minimatch.prototype.make = make
            +function make () {
            +  // don't do it more than once.
            +  if (this._made) return
            +
            +  var pattern = this.pattern
            +  var options = this.options
            +
            +  // empty patterns and comments match nothing.
            +  if (!options.nocomment && pattern.charAt(0) === "#") {
            +    this.comment = true
            +    return
            +  }
            +  if (!pattern) {
            +    this.empty = true
            +    return
            +  }
            +
            +  // step 1: figure out negation, etc.
            +  this.parseNegate()
            +
            +  // step 2: expand braces
            +  var set = this.globSet = this.braceExpand()
            +
            +  if (options.debug) this.debug = console.error
            +
            +  this.debug(this.pattern, set)
            +
            +  // step 3: now we have a set, so turn each one into a series of path-portion
            +  // matching patterns.
            +  // These will be regexps, except in the case of "**", which is
            +  // set to the GLOBSTAR object for globstar behavior,
            +  // and will not contain any / characters
            +  set = this.globParts = set.map(function (s) {
            +    return s.split(slashSplit)
            +  })
            +
            +  this.debug(this.pattern, set)
            +
            +  // glob --> regexps
            +  set = set.map(function (s, si, set) {
            +    return s.map(this.parse, this)
            +  }, this)
            +
            +  this.debug(this.pattern, set)
            +
            +  // filter out everything that didn't compile properly.
            +  set = set.filter(function (s) {
            +    return -1 === s.indexOf(false)
            +  })
            +
            +  this.debug(this.pattern, set)
            +
            +  this.set = set
            +}
            +
            +Minimatch.prototype.parseNegate = parseNegate
            +function parseNegate () {
            +  var pattern = this.pattern
            +    , negate = false
            +    , options = this.options
            +    , negateOffset = 0
            +
            +  if (options.nonegate) return
            +
            +  for ( var i = 0, l = pattern.length
            +      ; i < l && pattern.charAt(i) === "!"
            +      ; i ++) {
            +    negate = !negate
            +    negateOffset ++
            +  }
            +
            +  if (negateOffset) this.pattern = pattern.substr(negateOffset)
            +  this.negate = negate
            +}
            +
            +// Brace expansion:
            +// a{b,c}d -> abd acd
            +// a{b,}c -> abc ac
            +// a{0..3}d -> a0d a1d a2d a3d
            +// a{b,c{d,e}f}g -> abg acdfg acefg
            +// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
            +//
            +// Invalid sets are not expanded.
            +// a{2..}b -> a{2..}b
            +// a{b}c -> a{b}c
            +minimatch.braceExpand = function (pattern, options) {
            +  return new Minimatch(pattern, options).braceExpand()
            +}
            +
            +Minimatch.prototype.braceExpand = braceExpand
            +function braceExpand (pattern, options) {
            +  options = options || this.options
            +  pattern = typeof pattern === "undefined"
            +    ? this.pattern : pattern
            +
            +  if (typeof pattern === "undefined") {
            +    throw new Error("undefined pattern")
            +  }
            +
            +  if (options.nobrace ||
            +      !pattern.match(/\{.*\}/)) {
            +    // shortcut. no need to expand.
            +    return [pattern]
            +  }
            +
            +  var escaping = false
            +
            +  // examples and comments refer to this crazy pattern:
            +  // a{b,c{d,e},{f,g}h}x{y,z}
            +  // expected:
            +  // abxy
            +  // abxz
            +  // acdxy
            +  // acdxz
            +  // acexy
            +  // acexz
            +  // afhxy
            +  // afhxz
            +  // aghxy
            +  // aghxz
            +
            +  // everything before the first \{ is just a prefix.
            +  // So, we pluck that off, and work with the rest,
            +  // and then prepend it to everything we find.
            +  if (pattern.charAt(0) !== "{") {
            +    this.debug(pattern)
            +    var prefix = null
            +    for (var i = 0, l = pattern.length; i < l; i ++) {
            +      var c = pattern.charAt(i)
            +      this.debug(i, c)
            +      if (c === "\\") {
            +        escaping = !escaping
            +      } else if (c === "{" && !escaping) {
            +        prefix = pattern.substr(0, i)
            +        break
            +      }
            +    }
            +
            +    // actually no sets, all { were escaped.
            +    if (prefix === null) {
            +      this.debug("no sets")
            +      return [pattern]
            +    }
            +
            +   var tail = braceExpand.call(this, pattern.substr(i), options)
            +    return tail.map(function (t) {
            +      return prefix + t
            +    })
            +  }
            +
            +  // now we have something like:
            +  // {b,c{d,e},{f,g}h}x{y,z}
            +  // walk through the set, expanding each part, until
            +  // the set ends.  then, we'll expand the suffix.
            +  // If the set only has a single member, then'll put the {} back
            +
            +  // first, handle numeric sets, since they're easier
            +  var numset = pattern.match(/^\{(-?[0-9]+)\.\.(-?[0-9]+)\}/)
            +  if (numset) {
            +    this.debug("numset", numset[1], numset[2])
            +    var suf = braceExpand.call(this, pattern.substr(numset[0].length), options)
            +      , start = +numset[1]
            +      , end = +numset[2]
            +      , inc = start > end ? -1 : 1
            +      , set = []
            +    for (var i = start; i != (end + inc); i += inc) {
            +      // append all the suffixes
            +      for (var ii = 0, ll = suf.length; ii < ll; ii ++) {
            +        set.push(i + suf[ii])
            +      }
            +    }
            +    return set
            +  }
            +
            +  // ok, walk through the set
            +  // We hope, somewhat optimistically, that there
            +  // will be a } at the end.
            +  // If the closing brace isn't found, then the pattern is
            +  // interpreted as braceExpand("\\" + pattern) so that
            +  // the leading \{ will be interpreted literally.
            +  var i = 1 // skip the \{
            +    , depth = 1
            +    , set = []
            +    , member = ""
            +    , sawEnd = false
            +    , escaping = false
            +
            +  function addMember () {
            +    set.push(member)
            +    member = ""
            +  }
            +
            +  this.debug("Entering for")
            +  FOR: for (i = 1, l = pattern.length; i < l; i ++) {
            +    var c = pattern.charAt(i)
            +    this.debug("", i, c)
            +
            +    if (escaping) {
            +      escaping = false
            +      member += "\\" + c
            +    } else {
            +      switch (c) {
            +        case "\\":
            +          escaping = true
            +          continue
            +
            +        case "{":
            +          depth ++
            +          member += "{"
            +          continue
            +
            +        case "}":
            +          depth --
            +          // if this closes the actual set, then we're done
            +          if (depth === 0) {
            +            addMember()
            +            // pluck off the close-brace
            +            i ++
            +            break FOR
            +          } else {
            +            member += c
            +            continue
            +          }
            +
            +        case ",":
            +          if (depth === 1) {
            +            addMember()
            +          } else {
            +            member += c
            +          }
            +          continue
            +
            +        default:
            +          member += c
            +          continue
            +      } // switch
            +    } // else
            +  } // for
            +
            +  // now we've either finished the set, and the suffix is
            +  // pattern.substr(i), or we have *not* closed the set,
            +  // and need to escape the leading brace
            +  if (depth !== 0) {
            +    this.debug("didn't close", pattern)
            +    return braceExpand.call(this, "\\" + pattern, options)
            +  }
            +
            +  // x{y,z} -> ["xy", "xz"]
            +  this.debug("set", set)
            +  this.debug("suffix", pattern.substr(i))
            +  var suf = braceExpand.call(this, pattern.substr(i), options)
            +  // ["b", "c{d,e}","{f,g}h"] ->
            +  //   [["b"], ["cd", "ce"], ["fh", "gh"]]
            +  var addBraces = set.length === 1
            +  this.debug("set pre-expanded", set)
            +  set = set.map(function (p) {
            +    return braceExpand.call(this, p, options)
            +  }, this)
            +  this.debug("set expanded", set)
            +
            +
            +  // [["b"], ["cd", "ce"], ["fh", "gh"]] ->
            +  //   ["b", "cd", "ce", "fh", "gh"]
            +  set = set.reduce(function (l, r) {
            +    return l.concat(r)
            +  })
            +
            +  if (addBraces) {
            +    set = set.map(function (s) {
            +      return "{" + s + "}"
            +    })
            +  }
            +
            +  // now attach the suffixes.
            +  var ret = []
            +  for (var i = 0, l = set.length; i < l; i ++) {
            +    for (var ii = 0, ll = suf.length; ii < ll; ii ++) {
            +      ret.push(set[i] + suf[ii])
            +    }
            +  }
            +  return ret
            +}
            +
            +// parse a component of the expanded set.
            +// At this point, no pattern may contain "/" in it
            +// so we're going to return a 2d array, where each entry is the full
            +// pattern, split on '/', and then turned into a regular expression.
            +// A regexp is made at the end which joins each array with an
            +// escaped /, and another full one which joins each regexp with |.
            +//
            +// Following the lead of Bash 4.1, note that "**" only has special meaning
            +// when it is the *only* thing in a path portion.  Otherwise, any series
            +// of * is equivalent to a single *.  Globstar behavior is enabled by
            +// default, and can be disabled by setting options.noglobstar.
            +Minimatch.prototype.parse = parse
            +var SUBPARSE = {}
            +function parse (pattern, isSub) {
            +  var options = this.options
            +
            +  // shortcuts
            +  if (!options.noglobstar && pattern === "**") return GLOBSTAR
            +  if (pattern === "") return ""
            +
            +  var re = ""
            +    , hasMagic = !!options.nocase
            +    , escaping = false
            +    // ? => one single character
            +    , patternListStack = []
            +    , plType
            +    , stateChar
            +    , inClass = false
            +    , reClassStart = -1
            +    , classStart = -1
            +    // . and .. never match anything that doesn't start with .,
            +    // even when options.dot is set.
            +    , patternStart = pattern.charAt(0) === "." ? "" // anything
            +      // not (start or / followed by . or .. followed by / or end)
            +      : options.dot ? "(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))"
            +      : "(?!\\.)"
            +    , self = this
            +
            +  function clearStateChar () {
            +    if (stateChar) {
            +      // we had some state-tracking character
            +      // that wasn't consumed by this pass.
            +      switch (stateChar) {
            +        case "*":
            +          re += star
            +          hasMagic = true
            +          break
            +        case "?":
            +          re += qmark
            +          hasMagic = true
            +          break
            +        default:
            +          re += "\\"+stateChar
            +          break
            +      }
            +      self.debug('clearStateChar %j %j', stateChar, re)
            +      stateChar = false
            +    }
            +  }
            +
            +  for ( var i = 0, len = pattern.length, c
            +      ; (i < len) && (c = pattern.charAt(i))
            +      ; i ++ ) {
            +
            +    this.debug("%s\t%s %s %j", pattern, i, re, c)
            +
            +    // skip over any that are escaped.
            +    if (escaping && reSpecials[c]) {
            +      re += "\\" + c
            +      escaping = false
            +      continue
            +    }
            +
            +    SWITCH: switch (c) {
            +      case "/":
            +        // completely not allowed, even escaped.
            +        // Should already be path-split by now.
            +        return false
            +
            +      case "\\":
            +        clearStateChar()
            +        escaping = true
            +        continue
            +
            +      // the various stateChar values
            +      // for the "extglob" stuff.
            +      case "?":
            +      case "*":
            +      case "+":
            +      case "@":
            +      case "!":
            +        this.debug("%s\t%s %s %j <-- stateChar", pattern, i, re, c)
            +
            +        // all of those are literals inside a class, except that
            +        // the glob [!a] means [^a] in regexp
            +        if (inClass) {
            +          this.debug('  in class')
            +          if (c === "!" && i === classStart + 1) c = "^"
            +          re += c
            +          continue
            +        }
            +
            +        // if we already have a stateChar, then it means
            +        // that there was something like ** or +? in there.
            +        // Handle the stateChar, then proceed with this one.
            +        self.debug('call clearStateChar %j', stateChar)
            +        clearStateChar()
            +        stateChar = c
            +        // if extglob is disabled, then +(asdf|foo) isn't a thing.
            +        // just clear the statechar *now*, rather than even diving into
            +        // the patternList stuff.
            +        if (options.noext) clearStateChar()
            +        continue
            +
            +      case "(":
            +        if (inClass) {
            +          re += "("
            +          continue
            +        }
            +
            +        if (!stateChar) {
            +          re += "\\("
            +          continue
            +        }
            +
            +        plType = stateChar
            +        patternListStack.push({ type: plType
            +                              , start: i - 1
            +                              , reStart: re.length })
            +        // negation is (?:(?!js)[^/]*)
            +        re += stateChar === "!" ? "(?:(?!" : "(?:"
            +        this.debug('plType %j %j', stateChar, re)
            +        stateChar = false
            +        continue
            +
            +      case ")":
            +        if (inClass || !patternListStack.length) {
            +          re += "\\)"
            +          continue
            +        }
            +
            +        clearStateChar()
            +        hasMagic = true
            +        re += ")"
            +        plType = patternListStack.pop().type
            +        // negation is (?:(?!js)[^/]*)
            +        // The others are (?:)
            +        switch (plType) {
            +          case "!":
            +            re += "[^/]*?)"
            +            break
            +          case "?":
            +          case "+":
            +          case "*": re += plType
            +          case "@": break // the default anyway
            +        }
            +        continue
            +
            +      case "|":
            +        if (inClass || !patternListStack.length || escaping) {
            +          re += "\\|"
            +          escaping = false
            +          continue
            +        }
            +
            +        clearStateChar()
            +        re += "|"
            +        continue
            +
            +      // these are mostly the same in regexp and glob
            +      case "[":
            +        // swallow any state-tracking char before the [
            +        clearStateChar()
            +
            +        if (inClass) {
            +          re += "\\" + c
            +          continue
            +        }
            +
            +        inClass = true
            +        classStart = i
            +        reClassStart = re.length
            +        re += c
            +        continue
            +
            +      case "]":
            +        //  a right bracket shall lose its special
            +        //  meaning and represent itself in
            +        //  a bracket expression if it occurs
            +        //  first in the list.  -- POSIX.2 2.8.3.2
            +        if (i === classStart + 1 || !inClass) {
            +          re += "\\" + c
            +          escaping = false
            +          continue
            +        }
            +
            +        // finish up the class.
            +        hasMagic = true
            +        inClass = false
            +        re += c
            +        continue
            +
            +      default:
            +        // swallow any state char that wasn't consumed
            +        clearStateChar()
            +
            +        if (escaping) {
            +          // no need
            +          escaping = false
            +        } else if (reSpecials[c]
            +                   && !(c === "^" && inClass)) {
            +          re += "\\"
            +        }
            +
            +        re += c
            +
            +    } // switch
            +  } // for
            +
            +
            +  // handle the case where we left a class open.
            +  // "[abc" is valid, equivalent to "\[abc"
            +  if (inClass) {
            +    // split where the last [ was, and escape it
            +    // this is a huge pita.  We now have to re-walk
            +    // the contents of the would-be class to re-translate
            +    // any characters that were passed through as-is
            +    var cs = pattern.substr(classStart + 1)
            +      , sp = this.parse(cs, SUBPARSE)
            +    re = re.substr(0, reClassStart) + "\\[" + sp[0]
            +    hasMagic = hasMagic || sp[1]
            +  }
            +
            +  // handle the case where we had a +( thing at the *end*
            +  // of the pattern.
            +  // each pattern list stack adds 3 chars, and we need to go through
            +  // and escape any | chars that were passed through as-is for the regexp.
            +  // Go through and escape them, taking care not to double-escape any
            +  // | chars that were already escaped.
            +  var pl
            +  while (pl = patternListStack.pop()) {
            +    var tail = re.slice(pl.reStart + 3)
            +    // maybe some even number of \, then maybe 1 \, followed by a |
            +    tail = tail.replace(/((?:\\{2})*)(\\?)\|/g, function (_, $1, $2) {
            +      if (!$2) {
            +        // the | isn't already escaped, so escape it.
            +        $2 = "\\"
            +      }
            +
            +      // need to escape all those slashes *again*, without escaping the
            +      // one that we need for escaping the | character.  As it works out,
            +      // escaping an even number of slashes can be done by simply repeating
            +      // it exactly after itself.  That's why this trick works.
            +      //
            +      // I am sorry that you have to see this.
            +      return $1 + $1 + $2 + "|"
            +    })
            +
            +    this.debug("tail=%j\n   %s", tail, tail)
            +    var t = pl.type === "*" ? star
            +          : pl.type === "?" ? qmark
            +          : "\\" + pl.type
            +
            +    hasMagic = true
            +    re = re.slice(0, pl.reStart)
            +       + t + "\\("
            +       + tail
            +  }
            +
            +  // handle trailing things that only matter at the very end.
            +  clearStateChar()
            +  if (escaping) {
            +    // trailing \\
            +    re += "\\\\"
            +  }
            +
            +  // only need to apply the nodot start if the re starts with
            +  // something that could conceivably capture a dot
            +  var addPatternStart = false
            +  switch (re.charAt(0)) {
            +    case ".":
            +    case "[":
            +    case "(": addPatternStart = true
            +  }
            +
            +  // if the re is not "" at this point, then we need to make sure
            +  // it doesn't match against an empty path part.
            +  // Otherwise a/* will match a/, which it should not.
            +  if (re !== "" && hasMagic) re = "(?=.)" + re
            +
            +  if (addPatternStart) re = patternStart + re
            +
            +  // parsing just a piece of a larger pattern.
            +  if (isSub === SUBPARSE) {
            +    return [ re, hasMagic ]
            +  }
            +
            +  // skip the regexp for non-magical patterns
            +  // unescape anything in it, though, so that it'll be
            +  // an exact match against a file etc.
            +  if (!hasMagic) {
            +    return globUnescape(pattern)
            +  }
            +
            +  var flags = options.nocase ? "i" : ""
            +    , regExp = new RegExp("^" + re + "$", flags)
            +
            +  regExp._glob = pattern
            +  regExp._src = re
            +
            +  return regExp
            +}
            +
            +minimatch.makeRe = function (pattern, options) {
            +  return new Minimatch(pattern, options || {}).makeRe()
            +}
            +
            +Minimatch.prototype.makeRe = makeRe
            +function makeRe () {
            +  if (this.regexp || this.regexp === false) return this.regexp
            +
            +  // at this point, this.set is a 2d array of partial
            +  // pattern strings, or "**".
            +  //
            +  // It's better to use .match().  This function shouldn't
            +  // be used, really, but it's pretty convenient sometimes,
            +  // when you just want to work with a regex.
            +  var set = this.set
            +
            +  if (!set.length) return this.regexp = false
            +  var options = this.options
            +
            +  var twoStar = options.noglobstar ? star
            +      : options.dot ? twoStarDot
            +      : twoStarNoDot
            +    , flags = options.nocase ? "i" : ""
            +
            +  var re = set.map(function (pattern) {
            +    return pattern.map(function (p) {
            +      return (p === GLOBSTAR) ? twoStar
            +           : (typeof p === "string") ? regExpEscape(p)
            +           : p._src
            +    }).join("\\\/")
            +  }).join("|")
            +
            +  // must match entire pattern
            +  // ending in a * or ** will make it less strict.
            +  re = "^(?:" + re + ")$"
            +
            +  // can match anything, as long as it's not this.
            +  if (this.negate) re = "^(?!" + re + ").*$"
            +
            +  try {
            +    return this.regexp = new RegExp(re, flags)
            +  } catch (ex) {
            +    return this.regexp = false
            +  }
            +}
            +
            +minimatch.match = function (list, pattern, options) {
            +  var mm = new Minimatch(pattern, options)
            +  list = list.filter(function (f) {
            +    return mm.match(f)
            +  })
            +  if (options.nonull && !list.length) {
            +    list.push(pattern)
            +  }
            +  return list
            +}
            +
            +Minimatch.prototype.match = match
            +function match (f, partial) {
            +  this.debug("match", f, this.pattern)
            +  // short-circuit in the case of busted things.
            +  // comments, etc.
            +  if (this.comment) return false
            +  if (this.empty) return f === ""
            +
            +  if (f === "/" && partial) return true
            +
            +  var options = this.options
            +
            +  // windows: need to use /, not \
            +  // On other platforms, \ is a valid (albeit bad) filename char.
            +  if (platform === "win32") {
            +    f = f.split("\\").join("/")
            +  }
            +
            +  // treat the test path as a set of pathparts.
            +  f = f.split(slashSplit)
            +  this.debug(this.pattern, "split", f)
            +
            +  // just ONE of the pattern sets in this.set needs to match
            +  // in order for it to be valid.  If negating, then just one
            +  // match means that we have failed.
            +  // Either way, return on the first hit.
            +
            +  var set = this.set
            +  this.debug(this.pattern, "set", set)
            +
            +  var splitFile = path.basename(f.join("/")).split("/")
            +
            +  for (var i = 0, l = set.length; i < l; i ++) {
            +    var pattern = set[i], file = f
            +    if (options.matchBase && pattern.length === 1) {
            +      file = splitFile
            +    }
            +    var hit = this.matchOne(file, pattern, partial)
            +    if (hit) {
            +      if (options.flipNegate) return true
            +      return !this.negate
            +    }
            +  }
            +
            +  // didn't get any hits.  this is success if it's a negative
            +  // pattern, failure otherwise.
            +  if (options.flipNegate) return false
            +  return this.negate
            +}
            +
            +// set partial to true to test if, for example,
            +// "/a/b" matches the start of "/*/b/*/d"
            +// Partial means, if you run out of file before you run
            +// out of pattern, then that's fine, as long as all
            +// the parts match.
            +Minimatch.prototype.matchOne = function (file, pattern, partial) {
            +  var options = this.options
            +
            +  this.debug("matchOne",
            +              { "this": this
            +              , file: file
            +              , pattern: pattern })
            +
            +  this.debug("matchOne", file.length, pattern.length)
            +
            +  for ( var fi = 0
            +          , pi = 0
            +          , fl = file.length
            +          , pl = pattern.length
            +      ; (fi < fl) && (pi < pl)
            +      ; fi ++, pi ++ ) {
            +
            +    this.debug("matchOne loop")
            +    var p = pattern[pi]
            +      , f = file[fi]
            +
            +    this.debug(pattern, p, f)
            +
            +    // should be impossible.
            +    // some invalid regexp stuff in the set.
            +    if (p === false) return false
            +
            +    if (p === GLOBSTAR) {
            +      this.debug('GLOBSTAR', [pattern, p, f])
            +
            +      // "**"
            +      // a/**/b/**/c would match the following:
            +      // a/b/x/y/z/c
            +      // a/x/y/z/b/c
            +      // a/b/x/b/x/c
            +      // a/b/c
            +      // To do this, take the rest of the pattern after
            +      // the **, and see if it would match the file remainder.
            +      // If so, return success.
            +      // If not, the ** "swallows" a segment, and try again.
            +      // This is recursively awful.
            +      //
            +      // a/**/b/**/c matching a/b/x/y/z/c
            +      // - a matches a
            +      // - doublestar
            +      //   - matchOne(b/x/y/z/c, b/**/c)
            +      //     - b matches b
            +      //     - doublestar
            +      //       - matchOne(x/y/z/c, c) -> no
            +      //       - matchOne(y/z/c, c) -> no
            +      //       - matchOne(z/c, c) -> no
            +      //       - matchOne(c, c) yes, hit
            +      var fr = fi
            +        , pr = pi + 1
            +      if (pr === pl) {
            +        this.debug('** at the end')
            +        // a ** at the end will just swallow the rest.
            +        // We have found a match.
            +        // however, it will not swallow /.x, unless
            +        // options.dot is set.
            +        // . and .. are *never* matched by **, for explosively
            +        // exponential reasons.
            +        for ( ; fi < fl; fi ++) {
            +          if (file[fi] === "." || file[fi] === ".." ||
            +              (!options.dot && file[fi].charAt(0) === ".")) return false
            +        }
            +        return true
            +      }
            +
            +      // ok, let's see if we can swallow whatever we can.
            +      WHILE: while (fr < fl) {
            +        var swallowee = file[fr]
            +
            +        this.debug('\nglobstar while',
            +                    file, fr, pattern, pr, swallowee)
            +
            +        // XXX remove this slice.  Just pass the start index.
            +        if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
            +          this.debug('globstar found match!', fr, fl, swallowee)
            +          // found a match.
            +          return true
            +        } else {
            +          // can't swallow "." or ".." ever.
            +          // can only swallow ".foo" when explicitly asked.
            +          if (swallowee === "." || swallowee === ".." ||
            +              (!options.dot && swallowee.charAt(0) === ".")) {
            +            this.debug("dot detected!", file, fr, pattern, pr)
            +            break WHILE
            +          }
            +
            +          // ** swallows a segment, and continue.
            +          this.debug('globstar swallow a segment, and continue')
            +          fr ++
            +        }
            +      }
            +      // no match was found.
            +      // However, in partial mode, we can't say this is necessarily over.
            +      // If there's more *pattern* left, then 
            +      if (partial) {
            +        // ran out of file
            +        this.debug("\n>>> no match, partial?", file, fr, pattern, pr)
            +        if (fr === fl) return true
            +      }
            +      return false
            +    }
            +
            +    // something other than **
            +    // non-magic patterns just have to match exactly
            +    // patterns with magic have been turned into regexps.
            +    var hit
            +    if (typeof p === "string") {
            +      if (options.nocase) {
            +        hit = f.toLowerCase() === p.toLowerCase()
            +      } else {
            +        hit = f === p
            +      }
            +      this.debug("string match", p, f, hit)
            +    } else {
            +      hit = f.match(p)
            +      this.debug("pattern match", p, f, hit)
            +    }
            +
            +    if (!hit) return false
            +  }
            +
            +  // Note: ending in / means that we'll get a final ""
            +  // at the end of the pattern.  This can only match a
            +  // corresponding "" at the end of the file.
            +  // If the file ends in /, then it can only match a
            +  // a pattern that ends in /, unless the pattern just
            +  // doesn't have any more for it. But, a/b/ should *not*
            +  // match "a/b/*", even though "" matches against the
            +  // [^/]*? pattern, except in partial mode, where it might
            +  // simply not be reached yet.
            +  // However, a/b/ should still satisfy a/*
            +
            +  // now either we fell off the end of the pattern, or we're done.
            +  if (fi === fl && pi === pl) {
            +    // ran out of pattern and filename at the same time.
            +    // an exact hit!
            +    return true
            +  } else if (fi === fl) {
            +    // ran out of file, but still had pattern left.
            +    // this is ok if we're doing the match as part of
            +    // a glob fs traversal.
            +    return partial
            +  } else if (pi === pl) {
            +    // ran out of pattern, still have file left.
            +    // this is only acceptable if we're on the very last
            +    // empty segment of a file with a trailing slash.
            +    // a/* should match a/b/
            +    var emptyFileEnd = (fi === fl - 1) && (file[fi] === "")
            +    return emptyFileEnd
            +  }
            +
            +  // should be unreachable.
            +  throw new Error("wtf?")
            +}
            +
            +
            +// replace stuff like \* with *
            +function globUnescape (s) {
            +  return s.replace(/\\(.)/g, "$1")
            +}
            +
            +
            +function regExpEscape (s) {
            +  return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")
            +}
            +
            +})( typeof require === "function" ? require : null,
            +    this,
            +    typeof module === "object" ? module : null,
            +    typeof process === "object" ? process.platform : "win32"
            +  )
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.npmignore b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.npmignore
            new file mode 100644
            index 0000000..07e6e47
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.npmignore
            @@ -0,0 +1 @@
            +/node_modules
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.travis.yml b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.travis.yml
            new file mode 100644
            index 0000000..4af02b3
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.travis.yml
            @@ -0,0 +1,8 @@
            +language: node_js
            +node_js:
            +  - '0.8'
            +  - '0.10'
            +  - '0.12'
            +  - 'iojs'
            +before_install:
            +  - npm install -g npm@latest
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/CONTRIBUTORS b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/CONTRIBUTORS
            new file mode 100644
            index 0000000..4a0bc50
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/CONTRIBUTORS
            @@ -0,0 +1,14 @@
            +# Authors, sorted by whether or not they are me
            +Isaac Z. Schlueter 
            +Brian Cottingham 
            +Carlos Brito Lage 
            +Jesse Dailey 
            +Kevin O'Hara 
            +Marco Rogers 
            +Mark Cavage 
            +Marko Mikulicic 
            +Nathan Rajlich 
            +Satheesh Natesan 
            +Trent Mick 
            +ashleybrener 
            +n4kz 
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/LICENSE b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/LICENSE
            new file mode 100644
            index 0000000..19129e3
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/LICENSE
            @@ -0,0 +1,15 @@
            +The ISC License
            +
            +Copyright (c) Isaac Z. Schlueter and Contributors
            +
            +Permission to use, copy, modify, and/or distribute this software for any
            +purpose with or without fee is hereby granted, provided that the above
            +copyright notice and this permission notice appear in all copies.
            +
            +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
            +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
            +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
            +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
            +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
            +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
            +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/README.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/README.md
            new file mode 100644
            index 0000000..c06814e
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/README.md
            @@ -0,0 +1,137 @@
            +# lru cache
            +
            +A cache object that deletes the least-recently-used items.
            +
            +## Usage:
            +
            +```javascript
            +var LRU = require("lru-cache")
            +  , options = { max: 500
            +              , length: function (n) { return n * 2 }
            +              , dispose: function (key, n) { n.close() }
            +              , maxAge: 1000 * 60 * 60 }
            +  , cache = LRU(options)
            +  , otherCache = LRU(50) // sets just the max size
            +
            +cache.set("key", "value")
            +cache.get("key") // "value"
            +
            +cache.reset()    // empty the cache
            +```
            +
            +If you put more stuff in it, then items will fall out.
            +
            +If you try to put an oversized thing in it, then it'll fall out right
            +away.
            +
            +## Keys should always be Strings or Numbers
            +
            +Note: this module will print warnings to `console.error` if you use a
            +key that is not a String or Number.  Because items are stored in an
            +object, which coerces keys to a string, it won't go well for you if
            +you try to use a key that is not a unique string, it'll cause surprise
            +collisions.  For example:
            +
            +```JavaScript
            +// Bad Example!  Dont' do this!
            +var cache = LRU()
            +var a = {}
            +var b = {}
            +cache.set(a, 'this is a')
            +cache.set(b, 'this is b')
            +console.log(cache.get(a)) // prints: 'this is b'
            +```
            +
            +## Options
            +
            +* `max` The maximum size of the cache, checked by applying the length
            +  function to all values in the cache.  Not setting this is kind of
            +  silly, since that's the whole purpose of this lib, but it defaults
            +  to `Infinity`.
            +* `maxAge` Maximum age in ms.  Items are not pro-actively pruned out
            +  as they age, but if you try to get an item that is too old, it'll
            +  drop it and return undefined instead of giving it to you.
            +* `length` Function that is used to calculate the length of stored
            +  items.  If you're storing strings or buffers, then you probably want
            +  to do something like `function(n){return n.length}`.  The default is
            +  `function(n){return 1}`, which is fine if you want to store `max`
            +  like-sized things.
            +* `dispose` Function that is called on items when they are dropped
            +  from the cache.  This can be handy if you want to close file
            +  descriptors or do other cleanup tasks when items are no longer
            +  accessible.  Called with `key, value`.  It's called *before*
            +  actually removing the item from the internal cache, so if you want
            +  to immediately put it back in, you'll have to do that in a
            +  `nextTick` or `setTimeout` callback or it won't do anything.
            +* `stale` By default, if you set a `maxAge`, it'll only actually pull
            +  stale items out of the cache when you `get(key)`.  (That is, it's
            +  not pre-emptively doing a `setTimeout` or anything.)  If you set
            +  `stale:true`, it'll return the stale value before deleting it.  If
            +  you don't set this, then it'll return `undefined` when you try to
            +  get a stale entry, as if it had already been deleted.
            +
            +## API
            +
            +* `set(key, value, maxAge)`
            +* `get(key) => value`
            +
            +    Both of these will update the "recently used"-ness of the key.
            +    They do what you think. `max` is optional and overrides the
            +    cache `max` option if provided.
            +
            +* `peek(key)`
            +
            +    Returns the key value (or `undefined` if not found) without
            +    updating the "recently used"-ness of the key.
            +
            +    (If you find yourself using this a lot, you *might* be using the
            +    wrong sort of data structure, but there are some use cases where
            +    it's handy.)
            +
            +* `del(key)`
            +
            +    Deletes a key out of the cache.
            +
            +* `reset()`
            +
            +    Clear the cache entirely, throwing away all values.
            +
            +* `has(key)`
            +
            +    Check if a key is in the cache, without updating the recent-ness
            +    or deleting it for being stale.
            +
            +* `forEach(function(value,key,cache), [thisp])`
            +
            +    Just like `Array.prototype.forEach`.  Iterates over all the keys
            +    in the cache, in order of recent-ness.  (Ie, more recently used
            +    items are iterated over first.)
            +
            +* `keys()`
            +
            +    Return an array of the keys in the cache.
            +
            +* `values()`
            +
            +    Return an array of the values in the cache.
            +
            +* `length()`
            +
            +    Return total length of objects in cache taking into account
            +    `length` options function.
            +
            +* `itemCount`
            +
            +    Return total quantity of objects currently in cache. Note, that
            +    `stale` (see options) items are returned as part of this item
            +    count.
            +
            +* `dump()`
            +
            +    Return an array of the cache entries ready for serialization and usage
            +    with 'destinationCache.load(arr)`.
            +
            +* `load(cacheEntriesArray)`
            +
            +    Loads another cache entries array, obtained with `sourceCache.dump()`,
            +    into the cache. The destination cache is reset before loading new entries
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/lib/lru-cache.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/lib/lru-cache.js
            new file mode 100644
            index 0000000..2bbe653
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/lib/lru-cache.js
            @@ -0,0 +1,334 @@
            +;(function () { // closure for web browsers
            +
            +if (typeof module === 'object' && module.exports) {
            +  module.exports = LRUCache
            +} else {
            +  // just set the global for non-node platforms.
            +  this.LRUCache = LRUCache
            +}
            +
            +function hOP (obj, key) {
            +  return Object.prototype.hasOwnProperty.call(obj, key)
            +}
            +
            +function naiveLength () { return 1 }
            +
            +var didTypeWarning = false
            +function typeCheckKey(key) {
            +  if (!didTypeWarning && typeof key !== 'string' && typeof key !== 'number') {
            +    didTypeWarning = true
            +    console.error(new TypeError("LRU: key must be a string or number. Almost certainly a bug! " + typeof key).stack)
            +  }
            +}
            +
            +function LRUCache (options) {
            +  if (!(this instanceof LRUCache))
            +    return new LRUCache(options)
            +
            +  if (typeof options === 'number')
            +    options = { max: options }
            +
            +  if (!options)
            +    options = {}
            +
            +  this._max = options.max
            +  // Kind of weird to have a default max of Infinity, but oh well.
            +  if (!this._max || !(typeof this._max === "number") || this._max <= 0 )
            +    this._max = Infinity
            +
            +  this._lengthCalculator = options.length || naiveLength
            +  if (typeof this._lengthCalculator !== "function")
            +    this._lengthCalculator = naiveLength
            +
            +  this._allowStale = options.stale || false
            +  this._maxAge = options.maxAge || null
            +  this._dispose = options.dispose
            +  this.reset()
            +}
            +
            +// resize the cache when the max changes.
            +Object.defineProperty(LRUCache.prototype, "max",
            +  { set : function (mL) {
            +      if (!mL || !(typeof mL === "number") || mL <= 0 ) mL = Infinity
            +      this._max = mL
            +      if (this._length > this._max) trim(this)
            +    }
            +  , get : function () { return this._max }
            +  , enumerable : true
            +  })
            +
            +// resize the cache when the lengthCalculator changes.
            +Object.defineProperty(LRUCache.prototype, "lengthCalculator",
            +  { set : function (lC) {
            +      if (typeof lC !== "function") {
            +        this._lengthCalculator = naiveLength
            +        this._length = this._itemCount
            +        for (var key in this._cache) {
            +          this._cache[key].length = 1
            +        }
            +      } else {
            +        this._lengthCalculator = lC
            +        this._length = 0
            +        for (var key in this._cache) {
            +          this._cache[key].length = this._lengthCalculator(this._cache[key].value)
            +          this._length += this._cache[key].length
            +        }
            +      }
            +
            +      if (this._length > this._max) trim(this)
            +    }
            +  , get : function () { return this._lengthCalculator }
            +  , enumerable : true
            +  })
            +
            +Object.defineProperty(LRUCache.prototype, "length",
            +  { get : function () { return this._length }
            +  , enumerable : true
            +  })
            +
            +
            +Object.defineProperty(LRUCache.prototype, "itemCount",
            +  { get : function () { return this._itemCount }
            +  , enumerable : true
            +  })
            +
            +LRUCache.prototype.forEach = function (fn, thisp) {
            +  thisp = thisp || this
            +  var i = 0
            +  var itemCount = this._itemCount
            +
            +  for (var k = this._mru - 1; k >= 0 && i < itemCount; k--) if (this._lruList[k]) {
            +    i++
            +    var hit = this._lruList[k]
            +    if (isStale(this, hit)) {
            +      del(this, hit)
            +      if (!this._allowStale) hit = undefined
            +    }
            +    if (hit) {
            +      fn.call(thisp, hit.value, hit.key, this)
            +    }
            +  }
            +}
            +
            +LRUCache.prototype.keys = function () {
            +  var keys = new Array(this._itemCount)
            +  var i = 0
            +  for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) {
            +    var hit = this._lruList[k]
            +    keys[i++] = hit.key
            +  }
            +  return keys
            +}
            +
            +LRUCache.prototype.values = function () {
            +  var values = new Array(this._itemCount)
            +  var i = 0
            +  for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) {
            +    var hit = this._lruList[k]
            +    values[i++] = hit.value
            +  }
            +  return values
            +}
            +
            +LRUCache.prototype.reset = function () {
            +  if (this._dispose && this._cache) {
            +    for (var k in this._cache) {
            +      this._dispose(k, this._cache[k].value)
            +    }
            +  }
            +
            +  this._cache = Object.create(null) // hash of items by key
            +  this._lruList = Object.create(null) // list of items in order of use recency
            +  this._mru = 0 // most recently used
            +  this._lru = 0 // least recently used
            +  this._length = 0 // number of items in the list
            +  this._itemCount = 0
            +}
            +
            +LRUCache.prototype.dump = function () {
            +  var arr = []
            +  var i = 0
            +
            +  for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) {
            +    var hit = this._lruList[k]
            +    if (!isStale(this, hit)) {
            +      //Do not store staled hits
            +      ++i
            +      arr.push({
            +        k: hit.key,
            +        v: hit.value,
            +        e: hit.now + (hit.maxAge || 0)
            +      });
            +    }
            +  }
            +  //arr has the most read first
            +  return arr
            +}
            +
            +LRUCache.prototype.dumpLru = function () {
            +  return this._lruList
            +}
            +
            +LRUCache.prototype.set = function (key, value, maxAge) {
            +  maxAge = maxAge || this._maxAge
            +  typeCheckKey(key)
            +
            +  var now = maxAge ? Date.now() : 0
            +  var len = this._lengthCalculator(value)
            +
            +  if (hOP(this._cache, key)) {
            +    if (len > this._max) {
            +      del(this, this._cache[key])
            +      return false
            +    }
            +    // dispose of the old one before overwriting
            +    if (this._dispose)
            +      this._dispose(key, this._cache[key].value)
            +
            +    this._cache[key].now = now
            +    this._cache[key].maxAge = maxAge
            +    this._cache[key].value = value
            +    this._length += (len - this._cache[key].length)
            +    this._cache[key].length = len
            +    this.get(key)
            +
            +    if (this._length > this._max)
            +      trim(this)
            +
            +    return true
            +  }
            +
            +  var hit = new Entry(key, value, this._mru++, len, now, maxAge)
            +
            +  // oversized objects fall out of cache automatically.
            +  if (hit.length > this._max) {
            +    if (this._dispose) this._dispose(key, value)
            +    return false
            +  }
            +
            +  this._length += hit.length
            +  this._lruList[hit.lu] = this._cache[key] = hit
            +  this._itemCount ++
            +
            +  if (this._length > this._max)
            +    trim(this)
            +
            +  return true
            +}
            +
            +LRUCache.prototype.has = function (key) {
            +  typeCheckKey(key)
            +  if (!hOP(this._cache, key)) return false
            +  var hit = this._cache[key]
            +  if (isStale(this, hit)) {
            +    return false
            +  }
            +  return true
            +}
            +
            +LRUCache.prototype.get = function (key) {
            +  typeCheckKey(key)
            +  return get(this, key, true)
            +}
            +
            +LRUCache.prototype.peek = function (key) {
            +  typeCheckKey(key)
            +  return get(this, key, false)
            +}
            +
            +LRUCache.prototype.pop = function () {
            +  var hit = this._lruList[this._lru]
            +  del(this, hit)
            +  return hit || null
            +}
            +
            +LRUCache.prototype.del = function (key) {
            +  typeCheckKey(key)
            +  del(this, this._cache[key])
            +}
            +
            +LRUCache.prototype.load = function (arr) {
            +  //reset the cache
            +  this.reset();
            +
            +  var now = Date.now()
            +  //A previous serialized cache has the most recent items first
            +  for (var l = arr.length - 1; l >= 0; l-- ) {
            +    var hit = arr[l]
            +    typeCheckKey(hit.k)
            +    var expiresAt = hit.e || 0
            +    if (expiresAt === 0) {
            +      //the item was created without expiration in a non aged cache
            +      this.set(hit.k, hit.v)
            +    } else {
            +      var maxAge = expiresAt - now
            +      //dont add already expired items
            +      if (maxAge > 0) this.set(hit.k, hit.v, maxAge)
            +    }
            +  }
            +}
            +
            +function get (self, key, doUse) {
            +  typeCheckKey(key)
            +  var hit = self._cache[key]
            +  if (hit) {
            +    if (isStale(self, hit)) {
            +      del(self, hit)
            +      if (!self._allowStale) hit = undefined
            +    } else {
            +      if (doUse) use(self, hit)
            +    }
            +    if (hit) hit = hit.value
            +  }
            +  return hit
            +}
            +
            +function isStale(self, hit) {
            +  if (!hit || (!hit.maxAge && !self._maxAge)) return false
            +  var stale = false;
            +  var diff = Date.now() - hit.now
            +  if (hit.maxAge) {
            +    stale = diff > hit.maxAge
            +  } else {
            +    stale = self._maxAge && (diff > self._maxAge)
            +  }
            +  return stale;
            +}
            +
            +function use (self, hit) {
            +  shiftLU(self, hit)
            +  hit.lu = self._mru ++
            +  self._lruList[hit.lu] = hit
            +}
            +
            +function trim (self) {
            +  while (self._lru < self._mru && self._length > self._max)
            +    del(self, self._lruList[self._lru])
            +}
            +
            +function shiftLU (self, hit) {
            +  delete self._lruList[ hit.lu ]
            +  while (self._lru < self._mru && !self._lruList[self._lru]) self._lru ++
            +}
            +
            +function del (self, hit) {
            +  if (hit) {
            +    if (self._dispose) self._dispose(hit.key, hit.value)
            +    self._length -= hit.length
            +    self._itemCount --
            +    delete self._cache[ hit.key ]
            +    shiftLU(self, hit)
            +  }
            +}
            +
            +// classy, since V8 prefers predictable objects.
            +function Entry (key, value, lu, length, now, maxAge) {
            +  this.key = key
            +  this.value = value
            +  this.lu = lu
            +  this.length = length
            +  this.now = now
            +  if (maxAge) this.maxAge = maxAge
            +}
            +
            +})()
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/package.json
            new file mode 100644
            index 0000000..0f73137
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/package.json
            @@ -0,0 +1,57 @@
            +{
            +  "name": "lru-cache",
            +  "description": "A cache object that deletes the least-recently-used items.",
            +  "version": "2.7.3",
            +  "author": {
            +    "name": "Isaac Z. Schlueter",
            +    "email": "i@izs.me"
            +  },
            +  "keywords": [
            +    "mru",
            +    "lru",
            +    "cache"
            +  ],
            +  "scripts": {
            +    "test": "tap test --gc"
            +  },
            +  "main": "lib/lru-cache.js",
            +  "repository": {
            +    "type": "git",
            +    "url": "git://github.com/isaacs/node-lru-cache.git"
            +  },
            +  "devDependencies": {
            +    "tap": "^1.2.0",
            +    "weak": ""
            +  },
            +  "license": "ISC",
            +  "gitHead": "292048199f6d28b77fbe584279a1898e25e4c714",
            +  "bugs": {
            +    "url": "https://github.com/isaacs/node-lru-cache/issues"
            +  },
            +  "homepage": "https://github.com/isaacs/node-lru-cache#readme",
            +  "_id": "lru-cache@2.7.3",
            +  "_shasum": "6d4524e8b955f95d4f5b58851ce21dd72fb4e952",
            +  "_from": "lru-cache@>=2.0.0 <3.0.0",
            +  "_npmVersion": "3.3.2",
            +  "_nodeVersion": "4.0.0",
            +  "_npmUser": {
            +    "name": "isaacs",
            +    "email": "i@izs.me"
            +  },
            +  "dist": {
            +    "shasum": "6d4524e8b955f95d4f5b58851ce21dd72fb4e952",
            +    "tarball": "http://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz"
            +  },
            +  "maintainers": [
            +    {
            +      "name": "isaacs",
            +      "email": "isaacs@npmjs.com"
            +    },
            +    {
            +      "name": "othiym23",
            +      "email": "ogd@aoaioxxysz.net"
            +    }
            +  ],
            +  "directories": {},
            +  "_resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz"
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/basic.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/basic.js
            new file mode 100644
            index 0000000..b47225f
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/basic.js
            @@ -0,0 +1,396 @@
            +var test = require("tap").test
            +  , LRU = require("../")
            +
            +test("basic", function (t) {
            +  var cache = new LRU({max: 10})
            +  cache.set("key", "value")
            +  t.equal(cache.get("key"), "value")
            +  t.equal(cache.get("nada"), undefined)
            +  t.equal(cache.length, 1)
            +  t.equal(cache.max, 10)
            +  t.end()
            +})
            +
            +test("least recently set", function (t) {
            +  var cache = new LRU(2)
            +  cache.set("a", "A")
            +  cache.set("b", "B")
            +  cache.set("c", "C")
            +  t.equal(cache.get("c"), "C")
            +  t.equal(cache.get("b"), "B")
            +  t.equal(cache.get("a"), undefined)
            +  t.end()
            +})
            +
            +test("lru recently gotten", function (t) {
            +  var cache = new LRU(2)
            +  cache.set("a", "A")
            +  cache.set("b", "B")
            +  cache.get("a")
            +  cache.set("c", "C")
            +  t.equal(cache.get("c"), "C")
            +  t.equal(cache.get("b"), undefined)
            +  t.equal(cache.get("a"), "A")
            +  t.end()
            +})
            +
            +test("del", function (t) {
            +  var cache = new LRU(2)
            +  cache.set("a", "A")
            +  cache.del("a")
            +  t.equal(cache.get("a"), undefined)
            +  t.end()
            +})
            +
            +test("max", function (t) {
            +  var cache = new LRU(3)
            +
            +  // test changing the max, verify that the LRU items get dropped.
            +  cache.max = 100
            +  for (var i = 0; i < 100; i ++) cache.set(i, i)
            +  t.equal(cache.length, 100)
            +  for (var i = 0; i < 100; i ++) {
            +    t.equal(cache.get(i), i)
            +  }
            +  cache.max = 3
            +  t.equal(cache.length, 3)
            +  for (var i = 0; i < 97; i ++) {
            +    t.equal(cache.get(i), undefined)
            +  }
            +  for (var i = 98; i < 100; i ++) {
            +    t.equal(cache.get(i), i)
            +  }
            +
            +  // now remove the max restriction, and try again.
            +  cache.max = "hello"
            +  for (var i = 0; i < 100; i ++) cache.set(i, i)
            +  t.equal(cache.length, 100)
            +  for (var i = 0; i < 100; i ++) {
            +    t.equal(cache.get(i), i)
            +  }
            +  // should trigger an immediate resize
            +  cache.max = 3
            +  t.equal(cache.length, 3)
            +  for (var i = 0; i < 97; i ++) {
            +    t.equal(cache.get(i), undefined)
            +  }
            +  for (var i = 98; i < 100; i ++) {
            +    t.equal(cache.get(i), i)
            +  }
            +  t.end()
            +})
            +
            +test("reset", function (t) {
            +  var cache = new LRU(10)
            +  cache.set("a", "A")
            +  cache.set("b", "B")
            +  cache.reset()
            +  t.equal(cache.length, 0)
            +  t.equal(cache.max, 10)
            +  t.equal(cache.get("a"), undefined)
            +  t.equal(cache.get("b"), undefined)
            +  t.end()
            +})
            +
            +
            +test("basic with weighed length", function (t) {
            +  var cache = new LRU({
            +    max: 100,
            +    length: function (item) { return item.size }
            +  })
            +  cache.set("key", {val: "value", size: 50})
            +  t.equal(cache.get("key").val, "value")
            +  t.equal(cache.get("nada"), undefined)
            +  t.equal(cache.lengthCalculator(cache.get("key")), 50)
            +  t.equal(cache.length, 50)
            +  t.equal(cache.max, 100)
            +  t.end()
            +})
            +
            +
            +test("weighed length item too large", function (t) {
            +  var cache = new LRU({
            +    max: 10,
            +    length: function (item) { return item.size }
            +  })
            +  t.equal(cache.max, 10)
            +
            +  // should fall out immediately
            +  cache.set("key", {val: "value", size: 50})
            +
            +  t.equal(cache.length, 0)
            +  t.equal(cache.get("key"), undefined)
            +  t.end()
            +})
            +
            +test("least recently set with weighed length", function (t) {
            +  var cache = new LRU({
            +    max:8,
            +    length: function (item) { return item.length }
            +  })
            +  cache.set("a", "A")
            +  cache.set("b", "BB")
            +  cache.set("c", "CCC")
            +  cache.set("d", "DDDD")
            +  t.equal(cache.get("d"), "DDDD")
            +  t.equal(cache.get("c"), "CCC")
            +  t.equal(cache.get("b"), undefined)
            +  t.equal(cache.get("a"), undefined)
            +  t.end()
            +})
            +
            +test("lru recently gotten with weighed length", function (t) {
            +  var cache = new LRU({
            +    max: 8,
            +    length: function (item) { return item.length }
            +  })
            +  cache.set("a", "A")
            +  cache.set("b", "BB")
            +  cache.set("c", "CCC")
            +  cache.get("a")
            +  cache.get("b")
            +  cache.set("d", "DDDD")
            +  t.equal(cache.get("c"), undefined)
            +  t.equal(cache.get("d"), "DDDD")
            +  t.equal(cache.get("b"), "BB")
            +  t.equal(cache.get("a"), "A")
            +  t.end()
            +})
            +
            +test("lru recently updated with weighed length", function (t) {
            +  var cache = new LRU({
            +    max: 8,
            +    length: function (item) { return item.length }
            +  })
            +  cache.set("a", "A")
            +  cache.set("b", "BB")
            +  cache.set("c", "CCC")
            +  t.equal(cache.length, 6) //CCC BB A
            +  cache.set("a", "+A")
            +  t.equal(cache.length, 7) //+A CCC BB
            +  cache.set("b", "++BB")
            +  t.equal(cache.length, 6) //++BB +A
            +  t.equal(cache.get("c"), undefined)
            +
            +  cache.set("c", "oversized")
            +  t.equal(cache.length, 6) //++BB +A
            +  t.equal(cache.get("c"), undefined)
            +
            +  cache.set("a", "oversized")
            +  t.equal(cache.length, 4) //++BB
            +  t.equal(cache.get("a"), undefined)
            +  t.equal(cache.get("b"), "++BB")
            +  t.end()
            +})
            +
            +test("set returns proper booleans", function(t) {
            +  var cache = new LRU({
            +    max: 5,
            +    length: function (item) { return item.length }
            +  })
            +
            +  t.equal(cache.set("a", "A"), true)
            +
            +  // should return false for max exceeded
            +  t.equal(cache.set("b", "donuts"), false)
            +
            +  t.equal(cache.set("b", "B"), true)
            +  t.equal(cache.set("c", "CCCC"), true)
            +  t.end()
            +})
            +
            +test("drop the old items", function(t) {
            +  var cache = new LRU({
            +    max: 5,
            +    maxAge: 50
            +  })
            +
            +  cache.set("a", "A")
            +
            +  setTimeout(function () {
            +    cache.set("b", "b")
            +    t.equal(cache.get("a"), "A")
            +  }, 25)
            +
            +  setTimeout(function () {
            +    cache.set("c", "C")
            +    // timed out
            +    t.notOk(cache.get("a"))
            +  }, 60 + 25)
            +
            +  setTimeout(function () {
            +    t.notOk(cache.get("b"))
            +    t.equal(cache.get("c"), "C")
            +  }, 90)
            +
            +  setTimeout(function () {
            +    t.notOk(cache.get("c"))
            +    t.end()
            +  }, 155)
            +})
            +
            +test("individual item can have it's own maxAge", function(t) {
            +  var cache = new LRU({
            +    max: 5,
            +    maxAge: 50
            +  })
            +
            +  cache.set("a", "A", 20)
            +  setTimeout(function () {
            +    t.notOk(cache.get("a"))
            +    t.end()
            +  }, 25)
            +})
            +
            +test("individual item can have it's own maxAge > cache's", function(t) {
            +  var cache = new LRU({
            +    max: 5,
            +    maxAge: 20
            +  })
            +
            +  cache.set("a", "A", 50)
            +  setTimeout(function () {
            +    t.equal(cache.get("a"), "A")
            +    t.end()
            +  }, 25)
            +})
            +
            +test("disposal function", function(t) {
            +  var disposed = false
            +  var cache = new LRU({
            +    max: 1,
            +    dispose: function (k, n) {
            +      disposed = n
            +    }
            +  })
            +
            +  cache.set(1, 1)
            +  cache.set(2, 2)
            +  t.equal(disposed, 1)
            +  cache.set(3, 3)
            +  t.equal(disposed, 2)
            +  cache.reset()
            +  t.equal(disposed, 3)
            +  t.end()
            +})
            +
            +test("disposal function on too big of item", function(t) {
            +  var disposed = false
            +  var cache = new LRU({
            +    max: 1,
            +    length: function (k) {
            +      return k.length
            +    },
            +    dispose: function (k, n) {
            +      disposed = n
            +    }
            +  })
            +  var obj = [ 1, 2 ]
            +
            +  t.equal(disposed, false)
            +  cache.set("obj", obj)
            +  t.equal(disposed, obj)
            +  t.end()
            +})
            +
            +test("has()", function(t) {
            +  var cache = new LRU({
            +    max: 1,
            +    maxAge: 10
            +  })
            +
            +  cache.set('foo', 'bar')
            +  t.equal(cache.has('foo'), true)
            +  cache.set('blu', 'baz')
            +  t.equal(cache.has('foo'), false)
            +  t.equal(cache.has('blu'), true)
            +  setTimeout(function() {
            +    t.equal(cache.has('blu'), false)
            +    t.end()
            +  }, 15)
            +})
            +
            +test("stale", function(t) {
            +  var cache = new LRU({
            +    maxAge: 10,
            +    stale: true
            +  })
            +
            +  cache.set('foo', 'bar')
            +  t.equal(cache.get('foo'), 'bar')
            +  t.equal(cache.has('foo'), true)
            +  setTimeout(function() {
            +    t.equal(cache.has('foo'), false)
            +    t.equal(cache.get('foo'), 'bar')
            +    t.equal(cache.get('foo'), undefined)
            +    t.end()
            +  }, 15)
            +})
            +
            +test("lru update via set", function(t) {
            +  var cache = LRU({ max: 2 });
            +
            +  cache.set('foo', 1);
            +  cache.set('bar', 2);
            +  cache.del('bar');
            +  cache.set('baz', 3);
            +  cache.set('qux', 4);
            +
            +  t.equal(cache.get('foo'), undefined)
            +  t.equal(cache.get('bar'), undefined)
            +  t.equal(cache.get('baz'), 3)
            +  t.equal(cache.get('qux'), 4)
            +  t.end()
            +})
            +
            +test("least recently set w/ peek", function (t) {
            +  var cache = new LRU(2)
            +  cache.set("a", "A")
            +  cache.set("b", "B")
            +  t.equal(cache.peek("a"), "A")
            +  cache.set("c", "C")
            +  t.equal(cache.get("c"), "C")
            +  t.equal(cache.get("b"), "B")
            +  t.equal(cache.get("a"), undefined)
            +  t.end()
            +})
            +
            +test("pop the least used item", function (t) {
            +  var cache = new LRU(3)
            +  , last
            +
            +  cache.set("a", "A")
            +  cache.set("b", "B")
            +  cache.set("c", "C")
            +
            +  t.equal(cache.length, 3)
            +  t.equal(cache.max, 3)
            +
            +  // Ensure we pop a, c, b
            +  cache.get("b", "B")
            +
            +  last = cache.pop()
            +  t.equal(last.key, "a")
            +  t.equal(last.value, "A")
            +  t.equal(cache.length, 2)
            +  t.equal(cache.max, 3)
            +
            +  last = cache.pop()
            +  t.equal(last.key, "c")
            +  t.equal(last.value, "C")
            +  t.equal(cache.length, 1)
            +  t.equal(cache.max, 3)
            +
            +  last = cache.pop()
            +  t.equal(last.key, "b")
            +  t.equal(last.value, "B")
            +  t.equal(cache.length, 0)
            +  t.equal(cache.max, 3)
            +
            +  last = cache.pop()
            +  t.equal(last, null)
            +  t.equal(cache.length, 0)
            +  t.equal(cache.max, 3)
            +
            +  t.end()
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/foreach.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/foreach.js
            new file mode 100644
            index 0000000..4190417
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/foreach.js
            @@ -0,0 +1,120 @@
            +var test = require('tap').test
            +var LRU = require('../')
            +
            +test('forEach', function (t) {
            +  var l = new LRU(5)
            +  for (var i = 0; i < 10; i ++) {
            +    l.set(i.toString(), i.toString(2))
            +  }
            +
            +  var i = 9
            +  l.forEach(function (val, key, cache) {
            +    t.equal(cache, l)
            +    t.equal(key, i.toString())
            +    t.equal(val, i.toString(2))
            +    i -= 1
            +  })
            +
            +  // get in order of most recently used
            +  l.get(6)
            +  l.get(8)
            +
            +  var order = [ 8, 6, 9, 7, 5 ]
            +  var i = 0
            +
            +  l.forEach(function (val, key, cache) {
            +    var j = order[i ++]
            +    t.equal(cache, l)
            +    t.equal(key, j.toString())
            +    t.equal(val, j.toString(2))
            +  })
            +  t.equal(i, order.length);
            +
            +  t.end()
            +})
            +
            +test('keys() and values()', function (t) {
            +  var l = new LRU(5)
            +  for (var i = 0; i < 10; i ++) {
            +    l.set(i.toString(), i.toString(2))
            +  }
            +
            +  t.similar(l.keys(), ['9', '8', '7', '6', '5'])
            +  t.similar(l.values(), ['1001', '1000', '111', '110', '101'])
            +
            +  // get in order of most recently used
            +  l.get(6)
            +  l.get(8)
            +
            +  t.similar(l.keys(), ['8', '6', '9', '7', '5'])
            +  t.similar(l.values(), ['1000', '110', '1001', '111', '101'])
            +
            +  t.end()
            +})
            +
            +test('all entries are iterated over', function(t) {
            +  var l = new LRU(5)
            +  for (var i = 0; i < 10; i ++) {
            +    l.set(i.toString(), i.toString(2))
            +  }
            +
            +  var i = 0
            +  l.forEach(function (val, key, cache) {
            +    if (i > 0) {
            +      cache.del(key)
            +    }
            +    i += 1
            +  })
            +
            +  t.equal(i, 5)
            +  t.equal(l.keys().length, 1)
            +
            +  t.end()
            +})
            +
            +test('all stale entries are removed', function(t) {
            +  var l = new LRU({ max: 5, maxAge: -5, stale: true })
            +  for (var i = 0; i < 10; i ++) {
            +    l.set(i.toString(), i.toString(2))
            +  }
            +
            +  var i = 0
            +  l.forEach(function () {
            +    i += 1
            +  })
            +
            +  t.equal(i, 5)
            +  t.equal(l.keys().length, 0)
            +
            +  t.end()
            +})
            +
            +test('expires', function (t) {
            +  var l = new LRU({
            +    max: 10,
            +    maxAge: 50
            +  })
            +  for (var i = 0; i < 10; i++) {
            +    l.set(i.toString(), i.toString(2), ((i % 2) ? 25 : undefined))
            +  }
            +
            +  var i = 0
            +  var order = [ 8, 6, 4, 2, 0 ]
            +  setTimeout(function () {
            +    l.forEach(function (val, key, cache) {
            +      var j = order[i++]
            +      t.equal(cache, l)
            +      t.equal(key, j.toString())
            +      t.equal(val, j.toString(2))
            +    })
            +    t.equal(i, order.length);
            +
            +    setTimeout(function () {
            +      var count = 0;
            +      l.forEach(function (val, key, cache) { count++; })
            +      t.equal(0, count);
            +      t.end()
            +    }, 25)
            +
            +  }, 26)
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/memory-leak.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/memory-leak.js
            new file mode 100644
            index 0000000..b5912f6
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/memory-leak.js
            @@ -0,0 +1,51 @@
            +#!/usr/bin/env node --expose_gc
            +
            +
            +var weak = require('weak');
            +var test = require('tap').test
            +var LRU = require('../')
            +var l = new LRU({ max: 10 })
            +var refs = 0
            +function X() {
            +  refs ++
            +  weak(this, deref)
            +}
            +
            +function deref() {
            +  refs --
            +}
            +
            +test('no leaks', function (t) {
            +  // fill up the cache
            +  for (var i = 0; i < 100; i++) {
            +    l.set(i, new X);
            +    // throw some gets in there, too.
            +    if (i % 2 === 0)
            +      l.get(i / 2)
            +  }
            +
            +  gc()
            +
            +  var start = process.memoryUsage()
            +
            +  // capture the memory
            +  var startRefs = refs
            +
            +  // do it again, but more
            +  for (var i = 0; i < 10000; i++) {
            +    l.set(i, new X);
            +    // throw some gets in there, too.
            +    if (i % 2 === 0)
            +      l.get(i / 2)
            +  }
            +
            +  gc()
            +
            +  var end = process.memoryUsage()
            +  t.equal(refs, startRefs, 'no leaky refs')
            +
            +  console.error('start: %j\n' +
            +                'end:   %j', start, end);
            +  t.pass();
            +  t.end();
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/serialize.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/serialize.js
            new file mode 100644
            index 0000000..1094194
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/serialize.js
            @@ -0,0 +1,216 @@
            +var test = require('tap').test
            +var LRU = require('../')
            +
            +test('dump', function (t) {
            +  var cache = new LRU()
            +
            +  t.equal(cache.dump().length, 0, "nothing in dump for empty cache")
            +
            +  cache.set("a", "A")
            +  cache.set("b", "B")
            +  t.deepEqual(cache.dump(), [
            +    { k: "b", v: "B", e: 0 },
            +    { k: "a", v: "A", e: 0 }
            +  ])
            +
            +  cache.set("a", "A");
            +  t.deepEqual(cache.dump(), [
            +    { k: "a", v: "A", e: 0 },
            +    { k: "b", v: "B", e: 0 }
            +  ])
            +
            +  cache.get("b");
            +  t.deepEqual(cache.dump(), [
            +    { k: "b", v: "B", e: 0 },
            +    { k: "a", v: "A", e: 0 }
            +  ])
            +
            +  cache.del("a");
            +  t.deepEqual(cache.dump(), [
            +    { k: "b", v: "B",  e: 0 }
            +  ])
            +
            +  t.end()
            +})
            +
            +test("do not dump stale items", function(t) {
            +  var cache = new LRU({
            +    max: 5,
            +    maxAge: 50
            +  })
            +
            +  //expires at 50
            +  cache.set("a", "A")
            +
            +  setTimeout(function () {
            +    //expires at 75
            +    cache.set("b", "B")
            +    var s = cache.dump()
            +    t.equal(s.length, 2)
            +    t.equal(s[0].k, "b")
            +    t.equal(s[1].k, "a")
            +  }, 25)
            +
            +  setTimeout(function () {
            +    //expires at 110
            +    cache.set("c", "C")
            +    var s = cache.dump()
            +    t.equal(s.length, 2)
            +    t.equal(s[0].k, "c")
            +    t.equal(s[1].k, "b")
            +  }, 60)
            +
            +  setTimeout(function () {
            +    //expires at 130
            +    cache.set("d", "D", 40)
            +    var s = cache.dump()
            +    t.equal(s.length, 2)
            +    t.equal(s[0].k, "d")
            +    t.equal(s[1].k, "c")
            +  }, 90)
            +
            +  setTimeout(function () {
            +    var s = cache.dump()
            +    t.equal(s.length, 1)
            +    t.equal(s[0].k, "d")
            +  }, 120)
            +
            +  setTimeout(function () {
            +    var s = cache.dump()
            +    t.deepEqual(s, [])
            +    t.end()
            +  }, 155)
            +})
            +
            +test("load basic cache", function(t) {
            +  var cache = new LRU(),
            +      copy = new LRU()
            +
            +  cache.set("a", "A")
            +  cache.set("b", "B")
            +
            +  copy.load(cache.dump())
            +  t.deepEquals(cache.dump(), copy.dump())
            +
            +  t.end()
            +})
            +
            +
            +test("load staled cache", function(t) {
            +  var cache = new LRU({maxAge: 50}),
            +      copy = new LRU({maxAge: 50}),
            +      arr
            +
            +  //expires at 50
            +  cache.set("a", "A")
            +  setTimeout(function () {
            +    //expires at 80
            +    cache.set("b", "B")
            +    arr = cache.dump()
            +    t.equal(arr.length, 2)
            +  }, 30)
            +
            +  setTimeout(function () {
            +    copy.load(arr)
            +    t.equal(copy.get("a"), undefined)
            +    t.equal(copy.get("b"), "B")
            +  }, 60)
            +
            +  setTimeout(function () {
            +    t.equal(copy.get("b"), undefined)
            +    t.end()
            +  }, 90)
            +})
            +
            +test("load to other size cache", function(t) {
            +  var cache = new LRU({max: 2}),
            +      copy = new LRU({max: 1})
            +
            +  cache.set("a", "A")
            +  cache.set("b", "B")
            +
            +  copy.load(cache.dump())
            +  t.equal(copy.get("a"), undefined)
            +  t.equal(copy.get("b"), "B")
            +
            +  //update the last read from original cache
            +  cache.get("a")
            +  copy.load(cache.dump())
            +  t.equal(copy.get("a"), "A")
            +  t.equal(copy.get("b"), undefined)
            +
            +  t.end()
            +})
            +
            +
            +test("load to other age cache", function(t) {
            +  var cache = new LRU({maxAge: 50}),
            +      aged = new LRU({maxAge: 100}),
            +      simple = new LRU(),
            +      arr,
            +      expired
            +
            +  //created at 0
            +  //a would be valid till 0 + 50
            +  cache.set("a", "A")
            +  setTimeout(function () {
            +    //created at 20
            +    //b would be valid till 20 + 50
            +    cache.set("b", "B")
            +    //b would be valid till 20 + 70
            +    cache.set("c", "C", 70)
            +    arr = cache.dump()
            +    t.equal(arr.length, 3)
            +  }, 20)
            +
            +  setTimeout(function () {
            +    t.equal(cache.get("a"), undefined)
            +    t.equal(cache.get("b"), "B")
            +    t.equal(cache.get("c"), "C")
            +
            +    aged.load(arr)
            +    t.equal(aged.get("a"), undefined)
            +    t.equal(aged.get("b"), "B")
            +    t.equal(aged.get("c"), "C")
            +
            +    simple.load(arr)
            +    t.equal(simple.get("a"), undefined)
            +    t.equal(simple.get("b"), "B")
            +    t.equal(simple.get("c"), "C")
            +  }, 60)
            +
            +  setTimeout(function () {
            +    t.equal(cache.get("a"), undefined)
            +    t.equal(cache.get("b"), undefined)
            +    t.equal(cache.get("c"), "C")
            +
            +    aged.load(arr)
            +    t.equal(aged.get("a"), undefined)
            +    t.equal(aged.get("b"), undefined)
            +    t.equal(aged.get("c"), "C")
            +
            +    simple.load(arr)
            +    t.equal(simple.get("a"), undefined)
            +    t.equal(simple.get("b"), undefined)
            +    t.equal(simple.get("c"), "C")
            +  }, 80)
            +
            +  setTimeout(function () {
            +    t.equal(cache.get("a"), undefined)
            +    t.equal(cache.get("b"), undefined)
            +    t.equal(cache.get("c"), undefined)
            +
            +    aged.load(arr)
            +    t.equal(aged.get("a"), undefined)
            +    t.equal(aged.get("b"), undefined)
            +    t.equal(aged.get("c"), undefined)
            +
            +    simple.load(arr)
            +    t.equal(simple.get("a"), undefined)
            +    t.equal(simple.get("b"), undefined)
            +    t.equal(simple.get("c"), undefined)
            +    t.end()
            +  }, 100)
            +
            +})
            +
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/LICENSE b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/LICENSE
            new file mode 100644
            index 0000000..19129e3
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/LICENSE
            @@ -0,0 +1,15 @@
            +The ISC License
            +
            +Copyright (c) Isaac Z. Schlueter and Contributors
            +
            +Permission to use, copy, modify, and/or distribute this software for any
            +purpose with or without fee is hereby granted, provided that the above
            +copyright notice and this permission notice appear in all copies.
            +
            +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
            +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
            +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
            +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
            +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
            +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
            +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/README.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/README.md
            new file mode 100644
            index 0000000..25a38a5
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/README.md
            @@ -0,0 +1,53 @@
            +# sigmund
            +
            +Quick and dirty signatures for Objects.
            +
            +This is like a much faster `deepEquals` comparison, which returns a
            +string key suitable for caches and the like.
            +
            +## Usage
            +
            +```javascript
            +function doSomething (someObj) {
            +  var key = sigmund(someObj, maxDepth) // max depth defaults to 10
            +  var cached = cache.get(key)
            +  if (cached) return cached
            +
            +  var result = expensiveCalculation(someObj)
            +  cache.set(key, result)
            +  return result
            +}
            +```
            +
            +The resulting key will be as unique and reproducible as calling
            +`JSON.stringify` or `util.inspect` on the object, but is much faster.
            +In order to achieve this speed, some differences are glossed over.
            +For example, the object `{0:'foo'}` will be treated identically to the
            +array `['foo']`.
            +
            +Also, just as there is no way to summon the soul from the scribblings
            +of a cocaine-addled psychoanalyst, there is no way to revive the object
            +from the signature string that sigmund gives you.  In fact, it's
            +barely even readable.
            +
            +As with `util.inspect` and `JSON.stringify`, larger objects will
            +produce larger signature strings.
            +
            +Because sigmund is a bit less strict than the more thorough
            +alternatives, the strings will be shorter, and also there is a
            +slightly higher chance for collisions.  For example, these objects
            +have the same signature:
            +
            +    var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]}
            +    var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']}
            +
            +Like a good Freudian, sigmund is most effective when you already have
            +some understanding of what you're looking for.  It can help you help
            +yourself, but you must be willing to do some work as well.
            +
            +Cycles are handled, and cyclical objects are silently omitted (though
            +the key is included in the signature output.)
            +
            +The second argument is the maximum depth, which defaults to 10,
            +because that is the maximum object traversal depth covered by most
            +insurance carriers.
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/bench.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/bench.js
            new file mode 100644
            index 0000000..5acfd6d
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/bench.js
            @@ -0,0 +1,283 @@
            +// different ways to id objects
            +// use a req/res pair, since it's crazy deep and cyclical
            +
            +// sparseFE10 and sigmund are usually pretty close, which is to be expected,
            +// since they are essentially the same algorithm, except that sigmund handles
            +// regular expression objects properly.
            +
            +
            +var http = require('http')
            +var util = require('util')
            +var sigmund = require('./sigmund.js')
            +var sreq, sres, creq, cres, test
            +
            +http.createServer(function (q, s) {
            +  sreq = q
            +  sres = s
            +  sres.end('ok')
            +  this.close(function () { setTimeout(function () {
            +    start()
            +  }, 200) })
            +}).listen(1337, function () {
            +  creq = http.get({ port: 1337 })
            +  creq.on('response', function (s) { cres = s })
            +})
            +
            +function start () {
            +  test = [sreq, sres, creq, cres]
            +  // test = sreq
            +  // sreq.sres = sres
            +  // sreq.creq = creq
            +  // sreq.cres = cres
            +
            +  for (var i in exports.compare) {
            +    console.log(i)
            +    var hash = exports.compare[i]()
            +    console.log(hash)
            +    console.log(hash.length)
            +    console.log('')
            +  }
            +
            +  require('bench').runMain()
            +}
            +
            +function customWs (obj, md, d) {
            +  d = d || 0
            +  var to = typeof obj
            +  if (to === 'undefined' || to === 'function' || to === null) return ''
            +  if (d > md || !obj || to !== 'object') return ('' + obj).replace(/[\n ]+/g, '')
            +
            +  if (Array.isArray(obj)) {
            +    return obj.map(function (i, _, __) {
            +      return customWs(i, md, d + 1)
            +    }).reduce(function (a, b) { return a + b }, '')
            +  }
            +
            +  var keys = Object.keys(obj)
            +  return keys.map(function (k, _, __) {
            +    return k + ':' + customWs(obj[k], md, d + 1)
            +  }).reduce(function (a, b) { return a + b }, '')
            +}
            +
            +function custom (obj, md, d) {
            +  d = d || 0
            +  var to = typeof obj
            +  if (to === 'undefined' || to === 'function' || to === null) return ''
            +  if (d > md || !obj || to !== 'object') return '' + obj
            +
            +  if (Array.isArray(obj)) {
            +    return obj.map(function (i, _, __) {
            +      return custom(i, md, d + 1)
            +    }).reduce(function (a, b) { return a + b }, '')
            +  }
            +
            +  var keys = Object.keys(obj)
            +  return keys.map(function (k, _, __) {
            +    return k + ':' + custom(obj[k], md, d + 1)
            +  }).reduce(function (a, b) { return a + b }, '')
            +}
            +
            +function sparseFE2 (obj, maxDepth) {
            +  var seen = []
            +  var soFar = ''
            +  function ch (v, depth) {
            +    if (depth > maxDepth) return
            +    if (typeof v === 'function' || typeof v === 'undefined') return
            +    if (typeof v !== 'object' || !v) {
            +      soFar += v
            +      return
            +    }
            +    if (seen.indexOf(v) !== -1 || depth === maxDepth) return
            +    seen.push(v)
            +    soFar += '{'
            +    Object.keys(v).forEach(function (k, _, __) {
            +      // pseudo-private values.  skip those.
            +      if (k.charAt(0) === '_') return
            +      var to = typeof v[k]
            +      if (to === 'function' || to === 'undefined') return
            +      soFar += k + ':'
            +      ch(v[k], depth + 1)
            +    })
            +    soFar += '}'
            +  }
            +  ch(obj, 0)
            +  return soFar
            +}
            +
            +function sparseFE (obj, maxDepth) {
            +  var seen = []
            +  var soFar = ''
            +  function ch (v, depth) {
            +    if (depth > maxDepth) return
            +    if (typeof v === 'function' || typeof v === 'undefined') return
            +    if (typeof v !== 'object' || !v) {
            +      soFar += v
            +      return
            +    }
            +    if (seen.indexOf(v) !== -1 || depth === maxDepth) return
            +    seen.push(v)
            +    soFar += '{'
            +    Object.keys(v).forEach(function (k, _, __) {
            +      // pseudo-private values.  skip those.
            +      if (k.charAt(0) === '_') return
            +      var to = typeof v[k]
            +      if (to === 'function' || to === 'undefined') return
            +      soFar += k
            +      ch(v[k], depth + 1)
            +    })
            +  }
            +  ch(obj, 0)
            +  return soFar
            +}
            +
            +function sparse (obj, maxDepth) {
            +  var seen = []
            +  var soFar = ''
            +  function ch (v, depth) {
            +    if (depth > maxDepth) return
            +    if (typeof v === 'function' || typeof v === 'undefined') return
            +    if (typeof v !== 'object' || !v) {
            +      soFar += v
            +      return
            +    }
            +    if (seen.indexOf(v) !== -1 || depth === maxDepth) return
            +    seen.push(v)
            +    soFar += '{'
            +    for (var k in v) {
            +      // pseudo-private values.  skip those.
            +      if (k.charAt(0) === '_') continue
            +      var to = typeof v[k]
            +      if (to === 'function' || to === 'undefined') continue
            +      soFar += k
            +      ch(v[k], depth + 1)
            +    }
            +  }
            +  ch(obj, 0)
            +  return soFar
            +}
            +
            +function noCommas (obj, maxDepth) {
            +  var seen = []
            +  var soFar = ''
            +  function ch (v, depth) {
            +    if (depth > maxDepth) return
            +    if (typeof v === 'function' || typeof v === 'undefined') return
            +    if (typeof v !== 'object' || !v) {
            +      soFar += v
            +      return
            +    }
            +    if (seen.indexOf(v) !== -1 || depth === maxDepth) return
            +    seen.push(v)
            +    soFar += '{'
            +    for (var k in v) {
            +      // pseudo-private values.  skip those.
            +      if (k.charAt(0) === '_') continue
            +      var to = typeof v[k]
            +      if (to === 'function' || to === 'undefined') continue
            +      soFar += k + ':'
            +      ch(v[k], depth + 1)
            +    }
            +    soFar += '}'
            +  }
            +  ch(obj, 0)
            +  return soFar
            +}
            +
            +
            +function flatten (obj, maxDepth) {
            +  var seen = []
            +  var soFar = ''
            +  function ch (v, depth) {
            +    if (depth > maxDepth) return
            +    if (typeof v === 'function' || typeof v === 'undefined') return
            +    if (typeof v !== 'object' || !v) {
            +      soFar += v
            +      return
            +    }
            +    if (seen.indexOf(v) !== -1 || depth === maxDepth) return
            +    seen.push(v)
            +    soFar += '{'
            +    for (var k in v) {
            +      // pseudo-private values.  skip those.
            +      if (k.charAt(0) === '_') continue
            +      var to = typeof v[k]
            +      if (to === 'function' || to === 'undefined') continue
            +      soFar += k + ':'
            +      ch(v[k], depth + 1)
            +      soFar += ','
            +    }
            +    soFar += '}'
            +  }
            +  ch(obj, 0)
            +  return soFar
            +}
            +
            +exports.compare =
            +{
            +  // 'custom 2': function () {
            +  //   return custom(test, 2, 0)
            +  // },
            +  // 'customWs 2': function () {
            +  //   return customWs(test, 2, 0)
            +  // },
            +  'JSON.stringify (guarded)': function () {
            +    var seen = []
            +    return JSON.stringify(test, function (k, v) {
            +      if (typeof v !== 'object' || !v) return v
            +      if (seen.indexOf(v) !== -1) return undefined
            +      seen.push(v)
            +      return v
            +    })
            +  },
            +
            +  'flatten 10': function () {
            +    return flatten(test, 10)
            +  },
            +
            +  // 'flattenFE 10': function () {
            +  //   return flattenFE(test, 10)
            +  // },
            +
            +  'noCommas 10': function () {
            +    return noCommas(test, 10)
            +  },
            +
            +  'sparse 10': function () {
            +    return sparse(test, 10)
            +  },
            +
            +  'sparseFE 10': function () {
            +    return sparseFE(test, 10)
            +  },
            +
            +  'sparseFE2 10': function () {
            +    return sparseFE2(test, 10)
            +  },
            +
            +  sigmund: function() {
            +    return sigmund(test, 10)
            +  },
            +
            +
            +  // 'util.inspect 1': function () {
            +  //   return util.inspect(test, false, 1, false)
            +  // },
            +  // 'util.inspect undefined': function () {
            +  //   util.inspect(test)
            +  // },
            +  // 'util.inspect 2': function () {
            +  //   util.inspect(test, false, 2, false)
            +  // },
            +  // 'util.inspect 3': function () {
            +  //   util.inspect(test, false, 3, false)
            +  // },
            +  // 'util.inspect 4': function () {
            +  //   util.inspect(test, false, 4, false)
            +  // },
            +  // 'util.inspect Infinity': function () {
            +  //   util.inspect(test, false, Infinity, false)
            +  // }
            +}
            +
            +/** results
            +**/
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/package.json
            new file mode 100644
            index 0000000..edb68a4
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/package.json
            @@ -0,0 +1,59 @@
            +{
            +  "name": "sigmund",
            +  "version": "1.0.1",
            +  "description": "Quick and dirty signatures for Objects.",
            +  "main": "sigmund.js",
            +  "directories": {
            +    "test": "test"
            +  },
            +  "dependencies": {},
            +  "devDependencies": {
            +    "tap": "~0.3.0"
            +  },
            +  "scripts": {
            +    "test": "tap test/*.js",
            +    "bench": "node bench.js"
            +  },
            +  "repository": {
            +    "type": "git",
            +    "url": "git://github.com/isaacs/sigmund.git"
            +  },
            +  "keywords": [
            +    "object",
            +    "signature",
            +    "key",
            +    "data",
            +    "psychoanalysis"
            +  ],
            +  "author": {
            +    "name": "Isaac Z. Schlueter",
            +    "email": "i@izs.me",
            +    "url": "http://blog.izs.me/"
            +  },
            +  "license": "ISC",
            +  "gitHead": "527f97aa5bb253d927348698c0cd3bb267d098c6",
            +  "bugs": {
            +    "url": "https://github.com/isaacs/sigmund/issues"
            +  },
            +  "homepage": "https://github.com/isaacs/sigmund#readme",
            +  "_id": "sigmund@1.0.1",
            +  "_shasum": "3ff21f198cad2175f9f3b781853fd94d0d19b590",
            +  "_from": "sigmund@>=1.0.0 <1.1.0",
            +  "_npmVersion": "2.10.0",
            +  "_nodeVersion": "2.0.1",
            +  "_npmUser": {
            +    "name": "isaacs",
            +    "email": "isaacs@npmjs.com"
            +  },
            +  "dist": {
            +    "shasum": "3ff21f198cad2175f9f3b781853fd94d0d19b590",
            +    "tarball": "http://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
            +  },
            +  "maintainers": [
            +    {
            +      "name": "isaacs",
            +      "email": "i@izs.me"
            +    }
            +  ],
            +  "_resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/sigmund.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/sigmund.js
            new file mode 100644
            index 0000000..82c7ab8
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/sigmund.js
            @@ -0,0 +1,39 @@
            +module.exports = sigmund
            +function sigmund (subject, maxSessions) {
            +    maxSessions = maxSessions || 10;
            +    var notes = [];
            +    var analysis = '';
            +    var RE = RegExp;
            +
            +    function psychoAnalyze (subject, session) {
            +        if (session > maxSessions) return;
            +
            +        if (typeof subject === 'function' ||
            +            typeof subject === 'undefined') {
            +            return;
            +        }
            +
            +        if (typeof subject !== 'object' || !subject ||
            +            (subject instanceof RE)) {
            +            analysis += subject;
            +            return;
            +        }
            +
            +        if (notes.indexOf(subject) !== -1 || session === maxSessions) return;
            +
            +        notes.push(subject);
            +        analysis += '{';
            +        Object.keys(subject).forEach(function (issue, _, __) {
            +            // pseudo-private values.  skip those.
            +            if (issue.charAt(0) === '_') return;
            +            var to = typeof subject[issue];
            +            if (to === 'function' || to === 'undefined') return;
            +            analysis += issue;
            +            psychoAnalyze(subject[issue], session + 1);
            +        });
            +    }
            +    psychoAnalyze(subject, 0);
            +    return analysis;
            +}
            +
            +// vim: set softtabstop=4 shiftwidth=4:
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/test/basic.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/test/basic.js
            new file mode 100644
            index 0000000..50c53a1
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/test/basic.js
            @@ -0,0 +1,24 @@
            +var test = require('tap').test
            +var sigmund = require('../sigmund.js')
            +
            +
            +// occasionally there are duplicates
            +// that's an acceptable edge-case.  JSON.stringify and util.inspect
            +// have some collision potential as well, though less, and collision
            +// detection is expensive.
            +var hash = '{abc/def/g{0h1i2{jkl'
            +var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]}
            +var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']}
            +
            +var obj3 = JSON.parse(JSON.stringify(obj1))
            +obj3.c = /def/
            +obj3.g[2].cycle = obj3
            +var cycleHash = '{abc/def/g{0h1i2{jklcycle'
            +
            +test('basic', function (t) {
            +    t.equal(sigmund(obj1), hash)
            +    t.equal(sigmund(obj2), hash)
            +    t.equal(sigmund(obj3), cycleHash)
            +    t.end()
            +})
            +
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/package.json
            new file mode 100644
            index 0000000..5acdd21
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/package.json
            @@ -0,0 +1,57 @@
            +{
            +  "author": {
            +    "name": "Isaac Z. Schlueter",
            +    "email": "i@izs.me",
            +    "url": "http://blog.izs.me"
            +  },
            +  "name": "minimatch",
            +  "description": "a glob matcher in javascript",
            +  "version": "0.2.14",
            +  "repository": {
            +    "type": "git",
            +    "url": "git://github.com/isaacs/minimatch.git"
            +  },
            +  "main": "minimatch.js",
            +  "scripts": {
            +    "test": "tap test/*.js"
            +  },
            +  "engines": {
            +    "node": "*"
            +  },
            +  "dependencies": {
            +    "lru-cache": "2",
            +    "sigmund": "~1.0.0"
            +  },
            +  "devDependencies": {
            +    "tap": ""
            +  },
            +  "license": {
            +    "type": "MIT",
            +    "url": "http://github.com/isaacs/minimatch/raw/master/LICENSE"
            +  },
            +  "bugs": {
            +    "url": "https://github.com/isaacs/minimatch/issues"
            +  },
            +  "homepage": "https://github.com/isaacs/minimatch",
            +  "_id": "minimatch@0.2.14",
            +  "dist": {
            +    "shasum": "c74e780574f63c6f9a090e90efbe6ef53a6a756a",
            +    "tarball": "http://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz"
            +  },
            +  "_from": "minimatch@>=0.2.11 <0.3.0",
            +  "_npmVersion": "1.3.17",
            +  "_npmUser": {
            +    "name": "isaacs",
            +    "email": "i@izs.me"
            +  },
            +  "maintainers": [
            +    {
            +      "name": "isaacs",
            +      "email": "i@izs.me"
            +    }
            +  ],
            +  "directories": {},
            +  "_shasum": "c74e780574f63c6f9a090e90efbe6ef53a6a756a",
            +  "_resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
            +  "readme": "ERROR: No README data found!"
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/basic.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/basic.js
            new file mode 100644
            index 0000000..ae7ac73
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/basic.js
            @@ -0,0 +1,399 @@
            +// http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test
            +//
            +// TODO: Some of these tests do very bad things with backslashes, and will
            +// most likely fail badly on windows.  They should probably be skipped.
            +
            +var tap = require("tap")
            +  , globalBefore = Object.keys(global)
            +  , mm = require("../")
            +  , files = [ "a", "b", "c", "d", "abc"
            +            , "abd", "abe", "bb", "bcd"
            +            , "ca", "cb", "dd", "de"
            +            , "bdir/", "bdir/cfile"]
            +  , next = files.concat([ "a-b", "aXb"
            +                        , ".x", ".y" ])
            +
            +
            +var patterns =
            +  [ "http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test"
            +  , ["a*", ["a", "abc", "abd", "abe"]]
            +  , ["X*", ["X*"], {nonull: true}]
            +
            +  // allow null glob expansion
            +  , ["X*", []]
            +
            +  // isaacs: Slightly different than bash/sh/ksh
            +  // \\* is not un-escaped to literal "*" in a failed match,
            +  // but it does make it get treated as a literal star
            +  , ["\\*", ["\\*"], {nonull: true}]
            +  , ["\\**", ["\\**"], {nonull: true}]
            +  , ["\\*\\*", ["\\*\\*"], {nonull: true}]
            +
            +  , ["b*/", ["bdir/"]]
            +  , ["c*", ["c", "ca", "cb"]]
            +  , ["**", files]
            +
            +  , ["\\.\\./*/", ["\\.\\./*/"], {nonull: true}]
            +  , ["s/\\..*//", ["s/\\..*//"], {nonull: true}]
            +
            +  , "legendary larry crashes bashes"
            +  , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"
            +    , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"], {nonull: true}]
            +  , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"
            +    , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"], {nonull: true}]
            +
            +  , "character classes"
            +  , ["[a-c]b*", ["abc", "abd", "abe", "bb", "cb"]]
            +  , ["[a-y]*[^c]", ["abd", "abe", "bb", "bcd",
            +     "bdir/", "ca", "cb", "dd", "de"]]
            +  , ["a*[^c]", ["abd", "abe"]]
            +  , function () { files.push("a-b", "aXb") }
            +  , ["a[X-]b", ["a-b", "aXb"]]
            +  , function () { files.push(".x", ".y") }
            +  , ["[^a-c]*", ["d", "dd", "de"]]
            +  , function () { files.push("a*b/", "a*b/ooo") }
            +  , ["a\\*b/*", ["a*b/ooo"]]
            +  , ["a\\*?/*", ["a*b/ooo"]]
            +  , ["*\\\\!*", [], {null: true}, ["echo !7"]]
            +  , ["*\\!*", ["echo !7"], null, ["echo !7"]]
            +  , ["*.\\*", ["r.*"], null, ["r.*"]]
            +  , ["a[b]c", ["abc"]]
            +  , ["a[\\b]c", ["abc"]]
            +  , ["a?c", ["abc"]]
            +  , ["a\\*c", [], {null: true}, ["abc"]]
            +  , ["", [""], { null: true }, [""]]
            +
            +  , "http://www.opensource.apple.com/source/bash/bash-23/" +
            +    "bash/tests/glob-test"
            +  , function () { files.push("man/", "man/man1/", "man/man1/bash.1") }
            +  , ["*/man*/bash.*", ["man/man1/bash.1"]]
            +  , ["man/man1/bash.1", ["man/man1/bash.1"]]
            +  , ["a***c", ["abc"], null, ["abc"]]
            +  , ["a*****?c", ["abc"], null, ["abc"]]
            +  , ["?*****??", ["abc"], null, ["abc"]]
            +  , ["*****??", ["abc"], null, ["abc"]]
            +  , ["?*****?c", ["abc"], null, ["abc"]]
            +  , ["?***?****c", ["abc"], null, ["abc"]]
            +  , ["?***?****?", ["abc"], null, ["abc"]]
            +  , ["?***?****", ["abc"], null, ["abc"]]
            +  , ["*******c", ["abc"], null, ["abc"]]
            +  , ["*******?", ["abc"], null, ["abc"]]
            +  , ["a*cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
            +  , ["a**?**cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
            +  , ["a**?**cd**?**??k***", ["abcdecdhjk"], null, ["abcdecdhjk"]]
            +  , ["a**?**cd**?**??***k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
            +  , ["a**?**cd**?**??***k**", ["abcdecdhjk"], null, ["abcdecdhjk"]]
            +  , ["a****c**?**??*****", ["abcdecdhjk"], null, ["abcdecdhjk"]]
            +  , ["[-abc]", ["-"], null, ["-"]]
            +  , ["[abc-]", ["-"], null, ["-"]]
            +  , ["\\", ["\\"], null, ["\\"]]
            +  , ["[\\\\]", ["\\"], null, ["\\"]]
            +  , ["[[]", ["["], null, ["["]]
            +  , ["[", ["["], null, ["["]]
            +  , ["[*", ["[abc"], null, ["[abc"]]
            +  , "a right bracket shall lose its special meaning and\n" +
            +    "represent itself in a bracket expression if it occurs\n" +
            +    "first in the list.  -- POSIX.2 2.8.3.2"
            +  , ["[]]", ["]"], null, ["]"]]
            +  , ["[]-]", ["]"], null, ["]"]]
            +  , ["[a-\z]", ["p"], null, ["p"]]
            +  , ["??**********?****?", [], { null: true }, ["abc"]]
            +  , ["??**********?****c", [], { null: true }, ["abc"]]
            +  , ["?************c****?****", [], { null: true }, ["abc"]]
            +  , ["*c*?**", [], { null: true }, ["abc"]]
            +  , ["a*****c*?**", [], { null: true }, ["abc"]]
            +  , ["a********???*******", [], { null: true }, ["abc"]]
            +  , ["[]", [], { null: true }, ["a"]]
            +  , ["[abc", [], { null: true }, ["["]]
            +
            +  , "nocase tests"
            +  , ["XYZ", ["xYz"], { nocase: true, null: true }
            +    , ["xYz", "ABC", "IjK"]]
            +  , ["ab*", ["ABC"], { nocase: true, null: true }
            +    , ["xYz", "ABC", "IjK"]]
            +  , ["[ia]?[ck]", ["ABC", "IjK"], { nocase: true, null: true }
            +    , ["xYz", "ABC", "IjK"]]
            +
            +  // [ pattern, [matches], MM opts, files, TAP opts]
            +  , "onestar/twostar"
            +  , ["{/*,*}", [], {null: true}, ["/asdf/asdf/asdf"]]
            +  , ["{/?,*}", ["/a", "bb"], {null: true}
            +    , ["/a", "/b/b", "/a/b/c", "bb"]]
            +
            +  , "dots should not match unless requested"
            +  , ["**", ["a/b"], {}, ["a/b", "a/.d", ".a/.d"]]
            +
            +  // .. and . can only match patterns starting with .,
            +  // even when options.dot is set.
            +  , function () {
            +      files = ["a/./b", "a/../b", "a/c/b", "a/.d/b"]
            +    }
            +  , ["a/*/b", ["a/c/b", "a/.d/b"], {dot: true}]
            +  , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: true}]
            +  , ["a/*/b", ["a/c/b"], {dot:false}]
            +  , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: false}]
            +
            +
            +  // this also tests that changing the options needs
            +  // to change the cache key, even if the pattern is
            +  // the same!
            +  , ["**", ["a/b","a/.d",".a/.d"], { dot: true }
            +    , [ ".a/.d", "a/.d", "a/b"]]
            +
            +  , "paren sets cannot contain slashes"
            +  , ["*(a/b)", ["*(a/b)"], {nonull: true}, ["a/b"]]
            +
            +  // brace sets trump all else.
            +  //
            +  // invalid glob pattern.  fails on bash4 and bsdglob.
            +  // however, in this implementation, it's easier just
            +  // to do the intuitive thing, and let brace-expansion
            +  // actually come before parsing any extglob patterns,
            +  // like the documentation seems to say.
            +  //
            +  // XXX: if anyone complains about this, either fix it
            +  // or tell them to grow up and stop complaining.
            +  //
            +  // bash/bsdglob says this:
            +  // , ["*(a|{b),c)}", ["*(a|{b),c)}"], {}, ["a", "ab", "ac", "ad"]]
            +  // but we do this instead:
            +  , ["*(a|{b),c)}", ["a", "ab", "ac"], {}, ["a", "ab", "ac", "ad"]]
            +
            +  // test partial parsing in the presence of comment/negation chars
            +  , ["[!a*", ["[!ab"], {}, ["[!ab", "[ab"]]
            +  , ["[#a*", ["[#ab"], {}, ["[#ab", "[ab"]]
            +
            +  // like: {a,b|c\\,d\\\|e} except it's unclosed, so it has to be escaped.
            +  , ["+(a|*\\|c\\\\|d\\\\\\|e\\\\\\\\|f\\\\\\\\\\|g"
            +    , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g"]
            +    , {}
            +    , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g", "a", "b\\c"]]
            +
            +
            +  // crazy nested {,,} and *(||) tests.
            +  , function () {
            +      files = [ "a", "b", "c", "d"
            +              , "ab", "ac", "ad"
            +              , "bc", "cb"
            +              , "bc,d", "c,db", "c,d"
            +              , "d)", "(b|c", "*(b|c"
            +              , "b|c", "b|cc", "cb|c"
            +              , "x(a|b|c)", "x(a|c)"
            +              , "(a|b|c)", "(a|c)"]
            +    }
            +  , ["*(a|{b,c})", ["a", "b", "c", "ab", "ac"]]
            +  , ["{a,*(b|c,d)}", ["a","(b|c", "*(b|c", "d)"]]
            +  // a
            +  // *(b|c)
            +  // *(b|d)
            +  , ["{a,*(b|{c,d})}", ["a","b", "bc", "cb", "c", "d"]]
            +  , ["*(a|{b|c,c})", ["a", "b", "c", "ab", "ac", "bc", "cb"]]
            +
            +
            +  // test various flag settings.
            +  , [ "*(a|{b|c,c})", ["x(a|b|c)", "x(a|c)", "(a|b|c)", "(a|c)"]
            +    , { noext: true } ]
            +  , ["a?b", ["x/y/acb", "acb/"], {matchBase: true}
            +    , ["x/y/acb", "acb/", "acb/d/e", "x/y/acb/d"] ]
            +  , ["#*", ["#a", "#b"], {nocomment: true}, ["#a", "#b", "c#d"]]
            +
            +
            +  // begin channelling Boole and deMorgan...
            +  , "negation tests"
            +  , function () {
            +      files = ["d", "e", "!ab", "!abc", "a!b", "\\!a"]
            +    }
            +
            +  // anything that is NOT a* matches.
            +  , ["!a*", ["\\!a", "d", "e", "!ab", "!abc"]]
            +
            +  // anything that IS !a* matches.
            +  , ["!a*", ["!ab", "!abc"], {nonegate: true}]
            +
            +  // anything that IS a* matches
            +  , ["!!a*", ["a!b"]]
            +
            +  // anything that is NOT !a* matches
            +  , ["!\\!a*", ["a!b", "d", "e", "\\!a"]]
            +
            +  // negation nestled within a pattern
            +  , function () {
            +      files = [ "foo.js"
            +              , "foo.bar"
            +              // can't match this one without negative lookbehind.
            +              , "foo.js.js"
            +              , "blar.js"
            +              , "foo."
            +              , "boo.js.boo" ]
            +    }
            +  , ["*.!(js)", ["foo.bar", "foo.", "boo.js.boo"] ]
            +
            +  // https://github.com/isaacs/minimatch/issues/5
            +  , function () {
            +      files = [ 'a/b/.x/c'
            +              , 'a/b/.x/c/d'
            +              , 'a/b/.x/c/d/e'
            +              , 'a/b/.x'
            +              , 'a/b/.x/'
            +              , 'a/.x/b'
            +              , '.x'
            +              , '.x/'
            +              , '.x/a'
            +              , '.x/a/b'
            +              , 'a/.x/b/.x/c'
            +              , '.x/.x' ]
            +  }
            +  , ["**/.x/**", [ '.x/'
            +                 , '.x/a'
            +                 , '.x/a/b'
            +                 , 'a/.x/b'
            +                 , 'a/b/.x/'
            +                 , 'a/b/.x/c'
            +                 , 'a/b/.x/c/d'
            +                 , 'a/b/.x/c/d/e' ] ]
            +
            +  ]
            +
            +var regexps =
            +  [ '/^(?:(?=.)a[^/]*?)$/',
            +    '/^(?:(?=.)X[^/]*?)$/',
            +    '/^(?:(?=.)X[^/]*?)$/',
            +    '/^(?:\\*)$/',
            +    '/^(?:(?=.)\\*[^/]*?)$/',
            +    '/^(?:\\*\\*)$/',
            +    '/^(?:(?=.)b[^/]*?\\/)$/',
            +    '/^(?:(?=.)c[^/]*?)$/',
            +    '/^(?:(?:(?!(?:\\/|^)\\.).)*?)$/',
            +    '/^(?:\\.\\.\\/(?!\\.)(?=.)[^/]*?\\/)$/',
            +    '/^(?:s\\/(?=.)\\.\\.[^/]*?\\/)$/',
            +    '/^(?:\\/\\^root:\\/\\{s\\/(?=.)\\^[^:][^/]*?:[^:][^/]*?:\\([^:]\\)[^/]*?\\.[^/]*?\\$\\/1\\/)$/',
            +    '/^(?:\\/\\^root:\\/\\{s\\/(?=.)\\^[^:][^/]*?:[^:][^/]*?:\\([^:]\\)[^/]*?\\.[^/]*?\\$\\/\u0001\\/)$/',
            +    '/^(?:(?!\\.)(?=.)[a-c]b[^/]*?)$/',
            +    '/^(?:(?!\\.)(?=.)[a-y][^/]*?[^c])$/',
            +    '/^(?:(?=.)a[^/]*?[^c])$/',
            +    '/^(?:(?=.)a[X-]b)$/',
            +    '/^(?:(?!\\.)(?=.)[^a-c][^/]*?)$/',
            +    '/^(?:a\\*b\\/(?!\\.)(?=.)[^/]*?)$/',
            +    '/^(?:(?=.)a\\*[^/]\\/(?!\\.)(?=.)[^/]*?)$/',
            +    '/^(?:(?!\\.)(?=.)[^/]*?\\\\\\![^/]*?)$/',
            +    '/^(?:(?!\\.)(?=.)[^/]*?\\![^/]*?)$/',
            +    '/^(?:(?!\\.)(?=.)[^/]*?\\.\\*)$/',
            +    '/^(?:(?=.)a[b]c)$/',
            +    '/^(?:(?=.)a[b]c)$/',
            +    '/^(?:(?=.)a[^/]c)$/',
            +    '/^(?:a\\*c)$/',
            +    'false',
            +    '/^(?:(?!\\.)(?=.)[^/]*?\\/(?=.)man[^/]*?\\/(?=.)bash\\.[^/]*?)$/',
            +    '/^(?:man\\/man1\\/bash\\.1)$/',
            +    '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?c)$/',
            +    '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]c)$/',
            +    '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/])$/',
            +    '/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/])$/',
            +    '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]c)$/',
            +    '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?c)$/',
            +    '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/])$/',
            +    '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?)$/',
            +    '/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c)$/',
            +    '/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/])$/',
            +    '/^(?:(?=.)a[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k)$/',
            +    '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k)$/',
            +    '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k[^/]*?[^/]*?[^/]*?)$/',
            +    '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?k)$/',
            +    '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?k[^/]*?[^/]*?)$/',
            +    '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?)$/',
            +    '/^(?:(?!\\.)(?=.)[-abc])$/',
            +    '/^(?:(?!\\.)(?=.)[abc-])$/',
            +    '/^(?:\\\\)$/',
            +    '/^(?:(?!\\.)(?=.)[\\\\])$/',
            +    '/^(?:(?!\\.)(?=.)[\\[])$/',
            +    '/^(?:\\[)$/',
            +    '/^(?:(?=.)\\[(?!\\.)(?=.)[^/]*?)$/',
            +    '/^(?:(?!\\.)(?=.)[\\]])$/',
            +    '/^(?:(?!\\.)(?=.)[\\]-])$/',
            +    '/^(?:(?!\\.)(?=.)[a-z])$/',
            +    '/^(?:(?!\\.)(?=.)[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/])$/',
            +    '/^(?:(?!\\.)(?=.)[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?c)$/',
            +    '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?)$/',
            +    '/^(?:(?!\\.)(?=.)[^/]*?c[^/]*?[^/][^/]*?[^/]*?)$/',
            +    '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/][^/]*?[^/]*?)$/',
            +    '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?)$/',
            +    '/^(?:\\[\\])$/',
            +    '/^(?:\\[abc)$/',
            +    '/^(?:(?=.)XYZ)$/i',
            +    '/^(?:(?=.)ab[^/]*?)$/i',
            +    '/^(?:(?!\\.)(?=.)[ia][^/][ck])$/i',
            +    '/^(?:\\/(?!\\.)(?=.)[^/]*?|(?!\\.)(?=.)[^/]*?)$/',
            +    '/^(?:\\/(?!\\.)(?=.)[^/]|(?!\\.)(?=.)[^/]*?)$/',
            +    '/^(?:(?:(?!(?:\\/|^)\\.).)*?)$/',
            +    '/^(?:a\\/(?!(?:^|\\/)\\.{1,2}(?:$|\\/))(?=.)[^/]*?\\/b)$/',
            +    '/^(?:a\\/(?=.)\\.[^/]*?\\/b)$/',
            +    '/^(?:a\\/(?!\\.)(?=.)[^/]*?\\/b)$/',
            +    '/^(?:a\\/(?=.)\\.[^/]*?\\/b)$/',
            +    '/^(?:(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?)$/',
            +    '/^(?:(?!\\.)(?=.)[^/]*?\\(a\\/b\\))$/',
            +    '/^(?:(?!\\.)(?=.)(?:a|b)*|(?!\\.)(?=.)(?:a|c)*)$/',
            +    '/^(?:(?=.)\\[(?=.)\\!a[^/]*?)$/',
            +    '/^(?:(?=.)\\[(?=.)#a[^/]*?)$/',
            +    '/^(?:(?=.)\\+\\(a\\|[^/]*?\\|c\\\\\\\\\\|d\\\\\\\\\\|e\\\\\\\\\\\\\\\\\\|f\\\\\\\\\\\\\\\\\\|g)$/',
            +    '/^(?:(?!\\.)(?=.)(?:a|b)*|(?!\\.)(?=.)(?:a|c)*)$/',
            +    '/^(?:a|(?!\\.)(?=.)[^/]*?\\(b\\|c|d\\))$/',
            +    '/^(?:a|(?!\\.)(?=.)(?:b|c)*|(?!\\.)(?=.)(?:b|d)*)$/',
            +    '/^(?:(?!\\.)(?=.)(?:a|b|c)*|(?!\\.)(?=.)(?:a|c)*)$/',
            +    '/^(?:(?!\\.)(?=.)[^/]*?\\(a\\|b\\|c\\)|(?!\\.)(?=.)[^/]*?\\(a\\|c\\))$/',
            +    '/^(?:(?=.)a[^/]b)$/',
            +    '/^(?:(?=.)#[^/]*?)$/',
            +    '/^(?!^(?:(?=.)a[^/]*?)$).*$/',
            +    '/^(?:(?=.)\\!a[^/]*?)$/',
            +    '/^(?:(?=.)a[^/]*?)$/',
            +    '/^(?!^(?:(?=.)\\!a[^/]*?)$).*$/',
            +    '/^(?:(?!\\.)(?=.)[^/]*?\\.(?:(?!js)[^/]*?))$/',
            +    '/^(?:(?:(?!(?:\\/|^)\\.).)*?\\/\\.x\\/(?:(?!(?:\\/|^)\\.).)*?)$/' ]
            +var re = 0;
            +
            +tap.test("basic tests", function (t) {
            +  var start = Date.now()
            +
            +  // [ pattern, [matches], MM opts, files, TAP opts]
            +  patterns.forEach(function (c) {
            +    if (typeof c === "function") return c()
            +    if (typeof c === "string") return t.comment(c)
            +
            +    var pattern = c[0]
            +      , expect = c[1].sort(alpha)
            +      , options = c[2] || {}
            +      , f = c[3] || files
            +      , tapOpts = c[4] || {}
            +
            +    // options.debug = true
            +    var m = new mm.Minimatch(pattern, options)
            +    var r = m.makeRe()
            +    var expectRe = regexps[re++]
            +    tapOpts.re = String(r) || JSON.stringify(r)
            +    tapOpts.files = JSON.stringify(f)
            +    tapOpts.pattern = pattern
            +    tapOpts.set = m.set
            +    tapOpts.negated = m.negate
            +
            +    var actual = mm.match(f, pattern, options)
            +    actual.sort(alpha)
            +
            +    t.equivalent( actual, expect
            +                , JSON.stringify(pattern) + " " + JSON.stringify(expect)
            +                , tapOpts )
            +
            +    t.equal(tapOpts.re, expectRe, tapOpts)
            +  })
            +
            +  t.comment("time=" + (Date.now() - start) + "ms")
            +  t.end()
            +})
            +
            +tap.test("global leak test", function (t) {
            +  var globalAfter = Object.keys(global)
            +  t.equivalent(globalAfter, globalBefore, "no new globals, please")
            +  t.end()
            +})
            +
            +function alpha (a, b) {
            +  return a > b ? 1 : -1
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/brace-expand.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/brace-expand.js
            new file mode 100644
            index 0000000..7ee278a
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/brace-expand.js
            @@ -0,0 +1,33 @@
            +var tap = require("tap")
            +  , minimatch = require("../")
            +
            +tap.test("brace expansion", function (t) {
            +  // [ pattern, [expanded] ]
            +  ; [ [ "a{b,c{d,e},{f,g}h}x{y,z}"
            +      , [ "abxy"
            +        , "abxz"
            +        , "acdxy"
            +        , "acdxz"
            +        , "acexy"
            +        , "acexz"
            +        , "afhxy"
            +        , "afhxz"
            +        , "aghxy"
            +        , "aghxz" ] ]
            +    , [ "a{1..5}b"
            +      , [ "a1b"
            +        , "a2b"
            +        , "a3b"
            +        , "a4b"
            +        , "a5b" ] ]
            +    , [ "a{b}c", ["a{b}c"] ]
            +  ].forEach(function (tc) {
            +    var p = tc[0]
            +      , expect = tc[1]
            +    t.equivalent(minimatch.braceExpand(p), expect, p)
            +  })
            +  console.error("ending")
            +  t.end()
            +})
            +
            +
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/caching.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/caching.js
            new file mode 100644
            index 0000000..0fec4b0
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/caching.js
            @@ -0,0 +1,14 @@
            +var Minimatch = require("../minimatch.js").Minimatch
            +var tap = require("tap")
            +tap.test("cache test", function (t) {
            +  var mm1 = new Minimatch("a?b")
            +  var mm2 = new Minimatch("a?b")
            +  t.equal(mm1, mm2, "should get the same object")
            +  // the lru should drop it after 100 entries
            +  for (var i = 0; i < 100; i ++) {
            +    new Minimatch("a"+i)
            +  }
            +  mm2 = new Minimatch("a?b")
            +  t.notEqual(mm1, mm2, "cache should have dropped")
            +  t.end()
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/defaults.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/defaults.js
            new file mode 100644
            index 0000000..25f1f60
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/defaults.js
            @@ -0,0 +1,274 @@
            +// http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test
            +//
            +// TODO: Some of these tests do very bad things with backslashes, and will
            +// most likely fail badly on windows.  They should probably be skipped.
            +
            +var tap = require("tap")
            +  , globalBefore = Object.keys(global)
            +  , mm = require("../")
            +  , files = [ "a", "b", "c", "d", "abc"
            +            , "abd", "abe", "bb", "bcd"
            +            , "ca", "cb", "dd", "de"
            +            , "bdir/", "bdir/cfile"]
            +  , next = files.concat([ "a-b", "aXb"
            +                        , ".x", ".y" ])
            +
            +tap.test("basic tests", function (t) {
            +  var start = Date.now()
            +
            +  // [ pattern, [matches], MM opts, files, TAP opts]
            +  ; [ "http://www.bashcookbook.com/bashinfo" +
            +      "/source/bash-1.14.7/tests/glob-test"
            +    , ["a*", ["a", "abc", "abd", "abe"]]
            +    , ["X*", ["X*"], {nonull: true}]
            +
            +    // allow null glob expansion
            +    , ["X*", []]
            +
            +    // isaacs: Slightly different than bash/sh/ksh
            +    // \\* is not un-escaped to literal "*" in a failed match,
            +    // but it does make it get treated as a literal star
            +    , ["\\*", ["\\*"], {nonull: true}]
            +    , ["\\**", ["\\**"], {nonull: true}]
            +    , ["\\*\\*", ["\\*\\*"], {nonull: true}]
            +
            +    , ["b*/", ["bdir/"]]
            +    , ["c*", ["c", "ca", "cb"]]
            +    , ["**", files]
            +
            +    , ["\\.\\./*/", ["\\.\\./*/"], {nonull: true}]
            +    , ["s/\\..*//", ["s/\\..*//"], {nonull: true}]
            +
            +    , "legendary larry crashes bashes"
            +    , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"
            +      , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"], {nonull: true}]
            +    , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"
            +      , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"], {nonull: true}]
            +
            +    , "character classes"
            +    , ["[a-c]b*", ["abc", "abd", "abe", "bb", "cb"]]
            +    , ["[a-y]*[^c]", ["abd", "abe", "bb", "bcd",
            +       "bdir/", "ca", "cb", "dd", "de"]]
            +    , ["a*[^c]", ["abd", "abe"]]
            +    , function () { files.push("a-b", "aXb") }
            +    , ["a[X-]b", ["a-b", "aXb"]]
            +    , function () { files.push(".x", ".y") }
            +    , ["[^a-c]*", ["d", "dd", "de"]]
            +    , function () { files.push("a*b/", "a*b/ooo") }
            +    , ["a\\*b/*", ["a*b/ooo"]]
            +    , ["a\\*?/*", ["a*b/ooo"]]
            +    , ["*\\\\!*", [], {null: true}, ["echo !7"]]
            +    , ["*\\!*", ["echo !7"], null, ["echo !7"]]
            +    , ["*.\\*", ["r.*"], null, ["r.*"]]
            +    , ["a[b]c", ["abc"]]
            +    , ["a[\\b]c", ["abc"]]
            +    , ["a?c", ["abc"]]
            +    , ["a\\*c", [], {null: true}, ["abc"]]
            +    , ["", [""], { null: true }, [""]]
            +
            +    , "http://www.opensource.apple.com/source/bash/bash-23/" +
            +      "bash/tests/glob-test"
            +    , function () { files.push("man/", "man/man1/", "man/man1/bash.1") }
            +    , ["*/man*/bash.*", ["man/man1/bash.1"]]
            +    , ["man/man1/bash.1", ["man/man1/bash.1"]]
            +    , ["a***c", ["abc"], null, ["abc"]]
            +    , ["a*****?c", ["abc"], null, ["abc"]]
            +    , ["?*****??", ["abc"], null, ["abc"]]
            +    , ["*****??", ["abc"], null, ["abc"]]
            +    , ["?*****?c", ["abc"], null, ["abc"]]
            +    , ["?***?****c", ["abc"], null, ["abc"]]
            +    , ["?***?****?", ["abc"], null, ["abc"]]
            +    , ["?***?****", ["abc"], null, ["abc"]]
            +    , ["*******c", ["abc"], null, ["abc"]]
            +    , ["*******?", ["abc"], null, ["abc"]]
            +    , ["a*cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
            +    , ["a**?**cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
            +    , ["a**?**cd**?**??k***", ["abcdecdhjk"], null, ["abcdecdhjk"]]
            +    , ["a**?**cd**?**??***k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
            +    , ["a**?**cd**?**??***k**", ["abcdecdhjk"], null, ["abcdecdhjk"]]
            +    , ["a****c**?**??*****", ["abcdecdhjk"], null, ["abcdecdhjk"]]
            +    , ["[-abc]", ["-"], null, ["-"]]
            +    , ["[abc-]", ["-"], null, ["-"]]
            +    , ["\\", ["\\"], null, ["\\"]]
            +    , ["[\\\\]", ["\\"], null, ["\\"]]
            +    , ["[[]", ["["], null, ["["]]
            +    , ["[", ["["], null, ["["]]
            +    , ["[*", ["[abc"], null, ["[abc"]]
            +    , "a right bracket shall lose its special meaning and\n" +
            +      "represent itself in a bracket expression if it occurs\n" +
            +      "first in the list.  -- POSIX.2 2.8.3.2"
            +    , ["[]]", ["]"], null, ["]"]]
            +    , ["[]-]", ["]"], null, ["]"]]
            +    , ["[a-\z]", ["p"], null, ["p"]]
            +    , ["??**********?****?", [], { null: true }, ["abc"]]
            +    , ["??**********?****c", [], { null: true }, ["abc"]]
            +    , ["?************c****?****", [], { null: true }, ["abc"]]
            +    , ["*c*?**", [], { null: true }, ["abc"]]
            +    , ["a*****c*?**", [], { null: true }, ["abc"]]
            +    , ["a********???*******", [], { null: true }, ["abc"]]
            +    , ["[]", [], { null: true }, ["a"]]
            +    , ["[abc", [], { null: true }, ["["]]
            +
            +    , "nocase tests"
            +    , ["XYZ", ["xYz"], { nocase: true, null: true }
            +      , ["xYz", "ABC", "IjK"]]
            +    , ["ab*", ["ABC"], { nocase: true, null: true }
            +      , ["xYz", "ABC", "IjK"]]
            +    , ["[ia]?[ck]", ["ABC", "IjK"], { nocase: true, null: true }
            +      , ["xYz", "ABC", "IjK"]]
            +
            +    // [ pattern, [matches], MM opts, files, TAP opts]
            +    , "onestar/twostar"
            +    , ["{/*,*}", [], {null: true}, ["/asdf/asdf/asdf"]]
            +    , ["{/?,*}", ["/a", "bb"], {null: true}
            +      , ["/a", "/b/b", "/a/b/c", "bb"]]
            +
            +    , "dots should not match unless requested"
            +    , ["**", ["a/b"], {}, ["a/b", "a/.d", ".a/.d"]]
            +
            +    // .. and . can only match patterns starting with .,
            +    // even when options.dot is set.
            +    , function () {
            +        files = ["a/./b", "a/../b", "a/c/b", "a/.d/b"]
            +      }
            +    , ["a/*/b", ["a/c/b", "a/.d/b"], {dot: true}]
            +    , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: true}]
            +    , ["a/*/b", ["a/c/b"], {dot:false}]
            +    , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: false}]
            +
            +
            +    // this also tests that changing the options needs
            +    // to change the cache key, even if the pattern is
            +    // the same!
            +    , ["**", ["a/b","a/.d",".a/.d"], { dot: true }
            +      , [ ".a/.d", "a/.d", "a/b"]]
            +
            +    , "paren sets cannot contain slashes"
            +    , ["*(a/b)", ["*(a/b)"], {nonull: true}, ["a/b"]]
            +
            +    // brace sets trump all else.
            +    //
            +    // invalid glob pattern.  fails on bash4 and bsdglob.
            +    // however, in this implementation, it's easier just
            +    // to do the intuitive thing, and let brace-expansion
            +    // actually come before parsing any extglob patterns,
            +    // like the documentation seems to say.
            +    //
            +    // XXX: if anyone complains about this, either fix it
            +    // or tell them to grow up and stop complaining.
            +    //
            +    // bash/bsdglob says this:
            +    // , ["*(a|{b),c)}", ["*(a|{b),c)}"], {}, ["a", "ab", "ac", "ad"]]
            +    // but we do this instead:
            +    , ["*(a|{b),c)}", ["a", "ab", "ac"], {}, ["a", "ab", "ac", "ad"]]
            +
            +    // test partial parsing in the presence of comment/negation chars
            +    , ["[!a*", ["[!ab"], {}, ["[!ab", "[ab"]]
            +    , ["[#a*", ["[#ab"], {}, ["[#ab", "[ab"]]
            +
            +    // like: {a,b|c\\,d\\\|e} except it's unclosed, so it has to be escaped.
            +    , ["+(a|*\\|c\\\\|d\\\\\\|e\\\\\\\\|f\\\\\\\\\\|g"
            +      , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g"]
            +      , {}
            +      , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g", "a", "b\\c"]]
            +
            +
            +    // crazy nested {,,} and *(||) tests.
            +    , function () {
            +        files = [ "a", "b", "c", "d"
            +                , "ab", "ac", "ad"
            +                , "bc", "cb"
            +                , "bc,d", "c,db", "c,d"
            +                , "d)", "(b|c", "*(b|c"
            +                , "b|c", "b|cc", "cb|c"
            +                , "x(a|b|c)", "x(a|c)"
            +                , "(a|b|c)", "(a|c)"]
            +      }
            +    , ["*(a|{b,c})", ["a", "b", "c", "ab", "ac"]]
            +    , ["{a,*(b|c,d)}", ["a","(b|c", "*(b|c", "d)"]]
            +    // a
            +    // *(b|c)
            +    // *(b|d)
            +    , ["{a,*(b|{c,d})}", ["a","b", "bc", "cb", "c", "d"]]
            +    , ["*(a|{b|c,c})", ["a", "b", "c", "ab", "ac", "bc", "cb"]]
            +
            +
            +    // test various flag settings.
            +    , [ "*(a|{b|c,c})", ["x(a|b|c)", "x(a|c)", "(a|b|c)", "(a|c)"]
            +      , { noext: true } ]
            +    , ["a?b", ["x/y/acb", "acb/"], {matchBase: true}
            +      , ["x/y/acb", "acb/", "acb/d/e", "x/y/acb/d"] ]
            +    , ["#*", ["#a", "#b"], {nocomment: true}, ["#a", "#b", "c#d"]]
            +
            +
            +    // begin channelling Boole and deMorgan...
            +    , "negation tests"
            +    , function () {
            +        files = ["d", "e", "!ab", "!abc", "a!b", "\\!a"]
            +      }
            +
            +    // anything that is NOT a* matches.
            +    , ["!a*", ["\\!a", "d", "e", "!ab", "!abc"]]
            +
            +    // anything that IS !a* matches.
            +    , ["!a*", ["!ab", "!abc"], {nonegate: true}]
            +
            +    // anything that IS a* matches
            +    , ["!!a*", ["a!b"]]
            +
            +    // anything that is NOT !a* matches
            +    , ["!\\!a*", ["a!b", "d", "e", "\\!a"]]
            +
            +    // negation nestled within a pattern
            +    , function () {
            +        files = [ "foo.js"
            +                , "foo.bar"
            +                // can't match this one without negative lookbehind.
            +                , "foo.js.js"
            +                , "blar.js"
            +                , "foo."
            +                , "boo.js.boo" ]
            +      }
            +    , ["*.!(js)", ["foo.bar", "foo.", "boo.js.boo"] ]
            +
            +    ].forEach(function (c) {
            +      if (typeof c === "function") return c()
            +      if (typeof c === "string") return t.comment(c)
            +
            +      var pattern = c[0]
            +        , expect = c[1].sort(alpha)
            +        , options = c[2] || {}
            +        , f = c[3] || files
            +        , tapOpts = c[4] || {}
            +
            +      // options.debug = true
            +      var Class = mm.defaults(options).Minimatch
            +      var m = new Class(pattern, {})
            +      var r = m.makeRe()
            +      tapOpts.re = String(r) || JSON.stringify(r)
            +      tapOpts.files = JSON.stringify(f)
            +      tapOpts.pattern = pattern
            +      tapOpts.set = m.set
            +      tapOpts.negated = m.negate
            +
            +      var actual = mm.match(f, pattern, options)
            +      actual.sort(alpha)
            +
            +      t.equivalent( actual, expect
            +                  , JSON.stringify(pattern) + " " + JSON.stringify(expect)
            +                  , tapOpts )
            +    })
            +
            +  t.comment("time=" + (Date.now() - start) + "ms")
            +  t.end()
            +})
            +
            +tap.test("global leak test", function (t) {
            +  var globalAfter = Object.keys(global)
            +  t.equivalent(globalAfter, globalBefore, "no new globals, please")
            +  t.end()
            +})
            +
            +function alpha (a, b) {
            +  return a > b ? 1 : -1
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/extglob-ending-with-state-char.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/extglob-ending-with-state-char.js
            new file mode 100644
            index 0000000..6676e26
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/node_modules/minimatch/test/extglob-ending-with-state-char.js
            @@ -0,0 +1,8 @@
            +var test = require('tap').test
            +var minimatch = require('../')
            +
            +test('extglob ending with statechar', function(t) {
            +  t.notOk(minimatch('ax', 'a?(b*)'))
            +  t.ok(minimatch('ax', '?(a*|b)'))
            +  t.end()
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/package.json
            new file mode 100644
            index 0000000..a768fe2
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/package.json
            @@ -0,0 +1,57 @@
            +{
            +  "author": {
            +    "name": "Isaac Z. Schlueter",
            +    "email": "i@izs.me",
            +    "url": "http://blog.izs.me/"
            +  },
            +  "name": "glob",
            +  "description": "a little globber",
            +  "version": "3.2.3",
            +  "repository": {
            +    "type": "git",
            +    "url": "git://github.com/isaacs/node-glob.git"
            +  },
            +  "main": "glob.js",
            +  "engines": {
            +    "node": "*"
            +  },
            +  "dependencies": {
            +    "minimatch": "~0.2.11",
            +    "graceful-fs": "~2.0.0",
            +    "inherits": "2"
            +  },
            +  "devDependencies": {
            +    "tap": "~0.4.0",
            +    "mkdirp": "0",
            +    "rimraf": "1"
            +  },
            +  "scripts": {
            +    "test": "tap test/*.js"
            +  },
            +  "license": "BSD",
            +  "bugs": {
            +    "url": "https://github.com/isaacs/node-glob/issues"
            +  },
            +  "_id": "glob@3.2.3",
            +  "dist": {
            +    "shasum": "e313eeb249c7affaa5c475286b0e115b59839467",
            +    "tarball": "http://registry.npmjs.org/glob/-/glob-3.2.3.tgz"
            +  },
            +  "_from": "glob@3.2.3",
            +  "_npmVersion": "1.3.2",
            +  "_npmUser": {
            +    "name": "isaacs",
            +    "email": "i@izs.me"
            +  },
            +  "maintainers": [
            +    {
            +      "name": "isaacs",
            +      "email": "i@izs.me"
            +    }
            +  ],
            +  "directories": {},
            +  "_shasum": "e313eeb249c7affaa5c475286b0e115b59839467",
            +  "_resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz",
            +  "readme": "ERROR: No README data found!",
            +  "homepage": "https://github.com/isaacs/node-glob#readme"
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/00-setup.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/00-setup.js
            new file mode 100644
            index 0000000..245afaf
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/00-setup.js
            @@ -0,0 +1,176 @@
            +// just a little pre-run script to set up the fixtures.
            +// zz-finish cleans it up
            +
            +var mkdirp = require("mkdirp")
            +var path = require("path")
            +var i = 0
            +var tap = require("tap")
            +var fs = require("fs")
            +var rimraf = require("rimraf")
            +
            +var files =
            +[ "a/.abcdef/x/y/z/a"
            +, "a/abcdef/g/h"
            +, "a/abcfed/g/h"
            +, "a/b/c/d"
            +, "a/bc/e/f"
            +, "a/c/d/c/b"
            +, "a/cb/e/f"
            +]
            +
            +var symlinkTo = path.resolve(__dirname, "a/symlink/a/b/c")
            +var symlinkFrom = "../.."
            +
            +files = files.map(function (f) {
            +  return path.resolve(__dirname, f)
            +})
            +
            +tap.test("remove fixtures", function (t) {
            +  rimraf(path.resolve(__dirname, "a"), function (er) {
            +    t.ifError(er, "remove fixtures")
            +    t.end()
            +  })
            +})
            +
            +files.forEach(function (f) {
            +  tap.test(f, function (t) {
            +    var d = path.dirname(f)
            +    mkdirp(d, 0755, function (er) {
            +      if (er) {
            +        t.fail(er)
            +        return t.bailout()
            +      }
            +      fs.writeFile(f, "i like tests", function (er) {
            +        t.ifError(er, "make file")
            +        t.end()
            +      })
            +    })
            +  })
            +})
            +
            +if (process.platform !== "win32") {
            +  tap.test("symlinky", function (t) {
            +    var d = path.dirname(symlinkTo)
            +    console.error("mkdirp", d)
            +    mkdirp(d, 0755, function (er) {
            +      t.ifError(er)
            +      fs.symlink(symlinkFrom, symlinkTo, "dir", function (er) {
            +        t.ifError(er, "make symlink")
            +        t.end()
            +      })
            +    })
            +  })
            +}
            +
            +;["foo","bar","baz","asdf","quux","qwer","rewq"].forEach(function (w) {
            +  w = "/tmp/glob-test/" + w
            +  tap.test("create " + w, function (t) {
            +    mkdirp(w, function (er) {
            +      if (er)
            +        throw er
            +      t.pass(w)
            +      t.end()
            +    })
            +  })
            +})
            +
            +
            +// generate the bash pattern test-fixtures if possible
            +if (process.platform === "win32" || !process.env.TEST_REGEN) {
            +  console.error("Windows, or TEST_REGEN unset.  Using cached fixtures.")
            +  return
            +}
            +
            +var spawn = require("child_process").spawn;
            +var globs =
            +  // put more patterns here.
            +  // anything that would be directly in / should be in /tmp/glob-test
            +  ["test/a/*/+(c|g)/./d"
            +  ,"test/a/**/[cg]/../[cg]"
            +  ,"test/a/{b,c,d,e,f}/**/g"
            +  ,"test/a/b/**"
            +  ,"test/**/g"
            +  ,"test/a/abc{fed,def}/g/h"
            +  ,"test/a/abc{fed/g,def}/**/"
            +  ,"test/a/abc{fed/g,def}/**///**/"
            +  ,"test/**/a/**/"
            +  ,"test/+(a|b|c)/a{/,bc*}/**"
            +  ,"test/*/*/*/f"
            +  ,"test/**/f"
            +  ,"test/a/symlink/a/b/c/a/b/c/a/b/c//a/b/c////a/b/c/**/b/c/**"
            +  ,"{./*/*,/tmp/glob-test/*}"
            +  ,"{/tmp/glob-test/*,*}" // evil owl face!  how you taunt me!
            +  ,"test/a/!(symlink)/**"
            +  ]
            +var bashOutput = {}
            +var fs = require("fs")
            +
            +globs.forEach(function (pattern) {
            +  tap.test("generate fixture " + pattern, function (t) {
            +    var cmd = "shopt -s globstar && " +
            +              "shopt -s extglob && " +
            +              "shopt -s nullglob && " +
            +              // "shopt >&2; " +
            +              "eval \'for i in " + pattern + "; do echo $i; done\'"
            +    var cp = spawn("bash", ["-c", cmd], { cwd: path.dirname(__dirname) })
            +    var out = []
            +    cp.stdout.on("data", function (c) {
            +      out.push(c)
            +    })
            +    cp.stderr.pipe(process.stderr)
            +    cp.on("close", function (code) {
            +      out = flatten(out)
            +      if (!out)
            +        out = []
            +      else
            +        out = cleanResults(out.split(/\r*\n/))
            +
            +      bashOutput[pattern] = out
            +      t.notOk(code, "bash test should finish nicely")
            +      t.end()
            +    })
            +  })
            +})
            +
            +tap.test("save fixtures", function (t) {
            +  var fname = path.resolve(__dirname, "bash-results.json")
            +  var data = JSON.stringify(bashOutput, null, 2) + "\n"
            +  fs.writeFile(fname, data, function (er) {
            +    t.ifError(er)
            +    t.end()
            +  })
            +})
            +
            +function cleanResults (m) {
            +  // normalize discrepancies in ordering, duplication,
            +  // and ending slashes.
            +  return m.map(function (m) {
            +    return m.replace(/\/+/g, "/").replace(/\/$/, "")
            +  }).sort(alphasort).reduce(function (set, f) {
            +    if (f !== set[set.length - 1]) set.push(f)
            +    return set
            +  }, []).sort(alphasort).map(function (f) {
            +    // de-windows
            +    return (process.platform !== 'win32') ? f
            +           : f.replace(/^[a-zA-Z]:\\\\/, '/').replace(/\\/g, '/')
            +  })
            +}
            +
            +function flatten (chunks) {
            +  var s = 0
            +  chunks.forEach(function (c) { s += c.length })
            +  var out = new Buffer(s)
            +  s = 0
            +  chunks.forEach(function (c) {
            +    c.copy(out, s)
            +    s += c.length
            +  })
            +
            +  return out.toString().trim()
            +}
            +
            +function alphasort (a, b) {
            +  a = a.toLowerCase()
            +  b = b.toLowerCase()
            +  return a > b ? 1 : a < b ? -1 : 0
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/bash-comparison.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/bash-comparison.js
            new file mode 100644
            index 0000000..239ed1a
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/bash-comparison.js
            @@ -0,0 +1,63 @@
            +// basic test
            +// show that it does the same thing by default as the shell.
            +var tap = require("tap")
            +, child_process = require("child_process")
            +, bashResults = require("./bash-results.json")
            +, globs = Object.keys(bashResults)
            +, glob = require("../")
            +, path = require("path")
            +
            +// run from the root of the project
            +// this is usually where you're at anyway, but be sure.
            +process.chdir(path.resolve(__dirname, ".."))
            +
            +function alphasort (a, b) {
            +  a = a.toLowerCase()
            +  b = b.toLowerCase()
            +  return a > b ? 1 : a < b ? -1 : 0
            +}
            +
            +globs.forEach(function (pattern) {
            +  var expect = bashResults[pattern]
            +  // anything regarding the symlink thing will fail on windows, so just skip it
            +  if (process.platform === "win32" &&
            +      expect.some(function (m) {
            +        return /\/symlink\//.test(m)
            +      }))
            +    return
            +
            +  tap.test(pattern, function (t) {
            +    glob(pattern, function (er, matches) {
            +      if (er)
            +        throw er
            +
            +      // sort and unmark, just to match the shell results
            +      matches = cleanResults(matches)
            +
            +      t.deepEqual(matches, expect, pattern)
            +      t.end()
            +    })
            +  })
            +
            +  tap.test(pattern + " sync", function (t) {
            +    var matches = cleanResults(glob.sync(pattern))
            +
            +    t.deepEqual(matches, expect, "should match shell")
            +    t.end()
            +  })
            +})
            +
            +function cleanResults (m) {
            +  // normalize discrepancies in ordering, duplication,
            +  // and ending slashes.
            +  return m.map(function (m) {
            +    return m.replace(/\/+/g, "/").replace(/\/$/, "")
            +  }).sort(alphasort).reduce(function (set, f) {
            +    if (f !== set[set.length - 1]) set.push(f)
            +    return set
            +  }, []).sort(alphasort).map(function (f) {
            +    // de-windows
            +    return (process.platform !== 'win32') ? f
            +           : f.replace(/^[a-zA-Z]:[\/\\]+/, '/').replace(/[\\\/]+/g, '/')
            +  })
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/bash-results.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/bash-results.json
            new file mode 100644
            index 0000000..a9bc347
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/bash-results.json
            @@ -0,0 +1,350 @@
            +{
            +  "test/a/*/+(c|g)/./d": [
            +    "test/a/b/c/./d"
            +  ],
            +  "test/a/**/[cg]/../[cg]": [
            +    "test/a/abcdef/g/../g",
            +    "test/a/abcfed/g/../g",
            +    "test/a/b/c/../c",
            +    "test/a/c/../c",
            +    "test/a/c/d/c/../c",
            +    "test/a/symlink/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c"
            +  ],
            +  "test/a/{b,c,d,e,f}/**/g": [],
            +  "test/a/b/**": [
            +    "test/a/b",
            +    "test/a/b/c",
            +    "test/a/b/c/d"
            +  ],
            +  "test/**/g": [
            +    "test/a/abcdef/g",
            +    "test/a/abcfed/g"
            +  ],
            +  "test/a/abc{fed,def}/g/h": [
            +    "test/a/abcdef/g/h",
            +    "test/a/abcfed/g/h"
            +  ],
            +  "test/a/abc{fed/g,def}/**/": [
            +    "test/a/abcdef",
            +    "test/a/abcdef/g",
            +    "test/a/abcfed/g"
            +  ],
            +  "test/a/abc{fed/g,def}/**///**/": [
            +    "test/a/abcdef",
            +    "test/a/abcdef/g",
            +    "test/a/abcfed/g"
            +  ],
            +  "test/**/a/**/": [
            +    "test/a",
            +    "test/a/abcdef",
            +    "test/a/abcdef/g",
            +    "test/a/abcfed",
            +    "test/a/abcfed/g",
            +    "test/a/b",
            +    "test/a/b/c",
            +    "test/a/bc",
            +    "test/a/bc/e",
            +    "test/a/c",
            +    "test/a/c/d",
            +    "test/a/c/d/c",
            +    "test/a/cb",
            +    "test/a/cb/e",
            +    "test/a/symlink",
            +    "test/a/symlink/a",
            +    "test/a/symlink/a/b",
            +    "test/a/symlink/a/b/c",
            +    "test/a/symlink/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b"
            +  ],
            +  "test/+(a|b|c)/a{/,bc*}/**": [
            +    "test/a/abcdef",
            +    "test/a/abcdef/g",
            +    "test/a/abcdef/g/h",
            +    "test/a/abcfed",
            +    "test/a/abcfed/g",
            +    "test/a/abcfed/g/h"
            +  ],
            +  "test/*/*/*/f": [
            +    "test/a/bc/e/f",
            +    "test/a/cb/e/f"
            +  ],
            +  "test/**/f": [
            +    "test/a/bc/e/f",
            +    "test/a/cb/e/f"
            +  ],
            +  "test/a/symlink/a/b/c/a/b/c/a/b/c//a/b/c////a/b/c/**/b/c/**": [
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
            +    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c"
            +  ],
            +  "{./*/*,/tmp/glob-test/*}": [
            +    "./examples/g.js",
            +    "./examples/usr-local.js",
            +    "./node_modules/graceful-fs",
            +    "./node_modules/inherits",
            +    "./node_modules/minimatch",
            +    "./node_modules/mkdirp",
            +    "./node_modules/rimraf",
            +    "./node_modules/tap",
            +    "./test/00-setup.js",
            +    "./test/a",
            +    "./test/bash-comparison.js",
            +    "./test/bash-results.json",
            +    "./test/cwd-test.js",
            +    "./test/globstar-match.js",
            +    "./test/mark.js",
            +    "./test/nocase-nomagic.js",
            +    "./test/pause-resume.js",
            +    "./test/root-nomount.js",
            +    "./test/root.js",
            +    "./test/stat.js",
            +    "./test/zz-cleanup.js",
            +    "/tmp/glob-test/asdf",
            +    "/tmp/glob-test/bar",
            +    "/tmp/glob-test/baz",
            +    "/tmp/glob-test/foo",
            +    "/tmp/glob-test/quux",
            +    "/tmp/glob-test/qwer",
            +    "/tmp/glob-test/rewq"
            +  ],
            +  "{/tmp/glob-test/*,*}": [
            +    "/tmp/glob-test/asdf",
            +    "/tmp/glob-test/bar",
            +    "/tmp/glob-test/baz",
            +    "/tmp/glob-test/foo",
            +    "/tmp/glob-test/quux",
            +    "/tmp/glob-test/qwer",
            +    "/tmp/glob-test/rewq",
            +    "examples",
            +    "glob.js",
            +    "LICENSE",
            +    "node_modules",
            +    "package.json",
            +    "README.md",
            +    "test"
            +  ],
            +  "test/a/!(symlink)/**": [
            +    "test/a/abcdef",
            +    "test/a/abcdef/g",
            +    "test/a/abcdef/g/h",
            +    "test/a/abcfed",
            +    "test/a/abcfed/g",
            +    "test/a/abcfed/g/h",
            +    "test/a/b",
            +    "test/a/b/c",
            +    "test/a/b/c/d",
            +    "test/a/bc",
            +    "test/a/bc/e",
            +    "test/a/bc/e/f",
            +    "test/a/c",
            +    "test/a/c/d",
            +    "test/a/c/d/c",
            +    "test/a/c/d/c/b",
            +    "test/a/cb",
            +    "test/a/cb/e",
            +    "test/a/cb/e/f"
            +  ]
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/cwd-test.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/cwd-test.js
            new file mode 100644
            index 0000000..352c27e
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/cwd-test.js
            @@ -0,0 +1,55 @@
            +var tap = require("tap")
            +
            +var origCwd = process.cwd()
            +process.chdir(__dirname)
            +
            +tap.test("changing cwd and searching for **/d", function (t) {
            +  var glob = require('../')
            +  var path = require('path')
            +  t.test('.', function (t) {
            +    glob('**/d', function (er, matches) {
            +      t.ifError(er)
            +      t.like(matches, [ 'a/b/c/d', 'a/c/d' ])
            +      t.end()
            +    })
            +  })
            +
            +  t.test('a', function (t) {
            +    glob('**/d', {cwd:path.resolve('a')}, function (er, matches) {
            +      t.ifError(er)
            +      t.like(matches, [ 'b/c/d', 'c/d' ])
            +      t.end()
            +    })
            +  })
            +
            +  t.test('a/b', function (t) {
            +    glob('**/d', {cwd:path.resolve('a/b')}, function (er, matches) {
            +      t.ifError(er)
            +      t.like(matches, [ 'c/d' ])
            +      t.end()
            +    })
            +  })
            +
            +  t.test('a/b/', function (t) {
            +    glob('**/d', {cwd:path.resolve('a/b/')}, function (er, matches) {
            +      t.ifError(er)
            +      t.like(matches, [ 'c/d' ])
            +      t.end()
            +    })
            +  })
            +
            +  t.test('.', function (t) {
            +    glob('**/d', {cwd: process.cwd()}, function (er, matches) {
            +      t.ifError(er)
            +      t.like(matches, [ 'a/b/c/d', 'a/c/d' ])
            +      t.end()
            +    })
            +  })
            +
            +  t.test('cd -', function (t) {
            +    process.chdir(origCwd)
            +    t.end()
            +  })
            +
            +  t.end()
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/globstar-match.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/globstar-match.js
            new file mode 100644
            index 0000000..9b234fa
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/globstar-match.js
            @@ -0,0 +1,19 @@
            +var Glob = require("../glob.js").Glob
            +var test = require('tap').test
            +
            +test('globstar should not have dupe matches', function(t) {
            +  var pattern = 'a/**/[gh]'
            +  var g = new Glob(pattern, { cwd: __dirname })
            +  var matches = []
            +  g.on('match', function(m) {
            +    console.error('match %j', m)
            +    matches.push(m)
            +  })
            +  g.on('end', function(set) {
            +    console.error('set', set)
            +    matches = matches.sort()
            +    set = set.sort()
            +    t.same(matches, set, 'should have same set of matches')
            +    t.end()
            +  })
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/mark.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/mark.js
            new file mode 100644
            index 0000000..ed68a33
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/mark.js
            @@ -0,0 +1,74 @@
            +var test = require("tap").test
            +var glob = require('../')
            +process.chdir(__dirname)
            +
            +test("mark, no / on pattern", function (t) {
            +  glob("a/*", {mark: true}, function (er, results) {
            +    if (er)
            +      throw er
            +    var expect = [ 'a/abcdef/',
            +                   'a/abcfed/',
            +                   'a/b/',
            +                   'a/bc/',
            +                   'a/c/',
            +                   'a/cb/' ]
            +
            +    if (process.platform !== "win32")
            +      expect.push('a/symlink/')
            +
            +    t.same(results, expect)
            +    t.end()
            +  })
            +})
            +
            +test("mark=false, no / on pattern", function (t) {
            +  glob("a/*", function (er, results) {
            +    if (er)
            +      throw er
            +    var expect = [ 'a/abcdef',
            +                   'a/abcfed',
            +                   'a/b',
            +                   'a/bc',
            +                   'a/c',
            +                   'a/cb' ]
            +
            +    if (process.platform !== "win32")
            +      expect.push('a/symlink')
            +    t.same(results, expect)
            +    t.end()
            +  })
            +})
            +
            +test("mark=true, / on pattern", function (t) {
            +  glob("a/*/", {mark: true}, function (er, results) {
            +    if (er)
            +      throw er
            +    var expect = [ 'a/abcdef/',
            +                    'a/abcfed/',
            +                    'a/b/',
            +                    'a/bc/',
            +                    'a/c/',
            +                    'a/cb/' ]
            +    if (process.platform !== "win32")
            +      expect.push('a/symlink/')
            +    t.same(results, expect)
            +    t.end()
            +  })
            +})
            +
            +test("mark=false, / on pattern", function (t) {
            +  glob("a/*/", function (er, results) {
            +    if (er)
            +      throw er
            +    var expect = [ 'a/abcdef/',
            +                   'a/abcfed/',
            +                   'a/b/',
            +                   'a/bc/',
            +                   'a/c/',
            +                   'a/cb/' ]
            +    if (process.platform !== "win32")
            +      expect.push('a/symlink/')
            +    t.same(results, expect)
            +    t.end()
            +  })
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/nocase-nomagic.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/nocase-nomagic.js
            new file mode 100644
            index 0000000..d862970
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/nocase-nomagic.js
            @@ -0,0 +1,113 @@
            +var fs = require('graceful-fs');
            +var test = require('tap').test;
            +var glob = require('../');
            +
            +test('mock fs', function(t) {
            +  var stat = fs.stat
            +  var statSync = fs.statSync
            +  var readdir = fs.readdir
            +  var readdirSync = fs.readdirSync
            +
            +  function fakeStat(path) {
            +    var ret
            +    switch (path.toLowerCase()) {
            +      case '/tmp': case '/tmp/':
            +        ret = { isDirectory: function() { return true } }
            +        break
            +      case '/tmp/a':
            +        ret = { isDirectory: function() { return false } }
            +        break
            +    }
            +    return ret
            +  }
            +
            +  fs.stat = function(path, cb) {
            +    var f = fakeStat(path);
            +    if (f) {
            +      process.nextTick(function() {
            +        cb(null, f)
            +      })
            +    } else {
            +      stat.call(fs, path, cb)
            +    }
            +  }
            +
            +  fs.statSync = function(path) {
            +    return fakeStat(path) || statSync.call(fs, path)
            +  }
            +
            +  function fakeReaddir(path) {
            +    var ret
            +    switch (path.toLowerCase()) {
            +      case '/tmp': case '/tmp/':
            +        ret = [ 'a', 'A' ]
            +        break
            +      case '/':
            +        ret = ['tmp', 'tMp', 'tMP', 'TMP']
            +    }
            +    return ret
            +  }
            +
            +  fs.readdir = function(path, cb) {
            +    var f = fakeReaddir(path)
            +    if (f)
            +      process.nextTick(function() {
            +        cb(null, f)
            +      })
            +    else
            +      readdir.call(fs, path, cb)
            +  }
            +
            +  fs.readdirSync = function(path) {
            +    return fakeReaddir(path) || readdirSync.call(fs, path)
            +  }
            +
            +  t.pass('mocked')
            +  t.end()
            +})
            +
            +test('nocase, nomagic', function(t) {
            +  var n = 2
            +  var want = [ '/TMP/A',
            +               '/TMP/a',
            +               '/tMP/A',
            +               '/tMP/a',
            +               '/tMp/A',
            +               '/tMp/a',
            +               '/tmp/A',
            +               '/tmp/a' ]
            +  glob('/tmp/a', { nocase: true }, function(er, res) {
            +    if (er)
            +      throw er
            +    t.same(res.sort(), want)
            +    if (--n === 0) t.end()
            +  })
            +  glob('/tmp/A', { nocase: true }, function(er, res) {
            +    if (er)
            +      throw er
            +    t.same(res.sort(), want)
            +    if (--n === 0) t.end()
            +  })
            +})
            +
            +test('nocase, with some magic', function(t) {
            +  t.plan(2)
            +  var want = [ '/TMP/A',
            +               '/TMP/a',
            +               '/tMP/A',
            +               '/tMP/a',
            +               '/tMp/A',
            +               '/tMp/a',
            +               '/tmp/A',
            +               '/tmp/a' ]
            +  glob('/tmp/*', { nocase: true }, function(er, res) {
            +    if (er)
            +      throw er
            +    t.same(res.sort(), want)
            +  })
            +  glob('/tmp/*', { nocase: true }, function(er, res) {
            +    if (er)
            +      throw er
            +    t.same(res.sort(), want)
            +  })
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/pause-resume.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/pause-resume.js
            new file mode 100644
            index 0000000..e1ffbab
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/pause-resume.js
            @@ -0,0 +1,73 @@
            +// show that no match events happen while paused.
            +var tap = require("tap")
            +, child_process = require("child_process")
            +// just some gnarly pattern with lots of matches
            +, pattern = "test/a/!(symlink)/**"
            +, bashResults = require("./bash-results.json")
            +, patterns = Object.keys(bashResults)
            +, glob = require("../")
            +, Glob = glob.Glob
            +, path = require("path")
            +
            +// run from the root of the project
            +// this is usually where you're at anyway, but be sure.
            +process.chdir(path.resolve(__dirname, ".."))
            +
            +function alphasort (a, b) {
            +  a = a.toLowerCase()
            +  b = b.toLowerCase()
            +  return a > b ? 1 : a < b ? -1 : 0
            +}
            +
            +function cleanResults (m) {
            +  // normalize discrepancies in ordering, duplication,
            +  // and ending slashes.
            +  return m.map(function (m) {
            +    return m.replace(/\/+/g, "/").replace(/\/$/, "")
            +  }).sort(alphasort).reduce(function (set, f) {
            +    if (f !== set[set.length - 1]) set.push(f)
            +    return set
            +  }, []).sort(alphasort).map(function (f) {
            +    // de-windows
            +    return (process.platform !== 'win32') ? f
            +           : f.replace(/^[a-zA-Z]:\\\\/, '/').replace(/\\/g, '/')
            +  })
            +}
            +
            +var globResults = []
            +tap.test("use a Glob object, and pause/resume it", function (t) {
            +  var g = new Glob(pattern)
            +  , paused = false
            +  , res = []
            +  , expect = bashResults[pattern]
            +
            +  g.on("pause", function () {
            +    console.error("pause")
            +  })
            +
            +  g.on("resume", function () {
            +    console.error("resume")
            +  })
            +
            +  g.on("match", function (m) {
            +    t.notOk(g.paused, "must not be paused")
            +    globResults.push(m)
            +    g.pause()
            +    t.ok(g.paused, "must be paused")
            +    setTimeout(g.resume.bind(g), 10)
            +  })
            +
            +  g.on("end", function (matches) {
            +    t.pass("reached glob end")
            +    globResults = cleanResults(globResults)
            +    matches = cleanResults(matches)
            +    t.deepEqual(matches, globResults,
            +      "end event matches should be the same as match events")
            +
            +    t.deepEqual(matches, expect,
            +      "glob matches should be the same as bash results")
            +
            +    t.end()
            +  })
            +})
            +
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/root-nomount.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/root-nomount.js
            new file mode 100644
            index 0000000..3ac5979
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/root-nomount.js
            @@ -0,0 +1,39 @@
            +var tap = require("tap")
            +
            +var origCwd = process.cwd()
            +process.chdir(__dirname)
            +
            +tap.test("changing root and searching for /b*/**", function (t) {
            +  var glob = require('../')
            +  var path = require('path')
            +  t.test('.', function (t) {
            +    glob('/b*/**', { globDebug: true, root: '.', nomount: true }, function (er, matches) {
            +      t.ifError(er)
            +      t.like(matches, [])
            +      t.end()
            +    })
            +  })
            +
            +  t.test('a', function (t) {
            +    glob('/b*/**', { globDebug: true, root: path.resolve('a'), nomount: true }, function (er, matches) {
            +      t.ifError(er)
            +      t.like(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ])
            +      t.end()
            +    })
            +  })
            +
            +  t.test('root=a, cwd=a/b', function (t) {
            +    glob('/b*/**', { globDebug: true, root: 'a', cwd: path.resolve('a/b'), nomount: true }, function (er, matches) {
            +      t.ifError(er)
            +      t.like(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ])
            +      t.end()
            +    })
            +  })
            +
            +  t.test('cd -', function (t) {
            +    process.chdir(origCwd)
            +    t.end()
            +  })
            +
            +  t.end()
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/root.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/root.js
            new file mode 100644
            index 0000000..95c23f9
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/root.js
            @@ -0,0 +1,46 @@
            +var t = require("tap")
            +
            +var origCwd = process.cwd()
            +process.chdir(__dirname)
            +
            +var glob = require('../')
            +var path = require('path')
            +
            +t.test('.', function (t) {
            +  glob('/b*/**', { globDebug: true, root: '.' }, function (er, matches) {
            +    t.ifError(er)
            +    t.like(matches, [])
            +    t.end()
            +  })
            +})
            +
            +
            +t.test('a', function (t) {
            +  console.error("root=" + path.resolve('a'))
            +  glob('/b*/**', { globDebug: true, root: path.resolve('a') }, function (er, matches) {
            +    t.ifError(er)
            +    var wanted = [
            +        '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f'
            +      ].map(function (m) {
            +        return path.join(path.resolve('a'), m).replace(/\\/g, '/')
            +      })
            +
            +    t.like(matches, wanted)
            +    t.end()
            +  })
            +})
            +
            +t.test('root=a, cwd=a/b', function (t) {
            +  glob('/b*/**', { globDebug: true, root: 'a', cwd: path.resolve('a/b') }, function (er, matches) {
            +    t.ifError(er)
            +    t.like(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ].map(function (m) {
            +      return path.join(path.resolve('a'), m).replace(/\\/g, '/')
            +    }))
            +    t.end()
            +  })
            +})
            +
            +t.test('cd -', function (t) {
            +  process.chdir(origCwd)
            +  t.end()
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/stat.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/stat.js
            new file mode 100644
            index 0000000..6291711
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/stat.js
            @@ -0,0 +1,32 @@
            +var glob = require('../')
            +var test = require('tap').test
            +var path = require('path')
            +
            +test('stat all the things', function(t) {
            +  var g = new glob.Glob('a/*abc*/**', { stat: true, cwd: __dirname })
            +  var matches = []
            +  g.on('match', function(m) {
            +    matches.push(m)
            +  })
            +  var stats = []
            +  g.on('stat', function(m) {
            +    stats.push(m)
            +  })
            +  g.on('end', function(eof) {
            +    stats = stats.sort()
            +    matches = matches.sort()
            +    eof = eof.sort()
            +    t.same(stats, matches)
            +    t.same(eof, matches)
            +    var cache = Object.keys(this.statCache)
            +    t.same(cache.map(function (f) {
            +      return path.relative(__dirname, f)
            +    }).sort(), matches)
            +
            +    cache.forEach(function(c) {
            +      t.equal(typeof this.statCache[c], 'object')
            +    }, this)
            +
            +    t.end()
            +  })
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/zz-cleanup.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/zz-cleanup.js
            new file mode 100644
            index 0000000..e085f0f
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/glob/test/zz-cleanup.js
            @@ -0,0 +1,11 @@
            +// remove the fixtures
            +var tap = require("tap")
            +, rimraf = require("rimraf")
            +, path = require("path")
            +
            +tap.test("cleanup fixtures", function (t) {
            +  rimraf(path.resolve(__dirname, "a"), function (er) {
            +    t.ifError(er, "removed")
            +    t.end()
            +  })
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/History.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/History.md
            new file mode 100644
            index 0000000..a4b7b49
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/History.md
            @@ -0,0 +1,63 @@
            +
            +1.7.0 / 2012-12-30 
            +==================
            +
            +  * support transient notifications in Gnome
            +
            +1.6.1 / 2012-09-25 
            +==================
            +
            +  * restore compatibility with node < 0.8 [fgnass]
            +
            +1.6.0 / 2012-09-06 
            +==================
            +
            +  * add notification center support [drudge]
            +
            +1.5.1 / 2012-04-08 
            +==================
            +
            +  * Merge pull request #16 from KyleAMathews/patch-1
            +  * Fixes #15
            +
            +1.5.0 / 2012-02-08 
            +==================
            +
            +  * Added windows support [perfusorius]
            +
            +1.4.1 / 2011-12-28 
            +==================
            +
            +  * Fixed: dont exit(). Closes #9
            +
            +1.4.0 / 2011-12-17 
            +==================
            +
            +  * Changed API: `growl.notify()` -> `growl()`
            +
            +1.3.0 / 2011-12-17 
            +==================
            +
            +  * Added support for Ubuntu/Debian/Linux users [niftylettuce]
            +  * Fixed: send notifications even if title not specified [alessioalex]
            +
            +1.2.0 / 2011-10-06 
            +==================
            +
            +  * Add support for priority.
            +
            +1.1.0 / 2011-03-15 
            +==================
            +
            +  * Added optional callbacks
            +  * Added parsing of version
            +
            +1.0.1 / 2010-03-26
            +==================
            +
            +  * Fixed; sys.exec -> child_process.exec to support latest node
            +
            +1.0.0 / 2010-03-19
            +==================
            +  
            +  * Initial release
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/Readme.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/Readme.md
            new file mode 100644
            index 0000000..48d717c
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/Readme.md
            @@ -0,0 +1,99 @@
            +# Growl for nodejs
            +
            +Growl support for Nodejs. This is essentially a port of my [Ruby Growl Library](http://github.com/visionmedia/growl). Ubuntu/Linux support added thanks to [@niftylettuce](http://github.com/niftylettuce). 
            +
            +## Installation
            +
            +### Install 
            +
            +### Mac OS X (Darwin):
            +
            +  Install [growlnotify(1)](http://growl.info/extras.php#growlnotify). On OS X 10.8, Notification Center is supported using [terminal-notifier](https://github.com/alloy/terminal-notifier). To install:
            +  
            +      $ sudo gem install terminal-notifier
            +      
            +  Install [npm](http://npmjs.org/) and run:
            +  
            +      $ npm install growl
            +
            +### Ubuntu (Linux):
            +
            +  Install `notify-send` through the [libnotify-bin](http://packages.ubuntu.com/libnotify-bin) package:
            +
            +      $ sudo apt-get install libnotify-bin
            +
            +  Install [npm](http://npmjs.org/) and run:
            +  
            +      $ npm install growl
            +
            +### Windows:
            +
            +  Download and install [Growl for Windows](http://www.growlforwindows.com/gfw/default.aspx)
            +
            +  Download [growlnotify](http://www.growlforwindows.com/gfw/help/growlnotify.aspx) - **IMPORTANT :** Unpack growlnotify to a folder that is present in your path!
            +
            +  Install [npm](http://npmjs.org/) and run:
            +  
            +      $ npm install growl
            +
            +## Examples
            +
            +Callback functions are optional
            +
            +    var growl = require('growl')
            +    growl('You have mail!')
            +    growl('5 new messages', { sticky: true })
            +    growl('5 new emails', { title: 'Email Client', image: 'Safari', sticky: true })
            +    growl('Message with title', { title: 'Title'})
            +    growl('Set priority', { priority: 2 })
            +    growl('Show Safari icon', { image: 'Safari' })
            +    growl('Show icon', { image: 'path/to/icon.icns' })
            +    growl('Show image', { image: 'path/to/my.image.png' })
            +    growl('Show png filesystem icon', { image: 'png' })
            +    growl('Show pdf filesystem icon', { image: 'article.pdf' })
            +    growl('Show pdf filesystem icon', { image: 'article.pdf' }, function(err){
            +      // ... notified
            +    })
            +
            +## Options
            +
            +  - title
            +    - notification title
            +  - name
            +    - application name
            +  - priority
            +    - priority for the notification (default is 0)
            +  - sticky
            +    - weither or not the notification should remainin until closed
            +  - image
            +    - Auto-detects the context:
            +      - path to an icon sets --iconpath
            +      - path to an image sets --image
            +      - capitalized word sets --appIcon
            +      - filename uses extname as --icon
            +      - otherwise treated as --icon
            +      
            +## License 
            +
            +(The MIT License)
            +
            +Copyright (c) 2009 TJ Holowaychuk 
            +
            +Permission is hereby granted, free of charge, to any person obtaining
            +a copy of this software and associated documentation files (the
            +'Software'), to deal in the Software without restriction, including
            +without limitation the rights to use, copy, modify, merge, publish,
            +distribute, sublicense, and/or sell copies of the Software, and to
            +permit persons to whom the Software is furnished to do so, subject to
            +the following conditions:
            +
            +The above copyright notice and this permission notice shall be
            +included in all copies or substantial portions of the Software.
            +
            +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
            +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
            +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
            +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
            +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
            +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
            +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/lib/growl.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/lib/growl.js
            new file mode 100644
            index 0000000..c034c3e
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/lib/growl.js
            @@ -0,0 +1,234 @@
            +// Growl - Copyright TJ Holowaychuk  (MIT Licensed)
            +
            +/**
            + * Module dependencies.
            + */
            +
            +var exec = require('child_process').exec
            +  , fs = require('fs')
            +  , path = require('path')
            +  , exists = fs.existsSync || path.existsSync
            +  , os = require('os')
            +  , quote = JSON.stringify
            +  , cmd;
            +
            +function which(name) {
            +  var paths = process.env.PATH.split(':');
            +  var loc;
            +  
            +  for (var i = 0, len = paths.length; i < len; ++i) {
            +    loc = path.join(paths[i], name);
            +    if (exists(loc)) return loc;
            +  }
            +}
            +
            +switch(os.type()) {
            +  case 'Darwin':
            +    if (which('terminal-notifier')) {
            +      cmd = {
            +          type: "Darwin-NotificationCenter"
            +        , pkg: "terminal-notifier"
            +        , msg: '-message'
            +        , title: '-title'
            +        , subtitle: '-subtitle'
            +        , priority: {
            +              cmd: '-execute'
            +            , range: []
            +          }
            +      };
            +    } else {
            +      cmd = {
            +          type: "Darwin-Growl"
            +        , pkg: "growlnotify"
            +        , msg: '-m'
            +        , sticky: '--sticky'
            +        , priority: {
            +              cmd: '--priority'
            +            , range: [
            +                -2
            +              , -1
            +              , 0
            +              , 1
            +              , 2
            +              , "Very Low"
            +              , "Moderate"
            +              , "Normal"
            +              , "High"
            +              , "Emergency"
            +            ]
            +          }
            +      };
            +    }
            +    break;
            +  case 'Linux':
            +    cmd = {
            +        type: "Linux"
            +      , pkg: "notify-send"
            +      , msg: ''
            +      , sticky: '-t 0'
            +      , icon: '-i'
            +      , priority: {
            +          cmd: '-u'
            +        , range: [
            +            "low"
            +          , "normal"
            +          , "critical"
            +        ]
            +      }
            +    };
            +    break;
            +  case 'Windows_NT':
            +    cmd = {
            +        type: "Windows"
            +      , pkg: "growlnotify"
            +      , msg: ''
            +      , sticky: '/s:true'
            +      , title: '/t:'
            +      , icon: '/i:'
            +      , priority: {
            +            cmd: '/p:'
            +          , range: [
            +              -2
            +            , -1
            +            , 0
            +            , 1
            +            , 2
            +          ]
            +        }
            +    };
            +    break;
            +}
            +
            +/**
            + * Expose `growl`.
            + */
            +
            +exports = module.exports = growl;
            +
            +/**
            + * Node-growl version.
            + */
            +
            +exports.version = '1.4.1'
            +
            +/**
            + * Send growl notification _msg_ with _options_.
            + *
            + * Options:
            + *
            + *  - title   Notification title
            + *  - sticky  Make the notification stick (defaults to false)
            + *  - priority  Specify an int or named key (default is 0)
            + *  - name    Application name (defaults to growlnotify)
            + *  - image
            + *    - path to an icon sets --iconpath
            + *    - path to an image sets --image
            + *    - capitalized word sets --appIcon
            + *    - filename uses extname as --icon
            + *    - otherwise treated as --icon
            + *
            + * Examples:
            + *
            + *   growl('New email')
            + *   growl('5 new emails', { title: 'Thunderbird' })
            + *   growl('Email sent', function(){
            + *     // ... notification sent
            + *   })
            + *
            + * @param {string} msg
            + * @param {object} options
            + * @param {function} fn
            + * @api public
            + */
            +
            +function growl(msg, options, fn) {
            +  var image
            +    , args
            +    , options = options || {}
            +    , fn = fn || function(){};
            +
            +  // noop
            +  if (!cmd) return fn(new Error('growl not supported on this platform'));
            +  args = [cmd.pkg];
            +
            +  // image
            +  if (image = options.image) {
            +    switch(cmd.type) {
            +      case 'Darwin-Growl':
            +        var flag, ext = path.extname(image).substr(1)
            +        flag = flag || ext == 'icns' && 'iconpath'
            +        flag = flag || /^[A-Z]/.test(image) && 'appIcon'
            +        flag = flag || /^png|gif|jpe?g$/.test(ext) && 'image'
            +        flag = flag || ext && (image = ext) && 'icon'
            +        flag = flag || 'icon'
            +        args.push('--' + flag, quote(image))
            +        break;
            +      case 'Linux':
            +        args.push(cmd.icon, quote(image));
            +        // libnotify defaults to sticky, set a hint for transient notifications
            +        if (!options.sticky) args.push('--hint=int:transient:1');
            +        break;
            +      case 'Windows':
            +        args.push(cmd.icon + quote(image));
            +        break;
            +    }
            +  }
            +
            +  // sticky
            +  if (options.sticky) args.push(cmd.sticky);
            +
            +  // priority
            +  if (options.priority) {
            +    var priority = options.priority + '';
            +    var checkindexOf = cmd.priority.range.indexOf(priority);
            +    if (~cmd.priority.range.indexOf(priority)) {
            +      args.push(cmd.priority, options.priority);
            +    }
            +  }
            +
            +  // name
            +  if (options.name && cmd.type === "Darwin-Growl") {
            +    args.push('--name', options.name);
            +  }
            +
            +  switch(cmd.type) {
            +    case 'Darwin-Growl':
            +      args.push(cmd.msg);
            +      args.push(quote(msg));
            +      if (options.title) args.push(quote(options.title));
            +      break;
            +    case 'Darwin-NotificationCenter':
            +      args.push(cmd.msg);
            +      args.push(quote(msg));
            +      if (options.title) {
            +        args.push(cmd.title);
            +        args.push(quote(options.title));
            +      }
            +      if (options.subtitle) {
            +        args.push(cmd.subtitle);
            +        args.push(quote(options.subtitle));
            +      }
            +      break;
            +    case 'Darwin-Growl':
            +      args.push(cmd.msg);
            +      args.push(quote(msg));
            +      if (options.title) args.push(quote(options.title));
            +      break;
            +    case 'Linux':
            +      if (options.title) {
            +        args.push(quote(options.title));
            +        args.push(cmd.msg);
            +        args.push(quote(msg));
            +      } else {
            +        args.push(quote(msg));
            +      }
            +      break;
            +    case 'Windows':
            +      args.push(quote(msg));
            +      if (options.title) args.push(cmd.title + quote(options.title));
            +      break;
            +  }
            +
            +  // execute
            +  exec(args.join(' '), fn);
            +};
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/package.json
            new file mode 100644
            index 0000000..0a2ce00
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/package.json
            @@ -0,0 +1,45 @@
            +{
            +  "name": "growl",
            +  "version": "1.8.1",
            +  "description": "Growl unobtrusive notifications",
            +  "author": {
            +    "name": "TJ Holowaychuk",
            +    "email": "tj@vision-media.ca"
            +  },
            +  "maintainers": [
            +    {
            +      "name": "tjholowaychuk",
            +      "email": "tj@vision-media.ca"
            +    },
            +    {
            +      "name": "jbnicolai",
            +      "email": "jappelman@xebia.com"
            +    }
            +  ],
            +  "repository": {
            +    "type": "git",
            +    "url": "git://github.com/visionmedia/node-growl.git"
            +  },
            +  "main": "./lib/growl.js",
            +  "gitHead": "882ced3155a57f566887c884d5c6dccb7df435c1",
            +  "bugs": {
            +    "url": "https://github.com/visionmedia/node-growl/issues"
            +  },
            +  "homepage": "https://github.com/visionmedia/node-growl",
            +  "_id": "growl@1.8.1",
            +  "scripts": {},
            +  "_shasum": "4b2dec8d907e93db336624dcec0183502f8c9428",
            +  "_from": "growl@1.8.1",
            +  "_npmVersion": "1.4.20",
            +  "_npmUser": {
            +    "name": "jbnicolai",
            +    "email": "jappelman@xebia.com"
            +  },
            +  "dist": {
            +    "shasum": "4b2dec8d907e93db336624dcec0183502f8c9428",
            +    "tarball": "http://registry.npmjs.org/growl/-/growl-1.8.1.tgz"
            +  },
            +  "directories": {},
            +  "_resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz",
            +  "readme": "ERROR: No README data found!"
            +}
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/test.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/test.js
            new file mode 100644
            index 0000000..cf22d90
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/growl/test.js
            @@ -0,0 +1,20 @@
            +
            +var growl = require('./lib/growl')
            +
            +growl('You have mail!')
            +growl('5 new messages', { sticky: true })
            +growl('5 new emails', { title: 'Email Client', image: 'Safari', sticky: true })
            +growl('Message with title', { title: 'Title'})
            +growl('Set priority', { priority: 2 })
            +growl('Show Safari icon', { image: 'Safari' })
            +growl('Show icon', { image: 'path/to/icon.icns' })
            +growl('Show image', { image: 'path/to/my.image.png' })
            +growl('Show png filesystem icon', { image: 'png' })
            +growl('Show pdf filesystem icon', { image: 'article.pdf' })
            +growl('Show pdf filesystem icon', { image: 'article.pdf' }, function(){
            +  console.log('callback');
            +})
            +growl('Show pdf filesystem icon', { title: 'Use show()', image: 'article.pdf' })
            +growl('here \' are \n some \\ characters that " need escaping', {}, function(error, stdout, stderr) {
            +  if (error !== null) throw new Error('escaping failed:\n' + stdout + stderr);
            +})
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/.npmignore b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/.npmignore
            new file mode 100644
            index 0000000..b9af3d4
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/.npmignore
            @@ -0,0 +1,15 @@
            +test
            +support
            +benchmarks
            +examples
            +lib-cov
            +coverage.html
            +.gitmodules
            +.travis.yml
            +History.md
            +Readme.md
            +Makefile
            +test/
            +support/
            +benchmarks/
            +examples/
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/LICENSE b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/LICENSE
            new file mode 100644
            index 0000000..8ad0e0d
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/LICENSE
            @@ -0,0 +1,22 @@
            +(The MIT License)
            +
            +Copyright (c) 2009-2010 TJ Holowaychuk 
            +
            +Permission is hereby granted, free of charge, to any person obtaining
            +a copy of this software and associated documentation files (the
            +'Software'), to deal in the Software without restriction, including
            +without limitation the rights to use, copy, modify, merge, publish,
            +distribute, sublicense, and/or sell copies of the Software, and to
            +permit persons to whom the Software is furnished to do so, subject to
            +the following conditions:
            +
            +The above copyright notice and this permission notice shall be
            +included in all copies or substantial portions of the Software.
            +
            +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
            +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
            +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
            +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
            +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
            +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
            +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
            \ No newline at end of file
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/bin/jade b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/bin/jade
            new file mode 100755
            index 0000000..7e6002f
            --- /dev/null
            +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/bin/jade
            @@ -0,0 +1,147 @@
            +#!/usr/bin/env node
            +
            +/**
            + * Module dependencies.
            + */
            +
            +var fs = require('fs')
            +  , program = require('commander')
            +  , path = require('path')
            +  , basename = path.basename
            +  , dirname = path.dirname
            +  , resolve = path.resolve
            +  , join = path.join
            +  , mkdirp = require('mkdirp')
            +  , jade = require('../');
            +
            +// jade options
            +
            +var options = {};
            +
            +// options
            +
            +program
            +  .version(jade.version)
            +  .usage('[options] [dir|file ...]')
            +  .option('-o, --obj ', 'javascript options object')
            +  .option('-O, --out 
            ', 'output the compiled html to ') + .option('-p, --path ', 'filename used to resolve includes') + .option('-P, --pretty', 'compile pretty html output') + .option('-c, --client', 'compile for client-side runtime.js') + .option('-D, --no-debug', 'compile without debugging (smaller functions)') + +program.on('--help', function(){ + console.log(' Examples:'); + console.log(''); + console.log(' # translate jade the templates dir'); + console.log(' $ jade templates'); + console.log(''); + console.log(' # create {foo,bar}.html'); + console.log(' $ jade {foo,bar}.jade'); + console.log(''); + console.log(' # jade over stdio'); + console.log(' $ jade < my.jade > my.html'); + console.log(''); + console.log(' # jade over stdio'); + console.log(' $ echo "h1 Jade!" | jade'); + console.log(''); + console.log(' # foo, bar dirs rendering to /tmp'); + console.log(' $ jade foo bar --out /tmp '); + console.log(''); +}); + +program.parse(process.argv); + +// options given, parse them + +if (program.obj) options = eval('(' + program.obj + ')'); + +// --filename + +if (program.path) options.filename = program.path; + +// --no-debug + +options.compileDebug = program.debug; + +// --client + +options.client = program.client; + +// --pretty + +options.pretty = program.pretty; + +// left-over args are file paths + +var files = program.args; + +// compile files + +if (files.length) { + console.log(); + files.forEach(renderFile); + process.on('exit', console.log); +// stdio +} else { + stdin(); +} + +/** + * Compile from stdin. + */ + +function stdin() { + var buf = ''; + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function(chunk){ buf += chunk; }); + process.stdin.on('end', function(){ + var fn = jade.compile(buf, options); + var output = options.client + ? fn.toString() + : fn(options); + process.stdout.write(output); + }).resume(); +} + +/** + * Process the given path, compiling the jade files found. + * Always walk the subdirectories. + */ + +function renderFile(path) { + var re = /\.jade$/; + fs.lstat(path, function(err, stat) { + if (err) throw err; + // Found jade file + if (stat.isFile() && re.test(path)) { + fs.readFile(path, 'utf8', function(err, str){ + if (err) throw err; + options.filename = path; + var fn = jade.compile(str, options); + var extname = options.client ? '.js' : '.html'; + path = path.replace(re, extname); + if (program.out) path = join(program.out, basename(path)); + var dir = resolve(dirname(path)); + mkdirp(dir, 0755, function(err){ + if (err) throw err; + var output = options.client + ? fn.toString() + : fn(options); + fs.writeFile(path, output, function(err){ + if (err) throw err; + console.log(' \033[90mrendered \033[36m%s\033[0m', path); + }); + }); + }); + // Found directory + } else if (stat.isDirectory()) { + fs.readdir(path, function(err, files) { + if (err) throw err; + files.map(function(filename) { + return path + '/' + filename; + }).forEach(renderFile); + }); + } + }); +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/index.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/index.js new file mode 100644 index 0000000..8ad059f --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/index.js @@ -0,0 +1,4 @@ + +module.exports = process.env.JADE_COV + ? require('./lib-cov/jade') + : require('./lib/jade'); \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/jade.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/jade.js new file mode 100644 index 0000000..1983a20 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/jade.js @@ -0,0 +1,3586 @@ +(function() { + +// CommonJS require() + +function require(p){ + var path = require.resolve(p) + , mod = require.modules[path]; + if (!mod) throw new Error('failed to require "' + p + '"'); + if (!mod.exports) { + mod.exports = {}; + mod.call(mod.exports, mod, mod.exports, require.relative(path)); + } + return mod.exports; + } + +require.modules = {}; + +require.resolve = function (path){ + var orig = path + , reg = path + '.js' + , index = path + '/index.js'; + return require.modules[reg] && reg + || require.modules[index] && index + || orig; + }; + +require.register = function (path, fn){ + require.modules[path] = fn; + }; + +require.relative = function (parent) { + return function(p){ + if ('.' != p.charAt(0)) return require(p); + + var path = parent.split('/') + , segs = p.split('/'); + path.pop(); + + for (var i = 0; i < segs.length; i++) { + var seg = segs[i]; + if ('..' == seg) path.pop(); + else if ('.' != seg) path.push(seg); + } + + return require(path.join('/')); + }; + }; + + +require.register("compiler.js", function(module, exports, require){ + +/*! + * Jade - Compiler + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var nodes = require('./nodes') + , filters = require('./filters') + , doctypes = require('./doctypes') + , selfClosing = require('./self-closing') + , runtime = require('./runtime') + , utils = require('./utils'); + + + if (!Object.keys) { + Object.keys = function(obj){ + var arr = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + arr.push(key); + } + } + return arr; + } + } + + if (!String.prototype.trimLeft) { + String.prototype.trimLeft = function(){ + return this.replace(/^\s+/, ''); + } + } + + + +/** + * Initialize `Compiler` with the given `node`. + * + * @param {Node} node + * @param {Object} options + * @api public + */ + +var Compiler = module.exports = function Compiler(node, options) { + this.options = options = options || {}; + this.node = node; + this.hasCompiledDoctype = false; + this.hasCompiledTag = false; + this.pp = options.pretty || false; + this.debug = false !== options.compileDebug; + this.indents = 0; + this.parentIndents = 0; + if (options.doctype) this.setDoctype(options.doctype); +}; + +/** + * Compiler prototype. + */ + +Compiler.prototype = { + + /** + * Compile parse tree to JavaScript. + * + * @api public + */ + + compile: function(){ + this.buf = ['var interp;']; + if (this.pp) this.buf.push("var __indent = [];"); + this.lastBufferedIdx = -1; + this.visit(this.node); + return this.buf.join('\n'); + }, + + /** + * Sets the default doctype `name`. Sets terse mode to `true` when + * html 5 is used, causing self-closing tags to end with ">" vs "/>", + * and boolean attributes are not mirrored. + * + * @param {string} name + * @api public + */ + + setDoctype: function(name){ + var doctype = doctypes[(name || 'default').toLowerCase()]; + doctype = doctype || ''; + this.doctype = doctype; + this.terse = '5' == name || 'html' == name; + this.xml = 0 == this.doctype.indexOf(' 1 && !escape && block.nodes[0].isText && block.nodes[1].isText) + this.prettyIndent(1, true); + + for (var i = 0; i < len; ++i) { + // Pretty print text + if (pp && i > 0 && !escape && block.nodes[i].isText && block.nodes[i-1].isText) + this.prettyIndent(1, false); + + this.visit(block.nodes[i]); + // Multiple text nodes are separated by newlines + if (block.nodes[i+1] && block.nodes[i].isText && block.nodes[i+1].isText) + this.buffer('\\n'); + } + }, + + /** + * Visit `doctype`. Sets terse mode to `true` when html 5 + * is used, causing self-closing tags to end with ">" vs "/>", + * and boolean attributes are not mirrored. + * + * @param {Doctype} doctype + * @api public + */ + + visitDoctype: function(doctype){ + if (doctype && (doctype.val || !this.doctype)) { + this.setDoctype(doctype.val || 'default'); + } + + if (this.doctype) this.buffer(this.doctype); + this.hasCompiledDoctype = true; + }, + + /** + * Visit `mixin`, generating a function that + * may be called within the template. + * + * @param {Mixin} mixin + * @api public + */ + + visitMixin: function(mixin){ + var name = mixin.name.replace(/-/g, '_') + '_mixin' + , args = mixin.args || '' + , block = mixin.block + , attrs = mixin.attrs + , pp = this.pp; + + if (mixin.call) { + if (pp) this.buf.push("__indent.push('" + Array(this.indents + 1).join(' ') + "');") + if (block || attrs.length) { + + this.buf.push(name + '.call({'); + + if (block) { + this.buf.push('block: function(){'); + + // Render block with no indents, dynamically added when rendered + this.parentIndents++; + var _indents = this.indents; + this.indents = 0; + this.visit(mixin.block); + this.indents = _indents; + this.parentIndents--; + + if (attrs.length) { + this.buf.push('},'); + } else { + this.buf.push('}'); + } + } + + if (attrs.length) { + var val = this.attrs(attrs); + if (val.inherits) { + this.buf.push('attributes: merge({' + val.buf + + '}, attributes), escaped: merge(' + val.escaped + ', escaped, true)'); + } else { + this.buf.push('attributes: {' + val.buf + '}, escaped: ' + val.escaped); + } + } + + if (args) { + this.buf.push('}, ' + args + ');'); + } else { + this.buf.push('});'); + } + + } else { + this.buf.push(name + '(' + args + ');'); + } + if (pp) this.buf.push("__indent.pop();") + } else { + this.buf.push('var ' + name + ' = function(' + args + '){'); + this.buf.push('var block = this.block, attributes = this.attributes || {}, escaped = this.escaped || {};'); + this.parentIndents++; + this.visit(block); + this.parentIndents--; + this.buf.push('};'); + } + }, + + /** + * Visit `tag` buffering tag markup, generating + * attributes, visiting the `tag`'s code and block. + * + * @param {Tag} tag + * @api public + */ + + visitTag: function(tag){ + this.indents++; + var name = tag.name + , pp = this.pp; + + if (tag.buffer) name = "' + (" + name + ") + '"; + + if (!this.hasCompiledTag) { + if (!this.hasCompiledDoctype && 'html' == name) { + this.visitDoctype(); + } + this.hasCompiledTag = true; + } + + // pretty print + if (pp && !tag.isInline()) + this.prettyIndent(0, true); + + if ((~selfClosing.indexOf(name) || tag.selfClosing) && !this.xml) { + this.buffer('<' + name); + this.visitAttributes(tag.attrs); + this.terse + ? this.buffer('>') + : this.buffer('/>'); + } else { + // Optimize attributes buffering + if (tag.attrs.length) { + this.buffer('<' + name); + if (tag.attrs.length) this.visitAttributes(tag.attrs); + this.buffer('>'); + } else { + this.buffer('<' + name + '>'); + } + if (tag.code) this.visitCode(tag.code); + this.escape = 'pre' == tag.name; + this.visit(tag.block); + + // pretty print + if (pp && !tag.isInline() && 'pre' != tag.name && !tag.canInline()) + this.prettyIndent(0, true); + + this.buffer(''); + } + this.indents--; + }, + + /** + * Visit `filter`, throwing when the filter does not exist. + * + * @param {Filter} filter + * @api public + */ + + visitFilter: function(filter){ + var fn = filters[filter.name]; + + // unknown filter + if (!fn) { + if (filter.isASTFilter) { + throw new Error('unknown ast filter "' + filter.name + ':"'); + } else { + throw new Error('unknown filter ":' + filter.name + '"'); + } + } + + if (filter.isASTFilter) { + this.buf.push(fn(filter.block, this, filter.attrs)); + } else { + var text = filter.block.nodes.map(function(node){ return node.val }).join('\n'); + filter.attrs = filter.attrs || {}; + filter.attrs.filename = this.options.filename; + this.buffer(utils.text(fn(text, filter.attrs))); + } + }, + + /** + * Visit `text` node. + * + * @param {Text} text + * @api public + */ + + visitText: function(text){ + text = utils.text(text.val.replace(/\\/g, '\\\\')); + if (this.escape) text = escape(text); + this.buffer(text); + }, + + /** + * Visit a `comment`, only buffering when the buffer flag is set. + * + * @param {Comment} comment + * @api public + */ + + visitComment: function(comment){ + if (!comment.buffer) return; + if (this.pp) this.prettyIndent(1, true); + this.buffer(''); + }, + + /** + * Visit a `BlockComment`. + * + * @param {Comment} comment + * @api public + */ + + visitBlockComment: function(comment){ + if (!comment.buffer) return; + if (0 == comment.val.trim().indexOf('if')) { + this.buffer(''); + } else { + this.buffer(''); + } + }, + + /** + * Visit `code`, respecting buffer / escape flags. + * If the code is followed by a block, wrap it in + * a self-calling function. + * + * @param {Code} code + * @api public + */ + + visitCode: function(code){ + // Wrap code blocks with {}. + // we only wrap unbuffered code blocks ATM + // since they are usually flow control + + // Buffer code + if (code.buffer) { + var val = code.val.trimLeft(); + this.buf.push('var __val__ = ' + val); + val = 'null == __val__ ? "" : __val__'; + if (code.escape) val = 'escape(' + val + ')'; + this.buf.push("buf.push(" + val + ");"); + } else { + this.buf.push(code.val); + } + + // Block support + if (code.block) { + if (!code.buffer) this.buf.push('{'); + this.visit(code.block); + if (!code.buffer) this.buf.push('}'); + } + }, + + /** + * Visit `each` block. + * + * @param {Each} each + * @api public + */ + + visitEach: function(each){ + this.buf.push('' + + '// iterate ' + each.obj + '\n' + + ';(function(){\n' + + ' if (\'number\' == typeof ' + each.obj + '.length) {\n' + + ' for (var ' + each.key + ' = 0, $$l = ' + each.obj + '.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n' + + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); + + this.visit(each.block); + + this.buf.push('' + + ' }\n' + + ' } else {\n' + + ' for (var ' + each.key + ' in ' + each.obj + ') {\n' + + ' if (' + each.obj + '.hasOwnProperty(' + each.key + ')){' + + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); + + this.visit(each.block); + + this.buf.push(' }\n'); + + this.buf.push(' }\n }\n}).call(this);\n'); + }, + + /** + * Visit `attrs`. + * + * @param {Array} attrs + * @api public + */ + + visitAttributes: function(attrs){ + var val = this.attrs(attrs); + if (val.inherits) { + this.buf.push("buf.push(attrs(merge({ " + val.buf + + " }, attributes), merge(" + val.escaped + ", escaped, true)));"); + } else if (val.constant) { + eval('var buf={' + val.buf + '};'); + this.buffer(runtime.attrs(buf, JSON.parse(val.escaped)), true); + } else { + this.buf.push("buf.push(attrs({ " + val.buf + " }, " + val.escaped + "));"); + } + }, + + /** + * Compile attributes. + */ + + attrs: function(attrs){ + var buf = [] + , classes = [] + , escaped = {} + , constant = attrs.every(function(attr){ return isConstant(attr.val) }) + , inherits = false; + + if (this.terse) buf.push('terse: true'); + + attrs.forEach(function(attr){ + if (attr.name == 'attributes') return inherits = true; + escaped[attr.name] = attr.escaped; + if (attr.name == 'class') { + classes.push('(' + attr.val + ')'); + } else { + var pair = "'" + attr.name + "':(" + attr.val + ')'; + buf.push(pair); + } + }); + + if (classes.length) { + classes = classes.join(" + ' ' + "); + buf.push("class: " + classes); + } + + return { + buf: buf.join(', ').replace('class:', '"class":'), + escaped: JSON.stringify(escaped), + inherits: inherits, + constant: constant + }; + } +}; + +/** + * Check if expression can be evaluated to a constant + * + * @param {String} expression + * @return {Boolean} + * @api private + */ + +function isConstant(val){ + // Check strings/literals + if (/^ *("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|true|false|null|undefined) *$/i.test(val)) + return true; + + // Check numbers + if (!isNaN(Number(val))) + return true; + + // Check arrays + var matches; + if (matches = /^ *\[(.*)\] *$/.exec(val)) + return matches[1].split(',').every(isConstant); + + return false; +} + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +function escape(html){ + return String(html) + .replace(/&(?!\w+;)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} +}); // module: compiler.js + +require.register("doctypes.js", function(module, exports, require){ + +/*! + * Jade - doctypes + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = { + '5': '' + , 'default': '' + , 'xml': '' + , 'transitional': '' + , 'strict': '' + , 'frameset': '' + , '1.1': '' + , 'basic': '' + , 'mobile': '' +}; +}); // module: doctypes.js + +require.register("filters.js", function(module, exports, require){ + +/*! + * Jade - filters + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = { + + /** + * Wrap text with CDATA block. + */ + + cdata: function(str){ + return ''; + }, + + /** + * Transform sass to css, wrapped in style tags. + */ + + sass: function(str){ + str = str.replace(/\\n/g, '\n'); + var sass = require('sass').render(str).replace(/\n/g, '\\n'); + return ''; + }, + + /** + * Transform stylus to css, wrapped in style tags. + */ + + stylus: function(str, options){ + var ret; + str = str.replace(/\\n/g, '\n'); + var stylus = require('stylus'); + stylus(str, options).render(function(err, css){ + if (err) throw err; + ret = css.replace(/\n/g, '\\n'); + }); + return ''; + }, + + /** + * Transform less to css, wrapped in style tags. + */ + + less: function(str){ + var ret; + str = str.replace(/\\n/g, '\n'); + require('less').render(str, function(err, css){ + if (err) throw err; + ret = ''; + }); + return ret; + }, + + /** + * Transform markdown to html. + */ + + markdown: function(str){ + var md; + + // support markdown / discount + try { + md = require('markdown'); + } catch (err){ + try { + md = require('discount'); + } catch (err) { + try { + md = require('markdown-js'); + } catch (err) { + try { + md = require('marked'); + } catch (err) { + throw new + Error('Cannot find markdown library, install markdown, discount, or marked.'); + } + } + } + } + + str = str.replace(/\\n/g, '\n'); + return md.parse(str).replace(/\n/g, '\\n').replace(/'/g,'''); + }, + + /** + * Transform coffeescript to javascript. + */ + + coffeescript: function(str){ + str = str.replace(/\\n/g, '\n'); + var js = require('coffee-script').compile(str).replace(/\\/g, '\\\\').replace(/\n/g, '\\n'); + return ''; + } +}; + +}); // module: filters.js + +require.register("inline-tags.js", function(module, exports, require){ + +/*! + * Jade - inline tags + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = [ + 'a' + , 'abbr' + , 'acronym' + , 'b' + , 'br' + , 'code' + , 'em' + , 'font' + , 'i' + , 'img' + , 'ins' + , 'kbd' + , 'map' + , 'samp' + , 'small' + , 'span' + , 'strong' + , 'sub' + , 'sup' +]; +}); // module: inline-tags.js + +require.register("jade.js", function(module, exports, require){ +/*! + * Jade + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Parser = require('./parser') + , Lexer = require('./lexer') + , Compiler = require('./compiler') + , runtime = require('./runtime') + +/** + * Library version. + */ + +exports.version = '0.26.1'; + +/** + * Expose self closing tags. + */ + +exports.selfClosing = require('./self-closing'); + +/** + * Default supported doctypes. + */ + +exports.doctypes = require('./doctypes'); + +/** + * Text filters. + */ + +exports.filters = require('./filters'); + +/** + * Utilities. + */ + +exports.utils = require('./utils'); + +/** + * Expose `Compiler`. + */ + +exports.Compiler = Compiler; + +/** + * Expose `Parser`. + */ + +exports.Parser = Parser; + +/** + * Expose `Lexer`. + */ + +exports.Lexer = Lexer; + +/** + * Nodes. + */ + +exports.nodes = require('./nodes'); + +/** + * Jade runtime helpers. + */ + +exports.runtime = runtime; + +/** + * Template function cache. + */ + +exports.cache = {}; + +/** + * Parse the given `str` of jade and return a function body. + * + * @param {String} str + * @param {Object} options + * @return {String} + * @api private + */ + +function parse(str, options){ + try { + // Parse + var parser = new Parser(str, options.filename, options); + + // Compile + var compiler = new (options.compiler || Compiler)(parser.parse(), options) + , js = compiler.compile(); + + // Debug compiler + if (options.debug) { + console.error('\nCompiled Function:\n\n\033[90m%s\033[0m', js.replace(/^/gm, ' ')); + } + + return '' + + 'var buf = [];\n' + + (options.self + ? 'var self = locals || {};\n' + js + : 'with (locals || {}) {\n' + js + '\n}\n') + + 'return buf.join("");'; + } catch (err) { + parser = parser.context(); + runtime.rethrow(err, parser.filename, parser.lexer.lineno); + } +} + +/** + * Compile a `Function` representation of the given jade `str`. + * + * Options: + * + * - `compileDebug` when `false` debugging code is stripped from the compiled template + * - `client` when `true` the helper functions `escape()` etc will reference `jade.escape()` + * for use with the Jade client-side runtime.js + * + * @param {String} str + * @param {Options} options + * @return {Function} + * @api public + */ + +exports.compile = function(str, options){ + var options = options || {} + , client = options.client + , filename = options.filename + ? JSON.stringify(options.filename) + : 'undefined' + , fn; + + if (options.compileDebug !== false) { + fn = [ + 'var __jade = [{ lineno: 1, filename: ' + filename + ' }];' + , 'try {' + , parse(String(str), options) + , '} catch (err) {' + , ' rethrow(err, __jade[0].filename, __jade[0].lineno);' + , '}' + ].join('\n'); + } else { + fn = parse(String(str), options); + } + + if (client) { + fn = 'attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;\n' + fn; + } + + fn = new Function('locals, attrs, escape, rethrow, merge', fn); + + if (client) return fn; + + return function(locals){ + return fn(locals, runtime.attrs, runtime.escape, runtime.rethrow, runtime.merge); + }; +}; + +/** + * Render the given `str` of jade and invoke + * the callback `fn(err, str)`. + * + * Options: + * + * - `cache` enable template caching + * - `filename` filename required for `include` / `extends` and caching + * + * @param {String} str + * @param {Object|Function} options or fn + * @param {Function} fn + * @api public + */ + +exports.render = function(str, options, fn){ + // swap args + if ('function' == typeof options) { + fn = options, options = {}; + } + + // cache requires .filename + if (options.cache && !options.filename) { + return fn(new Error('the "filename" option is required for caching')); + } + + try { + var path = options.filename; + var tmpl = options.cache + ? exports.cache[path] || (exports.cache[path] = exports.compile(str, options)) + : exports.compile(str, options); + fn(null, tmpl(options)); + } catch (err) { + fn(err); + } +}; + +/** + * Render a Jade file at the given `path` and callback `fn(err, str)`. + * + * @param {String} path + * @param {Object|Function} options or callback + * @param {Function} fn + * @api public + */ + +exports.renderFile = function(path, options, fn){ + var key = path + ':string'; + + if ('function' == typeof options) { + fn = options, options = {}; + } + + try { + options.filename = path; + var str = options.cache + ? exports.cache[key] || (exports.cache[key] = fs.readFileSync(path, 'utf8')) + : fs.readFileSync(path, 'utf8'); + exports.render(str, options, fn); + } catch (err) { + fn(err); + } +}; + +/** + * Express support. + */ + +exports.__express = exports.renderFile; + +}); // module: jade.js + +require.register("lexer.js", function(module, exports, require){ + +/*! + * Jade - Lexer + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Initialize `Lexer` with the given `str`. + * + * Options: + * + * - `colons` allow colons for attr delimiters + * + * @param {String} str + * @param {Object} options + * @api private + */ + +var Lexer = module.exports = function Lexer(str, options) { + options = options || {}; + this.input = str.replace(/\r\n|\r/g, '\n'); + this.colons = options.colons; + this.deferredTokens = []; + this.lastIndents = 0; + this.lineno = 1; + this.stash = []; + this.indentStack = []; + this.indentRe = null; + this.pipeless = false; +}; + +/** + * Lexer prototype. + */ + +Lexer.prototype = { + + /** + * Construct a token with the given `type` and `val`. + * + * @param {String} type + * @param {String} val + * @return {Object} + * @api private + */ + + tok: function(type, val){ + return { + type: type + , line: this.lineno + , val: val + } + }, + + /** + * Consume the given `len` of input. + * + * @param {Number} len + * @api private + */ + + consume: function(len){ + this.input = this.input.substr(len); + }, + + /** + * Scan for `type` with the given `regexp`. + * + * @param {String} type + * @param {RegExp} regexp + * @return {Object} + * @api private + */ + + scan: function(regexp, type){ + var captures; + if (captures = regexp.exec(this.input)) { + this.consume(captures[0].length); + return this.tok(type, captures[1]); + } + }, + + /** + * Defer the given `tok`. + * + * @param {Object} tok + * @api private + */ + + defer: function(tok){ + this.deferredTokens.push(tok); + }, + + /** + * Lookahead `n` tokens. + * + * @param {Number} n + * @return {Object} + * @api private + */ + + lookahead: function(n){ + var fetch = n - this.stash.length; + while (fetch-- > 0) this.stash.push(this.next()); + return this.stash[--n]; + }, + + /** + * Return the indexOf `start` / `end` delimiters. + * + * @param {String} start + * @param {String} end + * @return {Number} + * @api private + */ + + indexOfDelimiters: function(start, end){ + var str = this.input + , nstart = 0 + , nend = 0 + , pos = 0; + for (var i = 0, len = str.length; i < len; ++i) { + if (start == str.charAt(i)) { + ++nstart; + } else if (end == str.charAt(i)) { + if (++nend == nstart) { + pos = i; + break; + } + } + } + return pos; + }, + + /** + * Stashed token. + */ + + stashed: function() { + return this.stash.length + && this.stash.shift(); + }, + + /** + * Deferred token. + */ + + deferred: function() { + return this.deferredTokens.length + && this.deferredTokens.shift(); + }, + + /** + * end-of-source. + */ + + eos: function() { + if (this.input.length) return; + if (this.indentStack.length) { + this.indentStack.shift(); + return this.tok('outdent'); + } else { + return this.tok('eos'); + } + }, + + /** + * Blank line. + */ + + blank: function() { + var captures; + if (captures = /^\n *\n/.exec(this.input)) { + this.consume(captures[0].length - 1); + if (this.pipeless) return this.tok('text', ''); + return this.next(); + } + }, + + /** + * Comment. + */ + + comment: function() { + var captures; + if (captures = /^ *\/\/(-)?([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('comment', captures[2]); + tok.buffer = '-' != captures[1]; + return tok; + } + }, + + /** + * Interpolated tag. + */ + + interpolation: function() { + var captures; + if (captures = /^#\{(.*?)\}/.exec(this.input)) { + this.consume(captures[0].length); + return this.tok('interpolation', captures[1]); + } + }, + + /** + * Tag. + */ + + tag: function() { + var captures; + if (captures = /^(\w[-:\w]*)(\/?)/.exec(this.input)) { + this.consume(captures[0].length); + var tok, name = captures[1]; + if (':' == name[name.length - 1]) { + name = name.slice(0, -1); + tok = this.tok('tag', name); + this.defer(this.tok(':')); + while (' ' == this.input[0]) this.input = this.input.substr(1); + } else { + tok = this.tok('tag', name); + } + tok.selfClosing = !! captures[2]; + return tok; + } + }, + + /** + * Filter. + */ + + filter: function() { + return this.scan(/^:(\w+)/, 'filter'); + }, + + /** + * Doctype. + */ + + doctype: function() { + return this.scan(/^(?:!!!|doctype) *([^\n]+)?/, 'doctype'); + }, + + /** + * Id. + */ + + id: function() { + return this.scan(/^#([\w-]+)/, 'id'); + }, + + /** + * Class. + */ + + className: function() { + return this.scan(/^\.([\w-]+)/, 'class'); + }, + + /** + * Text. + */ + + text: function() { + return this.scan(/^(?:\| ?| ?)?([^\n]+)/, 'text'); + }, + + /** + * Extends. + */ + + "extends": function() { + return this.scan(/^extends? +([^\n]+)/, 'extends'); + }, + + /** + * Block prepend. + */ + + prepend: function() { + var captures; + if (captures = /^prepend +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = 'prepend' + , name = captures[1] + , tok = this.tok('block', name); + tok.mode = mode; + return tok; + } + }, + + /** + * Block append. + */ + + append: function() { + var captures; + if (captures = /^append +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = 'append' + , name = captures[1] + , tok = this.tok('block', name); + tok.mode = mode; + return tok; + } + }, + + /** + * Block. + */ + + block: function() { + var captures; + if (captures = /^block\b *(?:(prepend|append) +)?([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = captures[1] || 'replace' + , name = captures[2] + , tok = this.tok('block', name); + + tok.mode = mode; + return tok; + } + }, + + /** + * Yield. + */ + + yield: function() { + return this.scan(/^yield */, 'yield'); + }, + + /** + * Include. + */ + + include: function() { + return this.scan(/^include +([^\n]+)/, 'include'); + }, + + /** + * Case. + */ + + "case": function() { + return this.scan(/^case +([^\n]+)/, 'case'); + }, + + /** + * When. + */ + + when: function() { + return this.scan(/^when +([^:\n]+)/, 'when'); + }, + + /** + * Default. + */ + + "default": function() { + return this.scan(/^default */, 'default'); + }, + + /** + * Assignment. + */ + + assignment: function() { + var captures; + if (captures = /^(\w+) += *([^;\n]+)( *;? *)/.exec(this.input)) { + this.consume(captures[0].length); + var name = captures[1] + , val = captures[2]; + return this.tok('code', 'var ' + name + ' = (' + val + ');'); + } + }, + + /** + * Call mixin. + */ + + call: function(){ + var captures; + if (captures = /^\+([-\w]+)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('call', captures[1]); + + // Check for args (not attributes) + if (captures = /^ *\((.*?)\)/.exec(this.input)) { + if (!/^ *[-\w]+ *=/.test(captures[1])) { + this.consume(captures[0].length); + tok.args = captures[1]; + } + } + + return tok; + } + }, + + /** + * Mixin. + */ + + mixin: function(){ + var captures; + if (captures = /^mixin +([-\w]+)(?: *\((.*)\))?/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('mixin', captures[1]); + tok.args = captures[2]; + return tok; + } + }, + + /** + * Conditional. + */ + + conditional: function() { + var captures; + if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var type = captures[1] + , js = captures[2]; + + switch (type) { + case 'if': js = 'if (' + js + ')'; break; + case 'unless': js = 'if (!(' + js + '))'; break; + case 'else if': js = 'else if (' + js + ')'; break; + case 'else': js = 'else'; break; + } + + return this.tok('code', js); + } + }, + + /** + * While. + */ + + "while": function() { + var captures; + if (captures = /^while +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + return this.tok('code', 'while (' + captures[1] + ')'); + } + }, + + /** + * Each. + */ + + each: function() { + var captures; + if (captures = /^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('each', captures[1]); + tok.key = captures[2] || '$index'; + tok.code = captures[3]; + return tok; + } + }, + + /** + * Code. + */ + + code: function() { + var captures; + if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var flags = captures[1]; + captures[1] = captures[2]; + var tok = this.tok('code', captures[1]); + tok.escape = flags[0] === '='; + tok.buffer = flags[0] === '=' || flags[1] === '='; + return tok; + } + }, + + /** + * Attributes. + */ + + attrs: function() { + if ('(' == this.input.charAt(0)) { + var index = this.indexOfDelimiters('(', ')') + , str = this.input.substr(1, index-1) + , tok = this.tok('attrs') + , len = str.length + , colons = this.colons + , states = ['key'] + , escapedAttr + , key = '' + , val = '' + , quote + , c + , p; + + function state(){ + return states[states.length - 1]; + } + + function interpolate(attr) { + return attr.replace(/#\{([^}]+)\}/g, function(_, expr){ + return quote + " + (" + expr + ") + " + quote; + }); + } + + this.consume(index + 1); + tok.attrs = {}; + tok.escaped = {}; + + function parse(c) { + var real = c; + // TODO: remove when people fix ":" + if (colons && ':' == c) c = '='; + switch (c) { + case ',': + case '\n': + switch (state()) { + case 'expr': + case 'array': + case 'string': + case 'object': + val += c; + break; + default: + states.push('key'); + val = val.trim(); + key = key.trim(); + if ('' == key) return; + key = key.replace(/^['"]|['"]$/g, '').replace('!', ''); + tok.escaped[key] = escapedAttr; + tok.attrs[key] = '' == val + ? true + : interpolate(val); + key = val = ''; + } + break; + case '=': + switch (state()) { + case 'key char': + key += real; + break; + case 'val': + case 'expr': + case 'array': + case 'string': + case 'object': + val += real; + break; + default: + escapedAttr = '!' != p; + states.push('val'); + } + break; + case '(': + if ('val' == state() + || 'expr' == state()) states.push('expr'); + val += c; + break; + case ')': + if ('expr' == state() + || 'val' == state()) states.pop(); + val += c; + break; + case '{': + if ('val' == state()) states.push('object'); + val += c; + break; + case '}': + if ('object' == state()) states.pop(); + val += c; + break; + case '[': + if ('val' == state()) states.push('array'); + val += c; + break; + case ']': + if ('array' == state()) states.pop(); + val += c; + break; + case '"': + case "'": + switch (state()) { + case 'key': + states.push('key char'); + break; + case 'key char': + states.pop(); + break; + case 'string': + if (c == quote) states.pop(); + val += c; + break; + default: + states.push('string'); + val += c; + quote = c; + } + break; + case '': + break; + default: + switch (state()) { + case 'key': + case 'key char': + key += c; + break; + default: + val += c; + } + } + p = c; + } + + for (var i = 0; i < len; ++i) { + parse(str.charAt(i)); + } + + parse(','); + + if ('/' == this.input.charAt(0)) { + this.consume(1); + tok.selfClosing = true; + } + + return tok; + } + }, + + /** + * Indent | Outdent | Newline. + */ + + indent: function() { + var captures, re; + + // established regexp + if (this.indentRe) { + captures = this.indentRe.exec(this.input); + // determine regexp + } else { + // tabs + re = /^\n(\t*) */; + captures = re.exec(this.input); + + // spaces + if (captures && !captures[1].length) { + re = /^\n( *)/; + captures = re.exec(this.input); + } + + // established + if (captures && captures[1].length) this.indentRe = re; + } + + if (captures) { + var tok + , indents = captures[1].length; + + ++this.lineno; + this.consume(indents + 1); + + if (' ' == this.input[0] || '\t' == this.input[0]) { + throw new Error('Invalid indentation, you can use tabs or spaces but not both'); + } + + // blank line + if ('\n' == this.input[0]) return this.tok('newline'); + + // outdent + if (this.indentStack.length && indents < this.indentStack[0]) { + while (this.indentStack.length && this.indentStack[0] > indents) { + this.stash.push(this.tok('outdent')); + this.indentStack.shift(); + } + tok = this.stash.pop(); + // indent + } else if (indents && indents != this.indentStack[0]) { + this.indentStack.unshift(indents); + tok = this.tok('indent', indents); + // newline + } else { + tok = this.tok('newline'); + } + + return tok; + } + }, + + /** + * Pipe-less text consumed only when + * pipeless is true; + */ + + pipelessText: function() { + if (this.pipeless) { + if ('\n' == this.input[0]) return; + var i = this.input.indexOf('\n'); + if (-1 == i) i = this.input.length; + var str = this.input.substr(0, i); + this.consume(str.length); + return this.tok('text', str); + } + }, + + /** + * ':' + */ + + colon: function() { + return this.scan(/^: */, ':'); + }, + + /** + * Return the next token object, or those + * previously stashed by lookahead. + * + * @return {Object} + * @api private + */ + + advance: function(){ + return this.stashed() + || this.next(); + }, + + /** + * Return the next token object. + * + * @return {Object} + * @api private + */ + + next: function() { + return this.deferred() + || this.blank() + || this.eos() + || this.pipelessText() + || this.yield() + || this.doctype() + || this.interpolation() + || this["case"]() + || this.when() + || this["default"]() + || this["extends"]() + || this.append() + || this.prepend() + || this.block() + || this.include() + || this.mixin() + || this.call() + || this.conditional() + || this.each() + || this["while"]() + || this.assignment() + || this.tag() + || this.filter() + || this.code() + || this.id() + || this.className() + || this.attrs() + || this.indent() + || this.comment() + || this.colon() + || this.text(); + } +}; + +}); // module: lexer.js + +require.register("nodes/attrs.js", function(module, exports, require){ + +/*! + * Jade - nodes - Attrs + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'), + Block = require('./block'); + +/** + * Initialize a `Attrs` node. + * + * @api public + */ + +var Attrs = module.exports = function Attrs() { + this.attrs = []; +}; + +/** + * Inherit from `Node`. + */ + +Attrs.prototype = new Node; +Attrs.prototype.constructor = Attrs; + + +/** + * Set attribute `name` to `val`, keep in mind these become + * part of a raw js object literal, so to quote a value you must + * '"quote me"', otherwise or example 'user.name' is literal JavaScript. + * + * @param {String} name + * @param {String} val + * @param {Boolean} escaped + * @return {Tag} for chaining + * @api public + */ + +Attrs.prototype.setAttribute = function(name, val, escaped){ + this.attrs.push({ name: name, val: val, escaped: escaped }); + return this; +}; + +/** + * Remove attribute `name` when present. + * + * @param {String} name + * @api public + */ + +Attrs.prototype.removeAttribute = function(name){ + for (var i = 0, len = this.attrs.length; i < len; ++i) { + if (this.attrs[i] && this.attrs[i].name == name) { + delete this.attrs[i]; + } + } +}; + +/** + * Get attribute value by `name`. + * + * @param {String} name + * @return {String} + * @api public + */ + +Attrs.prototype.getAttribute = function(name){ + for (var i = 0, len = this.attrs.length; i < len; ++i) { + if (this.attrs[i] && this.attrs[i].name == name) { + return this.attrs[i].val; + } + } +}; + +}); // module: nodes/attrs.js + +require.register("nodes/block-comment.js", function(module, exports, require){ + +/*! + * Jade - nodes - BlockComment + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `BlockComment` with the given `block`. + * + * @param {String} val + * @param {Block} block + * @param {Boolean} buffer + * @api public + */ + +var BlockComment = module.exports = function BlockComment(val, block, buffer) { + this.block = block; + this.val = val; + this.buffer = buffer; +}; + +/** + * Inherit from `Node`. + */ + +BlockComment.prototype = new Node; +BlockComment.prototype.constructor = BlockComment; + +}); // module: nodes/block-comment.js + +require.register("nodes/block.js", function(module, exports, require){ + +/*! + * Jade - nodes - Block + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a new `Block` with an optional `node`. + * + * @param {Node} node + * @api public + */ + +var Block = module.exports = function Block(node){ + this.nodes = []; + if (node) this.push(node); +}; + +/** + * Inherit from `Node`. + */ + +Block.prototype = new Node; +Block.prototype.constructor = Block; + + +/** + * Block flag. + */ + +Block.prototype.isBlock = true; + +/** + * Replace the nodes in `other` with the nodes + * in `this` block. + * + * @param {Block} other + * @api private + */ + +Block.prototype.replace = function(other){ + other.nodes = this.nodes; +}; + +/** + * Pust the given `node`. + * + * @param {Node} node + * @return {Number} + * @api public + */ + +Block.prototype.push = function(node){ + return this.nodes.push(node); +}; + +/** + * Check if this block is empty. + * + * @return {Boolean} + * @api public + */ + +Block.prototype.isEmpty = function(){ + return 0 == this.nodes.length; +}; + +/** + * Unshift the given `node`. + * + * @param {Node} node + * @return {Number} + * @api public + */ + +Block.prototype.unshift = function(node){ + return this.nodes.unshift(node); +}; + +/** + * Return the "last" block, or the first `yield` node. + * + * @return {Block} + * @api private + */ + +Block.prototype.includeBlock = function(){ + var ret = this + , node; + + for (var i = 0, len = this.nodes.length; i < len; ++i) { + node = this.nodes[i]; + if (node.yield) return node; + else if (node.textOnly) continue; + else if (node.includeBlock) ret = node.includeBlock(); + else if (node.block && !node.block.isEmpty()) ret = node.block.includeBlock(); + } + + return ret; +}; + +/** + * Return a clone of this block. + * + * @return {Block} + * @api private + */ + +Block.prototype.clone = function(){ + var clone = new Block; + for (var i = 0, len = this.nodes.length; i < len; ++i) { + clone.push(this.nodes[i].clone()); + } + return clone; +}; + + +}); // module: nodes/block.js + +require.register("nodes/case.js", function(module, exports, require){ + +/*! + * Jade - nodes - Case + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a new `Case` with `expr`. + * + * @param {String} expr + * @api public + */ + +var Case = exports = module.exports = function Case(expr, block){ + this.expr = expr; + this.block = block; +}; + +/** + * Inherit from `Node`. + */ + +Case.prototype = new Node; +Case.prototype.constructor = Case; + + +var When = exports.When = function When(expr, block){ + this.expr = expr; + this.block = block; + this.debug = false; +}; + +/** + * Inherit from `Node`. + */ + +When.prototype = new Node; +When.prototype.constructor = When; + + + +}); // module: nodes/case.js + +require.register("nodes/code.js", function(module, exports, require){ + +/*! + * Jade - nodes - Code + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Code` node with the given code `val`. + * Code may also be optionally buffered and escaped. + * + * @param {String} val + * @param {Boolean} buffer + * @param {Boolean} escape + * @api public + */ + +var Code = module.exports = function Code(val, buffer, escape) { + this.val = val; + this.buffer = buffer; + this.escape = escape; + if (val.match(/^ *else/)) this.debug = false; +}; + +/** + * Inherit from `Node`. + */ + +Code.prototype = new Node; +Code.prototype.constructor = Code; + +}); // module: nodes/code.js + +require.register("nodes/comment.js", function(module, exports, require){ + +/*! + * Jade - nodes - Comment + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Comment` with the given `val`, optionally `buffer`, + * otherwise the comment may render in the output. + * + * @param {String} val + * @param {Boolean} buffer + * @api public + */ + +var Comment = module.exports = function Comment(val, buffer) { + this.val = val; + this.buffer = buffer; +}; + +/** + * Inherit from `Node`. + */ + +Comment.prototype = new Node; +Comment.prototype.constructor = Comment; + +}); // module: nodes/comment.js + +require.register("nodes/doctype.js", function(module, exports, require){ + +/*! + * Jade - nodes - Doctype + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Doctype` with the given `val`. + * + * @param {String} val + * @api public + */ + +var Doctype = module.exports = function Doctype(val) { + this.val = val; +}; + +/** + * Inherit from `Node`. + */ + +Doctype.prototype = new Node; +Doctype.prototype.constructor = Doctype; + +}); // module: nodes/doctype.js + +require.register("nodes/each.js", function(module, exports, require){ + +/*! + * Jade - nodes - Each + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize an `Each` node, representing iteration + * + * @param {String} obj + * @param {String} val + * @param {String} key + * @param {Block} block + * @api public + */ + +var Each = module.exports = function Each(obj, val, key, block) { + this.obj = obj; + this.val = val; + this.key = key; + this.block = block; +}; + +/** + * Inherit from `Node`. + */ + +Each.prototype = new Node; +Each.prototype.constructor = Each; + +}); // module: nodes/each.js + +require.register("nodes/filter.js", function(module, exports, require){ + +/*! + * Jade - nodes - Filter + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node') + , Block = require('./block'); + +/** + * Initialize a `Filter` node with the given + * filter `name` and `block`. + * + * @param {String} name + * @param {Block|Node} block + * @api public + */ + +var Filter = module.exports = function Filter(name, block, attrs) { + this.name = name; + this.block = block; + this.attrs = attrs; + this.isASTFilter = !block.nodes.every(function(node){ return node.isText }); +}; + +/** + * Inherit from `Node`. + */ + +Filter.prototype = new Node; +Filter.prototype.constructor = Filter; + +}); // module: nodes/filter.js + +require.register("nodes/index.js", function(module, exports, require){ + +/*! + * Jade - nodes + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +exports.Node = require('./node'); +exports.Tag = require('./tag'); +exports.Code = require('./code'); +exports.Each = require('./each'); +exports.Case = require('./case'); +exports.Text = require('./text'); +exports.Block = require('./block'); +exports.Mixin = require('./mixin'); +exports.Filter = require('./filter'); +exports.Comment = require('./comment'); +exports.Literal = require('./literal'); +exports.BlockComment = require('./block-comment'); +exports.Doctype = require('./doctype'); + +}); // module: nodes/index.js + +require.register("nodes/literal.js", function(module, exports, require){ + +/*! + * Jade - nodes - Literal + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Literal` node with the given `str. + * + * @param {String} str + * @api public + */ + +var Literal = module.exports = function Literal(str) { + this.str = str + .replace(/\\/g, "\\\\") + .replace(/\n|\r\n/g, "\\n") + .replace(/'/g, "\\'"); +}; + +/** + * Inherit from `Node`. + */ + +Literal.prototype = new Node; +Literal.prototype.constructor = Literal; + + +}); // module: nodes/literal.js + +require.register("nodes/mixin.js", function(module, exports, require){ + +/*! + * Jade - nodes - Mixin + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Attrs = require('./attrs'); + +/** + * Initialize a new `Mixin` with `name` and `block`. + * + * @param {String} name + * @param {String} args + * @param {Block} block + * @api public + */ + +var Mixin = module.exports = function Mixin(name, args, block, call){ + this.name = name; + this.args = args; + this.block = block; + this.attrs = []; + this.call = call; +}; + +/** + * Inherit from `Attrs`. + */ + +Mixin.prototype = new Attrs; +Mixin.prototype.constructor = Mixin; + + + +}); // module: nodes/mixin.js + +require.register("nodes/node.js", function(module, exports, require){ + +/*! + * Jade - nodes - Node + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Initialize a `Node`. + * + * @api public + */ + +var Node = module.exports = function Node(){}; + +/** + * Clone this node (return itself) + * + * @return {Node} + * @api private + */ + +Node.prototype.clone = function(){ + return this; +}; + +}); // module: nodes/node.js + +require.register("nodes/tag.js", function(module, exports, require){ + +/*! + * Jade - nodes - Tag + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Attrs = require('./attrs'), + Block = require('./block'), + inlineTags = require('../inline-tags'); + +/** + * Initialize a `Tag` node with the given tag `name` and optional `block`. + * + * @param {String} name + * @param {Block} block + * @api public + */ + +var Tag = module.exports = function Tag(name, block) { + this.name = name; + this.attrs = []; + this.block = block || new Block; +}; + +/** + * Inherit from `Attrs`. + */ + +Tag.prototype = new Attrs; +Tag.prototype.constructor = Tag; + + +/** + * Clone this tag. + * + * @return {Tag} + * @api private + */ + +Tag.prototype.clone = function(){ + var clone = new Tag(this.name, this.block.clone()); + clone.line = this.line; + clone.attrs = this.attrs; + clone.textOnly = this.textOnly; + return clone; +}; + +/** + * Check if this tag is an inline tag. + * + * @return {Boolean} + * @api private + */ + +Tag.prototype.isInline = function(){ + return ~inlineTags.indexOf(this.name); +}; + +/** + * Check if this tag's contents can be inlined. Used for pretty printing. + * + * @return {Boolean} + * @api private + */ + +Tag.prototype.canInline = function(){ + var nodes = this.block.nodes; + + function isInline(node){ + // Recurse if the node is a block + if (node.isBlock) return node.nodes.every(isInline); + return node.isText || (node.isInline && node.isInline()); + } + + // Empty tag + if (!nodes.length) return true; + + // Text-only or inline-only tag + if (1 == nodes.length) return isInline(nodes[0]); + + // Multi-line inline-only tag + if (this.block.nodes.every(isInline)) { + for (var i = 1, len = nodes.length; i < len; ++i) { + if (nodes[i-1].isText && nodes[i].isText) + return false; + } + return true; + } + + // Mixed tag + return false; +}; +}); // module: nodes/tag.js + +require.register("nodes/text.js", function(module, exports, require){ + +/*! + * Jade - nodes - Text + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Text` node with optional `line`. + * + * @param {String} line + * @api public + */ + +var Text = module.exports = function Text(line) { + this.val = ''; + if ('string' == typeof line) this.val = line; +}; + +/** + * Inherit from `Node`. + */ + +Text.prototype = new Node; +Text.prototype.constructor = Text; + + +/** + * Flag as text. + */ + +Text.prototype.isText = true; +}); // module: nodes/text.js + +require.register("parser.js", function(module, exports, require){ + +/*! + * Jade - Parser + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Lexer = require('./lexer') + , nodes = require('./nodes'); + +/** + * Initialize `Parser` with the given input `str` and `filename`. + * + * @param {String} str + * @param {String} filename + * @param {Object} options + * @api public + */ + +var Parser = exports = module.exports = function Parser(str, filename, options){ + this.input = str; + this.lexer = new Lexer(str, options); + this.filename = filename; + this.blocks = {}; + this.mixins = {}; + this.options = options; + this.contexts = [this]; +}; + +/** + * Tags that may not contain tags. + */ + +var textOnly = exports.textOnly = ['script', 'style']; + +/** + * Parser prototype. + */ + +Parser.prototype = { + + /** + * Push `parser` onto the context stack, + * or pop and return a `Parser`. + */ + + context: function(parser){ + if (parser) { + this.contexts.push(parser); + } else { + return this.contexts.pop(); + } + }, + + /** + * Return the next token object. + * + * @return {Object} + * @api private + */ + + advance: function(){ + return this.lexer.advance(); + }, + + /** + * Skip `n` tokens. + * + * @param {Number} n + * @api private + */ + + skip: function(n){ + while (n--) this.advance(); + }, + + /** + * Single token lookahead. + * + * @return {Object} + * @api private + */ + + peek: function() { + return this.lookahead(1); + }, + + /** + * Return lexer lineno. + * + * @return {Number} + * @api private + */ + + line: function() { + return this.lexer.lineno; + }, + + /** + * `n` token lookahead. + * + * @param {Number} n + * @return {Object} + * @api private + */ + + lookahead: function(n){ + return this.lexer.lookahead(n); + }, + + /** + * Parse input returning a string of js for evaluation. + * + * @return {String} + * @api public + */ + + parse: function(){ + var block = new nodes.Block, parser; + block.line = this.line(); + + while ('eos' != this.peek().type) { + if ('newline' == this.peek().type) { + this.advance(); + } else { + block.push(this.parseExpr()); + } + } + + if (parser = this.extending) { + this.context(parser); + var ast = parser.parse(); + this.context(); + // hoist mixins + for (var name in this.mixins) + ast.unshift(this.mixins[name]); + return ast; + } + + return block; + }, + + /** + * Expect the given type, or throw an exception. + * + * @param {String} type + * @api private + */ + + expect: function(type){ + if (this.peek().type === type) { + return this.advance(); + } else { + throw new Error('expected "' + type + '", but got "' + this.peek().type + '"'); + } + }, + + /** + * Accept the given `type`. + * + * @param {String} type + * @api private + */ + + accept: function(type){ + if (this.peek().type === type) { + return this.advance(); + } + }, + + /** + * tag + * | doctype + * | mixin + * | include + * | filter + * | comment + * | text + * | each + * | code + * | yield + * | id + * | class + * | interpolation + */ + + parseExpr: function(){ + switch (this.peek().type) { + case 'tag': + return this.parseTag(); + case 'mixin': + return this.parseMixin(); + case 'block': + return this.parseBlock(); + case 'case': + return this.parseCase(); + case 'when': + return this.parseWhen(); + case 'default': + return this.parseDefault(); + case 'extends': + return this.parseExtends(); + case 'include': + return this.parseInclude(); + case 'doctype': + return this.parseDoctype(); + case 'filter': + return this.parseFilter(); + case 'comment': + return this.parseComment(); + case 'text': + return this.parseText(); + case 'each': + return this.parseEach(); + case 'code': + return this.parseCode(); + case 'call': + return this.parseCall(); + case 'interpolation': + return this.parseInterpolation(); + case 'yield': + this.advance(); + var block = new nodes.Block; + block.yield = true; + return block; + case 'id': + case 'class': + var tok = this.advance(); + this.lexer.defer(this.lexer.tok('tag', 'div')); + this.lexer.defer(tok); + return this.parseExpr(); + default: + throw new Error('unexpected token "' + this.peek().type + '"'); + } + }, + + /** + * Text + */ + + parseText: function(){ + var tok = this.expect('text') + , node = new nodes.Text(tok.val); + node.line = this.line(); + return node; + }, + + /** + * ':' expr + * | block + */ + + parseBlockExpansion: function(){ + if (':' == this.peek().type) { + this.advance(); + return new nodes.Block(this.parseExpr()); + } else { + return this.block(); + } + }, + + /** + * case + */ + + parseCase: function(){ + var val = this.expect('case').val + , node = new nodes.Case(val); + node.line = this.line(); + node.block = this.block(); + return node; + }, + + /** + * when + */ + + parseWhen: function(){ + var val = this.expect('when').val + return new nodes.Case.When(val, this.parseBlockExpansion()); + }, + + /** + * default + */ + + parseDefault: function(){ + this.expect('default'); + return new nodes.Case.When('default', this.parseBlockExpansion()); + }, + + /** + * code + */ + + parseCode: function(){ + var tok = this.expect('code') + , node = new nodes.Code(tok.val, tok.buffer, tok.escape) + , block + , i = 1; + node.line = this.line(); + while (this.lookahead(i) && 'newline' == this.lookahead(i).type) ++i; + block = 'indent' == this.lookahead(i).type; + if (block) { + this.skip(i-1); + node.block = this.block(); + } + return node; + }, + + /** + * comment + */ + + parseComment: function(){ + var tok = this.expect('comment') + , node; + + if ('indent' == this.peek().type) { + node = new nodes.BlockComment(tok.val, this.block(), tok.buffer); + } else { + node = new nodes.Comment(tok.val, tok.buffer); + } + + node.line = this.line(); + return node; + }, + + /** + * doctype + */ + + parseDoctype: function(){ + var tok = this.expect('doctype') + , node = new nodes.Doctype(tok.val); + node.line = this.line(); + return node; + }, + + /** + * filter attrs? text-block + */ + + parseFilter: function(){ + var block + , tok = this.expect('filter') + , attrs = this.accept('attrs'); + + this.lexer.pipeless = true; + block = this.parseTextBlock(); + this.lexer.pipeless = false; + + var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs); + node.line = this.line(); + return node; + }, + + /** + * tag ':' attrs? block + */ + + parseASTFilter: function(){ + var block + , tok = this.expect('tag') + , attrs = this.accept('attrs'); + + this.expect(':'); + block = this.block(); + + var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs); + node.line = this.line(); + return node; + }, + + /** + * each block + */ + + parseEach: function(){ + var tok = this.expect('each') + , node = new nodes.Each(tok.code, tok.val, tok.key); + node.line = this.line(); + node.block = this.block(); + return node; + }, + + /** + * 'extends' name + */ + + parseExtends: function(){ + var path = require('path') + , fs = require('fs') + , dirname = path.dirname + , basename = path.basename + , join = path.join; + + if (!this.filename) + throw new Error('the "filename" option is required to extend templates'); + + var path = this.expect('extends').val.trim() + , dir = dirname(this.filename); + + var path = join(dir, path + '.jade') + , str = fs.readFileSync(path, 'utf8') + , parser = new Parser(str, path, this.options); + + parser.blocks = this.blocks; + parser.contexts = this.contexts; + this.extending = parser; + + // TODO: null node + return new nodes.Literal(''); + }, + + /** + * 'block' name block + */ + + parseBlock: function(){ + var block = this.expect('block') + , mode = block.mode + , name = block.val.trim(); + + block = 'indent' == this.peek().type + ? this.block() + : new nodes.Block(new nodes.Literal('')); + + var prev = this.blocks[name]; + + if (prev) { + switch (prev.mode) { + case 'append': + block.nodes = block.nodes.concat(prev.nodes); + prev = block; + break; + case 'prepend': + block.nodes = prev.nodes.concat(block.nodes); + prev = block; + break; + } + } + + block.mode = mode; + return this.blocks[name] = prev || block; + }, + + /** + * include block? + */ + + parseInclude: function(){ + var path = require('path') + , fs = require('fs') + , dirname = path.dirname + , basename = path.basename + , join = path.join; + + var path = this.expect('include').val.trim() + , dir = dirname(this.filename); + + if (!this.filename) + throw new Error('the "filename" option is required to use includes'); + + // no extension + if (!~basename(path).indexOf('.')) { + path += '.jade'; + } + + // non-jade + if ('.jade' != path.substr(-5)) { + var path = join(dir, path) + , str = fs.readFileSync(path, 'utf8'); + return new nodes.Literal(str); + } + + var path = join(dir, path) + , str = fs.readFileSync(path, 'utf8') + , parser = new Parser(str, path, this.options); + parser.blocks = this.blocks; + parser.mixins = this.mixins; + + this.context(parser); + var ast = parser.parse(); + this.context(); + ast.filename = path; + + if ('indent' == this.peek().type) { + ast.includeBlock().push(this.block()); + } + + return ast; + }, + + /** + * call ident block + */ + + parseCall: function(){ + var tok = this.expect('call') + , name = tok.val + , args = tok.args + , mixin = new nodes.Mixin(name, args, new nodes.Block, true); + + this.tag(mixin); + if (mixin.block.isEmpty()) mixin.block = null; + return mixin; + }, + + /** + * mixin block + */ + + parseMixin: function(){ + var tok = this.expect('mixin') + , name = tok.val + , args = tok.args + , mixin; + + // definition + if ('indent' == this.peek().type) { + mixin = new nodes.Mixin(name, args, this.block(), false); + this.mixins[name] = mixin; + return mixin; + // call + } else { + return new nodes.Mixin(name, args, null, true); + } + }, + + /** + * indent (text | newline)* outdent + */ + + parseTextBlock: function(){ + var block = new nodes.Block; + block.line = this.line(); + var spaces = this.expect('indent').val; + if (null == this._spaces) this._spaces = spaces; + var indent = Array(spaces - this._spaces + 1).join(' '); + while ('outdent' != this.peek().type) { + switch (this.peek().type) { + case 'newline': + this.advance(); + break; + case 'indent': + this.parseTextBlock().nodes.forEach(function(node){ + block.push(node); + }); + break; + default: + var text = new nodes.Text(indent + this.advance().val); + text.line = this.line(); + block.push(text); + } + } + + if (spaces == this._spaces) this._spaces = null; + this.expect('outdent'); + return block; + }, + + /** + * indent expr* outdent + */ + + block: function(){ + var block = new nodes.Block; + block.line = this.line(); + this.expect('indent'); + while ('outdent' != this.peek().type) { + if ('newline' == this.peek().type) { + this.advance(); + } else { + block.push(this.parseExpr()); + } + } + this.expect('outdent'); + return block; + }, + + /** + * interpolation (attrs | class | id)* (text | code | ':')? newline* block? + */ + + parseInterpolation: function(){ + var tok = this.advance(); + var tag = new nodes.Tag(tok.val); + tag.buffer = true; + return this.tag(tag); + }, + + /** + * tag (attrs | class | id)* (text | code | ':')? newline* block? + */ + + parseTag: function(){ + // ast-filter look-ahead + var i = 2; + if ('attrs' == this.lookahead(i).type) ++i; + if (':' == this.lookahead(i).type) { + if ('indent' == this.lookahead(++i).type) { + return this.parseASTFilter(); + } + } + + var tok = this.advance() + , tag = new nodes.Tag(tok.val); + + tag.selfClosing = tok.selfClosing; + + return this.tag(tag); + }, + + /** + * Parse tag. + */ + + tag: function(tag){ + var dot; + + tag.line = this.line(); + + // (attrs | class | id)* + out: + while (true) { + switch (this.peek().type) { + case 'id': + case 'class': + var tok = this.advance(); + tag.setAttribute(tok.type, "'" + tok.val + "'"); + continue; + case 'attrs': + var tok = this.advance() + , obj = tok.attrs + , escaped = tok.escaped + , names = Object.keys(obj); + + if (tok.selfClosing) tag.selfClosing = true; + + for (var i = 0, len = names.length; i < len; ++i) { + var name = names[i] + , val = obj[name]; + tag.setAttribute(name, val, escaped[name]); + } + continue; + default: + break out; + } + } + + // check immediate '.' + if ('.' == this.peek().val) { + dot = tag.textOnly = true; + this.advance(); + } + + // (text | code | ':')? + switch (this.peek().type) { + case 'text': + tag.block.push(this.parseText()); + break; + case 'code': + tag.code = this.parseCode(); + break; + case ':': + this.advance(); + tag.block = new nodes.Block; + tag.block.push(this.parseExpr()); + break; + } + + // newline* + while ('newline' == this.peek().type) this.advance(); + + tag.textOnly = tag.textOnly || ~textOnly.indexOf(tag.name); + + // script special-case + if ('script' == tag.name) { + var type = tag.getAttribute('type'); + if (!dot && type && 'text/javascript' != type.replace(/^['"]|['"]$/g, '')) { + tag.textOnly = false; + } + } + + // block? + if ('indent' == this.peek().type) { + if (tag.textOnly) { + this.lexer.pipeless = true; + tag.block = this.parseTextBlock(); + this.lexer.pipeless = false; + } else { + var block = this.block(); + if (tag.block) { + for (var i = 0, len = block.nodes.length; i < len; ++i) { + tag.block.push(block.nodes[i]); + } + } else { + tag.block = block; + } + } + } + + return tag; + } +}; + +}); // module: parser.js + +require.register("runtime.js", function(module, exports, require){ + +/*! + * Jade - runtime + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Lame Array.isArray() polyfill for now. + */ + +if (!Array.isArray) { + Array.isArray = function(arr){ + return '[object Array]' == Object.prototype.toString.call(arr); + }; +} + +/** + * Lame Object.keys() polyfill for now. + */ + +if (!Object.keys) { + Object.keys = function(obj){ + var arr = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + arr.push(key); + } + } + return arr; + } +} + +/** + * Merge two attribute objects giving precedence + * to values in object `b`. Classes are special-cased + * allowing for arrays and merging/joining appropriately + * resulting in a string. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api private + */ + +exports.merge = function merge(a, b) { + var ac = a['class']; + var bc = b['class']; + + if (ac || bc) { + ac = ac || []; + bc = bc || []; + if (!Array.isArray(ac)) ac = [ac]; + if (!Array.isArray(bc)) bc = [bc]; + ac = ac.filter(nulls); + bc = bc.filter(nulls); + a['class'] = ac.concat(bc).join(' '); + } + + for (var key in b) { + if (key != 'class') { + a[key] = b[key]; + } + } + + return a; +}; + +/** + * Filter null `val`s. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function nulls(val) { + return val != null; +} + +/** + * Render the given attributes object. + * + * @param {Object} obj + * @param {Object} escaped + * @return {String} + * @api private + */ + +exports.attrs = function attrs(obj, escaped){ + var buf = [] + , terse = obj.terse; + + delete obj.terse; + var keys = Object.keys(obj) + , len = keys.length; + + if (len) { + buf.push(''); + for (var i = 0; i < len; ++i) { + var key = keys[i] + , val = obj[key]; + + if ('boolean' == typeof val || null == val) { + if (val) { + terse + ? buf.push(key) + : buf.push(key + '="' + key + '"'); + } + } else if (0 == key.indexOf('data') && 'string' != typeof val) { + buf.push(key + "='" + JSON.stringify(val) + "'"); + } else if ('class' == key && Array.isArray(val)) { + buf.push(key + '="' + exports.escape(val.join(' ')) + '"'); + } else if (escaped && escaped[key]) { + buf.push(key + '="' + exports.escape(val) + '"'); + } else { + buf.push(key + '="' + val + '"'); + } + } + } + + return buf.join(' '); +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function escape(html){ + return String(html) + .replace(/&(?!(\w+|\#\d+);)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; + +/** + * Re-throw the given `err` in context to the + * the jade in `filename` at the given `lineno`. + * + * @param {Error} err + * @param {String} filename + * @param {String} lineno + * @api private + */ + +exports.rethrow = function rethrow(err, filename, lineno){ + if (!filename) throw err; + + var context = 3 + , str = require('fs').readFileSync(filename, 'utf8') + , lines = str.split('\n') + , start = Math.max(lineno - context, 0) + , end = Math.min(lines.length, lineno + context); + + // Error context + var context = lines.slice(start, end).map(function(line, i){ + var curr = i + start + 1; + return (curr == lineno ? ' > ' : ' ') + + curr + + '| ' + + line; + }).join('\n'); + + // Alter exception message + err.path = filename; + err.message = (filename || 'Jade') + ':' + lineno + + '\n' + context + '\n\n' + err.message; + throw err; +}; + +}); // module: runtime.js + +require.register("self-closing.js", function(module, exports, require){ + +/*! + * Jade - self closing tags + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = [ + 'meta' + , 'img' + , 'link' + , 'input' + , 'source' + , 'area' + , 'base' + , 'col' + , 'br' + , 'hr' +]; +}); // module: self-closing.js + +require.register("utils.js", function(module, exports, require){ + +/*! + * Jade - utils + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Convert interpolation in the given string to JavaScript. + * + * @param {String} str + * @return {String} + * @api private + */ + +var interpolate = exports.interpolate = function(str){ + return str.replace(/(\\)?([#!]){(.*?)}/g, function(str, escape, flag, code){ + return escape + ? str + : "' + " + + ('!' == flag ? '' : 'escape') + + "((interp = " + code.replace(/\\'/g, "'") + + ") == null ? '' : interp) + '"; + }); +}; + +/** + * Escape single quotes in `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +var escape = exports.escape = function(str) { + return str.replace(/'/g, "\\'"); +}; + +/** + * Interpolate, and escape the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.text = function(str){ + return interpolate(escape(str)); +}; +}); // module: utils.js + +window.jade = require("jade"); +})(); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/jade.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/jade.md new file mode 100644 index 0000000..051dc03 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/jade.md @@ -0,0 +1,510 @@ + +# Jade + + The jade template engine for node.js + +## Synopsis + + jade [-h|--help] [-v|--version] [-o|--obj STR] + [-O|--out DIR] [-p|--path PATH] [-P|--pretty] + [-c|--client] [-D|--no-debug] + +## Examples + + translate jade the templates dir + + $ jade templates + + create {foo,bar}.html + + $ jade {foo,bar}.jade + + jade over stdio + + $ jade < my.jade > my.html + + jade over s + + $ echo "h1 Jade!" | jade + + foo, bar dirs rendering to /tmp + + $ jade foo bar --out /tmp + + compile client-side templates without debugging + instrumentation, making the output javascript + very light-weight. This requires runtime.js + in your projects. + + $ jade --client --no-debug < my.jade + +## Tags + + Tags are simply nested via whitespace, closing + tags defined for you. These indents are called "blocks". + + ul + li + a Foo + li + a Bar + + You may have several tags in one "block": + + ul + li + a Foo + a Bar + a Baz + +## Self-closing Tags + + Some tags are flagged as self-closing by default, such + as `meta`, `link`, and so on. To explicitly self-close + a tag simply append the `/` character: + + foo/ + foo(bar='baz')/ + + Would yield: + + + + +## Attributes + + Tag attributes look similar to HTML, however + the values are regular JavaScript, here are + some examples: + + a(href='google.com') Google + a(class='button', href='google.com') Google + + As mentioned the attribute values are just JavaScript, + this means ternary operations and other JavaScript expressions + work just fine: + + body(class=user.authenticated ? 'authenticated' : 'anonymous') + a(href=user.website || 'http://google.com') + + Multiple lines work too: + + input(type='checkbox', + name='agreement', + checked) + + Multiple lines without the comma work fine: + + input(type='checkbox' + name='agreement' + checked) + + Funky whitespace? fine: + + input( + type='checkbox' + name='agreement' + checked) + +## Boolean attributes + + Boolean attributes are mirrored by Jade, and accept + bools, aka _true_ or _false_. When no value is specified + _true_ is assumed. For example: + + input(type="checkbox", checked) + // => "" + + For example if the checkbox was for an agreement, perhaps `user.agreed` + was _true_ the following would also output 'checked="checked"': + + input(type="checkbox", checked=user.agreed) + +## Class attributes + + The _class_ attribute accepts an array of classes, + this can be handy when generated from a javascript + function etc: + + classes = ['foo', 'bar', 'baz'] + a(class=classes) + // => "" + +## Class literal + + Classes may be defined using a ".CLASSNAME" syntax: + + .button + // => "
            " + + Or chained: + + .large.button + // => "
            " + + The previous defaulted to divs, however you + may also specify the tag type: + + h1.title My Title + // => "

            My Title

            " + +## Id literal + + Much like the class literal there's an id literal: + + #user-1 + // => "
            " + + Again we may specify the tag as well: + + ul#menu + li: a(href='/home') Home + li: a(href='/store') Store + li: a(href='/contact') Contact + + Finally all of these may be used in any combination, + the following are all valid tags: + + a.button#contact(style: 'color: red') Contact + a.button(style: 'color: red')#contact Contact + a(style: 'color: red').button#contact Contact + +## Block expansion + + Jade supports the concept of "block expansion", in which + using a trailing ":" after a tag will inject a block: + + ul + li: a Foo + li: a Bar + li: a Baz + +## Text + + Arbitrary text may follow tags: + + p Welcome to my site + + yields: + +

            Welcome to my site

            + +## Pipe text + + Another form of text is "pipe" text. Pipes act + as the text margin for large bodies of text. + + p + | This is a large + | body of text for + | this tag. + | + | Nothing too + | exciting. + + yields: + +

            This is a large + body of text for + this tag. + + Nothing too + exciting. +

            + + Using pipes we can also specify regular Jade tags + within the text: + + p + | Click to visit + a(href='http://google.com') Google + | if you want. + +## Text only tags + + As an alternative to pipe text you may add + a trailing "." to indicate that the block + contains nothing but plain-text, no tags: + + p. + This is a large + body of text for + this tag. + + Nothing too + exciting. + + Some tags are text-only by default, for example + _script_, _textarea_, and _style_ tags do not + contain nested HTML so Jade implies the trailing ".": + + script + if (foo) { + bar(); + } + + style + body { + padding: 50px; + font: 14px Helvetica; + } + +## Template script tags + + Sometimes it's useful to define HTML in script + tags using Jade, typically for client-side templates. + + To do this simply give the _script_ tag an arbitrary + _type_ attribute such as _text/x-template_: + + script(type='text/template') + h1 Look! + p Jade still works in here! + +## Interpolation + + Both plain-text and piped-text support interpolation, + which comes in two forms, escapes and non-escaped. The + following will output the _user.name_ in the paragraph + but HTML within it will be escaped to prevent XSS attacks: + + p Welcome #{user.name} + + The following syntax is identical however it will _not_ escape + HTML, and should only be used with strings that you trust: + + p Welcome !{user.name} + +## Inline HTML + + Sometimes constructing small inline snippets of HTML + in Jade can be annoying, luckily we can add plain + HTML as well: + + p Welcome #{user.name} + +## Code + + To buffer output with Jade simply use _=_ at the beginning + of a line or after a tag. This method escapes any HTML + present in the string. + + p= user.description + + To buffer output unescaped use the _!=_ variant, but again + be careful of XSS. + + p!= user.description + + The final way to mess with JavaScript code in Jade is the unbuffered + _-_, which can be used for conditionals, defining variables etc: + + - var user = { description: 'foo bar baz' } + #user + - if (user.description) { + h2 Description + p.description= user.description + - } + + When compiled blocks are wrapped in anonymous functions, so the + following is also valid, without braces: + + - var user = { description: 'foo bar baz' } + #user + - if (user.description) + h2 Description + p.description= user.description + + If you really want you could even use `.forEach()` and others: + + - users.forEach(function(user){ + .user + h2= user.name + p User #{user.name} is #{user.age} years old + - }) + + Taking this further Jade provides some syntax for conditionals, + iteration, switch statements etc. Let's look at those next! + +## Assignment + + Jade's first-class assignment is simple, simply use the _=_ + operator and Jade will _var_ it for you. The following are equivalent: + + - var user = { name: 'tobi' } + user = { name: 'tobi' } + +## Conditionals + + Jade's first-class conditional syntax allows for optional + parenthesis, and you may now omit the leading _-_ otherwise + it's identical, still just regular javascript: + + user = { description: 'foo bar baz' } + #user + if user.description + h2 Description + p.description= user.description + + Jade provides the negated version, _unless_ as well, the following + are equivalent: + + - if (!(user.isAnonymous)) + p You're logged in as #{user.name} + + unless user.isAnonymous + p You're logged in as #{user.name} + +## Iteration + + JavaScript's _for_ loops don't look very declarative, so Jade + also provides its own _for_ loop construct, aliased as _each_: + + for user in users + .user + h2= user.name + p user #{user.name} is #{user.age} year old + + As mentioned _each_ is identical: + + each user in users + .user + h2= user.name + + If necessary the index is available as well: + + for user, i in users + .user(class='user-#{i}') + h2= user.name + + Remember, it's just JavaScript: + + ul#letters + for letter in ['a', 'b', 'c'] + li= letter + +## Mixins + + Mixins provide a way to define jade "functions" which "mix in" + their contents when called. This is useful for abstracting + out large fragments of Jade. + + The simplest possible mixin which accepts no arguments might + look like this: + + mixin hello + p Hello + + You use a mixin by placing `+` before the name: + + +hello + + For something a little more dynamic, mixins can take + arguments, the mixin itself is converted to a javascript + function internally: + + mixin hello(user) + p Hello #{user} + + +hello('Tobi') + + Yields: + +

            Hello Tobi

            + + Mixins may optionally take blocks, when a block is passed + its contents becomes the implicit `block` argument. For + example here is a mixin passed a block, and also invoked + without passing a block: + + mixin article(title) + .article + .article-wrapper + h1= title + if block + block + else + p No content provided + + +article('Hello world') + + +article('Hello world') + p This is my + p Amazing article + + yields: + +
            +
            +

            Hello world

            +

            No content provided

            +
            +
            + +
            +
            +

            Hello world

            +

            This is my

            +

            Amazing article

            +
            +
            + + Mixins can even take attributes, just like a tag. When + attributes are passed they become the implicit `attributes` + argument. Individual attributes can be accessed just like + normal object properties: + + mixin centered + .centered(class=attributes.class) + block + + +centered.bold Hello world + + +centered.red + p This is my + p Amazing article + + yields: + +
            Hello world
            +
            +

            This is my

            +

            Amazing article

            +
            + + If you use `attributes` directly, *all* passed attributes + get used: + + mixin link + a.menu(attributes) + block + + +link.highlight(href='#top') Top + +link#sec1.plain(href='#section1') Section 1 + +link#sec2.plain(href='#section2') Section 2 + + yields: + + Top + Section 1 + Section 2 + + If you pass arguments, they must directly follow the mixin: + + mixin list(arr) + if block + .title + block + ul(attributes) + each item in arr + li= item + + +list(['foo', 'bar', 'baz'])(id='myList', class='bold') + + yields: + +
              +
            • foo
            • +
            • bar
            • +
            • baz
            • +
            diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/jade.min.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/jade.min.js new file mode 100644 index 0000000..72e4535 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/jade.min.js @@ -0,0 +1,2 @@ +(function(){function require(p){var path=require.resolve(p),mod=require.modules[path];if(!mod)throw new Error('failed to require "'+p+'"');return mod.exports||(mod.exports={},mod.call(mod.exports,mod,mod.exports,require.relative(path))),mod.exports}require.modules={},require.resolve=function(path){var orig=path,reg=path+".js",index=path+"/index.js";return require.modules[reg]&®||require.modules[index]&&index||orig},require.register=function(path,fn){require.modules[path]=fn},require.relative=function(parent){return function(p){if("."!=p.charAt(0))return require(p);var path=parent.split("/"),segs=p.split("/");path.pop();for(var i=0;i",this.doctype=doctype,this.terse="5"==name||"html"==name,this.xml=0==this.doctype.indexOf("1&&!escape&&block.nodes[0].isText&&block.nodes[1].isText&&this.prettyIndent(1,!0);for(var i=0;i0&&!escape&&block.nodes[i].isText&&block.nodes[i-1].isText&&this.prettyIndent(1,!1),this.visit(block.nodes[i]),block.nodes[i+1]&&block.nodes[i].isText&&block.nodes[i+1].isText&&this.buffer("\\n")},visitDoctype:function(doctype){doctype&&(doctype.val||!this.doctype)&&this.setDoctype(doctype.val||"default"),this.doctype&&this.buffer(this.doctype),this.hasCompiledDoctype=!0},visitMixin:function(mixin){var name=mixin.name.replace(/-/g,"_")+"_mixin",args=mixin.args||"",block=mixin.block,attrs=mixin.attrs,pp=this.pp;if(mixin.call){pp&&this.buf.push("__indent.push('"+Array(this.indents+1).join(" ")+"');");if(block||attrs.length){this.buf.push(name+".call({");if(block){this.buf.push("block: function(){"),this.parentIndents++;var _indents=this.indents;this.indents=0,this.visit(mixin.block),this.indents=_indents,this.parentIndents--,attrs.length?this.buf.push("},"):this.buf.push("}")}if(attrs.length){var val=this.attrs(attrs);val.inherits?this.buf.push("attributes: merge({"+val.buf+"}, attributes), escaped: merge("+val.escaped+", escaped, true)"):this.buf.push("attributes: {"+val.buf+"}, escaped: "+val.escaped)}args?this.buf.push("}, "+args+");"):this.buf.push("});")}else this.buf.push(name+"("+args+");");pp&&this.buf.push("__indent.pop();")}else this.buf.push("var "+name+" = function("+args+"){"),this.buf.push("var block = this.block, attributes = this.attributes || {}, escaped = this.escaped || {};"),this.parentIndents++,this.visit(block),this.parentIndents--,this.buf.push("};")},visitTag:function(tag){this.indents++;var name=tag.name,pp=this.pp;tag.buffer&&(name="' + ("+name+") + '"),this.hasCompiledTag||(!this.hasCompiledDoctype&&"html"==name&&this.visitDoctype(),this.hasCompiledTag=!0),pp&&!tag.isInline()&&this.prettyIndent(0,!0),(~selfClosing.indexOf(name)||tag.selfClosing)&&!this.xml?(this.buffer("<"+name),this.visitAttributes(tag.attrs),this.terse?this.buffer(">"):this.buffer("/>")):(tag.attrs.length?(this.buffer("<"+name),tag.attrs.length&&this.visitAttributes(tag.attrs),this.buffer(">")):this.buffer("<"+name+">"),tag.code&&this.visitCode(tag.code),this.escape="pre"==tag.name,this.visit(tag.block),pp&&!tag.isInline()&&"pre"!=tag.name&&!tag.canInline()&&this.prettyIndent(0,!0),this.buffer("")),this.indents--},visitFilter:function(filter){var fn=filters[filter.name];if(!fn)throw filter.isASTFilter?new Error('unknown ast filter "'+filter.name+':"'):new Error('unknown filter ":'+filter.name+'"');if(filter.isASTFilter)this.buf.push(fn(filter.block,this,filter.attrs));else{var text=filter.block.nodes.map(function(node){return node.val}).join("\n");filter.attrs=filter.attrs||{},filter.attrs.filename=this.options.filename,this.buffer(utils.text(fn(text,filter.attrs)))}},visitText:function(text){text=utils.text(text.val.replace(/\\/g,"\\\\")),this.escape&&(text=escape(text)),this.buffer(text)},visitComment:function(comment){if(!comment.buffer)return;this.pp&&this.prettyIndent(1,!0),this.buffer("")},visitBlockComment:function(comment){if(!comment.buffer)return;0==comment.val.trim().indexOf("if")?(this.buffer("")):(this.buffer(""))},visitCode:function(code){if(code.buffer){var val=code.val.trimLeft();this.buf.push("var __val__ = "+val),val='null == __val__ ? "" : __val__',code.escape&&(val="escape("+val+")"),this.buf.push("buf.push("+val+");")}else this.buf.push(code.val);code.block&&(code.buffer||this.buf.push("{"),this.visit(code.block),code.buffer||this.buf.push("}"))},visitEach:function(each){this.buf.push("// iterate "+each.obj+"\n"+";(function(){\n"+" if ('number' == typeof "+each.obj+".length) {\n"+" for (var "+each.key+" = 0, $$l = "+each.obj+".length; "+each.key+" < $$l; "+each.key+"++) {\n"+" var "+each.val+" = "+each.obj+"["+each.key+"];\n"),this.visit(each.block),this.buf.push(" }\n } else {\n for (var "+each.key+" in "+each.obj+") {\n"+" if ("+each.obj+".hasOwnProperty("+each.key+")){"+" var "+each.val+" = "+each.obj+"["+each.key+"];\n"),this.visit(each.block),this.buf.push(" }\n"),this.buf.push(" }\n }\n}).call(this);\n")},visitAttributes:function(attrs){var val=this.attrs(attrs);val.inherits?this.buf.push("buf.push(attrs(merge({ "+val.buf+" }, attributes), merge("+val.escaped+", escaped, true)));"):val.constant?(eval("var buf={"+val.buf+"};"),this.buffer(runtime.attrs(buf,JSON.parse(val.escaped)),!0)):this.buf.push("buf.push(attrs({ "+val.buf+" }, "+val.escaped+"));")},attrs:function(attrs){var buf=[],classes=[],escaped={},constant=attrs.every(function(attr){return isConstant(attr.val)}),inherits=!1;return this.terse&&buf.push("terse: true"),attrs.forEach(function(attr){if(attr.name=="attributes")return inherits=!0;escaped[attr.name]=attr.escaped;if(attr.name=="class")classes.push("("+attr.val+")");else{var pair="'"+attr.name+"':("+attr.val+")";buf.push(pair)}}),classes.length&&(classes=classes.join(" + ' ' + "),buf.push("class: "+classes)),{buf:buf.join(", ").replace("class:",'"class":'),escaped:JSON.stringify(escaped),inherits:inherits,constant:constant}}};function isConstant(val){if(/^ *("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|true|false|null|undefined) *$/i.test(val))return!0;if(!isNaN(Number(val)))return!0;var matches;return(matches=/^ *\[(.*)\] *$/.exec(val))?matches[1].split(",").every(isConstant):!1}function escape(html){return String(html).replace(/&(?!\w+;)/g,"&").replace(//g,">").replace(/"/g,""")}}),require.register("doctypes.js",function(module,exports,require){module.exports={5:"","default":"",xml:'',transitional:'',strict:'',frameset:'',1.1:'',basic:'',mobile:''}}),require.register("filters.js",function(module,exports,require){module.exports={cdata:function(str){return""},sass:function(str){str=str.replace(/\\n/g,"\n");var sass=require("sass").render(str).replace(/\n/g,"\\n");return'"},stylus:function(str,options){var ret;str=str.replace(/\\n/g,"\n");var stylus=require("stylus");return stylus(str,options).render(function(err,css){if(err)throw err;ret=css.replace(/\n/g,"\\n")}),'"},less:function(str){var ret;return str=str.replace(/\\n/g,"\n"),require("less").render(str,function(err,css){if(err)throw err;ret='"}),ret},markdown:function(str){var md;try{md=require("markdown")}catch(err){try{md=require("discount")}catch(err){try{md=require("markdown-js")}catch(err){try{md=require("marked")}catch(err){throw new Error("Cannot find markdown library, install markdown, discount, or marked.")}}}}return str=str.replace(/\\n/g,"\n"),md.parse(str).replace(/\n/g,"\\n").replace(/'/g,"'")},coffeescript:function(str){str=str.replace(/\\n/g,"\n");var js=require("coffee-script").compile(str).replace(/\\/g,"\\\\").replace(/\n/g,"\\n");return'"}}}),require.register("inline-tags.js",function(module,exports,require){module.exports=["a","abbr","acronym","b","br","code","em","font","i","img","ins","kbd","map","samp","small","span","strong","sub","sup"]}),require.register("jade.js",function(module,exports,require){var Parser=require("./parser"),Lexer=require("./lexer"),Compiler=require("./compiler"),runtime=require("./runtime");exports.version="0.26.1",exports.selfClosing=require("./self-closing"),exports.doctypes=require("./doctypes"),exports.filters=require("./filters"),exports.utils=require("./utils"),exports.Compiler=Compiler,exports.Parser=Parser,exports.Lexer=Lexer,exports.nodes=require("./nodes"),exports.runtime=runtime,exports.cache={};function parse(str,options){try{var parser=new Parser(str,options.filename,options),compiler=new(options.compiler||Compiler)(parser.parse(),options),js=compiler.compile();return options.debug&&console.error("\nCompiled Function:\n\n%s",js.replace(/^/gm," ")),"var buf = [];\n"+(options.self?"var self = locals || {};\n"+js:"with (locals || {}) {\n"+js+"\n}\n")+'return buf.join("");'}catch(err){parser=parser.context(),runtime.rethrow(err,parser.filename,parser.lexer.lineno)}}exports.compile=function(str,options){var options=options||{},client=options.client,filename=options.filename?JSON.stringify(options.filename):"undefined",fn;return options.compileDebug!==!1?fn=["var __jade = [{ lineno: 1, filename: "+filename+" }];","try {",parse(String(str),options),"} catch (err) {"," rethrow(err, __jade[0].filename, __jade[0].lineno);","}"].join("\n"):fn=parse(String(str),options),client&&(fn="attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;\n"+fn),fn=new Function("locals, attrs, escape, rethrow, merge",fn),client?fn:function(locals){return fn(locals,runtime.attrs,runtime.escape,runtime.rethrow,runtime.merge)}},exports.render=function(str,options,fn){"function"==typeof options&&(fn=options,options={});if(options.cache&&!options.filename)return fn(new Error('the "filename" option is required for caching'));try{var path=options.filename,tmpl=options.cache?exports.cache[path]||(exports.cache[path]=exports.compile(str,options)):exports.compile(str,options);fn(null,tmpl(options))}catch(err){fn(err)}},exports.renderFile=function(path,options,fn){var key=path+":string";"function"==typeof options&&(fn=options,options={});try{options.filename=path;var str=options.cache?exports.cache[key]||(exports.cache[key]=fs.readFileSync(path,"utf8")):fs.readFileSync(path,"utf8");exports.render(str,options,fn)}catch(err){fn(err)}},exports.__express=exports.renderFile}),require.register("lexer.js",function(module,exports,require){var Lexer=module.exports=function Lexer(str,options){options=options||{},this.input=str.replace(/\r\n|\r/g,"\n"),this.colons=options.colons,this.deferredTokens=[],this.lastIndents=0,this.lineno=1,this.stash=[],this.indentStack=[],this.indentRe=null,this.pipeless=!1};Lexer.prototype={tok:function(type,val){return{type:type,line:this.lineno,val:val}},consume:function(len){this.input=this.input.substr(len)},scan:function(regexp,type){var captures;if(captures=regexp.exec(this.input))return this.consume(captures[0].length),this.tok(type,captures[1])},defer:function(tok){this.deferredTokens.push(tok)},lookahead:function(n){var fetch=n-this.stash.length;while(fetch-->0)this.stash.push(this.next());return this.stash[--n]},indexOfDelimiters:function(start,end){var str=this.input,nstart=0,nend=0,pos=0;for(var i=0,len=str.length;iindents)this.stash.push(this.tok("outdent")),this.indentStack.shift();tok=this.stash.pop()}else indents&&indents!=this.indentStack[0]?(this.indentStack.unshift(indents),tok=this.tok("indent",indents)):tok=this.tok("newline");return tok}},pipelessText:function(){if(this.pipeless){if("\n"==this.input[0])return;var i=this.input.indexOf("\n");-1==i&&(i=this.input.length);var str=this.input.substr(0,i);return this.consume(str.length),this.tok("text",str)}},colon:function(){return this.scan(/^: */,":")},advance:function(){return this.stashed()||this.next()},next:function(){return this.deferred()||this.blank()||this.eos()||this.pipelessText()||this.yield()||this.doctype()||this.interpolation()||this["case"]()||this.when()||this["default"]()||this["extends"]()||this.append()||this.prepend()||this.block()||this.include()||this.mixin()||this.call()||this.conditional()||this.each()||this["while"]()||this.assignment()||this.tag()||this.filter()||this.code()||this.id()||this.className()||this.attrs()||this.indent()||this.comment()||this.colon()||this.text()}}}),require.register("nodes/attrs.js",function(module,exports,require){var Node=require("./node"),Block=require("./block"),Attrs=module.exports=function Attrs(){this.attrs=[]};Attrs.prototype=new Node,Attrs.prototype.constructor=Attrs,Attrs.prototype.setAttribute=function(name,val,escaped){return this.attrs.push({name:name,val:val,escaped:escaped}),this},Attrs.prototype.removeAttribute=function(name){for(var i=0,len=this.attrs.length;i/g,">").replace(/"/g,""")},exports.rethrow=function rethrow(err,filename,lineno){if(!filename)throw err;var context=3,str=require("fs").readFileSync(filename,"utf8"),lines=str.split("\n"),start=Math.max(lineno-context,0),end=Math.min(lines.length,lineno+context),context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" > ":" ")+curr+"| "+line}).join("\n");throw err.path=filename,err.message=(filename||"Jade")+":"+lineno+"\n"+context+"\n\n"+err.message,err}}),require.register("self-closing.js",function(module,exports,require){module.exports=["meta","img","link","input","source","area","base","col","br","hr"]}),require.register("utils.js",function(module,exports,require){var interpolate=exports.interpolate=function(str){return str.replace(/(\\)?([#!]){(.*?)}/g,function(str,escape,flag,code){return escape?str:"' + "+("!"==flag?"":"escape")+"((interp = "+code.replace(/\\'/g,"'")+") == null ? '' : interp) + '"})},escape=exports.escape=function(str){return str.replace(/'/g,"\\'")};exports.text=function(str){return interpolate(escape(str))}}),window.jade=require("jade")})(); \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/compiler.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/compiler.js new file mode 100644 index 0000000..516ac83 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/compiler.js @@ -0,0 +1,642 @@ + +/*! + * Jade - Compiler + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var nodes = require('./nodes') + , filters = require('./filters') + , doctypes = require('./doctypes') + , selfClosing = require('./self-closing') + , runtime = require('./runtime') + , utils = require('./utils'); + +// if browser +// +// if (!Object.keys) { +// Object.keys = function(obj){ +// var arr = []; +// for (var key in obj) { +// if (obj.hasOwnProperty(key)) { +// arr.push(key); +// } +// } +// return arr; +// } +// } +// +// if (!String.prototype.trimLeft) { +// String.prototype.trimLeft = function(){ +// return this.replace(/^\s+/, ''); +// } +// } +// +// end + + +/** + * Initialize `Compiler` with the given `node`. + * + * @param {Node} node + * @param {Object} options + * @api public + */ + +var Compiler = module.exports = function Compiler(node, options) { + this.options = options = options || {}; + this.node = node; + this.hasCompiledDoctype = false; + this.hasCompiledTag = false; + this.pp = options.pretty || false; + this.debug = false !== options.compileDebug; + this.indents = 0; + this.parentIndents = 0; + if (options.doctype) this.setDoctype(options.doctype); +}; + +/** + * Compiler prototype. + */ + +Compiler.prototype = { + + /** + * Compile parse tree to JavaScript. + * + * @api public + */ + + compile: function(){ + this.buf = ['var interp;']; + if (this.pp) this.buf.push("var __indent = [];"); + this.lastBufferedIdx = -1; + this.visit(this.node); + return this.buf.join('\n'); + }, + + /** + * Sets the default doctype `name`. Sets terse mode to `true` when + * html 5 is used, causing self-closing tags to end with ">" vs "/>", + * and boolean attributes are not mirrored. + * + * @param {string} name + * @api public + */ + + setDoctype: function(name){ + var doctype = doctypes[(name || 'default').toLowerCase()]; + doctype = doctype || ''; + this.doctype = doctype; + this.terse = '5' == name || 'html' == name; + this.xml = 0 == this.doctype.indexOf(' 1 && !escape && block.nodes[0].isText && block.nodes[1].isText) + this.prettyIndent(1, true); + + for (var i = 0; i < len; ++i) { + // Pretty print text + if (pp && i > 0 && !escape && block.nodes[i].isText && block.nodes[i-1].isText) + this.prettyIndent(1, false); + + this.visit(block.nodes[i]); + // Multiple text nodes are separated by newlines + if (block.nodes[i+1] && block.nodes[i].isText && block.nodes[i+1].isText) + this.buffer('\\n'); + } + }, + + /** + * Visit `doctype`. Sets terse mode to `true` when html 5 + * is used, causing self-closing tags to end with ">" vs "/>", + * and boolean attributes are not mirrored. + * + * @param {Doctype} doctype + * @api public + */ + + visitDoctype: function(doctype){ + if (doctype && (doctype.val || !this.doctype)) { + this.setDoctype(doctype.val || 'default'); + } + + if (this.doctype) this.buffer(this.doctype); + this.hasCompiledDoctype = true; + }, + + /** + * Visit `mixin`, generating a function that + * may be called within the template. + * + * @param {Mixin} mixin + * @api public + */ + + visitMixin: function(mixin){ + var name = mixin.name.replace(/-/g, '_') + '_mixin' + , args = mixin.args || '' + , block = mixin.block + , attrs = mixin.attrs + , pp = this.pp; + + if (mixin.call) { + if (pp) this.buf.push("__indent.push('" + Array(this.indents + 1).join(' ') + "');") + if (block || attrs.length) { + + this.buf.push(name + '.call({'); + + if (block) { + this.buf.push('block: function(){'); + + // Render block with no indents, dynamically added when rendered + this.parentIndents++; + var _indents = this.indents; + this.indents = 0; + this.visit(mixin.block); + this.indents = _indents; + this.parentIndents--; + + if (attrs.length) { + this.buf.push('},'); + } else { + this.buf.push('}'); + } + } + + if (attrs.length) { + var val = this.attrs(attrs); + if (val.inherits) { + this.buf.push('attributes: merge({' + val.buf + + '}, attributes), escaped: merge(' + val.escaped + ', escaped, true)'); + } else { + this.buf.push('attributes: {' + val.buf + '}, escaped: ' + val.escaped); + } + } + + if (args) { + this.buf.push('}, ' + args + ');'); + } else { + this.buf.push('});'); + } + + } else { + this.buf.push(name + '(' + args + ');'); + } + if (pp) this.buf.push("__indent.pop();") + } else { + this.buf.push('var ' + name + ' = function(' + args + '){'); + this.buf.push('var block = this.block, attributes = this.attributes || {}, escaped = this.escaped || {};'); + this.parentIndents++; + this.visit(block); + this.parentIndents--; + this.buf.push('};'); + } + }, + + /** + * Visit `tag` buffering tag markup, generating + * attributes, visiting the `tag`'s code and block. + * + * @param {Tag} tag + * @api public + */ + + visitTag: function(tag){ + this.indents++; + var name = tag.name + , pp = this.pp; + + if (tag.buffer) name = "' + (" + name + ") + '"; + + if (!this.hasCompiledTag) { + if (!this.hasCompiledDoctype && 'html' == name) { + this.visitDoctype(); + } + this.hasCompiledTag = true; + } + + // pretty print + if (pp && !tag.isInline()) + this.prettyIndent(0, true); + + if ((~selfClosing.indexOf(name) || tag.selfClosing) && !this.xml) { + this.buffer('<' + name); + this.visitAttributes(tag.attrs); + this.terse + ? this.buffer('>') + : this.buffer('/>'); + } else { + // Optimize attributes buffering + if (tag.attrs.length) { + this.buffer('<' + name); + if (tag.attrs.length) this.visitAttributes(tag.attrs); + this.buffer('>'); + } else { + this.buffer('<' + name + '>'); + } + if (tag.code) this.visitCode(tag.code); + this.escape = 'pre' == tag.name; + this.visit(tag.block); + + // pretty print + if (pp && !tag.isInline() && 'pre' != tag.name && !tag.canInline()) + this.prettyIndent(0, true); + + this.buffer(''); + } + this.indents--; + }, + + /** + * Visit `filter`, throwing when the filter does not exist. + * + * @param {Filter} filter + * @api public + */ + + visitFilter: function(filter){ + var fn = filters[filter.name]; + + // unknown filter + if (!fn) { + if (filter.isASTFilter) { + throw new Error('unknown ast filter "' + filter.name + ':"'); + } else { + throw new Error('unknown filter ":' + filter.name + '"'); + } + } + + if (filter.isASTFilter) { + this.buf.push(fn(filter.block, this, filter.attrs)); + } else { + var text = filter.block.nodes.map(function(node){ return node.val }).join('\n'); + filter.attrs = filter.attrs || {}; + filter.attrs.filename = this.options.filename; + this.buffer(utils.text(fn(text, filter.attrs))); + } + }, + + /** + * Visit `text` node. + * + * @param {Text} text + * @api public + */ + + visitText: function(text){ + text = utils.text(text.val.replace(/\\/g, '\\\\')); + if (this.escape) text = escape(text); + this.buffer(text); + }, + + /** + * Visit a `comment`, only buffering when the buffer flag is set. + * + * @param {Comment} comment + * @api public + */ + + visitComment: function(comment){ + if (!comment.buffer) return; + if (this.pp) this.prettyIndent(1, true); + this.buffer(''); + }, + + /** + * Visit a `BlockComment`. + * + * @param {Comment} comment + * @api public + */ + + visitBlockComment: function(comment){ + if (!comment.buffer) return; + if (0 == comment.val.trim().indexOf('if')) { + this.buffer(''); + } else { + this.buffer(''); + } + }, + + /** + * Visit `code`, respecting buffer / escape flags. + * If the code is followed by a block, wrap it in + * a self-calling function. + * + * @param {Code} code + * @api public + */ + + visitCode: function(code){ + // Wrap code blocks with {}. + // we only wrap unbuffered code blocks ATM + // since they are usually flow control + + // Buffer code + if (code.buffer) { + var val = code.val.trimLeft(); + this.buf.push('var __val__ = ' + val); + val = 'null == __val__ ? "" : __val__'; + if (code.escape) val = 'escape(' + val + ')'; + this.buf.push("buf.push(" + val + ");"); + } else { + this.buf.push(code.val); + } + + // Block support + if (code.block) { + if (!code.buffer) this.buf.push('{'); + this.visit(code.block); + if (!code.buffer) this.buf.push('}'); + } + }, + + /** + * Visit `each` block. + * + * @param {Each} each + * @api public + */ + + visitEach: function(each){ + this.buf.push('' + + '// iterate ' + each.obj + '\n' + + ';(function(){\n' + + ' if (\'number\' == typeof ' + each.obj + '.length) {\n' + + ' for (var ' + each.key + ' = 0, $$l = ' + each.obj + '.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n' + + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); + + this.visit(each.block); + + this.buf.push('' + + ' }\n' + + ' } else {\n' + + ' for (var ' + each.key + ' in ' + each.obj + ') {\n' + // if browser + // + ' if (' + each.obj + '.hasOwnProperty(' + each.key + ')){' + // end + + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); + + this.visit(each.block); + + // if browser + // this.buf.push(' }\n'); + // end + + this.buf.push(' }\n }\n}).call(this);\n'); + }, + + /** + * Visit `attrs`. + * + * @param {Array} attrs + * @api public + */ + + visitAttributes: function(attrs){ + var val = this.attrs(attrs); + if (val.inherits) { + this.buf.push("buf.push(attrs(merge({ " + val.buf + + " }, attributes), merge(" + val.escaped + ", escaped, true)));"); + } else if (val.constant) { + eval('var buf={' + val.buf + '};'); + this.buffer(runtime.attrs(buf, JSON.parse(val.escaped)), true); + } else { + this.buf.push("buf.push(attrs({ " + val.buf + " }, " + val.escaped + "));"); + } + }, + + /** + * Compile attributes. + */ + + attrs: function(attrs){ + var buf = [] + , classes = [] + , escaped = {} + , constant = attrs.every(function(attr){ return isConstant(attr.val) }) + , inherits = false; + + if (this.terse) buf.push('terse: true'); + + attrs.forEach(function(attr){ + if (attr.name == 'attributes') return inherits = true; + escaped[attr.name] = attr.escaped; + if (attr.name == 'class') { + classes.push('(' + attr.val + ')'); + } else { + var pair = "'" + attr.name + "':(" + attr.val + ')'; + buf.push(pair); + } + }); + + if (classes.length) { + classes = classes.join(" + ' ' + "); + buf.push("class: " + classes); + } + + return { + buf: buf.join(', ').replace('class:', '"class":'), + escaped: JSON.stringify(escaped), + inherits: inherits, + constant: constant + }; + } +}; + +/** + * Check if expression can be evaluated to a constant + * + * @param {String} expression + * @return {Boolean} + * @api private + */ + +function isConstant(val){ + // Check strings/literals + if (/^ *("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|true|false|null|undefined) *$/i.test(val)) + return true; + + // Check numbers + if (!isNaN(Number(val))) + return true; + + // Check arrays + var matches; + if (matches = /^ *\[(.*)\] *$/.exec(val)) + return matches[1].split(',').every(isConstant); + + return false; +} + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +function escape(html){ + return String(html) + .replace(/&(?!\w+;)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/doctypes.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/doctypes.js new file mode 100644 index 0000000..e87ca1e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/doctypes.js @@ -0,0 +1,18 @@ + +/*! + * Jade - doctypes + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = { + '5': '' + , 'default': '' + , 'xml': '' + , 'transitional': '' + , 'strict': '' + , 'frameset': '' + , '1.1': '' + , 'basic': '' + , 'mobile': '' +}; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/filters.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/filters.js new file mode 100644 index 0000000..fdb634c --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/filters.js @@ -0,0 +1,97 @@ + +/*! + * Jade - filters + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = { + + /** + * Wrap text with CDATA block. + */ + + cdata: function(str){ + return ''; + }, + + /** + * Transform sass to css, wrapped in style tags. + */ + + sass: function(str){ + str = str.replace(/\\n/g, '\n'); + var sass = require('sass').render(str).replace(/\n/g, '\\n'); + return ''; + }, + + /** + * Transform stylus to css, wrapped in style tags. + */ + + stylus: function(str, options){ + var ret; + str = str.replace(/\\n/g, '\n'); + var stylus = require('stylus'); + stylus(str, options).render(function(err, css){ + if (err) throw err; + ret = css.replace(/\n/g, '\\n'); + }); + return ''; + }, + + /** + * Transform less to css, wrapped in style tags. + */ + + less: function(str){ + var ret; + str = str.replace(/\\n/g, '\n'); + require('less').render(str, function(err, css){ + if (err) throw err; + ret = ''; + }); + return ret; + }, + + /** + * Transform markdown to html. + */ + + markdown: function(str){ + var md; + + // support markdown / discount + try { + md = require('markdown'); + } catch (err){ + try { + md = require('discount'); + } catch (err) { + try { + md = require('markdown-js'); + } catch (err) { + try { + md = require('marked'); + } catch (err) { + throw new + Error('Cannot find markdown library, install markdown, discount, or marked.'); + } + } + } + } + + str = str.replace(/\\n/g, '\n'); + return md.parse(str).replace(/\n/g, '\\n').replace(/'/g,'''); + }, + + /** + * Transform coffeescript to javascript. + */ + + coffeescript: function(str){ + str = str.replace(/\\n/g, '\n'); + var js = require('coffee-script').compile(str).replace(/\\/g, '\\\\').replace(/\n/g, '\\n'); + return ''; + } +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/inline-tags.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/inline-tags.js new file mode 100644 index 0000000..491de0b --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/inline-tags.js @@ -0,0 +1,28 @@ + +/*! + * Jade - inline tags + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = [ + 'a' + , 'abbr' + , 'acronym' + , 'b' + , 'br' + , 'code' + , 'em' + , 'font' + , 'i' + , 'img' + , 'ins' + , 'kbd' + , 'map' + , 'samp' + , 'small' + , 'span' + , 'strong' + , 'sub' + , 'sup' +]; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/jade.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/jade.js new file mode 100644 index 0000000..00f0abb --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/jade.js @@ -0,0 +1,237 @@ +/*! + * Jade + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Parser = require('./parser') + , Lexer = require('./lexer') + , Compiler = require('./compiler') + , runtime = require('./runtime') +// if node + , fs = require('fs'); +// end + +/** + * Library version. + */ + +exports.version = '0.26.3'; + +/** + * Expose self closing tags. + */ + +exports.selfClosing = require('./self-closing'); + +/** + * Default supported doctypes. + */ + +exports.doctypes = require('./doctypes'); + +/** + * Text filters. + */ + +exports.filters = require('./filters'); + +/** + * Utilities. + */ + +exports.utils = require('./utils'); + +/** + * Expose `Compiler`. + */ + +exports.Compiler = Compiler; + +/** + * Expose `Parser`. + */ + +exports.Parser = Parser; + +/** + * Expose `Lexer`. + */ + +exports.Lexer = Lexer; + +/** + * Nodes. + */ + +exports.nodes = require('./nodes'); + +/** + * Jade runtime helpers. + */ + +exports.runtime = runtime; + +/** + * Template function cache. + */ + +exports.cache = {}; + +/** + * Parse the given `str` of jade and return a function body. + * + * @param {String} str + * @param {Object} options + * @return {String} + * @api private + */ + +function parse(str, options){ + try { + // Parse + var parser = new Parser(str, options.filename, options); + + // Compile + var compiler = new (options.compiler || Compiler)(parser.parse(), options) + , js = compiler.compile(); + + // Debug compiler + if (options.debug) { + console.error('\nCompiled Function:\n\n\033[90m%s\033[0m', js.replace(/^/gm, ' ')); + } + + return '' + + 'var buf = [];\n' + + (options.self + ? 'var self = locals || {};\n' + js + : 'with (locals || {}) {\n' + js + '\n}\n') + + 'return buf.join("");'; + } catch (err) { + parser = parser.context(); + runtime.rethrow(err, parser.filename, parser.lexer.lineno); + } +} + +/** + * Compile a `Function` representation of the given jade `str`. + * + * Options: + * + * - `compileDebug` when `false` debugging code is stripped from the compiled template + * - `client` when `true` the helper functions `escape()` etc will reference `jade.escape()` + * for use with the Jade client-side runtime.js + * + * @param {String} str + * @param {Options} options + * @return {Function} + * @api public + */ + +exports.compile = function(str, options){ + var options = options || {} + , client = options.client + , filename = options.filename + ? JSON.stringify(options.filename) + : 'undefined' + , fn; + + if (options.compileDebug !== false) { + fn = [ + 'var __jade = [{ lineno: 1, filename: ' + filename + ' }];' + , 'try {' + , parse(String(str), options) + , '} catch (err) {' + , ' rethrow(err, __jade[0].filename, __jade[0].lineno);' + , '}' + ].join('\n'); + } else { + fn = parse(String(str), options); + } + + if (client) { + fn = 'attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;\n' + fn; + } + + fn = new Function('locals, attrs, escape, rethrow, merge', fn); + + if (client) return fn; + + return function(locals){ + return fn(locals, runtime.attrs, runtime.escape, runtime.rethrow, runtime.merge); + }; +}; + +/** + * Render the given `str` of jade and invoke + * the callback `fn(err, str)`. + * + * Options: + * + * - `cache` enable template caching + * - `filename` filename required for `include` / `extends` and caching + * + * @param {String} str + * @param {Object|Function} options or fn + * @param {Function} fn + * @api public + */ + +exports.render = function(str, options, fn){ + // swap args + if ('function' == typeof options) { + fn = options, options = {}; + } + + // cache requires .filename + if (options.cache && !options.filename) { + return fn(new Error('the "filename" option is required for caching')); + } + + try { + var path = options.filename; + var tmpl = options.cache + ? exports.cache[path] || (exports.cache[path] = exports.compile(str, options)) + : exports.compile(str, options); + fn(null, tmpl(options)); + } catch (err) { + fn(err); + } +}; + +/** + * Render a Jade file at the given `path` and callback `fn(err, str)`. + * + * @param {String} path + * @param {Object|Function} options or callback + * @param {Function} fn + * @api public + */ + +exports.renderFile = function(path, options, fn){ + var key = path + ':string'; + + if ('function' == typeof options) { + fn = options, options = {}; + } + + try { + options.filename = path; + var str = options.cache + ? exports.cache[key] || (exports.cache[key] = fs.readFileSync(path, 'utf8')) + : fs.readFileSync(path, 'utf8'); + exports.render(str, options, fn); + } catch (err) { + fn(err); + } +}; + +/** + * Express support. + */ + +exports.__express = exports.renderFile; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/lexer.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/lexer.js new file mode 100644 index 0000000..bca314a --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/lexer.js @@ -0,0 +1,771 @@ + +/*! + * Jade - Lexer + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Initialize `Lexer` with the given `str`. + * + * Options: + * + * - `colons` allow colons for attr delimiters + * + * @param {String} str + * @param {Object} options + * @api private + */ + +var Lexer = module.exports = function Lexer(str, options) { + options = options || {}; + this.input = str.replace(/\r\n|\r/g, '\n'); + this.colons = options.colons; + this.deferredTokens = []; + this.lastIndents = 0; + this.lineno = 1; + this.stash = []; + this.indentStack = []; + this.indentRe = null; + this.pipeless = false; +}; + +/** + * Lexer prototype. + */ + +Lexer.prototype = { + + /** + * Construct a token with the given `type` and `val`. + * + * @param {String} type + * @param {String} val + * @return {Object} + * @api private + */ + + tok: function(type, val){ + return { + type: type + , line: this.lineno + , val: val + } + }, + + /** + * Consume the given `len` of input. + * + * @param {Number} len + * @api private + */ + + consume: function(len){ + this.input = this.input.substr(len); + }, + + /** + * Scan for `type` with the given `regexp`. + * + * @param {String} type + * @param {RegExp} regexp + * @return {Object} + * @api private + */ + + scan: function(regexp, type){ + var captures; + if (captures = regexp.exec(this.input)) { + this.consume(captures[0].length); + return this.tok(type, captures[1]); + } + }, + + /** + * Defer the given `tok`. + * + * @param {Object} tok + * @api private + */ + + defer: function(tok){ + this.deferredTokens.push(tok); + }, + + /** + * Lookahead `n` tokens. + * + * @param {Number} n + * @return {Object} + * @api private + */ + + lookahead: function(n){ + var fetch = n - this.stash.length; + while (fetch-- > 0) this.stash.push(this.next()); + return this.stash[--n]; + }, + + /** + * Return the indexOf `start` / `end` delimiters. + * + * @param {String} start + * @param {String} end + * @return {Number} + * @api private + */ + + indexOfDelimiters: function(start, end){ + var str = this.input + , nstart = 0 + , nend = 0 + , pos = 0; + for (var i = 0, len = str.length; i < len; ++i) { + if (start == str.charAt(i)) { + ++nstart; + } else if (end == str.charAt(i)) { + if (++nend == nstart) { + pos = i; + break; + } + } + } + return pos; + }, + + /** + * Stashed token. + */ + + stashed: function() { + return this.stash.length + && this.stash.shift(); + }, + + /** + * Deferred token. + */ + + deferred: function() { + return this.deferredTokens.length + && this.deferredTokens.shift(); + }, + + /** + * end-of-source. + */ + + eos: function() { + if (this.input.length) return; + if (this.indentStack.length) { + this.indentStack.shift(); + return this.tok('outdent'); + } else { + return this.tok('eos'); + } + }, + + /** + * Blank line. + */ + + blank: function() { + var captures; + if (captures = /^\n *\n/.exec(this.input)) { + this.consume(captures[0].length - 1); + if (this.pipeless) return this.tok('text', ''); + return this.next(); + } + }, + + /** + * Comment. + */ + + comment: function() { + var captures; + if (captures = /^ *\/\/(-)?([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('comment', captures[2]); + tok.buffer = '-' != captures[1]; + return tok; + } + }, + + /** + * Interpolated tag. + */ + + interpolation: function() { + var captures; + if (captures = /^#\{(.*?)\}/.exec(this.input)) { + this.consume(captures[0].length); + return this.tok('interpolation', captures[1]); + } + }, + + /** + * Tag. + */ + + tag: function() { + var captures; + if (captures = /^(\w[-:\w]*)(\/?)/.exec(this.input)) { + this.consume(captures[0].length); + var tok, name = captures[1]; + if (':' == name[name.length - 1]) { + name = name.slice(0, -1); + tok = this.tok('tag', name); + this.defer(this.tok(':')); + while (' ' == this.input[0]) this.input = this.input.substr(1); + } else { + tok = this.tok('tag', name); + } + tok.selfClosing = !! captures[2]; + return tok; + } + }, + + /** + * Filter. + */ + + filter: function() { + return this.scan(/^:(\w+)/, 'filter'); + }, + + /** + * Doctype. + */ + + doctype: function() { + return this.scan(/^(?:!!!|doctype) *([^\n]+)?/, 'doctype'); + }, + + /** + * Id. + */ + + id: function() { + return this.scan(/^#([\w-]+)/, 'id'); + }, + + /** + * Class. + */ + + className: function() { + return this.scan(/^\.([\w-]+)/, 'class'); + }, + + /** + * Text. + */ + + text: function() { + return this.scan(/^(?:\| ?| ?)?([^\n]+)/, 'text'); + }, + + /** + * Extends. + */ + + "extends": function() { + return this.scan(/^extends? +([^\n]+)/, 'extends'); + }, + + /** + * Block prepend. + */ + + prepend: function() { + var captures; + if (captures = /^prepend +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = 'prepend' + , name = captures[1] + , tok = this.tok('block', name); + tok.mode = mode; + return tok; + } + }, + + /** + * Block append. + */ + + append: function() { + var captures; + if (captures = /^append +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = 'append' + , name = captures[1] + , tok = this.tok('block', name); + tok.mode = mode; + return tok; + } + }, + + /** + * Block. + */ + + block: function() { + var captures; + if (captures = /^block\b *(?:(prepend|append) +)?([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = captures[1] || 'replace' + , name = captures[2] + , tok = this.tok('block', name); + + tok.mode = mode; + return tok; + } + }, + + /** + * Yield. + */ + + yield: function() { + return this.scan(/^yield */, 'yield'); + }, + + /** + * Include. + */ + + include: function() { + return this.scan(/^include +([^\n]+)/, 'include'); + }, + + /** + * Case. + */ + + "case": function() { + return this.scan(/^case +([^\n]+)/, 'case'); + }, + + /** + * When. + */ + + when: function() { + return this.scan(/^when +([^:\n]+)/, 'when'); + }, + + /** + * Default. + */ + + "default": function() { + return this.scan(/^default */, 'default'); + }, + + /** + * Assignment. + */ + + assignment: function() { + var captures; + if (captures = /^(\w+) += *([^;\n]+)( *;? *)/.exec(this.input)) { + this.consume(captures[0].length); + var name = captures[1] + , val = captures[2]; + return this.tok('code', 'var ' + name + ' = (' + val + ');'); + } + }, + + /** + * Call mixin. + */ + + call: function(){ + var captures; + if (captures = /^\+([-\w]+)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('call', captures[1]); + + // Check for args (not attributes) + if (captures = /^ *\((.*?)\)/.exec(this.input)) { + if (!/^ *[-\w]+ *=/.test(captures[1])) { + this.consume(captures[0].length); + tok.args = captures[1]; + } + } + + return tok; + } + }, + + /** + * Mixin. + */ + + mixin: function(){ + var captures; + if (captures = /^mixin +([-\w]+)(?: *\((.*)\))?/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('mixin', captures[1]); + tok.args = captures[2]; + return tok; + } + }, + + /** + * Conditional. + */ + + conditional: function() { + var captures; + if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var type = captures[1] + , js = captures[2]; + + switch (type) { + case 'if': js = 'if (' + js + ')'; break; + case 'unless': js = 'if (!(' + js + '))'; break; + case 'else if': js = 'else if (' + js + ')'; break; + case 'else': js = 'else'; break; + } + + return this.tok('code', js); + } + }, + + /** + * While. + */ + + "while": function() { + var captures; + if (captures = /^while +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + return this.tok('code', 'while (' + captures[1] + ')'); + } + }, + + /** + * Each. + */ + + each: function() { + var captures; + if (captures = /^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('each', captures[1]); + tok.key = captures[2] || '$index'; + tok.code = captures[3]; + return tok; + } + }, + + /** + * Code. + */ + + code: function() { + var captures; + if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var flags = captures[1]; + captures[1] = captures[2]; + var tok = this.tok('code', captures[1]); + tok.escape = flags[0] === '='; + tok.buffer = flags[0] === '=' || flags[1] === '='; + return tok; + } + }, + + /** + * Attributes. + */ + + attrs: function() { + if ('(' == this.input.charAt(0)) { + var index = this.indexOfDelimiters('(', ')') + , str = this.input.substr(1, index-1) + , tok = this.tok('attrs') + , len = str.length + , colons = this.colons + , states = ['key'] + , escapedAttr + , key = '' + , val = '' + , quote + , c + , p; + + function state(){ + return states[states.length - 1]; + } + + function interpolate(attr) { + return attr.replace(/#\{([^}]+)\}/g, function(_, expr){ + return quote + " + (" + expr + ") + " + quote; + }); + } + + this.consume(index + 1); + tok.attrs = {}; + tok.escaped = {}; + + function parse(c) { + var real = c; + // TODO: remove when people fix ":" + if (colons && ':' == c) c = '='; + switch (c) { + case ',': + case '\n': + switch (state()) { + case 'expr': + case 'array': + case 'string': + case 'object': + val += c; + break; + default: + states.push('key'); + val = val.trim(); + key = key.trim(); + if ('' == key) return; + key = key.replace(/^['"]|['"]$/g, '').replace('!', ''); + tok.escaped[key] = escapedAttr; + tok.attrs[key] = '' == val + ? true + : interpolate(val); + key = val = ''; + } + break; + case '=': + switch (state()) { + case 'key char': + key += real; + break; + case 'val': + case 'expr': + case 'array': + case 'string': + case 'object': + val += real; + break; + default: + escapedAttr = '!' != p; + states.push('val'); + } + break; + case '(': + if ('val' == state() + || 'expr' == state()) states.push('expr'); + val += c; + break; + case ')': + if ('expr' == state() + || 'val' == state()) states.pop(); + val += c; + break; + case '{': + if ('val' == state()) states.push('object'); + val += c; + break; + case '}': + if ('object' == state()) states.pop(); + val += c; + break; + case '[': + if ('val' == state()) states.push('array'); + val += c; + break; + case ']': + if ('array' == state()) states.pop(); + val += c; + break; + case '"': + case "'": + switch (state()) { + case 'key': + states.push('key char'); + break; + case 'key char': + states.pop(); + break; + case 'string': + if (c == quote) states.pop(); + val += c; + break; + default: + states.push('string'); + val += c; + quote = c; + } + break; + case '': + break; + default: + switch (state()) { + case 'key': + case 'key char': + key += c; + break; + default: + val += c; + } + } + p = c; + } + + for (var i = 0; i < len; ++i) { + parse(str.charAt(i)); + } + + parse(','); + + if ('/' == this.input.charAt(0)) { + this.consume(1); + tok.selfClosing = true; + } + + return tok; + } + }, + + /** + * Indent | Outdent | Newline. + */ + + indent: function() { + var captures, re; + + // established regexp + if (this.indentRe) { + captures = this.indentRe.exec(this.input); + // determine regexp + } else { + // tabs + re = /^\n(\t*) */; + captures = re.exec(this.input); + + // spaces + if (captures && !captures[1].length) { + re = /^\n( *)/; + captures = re.exec(this.input); + } + + // established + if (captures && captures[1].length) this.indentRe = re; + } + + if (captures) { + var tok + , indents = captures[1].length; + + ++this.lineno; + this.consume(indents + 1); + + if (' ' == this.input[0] || '\t' == this.input[0]) { + throw new Error('Invalid indentation, you can use tabs or spaces but not both'); + } + + // blank line + if ('\n' == this.input[0]) return this.tok('newline'); + + // outdent + if (this.indentStack.length && indents < this.indentStack[0]) { + while (this.indentStack.length && this.indentStack[0] > indents) { + this.stash.push(this.tok('outdent')); + this.indentStack.shift(); + } + tok = this.stash.pop(); + // indent + } else if (indents && indents != this.indentStack[0]) { + this.indentStack.unshift(indents); + tok = this.tok('indent', indents); + // newline + } else { + tok = this.tok('newline'); + } + + return tok; + } + }, + + /** + * Pipe-less text consumed only when + * pipeless is true; + */ + + pipelessText: function() { + if (this.pipeless) { + if ('\n' == this.input[0]) return; + var i = this.input.indexOf('\n'); + if (-1 == i) i = this.input.length; + var str = this.input.substr(0, i); + this.consume(str.length); + return this.tok('text', str); + } + }, + + /** + * ':' + */ + + colon: function() { + return this.scan(/^: */, ':'); + }, + + /** + * Return the next token object, or those + * previously stashed by lookahead. + * + * @return {Object} + * @api private + */ + + advance: function(){ + return this.stashed() + || this.next(); + }, + + /** + * Return the next token object. + * + * @return {Object} + * @api private + */ + + next: function() { + return this.deferred() + || this.blank() + || this.eos() + || this.pipelessText() + || this.yield() + || this.doctype() + || this.interpolation() + || this["case"]() + || this.when() + || this["default"]() + || this["extends"]() + || this.append() + || this.prepend() + || this.block() + || this.include() + || this.mixin() + || this.call() + || this.conditional() + || this.each() + || this["while"]() + || this.assignment() + || this.tag() + || this.filter() + || this.code() + || this.id() + || this.className() + || this.attrs() + || this.indent() + || this.comment() + || this.colon() + || this.text(); + } +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/attrs.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/attrs.js new file mode 100644 index 0000000..5de9b59 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/attrs.js @@ -0,0 +1,77 @@ + +/*! + * Jade - nodes - Attrs + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'), + Block = require('./block'); + +/** + * Initialize a `Attrs` node. + * + * @api public + */ + +var Attrs = module.exports = function Attrs() { + this.attrs = []; +}; + +/** + * Inherit from `Node`. + */ + +Attrs.prototype.__proto__ = Node.prototype; + +/** + * Set attribute `name` to `val`, keep in mind these become + * part of a raw js object literal, so to quote a value you must + * '"quote me"', otherwise or example 'user.name' is literal JavaScript. + * + * @param {String} name + * @param {String} val + * @param {Boolean} escaped + * @return {Tag} for chaining + * @api public + */ + +Attrs.prototype.setAttribute = function(name, val, escaped){ + this.attrs.push({ name: name, val: val, escaped: escaped }); + return this; +}; + +/** + * Remove attribute `name` when present. + * + * @param {String} name + * @api public + */ + +Attrs.prototype.removeAttribute = function(name){ + for (var i = 0, len = this.attrs.length; i < len; ++i) { + if (this.attrs[i] && this.attrs[i].name == name) { + delete this.attrs[i]; + } + } +}; + +/** + * Get attribute value by `name`. + * + * @param {String} name + * @return {String} + * @api public + */ + +Attrs.prototype.getAttribute = function(name){ + for (var i = 0, len = this.attrs.length; i < len; ++i) { + if (this.attrs[i] && this.attrs[i].name == name) { + return this.attrs[i].val; + } + } +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/block-comment.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/block-comment.js new file mode 100644 index 0000000..4f41e4a --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/block-comment.js @@ -0,0 +1,33 @@ + +/*! + * Jade - nodes - BlockComment + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `BlockComment` with the given `block`. + * + * @param {String} val + * @param {Block} block + * @param {Boolean} buffer + * @api public + */ + +var BlockComment = module.exports = function BlockComment(val, block, buffer) { + this.block = block; + this.val = val; + this.buffer = buffer; +}; + +/** + * Inherit from `Node`. + */ + +BlockComment.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/block.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/block.js new file mode 100644 index 0000000..bb00a1d --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/block.js @@ -0,0 +1,121 @@ + +/*! + * Jade - nodes - Block + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a new `Block` with an optional `node`. + * + * @param {Node} node + * @api public + */ + +var Block = module.exports = function Block(node){ + this.nodes = []; + if (node) this.push(node); +}; + +/** + * Inherit from `Node`. + */ + +Block.prototype.__proto__ = Node.prototype; + +/** + * Block flag. + */ + +Block.prototype.isBlock = true; + +/** + * Replace the nodes in `other` with the nodes + * in `this` block. + * + * @param {Block} other + * @api private + */ + +Block.prototype.replace = function(other){ + other.nodes = this.nodes; +}; + +/** + * Pust the given `node`. + * + * @param {Node} node + * @return {Number} + * @api public + */ + +Block.prototype.push = function(node){ + return this.nodes.push(node); +}; + +/** + * Check if this block is empty. + * + * @return {Boolean} + * @api public + */ + +Block.prototype.isEmpty = function(){ + return 0 == this.nodes.length; +}; + +/** + * Unshift the given `node`. + * + * @param {Node} node + * @return {Number} + * @api public + */ + +Block.prototype.unshift = function(node){ + return this.nodes.unshift(node); +}; + +/** + * Return the "last" block, or the first `yield` node. + * + * @return {Block} + * @api private + */ + +Block.prototype.includeBlock = function(){ + var ret = this + , node; + + for (var i = 0, len = this.nodes.length; i < len; ++i) { + node = this.nodes[i]; + if (node.yield) return node; + else if (node.textOnly) continue; + else if (node.includeBlock) ret = node.includeBlock(); + else if (node.block && !node.block.isEmpty()) ret = node.block.includeBlock(); + } + + return ret; +}; + +/** + * Return a clone of this block. + * + * @return {Block} + * @api private + */ + +Block.prototype.clone = function(){ + var clone = new Block; + for (var i = 0, len = this.nodes.length; i < len; ++i) { + clone.push(this.nodes[i].clone()); + } + return clone; +}; + diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/case.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/case.js new file mode 100644 index 0000000..08ff033 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/case.js @@ -0,0 +1,43 @@ + +/*! + * Jade - nodes - Case + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a new `Case` with `expr`. + * + * @param {String} expr + * @api public + */ + +var Case = exports = module.exports = function Case(expr, block){ + this.expr = expr; + this.block = block; +}; + +/** + * Inherit from `Node`. + */ + +Case.prototype.__proto__ = Node.prototype; + +var When = exports.When = function When(expr, block){ + this.expr = expr; + this.block = block; + this.debug = false; +}; + +/** + * Inherit from `Node`. + */ + +When.prototype.__proto__ = Node.prototype; + diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/code.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/code.js new file mode 100644 index 0000000..babc675 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/code.js @@ -0,0 +1,35 @@ + +/*! + * Jade - nodes - Code + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Code` node with the given code `val`. + * Code may also be optionally buffered and escaped. + * + * @param {String} val + * @param {Boolean} buffer + * @param {Boolean} escape + * @api public + */ + +var Code = module.exports = function Code(val, buffer, escape) { + this.val = val; + this.buffer = buffer; + this.escape = escape; + if (val.match(/^ *else/)) this.debug = false; +}; + +/** + * Inherit from `Node`. + */ + +Code.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/comment.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/comment.js new file mode 100644 index 0000000..2e1469e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/comment.js @@ -0,0 +1,32 @@ + +/*! + * Jade - nodes - Comment + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Comment` with the given `val`, optionally `buffer`, + * otherwise the comment may render in the output. + * + * @param {String} val + * @param {Boolean} buffer + * @api public + */ + +var Comment = module.exports = function Comment(val, buffer) { + this.val = val; + this.buffer = buffer; +}; + +/** + * Inherit from `Node`. + */ + +Comment.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/doctype.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/doctype.js new file mode 100644 index 0000000..b8f33e5 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/doctype.js @@ -0,0 +1,29 @@ + +/*! + * Jade - nodes - Doctype + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Doctype` with the given `val`. + * + * @param {String} val + * @api public + */ + +var Doctype = module.exports = function Doctype(val) { + this.val = val; +}; + +/** + * Inherit from `Node`. + */ + +Doctype.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/each.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/each.js new file mode 100644 index 0000000..f54101f --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/each.js @@ -0,0 +1,35 @@ + +/*! + * Jade - nodes - Each + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize an `Each` node, representing iteration + * + * @param {String} obj + * @param {String} val + * @param {String} key + * @param {Block} block + * @api public + */ + +var Each = module.exports = function Each(obj, val, key, block) { + this.obj = obj; + this.val = val; + this.key = key; + this.block = block; +}; + +/** + * Inherit from `Node`. + */ + +Each.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/filter.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/filter.js new file mode 100644 index 0000000..851a004 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/filter.js @@ -0,0 +1,35 @@ + +/*! + * Jade - nodes - Filter + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node') + , Block = require('./block'); + +/** + * Initialize a `Filter` node with the given + * filter `name` and `block`. + * + * @param {String} name + * @param {Block|Node} block + * @api public + */ + +var Filter = module.exports = function Filter(name, block, attrs) { + this.name = name; + this.block = block; + this.attrs = attrs; + this.isASTFilter = !block.nodes.every(function(node){ return node.isText }); +}; + +/** + * Inherit from `Node`. + */ + +Filter.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/index.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/index.js new file mode 100644 index 0000000..386ad2f --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/index.js @@ -0,0 +1,20 @@ + +/*! + * Jade - nodes + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +exports.Node = require('./node'); +exports.Tag = require('./tag'); +exports.Code = require('./code'); +exports.Each = require('./each'); +exports.Case = require('./case'); +exports.Text = require('./text'); +exports.Block = require('./block'); +exports.Mixin = require('./mixin'); +exports.Filter = require('./filter'); +exports.Comment = require('./comment'); +exports.Literal = require('./literal'); +exports.BlockComment = require('./block-comment'); +exports.Doctype = require('./doctype'); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/literal.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/literal.js new file mode 100644 index 0000000..fde586b --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/literal.js @@ -0,0 +1,32 @@ + +/*! + * Jade - nodes - Literal + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Literal` node with the given `str. + * + * @param {String} str + * @api public + */ + +var Literal = module.exports = function Literal(str) { + this.str = str + .replace(/\\/g, "\\\\") + .replace(/\n|\r\n/g, "\\n") + .replace(/'/g, "\\'"); +}; + +/** + * Inherit from `Node`. + */ + +Literal.prototype.__proto__ = Node.prototype; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/mixin.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/mixin.js new file mode 100644 index 0000000..8407bc7 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/mixin.js @@ -0,0 +1,36 @@ + +/*! + * Jade - nodes - Mixin + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Attrs = require('./attrs'); + +/** + * Initialize a new `Mixin` with `name` and `block`. + * + * @param {String} name + * @param {String} args + * @param {Block} block + * @api public + */ + +var Mixin = module.exports = function Mixin(name, args, block, call){ + this.name = name; + this.args = args; + this.block = block; + this.attrs = []; + this.call = call; +}; + +/** + * Inherit from `Attrs`. + */ + +Mixin.prototype.__proto__ = Attrs.prototype; + diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/node.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/node.js new file mode 100644 index 0000000..e98f042 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/node.js @@ -0,0 +1,25 @@ + +/*! + * Jade - nodes - Node + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Initialize a `Node`. + * + * @api public + */ + +var Node = module.exports = function Node(){}; + +/** + * Clone this node (return itself) + * + * @return {Node} + * @api private + */ + +Node.prototype.clone = function(){ + return this; +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/tag.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/tag.js new file mode 100644 index 0000000..4b6728a --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/tag.js @@ -0,0 +1,95 @@ + +/*! + * Jade - nodes - Tag + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Attrs = require('./attrs'), + Block = require('./block'), + inlineTags = require('../inline-tags'); + +/** + * Initialize a `Tag` node with the given tag `name` and optional `block`. + * + * @param {String} name + * @param {Block} block + * @api public + */ + +var Tag = module.exports = function Tag(name, block) { + this.name = name; + this.attrs = []; + this.block = block || new Block; +}; + +/** + * Inherit from `Attrs`. + */ + +Tag.prototype.__proto__ = Attrs.prototype; + +/** + * Clone this tag. + * + * @return {Tag} + * @api private + */ + +Tag.prototype.clone = function(){ + var clone = new Tag(this.name, this.block.clone()); + clone.line = this.line; + clone.attrs = this.attrs; + clone.textOnly = this.textOnly; + return clone; +}; + +/** + * Check if this tag is an inline tag. + * + * @return {Boolean} + * @api private + */ + +Tag.prototype.isInline = function(){ + return ~inlineTags.indexOf(this.name); +}; + +/** + * Check if this tag's contents can be inlined. Used for pretty printing. + * + * @return {Boolean} + * @api private + */ + +Tag.prototype.canInline = function(){ + var nodes = this.block.nodes; + + function isInline(node){ + // Recurse if the node is a block + if (node.isBlock) return node.nodes.every(isInline); + return node.isText || (node.isInline && node.isInline()); + } + + // Empty tag + if (!nodes.length) return true; + + // Text-only or inline-only tag + if (1 == nodes.length) return isInline(nodes[0]); + + // Multi-line inline-only tag + if (this.block.nodes.every(isInline)) { + for (var i = 1, len = nodes.length; i < len; ++i) { + if (nodes[i-1].isText && nodes[i].isText) + return false; + } + return true; + } + + // Mixed tag + return false; +}; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/text.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/text.js new file mode 100644 index 0000000..3b5dd55 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/nodes/text.js @@ -0,0 +1,36 @@ + +/*! + * Jade - nodes - Text + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Text` node with optional `line`. + * + * @param {String} line + * @api public + */ + +var Text = module.exports = function Text(line) { + this.val = ''; + if ('string' == typeof line) this.val = line; +}; + +/** + * Inherit from `Node`. + */ + +Text.prototype.__proto__ = Node.prototype; + +/** + * Flag as text. + */ + +Text.prototype.isText = true; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/parser.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/parser.js new file mode 100644 index 0000000..92f2af0 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/parser.js @@ -0,0 +1,710 @@ + +/*! + * Jade - Parser + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Lexer = require('./lexer') + , nodes = require('./nodes'); + +/** + * Initialize `Parser` with the given input `str` and `filename`. + * + * @param {String} str + * @param {String} filename + * @param {Object} options + * @api public + */ + +var Parser = exports = module.exports = function Parser(str, filename, options){ + this.input = str; + this.lexer = new Lexer(str, options); + this.filename = filename; + this.blocks = {}; + this.mixins = {}; + this.options = options; + this.contexts = [this]; +}; + +/** + * Tags that may not contain tags. + */ + +var textOnly = exports.textOnly = ['script', 'style']; + +/** + * Parser prototype. + */ + +Parser.prototype = { + + /** + * Push `parser` onto the context stack, + * or pop and return a `Parser`. + */ + + context: function(parser){ + if (parser) { + this.contexts.push(parser); + } else { + return this.contexts.pop(); + } + }, + + /** + * Return the next token object. + * + * @return {Object} + * @api private + */ + + advance: function(){ + return this.lexer.advance(); + }, + + /** + * Skip `n` tokens. + * + * @param {Number} n + * @api private + */ + + skip: function(n){ + while (n--) this.advance(); + }, + + /** + * Single token lookahead. + * + * @return {Object} + * @api private + */ + + peek: function() { + return this.lookahead(1); + }, + + /** + * Return lexer lineno. + * + * @return {Number} + * @api private + */ + + line: function() { + return this.lexer.lineno; + }, + + /** + * `n` token lookahead. + * + * @param {Number} n + * @return {Object} + * @api private + */ + + lookahead: function(n){ + return this.lexer.lookahead(n); + }, + + /** + * Parse input returning a string of js for evaluation. + * + * @return {String} + * @api public + */ + + parse: function(){ + var block = new nodes.Block, parser; + block.line = this.line(); + + while ('eos' != this.peek().type) { + if ('newline' == this.peek().type) { + this.advance(); + } else { + block.push(this.parseExpr()); + } + } + + if (parser = this.extending) { + this.context(parser); + var ast = parser.parse(); + this.context(); + // hoist mixins + for (var name in this.mixins) + ast.unshift(this.mixins[name]); + return ast; + } + + return block; + }, + + /** + * Expect the given type, or throw an exception. + * + * @param {String} type + * @api private + */ + + expect: function(type){ + if (this.peek().type === type) { + return this.advance(); + } else { + throw new Error('expected "' + type + '", but got "' + this.peek().type + '"'); + } + }, + + /** + * Accept the given `type`. + * + * @param {String} type + * @api private + */ + + accept: function(type){ + if (this.peek().type === type) { + return this.advance(); + } + }, + + /** + * tag + * | doctype + * | mixin + * | include + * | filter + * | comment + * | text + * | each + * | code + * | yield + * | id + * | class + * | interpolation + */ + + parseExpr: function(){ + switch (this.peek().type) { + case 'tag': + return this.parseTag(); + case 'mixin': + return this.parseMixin(); + case 'block': + return this.parseBlock(); + case 'case': + return this.parseCase(); + case 'when': + return this.parseWhen(); + case 'default': + return this.parseDefault(); + case 'extends': + return this.parseExtends(); + case 'include': + return this.parseInclude(); + case 'doctype': + return this.parseDoctype(); + case 'filter': + return this.parseFilter(); + case 'comment': + return this.parseComment(); + case 'text': + return this.parseText(); + case 'each': + return this.parseEach(); + case 'code': + return this.parseCode(); + case 'call': + return this.parseCall(); + case 'interpolation': + return this.parseInterpolation(); + case 'yield': + this.advance(); + var block = new nodes.Block; + block.yield = true; + return block; + case 'id': + case 'class': + var tok = this.advance(); + this.lexer.defer(this.lexer.tok('tag', 'div')); + this.lexer.defer(tok); + return this.parseExpr(); + default: + throw new Error('unexpected token "' + this.peek().type + '"'); + } + }, + + /** + * Text + */ + + parseText: function(){ + var tok = this.expect('text') + , node = new nodes.Text(tok.val); + node.line = this.line(); + return node; + }, + + /** + * ':' expr + * | block + */ + + parseBlockExpansion: function(){ + if (':' == this.peek().type) { + this.advance(); + return new nodes.Block(this.parseExpr()); + } else { + return this.block(); + } + }, + + /** + * case + */ + + parseCase: function(){ + var val = this.expect('case').val + , node = new nodes.Case(val); + node.line = this.line(); + node.block = this.block(); + return node; + }, + + /** + * when + */ + + parseWhen: function(){ + var val = this.expect('when').val + return new nodes.Case.When(val, this.parseBlockExpansion()); + }, + + /** + * default + */ + + parseDefault: function(){ + this.expect('default'); + return new nodes.Case.When('default', this.parseBlockExpansion()); + }, + + /** + * code + */ + + parseCode: function(){ + var tok = this.expect('code') + , node = new nodes.Code(tok.val, tok.buffer, tok.escape) + , block + , i = 1; + node.line = this.line(); + while (this.lookahead(i) && 'newline' == this.lookahead(i).type) ++i; + block = 'indent' == this.lookahead(i).type; + if (block) { + this.skip(i-1); + node.block = this.block(); + } + return node; + }, + + /** + * comment + */ + + parseComment: function(){ + var tok = this.expect('comment') + , node; + + if ('indent' == this.peek().type) { + node = new nodes.BlockComment(tok.val, this.block(), tok.buffer); + } else { + node = new nodes.Comment(tok.val, tok.buffer); + } + + node.line = this.line(); + return node; + }, + + /** + * doctype + */ + + parseDoctype: function(){ + var tok = this.expect('doctype') + , node = new nodes.Doctype(tok.val); + node.line = this.line(); + return node; + }, + + /** + * filter attrs? text-block + */ + + parseFilter: function(){ + var block + , tok = this.expect('filter') + , attrs = this.accept('attrs'); + + this.lexer.pipeless = true; + block = this.parseTextBlock(); + this.lexer.pipeless = false; + + var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs); + node.line = this.line(); + return node; + }, + + /** + * tag ':' attrs? block + */ + + parseASTFilter: function(){ + var block + , tok = this.expect('tag') + , attrs = this.accept('attrs'); + + this.expect(':'); + block = this.block(); + + var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs); + node.line = this.line(); + return node; + }, + + /** + * each block + */ + + parseEach: function(){ + var tok = this.expect('each') + , node = new nodes.Each(tok.code, tok.val, tok.key); + node.line = this.line(); + node.block = this.block(); + return node; + }, + + /** + * 'extends' name + */ + + parseExtends: function(){ + var path = require('path') + , fs = require('fs') + , dirname = path.dirname + , basename = path.basename + , join = path.join; + + if (!this.filename) + throw new Error('the "filename" option is required to extend templates'); + + var path = this.expect('extends').val.trim() + , dir = dirname(this.filename); + + var path = join(dir, path + '.jade') + , str = fs.readFileSync(path, 'utf8') + , parser = new Parser(str, path, this.options); + + parser.blocks = this.blocks; + parser.contexts = this.contexts; + this.extending = parser; + + // TODO: null node + return new nodes.Literal(''); + }, + + /** + * 'block' name block + */ + + parseBlock: function(){ + var block = this.expect('block') + , mode = block.mode + , name = block.val.trim(); + + block = 'indent' == this.peek().type + ? this.block() + : new nodes.Block(new nodes.Literal('')); + + var prev = this.blocks[name]; + + if (prev) { + switch (prev.mode) { + case 'append': + block.nodes = block.nodes.concat(prev.nodes); + prev = block; + break; + case 'prepend': + block.nodes = prev.nodes.concat(block.nodes); + prev = block; + break; + } + } + + block.mode = mode; + return this.blocks[name] = prev || block; + }, + + /** + * include block? + */ + + parseInclude: function(){ + var path = require('path') + , fs = require('fs') + , dirname = path.dirname + , basename = path.basename + , join = path.join; + + var path = this.expect('include').val.trim() + , dir = dirname(this.filename); + + if (!this.filename) + throw new Error('the "filename" option is required to use includes'); + + // no extension + if (!~basename(path).indexOf('.')) { + path += '.jade'; + } + + // non-jade + if ('.jade' != path.substr(-5)) { + var path = join(dir, path) + , str = fs.readFileSync(path, 'utf8'); + return new nodes.Literal(str); + } + + var path = join(dir, path) + , str = fs.readFileSync(path, 'utf8') + , parser = new Parser(str, path, this.options); + parser.blocks = this.blocks; + parser.mixins = this.mixins; + + this.context(parser); + var ast = parser.parse(); + this.context(); + ast.filename = path; + + if ('indent' == this.peek().type) { + ast.includeBlock().push(this.block()); + } + + return ast; + }, + + /** + * call ident block + */ + + parseCall: function(){ + var tok = this.expect('call') + , name = tok.val + , args = tok.args + , mixin = new nodes.Mixin(name, args, new nodes.Block, true); + + this.tag(mixin); + if (mixin.block.isEmpty()) mixin.block = null; + return mixin; + }, + + /** + * mixin block + */ + + parseMixin: function(){ + var tok = this.expect('mixin') + , name = tok.val + , args = tok.args + , mixin; + + // definition + if ('indent' == this.peek().type) { + mixin = new nodes.Mixin(name, args, this.block(), false); + this.mixins[name] = mixin; + return mixin; + // call + } else { + return new nodes.Mixin(name, args, null, true); + } + }, + + /** + * indent (text | newline)* outdent + */ + + parseTextBlock: function(){ + var block = new nodes.Block; + block.line = this.line(); + var spaces = this.expect('indent').val; + if (null == this._spaces) this._spaces = spaces; + var indent = Array(spaces - this._spaces + 1).join(' '); + while ('outdent' != this.peek().type) { + switch (this.peek().type) { + case 'newline': + this.advance(); + break; + case 'indent': + this.parseTextBlock().nodes.forEach(function(node){ + block.push(node); + }); + break; + default: + var text = new nodes.Text(indent + this.advance().val); + text.line = this.line(); + block.push(text); + } + } + + if (spaces == this._spaces) this._spaces = null; + this.expect('outdent'); + return block; + }, + + /** + * indent expr* outdent + */ + + block: function(){ + var block = new nodes.Block; + block.line = this.line(); + this.expect('indent'); + while ('outdent' != this.peek().type) { + if ('newline' == this.peek().type) { + this.advance(); + } else { + block.push(this.parseExpr()); + } + } + this.expect('outdent'); + return block; + }, + + /** + * interpolation (attrs | class | id)* (text | code | ':')? newline* block? + */ + + parseInterpolation: function(){ + var tok = this.advance(); + var tag = new nodes.Tag(tok.val); + tag.buffer = true; + return this.tag(tag); + }, + + /** + * tag (attrs | class | id)* (text | code | ':')? newline* block? + */ + + parseTag: function(){ + // ast-filter look-ahead + var i = 2; + if ('attrs' == this.lookahead(i).type) ++i; + if (':' == this.lookahead(i).type) { + if ('indent' == this.lookahead(++i).type) { + return this.parseASTFilter(); + } + } + + var tok = this.advance() + , tag = new nodes.Tag(tok.val); + + tag.selfClosing = tok.selfClosing; + + return this.tag(tag); + }, + + /** + * Parse tag. + */ + + tag: function(tag){ + var dot; + + tag.line = this.line(); + + // (attrs | class | id)* + out: + while (true) { + switch (this.peek().type) { + case 'id': + case 'class': + var tok = this.advance(); + tag.setAttribute(tok.type, "'" + tok.val + "'"); + continue; + case 'attrs': + var tok = this.advance() + , obj = tok.attrs + , escaped = tok.escaped + , names = Object.keys(obj); + + if (tok.selfClosing) tag.selfClosing = true; + + for (var i = 0, len = names.length; i < len; ++i) { + var name = names[i] + , val = obj[name]; + tag.setAttribute(name, val, escaped[name]); + } + continue; + default: + break out; + } + } + + // check immediate '.' + if ('.' == this.peek().val) { + dot = tag.textOnly = true; + this.advance(); + } + + // (text | code | ':')? + switch (this.peek().type) { + case 'text': + tag.block.push(this.parseText()); + break; + case 'code': + tag.code = this.parseCode(); + break; + case ':': + this.advance(); + tag.block = new nodes.Block; + tag.block.push(this.parseExpr()); + break; + } + + // newline* + while ('newline' == this.peek().type) this.advance(); + + tag.textOnly = tag.textOnly || ~textOnly.indexOf(tag.name); + + // script special-case + if ('script' == tag.name) { + var type = tag.getAttribute('type'); + if (!dot && type && 'text/javascript' != type.replace(/^['"]|['"]$/g, '')) { + tag.textOnly = false; + } + } + + // block? + if ('indent' == this.peek().type) { + if (tag.textOnly) { + this.lexer.pipeless = true; + tag.block = this.parseTextBlock(); + this.lexer.pipeless = false; + } else { + var block = this.block(); + if (tag.block) { + for (var i = 0, len = block.nodes.length; i < len; ++i) { + tag.block.push(block.nodes[i]); + } + } else { + tag.block = block; + } + } + } + + return tag; + } +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/runtime.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/runtime.js new file mode 100644 index 0000000..fb711f5 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/runtime.js @@ -0,0 +1,174 @@ + +/*! + * Jade - runtime + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Lame Array.isArray() polyfill for now. + */ + +if (!Array.isArray) { + Array.isArray = function(arr){ + return '[object Array]' == Object.prototype.toString.call(arr); + }; +} + +/** + * Lame Object.keys() polyfill for now. + */ + +if (!Object.keys) { + Object.keys = function(obj){ + var arr = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + arr.push(key); + } + } + return arr; + } +} + +/** + * Merge two attribute objects giving precedence + * to values in object `b`. Classes are special-cased + * allowing for arrays and merging/joining appropriately + * resulting in a string. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api private + */ + +exports.merge = function merge(a, b) { + var ac = a['class']; + var bc = b['class']; + + if (ac || bc) { + ac = ac || []; + bc = bc || []; + if (!Array.isArray(ac)) ac = [ac]; + if (!Array.isArray(bc)) bc = [bc]; + ac = ac.filter(nulls); + bc = bc.filter(nulls); + a['class'] = ac.concat(bc).join(' '); + } + + for (var key in b) { + if (key != 'class') { + a[key] = b[key]; + } + } + + return a; +}; + +/** + * Filter null `val`s. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function nulls(val) { + return val != null; +} + +/** + * Render the given attributes object. + * + * @param {Object} obj + * @param {Object} escaped + * @return {String} + * @api private + */ + +exports.attrs = function attrs(obj, escaped){ + var buf = [] + , terse = obj.terse; + + delete obj.terse; + var keys = Object.keys(obj) + , len = keys.length; + + if (len) { + buf.push(''); + for (var i = 0; i < len; ++i) { + var key = keys[i] + , val = obj[key]; + + if ('boolean' == typeof val || null == val) { + if (val) { + terse + ? buf.push(key) + : buf.push(key + '="' + key + '"'); + } + } else if (0 == key.indexOf('data') && 'string' != typeof val) { + buf.push(key + "='" + JSON.stringify(val) + "'"); + } else if ('class' == key && Array.isArray(val)) { + buf.push(key + '="' + exports.escape(val.join(' ')) + '"'); + } else if (escaped && escaped[key]) { + buf.push(key + '="' + exports.escape(val) + '"'); + } else { + buf.push(key + '="' + val + '"'); + } + } + } + + return buf.join(' '); +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function escape(html){ + return String(html) + .replace(/&(?!(\w+|\#\d+);)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; + +/** + * Re-throw the given `err` in context to the + * the jade in `filename` at the given `lineno`. + * + * @param {Error} err + * @param {String} filename + * @param {String} lineno + * @api private + */ + +exports.rethrow = function rethrow(err, filename, lineno){ + if (!filename) throw err; + + var context = 3 + , str = require('fs').readFileSync(filename, 'utf8') + , lines = str.split('\n') + , start = Math.max(lineno - context, 0) + , end = Math.min(lines.length, lineno + context); + + // Error context + var context = lines.slice(start, end).map(function(line, i){ + var curr = i + start + 1; + return (curr == lineno ? ' > ' : ' ') + + curr + + '| ' + + line; + }).join('\n'); + + // Alter exception message + err.path = filename; + err.message = (filename || 'Jade') + ':' + lineno + + '\n' + context + '\n\n' + err.message; + throw err; +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/self-closing.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/self-closing.js new file mode 100644 index 0000000..0548771 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/self-closing.js @@ -0,0 +1,19 @@ + +/*! + * Jade - self closing tags + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = [ + 'meta' + , 'img' + , 'link' + , 'input' + , 'source' + , 'area' + , 'base' + , 'col' + , 'br' + , 'hr' +]; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/utils.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/utils.js new file mode 100644 index 0000000..ff46d02 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/lib/utils.js @@ -0,0 +1,49 @@ + +/*! + * Jade - utils + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Convert interpolation in the given string to JavaScript. + * + * @param {String} str + * @return {String} + * @api private + */ + +var interpolate = exports.interpolate = function(str){ + return str.replace(/(\\)?([#!]){(.*?)}/g, function(str, escape, flag, code){ + return escape + ? str + : "' + " + + ('!' == flag ? '' : 'escape') + + "((interp = " + code.replace(/\\'/g, "'") + + ") == null ? '' : interp) + '"; + }); +}; + +/** + * Escape single quotes in `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +var escape = exports.escape = function(str) { + return str.replace(/'/g, "\\'"); +}; + +/** + * Interpolate, and escape the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.text = function(str){ + return interpolate(escape(str)); +}; \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/.npmignore b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/.travis.yml b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/.travis.yml new file mode 100644 index 0000000..f1d0f13 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.4 + - 0.6 diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/History.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/History.md new file mode 100644 index 0000000..4961d2e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/History.md @@ -0,0 +1,107 @@ + +0.6.1 / 2012-06-01 +================== + + * Added: append (yes or no) on confirmation + * Added: allow node.js v0.7.x + +0.6.0 / 2012-04-10 +================== + + * Added `.prompt(obj, callback)` support. Closes #49 + * Added default support to .choose(). Closes #41 + * Fixed the choice example + +0.5.1 / 2011-12-20 +================== + + * Fixed `password()` for recent nodes. Closes #36 + +0.5.0 / 2011-12-04 +================== + + * Added sub-command option support [itay] + +0.4.3 / 2011-12-04 +================== + + * Fixed custom help ordering. Closes #32 + +0.4.2 / 2011-11-24 +================== + + * Added travis support + * Fixed: line-buffered input automatically trimmed. Closes #31 + +0.4.1 / 2011-11-18 +================== + + * Removed listening for "close" on --help + +0.4.0 / 2011-11-15 +================== + + * Added support for `--`. Closes #24 + +0.3.3 / 2011-11-14 +================== + + * Fixed: wait for close event when writing help info [Jerry Hamlet] + +0.3.2 / 2011-11-01 +================== + + * Fixed long flag definitions with values [felixge] + +0.3.1 / 2011-10-31 +================== + + * Changed `--version` short flag to `-V` from `-v` + * Changed `.version()` so it's configurable [felixge] + +0.3.0 / 2011-10-31 +================== + + * Added support for long flags only. Closes #18 + +0.2.1 / 2011-10-24 +================== + + * "node": ">= 0.4.x < 0.7.0". Closes #20 + +0.2.0 / 2011-09-26 +================== + + * Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs] + +0.1.0 / 2011-08-24 +================== + + * Added support for custom `--help` output + +0.0.5 / 2011-08-18 +================== + + * Changed: when the user enters nothing prompt for password again + * Fixed issue with passwords beginning with numbers [NuckChorris] + +0.0.4 / 2011-08-15 +================== + + * Fixed `Commander#args` + +0.0.3 / 2011-08-15 +================== + + * Added default option value support + +0.0.2 / 2011-08-15 +================== + + * Added mask support to `Command#password(str[, mask], fn)` + * Added `Command#password(str, fn)` + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/Makefile b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/Makefile new file mode 100644 index 0000000..0074625 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/Makefile @@ -0,0 +1,7 @@ + +TESTS = $(shell find test/test.*.js) + +test: + @./test/run $(TESTS) + +.PHONY: test \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/Readme.md b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/Readme.md new file mode 100644 index 0000000..b8328c3 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/Readme.md @@ -0,0 +1,262 @@ +# Commander.js + + The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander). + + [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js) + +## Installation + + $ npm install commander + +## Option parsing + + Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander'); + +program + .version('0.0.1') + .option('-p, --peppers', 'Add peppers') + .option('-P, --pineapple', 'Add pineapple') + .option('-b, --bbq', 'Add bbq sauce') + .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble') + .parse(process.argv); + +console.log('you ordered a pizza with:'); +if (program.peppers) console.log(' - peppers'); +if (program.pineapple) console.log(' - pineappe'); +if (program.bbq) console.log(' - bbq'); +console.log(' - %s cheese', program.cheese); +``` + + Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. + +## Automated --help + + The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free: + +``` + $ ./examples/pizza --help + + Usage: pizza [options] + + Options: + + -V, --version output the version number + -p, --peppers Add peppers + -P, --pineapple Add pineappe + -b, --bbq Add bbq sauce + -c, --cheese Add the specified type of cheese [marble] + -h, --help output usage information + +``` + +## Coercion + +```js +function range(val) { + return val.split('..').map(Number); +} + +function list(val) { + return val.split(','); +} + +program + .version('0.0.1') + .usage('[options] ') + .option('-i, --integer ', 'An integer argument', parseInt) + .option('-f, --float ', 'A float argument', parseFloat) + .option('-r, --range ..', 'A range', range) + .option('-l, --list ', 'A list', list) + .option('-o, --optional [value]', 'An optional value') + .parse(process.argv); + +console.log(' int: %j', program.integer); +console.log(' float: %j', program.float); +console.log(' optional: %j', program.optional); +program.range = program.range || []; +console.log(' range: %j..%j', program.range[0], program.range[1]); +console.log(' list: %j', program.list); +console.log(' args: %j', program.args); +``` + +## Custom help + + You can display arbitrary `-h, --help` information + by listening for "--help". Commander will automatically + exit once you are done so that the remainder of your program + does not execute causing undesired behaviours, for example + in the following executable "stuff" will not output when + `--help` is used. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('../'); + +function list(val) { + return val.split(',').map(Number); +} + +program + .version('0.0.1') + .option('-f, --foo', 'enable some foo') + .option('-b, --bar', 'enable some bar') + .option('-B, --baz', 'enable some baz'); + +// must be before .parse() since +// node's emit() is immediate + +program.on('--help', function(){ + console.log(' Examples:'); + console.log(''); + console.log(' $ custom-help --help'); + console.log(' $ custom-help -h'); + console.log(''); +}); + +program.parse(process.argv); + +console.log('stuff'); +``` + +yielding the following help output: + +``` + +Usage: custom-help [options] + +Options: + + -h, --help output usage information + -V, --version output the version number + -f, --foo enable some foo + -b, --bar enable some bar + -B, --baz enable some baz + +Examples: + + $ custom-help --help + $ custom-help -h + +``` + +## .prompt(msg, fn) + + Single-line prompt: + +```js +program.prompt('name: ', function(name){ + console.log('hi %s', name); +}); +``` + + Multi-line prompt: + +```js +program.prompt('description:', function(name){ + console.log('hi %s', name); +}); +``` + + Coercion: + +```js +program.prompt('Age: ', Number, function(age){ + console.log('age: %j', age); +}); +``` + +```js +program.prompt('Birthdate: ', Date, function(date){ + console.log('date: %s', date); +}); +``` + +## .password(msg[, mask], fn) + +Prompt for password without echoing: + +```js +program.password('Password: ', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +Prompt for password with mask char "*": + +```js +program.password('Password: ', '*', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +## .confirm(msg, fn) + + Confirm with the given `msg`: + +```js +program.confirm('continue? ', function(ok){ + console.log(' got %j', ok); +}); +``` + +## .choose(list, fn) + + Let the user choose from a `list`: + +```js +var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + +console.log('Choose the coolest pet:'); +program.choose(list, function(i){ + console.log('you chose %d "%s"', i, list[i]); +}); +``` + +## Links + + - [API documentation](http://visionmedia.github.com/commander.js/) + - [ascii tables](https://github.com/LearnBoost/cli-table) + - [progress bars](https://github.com/visionmedia/node-progress) + - [more progress bars](https://github.com/substack/node-multimeter) + - [examples](https://github.com/visionmedia/commander.js/tree/master/examples) + +## License + +(The MIT License) + +Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/index.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/index.js new file mode 100644 index 0000000..06ec1e4 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/commander'); \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/lib/commander.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/lib/commander.js new file mode 100644 index 0000000..5ba87eb --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/lib/commander.js @@ -0,0 +1,1026 @@ + +/*! + * commander + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , path = require('path') + , tty = require('tty') + , basename = path.basename; + +/** + * Expose the root command. + */ + +exports = module.exports = new Command; + +/** + * Expose `Command`. + */ + +exports.Command = Command; + +/** + * Expose `Option`. + */ + +exports.Option = Option; + +/** + * Initialize a new `Option` with the given `flags` and `description`. + * + * @param {String} flags + * @param {String} description + * @api public + */ + +function Option(flags, description) { + this.flags = flags; + this.required = ~flags.indexOf('<'); + this.optional = ~flags.indexOf('['); + this.bool = !~flags.indexOf('-no-'); + flags = flags.split(/[ ,|]+/); + if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); + this.long = flags.shift(); + this.description = description; +} + +/** + * Return option name. + * + * @return {String} + * @api private + */ + +Option.prototype.name = function(){ + return this.long + .replace('--', '') + .replace('no-', ''); +}; + +/** + * Check if `arg` matches the short or long flag. + * + * @param {String} arg + * @return {Boolean} + * @api private + */ + +Option.prototype.is = function(arg){ + return arg == this.short + || arg == this.long; +}; + +/** + * Initialize a new `Command`. + * + * @param {String} name + * @api public + */ + +function Command(name) { + this.commands = []; + this.options = []; + this.args = []; + this.name = name; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Command.prototype.__proto__ = EventEmitter.prototype; + +/** + * Add command `name`. + * + * The `.action()` callback is invoked when the + * command `name` is specified via __ARGV__, + * and the remaining arguments are applied to the + * function for access. + * + * When the `name` is "*" an un-matched command + * will be passed as the first arg, followed by + * the rest of __ARGV__ remaining. + * + * Examples: + * + * program + * .version('0.0.1') + * .option('-C, --chdir ', 'change the working directory') + * .option('-c, --config ', 'set config path. defaults to ./deploy.conf') + * .option('-T, --no-tests', 'ignore test hook') + * + * program + * .command('setup') + * .description('run remote setup commands') + * .action(function(){ + * console.log('setup'); + * }); + * + * program + * .command('exec ') + * .description('run the given remote command') + * .action(function(cmd){ + * console.log('exec "%s"', cmd); + * }); + * + * program + * .command('*') + * .description('deploy the given env') + * .action(function(env){ + * console.log('deploying "%s"', env); + * }); + * + * program.parse(process.argv); + * + * @param {String} name + * @return {Command} the new command + * @api public + */ + +Command.prototype.command = function(name){ + var args = name.split(/ +/); + var cmd = new Command(args.shift()); + this.commands.push(cmd); + cmd.parseExpectedArgs(args); + cmd.parent = this; + return cmd; +}; + +/** + * Parse expected `args`. + * + * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`. + * + * @param {Array} args + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parseExpectedArgs = function(args){ + if (!args.length) return; + var self = this; + args.forEach(function(arg){ + switch (arg[0]) { + case '<': + self.args.push({ required: true, name: arg.slice(1, -1) }); + break; + case '[': + self.args.push({ required: false, name: arg.slice(1, -1) }); + break; + } + }); + return this; +}; + +/** + * Register callback `fn` for the command. + * + * Examples: + * + * program + * .command('help') + * .description('display verbose help') + * .action(function(){ + * // output help here + * }); + * + * @param {Function} fn + * @return {Command} for chaining + * @api public + */ + +Command.prototype.action = function(fn){ + var self = this; + this.parent.on(this.name, function(args, unknown){ + // Parse any so-far unknown options + unknown = unknown || []; + var parsed = self.parseOptions(unknown); + + // Output help if necessary + outputHelpIfNecessary(self, parsed.unknown); + + // If there are still any unknown options, then we simply + // die, unless someone asked for help, in which case we give it + // to them, and then we die. + if (parsed.unknown.length > 0) { + self.unknownOption(parsed.unknown[0]); + } + + self.args.forEach(function(arg, i){ + if (arg.required && null == args[i]) { + self.missingArgument(arg.name); + } + }); + + // Always append ourselves to the end of the arguments, + // to make sure we match the number of arguments the user + // expects + if (self.args.length) { + args[self.args.length] = self; + } else { + args.push(self); + } + + fn.apply(this, args); + }); + return this; +}; + +/** + * Define option with `flags`, `description` and optional + * coercion `fn`. + * + * The `flags` string should contain both the short and long flags, + * separated by comma, a pipe or space. The following are all valid + * all will output this way when `--help` is used. + * + * "-p, --pepper" + * "-p|--pepper" + * "-p --pepper" + * + * Examples: + * + * // simple boolean defaulting to false + * program.option('-p, --pepper', 'add pepper'); + * + * --pepper + * program.pepper + * // => Boolean + * + * // simple boolean defaulting to false + * program.option('-C, --no-cheese', 'remove cheese'); + * + * program.cheese + * // => true + * + * --no-cheese + * program.cheese + * // => true + * + * // required argument + * program.option('-C, --chdir ', 'change the working directory'); + * + * --chdir /tmp + * program.chdir + * // => "/tmp" + * + * // optional argument + * program.option('-c, --cheese [type]', 'add cheese [marble]'); + * + * @param {String} flags + * @param {String} description + * @param {Function|Mixed} fn or default + * @param {Mixed} defaultValue + * @return {Command} for chaining + * @api public + */ + +Command.prototype.option = function(flags, description, fn, defaultValue){ + var self = this + , option = new Option(flags, description) + , oname = option.name() + , name = camelcase(oname); + + // default as 3rd arg + if ('function' != typeof fn) defaultValue = fn, fn = null; + + // preassign default value only for --no-*, [optional], or + if (false == option.bool || option.optional || option.required) { + // when --no-* we make sure default is true + if (false == option.bool) defaultValue = true; + // preassign only if we have a default + if (undefined !== defaultValue) self[name] = defaultValue; + } + + // register the option + this.options.push(option); + + // when it's passed assign the value + // and conditionally invoke the callback + this.on(oname, function(val){ + // coercion + if (null != val && fn) val = fn(val); + + // unassigned or bool + if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) { + // if no value, bool true, and we have a default, then use it! + if (null == val) { + self[name] = option.bool + ? defaultValue || true + : false; + } else { + self[name] = val; + } + } else if (null !== val) { + // reassign + self[name] = val; + } + }); + + return this; +}; + +/** + * Parse `argv`, settings options and invoking commands when defined. + * + * @param {Array} argv + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parse = function(argv){ + // store raw args + this.rawArgs = argv; + + // guess name + if (!this.name) this.name = basename(argv[1]); + + // process argv + var parsed = this.parseOptions(this.normalize(argv.slice(2))); + this.args = parsed.args; + return this.parseArgs(this.args, parsed.unknown); +}; + +/** + * Normalize `args`, splitting joined short flags. For example + * the arg "-abc" is equivalent to "-a -b -c". + * + * @param {Array} args + * @return {Array} + * @api private + */ + +Command.prototype.normalize = function(args){ + var ret = [] + , arg; + + for (var i = 0, len = args.length; i < len; ++i) { + arg = args[i]; + if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) { + arg.slice(1).split('').forEach(function(c){ + ret.push('-' + c); + }); + } else { + ret.push(arg); + } + } + + return ret; +}; + +/** + * Parse command `args`. + * + * When listener(s) are available those + * callbacks are invoked, otherwise the "*" + * event is emitted and those actions are invoked. + * + * @param {Array} args + * @return {Command} for chaining + * @api private + */ + +Command.prototype.parseArgs = function(args, unknown){ + var cmds = this.commands + , len = cmds.length + , name; + + if (args.length) { + name = args[0]; + if (this.listeners(name).length) { + this.emit(args.shift(), args, unknown); + } else { + this.emit('*', args); + } + } else { + outputHelpIfNecessary(this, unknown); + + // If there were no args and we have unknown options, + // then they are extraneous and we need to error. + if (unknown.length > 0) { + this.unknownOption(unknown[0]); + } + } + + return this; +}; + +/** + * Return an option matching `arg` if any. + * + * @param {String} arg + * @return {Option} + * @api private + */ + +Command.prototype.optionFor = function(arg){ + for (var i = 0, len = this.options.length; i < len; ++i) { + if (this.options[i].is(arg)) { + return this.options[i]; + } + } +}; + +/** + * Parse options from `argv` returning `argv` + * void of these options. + * + * @param {Array} argv + * @return {Array} + * @api public + */ + +Command.prototype.parseOptions = function(argv){ + var args = [] + , len = argv.length + , literal + , option + , arg; + + var unknownOptions = []; + + // parse options + for (var i = 0; i < len; ++i) { + arg = argv[i]; + + // literal args after -- + if ('--' == arg) { + literal = true; + continue; + } + + if (literal) { + args.push(arg); + continue; + } + + // find matching Option + option = this.optionFor(arg); + + // option is defined + if (option) { + // requires arg + if (option.required) { + arg = argv[++i]; + if (null == arg) return this.optionMissingArgument(option); + if ('-' == arg[0]) return this.optionMissingArgument(option, arg); + this.emit(option.name(), arg); + // optional arg + } else if (option.optional) { + arg = argv[i+1]; + if (null == arg || '-' == arg[0]) { + arg = null; + } else { + ++i; + } + this.emit(option.name(), arg); + // bool + } else { + this.emit(option.name()); + } + continue; + } + + // looks like an option + if (arg.length > 1 && '-' == arg[0]) { + unknownOptions.push(arg); + + // If the next argument looks like it might be + // an argument for this option, we pass it on. + // If it isn't, then it'll simply be ignored + if (argv[i+1] && '-' != argv[i+1][0]) { + unknownOptions.push(argv[++i]); + } + continue; + } + + // arg + args.push(arg); + } + + return { args: args, unknown: unknownOptions }; +}; + +/** + * Argument `name` is missing. + * + * @param {String} name + * @api private + */ + +Command.prototype.missingArgument = function(name){ + console.error(); + console.error(" error: missing required argument `%s'", name); + console.error(); + process.exit(1); +}; + +/** + * `Option` is missing an argument, but received `flag` or nothing. + * + * @param {String} option + * @param {String} flag + * @api private + */ + +Command.prototype.optionMissingArgument = function(option, flag){ + console.error(); + if (flag) { + console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag); + } else { + console.error(" error: option `%s' argument missing", option.flags); + } + console.error(); + process.exit(1); +}; + +/** + * Unknown option `flag`. + * + * @param {String} flag + * @api private + */ + +Command.prototype.unknownOption = function(flag){ + console.error(); + console.error(" error: unknown option `%s'", flag); + console.error(); + process.exit(1); +}; + +/** + * Set the program version to `str`. + * + * This method auto-registers the "-V, --version" flag + * which will print the version number when passed. + * + * @param {String} str + * @param {String} flags + * @return {Command} for chaining + * @api public + */ + +Command.prototype.version = function(str, flags){ + if (0 == arguments.length) return this._version; + this._version = str; + flags = flags || '-V, --version'; + this.option(flags, 'output the version number'); + this.on('version', function(){ + console.log(str); + process.exit(0); + }); + return this; +}; + +/** + * Set the description `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.description = function(str){ + if (0 == arguments.length) return this._description; + this._description = str; + return this; +}; + +/** + * Set / get the command usage `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.usage = function(str){ + var args = this.args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }); + + var usage = '[options' + + (this.commands.length ? '] [command' : '') + + ']' + + (this.args.length ? ' ' + args : ''); + if (0 == arguments.length) return this._usage || usage; + this._usage = str; + + return this; +}; + +/** + * Return the largest option length. + * + * @return {Number} + * @api private + */ + +Command.prototype.largestOptionLength = function(){ + return this.options.reduce(function(max, option){ + return Math.max(max, option.flags.length); + }, 0); +}; + +/** + * Return help for options. + * + * @return {String} + * @api private + */ + +Command.prototype.optionHelp = function(){ + var width = this.largestOptionLength(); + + // Prepend the help information + return [pad('-h, --help', width) + ' ' + 'output usage information'] + .concat(this.options.map(function(option){ + return pad(option.flags, width) + + ' ' + option.description; + })) + .join('\n'); +}; + +/** + * Return command help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.commandHelp = function(){ + if (!this.commands.length) return ''; + return [ + '' + , ' Commands:' + , '' + , this.commands.map(function(cmd){ + var args = cmd.args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }).join(' '); + + return cmd.name + + (cmd.options.length + ? ' [options]' + : '') + ' ' + args + + (cmd.description() + ? '\n' + cmd.description() + : ''); + }).join('\n\n').replace(/^/gm, ' ') + , '' + ].join('\n'); +}; + +/** + * Return program help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.helpInformation = function(){ + return [ + '' + , ' Usage: ' + this.name + ' ' + this.usage() + , '' + this.commandHelp() + , ' Options:' + , '' + , '' + this.optionHelp().replace(/^/gm, ' ') + , '' + , '' + ].join('\n'); +}; + +/** + * Prompt for a `Number`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForNumber = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseNumber(val){ + val = Number(val); + if (isNaN(val)) return self.promptSingleLine(str + '(must be a number) ', parseNumber); + fn(val); + }); +}; + +/** + * Prompt for a `Date`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForDate = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseDate(val){ + val = new Date(val); + if (isNaN(val.getTime())) return self.promptSingleLine(str + '(must be a date) ', parseDate); + fn(val); + }); +}; + +/** + * Single-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptSingleLine = function(str, fn){ + if ('function' == typeof arguments[2]) { + return this['promptFor' + (fn.name || fn)](str, arguments[2]); + } + + process.stdout.write(str); + process.stdin.setEncoding('utf8'); + process.stdin.once('data', function(val){ + fn(val.trim()); + }).resume(); +}; + +/** + * Multi-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptMultiLine = function(str, fn){ + var buf = []; + console.log(str); + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function(val){ + if ('\n' == val || '\r\n' == val) { + process.stdin.removeAllListeners('data'); + fn(buf.join('\n')); + } else { + buf.push(val.trimRight()); + } + }).resume(); +}; + +/** + * Prompt `str` and callback `fn(val)` + * + * Commander supports single-line and multi-line prompts. + * To issue a single-line prompt simply add white-space + * to the end of `str`, something like "name: ", whereas + * for a multi-line prompt omit this "description:". + * + * + * Examples: + * + * program.prompt('Username: ', function(name){ + * console.log('hi %s', name); + * }); + * + * program.prompt('Description:', function(desc){ + * console.log('description was "%s"', desc.trim()); + * }); + * + * @param {String|Object} str + * @param {Function} fn + * @api public + */ + +Command.prototype.prompt = function(str, fn){ + var self = this; + + if ('string' == typeof str) { + if (/ $/.test(str)) return this.promptSingleLine.apply(this, arguments); + this.promptMultiLine(str, fn); + } else { + var keys = Object.keys(str) + , obj = {}; + + function next() { + var key = keys.shift() + , label = str[key]; + + if (!key) return fn(obj); + self.prompt(label, function(val){ + obj[key] = val; + next(); + }); + } + + next(); + } +}; + +/** + * Prompt for password with `str`, `mask` char and callback `fn(val)`. + * + * The mask string defaults to '', aka no output is + * written while typing, you may want to use "*" etc. + * + * Examples: + * + * program.password('Password: ', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * program.password('Password: ', '*', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {String} mask + * @param {Function} fn + * @api public + */ + +Command.prototype.password = function(str, mask, fn){ + var self = this + , buf = ''; + + // default mask + if ('function' == typeof mask) { + fn = mask; + mask = ''; + } + + process.stdin.resume(); + tty.setRawMode(true); + process.stdout.write(str); + + // keypress + process.stdin.on('keypress', function(c, key){ + if (key && 'enter' == key.name) { + console.log(); + process.stdin.removeAllListeners('keypress'); + tty.setRawMode(false); + if (!buf.trim().length) return self.password(str, mask, fn); + fn(buf); + return; + } + + if (key && key.ctrl && 'c' == key.name) { + console.log('%s', buf); + process.exit(); + } + + process.stdout.write(mask); + buf += c; + }).resume(); +}; + +/** + * Confirmation prompt with `str` and callback `fn(bool)` + * + * Examples: + * + * program.confirm('continue? ', function(ok){ + * console.log(' got %j', ok); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {Function} fn + * @api public + */ + + +Command.prototype.confirm = function(str, fn, verbose){ + var self = this; + this.prompt(str, function(ok){ + if (!ok.trim()) { + if (!verbose) str += '(yes or no) '; + return self.confirm(str, fn, true); + } + fn(parseBool(ok)); + }); +}; + +/** + * Choice prompt with `list` of items and callback `fn(index, item)` + * + * Examples: + * + * var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + * + * console.log('Choose the coolest pet:'); + * program.choose(list, function(i){ + * console.log('you chose %d "%s"', i, list[i]); + * process.stdin.destroy(); + * }); + * + * @param {Array} list + * @param {Number|Function} index or fn + * @param {Function} fn + * @api public + */ + +Command.prototype.choose = function(list, index, fn){ + var self = this + , hasDefault = 'number' == typeof index; + + if (!hasDefault) { + fn = index; + index = null; + } + + list.forEach(function(item, i){ + if (hasDefault && i == index) { + console.log('* %d) %s', i + 1, item); + } else { + console.log(' %d) %s', i + 1, item); + } + }); + + function again() { + self.prompt(' : ', function(val){ + val = parseInt(val, 10) - 1; + if (hasDefault && isNaN(val)) val = index; + + if (null == list[val]) { + again(); + } else { + fn(val, list[val]); + } + }); + } + + again(); +}; + +/** + * Camel-case the given `flag` + * + * @param {String} flag + * @return {String} + * @api private + */ + +function camelcase(flag) { + return flag.split('-').reduce(function(str, word){ + return str + word[0].toUpperCase() + word.slice(1); + }); +} + +/** + * Parse a boolean `str`. + * + * @param {String} str + * @return {Boolean} + * @api private + */ + +function parseBool(str) { + return /^y|yes|ok|true$/i.test(str); +} + +/** + * Pad `str` to `width`. + * + * @param {String} str + * @param {Number} width + * @return {String} + * @api private + */ + +function pad(str, width) { + var len = Math.max(0, width - str.length); + return str + Array(len + 1).join(' '); +} + +/** + * Output help information if necessary + * + * @param {Command} command to output help for + * @param {Array} array of options to search for -h or --help + * @api private + */ + +function outputHelpIfNecessary(cmd, options) { + options = options || []; + for (var i = 0; i < options.length; i++) { + if (options[i] == '--help' || options[i] == '-h') { + process.stdout.write(cmd.helpInformation()); + cmd.emit('--help'); + process.exit(0); + } + } +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/package.json new file mode 100644 index 0000000..b95aecb --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/commander/package.json @@ -0,0 +1,60 @@ +{ + "name": "commander", + "version": "0.6.1", + "description": "the complete solution for node.js command-line programs", + "keywords": [ + "command", + "option", + "parser", + "prompt", + "stdin" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/commander.js.git" + }, + "dependencies": {}, + "devDependencies": { + "should": ">= 0.0.1" + }, + "scripts": { + "test": "make test" + }, + "main": "index", + "engines": { + "node": ">= 0.4.x" + }, + "_npmUser": { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + "_id": "commander@0.6.1", + "optionalDependencies": {}, + "_engineSupported": true, + "_npmVersion": "1.1.0-3", + "_nodeVersion": "v0.6.12", + "_defaultsLoaded": true, + "dist": { + "shasum": "fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06", + "tarball": "http://registry.npmjs.org/commander/-/commander-0.6.1.tgz" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + } + ], + "directories": {}, + "_shasum": "fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06", + "_resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "_from": "commander@0.6.1", + "bugs": { + "url": "https://github.com/visionmedia/commander.js/issues" + }, + "readme": "ERROR: No README data found!", + "homepage": "https://github.com/visionmedia/commander.js#readme" +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.gitignore.orig b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.gitignore.orig new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.gitignore.orig @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.gitignore.rej b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.gitignore.rej new file mode 100644 index 0000000..69244ff --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.gitignore.rej @@ -0,0 +1,5 @@ +--- /dev/null ++++ .gitignore +@@ -0,0 +1,2 @@ ++node_modules/ ++npm-debug.log \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.npmignore b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.npmignore new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/LICENSE b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/LICENSE new file mode 100644 index 0000000..432d1ae --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/LICENSE @@ -0,0 +1,21 @@ +Copyright 2010 James Halliday (mail@substack.net) + +This project is free software released under the MIT/X11 license: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/README.markdown b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/README.markdown new file mode 100644 index 0000000..b4dd75f --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/README.markdown @@ -0,0 +1,54 @@ +mkdirp +====== + +Like `mkdir -p`, but in node.js! + +example +======= + +pow.js +------ + var mkdirp = require('mkdirp'); + + mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') + }); + +Output + pow! + +And now /tmp/foo/bar/baz exists, huzzah! + +methods +======= + +var mkdirp = require('mkdirp'); + +mkdirp(dir, mode, cb) +--------------------- + +Create a new directory and any necessary subdirectories at `dir` with octal +permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +mkdirp.sync(dir, mode) +---------------------- + +Synchronously create a new directory and any necessary subdirectories at `dir` +with octal permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +install +======= + +With [npm](http://npmjs.org) do: + + npm install mkdirp + +license +======= + +MIT/X11 diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js new file mode 100644 index 0000000..e692421 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js.orig b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js.orig new file mode 100644 index 0000000..7741462 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js.orig @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', 0755, function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js.rej b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js.rej new file mode 100644 index 0000000..81e7f43 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/examples/pow.js.rej @@ -0,0 +1,19 @@ +--- examples/pow.js ++++ examples/pow.js +@@ -1,6 +1,15 @@ +-var mkdirp = require('mkdirp').mkdirp; ++var mkdirp = require('../').mkdirp, ++ mkdirpSync = require('../').mkdirpSync; + + mkdirp('/tmp/foo/bar/baz', 0755, function (err) { + if (err) console.error(err) + else console.log('pow!') + }); ++ ++try { ++ mkdirpSync('/tmp/bar/foo/baz', 0755); ++ console.log('double pow!'); ++} ++catch (ex) { ++ console.log(ex); ++} \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/index.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/index.js new file mode 100644 index 0000000..25f43ad --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/index.js @@ -0,0 +1,79 @@ +var path = require('path'); +var fs = require('fs'); + +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; + +function mkdirP (p, mode, f) { + if (typeof mode === 'function' || mode === undefined) { + f = mode; + mode = 0777 & (~process.umask()); + } + + var cb = f || function () {}; + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + fs.mkdir(p, mode, function (er) { + if (!er) return cb(); + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), mode, function (er) { + if (er) cb(er); + else mkdirP(p, mode, cb); + }); + break; + + case 'EEXIST': + fs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original EEXIST be the failure reason. + if (er2 || !stat.isDirectory()) cb(er) + else cb(); + }); + break; + + default: + cb(er); + break; + } + }); +} + +mkdirP.sync = function sync (p, mode) { + if (mode === undefined) { + mode = 0777 & (~process.umask()); + } + + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + try { + fs.mkdirSync(p, mode) + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + var err1 = sync(path.dirname(p), mode) + if (err1) throw err1; + else return sync(p, mode); + break; + + case 'EEXIST' : + var stat; + try { + stat = fs.statSync(p); + } + catch (err1) { + throw err0 + } + if (!stat.isDirectory()) throw err0; + else return null; + break; + default : + throw err0 + break; + } + } + + return null; +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/package.json new file mode 100644 index 0000000..b93a7a1 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/package.json @@ -0,0 +1,58 @@ +{ + "name": "mkdirp", + "description": "Recursively mkdir, like `mkdir -p`", + "version": "0.3.0", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "main": "./index", + "keywords": [ + "mkdir", + "directory" + ], + "repository": { + "type": "git", + "url": "git://github.com/substack/node-mkdirp.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "devDependencies": { + "tap": "0.0.x" + }, + "license": "MIT/X11", + "engines": { + "node": "*" + }, + "_npmUser": { + "name": "substack", + "email": "mail@substack.net" + }, + "_id": "mkdirp@0.3.0", + "dependencies": {}, + "_engineSupported": true, + "_npmVersion": "1.0.106", + "_nodeVersion": "v0.4.12", + "_defaultsLoaded": true, + "dist": { + "shasum": "1bbf5ab1ba827af23575143490426455f481fe1e", + "tarball": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + }, + "maintainers": [ + { + "name": "substack", + "email": "mail@substack.net" + } + ], + "directories": {}, + "_shasum": "1bbf5ab1ba827af23575143490426455f481fe1e", + "_resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "_from": "mkdirp@0.3.0", + "bugs": { + "url": "https://github.com/substack/node-mkdirp/issues" + }, + "readme": "ERROR: No README data found!", + "homepage": "https://github.com/substack/node-mkdirp#readme" +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/chmod.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/chmod.js new file mode 100644 index 0000000..520dcb8 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/chmod.js @@ -0,0 +1,38 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +test('chmod-pre', function (t) { + var mode = 0744 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.equal(stat && stat.mode & 0777, mode, 'should be 0744'); + t.end(); + }); + }); +}); + +test('chmod', function (t) { + var mode = 0755 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.end(); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/clobber.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/clobber.js new file mode 100644 index 0000000..0eb7099 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/clobber.js @@ -0,0 +1,37 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +// a file in the way +var itw = ps.slice(0, 3).join('/'); + + +test('clobber-pre', function (t) { + console.error("about to write to "+itw) + fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.'); + + fs.stat(itw, function (er, stat) { + t.ifError(er) + t.ok(stat && stat.isFile(), 'should be file') + t.end() + }) +}) + +test('clobber', function (t) { + t.plan(2); + mkdirp(file, 0755, function (err) { + t.ok(err); + t.equal(err.code, 'ENOTDIR'); + t.end(); + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/mkdirp.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/mkdirp.js new file mode 100644 index 0000000..b07cd70 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/mkdirp.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('woo', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/perm.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/perm.js new file mode 100644 index 0000000..23a7abb --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/perm.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('async perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); + +test('async root perm', function (t) { + mkdirp('/tmp', 0755, function (err) { + if (err) t.fail(err); + t.end(); + }); + t.end(); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/perm_sync.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/perm_sync.js new file mode 100644 index 0000000..f685f60 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/perm_sync.js @@ -0,0 +1,39 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json'; + + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); + +test('sync root perm', function (t) { + t.plan(1); + + var file = '/tmp'; + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/race.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/race.js new file mode 100644 index 0000000..96a0447 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/race.js @@ -0,0 +1,41 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('race', function (t) { + t.plan(4); + var ps = [ '', 'tmp' ]; + + for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); + } + var file = ps.join('/'); + + var res = 2; + mk(file, function () { + if (--res === 0) t.end(); + }); + + mk(file, function () { + if (--res === 0) t.end(); + }); + + function mk (file, cb) { + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + if (cb) cb(); + } + }) + }) + }); + } +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/rel.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/rel.js new file mode 100644 index 0000000..7985824 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/rel.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('rel', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var cwd = process.cwd(); + process.chdir('/tmp'); + + var file = [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + process.chdir(cwd); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/sync.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/sync.js new file mode 100644 index 0000000..e0e389d --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/sync.js @@ -0,0 +1,27 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + var err = mkdirp.sync(file, 0755); + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/umask.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/umask.js new file mode 100644 index 0000000..64ccafe --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/umask.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('implicit mode from umask', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0777 & (~process.umask())); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/umask_sync.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/umask_sync.js new file mode 100644 index 0000000..83cba56 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/node_modules/mkdirp/test/umask_sync.js @@ -0,0 +1,27 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('umask sync modes', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + var err = mkdirp.sync(file); + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, (0777 & (~process.umask()))); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/package.json new file mode 100644 index 0000000..3362357 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/package.json @@ -0,0 +1,61 @@ +{ + "name": "jade", + "description": "Jade template engine", + "version": "0.26.3", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/jade.git" + }, + "main": "./index.js", + "bin": { + "jade": "./bin/jade" + }, + "man": [ + "./jade.1" + ], + "dependencies": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "devDependencies": { + "mocha": "*", + "markdown": "*", + "stylus": "*", + "uubench": "*", + "should": "*", + "less": "*", + "uglify-js": "*" + }, + "component": { + "scripts": { + "jade": "runtime.js" + } + }, + "scripts": { + "prepublish": "npm prune" + }, + "_id": "jade@0.26.3", + "dist": { + "shasum": "8f10d7977d8d79f2f6ff862a81b0513ccb25686c", + "tarball": "http://registry.npmjs.org/jade/-/jade-0.26.3.tgz" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + } + ], + "directories": {}, + "_shasum": "8f10d7977d8d79f2f6ff862a81b0513ccb25686c", + "_resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "_from": "jade@0.26.3", + "bugs": { + "url": "https://github.com/visionmedia/jade/issues" + }, + "readme": "ERROR: No README data found!", + "homepage": "https://github.com/visionmedia/jade#readme" +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/runtime.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/runtime.js new file mode 100644 index 0000000..0f54907 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/runtime.js @@ -0,0 +1,179 @@ + +jade = (function(exports){ +/*! + * Jade - runtime + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Lame Array.isArray() polyfill for now. + */ + +if (!Array.isArray) { + Array.isArray = function(arr){ + return '[object Array]' == Object.prototype.toString.call(arr); + }; +} + +/** + * Lame Object.keys() polyfill for now. + */ + +if (!Object.keys) { + Object.keys = function(obj){ + var arr = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + arr.push(key); + } + } + return arr; + } +} + +/** + * Merge two attribute objects giving precedence + * to values in object `b`. Classes are special-cased + * allowing for arrays and merging/joining appropriately + * resulting in a string. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api private + */ + +exports.merge = function merge(a, b) { + var ac = a['class']; + var bc = b['class']; + + if (ac || bc) { + ac = ac || []; + bc = bc || []; + if (!Array.isArray(ac)) ac = [ac]; + if (!Array.isArray(bc)) bc = [bc]; + ac = ac.filter(nulls); + bc = bc.filter(nulls); + a['class'] = ac.concat(bc).join(' '); + } + + for (var key in b) { + if (key != 'class') { + a[key] = b[key]; + } + } + + return a; +}; + +/** + * Filter null `val`s. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function nulls(val) { + return val != null; +} + +/** + * Render the given attributes object. + * + * @param {Object} obj + * @param {Object} escaped + * @return {String} + * @api private + */ + +exports.attrs = function attrs(obj, escaped){ + var buf = [] + , terse = obj.terse; + + delete obj.terse; + var keys = Object.keys(obj) + , len = keys.length; + + if (len) { + buf.push(''); + for (var i = 0; i < len; ++i) { + var key = keys[i] + , val = obj[key]; + + if ('boolean' == typeof val || null == val) { + if (val) { + terse + ? buf.push(key) + : buf.push(key + '="' + key + '"'); + } + } else if (0 == key.indexOf('data') && 'string' != typeof val) { + buf.push(key + "='" + JSON.stringify(val) + "'"); + } else if ('class' == key && Array.isArray(val)) { + buf.push(key + '="' + exports.escape(val.join(' ')) + '"'); + } else if (escaped && escaped[key]) { + buf.push(key + '="' + exports.escape(val) + '"'); + } else { + buf.push(key + '="' + val + '"'); + } + } + } + + return buf.join(' '); +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function escape(html){ + return String(html) + .replace(/&(?!(\w+|\#\d+);)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; + +/** + * Re-throw the given `err` in context to the + * the jade in `filename` at the given `lineno`. + * + * @param {Error} err + * @param {String} filename + * @param {String} lineno + * @api private + */ + +exports.rethrow = function rethrow(err, filename, lineno){ + if (!filename) throw err; + + var context = 3 + , str = require('fs').readFileSync(filename, 'utf8') + , lines = str.split('\n') + , start = Math.max(lineno - context, 0) + , end = Math.min(lines.length, lineno + context); + + // Error context + var context = lines.slice(start, end).map(function(line, i){ + var curr = i + start + 1; + return (curr == lineno ? ' > ' : ' ') + + curr + + '| ' + + line; + }).join('\n'); + + // Alter exception message + err.path = filename; + err.message = (filename || 'Jade') + ':' + lineno + + '\n' + context + '\n\n' + err.message; + throw err; +}; + + return exports; + +})({}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/runtime.min.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/runtime.min.js new file mode 100644 index 0000000..1714efb --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/runtime.min.js @@ -0,0 +1 @@ +jade=function(exports){Array.isArray||(Array.isArray=function(arr){return"[object Array]"==Object.prototype.toString.call(arr)}),Object.keys||(Object.keys=function(obj){var arr=[];for(var key in obj)obj.hasOwnProperty(key)&&arr.push(key);return arr}),exports.merge=function merge(a,b){var ac=a["class"],bc=b["class"];if(ac||bc)ac=ac||[],bc=bc||[],Array.isArray(ac)||(ac=[ac]),Array.isArray(bc)||(bc=[bc]),ac=ac.filter(nulls),bc=bc.filter(nulls),a["class"]=ac.concat(bc).join(" ");for(var key in b)key!="class"&&(a[key]=b[key]);return a};function nulls(val){return val!=null}return exports.attrs=function attrs(obj,escaped){var buf=[],terse=obj.terse;delete obj.terse;var keys=Object.keys(obj),len=keys.length;if(len){buf.push("");for(var i=0;i/g,">").replace(/"/g,""")},exports.rethrow=function rethrow(err,filename,lineno){if(!filename)throw err;var context=3,str=require("fs").readFileSync(filename,"utf8"),lines=str.split("\n"),start=Math.max(lineno-context,0),end=Math.min(lines.length,lineno+context),context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" > ":" ")+curr+"| "+line}).join("\n");throw err.path=filename,err.message=(filename||"Jade")+":"+lineno+"\n"+context+"\n\n"+err.message,err},exports}({}); \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/test.jade b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/test.jade new file mode 100644 index 0000000..b3a8988 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/test.jade @@ -0,0 +1,7 @@ +p. + This is a large + body of text for + this tag. + + Nothing too + exciting. \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/head.jade b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/head.jade new file mode 100644 index 0000000..8515406 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/head.jade @@ -0,0 +1,5 @@ +head + script(src='/jquery.js') + yield + if false + script(src='/jquery.ui.js') diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/index.jade b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/index.jade new file mode 100644 index 0000000..1032c5f --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/index.jade @@ -0,0 +1,22 @@ + +tag = 'p' +foo = 'bar' + +#{tag} value +#{tag}(foo='bar') value +#{foo ? 'a' : 'li'}(something) here + +mixin item(icon) + li + if attributes.href + a(attributes) + img.icon(src=icon) + block + else + span(attributes) + img.icon(src=icon) + block + +ul + +item('contact') Contact + +item(href='/contact') Contact diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/index.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/index.js new file mode 100644 index 0000000..226e8c0 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/index.js @@ -0,0 +1,11 @@ + +/** + * Module dependencies. + */ + +var jade = require('../'); + +jade.renderFile('testing/index.jade', { pretty: true, debug: true, compileDebug: false }, function(err, str){ + if (err) throw err; + console.log(str); +}); \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/layout.jade b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/layout.jade new file mode 100644 index 0000000..6923cf1 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/layout.jade @@ -0,0 +1,6 @@ +html + include head + script(src='/caustic.js') + script(src='/app.js') + body + block content \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/user.jade b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/user.jade new file mode 100644 index 0000000..3c636b7 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/user.jade @@ -0,0 +1,7 @@ +h1 Tobi +p Is a ferret + +ul + li: a foo + li: a bar + li: a baz \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/user.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/user.js new file mode 100644 index 0000000..2ecc45e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/jade/testing/user.js @@ -0,0 +1,27 @@ +function anonymous(locals, attrs, escape, rethrow) { +var attrs = jade.attrs, escape = jade.escape, rethrow = jade.rethrow; +var __jade = [{ lineno: 1, filename: "testing/user.jade" }]; +try { +var buf = []; +with (locals || {}) { +var interp; +__jade.unshift({ lineno: 1, filename: __jade[0].filename }); +__jade.unshift({ lineno: 1, filename: __jade[0].filename }); +buf.push('

            Tobi'); +__jade.unshift({ lineno: undefined, filename: __jade[0].filename }); +__jade.shift(); +buf.push('

            '); +__jade.shift(); +__jade.unshift({ lineno: 2, filename: __jade[0].filename }); +buf.push('

            Is a ferret'); +__jade.unshift({ lineno: undefined, filename: __jade[0].filename }); +__jade.shift(); +buf.push('

            '); +__jade.shift(); +__jade.shift(); +} +return buf.join(""); +} catch (err) { + rethrow(err, __jade[0].filename, __jade[0].lineno); +} +} \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/.npmignore b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/.npmignore new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/.travis.yml b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/.travis.yml new file mode 100644 index 0000000..c693a93 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.6 + - 0.8 + - "0.10" diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/LICENSE b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/LICENSE new file mode 100644 index 0000000..432d1ae --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/LICENSE @@ -0,0 +1,21 @@ +Copyright 2010 James Halliday (mail@substack.net) + +This project is free software released under the MIT/X11 license: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/bin/cmd.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/bin/cmd.js new file mode 100755 index 0000000..d95de15 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/bin/cmd.js @@ -0,0 +1,33 @@ +#!/usr/bin/env node + +var mkdirp = require('../'); +var minimist = require('minimist'); +var fs = require('fs'); + +var argv = minimist(process.argv.slice(2), { + alias: { m: 'mode', h: 'help' }, + string: [ 'mode' ] +}); +if (argv.help) { + fs.createReadStream(__dirname + '/usage.txt').pipe(process.stdout); + return; +} + +var paths = argv._.slice(); +var mode = argv.mode ? parseInt(argv.mode, 8) : undefined; + +(function next () { + if (paths.length === 0) return; + var p = paths.shift(); + + if (mode === undefined) mkdirp(p, cb) + else mkdirp(p, mode, cb) + + function cb (err) { + if (err) { + console.error(err.message); + process.exit(1); + } + else next(); + } +})(); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/bin/usage.txt b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/bin/usage.txt new file mode 100644 index 0000000..f952aa2 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/bin/usage.txt @@ -0,0 +1,12 @@ +usage: mkdirp [DIR1,DIR2..] {OPTIONS} + + Create each supplied directory including any necessary parent directories that + don't yet exist. + + If the directory already exists, do nothing. + +OPTIONS are: + + -m, --mode If a directory needs to be created, set the mode as an octal + permission string. + diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/examples/pow.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/examples/pow.js new file mode 100644 index 0000000..e692421 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/examples/pow.js @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/index.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/index.js new file mode 100644 index 0000000..a1742b2 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/index.js @@ -0,0 +1,97 @@ +var path = require('path'); +var fs = require('fs'); + +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; + +function mkdirP (p, opts, f, made) { + if (typeof opts === 'function') { + f = opts; + opts = {}; + } + else if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = 0777 & (~process.umask()); + } + if (!made) made = null; + + var cb = f || function () {}; + p = path.resolve(p); + + xfs.mkdir(p, mode, function (er) { + if (!er) { + made = made || p; + return cb(null, made); + } + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), opts, function (er, made) { + if (er) cb(er, made); + else mkdirP(p, opts, cb, made); + }); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + xfs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original error be the failure reason. + if (er2 || !stat.isDirectory()) cb(er, made) + else cb(null, made); + }); + break; + } + }); +} + +mkdirP.sync = function sync (p, opts, made) { + if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = 0777 & (~process.umask()); + } + if (!made) made = null; + + p = path.resolve(p); + + try { + xfs.mkdirSync(p, mode); + made = made || p; + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + made = sync(path.dirname(p), opts, made); + sync(p, opts, made); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + var stat; + try { + stat = xfs.statSync(p); + } + catch (err1) { + throw err0; + } + if (!stat.isDirectory()) throw err0; + break; + } + } + + return made; +}; diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/.travis.yml b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/.travis.yml new file mode 100644 index 0000000..cc4dba2 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - "0.8" + - "0.10" diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/LICENSE b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/LICENSE new file mode 100644 index 0000000..ee27ba4 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/LICENSE @@ -0,0 +1,18 @@ +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/example/parse.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/example/parse.js new file mode 100644 index 0000000..abff3e8 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/example/parse.js @@ -0,0 +1,2 @@ +var argv = require('../')(process.argv.slice(2)); +console.dir(argv); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/index.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/index.js new file mode 100644 index 0000000..584f551 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/index.js @@ -0,0 +1,187 @@ +module.exports = function (args, opts) { + if (!opts) opts = {}; + + var flags = { bools : {}, strings : {} }; + + [].concat(opts['boolean']).filter(Boolean).forEach(function (key) { + flags.bools[key] = true; + }); + + [].concat(opts.string).filter(Boolean).forEach(function (key) { + flags.strings[key] = true; + }); + + var aliases = {}; + Object.keys(opts.alias || {}).forEach(function (key) { + aliases[key] = [].concat(opts.alias[key]); + aliases[key].forEach(function (x) { + aliases[x] = [key].concat(aliases[key].filter(function (y) { + return x !== y; + })); + }); + }); + + var defaults = opts['default'] || {}; + + var argv = { _ : [] }; + Object.keys(flags.bools).forEach(function (key) { + setArg(key, defaults[key] === undefined ? false : defaults[key]); + }); + + var notFlags = []; + + if (args.indexOf('--') !== -1) { + notFlags = args.slice(args.indexOf('--')+1); + args = args.slice(0, args.indexOf('--')); + } + + function setArg (key, val) { + var value = !flags.strings[key] && isNumber(val) + ? Number(val) : val + ; + setKey(argv, key.split('.'), value); + + (aliases[key] || []).forEach(function (x) { + setKey(argv, x.split('.'), value); + }); + } + + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + + if (/^--.+=/.test(arg)) { + // Using [\s\S] instead of . because js doesn't support the + // 'dotall' regex modifier. See: + // http://stackoverflow.com/a/1068308/13216 + var m = arg.match(/^--([^=]+)=([\s\S]*)$/); + setArg(m[1], m[2]); + } + else if (/^--no-.+/.test(arg)) { + var key = arg.match(/^--no-(.+)/)[1]; + setArg(key, false); + } + else if (/^--.+/.test(arg)) { + var key = arg.match(/^--(.+)/)[1]; + var next = args[i + 1]; + if (next !== undefined && !/^-/.test(next) + && !flags.bools[key] + && (aliases[key] ? !flags.bools[aliases[key]] : true)) { + setArg(key, next); + i++; + } + else if (/^(true|false)$/.test(next)) { + setArg(key, next === 'true'); + i++; + } + else { + setArg(key, flags.strings[key] ? '' : true); + } + } + else if (/^-[^-]+/.test(arg)) { + var letters = arg.slice(1,-1).split(''); + + var broken = false; + for (var j = 0; j < letters.length; j++) { + var next = arg.slice(j+2); + + if (next === '-') { + setArg(letters[j], next) + continue; + } + + if (/[A-Za-z]/.test(letters[j]) + && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { + setArg(letters[j], next); + broken = true; + break; + } + + if (letters[j+1] && letters[j+1].match(/\W/)) { + setArg(letters[j], arg.slice(j+2)); + broken = true; + break; + } + else { + setArg(letters[j], flags.strings[letters[j]] ? '' : true); + } + } + + var key = arg.slice(-1)[0]; + if (!broken && key !== '-') { + if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1]) + && !flags.bools[key] + && (aliases[key] ? !flags.bools[aliases[key]] : true)) { + setArg(key, args[i+1]); + i++; + } + else if (args[i+1] && /true|false/.test(args[i+1])) { + setArg(key, args[i+1] === 'true'); + i++; + } + else { + setArg(key, flags.strings[key] ? '' : true); + } + } + } + else { + argv._.push( + flags.strings['_'] || !isNumber(arg) ? arg : Number(arg) + ); + } + } + + Object.keys(defaults).forEach(function (key) { + if (!hasKey(argv, key.split('.'))) { + setKey(argv, key.split('.'), defaults[key]); + + (aliases[key] || []).forEach(function (x) { + setKey(argv, x.split('.'), defaults[key]); + }); + } + }); + + notFlags.forEach(function(key) { + argv._.push(key); + }); + + return argv; +}; + +function hasKey (obj, keys) { + var o = obj; + keys.slice(0,-1).forEach(function (key) { + o = (o[key] || {}); + }); + + var key = keys[keys.length - 1]; + return key in o; +} + +function setKey (obj, keys, value) { + var o = obj; + keys.slice(0,-1).forEach(function (key) { + if (o[key] === undefined) o[key] = {}; + o = o[key]; + }); + + var key = keys[keys.length - 1]; + if (o[key] === undefined || typeof o[key] === 'boolean') { + o[key] = value; + } + else if (Array.isArray(o[key])) { + o[key].push(value); + } + else { + o[key] = [ o[key], value ]; + } +} + +function isNumber (x) { + if (typeof x === 'number') return true; + if (/^0x[0-9a-f]+$/i.test(x)) return true; + return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x); +} + +function longest (xs) { + return Math.max.apply(null, xs.map(function (x) { return x.length })); +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/package.json new file mode 100644 index 0000000..09e9ec4 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/package.json @@ -0,0 +1,67 @@ +{ + "name": "minimist", + "version": "0.0.8", + "description": "parse argument options", + "main": "index.js", + "devDependencies": { + "tape": "~1.0.4", + "tap": "~0.4.0" + }, + "scripts": { + "test": "tap test/*.js" + }, + "testling": { + "files": "test/*.js", + "browsers": [ + "ie/6..latest", + "ff/5", + "firefox/latest", + "chrome/10", + "chrome/latest", + "safari/5.1", + "safari/latest", + "opera/12" + ] + }, + "repository": { + "type": "git", + "url": "git://github.com/substack/minimist.git" + }, + "homepage": "https://github.com/substack/minimist", + "keywords": [ + "argv", + "getopt", + "parser", + "optimist" + ], + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/substack/minimist/issues" + }, + "_id": "minimist@0.0.8", + "dist": { + "shasum": "857fcabfc3397d2625b8228262e86aa7a011b05d", + "tarball": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + }, + "_from": "minimist@0.0.8", + "_npmVersion": "1.4.3", + "_npmUser": { + "name": "substack", + "email": "mail@substack.net" + }, + "maintainers": [ + { + "name": "substack", + "email": "mail@substack.net" + } + ], + "directories": {}, + "_shasum": "857fcabfc3397d2625b8228262e86aa7a011b05d", + "_resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/readme.markdown b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/readme.markdown new file mode 100644 index 0000000..c256353 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/readme.markdown @@ -0,0 +1,73 @@ +# minimist + +parse argument options + +This module is the guts of optimist's argument parser without all the +fanciful decoration. + +[![browser support](https://ci.testling.com/substack/minimist.png)](http://ci.testling.com/substack/minimist) + +[![build status](https://secure.travis-ci.org/substack/minimist.png)](http://travis-ci.org/substack/minimist) + +# example + +``` js +var argv = require('minimist')(process.argv.slice(2)); +console.dir(argv); +``` + +``` +$ node example/parse.js -a beep -b boop +{ _: [], a: 'beep', b: 'boop' } +``` + +``` +$ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz +{ _: [ 'foo', 'bar', 'baz' ], + x: 3, + y: 4, + n: 5, + a: true, + b: true, + c: true, + beep: 'boop' } +``` + +# methods + +``` js +var parseArgs = require('minimist') +``` + +## var argv = parseArgs(args, opts={}) + +Return an argument object `argv` populated with the array arguments from `args`. + +`argv._` contains all the arguments that didn't have an option associated with +them. + +Numeric-looking arguments will be returned as numbers unless `opts.string` or +`opts.boolean` is set for that argument name. + +Any arguments after `'--'` will not be parsed and will end up in `argv._`. + +options can be: + +* `opts.string` - a string or array of strings argument names to always treat as +strings +* `opts.boolean` - a string or array of strings to always treat as booleans +* `opts.alias` - an object mapping string names to strings or arrays of string +argument names to use as aliases +* `opts.default` - an object mapping string argument names to default values + +# install + +With [npm](https://npmjs.org) do: + +``` +npm install minimist +``` + +# license + +MIT diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/dash.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/dash.js new file mode 100644 index 0000000..8b034b9 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/dash.js @@ -0,0 +1,24 @@ +var parse = require('../'); +var test = require('tape'); + +test('-', function (t) { + t.plan(5); + t.deepEqual(parse([ '-n', '-' ]), { n: '-', _: [] }); + t.deepEqual(parse([ '-' ]), { _: [ '-' ] }); + t.deepEqual(parse([ '-f-' ]), { f: '-', _: [] }); + t.deepEqual( + parse([ '-b', '-' ], { boolean: 'b' }), + { b: true, _: [ '-' ] } + ); + t.deepEqual( + parse([ '-s', '-' ], { string: 's' }), + { s: '-', _: [] } + ); +}); + +test('-a -- b', function (t) { + t.plan(3); + t.deepEqual(parse([ '-a', '--', 'b' ]), { a: true, _: [ 'b' ] }); + t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] }); + t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/default_bool.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/default_bool.js new file mode 100644 index 0000000..f0041ee --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/default_bool.js @@ -0,0 +1,20 @@ +var test = require('tape'); +var parse = require('../'); + +test('boolean default true', function (t) { + var argv = parse([], { + boolean: 'sometrue', + default: { sometrue: true } + }); + t.equal(argv.sometrue, true); + t.end(); +}); + +test('boolean default false', function (t) { + var argv = parse([], { + boolean: 'somefalse', + default: { somefalse: false } + }); + t.equal(argv.somefalse, false); + t.end(); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/dotted.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/dotted.js new file mode 100644 index 0000000..ef0ae34 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/dotted.js @@ -0,0 +1,16 @@ +var parse = require('../'); +var test = require('tape'); + +test('dotted alias', function (t) { + var argv = parse(['--a.b', '22'], {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}}); + t.equal(argv.a.b, 22); + t.equal(argv.aa.bb, 22); + t.end(); +}); + +test('dotted default', function (t) { + var argv = parse('', {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}}); + t.equal(argv.a.b, 11); + t.equal(argv.aa.bb, 11); + t.end(); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/long.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/long.js new file mode 100644 index 0000000..5d3a1e0 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/long.js @@ -0,0 +1,31 @@ +var test = require('tape'); +var parse = require('../'); + +test('long opts', function (t) { + t.deepEqual( + parse([ '--bool' ]), + { bool : true, _ : [] }, + 'long boolean' + ); + t.deepEqual( + parse([ '--pow', 'xixxle' ]), + { pow : 'xixxle', _ : [] }, + 'long capture sp' + ); + t.deepEqual( + parse([ '--pow=xixxle' ]), + { pow : 'xixxle', _ : [] }, + 'long capture eq' + ); + t.deepEqual( + parse([ '--host', 'localhost', '--port', '555' ]), + { host : 'localhost', port : 555, _ : [] }, + 'long captures sp' + ); + t.deepEqual( + parse([ '--host=localhost', '--port=555' ]), + { host : 'localhost', port : 555, _ : [] }, + 'long captures eq' + ); + t.end(); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/parse.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/parse.js new file mode 100644 index 0000000..8a90646 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/parse.js @@ -0,0 +1,318 @@ +var parse = require('../'); +var test = require('tape'); + +test('parse args', function (t) { + t.deepEqual( + parse([ '--no-moo' ]), + { moo : false, _ : [] }, + 'no' + ); + t.deepEqual( + parse([ '-v', 'a', '-v', 'b', '-v', 'c' ]), + { v : ['a','b','c'], _ : [] }, + 'multi' + ); + t.end(); +}); + +test('comprehensive', function (t) { + t.deepEqual( + parse([ + '--name=meowmers', 'bare', '-cats', 'woo', + '-h', 'awesome', '--multi=quux', + '--key', 'value', + '-b', '--bool', '--no-meep', '--multi=baz', + '--', '--not-a-flag', 'eek' + ]), + { + c : true, + a : true, + t : true, + s : 'woo', + h : 'awesome', + b : true, + bool : true, + key : 'value', + multi : [ 'quux', 'baz' ], + meep : false, + name : 'meowmers', + _ : [ 'bare', '--not-a-flag', 'eek' ] + } + ); + t.end(); +}); + +test('nums', function (t) { + var argv = parse([ + '-x', '1234', + '-y', '5.67', + '-z', '1e7', + '-w', '10f', + '--hex', '0xdeadbeef', + '789' + ]); + t.deepEqual(argv, { + x : 1234, + y : 5.67, + z : 1e7, + w : '10f', + hex : 0xdeadbeef, + _ : [ 789 ] + }); + t.deepEqual(typeof argv.x, 'number'); + t.deepEqual(typeof argv.y, 'number'); + t.deepEqual(typeof argv.z, 'number'); + t.deepEqual(typeof argv.w, 'string'); + t.deepEqual(typeof argv.hex, 'number'); + t.deepEqual(typeof argv._[0], 'number'); + t.end(); +}); + +test('flag boolean', function (t) { + var argv = parse([ '-t', 'moo' ], { boolean: 't' }); + t.deepEqual(argv, { t : true, _ : [ 'moo' ] }); + t.deepEqual(typeof argv.t, 'boolean'); + t.end(); +}); + +test('flag boolean value', function (t) { + var argv = parse(['--verbose', 'false', 'moo', '-t', 'true'], { + boolean: [ 't', 'verbose' ], + default: { verbose: true } + }); + + t.deepEqual(argv, { + verbose: false, + t: true, + _: ['moo'] + }); + + t.deepEqual(typeof argv.verbose, 'boolean'); + t.deepEqual(typeof argv.t, 'boolean'); + t.end(); +}); + +test('flag boolean default false', function (t) { + var argv = parse(['moo'], { + boolean: ['t', 'verbose'], + default: { verbose: false, t: false } + }); + + t.deepEqual(argv, { + verbose: false, + t: false, + _: ['moo'] + }); + + t.deepEqual(typeof argv.verbose, 'boolean'); + t.deepEqual(typeof argv.t, 'boolean'); + t.end(); + +}); + +test('boolean groups', function (t) { + var argv = parse([ '-x', '-z', 'one', 'two', 'three' ], { + boolean: ['x','y','z'] + }); + + t.deepEqual(argv, { + x : true, + y : false, + z : true, + _ : [ 'one', 'two', 'three' ] + }); + + t.deepEqual(typeof argv.x, 'boolean'); + t.deepEqual(typeof argv.y, 'boolean'); + t.deepEqual(typeof argv.z, 'boolean'); + t.end(); +}); + +test('newlines in params' , function (t) { + var args = parse([ '-s', "X\nX" ]) + t.deepEqual(args, { _ : [], s : "X\nX" }); + + // reproduce in bash: + // VALUE="new + // line" + // node program.js --s="$VALUE" + args = parse([ "--s=X\nX" ]) + t.deepEqual(args, { _ : [], s : "X\nX" }); + t.end(); +}); + +test('strings' , function (t) { + var s = parse([ '-s', '0001234' ], { string: 's' }).s; + t.equal(s, '0001234'); + t.equal(typeof s, 'string'); + + var x = parse([ '-x', '56' ], { string: 'x' }).x; + t.equal(x, '56'); + t.equal(typeof x, 'string'); + t.end(); +}); + +test('stringArgs', function (t) { + var s = parse([ ' ', ' ' ], { string: '_' })._; + t.same(s.length, 2); + t.same(typeof s[0], 'string'); + t.same(s[0], ' '); + t.same(typeof s[1], 'string'); + t.same(s[1], ' '); + t.end(); +}); + +test('empty strings', function(t) { + var s = parse([ '-s' ], { string: 's' }).s; + t.equal(s, ''); + t.equal(typeof s, 'string'); + + var str = parse([ '--str' ], { string: 'str' }).str; + t.equal(str, ''); + t.equal(typeof str, 'string'); + + var letters = parse([ '-art' ], { + string: [ 'a', 't' ] + }); + + t.equal(letters.a, ''); + t.equal(letters.r, true); + t.equal(letters.t, ''); + + t.end(); +}); + + +test('slashBreak', function (t) { + t.same( + parse([ '-I/foo/bar/baz' ]), + { I : '/foo/bar/baz', _ : [] } + ); + t.same( + parse([ '-xyz/foo/bar/baz' ]), + { x : true, y : true, z : '/foo/bar/baz', _ : [] } + ); + t.end(); +}); + +test('alias', function (t) { + var argv = parse([ '-f', '11', '--zoom', '55' ], { + alias: { z: 'zoom' } + }); + t.equal(argv.zoom, 55); + t.equal(argv.z, argv.zoom); + t.equal(argv.f, 11); + t.end(); +}); + +test('multiAlias', function (t) { + var argv = parse([ '-f', '11', '--zoom', '55' ], { + alias: { z: [ 'zm', 'zoom' ] } + }); + t.equal(argv.zoom, 55); + t.equal(argv.z, argv.zoom); + t.equal(argv.z, argv.zm); + t.equal(argv.f, 11); + t.end(); +}); + +test('nested dotted objects', function (t) { + var argv = parse([ + '--foo.bar', '3', '--foo.baz', '4', + '--foo.quux.quibble', '5', '--foo.quux.o_O', + '--beep.boop' + ]); + + t.same(argv.foo, { + bar : 3, + baz : 4, + quux : { + quibble : 5, + o_O : true + } + }); + t.same(argv.beep, { boop : true }); + t.end(); +}); + +test('boolean and alias with chainable api', function (t) { + var aliased = [ '-h', 'derp' ]; + var regular = [ '--herp', 'derp' ]; + var opts = { + herp: { alias: 'h', boolean: true } + }; + var aliasedArgv = parse(aliased, { + boolean: 'herp', + alias: { h: 'herp' } + }); + var propertyArgv = parse(regular, { + boolean: 'herp', + alias: { h: 'herp' } + }); + var expected = { + herp: true, + h: true, + '_': [ 'derp' ] + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +test('boolean and alias with options hash', function (t) { + var aliased = [ '-h', 'derp' ]; + var regular = [ '--herp', 'derp' ]; + var opts = { + alias: { 'h': 'herp' }, + boolean: 'herp' + }; + var aliasedArgv = parse(aliased, opts); + var propertyArgv = parse(regular, opts); + var expected = { + herp: true, + h: true, + '_': [ 'derp' ] + }; + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +test('boolean and alias using explicit true', function (t) { + var aliased = [ '-h', 'true' ]; + var regular = [ '--herp', 'true' ]; + var opts = { + alias: { h: 'herp' }, + boolean: 'h' + }; + var aliasedArgv = parse(aliased, opts); + var propertyArgv = parse(regular, opts); + var expected = { + herp: true, + h: true, + '_': [ ] + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +// regression, see https://github.com/substack/node-optimist/issues/71 +test('boolean and --x=true', function(t) { + var parsed = parse(['--boool', '--other=true'], { + boolean: 'boool' + }); + + t.same(parsed.boool, true); + t.same(parsed.other, 'true'); + + parsed = parse(['--boool', '--other=false'], { + boolean: 'boool' + }); + + t.same(parsed.boool, true); + t.same(parsed.other, 'false'); + t.end(); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/parse_modified.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/parse_modified.js new file mode 100644 index 0000000..21851b0 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/parse_modified.js @@ -0,0 +1,9 @@ +var parse = require('../'); +var test = require('tape'); + +test('parse with modifier functions' , function (t) { + t.plan(1); + + var argv = parse([ '-b', '123' ], { boolean: 'b' }); + t.deepEqual(argv, { b: true, _: ['123'] }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/short.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/short.js new file mode 100644 index 0000000..d513a1c --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/short.js @@ -0,0 +1,67 @@ +var parse = require('../'); +var test = require('tape'); + +test('numeric short args', function (t) { + t.plan(2); + t.deepEqual(parse([ '-n123' ]), { n: 123, _: [] }); + t.deepEqual( + parse([ '-123', '456' ]), + { 1: true, 2: true, 3: 456, _: [] } + ); +}); + +test('short', function (t) { + t.deepEqual( + parse([ '-b' ]), + { b : true, _ : [] }, + 'short boolean' + ); + t.deepEqual( + parse([ 'foo', 'bar', 'baz' ]), + { _ : [ 'foo', 'bar', 'baz' ] }, + 'bare' + ); + t.deepEqual( + parse([ '-cats' ]), + { c : true, a : true, t : true, s : true, _ : [] }, + 'group' + ); + t.deepEqual( + parse([ '-cats', 'meow' ]), + { c : true, a : true, t : true, s : 'meow', _ : [] }, + 'short group next' + ); + t.deepEqual( + parse([ '-h', 'localhost' ]), + { h : 'localhost', _ : [] }, + 'short capture' + ); + t.deepEqual( + parse([ '-h', 'localhost', '-p', '555' ]), + { h : 'localhost', p : 555, _ : [] }, + 'short captures' + ); + t.end(); +}); + +test('mixed short bool and capture', function (t) { + t.same( + parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]), + { + f : true, p : 555, h : 'localhost', + _ : [ 'script.js' ] + } + ); + t.end(); +}); + +test('short and long', function (t) { + t.deepEqual( + parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]), + { + f : true, p : 555, h : 'localhost', + _ : [ 'script.js' ] + } + ); + t.end(); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/whitespace.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/whitespace.js new file mode 100644 index 0000000..8a52a58 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/node_modules/minimist/test/whitespace.js @@ -0,0 +1,8 @@ +var parse = require('../'); +var test = require('tape'); + +test('whitespace should be whitespace' , function (t) { + t.plan(1); + var x = parse([ '-x', '\t' ]).x; + t.equal(x, '\t'); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/package.json new file mode 100644 index 0000000..14bae95 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/package.json @@ -0,0 +1,58 @@ +{ + "name": "mkdirp", + "description": "Recursively mkdir, like `mkdir -p`", + "version": "0.5.0", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "main": "./index", + "keywords": [ + "mkdir", + "directory" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/substack/node-mkdirp.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "dependencies": { + "minimist": "0.0.8" + }, + "devDependencies": { + "tap": "~0.4.0", + "mock-fs": "~2.2.0" + }, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/substack/node-mkdirp/issues" + }, + "homepage": "https://github.com/substack/node-mkdirp", + "_id": "mkdirp@0.5.0", + "dist": { + "shasum": "1d73076a6df986cd9344e15e71fcc05a4c9abf12", + "tarball": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz" + }, + "_from": "mkdirp@0.5.0", + "_npmVersion": "1.4.3", + "_npmUser": { + "name": "substack", + "email": "mail@substack.net" + }, + "maintainers": [ + { + "name": "substack", + "email": "mail@substack.net" + } + ], + "directories": {}, + "_shasum": "1d73076a6df986cd9344e15e71fcc05a4c9abf12", + "_resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/readme.markdown b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/readme.markdown new file mode 100644 index 0000000..3cc1315 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/readme.markdown @@ -0,0 +1,100 @@ +# mkdirp + +Like `mkdir -p`, but in node.js! + +[![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp) + +# example + +## pow.js + +```js +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); +``` + +Output + +``` +pow! +``` + +And now /tmp/foo/bar/baz exists, huzzah! + +# methods + +```js +var mkdirp = require('mkdirp'); +``` + +## mkdirp(dir, opts, cb) + +Create a new directory and any necessary subdirectories at `dir` with octal +permission string `opts.mode`. If `opts` is a non-object, it will be treated as +the `opts.mode`. + +If `opts.mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +`cb(err, made)` fires with the error or the first directory `made` +that had to be created, if any. + +You can optionally pass in an alternate `fs` implementation by passing in +`opts.fs`. Your implementation should have `opts.fs.mkdir(path, mode, cb)` and +`opts.fs.stat(path, cb)`. + +## mkdirp.sync(dir, opts) + +Synchronously create a new directory and any necessary subdirectories at `dir` +with octal permission string `opts.mode`. If `opts` is a non-object, it will be +treated as the `opts.mode`. + +If `opts.mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +Returns the first directory that had to be created, if any. + +You can optionally pass in an alternate `fs` implementation by passing in +`opts.fs`. Your implementation should have `opts.fs.mkdirSync(path, mode)` and +`opts.fs.statSync(path)`. + +# usage + +This package also ships with a `mkdirp` command. + +``` +usage: mkdirp [DIR1,DIR2..] {OPTIONS} + + Create each supplied directory including any necessary parent directories that + don't yet exist. + + If the directory already exists, do nothing. + +OPTIONS are: + + -m, --mode If a directory needs to be created, set the mode as an octal + permission string. + +``` + +# install + +With [npm](http://npmjs.org) do: + +``` +npm install mkdirp +``` + +to get the library, or + +``` +npm install -g mkdirp +``` + +to get the command. + +# license + +MIT diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/chmod.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/chmod.js new file mode 100644 index 0000000..520dcb8 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/chmod.js @@ -0,0 +1,38 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +test('chmod-pre', function (t) { + var mode = 0744 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.equal(stat && stat.mode & 0777, mode, 'should be 0744'); + t.end(); + }); + }); +}); + +test('chmod', function (t) { + var mode = 0755 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.end(); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/clobber.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/clobber.js new file mode 100644 index 0000000..0eb7099 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/clobber.js @@ -0,0 +1,37 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +// a file in the way +var itw = ps.slice(0, 3).join('/'); + + +test('clobber-pre', function (t) { + console.error("about to write to "+itw) + fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.'); + + fs.stat(itw, function (er, stat) { + t.ifError(er) + t.ok(stat && stat.isFile(), 'should be file') + t.end() + }) +}) + +test('clobber', function (t) { + t.plan(2); + mkdirp(file, 0755, function (err) { + t.ok(err); + t.equal(err.code, 'ENOTDIR'); + t.end(); + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/mkdirp.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/mkdirp.js new file mode 100644 index 0000000..3b624dd --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/mkdirp.js @@ -0,0 +1,26 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('woo', function (t) { + t.plan(5); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }) + }) + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/opts_fs.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/opts_fs.js new file mode 100644 index 0000000..f1fbeca --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/opts_fs.js @@ -0,0 +1,27 @@ +var mkdirp = require('../'); +var path = require('path'); +var test = require('tap').test; +var mockfs = require('mock-fs'); + +test('opts.fs', function (t) { + t.plan(5); + + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/beep/boop/' + [x,y,z].join('/'); + var xfs = mockfs.fs(); + + mkdirp(file, { fs: xfs, mode: 0755 }, function (err) { + t.ifError(err); + xfs.exists(file, function (ex) { + t.ok(ex, 'created file'); + xfs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/opts_fs_sync.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/opts_fs_sync.js new file mode 100644 index 0000000..224b506 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/opts_fs_sync.js @@ -0,0 +1,25 @@ +var mkdirp = require('../'); +var path = require('path'); +var test = require('tap').test; +var mockfs = require('mock-fs'); + +test('opts.fs sync', function (t) { + t.plan(4); + + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/beep/boop/' + [x,y,z].join('/'); + var xfs = mockfs.fs(); + + mkdirp.sync(file, { fs: xfs, mode: 0755 }); + xfs.exists(file, function (ex) { + t.ok(ex, 'created file'); + xfs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/perm.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/perm.js new file mode 100644 index 0000000..2c97590 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/perm.js @@ -0,0 +1,30 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('async perm', function (t) { + t.plan(5); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16); + + mkdirp(file, 0755, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }) + }) + }); +}); + +test('async root perm', function (t) { + mkdirp('/tmp', 0755, function (err) { + if (err) t.fail(err); + t.end(); + }); + t.end(); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/perm_sync.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/perm_sync.js new file mode 100644 index 0000000..327e54b --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/perm_sync.js @@ -0,0 +1,34 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('sync perm', function (t) { + t.plan(4); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json'; + + mkdirp.sync(file, 0755); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); +}); + +test('sync root perm', function (t) { + t.plan(3); + + var file = '/tmp'; + mkdirp.sync(file, 0755); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.ok(stat.isDirectory(), 'target not a directory'); + }) + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/race.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/race.js new file mode 100644 index 0000000..7c295f4 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/race.js @@ -0,0 +1,40 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('race', function (t) { + t.plan(6); + var ps = [ '', 'tmp' ]; + + for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); + } + var file = ps.join('/'); + + var res = 2; + mk(file, function () { + if (--res === 0) t.end(); + }); + + mk(file, function () { + if (--res === 0) t.end(); + }); + + function mk (file, cb) { + mkdirp(file, 0755, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + if (cb) cb(); + }); + }) + }); + } +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/rel.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/rel.js new file mode 100644 index 0000000..d1f175c --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/rel.js @@ -0,0 +1,30 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('rel', function (t) { + t.plan(5); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var cwd = process.cwd(); + process.chdir('/tmp'); + + var file = [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + process.chdir(cwd); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }) + }) + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/return.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/return.js new file mode 100644 index 0000000..bce68e5 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/return.js @@ -0,0 +1,25 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(4); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, '/tmp/' + x); + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, null); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/return_sync.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/return_sync.js new file mode 100644 index 0000000..7c222d3 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/return_sync.js @@ -0,0 +1,24 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + // Note that this will throw on failure, which will fail the test. + var made = mkdirp.sync(file); + t.equal(made, '/tmp/' + x); + + // making the same file again should have no effect. + made = mkdirp.sync(file); + t.equal(made, null); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/root.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/root.js new file mode 100644 index 0000000..97ad7a2 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/root.js @@ -0,0 +1,18 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('root', function (t) { + // '/' on unix, 'c:/' on windows. + var file = path.resolve('/'); + + mkdirp(file, 0755, function (err) { + if (err) throw err + fs.stat(file, function (er, stat) { + if (er) throw er + t.ok(stat.isDirectory(), 'target is a directory'); + t.end(); + }) + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/sync.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/sync.js new file mode 100644 index 0000000..88fa432 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/sync.js @@ -0,0 +1,30 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('sync', function (t) { + t.plan(4); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file, 0755); + } catch (err) { + t.fail(err); + return t.end(); + } + + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/umask.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/umask.js new file mode 100644 index 0000000..82c393a --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/umask.js @@ -0,0 +1,26 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('implicit mode from umask', function (t) { + t.plan(5); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, 0777 & (~process.umask())); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }) + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/umask_sync.js b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/umask_sync.js new file mode 100644 index 0000000..e537fbe --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/node_modules/mkdirp/test/umask_sync.js @@ -0,0 +1,30 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; + +test('umask sync modes', function (t) { + t.plan(4); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file); + } catch (err) { + t.fail(err); + return t.end(); + } + + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & 0777, (0777 & (~process.umask()))); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/mocha/package.json b/node_modules/promises-aplus-tests/node_modules/mocha/package.json new file mode 100644 index 0000000..f259830 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/mocha/package.json @@ -0,0 +1,127 @@ +{ + "name": "mocha", + "version": "1.21.5", + "description": "simple, flexible, fun test framework", + "keywords": [ + "mocha", + "test", + "bdd", + "tdd", + "tap" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "contributors": [ + { + "name": "Joshua Appelman", + "email": "joshua@jbna.nl" + }, + { + "name": "Oleg Gaidarenko", + "email": "markelog@gmail.com" + }, + { + "name": "Christoffer Hallas", + "email": "christoffer.hallas@gmail.com" + }, + { + "name": "Christopher Hiller", + "email": "chiller@badwing.com" + }, + { + "name": "Travis Jeffery", + "email": "tj@travisjeffery.com" + }, + { + "name": "Johnathan Ong", + "email": "me@jongleberry.com" + }, + { + "name": "Guillermo Rauch", + "email": "rauchg@gmail.com" + } + ], + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/mocha.git" + }, + "main": "./index", + "browser": "./mocha.js", + "bin": { + "mocha": "./bin/mocha", + "_mocha": "./bin/_mocha" + }, + "engines": { + "node": ">= 0.4.x" + }, + "scripts": { + "test": "make test-all" + }, + "dependencies": { + "commander": "2.3.0", + "debug": "2.0.0", + "diff": "1.0.8", + "escape-string-regexp": "1.0.2", + "glob": "3.2.3", + "growl": "1.8.1", + "jade": "0.26.3", + "mkdirp": "0.5.0" + }, + "devDependencies": { + "coffee-script": "~1.8.0", + "should": "~4.0.0" + }, + "files": [ + "bin", + "images", + "lib", + "index.js", + "mocha.css", + "mocha.js", + "LICENSE" + ], + "licenses": [ + { + "type": "MIT", + "url": "https://raw.github.com/visionmedia/mocha/master/LICENSE" + } + ], + "bugs": { + "url": "https://github.com/visionmedia/mocha/issues" + }, + "homepage": "https://github.com/visionmedia/mocha", + "_id": "mocha@1.21.5", + "_shasum": "7c58b09174df976e434a23b1e8d639873fc529e9", + "_from": "mocha@>=1.21.4 <1.22.0", + "_npmVersion": "1.4.9", + "_npmUser": { + "name": "travisjeffery", + "email": "tj@travisjeffery.com" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "travisjeffery", + "email": "tj@travisjeffery.com" + }, + { + "name": "markelog", + "email": "markelog@gmail.com" + }, + { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + } + ], + "dist": { + "shasum": "7c58b09174df976e434a23b1e8d639873fc529e9", + "tarball": "http://registry.npmjs.org/mocha/-/mocha-1.21.5.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.5.tgz" +} diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/AUTHORS b/node_modules/promises-aplus-tests/node_modules/sinon/AUTHORS new file mode 100644 index 0000000..d88fe23 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/AUTHORS @@ -0,0 +1,165 @@ +Christian Johansen +Morgan Roderick +Maximilian Antoni +ben hockey +Tim Fischbach +Max Antoni +Tim Ruffles +Jonathan Sokolowski +Phred +Domenic Denicola +William Sears +Andreas Lind +Tim Ruffles +Tim Perry +pimterry +Felix Geisendörfer +kpdecker +Bryan Donovan +Andrew Gurinovich +Martin Sander +Luis Cardoso +Keith Cirkel +Tristan Koch +Tobias Ebnöther +Cory +Christian Johansen +Marten Lienen +Travis Kaufman +Benjamin Coe +ben fleis +Garrick Cheung +Gavin Huang +Jonny Reeves +Konrad Holowinski +zcicala +Duncan Beevers +Jmeas +August Lilleaas +Scott Andrews +Robin Pedersen +Garrick +Carl-Erik Kopseng +geries +Cormac Flynn +Ming Liu +Thomas Meyer +Roman Potashow +Tamas Szebeni +Glen Mailer +Soutaro Matsumoto +なつき +Alex Urbano +Alexander Schmidt +Ben Hockey +Brandon Heyer +Christian Johansen +Devin Weaver +Farid Neshat +G.Serebryanskyi +Henry Tung +Irina Dumitrascu +James Barwell +Jason Karns +Jeffrey Falgout +Jeffrey Falgout +Jonathan Freeman +Josh Graham +Marcus Hüsgen +Martin Hansen +Matt Kern +Max Calabrese +Márton Salomváry +Satoshi Nakamura +Simen Bekkhus +Spencer Elliott +Travis Kaufman +Victor Costan +gtothesquare +mohayonao +vitalets +yoshimura-toshihide +Ian Thomas +jamestalmage +Mario Pareja +Ian Lewis +Martin Brochhaus +kbackowski +Harry Wolff +Adrian Phinney +Gyandeep Singh +AJ Ortega +Max Klymyshyn +Gordon L. Hempton +Michael Jackson +Mikolaj Banasik +Gord Tanner +Glen Mailer +Mustafa Sak +ngryman +Nicholas Stephan +Nikita Litvin +Niklas Andreasson +Olmo Maldonado +Giorgos Giannoutsos +Rajeesh C V +Raynos +Gilad Peleg +Rodion Vynnychenko +Gavin Boulton +Ryan Wholey +Adam Hull +Felix Geisendörfer +Sergio Cinos +Shawn Krisman +Shawn Krisman +Shinnosuke Watanabe +simonzack +Simone Fonda +Eric Wendelin +stevesouth +Sven Fuchs +Søren Enemærke +TEHEK Firefox +Dmitriy Kubyshkin +Tek Nynja +Daryl Lau +Tim Branyen +Christian Johansen +Burak Yiğit Kaya +Brian M Hunt +Blake Israel +Timo Tijhof +Blake Embrey +Blaine Bublitz +till +Antonio D'Ettole +Tristan Koch +AJ Ortega +Volkan Ozcelik +Will Butler +William Meleyal +Ali Shakiba +Xiao Ma +Alfonso Boza +Alexander Aivars +brandonheyer +charlierudolph +Alex Kessaris +goligo +John Bernardo +wwalser +Jason Anderson +Jan Suchý +Jordan Hawker +Joseph Spens +hashchange +Jan Kopriva +Kevin Turner +Kim Joar Bekkelund +James Beavers +Kris Kowal +Kurt Ruppel +Lars Thorup +Luchs +Marco Ramirez diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/CONTRIBUTING.md b/node_modules/promises-aplus-tests/node_modules/sinon/CONTRIBUTING.md new file mode 100644 index 0000000..43b68bd --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/CONTRIBUTING.md @@ -0,0 +1,150 @@ +# Contributing to Sinon.JS + +There are several ways of contributing to Sinon.JS + +* Help [improve the documentation](https://github.com/sinonjs/sinon-docs) published at [the Sinon.JS website](http://sinonjs.org) +* Help someone understand and use Sinon.JS on the [the mailing list](http://groups.google.com/group/sinonjs) +* Report an issue, please read instructions below +* Help with triaging the [issues](http://github.com/cjohansen/Sinon.JS/issues). The clearer they are, the more likely they are to be fixed soon. +* Contribute to the code base. + +## Reporting an issue + +To save everyone time and make it much more likely for your issue to be understood, worked on and resolved quickly, it would help if you're mindful of [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) when pressing the "Submit new issue" button. + +As a minimum, please report the following: + +* Which environment are you using? Browser? Node? Which version(s)? +* Which version of SinonJS? +* How are you loading SinonJS? +* What other libraries are you using? +* What you expected to happen +* What actually happens +* Describe **with code** how to reproduce the faulty behaviour + +### Bug report template + +Here's a template for a bug report + +> Sinon version : +> Environment : +> Example URL : +> +> ##### Bug description + +Here's an example use of the template + +> Sinon version : 1.10.3 +> Environment : OSX Chrome 37.0.2062.94 +> Example URL : http://jsbin.com/iyebiWI/8/edit +> +> ##### Bug description +> +> If `respondWith` called with a URL including query parameter and a function , it doesn't work. +> This error reported in console. +> ``` +> `TypeError: requestUrl.match(...) is null` +> ``` + +## Contributing to the code base + +Pick [an issue](http://github.com/cjohansen/Sinon.JS/issues) to fix, or pitch +new features. To avoid wasting your time, please ask for feedback on feature +suggestions either with [an issue](http://github.com/cjohansen/Sinon.JS/issues/new) +or on [the mailing list](http://groups.google.com/group/sinonjs). + +### Making a pull request + +Please try to [write great commit messages](http://chris.beams.io/posts/git-commit/). + +There are numerous benefits to great commit messages + +* They allow Sinon.JS users to easily understand the consequences of updating to a newer version +* They help contributors understand what is going on with the codebase, allowing features and fixes to be developed faster +* They save maintainers time when compiling the changelog for a new release + +If you're already a few commits in by the time you read this, you can still [change your commit messages](https://help.github.com/articles/changing-a-commit-message/). + +Also, before making your pull request, consider if your commits make sense on their own (and potentially should be multiple pull requests) or if they can be squashed down to one commit (with a great message). There are no hard and fast rules about this, but being mindful of your readers greatly help you author good commits. + +### Use EditorConfig + +To save everyone some time, please use [EditorConfig](http://editorconfig.org), so your editor helps make +sure we all use the same encoding, indentation, line endings, etc. + +### Installation + +The Sinon.JS developer environment requires Node/NPM. Please make sure you have +Node installed, and install Sinon's dependencies: + + $ npm install + +This will also install a pre-commit hook, that runs style validation on staged files. + +#### PhantomJS + +In order to run the tests, you'll need a [PhantomJS](http://phantomjs.org) global. + +The test suite runs well with both `1.9.x` and `2.0.0` + +### Style + +Sinon.JS uses [ESLint](http://eslint.org) to keep consistent style. You probably want to install a plugin for your editor. + +The ESLint test will be run before unit tests in the CI environment, your build will fail if it doesn't pass the style check. + +``` +$ npm run lint +``` + +To ensure consistent reporting of lint warnings, you should use the same version as CI environment (defined in `package.json`) + +### Run the tests + +This runs linting as well as unit tests in both PhantomJS and node + + $ npm test + +##### Testing in development + +Sinon.JS uses [Buster.JS](http://busterjs.org), please read those docs if you're unfamiliar with it. + +If you just want to run tests a few times + + $ npm run ci-test + +If you're doing more than a one line edit, you'll want to have finer control and less restarting of the Buster server and PhantomJS process + + # start a server + $ $(npm bin)/buster-server + + # capture a browser by pointing it to http://localhost:1111/capture + # run tests (in both browser and node) + $ $(npm bin)/buster-test + + # run tests only in browser + $ $(npm bin)/buster-test --config-group browser + + # run tests only in node + $ $(npm bin)/buster-test --config-group node + +If you install `Buster.JS` as a global, you can remove `$(npm-bin)/` from the lines above. + +##### Testing a built version + +To test against a built distribution, first +make sure you have a build (requires [Ruby][ruby] and [Juicer][juicer]): + + $ ./build + +[ruby]: https://www.ruby-lang.org/en/ +[juicer]: http://rubygems.org/gems/juicer + +If the build script is unable to find Juicer, try + + $ ruby -rubygems build + +Once built, you can run the tests against the packaged version as part of the `ci-test.sh` script. + + ./scripts/ci-test.sh + diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/Changelog.txt b/node_modules/promises-aplus-tests/node_modules/sinon/Changelog.txt new file mode 100644 index 0000000..c8bf203 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/Changelog.txt @@ -0,0 +1,536 @@ + +1.17.2 / 2015-10-21 +================== + + * Fix #867: Walk properties only once + +1.17.1 / 2015-09-26 +================== + + * Fix #851: Do not attempt to re-stub constructors + * Fix #847: Ensure walk invokes accessors directly on target + * Run tests in node 4.1.x also + +1.17.0 / 2015-09-22 +================== + + * Fix #821 where Sinon.JS would leak a setImmdiate into global scope + * Removed sinon-timers from the build. refs #811 + * Added flag that, when set to true, makes sinon.logError throw errors synchronously. + * Fix #777: Support non-enumerable props when stubbing objects + * Made the sinon.test() function pass on errors to the callback + * Expand conversion from ArrayBuffer to binary string + * Add support for ArrayBuffer, blob responseTypes + +1.16.1 / 2015-08-20 +=================== +* Bump Lolex to stop throwing an error when faking Date but not setTimeout + +1.16.0 / 2015-08-19 +=================== +* Capture the stack on each spy call +* fakeServer.create accepts configuration settings +* Update Lolex to 1.3.0 +* Fire onreadystatechange with event argument +* Returns supersedes previous throws +* Bunch of bug fixes + +1.15.0 / 2015-05-30 +================== +* Fixed bug where assertions don't handle functions that have a property named proxy +* update license attribute +* Add test coverage report +* responseHeaders on abort are empty object +* Fix pre-existing style error +* Update documentation to cover testing built version +* Update CONTRIBUTING.md with section about "Making a pull request" +* Improve RELEASE.md to reduce effort when cutting a new release +* Deprecate mock +* Release.md +* Make `npm docs sinon` work. +* Run unit tests against packaged version in CI environment +* Remove unused Gruntfile +* Use Vanilla Buster.JS +* Use `files` in package.json +* Fix code style +* Don't stub getter properties +* Event listeners for `progress`, `load` and `readystatechange` in the `readyStateChange` function in `FakeXMLHttpRequest` are dispatched in a different order in comparison to a browser. Reorder the events dispatched to reflect general browser behaviour. +* Update linting instructions in CONTRIBUTING.md +* Lint all files with new linter +* Update JSCS to 1.11.3 and make npm lint task verify all files +* Cleanup .restore() + +== 1.14.1 / 2015-03-16 +* Fallback for .restore() native code functions on Chrome & PhantomJS (なつき) +* Restore support for sinon in IE<9 (Harry Wolff) + +== 1.14.0 / 2015-03-13 +* Stub & spy getters & setters (Simon Zack) +* Fix #702 async sinon.test using mocha interface (Mohayonao) +* Add respondImmediately to fake servers (Jonathan Freeman) + +== 1.13.0 / 2015-03-04 +* fix @depends-require mismatches (fixes AMD issues) (Ben Hockey) +* Fix spy.calledWith(undefined) to return false if it was called without args +* yieldsRight (Alexander Schmidt) +* stubs retain function arity (Charlie Rudolph) +* (AMD) use explicit define in built version +* spy().reset() returns this (Ali Shakiba) +* Add lengthComputable and download progress (Tamas Szebeni) +* Don't setContent-type when sending FormData (AJ Ortega) +* sinon.assert with spyCall (Alex Urbano) +* fakeXHR requests in Node. (Jmeas) +* Enhancement: run builds on docker (till@php.net) +* Use FakeXDomainRequest when XHR does not support CORS. Fixes #584 (Eric Wendelin) +* More lenient check for ActiveXObject +* aligned sandbox.useFakeXMLHttpRequest API to documentation (Phred) +* Fix #643. Returns supersedes previous throws (Adam Hull) +* Safely overwrite properties in IE - no more IE files! +* Add check for setInterval/clearInterval (kdpecker) +* Add safety check for document.createElement (kdpecker) +* Fix #633. Use a try/catch when deleting a property in IE8. (Garrick Cheung) + +== 1.12.1 / 2014-11-16 +* Fixed lolex issue on node + +== 1.12.0 / 2014-11-16 +* Fake timers are now extracted as lolex: http://github.com/sinonjs/lolex +* Improved setImmediate fake +* Proper AMD solution + +== 1.11.1 / 2014-10-27 + +* Expose match on returned sandbox (Duncan Beevers) +* Fix issue #586 (Antonio D'Ettole) +* Declare log_error dependency (Kurt Ruppel) + +== 1.11.0 / 2014-10-26 + +* Proper AMD support +* Don't call sinon.expectation.pass if there aren't any expectations (Jeffrey Falgout) +* Throw error when reset-ing while calling fake +* Added xhr.response property (Gyandeep Singh) +* Fixed premature sandbox destruction (Andrew Gurinovich) +* Add sandbox reset method (vitalets) +* A bunch of bug fixes (git log) +* Various source organizational improvements (Morgan Roderick and others) + +== 1.10.3 / 2014-07-11 + +* Fix loading in Web Workers (Victor Costan) +* Allow null as argument to clearTimeout and clearInterval (Lars Thorup) + +== 1.10.2 / 2014-06-02 + +* Fix `returnValue` and `exception` regression on spy calls (Maximilian Antoni) + +== 1.10.1 / 2014-05-30 + +* Improved mocha compatibility for async tests (Ming Liu) +* Make the fakeServer log function overloadable (Brian M Hunt) + +== 1.10.0 / 2014-05-19 + +* Ensure that spy createCallProperties is set before function invocation (James Barwell) +* XDomainRequest support (Søren Enemærke, Jonathan Sokolowski) +* Correct AMD behavior (Tim Branyen) +* Allow explicit naming of spies and stubs (Glen Mailer) +* deepEqual test for unequal objects in calledWithExactly (Bryan Donovan) +* Fix clearTimeout() for Node.js (Xiao Ma) +* fix fakeServer.respond() in IE8 (John Bernardo) +* Fix #448 (AMD require.amd) +* Fix wrapMethod error handling (Nikita Litvin) + +== 1.9.1 / 2014-04-03 + +* Fix an issue passing `NaN` to `calledWith` (Blake Israel) +* Explicate dependency on util package (Kris Kowal) +* Fake timers return an object with `ref` and `unref` properties on Node (Ben Fleis) + +== 1.9.0 / 2014-03-05 + +* Add sinon.assert.match (Robin Pedersen) +* Added ProgressEvent and CustomEvent. Fixes bug with progress events on IE. (Geries Handal) +* prevent setRequestHeaders from being called twice (Phred) +* Fix onload call, 'this' should be equal to XHR object (Niklas Andreasson) +* Remove sandbox injected values on restore (Marcus Hüsgen) +* Coerce matcher.or/and arguments into matchers (Glen Mailer) +* Don't use buster.format any more +* Fix comparison for regexp deepEqual (Matt Kern) + +== 1.8.2 / 2014-02-11 + +* Fixes an edge case with calledWithNew and spied native functions, and other + functions that lack a .prototype +* Add feature detection for the new ProgressEvent support + +== 1.8.1 / 2014-02-02 + +* Screwed up NPM release of 1.8.0, unable to replace it + +== 1.8.0 / 2014-02-02 + +* Add clearImmediate mocking support to the timers API (Tim Perry) +* Mirror custom Date properties when faking time +* Improved Weinre support +* Update call properties even if exceptions are thrown (Tim Perry) +* Update call properties even if exceptions are thrown (Tim Perry) +* Reverse matching order for fake server (Gordon L. Hempton) +* Fix restoring globals on another frame fails on Firefox (Burak Yiğit Kaya) +* Handle stubbing falsey properties (Tim Perry) +* Set returnValues correctly when the spied function is called as a constructor (Tim Perry) +* When creating a sandbox, do not overwrite existing properties when inject + properties into an object (Sergio Cinos) +* Added withCredentials property to fake xhr (Geries) +* Refine behavior withArgs error message (Tim Fischbach) +* Auto-respond to successive requests made with a single XHR object (Jan Suchý) +* Add the ability for mock to accept sinon.match matchers as expected arguments (Zcicala) +* Adding support for XMLHttpRequest.upload to FakeXMLHttpRequest (Benjamin Coe) +* Allow onCall to be combined with returns* and throwsException in stub behavior + sequences (Tim Fischbach) +* Fixed deepEqual to detect properties on array objects +* Fixed problem with chained timers with delay=0 (Ian Lewis) +* Use formatio in place of buster-format (Devin Weaver) + +== 1.7.3 / 2013-06-20 + +* Removed use of array forEach, breaks in older browsers (Martin Hansen) +* sinon.deepEqual(new Date(0), new Date()) returns true (G.Serebryanskyi) + +== 1.7.2 / 2013-05-08 + +* Sinon 1.7 has split calls out to a separate file. This caused some problems, + so 1.7.2 ships with spyCall as part of spy.js like it used to be. + +== 1.7.1 / 2013-05-07 + +* Fake XMLHttpRequest updated to call onerror and onsuccess callbacks, fixing + jQuery 2.0 problems (Roman Potashow) +* Implement XMLHttpRequest progress event api (Marten Lienen) +* Added sinon.restore() (Jonny Reeves) +* Fix bug where throwing a string was handled incorrectly by Sinon (Brandon Heyer) +* Web workers support (Victor Costan) + +== 1.7.0 + +* Failed release, see 1.7.1 + +== 1.6.0 / 2013-02-18 +* Add methods to spyCall interface: callArgOn, callArgOnWith, yieldOn, + yieldToOn (William Sears) +* sinon.createStubInstance creates a fully stubbed instance from a constructor + (Shawn Krisman) +* resetBehavior resets fakes created by withArgs (Martin Sander) +* The fake server now logs to sinon.log, if set (Luis Cardoso) +* Cleaner npm package that also includes pkg/sinon.js and + pkg/sinon-ie.js for cases where npm is used to install Sinon for + browser usage (Domenic Denicola) +* Improved spy formatter %C output (Farid Neshat) +* clock.tick returns clock.now (Michael Jackson) +* Fixed issue #248 with callOrder assertion + Did not fail if the last given spy was never called (Maximilian Antoni) +* Fixed issue with setResponseHeader for synchronous requests (goligo) +* Remove msSetImmediate; it only existed in IE10 previews (Domenic Denicola) +* Fix #231: not always picking up the latest calls to callsArgWith, etc. + (Domenic Denicola) +* Fix failing anonymous mock expectations + +== 1.5.2 / 2012-11-28 +* Revert stub.reset changes that caused existing tests to fail. + +== 1.5.1 / 2012-11-27 +* Ensure window.Image can be stubbed. (Adrian Phinney) +* Fix spy() in IE 8 (Scott Andrews) +* Fix sinon base in IE 8 (Scott Andrews) +* Format arguments ouput when mock excpetation is not met (kbackowski) +* Calling spy.reset directly from stub.reset (Thomas Meyer) + +== 1.5.0 / 2012-10-19 +* Don't force "use strict" on Sinon consumers +* Don't assume objects have hasOwnProperties. Fixes problem with e.g. + stubbing properties on process.env +* Preserve function length for spy (Maximilian Antoni) +* Add 'invokeCallback' alias for 'yield' on calls (Maximilian Antoni) +* Added matcher support for calledOn (Maximilian Antoni) +* Retain original expectation messages, for failed mocks under sinon.test + (Giorgos Giannoutsos) +* Allow yields* and callsArg* to create sequences of calls. (Domenic Denicola) +* sinon.js can catch itself in endless loop while filling stub prototype + with asynch methods (Jan Kopriva) + +== 1.4.2 / 2012-07-11 +* sinon.match for arrays (Maximilian Antoni) + +== 1.4.1 / 2012-07-11 +* Strengthen a Node.JS inference to avoid quirky behavior with Mocha + (which provides a shim process object) + +== 1.4.0 / 2012-07-09 +* Argument matchers (Maximillian Antoni) + sinon.match.{any, same, typeOf, instanceOf, has, hasOwn, defined, truthy, + falsy} as well as typeOf shortcuts for boolean, number, string, object, + function, array, regexp and date. The result of a call can be used with + spy.calledWith. +* spy.returned now works with matchers and compares objects deeply. +* Matcher assertions: calledWithMatch, alwaysCalledWithMatch and + neverCalledWithMatch +* calledWithNew and alwaysCalledWithNew for assert (Maximilian Antoni) +* Easier stubbed fluent interfaces: stub.returnsThis() (Glen Mailer) +* allow yields() and family to be used along with returns()/throws() and + family (Glen Mailer) +* Async versions `callsArg*` and `yields*` for stubs (TEHEK) +* Format args when doing `spy.printf("%*")` (Domenic Denicola) +* Add notCalled property to spies +* Fix: spy.reset did not reset fakes created by spy.withArgs (Maximilian Antoni) +* Properly restore stubs when stubbing entire objects through the sandbox + (Konrad Holowinski) +* Restore global methods properly - delete properties that where not own + properties (Keith Cirkel) +* setTimeout and setInterval pass arguments (Rodion Vynnychenko) +* Timer callbacks that contain a clock.tick no longer fails (Wesley Waser) +* spy(undefined, "property") now throws +* Prevent multiple restore for fake timers (Kevin Decker) +* Fix toString format under Node (Kevin Decker) +* Mock expectations emit success and failure events (Kevin Decker) +* Development improvement: Use Buster.JS to test Sinon +* Fix bug where expect.atLeast failed when minimum calls where received +* Make fake server safe to load on node.js +* Add support for no args on .withArgs and .withExactArgs (Tek Nynja) +* Avoid hasOwnProperty for host objects + +== 1.3.2 / 2012-03-11 +* Stronger Node inference in sandbox +* Fixed issue with sinon.useFakeTimers() and Rhino.js 1.7R3 +* Formatting brush-up +* FIX Internet Explorer misreporting the type of Function objects + originating in an external window as "object" instead of "function". +* New methods stub.callsArgOn, stub.callsArgOnWith, + stub.yieldsOn, stub.yieldsToOn +* Implemented +* Fixing `clearTimeout` to not throw when called for nonexistant IDs. +* Spys that are created using 'withArgs' now get initialized with previous + calls to the original spy. +* Minor bug fixes and docs cleanup. + +== 1.3.1 / 2012-01-04 +* Fix bug in core sinon: isNode was used for both a variable and a function, + causing load errors and lots of bugs. Should have never left the door. + +== 1.3.0 / 2012-01-01 +* Support using bare functions as fake server response handlers (< 1.3.0 + required URL and/or method matcher too) +* Log some internal errors to sinon.log (defaults to noop). Set sinon.log + to your logging utility of choice for better feedback when. +* White-list fake XHRs: Allows some fake requests and some that fall through to + the backend server (Tim Ruffles) +* Decide Date.now support at fake-time. Makes it possible to load something that + polyfills Date.now after Sinon loaded and still have Date.now on fake Dates. +* Mirror properties on replaced function properties +* New methods: spy.yield(), spy.yieldTo(), spy.callArg() and spy.callArgWith() + can be used to invoke callbacks passed to spies (while avoiding the mock-like + upfront yields() and friends). invokeCallback is available as an alias for + yield for people working with strict mode. (Maximilian Antoni) +* New properties: spy.firstCall, spy.secondCall, spy.thirdCall and spy.lastCall. + (Maximilian Antoni) +* New method: stub.returnsArg(), causes stub to return one of its arguments. + (Gavin Huang) +* Stubs now work for inherited methods. This was previously prohibited to avoid + stubbing not-yet-implemented methods. (Felix Geisendörfer) +* server.respond() can now accept the same arguments as server.respondWith() for + quick-and-dirty respondWith+respond. (Gavin Huang) +* Format objects with buster-format in the default bundle. Default to + util.inspect on node unless buster-format is available (not a hard dependency, + more like a 'preference'). + +* Bug fix: Make sure XHRs can complete even if onreadystatechange handler fails +* Bug fix: Mirror entire Date.prototype, including toUTCString when faking +* Bug fix: Default this object to global in exposed asserts +* Bug fix: sinon.test: use try/finally instead of catch and throw - preserves + stack traces (Kevin Turner) +* Bug fix: Fake `setTimeout` now returns ids greater than 0. (Domenic Denicola) +* Bug fix: NPM install warning (Felix Geisendörfer) +* Bug fix: Fake timers no longer swallows exceptions (Felix Geisendörfer) +* Bug fix: Properly expose all needed asserts for node +* Bug fix: wrapMethod on window property (i.e. when stubbing/spying on global + functions) +* Bug fix: Quote "yield" (Ben Hockey) +* Bug fix: callOrder works correctly when spies have been called multiple times + +== 1.2.0 / 2011-09-27 +* Bug fix: abort() switches state to DONE when OPENED and sent. Fix by + Tristan Koch. +* Bug fix: Mootools uses MSXML2.XMLHTTP as objectId, which Sinon matched with + different casing. Fix by Olmo Maldonado. +* Bug fix: When wrapping a non-owned property, restore now removes the wrapper + instead of replacing it. Fix by Will Butler. +* Bug fix: Make it possibly to stub Array.prototype.push by not using that + method directly inside Sinon. +* Bug fix: Don't assume that req.requestBody is a string in the fake server. +* Added spy.printf(format) to print a nicely formatted message with details + about a spy. +* Garbage collection: removing fakes from collections when restoring the + original methods. Fix by Tristan Koch. +* Add spy.calledWithNew to check if a function was used as a constructor +* Add spy.notCalledWith(), spy.neverCalledWith() and + sinon.assert.neverCalledWith. By Max Antoni +* Publicly expose sinon.expectation.fail to allow tools to integrate with mock + expectations. +* Fake XMLHttpRequests now support a minimal portion of the events API, making + them work seamlessly with e.g. SproutCode (which uses + xhr.addEventListener("readystatechange"). Partially by Sven Fuchs. + +== 1.1.1 / 2011-05-17 +* Fix broken mock verification in CommonJS when not including the full Sinon + package. + +== 1.1.0 / 2011-05-04 +* The fake server now has a autoRespond method which allows it to respond to + requests on the fly (asynchronously), making it a good fit for mockup + development +* Stubs and spies now has a withArgs method. Using it allows you to create + several spies/stubs for the same method, filtered by received arguments +* Stubs now has yields and yieldsTo methods for fuzzily invoking callbacks. + They work like callsArgAt only by inferring what callback to invoke, and + yieldsTo can invoke callbacks in object "options" arguments. +* Allow sandboxes/collections to stub any property so long as the object + has the property as an own property +* Significantly improve error reporting from failed mock expecations. Now prints + all met and unmet expectations with expected and received arguments +* Allow mock expectations to be consumed in any order +* Add pretty printing of all calls when assertions fail +* Fix bug: Stub exception message ended up as "undefined" (string) if not + specified +* Pass capture groups in URLs to fakeServer function handlers +* Pass through return value from test function in testCase +* typeof require is not enough to assume node, also use typeof module +* Don't use Object.create in sinon.create. In the off chance that someone stubs + it, sinon will fail mysteriously (Thanks to Espen Dalløkken) +* Catch exceptions when parsing DOM elements "on a hunch" + When responding to XHRs, Sinon acts like most browsers and try to parse the + response into responseXML if Content-Type indicates XML or HTML. However, it + also does this if the type is not set. Obviously, this may misfire and + should be caught. +* Fix fakeServer.respond() to not drop requests when they are queued during the + processing of an existing queue. (Sven Fuchs) +* Clean up module loading in CommonJS environments (Node.js still the only + tested such environment). No longer (temporarily) modifies require.paths, + always loads all modules. + +== 1.0.2 / 2011-02-22 +* Fix JSON bug in package.json +* Sandbox no longer tries to use a fake server if config says so, but + server is not loaded + +== 1.0.1 / 2010-12-20 +* Make sure sinon.sandbox is exposed in node.js (fix by Gord Tanner) + +== 1.0.0 / 2010-12-08 +* Switched indentation from 2 to 4 spaces :) +* Node.js compatibility improvements +* Remove magic booleans from sinon.assert.expose, replace with option object +* Put QUnit adapter in it's own repository +* Update build script to build standalone timers and server files +* Breaking change: thisObj -> thisValue + Change brings consistency to the code-base, always use thisValue +* Add sinon.assert.pass callback for successful assertions +* Extract sandbox configuration from sinon.test + + Refactored sinon.test to not do all the heavy lifting in creating sandbox + objects from sinon.config. Now sinon.sandbox.create accepts an optional + configuration that can be retrieved through sinon.getConfig({ ... }) - or, to + match previous behavior, through sinon.getConfig(sinon.config); + + The default configuration now lives in sinon.defaultConfig rather than the + previous sinon.test. + + This change enables external tools, such as test framework adapters, to easily + create configurable sandboxes without going through sinon.test +* Rewrite sinon.clock.tick to fix bug and make implementation clearer +* Test config load correct files +* Make timers and XHR truly standalone by splitting the IE work-around in two files +* Don't fail when comparing DOM elements in sinon.deepEqual (used in calledWith(...)) +* Should mirror properties on Date when faking it +* Added and updated configuration for both JsLint and JavaScript lint +* [August Lilleaas] The build script can optionally build a file without the + version name in it, by passing 'plain', i.e. './build plain'. + + Useful when using the build script to build and use sinon programatically, so + one can 'cp path/to/sinon/pkg/sinon.js my/scripts/' +* [August Lilleaas] Checking and warning if we got a load error and rubygems + isn't present. +* [August Lilleaas] Updating build script to be runnable from any + directory. Current working directory doesn't have to be repo root. + +== 0.8.0 / 2010-10-30 +* sinon.wrapMethod no longer accepts faking already faked methods +* sinon-qunit 'plugin' +* sinon.test / sinon.config can now expose the sandbox object + +== 0.7.2 / 2010-10-25 +* Add sinon.sandbox.create back in +* Fix bug where clock.tick would fire timeouts in intervals when + setInterval was also called + +== 0.7.1 / 2010-10-16 +* The fake server will now match paths against full URLs, meaning that + server.respondWith("/", "OK"); will match requests for + "http://currentHost/". +* Improved toString method for spies and stubs which leads to more + precise error messages from sinon.assert.* + +== 0.7.0 / 2010-09-19 +* sinon.useFakeTimers now fakes the Date constructor by default +* sinon.testCase now fakes XHR and timers by default +* sinon.config controls the behavior of sinon.testCase +* Fixed bug in clock.tick - now fires timers in correct order +* Added the ability to tick a clock string for longer ticks. + Passing a number causes the clock to tick the specified amount of + milliseconds, passing a string like "12:32" ticks 12 minutes and 32 + seconds. +* calledBefore and calledAfter for individual calls +* New assertions + sinon.assert.notCalled + sinon.assert.calledOnce + sinon.assert.calledTwice + sinon.assert.calledThrice +* sinon.test now throws if passed anything other than a function +* sinon.testCase now throws if passed anything other than an object +* sinon.{spy,stub}(obj, method) now throws if the property is not an + existing function - helps avoid perpetuating typo bugs +* Vastly improved error messages from assertions +* Spies/stubs/expectations can have their names resolved in many cases +* Removed feature where sinon.testCase allowed for nested test cases + (does not belong in Sinon.JS) +* Organizational change: src/ becomes lib/ Helps npm compatibility +* Thanks to Cory Flanigan for help on npm compatibility + +== 0.6.2 / 2010-08-12 +* Fixed another bug in sinon.fakeServerWithClock where consecutive + respond() calls did not trigger timeouts. + +== 0.6.1 / 2010-08-12 +* Fixed a bug in sinon.fakeServerWithClock where the clock was ticked + before the server had responded to all requests, resulting in + objects not having been responded to by the time the timeout ran. + +== 0.6.0 / 2010-08-10 +* FakeXMLHttpRequest +* sinon.useFakeXMLHttpRequest +* sinon.fakeServer +* sinon.fakeServerWithClock +* Improved fake timers implementation, made them work properly in IE 6-8 +* Improved sinon.sandbox + * Added useFakeServer + * Added inject method +* Improved sinon.test method + * Made configuration aware + * Now uses sinon.sandbox in place of sinon.collection +* Changed default configuration for sinon.test, breaking compatibility + with 0.5.0 - can be changed through sinon.config + +== 0.5.0 / 2010-06-09 +* Initial release +* Spies, stubs, mocks +* Assertions +* collections, test, testCase +* Fake timers (half-baked) diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/LICENSE b/node_modules/promises-aplus-tests/node_modules/sinon/LICENSE new file mode 100644 index 0000000..e7f50d1 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/LICENSE @@ -0,0 +1,27 @@ +(The BSD License) + +Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Christian Johansen nor the names of his contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/README.md b/node_modules/promises-aplus-tests/node_modules/sinon/README.md new file mode 100644 index 0000000..d6eb5b9 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/README.md @@ -0,0 +1,46 @@ +# Sinon.JS + +[![Build status](https://secure.travis-ci.org/cjohansen/Sinon.JS.svg?branch=master)](http://travis-ci.org/cjohansen/Sinon.JS) + +Standalone and test framework agnostic JavaScript test spies, stubs and mocks. + +## Installation + +via [npm (node package manager)](http://github.com/isaacs/npm) + + $ npm install sinon + +via [NuGet (package manager for Microsoft development platform)](https://www.nuget.org/packages/SinonJS) + + Install-Package SinonJS + +or install via git by cloning the repository and including sinon.js +in your project, as you would any other third party library. + +Don't forget to include the parts of Sinon.JS that you want to use as well +(i.e. spy.js). + +## Usage + +See the [sinon project homepage](http://sinonjs.org/) for documentation on usage. + +If you have questions that are not covered by the documentation, please post them to the [Sinon.JS mailing list](http://groups.google.com/group/sinonjs) or drop by
            #sinon.js on irc.freenode.net:6667 + +### Important: AMD needs pre-built version + +Sinon.JS *as source* **doesn't work with AMD loaders** (when they're asynchronous, like loading via script tags in the browser). For that you will have to use a pre-built version. You can either [build it yourself](CONTRIBUTING.md#testing-a-built-version) or get a numbered version from http://sinonjs.org. + +This might or might not change in future versions, depending of outcome of investigations. Please don't report this as a bug, just use pre-built versions. + +## Goals + +* No global pollution +* Easy to use +* Require minimal “integration” +* Easy to embed seamlessly with any testing framework +* Easily fake any interface +* Ship with ready-to-use fakes for XMLHttpRequest, timers and more + +## Contribute? + +See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how you can contribute to Sinon.JS diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon.js new file mode 100644 index 0000000..84f7834 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon.js @@ -0,0 +1,47 @@ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +var sinon = (function () { // eslint-disable-line no-unused-vars + "use strict"; + + var sinonModule; + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + sinonModule = module.exports = require("./sinon/util/core"); + require("./sinon/extend"); + require("./sinon/walk"); + require("./sinon/typeOf"); + require("./sinon/times_in_words"); + require("./sinon/spy"); + require("./sinon/call"); + require("./sinon/behavior"); + require("./sinon/stub"); + require("./sinon/mock"); + require("./sinon/collection"); + require("./sinon/assert"); + require("./sinon/sandbox"); + require("./sinon/test"); + require("./sinon/test_case"); + require("./sinon/match"); + require("./sinon/format"); + require("./sinon/log_error"); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + sinonModule = module.exports; + } else { + sinonModule = {}; + } + + return sinonModule; +}()); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/assert.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/assert.js new file mode 100644 index 0000000..50aa5ee --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/assert.js @@ -0,0 +1,226 @@ +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend match.js + * @depend format.js + */ +/** + * Assertions matching the test spy retrieval interface. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal, global) { + "use strict"; + + var slice = Array.prototype.slice; + + function makeApi(sinon) { + var assert; + + function verifyIsStub() { + var method; + + for (var i = 0, l = arguments.length; i < l; ++i) { + method = arguments[i]; + + if (!method) { + assert.fail("fake is not a spy"); + } + + if (method.proxy && method.proxy.isSinonProxy) { + verifyIsStub(method.proxy); + } else { + if (typeof method !== "function") { + assert.fail(method + " is not a function"); + } + + if (typeof method.getCall !== "function") { + assert.fail(method + " is not stubbed"); + } + } + + } + } + + function failAssertion(object, msg) { + object = object || global; + var failMethod = object.fail || assert.fail; + failMethod.call(object, msg); + } + + function mirrorPropAsAssertion(name, method, message) { + if (arguments.length === 2) { + message = method; + method = name; + } + + assert[name] = function (fake) { + verifyIsStub(fake); + + var args = slice.call(arguments, 1); + var failed = false; + + if (typeof method === "function") { + failed = !method(fake); + } else { + failed = typeof fake[method] === "function" ? + !fake[method].apply(fake, args) : !fake[method]; + } + + if (failed) { + failAssertion(this, (fake.printf || fake.proxy.printf).apply(fake, [message].concat(args))); + } else { + assert.pass(name); + } + }; + } + + function exposedName(prefix, prop) { + return !prefix || /^fail/.test(prop) ? prop : + prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1); + } + + assert = { + failException: "AssertError", + + fail: function fail(message) { + var error = new Error(message); + error.name = this.failException || assert.failException; + + throw error; + }, + + pass: function pass() {}, + + callOrder: function assertCallOrder() { + verifyIsStub.apply(null, arguments); + var expected = ""; + var actual = ""; + + if (!sinon.calledInOrder(arguments)) { + try { + expected = [].join.call(arguments, ", "); + var calls = slice.call(arguments); + var i = calls.length; + while (i) { + if (!calls[--i].called) { + calls.splice(i, 1); + } + } + actual = sinon.orderByFirstCall(calls).join(", "); + } catch (e) { + // If this fails, we'll just fall back to the blank string + } + + failAssertion(this, "expected " + expected + " to be " + + "called in order but were called as " + actual); + } else { + assert.pass("callOrder"); + } + }, + + callCount: function assertCallCount(method, count) { + verifyIsStub(method); + + if (method.callCount !== count) { + var msg = "expected %n to be called " + sinon.timesInWords(count) + + " but was called %c%C"; + failAssertion(this, method.printf(msg)); + } else { + assert.pass("callCount"); + } + }, + + expose: function expose(target, options) { + if (!target) { + throw new TypeError("target is null or undefined"); + } + + var o = options || {}; + var prefix = typeof o.prefix === "undefined" && "assert" || o.prefix; + var includeFail = typeof o.includeFail === "undefined" || !!o.includeFail; + + for (var method in this) { + if (method !== "expose" && (includeFail || !/^(fail)/.test(method))) { + target[exposedName(prefix, method)] = this[method]; + } + } + + return target; + }, + + match: function match(actual, expectation) { + var matcher = sinon.match(expectation); + if (matcher.test(actual)) { + assert.pass("match"); + } else { + var formatted = [ + "expected value to match", + " expected = " + sinon.format(expectation), + " actual = " + sinon.format(actual) + ]; + + failAssertion(this, formatted.join("\n")); + } + } + }; + + mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called"); + mirrorPropAsAssertion("notCalled", function (spy) { + return !spy.called; + }, "expected %n to not have been called but was called %c%C"); + mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C"); + mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C"); + mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C"); + mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t"); + mirrorPropAsAssertion( + "alwaysCalledOn", + "expected %n to always be called with %1 as this but was called with %t" + ); + mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new"); + mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new"); + mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C"); + mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C"); + mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C"); + mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C"); + mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C"); + mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C"); + mirrorPropAsAssertion("threw", "%n did not throw exception%C"); + mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C"); + + sinon.assert = assert; + return assert; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./match"); + require("./format"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof global !== "undefined" ? global : self +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/behavior.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/behavior.js new file mode 100644 index 0000000..3655339 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/behavior.js @@ -0,0 +1,371 @@ +/** + * @depend util/core.js + * @depend extend.js + */ +/** + * Stub behavior + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Tim Fischbach (mail@timfischbach.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + var slice = Array.prototype.slice; + var join = Array.prototype.join; + var useLeftMostCallback = -1; + var useRightMostCallback = -2; + + var nextTick = (function () { + if (typeof process === "object" && typeof process.nextTick === "function") { + return process.nextTick; + } + + if (typeof setImmediate === "function") { + return setImmediate; + } + + return function (callback) { + setTimeout(callback, 0); + }; + })(); + + function throwsException(error, message) { + if (typeof error === "string") { + this.exception = new Error(message || ""); + this.exception.name = error; + } else if (!error) { + this.exception = new Error("Error"); + } else { + this.exception = error; + } + + return this; + } + + function getCallback(behavior, args) { + var callArgAt = behavior.callArgAt; + + if (callArgAt >= 0) { + return args[callArgAt]; + } + + var argumentList; + + if (callArgAt === useLeftMostCallback) { + argumentList = args; + } + + if (callArgAt === useRightMostCallback) { + argumentList = slice.call(args).reverse(); + } + + var callArgProp = behavior.callArgProp; + + for (var i = 0, l = argumentList.length; i < l; ++i) { + if (!callArgProp && typeof argumentList[i] === "function") { + return argumentList[i]; + } + + if (callArgProp && argumentList[i] && + typeof argumentList[i][callArgProp] === "function") { + return argumentList[i][callArgProp]; + } + } + + return null; + } + + function makeApi(sinon) { + function getCallbackError(behavior, func, args) { + if (behavior.callArgAt < 0) { + var msg; + + if (behavior.callArgProp) { + msg = sinon.functionName(behavior.stub) + + " expected to yield to '" + behavior.callArgProp + + "', but no object with such a property was passed."; + } else { + msg = sinon.functionName(behavior.stub) + + " expected to yield, but no callback was passed."; + } + + if (args.length > 0) { + msg += " Received [" + join.call(args, ", ") + "]"; + } + + return msg; + } + + return "argument at index " + behavior.callArgAt + " is not a function: " + func; + } + + function callCallback(behavior, args) { + if (typeof behavior.callArgAt === "number") { + var func = getCallback(behavior, args); + + if (typeof func !== "function") { + throw new TypeError(getCallbackError(behavior, func, args)); + } + + if (behavior.callbackAsync) { + nextTick(function () { + func.apply(behavior.callbackContext, behavior.callbackArguments); + }); + } else { + func.apply(behavior.callbackContext, behavior.callbackArguments); + } + } + } + + var proto = { + create: function create(stub) { + var behavior = sinon.extend({}, sinon.behavior); + delete behavior.create; + behavior.stub = stub; + + return behavior; + }, + + isPresent: function isPresent() { + return (typeof this.callArgAt === "number" || + this.exception || + typeof this.returnArgAt === "number" || + this.returnThis || + this.returnValueDefined); + }, + + invoke: function invoke(context, args) { + callCallback(this, args); + + if (this.exception) { + throw this.exception; + } else if (typeof this.returnArgAt === "number") { + return args[this.returnArgAt]; + } else if (this.returnThis) { + return context; + } + + return this.returnValue; + }, + + onCall: function onCall(index) { + return this.stub.onCall(index); + }, + + onFirstCall: function onFirstCall() { + return this.stub.onFirstCall(); + }, + + onSecondCall: function onSecondCall() { + return this.stub.onSecondCall(); + }, + + onThirdCall: function onThirdCall() { + return this.stub.onThirdCall(); + }, + + withArgs: function withArgs(/* arguments */) { + throw new Error( + "Defining a stub by invoking \"stub.onCall(...).withArgs(...)\" " + + "is not supported. Use \"stub.withArgs(...).onCall(...)\" " + + "to define sequential behavior for calls with certain arguments." + ); + }, + + callsArg: function callsArg(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOn: function callsArgOn(pos, context) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgWith: function callsArgWith(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOnWith: function callsArgWith(pos, context) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yields: function () { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsRight: function () { + this.callArgAt = useRightMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsOn: function (context) { + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsTo: function (prop) { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + yieldsToOn: function (prop, context) { + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + throws: throwsException, + throwsException: throwsException, + + returns: function returns(value) { + this.returnValue = value; + this.returnValueDefined = true; + this.exception = undefined; + + return this; + }, + + returnsArg: function returnsArg(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.returnArgAt = pos; + + return this; + }, + + returnsThis: function returnsThis() { + this.returnThis = true; + + return this; + } + }; + + function createAsyncVersion(syncFnName) { + return function () { + var result = this[syncFnName].apply(this, arguments); + this.callbackAsync = true; + return result; + }; + } + + // create asynchronous versions of callsArg* and yields* methods + for (var method in proto) { + // need to avoid creating anotherasync versions of the newly added async methods + if (proto.hasOwnProperty(method) && method.match(/^(callsArg|yields)/) && !method.match(/Async/)) { + proto[method + "Async"] = createAsyncVersion(method); + } + } + + sinon.behavior = proto; + return proto; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./extend"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/call.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/call.js new file mode 100644 index 0000000..920da18 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/call.js @@ -0,0 +1,235 @@ +/** + * @depend util/core.js + * @depend match.js + * @depend format.js + */ +/** + * Spy calls + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + * Copyright (c) 2013 Maximilian Antoni + */ +(function (sinonGlobal) { + "use strict"; + + var slice = Array.prototype.slice; + + function makeApi(sinon) { + function throwYieldError(proxy, text, args) { + var msg = sinon.functionName(proxy) + text; + if (args.length) { + msg += " Received [" + slice.call(args).join(", ") + "]"; + } + throw new Error(msg); + } + + var callProto = { + calledOn: function calledOn(thisValue) { + if (sinon.match && sinon.match.isMatcher(thisValue)) { + return thisValue.test(this.thisValue); + } + return this.thisValue === thisValue; + }, + + calledWith: function calledWith() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + if (!sinon.deepEqual(arguments[i], this.args[i])) { + return false; + } + } + + return true; + }, + + calledWithMatch: function calledWithMatch() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + var actual = this.args[i]; + var expectation = arguments[i]; + if (!sinon.match || !sinon.match(expectation).test(actual)) { + return false; + } + } + return true; + }, + + calledWithExactly: function calledWithExactly() { + return arguments.length === this.args.length && + this.calledWith.apply(this, arguments); + }, + + notCalledWith: function notCalledWith() { + return !this.calledWith.apply(this, arguments); + }, + + notCalledWithMatch: function notCalledWithMatch() { + return !this.calledWithMatch.apply(this, arguments); + }, + + returned: function returned(value) { + return sinon.deepEqual(value, this.returnValue); + }, + + threw: function threw(error) { + if (typeof error === "undefined" || !this.exception) { + return !!this.exception; + } + + return this.exception === error || this.exception.name === error; + }, + + calledWithNew: function calledWithNew() { + return this.proxy.prototype && this.thisValue instanceof this.proxy; + }, + + calledBefore: function (other) { + return this.callId < other.callId; + }, + + calledAfter: function (other) { + return this.callId > other.callId; + }, + + callArg: function (pos) { + this.args[pos](); + }, + + callArgOn: function (pos, thisValue) { + this.args[pos].apply(thisValue); + }, + + callArgWith: function (pos) { + this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1))); + }, + + callArgOnWith: function (pos, thisValue) { + var args = slice.call(arguments, 2); + this.args[pos].apply(thisValue, args); + }, + + "yield": function () { + this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0))); + }, + + yieldOn: function (thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (typeof args[i] === "function") { + args[i].apply(thisValue, slice.call(arguments, 1)); + return; + } + } + throwYieldError(this.proxy, " cannot yield since no callback was passed.", args); + }, + + yieldTo: function (prop) { + this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1))); + }, + + yieldToOn: function (prop, thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (args[i] && typeof args[i][prop] === "function") { + args[i][prop].apply(thisValue, slice.call(arguments, 2)); + return; + } + } + throwYieldError(this.proxy, " cannot yield to '" + prop + + "' since no callback was passed.", args); + }, + + getStackFrames: function () { + // Omit the error message and the two top stack frames in sinon itself: + return this.stack && this.stack.split("\n").slice(3); + }, + + toString: function () { + var callStr = this.proxy.toString() + "("; + var args = []; + + for (var i = 0, l = this.args.length; i < l; ++i) { + args.push(sinon.format(this.args[i])); + } + + callStr = callStr + args.join(", ") + ")"; + + if (typeof this.returnValue !== "undefined") { + callStr += " => " + sinon.format(this.returnValue); + } + + if (this.exception) { + callStr += " !" + this.exception.name; + + if (this.exception.message) { + callStr += "(" + this.exception.message + ")"; + } + } + if (this.stack) { + callStr += this.getStackFrames()[0].replace(/^\s*(?:at\s+|@)?/, " at "); + + } + + return callStr; + } + }; + + callProto.invokeCallback = callProto.yield; + + function createSpyCall(spy, thisValue, args, returnValue, exception, id, stack) { + if (typeof id !== "number") { + throw new TypeError("Call id is not a number"); + } + var proxyCall = sinon.create(callProto); + proxyCall.proxy = spy; + proxyCall.thisValue = thisValue; + proxyCall.args = args; + proxyCall.returnValue = returnValue; + proxyCall.exception = exception; + proxyCall.callId = id; + proxyCall.stack = stack; + + return proxyCall; + } + createSpyCall.toString = callProto.toString; // used by mocks + + sinon.spyCall = createSpyCall; + return createSpyCall; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./match"); + require("./format"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/collection.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/collection.js new file mode 100644 index 0000000..b9efd88 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/collection.js @@ -0,0 +1,173 @@ +/** + * @depend util/core.js + * @depend spy.js + * @depend stub.js + * @depend mock.js + */ +/** + * Collections of stubs, spies and mocks. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + var push = [].push; + var hasOwnProperty = Object.prototype.hasOwnProperty; + + function getFakes(fakeCollection) { + if (!fakeCollection.fakes) { + fakeCollection.fakes = []; + } + + return fakeCollection.fakes; + } + + function each(fakeCollection, method) { + var fakes = getFakes(fakeCollection); + + for (var i = 0, l = fakes.length; i < l; i += 1) { + if (typeof fakes[i][method] === "function") { + fakes[i][method](); + } + } + } + + function compact(fakeCollection) { + var fakes = getFakes(fakeCollection); + var i = 0; + while (i < fakes.length) { + fakes.splice(i, 1); + } + } + + function makeApi(sinon) { + var collection = { + verify: function resolve() { + each(this, "verify"); + }, + + restore: function restore() { + each(this, "restore"); + compact(this); + }, + + reset: function restore() { + each(this, "reset"); + }, + + verifyAndRestore: function verifyAndRestore() { + var exception; + + try { + this.verify(); + } catch (e) { + exception = e; + } + + this.restore(); + + if (exception) { + throw exception; + } + }, + + add: function add(fake) { + push.call(getFakes(this), fake); + return fake; + }, + + spy: function spy() { + return this.add(sinon.spy.apply(sinon, arguments)); + }, + + stub: function stub(object, property, value) { + if (property) { + var original = object[property]; + + if (typeof original !== "function") { + if (!hasOwnProperty.call(object, property)) { + throw new TypeError("Cannot stub non-existent own property " + property); + } + + object[property] = value; + + return this.add({ + restore: function () { + object[property] = original; + } + }); + } + } + if (!property && !!object && typeof object === "object") { + var stubbedObj = sinon.stub.apply(sinon, arguments); + + for (var prop in stubbedObj) { + if (typeof stubbedObj[prop] === "function") { + this.add(stubbedObj[prop]); + } + } + + return stubbedObj; + } + + return this.add(sinon.stub.apply(sinon, arguments)); + }, + + mock: function mock() { + return this.add(sinon.mock.apply(sinon, arguments)); + }, + + inject: function inject(obj) { + var col = this; + + obj.spy = function () { + return col.spy.apply(col, arguments); + }; + + obj.stub = function () { + return col.stub.apply(col, arguments); + }; + + obj.mock = function () { + return col.mock.apply(col, arguments); + }; + + return obj; + } + }; + + sinon.collection = collection; + return collection; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./mock"); + require("./spy"); + require("./stub"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/extend.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/extend.js new file mode 100644 index 0000000..a57e9eb --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/extend.js @@ -0,0 +1,111 @@ +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + + // Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + var hasDontEnumBug = (function () { + var obj = { + constructor: function () { + return "0"; + }, + toString: function () { + return "1"; + }, + valueOf: function () { + return "2"; + }, + toLocaleString: function () { + return "3"; + }, + prototype: function () { + return "4"; + }, + isPrototypeOf: function () { + return "5"; + }, + propertyIsEnumerable: function () { + return "6"; + }, + hasOwnProperty: function () { + return "7"; + }, + length: function () { + return "8"; + }, + unique: function () { + return "9"; + } + }; + + var result = []; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + result.push(obj[prop]()); + } + } + return result.join("") !== "0123456789"; + })(); + + /* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will + * override properties in previous sources. + * + * target - The Object to extend + * sources - Objects to copy properties from. + * + * Returns the extended target + */ + function extend(target /*, sources */) { + var sources = Array.prototype.slice.call(arguments, 1); + var source, i, prop; + + for (i = 0; i < sources.length; i++) { + source = sources[i]; + + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // Make sure we copy (own) toString method even when in JScript with DontEnum bug + // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) { + target.toString = source.toString; + } + } + + return target; + } + + sinon.extend = extend; + return sinon.extend; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/format.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/format.js new file mode 100644 index 0000000..942d0a2 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/format.js @@ -0,0 +1,94 @@ +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal, formatio) { + "use strict"; + + function makeApi(sinon) { + function valueFormatter(value) { + return "" + value; + } + + function getFormatioFormatter() { + var formatter = formatio.configure({ + quoteStrings: false, + limitChildrenCount: 250 + }); + + function format() { + return formatter.ascii.apply(formatter, arguments); + } + + return format; + } + + function getNodeFormatter() { + try { + var util = require("util"); + } catch (e) { + /* Node, but no util module - would be very old, but better safe than sorry */ + } + + function format(v) { + var isObjectWithNativeToString = typeof v === "object" && v.toString === Object.prototype.toString; + return isObjectWithNativeToString ? util.inspect(v) : v; + } + + return util ? format : valueFormatter; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var formatter; + + if (isNode) { + try { + formatio = require("formatio"); + } + catch (e) {} // eslint-disable-line no-empty + } + + if (formatio) { + formatter = getFormatioFormatter(); + } else if (isNode) { + formatter = getNodeFormatter(); + } else { + formatter = valueFormatter; + } + + sinon.format = formatter; + return sinon.format; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof formatio === "object" && formatio // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/log_error.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/log_error.js new file mode 100644 index 0000000..3e41201 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/log_error.js @@ -0,0 +1,84 @@ +/** + * @depend util/core.js + */ +/** + * Logs errors + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + // cache a reference to setTimeout, so that our reference won't be stubbed out + // when using fake timers and errors will still get logged + // https://github.com/cjohansen/Sinon.JS/issues/381 + var realSetTimeout = setTimeout; + + function makeApi(sinon) { + + function log() {} + + function logError(label, err) { + var msg = label + " threw exception: "; + + function throwLoggedError() { + err.message = msg + err.message; + throw err; + } + + sinon.log(msg + "[" + err.name + "] " + err.message); + + if (err.stack) { + sinon.log(err.stack); + } + + if (logError.useImmediateExceptions) { + throwLoggedError(); + } else { + logError.setTimeout(throwLoggedError, 0); + } + } + + // When set to true, any errors logged will be thrown immediately; + // If set to false, the errors will be thrown in separate execution frame. + logError.useImmediateExceptions = false; + + // wrap realSetTimeout with something we can stub in tests + logError.setTimeout = function (func, timeout) { + realSetTimeout(func, timeout); + }; + + var exports = {}; + exports.log = sinon.log = log; + exports.logError = sinon.logError = logError; + + return exports; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/match.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/match.js new file mode 100644 index 0000000..0f72527 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/match.js @@ -0,0 +1,261 @@ +/** + * @depend util/core.js + * @depend typeOf.js + */ +/*jslint eqeqeq: false, onevar: false, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Match functions + * + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2012 Maximilian Antoni + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + function assertType(value, type, name) { + var actual = sinon.typeOf(value); + if (actual !== type) { + throw new TypeError("Expected type of " + name + " to be " + + type + ", but was " + actual); + } + } + + var matcher = { + toString: function () { + return this.message; + } + }; + + function isMatcher(object) { + return matcher.isPrototypeOf(object); + } + + function matchObject(expectation, actual) { + if (actual === null || actual === undefined) { + return false; + } + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + var exp = expectation[key]; + var act = actual[key]; + if (isMatcher(exp)) { + if (!exp.test(act)) { + return false; + } + } else if (sinon.typeOf(exp) === "object") { + if (!matchObject(exp, act)) { + return false; + } + } else if (!sinon.deepEqual(exp, act)) { + return false; + } + } + } + return true; + } + + function match(expectation, message) { + var m = sinon.create(matcher); + var type = sinon.typeOf(expectation); + switch (type) { + case "object": + if (typeof expectation.test === "function") { + m.test = function (actual) { + return expectation.test(actual) === true; + }; + m.message = "match(" + sinon.functionName(expectation.test) + ")"; + return m; + } + var str = []; + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + str.push(key + ": " + expectation[key]); + } + } + m.test = function (actual) { + return matchObject(expectation, actual); + }; + m.message = "match(" + str.join(", ") + ")"; + break; + case "number": + m.test = function (actual) { + // we need type coercion here + return expectation == actual; // eslint-disable-line eqeqeq + }; + break; + case "string": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return actual.indexOf(expectation) !== -1; + }; + m.message = "match(\"" + expectation + "\")"; + break; + case "regexp": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return expectation.test(actual); + }; + break; + case "function": + m.test = expectation; + if (message) { + m.message = message; + } else { + m.message = "match(" + sinon.functionName(expectation) + ")"; + } + break; + default: + m.test = function (actual) { + return sinon.deepEqual(expectation, actual); + }; + } + if (!m.message) { + m.message = "match(" + expectation + ")"; + } + return m; + } + + matcher.or = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var or = sinon.create(matcher); + or.test = function (actual) { + return m1.test(actual) || m2.test(actual); + }; + or.message = m1.message + ".or(" + m2.message + ")"; + return or; + }; + + matcher.and = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var and = sinon.create(matcher); + and.test = function (actual) { + return m1.test(actual) && m2.test(actual); + }; + and.message = m1.message + ".and(" + m2.message + ")"; + return and; + }; + + match.isMatcher = isMatcher; + + match.any = match(function () { + return true; + }, "any"); + + match.defined = match(function (actual) { + return actual !== null && actual !== undefined; + }, "defined"); + + match.truthy = match(function (actual) { + return !!actual; + }, "truthy"); + + match.falsy = match(function (actual) { + return !actual; + }, "falsy"); + + match.same = function (expectation) { + return match(function (actual) { + return expectation === actual; + }, "same(" + expectation + ")"); + }; + + match.typeOf = function (type) { + assertType(type, "string", "type"); + return match(function (actual) { + return sinon.typeOf(actual) === type; + }, "typeOf(\"" + type + "\")"); + }; + + match.instanceOf = function (type) { + assertType(type, "function", "type"); + return match(function (actual) { + return actual instanceof type; + }, "instanceOf(" + sinon.functionName(type) + ")"); + }; + + function createPropertyMatcher(propertyTest, messagePrefix) { + return function (property, value) { + assertType(property, "string", "property"); + var onlyProperty = arguments.length === 1; + var message = messagePrefix + "(\"" + property + "\""; + if (!onlyProperty) { + message += ", " + value; + } + message += ")"; + return match(function (actual) { + if (actual === undefined || actual === null || + !propertyTest(actual, property)) { + return false; + } + return onlyProperty || sinon.deepEqual(value, actual[property]); + }, message); + }; + } + + match.has = createPropertyMatcher(function (actual, property) { + if (typeof actual === "object") { + return property in actual; + } + return actual[property] !== undefined; + }, "has"); + + match.hasOwn = createPropertyMatcher(function (actual, property) { + return actual.hasOwnProperty(property); + }, "hasOwn"); + + match.bool = match.typeOf("boolean"); + match.number = match.typeOf("number"); + match.string = match.typeOf("string"); + match.object = match.typeOf("object"); + match.func = match.typeOf("function"); + match.array = match.typeOf("array"); + match.regexp = match.typeOf("regexp"); + match.date = match.typeOf("date"); + + sinon.match = match; + return match; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./typeOf"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/mock.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/mock.js new file mode 100644 index 0000000..8af2262 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/mock.js @@ -0,0 +1,491 @@ +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend call.js + * @depend extend.js + * @depend match.js + * @depend spy.js + * @depend stub.js + * @depend format.js + */ +/** + * Mock functions. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + var push = [].push; + var match = sinon.match; + + function mock(object) { + // if (typeof console !== undefined && console.warn) { + // console.warn("mock will be removed from Sinon.JS v2.0"); + // } + + if (!object) { + return sinon.expectation.create("Anonymous mock"); + } + + return mock.create(object); + } + + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + + function arrayEquals(arr1, arr2, compareLength) { + if (compareLength && (arr1.length !== arr2.length)) { + return false; + } + + for (var i = 0, l = arr1.length; i < l; i++) { + if (!sinon.deepEqual(arr1[i], arr2[i])) { + return false; + } + } + return true; + } + + sinon.extend(mock, { + create: function create(object) { + if (!object) { + throw new TypeError("object is null"); + } + + var mockObject = sinon.extend({}, mock); + mockObject.object = object; + delete mockObject.create; + + return mockObject; + }, + + expects: function expects(method) { + if (!method) { + throw new TypeError("method is falsy"); + } + + if (!this.expectations) { + this.expectations = {}; + this.proxies = []; + } + + if (!this.expectations[method]) { + this.expectations[method] = []; + var mockObject = this; + + sinon.wrapMethod(this.object, method, function () { + return mockObject.invokeMethod(method, this, arguments); + }); + + push.call(this.proxies, method); + } + + var expectation = sinon.expectation.create(method); + push.call(this.expectations[method], expectation); + + return expectation; + }, + + restore: function restore() { + var object = this.object; + + each(this.proxies, function (proxy) { + if (typeof object[proxy].restore === "function") { + object[proxy].restore(); + } + }); + }, + + verify: function verify() { + var expectations = this.expectations || {}; + var messages = []; + var met = []; + + each(this.proxies, function (proxy) { + each(expectations[proxy], function (expectation) { + if (!expectation.met()) { + push.call(messages, expectation.toString()); + } else { + push.call(met, expectation.toString()); + } + }); + }); + + this.restore(); + + if (messages.length > 0) { + sinon.expectation.fail(messages.concat(met).join("\n")); + } else if (met.length > 0) { + sinon.expectation.pass(messages.concat(met).join("\n")); + } + + return true; + }, + + invokeMethod: function invokeMethod(method, thisValue, args) { + var expectations = this.expectations && this.expectations[method] ? this.expectations[method] : []; + var expectationsWithMatchingArgs = []; + var currentArgs = args || []; + var i, available; + + for (i = 0; i < expectations.length; i += 1) { + var expectedArgs = expectations[i].expectedArguments || []; + if (arrayEquals(expectedArgs, currentArgs, expectations[i].expectsExactArgCount)) { + expectationsWithMatchingArgs.push(expectations[i]); + } + } + + for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) { + if (!expectationsWithMatchingArgs[i].met() && + expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) { + return expectationsWithMatchingArgs[i].apply(thisValue, args); + } + } + + var messages = []; + var exhausted = 0; + + for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) { + if (expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) { + available = available || expectationsWithMatchingArgs[i]; + } else { + exhausted += 1; + } + } + + if (available && exhausted === 0) { + return available.apply(thisValue, args); + } + + for (i = 0; i < expectations.length; i += 1) { + push.call(messages, " " + expectations[i].toString()); + } + + messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({ + proxy: method, + args: args + })); + + sinon.expectation.fail(messages.join("\n")); + } + }); + + var times = sinon.timesInWords; + var slice = Array.prototype.slice; + + function callCountInWords(callCount) { + if (callCount === 0) { + return "never called"; + } + + return "called " + times(callCount); + } + + function expectedCallCountInWords(expectation) { + var min = expectation.minCalls; + var max = expectation.maxCalls; + + if (typeof min === "number" && typeof max === "number") { + var str = times(min); + + if (min !== max) { + str = "at least " + str + " and at most " + times(max); + } + + return str; + } + + if (typeof min === "number") { + return "at least " + times(min); + } + + return "at most " + times(max); + } + + function receivedMinCalls(expectation) { + var hasMinLimit = typeof expectation.minCalls === "number"; + return !hasMinLimit || expectation.callCount >= expectation.minCalls; + } + + function receivedMaxCalls(expectation) { + if (typeof expectation.maxCalls !== "number") { + return false; + } + + return expectation.callCount === expectation.maxCalls; + } + + function verifyMatcher(possibleMatcher, arg) { + var isMatcher = match && match.isMatcher(possibleMatcher); + + return isMatcher && possibleMatcher.test(arg) || true; + } + + sinon.expectation = { + minCalls: 1, + maxCalls: 1, + + create: function create(methodName) { + var expectation = sinon.extend(sinon.stub.create(), sinon.expectation); + delete expectation.create; + expectation.method = methodName; + + return expectation; + }, + + invoke: function invoke(func, thisValue, args) { + this.verifyCallAllowed(thisValue, args); + + return sinon.spy.invoke.apply(this, arguments); + }, + + atLeast: function atLeast(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.maxCalls = null; + this.limitsSet = true; + } + + this.minCalls = num; + + return this; + }, + + atMost: function atMost(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.minCalls = null; + this.limitsSet = true; + } + + this.maxCalls = num; + + return this; + }, + + never: function never() { + return this.exactly(0); + }, + + once: function once() { + return this.exactly(1); + }, + + twice: function twice() { + return this.exactly(2); + }, + + thrice: function thrice() { + return this.exactly(3); + }, + + exactly: function exactly(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not a number"); + } + + this.atLeast(num); + return this.atMost(num); + }, + + met: function met() { + return !this.failed && receivedMinCalls(this); + }, + + verifyCallAllowed: function verifyCallAllowed(thisValue, args) { + if (receivedMaxCalls(this)) { + this.failed = true; + sinon.expectation.fail(this.method + " already called " + times(this.maxCalls)); + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " + + this.expectedThis); + } + + if (!("expectedArguments" in this)) { + return; + } + + if (!args) { + sinon.expectation.fail(this.method + " received no arguments, expected " + + sinon.format(this.expectedArguments)); + } + + if (args.length < this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + if (this.expectsExactArgCount && + args.length !== this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", didn't match " + this.expectedArguments.toString()); + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", expected " + sinon.format(this.expectedArguments)); + } + } + }, + + allowsCall: function allowsCall(thisValue, args) { + if (this.met() && receivedMaxCalls(this)) { + return false; + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + return false; + } + + if (!("expectedArguments" in this)) { + return true; + } + + args = args || []; + + if (args.length < this.expectedArguments.length) { + return false; + } + + if (this.expectsExactArgCount && + args.length !== this.expectedArguments.length) { + return false; + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + return false; + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + return false; + } + } + + return true; + }, + + withArgs: function withArgs() { + this.expectedArguments = slice.call(arguments); + return this; + }, + + withExactArgs: function withExactArgs() { + this.withArgs.apply(this, arguments); + this.expectsExactArgCount = true; + return this; + }, + + on: function on(thisValue) { + this.expectedThis = thisValue; + return this; + }, + + toString: function () { + var args = (this.expectedArguments || []).slice(); + + if (!this.expectsExactArgCount) { + push.call(args, "[...]"); + } + + var callStr = sinon.spyCall.toString.call({ + proxy: this.method || "anonymous mock expectation", + args: args + }); + + var message = callStr.replace(", [...", "[, ...") + " " + + expectedCallCountInWords(this); + + if (this.met()) { + return "Expectation met: " + message; + } + + return "Expected " + message + " (" + + callCountInWords(this.callCount) + ")"; + }, + + verify: function verify() { + if (!this.met()) { + sinon.expectation.fail(this.toString()); + } else { + sinon.expectation.pass(this.toString()); + } + + return true; + }, + + pass: function pass(message) { + sinon.assert.pass(message); + }, + + fail: function fail(message) { + var exception = new Error(message); + exception.name = "ExpectationError"; + + throw exception; + } + }; + + sinon.mock = mock; + return mock; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./times_in_words"); + require("./call"); + require("./extend"); + require("./match"); + require("./spy"); + require("./stub"); + require("./format"); + + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/sandbox.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/sandbox.js new file mode 100644 index 0000000..26c8826 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/sandbox.js @@ -0,0 +1,170 @@ +/** + * @depend util/core.js + * @depend extend.js + * @depend collection.js + * @depend util/fake_timers.js + * @depend util/fake_server_with_clock.js + */ +/** + * Manages fake collections as well as fake utilities such as Sinon's + * timers and fake XHR implementation in one convenient object. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + var push = [].push; + + function exposeValue(sandbox, config, key, value) { + if (!value) { + return; + } + + if (config.injectInto && !(key in config.injectInto)) { + config.injectInto[key] = value; + sandbox.injectedKeys.push(key); + } else { + push.call(sandbox.args, value); + } + } + + function prepareSandboxFromConfig(config) { + var sandbox = sinon.create(sinon.sandbox); + + if (config.useFakeServer) { + if (typeof config.useFakeServer === "object") { + sandbox.serverPrototype = config.useFakeServer; + } + + sandbox.useFakeServer(); + } + + if (config.useFakeTimers) { + if (typeof config.useFakeTimers === "object") { + sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers); + } else { + sandbox.useFakeTimers(); + } + } + + return sandbox; + } + + sinon.sandbox = sinon.extend(sinon.create(sinon.collection), { + useFakeTimers: function useFakeTimers() { + this.clock = sinon.useFakeTimers.apply(sinon, arguments); + + return this.add(this.clock); + }, + + serverPrototype: sinon.fakeServer, + + useFakeServer: function useFakeServer() { + var proto = this.serverPrototype || sinon.fakeServer; + + if (!proto || !proto.create) { + return null; + } + + this.server = proto.create(); + return this.add(this.server); + }, + + inject: function (obj) { + sinon.collection.inject.call(this, obj); + + if (this.clock) { + obj.clock = this.clock; + } + + if (this.server) { + obj.server = this.server; + obj.requests = this.server.requests; + } + + obj.match = sinon.match; + + return obj; + }, + + restore: function () { + sinon.collection.restore.apply(this, arguments); + this.restoreContext(); + }, + + restoreContext: function () { + if (this.injectedKeys) { + for (var i = 0, j = this.injectedKeys.length; i < j; i++) { + delete this.injectInto[this.injectedKeys[i]]; + } + this.injectedKeys = []; + } + }, + + create: function (config) { + if (!config) { + return sinon.create(sinon.sandbox); + } + + var sandbox = prepareSandboxFromConfig(config); + sandbox.args = sandbox.args || []; + sandbox.injectedKeys = []; + sandbox.injectInto = config.injectInto; + var prop, + value; + var exposed = sandbox.inject({}); + + if (config.properties) { + for (var i = 0, l = config.properties.length; i < l; i++) { + prop = config.properties[i]; + value = exposed[prop] || prop === "sandbox" && sandbox; + exposeValue(sandbox, config, prop, value); + } + } else { + exposeValue(sandbox, config, "sandbox", value); + } + + return sandbox; + }, + + match: sinon.match + }); + + sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer; + + return sinon.sandbox; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./extend"); + require("./util/fake_server_with_clock"); + require("./util/fake_timers"); + require("./collection"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/spy.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/spy.js new file mode 100644 index 0000000..104f1ec --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/spy.js @@ -0,0 +1,463 @@ +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend extend.js + * @depend call.js + * @depend format.js + */ +/** + * Spy functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + var push = Array.prototype.push; + var slice = Array.prototype.slice; + var callId = 0; + + function spy(object, property, types) { + if (!property && typeof object === "function") { + return spy.create(object); + } + + if (!object && !property) { + return spy.create(function () { }); + } + + if (types) { + var methodDesc = sinon.getPropertyDescriptor(object, property); + for (var i = 0; i < types.length; i++) { + methodDesc[types[i]] = spy.create(methodDesc[types[i]]); + } + return sinon.wrapMethod(object, property, methodDesc); + } + + return sinon.wrapMethod(object, property, spy.create(object[property])); + } + + function matchingFake(fakes, args, strict) { + if (!fakes) { + return undefined; + } + + for (var i = 0, l = fakes.length; i < l; i++) { + if (fakes[i].matches(args, strict)) { + return fakes[i]; + } + } + } + + function incrementCallCount() { + this.called = true; + this.callCount += 1; + this.notCalled = false; + this.calledOnce = this.callCount === 1; + this.calledTwice = this.callCount === 2; + this.calledThrice = this.callCount === 3; + } + + function createCallProperties() { + this.firstCall = this.getCall(0); + this.secondCall = this.getCall(1); + this.thirdCall = this.getCall(2); + this.lastCall = this.getCall(this.callCount - 1); + } + + var vars = "a,b,c,d,e,f,g,h,i,j,k,l"; + function createProxy(func, proxyLength) { + // Retain the function length: + var p; + if (proxyLength) { + eval("p = (function proxy(" + vars.substring(0, proxyLength * 2 - 1) + // eslint-disable-line no-eval + ") { return p.invoke(func, this, slice.call(arguments)); });"); + } else { + p = function proxy() { + return p.invoke(func, this, slice.call(arguments)); + }; + } + p.isSinonProxy = true; + return p; + } + + var uuid = 0; + + // Public API + var spyApi = { + reset: function () { + if (this.invoking) { + var err = new Error("Cannot reset Sinon function while invoking it. " + + "Move the call to .reset outside of the callback."); + err.name = "InvalidResetException"; + throw err; + } + + this.called = false; + this.notCalled = true; + this.calledOnce = false; + this.calledTwice = false; + this.calledThrice = false; + this.callCount = 0; + this.firstCall = null; + this.secondCall = null; + this.thirdCall = null; + this.lastCall = null; + this.args = []; + this.returnValues = []; + this.thisValues = []; + this.exceptions = []; + this.callIds = []; + this.stacks = []; + if (this.fakes) { + for (var i = 0; i < this.fakes.length; i++) { + this.fakes[i].reset(); + } + } + + return this; + }, + + create: function create(func, spyLength) { + var name; + + if (typeof func !== "function") { + func = function () { }; + } else { + name = sinon.functionName(func); + } + + if (!spyLength) { + spyLength = func.length; + } + + var proxy = createProxy(func, spyLength); + + sinon.extend(proxy, spy); + delete proxy.create; + sinon.extend(proxy, func); + + proxy.reset(); + proxy.prototype = func.prototype; + proxy.displayName = name || "spy"; + proxy.toString = sinon.functionToString; + proxy.instantiateFake = sinon.spy.create; + proxy.id = "spy#" + uuid++; + + return proxy; + }, + + invoke: function invoke(func, thisValue, args) { + var matching = matchingFake(this.fakes, args); + var exception, returnValue; + + incrementCallCount.call(this); + push.call(this.thisValues, thisValue); + push.call(this.args, args); + push.call(this.callIds, callId++); + + // Make call properties available from within the spied function: + createCallProperties.call(this); + + try { + this.invoking = true; + + if (matching) { + returnValue = matching.invoke(func, thisValue, args); + } else { + returnValue = (this.func || func).apply(thisValue, args); + } + + var thisCall = this.getCall(this.callCount - 1); + if (thisCall.calledWithNew() && typeof returnValue !== "object") { + returnValue = thisValue; + } + } catch (e) { + exception = e; + } finally { + delete this.invoking; + } + + push.call(this.exceptions, exception); + push.call(this.returnValues, returnValue); + push.call(this.stacks, new Error().stack); + + // Make return value and exception available in the calls: + createCallProperties.call(this); + + if (exception !== undefined) { + throw exception; + } + + return returnValue; + }, + + named: function named(name) { + this.displayName = name; + return this; + }, + + getCall: function getCall(i) { + if (i < 0 || i >= this.callCount) { + return null; + } + + return sinon.spyCall(this, this.thisValues[i], this.args[i], + this.returnValues[i], this.exceptions[i], + this.callIds[i], this.stacks[i]); + }, + + getCalls: function () { + var calls = []; + var i; + + for (i = 0; i < this.callCount; i++) { + calls.push(this.getCall(i)); + } + + return calls; + }, + + calledBefore: function calledBefore(spyFn) { + if (!this.called) { + return false; + } + + if (!spyFn.called) { + return true; + } + + return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1]; + }, + + calledAfter: function calledAfter(spyFn) { + if (!this.called || !spyFn.called) { + return false; + } + + return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1]; + }, + + withArgs: function () { + var args = slice.call(arguments); + + if (this.fakes) { + var match = matchingFake(this.fakes, args, true); + + if (match) { + return match; + } + } else { + this.fakes = []; + } + + var original = this; + var fake = this.instantiateFake(); + fake.matchingAguments = args; + fake.parent = this; + push.call(this.fakes, fake); + + fake.withArgs = function () { + return original.withArgs.apply(original, arguments); + }; + + for (var i = 0; i < this.args.length; i++) { + if (fake.matches(this.args[i])) { + incrementCallCount.call(fake); + push.call(fake.thisValues, this.thisValues[i]); + push.call(fake.args, this.args[i]); + push.call(fake.returnValues, this.returnValues[i]); + push.call(fake.exceptions, this.exceptions[i]); + push.call(fake.callIds, this.callIds[i]); + } + } + createCallProperties.call(fake); + + return fake; + }, + + matches: function (args, strict) { + var margs = this.matchingAguments; + + if (margs.length <= args.length && + sinon.deepEqual(margs, args.slice(0, margs.length))) { + return !strict || margs.length === args.length; + } + }, + + printf: function (format) { + var spyInstance = this; + var args = slice.call(arguments, 1); + var formatter; + + return (format || "").replace(/%(.)/g, function (match, specifyer) { + formatter = spyApi.formatters[specifyer]; + + if (typeof formatter === "function") { + return formatter.call(null, spyInstance, args); + } else if (!isNaN(parseInt(specifyer, 10))) { + return sinon.format(args[specifyer - 1]); + } + + return "%" + specifyer; + }); + } + }; + + function delegateToCalls(method, matchAny, actual, notCalled) { + spyApi[method] = function () { + if (!this.called) { + if (notCalled) { + return notCalled.apply(this, arguments); + } + return false; + } + + var currentCall; + var matches = 0; + + for (var i = 0, l = this.callCount; i < l; i += 1) { + currentCall = this.getCall(i); + + if (currentCall[actual || method].apply(currentCall, arguments)) { + matches += 1; + + if (matchAny) { + return true; + } + } + } + + return matches === this.callCount; + }; + } + + delegateToCalls("calledOn", true); + delegateToCalls("alwaysCalledOn", false, "calledOn"); + delegateToCalls("calledWith", true); + delegateToCalls("calledWithMatch", true); + delegateToCalls("alwaysCalledWith", false, "calledWith"); + delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch"); + delegateToCalls("calledWithExactly", true); + delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly"); + delegateToCalls("neverCalledWith", false, "notCalledWith", function () { + return true; + }); + delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch", function () { + return true; + }); + delegateToCalls("threw", true); + delegateToCalls("alwaysThrew", false, "threw"); + delegateToCalls("returned", true); + delegateToCalls("alwaysReturned", false, "returned"); + delegateToCalls("calledWithNew", true); + delegateToCalls("alwaysCalledWithNew", false, "calledWithNew"); + delegateToCalls("callArg", false, "callArgWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgWith = spyApi.callArg; + delegateToCalls("callArgOn", false, "callArgOnWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgOnWith = spyApi.callArgOn; + delegateToCalls("yield", false, "yield", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode. + spyApi.invokeCallback = spyApi.yield; + delegateToCalls("yieldOn", false, "yieldOn", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + delegateToCalls("yieldTo", false, "yieldTo", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + delegateToCalls("yieldToOn", false, "yieldToOn", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + + spyApi.formatters = { + c: function (spyInstance) { + return sinon.timesInWords(spyInstance.callCount); + }, + + n: function (spyInstance) { + return spyInstance.toString(); + }, + + C: function (spyInstance) { + var calls = []; + + for (var i = 0, l = spyInstance.callCount; i < l; ++i) { + var stringifiedCall = " " + spyInstance.getCall(i).toString(); + if (/\n/.test(calls[i - 1])) { + stringifiedCall = "\n" + stringifiedCall; + } + push.call(calls, stringifiedCall); + } + + return calls.length > 0 ? "\n" + calls.join("\n") : ""; + }, + + t: function (spyInstance) { + var objects = []; + + for (var i = 0, l = spyInstance.callCount; i < l; ++i) { + push.call(objects, sinon.format(spyInstance.thisValues[i])); + } + + return objects.join(", "); + }, + + "*": function (spyInstance, args) { + var formatted = []; + + for (var i = 0, l = args.length; i < l; ++i) { + push.call(formatted, sinon.format(args[i])); + } + + return formatted.join(", "); + } + }; + + sinon.extend(spy, spyApi); + + spy.spyCall = sinon.spyCall; + sinon.spy = spy; + + return spy; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./call"); + require("./extend"); + require("./times_in_words"); + require("./format"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/stub.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/stub.js new file mode 100644 index 0000000..903081e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/stub.js @@ -0,0 +1,200 @@ +/** + * @depend util/core.js + * @depend extend.js + * @depend spy.js + * @depend behavior.js + * @depend walk.js + */ +/** + * Stub functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + function stub(object, property, func) { + if (!!func && typeof func !== "function" && typeof func !== "object") { + throw new TypeError("Custom stub should be a function or a property descriptor"); + } + + var wrapper; + + if (func) { + if (typeof func === "function") { + wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func; + } else { + wrapper = func; + if (sinon.spy && sinon.spy.create) { + var types = sinon.objectKeys(wrapper); + for (var i = 0; i < types.length; i++) { + wrapper[types[i]] = sinon.spy.create(wrapper[types[i]]); + } + } + } + } else { + var stubLength = 0; + if (typeof object === "object" && typeof object[property] === "function") { + stubLength = object[property].length; + } + wrapper = stub.create(stubLength); + } + + if (!object && typeof property === "undefined") { + return sinon.stub.create(); + } + + if (typeof property === "undefined" && typeof object === "object") { + sinon.walk(object || {}, function (value, prop, propOwner) { + // we don't want to stub things like toString(), valueOf(), etc. so we only stub if the object + // is not Object.prototype + if ( + propOwner !== Object.prototype && + prop !== "constructor" && + typeof sinon.getPropertyDescriptor(propOwner, prop).value === "function" + ) { + stub(object, prop); + } + }); + + return object; + } + + return sinon.wrapMethod(object, property, wrapper); + } + + + /*eslint-disable no-use-before-define*/ + function getParentBehaviour(stubInstance) { + return (stubInstance.parent && getCurrentBehavior(stubInstance.parent)); + } + + function getDefaultBehavior(stubInstance) { + return stubInstance.defaultBehavior || + getParentBehaviour(stubInstance) || + sinon.behavior.create(stubInstance); + } + + function getCurrentBehavior(stubInstance) { + var behavior = stubInstance.behaviors[stubInstance.callCount - 1]; + return behavior && behavior.isPresent() ? behavior : getDefaultBehavior(stubInstance); + } + /*eslint-enable no-use-before-define*/ + + var uuid = 0; + + var proto = { + create: function create(stubLength) { + var functionStub = function () { + return getCurrentBehavior(functionStub).invoke(this, arguments); + }; + + functionStub.id = "stub#" + uuid++; + var orig = functionStub; + functionStub = sinon.spy.create(functionStub, stubLength); + functionStub.func = orig; + + sinon.extend(functionStub, stub); + functionStub.instantiateFake = sinon.stub.create; + functionStub.displayName = "stub"; + functionStub.toString = sinon.functionToString; + + functionStub.defaultBehavior = null; + functionStub.behaviors = []; + + return functionStub; + }, + + resetBehavior: function () { + var i; + + this.defaultBehavior = null; + this.behaviors = []; + + delete this.returnValue; + delete this.returnArgAt; + this.returnThis = false; + + if (this.fakes) { + for (i = 0; i < this.fakes.length; i++) { + this.fakes[i].resetBehavior(); + } + } + }, + + onCall: function onCall(index) { + if (!this.behaviors[index]) { + this.behaviors[index] = sinon.behavior.create(this); + } + + return this.behaviors[index]; + }, + + onFirstCall: function onFirstCall() { + return this.onCall(0); + }, + + onSecondCall: function onSecondCall() { + return this.onCall(1); + }, + + onThirdCall: function onThirdCall() { + return this.onCall(2); + } + }; + + function createBehavior(behaviorMethod) { + return function () { + this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this); + this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments); + return this; + }; + } + + for (var method in sinon.behavior) { + if (sinon.behavior.hasOwnProperty(method) && + !proto.hasOwnProperty(method) && + method !== "create" && + method !== "withArgs" && + method !== "invoke") { + proto[method] = createBehavior(method); + } + } + + sinon.extend(stub, proto); + sinon.stub = stub; + + return stub; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./behavior"); + require("./spy"); + require("./extend"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/test.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/test.js new file mode 100644 index 0000000..7eed91a --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/test.js @@ -0,0 +1,100 @@ +/** + * @depend util/core.js + * @depend sandbox.js + */ +/** + * Test function, sandboxes fakes + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + var slice = Array.prototype.slice; + + function test(callback) { + var type = typeof callback; + + if (type !== "function") { + throw new TypeError("sinon.test needs to wrap a test function, got " + type); + } + + function sinonSandboxedTest() { + var config = sinon.getConfig(sinon.config); + config.injectInto = config.injectIntoThis && this || config.injectInto; + var sandbox = sinon.sandbox.create(config); + var args = slice.call(arguments); + var oldDone = args.length && args[args.length - 1]; + var exception, result; + + if (typeof oldDone === "function") { + args[args.length - 1] = function sinonDone(res) { + if (res) { + sandbox.restore(); + } else { + sandbox.verifyAndRestore(); + } + oldDone(res); + }; + } + + try { + result = callback.apply(this, args.concat(sandbox.args)); + } catch (e) { + exception = e; + } + + if (typeof oldDone !== "function") { + if (typeof exception !== "undefined") { + sandbox.restore(); + throw exception; + } else { + sandbox.verifyAndRestore(); + } + } + + return result; + } + + if (callback.length) { + return function sinonAsyncSandboxedTest(done) { // eslint-disable-line no-unused-vars + return sinonSandboxedTest.apply(this, arguments); + }; + } + + return sinonSandboxedTest; + } + + test.config = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.test = test; + return test; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./sandbox"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (sinonGlobal) { + makeApi(sinonGlobal); + } +}(typeof sinon === "object" && sinon || null)); // eslint-disable-line no-undef diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/test_case.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/test_case.js new file mode 100644 index 0000000..c56f94a --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/test_case.js @@ -0,0 +1,106 @@ +/** + * @depend util/core.js + * @depend test.js + */ +/** + * Test case, sandboxes all test functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function createTest(property, setUp, tearDown) { + return function () { + if (setUp) { + setUp.apply(this, arguments); + } + + var exception, result; + + try { + result = property.apply(this, arguments); + } catch (e) { + exception = e; + } + + if (tearDown) { + tearDown.apply(this, arguments); + } + + if (exception) { + throw exception; + } + + return result; + }; + } + + function makeApi(sinon) { + function testCase(tests, prefix) { + if (!tests || typeof tests !== "object") { + throw new TypeError("sinon.testCase needs an object with test functions"); + } + + prefix = prefix || "test"; + var rPrefix = new RegExp("^" + prefix); + var methods = {}; + var setUp = tests.setUp; + var tearDown = tests.tearDown; + var testName, + property, + method; + + for (testName in tests) { + if (tests.hasOwnProperty(testName) && !/^(setUp|tearDown)$/.test(testName)) { + property = tests[testName]; + + if (typeof property === "function" && rPrefix.test(testName)) { + method = property; + + if (setUp || tearDown) { + method = createTest(property, setUp, tearDown); + } + + methods[testName] = sinon.test(method); + } else { + methods[testName] = tests[testName]; + } + } + } + + return methods; + } + + sinon.testCase = testCase; + return testCase; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./test"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/times_in_words.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/times_in_words.js new file mode 100644 index 0000000..61dc249 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/times_in_words.js @@ -0,0 +1,49 @@ +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + + function timesInWords(count) { + switch (count) { + case 1: + return "once"; + case 2: + return "twice"; + case 3: + return "thrice"; + default: + return (count || 0) + " times"; + } + } + + sinon.timesInWords = timesInWords; + return sinon.timesInWords; + } + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + module.exports = makeApi(core); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/typeOf.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/typeOf.js new file mode 100644 index 0000000..afb41ff --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/typeOf.js @@ -0,0 +1,53 @@ +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + function typeOf(value) { + if (value === null) { + return "null"; + } else if (value === undefined) { + return "undefined"; + } + var string = Object.prototype.toString.call(value); + return string.substring(8, string.length - 1).toLowerCase(); + } + + sinon.typeOf = typeOf; + return sinon.typeOf; + } + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + module.exports = makeApi(core); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/core.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/core.js new file mode 100644 index 0000000..d872d6c --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/core.js @@ -0,0 +1,401 @@ +/** + * @depend ../../sinon.js + */ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + var div = typeof document !== "undefined" && document.createElement("div"); + var hasOwn = Object.prototype.hasOwnProperty; + + function isDOMNode(obj) { + var success = false; + + try { + obj.appendChild(div); + success = div.parentNode === obj; + } catch (e) { + return false; + } finally { + try { + obj.removeChild(div); + } catch (e) { + // Remove failed, not much we can do about that + } + } + + return success; + } + + function isElement(obj) { + return div && obj && obj.nodeType === 1 && isDOMNode(obj); + } + + function isFunction(obj) { + return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply); + } + + function isReallyNaN(val) { + return typeof val === "number" && isNaN(val); + } + + function mirrorProperties(target, source) { + for (var prop in source) { + if (!hasOwn.call(target, prop)) { + target[prop] = source[prop]; + } + } + } + + function isRestorable(obj) { + return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon; + } + + // Cheap way to detect if we have ES5 support. + var hasES5Support = "keys" in Object; + + function makeApi(sinon) { + sinon.wrapMethod = function wrapMethod(object, property, method) { + if (!object) { + throw new TypeError("Should wrap property of object"); + } + + if (typeof method !== "function" && typeof method !== "object") { + throw new TypeError("Method wrapper should be a function or a property descriptor"); + } + + function checkWrappedMethod(wrappedMethod) { + var error; + + if (!isFunction(wrappedMethod)) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } else if (wrappedMethod.calledBefore) { + var verb = wrappedMethod.returns ? "stubbed" : "spied on"; + error = new TypeError("Attempted to wrap " + property + " which is already " + verb); + } + + if (error) { + if (wrappedMethod && wrappedMethod.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethod.stackTrace; + } + throw error; + } + } + + var error, wrappedMethod, i; + + // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem + // when using hasOwn.call on objects from other frames. + var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property); + + if (hasES5Support) { + var methodDesc = (typeof method === "function") ? {value: method} : method; + var wrappedMethodDesc = sinon.getPropertyDescriptor(object, property); + + if (!wrappedMethodDesc) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } + if (error) { + if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace; + } + throw error; + } + + var types = sinon.objectKeys(methodDesc); + for (i = 0; i < types.length; i++) { + wrappedMethod = wrappedMethodDesc[types[i]]; + checkWrappedMethod(wrappedMethod); + } + + mirrorProperties(methodDesc, wrappedMethodDesc); + for (i = 0; i < types.length; i++) { + mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]); + } + Object.defineProperty(object, property, methodDesc); + } else { + wrappedMethod = object[property]; + checkWrappedMethod(wrappedMethod); + object[property] = method; + method.displayName = property; + } + + method.displayName = property; + + // Set up a stack trace which can be used later to find what line of + // code the original method was created on. + method.stackTrace = (new Error("Stack Trace for original")).stack; + + method.restore = function () { + // For prototype properties try to reset by delete first. + // If this fails (ex: localStorage on mobile safari) then force a reset + // via direct assignment. + if (!owned) { + // In some cases `delete` may throw an error + try { + delete object[property]; + } catch (e) {} // eslint-disable-line no-empty + // For native code functions `delete` fails without throwing an error + // on Chrome < 43, PhantomJS, etc. + } else if (hasES5Support) { + Object.defineProperty(object, property, wrappedMethodDesc); + } + + // Use strict equality comparison to check failures then force a reset + // via direct assignment. + if (object[property] === method) { + object[property] = wrappedMethod; + } + }; + + method.restore.sinon = true; + + if (!hasES5Support) { + mirrorProperties(method, wrappedMethod); + } + + return method; + }; + + sinon.create = function create(proto) { + var F = function () {}; + F.prototype = proto; + return new F(); + }; + + sinon.deepEqual = function deepEqual(a, b) { + if (sinon.match && sinon.match.isMatcher(a)) { + return a.test(b); + } + + if (typeof a !== "object" || typeof b !== "object") { + return isReallyNaN(a) && isReallyNaN(b) || a === b; + } + + if (isElement(a) || isElement(b)) { + return a === b; + } + + if (a === b) { + return true; + } + + if ((a === null && b !== null) || (a !== null && b === null)) { + return false; + } + + if (a instanceof RegExp && b instanceof RegExp) { + return (a.source === b.source) && (a.global === b.global) && + (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline); + } + + var aString = Object.prototype.toString.call(a); + if (aString !== Object.prototype.toString.call(b)) { + return false; + } + + if (aString === "[object Date]") { + return a.valueOf() === b.valueOf(); + } + + var prop; + var aLength = 0; + var bLength = 0; + + if (aString === "[object Array]" && a.length !== b.length) { + return false; + } + + for (prop in a) { + if (a.hasOwnProperty(prop)) { + aLength += 1; + + if (!(prop in b)) { + return false; + } + + if (!deepEqual(a[prop], b[prop])) { + return false; + } + } + } + + for (prop in b) { + if (b.hasOwnProperty(prop)) { + bLength += 1; + } + } + + return aLength === bLength; + }; + + sinon.functionName = function functionName(func) { + var name = func.displayName || func.name; + + // Use function decomposition as a last resort to get function + // name. Does not rely on function decomposition to work - if it + // doesn't debugging will be slightly less informative + // (i.e. toString will say 'spy' rather than 'myFunc'). + if (!name) { + var matches = func.toString().match(/function ([^\s\(]+)/); + name = matches && matches[1]; + } + + return name; + }; + + sinon.functionToString = function toString() { + if (this.getCall && this.callCount) { + var thisValue, + prop; + var i = this.callCount; + + while (i--) { + thisValue = this.getCall(i).thisValue; + + for (prop in thisValue) { + if (thisValue[prop] === this) { + return prop; + } + } + } + } + + return this.displayName || "sinon fake"; + }; + + sinon.objectKeys = function objectKeys(obj) { + if (obj !== Object(obj)) { + throw new TypeError("sinon.objectKeys called on a non-object"); + } + + var keys = []; + var key; + for (key in obj) { + if (hasOwn.call(obj, key)) { + keys.push(key); + } + } + + return keys; + }; + + sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) { + var proto = object; + var descriptor; + + while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) { + proto = Object.getPrototypeOf(proto); + } + return descriptor; + }; + + sinon.getConfig = function (custom) { + var config = {}; + custom = custom || {}; + var defaults = sinon.defaultConfig; + + for (var prop in defaults) { + if (defaults.hasOwnProperty(prop)) { + config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; + } + } + + return config; + }; + + sinon.defaultConfig = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.timesInWords = function timesInWords(count) { + return count === 1 && "once" || + count === 2 && "twice" || + count === 3 && "thrice" || + (count || 0) + " times"; + }; + + sinon.calledInOrder = function (spies) { + for (var i = 1, l = spies.length; i < l; i++) { + if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) { + return false; + } + } + + return true; + }; + + sinon.orderByFirstCall = function (spies) { + return spies.sort(function (a, b) { + // uuid, won't ever be equal + var aCall = a.getCall(0); + var bCall = b.getCall(0); + var aId = aCall && aCall.callId || -1; + var bId = bCall && bCall.callId || -1; + + return aId < bId ? -1 : 1; + }); + }; + + sinon.createStubInstance = function (constructor) { + if (typeof constructor !== "function") { + throw new TypeError("The constructor should be a function."); + } + return sinon.stub(sinon.create(constructor.prototype)); + }; + + sinon.restore = function (object) { + if (object !== null && typeof object === "object") { + for (var prop in object) { + if (isRestorable(object[prop])) { + object[prop].restore(); + } + } + } else if (isRestorable(object)) { + object.restore(); + } + }; + + return sinon; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports) { + makeApi(exports); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/event.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/event.js new file mode 100644 index 0000000..6d60edd --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/event.js @@ -0,0 +1,111 @@ +/** + * Minimal Event interface implementation + * + * Original implementation by Sven Fuchs: https://gist.github.com/995028 + * Modifications and tests by Christian Johansen. + * + * @author Sven Fuchs (svenfuchs@artweb-design.de) + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2011 Sven Fuchs, Christian Johansen + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +(function () { + "use strict"; + + var push = [].push; + + function makeApi(sinon) { + sinon.Event = function Event(type, bubbles, cancelable, target) { + this.initEvent(type, bubbles, cancelable, target); + }; + + sinon.Event.prototype = { + initEvent: function (type, bubbles, cancelable, target) { + this.type = type; + this.bubbles = bubbles; + this.cancelable = cancelable; + this.target = target; + }, + + stopPropagation: function () {}, + + preventDefault: function () { + this.defaultPrevented = true; + } + }; + + sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) { + this.initEvent(type, false, false, target); + this.loaded = progressEventRaw.loaded || null; + this.total = progressEventRaw.total || null; + this.lengthComputable = !!progressEventRaw.total; + }; + + sinon.ProgressEvent.prototype = new sinon.Event(); + + sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent; + + sinon.CustomEvent = function CustomEvent(type, customData, target) { + this.initEvent(type, false, false, target); + this.detail = customData.detail || null; + }; + + sinon.CustomEvent.prototype = new sinon.Event(); + + sinon.CustomEvent.prototype.constructor = sinon.CustomEvent; + + sinon.EventTarget = { + addEventListener: function addEventListener(event, listener) { + this.eventListeners = this.eventListeners || {}; + this.eventListeners[event] = this.eventListeners[event] || []; + push.call(this.eventListeners[event], listener); + }, + + removeEventListener: function removeEventListener(event, listener) { + var listeners = this.eventListeners && this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }, + + dispatchEvent: function dispatchEvent(event) { + var type = event.type; + var listeners = this.eventListeners && this.eventListeners[type] || []; + + for (var i = 0; i < listeners.length; i++) { + if (typeof listeners[i] === "function") { + listeners[i].call(this, event); + } else { + listeners[i].handleEvent(event); + } + } + + return !!event.defaultPrevented; + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_server.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_server.js new file mode 100644 index 0000000..32be9c8 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_server.js @@ -0,0 +1,247 @@ +/** + * @depend fake_xdomain_request.js + * @depend fake_xml_http_request.js + * @depend ../format.js + * @depend ../log_error.js + */ +/** + * The Sinon "server" mimics a web server that receives requests from + * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, + * both synchronously and asynchronously. To respond synchronuously, canned + * answers have to be provided upfront. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + "use strict"; + + var push = [].push; + + function responseArray(handler) { + var response = handler; + + if (Object.prototype.toString.call(handler) !== "[object Array]") { + response = [200, {}, handler]; + } + + if (typeof response[2] !== "string") { + throw new TypeError("Fake server response body should be string, but was " + + typeof response[2]); + } + + return response; + } + + var wloc = typeof window !== "undefined" ? window.location : {}; + var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host); + + function matchOne(response, reqMethod, reqUrl) { + var rmeth = response.method; + var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase(); + var url = response.url; + var matchUrl = !url || url === reqUrl || (typeof url.test === "function" && url.test(reqUrl)); + + return matchMethod && matchUrl; + } + + function match(response, request) { + var requestUrl = request.url; + + if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { + requestUrl = requestUrl.replace(rCurrLoc, ""); + } + + if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { + if (typeof response.response === "function") { + var ru = response.url; + var args = [request].concat(ru && typeof ru.exec === "function" ? ru.exec(requestUrl).slice(1) : []); + return response.response.apply(response, args); + } + + return true; + } + + return false; + } + + function makeApi(sinon) { + sinon.fakeServer = { + create: function (config) { + var server = sinon.create(this); + server.configure(config); + if (!sinon.xhr.supportsCORS) { + this.xhr = sinon.useFakeXDomainRequest(); + } else { + this.xhr = sinon.useFakeXMLHttpRequest(); + } + server.requests = []; + + this.xhr.onCreate = function (xhrObj) { + server.addRequest(xhrObj); + }; + + return server; + }, + configure: function (config) { + var whitelist = { + "autoRespond": true, + "autoRespondAfter": true, + "respondImmediately": true, + "fakeHTTPMethods": true + }; + var setting; + + config = config || {}; + for (setting in config) { + if (whitelist.hasOwnProperty(setting) && config.hasOwnProperty(setting)) { + this[setting] = config[setting]; + } + } + }, + addRequest: function addRequest(xhrObj) { + var server = this; + push.call(this.requests, xhrObj); + + xhrObj.onSend = function () { + server.handleRequest(this); + + if (server.respondImmediately) { + server.respond(); + } else if (server.autoRespond && !server.responding) { + setTimeout(function () { + server.responding = false; + server.respond(); + }, server.autoRespondAfter || 10); + + server.responding = true; + } + }; + }, + + getHTTPMethod: function getHTTPMethod(request) { + if (this.fakeHTTPMethods && /post/i.test(request.method)) { + var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); + return matches ? matches[1] : request.method; + } + + return request.method; + }, + + handleRequest: function handleRequest(xhr) { + if (xhr.async) { + if (!this.queue) { + this.queue = []; + } + + push.call(this.queue, xhr); + } else { + this.processRequest(xhr); + } + }, + + log: function log(response, request) { + var str; + + str = "Request:\n" + sinon.format(request) + "\n\n"; + str += "Response:\n" + sinon.format(response) + "\n\n"; + + sinon.log(str); + }, + + respondWith: function respondWith(method, url, body) { + if (arguments.length === 1 && typeof method !== "function") { + this.response = responseArray(method); + return; + } + + if (!this.responses) { + this.responses = []; + } + + if (arguments.length === 1) { + body = method; + url = method = null; + } + + if (arguments.length === 2) { + body = url; + url = method; + method = null; + } + + push.call(this.responses, { + method: method, + url: url, + response: typeof body === "function" ? body : responseArray(body) + }); + }, + + respond: function respond() { + if (arguments.length > 0) { + this.respondWith.apply(this, arguments); + } + + var queue = this.queue || []; + var requests = queue.splice(0, queue.length); + + for (var i = 0; i < requests.length; i++) { + this.processRequest(requests[i]); + } + }, + + processRequest: function processRequest(request) { + try { + if (request.aborted) { + return; + } + + var response = this.response || [404, {}, ""]; + + if (this.responses) { + for (var l = this.responses.length, i = l - 1; i >= 0; i--) { + if (match.call(this, this.responses[i], request)) { + response = this.responses[i].response; + break; + } + } + } + + if (request.readyState !== 4) { + this.log(response, request); + + request.respond(response[0], response[1], response[2]); + } + } catch (e) { + sinon.logError("Fake server request processing", e); + } + }, + + restore: function restore() { + return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("./fake_xdomain_request"); + require("./fake_xml_http_request"); + require("../format"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_server_with_clock.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_server_with_clock.js new file mode 100644 index 0000000..730555e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_server_with_clock.js @@ -0,0 +1,101 @@ +/** + * @depend fake_server.js + * @depend fake_timers.js + */ +/** + * Add-on for sinon.fakeServer that automatically handles a fake timer along with + * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery + * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, + * it polls the object for completion with setInterval. Dispite the direct + * motivation, there is nothing jQuery-specific in this file, so it can be used + * in any environment where the ajax implementation depends on setInterval or + * setTimeout. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + "use strict"; + + function makeApi(sinon) { + function Server() {} + Server.prototype = sinon.fakeServer; + + sinon.fakeServerWithClock = new Server(); + + sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { + if (xhr.async) { + if (typeof setTimeout.clock === "object") { + this.clock = setTimeout.clock; + } else { + this.clock = sinon.useFakeTimers(); + this.resetClock = true; + } + + if (!this.longestTimeout) { + var clockSetTimeout = this.clock.setTimeout; + var clockSetInterval = this.clock.setInterval; + var server = this; + + this.clock.setTimeout = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetTimeout.apply(this, arguments); + }; + + this.clock.setInterval = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetInterval.apply(this, arguments); + }; + } + } + + return sinon.fakeServer.addRequest.call(this, xhr); + }; + + sinon.fakeServerWithClock.respond = function respond() { + var returnVal = sinon.fakeServer.respond.apply(this, arguments); + + if (this.clock) { + this.clock.tick(this.longestTimeout || 0); + this.longestTimeout = 0; + + if (this.resetClock) { + this.clock.restore(); + this.resetClock = false; + } + } + + return returnVal; + }; + + sinon.fakeServerWithClock.restore = function restore() { + if (this.clock) { + this.clock.restore(); + } + + return sinon.fakeServer.restore.apply(this, arguments); + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + require("./fake_server"); + require("./fake_timers"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_timers.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_timers.js new file mode 100644 index 0000000..7e11536 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_timers.js @@ -0,0 +1,73 @@ +/** + * Fake timer API + * setTimeout + * setInterval + * clearTimeout + * clearInterval + * tick + * reset + * Date + * + * Inspired by jsUnitMockTimeOut from JsUnit + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + "use strict"; + + function makeApi(s, lol) { + /*global lolex */ + var llx = typeof lolex !== "undefined" ? lolex : lol; + + s.useFakeTimers = function () { + var now; + var methods = Array.prototype.slice.call(arguments); + + if (typeof methods[0] === "string") { + now = 0; + } else { + now = methods.shift(); + } + + var clock = llx.install(now || 0, methods); + clock.restore = clock.uninstall; + return clock; + }; + + s.clock = { + create: function (now) { + return llx.createClock(now); + } + }; + + s.timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), + clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined), + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, epxorts, module, lolex) { + var core = require("./core"); + makeApi(core, lolex); + module.exports = core; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module, require("lolex")); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_xdomain_request.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_xdomain_request.js new file mode 100644 index 0000000..f298a20 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_xdomain_request.js @@ -0,0 +1,223 @@ +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XDomainRequest object + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +// wrapper for global +(function (global) { + "use strict"; + + var xdr = { XDomainRequest: global.XDomainRequest }; + xdr.GlobalXDomainRequest = global.XDomainRequest; + xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== "undefined"; + xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false; + + function makeApi(sinon) { + sinon.xdr = xdr; + + function FakeXDomainRequest() { + this.readyState = FakeXDomainRequest.UNSENT; + this.requestBody = null; + this.requestHeaders = {}; + this.status = 0; + this.timeout = null; + + if (typeof FakeXDomainRequest.onCreate === "function") { + FakeXDomainRequest.onCreate(this); + } + } + + function verifyState(x) { + if (x.readyState !== FakeXDomainRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (x.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function verifyRequestSent(x) { + if (x.readyState === FakeXDomainRequest.UNSENT) { + throw new Error("Request not sent"); + } + if (x.readyState === FakeXDomainRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XDomainRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, { + open: function open(method, url) { + this.method = method; + this.url = url; + + this.responseText = null; + this.sendFlag = false; + + this.readyStateChange(FakeXDomainRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + var eventName = ""; + switch (this.readyState) { + case FakeXDomainRequest.UNSENT: + break; + case FakeXDomainRequest.OPENED: + break; + case FakeXDomainRequest.LOADING: + if (this.sendFlag) { + //raise the progress event + eventName = "onprogress"; + } + break; + case FakeXDomainRequest.DONE: + if (this.isTimeout) { + eventName = "ontimeout"; + } else if (this.errorFlag || (this.status < 200 || this.status > 299)) { + eventName = "onerror"; + } else { + eventName = "onload"; + } + break; + } + + // raising event (if defined) + if (eventName) { + if (typeof this[eventName] === "function") { + try { + this[eventName](); + } catch (e) { + sinon.logError("Fake XHR " + eventName + " handler", e); + } + } + } + }, + + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + this.requestBody = data; + } + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + + this.errorFlag = false; + this.sendFlag = true; + this.readyStateChange(FakeXDomainRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + + if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) { + this.readyStateChange(sinon.FakeXDomainRequest.DONE); + this.sendFlag = false; + } + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyResponseBodyType(body); + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + this.readyStateChange(FakeXDomainRequest.LOADING); + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + this.readyStateChange(FakeXDomainRequest.DONE); + }, + + respond: function respond(status, contentType, body) { + // content-type ignored, since XDomainRequest does not carry this + // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease + // test integration across browsers + this.status = typeof status === "number" ? status : 200; + this.setResponseBody(body || ""); + }, + + simulatetimeout: function simulatetimeout() { + this.status = 0; + this.isTimeout = true; + // Access to this should actually throw an error + this.responseText = undefined; + this.readyStateChange(FakeXDomainRequest.DONE); + } + }); + + sinon.extend(FakeXDomainRequest, { + UNSENT: 0, + OPENED: 1, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXDomainRequest = function useFakeXDomainRequest() { + sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) { + if (xdr.supportsXDR) { + global.XDomainRequest = xdr.GlobalXDomainRequest; + } + + delete sinon.FakeXDomainRequest.restore; + + if (keepOnCreate !== true) { + delete sinon.FakeXDomainRequest.onCreate; + } + }; + if (xdr.supportsXDR) { + global.XDomainRequest = sinon.FakeXDomainRequest; + } + return sinon.FakeXDomainRequest; + }; + + sinon.FakeXDomainRequest = FakeXDomainRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +})(typeof global !== "undefined" ? global : self); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_xml_http_request.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_xml_http_request.js new file mode 100644 index 0000000..1598007 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/fake_xml_http_request.js @@ -0,0 +1,716 @@ +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XMLHttpRequest object + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal, global) { + "use strict"; + + function getWorkingXHR(globalScope) { + var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined"; + if (supportsXHR) { + return globalScope.XMLHttpRequest; + } + + var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined"; + if (supportsActiveX) { + return function () { + return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0"); + }; + } + + return false; + } + + var supportsProgress = typeof ProgressEvent !== "undefined"; + var supportsCustomEvent = typeof CustomEvent !== "undefined"; + var supportsFormData = typeof FormData !== "undefined"; + var supportsArrayBuffer = typeof ArrayBuffer !== "undefined"; + var supportsBlob = typeof Blob === "function"; + var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest }; + sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest; + sinonXhr.GlobalActiveXObject = global.ActiveXObject; + sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== "undefined"; + sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined"; + sinonXhr.workingXHR = getWorkingXHR(global); + sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest()); + + var unsafeHeaders = { + "Accept-Charset": true, + "Accept-Encoding": true, + Connection: true, + "Content-Length": true, + Cookie: true, + Cookie2: true, + "Content-Transfer-Encoding": true, + Date: true, + Expect: true, + Host: true, + "Keep-Alive": true, + Referer: true, + TE: true, + Trailer: true, + "Transfer-Encoding": true, + Upgrade: true, + "User-Agent": true, + Via: true + }; + + // An upload object is created for each + // FakeXMLHttpRequest and allows upload + // events to be simulated using uploadProgress + // and uploadError. + function UploadProgress() { + this.eventListeners = { + progress: [], + load: [], + abort: [], + error: [] + }; + } + + UploadProgress.prototype.addEventListener = function addEventListener(event, listener) { + this.eventListeners[event].push(listener); + }; + + UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) { + var listeners = this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }; + + UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) { + var listeners = this.eventListeners[event.type] || []; + + for (var i = 0, listener; (listener = listeners[i]) != null; i++) { + listener(event); + } + }; + + // Note that for FakeXMLHttpRequest to work pre ES5 + // we lose some of the alignment with the spec. + // To ensure as close a match as possible, + // set responseType before calling open, send or respond; + function FakeXMLHttpRequest() { + this.readyState = FakeXMLHttpRequest.UNSENT; + this.requestHeaders = {}; + this.requestBody = null; + this.status = 0; + this.statusText = ""; + this.upload = new UploadProgress(); + this.responseType = ""; + this.response = ""; + if (sinonXhr.supportsCORS) { + this.withCredentials = false; + } + + var xhr = this; + var events = ["loadstart", "load", "abort", "loadend"]; + + function addEventListener(eventName) { + xhr.addEventListener(eventName, function (event) { + var listener = xhr["on" + eventName]; + + if (listener && typeof listener === "function") { + listener.call(this, event); + } + }); + } + + for (var i = events.length - 1; i >= 0; i--) { + addEventListener(events[i]); + } + + if (typeof FakeXMLHttpRequest.onCreate === "function") { + FakeXMLHttpRequest.onCreate(this); + } + } + + function verifyState(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xhr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function getHeader(headers, header) { + header = header.toLowerCase(); + + for (var h in headers) { + if (h.toLowerCase() === header) { + return h; + } + } + + return null; + } + + // filtering to enable a white-list version of Sinon FakeXhr, + // where whitelisted requests are passed through to real XHR + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + function some(collection, callback) { + for (var index = 0; index < collection.length; index++) { + if (callback(collection[index]) === true) { + return true; + } + } + return false; + } + // largest arity in XHR is 5 - XHR#open + var apply = function (obj, method, args) { + switch (args.length) { + case 0: return obj[method](); + case 1: return obj[method](args[0]); + case 2: return obj[method](args[0], args[1]); + case 3: return obj[method](args[0], args[1], args[2]); + case 4: return obj[method](args[0], args[1], args[2], args[3]); + case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]); + } + }; + + FakeXMLHttpRequest.filters = []; + FakeXMLHttpRequest.addFilter = function addFilter(fn) { + this.filters.push(fn); + }; + var IE6Re = /MSIE 6/; + FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) { + var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap + + each([ + "open", + "setRequestHeader", + "send", + "abort", + "getResponseHeader", + "getAllResponseHeaders", + "addEventListener", + "overrideMimeType", + "removeEventListener" + ], function (method) { + fakeXhr[method] = function () { + return apply(xhr, method, arguments); + }; + }); + + var copyAttrs = function (args) { + each(args, function (attr) { + try { + fakeXhr[attr] = xhr[attr]; + } catch (e) { + if (!IE6Re.test(navigator.userAgent)) { + throw e; + } + } + }); + }; + + var stateChange = function stateChange() { + fakeXhr.readyState = xhr.readyState; + if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { + copyAttrs(["status", "statusText"]); + } + if (xhr.readyState >= FakeXMLHttpRequest.LOADING) { + copyAttrs(["responseText", "response"]); + } + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + copyAttrs(["responseXML"]); + } + if (fakeXhr.onreadystatechange) { + fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr }); + } + }; + + if (xhr.addEventListener) { + for (var event in fakeXhr.eventListeners) { + if (fakeXhr.eventListeners.hasOwnProperty(event)) { + + /*eslint-disable no-loop-func*/ + each(fakeXhr.eventListeners[event], function (handler) { + xhr.addEventListener(event, handler); + }); + /*eslint-enable no-loop-func*/ + } + } + xhr.addEventListener("readystatechange", stateChange); + } else { + xhr.onreadystatechange = stateChange; + } + apply(xhr, "open", xhrArgs); + }; + FakeXMLHttpRequest.useFilters = false; + + function verifyRequestOpened(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR - " + xhr.readyState); + } + } + + function verifyRequestSent(xhr) { + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyHeadersReceived(xhr) { + if (xhr.async && xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) { + throw new Error("No headers received"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XMLHttpRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + function convertToArrayBuffer(body) { + var buffer = new ArrayBuffer(body.length); + var view = new Uint8Array(buffer); + for (var i = 0; i < body.length; i++) { + var charCode = body.charCodeAt(i); + if (charCode >= 256) { + throw new TypeError("arraybuffer or blob responseTypes require binary string, " + + "invalid character " + body[i] + " found."); + } + view[i] = charCode; + } + return buffer; + } + + function isXmlContentType(contentType) { + return !contentType || /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType); + } + + function convertResponseBody(responseType, contentType, body) { + if (responseType === "" || responseType === "text") { + return body; + } else if (supportsArrayBuffer && responseType === "arraybuffer") { + return convertToArrayBuffer(body); + } else if (responseType === "json") { + try { + return JSON.parse(body); + } catch (e) { + // Return parsing failure as null + return null; + } + } else if (supportsBlob && responseType === "blob") { + var blobOptions = {}; + if (contentType) { + blobOptions.type = contentType; + } + return new Blob([convertToArrayBuffer(body)], blobOptions); + } else if (responseType === "document") { + if (isXmlContentType(contentType)) { + return FakeXMLHttpRequest.parseXML(body); + } + return null; + } + throw new Error("Invalid responseType " + responseType); + } + + function clearResponse(xhr) { + if (xhr.responseType === "" || xhr.responseType === "text") { + xhr.response = xhr.responseText = ""; + } else { + xhr.response = xhr.responseText = null; + } + xhr.responseXML = null; + } + + FakeXMLHttpRequest.parseXML = function parseXML(text) { + // Treat empty string as parsing failure + if (text !== "") { + try { + if (typeof DOMParser !== "undefined") { + var parser = new DOMParser(); + return parser.parseFromString(text, "text/xml"); + } + var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(text); + return xmlDoc; + } catch (e) { + // Unable to parse XML - no biggie + } + } + + return null; + }; + + FakeXMLHttpRequest.statusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 300: "Multiple Choice", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 422: "Unprocessable Entity", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" + }; + + function makeApi(sinon) { + sinon.xhr = sinonXhr; + + sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { + async: true, + + open: function open(method, url, async, username, password) { + this.method = method; + this.url = url; + this.async = typeof async === "boolean" ? async : true; + this.username = username; + this.password = password; + clearResponse(this); + this.requestHeaders = {}; + this.sendFlag = false; + + if (FakeXMLHttpRequest.useFilters === true) { + var xhrArgs = arguments; + var defake = some(FakeXMLHttpRequest.filters, function (filter) { + return filter.apply(this, xhrArgs); + }); + if (defake) { + return FakeXMLHttpRequest.defake(this, arguments); + } + } + this.readyStateChange(FakeXMLHttpRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + + var readyStateChangeEvent = new sinon.Event("readystatechange", false, false, this); + + if (typeof this.onreadystatechange === "function") { + try { + this.onreadystatechange(readyStateChangeEvent); + } catch (e) { + sinon.logError("Fake XHR onreadystatechange handler", e); + } + } + + switch (this.readyState) { + case FakeXMLHttpRequest.DONE: + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + this.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + } + this.upload.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("loadend", false, false, this)); + break; + } + + this.dispatchEvent(readyStateChangeEvent); + }, + + setRequestHeader: function setRequestHeader(header, value) { + verifyState(this); + + if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { + throw new Error("Refused to set unsafe header \"" + header + "\""); + } + + if (this.requestHeaders[header]) { + this.requestHeaders[header] += "," + value; + } else { + this.requestHeaders[header] = value; + } + }, + + // Helps testing + setResponseHeaders: function setResponseHeaders(headers) { + verifyRequestOpened(this); + this.responseHeaders = {}; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + this.responseHeaders[header] = headers[header]; + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); + } else { + this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; + } + }, + + // Currently treats ALL data as a DOMString (i.e. no Document) + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + var contentType = getHeader(this.requestHeaders, "Content-Type"); + if (this.requestHeaders[contentType]) { + var value = this.requestHeaders[contentType].split(";"); + this.requestHeaders[contentType] = value[0] + ";charset=utf-8"; + } else if (supportsFormData && !(data instanceof FormData)) { + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + } + + this.requestBody = data; + } + + this.errorFlag = false; + this.sendFlag = this.async; + clearResponse(this); + this.readyStateChange(FakeXMLHttpRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + + this.dispatchEvent(new sinon.Event("loadstart", false, false, this)); + }, + + abort: function abort() { + this.aborted = true; + clearResponse(this); + this.errorFlag = true; + this.requestHeaders = {}; + this.responseHeaders = {}; + + if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) { + this.readyStateChange(FakeXMLHttpRequest.DONE); + this.sendFlag = false; + } + + this.readyState = FakeXMLHttpRequest.UNSENT; + + this.dispatchEvent(new sinon.Event("abort", false, false, this)); + + this.upload.dispatchEvent(new sinon.Event("abort", false, false, this)); + + if (typeof this.onerror === "function") { + this.onerror(); + } + }, + + getResponseHeader: function getResponseHeader(header) { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return null; + } + + if (/^Set-Cookie2?$/i.test(header)) { + return null; + } + + header = getHeader(this.responseHeaders, header); + + return this.responseHeaders[header] || null; + }, + + getAllResponseHeaders: function getAllResponseHeaders() { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return ""; + } + + var headers = ""; + + for (var header in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(header) && + !/^Set-Cookie2?$/i.test(header)) { + headers += header + ": " + this.responseHeaders[header] + "\r\n"; + } + } + + return headers; + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyHeadersReceived(this); + verifyResponseBodyType(body); + var contentType = this.getResponseHeader("Content-Type"); + + var isTextResponse = this.responseType === "" || this.responseType === "text"; + clearResponse(this); + if (this.async) { + var chunkSize = this.chunkSize || 10; + var index = 0; + + do { + this.readyStateChange(FakeXMLHttpRequest.LOADING); + + if (isTextResponse) { + this.responseText = this.response += body.substring(index, index + chunkSize); + } + index += chunkSize; + } while (index < body.length); + } + + this.response = convertResponseBody(this.responseType, contentType, body); + if (isTextResponse) { + this.responseText = this.response; + } + + if (this.responseType === "document") { + this.responseXML = this.response; + } else if (this.responseType === "" && isXmlContentType(contentType)) { + this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); + } + this.readyStateChange(FakeXMLHttpRequest.DONE); + }, + + respond: function respond(status, headers, body) { + this.status = typeof status === "number" ? status : 200; + this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; + this.setResponseHeaders(headers || {}); + this.setResponseBody(body || ""); + }, + + uploadProgress: function uploadProgress(progressEventRaw) { + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + downloadProgress: function downloadProgress(progressEventRaw) { + if (supportsProgress) { + this.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + uploadError: function uploadError(error) { + if (supportsCustomEvent) { + this.upload.dispatchEvent(new sinon.CustomEvent("error", {detail: error})); + } + } + }); + + sinon.extend(FakeXMLHttpRequest, { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXMLHttpRequest = function () { + FakeXMLHttpRequest.restore = function restore(keepOnCreate) { + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = sinonXhr.GlobalActiveXObject; + } + + delete FakeXMLHttpRequest.restore; + + if (keepOnCreate !== true) { + delete FakeXMLHttpRequest.onCreate; + } + }; + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = FakeXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = function ActiveXObject(objId) { + if (objId === "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { + + return new FakeXMLHttpRequest(); + } + + return new sinonXhr.GlobalActiveXObject(objId); + }; + } + + return FakeXMLHttpRequest; + }; + + sinon.FakeXMLHttpRequest = FakeXMLHttpRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof global !== "undefined" ? global : self +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/timers_ie.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/timers_ie.js new file mode 100644 index 0000000..f1eb9fc --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/timers_ie.js @@ -0,0 +1,35 @@ +/** + * Helps IE run the fake timers. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake timers to work in IE, don't include this file. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +/*eslint-disable strict, no-inner-declarations, no-unused-vars*/ +if (typeof window !== "undefined") { + function setTimeout() {} + function clearTimeout() {} + function setImmediate() {} + function clearImmediate() {} + function setInterval() {} + function clearInterval() {} + function Date() {} + + // Reassign the original functions. Now their writable attribute + // should be true. Hackish, I know, but it works. + /*global sinon*/ + setTimeout = sinon.timers.setTimeout; + clearTimeout = sinon.timers.clearTimeout; + setImmediate = sinon.timers.setImmediate; + clearImmediate = sinon.timers.clearImmediate; + setInterval = sinon.timers.setInterval; + clearInterval = sinon.timers.clearInterval; + Date = sinon.timers.Date; // eslint-disable-line no-native-reassign +} +/*eslint-enable no-inner-declarations*/ diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/xdr_ie.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/xdr_ie.js new file mode 100644 index 0000000..13bbfc5 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/xdr_ie.js @@ -0,0 +1,18 @@ +/** + * Helps IE run the fake XDomainRequest. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake XDR to work in IE, don't include this file. + */ +/*eslint-disable strict*/ +if (typeof window !== "undefined") { + function XDomainRequest() {} // eslint-disable-line no-unused-vars, no-inner-declarations + + // Reassign the original function. Now its writable attribute + // should be true. Hackish, I know, but it works. + /*global sinon*/ + XDomainRequest = sinon.xdr.XDomainRequest || undefined; +} +/*eslint-enable strict*/ diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/xhr_ie.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/xhr_ie.js new file mode 100644 index 0000000..fe72894 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/util/xhr_ie.js @@ -0,0 +1,23 @@ +/** + * Helps IE run the fake XMLHttpRequest. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake XHR to work in IE, don't include this file. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +/*eslint-disable strict*/ +if (typeof window !== "undefined") { + function XMLHttpRequest() {} // eslint-disable-line no-unused-vars, no-inner-declarations + + // Reassign the original function. Now its writable attribute + // should be true. Hackish, I know, but it works. + /*global sinon*/ + XMLHttpRequest = sinon.xhr.XMLHttpRequest || undefined; +} +/*eslint-enable strict*/ diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/walk.js b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/walk.js new file mode 100644 index 0000000..b03d2bd --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/lib/sinon/walk.js @@ -0,0 +1,79 @@ +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + function walkInternal(obj, iterator, context, originalObj, seen) { + var proto, prop; + + if (typeof Object.getOwnPropertyNames !== "function") { + // We explicitly want to enumerate through all of the prototype's properties + // in this case, therefore we deliberately leave out an own property check. + /* eslint-disable guard-for-in */ + for (prop in obj) { + iterator.call(context, obj[prop], prop, obj); + } + /* eslint-enable guard-for-in */ + + return; + } + + Object.getOwnPropertyNames(obj).forEach(function (k) { + if (!seen[k]) { + seen[k] = true; + var target = typeof Object.getOwnPropertyDescriptor(obj, k).get === "function" ? + originalObj : obj; + iterator.call(context, target[k], k, target); + } + }); + + proto = Object.getPrototypeOf(obj); + if (proto) { + walkInternal(proto, iterator, context, originalObj, seen); + } + } + + /* Public: walks the prototype chain of an object and iterates over every own property + * name encountered. The iterator is called in the same fashion that Array.prototype.forEach + * works, where it is passed the value, key, and own object as the 1st, 2nd, and 3rd positional + * argument, respectively. In cases where Object.getOwnPropertyNames is not available, walk will + * default to using a simple for..in loop. + * + * obj - The object to walk the prototype chain for. + * iterator - The function to be called on each pass of the walk. + * context - (Optional) When given, the iterator will be called with this object as the receiver. + */ + function walk(obj, iterator, context) { + return walkInternal(obj, iterator, context, obj, {}); + } + + sinon.walk = walk; + return sinon.walk; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/.travis.yml b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/.travis.yml new file mode 100644 index 0000000..20fd86b --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - 0.10 diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/AUTHORS b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/AUTHORS new file mode 100644 index 0000000..103c176 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/AUTHORS @@ -0,0 +1,6 @@ +Buster.JS Format was written by +Christian Johansen, christian@cjohansen.no +August Lilleaas, august.lilleaas@gmail.com +Dave Geddes, davidcgeddes@gmail.com +Stein Magnus Jodal, stein.magnus@jodal.no +Tek Nynja, github@teknynja.com diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/LICENSE b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/LICENSE new file mode 100644 index 0000000..d5908f3 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/LICENSE @@ -0,0 +1,27 @@ +(The BSD License) + +Copyright (c) 2010-2012, Christian Johansen (christian@cjohansen.no) and +August Lilleaas (august.lilleaas@gmail.com). All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Christian Johansen nor the names of his contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/Readme.md b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/Readme.md new file mode 100644 index 0000000..91cdf7b --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/Readme.md @@ -0,0 +1,244 @@ +# formatio + +[![Build status](https://secure.travis-ci.org/busterjs/formatio.png?branch=master)](http://travis-ci.org/busterjs/formatio) + +> The cheesy object formatter + +Pretty formatting of arbitrary JavaScript values. Currently only supports ascii +formatting, suitable for command-line utilities. Like `JSON.stringify`, it +formats objects recursively, but unlike `JSON.stringify`, it can handle +regular expressions, functions, circular objects and more. + +`formatio` is a general-purpose library. It works in browsers (including old +and rowdy ones, like IE6) and Node. It will define itself as an AMD module if +you want it to (i.e. if there's a `define` function available). + + +## Running tests + +``` +npm test +``` + +Or use Buster.JS manually: + +``` +node_modules/buster/bin/buster-test --help +``` + + +## `formatio.ascii` API + +`formatio.ascii` can take any JavaScript object and format it nicely as plain +text. It uses the helper functions described below to format different types of +objects. + + +### `formatio.ascii(object)` + +`object` can be any kind of object, including DOM elements. + + +**Simple object** + +```javascript +var formatio = require("formatio"); + +var object = { name: "Christian" }; +console.log(formatio.ascii(object)); + +// Outputs: +// { name: "Christian" } +``` + + +**Complex object** + +```javascript +var formatio = require("formatio"); + +var developer = { + name: "Christian", + interests: ["Programming", "Guitar", "TV"], + + location: { + language: "Norway", + city: "Oslo", + + getLatLon: function getLatLon(callback) { + // ... + }, + + distanceTo: function distanceTo(location) { + } + }, + + speak: function () { + return "Oh hi!"; + } +}; + +console.log(formatio.ascii(developer)); + +// Outputs: +// { +// interests: ["Programming", "Guitar", "TV"], +// location: { +// city: "Oslo", +// distanceTo: function distanceTo() {}, +// getLatLon: function getLatLon() {}, +// language: "Norway" +// }, +// name: "Christian", +// speak: function () {} +// } +``` + + +**Custom constructor** + +If the object to format is not a generic `Object` object, **formatio** +displays the type of object (i.e. name of constructor). Set the +`excludeConstructors` (see below) property to control what constructors to +include in formatted output. + +```javascript +var formatio = require("formatio"); + +function Person(name) { this.name = name; } + +var dude = new Person("Dude"); +console.log(format.ascii(dude)); + +// Outputs: +// [Person] { name: "Dude" } +``` + + +**DOM elements** + +DOM elements are formatted as abbreviated HTML source. 20 characters of +`innerHTML` is included, and if the content is longer, it is truncated with +`"[...]"`. Future editions will add the possibility to format nested markup +structures. + +```javascript +var p = document.createElement("p"); +p.id = "sample"; +p.className = "notice"; +p.setAttribute("data-custom", "42"); +p.innerHTML = "Hey there, here's some text for ya there buddy"; + +console.log(formatio.ascii(p)); + +// Outputs +// <p id="sample" class="notice" data-custom="42">Hey there, here's so[...]</p> +``` + + +### `formatio.ascii.func(func)` + +Formats a function like `"function [name]() {}"`. The name is retrieved from +`formatio.functionName`. + + +### `formatio.ascii.array(array)` + +Formats an array as `"[item1, item2, item3]"` where each item is formatted +with `formatio.ascii`. Circular references are represented in the resulting +string as `"[Circular]"`. + + +### `formatio.ascii.object(object)` + +Formats all properties of the object with `formatio.ascii`. If the object can +be fully represented in 80 characters, it's formatted in one line. Otherwise, +it's nicely indented over as many lines as necessary. Circular references are +represented by `"[Circular]"`. + +Objects created with custom constructors will be formatted as +`"[ConstructorName] { ... }"`. Set the `excludeConstructors` property to +control what constructors are included in the output like this. + + +### `formatio.ascii.element(element)` + +Formats a DOM element as HTML source. The tag name is represented in lower-case +and all attributes and their values are included. The element's content is +included, up to 20 characters. If the length exceeds 20 characters, it's +truncated with a `"[...]"`. + + +### `formatio.functionName(func)` + +Guesses a function's name. If the function defines the `displayName` property +(used by `some debugging tools `_) it is +preferred. If it is not found, the `name` property is tried. If no name can be +found this way, an attempt is made to find the function name by looking at the +function's `toString()` representation. + + +### `formatio.constructorName(object)` + +Attempts to guess the name of the constructor that created the object. It does +so by getting the name of `object.constructor` using `functionName`. If a +name is found, `excludeConstructors` is consulted. If the constructor name +matches any of these elements, an empty string is returned, otherwise the name +is returned. + + +## `formatio.ascii` properties + +### `quoteStrings(true)` + +Whether or not to quote simple strings. When set to `false`, simple strings +are not quoted. Strings in arrays and objects will still be quoted, but +`ascii("Some string")` will not gain additional quotes. + +### `limitChildrenCount(number)` + +This property allows to limit the number of printed array elements or object +properties. When set to 0, all elements will be included in output, any number +greater than zero will set the limit to that number. + +### `excludeConstructors (["Object", /^.$/])` + +An array of strings and/or regular expressions naming constructors that should +be stripped from the formatted output. The default value skips objects created +by `Object` and constructors that have one character names (which are +typically used in `Object.create` shims). + +While you can set this property directly on `formatio.ascii`, it is +recommended to create an instance of `formatio.ascii` and override the +property on that object. + +**Strings** represent constructor names that should not be represented in the +formatted output. **Regular expressions** are tested against constructor names +when formatting. If the expression is a match, the constructor name is not +included in the formatted output. + +```javascript +function Person(name) { + this.name = name; +} + +var person = new Person("Chris"); +console.log(formatio.ascii(person)); + +// Outputs +// [Person] { name: "Chris" } + +var formatter = Object.create(formatio); +formatter.excludeConstructors = ["Object", /^.$/, "Person"]; +console.log(formatter.ascii(person)); + +// Outputs +// { name: "Chris" } + +// Global overwrite, generally not recommended +formatio.excludeConstructors = ["Object", /^.$/, "Person"]; +console.log(formatio.ascii(person)); + +// Outputs +// { name: "Chris" } +``` diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/autolint.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/autolint.js new file mode 100644 index 0000000..62ded41 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/autolint.js @@ -0,0 +1,23 @@ +module.exports = { + paths: [ + "lib/*.js", + "test/*.js" + ], + linterOptions: { + node: true, + browser: true, + plusplus: true, + vars: true, + nomen: true, + forin: true, + sloppy: true, + regexp: true, + predef: [ + "samsam", + "define", + "assert", + "refute", + "buster" + ] + } +}; diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/buster.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/buster.js new file mode 100644 index 0000000..697bab1 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/buster.js @@ -0,0 +1,14 @@ +exports["Browser"] = { + // TODO: Needs fixing + environment: "browser", + libs: [ + "node_modules/samsam/lib/samsam.js" + ], + sources: ["lib/*.js"], + tests: ["test/*-test.js"] +}; + +exports["Node"] = { + extends: "Browser", + environment: "node" +}; diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/lib/formatio.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/lib/formatio.js new file mode 100644 index 0000000..ffe234a --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/lib/formatio.js @@ -0,0 +1,213 @@ +((typeof define === "function" && define.amd && function (m) { + define("formatio", ["samsam"], m); +}) || (typeof module === "object" && function (m) { + module.exports = m(require("samsam")); +}) || function (m) { this.formatio = m(this.samsam); } +)(function (samsam) { + "use strict"; + + var formatio = { + excludeConstructors: ["Object", /^.$/], + quoteStrings: true, + limitChildrenCount: 0 + }; + + var hasOwn = Object.prototype.hasOwnProperty; + + var specialObjects = []; + if (typeof global !== "undefined") { + specialObjects.push({ object: global, value: "[object global]" }); + } + if (typeof document !== "undefined") { + specialObjects.push({ + object: document, + value: "[object HTMLDocument]" + }); + } + if (typeof window !== "undefined") { + specialObjects.push({ object: window, value: "[object Window]" }); + } + + function functionName(func) { + if (!func) { return ""; } + if (func.displayName) { return func.displayName; } + if (func.name) { return func.name; } + var matches = func.toString().match(/function\s+([^\(]+)/m); + return (matches && matches[1]) || ""; + } + + function constructorName(f, object) { + var name = functionName(object && object.constructor); + var excludes = f.excludeConstructors || + formatio.excludeConstructors || []; + + var i, l; + for (i = 0, l = excludes.length; i < l; ++i) { + if (typeof excludes[i] === "string" && excludes[i] === name) { + return ""; + } else if (excludes[i].test && excludes[i].test(name)) { + return ""; + } + } + + return name; + } + + function isCircular(object, objects) { + if (typeof object !== "object") { return false; } + var i, l; + for (i = 0, l = objects.length; i < l; ++i) { + if (objects[i] === object) { return true; } + } + return false; + } + + function ascii(f, object, processed, indent) { + if (typeof object === "string") { + var qs = f.quoteStrings; + var quote = typeof qs !== "boolean" || qs; + return processed || quote ? '"' + object + '"' : object; + } + + if (typeof object === "function" && !(object instanceof RegExp)) { + return ascii.func(object); + } + + processed = processed || []; + + if (isCircular(object, processed)) { return "[Circular]"; } + + if (Object.prototype.toString.call(object) === "[object Array]") { + return ascii.array.call(f, object, processed); + } + + if (!object) { return String((1/object) === -Infinity ? "-0" : object); } + if (samsam.isElement(object)) { return ascii.element(object); } + + if (typeof object.toString === "function" && + object.toString !== Object.prototype.toString) { + return object.toString(); + } + + var i, l; + for (i = 0, l = specialObjects.length; i < l; i++) { + if (object === specialObjects[i].object) { + return specialObjects[i].value; + } + } + + return ascii.object.call(f, object, processed, indent); + } + + ascii.func = function (func) { + return "function " + functionName(func) + "() {}"; + }; + + ascii.array = function (array, processed) { + processed = processed || []; + processed.push(array); + var pieces = []; + var i, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, array.length) : array.length; + + for (i = 0; i < l; ++i) { + pieces.push(ascii(this, array[i], processed)); + } + + if(l < array.length) + pieces.push("[... " + (array.length - l) + " more elements]"); + + return "[" + pieces.join(", ") + "]"; + }; + + ascii.object = function (object, processed, indent) { + processed = processed || []; + processed.push(object); + indent = indent || 0; + var pieces = [], properties = samsam.keys(object).sort(); + var length = 3; + var prop, str, obj, i, k, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, properties.length) : properties.length; + + for (i = 0; i < l; ++i) { + prop = properties[i]; + obj = object[prop]; + + if (isCircular(obj, processed)) { + str = "[Circular]"; + } else { + str = ascii(this, obj, processed, indent + 2); + } + + str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str; + length += str.length; + pieces.push(str); + } + + var cons = constructorName(this, object); + var prefix = cons ? "[" + cons + "] " : ""; + var is = ""; + for (i = 0, k = indent; i < k; ++i) { is += " "; } + + if(l < properties.length) + pieces.push("[... " + (properties.length - l) + " more elements]"); + + if (length + indent > 80) { + return prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" + + is + "}"; + } + return prefix + "{ " + pieces.join(", ") + " }"; + }; + + ascii.element = function (element) { + var tagName = element.tagName.toLowerCase(); + var attrs = element.attributes, attr, pairs = [], attrName, i, l, val; + + for (i = 0, l = attrs.length; i < l; ++i) { + attr = attrs.item(i); + attrName = attr.nodeName.toLowerCase().replace("html:", ""); + val = attr.nodeValue; + if (attrName !== "contenteditable" || val !== "inherit") { + if (!!val) { pairs.push(attrName + "=\"" + val + "\""); } + } + } + + var formatted = "<" + tagName + (pairs.length > 0 ? " " : ""); + var content = element.innerHTML; + + if (content.length > 20) { + content = content.substr(0, 20) + "[...]"; + } + + var res = formatted + pairs.join(" ") + ">" + content + + ""; + + return res.replace(/ contentEditable="inherit"/, ""); + }; + + function Formatio(options) { + for (var opt in options) { + this[opt] = options[opt]; + } + } + + Formatio.prototype = { + functionName: functionName, + + configure: function (options) { + return new Formatio(options); + }, + + constructorName: function (object) { + return constructorName(this, object); + }, + + ascii: function (object, processed, indent) { + return ascii(this, object, processed, indent); + } + }; + + return Formatio.prototype; +}); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/package.json b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/package.json new file mode 100644 index 0000000..23a3f5d --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/package.json @@ -0,0 +1,79 @@ +{ + "name": "formatio", + "version": "1.1.1", + "description": "Human-readable object formatting", + "homepage": "http://busterjs.org/docs/formatio/", + "author": { + "name": "Christian Johansen" + }, + "contributors": [ + { + "name": "Christian Johansen", + "email": "christian@cjohansen.no", + "url": "http://cjohansen.no" + }, + { + "name": "August Lilleaas", + "email": "august.lilleaas@gmail.com", + "url": "http://augustl.com" + }, + { + "name": "Dave Geddes", + "email": "davidcgeddes@gmail.com" + }, + { + "name": "Stein Magnus Jodal", + "email": "stein.magnus@jodal.no" + }, + { + "name": "Tek Nynja", + "email": "github@teknynja.com" + } + ], + "main": "./lib/formatio", + "repository": { + "type": "git", + "url": "https://github.com/busterjs/formatio.git" + }, + "scripts": { + "test": "node node_modules/buster/bin/buster-test --node", + "test-debug": "node --debug-brk node_modules/buster/bin/buster-test --node" + }, + "dependencies": { + "samsam": "~1.1" + }, + "devDependencies": { + "buster": "*" + }, + "bugs": { + "url": "https://github.com/busterjs/formatio/issues" + }, + "_id": "formatio@1.1.1", + "dist": { + "shasum": "5ed3ccd636551097383465d996199100e86161e9", + "tarball": "http://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz" + }, + "_from": "formatio@1.1.1", + "_npmVersion": "1.3.21", + "_npmUser": { + "name": "cjohansen", + "email": "christian@cjohansen.no" + }, + "maintainers": [ + { + "name": "cjohansen", + "email": "christian@cjohansen.no" + }, + { + "name": "augustl", + "email": "august@augustl.com" + }, + { + "name": "dwittner", + "email": "d.wittner@gmx.de" + } + ], + "directories": {}, + "_shasum": "5ed3ccd636551097383465d996199100e86161e9", + "_resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz" +} diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/test/formatio-test.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/test/formatio-test.js new file mode 100644 index 0000000..5edb20e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/formatio/test/formatio-test.js @@ -0,0 +1,476 @@ +/*global formatio*/ +((typeof module === "object" && typeof require === "function" && function (t) { + t(require("buster"), require("../lib/formatio")); +}) || function (t) { + t(buster, formatio); +})(function (buster, formatio) { + + var assert = buster.referee.assert; + var refute = buster.referee.refute; + + function getArrayOfNumbers(size){ + var array = new Array(), + i; + + for (i = 0; i < size; i++){ + array[i] = i; + } + + return array + } + + function getObjectWithManyProperties(size){ + var object = {}; + + for (i = 0; i < size; i++) { + object[i.toString()] = i; + } + + return object; + } + + buster.testCase("formatio.ascii", { + "formats strings with quotes": function () { + assert.equals(formatio.ascii("A string"), '"A string"'); + }, + + "formats booleans without quotes": function () { + assert.equals(formatio.ascii(true), "true"); + assert.equals(formatio.ascii(false), "false"); + }, + + "formats null and undefined without quotes": function () { + assert.equals(formatio.ascii(null), "null"); + assert.equals(formatio.ascii(undefined), "undefined"); + }, + + "formats numbers without quotes": function () { + assert.equals(formatio.ascii(3), "3"); + assert.equals(formatio.ascii(3987.56), "3987.56"); + assert.equals(formatio.ascii(-980.0), "-980"); + assert.equals(formatio.ascii(NaN), "NaN"); + assert.equals(formatio.ascii(Infinity), "Infinity"); + assert.equals(formatio.ascii(-Infinity), "-Infinity"); + assert.equals(formatio.ascii(-0), "-0"); + }, + + "formats regexp using toString": function () { + assert.equals(formatio.ascii(/[a-zA-Z0-9]+\.?/), + "/[a-zA-Z0-9]+\\.?/"); + }, + + "formats functions with name": function () { + var fn = function doIt() {}; + assert.equals(formatio.ascii(fn), "function doIt() {}"); + }, + + "formats functions without name": function () { + assert.equals(formatio.ascii(function () {}), "function () {}"); + }, + + "formats functions with display name": function () { + function doIt() {} + doIt.displayName = "ohHai"; + + assert.equals(formatio.ascii(doIt), "function ohHai() {}"); + }, + + "shortens functions with long bodies": function () { + function doIt() { + var i; + function hey() {} + for (i = 0; i < 10; i++) { console.log(i); } + } + + assert.equals(formatio.ascii(doIt), "function doIt() {}"); + }, + + "formats functions with no name or display name": function () { + function doIt() {} + doIt.name = ""; + + assert.equals(formatio.ascii(doIt), "function doIt() {}"); + }, + + "formats arrays": function () { + function ohNo() { return "Oh yes!"; } + + var array = ["String", 123, /a-z/, null]; + + var str = formatio.ascii(array); + assert.equals(str, '["String", 123, /a-z/, null]'); + + str = formatio.ascii([ohNo, array]); + assert.equals(str, + '[function ohNo() {}, ["String", 123, /a-z/, null]]'); + }, + + "does not trip on circular arrays": function () { + var array = ["String", 123, /a-z/]; + array.push(array); + + var str = formatio.ascii(array); + assert.equals(str, '["String", 123, /a-z/, [Circular]]'); + }, + + "limit formatted array length": { + "should stop at given limit" : function () { + var array = getArrayOfNumbers(300); + var configuredFormatio = formatio.configure({ + limitChildrenCount : 30 + }); + var str = configuredFormatio.ascii(array); + + refute.contains(str, "30"); + assert.contains(str, "29"); + assert.contains(str, "[... 270 more elements]"); + }, + + "should only format as many elements as exists" : function(){ + var array = getArrayOfNumbers(10); + configuredFormatio = formatio.configure({ + limitChildrenCount : 30 + }); + var str = configuredFormatio.ascii(array); + + refute.contains(str, "10"); + assert.contains(str, "9"); + refute.contains(str, "undefined"); + refute.contains(str, "[..."); + }, + + "should format all array elements if no config is used" : function () { + var array = getArrayOfNumbers(300); + var str = formatio.ascii(array); + + assert.contains(str, "100"); + assert.contains(str, "299]"); + refute.contains(str, "[..."); + }, + }, + + "limit count of formated object properties": { + setUp: function() { + this.testobject = {}; + for (i = 0; i < 300; i++) { + this.testobject[i.toString()] = i; + } + }, + + "should stop at given limit" : function () { + var object = getObjectWithManyProperties(300); + configuredFormatio = formatio.configure({ + limitChildrenCount : 30 + }); + var str = configuredFormatio.ascii(object); + + // returned formation may not be in the original order + assert.equals(30 + 3, str.split("\n").length); + assert.contains(str, "[... 270 more elements]"); + }, + + "should only format as many properties as exists" : function(){ + var object = getObjectWithManyProperties(10); + configuredFormatio = formatio.configure({ + limitChildrenCount : 30 + }); + var str = configuredFormatio.ascii(object); + + refute.contains(str, "10"); + assert.contains(str, "9"); + refute.contains(str, "undefined"); + refute.contains(str, "[..."); + }, + + "should format all properties if no config is used" : function () { + var object = getObjectWithManyProperties(300); + var str = formatio.ascii(object); + + assert.equals(300 + 2, str.split("\n").length); + }, + }, + + "formats object": function () { + var object = { + id: 42, + hello: function () {}, + prop: "Some", + more: "properties", + please: "Gimme some more", + "oh hi": 42, + seriously: "many properties" + }; + + var expected = "{\n hello: function () {},\n id: 42,\n " + + "more: \"properties\",\n \"oh hi\": 42,\n please: " + + "\"Gimme some more\",\n prop: \"Some\"," + + "\n seriously: \"many properties\"\n}"; + + assert.equals(formatio.ascii(object), expected); + }, + + "formats short object on one line": function () { + var object = { + id: 42, + hello: function () {}, + prop: "Some" + }; + + var expected = "{ hello: function () {}, id: 42, prop: \"Some\" }"; + assert.equals(formatio.ascii(object), expected); + }, + + "formats object with a non-function toString": function () { + var object = { toString: 42 }; + assert.equals(formatio.ascii(object), "{ toString: 42 }"); + }, + + "formats nested object": function () { + var object = { + id: 42, + hello: function () {}, + prop: "Some", + obj: { + num: 23, + string: "Here you go you little mister" + } + }; + + var expected = "{\n hello: function () {},\n id: 42,\n obj" + + ": { num: 23, string: \"Here you go you little mister\"" + + " },\n prop: \"Some\"\n}"; + + assert.equals(formatio.ascii(object), expected); + }, + + "includes constructor if known and not Object": function () { + function Person(name) { + this.name = name; + } + + var person = new Person("Christian"); + + assert.equals(formatio.ascii(person), + "[Person] { name: \"Christian\" }"); + }, + + "does not include one letter constructors": function () { + function F(name) { + this.name = name; + } + + var person = new F("Christian"); + + assert.equals(formatio.ascii(person), "{ name: \"Christian\" }"); + }, + + "includes one letter constructors when configured so": function () { + function C(name) { + this.name = name; + } + + var person = new C("Christian"); + var formatter = formatio.configure({ excludeConstructors: [] }); + + assert.equals(formatter.ascii(person), + "[C] { name: \"Christian\" }"); + }, + + "excludes constructors when configured to do so": function () { + function Person(name) { + this.name = name; + } + + var person = new Person("Christian"); + var formatter = formatio.configure({ excludeConstructors: ["Person"] }); + + assert.equals(formatter.ascii(person), "{ name: \"Christian\" }"); + }, + + "excludes constructors by pattern when configured so": function () { + function Person(name) { this.name = name; } + function Ninja(name) { this.name = name; } + function Pervert(name) { this.name = name; } + + var person = new Person("Christian"); + var ninja = new Ninja("Haruhachi"); + var pervert = new Pervert("Mr. Garrison"); + var formatter = formatio.configure({ excludeConstructors: [/^Per/] }); + + assert.equals(formatter.ascii(person), "{ name: \"Christian\" }"); + assert.equals(formatter.ascii(ninja), + "[Ninja] { name: \"Haruhachi\" }"); + assert.equals(formatter.ascii(pervert), + "{ name: \"Mr. Garrison\" }"); + }, + + "excludes constructors when run on other objects": function () { + function Person(name) { this.name = name; } + + var person = new Person("Christian"); + var formatter = { ascii: formatio.ascii }; + formatter.excludeConstructors = ["Person"]; + + assert.equals(formatter.ascii(person), "{ name: \"Christian\" }"); + }, + + "excludes default constructors when run on other objects": function () { + var person = { name: "Christian" }; + var formatter = { ascii: formatio.ascii }; + + assert.equals(formatter.ascii(person), "{ name: \"Christian\" }"); + }, + + "does not trip on circular formatting": function () { + var object = {}; + object.foo = object; + + assert.equals(formatio.ascii(object), "{ foo: [Circular] }"); + }, + + "does not trip on indirect circular formatting": function () { + var object = { someProp: {} }; + object.someProp.foo = object; + + assert.equals(formatio.ascii(object), + "{ someProp: { foo: [Circular] } }"); + }, + + "formats nested array nicely": function () { + var object = { people: ["Chris", "August"] }; + + assert.equals(formatio.ascii(object), + "{ people: [\"Chris\", \"August\"] }"); + }, + + "does not rely on object's hasOwnProperty": function () { + // Create object with no "own" properties to get past + // Object.keys test and no .hasOwnProperty() function + var Obj = function () {}; + Obj.prototype = { hasOwnProperty: undefined }; + var object = new Obj(); + + assert.equals(formatio.ascii(object), "{ }"); + }, + + "handles cyclic structures": function () { + var obj = {}; + obj.list1 = [obj]; + obj.list2 = [obj]; + obj.list3 = [{ prop: obj }]; + + refute.exception(function () { + formatio.ascii(obj); + }); + }, + + "unquoted strings": { + setUp: function () { + this.formatter = formatio.configure({ quoteStrings: false }); + }, + + "does not quote strings": function () { + assert.equals(this.formatter.ascii("Hey there"), "Hey there"); + }, + + "quotes string properties": function () { + var obj = { hey: "Mister" }; + assert.equals(this.formatter.ascii(obj), "{ hey: \"Mister\" }"); + } + }, + + "numbers": { + "formats object with 0": function () { + var str = formatio.ascii({ me: 0 }); + refute.match(str, "-0"); + }, + + "formats object with -0": function () { + var str = formatio.ascii({ me: -0 }); + assert.match(str, "-0"); + } + }, + + "DOM elements": { + requiresSupportFor: { "DOM": typeof document !== "undefined" }, + + "formats dom element": function () { + var element = document.createElement("div"); + + assert.equals(formatio.ascii(element), "
            "); + }, + + "formats dom element with attributes": function () { + var element = document.createElement("div"); + element.className = "hey there"; + element.id = "ohyeah"; + var str = formatio.ascii(element); + + assert.match(str, /
            <\/div>/); + assert.match(str, /class="hey there"/); + assert.match(str, /id="ohyeah"/); + }, + + "formats dom element with content": function () { + var element = document.createElement("div"); + element.innerHTML = "Oh hi!"; + + assert.equals(formatio.ascii(element), "
            Oh hi!
            "); + }, + + "truncates dom element content": function () { + var element = document.createElement("div"); + element.innerHTML = "Oh hi! I'm Christian, and this " + + "is a lot of content"; + + assert.equals(formatio.ascii(element), + "
            Oh hi! I'm Christian[...]
            "); + }, + + "includes attributes and truncated content": function () { + var element = document.createElement("div"); + element.id = "anid"; + element.lang = "en"; + element.innerHTML = "Oh hi! I'm Christian, and this " + + "is a lot of content"; + var str = formatio.ascii(element); + + assert.match(str, + /
            Oh hi! I'm Christian\[\.\.\.\]<\/div>/); + assert.match(str, /lang="en"/); + assert.match(str, /id="anid"/); + }, + + "formats document object as toString": function () { + var str; + buster.assertions.refute.exception(function () { + str = formatio.ascii(document); + }); + + assert.equals(str, "[object HTMLDocument]"); + }, + + "formats window object as toString": function () { + var str; + buster.assertions.refute.exception(function () { + str = formatio.ascii(window); + }); + + assert.equals(str, "[object Window]"); + } + }, + + "global object": { + requiresSupportFor: { "global": typeof global !== "undefined" }, + + "formats global object as toString": function () { + var str; + buster.assertions.refute.exception(function () { + str = formatio.ascii(global); + }); + + assert.equals(str, "[object global]"); + } + } + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.editorconfig b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.editorconfig new file mode 100644 index 0000000..bdfa15e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.editorconfig @@ -0,0 +1,17 @@ +; EditorConfig file: http://EditorConfig.org +; Install the "EditorConfig" plugin into your editor to use + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +# Matches the exact files either package.json or .travis.yml +[{package.json, .travis.yml}] +indent_style = space +indent_size = 2 diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.jslintrc b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.jslintrc new file mode 100644 index 0000000..fdb799f --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.jslintrc @@ -0,0 +1,5 @@ +{ + "evil":true, + "vars":true, + "plusplus":true +} diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.min-wd b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.min-wd new file mode 100644 index 0000000..40f963f --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.min-wd @@ -0,0 +1,13 @@ +{ + "sauceLabs": true, + "browsers": [{ + "name": "chrome" + }, { + "name": "internet explorer", + "version": "9", + "url": "http://maxantoni.de/doctype.html" + }, { + "name": "internet explorer", + "version": "10" + }] +} diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.npmignore b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.npmignore new file mode 100644 index 0000000..07e6e47 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.npmignore @@ -0,0 +1 @@ +/node_modules diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.travis.yml b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.travis.yml new file mode 100644 index 0000000..c28affe --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/.travis.yml @@ -0,0 +1,10 @@ +language: node_js + +sudo: false + +node_js: + - "0.10" + - "0.12" +script: + - "./script/ci-test.sh" + diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/LICENSE b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/LICENSE new file mode 100644 index 0000000..eb84755 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/LICENSE @@ -0,0 +1,11 @@ +Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/Readme.md b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/Readme.md new file mode 100644 index 0000000..69b0488 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/Readme.md @@ -0,0 +1,139 @@ +# Lolex [![Build Status](https://secure.travis-ci.org/sinonjs/lolex.png)](http://travis-ci.org/sinonjs/lolex) + +JavaScript implementation of the timer APIs; `setTimeout`, `clearTimeout`, +`setImmediate`, `clearImmediate`, `setInterval`, `clearInterval`, and +`requestAnimationFrame`, along with a clock instance that controls the flow of +time. Lolex also provides a `Date` implementation that gets its time from the +clock. + +Lolex can be used to simulate passing time in automated tests and other +situations where you want the scheduling semantics, but don't want to actually +wait. Lolex is extracted from [Sinon.JS](https://github.com/sinonjs/sinon.js). + +## Installation + +Lolex can be installed using `npm`: + +```sh +npm install lolex +``` + +If you want to use Lolex in a browser, you have a few options. Releases are +hosted on the [sinonjs.org](http://sinonjs.org/download/) website. You can also +get the node module and build a file for the browser using browserify: + +```sh +npm install lolex +npm install browserify # If you don't already have it globally installed +browserify node_modules/lolex/lolex.js +``` + +## Usage + +To use lolex, create a new clock, schedule events on it using the timer +functions and pass time using the `tick` method. + +```js +// In the browser distribution, a global `lolex` is already available +var lolex = require("lolex"); +var clock = lolex.createClock(); + +clock.setTimeout(function () { + console.log("The poblano is a mild chili pepper originating in the state of Puebla, Mexico."); +}, 15); + +// ... + +clock.tick(15); +``` + +Upon executing the last line, an interesting fact about the +[Poblano](http://en.wikipedia.org/wiki/Poblano) will be printed synchronously to +the screen. If you want to simulate asynchronous behavior, you have to use your +imagination when calling the various functions. + +### Faking the native timers + +When using lolex to test timers, you will most likely want to replace the native +timers such that calling `setTimeout` actually schedules a callback with your +clock instance, not the browser's internals. + +To hijack timers in another context, use the `install` method. You can then call +`uninstall` later to restore things as they were again. + +```js +var lolex = require("lolex"); +var clock = lolex.install(window); + +window.setTimeout(fn, 15); // Schedules with clock.setTimeout + +clock.uninstall(); + +// window.setTimeout is restored to the native implementation +``` + +In 90% av the times, you want to install the timers onto the global object. +Calling `install` with no arguments achieves this: + +```js +var clock = lolex.install(); + +// Equivalent to +// var clock = lolex.install(typeof global !== "undefined" ? global : window); +``` + +## API Reference + +### `var clock = lolex.createClock([now])` + +### `var clock = lolex.install([context[, now[, toFake]]])` + +### `var clock = lolex.install([now[, toFake]])` + +### `var id = clock.setTimeout(callback, timeout)` + +### `clock.clearTimeout(id)` + +### `var id = clock.setInterval(callback, timeout)` + +### `clock.clearInterval(id)` + +### `var id = clock.setImmediate(callback)` + +### `clock.clearImmediate(id)` + +### `clock.tick(time)` + +### `clock.setSystemTime([now])` +This simulates a user changing the system clock while your program is running. +It affects the current time but it does not in itself cause e.g. timers to fire; they will fire exactly as they would have done without the call to setSystemTime(). + +### `clock.uninstall()` + +### `Date` + +## Running tests + +Lolex has a comprehensive test suite. If you're thinking of contributing bug +fixes or suggest new features, you need to make sure you have not broken any +tests. You are also expected to add tests for any new behavior. + +### On node: + +```sh +npm test +``` + +Or, if you prefer slightly less verbose output: + +``` +mocha ./test/lolex-test.js +``` + +### In the browser + + + +## License + +BSD 3-clause "New" or "Revised" License (see LICENSE file) diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/lolex.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/lolex.js new file mode 100644 index 0000000..6120035 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/lolex.js @@ -0,0 +1,525 @@ +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.lolex=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { + throw new Error("tick only understands numbers and 'h:m:s'"); + } + + while (i--) { + parsed = parseInt(strings[i], 10); + + if (parsed >= 60) { + throw new Error("Invalid time " + str); + } + + ms += parsed * Math.pow(60, (l - i - 1)); + } + + return ms * 1000; + } + + /** + * Used to grok the `now` parameter to createClock. + */ + function getEpoch(epoch) { + if (!epoch) { return 0; } + if (typeof epoch.getTime === "function") { return epoch.getTime(); } + if (typeof epoch === "number") { return epoch; } + throw new TypeError("now should be milliseconds since UNIX epoch"); + } + + function inRange(from, to, timer) { + return timer && timer.callAt >= from && timer.callAt <= to; + } + + function mirrorDateProperties(target, source) { + var prop; + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // set special now implementation + if (source.now) { + target.now = function now() { + return target.clock.now; + }; + } else { + delete target.now; + } + + // set special toSource implementation + if (source.toSource) { + target.toSource = function toSource() { + return source.toSource(); + }; + } else { + delete target.toSource; + } + + // set special toString implementation + target.toString = function toString() { + return source.toString(); + }; + + target.prototype = source.prototype; + target.parse = source.parse; + target.UTC = source.UTC; + target.prototype.toUTCString = source.prototype.toUTCString; + + return target; + } + + function createDate() { + function ClockDate(year, month, date, hour, minute, second, ms) { + // Defensive and verbose to avoid potential harm in passing + // explicit undefined when user does not pass argument + switch (arguments.length) { + case 0: + return new NativeDate(ClockDate.clock.now); + case 1: + return new NativeDate(year); + case 2: + return new NativeDate(year, month); + case 3: + return new NativeDate(year, month, date); + case 4: + return new NativeDate(year, month, date, hour); + case 5: + return new NativeDate(year, month, date, hour, minute); + case 6: + return new NativeDate(year, month, date, hour, minute, second); + default: + return new NativeDate(year, month, date, hour, minute, second, ms); + } + } + + return mirrorDateProperties(ClockDate, NativeDate); + } + + function addTimer(clock, timer) { + if (timer.func === undefined) { + throw new Error("Callback must be provided to timer calls"); + } + + if (!clock.timers) { + clock.timers = {}; + } + + timer.id = uniqueTimerId++; + timer.createdAt = clock.now; + timer.callAt = clock.now + (timer.delay || (clock.duringTick ? 1 : 0)); + + clock.timers[timer.id] = timer; + + if (addTimerReturnsObject) { + return { + id: timer.id, + ref: NOOP, + unref: NOOP + }; + } + + return timer.id; + } + + + function compareTimers(a, b) { + // Sort first by absolute timing + if (a.callAt < b.callAt) { + return -1; + } + if (a.callAt > b.callAt) { + return 1; + } + + // Sort next by immediate, immediate timers take precedence + if (a.immediate && !b.immediate) { + return -1; + } + if (!a.immediate && b.immediate) { + return 1; + } + + // Sort next by creation time, earlier-created timers take precedence + if (a.createdAt < b.createdAt) { + return -1; + } + if (a.createdAt > b.createdAt) { + return 1; + } + + // Sort next by id, lower-id timers take precedence + if (a.id < b.id) { + return -1; + } + if (a.id > b.id) { + return 1; + } + + // As timer ids are unique, no fallback `0` is necessary + } + + function firstTimerInRange(clock, from, to) { + var timers = clock.timers, + timer = null, + id, + isInRange; + + for (id in timers) { + if (timers.hasOwnProperty(id)) { + isInRange = inRange(from, to, timers[id]); + + if (isInRange && (!timer || compareTimers(timer, timers[id]) === 1)) { + timer = timers[id]; + } + } + } + + return timer; + } + + function callTimer(clock, timer) { + var exception; + + if (typeof timer.interval === "number") { + clock.timers[timer.id].callAt += timer.interval; + } else { + delete clock.timers[timer.id]; + } + + try { + if (typeof timer.func === "function") { + timer.func.apply(null, timer.args); + } else { + eval(timer.func); + } + } catch (e) { + exception = e; + } + + if (!clock.timers[timer.id]) { + if (exception) { + throw exception; + } + return; + } + + if (exception) { + throw exception; + } + } + + function timerType(timer) { + if (timer.immediate) { + return "Immediate"; + } else if (typeof timer.interval !== "undefined") { + return "Interval"; + } else { + return "Timeout"; + } + } + + function clearTimer(clock, timerId, ttype) { + if (!timerId) { + // null appears to be allowed in most browsers, and appears to be + // relied upon by some libraries, like Bootstrap carousel + return; + } + + if (!clock.timers) { + clock.timers = []; + } + + // in Node, timerId is an object with .ref()/.unref(), and + // its .id field is the actual timer id. + if (typeof timerId === "object") { + timerId = timerId.id; + } + + if (clock.timers.hasOwnProperty(timerId)) { + // check that the ID matches a timer of the correct type + var timer = clock.timers[timerId]; + if (timerType(timer) === ttype) { + delete clock.timers[timerId]; + } else { + throw new Error("Cannot clear timer: timer created with set" + ttype + "() but cleared with clear" + timerType(timer) + "()"); + } + } + } + + function uninstall(clock, target) { + var method, + i, + l; + + for (i = 0, l = clock.methods.length; i < l; i++) { + method = clock.methods[i]; + + if (target[method].hadOwnProperty) { + target[method] = clock["_" + method]; + } else { + try { + delete target[method]; + } catch (ignore) {} + } + } + + // Prevent multiple executions which will completely remove these props + clock.methods = []; + } + + function hijackMethod(target, method, clock) { + var prop; + + clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method); + clock["_" + method] = target[method]; + + if (method === "Date") { + var date = mirrorDateProperties(clock[method], target[method]); + target[method] = date; + } else { + target[method] = function () { + return clock[method].apply(clock, arguments); + }; + + for (prop in clock[method]) { + if (clock[method].hasOwnProperty(prop)) { + target[method][prop] = clock[method][prop]; + } + } + } + + target[method].clock = clock; + } + + var timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: global.setImmediate, + clearImmediate: global.clearImmediate, + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + + var keys = Object.keys || function (obj) { + var ks = [], + key; + + for (key in obj) { + if (obj.hasOwnProperty(key)) { + ks.push(key); + } + } + + return ks; + }; + + exports.timers = timers; + + function createClock(now) { + var clock = { + now: getEpoch(now), + timeouts: {}, + Date: createDate() + }; + + clock.Date.clock = clock; + + clock.setTimeout = function setTimeout(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout + }); + }; + + clock.clearTimeout = function clearTimeout(timerId) { + return clearTimer(clock, timerId, "Timeout"); + }; + + clock.setInterval = function setInterval(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout, + interval: timeout + }); + }; + + clock.clearInterval = function clearInterval(timerId) { + return clearTimer(clock, timerId, "Interval"); + }; + + clock.setImmediate = function setImmediate(func) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 1), + immediate: true + }); + }; + + clock.clearImmediate = function clearImmediate(timerId) { + return clearTimer(clock, timerId, "Immediate"); + }; + + clock.tick = function tick(ms) { + ms = typeof ms === "number" ? ms : parseTime(ms); + var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now; + var timer = firstTimerInRange(clock, tickFrom, tickTo); + var oldNow; + + clock.duringTick = true; + + var firstException; + while (timer && tickFrom <= tickTo) { + if (clock.timers[timer.id]) { + tickFrom = clock.now = timer.callAt; + try { + oldNow = clock.now; + callTimer(clock, timer); + // compensate for any setSystemTime() call during timer callback + if (oldNow !== clock.now) { + tickFrom += clock.now - oldNow; + tickTo += clock.now - oldNow; + previous += clock.now - oldNow; + } + } catch (e) { + firstException = firstException || e; + } + } + + timer = firstTimerInRange(clock, previous, tickTo); + previous = tickFrom; + } + + clock.duringTick = false; + clock.now = tickTo; + + if (firstException) { + throw firstException; + } + + return clock.now; + }; + + clock.reset = function reset() { + clock.timers = {}; + }; + + clock.setSystemTime = function setSystemTime(now) { + // determine time difference + var newNow = getEpoch(now); + var difference = newNow - clock.now; + + // update 'system clock' + clock.now = newNow; + + // update timers and intervals to keep them stable + for (var id in clock.timers) { + if (clock.timers.hasOwnProperty(id)) { + var timer = clock.timers[id]; + timer.createdAt += difference; + timer.callAt += difference; + } + } + }; + + return clock; + } + exports.createClock = createClock; + + exports.install = function install(target, now, toFake) { + var i, + l; + + if (typeof target === "number") { + toFake = now; + now = target; + target = null; + } + + if (!target) { + target = global; + } + + var clock = createClock(now); + + clock.uninstall = function () { + uninstall(clock, target); + }; + + clock.methods = toFake || []; + + if (clock.methods.length === 0) { + clock.methods = keys(timers); + } + + for (i = 0, l = clock.methods.length; i < l; i++) { + hijackMethod(target, clock.methods[i], clock); + } + + return clock; + }; + +}(global || this)); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}]},{},[1])(1) +}); \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/package.json b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/package.json new file mode 100644 index 0000000..c51d843 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/package.json @@ -0,0 +1,64 @@ +{ + "name": "lolex", + "description": "Fake JavaScript timers", + "version": "1.3.2", + "homepage": "http://github.com/sinonjs/lolex", + "author": { + "name": "Christian Johansen" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/sinonjs/lolex.git" + }, + "bugs": { + "url": "http://github.com/sinonjs/lolex/issues" + }, + "license": "BSD-3-Clause", + "scripts": { + "lint": "$(npm bin)/jslint src/**/*.js", + "test-node": "mocha -R dot", + "test-headless": "mochify", + "test-cloud": "mochify --wd", + "test": "npm run lint && npm run test-node && npm run test-headless && npm run test-cloud", + "bundle": "browserify -s lolex -o lolex.js src/lolex.js" + }, + "devDependencies": { + "browserify": ">=5.9 <6", + "jslint": "^0.6.6", + "mocha": ">=1.21 <2", + "mochify": ">=1.0 <2", + "referee": "~1.0", + "sinon": "~1.10" + }, + "main": "./src/lolex.js", + "gitHead": "9457345f0bcb0c98d085fae5ea18c7efd950a703", + "_id": "lolex@1.3.2", + "_shasum": "7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31", + "_from": "lolex@1.3.2", + "_npmVersion": "2.14.1", + "_nodeVersion": "0.10.40", + "_npmUser": { + "name": "mrgnrdrck", + "email": "morgan@roderick.dk" + }, + "maintainers": [ + { + "name": "cjohansen", + "email": "christian@cjohansen.no" + }, + { + "name": "mrgnrdrck", + "email": "morgan@roderick.dk" + }, + { + "name": "mantoni", + "email": "mail@maxantoni.de" + } + ], + "dist": { + "shasum": "7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31", + "tarball": "http://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz" +} diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/script/ci-test.sh b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/script/ci-test.sh new file mode 100755 index 0000000..cb8e2f4 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/script/ci-test.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -eu + +npm run test-node +npm run test-headless \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/src/lolex.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/src/lolex.js new file mode 100644 index 0000000..b7da9b3 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/src/lolex.js @@ -0,0 +1,519 @@ +/*global global, window*/ +/** + * @author Christian Johansen (christian@cjohansen.no) and contributors + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ + +(function (global) { + "use strict"; + + // Make properties writable in IE, as per + // http://www.adequatelygood.com/Replacing-setTimeout-Globally.html + // JSLint being anal + var glbl = global; + + global.setTimeout = glbl.setTimeout; + global.clearTimeout = glbl.clearTimeout; + global.setInterval = glbl.setInterval; + global.clearInterval = glbl.clearInterval; + global.Date = glbl.Date; + + // setImmediate is not a standard function + // avoid adding the prop to the window object if not present + if('setImmediate' in global) { + global.setImmediate = glbl.setImmediate; + global.clearImmediate = glbl.clearImmediate; + } + + // node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref() + // browsers, a number. + // see https://github.com/cjohansen/Sinon.JS/pull/436 + + var NOOP = function () { return undefined; }; + var timeoutResult = setTimeout(NOOP, 0); + var addTimerReturnsObject = typeof timeoutResult === "object"; + clearTimeout(timeoutResult); + + var NativeDate = Date; + var uniqueTimerId = 1; + + /** + * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into + * number of milliseconds. This is used to support human-readable strings passed + * to clock.tick() + */ + function parseTime(str) { + if (!str) { + return 0; + } + + var strings = str.split(":"); + var l = strings.length, i = l; + var ms = 0, parsed; + + if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { + throw new Error("tick only understands numbers and 'h:m:s'"); + } + + while (i--) { + parsed = parseInt(strings[i], 10); + + if (parsed >= 60) { + throw new Error("Invalid time " + str); + } + + ms += parsed * Math.pow(60, (l - i - 1)); + } + + return ms * 1000; + } + + /** + * Used to grok the `now` parameter to createClock. + */ + function getEpoch(epoch) { + if (!epoch) { return 0; } + if (typeof epoch.getTime === "function") { return epoch.getTime(); } + if (typeof epoch === "number") { return epoch; } + throw new TypeError("now should be milliseconds since UNIX epoch"); + } + + function inRange(from, to, timer) { + return timer && timer.callAt >= from && timer.callAt <= to; + } + + function mirrorDateProperties(target, source) { + var prop; + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // set special now implementation + if (source.now) { + target.now = function now() { + return target.clock.now; + }; + } else { + delete target.now; + } + + // set special toSource implementation + if (source.toSource) { + target.toSource = function toSource() { + return source.toSource(); + }; + } else { + delete target.toSource; + } + + // set special toString implementation + target.toString = function toString() { + return source.toString(); + }; + + target.prototype = source.prototype; + target.parse = source.parse; + target.UTC = source.UTC; + target.prototype.toUTCString = source.prototype.toUTCString; + + return target; + } + + function createDate() { + function ClockDate(year, month, date, hour, minute, second, ms) { + // Defensive and verbose to avoid potential harm in passing + // explicit undefined when user does not pass argument + switch (arguments.length) { + case 0: + return new NativeDate(ClockDate.clock.now); + case 1: + return new NativeDate(year); + case 2: + return new NativeDate(year, month); + case 3: + return new NativeDate(year, month, date); + case 4: + return new NativeDate(year, month, date, hour); + case 5: + return new NativeDate(year, month, date, hour, minute); + case 6: + return new NativeDate(year, month, date, hour, minute, second); + default: + return new NativeDate(year, month, date, hour, minute, second, ms); + } + } + + return mirrorDateProperties(ClockDate, NativeDate); + } + + function addTimer(clock, timer) { + if (timer.func === undefined) { + throw new Error("Callback must be provided to timer calls"); + } + + if (!clock.timers) { + clock.timers = {}; + } + + timer.id = uniqueTimerId++; + timer.createdAt = clock.now; + timer.callAt = clock.now + (timer.delay || (clock.duringTick ? 1 : 0)); + + clock.timers[timer.id] = timer; + + if (addTimerReturnsObject) { + return { + id: timer.id, + ref: NOOP, + unref: NOOP + }; + } + + return timer.id; + } + + + function compareTimers(a, b) { + // Sort first by absolute timing + if (a.callAt < b.callAt) { + return -1; + } + if (a.callAt > b.callAt) { + return 1; + } + + // Sort next by immediate, immediate timers take precedence + if (a.immediate && !b.immediate) { + return -1; + } + if (!a.immediate && b.immediate) { + return 1; + } + + // Sort next by creation time, earlier-created timers take precedence + if (a.createdAt < b.createdAt) { + return -1; + } + if (a.createdAt > b.createdAt) { + return 1; + } + + // Sort next by id, lower-id timers take precedence + if (a.id < b.id) { + return -1; + } + if (a.id > b.id) { + return 1; + } + + // As timer ids are unique, no fallback `0` is necessary + } + + function firstTimerInRange(clock, from, to) { + var timers = clock.timers, + timer = null, + id, + isInRange; + + for (id in timers) { + if (timers.hasOwnProperty(id)) { + isInRange = inRange(from, to, timers[id]); + + if (isInRange && (!timer || compareTimers(timer, timers[id]) === 1)) { + timer = timers[id]; + } + } + } + + return timer; + } + + function callTimer(clock, timer) { + var exception; + + if (typeof timer.interval === "number") { + clock.timers[timer.id].callAt += timer.interval; + } else { + delete clock.timers[timer.id]; + } + + try { + if (typeof timer.func === "function") { + timer.func.apply(null, timer.args); + } else { + eval(timer.func); + } + } catch (e) { + exception = e; + } + + if (!clock.timers[timer.id]) { + if (exception) { + throw exception; + } + return; + } + + if (exception) { + throw exception; + } + } + + function timerType(timer) { + if (timer.immediate) { + return "Immediate"; + } else if (typeof timer.interval !== "undefined") { + return "Interval"; + } else { + return "Timeout"; + } + } + + function clearTimer(clock, timerId, ttype) { + if (!timerId) { + // null appears to be allowed in most browsers, and appears to be + // relied upon by some libraries, like Bootstrap carousel + return; + } + + if (!clock.timers) { + clock.timers = []; + } + + // in Node, timerId is an object with .ref()/.unref(), and + // its .id field is the actual timer id. + if (typeof timerId === "object") { + timerId = timerId.id; + } + + if (clock.timers.hasOwnProperty(timerId)) { + // check that the ID matches a timer of the correct type + var timer = clock.timers[timerId]; + if (timerType(timer) === ttype) { + delete clock.timers[timerId]; + } else { + throw new Error("Cannot clear timer: timer created with set" + ttype + "() but cleared with clear" + timerType(timer) + "()"); + } + } + } + + function uninstall(clock, target) { + var method, + i, + l; + + for (i = 0, l = clock.methods.length; i < l; i++) { + method = clock.methods[i]; + + if (target[method].hadOwnProperty) { + target[method] = clock["_" + method]; + } else { + try { + delete target[method]; + } catch (ignore) {} + } + } + + // Prevent multiple executions which will completely remove these props + clock.methods = []; + } + + function hijackMethod(target, method, clock) { + var prop; + + clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method); + clock["_" + method] = target[method]; + + if (method === "Date") { + var date = mirrorDateProperties(clock[method], target[method]); + target[method] = date; + } else { + target[method] = function () { + return clock[method].apply(clock, arguments); + }; + + for (prop in clock[method]) { + if (clock[method].hasOwnProperty(prop)) { + target[method][prop] = clock[method][prop]; + } + } + } + + target[method].clock = clock; + } + + var timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: global.setImmediate, + clearImmediate: global.clearImmediate, + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + + var keys = Object.keys || function (obj) { + var ks = [], + key; + + for (key in obj) { + if (obj.hasOwnProperty(key)) { + ks.push(key); + } + } + + return ks; + }; + + exports.timers = timers; + + function createClock(now) { + var clock = { + now: getEpoch(now), + timeouts: {}, + Date: createDate() + }; + + clock.Date.clock = clock; + + clock.setTimeout = function setTimeout(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout + }); + }; + + clock.clearTimeout = function clearTimeout(timerId) { + return clearTimer(clock, timerId, "Timeout"); + }; + + clock.setInterval = function setInterval(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout, + interval: timeout + }); + }; + + clock.clearInterval = function clearInterval(timerId) { + return clearTimer(clock, timerId, "Interval"); + }; + + clock.setImmediate = function setImmediate(func) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 1), + immediate: true + }); + }; + + clock.clearImmediate = function clearImmediate(timerId) { + return clearTimer(clock, timerId, "Immediate"); + }; + + clock.tick = function tick(ms) { + ms = typeof ms === "number" ? ms : parseTime(ms); + var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now; + var timer = firstTimerInRange(clock, tickFrom, tickTo); + var oldNow; + + clock.duringTick = true; + + var firstException; + while (timer && tickFrom <= tickTo) { + if (clock.timers[timer.id]) { + tickFrom = clock.now = timer.callAt; + try { + oldNow = clock.now; + callTimer(clock, timer); + // compensate for any setSystemTime() call during timer callback + if (oldNow !== clock.now) { + tickFrom += clock.now - oldNow; + tickTo += clock.now - oldNow; + previous += clock.now - oldNow; + } + } catch (e) { + firstException = firstException || e; + } + } + + timer = firstTimerInRange(clock, previous, tickTo); + previous = tickFrom; + } + + clock.duringTick = false; + clock.now = tickTo; + + if (firstException) { + throw firstException; + } + + return clock.now; + }; + + clock.reset = function reset() { + clock.timers = {}; + }; + + clock.setSystemTime = function setSystemTime(now) { + // determine time difference + var newNow = getEpoch(now); + var difference = newNow - clock.now; + + // update 'system clock' + clock.now = newNow; + + // update timers and intervals to keep them stable + for (var id in clock.timers) { + if (clock.timers.hasOwnProperty(id)) { + var timer = clock.timers[id]; + timer.createdAt += difference; + timer.callAt += difference; + } + } + }; + + return clock; + } + exports.createClock = createClock; + + exports.install = function install(target, now, toFake) { + var i, + l; + + if (typeof target === "number") { + toFake = now; + now = target; + target = null; + } + + if (!target) { + target = global; + } + + var clock = createClock(now); + + clock.uninstall = function () { + uninstall(clock, target); + }; + + clock.methods = toFake || []; + + if (clock.methods.length === 0) { + clock.methods = keys(timers); + } + + for (i = 0, l = clock.methods.length; i < l; i++) { + hijackMethod(target, clock.methods[i], clock); + } + + return clock; + }; + +}(global || this)); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/test/lolex-test.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/test/lolex-test.js new file mode 100644 index 0000000..a1ef996 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/lolex/test/lolex-test.js @@ -0,0 +1,1260 @@ +/*global + describe, + beforeEach, + afterEach, + it, + assert +*/ +/** + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +"use strict"; + +if (typeof require === "function" && typeof module === "object") { + var referee = require("referee"); + var lolex = require("../src/lolex"); + var sinon = require("sinon"); + + global.lolex = lolex; // For testing eval +} + +var assert = referee.assert; +var refute = referee.refute; +var GlobalDate = Date; +var NOOP = function NOOP() { return undefined; }; + +describe("lolex", function () { + + describe("setTimeout", function () { + + beforeEach(function () { + this.clock = lolex.createClock(); + lolex.evalCalled = false; + }); + + afterEach(function () { + delete lolex.evalCalled; + }); + + it("throws if no arguments", function () { + var clock = this.clock; + + assert.exception(function () { clock.setTimeout(); }); + }); + + it("returns numeric id or object with numeric id", function () { + var result = this.clock.setTimeout(""); + + if (typeof result === 'object') { + assert.isNumber(result.id); + } else { + assert.isNumber(result); + } + }); + + it("returns unique id", function () { + var id1 = this.clock.setTimeout(""); + var id2 = this.clock.setTimeout(""); + + refute.equals(id2, id1); + }); + + it("sets timers on instance", function () { + var clock1 = lolex.createClock(); + var clock2 = lolex.createClock(); + var stubs = [sinon.stub(), sinon.stub()]; + + clock1.setTimeout(stubs[0], 100); + clock2.setTimeout(stubs[1], 100); + clock2.tick(200); + + assert.isFalse(stubs[0].called); + assert(stubs[1].called); + }); + + it("evals non-function callbacks", function () { + this.clock.setTimeout("lolex.evalCalled = true", 10); + this.clock.tick(10); + + assert(lolex.evalCalled); + }); + + it("passes setTimeout parameters", function () { + var clock = lolex.createClock(); + var stub = sinon.stub(); + + clock.setTimeout(stub, 2, "the first", "the second"); + + clock.tick(3); + + assert.isTrue(stub.calledWithExactly("the first", "the second")); + }); + + it("calls correct timeout on recursive tick", function () { + var clock = lolex.createClock(); + var stub = sinon.stub(); + var recurseCallback = function () { clock.tick(100); }; + + clock.setTimeout(recurseCallback, 50); + clock.setTimeout(stub, 100); + + clock.tick(50); + assert(stub.called); + }); + + it("does not depend on this", function () { + var clock = lolex.createClock(); + var stub = sinon.stub(); + var setTimeout = clock.setTimeout; + + setTimeout(stub, 100); + + clock.tick(100); + assert(stub.called); + }); + + it("is not influenced by forward system clock changes", function () { + var stub = sinon.stub(); + this.clock.setTimeout(stub, 5000); + this.clock.tick(1000); + this.clock.setSystemTime((new this.clock.Date()).getTime() + 1000); + this.clock.tick(3990); + assert.equals(stub.callCount, 0); + this.clock.tick(20); + assert.equals(stub.callCount, 1); + }); + + it("is not influenced by backward system clock changes", function () { + var stub = sinon.stub(); + this.clock.setTimeout(stub, 5000); + this.clock.tick(1000); + this.clock.setSystemTime((new this.clock.Date()).getTime() - 1000); + this.clock.tick(3990); + assert.equals(stub.callCount, 0); + this.clock.tick(20); + assert.equals(stub.callCount, 1); + }); + }); + + describe("setImmediate", function () { + + beforeEach(function () { + this.clock = lolex.createClock(); + }); + + it("returns numeric id or object with numeric id", function () { + var result = this.clock.setImmediate(NOOP); + + if (typeof result === 'object') { + assert.isNumber(result.id); + } else { + assert.isNumber(result); + } + }); + + it("calls the given callback immediately", function () { + var stub = sinon.stub(); + + this.clock.setImmediate(stub); + this.clock.tick(0); + + assert(stub.called); + }); + + it("throws if no arguments", function () { + var clock = this.clock; + + assert.exception(function () { + clock.setImmediate(); + }); + }); + + it("manages separate timers per clock instance", function () { + var clock1 = lolex.createClock(); + var clock2 = lolex.createClock(); + var stubs = [sinon.stub(), sinon.stub()]; + + clock1.setImmediate(stubs[0]); + clock2.setImmediate(stubs[1]); + clock2.tick(0); + + assert.isFalse(stubs[0].called); + assert(stubs[1].called); + }); + + it("passes extra parameters through to the callback", function () { + var stub = sinon.stub(); + + this.clock.setImmediate(stub, 'value1', 2); + this.clock.tick(1); + + assert(stub.calledWithExactly('value1', 2)); + }); + + it("calls the given callback before setTimeout", function () { + var stub1 = sinon.stub.create(); + var stub2 = sinon.stub.create(); + + this.clock.setTimeout(stub1, 0); + this.clock.setImmediate(stub2); + this.clock.tick(0); + + assert(stub1.calledOnce); + assert(stub2.calledOnce); + assert(stub2.calledBefore(stub1)); + }); + + it("does not stuck next tick even if nested", function () { + var clock = this.clock; + + clock.setImmediate(function f() { + clock.setImmediate(f); + }); + + clock.tick(0); + }); + }); + + describe("clearImmediate", function () { + + beforeEach(function () { + this.clock = lolex.createClock(); + }); + + it("removes immediate callbacks", function () { + var callback = sinon.stub(); + + var id = this.clock.setImmediate(callback); + this.clock.clearImmediate(id); + this.clock.tick(1); + + assert.isFalse(callback.called); + }); + + it("does not remove timeout", function () { + var callback = sinon.stub(); + + var id = this.clock.setTimeout(callback, 50); + assert.exception(function() { + this.clock.clearImmediate(id); + }); + this.clock.tick(55); + + assert.isTrue(callback.called); + }); + + it("does not remove interval", function () { + var callback = sinon.stub(); + + var id = this.clock.setInterval(callback, 50); + assert.exception(function() { + this.clock.clearImmediate(id); + }); + this.clock.tick(55); + + assert.isTrue(callback.called); + }); + + }); + + describe("tick", function () { + + beforeEach(function () { + this.clock = lolex.install(0); + }); + + afterEach(function () { + this.clock.uninstall(); + }); + + it("triggers immediately without specified delay", function () { + var stub = sinon.stub(); + this.clock.setTimeout(stub); + + this.clock.tick(0); + + assert(stub.called); + }); + + it("does not trigger without sufficient delay", function () { + var stub = sinon.stub(); + this.clock.setTimeout(stub, 100); + this.clock.tick(10); + + assert.isFalse(stub.called); + }); + + it("triggers after sufficient delay", function () { + var stub = sinon.stub(); + this.clock.setTimeout(stub, 100); + this.clock.tick(100); + + assert(stub.called); + }); + + it("triggers simultaneous timers", function () { + var spies = [sinon.spy(), sinon.spy()]; + this.clock.setTimeout(spies[0], 100); + this.clock.setTimeout(spies[1], 100); + + this.clock.tick(100); + + assert(spies[0].called); + assert(spies[1].called); + }); + + it("triggers multiple simultaneous timers", function () { + var spies = [sinon.spy(), sinon.spy(), sinon.spy(), sinon.spy()]; + this.clock.setTimeout(spies[0], 100); + this.clock.setTimeout(spies[1], 100); + this.clock.setTimeout(spies[2], 99); + this.clock.setTimeout(spies[3], 100); + + this.clock.tick(100); + + assert(spies[0].called); + assert(spies[1].called); + assert(spies[2].called); + assert(spies[3].called); + }); + + it("triggers multiple simultaneous timers with zero callAt", function () { + var test = this; + var spies = [ + sinon.spy(function () { + test.clock.setTimeout(spies[1], 0); + }), + sinon.spy(), + sinon.spy() + ]; + + // First spy calls another setTimeout with delay=0 + this.clock.setTimeout(spies[0], 0); + this.clock.setTimeout(spies[2], 10); + + this.clock.tick(10); + + assert(spies[0].called); + assert(spies[1].called); + assert(spies[2].called); + }); + + it("waits after setTimeout was called", function () { + this.clock.tick(100); + var stub = sinon.stub(); + this.clock.setTimeout(stub, 150); + this.clock.tick(50); + + assert.isFalse(stub.called); + this.clock.tick(100); + assert(stub.called); + }); + + it("mini integration test", function () { + var stubs = [sinon.stub(), sinon.stub(), sinon.stub()]; + this.clock.setTimeout(stubs[0], 100); + this.clock.setTimeout(stubs[1], 120); + this.clock.tick(10); + this.clock.tick(89); + assert.isFalse(stubs[0].called); + assert.isFalse(stubs[1].called); + this.clock.setTimeout(stubs[2], 20); + this.clock.tick(1); + assert(stubs[0].called); + assert.isFalse(stubs[1].called); + assert.isFalse(stubs[2].called); + this.clock.tick(19); + assert.isFalse(stubs[1].called); + assert(stubs[2].called); + this.clock.tick(1); + assert(stubs[1].called); + }); + + it("triggers even when some throw", function () { + var clock = this.clock; + var stubs = [sinon.stub().throws(), sinon.stub()]; + + clock.setTimeout(stubs[0], 100); + clock.setTimeout(stubs[1], 120); + + assert.exception(function () { + clock.tick(120); + }); + + assert(stubs[0].called); + assert(stubs[1].called); + }); + + it("calls function with global object or null (strict mode) as this", function () { + var clock = this.clock; + var stub = sinon.stub().throws(); + clock.setTimeout(stub, 100); + + assert.exception(function () { + clock.tick(100); + }); + + assert(stub.calledOn(global) || stub.calledOn(null)); + }); + + it("triggers in the order scheduled", function () { + var spies = [sinon.spy(), sinon.spy()]; + this.clock.setTimeout(spies[0], 13); + this.clock.setTimeout(spies[1], 11); + + this.clock.tick(15); + + assert(spies[1].calledBefore(spies[0])); + }); + + it("creates updated Date while ticking", function () { + var spy = sinon.spy(); + + this.clock.setInterval(function () { + spy(new Date().getTime()); + }, 10); + + this.clock.tick(100); + + assert.equals(spy.callCount, 10); + assert(spy.calledWith(10)); + assert(spy.calledWith(20)); + assert(spy.calledWith(30)); + assert(spy.calledWith(40)); + assert(spy.calledWith(50)); + assert(spy.calledWith(60)); + assert(spy.calledWith(70)); + assert(spy.calledWith(80)); + assert(spy.calledWith(90)); + assert(spy.calledWith(100)); + }); + + it("fires timer in intervals of 13", function () { + var spy = sinon.spy(); + this.clock.setInterval(spy, 13); + + this.clock.tick(500); + + assert.equals(spy.callCount, 38); + }); + + it("fires timers in correct order", function () { + var spy13 = sinon.spy(); + var spy10 = sinon.spy(); + + this.clock.setInterval(function () { + spy13(new Date().getTime()); + }, 13); + + this.clock.setInterval(function () { + spy10(new Date().getTime()); + }, 10); + + this.clock.tick(500); + + assert.equals(spy13.callCount, 38); + assert.equals(spy10.callCount, 50); + + assert(spy13.calledWith(416)); + assert(spy10.calledWith(320)); + + assert(spy10.getCall(0).calledBefore(spy13.getCall(0))); + assert(spy10.getCall(4).calledBefore(spy13.getCall(3))); + }); + + it("triggers timeouts and intervals in the order scheduled", function () { + var spies = [sinon.spy(), sinon.spy()]; + this.clock.setInterval(spies[0], 10); + this.clock.setTimeout(spies[1], 50); + + this.clock.tick(100); + + assert(spies[0].calledBefore(spies[1])); + assert.equals(spies[0].callCount, 10); + assert.equals(spies[1].callCount, 1); + }); + + it("does not fire canceled intervals", function () { + var id; + var callback = sinon.spy(function () { + if (callback.callCount === 3) { + clearInterval(id); + } + }); + + id = this.clock.setInterval(callback, 10); + this.clock.tick(100); + + assert.equals(callback.callCount, 3); + }); + + it("passes 6 seconds", function () { + var spy = sinon.spy(); + this.clock.setInterval(spy, 4000); + + this.clock.tick("08"); + + assert.equals(spy.callCount, 2); + }); + + it("passes 1 minute", function () { + var spy = sinon.spy(); + this.clock.setInterval(spy, 6000); + + this.clock.tick("01:00"); + + assert.equals(spy.callCount, 10); + }); + + it("passes 2 hours, 34 minutes and 12 seconds", function () { + var spy = sinon.spy(); + this.clock.setInterval(spy, 10000); + + this.clock.tick("02:34:10"); + + assert.equals(spy.callCount, 925); + }); + + it("throws for invalid format", function () { + var spy = sinon.spy(); + this.clock.setInterval(spy, 10000); + var test = this; + + assert.exception(function () { + test.clock.tick("12:02:34:10"); + }); + + assert.equals(spy.callCount, 0); + }); + + it("throws for invalid minutes", function () { + var spy = sinon.spy(); + this.clock.setInterval(spy, 10000); + var test = this; + + assert.exception(function () { + test.clock.tick("67:10"); + }); + + assert.equals(spy.callCount, 0); + }); + + it("throws for negative minutes", function () { + var spy = sinon.spy(); + this.clock.setInterval(spy, 10000); + var test = this; + + assert.exception(function () { + test.clock.tick("-7:10"); + }); + + assert.equals(spy.callCount, 0); + }); + + it("treats missing argument as 0", function () { + this.clock.tick(); + + assert.equals(this.clock.now, 0); + }); + + it("fires nested setTimeout calls properly", function () { + var i = 0; + var clock = this.clock; + + var callback = function () { + ++i; + clock.setTimeout(function () { + callback(); + }, 100); + }; + + callback(); + + clock.tick(1000); + + assert.equals(i, 11); + }); + + it("does not silently catch errors", function () { + var clock = this.clock; + + clock.setTimeout(function () { + throw new Error("oh no!"); + }, 1000); + + assert.exception(function () { + clock.tick(1000); + }); + }); + + it("returns the current now value", function () { + var clock = this.clock; + var value = clock.tick(200); + assert.equals(clock.now, value); + }); + }); + + describe("clearTimeout", function () { + + beforeEach(function () { + this.clock = lolex.createClock(); + }); + + it("removes timeout", function () { + var stub = sinon.stub(); + var id = this.clock.setTimeout(stub, 50); + this.clock.clearTimeout(id); + this.clock.tick(50); + + assert.isFalse(stub.called); + }); + + it("does not remove interval", function () { + var stub = sinon.stub(); + var id = this.clock.setInterval(stub, 50); + assert.exception(function() { + this.clock.clearTimeout(id); + }); + this.clock.tick(50); + + assert.isTrue(stub.called); + }); + + it("does not remove immediate", function () { + var stub = sinon.stub(); + var id = this.clock.setImmediate(stub); + assert.exception(function() { + this.clock.clearTimeout(id); + }); + this.clock.tick(50); + + assert.isTrue(stub.called); + }); + + it("ignores null argument", function () { + this.clock.clearTimeout(null); + assert(true); // doesn't fail + }); + }); + + describe("reset", function () { + + beforeEach(function () { + this.clock = lolex.createClock(); + }); + + it("empties timeouts queue", function () { + var stub = sinon.stub(); + this.clock.setTimeout(stub); + this.clock.reset(); + this.clock.tick(0); + + assert.isFalse(stub.called); + }); + }); + + describe("setInterval", function () { + + beforeEach(function () { + this.clock = lolex.createClock(); + }); + + it("throws if no arguments", function () { + var clock = this.clock; + + assert.exception(function () { + clock.setInterval(); + }); + }); + + it("returns numeric id or object with numeric id", function () { + var result = this.clock.setInterval(""); + + if (typeof result === 'object') { + assert.isNumber(result.id); + } else { + assert.isNumber(result); + } + }); + + it("returns unique id", function () { + var id1 = this.clock.setInterval(""); + var id2 = this.clock.setInterval(""); + + refute.equals(id2, id1); + }); + + it("schedules recurring timeout", function () { + var stub = sinon.stub(); + this.clock.setInterval(stub, 10); + this.clock.tick(99); + + assert.equals(stub.callCount, 9); + }); + + it("is not influenced by forward system clock changes", function () { + var stub = sinon.stub(); + this.clock.setInterval(stub, 10); + this.clock.tick(11); + assert.equals(stub.callCount, 1); + this.clock.setSystemTime((new this.clock.Date()).getTime() + 1000); + this.clock.tick(8); + assert.equals(stub.callCount, 1); + this.clock.tick(3); + assert.equals(stub.callCount, 2); + }); + + it("is not influenced by backward system clock changes", function () { + var stub = sinon.stub(); + this.clock.setInterval(stub, 10); + this.clock.tick(5); + this.clock.setSystemTime((new this.clock.Date()).getTime() - 1000); + this.clock.tick(6); + assert.equals(stub.callCount, 1); + this.clock.tick(10); + assert.equals(stub.callCount, 2); + }); + + it("does not schedule recurring timeout when cleared", function () { + var clock = this.clock; + var id; + var stub = sinon.spy(function () { + if (stub.callCount === 3) { + clock.clearInterval(id); + } + }); + + id = this.clock.setInterval(stub, 10); + this.clock.tick(100); + + assert.equals(stub.callCount, 3); + }); + + it("passes setTimeout parameters", function () { + var clock = lolex.createClock(); + var stub = sinon.stub(); + + clock.setInterval(stub, 2, "the first", "the second"); + + clock.tick(3); + + assert.isTrue(stub.calledWithExactly("the first", "the second")); + }); + }); + + describe("clearInterval", function () { + + beforeEach(function () { + this.clock = lolex.createClock(); + }); + + it("removes interval", function () { + var stub = sinon.stub(); + var id = this.clock.setInterval(stub, 50); + this.clock.clearInterval(id); + this.clock.tick(50); + + assert.isFalse(stub.called); + }); + + it("does not remove timeout", function () { + var stub = sinon.stub(); + var id = this.clock.setTimeout(stub, 50); + assert.exception(function() { + this.clock.clearInterval(id); + }); + this.clock.tick(50); + assert.isTrue(stub.called); + }); + + it("does not remove immediate", function () { + var stub = sinon.stub(); + var id = this.clock.setImmediate(stub); + assert.exception(function() { + this.clock.clearInterval(id); + }); + this.clock.tick(50); + + assert.isTrue(stub.called); + }); + + it("ignores null argument", function () { + this.clock.clearInterval(null); + assert(true); // doesn't fail + }); + }); + + describe("date", function () { + + beforeEach(function () { + this.now = new GlobalDate().getTime() - 3000; + this.clock = lolex.createClock(this.now); + this.Date = global.Date; + }); + + afterEach(function () { + global.Date = this.Date; + }); + + it("provides date constructor", function () { + assert.isFunction(this.clock.Date); + }); + + it("creates real Date objects", function () { + var date = new this.clock.Date(); + + assert(Date.prototype.isPrototypeOf(date)); + }); + + it("creates real Date objects when called as function", function () { + var date = this.clock.Date(); + + assert(Date.prototype.isPrototypeOf(date)); + }); + + it("creates real Date objects when Date constructor is gone", function () { + var realDate = new Date(); + Date = NOOP; + global.Date = NOOP; + + var date = new this.clock.Date(); + + assert.same(date.constructor.prototype, realDate.constructor.prototype); + }); + + it("creates Date objects representing clock time", function () { + var date = new this.clock.Date(); + + assert.equals(date.getTime(), new Date(this.now).getTime()); + }); + + it("returns Date object representing clock time", function () { + var date = this.clock.Date(); + + assert.equals(date.getTime(), new Date(this.now).getTime()); + }); + + it("listens to ticking clock", function () { + var date1 = new this.clock.Date(); + this.clock.tick(3); + var date2 = new this.clock.Date(); + + assert.equals(date2.getTime() - date1.getTime(), 3); + }); + + it("listens to system clock changes", function () { + var date1 = new this.clock.Date(); + this.clock.setSystemTime(date1.getTime() + 1000); + var date2 = new this.clock.Date(); + + assert.equals(date2.getTime() - date1.getTime(), 1000); + }); + + it("creates regular date when passing timestamp", function () { + var date = new Date(); + var fakeDate = new this.clock.Date(date.getTime()); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("returns regular date when calling with timestamp", function () { + var date = new Date(); + var fakeDate = this.clock.Date(date.getTime()); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("creates regular date when passing year, month", function () { + var date = new Date(2010, 4); + var fakeDate = new this.clock.Date(2010, 4); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("returns regular date when calling with year, month", function () { + var date = new Date(2010, 4); + var fakeDate = this.clock.Date(2010, 4); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("creates regular date when passing y, m, d", function () { + var date = new Date(2010, 4, 2); + var fakeDate = new this.clock.Date(2010, 4, 2); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("returns regular date when calling with y, m, d", function () { + var date = new Date(2010, 4, 2); + var fakeDate = this.clock.Date(2010, 4, 2); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("creates regular date when passing y, m, d, h", function () { + var date = new Date(2010, 4, 2, 12); + var fakeDate = new this.clock.Date(2010, 4, 2, 12); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("returns regular date when calling with y, m, d, h", function () { + var date = new Date(2010, 4, 2, 12); + var fakeDate = this.clock.Date(2010, 4, 2, 12); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("creates regular date when passing y, m, d, h, m", function () { + var date = new Date(2010, 4, 2, 12, 42); + var fakeDate = new this.clock.Date(2010, 4, 2, 12, 42); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("returns regular date when calling with y, m, d, h, m", function () { + var date = new Date(2010, 4, 2, 12, 42); + var fakeDate = this.clock.Date(2010, 4, 2, 12, 42); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("creates regular date when passing y, m, d, h, m, s", function () { + var date = new Date(2010, 4, 2, 12, 42, 53); + var fakeDate = new this.clock.Date(2010, 4, 2, 12, 42, 53); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("returns regular date when calling with y, m, d, h, m, s", function () { + var date = new Date(2010, 4, 2, 12, 42, 53); + var fakeDate = this.clock.Date(2010, 4, 2, 12, 42, 53); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("creates regular date when passing y, m, d, h, m, s, ms", function () { + var date = new Date(2010, 4, 2, 12, 42, 53, 498); + var fakeDate = new this.clock.Date(2010, 4, 2, 12, 42, 53, 498); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("returns regular date when calling with y, m, d, h, m, s, ms", function () { + var date = new Date(2010, 4, 2, 12, 42, 53, 498); + var fakeDate = this.clock.Date(2010, 4, 2, 12, 42, 53, 498); + + assert.equals(fakeDate.getTime(), date.getTime()); + }); + + it("mirrors native Date.prototype", function () { + assert.same(this.clock.Date.prototype, Date.prototype); + }); + + it("supports now method if present", function () { + assert.same(typeof this.clock.Date.now, typeof Date.now); + }); + + if (Date.now) { + describe("now", function () { + it("returns clock.now", function () { + var clock_now = this.clock.Date.now(); + var global_now = GlobalDate.now(); + + assert(this.now <= clock_now && clock_now <= global_now); + }); + }); + } else { + describe("unsupported now", function () { + it("is undefined", function () { + refute.defined(this.clock.Date.now); + }); + }); + } + + it("mirrors parse method", function () { + assert.same(this.clock.Date.parse, Date.parse); + }); + + it("mirrors UTC method", function () { + assert.same(this.clock.Date.UTC, Date.UTC); + }); + + it("mirrors toUTCString method", function () { + assert.same(this.clock.Date.prototype.toUTCString, Date.prototype.toUTCString); + }); + + if (Date.toSource) { + describe("toSource", function () { + + it("is mirrored", function () { + assert.same(this.clock.Date.toSource(), Date.toSource()); + }); + + }); + } else { + describe("unsupported toSource", function () { + + it("is undefined", function () { + refute.defined(this.clock.Date.toSource); + }); + + }); + } + + it("mirrors toString", function () { + assert.same(this.clock.Date.toString(), Date.toString()); + }); + }); + + describe("stubTimers", function () { + + beforeEach(function () { + this.dateNow = global.Date.now; + }); + + afterEach(function () { + if (this.clock) { + this.clock.uninstall(); + } + + clearTimeout(this.timer); + if (this.dateNow === undefined) { + delete global.Date.now; + } else { + global.Date.now = this.dateNow; + } + }); + + it("returns clock object", function () { + this.clock = lolex.install(); + + assert.isObject(this.clock); + assert.isFunction(this.clock.tick); + }); + + it("has clock property", function () { + this.clock = lolex.install(); + + assert.same(setTimeout.clock, this.clock); + assert.same(clearTimeout.clock, this.clock); + assert.same(setInterval.clock, this.clock); + assert.same(clearInterval.clock, this.clock); + assert.same(Date.clock, this.clock); + }); + + it("sets initial timestamp", function () { + this.clock = lolex.install(1400); + + assert.equals(this.clock.now, 1400); + }); + + it("replaces global setTimeout", function () { + this.clock = lolex.install(); + var stub = sinon.stub(); + + setTimeout(stub, 1000); + this.clock.tick(1000); + + assert(stub.called); + }); + + it("global fake setTimeout should return id", function () { + this.clock = lolex.install(); + var stub = sinon.stub(); + + var to = setTimeout(stub, 1000); + + if (typeof (setTimeout(NOOP, 0)) === 'object') { + assert.isNumber(to.id); + assert.isFunction(to.ref); + assert.isFunction(to.unref); + } else { + assert.isNumber(to); + } + }); + + it("replaces global clearTimeout", function () { + this.clock = lolex.install(); + var stub = sinon.stub(); + + clearTimeout(setTimeout(stub, 1000)); + this.clock.tick(1000); + + assert.isFalse(stub.called); + }); + + it("uninstalls global setTimeout", function () { + this.clock = lolex.install(); + var stub = sinon.stub(); + this.clock.uninstall(); + + this.timer = setTimeout(stub, 1000); + this.clock.tick(1000); + + assert.isFalse(stub.called); + assert.same(setTimeout, lolex.timers.setTimeout); + }); + + it("uninstalls global clearTimeout", function () { + this.clock = lolex.install(); + sinon.stub(); + this.clock.uninstall(); + + assert.same(clearTimeout, lolex.timers.clearTimeout); + }); + + it("replaces global setInterval", function () { + this.clock = lolex.install(); + var stub = sinon.stub(); + + setInterval(stub, 500); + this.clock.tick(1000); + + assert(stub.calledTwice); + }); + + it("replaces global clearInterval", function () { + this.clock = lolex.install(); + var stub = sinon.stub(); + + clearInterval(setInterval(stub, 500)); + this.clock.tick(1000); + + assert.isFalse(stub.called); + }); + + it("uninstalls global setInterval", function () { + this.clock = lolex.install(); + var stub = sinon.stub(); + this.clock.uninstall(); + + this.timer = setInterval(stub, 1000); + this.clock.tick(1000); + + assert.isFalse(stub.called); + assert.same(setInterval, lolex.timers.setInterval); + }); + + it("uninstalls global clearInterval", function () { + this.clock = lolex.install(); + sinon.stub(); + this.clock.uninstall(); + + assert.same(clearInterval, lolex.timers.clearInterval); + }); + + if (global.__proto__) { + delete global.hasOwnPropertyTest; + global.__proto__.hasOwnPropertyTest = function() {}; + + if (!global.hasOwnProperty("hasOwnPropertyTest")) { + it("deletes global property on uninstall if it was inherited onto the global object", function () { + // Give the global object an inherited 'tick' method + delete global.tick; + global.__proto__.tick = function() { }; + + this.clock = lolex.install(0, ['tick']); + assert.isTrue(global.hasOwnProperty("tick")); + this.clock.uninstall(); + + assert.isFalse(global.hasOwnProperty("tick")); + delete global.__proto__.tick; + }); + } + + delete global.__proto__.hasOwnPropertyTest; + } + + it("uninstalls global property on uninstall if it is present on the global object itself", function () { + // Directly give the global object a tick method + global.tick = NOOP; + + this.clock = lolex.install(0, ['tick']); + assert.isTrue(global.hasOwnProperty("tick")); + this.clock.uninstall(); + + assert.isTrue(global.hasOwnProperty("tick")); + delete global.tick; + }); + + it("fakes Date constructor", function () { + this.clock = lolex.install(0); + var now = new Date(); + + refute.same(Date, lolex.timers.Date); + assert.equals(now.getTime(), 0); + }); + + it("fake Date constructor should mirror Date's properties", function () { + this.clock = lolex.install(0); + + assert(!!Date.parse); + assert(!!Date.UTC); + }); + + it("decide on Date.now support at call-time when supported", function () { + global.Date.now = NOOP; + this.clock = lolex.install(0); + + assert.equals(typeof Date.now, "function"); + }); + + it("decide on Date.now support at call-time when unsupported", function () { + global.Date.now = undefined; + this.clock = lolex.install(0); + + refute.defined(Date.now); + }); + + // TODO: The following tests causes test suite instability + + it("mirrors custom Date properties", function () { + var f = function () { }; + global.Date.format = f; + this.clock = lolex.install(); + + assert.equals(Date.format, f); + }); + + it("uninstalls Date constructor", function () { + this.clock = lolex.install(0); + this.clock.uninstall(); + + assert.same(GlobalDate, lolex.timers.Date); + }); + + it("fakes provided methods", function () { + this.clock = lolex.install(0, ["setTimeout", "Date", "setImmediate"]); + + refute.same(setTimeout, lolex.timers.setTimeout); + refute.same(Date, lolex.timers.Date); + }); + + it("resets faked methods", function () { + this.clock = lolex.install(0, ["setTimeout", "Date", "setImmediate"]); + this.clock.uninstall(); + + assert.same(setTimeout, lolex.timers.setTimeout); + assert.same(Date, lolex.timers.Date); + }); + + it("does not fake methods not provided", function () { + this.clock = lolex.install(0, ["setTimeout", "Date", "setImmediate"]); + + assert.same(clearTimeout, lolex.timers.clearTimeout); + assert.same(setInterval, lolex.timers.setInterval); + assert.same(clearInterval, lolex.timers.clearInterval); + }); + + it("does not be able to use date object for now", function () { + assert.exception(function () { + lolex.install(new Date(2011, 9, 1)); + }); + }); + }); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/.npmignore b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/.npmignore new file mode 100644 index 0000000..07e6e47 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/.npmignore @@ -0,0 +1 @@ +/node_modules diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/.travis.yml b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/.travis.yml new file mode 100644 index 0000000..587bd3e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/.travis.yml @@ -0,0 +1 @@ +language: node_js diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/AUTHORS b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/AUTHORS new file mode 100644 index 0000000..13df013 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/AUTHORS @@ -0,0 +1,2 @@ +Christian Johansen (christian@cjohansen.no) +August Lilleaas (august@augustl.com) diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/LICENSE b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/LICENSE new file mode 100644 index 0000000..f00310b --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/LICENSE @@ -0,0 +1,27 @@ +(The BSD License) + +Copyright (c) 2010-2012, Christian Johansen, christian@cjohansen.no and +August Lilleaas, august.lilleaas@gmail.com. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Christian Johansen nor the names of his contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/Readme.md b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/Readme.md new file mode 100644 index 0000000..a2a56c3 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/Readme.md @@ -0,0 +1,226 @@ +# samsam + +[![Build status](https://secure.travis-ci.org/busterjs/samsam.png?branch=master)](http://travis-ci.org/busterjs/samsam) + +> Same same, but different + +`samsam` is a collection of predicate and comparison functions useful for +identifiying the type of values and to compare values with varying degrees of +strictness. + +`samsam` is a general-purpose library with no dependencies. It works in browsers +(including old and rowdy ones, like IE6) and Node. It will define itself as an +AMD module if you want it to (i.e. if there's a `define` function available). + +`samsam` was originally extracted from the +`referee `_ assertion library, which +ships with the Buster.JS testing framework. + + +## Predicate functions + + +### `isArguments(object)` + +Returns `true` if `object` is an `arguments` object, `false` otherwise. + + +### `isNegZero(value)` + +Returns `true` if `value` is `-0`. + + +## `isElement(object)` + +Returns `true` if `object` is a DOM element node. Unlike +Underscore.js/lodash, this function will return `false` if `object` is an +*element-like* object, i.e. a regular object with a `nodeType` property that +holds the value `1`. + + +###`isDate(object)` + +Returns true if the object is a `Date`, or *date-like*. Duck typing of date +objects work by checking that the object has a `getTime` function whose return +value equals the return value from the object's `valueOf`. + + +## Comparison functions + + +###`identical(x, y)` + +Strict equality check according to `EcmaScript Harmony's `egal`. + +**From the Harmony wiki:** + +> An egal function simply makes available the internal `SameValue` function +from section 9.12 of the ES5 spec. If two values are egal, then they are not +observably distinguishable. + +`identical` returns `true` when `===` is `true`, except for `-0` and +`+0`, where it returns `false`. Additionally, it returns `true` when +`NaN` is compared to itself. + + +### `deepEqual(obj1, obj2)` + +Deep equal comparison. Two values are "deep equal" if: + +* They are identical +* They are both date objects representing the same time +* They are both arrays containing elements that are all deepEqual +* They are objects with the same set of properties, and each property + in `obj1` is deepEqual to the corresponding property in `obj2` + + +### `match(object, matcher)` + +Partial equality check. Compares `object` with matcher according a wide set of +rules: + + +**String matcher** + +In its simplest form, `match` performs a case insensitive substring match. +When the matcher is a string, `object` is converted to a string, and the +function returns `true` if the matcher is a case-insensitive substring of +`object` as a string. + +```javascript +samsam.match("Give me something", "Give"); //true +samsam.match("Give me something", "sumptn"); // false +samsam.match({ toString: function () { return "yeah"; } }, "Yeah!"); // true +``` + +The last example is not symmetric. When the matcher is a string, the `object` +is coerced to a string - in this case using `toString`. Changing the order of +the arguments would cause the matcher to be an object, in which case different +rules apply (see below). + + +**Boolean matcher** + +Performs a strict (i.e. `===`) match with the object. So, only `true` +matches `true`, and only `false` matches `false`. + + +**Regular expression matcher** + +When the matcher is a regular expression, the function will pass if +`object.test(matcher)` is `true`. `match` is written in a generic way, so +any object with a `test` method will be used as a matcher this way. + +```javascript +samsam.match("Give me something", /^[a-z\s]$/i); // true +samsam.match("Give me something", /[0-9]/); // false +samsam.match({ toString: function () { return "yeah!"; } }, /yeah/); // true +samsam.match(234, /[a-z]/); // false +``` + + +**Number matcher** + +When the matcher is a number, the assertion will pass if `object == matcher`. + +```javascript +samsam.match("123", 123); // true +samsam.match("Give me something", 425); // false +samsam.match({ toString: function () { return "42"; } }, 42); // true +samsam.match(234, 1234); // false +``` + + +**Function matcher** + +When the matcher is a function, it is called with `object` as its only +argument. `match` returns `true` if the function returns `true`. A strict +match is performed against the return value, so a boolean `true` is required, +truthy is not enough. + +```javascript +// true +samsam.match("123", function (exp) { + return exp == "123"; +}); + +// false +samsam.match("Give me something", function () { + return "ok"; +}); + +// true +samsam.match({ + toString: function () { + return "42"; + } +}, function () { return true; }); + +// false +samsam.match(234, function () {}); +``` + + +**Object matcher** + +As mentioned above, if an object matcher defines a `test` method, `match` +will return `true` if `matcher.test(object)` returns truthy. + +If the matcher does not have a test method, a recursive match is performed. If +all properties of `matcher` matches corresponding properties in `object`, +`match` returns `true`. Note that the object matcher does not care if the +number of properties in the two objects are the same - only if all properties in +the matcher recursively matches ones in `object`. + +```javascript +// true +samsam.match("123", { + test: function (arg) { + return arg == 123; + } +}); + +// false +samsam.match({}, { prop: 42 }); + +// true +samsam.match({ + name: "Chris", + profession: "Programmer" +}, { + name: "Chris" +}); + +// false +samsam.match(234, { name: "Chris" }); +``` + + +**DOM elements** + +`match` can be very helpful when comparing DOM elements, because it allows +you to compare several properties with one call: + +```javascript +var el = document.getElementById("myEl"); + +samsam.match(el, { + tagName: "h2", + className: "item", + innerHTML: "Howdy" +}); +``` + + +## Changelog + +**1.1.2** (11.12.2014) + +* Fix for issue [#359 - `assert.match` does not support objects with `null` properties`](https://github.com/busterjs/buster/issues/359) +* Implementation of feature request [#64 - assert.match and parentNode](https://github.com/busterjs/buster/issues/64) + +**1.1.1** (26.03.2014) + +* [Make `isArguments` work with arguments from `"strict mode"` functions](https://github.com/busterjs/samsam/commit/72903613af90f39474f8388ed8957eaea4cf46ae) +* [Fix type error for nested object in function `match`](https://github.com/busterjs/samsam/commit/9d3420a11e9b3c65559945e60ca56980820db20f) +* Fix for issue [#366 - Assertion match fails with data attribute](https://github.com/busterjs/buster/issues/366) diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/autolint.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/autolint.js new file mode 100644 index 0000000..4f9755e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/autolint.js @@ -0,0 +1,23 @@ +module.exports = { + paths: [ + "lib/*.js", + "test/*.js" + ], + linterOptions: { + node: true, + browser: true, + plusplus: true, + vars: true, + nomen: true, + forin: true, + sloppy: true, + eqeq: true, + predef: [ + "_", + "define", + "assert", + "refute", + "buster" + ] + } +}; diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/jsTestDriver.conf b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/jsTestDriver.conf new file mode 100644 index 0000000..ee91c70 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/jsTestDriver.conf @@ -0,0 +1,9 @@ +server: http://localhost:4224 + +load: + - node_modules/sinon/lib/sinon.js + - node_modules/sinon/lib/sinon/spy.js + - lib/samsam.js + - node_modules/buster-util/lib/buster-util/test-case.js + - node_modules/buster-util/lib/buster-util/jstestdriver-shim.js + - test/samsam-test.js diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/lib/samsam.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/lib/samsam.js new file mode 100644 index 0000000..c451e68 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/lib/samsam.js @@ -0,0 +1,399 @@ +((typeof define === "function" && define.amd && function (m) { define("samsam", m); }) || + (typeof module === "object" && + function (m) { module.exports = m(); }) || // Node + function (m) { this.samsam = m(); } // Browser globals +)(function () { + var o = Object.prototype; + var div = typeof document !== "undefined" && document.createElement("div"); + + function isNaN(value) { + // Unlike global isNaN, this avoids type coercion + // typeof check avoids IE host object issues, hat tip to + // lodash + var val = value; // JsLint thinks value !== value is "weird" + return typeof value === "number" && value !== val; + } + + function getClass(value) { + // Returns the internal [[Class]] by calling Object.prototype.toString + // with the provided value as this. Return value is a string, naming the + // internal class, e.g. "Array" + return o.toString.call(value).split(/[ \]]/)[1]; + } + + /** + * @name samsam.isArguments + * @param Object object + * + * Returns ``true`` if ``object`` is an ``arguments`` object, + * ``false`` otherwise. + */ + function isArguments(object) { + if (getClass(object) === 'Arguments') { return true; } + if (typeof object !== "object" || typeof object.length !== "number" || + getClass(object) === "Array") { + return false; + } + if (typeof object.callee == "function") { return true; } + try { + object[object.length] = 6; + delete object[object.length]; + } catch (e) { + return true; + } + return false; + } + + /** + * @name samsam.isElement + * @param Object object + * + * Returns ``true`` if ``object`` is a DOM element node. Unlike + * Underscore.js/lodash, this function will return ``false`` if ``object`` + * is an *element-like* object, i.e. a regular object with a ``nodeType`` + * property that holds the value ``1``. + */ + function isElement(object) { + if (!object || object.nodeType !== 1 || !div) { return false; } + try { + object.appendChild(div); + object.removeChild(div); + } catch (e) { + return false; + } + return true; + } + + /** + * @name samsam.keys + * @param Object object + * + * Return an array of own property names. + */ + function keys(object) { + var ks = [], prop; + for (prop in object) { + if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); } + } + return ks; + } + + /** + * @name samsam.isDate + * @param Object value + * + * Returns true if the object is a ``Date``, or *date-like*. Duck typing + * of date objects work by checking that the object has a ``getTime`` + * function whose return value equals the return value from the object's + * ``valueOf``. + */ + function isDate(value) { + return typeof value.getTime == "function" && + value.getTime() == value.valueOf(); + } + + /** + * @name samsam.isNegZero + * @param Object value + * + * Returns ``true`` if ``value`` is ``-0``. + */ + function isNegZero(value) { + return value === 0 && 1 / value === -Infinity; + } + + /** + * @name samsam.equal + * @param Object obj1 + * @param Object obj2 + * + * Returns ``true`` if two objects are strictly equal. Compared to + * ``===`` there are two exceptions: + * + * - NaN is considered equal to NaN + * - -0 and +0 are not considered equal + */ + function identical(obj1, obj2) { + if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) { + return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2); + } + } + + + /** + * @name samsam.deepEqual + * @param Object obj1 + * @param Object obj2 + * + * Deep equal comparison. Two values are "deep equal" if: + * + * - They are equal, according to samsam.identical + * - They are both date objects representing the same time + * - They are both arrays containing elements that are all deepEqual + * - They are objects with the same set of properties, and each property + * in ``obj1`` is deepEqual to the corresponding property in ``obj2`` + * + * Supports cyclic objects. + */ + function deepEqualCyclic(obj1, obj2) { + + // used for cyclic comparison + // contain already visited objects + var objects1 = [], + objects2 = [], + // contain pathes (position in the object structure) + // of the already visited objects + // indexes same as in objects arrays + paths1 = [], + paths2 = [], + // contains combinations of already compared objects + // in the manner: { "$1['ref']$2['ref']": true } + compared = {}; + + /** + * used to check, if the value of a property is an object + * (cyclic logic is only needed for objects) + * only needed for cyclic logic + */ + function isObject(value) { + + if (typeof value === 'object' && value !== null && + !(value instanceof Boolean) && + !(value instanceof Date) && + !(value instanceof Number) && + !(value instanceof RegExp) && + !(value instanceof String)) { + + return true; + } + + return false; + } + + /** + * returns the index of the given object in the + * given objects array, -1 if not contained + * only needed for cyclic logic + */ + function getIndex(objects, obj) { + + var i; + for (i = 0; i < objects.length; i++) { + if (objects[i] === obj) { + return i; + } + } + + return -1; + } + + // does the recursion for the deep equal check + return (function deepEqual(obj1, obj2, path1, path2) { + var type1 = typeof obj1; + var type2 = typeof obj2; + + // == null also matches undefined + if (obj1 === obj2 || + isNaN(obj1) || isNaN(obj2) || + obj1 == null || obj2 == null || + type1 !== "object" || type2 !== "object") { + + return identical(obj1, obj2); + } + + // Elements are only equal if identical(expected, actual) + if (isElement(obj1) || isElement(obj2)) { return false; } + + var isDate1 = isDate(obj1), isDate2 = isDate(obj2); + if (isDate1 || isDate2) { + if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) { + return false; + } + } + + if (obj1 instanceof RegExp && obj2 instanceof RegExp) { + if (obj1.toString() !== obj2.toString()) { return false; } + } + + var class1 = getClass(obj1); + var class2 = getClass(obj2); + var keys1 = keys(obj1); + var keys2 = keys(obj2); + + if (isArguments(obj1) || isArguments(obj2)) { + if (obj1.length !== obj2.length) { return false; } + } else { + if (type1 !== type2 || class1 !== class2 || + keys1.length !== keys2.length) { + return false; + } + } + + var key, i, l, + // following vars are used for the cyclic logic + value1, value2, + isObject1, isObject2, + index1, index2, + newPath1, newPath2; + + for (i = 0, l = keys1.length; i < l; i++) { + key = keys1[i]; + if (!o.hasOwnProperty.call(obj2, key)) { + return false; + } + + // Start of the cyclic logic + + value1 = obj1[key]; + value2 = obj2[key]; + + isObject1 = isObject(value1); + isObject2 = isObject(value2); + + // determine, if the objects were already visited + // (it's faster to check for isObject first, than to + // get -1 from getIndex for non objects) + index1 = isObject1 ? getIndex(objects1, value1) : -1; + index2 = isObject2 ? getIndex(objects2, value2) : -1; + + // determine the new pathes of the objects + // - for non cyclic objects the current path will be extended + // by current property name + // - for cyclic objects the stored path is taken + newPath1 = index1 !== -1 + ? paths1[index1] + : path1 + '[' + JSON.stringify(key) + ']'; + newPath2 = index2 !== -1 + ? paths2[index2] + : path2 + '[' + JSON.stringify(key) + ']'; + + // stop recursion if current objects are already compared + if (compared[newPath1 + newPath2]) { + return true; + } + + // remember the current objects and their pathes + if (index1 === -1 && isObject1) { + objects1.push(value1); + paths1.push(newPath1); + } + if (index2 === -1 && isObject2) { + objects2.push(value2); + paths2.push(newPath2); + } + + // remember that the current objects are already compared + if (isObject1 && isObject2) { + compared[newPath1 + newPath2] = true; + } + + // End of cyclic logic + + // neither value1 nor value2 is a cycle + // continue with next level + if (!deepEqual(value1, value2, newPath1, newPath2)) { + return false; + } + } + + return true; + + }(obj1, obj2, '$1', '$2')); + } + + var match; + + function arrayContains(array, subset) { + if (subset.length === 0) { return true; } + var i, l, j, k; + for (i = 0, l = array.length; i < l; ++i) { + if (match(array[i], subset[0])) { + for (j = 0, k = subset.length; j < k; ++j) { + if (!match(array[i + j], subset[j])) { return false; } + } + return true; + } + } + return false; + } + + /** + * @name samsam.match + * @param Object object + * @param Object matcher + * + * Compare arbitrary value ``object`` with matcher. + */ + match = function match(object, matcher) { + if (matcher && typeof matcher.test === "function") { + return matcher.test(object); + } + + if (typeof matcher === "function") { + return matcher(object) === true; + } + + if (typeof matcher === "string") { + matcher = matcher.toLowerCase(); + var notNull = typeof object === "string" || !!object; + return notNull && + (String(object)).toLowerCase().indexOf(matcher) >= 0; + } + + if (typeof matcher === "number") { + return matcher === object; + } + + if (typeof matcher === "boolean") { + return matcher === object; + } + + if (typeof(matcher) === "undefined") { + return typeof(object) === "undefined"; + } + + if (matcher === null) { + return object === null; + } + + if (getClass(object) === "Array" && getClass(matcher) === "Array") { + return arrayContains(object, matcher); + } + + if (matcher && typeof matcher === "object") { + if (matcher === object) { + return true; + } + var prop; + for (prop in matcher) { + var value = object[prop]; + if (typeof value === "undefined" && + typeof object.getAttribute === "function") { + value = object.getAttribute(prop); + } + if (matcher[prop] === null || typeof matcher[prop] === 'undefined') { + if (value !== matcher[prop]) { + return false; + } + } else if (typeof value === "undefined" || !match(value, matcher[prop])) { + return false; + } + } + return true; + } + + throw new Error("Matcher was not a string, a number, a " + + "function, a boolean or an object"); + }; + + return { + isArguments: isArguments, + isElement: isElement, + isDate: isDate, + isNegZero: isNegZero, + identical: identical, + deepEqual: deepEqualCyclic, + match: match, + keys: keys + }; +}); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/package.json b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/package.json new file mode 100644 index 0000000..e760a15 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/package.json @@ -0,0 +1,69 @@ +{ + "name": "samsam", + "version": "1.1.2", + "description": "Value identification and comparison functions", + "homepage": "http://busterjs.org/docs/buster-assertions", + "author": { + "name": "Christian Johansen" + }, + "contributors": [ + { + "name": "Christian Johansen", + "email": "christian@cjohansen.no", + "url": "http://cjohansen.no" + }, + { + "name": "August Lilleaas", + "email": "august.lilleaas@gmail.com", + "url": "http://augustl.com" + }, + { + "name": "Daniel Wittner", + "email": "d.wittner@gmx.de", + "url": "https://github.com/dwittner" + } + ], + "main": "./lib/samsam", + "repository": { + "type": "git", + "url": "https://github.com/busterjs/samsam.git" + }, + "scripts": { + "test": "node test/samsam-test.js", + "test-debug": "node --debug-brk test/samsam-test.js" + }, + "devDependencies": { + "buster": "0.6.11" + }, + "bugs": { + "url": "https://github.com/busterjs/samsam/issues" + }, + "_id": "samsam@1.1.2", + "_shasum": "bec11fdc83a9fda063401210e40176c3024d1567", + "_from": "samsam@1.1.2", + "_npmVersion": "1.4.9", + "_npmUser": { + "name": "dwittner", + "email": "d.wittner@gmx.de" + }, + "maintainers": [ + { + "name": "cjohansen", + "email": "christian@cjohansen.no" + }, + { + "name": "augustl", + "email": "august@augustl.com" + }, + { + "name": "dwittner", + "email": "d.wittner@gmx.de" + } + ], + "dist": { + "shasum": "bec11fdc83a9fda063401210e40176c3024d1567", + "tarball": "http://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz" +} diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/test/samsam-test.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/test/samsam-test.js new file mode 100644 index 0000000..a55f9a2 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/samsam/test/samsam-test.js @@ -0,0 +1,386 @@ +if (typeof module === "object" && typeof require === "function") { + var buster = require("buster"); + var samsam = require("../lib/samsam"); +} + +(function () { + + var assert = buster.assert; + + function tests(method, body) { + var tc = {}; + + function pass(name) { + var args = Array.prototype.slice.call(arguments, 1); + tc["should return true for " + name] = function () { + assert(samsam[method].apply(samsam, args)); + }; + } + + function fail(name) { + var args = Array.prototype.slice.call(arguments, 1); + tc["should return false for " + name] = function () { + assert(!samsam[method].apply(samsam, args)); + }; + } + + function shouldThrow(name) { + var args = Array.prototype.slice.call(arguments, 1); + try { + samsam[method].apply(samsam, args); + buster.assertion.fail("Expected to throw"); + } catch (e) { + assert(true); + } + } + + function add(name, func) { + tc[name] = func; + } + + body(pass, fail, shouldThrow, add); + buster.testCase(method, tc); + } + + tests("isElement", function (pass, fail) { + if (typeof document !== "undefined") { + pass("DOM element node", document.createElement("div")); + fail("DOM text node", document.createTextNode("Hello")); + } + + fail("primitive", 42); + fail("object", {}); + fail("node-like object", { nodeType: 1 }); + }); + + tests("isNegZero", function (pass, fail) { + pass("-0", -0); + fail("0", 0); + fail("object", {}); + }); + + tests("identical", function (pass, fail) { + var object = { id: 42 }; + pass("same object", object, object); + pass("same primitive", 42, 42); + fail("-0 and 0", -0, 0); + pass("NaN and NaN", NaN, NaN); + }); + + tests("deepEqual", function (pass, fail) { + var func = function () {}; + var obj = {}; + var arr = []; + var date = new Date(); + var sameDate = new Date(date.getTime()); + var anotherDate = new Date(date.getTime() - 10); + var sameDateWithProp = new Date(date.getTime()); + sameDateWithProp.prop = 42; + + pass("object to itself", obj, obj); + pass("strings", "Hey", "Hey"); + pass("numbers", 32, 32); + pass("booleans", false, false); + pass("null", null, null); + pass("undefined", undefined, undefined); + pass("function to itself", func, func); + fail("functions", function () {}, function () {}); + pass("array to itself", arr, arr); + pass("date objects with same date", date, sameDate); + fail("date objects with different dates", date, anotherDate); + fail("date objects to null", date, null); + fail("date with different custom properties", date, sameDateWithProp); + fail("strings and numbers with coercion", "4", 4); + fail("numbers and strings with coercion", 4, "4"); + fail("number object with coercion", 32, new Number(32)); + fail("number object reverse with coercion", new Number(32), 32); + fail("falsy values with coercion", 0, ""); + fail("falsy values reverse with coercion", "", 0); + fail("string boxing with coercion", "4", new String("4")); + fail("string boxing reverse with coercion", new String("4"), "4"); + pass("NaN to NaN", NaN, NaN); + fail("-0 to +0", -0, +0); + fail("-0 to 0", -0, 0); + fail("objects with different own properties", + { id: 42 }, { id: 42, di: 24 }); + fail("objects with different own properties #2", + { id: undefined }, { di: 24 }); + fail("objects with different own properties #3", + { id: 24 }, { di: undefined }); + pass("objects with one property", { id: 42 }, { id: 42 }); + pass("objects with one object property", + { obj: { id: 42 } }, { obj: { id: 42 } }); + fail("objects with one property with different values", + { id: 42 }, { id: 24 }); + + var deepObject = { + id: 42, + name: "Hey", + sayIt: function () { + return this.name; + }, + + child: { + speaking: function () {} + } + }; + + pass("complex objects", deepObject, { + sayIt: deepObject.sayIt, + child: { speaking: deepObject.child.speaking }, + id: 42, + name: "Hey" + }); + + pass("arrays", + [1, 2, "Hey there", func, { id: 42, prop: [2, 3] }], + [1, 2, "Hey there", func, { id: 42, prop: [2, 3] }]); + + fail("nested array with shallow array", [["hey"]], ["hey"]); + + var arr1 = [1, 2, 3]; + var arr2 = [1, 2, 3]; + arr1.prop = 42; + fail("arrays with different custom properties", arr1, arr2); + + pass("regexp literals", /a/, /a/); + pass("regexp objects", new RegExp("[a-z]+"), new RegExp("[a-z]+")); + + var re1 = new RegExp("[a-z]+"); + var re2 = new RegExp("[a-z]+"); + re2.id = 42; + + fail("regexp objects with custom properties", re1, re2); + fail("different objects", { id: 42 }, {}); + fail("object to null", {}, null); + fail("object to undefined", {}, undefined); + fail("object to false", {}, false); + fail("false to object", false, {}); + fail("object to true", {}, true); + fail("true to object", true, {}); + fail("'empty' object to date", {}, new Date()); + fail("'empty' object to string object", {}, String()); + fail("'empty' object to number object", {}, Number()); + fail("'empty' object to empty array", {}, []); + + function gather() { return arguments; } + var arrayLike = { length: 4, "0": 1, "1": 2, "2": {}, "3": [] }; + + pass("arguments to array", [1, 2, {}, []], gather(1, 2, {}, [])); + pass("array to arguments", gather(), []); + + pass("arguments to array like object", + arrayLike, gather(1, 2, {}, [])); + }); + + /** + * Tests for cyclic objects. + */ + tests("deepEqual", function (pass, fail) { + + (function () { + var cyclic1 = {}, cyclic2 = {}; + cyclic1.ref = cyclic1; + cyclic2.ref = cyclic2; + pass("equal cyclic objects (cycle on 2nd level)", cyclic1, cyclic2); + }()); + + (function () { + var cyclic1 = {}, cyclic2 = {}; + cyclic1.ref = cyclic1; + cyclic2.ref = cyclic2; + cyclic2.ref2 = cyclic2; + fail("different cyclic objects (cycle on 2nd level)", + cyclic1, cyclic2); + }()); + + (function () { + var cyclic1 = {}, cyclic2 = {}; + cyclic1.ref = {}; + cyclic1.ref.ref = cyclic1; + cyclic2.ref = {}; + cyclic2.ref.ref = cyclic2; + pass("equal cyclic objects (cycle on 3rd level)", cyclic1, cyclic2); + }()); + + (function () { + var cyclic1 = {}, cyclic2 = {}; + cyclic1.ref = {}; + cyclic1.ref.ref = cyclic1; + cyclic2.ref = {}; + cyclic2.ref.ref = cyclic2; + cyclic2.ref.ref2 = cyclic2; + fail("different cyclic objects (cycle on 3rd level)", + cyclic1, cyclic2); + }()); + + (function () { + var cyclic1 = {}, cyclic2 = {}; + cyclic1.ref = cyclic1; + cyclic2.ref = cyclic1; + pass("equal objects even though only one object is cyclic", + cyclic1, cyclic2); + }()); + + (function () { + var cyclic1 = {}, cyclic2 = {}; + cyclic1.ref = { + ref: cyclic1 + }; + cyclic2.ref = {}; + cyclic2.ref.ref = cyclic2.ref; + pass("referencing different but equal cyclic objects", + cyclic1, cyclic2); + }()); + + (function () { + var cyclic1 = {a: "a"}, cyclic2 = {a: "a"}; + cyclic1.ref = { + b: "b", + ref: cyclic1 + }; + cyclic2.ref = { + b: "b" + }; + cyclic2.ref.ref = cyclic2.ref; + fail("referencing different and unequal cyclic objects", + cyclic1, cyclic2); + }()); + }); + + tests("match", function (pass, fail, shouldThrow, add) { + pass("matching regexp", "Assertions", /[a-z]/); + pass("generic object and test method returning true", "Assertions", { + test: function () { return true; } + }); + fail("non-matching regexp", "Assertions 123", /^[a-z]$/); + pass("matching boolean", true, true); + fail("mismatching boolean", true, false); + fail("generic object with test method returning false", "Assertions", { + test: function () { return false; } + }); + shouldThrow("match object === null", "Assertions 123", null); + fail("match object === false", "Assertions 123", false); + fail("matching number against string", "Assertions 123", 23); + fail("matching number against similar string", "23", 23); + pass("matching number against itself", 23, 23); + pass("matcher function returns true", + "Assertions 123", function (obj) { return true; }); + fail("matcher function returns false", + "Assertions 123", function (obj) { return false; }); + fail("matcher function returns falsy", + "Assertions 123", function () {}); + fail("matcher does not return explicit true", + "Assertions 123", function () { return "Hey"; }); + + add("should call matcher with object", function () { + var spy = this.spy(); + samsam.match("Assertions 123", spy); + assert.calledWith(spy, "Assertions 123"); + }); + + pass("matcher is substring of matchee", "Diskord", "or"); + pass("matcher is string equal to matchee", "Diskord", "Diskord"); + pass("strings ignoring case", "Look ma, case-insensitive", + "LoOk Ma, CaSe-InSenSiTiVe"); + fail("match string is not substring of matchee", "Vim", "Emacs"); + fail("match string is not substring of object", {}, "Emacs"); + fail("matcher is not substring of object.toString", { + toString: function () { return "Vim"; } + }, "Emacs"); + fail("null and empty string", null, ""); + fail("undefined and empty string", undefined, ""); + fail("false and empty string", false, ""); + fail("0 and empty string", 0, ""); + fail("NaN and empty string", NaN, ""); + + var object = { + id: 42, + name: "Christian", + doIt: "yes", + + speak: function () { + return this.name; + } + }; + + pass("object containing all properties in matcher", object, { + id: 42, + doIt: "yes" + }); + + var object2 = { + id: 42, + name: "Christian", + doIt: "yes", + owner: { + someDude: "Yes", + hello: "ok" + }, + + speak: function () { + return this.name; + } + }; + + pass("nested matcher", object2, { + owner: { + someDude: "Yes", + hello: function (value) { + return value == "ok"; + } + } + }); + + pass("empty strings", "", ""); + pass("empty strings as object properties", { foo: "" }, { foo: "" }); + pass("similar arrays", [1, 2, 3], [1, 2, 3]); + pass("array subset", [1, 2, 3], [2, 3]); + pass("single-element array subset", [1, 2, 3], [1]); + pass("matching array subset", [1, 2, 3, { id: 42 }], [{ id: 42 }]); + fail("mis-matching array 'subset'", [1, 2, 3], [2, 3, 4]); + fail("mis-ordered array 'subset'", [1, 2, 3], [1, 3]); + pass("empty arrays", [], []); + pass("objects with empty arrays", { xs: [] }, { xs: [] }); + fail("nested objects with different depth", { a: 1 }, { b: { c: 2 } }); + pass("dom elements with matching data attributes", { + getAttribute: function (name) { + if (name === "data-path") { + return "foo.bar"; + } + } + }, { "data-path": "foo.bar" }); + fail("dom elements with not matching data attributes", { + getAttribute: function (name) { + if (name === "data-path") { + return "foo.foo"; + } + } + }, { "data-path": "foo.bar" }); + + pass("equal null properties", { foo: null }, { foo: null }); + fail("unmatched null property", {}, { foo: null }); + fail("matcher with unmatched null property", { foo: 'arbitrary' }, { foo: null }); + pass("equal undefined properties", { foo: undefined }, { foo: undefined }); + fail("matcher with unmatched undefined property", { foo: 'arbitrary' }, { foo: undefined }); + pass('unmatched undefined property', {}, { foo: undefined }); + + var obj = { foo: undefined }; + pass("same object matches self", obj, obj); + + pass("null matches null", null, null); + fail("null does not match undefined", null, undefined); + + pass("undefined matches undefined", undefined, undefined); + fail("undefined does not match null", undefined, null); + + }); + + tests("isArguments", function (pass, fail) { + pass("arguments object", arguments); + fail("primitive", 42); + fail("object", {}); + pass("arguments object from strict-mode function", + (function () { "use strict"; return arguments; }())); + }); +}()); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/.npmignore b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/.travis.yml b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/.travis.yml new file mode 100644 index 0000000..ded625c --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/.travis.yml @@ -0,0 +1,8 @@ +language: node_js +node_js: +- '0.8' +- '0.10' +env: + global: + - secure: AdUubswCR68/eGD+WWjwTHgFbelwQGnNo81j1IOaUxKw+zgFPzSnFEEtDw7z98pWgg7p9DpCnyzzSnSllP40wq6AG19OwyUJjSLoZK57fp+r8zwTQwWiSqUgMu2YSMmKJPIO/aoSGpRQXT+L1nRrHoUJXgFodyIZgz40qzJeZjc= + - secure: heQuxPVsQ7jBbssoVKimXDpqGjQFiucm6W5spoujmspjDG7oEcHD9ANo9++LoRPrsAmNx56SpMK5fNfVmYediw6SvhXm4Mxt56/fYCrLDBtgGG+1neCeffAi8z1rO8x48m77hcQ6YhbUL5R9uBimUjMX92fZcygAt8Rg804zjFo= diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/.zuul.yml b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/.zuul.yml new file mode 100644 index 0000000..2105010 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/.zuul.yml @@ -0,0 +1,10 @@ +ui: mocha-qunit +browsers: + - name: chrome + version: 27..latest + - name: firefox + version: latest + - name: safari + version: latest + - name: ie + version: 9..latest diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/LICENSE b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/LICENSE new file mode 100644 index 0000000..e3d4e69 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/LICENSE @@ -0,0 +1,18 @@ +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/README.md b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/README.md new file mode 100644 index 0000000..1c473d2 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/README.md @@ -0,0 +1,15 @@ +# util + +[![Build Status](https://travis-ci.org/defunctzombie/node-util.png?branch=master)](https://travis-ci.org/defunctzombie/node-util) + +node.js [util](http://nodejs.org/api/util.html) module as a module + +## install via [npm](npmjs.org) + +```shell +npm install util +``` + +## browser support + +This module also works in modern browsers. If you need legacy browser support you will need to polyfill ES5 features. diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/LICENSE b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/LICENSE new file mode 100644 index 0000000..dea3013 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/LICENSE @@ -0,0 +1,16 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/README.md b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/README.md new file mode 100644 index 0000000..b1c5665 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/README.md @@ -0,0 +1,42 @@ +Browser-friendly inheritance fully compatible with standard node.js +[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor). + +This package exports standard `inherits` from node.js `util` module in +node environment, but also provides alternative browser-friendly +implementation through [browser +field](https://gist.github.com/shtylman/4339901). Alternative +implementation is a literal copy of standard one located in standalone +module to avoid requiring of `util`. It also has a shim for old +browsers with no `Object.create` support. + +While keeping you sure you are using standard `inherits` +implementation in node.js environment, it allows bundlers such as +[browserify](https://github.com/substack/node-browserify) to not +include full `util` package to your client code if all you need is +just `inherits` function. It worth, because browser shim for `util` +package is large and `inherits` is often the single function you need +from it. + +It's recommended to use this package instead of +`require('util').inherits` for any code that has chances to be used +not only in node.js but in browser too. + +## usage + +```js +var inherits = require('inherits'); +// then use exactly as the standard one +``` + +## note on version ~1.0 + +Version ~1.0 had completely different motivation and is not compatible +neither with 2.0 nor with standard node.js `inherits`. + +If you are using version ~1.0 and planning to switch to ~2.0, be +careful: + +* new version uses `super_` instead of `super` for referencing + superclass +* new version overwrites current prototype while old one preserves any + existing fields on it diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/inherits.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/inherits.js new file mode 100644 index 0000000..29f5e24 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/inherits.js @@ -0,0 +1 @@ +module.exports = require('util').inherits diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/inherits_browser.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/inherits_browser.js new file mode 100644 index 0000000..c1e78a7 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/inherits_browser.js @@ -0,0 +1,23 @@ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/package.json b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/package.json new file mode 100644 index 0000000..d6435d0 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/package.json @@ -0,0 +1,50 @@ +{ + "name": "inherits", + "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()", + "version": "2.0.1", + "keywords": [ + "inheritance", + "class", + "klass", + "oop", + "object-oriented", + "inherits", + "browser", + "browserify" + ], + "main": "./inherits.js", + "browser": "./inherits_browser.js", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/inherits.git" + }, + "license": "ISC", + "scripts": { + "test": "node test" + }, + "bugs": { + "url": "https://github.com/isaacs/inherits/issues" + }, + "_id": "inherits@2.0.1", + "dist": { + "shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1", + "tarball": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "_from": "inherits@2.0.1", + "_npmVersion": "1.3.8", + "_npmUser": { + "name": "isaacs", + "email": "i@izs.me" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + } + ], + "directories": {}, + "_shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1", + "_resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "readme": "ERROR: No README data found!", + "homepage": "https://github.com/isaacs/inherits#readme" +} diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/test.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/test.js new file mode 100644 index 0000000..fc53012 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/node_modules/inherits/test.js @@ -0,0 +1,25 @@ +var inherits = require('./inherits.js') +var assert = require('assert') + +function test(c) { + assert(c.constructor === Child) + assert(c.constructor.super_ === Parent) + assert(Object.getPrototypeOf(c) === Child.prototype) + assert(Object.getPrototypeOf(Object.getPrototypeOf(c)) === Parent.prototype) + assert(c instanceof Child) + assert(c instanceof Parent) +} + +function Child() { + Parent.call(this) + test(this) +} + +function Parent() {} + +inherits(Child, Parent) + +var c = new Child +test(c) + +console.log('ok') diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/package.json b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/package.json new file mode 100644 index 0000000..cef5588 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/package.json @@ -0,0 +1,54 @@ +{ + "author": { + "name": "Joyent", + "url": "http://www.joyent.com" + }, + "name": "util", + "description": "Node.JS util module", + "keywords": [ + "util" + ], + "version": "0.10.3", + "homepage": "https://github.com/defunctzombie/node-util", + "repository": { + "type": "git", + "url": "git://github.com/defunctzombie/node-util" + }, + "main": "./util.js", + "scripts": { + "test": "node test/node/*.js && zuul test/browser/*.js" + }, + "dependencies": { + "inherits": "2.0.1" + }, + "license": "MIT", + "devDependencies": { + "zuul": "~1.0.9" + }, + "browser": { + "./support/isBuffer.js": "./support/isBufferBrowser.js" + }, + "bugs": { + "url": "https://github.com/defunctzombie/node-util/issues" + }, + "_id": "util@0.10.3", + "dist": { + "shasum": "7afb1afe50805246489e3db7fe0ed379336ac0f9", + "tarball": "http://registry.npmjs.org/util/-/util-0.10.3.tgz" + }, + "_from": "util@>=0.10.3 <1.0.0", + "_npmVersion": "1.3.24", + "_npmUser": { + "name": "shtylman", + "email": "shtylman@gmail.com" + }, + "maintainers": [ + { + "name": "shtylman", + "email": "shtylman@gmail.com" + } + ], + "directories": {}, + "_shasum": "7afb1afe50805246489e3db7fe0ed379336ac0f9", + "_resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz" +} diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/support/isBuffer.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/support/isBuffer.js new file mode 100644 index 0000000..ace9ac0 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/support/isBuffer.js @@ -0,0 +1,3 @@ +module.exports = function isBuffer(arg) { + return arg instanceof Buffer; +} diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/support/isBufferBrowser.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/support/isBufferBrowser.js new file mode 100644 index 0000000..0e1bee1 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/support/isBufferBrowser.js @@ -0,0 +1,6 @@ +module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; +} \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/browser/inspect.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/browser/inspect.js new file mode 100644 index 0000000..91af3b0 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/browser/inspect.js @@ -0,0 +1,41 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var util = require('../../'); + +suite('inspect'); + +test('util.inspect - test for sparse array', function () { + var a = ['foo', 'bar', 'baz']; + assert.equal(util.inspect(a), '[ \'foo\', \'bar\', \'baz\' ]'); + delete a[1]; + assert.equal(util.inspect(a), '[ \'foo\', , \'baz\' ]'); + assert.equal(util.inspect(a, true), '[ \'foo\', , \'baz\', [length]: 3 ]'); + assert.equal(util.inspect(new Array(5)), '[ , , , , ]'); +}); + +test('util.inspect - exceptions should print the error message, not \'{}\'', function () { + assert.equal(util.inspect(new Error()), '[Error]'); + assert.equal(util.inspect(new Error('FAIL')), '[Error: FAIL]'); + assert.equal(util.inspect(new TypeError('FAIL')), '[TypeError: FAIL]'); + assert.equal(util.inspect(new SyntaxError('FAIL')), '[SyntaxError: FAIL]'); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/browser/is.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/browser/is.js new file mode 100644 index 0000000..f63bff9 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/browser/is.js @@ -0,0 +1,91 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); + +var util = require('../../'); + +suite('is'); + +test('util.isArray', function () { + assert.equal(true, util.isArray([])); + assert.equal(true, util.isArray(Array())); + assert.equal(true, util.isArray(new Array())); + assert.equal(true, util.isArray(new Array(5))); + assert.equal(true, util.isArray(new Array('with', 'some', 'entries'))); + assert.equal(false, util.isArray({})); + assert.equal(false, util.isArray({ push: function() {} })); + assert.equal(false, util.isArray(/regexp/)); + assert.equal(false, util.isArray(new Error())); + assert.equal(false, util.isArray(Object.create(Array.prototype))); +}); + +test('util.isRegExp', function () { + assert.equal(true, util.isRegExp(/regexp/)); + assert.equal(true, util.isRegExp(RegExp())); + assert.equal(true, util.isRegExp(new RegExp())); + assert.equal(false, util.isRegExp({})); + assert.equal(false, util.isRegExp([])); + assert.equal(false, util.isRegExp(new Date())); + assert.equal(false, util.isRegExp(Object.create(RegExp.prototype))); +}); + +test('util.isDate', function () { + assert.equal(true, util.isDate(new Date())); + assert.equal(true, util.isDate(new Date(0))); + assert.equal(false, util.isDate(Date())); + assert.equal(false, util.isDate({})); + assert.equal(false, util.isDate([])); + assert.equal(false, util.isDate(new Error())); + assert.equal(false, util.isDate(Object.create(Date.prototype))); +}); + +test('util.isError', function () { + assert.equal(true, util.isError(new Error())); + assert.equal(true, util.isError(new TypeError())); + assert.equal(true, util.isError(new SyntaxError())); + assert.equal(false, util.isError({})); + assert.equal(false, util.isError({ name: 'Error', message: '' })); + assert.equal(false, util.isError([])); + assert.equal(true, util.isError(Object.create(Error.prototype))); +}); + +test('util._extend', function () { + assert.deepEqual(util._extend({a:1}), {a:1}); + assert.deepEqual(util._extend({a:1}, []), {a:1}); + assert.deepEqual(util._extend({a:1}, null), {a:1}); + assert.deepEqual(util._extend({a:1}, true), {a:1}); + assert.deepEqual(util._extend({a:1}, false), {a:1}); + assert.deepEqual(util._extend({a:1}, {b:2}), {a:1, b:2}); + assert.deepEqual(util._extend({a:1, b:2}, {b:3}), {a:1, b:3}); +}); + +test('util.isBuffer', function () { + assert.equal(true, util.isBuffer(new Buffer(4))); + assert.equal(true, util.isBuffer(Buffer(4))); + assert.equal(true, util.isBuffer(new Buffer(4))); + assert.equal(true, util.isBuffer(new Buffer([1, 2, 3, 4]))); + assert.equal(false, util.isBuffer({})); + assert.equal(false, util.isBuffer([])); + assert.equal(false, util.isBuffer(new Error())); + assert.equal(false, util.isRegExp(new Date())); + assert.equal(true, util.isBuffer(Object.create(Buffer.prototype))); +}); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/debug.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/debug.js new file mode 100644 index 0000000..ef5f69f --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/debug.js @@ -0,0 +1,86 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var util = require('../../'); + +if (process.argv[2] === 'child') + child(); +else + parent(); + +function parent() { + test('foo,tud,bar', true); + test('foo,tud', true); + test('tud,bar', true); + test('tud', true); + test('foo,bar', false); + test('', false); +} + +function test(environ, shouldWrite) { + var expectErr = ''; + if (shouldWrite) { + expectErr = 'TUD %PID%: this { is: \'a\' } /debugging/\n' + + 'TUD %PID%: number=1234 string=asdf obj={"foo":"bar"}\n'; + } + var expectOut = 'ok\n'; + var didTest = false; + + var spawn = require('child_process').spawn; + var child = spawn(process.execPath, [__filename, 'child'], { + env: { NODE_DEBUG: environ } + }); + + expectErr = expectErr.split('%PID%').join(child.pid); + + var err = ''; + child.stderr.setEncoding('utf8'); + child.stderr.on('data', function(c) { + err += c; + }); + + var out = ''; + child.stdout.setEncoding('utf8'); + child.stdout.on('data', function(c) { + out += c; + }); + + child.on('close', function(c) { + assert(!c); + assert.equal(err, expectErr); + assert.equal(out, expectOut); + didTest = true; + console.log('ok %j %j', environ, shouldWrite); + }); + + process.on('exit', function() { + assert(didTest); + }); +} + + +function child() { + var debug = util.debuglog('tud'); + debug('this', { is: 'a' }, /debugging/); + debug('number=%d string=%s obj=%j', 1234, 'asdf', { foo: 'bar' }); + console.log('ok'); +} diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/format.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/format.js new file mode 100644 index 0000000..f2d1862 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/format.js @@ -0,0 +1,77 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + +var assert = require('assert'); +var util = require('../../'); + +assert.equal(util.format(), ''); +assert.equal(util.format(''), ''); +assert.equal(util.format([]), '[]'); +assert.equal(util.format({}), '{}'); +assert.equal(util.format(null), 'null'); +assert.equal(util.format(true), 'true'); +assert.equal(util.format(false), 'false'); +assert.equal(util.format('test'), 'test'); + +// CHECKME this is for console.log() compatibility - but is it *right*? +assert.equal(util.format('foo', 'bar', 'baz'), 'foo bar baz'); + +assert.equal(util.format('%d', 42.0), '42'); +assert.equal(util.format('%d', 42), '42'); +assert.equal(util.format('%s', 42), '42'); +assert.equal(util.format('%j', 42), '42'); + +assert.equal(util.format('%d', '42.0'), '42'); +assert.equal(util.format('%d', '42'), '42'); +assert.equal(util.format('%s', '42'), '42'); +assert.equal(util.format('%j', '42'), '"42"'); + +assert.equal(util.format('%%s%s', 'foo'), '%sfoo'); + +assert.equal(util.format('%s'), '%s'); +assert.equal(util.format('%s', undefined), 'undefined'); +assert.equal(util.format('%s', 'foo'), 'foo'); +assert.equal(util.format('%s:%s'), '%s:%s'); +assert.equal(util.format('%s:%s', undefined), 'undefined:%s'); +assert.equal(util.format('%s:%s', 'foo'), 'foo:%s'); +assert.equal(util.format('%s:%s', 'foo', 'bar'), 'foo:bar'); +assert.equal(util.format('%s:%s', 'foo', 'bar', 'baz'), 'foo:bar baz'); +assert.equal(util.format('%%%s%%', 'hi'), '%hi%'); +assert.equal(util.format('%%%s%%%%', 'hi'), '%hi%%'); + +(function() { + var o = {}; + o.o = o; + assert.equal(util.format('%j', o), '[Circular]'); +})(); + +// Errors +assert.equal(util.format(new Error('foo')), '[Error: foo]'); +function CustomError(msg) { + Error.call(this); + Object.defineProperty(this, 'message', { value: msg, enumerable: false }); + Object.defineProperty(this, 'name', { value: 'CustomError', enumerable: false }); +} +util.inherits(CustomError, Error); +assert.equal(util.format(new CustomError('bar')), '[CustomError: bar]'); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/inspect.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/inspect.js new file mode 100644 index 0000000..f766d11 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/inspect.js @@ -0,0 +1,195 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + +var assert = require('assert'); +var util = require('../../'); + +// test the internal isDate implementation +var Date2 = require('vm').runInNewContext('Date'); +var d = new Date2(); +var orig = util.inspect(d); +Date2.prototype.foo = 'bar'; +var after = util.inspect(d); +assert.equal(orig, after); + +// test for sparse array +var a = ['foo', 'bar', 'baz']; +assert.equal(util.inspect(a), '[ \'foo\', \'bar\', \'baz\' ]'); +delete a[1]; +assert.equal(util.inspect(a), '[ \'foo\', , \'baz\' ]'); +assert.equal(util.inspect(a, true), '[ \'foo\', , \'baz\', [length]: 3 ]'); +assert.equal(util.inspect(new Array(5)), '[ , , , , ]'); + +// test for property descriptors +var getter = Object.create(null, { + a: { + get: function() { return 'aaa'; } + } +}); +var setter = Object.create(null, { + b: { + set: function() {} + } +}); +var getterAndSetter = Object.create(null, { + c: { + get: function() { return 'ccc'; }, + set: function() {} + } +}); +assert.equal(util.inspect(getter, true), '{ [a]: [Getter] }'); +assert.equal(util.inspect(setter, true), '{ [b]: [Setter] }'); +assert.equal(util.inspect(getterAndSetter, true), '{ [c]: [Getter/Setter] }'); + +// exceptions should print the error message, not '{}' +assert.equal(util.inspect(new Error()), '[Error]'); +assert.equal(util.inspect(new Error('FAIL')), '[Error: FAIL]'); +assert.equal(util.inspect(new TypeError('FAIL')), '[TypeError: FAIL]'); +assert.equal(util.inspect(new SyntaxError('FAIL')), '[SyntaxError: FAIL]'); +try { + undef(); +} catch (e) { + assert.equal(util.inspect(e), '[ReferenceError: undef is not defined]'); +} +var ex = util.inspect(new Error('FAIL'), true); +assert.ok(ex.indexOf('[Error: FAIL]') != -1); +assert.ok(ex.indexOf('[stack]') != -1); +assert.ok(ex.indexOf('[message]') != -1); + +// GH-1941 +// should not throw: +assert.equal(util.inspect(Object.create(Date.prototype)), '{}'); + +// GH-1944 +assert.doesNotThrow(function() { + var d = new Date(); + d.toUTCString = null; + util.inspect(d); +}); + +assert.doesNotThrow(function() { + var r = /regexp/; + r.toString = null; + util.inspect(r); +}); + +// bug with user-supplied inspect function returns non-string +assert.doesNotThrow(function() { + util.inspect([{ + inspect: function() { return 123; } + }]); +}); + +// GH-2225 +var x = { inspect: util.inspect }; +assert.ok(util.inspect(x).indexOf('inspect') != -1); + +// util.inspect.styles and util.inspect.colors +function test_color_style(style, input, implicit) { + var color_name = util.inspect.styles[style]; + var color = ['', '']; + if(util.inspect.colors[color_name]) + color = util.inspect.colors[color_name]; + + var without_color = util.inspect(input, false, 0, false); + var with_color = util.inspect(input, false, 0, true); + var expect = '\u001b[' + color[0] + 'm' + without_color + + '\u001b[' + color[1] + 'm'; + assert.equal(with_color, expect, 'util.inspect color for style '+style); +} + +test_color_style('special', function(){}); +test_color_style('number', 123.456); +test_color_style('boolean', true); +test_color_style('undefined', undefined); +test_color_style('null', null); +test_color_style('string', 'test string'); +test_color_style('date', new Date); +test_color_style('regexp', /regexp/); + +// an object with "hasOwnProperty" overwritten should not throw +assert.doesNotThrow(function() { + util.inspect({ + hasOwnProperty: null + }); +}); + +// new API, accepts an "options" object +var subject = { foo: 'bar', hello: 31, a: { b: { c: { d: 0 } } } }; +Object.defineProperty(subject, 'hidden', { enumerable: false, value: null }); + +assert(util.inspect(subject, { showHidden: false }).indexOf('hidden') === -1); +assert(util.inspect(subject, { showHidden: true }).indexOf('hidden') !== -1); +assert(util.inspect(subject, { colors: false }).indexOf('\u001b[32m') === -1); +assert(util.inspect(subject, { colors: true }).indexOf('\u001b[32m') !== -1); +assert(util.inspect(subject, { depth: 2 }).indexOf('c: [Object]') !== -1); +assert(util.inspect(subject, { depth: 0 }).indexOf('a: [Object]') !== -1); +assert(util.inspect(subject, { depth: null }).indexOf('{ d: 0 }') !== -1); + +// "customInspect" option can enable/disable calling inspect() on objects +subject = { inspect: function() { return 123; } }; + +assert(util.inspect(subject, { customInspect: true }).indexOf('123') !== -1); +assert(util.inspect(subject, { customInspect: true }).indexOf('inspect') === -1); +assert(util.inspect(subject, { customInspect: false }).indexOf('123') === -1); +assert(util.inspect(subject, { customInspect: false }).indexOf('inspect') !== -1); + +// custom inspect() functions should be able to return other Objects +subject.inspect = function() { return { foo: 'bar' }; }; + +assert.equal(util.inspect(subject), '{ foo: \'bar\' }'); + +subject.inspect = function(depth, opts) { + assert.strictEqual(opts.customInspectOptions, true); +}; + +util.inspect(subject, { customInspectOptions: true }); + +// util.inspect with "colors" option should produce as many lines as without it +function test_lines(input) { + var count_lines = function(str) { + return (str.match(/\n/g) || []).length; + } + + var without_color = util.inspect(input); + var with_color = util.inspect(input, {colors: true}); + assert.equal(count_lines(without_color), count_lines(with_color)); +} + +test_lines([1, 2, 3, 4, 5, 6, 7]); +test_lines(function() { + var big_array = []; + for (var i = 0; i < 100; i++) { + big_array.push(i); + } + return big_array; +}()); +test_lines({foo: 'bar', baz: 35, b: {a: 35}}); +test_lines({ + foo: 'bar', + baz: 35, + b: {a: 35}, + very_long_key: 'very_long_value', + even_longer_key: ['with even longer value in array'] +}); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/log.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/log.js new file mode 100644 index 0000000..6bd96d1 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/log.js @@ -0,0 +1,58 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var assert = require('assert'); +var util = require('../../'); + +assert.ok(process.stdout.writable); +assert.ok(process.stderr.writable); + +var stdout_write = global.process.stdout.write; +var strings = []; +global.process.stdout.write = function(string) { + strings.push(string); +}; +console._stderr = process.stdout; + +var tests = [ + {input: 'foo', output: 'foo'}, + {input: undefined, output: 'undefined'}, + {input: null, output: 'null'}, + {input: false, output: 'false'}, + {input: 42, output: '42'}, + {input: function(){}, output: '[Function]'}, + {input: parseInt('not a number', 10), output: 'NaN'}, + {input: {answer: 42}, output: '{ answer: 42 }'}, + {input: [1,2,3], output: '[ 1, 2, 3 ]'} +]; + +// test util.log() +tests.forEach(function(test) { + util.log(test.input); + var result = strings.shift().trim(), + re = (/[0-9]{1,2} [A-Z][a-z]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} - (.+)$/), + match = re.exec(result); + assert.ok(match); + assert.equal(match[1], test.output); +}); + +global.process.stdout.write = stdout_write; diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/util.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/util.js new file mode 100644 index 0000000..633ba69 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/test/node/util.js @@ -0,0 +1,83 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var assert = require('assert'); +var context = require('vm').runInNewContext; + +var util = require('../../'); + +// isArray +assert.equal(true, util.isArray([])); +assert.equal(true, util.isArray(Array())); +assert.equal(true, util.isArray(new Array())); +assert.equal(true, util.isArray(new Array(5))); +assert.equal(true, util.isArray(new Array('with', 'some', 'entries'))); +assert.equal(true, util.isArray(context('Array')())); +assert.equal(false, util.isArray({})); +assert.equal(false, util.isArray({ push: function() {} })); +assert.equal(false, util.isArray(/regexp/)); +assert.equal(false, util.isArray(new Error)); +assert.equal(false, util.isArray(Object.create(Array.prototype))); + +// isRegExp +assert.equal(true, util.isRegExp(/regexp/)); +assert.equal(true, util.isRegExp(RegExp())); +assert.equal(true, util.isRegExp(new RegExp())); +assert.equal(true, util.isRegExp(context('RegExp')())); +assert.equal(false, util.isRegExp({})); +assert.equal(false, util.isRegExp([])); +assert.equal(false, util.isRegExp(new Date())); +assert.equal(false, util.isRegExp(Object.create(RegExp.prototype))); + +// isDate +assert.equal(true, util.isDate(new Date())); +assert.equal(true, util.isDate(new Date(0))); +assert.equal(true, util.isDate(new (context('Date')))); +assert.equal(false, util.isDate(Date())); +assert.equal(false, util.isDate({})); +assert.equal(false, util.isDate([])); +assert.equal(false, util.isDate(new Error)); +assert.equal(false, util.isDate(Object.create(Date.prototype))); + +// isError +assert.equal(true, util.isError(new Error)); +assert.equal(true, util.isError(new TypeError)); +assert.equal(true, util.isError(new SyntaxError)); +assert.equal(true, util.isError(new (context('Error')))); +assert.equal(true, util.isError(new (context('TypeError')))); +assert.equal(true, util.isError(new (context('SyntaxError')))); +assert.equal(false, util.isError({})); +assert.equal(false, util.isError({ name: 'Error', message: '' })); +assert.equal(false, util.isError([])); +assert.equal(true, util.isError(Object.create(Error.prototype))); + +// isObject +assert.ok(util.isObject({}) === true); + +// _extend +assert.deepEqual(util._extend({a:1}), {a:1}); +assert.deepEqual(util._extend({a:1}, []), {a:1}); +assert.deepEqual(util._extend({a:1}, null), {a:1}); +assert.deepEqual(util._extend({a:1}, true), {a:1}); +assert.deepEqual(util._extend({a:1}, false), {a:1}); +assert.deepEqual(util._extend({a:1}, {b:2}), {a:1, b:2}); +assert.deepEqual(util._extend({a:1, b:2}, {b:3}), {a:1, b:3}); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/util.js b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/util.js new file mode 100644 index 0000000..e0ea321 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/node_modules/util/util.js @@ -0,0 +1,586 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; + + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = require('./support/isBuffer'); + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ +exports.inherits = require('inherits'); + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/package.json b/node_modules/promises-aplus-tests/node_modules/sinon/package.json new file mode 100644 index 0000000..666734b --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/package.json @@ -0,0 +1,747 @@ +{ + "name": "sinon", + "description": "JavaScript test spies, stubs and mocks.", + "version": "1.17.2", + "homepage": "http://sinonjs.org/", + "author": { + "name": "Christian Johansen" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/cjohansen/Sinon.JS.git" + }, + "bugs": { + "url": "http://github.com/cjohansen/Sinon.JS/issues" + }, + "license": "BSD-3-Clause", + "scripts": { + "ci-test": "npm run lint && ./scripts/ci-test.sh", + "test": "./scripts/ci-test.sh", + "lint": "$(npm bin)/eslint .", + "prepublish": "./build", + "eslint-pre-commit": "./scripts/eslint-pre-commit" + }, + "pre-commit": [ + "eslint-pre-commit" + ], + "dependencies": { + "formatio": "1.1.1", + "util": ">=0.10.3 <1", + "lolex": "1.3.2", + "samsam": "1.1.2" + }, + "devDependencies": { + "buster": "0.7.18", + "buster-core": "^0.6.4", + "buster-istanbul": "0.1.13", + "eslint": "0.24.0", + "eslint-config-defaults": "^2.1.0", + "jscs": "1.13.1", + "pre-commit": "1.0.10" + }, + "files": [ + "lib", + "pkg", + "AUTHORS", + "CONTRIBUTING.md", + "Changelog.txt", + "LICENSE", + "README.md" + ], + "main": "./lib/sinon.js", + "engines": { + "node": ">=0.1.103" + }, + "contributors": [ + { + "name": "Christian Johansen", + "email": "christian@cjohansen.no" + }, + { + "name": "Morgan Roderick", + "email": "morgan@roderick.dk" + }, + { + "name": "Maximilian Antoni", + "email": "mail@maxantoni.de" + }, + { + "name": "ben hockey", + "email": "neonstalwart@gmail.com" + }, + { + "name": "Tim Fischbach", + "email": "mail@timfischbach.de" + }, + { + "name": "Max Antoni", + "email": "mail@maxantoni.de" + }, + { + "name": "Tim Ruffles", + "email": "timruffles@googlemail.com" + }, + { + "name": "Jonathan Sokolowski", + "email": "jonathan.sokolowski@gmail.com" + }, + { + "name": "Phred", + "email": "fearphage@gmail.com" + }, + { + "name": "Domenic Denicola", + "email": "domenic@domenicdenicola.com" + }, + { + "name": "William Sears", + "email": "MrBigDog2U@gmail.com" + }, + { + "name": "Andreas Lind", + "email": "andreas@one.com" + }, + { + "name": "Tim Ruffles", + "email": "timr@picklive.com" + }, + { + "name": "Tim Perry", + "email": "pimterry@gmail.com" + }, + { + "name": "pimterry", + "email": "pimterry@gmail.com" + }, + { + "name": "Felix Geisendörfer", + "email": "felix@debuggable.com" + }, + { + "name": "kpdecker", + "email": "kpdecker@gmail.com" + }, + { + "name": "Bryan Donovan", + "email": "bdondo@gmail.com" + }, + { + "name": "Andrew Gurinovich", + "email": "altmind@gmail.com" + }, + { + "name": "Martin Sander", + "email": "forke@uni-bonn.de" + }, + { + "name": "Luis Cardoso", + "email": "luis.cardoso@feedzai.com" + }, + { + "name": "Keith Cirkel", + "email": "github@keithcirkel.co.uk" + }, + { + "name": "Tristan Koch", + "email": "tristan.koch@1und1.de" + }, + { + "name": "Tobias Ebnöther", + "email": "ebi@gorn.ch" + }, + { + "name": "Cory", + "email": "seeflanigan@gmail.com" + }, + { + "name": "Christian Johansen", + "email": "christian.johansen@nrk.no" + }, + { + "name": "Marten Lienen", + "email": "marten.lienen@gmail.com" + }, + { + "name": "Travis Kaufman", + "email": "travis.kaufman@gmail.com" + }, + { + "name": "Benjamin Coe", + "email": "ben@yesware.com" + }, + { + "name": "ben fleis", + "email": "ben.fleis@gmail.com" + }, + { + "name": "Garrick Cheung", + "email": "garrick@garrickcheung.com" + }, + { + "name": "Gavin Huang", + "email": "gravof@gmail.com" + }, + { + "name": "Jonny Reeves", + "email": "github@jonnyreeves.co.uk" + }, + { + "name": "Konrad Holowinski", + "email": "konrad.holowinski@gmail.com" + }, + { + "name": "zcicala", + "email": "zcicala@fitbit.com" + }, + { + "name": "Duncan Beevers", + "email": "duncan@dweebd.com" + }, + { + "name": "Jmeas", + "email": "jellyes2@gmail.com" + }, + { + "name": "August Lilleaas", + "email": "august.lilleaas@gmail.com" + }, + { + "name": "Scott Andrews", + "email": "scothis@gmail.com" + }, + { + "name": "Robin Pedersen", + "email": "robinp@snap.tv" + }, + { + "name": "Garrick", + "email": "gcheung@fitbit.com" + }, + { + "name": "Carl-Erik Kopseng", + "email": "carlerik@gmail.com" + }, + { + "name": "geries", + "email": "geries.handal@videoplaza.com" + }, + { + "name": "Cormac Flynn", + "email": "cormac.flynn@viadeoteam.com" + }, + { + "name": "Ming Liu", + "email": "vmliu1@gmail.com" + }, + { + "name": "Thomas Meyer", + "email": "meyertee@gmail.com" + }, + { + "name": "Roman Potashow", + "email": "justgook@gmail.com" + }, + { + "name": "Tamas Szebeni", + "email": "tamas_szebeni@epam.com" + }, + { + "name": "Glen Mailer", + "email": "glen.mailer@bskyb.com" + }, + { + "name": "Soutaro Matsumoto", + "email": "matsumoto@soutaro.com" + }, + { + "name": "なつき", + "email": "i@ntk.me" + }, + { + "name": "Alex Urbano", + "email": "asgaroth.belem@gmail.com" + }, + { + "name": "Alexander Schmidt", + "email": "alexanderschmidt1@gmail.com" + }, + { + "name": "Ben Hockey", + "email": "neonstalwart@gmail.com" + }, + { + "name": "Brandon Heyer", + "email": "brandonheyer@gmail.com" + }, + { + "name": "Christian Johansen", + "email": "christian.johansen@finn.no" + }, + { + "name": "Devin Weaver", + "email": "suki@tritarget.org" + }, + { + "name": "Farid Neshat", + "email": "FaridN_SOAD@yahoo.com" + }, + { + "name": "G.Serebryanskyi", + "email": "x5x3x5x@gmail.com" + }, + { + "name": "Henry Tung", + "email": "henryptung@gmail.com" + }, + { + "name": "Irina Dumitrascu", + "email": "me@dira.ro" + }, + { + "name": "James Barwell", + "email": "jb@jamesbarwell.co.uk" + }, + { + "name": "Jason Karns", + "email": "jason.karns@gmail.com" + }, + { + "name": "Jeffrey Falgout", + "email": "jeffrey.falgout@gmail.com" + }, + { + "name": "Jeffrey Falgout", + "email": "jfalgout@bloomberg.net" + }, + { + "name": "Jonathan Freeman", + "email": "freethejazz@gmail.com" + }, + { + "name": "Josh Graham", + "email": "josh@canva.com" + }, + { + "name": "Marcus Hüsgen", + "email": "marcus.huesgen@lusini.com" + }, + { + "name": "Martin Hansen", + "email": "martin@martinhansen.no" + }, + { + "name": "Matt Kern", + "email": "matt@bloomcrush.com" + }, + { + "name": "Max Calabrese", + "email": "max.calabrese@ymail.com" + }, + { + "name": "Márton Salomváry", + "email": "salomvary@gmail.com" + }, + { + "name": "Satoshi Nakamura", + "email": "snakamura@infoteria.com" + }, + { + "name": "Simen Bekkhus", + "email": "sbekkhus91@gmail.com" + }, + { + "name": "Spencer Elliott", + "email": "me@elliottsj.com" + }, + { + "name": "Travis Kaufman", + "email": "travis.kaufman@refinery29.com" + }, + { + "name": "Victor Costan", + "email": "costan@gmail.com" + }, + { + "name": "gtothesquare", + "email": "me@gerieshandal.com" + }, + { + "name": "mohayonao", + "email": "mohayonao@gmail.com" + }, + { + "name": "vitalets", + "email": "vitalets@yandex-team.ru" + }, + { + "name": "yoshimura-toshihide", + "email": "toshihide0105yoshimura@gmail.com" + }, + { + "name": "Ian Thomas", + "email": "ian@ian-thomas.net" + }, + { + "name": "jamestalmage", + "email": "james.talmage@jrtechnical.com" + }, + { + "name": "Mario Pareja", + "email": "mpareja@360incentives.com" + }, + { + "name": "Ian Lewis", + "email": "IanMLewis@gmail.com" + }, + { + "name": "Martin Brochhaus", + "email": "mbrochh@gmail.com" + }, + { + "name": "kbackowski", + "email": "kbackowski@gmail.com" + }, + { + "name": "Harry Wolff", + "email": "hswolff@gmail.com" + }, + { + "name": "Adrian Phinney", + "email": "adrian.phinney@bellaliant.net" + }, + { + "name": "Gyandeep Singh", + "email": "gyandeeps@gmail.com" + }, + { + "name": "AJ Ortega", + "email": "ajo@google.com" + }, + { + "name": "Max Klymyshyn", + "email": "klymyshyn@gmail.com" + }, + { + "name": "Gordon L. Hempton", + "email": "ghempton@gmail.com" + }, + { + "name": "Michael Jackson", + "email": "mjijackson@gmail.com" + }, + { + "name": "Mikolaj Banasik", + "email": "d1sover@gmail.com" + }, + { + "name": "Gord Tanner", + "email": "gord@tinyhippos.com" + }, + { + "name": "Glen Mailer", + "email": "glenjamin@gmail.com" + }, + { + "name": "Mustafa Sak", + "email": "mustafa.sak@1und1.de" + }, + { + "name": "ngryman", + "email": "ngryman@gmail.com" + }, + { + "name": "Nicholas Stephan", + "email": "nicholas.stephan@gmail.com" + }, + { + "name": "Nikita Litvin", + "email": "deltaidea@derpy.ru" + }, + { + "name": "Niklas Andreasson", + "email": "eaglus_@hotmail.com" + }, + { + "name": "Olmo Maldonado", + "email": "olmo.maldonado@gmail.com" + }, + { + "name": "Giorgos Giannoutsos", + "email": "contact@nuc.gr" + }, + { + "name": "Rajeesh C V", + "email": "cvrajeesh@gmail.com" + }, + { + "name": "Raynos", + "email": "raynos2@gmail.com" + }, + { + "name": "Gilad Peleg", + "email": "giladp007@gmail.com" + }, + { + "name": "Rodion Vynnychenko", + "email": "roddiku@gmail.com" + }, + { + "name": "Gavin Boulton", + "email": "gavin.boulton@digital.cabinet-office.gov.uk" + }, + { + "name": "Ryan Wholey", + "email": "rjwholey@gmail.com" + }, + { + "name": "Adam Hull", + "email": "adam@hmlad.com" + }, + { + "name": "Felix Geisendörfer", + "email": "felix@debuggable.com" + }, + { + "name": "Sergio Cinos", + "email": "scinos@atlassian.com" + }, + { + "name": "Shawn Krisman", + "email": "skrisman@nodelings" + }, + { + "name": "Shawn Krisman", + "email": "telaviv@github" + }, + { + "name": "Shinnosuke Watanabe", + "email": "snnskwtnb@gmail.com" + }, + { + "name": "simonzack", + "email": "simonzack@gmail.com" + }, + { + "name": "Simone Fonda", + "email": "fonda@netseven.it" + }, + { + "name": "Eric Wendelin", + "email": "ewendelin@twitter.com" + }, + { + "name": "stevesouth", + "email": "stephen.south@caplin.com" + }, + { + "name": "Sven Fuchs", + "email": "svenfuchs@artweb-design.de" + }, + { + "name": "Søren Enemærke", + "email": "soren.enemaerke@gmail.com" + }, + { + "name": "TEHEK Firefox", + "email": "tehek@tehek.net" + }, + { + "name": "Dmitriy Kubyshkin", + "email": "grassator@gmail.com" + }, + { + "name": "Tek Nynja", + "email": "github@teknynja.com" + }, + { + "name": "Daryl Lau", + "email": "daryl@goodeggs.com" + }, + { + "name": "Tim Branyen", + "email": "tim@tabdeveloper.com" + }, + { + "name": "Christian Johansen", + "email": "christian@shortcut.no" + }, + { + "name": "Burak Yiğit Kaya", + "email": "ben@byk.im" + }, + { + "name": "Brian M Hunt", + "email": "brianmhunt@gmail.com" + }, + { + "name": "Blake Israel", + "email": "blake.israel@gatech.edu" + }, + { + "name": "Timo Tijhof", + "email": "krinklemail@gmail.com" + }, + { + "name": "Blake Embrey", + "email": "hello@blakeembrey.com" + }, + { + "name": "Blaine Bublitz", + "email": "blaine@iceddev.com" + }, + { + "name": "till", + "email": "till@php.net" + }, + { + "name": "Antonio D'Ettole", + "email": "antonio@brandwatch.com" + }, + { + "name": "Tristan Koch", + "email": "tristan@tknetwork.de" + }, + { + "name": "AJ Ortega", + "email": "ajo@renitservices.com" + }, + { + "name": "Volkan Ozcelik", + "email": "volkan.ozcelik@jivesoftware.com" + }, + { + "name": "Will Butler", + "email": "will@butlerhq.com" + }, + { + "name": "William Meleyal", + "email": "w.meleyal@wollzelle.com" + }, + { + "name": "Ali Shakiba", + "email": "ali@shakiba.me" + }, + { + "name": "Xiao Ma", + "email": "x@medium.com" + }, + { + "name": "Alfonso Boza", + "email": "alfonso@cloud.com" + }, + { + "name": "Alexander Aivars", + "email": "alex@aivars.se" + }, + { + "name": "brandonheyer", + "email": "brandonheyer@gmail.com" + }, + { + "name": "charlierudolph", + "email": "charles.w.rudolph@gmail.com" + }, + { + "name": "Alex Kessaris", + "email": "alex@artsy.ca" + }, + { + "name": "goligo", + "email": "ich@malte.de" + }, + { + "name": "John Bernardo", + "email": "jbernardo@linkedin.com" + }, + { + "name": "wwalser", + "email": "waw325@gmail.com" + }, + { + "name": "Jason Anderson", + "email": "diurnalist@gmail.com" + }, + { + "name": "Jan Suchý", + "email": "jan.sandokan@gmail.com" + }, + { + "name": "Jordan Hawker", + "email": "hawker.jordan@gmail.com" + }, + { + "name": "Joseph Spens", + "email": "joseph@workmarket.com" + }, + { + "name": "hashchange", + "email": "heim@zeilenwechsel.de" + }, + { + "name": "Jan Kopriva", + "email": "jan.kopriva@gooddata.com" + }, + { + "name": "Kevin Turner", + "email": "kevin@decipherinc.com" + }, + { + "name": "Kim Joar Bekkelund", + "email": "kjbekkelund@gmail.com" + }, + { + "name": "James Beavers", + "email": "jamesjbeavers@gmail.com" + }, + { + "name": "Kris Kowal", + "email": "kris.kowal@cixar.com" + }, + { + "name": "Kurt Ruppel", + "email": "me@kurtruppel.com" + }, + { + "name": "Lars Thorup", + "email": "lars@zealake.com" + }, + { + "name": "Luchs", + "email": "Luchs@euirc.eu" + }, + { + "name": "Marco Ramirez", + "email": "marco-ramirez@bankofamerica.com" + } + ], + "gitHead": "aa333b775bb7b3a6d06ee1d8e28b33b82d56014e", + "_id": "sinon@1.17.2", + "_shasum": "c1ea67b84a1e7b3350f6c4713efacef8e4ae8b71", + "_from": "sinon@>=1.10.3 <2.0.0", + "_npmVersion": "2.14.7", + "_nodeVersion": "0.10.40", + "_npmUser": { + "name": "mrgnrdrck", + "email": "morgan@roderick.dk" + }, + "maintainers": [ + { + "name": "cjohansen", + "email": "christian@cjohansen.no" + }, + { + "name": "mrgnrdrck", + "email": "morgan@roderick.dk" + }, + { + "name": "mantoni", + "email": "mail@maxantoni.de" + } + ], + "dist": { + "shasum": "c1ea67b84a1e7b3350f6c4713efacef8e4ae8b71", + "tarball": "http://registry.npmjs.org/sinon/-/sinon-1.17.2.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.2.tgz" +} diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-1.16.1.js b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-1.16.1.js new file mode 100644 index 0000000..53b0ed7 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-1.16.1.js @@ -0,0 +1,6279 @@ +/** + * Sinon.JS 1.16.1, 2015/09/22 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +(function (root, factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + define('sinon', [], function () { + return (root.sinon = factory()); + }); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.sinon = factory(); + } +}(this, function () { + 'use strict'; + var samsam, formatio, lolex; + (function () { + function define(mod, deps, fn) { + if (mod == "samsam") { + samsam = deps(); + } else if (typeof deps === "function" && mod.length === 0) { + lolex = deps(); + } else if (typeof fn === "function") { + formatio = fn(samsam); + } + } + define.amd = {}; +((typeof define === "function" && define.amd && function (m) { define("samsam", m); }) || + (typeof module === "object" && + function (m) { module.exports = m(); }) || // Node + function (m) { this.samsam = m(); } // Browser globals +)(function () { + var o = Object.prototype; + var div = typeof document !== "undefined" && document.createElement("div"); + + function isNaN(value) { + // Unlike global isNaN, this avoids type coercion + // typeof check avoids IE host object issues, hat tip to + // lodash + var val = value; // JsLint thinks value !== value is "weird" + return typeof value === "number" && value !== val; + } + + function getClass(value) { + // Returns the internal [[Class]] by calling Object.prototype.toString + // with the provided value as this. Return value is a string, naming the + // internal class, e.g. "Array" + return o.toString.call(value).split(/[ \]]/)[1]; + } + + /** + * @name samsam.isArguments + * @param Object object + * + * Returns ``true`` if ``object`` is an ``arguments`` object, + * ``false`` otherwise. + */ + function isArguments(object) { + if (getClass(object) === 'Arguments') { return true; } + if (typeof object !== "object" || typeof object.length !== "number" || + getClass(object) === "Array") { + return false; + } + if (typeof object.callee == "function") { return true; } + try { + object[object.length] = 6; + delete object[object.length]; + } catch (e) { + return true; + } + return false; + } + + /** + * @name samsam.isElement + * @param Object object + * + * Returns ``true`` if ``object`` is a DOM element node. Unlike + * Underscore.js/lodash, this function will return ``false`` if ``object`` + * is an *element-like* object, i.e. a regular object with a ``nodeType`` + * property that holds the value ``1``. + */ + function isElement(object) { + if (!object || object.nodeType !== 1 || !div) { return false; } + try { + object.appendChild(div); + object.removeChild(div); + } catch (e) { + return false; + } + return true; + } + + /** + * @name samsam.keys + * @param Object object + * + * Return an array of own property names. + */ + function keys(object) { + var ks = [], prop; + for (prop in object) { + if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); } + } + return ks; + } + + /** + * @name samsam.isDate + * @param Object value + * + * Returns true if the object is a ``Date``, or *date-like*. Duck typing + * of date objects work by checking that the object has a ``getTime`` + * function whose return value equals the return value from the object's + * ``valueOf``. + */ + function isDate(value) { + return typeof value.getTime == "function" && + value.getTime() == value.valueOf(); + } + + /** + * @name samsam.isNegZero + * @param Object value + * + * Returns ``true`` if ``value`` is ``-0``. + */ + function isNegZero(value) { + return value === 0 && 1 / value === -Infinity; + } + + /** + * @name samsam.equal + * @param Object obj1 + * @param Object obj2 + * + * Returns ``true`` if two objects are strictly equal. Compared to + * ``===`` there are two exceptions: + * + * - NaN is considered equal to NaN + * - -0 and +0 are not considered equal + */ + function identical(obj1, obj2) { + if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) { + return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2); + } + } + + + /** + * @name samsam.deepEqual + * @param Object obj1 + * @param Object obj2 + * + * Deep equal comparison. Two values are "deep equal" if: + * + * - They are equal, according to samsam.identical + * - They are both date objects representing the same time + * - They are both arrays containing elements that are all deepEqual + * - They are objects with the same set of properties, and each property + * in ``obj1`` is deepEqual to the corresponding property in ``obj2`` + * + * Supports cyclic objects. + */ + function deepEqualCyclic(obj1, obj2) { + + // used for cyclic comparison + // contain already visited objects + var objects1 = [], + objects2 = [], + // contain pathes (position in the object structure) + // of the already visited objects + // indexes same as in objects arrays + paths1 = [], + paths2 = [], + // contains combinations of already compared objects + // in the manner: { "$1['ref']$2['ref']": true } + compared = {}; + + /** + * used to check, if the value of a property is an object + * (cyclic logic is only needed for objects) + * only needed for cyclic logic + */ + function isObject(value) { + + if (typeof value === 'object' && value !== null && + !(value instanceof Boolean) && + !(value instanceof Date) && + !(value instanceof Number) && + !(value instanceof RegExp) && + !(value instanceof String)) { + + return true; + } + + return false; + } + + /** + * returns the index of the given object in the + * given objects array, -1 if not contained + * only needed for cyclic logic + */ + function getIndex(objects, obj) { + + var i; + for (i = 0; i < objects.length; i++) { + if (objects[i] === obj) { + return i; + } + } + + return -1; + } + + // does the recursion for the deep equal check + return (function deepEqual(obj1, obj2, path1, path2) { + var type1 = typeof obj1; + var type2 = typeof obj2; + + // == null also matches undefined + if (obj1 === obj2 || + isNaN(obj1) || isNaN(obj2) || + obj1 == null || obj2 == null || + type1 !== "object" || type2 !== "object") { + + return identical(obj1, obj2); + } + + // Elements are only equal if identical(expected, actual) + if (isElement(obj1) || isElement(obj2)) { return false; } + + var isDate1 = isDate(obj1), isDate2 = isDate(obj2); + if (isDate1 || isDate2) { + if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) { + return false; + } + } + + if (obj1 instanceof RegExp && obj2 instanceof RegExp) { + if (obj1.toString() !== obj2.toString()) { return false; } + } + + var class1 = getClass(obj1); + var class2 = getClass(obj2); + var keys1 = keys(obj1); + var keys2 = keys(obj2); + + if (isArguments(obj1) || isArguments(obj2)) { + if (obj1.length !== obj2.length) { return false; } + } else { + if (type1 !== type2 || class1 !== class2 || + keys1.length !== keys2.length) { + return false; + } + } + + var key, i, l, + // following vars are used for the cyclic logic + value1, value2, + isObject1, isObject2, + index1, index2, + newPath1, newPath2; + + for (i = 0, l = keys1.length; i < l; i++) { + key = keys1[i]; + if (!o.hasOwnProperty.call(obj2, key)) { + return false; + } + + // Start of the cyclic logic + + value1 = obj1[key]; + value2 = obj2[key]; + + isObject1 = isObject(value1); + isObject2 = isObject(value2); + + // determine, if the objects were already visited + // (it's faster to check for isObject first, than to + // get -1 from getIndex for non objects) + index1 = isObject1 ? getIndex(objects1, value1) : -1; + index2 = isObject2 ? getIndex(objects2, value2) : -1; + + // determine the new pathes of the objects + // - for non cyclic objects the current path will be extended + // by current property name + // - for cyclic objects the stored path is taken + newPath1 = index1 !== -1 + ? paths1[index1] + : path1 + '[' + JSON.stringify(key) + ']'; + newPath2 = index2 !== -1 + ? paths2[index2] + : path2 + '[' + JSON.stringify(key) + ']'; + + // stop recursion if current objects are already compared + if (compared[newPath1 + newPath2]) { + return true; + } + + // remember the current objects and their pathes + if (index1 === -1 && isObject1) { + objects1.push(value1); + paths1.push(newPath1); + } + if (index2 === -1 && isObject2) { + objects2.push(value2); + paths2.push(newPath2); + } + + // remember that the current objects are already compared + if (isObject1 && isObject2) { + compared[newPath1 + newPath2] = true; + } + + // End of cyclic logic + + // neither value1 nor value2 is a cycle + // continue with next level + if (!deepEqual(value1, value2, newPath1, newPath2)) { + return false; + } + } + + return true; + + }(obj1, obj2, '$1', '$2')); + } + + var match; + + function arrayContains(array, subset) { + if (subset.length === 0) { return true; } + var i, l, j, k; + for (i = 0, l = array.length; i < l; ++i) { + if (match(array[i], subset[0])) { + for (j = 0, k = subset.length; j < k; ++j) { + if (!match(array[i + j], subset[j])) { return false; } + } + return true; + } + } + return false; + } + + /** + * @name samsam.match + * @param Object object + * @param Object matcher + * + * Compare arbitrary value ``object`` with matcher. + */ + match = function match(object, matcher) { + if (matcher && typeof matcher.test === "function") { + return matcher.test(object); + } + + if (typeof matcher === "function") { + return matcher(object) === true; + } + + if (typeof matcher === "string") { + matcher = matcher.toLowerCase(); + var notNull = typeof object === "string" || !!object; + return notNull && + (String(object)).toLowerCase().indexOf(matcher) >= 0; + } + + if (typeof matcher === "number") { + return matcher === object; + } + + if (typeof matcher === "boolean") { + return matcher === object; + } + + if (typeof(matcher) === "undefined") { + return typeof(object) === "undefined"; + } + + if (matcher === null) { + return object === null; + } + + if (getClass(object) === "Array" && getClass(matcher) === "Array") { + return arrayContains(object, matcher); + } + + if (matcher && typeof matcher === "object") { + if (matcher === object) { + return true; + } + var prop; + for (prop in matcher) { + var value = object[prop]; + if (typeof value === "undefined" && + typeof object.getAttribute === "function") { + value = object.getAttribute(prop); + } + if (matcher[prop] === null || typeof matcher[prop] === 'undefined') { + if (value !== matcher[prop]) { + return false; + } + } else if (typeof value === "undefined" || !match(value, matcher[prop])) { + return false; + } + } + return true; + } + + throw new Error("Matcher was not a string, a number, a " + + "function, a boolean or an object"); + }; + + return { + isArguments: isArguments, + isElement: isElement, + isDate: isDate, + isNegZero: isNegZero, + identical: identical, + deepEqual: deepEqualCyclic, + match: match, + keys: keys + }; +}); +((typeof define === "function" && define.amd && function (m) { + define("formatio", ["samsam"], m); +}) || (typeof module === "object" && function (m) { + module.exports = m(require("samsam")); +}) || function (m) { this.formatio = m(this.samsam); } +)(function (samsam) { + + var formatio = { + excludeConstructors: ["Object", /^.$/], + quoteStrings: true, + limitChildrenCount: 0 + }; + + var hasOwn = Object.prototype.hasOwnProperty; + + var specialObjects = []; + if (typeof global !== "undefined") { + specialObjects.push({ object: global, value: "[object global]" }); + } + if (typeof document !== "undefined") { + specialObjects.push({ + object: document, + value: "[object HTMLDocument]" + }); + } + if (typeof window !== "undefined") { + specialObjects.push({ object: window, value: "[object Window]" }); + } + + function functionName(func) { + if (!func) { return ""; } + if (func.displayName) { return func.displayName; } + if (func.name) { return func.name; } + var matches = func.toString().match(/function\s+([^\(]+)/m); + return (matches && matches[1]) || ""; + } + + function constructorName(f, object) { + var name = functionName(object && object.constructor); + var excludes = f.excludeConstructors || + formatio.excludeConstructors || []; + + var i, l; + for (i = 0, l = excludes.length; i < l; ++i) { + if (typeof excludes[i] === "string" && excludes[i] === name) { + return ""; + } else if (excludes[i].test && excludes[i].test(name)) { + return ""; + } + } + + return name; + } + + function isCircular(object, objects) { + if (typeof object !== "object") { return false; } + var i, l; + for (i = 0, l = objects.length; i < l; ++i) { + if (objects[i] === object) { return true; } + } + return false; + } + + function ascii(f, object, processed, indent) { + if (typeof object === "string") { + var qs = f.quoteStrings; + var quote = typeof qs !== "boolean" || qs; + return processed || quote ? '"' + object + '"' : object; + } + + if (typeof object === "function" && !(object instanceof RegExp)) { + return ascii.func(object); + } + + processed = processed || []; + + if (isCircular(object, processed)) { return "[Circular]"; } + + if (Object.prototype.toString.call(object) === "[object Array]") { + return ascii.array.call(f, object, processed); + } + + if (!object) { return String((1/object) === -Infinity ? "-0" : object); } + if (samsam.isElement(object)) { return ascii.element(object); } + + if (typeof object.toString === "function" && + object.toString !== Object.prototype.toString) { + return object.toString(); + } + + var i, l; + for (i = 0, l = specialObjects.length; i < l; i++) { + if (object === specialObjects[i].object) { + return specialObjects[i].value; + } + } + + return ascii.object.call(f, object, processed, indent); + } + + ascii.func = function (func) { + return "function " + functionName(func) + "() {}"; + }; + + ascii.array = function (array, processed) { + processed = processed || []; + processed.push(array); + var pieces = []; + var i, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, array.length) : array.length; + + for (i = 0; i < l; ++i) { + pieces.push(ascii(this, array[i], processed)); + } + + if(l < array.length) + pieces.push("[... " + (array.length - l) + " more elements]"); + + return "[" + pieces.join(", ") + "]"; + }; + + ascii.object = function (object, processed, indent) { + processed = processed || []; + processed.push(object); + indent = indent || 0; + var pieces = [], properties = samsam.keys(object).sort(); + var length = 3; + var prop, str, obj, i, k, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, properties.length) : properties.length; + + for (i = 0; i < l; ++i) { + prop = properties[i]; + obj = object[prop]; + + if (isCircular(obj, processed)) { + str = "[Circular]"; + } else { + str = ascii(this, obj, processed, indent + 2); + } + + str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str; + length += str.length; + pieces.push(str); + } + + var cons = constructorName(this, object); + var prefix = cons ? "[" + cons + "] " : ""; + var is = ""; + for (i = 0, k = indent; i < k; ++i) { is += " "; } + + if(l < properties.length) + pieces.push("[... " + (properties.length - l) + " more elements]"); + + if (length + indent > 80) { + return prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" + + is + "}"; + } + return prefix + "{ " + pieces.join(", ") + " }"; + }; + + ascii.element = function (element) { + var tagName = element.tagName.toLowerCase(); + var attrs = element.attributes, attr, pairs = [], attrName, i, l, val; + + for (i = 0, l = attrs.length; i < l; ++i) { + attr = attrs.item(i); + attrName = attr.nodeName.toLowerCase().replace("html:", ""); + val = attr.nodeValue; + if (attrName !== "contenteditable" || val !== "inherit") { + if (!!val) { pairs.push(attrName + "=\"" + val + "\""); } + } + } + + var formatted = "<" + tagName + (pairs.length > 0 ? " " : ""); + var content = element.innerHTML; + + if (content.length > 20) { + content = content.substr(0, 20) + "[...]"; + } + + var res = formatted + pairs.join(" ") + ">" + content + + ""; + + return res.replace(/ contentEditable="inherit"/, ""); + }; + + function Formatio(options) { + for (var opt in options) { + this[opt] = options[opt]; + } + } + + Formatio.prototype = { + functionName: functionName, + + configure: function (options) { + return new Formatio(options); + }, + + constructorName: function (object) { + return constructorName(this, object); + }, + + ascii: function (object, processed, indent) { + return ascii(this, object, processed, indent); + } + }; + + return Formatio.prototype; +}); +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.lolex=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { + throw new Error("tick only understands numbers and 'h:m:s'"); + } + + while (i--) { + parsed = parseInt(strings[i], 10); + + if (parsed >= 60) { + throw new Error("Invalid time " + str); + } + + ms += parsed * Math.pow(60, (l - i - 1)); + } + + return ms * 1000; +} + +/** + * Used to grok the `now` parameter to createClock. + */ +function getEpoch(epoch) { + if (!epoch) { return 0; } + if (typeof epoch.getTime === "function") { return epoch.getTime(); } + if (typeof epoch === "number") { return epoch; } + throw new TypeError("now should be milliseconds since UNIX epoch"); +} + +function inRange(from, to, timer) { + return timer && timer.callAt >= from && timer.callAt <= to; +} + +function mirrorDateProperties(target, source) { + if (source.now) { + target.now = function now() { + return target.clock.now; + }; + } else { + delete target.now; + } + + if (source.toSource) { + target.toSource = function toSource() { + return source.toSource(); + }; + } else { + delete target.toSource; + } + + target.toString = function toString() { + return source.toString(); + }; + + target.prototype = source.prototype; + target.parse = source.parse; + target.UTC = source.UTC; + target.prototype.toUTCString = source.prototype.toUTCString; + + for (var prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + return target; +} + +function createDate() { + function ClockDate(year, month, date, hour, minute, second, ms) { + // Defensive and verbose to avoid potential harm in passing + // explicit undefined when user does not pass argument + switch (arguments.length) { + case 0: + return new NativeDate(ClockDate.clock.now); + case 1: + return new NativeDate(year); + case 2: + return new NativeDate(year, month); + case 3: + return new NativeDate(year, month, date); + case 4: + return new NativeDate(year, month, date, hour); + case 5: + return new NativeDate(year, month, date, hour, minute); + case 6: + return new NativeDate(year, month, date, hour, minute, second); + default: + return new NativeDate(year, month, date, hour, minute, second, ms); + } + } + + return mirrorDateProperties(ClockDate, NativeDate); +} + +function addTimer(clock, timer) { + if (typeof timer.func === "undefined") { + throw new Error("Callback must be provided to timer calls"); + } + + if (!clock.timers) { + clock.timers = {}; + } + + timer.id = id++; + timer.createdAt = clock.now; + timer.callAt = clock.now + (timer.delay || 0); + + clock.timers[timer.id] = timer; + + if (addTimerReturnsObject) { + return { + id: timer.id, + ref: function() {}, + unref: function() {} + }; + } + else { + return timer.id; + } +} + +function firstTimerInRange(clock, from, to) { + var timers = clock.timers, timer = null; + + for (var id in timers) { + if (!inRange(from, to, timers[id])) { + continue; + } + + if (!timer || ~compareTimers(timer, timers[id])) { + timer = timers[id]; + } + } + + return timer; +} + +function compareTimers(a, b) { + // Sort first by absolute timing + if (a.callAt < b.callAt) { + return -1; + } + if (a.callAt > b.callAt) { + return 1; + } + + // Sort next by immediate, immediate timers take precedence + if (a.immediate && !b.immediate) { + return -1; + } + if (!a.immediate && b.immediate) { + return 1; + } + + // Sort next by creation time, earlier-created timers take precedence + if (a.createdAt < b.createdAt) { + return -1; + } + if (a.createdAt > b.createdAt) { + return 1; + } + + // Sort next by id, lower-id timers take precedence + if (a.id < b.id) { + return -1; + } + if (a.id > b.id) { + return 1; + } + + // As timer ids are unique, no fallback `0` is necessary +} + +function callTimer(clock, timer) { + if (typeof timer.interval == "number") { + clock.timers[timer.id].callAt += timer.interval; + } else { + delete clock.timers[timer.id]; + } + + try { + if (typeof timer.func == "function") { + timer.func.apply(null, timer.args); + } else { + eval(timer.func); + } + } catch (e) { + var exception = e; + } + + if (!clock.timers[timer.id]) { + if (exception) { + throw exception; + } + return; + } + + if (exception) { + throw exception; + } +} + +function uninstall(clock, target) { + var method; + + for (var i = 0, l = clock.methods.length; i < l; i++) { + method = clock.methods[i]; + + if (target[method].hadOwnProperty) { + target[method] = clock["_" + method]; + } else { + try { + delete target[method]; + } catch (e) {} + } + } + + // Prevent multiple executions which will completely remove these props + clock.methods = []; +} + +function hijackMethod(target, method, clock) { + clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method); + clock["_" + method] = target[method]; + + if (method == "Date") { + var date = mirrorDateProperties(clock[method], target[method]); + target[method] = date; + } else { + target[method] = function () { + return clock[method].apply(clock, arguments); + }; + + for (var prop in clock[method]) { + if (clock[method].hasOwnProperty(prop)) { + target[method][prop] = clock[method][prop]; + } + } + } + + target[method].clock = clock; +} + +var timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), + clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate: undefined), + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date +}; + +var keys = Object.keys || function (obj) { + var ks = []; + for (var key in obj) { + ks.push(key); + } + return ks; +}; + +exports.timers = timers; + +var createClock = exports.createClock = function (now) { + var clock = { + now: getEpoch(now), + timeouts: {}, + Date: createDate() + }; + + clock.Date.clock = clock; + + clock.setTimeout = function setTimeout(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout + }); + }; + + clock.clearTimeout = function clearTimeout(timerId) { + if (!timerId) { + // null appears to be allowed in most browsers, and appears to be + // relied upon by some libraries, like Bootstrap carousel + return; + } + if (!clock.timers) { + clock.timers = []; + } + // in Node, timerId is an object with .ref()/.unref(), and + // its .id field is the actual timer id. + if (typeof timerId === "object") { + timerId = timerId.id + } + if (timerId in clock.timers) { + delete clock.timers[timerId]; + } + }; + + clock.setInterval = function setInterval(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout, + interval: timeout + }); + }; + + clock.clearInterval = function clearInterval(timerId) { + clock.clearTimeout(timerId); + }; + + clock.setImmediate = function setImmediate(func) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 1), + immediate: true + }); + }; + + clock.clearImmediate = function clearImmediate(timerId) { + clock.clearTimeout(timerId); + }; + + clock.tick = function tick(ms) { + ms = typeof ms == "number" ? ms : parseTime(ms); + var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now; + var timer = firstTimerInRange(clock, tickFrom, tickTo); + + var firstException; + while (timer && tickFrom <= tickTo) { + if (clock.timers[timer.id]) { + tickFrom = clock.now = timer.callAt; + try { + callTimer(clock, timer); + } catch (e) { + firstException = firstException || e; + } + } + + timer = firstTimerInRange(clock, previous, tickTo); + previous = tickFrom; + } + + clock.now = tickTo; + + if (firstException) { + throw firstException; + } + + return clock.now; + }; + + clock.reset = function reset() { + clock.timers = {}; + }; + + return clock; +}; + +exports.install = function install(target, now, toFake) { + if (typeof target === "number") { + toFake = now; + now = target; + target = null; + } + + if (!target) { + target = global; + } + + var clock = createClock(now); + + clock.uninstall = function () { + uninstall(clock, target); + }; + + clock.methods = toFake || []; + + if (clock.methods.length === 0) { + clock.methods = keys(timers); + } + + for (var i = 0, l = clock.methods.length; i < l; i++) { + hijackMethod(target, clock.methods[i], clock); + } + + return clock; +}; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}]},{},[1])(1) +}); + })(); + var define; +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +var sinon = (function () { +"use strict"; + // eslint-disable-line no-unused-vars + + var sinonModule; + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + sinonModule = module.exports = require("./sinon/util/core"); + require("./sinon/extend"); + require("./sinon/walk"); + require("./sinon/typeOf"); + require("./sinon/times_in_words"); + require("./sinon/spy"); + require("./sinon/call"); + require("./sinon/behavior"); + require("./sinon/stub"); + require("./sinon/mock"); + require("./sinon/collection"); + require("./sinon/assert"); + require("./sinon/sandbox"); + require("./sinon/test"); + require("./sinon/test_case"); + require("./sinon/match"); + require("./sinon/format"); + require("./sinon/log_error"); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + sinonModule = module.exports; + } else { + sinonModule = {}; + } + + return sinonModule; +}()); + +/** + * @depend ../../sinon.js + */ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + var div = typeof document !== "undefined" && document.createElement("div"); + var hasOwn = Object.prototype.hasOwnProperty; + + function isDOMNode(obj) { + var success = false; + + try { + obj.appendChild(div); + success = div.parentNode === obj; + } catch (e) { + return false; + } finally { + try { + obj.removeChild(div); + } catch (e) { + // Remove failed, not much we can do about that + } + } + + return success; + } + + function isElement(obj) { + return div && obj && obj.nodeType === 1 && isDOMNode(obj); + } + + function isFunction(obj) { + return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply); + } + + function isReallyNaN(val) { + return typeof val === "number" && isNaN(val); + } + + function mirrorProperties(target, source) { + for (var prop in source) { + if (!hasOwn.call(target, prop)) { + target[prop] = source[prop]; + } + } + } + + function isRestorable(obj) { + return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon; + } + + // Cheap way to detect if we have ES5 support. + var hasES5Support = "keys" in Object; + + function makeApi(sinon) { + sinon.wrapMethod = function wrapMethod(object, property, method) { + if (!object) { + throw new TypeError("Should wrap property of object"); + } + + if (typeof method !== "function" && typeof method !== "object") { + throw new TypeError("Method wrapper should be a function or a property descriptor"); + } + + function checkWrappedMethod(wrappedMethod) { + var error; + + if (!isFunction(wrappedMethod)) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } else if (wrappedMethod.calledBefore) { + var verb = wrappedMethod.returns ? "stubbed" : "spied on"; + error = new TypeError("Attempted to wrap " + property + " which is already " + verb); + } + + if (error) { + if (wrappedMethod && wrappedMethod.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethod.stackTrace; + } + throw error; + } + } + + var error, wrappedMethod, i; + + // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem + // when using hasOwn.call on objects from other frames. + var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property); + + if (hasES5Support) { + var methodDesc = (typeof method === "function") ? {value: method} : method; + var wrappedMethodDesc = sinon.getPropertyDescriptor(object, property); + + if (!wrappedMethodDesc) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } + if (error) { + if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace; + } + throw error; + } + + var types = sinon.objectKeys(methodDesc); + for (i = 0; i < types.length; i++) { + wrappedMethod = wrappedMethodDesc[types[i]]; + checkWrappedMethod(wrappedMethod); + } + + mirrorProperties(methodDesc, wrappedMethodDesc); + for (i = 0; i < types.length; i++) { + mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]); + } + Object.defineProperty(object, property, methodDesc); + } else { + wrappedMethod = object[property]; + checkWrappedMethod(wrappedMethod); + object[property] = method; + method.displayName = property; + } + + method.displayName = property; + + // Set up a stack trace which can be used later to find what line of + // code the original method was created on. + method.stackTrace = (new Error("Stack Trace for original")).stack; + + method.restore = function () { + // For prototype properties try to reset by delete first. + // If this fails (ex: localStorage on mobile safari) then force a reset + // via direct assignment. + if (!owned) { + // In some cases `delete` may throw an error + try { + delete object[property]; + } catch (e) {} // eslint-disable-line no-empty + // For native code functions `delete` fails without throwing an error + // on Chrome < 43, PhantomJS, etc. + } else if (hasES5Support) { + Object.defineProperty(object, property, wrappedMethodDesc); + } + + // Use strict equality comparison to check failures then force a reset + // via direct assignment. + if (object[property] === method) { + object[property] = wrappedMethod; + } + }; + + method.restore.sinon = true; + + if (!hasES5Support) { + mirrorProperties(method, wrappedMethod); + } + + return method; + }; + + sinon.create = function create(proto) { + var F = function () {}; + F.prototype = proto; + return new F(); + }; + + sinon.deepEqual = function deepEqual(a, b) { + if (sinon.match && sinon.match.isMatcher(a)) { + return a.test(b); + } + + if (typeof a !== "object" || typeof b !== "object") { + return isReallyNaN(a) && isReallyNaN(b) || a === b; + } + + if (isElement(a) || isElement(b)) { + return a === b; + } + + if (a === b) { + return true; + } + + if ((a === null && b !== null) || (a !== null && b === null)) { + return false; + } + + if (a instanceof RegExp && b instanceof RegExp) { + return (a.source === b.source) && (a.global === b.global) && + (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline); + } + + var aString = Object.prototype.toString.call(a); + if (aString !== Object.prototype.toString.call(b)) { + return false; + } + + if (aString === "[object Date]") { + return a.valueOf() === b.valueOf(); + } + + var prop; + var aLength = 0; + var bLength = 0; + + if (aString === "[object Array]" && a.length !== b.length) { + return false; + } + + for (prop in a) { + if (a.hasOwnProperty(prop)) { + aLength += 1; + + if (!(prop in b)) { + return false; + } + + if (!deepEqual(a[prop], b[prop])) { + return false; + } + } + } + + for (prop in b) { + if (b.hasOwnProperty(prop)) { + bLength += 1; + } + } + + return aLength === bLength; + }; + + sinon.functionName = function functionName(func) { + var name = func.displayName || func.name; + + // Use function decomposition as a last resort to get function + // name. Does not rely on function decomposition to work - if it + // doesn't debugging will be slightly less informative + // (i.e. toString will say 'spy' rather than 'myFunc'). + if (!name) { + var matches = func.toString().match(/function ([^\s\(]+)/); + name = matches && matches[1]; + } + + return name; + }; + + sinon.functionToString = function toString() { + if (this.getCall && this.callCount) { + var thisValue, + prop; + var i = this.callCount; + + while (i--) { + thisValue = this.getCall(i).thisValue; + + for (prop in thisValue) { + if (thisValue[prop] === this) { + return prop; + } + } + } + } + + return this.displayName || "sinon fake"; + }; + + sinon.objectKeys = function objectKeys(obj) { + if (obj !== Object(obj)) { + throw new TypeError("sinon.objectKeys called on a non-object"); + } + + var keys = []; + var key; + for (key in obj) { + if (hasOwn.call(obj, key)) { + keys.push(key); + } + } + + return keys; + }; + + sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) { + var proto = object; + var descriptor; + + while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) { + proto = Object.getPrototypeOf(proto); + } + return descriptor; + }; + + sinon.getConfig = function (custom) { + var config = {}; + custom = custom || {}; + var defaults = sinon.defaultConfig; + + for (var prop in defaults) { + if (defaults.hasOwnProperty(prop)) { + config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; + } + } + + return config; + }; + + sinon.defaultConfig = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.timesInWords = function timesInWords(count) { + return count === 1 && "once" || + count === 2 && "twice" || + count === 3 && "thrice" || + (count || 0) + " times"; + }; + + sinon.calledInOrder = function (spies) { + for (var i = 1, l = spies.length; i < l; i++) { + if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) { + return false; + } + } + + return true; + }; + + sinon.orderByFirstCall = function (spies) { + return spies.sort(function (a, b) { + // uuid, won't ever be equal + var aCall = a.getCall(0); + var bCall = b.getCall(0); + var aId = aCall && aCall.callId || -1; + var bId = bCall && bCall.callId || -1; + + return aId < bId ? -1 : 1; + }); + }; + + sinon.createStubInstance = function (constructor) { + if (typeof constructor !== "function") { + throw new TypeError("The constructor should be a function."); + } + return sinon.stub(sinon.create(constructor.prototype)); + }; + + sinon.restore = function (object) { + if (object !== null && typeof object === "object") { + for (var prop in object) { + if (isRestorable(object[prop])) { + object[prop].restore(); + } + } + } else if (isRestorable(object)) { + object.restore(); + } + }; + + return sinon; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports) { + makeApi(exports); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + + // Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + var hasDontEnumBug = (function () { + var obj = { + constructor: function () { + return "0"; + }, + toString: function () { + return "1"; + }, + valueOf: function () { + return "2"; + }, + toLocaleString: function () { + return "3"; + }, + prototype: function () { + return "4"; + }, + isPrototypeOf: function () { + return "5"; + }, + propertyIsEnumerable: function () { + return "6"; + }, + hasOwnProperty: function () { + return "7"; + }, + length: function () { + return "8"; + }, + unique: function () { + return "9"; + } + }; + + var result = []; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + result.push(obj[prop]()); + } + } + return result.join("") !== "0123456789"; + })(); + + /* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will + * override properties in previous sources. + * + * target - The Object to extend + * sources - Objects to copy properties from. + * + * Returns the extended target + */ + function extend(target /*, sources */) { + var sources = Array.prototype.slice.call(arguments, 1); + var source, i, prop; + + for (i = 0; i < sources.length; i++) { + source = sources[i]; + + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // Make sure we copy (own) toString method even when in JScript with DontEnum bug + // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) { + target.toString = source.toString; + } + } + + return target; + } + + sinon.extend = extend; + return sinon.extend; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + + function timesInWords(count) { + switch (count) { + case 1: + return "once"; + case 2: + return "twice"; + case 3: + return "thrice"; + default: + return (count || 0) + " times"; + } + } + + sinon.timesInWords = timesInWords; + return sinon.timesInWords; + } + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + module.exports = makeApi(core); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + function typeOf(value) { + if (value === null) { + return "null"; + } else if (value === undefined) { + return "undefined"; + } + var string = Object.prototype.toString.call(value); + return string.substring(8, string.length - 1).toLowerCase(); + } + + sinon.typeOf = typeOf; + return sinon.typeOf; + } + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + module.exports = makeApi(core); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend typeOf.js + */ +/*jslint eqeqeq: false, onevar: false, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Match functions + * + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2012 Maximilian Antoni + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + function assertType(value, type, name) { + var actual = sinon.typeOf(value); + if (actual !== type) { + throw new TypeError("Expected type of " + name + " to be " + + type + ", but was " + actual); + } + } + + var matcher = { + toString: function () { + return this.message; + } + }; + + function isMatcher(object) { + return matcher.isPrototypeOf(object); + } + + function matchObject(expectation, actual) { + if (actual === null || actual === undefined) { + return false; + } + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + var exp = expectation[key]; + var act = actual[key]; + if (isMatcher(exp)) { + if (!exp.test(act)) { + return false; + } + } else if (sinon.typeOf(exp) === "object") { + if (!matchObject(exp, act)) { + return false; + } + } else if (!sinon.deepEqual(exp, act)) { + return false; + } + } + } + return true; + } + + function match(expectation, message) { + var m = sinon.create(matcher); + var type = sinon.typeOf(expectation); + switch (type) { + case "object": + if (typeof expectation.test === "function") { + m.test = function (actual) { + return expectation.test(actual) === true; + }; + m.message = "match(" + sinon.functionName(expectation.test) + ")"; + return m; + } + var str = []; + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + str.push(key + ": " + expectation[key]); + } + } + m.test = function (actual) { + return matchObject(expectation, actual); + }; + m.message = "match(" + str.join(", ") + ")"; + break; + case "number": + m.test = function (actual) { + // we need type coercion here + return expectation == actual; // eslint-disable-line eqeqeq + }; + break; + case "string": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return actual.indexOf(expectation) !== -1; + }; + m.message = "match(\"" + expectation + "\")"; + break; + case "regexp": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return expectation.test(actual); + }; + break; + case "function": + m.test = expectation; + if (message) { + m.message = message; + } else { + m.message = "match(" + sinon.functionName(expectation) + ")"; + } + break; + default: + m.test = function (actual) { + return sinon.deepEqual(expectation, actual); + }; + } + if (!m.message) { + m.message = "match(" + expectation + ")"; + } + return m; + } + + matcher.or = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var or = sinon.create(matcher); + or.test = function (actual) { + return m1.test(actual) || m2.test(actual); + }; + or.message = m1.message + ".or(" + m2.message + ")"; + return or; + }; + + matcher.and = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var and = sinon.create(matcher); + and.test = function (actual) { + return m1.test(actual) && m2.test(actual); + }; + and.message = m1.message + ".and(" + m2.message + ")"; + return and; + }; + + match.isMatcher = isMatcher; + + match.any = match(function () { + return true; + }, "any"); + + match.defined = match(function (actual) { + return actual !== null && actual !== undefined; + }, "defined"); + + match.truthy = match(function (actual) { + return !!actual; + }, "truthy"); + + match.falsy = match(function (actual) { + return !actual; + }, "falsy"); + + match.same = function (expectation) { + return match(function (actual) { + return expectation === actual; + }, "same(" + expectation + ")"); + }; + + match.typeOf = function (type) { + assertType(type, "string", "type"); + return match(function (actual) { + return sinon.typeOf(actual) === type; + }, "typeOf(\"" + type + "\")"); + }; + + match.instanceOf = function (type) { + assertType(type, "function", "type"); + return match(function (actual) { + return actual instanceof type; + }, "instanceOf(" + sinon.functionName(type) + ")"); + }; + + function createPropertyMatcher(propertyTest, messagePrefix) { + return function (property, value) { + assertType(property, "string", "property"); + var onlyProperty = arguments.length === 1; + var message = messagePrefix + "(\"" + property + "\""; + if (!onlyProperty) { + message += ", " + value; + } + message += ")"; + return match(function (actual) { + if (actual === undefined || actual === null || + !propertyTest(actual, property)) { + return false; + } + return onlyProperty || sinon.deepEqual(value, actual[property]); + }, message); + }; + } + + match.has = createPropertyMatcher(function (actual, property) { + if (typeof actual === "object") { + return property in actual; + } + return actual[property] !== undefined; + }, "has"); + + match.hasOwn = createPropertyMatcher(function (actual, property) { + return actual.hasOwnProperty(property); + }, "hasOwn"); + + match.bool = match.typeOf("boolean"); + match.number = match.typeOf("number"); + match.string = match.typeOf("string"); + match.object = match.typeOf("object"); + match.func = match.typeOf("function"); + match.array = match.typeOf("array"); + match.regexp = match.typeOf("regexp"); + match.date = match.typeOf("date"); + + sinon.match = match; + return match; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./typeOf"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal, formatio) { + + function makeApi(sinon) { + function valueFormatter(value) { + return "" + value; + } + + function getFormatioFormatter() { + var formatter = formatio.configure({ + quoteStrings: false, + limitChildrenCount: 250 + }); + + function format() { + return formatter.ascii.apply(formatter, arguments); + } + + return format; + } + + function getNodeFormatter() { + try { + var util = require("util"); + } catch (e) { + /* Node, but no util module - would be very old, but better safe than sorry */ + } + + function format(v) { + var isObjectWithNativeToString = typeof v === "object" && v.toString === Object.prototype.toString; + return isObjectWithNativeToString ? util.inspect(v) : v; + } + + return util ? format : valueFormatter; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var formatter; + + if (isNode) { + try { + formatio = require("formatio"); + } + catch (e) {} // eslint-disable-line no-empty + } + + if (formatio) { + formatter = getFormatioFormatter(); + } else if (isNode) { + formatter = getNodeFormatter(); + } else { + formatter = valueFormatter; + } + + sinon.format = formatter; + return sinon.format; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof formatio === "object" && formatio // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend match.js + * @depend format.js + */ +/** + * Spy calls + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + * Copyright (c) 2013 Maximilian Antoni + */ +(function (sinonGlobal) { + + var slice = Array.prototype.slice; + + function makeApi(sinon) { + function throwYieldError(proxy, text, args) { + var msg = sinon.functionName(proxy) + text; + if (args.length) { + msg += " Received [" + slice.call(args).join(", ") + "]"; + } + throw new Error(msg); + } + + var callProto = { + calledOn: function calledOn(thisValue) { + if (sinon.match && sinon.match.isMatcher(thisValue)) { + return thisValue.test(this.thisValue); + } + return this.thisValue === thisValue; + }, + + calledWith: function calledWith() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + if (!sinon.deepEqual(arguments[i], this.args[i])) { + return false; + } + } + + return true; + }, + + calledWithMatch: function calledWithMatch() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + var actual = this.args[i]; + var expectation = arguments[i]; + if (!sinon.match || !sinon.match(expectation).test(actual)) { + return false; + } + } + return true; + }, + + calledWithExactly: function calledWithExactly() { + return arguments.length === this.args.length && + this.calledWith.apply(this, arguments); + }, + + notCalledWith: function notCalledWith() { + return !this.calledWith.apply(this, arguments); + }, + + notCalledWithMatch: function notCalledWithMatch() { + return !this.calledWithMatch.apply(this, arguments); + }, + + returned: function returned(value) { + return sinon.deepEqual(value, this.returnValue); + }, + + threw: function threw(error) { + if (typeof error === "undefined" || !this.exception) { + return !!this.exception; + } + + return this.exception === error || this.exception.name === error; + }, + + calledWithNew: function calledWithNew() { + return this.proxy.prototype && this.thisValue instanceof this.proxy; + }, + + calledBefore: function (other) { + return this.callId < other.callId; + }, + + calledAfter: function (other) { + return this.callId > other.callId; + }, + + callArg: function (pos) { + this.args[pos](); + }, + + callArgOn: function (pos, thisValue) { + this.args[pos].apply(thisValue); + }, + + callArgWith: function (pos) { + this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1))); + }, + + callArgOnWith: function (pos, thisValue) { + var args = slice.call(arguments, 2); + this.args[pos].apply(thisValue, args); + }, + + "yield": function () { + this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0))); + }, + + yieldOn: function (thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (typeof args[i] === "function") { + args[i].apply(thisValue, slice.call(arguments, 1)); + return; + } + } + throwYieldError(this.proxy, " cannot yield since no callback was passed.", args); + }, + + yieldTo: function (prop) { + this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1))); + }, + + yieldToOn: function (prop, thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (args[i] && typeof args[i][prop] === "function") { + args[i][prop].apply(thisValue, slice.call(arguments, 2)); + return; + } + } + throwYieldError(this.proxy, " cannot yield to '" + prop + + "' since no callback was passed.", args); + }, + + getStackFrames: function () { + // Omit the error message and the two top stack frames in sinon itself: + return this.stack && this.stack.split("\n").slice(3); + }, + + toString: function () { + var callStr = this.proxy.toString() + "("; + var args = []; + + for (var i = 0, l = this.args.length; i < l; ++i) { + args.push(sinon.format(this.args[i])); + } + + callStr = callStr + args.join(", ") + ")"; + + if (typeof this.returnValue !== "undefined") { + callStr += " => " + sinon.format(this.returnValue); + } + + if (this.exception) { + callStr += " !" + this.exception.name; + + if (this.exception.message) { + callStr += "(" + this.exception.message + ")"; + } + } + if (this.stack) { + callStr += this.getStackFrames()[0].replace(/^\s*(?:at\s+|@)?/, " at "); + + } + + return callStr; + } + }; + + callProto.invokeCallback = callProto.yield; + + function createSpyCall(spy, thisValue, args, returnValue, exception, id, stack) { + if (typeof id !== "number") { + throw new TypeError("Call id is not a number"); + } + var proxyCall = sinon.create(callProto); + proxyCall.proxy = spy; + proxyCall.thisValue = thisValue; + proxyCall.args = args; + proxyCall.returnValue = returnValue; + proxyCall.exception = exception; + proxyCall.callId = id; + proxyCall.stack = stack; + + return proxyCall; + } + createSpyCall.toString = callProto.toString; // used by mocks + + sinon.spyCall = createSpyCall; + return createSpyCall; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./match"); + require("./format"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend extend.js + * @depend call.js + * @depend format.js + */ +/** + * Spy functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + var push = Array.prototype.push; + var slice = Array.prototype.slice; + var callId = 0; + + function spy(object, property, types) { + if (!property && typeof object === "function") { + return spy.create(object); + } + + if (!object && !property) { + return spy.create(function () { }); + } + + if (types) { + var methodDesc = sinon.getPropertyDescriptor(object, property); + for (var i = 0; i < types.length; i++) { + methodDesc[types[i]] = spy.create(methodDesc[types[i]]); + } + return sinon.wrapMethod(object, property, methodDesc); + } + + return sinon.wrapMethod(object, property, spy.create(object[property])); + } + + function matchingFake(fakes, args, strict) { + if (!fakes) { + return undefined; + } + + for (var i = 0, l = fakes.length; i < l; i++) { + if (fakes[i].matches(args, strict)) { + return fakes[i]; + } + } + } + + function incrementCallCount() { + this.called = true; + this.callCount += 1; + this.notCalled = false; + this.calledOnce = this.callCount === 1; + this.calledTwice = this.callCount === 2; + this.calledThrice = this.callCount === 3; + } + + function createCallProperties() { + this.firstCall = this.getCall(0); + this.secondCall = this.getCall(1); + this.thirdCall = this.getCall(2); + this.lastCall = this.getCall(this.callCount - 1); + } + + var vars = "a,b,c,d,e,f,g,h,i,j,k,l"; + function createProxy(func, proxyLength) { + // Retain the function length: + var p; + if (proxyLength) { + eval("p = (function proxy(" + vars.substring(0, proxyLength * 2 - 1) + // eslint-disable-line no-eval + ") { return p.invoke(func, this, slice.call(arguments)); });"); + } else { + p = function proxy() { + return p.invoke(func, this, slice.call(arguments)); + }; + } + p.isSinonProxy = true; + return p; + } + + var uuid = 0; + + // Public API + var spyApi = { + reset: function () { + if (this.invoking) { + var err = new Error("Cannot reset Sinon function while invoking it. " + + "Move the call to .reset outside of the callback."); + err.name = "InvalidResetException"; + throw err; + } + + this.called = false; + this.notCalled = true; + this.calledOnce = false; + this.calledTwice = false; + this.calledThrice = false; + this.callCount = 0; + this.firstCall = null; + this.secondCall = null; + this.thirdCall = null; + this.lastCall = null; + this.args = []; + this.returnValues = []; + this.thisValues = []; + this.exceptions = []; + this.callIds = []; + this.stacks = []; + if (this.fakes) { + for (var i = 0; i < this.fakes.length; i++) { + this.fakes[i].reset(); + } + } + + return this; + }, + + create: function create(func, spyLength) { + var name; + + if (typeof func !== "function") { + func = function () { }; + } else { + name = sinon.functionName(func); + } + + if (!spyLength) { + spyLength = func.length; + } + + var proxy = createProxy(func, spyLength); + + sinon.extend(proxy, spy); + delete proxy.create; + sinon.extend(proxy, func); + + proxy.reset(); + proxy.prototype = func.prototype; + proxy.displayName = name || "spy"; + proxy.toString = sinon.functionToString; + proxy.instantiateFake = sinon.spy.create; + proxy.id = "spy#" + uuid++; + + return proxy; + }, + + invoke: function invoke(func, thisValue, args) { + var matching = matchingFake(this.fakes, args); + var exception, returnValue; + + incrementCallCount.call(this); + push.call(this.thisValues, thisValue); + push.call(this.args, args); + push.call(this.callIds, callId++); + + // Make call properties available from within the spied function: + createCallProperties.call(this); + + try { + this.invoking = true; + + if (matching) { + returnValue = matching.invoke(func, thisValue, args); + } else { + returnValue = (this.func || func).apply(thisValue, args); + } + + var thisCall = this.getCall(this.callCount - 1); + if (thisCall.calledWithNew() && typeof returnValue !== "object") { + returnValue = thisValue; + } + } catch (e) { + exception = e; + } finally { + delete this.invoking; + } + + push.call(this.exceptions, exception); + push.call(this.returnValues, returnValue); + push.call(this.stacks, new Error().stack); + + // Make return value and exception available in the calls: + createCallProperties.call(this); + + if (exception !== undefined) { + throw exception; + } + + return returnValue; + }, + + named: function named(name) { + this.displayName = name; + return this; + }, + + getCall: function getCall(i) { + if (i < 0 || i >= this.callCount) { + return null; + } + + return sinon.spyCall(this, this.thisValues[i], this.args[i], + this.returnValues[i], this.exceptions[i], + this.callIds[i], this.stacks[i]); + }, + + getCalls: function () { + var calls = []; + var i; + + for (i = 0; i < this.callCount; i++) { + calls.push(this.getCall(i)); + } + + return calls; + }, + + calledBefore: function calledBefore(spyFn) { + if (!this.called) { + return false; + } + + if (!spyFn.called) { + return true; + } + + return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1]; + }, + + calledAfter: function calledAfter(spyFn) { + if (!this.called || !spyFn.called) { + return false; + } + + return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1]; + }, + + withArgs: function () { + var args = slice.call(arguments); + + if (this.fakes) { + var match = matchingFake(this.fakes, args, true); + + if (match) { + return match; + } + } else { + this.fakes = []; + } + + var original = this; + var fake = this.instantiateFake(); + fake.matchingAguments = args; + fake.parent = this; + push.call(this.fakes, fake); + + fake.withArgs = function () { + return original.withArgs.apply(original, arguments); + }; + + for (var i = 0; i < this.args.length; i++) { + if (fake.matches(this.args[i])) { + incrementCallCount.call(fake); + push.call(fake.thisValues, this.thisValues[i]); + push.call(fake.args, this.args[i]); + push.call(fake.returnValues, this.returnValues[i]); + push.call(fake.exceptions, this.exceptions[i]); + push.call(fake.callIds, this.callIds[i]); + } + } + createCallProperties.call(fake); + + return fake; + }, + + matches: function (args, strict) { + var margs = this.matchingAguments; + + if (margs.length <= args.length && + sinon.deepEqual(margs, args.slice(0, margs.length))) { + return !strict || margs.length === args.length; + } + }, + + printf: function (format) { + var spyInstance = this; + var args = slice.call(arguments, 1); + var formatter; + + return (format || "").replace(/%(.)/g, function (match, specifyer) { + formatter = spyApi.formatters[specifyer]; + + if (typeof formatter === "function") { + return formatter.call(null, spyInstance, args); + } else if (!isNaN(parseInt(specifyer, 10))) { + return sinon.format(args[specifyer - 1]); + } + + return "%" + specifyer; + }); + } + }; + + function delegateToCalls(method, matchAny, actual, notCalled) { + spyApi[method] = function () { + if (!this.called) { + if (notCalled) { + return notCalled.apply(this, arguments); + } + return false; + } + + var currentCall; + var matches = 0; + + for (var i = 0, l = this.callCount; i < l; i += 1) { + currentCall = this.getCall(i); + + if (currentCall[actual || method].apply(currentCall, arguments)) { + matches += 1; + + if (matchAny) { + return true; + } + } + } + + return matches === this.callCount; + }; + } + + delegateToCalls("calledOn", true); + delegateToCalls("alwaysCalledOn", false, "calledOn"); + delegateToCalls("calledWith", true); + delegateToCalls("calledWithMatch", true); + delegateToCalls("alwaysCalledWith", false, "calledWith"); + delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch"); + delegateToCalls("calledWithExactly", true); + delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly"); + delegateToCalls("neverCalledWith", false, "notCalledWith", function () { + return true; + }); + delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch", function () { + return true; + }); + delegateToCalls("threw", true); + delegateToCalls("alwaysThrew", false, "threw"); + delegateToCalls("returned", true); + delegateToCalls("alwaysReturned", false, "returned"); + delegateToCalls("calledWithNew", true); + delegateToCalls("alwaysCalledWithNew", false, "calledWithNew"); + delegateToCalls("callArg", false, "callArgWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgWith = spyApi.callArg; + delegateToCalls("callArgOn", false, "callArgOnWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgOnWith = spyApi.callArgOn; + delegateToCalls("yield", false, "yield", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode. + spyApi.invokeCallback = spyApi.yield; + delegateToCalls("yieldOn", false, "yieldOn", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + delegateToCalls("yieldTo", false, "yieldTo", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + delegateToCalls("yieldToOn", false, "yieldToOn", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + + spyApi.formatters = { + c: function (spyInstance) { + return sinon.timesInWords(spyInstance.callCount); + }, + + n: function (spyInstance) { + return spyInstance.toString(); + }, + + C: function (spyInstance) { + var calls = []; + + for (var i = 0, l = spyInstance.callCount; i < l; ++i) { + var stringifiedCall = " " + spyInstance.getCall(i).toString(); + if (/\n/.test(calls[i - 1])) { + stringifiedCall = "\n" + stringifiedCall; + } + push.call(calls, stringifiedCall); + } + + return calls.length > 0 ? "\n" + calls.join("\n") : ""; + }, + + t: function (spyInstance) { + var objects = []; + + for (var i = 0, l = spyInstance.callCount; i < l; ++i) { + push.call(objects, sinon.format(spyInstance.thisValues[i])); + } + + return objects.join(", "); + }, + + "*": function (spyInstance, args) { + var formatted = []; + + for (var i = 0, l = args.length; i < l; ++i) { + push.call(formatted, sinon.format(args[i])); + } + + return formatted.join(", "); + } + }; + + sinon.extend(spy, spyApi); + + spy.spyCall = sinon.spyCall; + sinon.spy = spy; + + return spy; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./call"); + require("./extend"); + require("./times_in_words"); + require("./format"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend extend.js + */ +/** + * Stub behavior + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Tim Fischbach (mail@timfischbach.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + var slice = Array.prototype.slice; + var join = Array.prototype.join; + var useLeftMostCallback = -1; + var useRightMostCallback = -2; + + var nextTick = (function () { + if (typeof process === "object" && typeof process.nextTick === "function") { + return process.nextTick; + } + + if (typeof setImmediate === "function") { + return setImmediate; + } + + return function (callback) { + setTimeout(callback, 0); + }; + })(); + + function throwsException(error, message) { + if (typeof error === "string") { + this.exception = new Error(message || ""); + this.exception.name = error; + } else if (!error) { + this.exception = new Error("Error"); + } else { + this.exception = error; + } + + return this; + } + + function getCallback(behavior, args) { + var callArgAt = behavior.callArgAt; + + if (callArgAt >= 0) { + return args[callArgAt]; + } + + var argumentList; + + if (callArgAt === useLeftMostCallback) { + argumentList = args; + } + + if (callArgAt === useRightMostCallback) { + argumentList = slice.call(args).reverse(); + } + + var callArgProp = behavior.callArgProp; + + for (var i = 0, l = argumentList.length; i < l; ++i) { + if (!callArgProp && typeof argumentList[i] === "function") { + return argumentList[i]; + } + + if (callArgProp && argumentList[i] && + typeof argumentList[i][callArgProp] === "function") { + return argumentList[i][callArgProp]; + } + } + + return null; + } + + function makeApi(sinon) { + function getCallbackError(behavior, func, args) { + if (behavior.callArgAt < 0) { + var msg; + + if (behavior.callArgProp) { + msg = sinon.functionName(behavior.stub) + + " expected to yield to '" + behavior.callArgProp + + "', but no object with such a property was passed."; + } else { + msg = sinon.functionName(behavior.stub) + + " expected to yield, but no callback was passed."; + } + + if (args.length > 0) { + msg += " Received [" + join.call(args, ", ") + "]"; + } + + return msg; + } + + return "argument at index " + behavior.callArgAt + " is not a function: " + func; + } + + function callCallback(behavior, args) { + if (typeof behavior.callArgAt === "number") { + var func = getCallback(behavior, args); + + if (typeof func !== "function") { + throw new TypeError(getCallbackError(behavior, func, args)); + } + + if (behavior.callbackAsync) { + nextTick(function () { + func.apply(behavior.callbackContext, behavior.callbackArguments); + }); + } else { + func.apply(behavior.callbackContext, behavior.callbackArguments); + } + } + } + + var proto = { + create: function create(stub) { + var behavior = sinon.extend({}, sinon.behavior); + delete behavior.create; + behavior.stub = stub; + + return behavior; + }, + + isPresent: function isPresent() { + return (typeof this.callArgAt === "number" || + this.exception || + typeof this.returnArgAt === "number" || + this.returnThis || + this.returnValueDefined); + }, + + invoke: function invoke(context, args) { + callCallback(this, args); + + if (this.exception) { + throw this.exception; + } else if (typeof this.returnArgAt === "number") { + return args[this.returnArgAt]; + } else if (this.returnThis) { + return context; + } + + return this.returnValue; + }, + + onCall: function onCall(index) { + return this.stub.onCall(index); + }, + + onFirstCall: function onFirstCall() { + return this.stub.onFirstCall(); + }, + + onSecondCall: function onSecondCall() { + return this.stub.onSecondCall(); + }, + + onThirdCall: function onThirdCall() { + return this.stub.onThirdCall(); + }, + + withArgs: function withArgs(/* arguments */) { + throw new Error( + "Defining a stub by invoking \"stub.onCall(...).withArgs(...)\" " + + "is not supported. Use \"stub.withArgs(...).onCall(...)\" " + + "to define sequential behavior for calls with certain arguments." + ); + }, + + callsArg: function callsArg(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOn: function callsArgOn(pos, context) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgWith: function callsArgWith(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOnWith: function callsArgWith(pos, context) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yields: function () { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsRight: function () { + this.callArgAt = useRightMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsOn: function (context) { + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsTo: function (prop) { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + yieldsToOn: function (prop, context) { + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + throws: throwsException, + throwsException: throwsException, + + returns: function returns(value) { + this.returnValue = value; + this.returnValueDefined = true; + this.exception = undefined; + + return this; + }, + + returnsArg: function returnsArg(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.returnArgAt = pos; + + return this; + }, + + returnsThis: function returnsThis() { + this.returnThis = true; + + return this; + } + }; + + function createAsyncVersion(syncFnName) { + return function () { + var result = this[syncFnName].apply(this, arguments); + this.callbackAsync = true; + return result; + }; + } + + // create asynchronous versions of callsArg* and yields* methods + for (var method in proto) { + // need to avoid creating anotherasync versions of the newly added async methods + if (proto.hasOwnProperty(method) && method.match(/^(callsArg|yields)/) && !method.match(/Async/)) { + proto[method + "Async"] = createAsyncVersion(method); + } + } + + sinon.behavior = proto; + return proto; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./extend"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + /* Public: walks the prototype chain of an object and iterates over every own property + * name encountered. The iterator is called in the same fashion that Array.prototype.forEach + * works, where it is passed the value, key, and own object as the 1st, 2nd, and 3rd positional + * argument, respectively. In cases where Object.getOwnPropertyNames is not available, walk will + * default to using a simple for..in loop. + * + * obj - The object to walk the prototype chain for. + * iterator - The function to be called on each pass of the walk. + * context - (Optional) When given, the iterator will be called with this object as the receiver. + */ + function walk(obj, iterator, context) { + var proto, prop; + + if (typeof Object.getOwnPropertyNames !== "function") { + // We explicitly want to enumerate through all of the prototype's properties + // in this case, therefore we deliberately leave out an own property check. + /* eslint-disable guard-for-in */ + for (prop in obj) { + iterator.call(context, obj[prop], prop, obj); + } + /* eslint-enable guard-for-in */ + + return; + } + + Object.getOwnPropertyNames(obj).forEach(function (k) { + iterator.call(context, obj[k], k, obj); + }); + + proto = Object.getPrototypeOf(obj); + if (proto) { + walk(proto, iterator, context); + } + } + + sinon.walk = walk; + return sinon.walk; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend extend.js + * @depend spy.js + * @depend behavior.js + * @depend walk.js + */ +/** + * Stub functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + function stub(object, property, func) { + if (!!func && typeof func !== "function" && typeof func !== "object") { + throw new TypeError("Custom stub should be a function or a property descriptor"); + } + + var wrapper; + + if (func) { + if (typeof func === "function") { + wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func; + } else { + wrapper = func; + if (sinon.spy && sinon.spy.create) { + var types = sinon.objectKeys(wrapper); + for (var i = 0; i < types.length; i++) { + wrapper[types[i]] = sinon.spy.create(wrapper[types[i]]); + } + } + } + } else { + var stubLength = 0; + if (typeof object === "object" && typeof object[property] === "function") { + stubLength = object[property].length; + } + wrapper = stub.create(stubLength); + } + + if (!object && typeof property === "undefined") { + return sinon.stub.create(); + } + + if (typeof property === "undefined" && typeof object === "object") { + sinon.walk(object || {}, function (value, prop, propOwner) { + // we don't want to stub things like toString(), valueOf(), etc. so we only stub if the object + // is not Object.prototype + if ( + propOwner !== Object.prototype && + typeof sinon.getPropertyDescriptor(propOwner, prop).value === "function" + ) { + stub(object, prop); + } + }); + + return object; + } + + return sinon.wrapMethod(object, property, wrapper); + } + + + /*eslint-disable no-use-before-define*/ + function getParentBehaviour(stubInstance) { + return (stubInstance.parent && getCurrentBehavior(stubInstance.parent)); + } + + function getDefaultBehavior(stubInstance) { + return stubInstance.defaultBehavior || + getParentBehaviour(stubInstance) || + sinon.behavior.create(stubInstance); + } + + function getCurrentBehavior(stubInstance) { + var behavior = stubInstance.behaviors[stubInstance.callCount - 1]; + return behavior && behavior.isPresent() ? behavior : getDefaultBehavior(stubInstance); + } + /*eslint-enable no-use-before-define*/ + + var uuid = 0; + + var proto = { + create: function create(stubLength) { + var functionStub = function () { + return getCurrentBehavior(functionStub).invoke(this, arguments); + }; + + functionStub.id = "stub#" + uuid++; + var orig = functionStub; + functionStub = sinon.spy.create(functionStub, stubLength); + functionStub.func = orig; + + sinon.extend(functionStub, stub); + functionStub.instantiateFake = sinon.stub.create; + functionStub.displayName = "stub"; + functionStub.toString = sinon.functionToString; + + functionStub.defaultBehavior = null; + functionStub.behaviors = []; + + return functionStub; + }, + + resetBehavior: function () { + var i; + + this.defaultBehavior = null; + this.behaviors = []; + + delete this.returnValue; + delete this.returnArgAt; + this.returnThis = false; + + if (this.fakes) { + for (i = 0; i < this.fakes.length; i++) { + this.fakes[i].resetBehavior(); + } + } + }, + + onCall: function onCall(index) { + if (!this.behaviors[index]) { + this.behaviors[index] = sinon.behavior.create(this); + } + + return this.behaviors[index]; + }, + + onFirstCall: function onFirstCall() { + return this.onCall(0); + }, + + onSecondCall: function onSecondCall() { + return this.onCall(1); + }, + + onThirdCall: function onThirdCall() { + return this.onCall(2); + } + }; + + function createBehavior(behaviorMethod) { + return function () { + this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this); + this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments); + return this; + }; + } + + for (var method in sinon.behavior) { + if (sinon.behavior.hasOwnProperty(method) && + !proto.hasOwnProperty(method) && + method !== "create" && + method !== "withArgs" && + method !== "invoke") { + proto[method] = createBehavior(method); + } + } + + sinon.extend(stub, proto); + sinon.stub = stub; + + return stub; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./behavior"); + require("./spy"); + require("./extend"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend call.js + * @depend extend.js + * @depend match.js + * @depend spy.js + * @depend stub.js + * @depend format.js + */ +/** + * Mock functions. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + var push = [].push; + var match = sinon.match; + + function mock(object) { + // if (typeof console !== undefined && console.warn) { + // console.warn("mock will be removed from Sinon.JS v2.0"); + // } + + if (!object) { + return sinon.expectation.create("Anonymous mock"); + } + + return mock.create(object); + } + + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + + function arrayEquals(arr1, arr2, compareLength) { + if (compareLength && (arr1.length !== arr2.length)) { + return false; + } + + for (var i = 0, l = arr1.length; i < l; i++) { + if (!sinon.deepEqual(arr1[i], arr2[i])) { + return false; + } + } + return true; + } + + sinon.extend(mock, { + create: function create(object) { + if (!object) { + throw new TypeError("object is null"); + } + + var mockObject = sinon.extend({}, mock); + mockObject.object = object; + delete mockObject.create; + + return mockObject; + }, + + expects: function expects(method) { + if (!method) { + throw new TypeError("method is falsy"); + } + + if (!this.expectations) { + this.expectations = {}; + this.proxies = []; + } + + if (!this.expectations[method]) { + this.expectations[method] = []; + var mockObject = this; + + sinon.wrapMethod(this.object, method, function () { + return mockObject.invokeMethod(method, this, arguments); + }); + + push.call(this.proxies, method); + } + + var expectation = sinon.expectation.create(method); + push.call(this.expectations[method], expectation); + + return expectation; + }, + + restore: function restore() { + var object = this.object; + + each(this.proxies, function (proxy) { + if (typeof object[proxy].restore === "function") { + object[proxy].restore(); + } + }); + }, + + verify: function verify() { + var expectations = this.expectations || {}; + var messages = []; + var met = []; + + each(this.proxies, function (proxy) { + each(expectations[proxy], function (expectation) { + if (!expectation.met()) { + push.call(messages, expectation.toString()); + } else { + push.call(met, expectation.toString()); + } + }); + }); + + this.restore(); + + if (messages.length > 0) { + sinon.expectation.fail(messages.concat(met).join("\n")); + } else if (met.length > 0) { + sinon.expectation.pass(messages.concat(met).join("\n")); + } + + return true; + }, + + invokeMethod: function invokeMethod(method, thisValue, args) { + var expectations = this.expectations && this.expectations[method] ? this.expectations[method] : []; + var expectationsWithMatchingArgs = []; + var currentArgs = args || []; + var i, available; + + for (i = 0; i < expectations.length; i += 1) { + var expectedArgs = expectations[i].expectedArguments || []; + if (arrayEquals(expectedArgs, currentArgs, expectations[i].expectsExactArgCount)) { + expectationsWithMatchingArgs.push(expectations[i]); + } + } + + for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) { + if (!expectationsWithMatchingArgs[i].met() && + expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) { + return expectationsWithMatchingArgs[i].apply(thisValue, args); + } + } + + var messages = []; + var exhausted = 0; + + for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) { + if (expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) { + available = available || expectationsWithMatchingArgs[i]; + } else { + exhausted += 1; + } + } + + if (available && exhausted === 0) { + return available.apply(thisValue, args); + } + + for (i = 0; i < expectations.length; i += 1) { + push.call(messages, " " + expectations[i].toString()); + } + + messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({ + proxy: method, + args: args + })); + + sinon.expectation.fail(messages.join("\n")); + } + }); + + var times = sinon.timesInWords; + var slice = Array.prototype.slice; + + function callCountInWords(callCount) { + if (callCount === 0) { + return "never called"; + } + + return "called " + times(callCount); + } + + function expectedCallCountInWords(expectation) { + var min = expectation.minCalls; + var max = expectation.maxCalls; + + if (typeof min === "number" && typeof max === "number") { + var str = times(min); + + if (min !== max) { + str = "at least " + str + " and at most " + times(max); + } + + return str; + } + + if (typeof min === "number") { + return "at least " + times(min); + } + + return "at most " + times(max); + } + + function receivedMinCalls(expectation) { + var hasMinLimit = typeof expectation.minCalls === "number"; + return !hasMinLimit || expectation.callCount >= expectation.minCalls; + } + + function receivedMaxCalls(expectation) { + if (typeof expectation.maxCalls !== "number") { + return false; + } + + return expectation.callCount === expectation.maxCalls; + } + + function verifyMatcher(possibleMatcher, arg) { + var isMatcher = match && match.isMatcher(possibleMatcher); + + return isMatcher && possibleMatcher.test(arg) || true; + } + + sinon.expectation = { + minCalls: 1, + maxCalls: 1, + + create: function create(methodName) { + var expectation = sinon.extend(sinon.stub.create(), sinon.expectation); + delete expectation.create; + expectation.method = methodName; + + return expectation; + }, + + invoke: function invoke(func, thisValue, args) { + this.verifyCallAllowed(thisValue, args); + + return sinon.spy.invoke.apply(this, arguments); + }, + + atLeast: function atLeast(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.maxCalls = null; + this.limitsSet = true; + } + + this.minCalls = num; + + return this; + }, + + atMost: function atMost(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.minCalls = null; + this.limitsSet = true; + } + + this.maxCalls = num; + + return this; + }, + + never: function never() { + return this.exactly(0); + }, + + once: function once() { + return this.exactly(1); + }, + + twice: function twice() { + return this.exactly(2); + }, + + thrice: function thrice() { + return this.exactly(3); + }, + + exactly: function exactly(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not a number"); + } + + this.atLeast(num); + return this.atMost(num); + }, + + met: function met() { + return !this.failed && receivedMinCalls(this); + }, + + verifyCallAllowed: function verifyCallAllowed(thisValue, args) { + if (receivedMaxCalls(this)) { + this.failed = true; + sinon.expectation.fail(this.method + " already called " + times(this.maxCalls)); + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " + + this.expectedThis); + } + + if (!("expectedArguments" in this)) { + return; + } + + if (!args) { + sinon.expectation.fail(this.method + " received no arguments, expected " + + sinon.format(this.expectedArguments)); + } + + if (args.length < this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + if (this.expectsExactArgCount && + args.length !== this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", didn't match " + this.expectedArguments.toString()); + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", expected " + sinon.format(this.expectedArguments)); + } + } + }, + + allowsCall: function allowsCall(thisValue, args) { + if (this.met() && receivedMaxCalls(this)) { + return false; + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + return false; + } + + if (!("expectedArguments" in this)) { + return true; + } + + args = args || []; + + if (args.length < this.expectedArguments.length) { + return false; + } + + if (this.expectsExactArgCount && + args.length !== this.expectedArguments.length) { + return false; + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + return false; + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + return false; + } + } + + return true; + }, + + withArgs: function withArgs() { + this.expectedArguments = slice.call(arguments); + return this; + }, + + withExactArgs: function withExactArgs() { + this.withArgs.apply(this, arguments); + this.expectsExactArgCount = true; + return this; + }, + + on: function on(thisValue) { + this.expectedThis = thisValue; + return this; + }, + + toString: function () { + var args = (this.expectedArguments || []).slice(); + + if (!this.expectsExactArgCount) { + push.call(args, "[...]"); + } + + var callStr = sinon.spyCall.toString.call({ + proxy: this.method || "anonymous mock expectation", + args: args + }); + + var message = callStr.replace(", [...", "[, ...") + " " + + expectedCallCountInWords(this); + + if (this.met()) { + return "Expectation met: " + message; + } + + return "Expected " + message + " (" + + callCountInWords(this.callCount) + ")"; + }, + + verify: function verify() { + if (!this.met()) { + sinon.expectation.fail(this.toString()); + } else { + sinon.expectation.pass(this.toString()); + } + + return true; + }, + + pass: function pass(message) { + sinon.assert.pass(message); + }, + + fail: function fail(message) { + var exception = new Error(message); + exception.name = "ExpectationError"; + + throw exception; + } + }; + + sinon.mock = mock; + return mock; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./times_in_words"); + require("./call"); + require("./extend"); + require("./match"); + require("./spy"); + require("./stub"); + require("./format"); + + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend spy.js + * @depend stub.js + * @depend mock.js + */ +/** + * Collections of stubs, spies and mocks. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + var push = [].push; + var hasOwnProperty = Object.prototype.hasOwnProperty; + + function getFakes(fakeCollection) { + if (!fakeCollection.fakes) { + fakeCollection.fakes = []; + } + + return fakeCollection.fakes; + } + + function each(fakeCollection, method) { + var fakes = getFakes(fakeCollection); + + for (var i = 0, l = fakes.length; i < l; i += 1) { + if (typeof fakes[i][method] === "function") { + fakes[i][method](); + } + } + } + + function compact(fakeCollection) { + var fakes = getFakes(fakeCollection); + var i = 0; + while (i < fakes.length) { + fakes.splice(i, 1); + } + } + + function makeApi(sinon) { + var collection = { + verify: function resolve() { + each(this, "verify"); + }, + + restore: function restore() { + each(this, "restore"); + compact(this); + }, + + reset: function restore() { + each(this, "reset"); + }, + + verifyAndRestore: function verifyAndRestore() { + var exception; + + try { + this.verify(); + } catch (e) { + exception = e; + } + + this.restore(); + + if (exception) { + throw exception; + } + }, + + add: function add(fake) { + push.call(getFakes(this), fake); + return fake; + }, + + spy: function spy() { + return this.add(sinon.spy.apply(sinon, arguments)); + }, + + stub: function stub(object, property, value) { + if (property) { + var original = object[property]; + + if (typeof original !== "function") { + if (!hasOwnProperty.call(object, property)) { + throw new TypeError("Cannot stub non-existent own property " + property); + } + + object[property] = value; + + return this.add({ + restore: function () { + object[property] = original; + } + }); + } + } + if (!property && !!object && typeof object === "object") { + var stubbedObj = sinon.stub.apply(sinon, arguments); + + for (var prop in stubbedObj) { + if (typeof stubbedObj[prop] === "function") { + this.add(stubbedObj[prop]); + } + } + + return stubbedObj; + } + + return this.add(sinon.stub.apply(sinon, arguments)); + }, + + mock: function mock() { + return this.add(sinon.mock.apply(sinon, arguments)); + }, + + inject: function inject(obj) { + var col = this; + + obj.spy = function () { + return col.spy.apply(col, arguments); + }; + + obj.stub = function () { + return col.stub.apply(col, arguments); + }; + + obj.mock = function () { + return col.mock.apply(col, arguments); + }; + + return obj; + } + }; + + sinon.collection = collection; + return collection; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./mock"); + require("./spy"); + require("./stub"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * Fake timer API + * setTimeout + * setInterval + * clearTimeout + * clearInterval + * tick + * reset + * Date + * + * Inspired by jsUnitMockTimeOut from JsUnit + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + function makeApi(s, lol) { + /*global lolex */ + var llx = typeof lolex !== "undefined" ? lolex : lol; + + s.useFakeTimers = function () { + var now; + var methods = Array.prototype.slice.call(arguments); + + if (typeof methods[0] === "string") { + now = 0; + } else { + now = methods.shift(); + } + + var clock = llx.install(now || 0, methods); + clock.restore = clock.uninstall; + return clock; + }; + + s.clock = { + create: function (now) { + return llx.createClock(now); + } + }; + + s.timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), + clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined), + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, epxorts, module, lolex) { + var core = require("./core"); + makeApi(core, lolex); + module.exports = core; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module, require("lolex")); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * Minimal Event interface implementation + * + * Original implementation by Sven Fuchs: https://gist.github.com/995028 + * Modifications and tests by Christian Johansen. + * + * @author Sven Fuchs (svenfuchs@artweb-design.de) + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2011 Sven Fuchs, Christian Johansen + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +(function () { + + var push = [].push; + + function makeApi(sinon) { + sinon.Event = function Event(type, bubbles, cancelable, target) { + this.initEvent(type, bubbles, cancelable, target); + }; + + sinon.Event.prototype = { + initEvent: function (type, bubbles, cancelable, target) { + this.type = type; + this.bubbles = bubbles; + this.cancelable = cancelable; + this.target = target; + }, + + stopPropagation: function () {}, + + preventDefault: function () { + this.defaultPrevented = true; + } + }; + + sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) { + this.initEvent(type, false, false, target); + this.loaded = progressEventRaw.loaded || null; + this.total = progressEventRaw.total || null; + this.lengthComputable = !!progressEventRaw.total; + }; + + sinon.ProgressEvent.prototype = new sinon.Event(); + + sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent; + + sinon.CustomEvent = function CustomEvent(type, customData, target) { + this.initEvent(type, false, false, target); + this.detail = customData.detail || null; + }; + + sinon.CustomEvent.prototype = new sinon.Event(); + + sinon.CustomEvent.prototype.constructor = sinon.CustomEvent; + + sinon.EventTarget = { + addEventListener: function addEventListener(event, listener) { + this.eventListeners = this.eventListeners || {}; + this.eventListeners[event] = this.eventListeners[event] || []; + push.call(this.eventListeners[event], listener); + }, + + removeEventListener: function removeEventListener(event, listener) { + var listeners = this.eventListeners && this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }, + + dispatchEvent: function dispatchEvent(event) { + var type = event.type; + var listeners = this.eventListeners && this.eventListeners[type] || []; + + for (var i = 0; i < listeners.length; i++) { + if (typeof listeners[i] === "function") { + listeners[i].call(this, event); + } else { + listeners[i].handleEvent(event); + } + } + + return !!event.defaultPrevented; + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend util/core.js + */ +/** + * Logs errors + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal) { + + // cache a reference to setTimeout, so that our reference won't be stubbed out + // when using fake timers and errors will still get logged + // https://github.com/cjohansen/Sinon.JS/issues/381 + var realSetTimeout = setTimeout; + + function makeApi(sinon) { + + function log() {} + + function logError(label, err) { + var msg = label + " threw exception: "; + + function throwLoggedError() { + err.message = msg + err.message; + throw err; + } + + sinon.log(msg + "[" + err.name + "] " + err.message); + + if (err.stack) { + sinon.log(err.stack); + } + + if (logError.useImmediateExceptions) { + throwLoggedError(); + } else { + logError.setTimeout(throwLoggedError, 0); + } + } + + // When set to true, any errors logged will be thrown immediately; + // If set to false, the errors will be thrown in separate execution frame. + logError.useImmediateExceptions = false; + + // wrap realSetTimeout with something we can stub in tests + logError.setTimeout = function (func, timeout) { + realSetTimeout(func, timeout); + }; + + var exports = {}; + exports.log = sinon.log = log; + exports.logError = sinon.logError = logError; + + return exports; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XDomainRequest object + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +// wrapper for global +(function (global) { + + var xdr = { XDomainRequest: global.XDomainRequest }; + xdr.GlobalXDomainRequest = global.XDomainRequest; + xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== "undefined"; + xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false; + + function makeApi(sinon) { + sinon.xdr = xdr; + + function FakeXDomainRequest() { + this.readyState = FakeXDomainRequest.UNSENT; + this.requestBody = null; + this.requestHeaders = {}; + this.status = 0; + this.timeout = null; + + if (typeof FakeXDomainRequest.onCreate === "function") { + FakeXDomainRequest.onCreate(this); + } + } + + function verifyState(x) { + if (x.readyState !== FakeXDomainRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (x.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function verifyRequestSent(x) { + if (x.readyState === FakeXDomainRequest.UNSENT) { + throw new Error("Request not sent"); + } + if (x.readyState === FakeXDomainRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XDomainRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, { + open: function open(method, url) { + this.method = method; + this.url = url; + + this.responseText = null; + this.sendFlag = false; + + this.readyStateChange(FakeXDomainRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + var eventName = ""; + switch (this.readyState) { + case FakeXDomainRequest.UNSENT: + break; + case FakeXDomainRequest.OPENED: + break; + case FakeXDomainRequest.LOADING: + if (this.sendFlag) { + //raise the progress event + eventName = "onprogress"; + } + break; + case FakeXDomainRequest.DONE: + if (this.isTimeout) { + eventName = "ontimeout"; + } else if (this.errorFlag || (this.status < 200 || this.status > 299)) { + eventName = "onerror"; + } else { + eventName = "onload"; + } + break; + } + + // raising event (if defined) + if (eventName) { + if (typeof this[eventName] === "function") { + try { + this[eventName](); + } catch (e) { + sinon.logError("Fake XHR " + eventName + " handler", e); + } + } + } + }, + + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + this.requestBody = data; + } + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + + this.errorFlag = false; + this.sendFlag = true; + this.readyStateChange(FakeXDomainRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + + if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) { + this.readyStateChange(sinon.FakeXDomainRequest.DONE); + this.sendFlag = false; + } + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyResponseBodyType(body); + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + this.readyStateChange(FakeXDomainRequest.LOADING); + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + this.readyStateChange(FakeXDomainRequest.DONE); + }, + + respond: function respond(status, contentType, body) { + // content-type ignored, since XDomainRequest does not carry this + // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease + // test integration across browsers + this.status = typeof status === "number" ? status : 200; + this.setResponseBody(body || ""); + }, + + simulatetimeout: function simulatetimeout() { + this.status = 0; + this.isTimeout = true; + // Access to this should actually throw an error + this.responseText = undefined; + this.readyStateChange(FakeXDomainRequest.DONE); + } + }); + + sinon.extend(FakeXDomainRequest, { + UNSENT: 0, + OPENED: 1, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXDomainRequest = function useFakeXDomainRequest() { + sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) { + if (xdr.supportsXDR) { + global.XDomainRequest = xdr.GlobalXDomainRequest; + } + + delete sinon.FakeXDomainRequest.restore; + + if (keepOnCreate !== true) { + delete sinon.FakeXDomainRequest.onCreate; + } + }; + if (xdr.supportsXDR) { + global.XDomainRequest = sinon.FakeXDomainRequest; + } + return sinon.FakeXDomainRequest; + }; + + sinon.FakeXDomainRequest = FakeXDomainRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +})(typeof global !== "undefined" ? global : self); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XMLHttpRequest object + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal, global) { + + function getWorkingXHR(globalScope) { + var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined"; + if (supportsXHR) { + return globalScope.XMLHttpRequest; + } + + var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined"; + if (supportsActiveX) { + return function () { + return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0"); + }; + } + + return false; + } + + var supportsProgress = typeof ProgressEvent !== "undefined"; + var supportsCustomEvent = typeof CustomEvent !== "undefined"; + var supportsFormData = typeof FormData !== "undefined"; + var supportsArrayBuffer = typeof ArrayBuffer !== "undefined"; + var supportsBlob = typeof Blob === "function"; + var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest }; + sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest; + sinonXhr.GlobalActiveXObject = global.ActiveXObject; + sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== "undefined"; + sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined"; + sinonXhr.workingXHR = getWorkingXHR(global); + sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest()); + + var unsafeHeaders = { + "Accept-Charset": true, + "Accept-Encoding": true, + Connection: true, + "Content-Length": true, + Cookie: true, + Cookie2: true, + "Content-Transfer-Encoding": true, + Date: true, + Expect: true, + Host: true, + "Keep-Alive": true, + Referer: true, + TE: true, + Trailer: true, + "Transfer-Encoding": true, + Upgrade: true, + "User-Agent": true, + Via: true + }; + + // An upload object is created for each + // FakeXMLHttpRequest and allows upload + // events to be simulated using uploadProgress + // and uploadError. + function UploadProgress() { + this.eventListeners = { + progress: [], + load: [], + abort: [], + error: [] + }; + } + + UploadProgress.prototype.addEventListener = function addEventListener(event, listener) { + this.eventListeners[event].push(listener); + }; + + UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) { + var listeners = this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }; + + UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) { + var listeners = this.eventListeners[event.type] || []; + + for (var i = 0, listener; (listener = listeners[i]) != null; i++) { + listener(event); + } + }; + + // Note that for FakeXMLHttpRequest to work pre ES5 + // we lose some of the alignment with the spec. + // To ensure as close a match as possible, + // set responseType before calling open, send or respond; + function FakeXMLHttpRequest() { + this.readyState = FakeXMLHttpRequest.UNSENT; + this.requestHeaders = {}; + this.requestBody = null; + this.status = 0; + this.statusText = ""; + this.upload = new UploadProgress(); + this.responseType = ""; + this.response = ""; + if (sinonXhr.supportsCORS) { + this.withCredentials = false; + } + + var xhr = this; + var events = ["loadstart", "load", "abort", "loadend"]; + + function addEventListener(eventName) { + xhr.addEventListener(eventName, function (event) { + var listener = xhr["on" + eventName]; + + if (listener && typeof listener === "function") { + listener.call(this, event); + } + }); + } + + for (var i = events.length - 1; i >= 0; i--) { + addEventListener(events[i]); + } + + if (typeof FakeXMLHttpRequest.onCreate === "function") { + FakeXMLHttpRequest.onCreate(this); + } + } + + function verifyState(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xhr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function getHeader(headers, header) { + header = header.toLowerCase(); + + for (var h in headers) { + if (h.toLowerCase() === header) { + return h; + } + } + + return null; + } + + // filtering to enable a white-list version of Sinon FakeXhr, + // where whitelisted requests are passed through to real XHR + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + function some(collection, callback) { + for (var index = 0; index < collection.length; index++) { + if (callback(collection[index]) === true) { + return true; + } + } + return false; + } + // largest arity in XHR is 5 - XHR#open + var apply = function (obj, method, args) { + switch (args.length) { + case 0: return obj[method](); + case 1: return obj[method](args[0]); + case 2: return obj[method](args[0], args[1]); + case 3: return obj[method](args[0], args[1], args[2]); + case 4: return obj[method](args[0], args[1], args[2], args[3]); + case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]); + } + }; + + FakeXMLHttpRequest.filters = []; + FakeXMLHttpRequest.addFilter = function addFilter(fn) { + this.filters.push(fn); + }; + var IE6Re = /MSIE 6/; + FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) { + var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap + + each([ + "open", + "setRequestHeader", + "send", + "abort", + "getResponseHeader", + "getAllResponseHeaders", + "addEventListener", + "overrideMimeType", + "removeEventListener" + ], function (method) { + fakeXhr[method] = function () { + return apply(xhr, method, arguments); + }; + }); + + var copyAttrs = function (args) { + each(args, function (attr) { + try { + fakeXhr[attr] = xhr[attr]; + } catch (e) { + if (!IE6Re.test(navigator.userAgent)) { + throw e; + } + } + }); + }; + + var stateChange = function stateChange() { + fakeXhr.readyState = xhr.readyState; + if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { + copyAttrs(["status", "statusText"]); + } + if (xhr.readyState >= FakeXMLHttpRequest.LOADING) { + copyAttrs(["responseText", "response"]); + } + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + copyAttrs(["responseXML"]); + } + if (fakeXhr.onreadystatechange) { + fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr }); + } + }; + + if (xhr.addEventListener) { + for (var event in fakeXhr.eventListeners) { + if (fakeXhr.eventListeners.hasOwnProperty(event)) { + + /*eslint-disable no-loop-func*/ + each(fakeXhr.eventListeners[event], function (handler) { + xhr.addEventListener(event, handler); + }); + /*eslint-enable no-loop-func*/ + } + } + xhr.addEventListener("readystatechange", stateChange); + } else { + xhr.onreadystatechange = stateChange; + } + apply(xhr, "open", xhrArgs); + }; + FakeXMLHttpRequest.useFilters = false; + + function verifyRequestOpened(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR - " + xhr.readyState); + } + } + + function verifyRequestSent(xhr) { + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyHeadersReceived(xhr) { + if (xhr.async && xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) { + throw new Error("No headers received"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XMLHttpRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + function convertToArrayBuffer(body) { + var buffer = new ArrayBuffer(body.length); + var view = new Uint8Array(buffer); + for (var i = 0; i < body.length; i++) { + var charCode = body.charCodeAt(i); + if (charCode >= 256) { + throw new TypeError("arraybuffer or blob responseTypes require binary string, " + + "invalid character " + body[i] + " found."); + } + view[i] = charCode; + } + return buffer; + } + + function isXmlContentType(contentType) { + return !contentType || /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType); + } + + function convertResponseBody(responseType, contentType, body) { + if (responseType === "" || responseType === "text") { + return body; + } else if (supportsArrayBuffer && responseType === "arraybuffer") { + return convertToArrayBuffer(body); + } else if (responseType === "json") { + try { + return JSON.parse(body); + } catch (e) { + // Return parsing failure as null + return null; + } + } else if (supportsBlob && responseType === "blob") { + var blobOptions = {}; + if (contentType) { + blobOptions.type = contentType; + } + return new Blob([convertToArrayBuffer(body)], blobOptions); + } else if (responseType === "document") { + if (isXmlContentType(contentType)) { + return FakeXMLHttpRequest.parseXML(body); + } + return null; + } + throw new Error("Invalid responseType " + responseType); + } + + function clearResponse(xhr) { + if (xhr.responseType === "" || xhr.responseType === "text") { + xhr.response = xhr.responseText = ""; + } else { + xhr.response = xhr.responseText = null; + } + xhr.responseXML = null; + } + + FakeXMLHttpRequest.parseXML = function parseXML(text) { + // Treat empty string as parsing failure + if (text !== "") { + try { + if (typeof DOMParser !== "undefined") { + var parser = new DOMParser(); + return parser.parseFromString(text, "text/xml"); + } + var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(text); + return xmlDoc; + } catch (e) { + // Unable to parse XML - no biggie + } + } + + return null; + }; + + FakeXMLHttpRequest.statusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 300: "Multiple Choice", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 422: "Unprocessable Entity", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" + }; + + function makeApi(sinon) { + sinon.xhr = sinonXhr; + + sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { + async: true, + + open: function open(method, url, async, username, password) { + this.method = method; + this.url = url; + this.async = typeof async === "boolean" ? async : true; + this.username = username; + this.password = password; + clearResponse(this); + this.requestHeaders = {}; + this.sendFlag = false; + + if (FakeXMLHttpRequest.useFilters === true) { + var xhrArgs = arguments; + var defake = some(FakeXMLHttpRequest.filters, function (filter) { + return filter.apply(this, xhrArgs); + }); + if (defake) { + return FakeXMLHttpRequest.defake(this, arguments); + } + } + this.readyStateChange(FakeXMLHttpRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + + var readyStateChangeEvent = new sinon.Event("readystatechange", false, false, this); + + if (typeof this.onreadystatechange === "function") { + try { + this.onreadystatechange(readyStateChangeEvent); + } catch (e) { + sinon.logError("Fake XHR onreadystatechange handler", e); + } + } + + switch (this.readyState) { + case FakeXMLHttpRequest.DONE: + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + this.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + } + this.upload.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("loadend", false, false, this)); + break; + } + + this.dispatchEvent(readyStateChangeEvent); + }, + + setRequestHeader: function setRequestHeader(header, value) { + verifyState(this); + + if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { + throw new Error("Refused to set unsafe header \"" + header + "\""); + } + + if (this.requestHeaders[header]) { + this.requestHeaders[header] += "," + value; + } else { + this.requestHeaders[header] = value; + } + }, + + // Helps testing + setResponseHeaders: function setResponseHeaders(headers) { + verifyRequestOpened(this); + this.responseHeaders = {}; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + this.responseHeaders[header] = headers[header]; + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); + } else { + this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; + } + }, + + // Currently treats ALL data as a DOMString (i.e. no Document) + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + var contentType = getHeader(this.requestHeaders, "Content-Type"); + if (this.requestHeaders[contentType]) { + var value = this.requestHeaders[contentType].split(";"); + this.requestHeaders[contentType] = value[0] + ";charset=utf-8"; + } else if (supportsFormData && !(data instanceof FormData)) { + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + } + + this.requestBody = data; + } + + this.errorFlag = false; + this.sendFlag = this.async; + clearResponse(this); + this.readyStateChange(FakeXMLHttpRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + + this.dispatchEvent(new sinon.Event("loadstart", false, false, this)); + }, + + abort: function abort() { + this.aborted = true; + clearResponse(this); + this.errorFlag = true; + this.requestHeaders = {}; + this.responseHeaders = {}; + + if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) { + this.readyStateChange(FakeXMLHttpRequest.DONE); + this.sendFlag = false; + } + + this.readyState = FakeXMLHttpRequest.UNSENT; + + this.dispatchEvent(new sinon.Event("abort", false, false, this)); + + this.upload.dispatchEvent(new sinon.Event("abort", false, false, this)); + + if (typeof this.onerror === "function") { + this.onerror(); + } + }, + + getResponseHeader: function getResponseHeader(header) { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return null; + } + + if (/^Set-Cookie2?$/i.test(header)) { + return null; + } + + header = getHeader(this.responseHeaders, header); + + return this.responseHeaders[header] || null; + }, + + getAllResponseHeaders: function getAllResponseHeaders() { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return ""; + } + + var headers = ""; + + for (var header in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(header) && + !/^Set-Cookie2?$/i.test(header)) { + headers += header + ": " + this.responseHeaders[header] + "\r\n"; + } + } + + return headers; + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyHeadersReceived(this); + verifyResponseBodyType(body); + var contentType = this.getResponseHeader("Content-Type"); + + var isTextResponse = this.responseType === "" || this.responseType === "text"; + clearResponse(this); + if (this.async) { + var chunkSize = this.chunkSize || 10; + var index = 0; + + do { + this.readyStateChange(FakeXMLHttpRequest.LOADING); + + if (isTextResponse) { + this.responseText = this.response += body.substring(index, index + chunkSize); + } + index += chunkSize; + } while (index < body.length); + } + + this.response = convertResponseBody(this.responseType, contentType, body); + if (isTextResponse) { + this.responseText = this.response; + } + + if (this.responseType === "document") { + this.responseXML = this.response; + } else if (this.responseType === "" && isXmlContentType(contentType)) { + this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); + } + this.readyStateChange(FakeXMLHttpRequest.DONE); + }, + + respond: function respond(status, headers, body) { + this.status = typeof status === "number" ? status : 200; + this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; + this.setResponseHeaders(headers || {}); + this.setResponseBody(body || ""); + }, + + uploadProgress: function uploadProgress(progressEventRaw) { + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + downloadProgress: function downloadProgress(progressEventRaw) { + if (supportsProgress) { + this.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + uploadError: function uploadError(error) { + if (supportsCustomEvent) { + this.upload.dispatchEvent(new sinon.CustomEvent("error", {detail: error})); + } + } + }); + + sinon.extend(FakeXMLHttpRequest, { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXMLHttpRequest = function () { + FakeXMLHttpRequest.restore = function restore(keepOnCreate) { + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = sinonXhr.GlobalActiveXObject; + } + + delete FakeXMLHttpRequest.restore; + + if (keepOnCreate !== true) { + delete FakeXMLHttpRequest.onCreate; + } + }; + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = FakeXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = function ActiveXObject(objId) { + if (objId === "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { + + return new FakeXMLHttpRequest(); + } + + return new sinonXhr.GlobalActiveXObject(objId); + }; + } + + return FakeXMLHttpRequest; + }; + + sinon.FakeXMLHttpRequest = FakeXMLHttpRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof global !== "undefined" ? global : self +)); + +/** + * @depend fake_xdomain_request.js + * @depend fake_xml_http_request.js + * @depend ../format.js + * @depend ../log_error.js + */ +/** + * The Sinon "server" mimics a web server that receives requests from + * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, + * both synchronously and asynchronously. To respond synchronuously, canned + * answers have to be provided upfront. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + var push = [].push; + + function responseArray(handler) { + var response = handler; + + if (Object.prototype.toString.call(handler) !== "[object Array]") { + response = [200, {}, handler]; + } + + if (typeof response[2] !== "string") { + throw new TypeError("Fake server response body should be string, but was " + + typeof response[2]); + } + + return response; + } + + var wloc = typeof window !== "undefined" ? window.location : {}; + var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host); + + function matchOne(response, reqMethod, reqUrl) { + var rmeth = response.method; + var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase(); + var url = response.url; + var matchUrl = !url || url === reqUrl || (typeof url.test === "function" && url.test(reqUrl)); + + return matchMethod && matchUrl; + } + + function match(response, request) { + var requestUrl = request.url; + + if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { + requestUrl = requestUrl.replace(rCurrLoc, ""); + } + + if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { + if (typeof response.response === "function") { + var ru = response.url; + var args = [request].concat(ru && typeof ru.exec === "function" ? ru.exec(requestUrl).slice(1) : []); + return response.response.apply(response, args); + } + + return true; + } + + return false; + } + + function makeApi(sinon) { + sinon.fakeServer = { + create: function (config) { + var server = sinon.create(this); + server.configure(config); + if (!sinon.xhr.supportsCORS) { + this.xhr = sinon.useFakeXDomainRequest(); + } else { + this.xhr = sinon.useFakeXMLHttpRequest(); + } + server.requests = []; + + this.xhr.onCreate = function (xhrObj) { + server.addRequest(xhrObj); + }; + + return server; + }, + configure: function (config) { + var whitelist = { + "autoRespond": true, + "autoRespondAfter": true, + "respondImmediately": true, + "fakeHTTPMethods": true + }; + var setting; + + config = config || {}; + for (setting in config) { + if (whitelist.hasOwnProperty(setting) && config.hasOwnProperty(setting)) { + this[setting] = config[setting]; + } + } + }, + addRequest: function addRequest(xhrObj) { + var server = this; + push.call(this.requests, xhrObj); + + xhrObj.onSend = function () { + server.handleRequest(this); + + if (server.respondImmediately) { + server.respond(); + } else if (server.autoRespond && !server.responding) { + setTimeout(function () { + server.responding = false; + server.respond(); + }, server.autoRespondAfter || 10); + + server.responding = true; + } + }; + }, + + getHTTPMethod: function getHTTPMethod(request) { + if (this.fakeHTTPMethods && /post/i.test(request.method)) { + var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); + return matches ? matches[1] : request.method; + } + + return request.method; + }, + + handleRequest: function handleRequest(xhr) { + if (xhr.async) { + if (!this.queue) { + this.queue = []; + } + + push.call(this.queue, xhr); + } else { + this.processRequest(xhr); + } + }, + + log: function log(response, request) { + var str; + + str = "Request:\n" + sinon.format(request) + "\n\n"; + str += "Response:\n" + sinon.format(response) + "\n\n"; + + sinon.log(str); + }, + + respondWith: function respondWith(method, url, body) { + if (arguments.length === 1 && typeof method !== "function") { + this.response = responseArray(method); + return; + } + + if (!this.responses) { + this.responses = []; + } + + if (arguments.length === 1) { + body = method; + url = method = null; + } + + if (arguments.length === 2) { + body = url; + url = method; + method = null; + } + + push.call(this.responses, { + method: method, + url: url, + response: typeof body === "function" ? body : responseArray(body) + }); + }, + + respond: function respond() { + if (arguments.length > 0) { + this.respondWith.apply(this, arguments); + } + + var queue = this.queue || []; + var requests = queue.splice(0, queue.length); + + for (var i = 0; i < requests.length; i++) { + this.processRequest(requests[i]); + } + }, + + processRequest: function processRequest(request) { + try { + if (request.aborted) { + return; + } + + var response = this.response || [404, {}, ""]; + + if (this.responses) { + for (var l = this.responses.length, i = l - 1; i >= 0; i--) { + if (match.call(this, this.responses[i], request)) { + response = this.responses[i].response; + break; + } + } + } + + if (request.readyState !== 4) { + this.log(response, request); + + request.respond(response[0], response[1], response[2]); + } + } catch (e) { + sinon.logError("Fake server request processing", e); + } + }, + + restore: function restore() { + return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("./fake_xdomain_request"); + require("./fake_xml_http_request"); + require("../format"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend fake_server.js + * @depend fake_timers.js + */ +/** + * Add-on for sinon.fakeServer that automatically handles a fake timer along with + * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery + * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, + * it polls the object for completion with setInterval. Dispite the direct + * motivation, there is nothing jQuery-specific in this file, so it can be used + * in any environment where the ajax implementation depends on setInterval or + * setTimeout. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + function makeApi(sinon) { + function Server() {} + Server.prototype = sinon.fakeServer; + + sinon.fakeServerWithClock = new Server(); + + sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { + if (xhr.async) { + if (typeof setTimeout.clock === "object") { + this.clock = setTimeout.clock; + } else { + this.clock = sinon.useFakeTimers(); + this.resetClock = true; + } + + if (!this.longestTimeout) { + var clockSetTimeout = this.clock.setTimeout; + var clockSetInterval = this.clock.setInterval; + var server = this; + + this.clock.setTimeout = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetTimeout.apply(this, arguments); + }; + + this.clock.setInterval = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetInterval.apply(this, arguments); + }; + } + } + + return sinon.fakeServer.addRequest.call(this, xhr); + }; + + sinon.fakeServerWithClock.respond = function respond() { + var returnVal = sinon.fakeServer.respond.apply(this, arguments); + + if (this.clock) { + this.clock.tick(this.longestTimeout || 0); + this.longestTimeout = 0; + + if (this.resetClock) { + this.clock.restore(); + this.resetClock = false; + } + } + + return returnVal; + }; + + sinon.fakeServerWithClock.restore = function restore() { + if (this.clock) { + this.clock.restore(); + } + + return sinon.fakeServer.restore.apply(this, arguments); + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + require("./fake_server"); + require("./fake_timers"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend util/core.js + * @depend extend.js + * @depend collection.js + * @depend util/fake_timers.js + * @depend util/fake_server_with_clock.js + */ +/** + * Manages fake collections as well as fake utilities such as Sinon's + * timers and fake XHR implementation in one convenient object. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + var push = [].push; + + function exposeValue(sandbox, config, key, value) { + if (!value) { + return; + } + + if (config.injectInto && !(key in config.injectInto)) { + config.injectInto[key] = value; + sandbox.injectedKeys.push(key); + } else { + push.call(sandbox.args, value); + } + } + + function prepareSandboxFromConfig(config) { + var sandbox = sinon.create(sinon.sandbox); + + if (config.useFakeServer) { + if (typeof config.useFakeServer === "object") { + sandbox.serverPrototype = config.useFakeServer; + } + + sandbox.useFakeServer(); + } + + if (config.useFakeTimers) { + if (typeof config.useFakeTimers === "object") { + sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers); + } else { + sandbox.useFakeTimers(); + } + } + + return sandbox; + } + + sinon.sandbox = sinon.extend(sinon.create(sinon.collection), { + useFakeTimers: function useFakeTimers() { + this.clock = sinon.useFakeTimers.apply(sinon, arguments); + + return this.add(this.clock); + }, + + serverPrototype: sinon.fakeServer, + + useFakeServer: function useFakeServer() { + var proto = this.serverPrototype || sinon.fakeServer; + + if (!proto || !proto.create) { + return null; + } + + this.server = proto.create(); + return this.add(this.server); + }, + + inject: function (obj) { + sinon.collection.inject.call(this, obj); + + if (this.clock) { + obj.clock = this.clock; + } + + if (this.server) { + obj.server = this.server; + obj.requests = this.server.requests; + } + + obj.match = sinon.match; + + return obj; + }, + + restore: function () { + sinon.collection.restore.apply(this, arguments); + this.restoreContext(); + }, + + restoreContext: function () { + if (this.injectedKeys) { + for (var i = 0, j = this.injectedKeys.length; i < j; i++) { + delete this.injectInto[this.injectedKeys[i]]; + } + this.injectedKeys = []; + } + }, + + create: function (config) { + if (!config) { + return sinon.create(sinon.sandbox); + } + + var sandbox = prepareSandboxFromConfig(config); + sandbox.args = sandbox.args || []; + sandbox.injectedKeys = []; + sandbox.injectInto = config.injectInto; + var prop, + value; + var exposed = sandbox.inject({}); + + if (config.properties) { + for (var i = 0, l = config.properties.length; i < l; i++) { + prop = config.properties[i]; + value = exposed[prop] || prop === "sandbox" && sandbox; + exposeValue(sandbox, config, prop, value); + } + } else { + exposeValue(sandbox, config, "sandbox", value); + } + + return sandbox; + }, + + match: sinon.match + }); + + sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer; + + return sinon.sandbox; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./extend"); + require("./util/fake_server_with_clock"); + require("./util/fake_timers"); + require("./collection"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend sandbox.js + */ +/** + * Test function, sandboxes fakes + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + var slice = Array.prototype.slice; + + function test(callback) { + var type = typeof callback; + + if (type !== "function") { + throw new TypeError("sinon.test needs to wrap a test function, got " + type); + } + + function sinonSandboxedTest() { + var config = sinon.getConfig(sinon.config); + config.injectInto = config.injectIntoThis && this || config.injectInto; + var sandbox = sinon.sandbox.create(config); + var args = slice.call(arguments); + var oldDone = args.length && args[args.length - 1]; + var exception, result; + + if (typeof oldDone === "function") { + args[args.length - 1] = function sinonDone(res) { + if (res) { + sandbox.restore(); + } else { + sandbox.verifyAndRestore(); + } + oldDone(res); + }; + } + + try { + result = callback.apply(this, args.concat(sandbox.args)); + } catch (e) { + exception = e; + } + + if (typeof oldDone !== "function") { + if (typeof exception !== "undefined") { + sandbox.restore(); + throw exception; + } else { + sandbox.verifyAndRestore(); + } + } + + return result; + } + + if (callback.length) { + return function sinonAsyncSandboxedTest(done) { // eslint-disable-line no-unused-vars + return sinonSandboxedTest.apply(this, arguments); + }; + } + + return sinonSandboxedTest; + } + + test.config = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.test = test; + return test; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./sandbox"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (sinonGlobal) { + makeApi(sinonGlobal); + } +}(typeof sinon === "object" && sinon || null)); // eslint-disable-line no-undef + +/** + * @depend util/core.js + * @depend test.js + */ +/** + * Test case, sandboxes all test functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function createTest(property, setUp, tearDown) { + return function () { + if (setUp) { + setUp.apply(this, arguments); + } + + var exception, result; + + try { + result = property.apply(this, arguments); + } catch (e) { + exception = e; + } + + if (tearDown) { + tearDown.apply(this, arguments); + } + + if (exception) { + throw exception; + } + + return result; + }; + } + + function makeApi(sinon) { + function testCase(tests, prefix) { + if (!tests || typeof tests !== "object") { + throw new TypeError("sinon.testCase needs an object with test functions"); + } + + prefix = prefix || "test"; + var rPrefix = new RegExp("^" + prefix); + var methods = {}; + var setUp = tests.setUp; + var tearDown = tests.tearDown; + var testName, + property, + method; + + for (testName in tests) { + if (tests.hasOwnProperty(testName) && !/^(setUp|tearDown)$/.test(testName)) { + property = tests[testName]; + + if (typeof property === "function" && rPrefix.test(testName)) { + method = property; + + if (setUp || tearDown) { + method = createTest(property, setUp, tearDown); + } + + methods[testName] = sinon.test(method); + } else { + methods[testName] = tests[testName]; + } + } + } + + return methods; + } + + sinon.testCase = testCase; + return testCase; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./test"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend match.js + * @depend format.js + */ +/** + * Assertions matching the test spy retrieval interface. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal, global) { + + var slice = Array.prototype.slice; + + function makeApi(sinon) { + var assert; + + function verifyIsStub() { + var method; + + for (var i = 0, l = arguments.length; i < l; ++i) { + method = arguments[i]; + + if (!method) { + assert.fail("fake is not a spy"); + } + + if (method.proxy && method.proxy.isSinonProxy) { + verifyIsStub(method.proxy); + } else { + if (typeof method !== "function") { + assert.fail(method + " is not a function"); + } + + if (typeof method.getCall !== "function") { + assert.fail(method + " is not stubbed"); + } + } + + } + } + + function failAssertion(object, msg) { + object = object || global; + var failMethod = object.fail || assert.fail; + failMethod.call(object, msg); + } + + function mirrorPropAsAssertion(name, method, message) { + if (arguments.length === 2) { + message = method; + method = name; + } + + assert[name] = function (fake) { + verifyIsStub(fake); + + var args = slice.call(arguments, 1); + var failed = false; + + if (typeof method === "function") { + failed = !method(fake); + } else { + failed = typeof fake[method] === "function" ? + !fake[method].apply(fake, args) : !fake[method]; + } + + if (failed) { + failAssertion(this, (fake.printf || fake.proxy.printf).apply(fake, [message].concat(args))); + } else { + assert.pass(name); + } + }; + } + + function exposedName(prefix, prop) { + return !prefix || /^fail/.test(prop) ? prop : + prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1); + } + + assert = { + failException: "AssertError", + + fail: function fail(message) { + var error = new Error(message); + error.name = this.failException || assert.failException; + + throw error; + }, + + pass: function pass() {}, + + callOrder: function assertCallOrder() { + verifyIsStub.apply(null, arguments); + var expected = ""; + var actual = ""; + + if (!sinon.calledInOrder(arguments)) { + try { + expected = [].join.call(arguments, ", "); + var calls = slice.call(arguments); + var i = calls.length; + while (i) { + if (!calls[--i].called) { + calls.splice(i, 1); + } + } + actual = sinon.orderByFirstCall(calls).join(", "); + } catch (e) { + // If this fails, we'll just fall back to the blank string + } + + failAssertion(this, "expected " + expected + " to be " + + "called in order but were called as " + actual); + } else { + assert.pass("callOrder"); + } + }, + + callCount: function assertCallCount(method, count) { + verifyIsStub(method); + + if (method.callCount !== count) { + var msg = "expected %n to be called " + sinon.timesInWords(count) + + " but was called %c%C"; + failAssertion(this, method.printf(msg)); + } else { + assert.pass("callCount"); + } + }, + + expose: function expose(target, options) { + if (!target) { + throw new TypeError("target is null or undefined"); + } + + var o = options || {}; + var prefix = typeof o.prefix === "undefined" && "assert" || o.prefix; + var includeFail = typeof o.includeFail === "undefined" || !!o.includeFail; + + for (var method in this) { + if (method !== "expose" && (includeFail || !/^(fail)/.test(method))) { + target[exposedName(prefix, method)] = this[method]; + } + } + + return target; + }, + + match: function match(actual, expectation) { + var matcher = sinon.match(expectation); + if (matcher.test(actual)) { + assert.pass("match"); + } else { + var formatted = [ + "expected value to match", + " expected = " + sinon.format(expectation), + " actual = " + sinon.format(actual) + ]; + + failAssertion(this, formatted.join("\n")); + } + } + }; + + mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called"); + mirrorPropAsAssertion("notCalled", function (spy) { + return !spy.called; + }, "expected %n to not have been called but was called %c%C"); + mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C"); + mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C"); + mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C"); + mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t"); + mirrorPropAsAssertion( + "alwaysCalledOn", + "expected %n to always be called with %1 as this but was called with %t" + ); + mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new"); + mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new"); + mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C"); + mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C"); + mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C"); + mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C"); + mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C"); + mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C"); + mirrorPropAsAssertion("threw", "%n did not throw exception%C"); + mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C"); + + sinon.assert = assert; + return assert; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./match"); + require("./format"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof global !== "undefined" ? global : self +)); + + return sinon; +})); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-1.17.0.js b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-1.17.0.js new file mode 100644 index 0000000..ae57a1a --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-1.17.0.js @@ -0,0 +1,5809 @@ +/** + * Sinon.JS 1.17.0, 2015/10/21 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +(function () { + 'use strict'; +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.sinon = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0) { + return args[callArgAt]; + } + + var argumentList; + + if (callArgAt === useLeftMostCallback) { + argumentList = args; + } + + if (callArgAt === useRightMostCallback) { + argumentList = slice.call(args).reverse(); + } + + var callArgProp = behavior.callArgProp; + + for (var i = 0, l = argumentList.length; i < l; ++i) { + if (!callArgProp && typeof argumentList[i] === "function") { + return argumentList[i]; + } + + if (callArgProp && argumentList[i] && + typeof argumentList[i][callArgProp] === "function") { + return argumentList[i][callArgProp]; + } + } + + return null; +} + +function getCallbackError(behavior, func, args) { + if (behavior.callArgAt < 0) { + var msg; + + if (behavior.callArgProp) { + msg = sinon.functionName(behavior.stub) + + " expected to yield to '" + behavior.callArgProp + + "', but no object with such a property was passed."; + } else { + msg = sinon.functionName(behavior.stub) + + " expected to yield, but no callback was passed."; + } + + if (args.length > 0) { + msg += " Received [" + join.call(args, ", ") + "]"; + } + + return msg; + } + + return "argument at index " + behavior.callArgAt + " is not a function: " + func; +} + +function callCallback(behavior, args) { + if (typeof behavior.callArgAt === "number") { + var func = getCallback(behavior, args); + + if (typeof func !== "function") { + throw new TypeError(getCallbackError(behavior, func, args)); + } + + if (behavior.callbackAsync) { + nextTick(function () { + func.apply(behavior.callbackContext, behavior.callbackArguments); + }); + } else { + func.apply(behavior.callbackContext, behavior.callbackArguments); + } + } +} + +var proto = { + create: function create(stub) { + var behavior = sinon.extend({}, sinon.behavior); + delete behavior.create; + behavior.stub = stub; + + return behavior; + }, + + isPresent: function isPresent() { + return (typeof this.callArgAt === "number" || + this.exception || + typeof this.returnArgAt === "number" || + this.returnThis || + this.returnValueDefined); + }, + + invoke: function invoke(context, args) { + callCallback(this, args); + + if (this.exception) { + throw this.exception; + } else if (typeof this.returnArgAt === "number") { + return args[this.returnArgAt]; + } else if (this.returnThis) { + return context; + } + + return this.returnValue; + }, + + onCall: function onCall(index) { + return this.stub.onCall(index); + }, + + onFirstCall: function onFirstCall() { + return this.stub.onFirstCall(); + }, + + onSecondCall: function onSecondCall() { + return this.stub.onSecondCall(); + }, + + onThirdCall: function onThirdCall() { + return this.stub.onThirdCall(); + }, + + withArgs: function withArgs(/* arguments */) { + throw new Error( + "Defining a stub by invoking \"stub.onCall(...).withArgs(...)\" " + + "is not supported. Use \"stub.withArgs(...).onCall(...)\" " + + "to define sequential behavior for calls with certain arguments." + ); + }, + + callsArg: function callsArg(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOn: function callsArgOn(pos, context) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgWith: function callsArgWith(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOnWith: function callsArgWith(pos, context) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yields: function () { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsRight: function () { + this.callArgAt = useRightMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsOn: function (context) { + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsTo: function (prop) { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + yieldsToOn: function (prop, context) { + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + throws: throwsException, + throwsException: throwsException, + + returns: function returns(value) { + this.returnValue = value; + this.returnValueDefined = true; + this.exception = undefined; + + return this; + }, + + returnsArg: function returnsArg(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.returnArgAt = pos; + + return this; + }, + + returnsThis: function returnsThis() { + this.returnThis = true; + + return this; + } +}; + +function createAsyncVersion(syncFnName) { + return function () { + var result = this[syncFnName].apply(this, arguments); + this.callbackAsync = true; + return result; + }; +} + +// create asynchronous versions of callsArg* and yields* methods +for (var method in proto) { + // need to avoid creating anotherasync versions of the newly added async methods + if (proto.hasOwnProperty(method) && method.match(/^(callsArg|yields)/) && !method.match(/Async/)) { + proto[method + "Async"] = createAsyncVersion(method); + } +} + +sinon.behavior = proto; + +}).call(this,require('_process')) +},{"./extend":6,"./util/core":25,"_process":38}],4:[function(require,module,exports){ +/** + * Spy calls + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + * Copyright (c) 2013 Maximilian Antoni + */ +"use strict"; + +require("./match"); +require("./format"); +var sinon = require("./util/core"); +var slice = Array.prototype.slice; + +function throwYieldError(proxy, text, args) { + var msg = sinon.functionName(proxy) + text; + if (args.length) { + msg += " Received [" + slice.call(args).join(", ") + "]"; + } + throw new Error(msg); +} + +var callProto = { + calledOn: function calledOn(thisValue) { + if (sinon.match && sinon.match.isMatcher(thisValue)) { + return thisValue.test(this.thisValue); + } + return this.thisValue === thisValue; + }, + + calledWith: function calledWith() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + if (!sinon.deepEqual(arguments[i], this.args[i])) { + return false; + } + } + + return true; + }, + + calledWithMatch: function calledWithMatch() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + var actual = this.args[i]; + var expectation = arguments[i]; + if (!sinon.match || !sinon.match(expectation).test(actual)) { + return false; + } + } + return true; + }, + + calledWithExactly: function calledWithExactly() { + return arguments.length === this.args.length && + this.calledWith.apply(this, arguments); + }, + + notCalledWith: function notCalledWith() { + return !this.calledWith.apply(this, arguments); + }, + + notCalledWithMatch: function notCalledWithMatch() { + return !this.calledWithMatch.apply(this, arguments); + }, + + returned: function returned(value) { + return sinon.deepEqual(value, this.returnValue); + }, + + threw: function threw(error) { + if (typeof error === "undefined" || !this.exception) { + return !!this.exception; + } + + return this.exception === error || this.exception.name === error; + }, + + calledWithNew: function calledWithNew() { + return this.proxy.prototype && this.thisValue instanceof this.proxy; + }, + + calledBefore: function (other) { + return this.callId < other.callId; + }, + + calledAfter: function (other) { + return this.callId > other.callId; + }, + + callArg: function (pos) { + this.args[pos](); + }, + + callArgOn: function (pos, thisValue) { + this.args[pos].apply(thisValue); + }, + + callArgWith: function (pos) { + this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1))); + }, + + callArgOnWith: function (pos, thisValue) { + var args = slice.call(arguments, 2); + this.args[pos].apply(thisValue, args); + }, + + "yield": function () { + this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0))); + }, + + yieldOn: function (thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (typeof args[i] === "function") { + args[i].apply(thisValue, slice.call(arguments, 1)); + return; + } + } + throwYieldError(this.proxy, " cannot yield since no callback was passed.", args); + }, + + yieldTo: function (prop) { + this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1))); + }, + + yieldToOn: function (prop, thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (args[i] && typeof args[i][prop] === "function") { + args[i][prop].apply(thisValue, slice.call(arguments, 2)); + return; + } + } + throwYieldError(this.proxy, " cannot yield to '" + prop + + "' since no callback was passed.", args); + }, + + getStackFrames: function () { + // Omit the error message and the two top stack frames in sinon itself: + return this.stack && this.stack.split("\n").slice(3); + }, + + toString: function () { + var callStr = this.proxy.toString() + "("; + var args = []; + + for (var i = 0, l = this.args.length; i < l; ++i) { + args.push(sinon.format(this.args[i])); + } + + callStr = callStr + args.join(", ") + ")"; + + if (typeof this.returnValue !== "undefined") { + callStr += " => " + sinon.format(this.returnValue); + } + + if (this.exception) { + callStr += " !" + this.exception.name; + + if (this.exception.message) { + callStr += "(" + this.exception.message + ")"; + } + } + if (this.stack) { + callStr += this.getStackFrames()[0].replace(/^\s*(?:at\s+|@)?/, " at "); + + } + + return callStr; + } +}; + +callProto.invokeCallback = callProto.yield; + +function createSpyCall(spy, thisValue, args, returnValue, exception, id, stack) { + if (typeof id !== "number") { + throw new TypeError("Call id is not a number"); + } + var proxyCall = sinon.create(callProto); + proxyCall.proxy = spy; + proxyCall.thisValue = thisValue; + proxyCall.args = args; + proxyCall.returnValue = returnValue; + proxyCall.exception = exception; + proxyCall.callId = id; + proxyCall.stack = stack; + + return proxyCall; +} +createSpyCall.toString = callProto.toString; // used by mocks + +sinon.spyCall = createSpyCall; + +},{"./format":7,"./match":9,"./util/core":25}],5:[function(require,module,exports){ +/** + * Collections of stubs, spies and mocks. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +require("./mock"); +require("./spy"); +require("./stub"); +var sinon = require("./util/core"); + +var push = [].push; +var hasOwnProperty = Object.prototype.hasOwnProperty; + +function getFakes(fakeCollection) { + if (!fakeCollection.fakes) { + fakeCollection.fakes = []; + } + + return fakeCollection.fakes; +} + +function each(fakeCollection, method) { + var fakes = getFakes(fakeCollection); + + for (var i = 0, l = fakes.length; i < l; i += 1) { + if (typeof fakes[i][method] === "function") { + fakes[i][method](); + } + } +} + +function compact(fakeCollection) { + var fakes = getFakes(fakeCollection); + var i = 0; + while (i < fakes.length) { + fakes.splice(i, 1); + } +} + +var collection = { + verify: function resolve() { + each(this, "verify"); + }, + + restore: function restore() { + each(this, "restore"); + compact(this); + }, + + reset: function restore() { + each(this, "reset"); + }, + + verifyAndRestore: function verifyAndRestore() { + var exception; + + try { + this.verify(); + } catch (e) { + exception = e; + } + + this.restore(); + + if (exception) { + throw exception; + } + }, + + add: function add(fake) { + push.call(getFakes(this), fake); + return fake; + }, + + spy: function spy() { + return this.add(sinon.spy.apply(sinon, arguments)); + }, + + stub: function stub(object, property, value) { + if (property) { + if (!object) { + var type = object === null ? "null" : "undefined"; + throw new Error("Trying to stub property '" + property + "' of " + type); + } + + var original = object[property]; + + if (typeof original !== "function") { + if (!hasOwnProperty.call(object, property)) { + throw new TypeError("Cannot stub non-existent own property " + property); + } + + object[property] = value; + + return this.add({ + restore: function () { + object[property] = original; + } + }); + } + } + if (!property && !!object && typeof object === "object") { + var stubbedObj = sinon.stub.apply(sinon, arguments); + + for (var prop in stubbedObj) { + if (typeof stubbedObj[prop] === "function") { + this.add(stubbedObj[prop]); + } + } + + return stubbedObj; + } + + return this.add(sinon.stub.apply(sinon, arguments)); + }, + + mock: function mock() { + return this.add(sinon.mock.apply(sinon, arguments)); + }, + + inject: function inject(obj) { + var col = this; + + obj.spy = function () { + return col.spy.apply(col, arguments); + }; + + obj.stub = function () { + return col.stub.apply(col, arguments); + }; + + obj.mock = function () { + return col.mock.apply(col, arguments); + }; + + return obj; + } +}; + +sinon.collection = collection; + +},{"./mock":10,"./spy":12,"./stub":13,"./util/core":25}],6:[function(require,module,exports){ +"use strict"; + +// Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug +var hasDontEnumBug = (function () { + var obj = { + constructor: function () { + return "0"; + }, + toString: function () { + return "1"; + }, + valueOf: function () { + return "2"; + }, + toLocaleString: function () { + return "3"; + }, + prototype: function () { + return "4"; + }, + isPrototypeOf: function () { + return "5"; + }, + propertyIsEnumerable: function () { + return "6"; + }, + hasOwnProperty: function () { + return "7"; + }, + length: function () { + return "8"; + }, + unique: function () { + return "9"; + } + }; + + var result = []; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + result.push(obj[prop]()); + } + } + return result.join("") !== "0123456789"; +})(); + +/* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will + * override properties in previous sources. + * + * target - The Object to extend + * sources - Objects to copy properties from. + * + * Returns the extended target + */ +module.exports = function extend(target /*, sources */) { + var sources = Array.prototype.slice.call(arguments, 1); + var source, i, prop; + + for (i = 0; i < sources.length; i++) { + source = sources[i]; + + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // Make sure we copy (own) toString method even when in JScript with DontEnum bug + // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) { + target.toString = source.toString; + } + } + + return target; +}; + +},{}],7:[function(require,module,exports){ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +"use strict"; + +var formatio = require("formatio"); +var sinon = require("./util/core"); + +function getFormatioFormatter() { + var formatter = formatio.configure({ + quoteStrings: false, + limitChildrenCount: 250 + }); + + function format() { + return formatter.ascii.apply(formatter, arguments); + } + + return format; +} + +sinon.format = getFormatioFormatter(); + +},{"./util/core":25,"formatio":39}],8:[function(require,module,exports){ +/** + * Logs errors + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +"use strict"; + +var sinon = require("./util/core"); + +// cache a reference to setTimeout, so that our reference won't be stubbed out +// when using fake timers and errors will still get logged +// https://github.com/cjohansen/Sinon.JS/issues/381 +var realSetTimeout = setTimeout; + +function log() {} + +function logError(label, err) { + var msg = label + " threw exception: "; + + function throwLoggedError() { + err.message = msg + err.message; + throw err; + } + + sinon.log(msg + "[" + err.name + "] " + err.message); + + if (err.stack) { + sinon.log(err.stack); + } + + if (logError.useImmediateExceptions) { + throwLoggedError(); + } else { + logError.setTimeout(throwLoggedError, 0); + } +} + +// When set to true, any errors logged will be thrown immediately; +// If set to false, the errors will be thrown in separate execution frame. +logError.useImmediateExceptions = true; + +// wrap realSetTimeout with something we can stub in tests +logError.setTimeout = function (func, timeout) { + realSetTimeout(func, timeout); +}; + +var exports = {}; +exports.log = sinon.log = log; +exports.logError = sinon.logError = logError; + +},{"./util/core":25}],9:[function(require,module,exports){ +/** + * Match functions + * + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2012 Maximilian Antoni + */ +"use strict"; + +var create = require("./util/core/create"); +var deepEqual = require("./util/core/deep-equal").use(match); // eslint-disable-line no-use-before-define +var functionName = require("./util/core/function-name"); +var typeOf = require("./typeOf"); + +function assertType(value, type, name) { + var actual = typeOf(value); + if (actual !== type) { + throw new TypeError("Expected type of " + name + " to be " + + type + ", but was " + actual); + } +} + +var matcher = { + toString: function () { + return this.message; + } +}; + +function isMatcher(object) { + return matcher.isPrototypeOf(object); +} + +function matchObject(expectation, actual) { + if (actual === null || actual === undefined) { + return false; + } + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + var exp = expectation[key]; + var act = actual[key]; + if (isMatcher(exp)) { + if (!exp.test(act)) { + return false; + } + } else if (typeOf(exp) === "object") { + if (!matchObject(exp, act)) { + return false; + } + } else if (!deepEqual(exp, act)) { + return false; + } + } + } + return true; +} + +function match(expectation, message) { + var m = create(matcher); + var type = typeOf(expectation); + switch (type) { + case "object": + if (typeof expectation.test === "function") { + m.test = function (actual) { + return expectation.test(actual) === true; + }; + m.message = "match(" + functionName(expectation.test) + ")"; + return m; + } + var str = []; + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + str.push(key + ": " + expectation[key]); + } + } + m.test = function (actual) { + return matchObject(expectation, actual); + }; + m.message = "match(" + str.join(", ") + ")"; + break; + case "number": + m.test = function (actual) { + // we need type coercion here + return expectation == actual; // eslint-disable-line eqeqeq + }; + break; + case "string": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return actual.indexOf(expectation) !== -1; + }; + m.message = "match(\"" + expectation + "\")"; + break; + case "regexp": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return expectation.test(actual); + }; + break; + case "function": + m.test = expectation; + if (message) { + m.message = message; + } else { + m.message = "match(" + functionName(expectation) + ")"; + } + break; + default: + m.test = function (actual) { + return deepEqual(expectation, actual); + }; + } + if (!m.message) { + m.message = "match(" + expectation + ")"; + } + return m; +} + +matcher.or = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var or = create(matcher); + or.test = function (actual) { + return m1.test(actual) || m2.test(actual); + }; + or.message = m1.message + ".or(" + m2.message + ")"; + return or; +}; + +matcher.and = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var and = create(matcher); + and.test = function (actual) { + return m1.test(actual) && m2.test(actual); + }; + and.message = m1.message + ".and(" + m2.message + ")"; + return and; +}; + +match.isMatcher = isMatcher; + +match.any = match(function () { + return true; +}, "any"); + +match.defined = match(function (actual) { + return actual !== null && actual !== undefined; +}, "defined"); + +match.truthy = match(function (actual) { + return !!actual; +}, "truthy"); + +match.falsy = match(function (actual) { + return !actual; +}, "falsy"); + +match.same = function (expectation) { + return match(function (actual) { + return expectation === actual; + }, "same(" + expectation + ")"); +}; + +match.typeOf = function (type) { + assertType(type, "string", "type"); + return match(function (actual) { + return typeOf(actual) === type; + }, "typeOf(\"" + type + "\")"); +}; + +match.instanceOf = function (type) { + assertType(type, "function", "type"); + return match(function (actual) { + return actual instanceof type; + }, "instanceOf(" + functionName(type) + ")"); +}; + +function createPropertyMatcher(propertyTest, messagePrefix) { + return function (property, value) { + assertType(property, "string", "property"); + var onlyProperty = arguments.length === 1; + var message = messagePrefix + "(\"" + property + "\""; + if (!onlyProperty) { + message += ", " + value; + } + message += ")"; + return match(function (actual) { + if (actual === undefined || actual === null || + !propertyTest(actual, property)) { + return false; + } + return onlyProperty || deepEqual(value, actual[property]); + }, message); + }; +} + +match.has = createPropertyMatcher(function (actual, property) { + if (typeof actual === "object") { + return property in actual; + } + return actual[property] !== undefined; +}, "has"); + +match.hasOwn = createPropertyMatcher(function (actual, property) { + return actual.hasOwnProperty(property); +}, "hasOwn"); + +match.bool = match.typeOf("boolean"); +match.number = match.typeOf("number"); +match.string = match.typeOf("string"); +match.object = match.typeOf("object"); +match.func = match.typeOf("function"); +match.array = match.typeOf("array"); +match.regexp = match.typeOf("regexp"); +match.date = match.typeOf("date"); + +module.exports = match; + +},{"./typeOf":16,"./util/core/create":18,"./util/core/deep-equal":19,"./util/core/function-name":21}],10:[function(require,module,exports){ +/** + * Mock functions. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +require("./call"); +require("./extend"); +require("./match"); +require("./spy"); +require("./stub"); +require("./format"); +var sinon = require("./util/core"); + +var push = [].push; +var match = sinon.match; + +function mock(object) { + // if (typeof console !== undefined && console.warn) { + // console.warn("mock will be removed from Sinon.JS v2.0"); + // } + + if (!object) { + return sinon.expectation.create("Anonymous mock"); + } + + return mock.create(object); +} + +function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } +} + +function arrayEquals(arr1, arr2, compareLength) { + if (compareLength && (arr1.length !== arr2.length)) { + return false; + } + + for (var i = 0, l = arr1.length; i < l; i++) { + if (!sinon.deepEqual(arr1[i], arr2[i])) { + return false; + } + } + return true; +} + +sinon.extend(mock, { + create: function create(object) { + if (!object) { + throw new TypeError("object is null"); + } + + var mockObject = sinon.extend({}, mock); + mockObject.object = object; + delete mockObject.create; + + return mockObject; + }, + + expects: function expects(method) { + if (!method) { + throw new TypeError("method is falsy"); + } + + if (!this.expectations) { + this.expectations = {}; + this.proxies = []; + } + + if (!this.expectations[method]) { + this.expectations[method] = []; + var mockObject = this; + + sinon.wrapMethod(this.object, method, function () { + return mockObject.invokeMethod(method, this, arguments); + }); + + push.call(this.proxies, method); + } + + var expectation = sinon.expectation.create(method); + push.call(this.expectations[method], expectation); + + return expectation; + }, + + restore: function restore() { + var object = this.object; + + each(this.proxies, function (proxy) { + if (typeof object[proxy].restore === "function") { + object[proxy].restore(); + } + }); + }, + + verify: function verify() { + var expectations = this.expectations || {}; + var messages = []; + var met = []; + + each(this.proxies, function (proxy) { + each(expectations[proxy], function (expectation) { + if (!expectation.met()) { + push.call(messages, expectation.toString()); + } else { + push.call(met, expectation.toString()); + } + }); + }); + + this.restore(); + + if (messages.length > 0) { + sinon.expectation.fail(messages.concat(met).join("\n")); + } else if (met.length > 0) { + sinon.expectation.pass(messages.concat(met).join("\n")); + } + + return true; + }, + + invokeMethod: function invokeMethod(method, thisValue, args) { + var expectations = this.expectations && this.expectations[method] ? this.expectations[method] : []; + var expectationsWithMatchingArgs = []; + var currentArgs = args || []; + var i, available; + + for (i = 0; i < expectations.length; i += 1) { + var expectedArgs = expectations[i].expectedArguments || []; + if (arrayEquals(expectedArgs, currentArgs, expectations[i].expectsExactArgCount)) { + expectationsWithMatchingArgs.push(expectations[i]); + } + } + + for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) { + if (!expectationsWithMatchingArgs[i].met() && + expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) { + return expectationsWithMatchingArgs[i].apply(thisValue, args); + } + } + + var messages = []; + var exhausted = 0; + + for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) { + if (expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) { + available = available || expectationsWithMatchingArgs[i]; + } else { + exhausted += 1; + } + } + + if (available && exhausted === 0) { + return available.apply(thisValue, args); + } + + for (i = 0; i < expectations.length; i += 1) { + push.call(messages, " " + expectations[i].toString()); + } + + messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({ + proxy: method, + args: args + })); + + sinon.expectation.fail(messages.join("\n")); + } +}); + +var times = sinon.timesInWords; +var slice = Array.prototype.slice; + +function callCountInWords(callCount) { + if (callCount === 0) { + return "never called"; + } + + return "called " + times(callCount); +} + +function expectedCallCountInWords(expectation) { + var min = expectation.minCalls; + var max = expectation.maxCalls; + + if (typeof min === "number" && typeof max === "number") { + var str = times(min); + + if (min !== max) { + str = "at least " + str + " and at most " + times(max); + } + + return str; + } + + if (typeof min === "number") { + return "at least " + times(min); + } + + return "at most " + times(max); +} + +function receivedMinCalls(expectation) { + var hasMinLimit = typeof expectation.minCalls === "number"; + return !hasMinLimit || expectation.callCount >= expectation.minCalls; +} + +function receivedMaxCalls(expectation) { + if (typeof expectation.maxCalls !== "number") { + return false; + } + + return expectation.callCount === expectation.maxCalls; +} + +function verifyMatcher(possibleMatcher, arg) { + var isMatcher = match && match.isMatcher(possibleMatcher); + + return isMatcher && possibleMatcher.test(arg) || true; +} + +sinon.expectation = { + minCalls: 1, + maxCalls: 1, + + create: function create(methodName) { + var expectation = sinon.extend(sinon.stub.create(), sinon.expectation); + delete expectation.create; + expectation.method = methodName; + + return expectation; + }, + + invoke: function invoke(func, thisValue, args) { + this.verifyCallAllowed(thisValue, args); + + return sinon.spy.invoke.apply(this, arguments); + }, + + atLeast: function atLeast(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.maxCalls = null; + this.limitsSet = true; + } + + this.minCalls = num; + + return this; + }, + + atMost: function atMost(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.minCalls = null; + this.limitsSet = true; + } + + this.maxCalls = num; + + return this; + }, + + never: function never() { + return this.exactly(0); + }, + + once: function once() { + return this.exactly(1); + }, + + twice: function twice() { + return this.exactly(2); + }, + + thrice: function thrice() { + return this.exactly(3); + }, + + exactly: function exactly(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not a number"); + } + + this.atLeast(num); + return this.atMost(num); + }, + + met: function met() { + return !this.failed && receivedMinCalls(this); + }, + + verifyCallAllowed: function verifyCallAllowed(thisValue, args) { + if (receivedMaxCalls(this)) { + this.failed = true; + sinon.expectation.fail(this.method + " already called " + times(this.maxCalls)); + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " + + this.expectedThis); + } + + if (!("expectedArguments" in this)) { + return; + } + + if (!args) { + sinon.expectation.fail(this.method + " received no arguments, expected " + + sinon.format(this.expectedArguments)); + } + + if (args.length < this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + if (this.expectsExactArgCount && + args.length !== this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", didn't match " + this.expectedArguments.toString()); + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", expected " + sinon.format(this.expectedArguments)); + } + } + }, + + allowsCall: function allowsCall(thisValue, args) { + if (this.met() && receivedMaxCalls(this)) { + return false; + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + return false; + } + + if (!("expectedArguments" in this)) { + return true; + } + + args = args || []; + + if (args.length < this.expectedArguments.length) { + return false; + } + + if (this.expectsExactArgCount && + args.length !== this.expectedArguments.length) { + return false; + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + return false; + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + return false; + } + } + + return true; + }, + + withArgs: function withArgs() { + this.expectedArguments = slice.call(arguments); + return this; + }, + + withExactArgs: function withExactArgs() { + this.withArgs.apply(this, arguments); + this.expectsExactArgCount = true; + return this; + }, + + on: function on(thisValue) { + this.expectedThis = thisValue; + return this; + }, + + toString: function () { + var args = (this.expectedArguments || []).slice(); + + if (!this.expectsExactArgCount) { + push.call(args, "[...]"); + } + + var callStr = sinon.spyCall.toString.call({ + proxy: this.method || "anonymous mock expectation", + args: args + }); + + var message = callStr.replace(", [...", "[, ...") + " " + + expectedCallCountInWords(this); + + if (this.met()) { + return "Expectation met: " + message; + } + + return "Expected " + message + " (" + + callCountInWords(this.callCount) + ")"; + }, + + verify: function verify() { + if (!this.met()) { + sinon.expectation.fail(this.toString()); + } else { + sinon.expectation.pass(this.toString()); + } + + return true; + }, + + pass: function pass(message) { + sinon.assert.pass(message); + }, + + fail: function fail(message) { + var exception = new Error(message); + exception.name = "ExpectationError"; + + throw exception; + } +}; + +sinon.mock = mock; + +},{"./call":4,"./extend":6,"./format":7,"./match":9,"./spy":12,"./stub":13,"./util/core":25}],11:[function(require,module,exports){ +/** + * Manages fake collections as well as fake utilities such as Sinon's + * timers and fake XHR implementation in one convenient object. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +require("./extend"); +require("./collection"); +require("./util/fake_server_with_clock"); +require("./util/fake_timers"); +var sinon = require("./util/core"); + +var push = [].push; + +function exposeValue(sandbox, config, key, value) { + if (!value) { + return; + } + + if (config.injectInto && !(key in config.injectInto)) { + config.injectInto[key] = value; + sandbox.injectedKeys.push(key); + } else { + push.call(sandbox.args, value); + } +} + +function prepareSandboxFromConfig(config) { + var sandbox = sinon.create(sinon.sandbox); + + if (config.useFakeServer) { + if (typeof config.useFakeServer === "object") { + sandbox.serverPrototype = config.useFakeServer; + } + + sandbox.useFakeServer(); + } + + if (config.useFakeTimers) { + if (typeof config.useFakeTimers === "object") { + sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers); + } else { + sandbox.useFakeTimers(); + } + } + + return sandbox; +} + +sinon.sandbox = sinon.extend(sinon.create(sinon.collection), { + useFakeTimers: function useFakeTimers() { + this.clock = sinon.useFakeTimers.apply(sinon, arguments); + + return this.add(this.clock); + }, + + serverPrototype: sinon.fakeServer, + + useFakeServer: function useFakeServer() { + var proto = this.serverPrototype || sinon.fakeServer; + + if (!proto || !proto.create) { + return null; + } + + this.server = proto.create(); + return this.add(this.server); + }, + + inject: function (obj) { + sinon.collection.inject.call(this, obj); + + if (this.clock) { + obj.clock = this.clock; + } + + if (this.server) { + obj.server = this.server; + obj.requests = this.server.requests; + } + + obj.match = sinon.match; + + return obj; + }, + + restore: function () { + sinon.collection.restore.apply(this, arguments); + this.restoreContext(); + }, + + restoreContext: function () { + if (this.injectedKeys) { + for (var i = 0, j = this.injectedKeys.length; i < j; i++) { + delete this.injectInto[this.injectedKeys[i]]; + } + this.injectedKeys = []; + } + }, + + create: function (config) { + if (!config) { + return sinon.create(sinon.sandbox); + } + + var sandbox = prepareSandboxFromConfig(config); + sandbox.args = sandbox.args || []; + sandbox.injectedKeys = []; + sandbox.injectInto = config.injectInto; + var prop, + value; + var exposed = sandbox.inject({}); + + if (config.properties) { + for (var i = 0, l = config.properties.length; i < l; i++) { + prop = config.properties[i]; + value = exposed[prop] || prop === "sandbox" && sandbox; + exposeValue(sandbox, config, prop, value); + } + } else { + exposeValue(sandbox, config, "sandbox", value); + } + + return sandbox; + }, + + match: sinon.match +}); + +sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer; + +},{"./collection":5,"./extend":6,"./util/core":25,"./util/fake_server_with_clock":33,"./util/fake_timers":34}],12:[function(require,module,exports){ +/** + * Spy functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +require("./extend"); +require("./call"); +require("./format"); +var sinon = require("./util/core"); + +var push = Array.prototype.push; +var slice = Array.prototype.slice; +var callId = 0; + +function spy(object, property, types) { + if (!property && typeof object === "function") { + return spy.create(object); + } + + if (!object && !property) { + return spy.create(function () { }); + } + + if (types) { + var methodDesc = sinon.getPropertyDescriptor(object, property); + for (var i = 0; i < types.length; i++) { + methodDesc[types[i]] = spy.create(methodDesc[types[i]]); + } + return sinon.wrapMethod(object, property, methodDesc); + } + + return sinon.wrapMethod(object, property, spy.create(object[property])); +} + +function matchingFake(fakes, args, strict) { + if (!fakes) { + return undefined; + } + + for (var i = 0, l = fakes.length; i < l; i++) { + if (fakes[i].matches(args, strict)) { + return fakes[i]; + } + } +} + +function incrementCallCount() { + this.called = true; + this.callCount += 1; + this.notCalled = false; + this.calledOnce = this.callCount === 1; + this.calledTwice = this.callCount === 2; + this.calledThrice = this.callCount === 3; +} + +function createCallProperties() { + this.firstCall = this.getCall(0); + this.secondCall = this.getCall(1); + this.thirdCall = this.getCall(2); + this.lastCall = this.getCall(this.callCount - 1); +} + +var vars = "a,b,c,d,e,f,g,h,i,j,k,l"; +function createProxy(func, proxyLength) { + // Retain the function length: + var p; + if (proxyLength) { + eval("p = (function proxy(" + vars.substring(0, proxyLength * 2 - 1) + // eslint-disable-line no-eval + ") { return p.invoke(func, this, slice.call(arguments)); });"); + } else { + p = function proxy() { + return p.invoke(func, this, slice.call(arguments)); + }; + } + p.isSinonProxy = true; + return p; +} + +var uuid = 0; + +// Public API +var spyApi = { + reset: function () { + if (this.invoking) { + var err = new Error("Cannot reset Sinon function while invoking it. " + + "Move the call to .reset outside of the callback."); + err.name = "InvalidResetException"; + throw err; + } + + this.called = false; + this.notCalled = true; + this.calledOnce = false; + this.calledTwice = false; + this.calledThrice = false; + this.callCount = 0; + this.firstCall = null; + this.secondCall = null; + this.thirdCall = null; + this.lastCall = null; + this.args = []; + this.returnValues = []; + this.thisValues = []; + this.exceptions = []; + this.callIds = []; + this.stacks = []; + if (this.fakes) { + for (var i = 0; i < this.fakes.length; i++) { + this.fakes[i].reset(); + } + } + + return this; + }, + + create: function create(func, spyLength) { + var name; + + if (typeof func !== "function") { + func = function () { }; + } else { + name = sinon.functionName(func); + } + + if (!spyLength) { + spyLength = func.length; + } + + var proxy = createProxy(func, spyLength); + + sinon.extend(proxy, spy); + delete proxy.create; + sinon.extend(proxy, func); + + proxy.reset(); + proxy.prototype = func.prototype; + proxy.displayName = name || "spy"; + proxy.toString = sinon.functionToString; + proxy.instantiateFake = sinon.spy.create; + proxy.id = "spy#" + uuid++; + + return proxy; + }, + + invoke: function invoke(func, thisValue, args) { + var matching = matchingFake(this.fakes, args); + var exception, returnValue; + + incrementCallCount.call(this); + push.call(this.thisValues, thisValue); + push.call(this.args, args); + push.call(this.callIds, callId++); + + // Make call properties available from within the spied function: + createCallProperties.call(this); + + try { + this.invoking = true; + + if (matching) { + returnValue = matching.invoke(func, thisValue, args); + } else { + returnValue = (this.func || func).apply(thisValue, args); + } + + var thisCall = this.getCall(this.callCount - 1); + if (thisCall.calledWithNew() && typeof returnValue !== "object") { + returnValue = thisValue; + } + } catch (e) { + exception = e; + } finally { + delete this.invoking; + } + + push.call(this.exceptions, exception); + push.call(this.returnValues, returnValue); + push.call(this.stacks, new Error().stack); + + // Make return value and exception available in the calls: + createCallProperties.call(this); + + if (exception !== undefined) { + throw exception; + } + + return returnValue; + }, + + named: function named(name) { + this.displayName = name; + return this; + }, + + getCall: function getCall(i) { + if (i < 0 || i >= this.callCount) { + return null; + } + + return sinon.spyCall(this, this.thisValues[i], this.args[i], + this.returnValues[i], this.exceptions[i], + this.callIds[i], this.stacks[i]); + }, + + getCalls: function () { + var calls = []; + var i; + + for (i = 0; i < this.callCount; i++) { + calls.push(this.getCall(i)); + } + + return calls; + }, + + calledBefore: function calledBefore(spyFn) { + if (!this.called) { + return false; + } + + if (!spyFn.called) { + return true; + } + + return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1]; + }, + + calledAfter: function calledAfter(spyFn) { + if (!this.called || !spyFn.called) { + return false; + } + + return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1]; + }, + + withArgs: function () { + var args = slice.call(arguments); + + if (this.fakes) { + var match = matchingFake(this.fakes, args, true); + + if (match) { + return match; + } + } else { + this.fakes = []; + } + + var original = this; + var fake = this.instantiateFake(); + fake.matchingAguments = args; + fake.parent = this; + push.call(this.fakes, fake); + + fake.withArgs = function () { + return original.withArgs.apply(original, arguments); + }; + + for (var i = 0; i < this.args.length; i++) { + if (fake.matches(this.args[i])) { + incrementCallCount.call(fake); + push.call(fake.thisValues, this.thisValues[i]); + push.call(fake.args, this.args[i]); + push.call(fake.returnValues, this.returnValues[i]); + push.call(fake.exceptions, this.exceptions[i]); + push.call(fake.callIds, this.callIds[i]); + } + } + createCallProperties.call(fake); + + return fake; + }, + + matches: function (args, strict) { + var margs = this.matchingAguments; + + if (margs.length <= args.length && + sinon.deepEqual(margs, args.slice(0, margs.length))) { + return !strict || margs.length === args.length; + } + }, + + printf: function (format) { + var spyInstance = this; + var args = slice.call(arguments, 1); + var formatter; + + return (format || "").replace(/%(.)/g, function (match, specifyer) { + formatter = spyApi.formatters[specifyer]; + + if (typeof formatter === "function") { + return formatter.call(null, spyInstance, args); + } else if (!isNaN(parseInt(specifyer, 10))) { + return sinon.format(args[specifyer - 1]); + } + + return "%" + specifyer; + }); + } +}; + +function delegateToCalls(method, matchAny, actual, notCalled) { + spyApi[method] = function () { + if (!this.called) { + if (notCalled) { + return notCalled.apply(this, arguments); + } + return false; + } + + var currentCall; + var matches = 0; + + for (var i = 0, l = this.callCount; i < l; i += 1) { + currentCall = this.getCall(i); + + if (currentCall[actual || method].apply(currentCall, arguments)) { + matches += 1; + + if (matchAny) { + return true; + } + } + } + + return matches === this.callCount; + }; +} + +delegateToCalls("calledOn", true); +delegateToCalls("alwaysCalledOn", false, "calledOn"); +delegateToCalls("calledWith", true); +delegateToCalls("calledWithMatch", true); +delegateToCalls("alwaysCalledWith", false, "calledWith"); +delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch"); +delegateToCalls("calledWithExactly", true); +delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly"); +delegateToCalls("neverCalledWith", false, "notCalledWith", function () { + return true; +}); +delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch", function () { + return true; +}); +delegateToCalls("threw", true); +delegateToCalls("alwaysThrew", false, "threw"); +delegateToCalls("returned", true); +delegateToCalls("alwaysReturned", false, "returned"); +delegateToCalls("calledWithNew", true); +delegateToCalls("alwaysCalledWithNew", false, "calledWithNew"); +delegateToCalls("callArg", false, "callArgWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); +}); +spyApi.callArgWith = spyApi.callArg; +delegateToCalls("callArgOn", false, "callArgOnWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); +}); +spyApi.callArgOnWith = spyApi.callArgOn; +delegateToCalls("yield", false, "yield", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); +}); +// "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode. +spyApi.invokeCallback = spyApi.yield; +delegateToCalls("yieldOn", false, "yieldOn", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); +}); +delegateToCalls("yieldTo", false, "yieldTo", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); +}); +delegateToCalls("yieldToOn", false, "yieldToOn", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); +}); + +spyApi.formatters = { + c: function (spyInstance) { + return sinon.timesInWords(spyInstance.callCount); + }, + + n: function (spyInstance) { + return spyInstance.toString(); + }, + + C: function (spyInstance) { + var calls = []; + + for (var i = 0, l = spyInstance.callCount; i < l; ++i) { + var stringifiedCall = " " + spyInstance.getCall(i).toString(); + if (/\n/.test(calls[i - 1])) { + stringifiedCall = "\n" + stringifiedCall; + } + push.call(calls, stringifiedCall); + } + + return calls.length > 0 ? "\n" + calls.join("\n") : ""; + }, + + t: function (spyInstance) { + var objects = []; + + for (var i = 0, l = spyInstance.callCount; i < l; ++i) { + push.call(objects, sinon.format(spyInstance.thisValues[i])); + } + + return objects.join(", "); + }, + + "*": function (spyInstance, args) { + var formatted = []; + + for (var i = 0, l = args.length; i < l; ++i) { + push.call(formatted, sinon.format(args[i])); + } + + return formatted.join(", "); + } +}; + +sinon.extend(spy, spyApi); + +spy.spyCall = sinon.spyCall; +sinon.spy = spy; + +},{"./call":4,"./extend":6,"./format":7,"./util/core":25}],13:[function(require,module,exports){ +/** + * Stub functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +require("./behavior"); +require("./spy"); +require("./extend"); +require("./walk"); +var sinon = require("./util/core"); + +function stub(object, property, func) { + if (!!func && typeof func !== "function" && typeof func !== "object") { + throw new TypeError("Custom stub should be a function or a property descriptor"); + } + + if (property && !object) { + var type = object === null ? "null" : "undefined"; + throw new Error("Trying to stub property '" + property + "' of " + type); + } + + var wrapper; + + if (func) { + if (typeof func === "function") { + wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func; + } else { + wrapper = func; + if (sinon.spy && sinon.spy.create) { + var types = sinon.objectKeys(wrapper); + for (var i = 0; i < types.length; i++) { + wrapper[types[i]] = sinon.spy.create(wrapper[types[i]]); + } + } + } + } else { + var stubLength = 0; + if (typeof object === "object" && typeof object[property] === "function") { + stubLength = object[property].length; + } + wrapper = stub.create(stubLength); + } + + if (!object && typeof property === "undefined") { + return sinon.stub.create(); + } + + if (typeof property === "undefined" && typeof object === "object") { + sinon.walk(object || {}, function (value, prop, propOwner) { + // we don't want to stub things like toString(), valueOf(), etc. so we only stub if the object + // is not Object.prototype + if ( + propOwner !== Object.prototype && + prop !== "constructor" && + typeof sinon.getPropertyDescriptor(propOwner, prop).value === "function" + ) { + stub(object, prop); + } + }); + + return object; + } + + return sinon.wrapMethod(object, property, wrapper); +} + + +/*eslint-disable no-use-before-define*/ +function getParentBehaviour(stubInstance) { + return (stubInstance.parent && getCurrentBehavior(stubInstance.parent)); +} + +function getDefaultBehavior(stubInstance) { + return stubInstance.defaultBehavior || + getParentBehaviour(stubInstance) || + sinon.behavior.create(stubInstance); +} + +function getCurrentBehavior(stubInstance) { + var behavior = stubInstance.behaviors[stubInstance.callCount - 1]; + return behavior && behavior.isPresent() ? behavior : getDefaultBehavior(stubInstance); +} +/*eslint-enable no-use-before-define*/ + +var uuid = 0; + +var proto = { + create: function create(stubLength) { + var functionStub = function () { + return getCurrentBehavior(functionStub).invoke(this, arguments); + }; + + functionStub.id = "stub#" + uuid++; + var orig = functionStub; + functionStub = sinon.spy.create(functionStub, stubLength); + functionStub.func = orig; + + sinon.extend(functionStub, stub); + functionStub.instantiateFake = sinon.stub.create; + functionStub.displayName = "stub"; + functionStub.toString = sinon.functionToString; + + functionStub.defaultBehavior = null; + functionStub.behaviors = []; + + return functionStub; + }, + + resetBehavior: function () { + var i; + + this.defaultBehavior = null; + this.behaviors = []; + + delete this.returnValue; + delete this.returnArgAt; + this.returnThis = false; + + if (this.fakes) { + for (i = 0; i < this.fakes.length; i++) { + this.fakes[i].resetBehavior(); + } + } + }, + + resetHistory: sinon.spy.reset, + + reset: function () { + this.resetHistory(); + this.resetBehavior(); + }, + + onCall: function onCall(index) { + if (!this.behaviors[index]) { + this.behaviors[index] = sinon.behavior.create(this); + } + + return this.behaviors[index]; + }, + + onFirstCall: function onFirstCall() { + return this.onCall(0); + }, + + onSecondCall: function onSecondCall() { + return this.onCall(1); + }, + + onThirdCall: function onThirdCall() { + return this.onCall(2); + } +}; + +function createBehavior(behaviorMethod) { + return function () { + this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this); + this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments); + return this; + }; +} + +for (var method in sinon.behavior) { + if (sinon.behavior.hasOwnProperty(method) && + !proto.hasOwnProperty(method) && + method !== "create" && + method !== "withArgs" && + method !== "invoke") { + proto[method] = createBehavior(method); + } +} + +sinon.extend(stub, proto); +sinon.stub = stub; +sinon.createStubInstance = function (constructor) { + if (typeof constructor !== "function") { + throw new TypeError("The constructor should be a function."); + } + return sinon.stub(sinon.create(constructor.prototype)); +}; + +},{"./behavior":3,"./extend":6,"./spy":12,"./util/core":25,"./walk":37}],14:[function(require,module,exports){ +/** + * Test function, sandboxes fakes + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +require("./sandbox"); +var sinon = require("./util/core"); + +var slice = Array.prototype.slice; + +function test(callback) { + var type = typeof callback; + + if (type !== "function") { + throw new TypeError("sinon.test needs to wrap a test function, got " + type); + } + + function sinonSandboxedTest() { + var config = sinon.getConfig(sinon.config); + config.injectInto = config.injectIntoThis && this || config.injectInto; + var sandbox = sinon.sandbox.create(config); + var args = slice.call(arguments); + var oldDone = args.length && args[args.length - 1]; + var exception, result; + + if (typeof oldDone === "function") { + args[args.length - 1] = function sinonDone(res) { + if (res) { + sandbox.restore(); + } else { + sandbox.verifyAndRestore(); + } + oldDone(res); + }; + } + + try { + result = callback.apply(this, args.concat(sandbox.args)); + } catch (e) { + exception = e; + } + + if (typeof oldDone !== "function") { + if (typeof exception !== "undefined") { + sandbox.restore(); + throw exception; + } else { + sandbox.verifyAndRestore(); + } + } + + return result; + } + + if (callback.length) { + return function sinonAsyncSandboxedTest(done) { // eslint-disable-line no-unused-vars + return sinonSandboxedTest.apply(this, arguments); + }; + } + + return sinonSandboxedTest; +} + +test.config = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true +}; + +sinon.test = test; + +},{"./sandbox":11,"./util/core":25}],15:[function(require,module,exports){ +/** + * Test case, sandboxes all test functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +require("./test"); +var sinon = require("./util/core"); + +function createTest(property, setUp, tearDown) { + return function () { + if (setUp) { + setUp.apply(this, arguments); + } + + var exception, result; + + try { + result = property.apply(this, arguments); + } catch (e) { + exception = e; + } + + if (tearDown) { + tearDown.apply(this, arguments); + } + + if (exception) { + throw exception; + } + + return result; + }; +} + +function testCase(tests, prefix) { + if (!tests || typeof tests !== "object") { + throw new TypeError("sinon.testCase needs an object with test functions"); + } + + prefix = prefix || "test"; + var rPrefix = new RegExp("^" + prefix); + var methods = {}; + var setUp = tests.setUp; + var tearDown = tests.tearDown; + var testName, + property, + method; + + for (testName in tests) { + if (tests.hasOwnProperty(testName) && !/^(setUp|tearDown)$/.test(testName)) { + property = tests[testName]; + + if (typeof property === "function" && rPrefix.test(testName)) { + method = property; + + if (setUp || tearDown) { + method = createTest(property, setUp, tearDown); + } + + methods[testName] = sinon.test(method); + } else { + methods[testName] = tests[testName]; + } + } + } + + return methods; +} + +sinon.testCase = testCase; + +},{"./test":14,"./util/core":25}],16:[function(require,module,exports){ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +"use strict"; + +module.exports = function typeOf(value) { + if (value === null) { + return "null"; + } else if (value === undefined) { + return "undefined"; + } + var string = Object.prototype.toString.call(value); + return string.substring(8, string.length - 1).toLowerCase(); +}; + +},{}],17:[function(require,module,exports){ +"use strict"; + +module.exports = function calledInOrder(spies) { + for (var i = 1, l = spies.length; i < l; i++) { + if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) { + return false; + } + } + + return true; +}; + +},{}],18:[function(require,module,exports){ +"use strict"; + +var Klass = function () {}; + +module.exports = function create(proto) { + Klass.prototype = proto; + return new Klass(); +}; + +},{}],19:[function(require,module,exports){ +"use strict"; + +var div = typeof document !== "undefined" && document.createElement("div"); + +function isReallyNaN(val) { + return val !== val; +} + +function isDOMNode(obj) { + var success = false; + + try { + obj.appendChild(div); + success = div.parentNode === obj; + } catch (e) { + return false; + } finally { + try { + obj.removeChild(div); + } catch (e) { + // Remove failed, not much we can do about that + } + } + + return success; +} + +function isElement(obj) { + return div && obj && obj.nodeType === 1 && isDOMNode(obj); +} + +var deepEqual = module.exports = function deepEqual(a, b) { + if (typeof a !== "object" || typeof b !== "object") { + return isReallyNaN(a) && isReallyNaN(b) || a === b; + } + + if (isElement(a) || isElement(b)) { + return a === b; + } + + if (a === b) { + return true; + } + + if ((a === null && b !== null) || (a !== null && b === null)) { + return false; + } + + if (a instanceof RegExp && b instanceof RegExp) { + return (a.source === b.source) && (a.global === b.global) && + (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline); + } + + var aString = Object.prototype.toString.call(a); + if (aString !== Object.prototype.toString.call(b)) { + return false; + } + + if (aString === "[object Date]") { + return a.valueOf() === b.valueOf(); + } + + var prop; + var aLength = 0; + var bLength = 0; + + if (aString === "[object Array]" && a.length !== b.length) { + return false; + } + + for (prop in a) { + if (a.hasOwnProperty(prop)) { + aLength += 1; + + if (!(prop in b)) { + return false; + } + + // allow alternative function for recursion + if (!(arguments[2] || deepEqual)(a[prop], b[prop])) { + return false; + } + } + } + + for (prop in b) { + if (b.hasOwnProperty(prop)) { + bLength += 1; + } + } + + return aLength === bLength; +}; + +deepEqual.use = function (match) { + return function deepEqual$matcher(a, b) { + if (match.isMatcher(a)) { + return a.test(b); + } + + return deepEqual(a, b, deepEqual$matcher); + }; +}; + +},{}],20:[function(require,module,exports){ +"use strict"; + +module.exports = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true +}; + +},{}],21:[function(require,module,exports){ +"use strict"; + +module.exports = function functionName(func) { + var name = func.displayName || func.name; + var matches; + + // Use function decomposition as a last resort to get function + // name. Does not rely on function decomposition to work - if it + // doesn't debugging will be slightly less informative + // (i.e. toString will say 'spy' rather than 'myFunc'). + if (!name && (matches = func.toString().match(/function ([^\s\(]+)/))) { + name = matches[1]; + } + + return name; +}; + + +},{}],22:[function(require,module,exports){ +"use strict"; + +module.exports = function toString() { + var i, prop, thisValue; + if (this.getCall && this.callCount) { + i = this.callCount; + + while (i--) { + thisValue = this.getCall(i).thisValue; + + for (prop in thisValue) { + if (thisValue[prop] === this) { + return prop; + } + } + } + } + + return this.displayName || "sinon fake"; +}; + +},{}],23:[function(require,module,exports){ +"use strict"; + +var defaultConfig = require("./default-config"); + +module.exports = function getConfig(custom) { + var config = {}; + var prop; + + custom = custom || {}; + + for (prop in defaultConfig) { + if (defaultConfig.hasOwnProperty(prop)) { + config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaultConfig[prop]; + } + } + + return config; +}; + +},{"./default-config":20}],24:[function(require,module,exports){ +"use strict"; + +module.exports = function getPropertyDescriptor(object, property) { + var proto = object; + var descriptor; + + while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) { + proto = Object.getPrototypeOf(proto); + } + return descriptor; +}; + +},{}],25:[function(require,module,exports){ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +exports.wrapMethod = require("./wrap-method"); + +exports.create = require("./create"); + +exports.deepEqual = require("./deep-equal"); + +exports.functionName = require("./function-name"); + +exports.functionToString = require("./function-to-string"); + +exports.objectKeys = require("./object-keys"); + +exports.getPropertyDescriptor = require("./get-property-descriptor"); + +exports.getConfig = require("./get-config"); + +exports.defaultConfig = require("./default-config"); + +exports.timesInWords = require("./times-in-words"); + +exports.calledInOrder = require("./called-in-order"); + +exports.orderByFirstCall = require("./order-by-first-call"); + +exports.restore = require("./restore"); + +},{"./called-in-order":17,"./create":18,"./deep-equal":19,"./default-config":20,"./function-name":21,"./function-to-string":22,"./get-config":23,"./get-property-descriptor":24,"./object-keys":26,"./order-by-first-call":27,"./restore":28,"./times-in-words":29,"./wrap-method":30}],26:[function(require,module,exports){ +"use strict"; + +var hasOwn = Object.prototype.hasOwnProperty; + +module.exports = function objectKeys(obj) { + if (obj !== Object(obj)) { + throw new TypeError("sinon.objectKeys called on a non-object"); + } + + var keys = []; + var key; + for (key in obj) { + if (hasOwn.call(obj, key)) { + keys.push(key); + } + } + + return keys; +}; + +},{}],27:[function(require,module,exports){ +"use strict"; + +module.exports = function orderByFirstCall(spies) { + return spies.sort(function (a, b) { + // uuid, won't ever be equal + var aCall = a.getCall(0); + var bCall = b.getCall(0); + var aId = aCall && aCall.callId || -1; + var bId = bCall && bCall.callId || -1; + + return aId < bId ? -1 : 1; + }); +}; + +},{}],28:[function(require,module,exports){ +"use strict"; + +function isRestorable(obj) { + return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon; +} + +module.exports = function restore(object) { + if (object !== null && typeof object === "object") { + for (var prop in object) { + if (isRestorable(object[prop])) { + object[prop].restore(); + } + } + } else if (isRestorable(object)) { + object.restore(); + } +}; + +},{}],29:[function(require,module,exports){ +"use strict"; + +var array = [null, "once", "twice", "thrice"]; + +module.exports = function timesInWords(count) { + return array[count] || (count || 0) + " times"; +}; + +},{}],30:[function(require,module,exports){ +"use strict"; + +var getPropertyDescriptor = require("./get-property-descriptor"); +var objectKeys = require("./object-keys"); + +var hasOwn = Object.prototype.hasOwnProperty; + +function isFunction(obj) { + return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply); +} + +function mirrorProperties(target, source) { + for (var prop in source) { + if (!hasOwn.call(target, prop)) { + target[prop] = source[prop]; + } + } +} + +// Cheap way to detect if we have ES5 support. +var hasES5Support = "keys" in Object; + +module.exports = function wrapMethod(object, property, method) { + if (!object) { + throw new TypeError("Should wrap property of object"); + } + + if (typeof method !== "function" && typeof method !== "object") { + throw new TypeError("Method wrapper should be a function or a property descriptor"); + } + + function checkWrappedMethod(wrappedMethod) { + var error; + + if (!isFunction(wrappedMethod)) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } else if (wrappedMethod.calledBefore) { + var verb = wrappedMethod.returns ? "stubbed" : "spied on"; + error = new TypeError("Attempted to wrap " + property + " which is already " + verb); + } + + if (error) { + if (wrappedMethod && wrappedMethod.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethod.stackTrace; + } + throw error; + } + } + + var error, wrappedMethod, i; + + // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem + // when using hasOwn.call on objects from other frames. + var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property); + + if (hasES5Support) { + var methodDesc = (typeof method === "function") ? {value: method} : method; + var wrappedMethodDesc = getPropertyDescriptor(object, property); + + if (!wrappedMethodDesc) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } + if (error) { + if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace; + } + throw error; + } + + var types = objectKeys(methodDesc); + for (i = 0; i < types.length; i++) { + wrappedMethod = wrappedMethodDesc[types[i]]; + checkWrappedMethod(wrappedMethod); + } + + mirrorProperties(methodDesc, wrappedMethodDesc); + for (i = 0; i < types.length; i++) { + mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]); + } + Object.defineProperty(object, property, methodDesc); + } else { + wrappedMethod = object[property]; + checkWrappedMethod(wrappedMethod); + object[property] = method; + method.displayName = property; + } + + method.displayName = property; + + // Set up a stack trace which can be used later to find what line of + // code the original method was created on. + method.stackTrace = (new Error("Stack Trace for original")).stack; + + method.restore = function () { + // For prototype properties try to reset by delete first. + // If this fails (ex: localStorage on mobile safari) then force a reset + // via direct assignment. + if (!owned) { + // In some cases `delete` may throw an error + try { + delete object[property]; + } catch (e) {} // eslint-disable-line no-empty + // For native code functions `delete` fails without throwing an error + // on Chrome < 43, PhantomJS, etc. + } else if (hasES5Support) { + Object.defineProperty(object, property, wrappedMethodDesc); + } + + // Use strict equality comparison to check failures then force a reset + // via direct assignment. + if (object[property] === method) { + object[property] = wrappedMethod; + } + }; + + method.restore.sinon = true; + + if (!hasES5Support) { + mirrorProperties(method, wrappedMethod); + } + + return method; +}; + +},{"./get-property-descriptor":24,"./object-keys":26}],31:[function(require,module,exports){ +/** + * Minimal Event interface implementation + * + * Original implementation by Sven Fuchs: https://gist.github.com/995028 + * Modifications and tests by Christian Johansen. + * + * @author Sven Fuchs (svenfuchs@artweb-design.de) + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2011 Sven Fuchs, Christian Johansen + */ +"use strict"; + +var push = [].push; +var sinon = require("./core"); + +sinon.Event = function Event(type, bubbles, cancelable, target) { + this.initEvent(type, bubbles, cancelable, target); +}; + +sinon.Event.prototype = { + initEvent: function (type, bubbles, cancelable, target) { + this.type = type; + this.bubbles = bubbles; + this.cancelable = cancelable; + this.target = target; + }, + + stopPropagation: function () {}, + + preventDefault: function () { + this.defaultPrevented = true; + } +}; + +sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) { + this.initEvent(type, false, false, target); + this.loaded = typeof progressEventRaw.loaded === "number" ? progressEventRaw.loaded : null; + this.total = typeof progressEventRaw.total === "number" ? progressEventRaw.total : null; + this.lengthComputable = !!progressEventRaw.total; +}; + +sinon.ProgressEvent.prototype = new sinon.Event(); + +sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent; + +sinon.CustomEvent = function CustomEvent(type, customData, target) { + this.initEvent(type, false, false, target); + this.detail = customData.detail || null; +}; + +sinon.CustomEvent.prototype = new sinon.Event(); + +sinon.CustomEvent.prototype.constructor = sinon.CustomEvent; + +sinon.EventTarget = { + addEventListener: function addEventListener(event, listener) { + this.eventListeners = this.eventListeners || {}; + this.eventListeners[event] = this.eventListeners[event] || []; + push.call(this.eventListeners[event], listener); + }, + + removeEventListener: function removeEventListener(event, listener) { + var listeners = this.eventListeners && this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }, + + dispatchEvent: function dispatchEvent(event) { + var type = event.type; + var listeners = this.eventListeners && this.eventListeners[type] || []; + + for (var i = 0; i < listeners.length; i++) { + if (typeof listeners[i] === "function") { + listeners[i].call(this, event); + } else { + listeners[i].handleEvent(event); + } + } + + return !!event.defaultPrevented; + } +}; + +},{"./core":25}],32:[function(require,module,exports){ +/** + * The Sinon "server" mimics a web server that receives requests from + * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, + * both synchronously and asynchronously. To respond synchronuously, canned + * answers have to be provided upfront. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +require("./fake_xdomain_request"); +require("./fake_xml_http_request"); +require("../format"); +require("../log_error"); + +var push = [].push; +var sinon = require("./core"); + +function responseArray(handler) { + var response = handler; + + if (Object.prototype.toString.call(handler) !== "[object Array]") { + response = [200, {}, handler]; + } + + if (typeof response[2] !== "string") { + throw new TypeError("Fake server response body should be string, but was " + + typeof response[2]); + } + + return response; +} + +var wloc = typeof window !== "undefined" ? window.location : {}; +var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host); + +function matchOne(response, reqMethod, reqUrl) { + var rmeth = response.method; + var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase(); + var url = response.url; + var matchUrl = !url || url === reqUrl || (typeof url.test === "function" && url.test(reqUrl)); + + return matchMethod && matchUrl; +} + +function match(response, request) { + var requestUrl = request.url; + + if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { + requestUrl = requestUrl.replace(rCurrLoc, ""); + } + + if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { + if (typeof response.response === "function") { + var ru = response.url; + var args = [request].concat(ru && typeof ru.exec === "function" ? ru.exec(requestUrl).slice(1) : []); + return response.response.apply(response, args); + } + + return true; + } + + return false; +} + +sinon.fakeServer = { + create: function (config) { + var server = sinon.create(this); + server.configure(config); + if (!sinon.xhr.supportsCORS) { + this.xhr = sinon.useFakeXDomainRequest(); + } else { + this.xhr = sinon.useFakeXMLHttpRequest(); + } + server.requests = []; + + this.xhr.onCreate = function (xhrObj) { + server.addRequest(xhrObj); + }; + + return server; + }, + configure: function (config) { + var whitelist = { + "autoRespond": true, + "autoRespondAfter": true, + "respondImmediately": true, + "fakeHTTPMethods": true + }; + var setting; + + config = config || {}; + for (setting in config) { + if (whitelist.hasOwnProperty(setting) && config.hasOwnProperty(setting)) { + this[setting] = config[setting]; + } + } + }, + addRequest: function addRequest(xhrObj) { + var server = this; + push.call(this.requests, xhrObj); + + xhrObj.onSend = function () { + server.handleRequest(this); + + if (server.respondImmediately) { + server.respond(); + } else if (server.autoRespond && !server.responding) { + setTimeout(function () { + server.responding = false; + server.respond(); + }, server.autoRespondAfter || 10); + + server.responding = true; + } + }; + }, + + getHTTPMethod: function getHTTPMethod(request) { + if (this.fakeHTTPMethods && /post/i.test(request.method)) { + var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); + return matches ? matches[1] : request.method; + } + + return request.method; + }, + + handleRequest: function handleRequest(xhr) { + if (xhr.async) { + if (!this.queue) { + this.queue = []; + } + + push.call(this.queue, xhr); + } else { + this.processRequest(xhr); + } + }, + + log: function log(response, request) { + var str; + + str = "Request:\n" + sinon.format(request) + "\n\n"; + str += "Response:\n" + sinon.format(response) + "\n\n"; + + sinon.log(str); + }, + + respondWith: function respondWith(method, url, body) { + if (arguments.length === 1 && typeof method !== "function") { + this.response = responseArray(method); + return; + } + + if (!this.responses) { + this.responses = []; + } + + if (arguments.length === 1) { + body = method; + url = method = null; + } + + if (arguments.length === 2) { + body = url; + url = method; + method = null; + } + + push.call(this.responses, { + method: method, + url: url, + response: typeof body === "function" ? body : responseArray(body) + }); + }, + + respond: function respond() { + if (arguments.length > 0) { + this.respondWith.apply(this, arguments); + } + + var queue = this.queue || []; + var requests = queue.splice(0, queue.length); + + for (var i = 0; i < requests.length; i++) { + this.processRequest(requests[i]); + } + }, + + processRequest: function processRequest(request) { + try { + if (request.aborted) { + return; + } + + var response = this.response || [404, {}, ""]; + + if (this.responses) { + for (var l = this.responses.length, i = l - 1; i >= 0; i--) { + if (match.call(this, this.responses[i], request)) { + response = this.responses[i].response; + break; + } + } + } + + if (request.readyState !== 4) { + this.log(response, request); + + request.respond(response[0], response[1], response[2]); + } + } catch (e) { + sinon.logError("Fake server request processing", e); + } + }, + + restore: function restore() { + return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); + } +}; + +},{"../format":7,"../log_error":8,"./core":25,"./fake_xdomain_request":35,"./fake_xml_http_request":36}],33:[function(require,module,exports){ +/** + * Add-on for sinon.fakeServer that automatically handles a fake timer along with + * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery + * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, + * it polls the object for completion with setInterval. Dispite the direct + * motivation, there is nothing jQuery-specific in this file, so it can be used + * in any environment where the ajax implementation depends on setInterval or + * setTimeout. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +require("./fake_server"); +require("./fake_timers"); +var sinon = require("./core"); + +function Server() {} +Server.prototype = sinon.fakeServer; + +sinon.fakeServerWithClock = new Server(); + +sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { + if (xhr.async) { + if (typeof setTimeout.clock === "object") { + this.clock = setTimeout.clock; + } else { + this.clock = sinon.useFakeTimers(); + this.resetClock = true; + } + + if (!this.longestTimeout) { + var clockSetTimeout = this.clock.setTimeout; + var clockSetInterval = this.clock.setInterval; + var server = this; + + this.clock.setTimeout = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetTimeout.apply(this, arguments); + }; + + this.clock.setInterval = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetInterval.apply(this, arguments); + }; + } + } + + return sinon.fakeServer.addRequest.call(this, xhr); +}; + +sinon.fakeServerWithClock.respond = function respond() { + var returnVal = sinon.fakeServer.respond.apply(this, arguments); + + if (this.clock) { + this.clock.tick(this.longestTimeout || 0); + this.longestTimeout = 0; + + if (this.resetClock) { + this.clock.restore(); + this.resetClock = false; + } + } + + return returnVal; +}; + +sinon.fakeServerWithClock.restore = function restore() { + if (this.clock) { + this.clock.restore(); + } + + return sinon.fakeServer.restore.apply(this, arguments); +}; + +},{"./core":25,"./fake_server":32,"./fake_timers":34}],34:[function(require,module,exports){ +/** + * Fake timer API + * setTimeout + * setInterval + * clearTimeout + * clearInterval + * tick + * reset + * Date + * + * Inspired by jsUnitMockTimeOut from JsUnit + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +var s = require("./core"); +var llx = require("lolex"); + +s.useFakeTimers = function () { + var now; + var methods = Array.prototype.slice.call(arguments); + + if (typeof methods[0] === "string") { + now = 0; + } else { + now = methods.shift(); + } + + var clock = llx.install(now || 0, methods); + clock.restore = clock.uninstall; + return clock; +}; + +s.clock = { + create: function (now) { + return llx.createClock(now); + } +}; + +s.timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), + clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined), + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date +}; + +},{"./core":25,"lolex":40}],35:[function(require,module,exports){ +(function (global){ +/** + * Fake XDomainRequest object + */ + +"use strict"; + +require("../extend"); +require("./event"); +require("../log_error"); + +var xdr = { XDomainRequest: global.XDomainRequest }; +xdr.GlobalXDomainRequest = global.XDomainRequest; +xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== "undefined"; +xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false; + +var sinon = require("./core"); +sinon.xdr = xdr; + +function FakeXDomainRequest() { + this.readyState = FakeXDomainRequest.UNSENT; + this.requestBody = null; + this.requestHeaders = {}; + this.status = 0; + this.timeout = null; + + if (typeof FakeXDomainRequest.onCreate === "function") { + FakeXDomainRequest.onCreate(this); + } +} + +function verifyState(x) { + if (x.readyState !== FakeXDomainRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (x.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } +} + +function verifyRequestSent(x) { + if (x.readyState === FakeXDomainRequest.UNSENT) { + throw new Error("Request not sent"); + } + if (x.readyState === FakeXDomainRequest.DONE) { + throw new Error("Request done"); + } +} + +function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XDomainRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } +} + +sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, { + open: function open(method, url) { + this.method = method; + this.url = url; + + this.responseText = null; + this.sendFlag = false; + + this.readyStateChange(FakeXDomainRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + var eventName = ""; + switch (this.readyState) { + case FakeXDomainRequest.UNSENT: + break; + case FakeXDomainRequest.OPENED: + break; + case FakeXDomainRequest.LOADING: + if (this.sendFlag) { + //raise the progress event + eventName = "onprogress"; + } + break; + case FakeXDomainRequest.DONE: + if (this.isTimeout) { + eventName = "ontimeout"; + } else if (this.errorFlag || (this.status < 200 || this.status > 299)) { + eventName = "onerror"; + } else { + eventName = "onload"; + } + break; + } + + // raising event (if defined) + if (eventName) { + if (typeof this[eventName] === "function") { + try { + this[eventName](); + } catch (e) { + sinon.logError("Fake XHR " + eventName + " handler", e); + } + } + } + }, + + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + this.requestBody = data; + } + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + + this.errorFlag = false; + this.sendFlag = true; + this.readyStateChange(FakeXDomainRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + + if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) { + this.readyStateChange(sinon.FakeXDomainRequest.DONE); + this.sendFlag = false; + } + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyResponseBodyType(body); + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + this.readyStateChange(FakeXDomainRequest.LOADING); + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + this.readyStateChange(FakeXDomainRequest.DONE); + }, + + respond: function respond(status, contentType, body) { + // content-type ignored, since XDomainRequest does not carry this + // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease + // test integration across browsers + this.status = typeof status === "number" ? status : 200; + this.setResponseBody(body || ""); + }, + + simulatetimeout: function simulatetimeout() { + this.status = 0; + this.isTimeout = true; + // Access to this should actually throw an error + this.responseText = undefined; + this.readyStateChange(FakeXDomainRequest.DONE); + } +}); + +sinon.extend(FakeXDomainRequest, { + UNSENT: 0, + OPENED: 1, + LOADING: 3, + DONE: 4 +}); + +sinon.useFakeXDomainRequest = function useFakeXDomainRequest() { + sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) { + if (xdr.supportsXDR) { + global.XDomainRequest = xdr.GlobalXDomainRequest; + } + + delete sinon.FakeXDomainRequest.restore; + + if (keepOnCreate !== true) { + delete sinon.FakeXDomainRequest.onCreate; + } + }; + if (xdr.supportsXDR) { + global.XDomainRequest = sinon.FakeXDomainRequest; + } + return sinon.FakeXDomainRequest; +}; + +sinon.FakeXDomainRequest = FakeXDomainRequest; + + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../extend":6,"../log_error":8,"./core":25,"./event":31}],36:[function(require,module,exports){ +(function (global){ +/** + * Fake XMLHttpRequest object + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +require("../extend"); +require("./event"); +require("../log_error"); + +var sinon = require("./core"); + +function getWorkingXHR(globalScope) { + var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined"; + if (supportsXHR) { + return globalScope.XMLHttpRequest; + } + + var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined"; + if (supportsActiveX) { + return function () { + return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0"); + }; + } + + return false; +} + +var supportsProgress = typeof ProgressEvent !== "undefined"; +var supportsCustomEvent = typeof CustomEvent !== "undefined"; +var supportsFormData = typeof FormData !== "undefined"; +var supportsArrayBuffer = typeof ArrayBuffer !== "undefined"; +var supportsBlob = typeof Blob === "function"; +var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest }; +sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest; +sinonXhr.GlobalActiveXObject = global.ActiveXObject; +sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== "undefined"; +sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined"; +sinonXhr.workingXHR = getWorkingXHR(global); +sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest()); + +var unsafeHeaders = { + "Accept-Charset": true, + "Accept-Encoding": true, + Connection: true, + "Content-Length": true, + Cookie: true, + Cookie2: true, + "Content-Transfer-Encoding": true, + Date: true, + Expect: true, + Host: true, + "Keep-Alive": true, + Referer: true, + TE: true, + Trailer: true, + "Transfer-Encoding": true, + Upgrade: true, + "User-Agent": true, + Via: true +}; + +// An upload object is created for each +// FakeXMLHttpRequest and allows upload +// events to be simulated using uploadProgress +// and uploadError. +function UploadProgress() { + this.eventListeners = { + abort: [], + error: [], + load: [], + loadend: [], + progress: [] + }; +} + +UploadProgress.prototype.addEventListener = function addEventListener(event, listener) { + this.eventListeners[event].push(listener); +}; + +UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) { + var listeners = this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } +}; + +UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) { + var listeners = this.eventListeners[event.type] || []; + + for (var i = 0, listener; (listener = listeners[i]) != null; i++) { + listener(event); + } +}; + +// Note that for FakeXMLHttpRequest to work pre ES5 +// we lose some of the alignment with the spec. +// To ensure as close a match as possible, +// set responseType before calling open, send or respond; +function FakeXMLHttpRequest() { + this.readyState = FakeXMLHttpRequest.UNSENT; + this.requestHeaders = {}; + this.requestBody = null; + this.status = 0; + this.statusText = ""; + this.upload = new UploadProgress(); + this.responseType = ""; + this.response = ""; + if (sinonXhr.supportsCORS) { + this.withCredentials = false; + } + + var xhr = this; + var events = ["loadstart", "load", "abort", "loadend"]; + + function addEventListener(eventName) { + xhr.addEventListener(eventName, function (event) { + var listener = xhr["on" + eventName]; + + if (listener && typeof listener === "function") { + listener.call(this, event); + } + }); + } + + for (var i = events.length - 1; i >= 0; i--) { + addEventListener(events[i]); + } + + if (typeof FakeXMLHttpRequest.onCreate === "function") { + FakeXMLHttpRequest.onCreate(this); + } +} + +function verifyState(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xhr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } +} + +function getHeader(headers, header) { + header = header.toLowerCase(); + + for (var h in headers) { + if (h.toLowerCase() === header) { + return h; + } + } + + return null; +} + +// filtering to enable a white-list version of Sinon FakeXhr, +// where whitelisted requests are passed through to real XHR +function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } +} +function some(collection, callback) { + for (var index = 0; index < collection.length; index++) { + if (callback(collection[index]) === true) { + return true; + } + } + return false; +} +// largest arity in XHR is 5 - XHR#open +var apply = function (obj, method, args) { + switch (args.length) { + case 0: return obj[method](); + case 1: return obj[method](args[0]); + case 2: return obj[method](args[0], args[1]); + case 3: return obj[method](args[0], args[1], args[2]); + case 4: return obj[method](args[0], args[1], args[2], args[3]); + case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]); + } +}; + +FakeXMLHttpRequest.filters = []; +FakeXMLHttpRequest.addFilter = function addFilter(fn) { + this.filters.push(fn); +}; +var IE6Re = /MSIE 6/; +FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) { + var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap + + each([ + "open", + "setRequestHeader", + "send", + "abort", + "getResponseHeader", + "getAllResponseHeaders", + "addEventListener", + "overrideMimeType", + "removeEventListener" + ], function (method) { + fakeXhr[method] = function () { + return apply(xhr, method, arguments); + }; + }); + + var copyAttrs = function (args) { + each(args, function (attr) { + try { + fakeXhr[attr] = xhr[attr]; + } catch (e) { + if (!IE6Re.test(navigator.userAgent)) { + throw e; + } + } + }); + }; + + var stateChange = function stateChange() { + fakeXhr.readyState = xhr.readyState; + if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { + copyAttrs(["status", "statusText"]); + } + if (xhr.readyState >= FakeXMLHttpRequest.LOADING) { + copyAttrs(["responseText", "response"]); + } + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + copyAttrs(["responseXML"]); + } + if (fakeXhr.onreadystatechange) { + fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr }); + } + }; + + if (xhr.addEventListener) { + for (var event in fakeXhr.eventListeners) { + if (fakeXhr.eventListeners.hasOwnProperty(event)) { + + /*eslint-disable no-loop-func*/ + each(fakeXhr.eventListeners[event], function (handler) { + xhr.addEventListener(event, handler); + }); + /*eslint-enable no-loop-func*/ + } + } + xhr.addEventListener("readystatechange", stateChange); + } else { + xhr.onreadystatechange = stateChange; + } + apply(xhr, "open", xhrArgs); +}; +FakeXMLHttpRequest.useFilters = false; + +function verifyRequestOpened(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR - " + xhr.readyState); + } +} + +function verifyRequestSent(xhr) { + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + throw new Error("Request done"); + } +} + +function verifyHeadersReceived(xhr) { + if (xhr.async && xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) { + throw new Error("No headers received"); + } +} + +function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XMLHttpRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } +} + +function convertToArrayBuffer(body) { + var buffer = new ArrayBuffer(body.length); + var view = new Uint8Array(buffer); + for (var i = 0; i < body.length; i++) { + var charCode = body.charCodeAt(i); + if (charCode >= 256) { + throw new TypeError("arraybuffer or blob responseTypes require binary string, " + + "invalid character " + body[i] + " found."); + } + view[i] = charCode; + } + return buffer; +} + +function isXmlContentType(contentType) { + return !contentType || /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType); +} + +function convertResponseBody(responseType, contentType, body) { + if (responseType === "" || responseType === "text") { + return body; + } else if (supportsArrayBuffer && responseType === "arraybuffer") { + return convertToArrayBuffer(body); + } else if (responseType === "json") { + try { + return JSON.parse(body); + } catch (e) { + // Return parsing failure as null + return null; + } + } else if (supportsBlob && responseType === "blob") { + var blobOptions = {}; + if (contentType) { + blobOptions.type = contentType; + } + return new Blob([convertToArrayBuffer(body)], blobOptions); + } else if (responseType === "document") { + if (isXmlContentType(contentType)) { + return FakeXMLHttpRequest.parseXML(body); + } + return null; + } + throw new Error("Invalid responseType " + responseType); +} + +function clearResponse(xhr) { + if (xhr.responseType === "" || xhr.responseType === "text") { + xhr.response = xhr.responseText = ""; + } else { + xhr.response = xhr.responseText = null; + } + xhr.responseXML = null; +} + +FakeXMLHttpRequest.parseXML = function parseXML(text) { + // Treat empty string as parsing failure + if (text !== "") { + try { + if (typeof DOMParser !== "undefined") { + var parser = new DOMParser(); + return parser.parseFromString(text, "text/xml"); + } + var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(text); + return xmlDoc; + } catch (e) { + // Unable to parse XML - no biggie + } + } + + return null; +}; + +FakeXMLHttpRequest.statusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 300: "Multiple Choice", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 422: "Unprocessable Entity", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" +}; + +sinon.xhr = sinonXhr; + +sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { + async: true, + + open: function open(method, url, async, username, password) { + this.method = method; + this.url = url; + this.async = typeof async === "boolean" ? async : true; + this.username = username; + this.password = password; + clearResponse(this); + this.requestHeaders = {}; + this.sendFlag = false; + + if (FakeXMLHttpRequest.useFilters === true) { + var xhrArgs = arguments; + var defake = some(FakeXMLHttpRequest.filters, function (filter) { + return filter.apply(this, xhrArgs); + }); + if (defake) { + return FakeXMLHttpRequest.defake(this, arguments); + } + } + this.readyStateChange(FakeXMLHttpRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + + var readyStateChangeEvent = new sinon.Event("readystatechange", false, false, this); + var event, progress; + + if (typeof this.onreadystatechange === "function") { + try { + this.onreadystatechange(readyStateChangeEvent); + } catch (e) { + sinon.logError("Fake XHR onreadystatechange handler", e); + } + } + + if (this.readyState === FakeXMLHttpRequest.DONE) { + if (this.status < 200 || this.status > 299) { + progress = {loaded: 0, total: 0}; + event = this.aborted ? "abort" : "error"; + } + else { + progress = {loaded: 100, total: 100}; + event = "load"; + } + + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progress, this)); + this.upload.dispatchEvent(new sinon.ProgressEvent(event, progress, this)); + this.upload.dispatchEvent(new sinon.ProgressEvent("loadend", progress, this)); + } + + this.dispatchEvent(new sinon.ProgressEvent("progress", progress, this)); + this.dispatchEvent(new sinon.ProgressEvent(event, progress, this)); + this.dispatchEvent(new sinon.ProgressEvent("loadend", progress, this)); + } + + this.dispatchEvent(readyStateChangeEvent); + }, + + setRequestHeader: function setRequestHeader(header, value) { + verifyState(this); + + if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { + throw new Error("Refused to set unsafe header \"" + header + "\""); + } + + if (this.requestHeaders[header]) { + this.requestHeaders[header] += "," + value; + } else { + this.requestHeaders[header] = value; + } + }, + + // Helps testing + setResponseHeaders: function setResponseHeaders(headers) { + verifyRequestOpened(this); + this.responseHeaders = {}; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + this.responseHeaders[header] = headers[header]; + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); + } else { + this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; + } + }, + + // Currently treats ALL data as a DOMString (i.e. no Document) + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + var contentType = getHeader(this.requestHeaders, "Content-Type"); + if (this.requestHeaders[contentType]) { + var value = this.requestHeaders[contentType].split(";"); + this.requestHeaders[contentType] = value[0] + ";charset=utf-8"; + } else if (supportsFormData && !(data instanceof FormData)) { + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + } + + this.requestBody = data; + } + + this.errorFlag = false; + this.sendFlag = this.async; + clearResponse(this); + this.readyStateChange(FakeXMLHttpRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + + this.dispatchEvent(new sinon.Event("loadstart", false, false, this)); + }, + + abort: function abort() { + this.aborted = true; + clearResponse(this); + this.errorFlag = true; + this.requestHeaders = {}; + this.responseHeaders = {}; + + if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) { + this.readyStateChange(FakeXMLHttpRequest.DONE); + this.sendFlag = false; + } + + this.readyState = FakeXMLHttpRequest.UNSENT; + }, + + getResponseHeader: function getResponseHeader(header) { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return null; + } + + if (/^Set-Cookie2?$/i.test(header)) { + return null; + } + + header = getHeader(this.responseHeaders, header); + + return this.responseHeaders[header] || null; + }, + + getAllResponseHeaders: function getAllResponseHeaders() { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return ""; + } + + var headers = ""; + + for (var header in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(header) && + !/^Set-Cookie2?$/i.test(header)) { + headers += header + ": " + this.responseHeaders[header] + "\r\n"; + } + } + + return headers; + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyHeadersReceived(this); + verifyResponseBodyType(body); + var contentType = this.getResponseHeader("Content-Type"); + + var isTextResponse = this.responseType === "" || this.responseType === "text"; + clearResponse(this); + if (this.async) { + var chunkSize = this.chunkSize || 10; + var index = 0; + + do { + this.readyStateChange(FakeXMLHttpRequest.LOADING); + + if (isTextResponse) { + this.responseText = this.response += body.substring(index, index + chunkSize); + } + index += chunkSize; + } while (index < body.length); + } + + this.response = convertResponseBody(this.responseType, contentType, body); + if (isTextResponse) { + this.responseText = this.response; + } + + if (this.responseType === "document") { + this.responseXML = this.response; + } else if (this.responseType === "" && isXmlContentType(contentType)) { + this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); + } + this.readyStateChange(FakeXMLHttpRequest.DONE); + }, + + respond: function respond(status, headers, body) { + this.status = typeof status === "number" ? status : 200; + this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; + this.setResponseHeaders(headers || {}); + this.setResponseBody(body || ""); + }, + + uploadProgress: function uploadProgress(progressEventRaw) { + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + downloadProgress: function downloadProgress(progressEventRaw) { + if (supportsProgress) { + this.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + uploadError: function uploadError(error) { + if (supportsCustomEvent) { + this.upload.dispatchEvent(new sinon.CustomEvent("error", {detail: error})); + } + } +}); + +sinon.extend(FakeXMLHttpRequest, { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 +}); + +sinon.useFakeXMLHttpRequest = function () { + FakeXMLHttpRequest.restore = function restore(keepOnCreate) { + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = sinonXhr.GlobalActiveXObject; + } + + delete FakeXMLHttpRequest.restore; + + if (keepOnCreate !== true) { + delete FakeXMLHttpRequest.onCreate; + } + }; + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = FakeXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = function ActiveXObject(objId) { + if (objId === "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { + + return new FakeXMLHttpRequest(); + } + + return new sinonXhr.GlobalActiveXObject(objId); + }; + } + + return FakeXMLHttpRequest; +}; + +sinon.FakeXMLHttpRequest = FakeXMLHttpRequest; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../extend":6,"../log_error":8,"./core":25,"./event":31}],37:[function(require,module,exports){ +"use strict"; + +var sinon = require("./util/core"); + +function walkInternal(obj, iterator, context, originalObj, seen) { + var proto, prop; + + if (typeof Object.getOwnPropertyNames !== "function") { + // We explicitly want to enumerate through all of the prototype's properties + // in this case, therefore we deliberately leave out an own property check. + /* eslint-disable guard-for-in */ + for (prop in obj) { + iterator.call(context, obj[prop], prop, obj); + } + /* eslint-enable guard-for-in */ + + return; + } + + Object.getOwnPropertyNames(obj).forEach(function (k) { + if (!seen[k]) { + seen[k] = true; + var target = typeof Object.getOwnPropertyDescriptor(obj, k).get === "function" ? + originalObj : obj; + iterator.call(context, target[k], k, target); + } + }); + + proto = Object.getPrototypeOf(obj); + if (proto) { + walkInternal(proto, iterator, context, originalObj, seen); + } +} + +/* Public: walks the prototype chain of an object and iterates over every own property + * name encountered. The iterator is called in the same fashion that Array.prototype.forEach + * works, where it is passed the value, key, and own object as the 1st, 2nd, and 3rd positional + * argument, respectively. In cases where Object.getOwnPropertyNames is not available, walk will + * default to using a simple for..in loop. + * + * obj - The object to walk the prototype chain for. + * iterator - The function to be called on each pass of the walk. + * context - (Optional) When given, the iterator will be called with this object as the receiver. + */ +function walk(obj, iterator, context) { + return walkInternal(obj, iterator, context, obj, {}); +} + +sinon.walk = walk; + +},{"./util/core":25}],38:[function(require,module,exports){ +// shim for using process in browser + +var process = module.exports = {}; +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = setTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + clearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + setTimeout(drainQueue, 0); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],39:[function(require,module,exports){ +(function (global){ +((typeof define === "function" && define.amd && function (m) { + define("formatio", ["samsam"], m); +}) || (typeof module === "object" && function (m) { + module.exports = m(require("samsam")); +}) || function (m) { this.formatio = m(this.samsam); } +)(function (samsam) { + "use strict"; + + var formatio = { + excludeConstructors: ["Object", /^.$/], + quoteStrings: true, + limitChildrenCount: 0 + }; + + var hasOwn = Object.prototype.hasOwnProperty; + + var specialObjects = []; + if (typeof global !== "undefined") { + specialObjects.push({ object: global, value: "[object global]" }); + } + if (typeof document !== "undefined") { + specialObjects.push({ + object: document, + value: "[object HTMLDocument]" + }); + } + if (typeof window !== "undefined") { + specialObjects.push({ object: window, value: "[object Window]" }); + } + + function functionName(func) { + if (!func) { return ""; } + if (func.displayName) { return func.displayName; } + if (func.name) { return func.name; } + var matches = func.toString().match(/function\s+([^\(]+)/m); + return (matches && matches[1]) || ""; + } + + function constructorName(f, object) { + var name = functionName(object && object.constructor); + var excludes = f.excludeConstructors || + formatio.excludeConstructors || []; + + var i, l; + for (i = 0, l = excludes.length; i < l; ++i) { + if (typeof excludes[i] === "string" && excludes[i] === name) { + return ""; + } else if (excludes[i].test && excludes[i].test(name)) { + return ""; + } + } + + return name; + } + + function isCircular(object, objects) { + if (typeof object !== "object") { return false; } + var i, l; + for (i = 0, l = objects.length; i < l; ++i) { + if (objects[i] === object) { return true; } + } + return false; + } + + function ascii(f, object, processed, indent) { + if (typeof object === "string") { + var qs = f.quoteStrings; + var quote = typeof qs !== "boolean" || qs; + return processed || quote ? '"' + object + '"' : object; + } + + if (typeof object === "function" && !(object instanceof RegExp)) { + return ascii.func(object); + } + + processed = processed || []; + + if (isCircular(object, processed)) { return "[Circular]"; } + + if (Object.prototype.toString.call(object) === "[object Array]") { + return ascii.array.call(f, object, processed); + } + + if (!object) { return String((1/object) === -Infinity ? "-0" : object); } + if (samsam.isElement(object)) { return ascii.element(object); } + + if (typeof object.toString === "function" && + object.toString !== Object.prototype.toString) { + return object.toString(); + } + + var i, l; + for (i = 0, l = specialObjects.length; i < l; i++) { + if (object === specialObjects[i].object) { + return specialObjects[i].value; + } + } + + return ascii.object.call(f, object, processed, indent); + } + + ascii.func = function (func) { + return "function " + functionName(func) + "() {}"; + }; + + ascii.array = function (array, processed) { + processed = processed || []; + processed.push(array); + var pieces = []; + var i, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, array.length) : array.length; + + for (i = 0; i < l; ++i) { + pieces.push(ascii(this, array[i], processed)); + } + + if(l < array.length) + pieces.push("[... " + (array.length - l) + " more elements]"); + + return "[" + pieces.join(", ") + "]"; + }; + + ascii.object = function (object, processed, indent) { + processed = processed || []; + processed.push(object); + indent = indent || 0; + var pieces = [], properties = samsam.keys(object).sort(); + var length = 3; + var prop, str, obj, i, k, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, properties.length) : properties.length; + + for (i = 0; i < l; ++i) { + prop = properties[i]; + obj = object[prop]; + + if (isCircular(obj, processed)) { + str = "[Circular]"; + } else { + str = ascii(this, obj, processed, indent + 2); + } + + str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str; + length += str.length; + pieces.push(str); + } + + var cons = constructorName(this, object); + var prefix = cons ? "[" + cons + "] " : ""; + var is = ""; + for (i = 0, k = indent; i < k; ++i) { is += " "; } + + if(l < properties.length) + pieces.push("[... " + (properties.length - l) + " more elements]"); + + if (length + indent > 80) { + return prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" + + is + "}"; + } + return prefix + "{ " + pieces.join(", ") + " }"; + }; + + ascii.element = function (element) { + var tagName = element.tagName.toLowerCase(); + var attrs = element.attributes, attr, pairs = [], attrName, i, l, val; + + for (i = 0, l = attrs.length; i < l; ++i) { + attr = attrs.item(i); + attrName = attr.nodeName.toLowerCase().replace("html:", ""); + val = attr.nodeValue; + if (attrName !== "contenteditable" || val !== "inherit") { + if (!!val) { pairs.push(attrName + "=\"" + val + "\""); } + } + } + + var formatted = "<" + tagName + (pairs.length > 0 ? " " : ""); + var content = element.innerHTML; + + if (content.length > 20) { + content = content.substr(0, 20) + "[...]"; + } + + var res = formatted + pairs.join(" ") + ">" + content + + ""; + + return res.replace(/ contentEditable="inherit"/, ""); + }; + + function Formatio(options) { + for (var opt in options) { + this[opt] = options[opt]; + } + } + + Formatio.prototype = { + functionName: functionName, + + configure: function (options) { + return new Formatio(options); + }, + + constructorName: function (object) { + return constructorName(this, object); + }, + + ascii: function (object, processed, indent) { + return ascii(this, object, processed, indent); + } + }; + + return Formatio.prototype; +}); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"samsam":41}],40:[function(require,module,exports){ +(function (global){ +/*global global, window*/ +/** + * @author Christian Johansen (christian@cjohansen.no) and contributors + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ + +(function (global) { + "use strict"; + + // Make properties writable in IE, as per + // http://www.adequatelygood.com/Replacing-setTimeout-Globally.html + // JSLint being anal + var glbl = global; + + global.setTimeout = glbl.setTimeout; + global.clearTimeout = glbl.clearTimeout; + global.setInterval = glbl.setInterval; + global.clearInterval = glbl.clearInterval; + global.Date = glbl.Date; + + // setImmediate is not a standard function + // avoid adding the prop to the window object if not present + if('setImmediate' in global) { + global.setImmediate = glbl.setImmediate; + global.clearImmediate = glbl.clearImmediate; + } + + // node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref() + // browsers, a number. + // see https://github.com/cjohansen/Sinon.JS/pull/436 + + var NOOP = function () { return undefined; }; + var timeoutResult = setTimeout(NOOP, 0); + var addTimerReturnsObject = typeof timeoutResult === "object"; + clearTimeout(timeoutResult); + + var NativeDate = Date; + var uniqueTimerId = 1; + + /** + * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into + * number of milliseconds. This is used to support human-readable strings passed + * to clock.tick() + */ + function parseTime(str) { + if (!str) { + return 0; + } + + var strings = str.split(":"); + var l = strings.length, i = l; + var ms = 0, parsed; + + if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { + throw new Error("tick only understands numbers and 'h:m:s'"); + } + + while (i--) { + parsed = parseInt(strings[i], 10); + + if (parsed >= 60) { + throw new Error("Invalid time " + str); + } + + ms += parsed * Math.pow(60, (l - i - 1)); + } + + return ms * 1000; + } + + /** + * Used to grok the `now` parameter to createClock. + */ + function getEpoch(epoch) { + if (!epoch) { return 0; } + if (typeof epoch.getTime === "function") { return epoch.getTime(); } + if (typeof epoch === "number") { return epoch; } + throw new TypeError("now should be milliseconds since UNIX epoch"); + } + + function inRange(from, to, timer) { + return timer && timer.callAt >= from && timer.callAt <= to; + } + + function mirrorDateProperties(target, source) { + var prop; + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // set special now implementation + if (source.now) { + target.now = function now() { + return target.clock.now; + }; + } else { + delete target.now; + } + + // set special toSource implementation + if (source.toSource) { + target.toSource = function toSource() { + return source.toSource(); + }; + } else { + delete target.toSource; + } + + // set special toString implementation + target.toString = function toString() { + return source.toString(); + }; + + target.prototype = source.prototype; + target.parse = source.parse; + target.UTC = source.UTC; + target.prototype.toUTCString = source.prototype.toUTCString; + + return target; + } + + function createDate() { + function ClockDate(year, month, date, hour, minute, second, ms) { + // Defensive and verbose to avoid potential harm in passing + // explicit undefined when user does not pass argument + switch (arguments.length) { + case 0: + return new NativeDate(ClockDate.clock.now); + case 1: + return new NativeDate(year); + case 2: + return new NativeDate(year, month); + case 3: + return new NativeDate(year, month, date); + case 4: + return new NativeDate(year, month, date, hour); + case 5: + return new NativeDate(year, month, date, hour, minute); + case 6: + return new NativeDate(year, month, date, hour, minute, second); + default: + return new NativeDate(year, month, date, hour, minute, second, ms); + } + } + + return mirrorDateProperties(ClockDate, NativeDate); + } + + function addTimer(clock, timer) { + if (timer.func === undefined) { + throw new Error("Callback must be provided to timer calls"); + } + + if (!clock.timers) { + clock.timers = {}; + } + + timer.id = uniqueTimerId++; + timer.createdAt = clock.now; + timer.callAt = clock.now + (timer.delay || (clock.duringTick ? 1 : 0)); + + clock.timers[timer.id] = timer; + + if (addTimerReturnsObject) { + return { + id: timer.id, + ref: NOOP, + unref: NOOP + }; + } + + return timer.id; + } + + + function compareTimers(a, b) { + // Sort first by absolute timing + if (a.callAt < b.callAt) { + return -1; + } + if (a.callAt > b.callAt) { + return 1; + } + + // Sort next by immediate, immediate timers take precedence + if (a.immediate && !b.immediate) { + return -1; + } + if (!a.immediate && b.immediate) { + return 1; + } + + // Sort next by creation time, earlier-created timers take precedence + if (a.createdAt < b.createdAt) { + return -1; + } + if (a.createdAt > b.createdAt) { + return 1; + } + + // Sort next by id, lower-id timers take precedence + if (a.id < b.id) { + return -1; + } + if (a.id > b.id) { + return 1; + } + + // As timer ids are unique, no fallback `0` is necessary + } + + function firstTimerInRange(clock, from, to) { + var timers = clock.timers, + timer = null, + id, + isInRange; + + for (id in timers) { + if (timers.hasOwnProperty(id)) { + isInRange = inRange(from, to, timers[id]); + + if (isInRange && (!timer || compareTimers(timer, timers[id]) === 1)) { + timer = timers[id]; + } + } + } + + return timer; + } + + function callTimer(clock, timer) { + var exception; + + if (typeof timer.interval === "number") { + clock.timers[timer.id].callAt += timer.interval; + } else { + delete clock.timers[timer.id]; + } + + try { + if (typeof timer.func === "function") { + timer.func.apply(null, timer.args); + } else { + eval(timer.func); + } + } catch (e) { + exception = e; + } + + if (!clock.timers[timer.id]) { + if (exception) { + throw exception; + } + return; + } + + if (exception) { + throw exception; + } + } + + function timerType(timer) { + if (timer.immediate) { + return "Immediate"; + } else if (typeof timer.interval !== "undefined") { + return "Interval"; + } else { + return "Timeout"; + } + } + + function clearTimer(clock, timerId, ttype) { + if (!timerId) { + // null appears to be allowed in most browsers, and appears to be + // relied upon by some libraries, like Bootstrap carousel + return; + } + + if (!clock.timers) { + clock.timers = []; + } + + // in Node, timerId is an object with .ref()/.unref(), and + // its .id field is the actual timer id. + if (typeof timerId === "object") { + timerId = timerId.id; + } + + if (clock.timers.hasOwnProperty(timerId)) { + // check that the ID matches a timer of the correct type + var timer = clock.timers[timerId]; + if (timerType(timer) === ttype) { + delete clock.timers[timerId]; + } else { + throw new Error("Cannot clear timer: timer created with set" + ttype + "() but cleared with clear" + timerType(timer) + "()"); + } + } + } + + function uninstall(clock, target) { + var method, + i, + l; + + for (i = 0, l = clock.methods.length; i < l; i++) { + method = clock.methods[i]; + + if (target[method].hadOwnProperty) { + target[method] = clock["_" + method]; + } else { + try { + delete target[method]; + } catch (ignore) {} + } + } + + // Prevent multiple executions which will completely remove these props + clock.methods = []; + } + + function hijackMethod(target, method, clock) { + var prop; + + clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method); + clock["_" + method] = target[method]; + + if (method === "Date") { + var date = mirrorDateProperties(clock[method], target[method]); + target[method] = date; + } else { + target[method] = function () { + return clock[method].apply(clock, arguments); + }; + + for (prop in clock[method]) { + if (clock[method].hasOwnProperty(prop)) { + target[method][prop] = clock[method][prop]; + } + } + } + + target[method].clock = clock; + } + + var timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: global.setImmediate, + clearImmediate: global.clearImmediate, + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + + var keys = Object.keys || function (obj) { + var ks = [], + key; + + for (key in obj) { + if (obj.hasOwnProperty(key)) { + ks.push(key); + } + } + + return ks; + }; + + exports.timers = timers; + + function createClock(now) { + var clock = { + now: getEpoch(now), + timeouts: {}, + Date: createDate() + }; + + clock.Date.clock = clock; + + clock.setTimeout = function setTimeout(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout + }); + }; + + clock.clearTimeout = function clearTimeout(timerId) { + return clearTimer(clock, timerId, "Timeout"); + }; + + clock.setInterval = function setInterval(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout, + interval: timeout + }); + }; + + clock.clearInterval = function clearInterval(timerId) { + return clearTimer(clock, timerId, "Interval"); + }; + + clock.setImmediate = function setImmediate(func) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 1), + immediate: true + }); + }; + + clock.clearImmediate = function clearImmediate(timerId) { + return clearTimer(clock, timerId, "Immediate"); + }; + + clock.tick = function tick(ms) { + ms = typeof ms === "number" ? ms : parseTime(ms); + var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now; + var timer = firstTimerInRange(clock, tickFrom, tickTo); + var oldNow; + + clock.duringTick = true; + + var firstException; + while (timer && tickFrom <= tickTo) { + if (clock.timers[timer.id]) { + tickFrom = clock.now = timer.callAt; + try { + oldNow = clock.now; + callTimer(clock, timer); + // compensate for any setSystemTime() call during timer callback + if (oldNow !== clock.now) { + tickFrom += clock.now - oldNow; + tickTo += clock.now - oldNow; + previous += clock.now - oldNow; + } + } catch (e) { + firstException = firstException || e; + } + } + + timer = firstTimerInRange(clock, previous, tickTo); + previous = tickFrom; + } + + clock.duringTick = false; + clock.now = tickTo; + + if (firstException) { + throw firstException; + } + + return clock.now; + }; + + clock.reset = function reset() { + clock.timers = {}; + }; + + clock.setSystemTime = function setSystemTime(now) { + // determine time difference + var newNow = getEpoch(now); + var difference = newNow - clock.now; + + // update 'system clock' + clock.now = newNow; + + // update timers and intervals to keep them stable + for (var id in clock.timers) { + if (clock.timers.hasOwnProperty(id)) { + var timer = clock.timers[id]; + timer.createdAt += difference; + timer.callAt += difference; + } + } + }; + + return clock; + } + exports.createClock = createClock; + + exports.install = function install(target, now, toFake) { + var i, + l; + + if (typeof target === "number") { + toFake = now; + now = target; + target = null; + } + + if (!target) { + target = global; + } + + var clock = createClock(now); + + clock.uninstall = function () { + uninstall(clock, target); + }; + + clock.methods = toFake || []; + + if (clock.methods.length === 0) { + clock.methods = keys(timers); + } + + for (i = 0, l = clock.methods.length; i < l; i++) { + hijackMethod(target, clock.methods[i], clock); + } + + return clock; + }; + +}(global || this)); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],41:[function(require,module,exports){ +((typeof define === "function" && define.amd && function (m) { define("samsam", m); }) || + (typeof module === "object" && + function (m) { module.exports = m(); }) || // Node + function (m) { this.samsam = m(); } // Browser globals +)(function () { + var o = Object.prototype; + var div = typeof document !== "undefined" && document.createElement("div"); + + function isNaN(value) { + // Unlike global isNaN, this avoids type coercion + // typeof check avoids IE host object issues, hat tip to + // lodash + var val = value; // JsLint thinks value !== value is "weird" + return typeof value === "number" && value !== val; + } + + function getClass(value) { + // Returns the internal [[Class]] by calling Object.prototype.toString + // with the provided value as this. Return value is a string, naming the + // internal class, e.g. "Array" + return o.toString.call(value).split(/[ \]]/)[1]; + } + + /** + * @name samsam.isArguments + * @param Object object + * + * Returns ``true`` if ``object`` is an ``arguments`` object, + * ``false`` otherwise. + */ + function isArguments(object) { + if (getClass(object) === 'Arguments') { return true; } + if (typeof object !== "object" || typeof object.length !== "number" || + getClass(object) === "Array") { + return false; + } + if (typeof object.callee == "function") { return true; } + try { + object[object.length] = 6; + delete object[object.length]; + } catch (e) { + return true; + } + return false; + } + + /** + * @name samsam.isElement + * @param Object object + * + * Returns ``true`` if ``object`` is a DOM element node. Unlike + * Underscore.js/lodash, this function will return ``false`` if ``object`` + * is an *element-like* object, i.e. a regular object with a ``nodeType`` + * property that holds the value ``1``. + */ + function isElement(object) { + if (!object || object.nodeType !== 1 || !div) { return false; } + try { + object.appendChild(div); + object.removeChild(div); + } catch (e) { + return false; + } + return true; + } + + /** + * @name samsam.keys + * @param Object object + * + * Return an array of own property names. + */ + function keys(object) { + var ks = [], prop; + for (prop in object) { + if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); } + } + return ks; + } + + /** + * @name samsam.isDate + * @param Object value + * + * Returns true if the object is a ``Date``, or *date-like*. Duck typing + * of date objects work by checking that the object has a ``getTime`` + * function whose return value equals the return value from the object's + * ``valueOf``. + */ + function isDate(value) { + return typeof value.getTime == "function" && + value.getTime() == value.valueOf(); + } + + /** + * @name samsam.isNegZero + * @param Object value + * + * Returns ``true`` if ``value`` is ``-0``. + */ + function isNegZero(value) { + return value === 0 && 1 / value === -Infinity; + } + + /** + * @name samsam.equal + * @param Object obj1 + * @param Object obj2 + * + * Returns ``true`` if two objects are strictly equal. Compared to + * ``===`` there are two exceptions: + * + * - NaN is considered equal to NaN + * - -0 and +0 are not considered equal + */ + function identical(obj1, obj2) { + if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) { + return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2); + } + } + + + /** + * @name samsam.deepEqual + * @param Object obj1 + * @param Object obj2 + * + * Deep equal comparison. Two values are "deep equal" if: + * + * - They are equal, according to samsam.identical + * - They are both date objects representing the same time + * - They are both arrays containing elements that are all deepEqual + * - They are objects with the same set of properties, and each property + * in ``obj1`` is deepEqual to the corresponding property in ``obj2`` + * + * Supports cyclic objects. + */ + function deepEqualCyclic(obj1, obj2) { + + // used for cyclic comparison + // contain already visited objects + var objects1 = [], + objects2 = [], + // contain pathes (position in the object structure) + // of the already visited objects + // indexes same as in objects arrays + paths1 = [], + paths2 = [], + // contains combinations of already compared objects + // in the manner: { "$1['ref']$2['ref']": true } + compared = {}; + + /** + * used to check, if the value of a property is an object + * (cyclic logic is only needed for objects) + * only needed for cyclic logic + */ + function isObject(value) { + + if (typeof value === 'object' && value !== null && + !(value instanceof Boolean) && + !(value instanceof Date) && + !(value instanceof Number) && + !(value instanceof RegExp) && + !(value instanceof String)) { + + return true; + } + + return false; + } + + /** + * returns the index of the given object in the + * given objects array, -1 if not contained + * only needed for cyclic logic + */ + function getIndex(objects, obj) { + + var i; + for (i = 0; i < objects.length; i++) { + if (objects[i] === obj) { + return i; + } + } + + return -1; + } + + // does the recursion for the deep equal check + return (function deepEqual(obj1, obj2, path1, path2) { + var type1 = typeof obj1; + var type2 = typeof obj2; + + // == null also matches undefined + if (obj1 === obj2 || + isNaN(obj1) || isNaN(obj2) || + obj1 == null || obj2 == null || + type1 !== "object" || type2 !== "object") { + + return identical(obj1, obj2); + } + + // Elements are only equal if identical(expected, actual) + if (isElement(obj1) || isElement(obj2)) { return false; } + + var isDate1 = isDate(obj1), isDate2 = isDate(obj2); + if (isDate1 || isDate2) { + if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) { + return false; + } + } + + if (obj1 instanceof RegExp && obj2 instanceof RegExp) { + if (obj1.toString() !== obj2.toString()) { return false; } + } + + var class1 = getClass(obj1); + var class2 = getClass(obj2); + var keys1 = keys(obj1); + var keys2 = keys(obj2); + + if (isArguments(obj1) || isArguments(obj2)) { + if (obj1.length !== obj2.length) { return false; } + } else { + if (type1 !== type2 || class1 !== class2 || + keys1.length !== keys2.length) { + return false; + } + } + + var key, i, l, + // following vars are used for the cyclic logic + value1, value2, + isObject1, isObject2, + index1, index2, + newPath1, newPath2; + + for (i = 0, l = keys1.length; i < l; i++) { + key = keys1[i]; + if (!o.hasOwnProperty.call(obj2, key)) { + return false; + } + + // Start of the cyclic logic + + value1 = obj1[key]; + value2 = obj2[key]; + + isObject1 = isObject(value1); + isObject2 = isObject(value2); + + // determine, if the objects were already visited + // (it's faster to check for isObject first, than to + // get -1 from getIndex for non objects) + index1 = isObject1 ? getIndex(objects1, value1) : -1; + index2 = isObject2 ? getIndex(objects2, value2) : -1; + + // determine the new pathes of the objects + // - for non cyclic objects the current path will be extended + // by current property name + // - for cyclic objects the stored path is taken + newPath1 = index1 !== -1 + ? paths1[index1] + : path1 + '[' + JSON.stringify(key) + ']'; + newPath2 = index2 !== -1 + ? paths2[index2] + : path2 + '[' + JSON.stringify(key) + ']'; + + // stop recursion if current objects are already compared + if (compared[newPath1 + newPath2]) { + return true; + } + + // remember the current objects and their pathes + if (index1 === -1 && isObject1) { + objects1.push(value1); + paths1.push(newPath1); + } + if (index2 === -1 && isObject2) { + objects2.push(value2); + paths2.push(newPath2); + } + + // remember that the current objects are already compared + if (isObject1 && isObject2) { + compared[newPath1 + newPath2] = true; + } + + // End of cyclic logic + + // neither value1 nor value2 is a cycle + // continue with next level + if (!deepEqual(value1, value2, newPath1, newPath2)) { + return false; + } + } + + return true; + + }(obj1, obj2, '$1', '$2')); + } + + var match; + + function arrayContains(array, subset) { + if (subset.length === 0) { return true; } + var i, l, j, k; + for (i = 0, l = array.length; i < l; ++i) { + if (match(array[i], subset[0])) { + for (j = 0, k = subset.length; j < k; ++j) { + if (!match(array[i + j], subset[j])) { return false; } + } + return true; + } + } + return false; + } + + /** + * @name samsam.match + * @param Object object + * @param Object matcher + * + * Compare arbitrary value ``object`` with matcher. + */ + match = function match(object, matcher) { + if (matcher && typeof matcher.test === "function") { + return matcher.test(object); + } + + if (typeof matcher === "function") { + return matcher(object) === true; + } + + if (typeof matcher === "string") { + matcher = matcher.toLowerCase(); + var notNull = typeof object === "string" || !!object; + return notNull && + (String(object)).toLowerCase().indexOf(matcher) >= 0; + } + + if (typeof matcher === "number") { + return matcher === object; + } + + if (typeof matcher === "boolean") { + return matcher === object; + } + + if (typeof(matcher) === "undefined") { + return typeof(object) === "undefined"; + } + + if (matcher === null) { + return object === null; + } + + if (getClass(object) === "Array" && getClass(matcher) === "Array") { + return arrayContains(object, matcher); + } + + if (matcher && typeof matcher === "object") { + if (matcher === object) { + return true; + } + var prop; + for (prop in matcher) { + var value = object[prop]; + if (typeof value === "undefined" && + typeof object.getAttribute === "function") { + value = object.getAttribute(prop); + } + if (matcher[prop] === null || typeof matcher[prop] === 'undefined') { + if (value !== matcher[prop]) { + return false; + } + } else if (typeof value === "undefined" || !match(value, matcher[prop])) { + return false; + } + } + return true; + } + + throw new Error("Matcher was not a string, a number, a " + + "function, a boolean or an object"); + }; + + return { + isArguments: isArguments, + isElement: isElement, + isDate: isDate, + isNegZero: isNegZero, + identical: identical, + deepEqual: deepEqualCyclic, + match: match, + keys: keys + }; +}); + +},{}]},{},[1])(1) +}); +}()); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-1.17.2.js b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-1.17.2.js new file mode 100644 index 0000000..f2305ef --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-1.17.2.js @@ -0,0 +1,6386 @@ +/** + * Sinon.JS 1.17.2, 2015/10/21 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +(function (root, factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + define('sinon', [], function () { + return (root.sinon = factory()); + }); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.sinon = factory(); + } +}(this, function () { + 'use strict'; + var samsam, formatio, lolex; + (function () { + function define(mod, deps, fn) { + if (mod == "samsam") { + samsam = deps(); + } else if (typeof deps === "function" && mod.length === 0) { + lolex = deps(); + } else if (typeof fn === "function") { + formatio = fn(samsam); + } + } + define.amd = {}; +((typeof define === "function" && define.amd && function (m) { define("samsam", m); }) || + (typeof module === "object" && + function (m) { module.exports = m(); }) || // Node + function (m) { this.samsam = m(); } // Browser globals +)(function () { + var o = Object.prototype; + var div = typeof document !== "undefined" && document.createElement("div"); + + function isNaN(value) { + // Unlike global isNaN, this avoids type coercion + // typeof check avoids IE host object issues, hat tip to + // lodash + var val = value; // JsLint thinks value !== value is "weird" + return typeof value === "number" && value !== val; + } + + function getClass(value) { + // Returns the internal [[Class]] by calling Object.prototype.toString + // with the provided value as this. Return value is a string, naming the + // internal class, e.g. "Array" + return o.toString.call(value).split(/[ \]]/)[1]; + } + + /** + * @name samsam.isArguments + * @param Object object + * + * Returns ``true`` if ``object`` is an ``arguments`` object, + * ``false`` otherwise. + */ + function isArguments(object) { + if (getClass(object) === 'Arguments') { return true; } + if (typeof object !== "object" || typeof object.length !== "number" || + getClass(object) === "Array") { + return false; + } + if (typeof object.callee == "function") { return true; } + try { + object[object.length] = 6; + delete object[object.length]; + } catch (e) { + return true; + } + return false; + } + + /** + * @name samsam.isElement + * @param Object object + * + * Returns ``true`` if ``object`` is a DOM element node. Unlike + * Underscore.js/lodash, this function will return ``false`` if ``object`` + * is an *element-like* object, i.e. a regular object with a ``nodeType`` + * property that holds the value ``1``. + */ + function isElement(object) { + if (!object || object.nodeType !== 1 || !div) { return false; } + try { + object.appendChild(div); + object.removeChild(div); + } catch (e) { + return false; + } + return true; + } + + /** + * @name samsam.keys + * @param Object object + * + * Return an array of own property names. + */ + function keys(object) { + var ks = [], prop; + for (prop in object) { + if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); } + } + return ks; + } + + /** + * @name samsam.isDate + * @param Object value + * + * Returns true if the object is a ``Date``, or *date-like*. Duck typing + * of date objects work by checking that the object has a ``getTime`` + * function whose return value equals the return value from the object's + * ``valueOf``. + */ + function isDate(value) { + return typeof value.getTime == "function" && + value.getTime() == value.valueOf(); + } + + /** + * @name samsam.isNegZero + * @param Object value + * + * Returns ``true`` if ``value`` is ``-0``. + */ + function isNegZero(value) { + return value === 0 && 1 / value === -Infinity; + } + + /** + * @name samsam.equal + * @param Object obj1 + * @param Object obj2 + * + * Returns ``true`` if two objects are strictly equal. Compared to + * ``===`` there are two exceptions: + * + * - NaN is considered equal to NaN + * - -0 and +0 are not considered equal + */ + function identical(obj1, obj2) { + if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) { + return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2); + } + } + + + /** + * @name samsam.deepEqual + * @param Object obj1 + * @param Object obj2 + * + * Deep equal comparison. Two values are "deep equal" if: + * + * - They are equal, according to samsam.identical + * - They are both date objects representing the same time + * - They are both arrays containing elements that are all deepEqual + * - They are objects with the same set of properties, and each property + * in ``obj1`` is deepEqual to the corresponding property in ``obj2`` + * + * Supports cyclic objects. + */ + function deepEqualCyclic(obj1, obj2) { + + // used for cyclic comparison + // contain already visited objects + var objects1 = [], + objects2 = [], + // contain pathes (position in the object structure) + // of the already visited objects + // indexes same as in objects arrays + paths1 = [], + paths2 = [], + // contains combinations of already compared objects + // in the manner: { "$1['ref']$2['ref']": true } + compared = {}; + + /** + * used to check, if the value of a property is an object + * (cyclic logic is only needed for objects) + * only needed for cyclic logic + */ + function isObject(value) { + + if (typeof value === 'object' && value !== null && + !(value instanceof Boolean) && + !(value instanceof Date) && + !(value instanceof Number) && + !(value instanceof RegExp) && + !(value instanceof String)) { + + return true; + } + + return false; + } + + /** + * returns the index of the given object in the + * given objects array, -1 if not contained + * only needed for cyclic logic + */ + function getIndex(objects, obj) { + + var i; + for (i = 0; i < objects.length; i++) { + if (objects[i] === obj) { + return i; + } + } + + return -1; + } + + // does the recursion for the deep equal check + return (function deepEqual(obj1, obj2, path1, path2) { + var type1 = typeof obj1; + var type2 = typeof obj2; + + // == null also matches undefined + if (obj1 === obj2 || + isNaN(obj1) || isNaN(obj2) || + obj1 == null || obj2 == null || + type1 !== "object" || type2 !== "object") { + + return identical(obj1, obj2); + } + + // Elements are only equal if identical(expected, actual) + if (isElement(obj1) || isElement(obj2)) { return false; } + + var isDate1 = isDate(obj1), isDate2 = isDate(obj2); + if (isDate1 || isDate2) { + if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) { + return false; + } + } + + if (obj1 instanceof RegExp && obj2 instanceof RegExp) { + if (obj1.toString() !== obj2.toString()) { return false; } + } + + var class1 = getClass(obj1); + var class2 = getClass(obj2); + var keys1 = keys(obj1); + var keys2 = keys(obj2); + + if (isArguments(obj1) || isArguments(obj2)) { + if (obj1.length !== obj2.length) { return false; } + } else { + if (type1 !== type2 || class1 !== class2 || + keys1.length !== keys2.length) { + return false; + } + } + + var key, i, l, + // following vars are used for the cyclic logic + value1, value2, + isObject1, isObject2, + index1, index2, + newPath1, newPath2; + + for (i = 0, l = keys1.length; i < l; i++) { + key = keys1[i]; + if (!o.hasOwnProperty.call(obj2, key)) { + return false; + } + + // Start of the cyclic logic + + value1 = obj1[key]; + value2 = obj2[key]; + + isObject1 = isObject(value1); + isObject2 = isObject(value2); + + // determine, if the objects were already visited + // (it's faster to check for isObject first, than to + // get -1 from getIndex for non objects) + index1 = isObject1 ? getIndex(objects1, value1) : -1; + index2 = isObject2 ? getIndex(objects2, value2) : -1; + + // determine the new pathes of the objects + // - for non cyclic objects the current path will be extended + // by current property name + // - for cyclic objects the stored path is taken + newPath1 = index1 !== -1 + ? paths1[index1] + : path1 + '[' + JSON.stringify(key) + ']'; + newPath2 = index2 !== -1 + ? paths2[index2] + : path2 + '[' + JSON.stringify(key) + ']'; + + // stop recursion if current objects are already compared + if (compared[newPath1 + newPath2]) { + return true; + } + + // remember the current objects and their pathes + if (index1 === -1 && isObject1) { + objects1.push(value1); + paths1.push(newPath1); + } + if (index2 === -1 && isObject2) { + objects2.push(value2); + paths2.push(newPath2); + } + + // remember that the current objects are already compared + if (isObject1 && isObject2) { + compared[newPath1 + newPath2] = true; + } + + // End of cyclic logic + + // neither value1 nor value2 is a cycle + // continue with next level + if (!deepEqual(value1, value2, newPath1, newPath2)) { + return false; + } + } + + return true; + + }(obj1, obj2, '$1', '$2')); + } + + var match; + + function arrayContains(array, subset) { + if (subset.length === 0) { return true; } + var i, l, j, k; + for (i = 0, l = array.length; i < l; ++i) { + if (match(array[i], subset[0])) { + for (j = 0, k = subset.length; j < k; ++j) { + if (!match(array[i + j], subset[j])) { return false; } + } + return true; + } + } + return false; + } + + /** + * @name samsam.match + * @param Object object + * @param Object matcher + * + * Compare arbitrary value ``object`` with matcher. + */ + match = function match(object, matcher) { + if (matcher && typeof matcher.test === "function") { + return matcher.test(object); + } + + if (typeof matcher === "function") { + return matcher(object) === true; + } + + if (typeof matcher === "string") { + matcher = matcher.toLowerCase(); + var notNull = typeof object === "string" || !!object; + return notNull && + (String(object)).toLowerCase().indexOf(matcher) >= 0; + } + + if (typeof matcher === "number") { + return matcher === object; + } + + if (typeof matcher === "boolean") { + return matcher === object; + } + + if (typeof(matcher) === "undefined") { + return typeof(object) === "undefined"; + } + + if (matcher === null) { + return object === null; + } + + if (getClass(object) === "Array" && getClass(matcher) === "Array") { + return arrayContains(object, matcher); + } + + if (matcher && typeof matcher === "object") { + if (matcher === object) { + return true; + } + var prop; + for (prop in matcher) { + var value = object[prop]; + if (typeof value === "undefined" && + typeof object.getAttribute === "function") { + value = object.getAttribute(prop); + } + if (matcher[prop] === null || typeof matcher[prop] === 'undefined') { + if (value !== matcher[prop]) { + return false; + } + } else if (typeof value === "undefined" || !match(value, matcher[prop])) { + return false; + } + } + return true; + } + + throw new Error("Matcher was not a string, a number, a " + + "function, a boolean or an object"); + }; + + return { + isArguments: isArguments, + isElement: isElement, + isDate: isDate, + isNegZero: isNegZero, + identical: identical, + deepEqual: deepEqualCyclic, + match: match, + keys: keys + }; +}); +((typeof define === "function" && define.amd && function (m) { + define("formatio", ["samsam"], m); +}) || (typeof module === "object" && function (m) { + module.exports = m(require("samsam")); +}) || function (m) { this.formatio = m(this.samsam); } +)(function (samsam) { + + var formatio = { + excludeConstructors: ["Object", /^.$/], + quoteStrings: true, + limitChildrenCount: 0 + }; + + var hasOwn = Object.prototype.hasOwnProperty; + + var specialObjects = []; + if (typeof global !== "undefined") { + specialObjects.push({ object: global, value: "[object global]" }); + } + if (typeof document !== "undefined") { + specialObjects.push({ + object: document, + value: "[object HTMLDocument]" + }); + } + if (typeof window !== "undefined") { + specialObjects.push({ object: window, value: "[object Window]" }); + } + + function functionName(func) { + if (!func) { return ""; } + if (func.displayName) { return func.displayName; } + if (func.name) { return func.name; } + var matches = func.toString().match(/function\s+([^\(]+)/m); + return (matches && matches[1]) || ""; + } + + function constructorName(f, object) { + var name = functionName(object && object.constructor); + var excludes = f.excludeConstructors || + formatio.excludeConstructors || []; + + var i, l; + for (i = 0, l = excludes.length; i < l; ++i) { + if (typeof excludes[i] === "string" && excludes[i] === name) { + return ""; + } else if (excludes[i].test && excludes[i].test(name)) { + return ""; + } + } + + return name; + } + + function isCircular(object, objects) { + if (typeof object !== "object") { return false; } + var i, l; + for (i = 0, l = objects.length; i < l; ++i) { + if (objects[i] === object) { return true; } + } + return false; + } + + function ascii(f, object, processed, indent) { + if (typeof object === "string") { + var qs = f.quoteStrings; + var quote = typeof qs !== "boolean" || qs; + return processed || quote ? '"' + object + '"' : object; + } + + if (typeof object === "function" && !(object instanceof RegExp)) { + return ascii.func(object); + } + + processed = processed || []; + + if (isCircular(object, processed)) { return "[Circular]"; } + + if (Object.prototype.toString.call(object) === "[object Array]") { + return ascii.array.call(f, object, processed); + } + + if (!object) { return String((1/object) === -Infinity ? "-0" : object); } + if (samsam.isElement(object)) { return ascii.element(object); } + + if (typeof object.toString === "function" && + object.toString !== Object.prototype.toString) { + return object.toString(); + } + + var i, l; + for (i = 0, l = specialObjects.length; i < l; i++) { + if (object === specialObjects[i].object) { + return specialObjects[i].value; + } + } + + return ascii.object.call(f, object, processed, indent); + } + + ascii.func = function (func) { + return "function " + functionName(func) + "() {}"; + }; + + ascii.array = function (array, processed) { + processed = processed || []; + processed.push(array); + var pieces = []; + var i, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, array.length) : array.length; + + for (i = 0; i < l; ++i) { + pieces.push(ascii(this, array[i], processed)); + } + + if(l < array.length) + pieces.push("[... " + (array.length - l) + " more elements]"); + + return "[" + pieces.join(", ") + "]"; + }; + + ascii.object = function (object, processed, indent) { + processed = processed || []; + processed.push(object); + indent = indent || 0; + var pieces = [], properties = samsam.keys(object).sort(); + var length = 3; + var prop, str, obj, i, k, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, properties.length) : properties.length; + + for (i = 0; i < l; ++i) { + prop = properties[i]; + obj = object[prop]; + + if (isCircular(obj, processed)) { + str = "[Circular]"; + } else { + str = ascii(this, obj, processed, indent + 2); + } + + str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str; + length += str.length; + pieces.push(str); + } + + var cons = constructorName(this, object); + var prefix = cons ? "[" + cons + "] " : ""; + var is = ""; + for (i = 0, k = indent; i < k; ++i) { is += " "; } + + if(l < properties.length) + pieces.push("[... " + (properties.length - l) + " more elements]"); + + if (length + indent > 80) { + return prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" + + is + "}"; + } + return prefix + "{ " + pieces.join(", ") + " }"; + }; + + ascii.element = function (element) { + var tagName = element.tagName.toLowerCase(); + var attrs = element.attributes, attr, pairs = [], attrName, i, l, val; + + for (i = 0, l = attrs.length; i < l; ++i) { + attr = attrs.item(i); + attrName = attr.nodeName.toLowerCase().replace("html:", ""); + val = attr.nodeValue; + if (attrName !== "contenteditable" || val !== "inherit") { + if (!!val) { pairs.push(attrName + "=\"" + val + "\""); } + } + } + + var formatted = "<" + tagName + (pairs.length > 0 ? " " : ""); + var content = element.innerHTML; + + if (content.length > 20) { + content = content.substr(0, 20) + "[...]"; + } + + var res = formatted + pairs.join(" ") + ">" + content + + ""; + + return res.replace(/ contentEditable="inherit"/, ""); + }; + + function Formatio(options) { + for (var opt in options) { + this[opt] = options[opt]; + } + } + + Formatio.prototype = { + functionName: functionName, + + configure: function (options) { + return new Formatio(options); + }, + + constructorName: function (object) { + return constructorName(this, object); + }, + + ascii: function (object, processed, indent) { + return ascii(this, object, processed, indent); + } + }; + + return Formatio.prototype; +}); +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.lolex=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { + throw new Error("tick only understands numbers and 'h:m:s'"); + } + + while (i--) { + parsed = parseInt(strings[i], 10); + + if (parsed >= 60) { + throw new Error("Invalid time " + str); + } + + ms += parsed * Math.pow(60, (l - i - 1)); + } + + return ms * 1000; + } + + /** + * Used to grok the `now` parameter to createClock. + */ + function getEpoch(epoch) { + if (!epoch) { return 0; } + if (typeof epoch.getTime === "function") { return epoch.getTime(); } + if (typeof epoch === "number") { return epoch; } + throw new TypeError("now should be milliseconds since UNIX epoch"); + } + + function inRange(from, to, timer) { + return timer && timer.callAt >= from && timer.callAt <= to; + } + + function mirrorDateProperties(target, source) { + var prop; + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // set special now implementation + if (source.now) { + target.now = function now() { + return target.clock.now; + }; + } else { + delete target.now; + } + + // set special toSource implementation + if (source.toSource) { + target.toSource = function toSource() { + return source.toSource(); + }; + } else { + delete target.toSource; + } + + // set special toString implementation + target.toString = function toString() { + return source.toString(); + }; + + target.prototype = source.prototype; + target.parse = source.parse; + target.UTC = source.UTC; + target.prototype.toUTCString = source.prototype.toUTCString; + + return target; + } + + function createDate() { + function ClockDate(year, month, date, hour, minute, second, ms) { + // Defensive and verbose to avoid potential harm in passing + // explicit undefined when user does not pass argument + switch (arguments.length) { + case 0: + return new NativeDate(ClockDate.clock.now); + case 1: + return new NativeDate(year); + case 2: + return new NativeDate(year, month); + case 3: + return new NativeDate(year, month, date); + case 4: + return new NativeDate(year, month, date, hour); + case 5: + return new NativeDate(year, month, date, hour, minute); + case 6: + return new NativeDate(year, month, date, hour, minute, second); + default: + return new NativeDate(year, month, date, hour, minute, second, ms); + } + } + + return mirrorDateProperties(ClockDate, NativeDate); + } + + function addTimer(clock, timer) { + if (timer.func === undefined) { + throw new Error("Callback must be provided to timer calls"); + } + + if (!clock.timers) { + clock.timers = {}; + } + + timer.id = uniqueTimerId++; + timer.createdAt = clock.now; + timer.callAt = clock.now + (timer.delay || (clock.duringTick ? 1 : 0)); + + clock.timers[timer.id] = timer; + + if (addTimerReturnsObject) { + return { + id: timer.id, + ref: NOOP, + unref: NOOP + }; + } + + return timer.id; + } + + + function compareTimers(a, b) { + // Sort first by absolute timing + if (a.callAt < b.callAt) { + return -1; + } + if (a.callAt > b.callAt) { + return 1; + } + + // Sort next by immediate, immediate timers take precedence + if (a.immediate && !b.immediate) { + return -1; + } + if (!a.immediate && b.immediate) { + return 1; + } + + // Sort next by creation time, earlier-created timers take precedence + if (a.createdAt < b.createdAt) { + return -1; + } + if (a.createdAt > b.createdAt) { + return 1; + } + + // Sort next by id, lower-id timers take precedence + if (a.id < b.id) { + return -1; + } + if (a.id > b.id) { + return 1; + } + + // As timer ids are unique, no fallback `0` is necessary + } + + function firstTimerInRange(clock, from, to) { + var timers = clock.timers, + timer = null, + id, + isInRange; + + for (id in timers) { + if (timers.hasOwnProperty(id)) { + isInRange = inRange(from, to, timers[id]); + + if (isInRange && (!timer || compareTimers(timer, timers[id]) === 1)) { + timer = timers[id]; + } + } + } + + return timer; + } + + function callTimer(clock, timer) { + var exception; + + if (typeof timer.interval === "number") { + clock.timers[timer.id].callAt += timer.interval; + } else { + delete clock.timers[timer.id]; + } + + try { + if (typeof timer.func === "function") { + timer.func.apply(null, timer.args); + } else { + eval(timer.func); + } + } catch (e) { + exception = e; + } + + if (!clock.timers[timer.id]) { + if (exception) { + throw exception; + } + return; + } + + if (exception) { + throw exception; + } + } + + function timerType(timer) { + if (timer.immediate) { + return "Immediate"; + } else if (typeof timer.interval !== "undefined") { + return "Interval"; + } else { + return "Timeout"; + } + } + + function clearTimer(clock, timerId, ttype) { + if (!timerId) { + // null appears to be allowed in most browsers, and appears to be + // relied upon by some libraries, like Bootstrap carousel + return; + } + + if (!clock.timers) { + clock.timers = []; + } + + // in Node, timerId is an object with .ref()/.unref(), and + // its .id field is the actual timer id. + if (typeof timerId === "object") { + timerId = timerId.id; + } + + if (clock.timers.hasOwnProperty(timerId)) { + // check that the ID matches a timer of the correct type + var timer = clock.timers[timerId]; + if (timerType(timer) === ttype) { + delete clock.timers[timerId]; + } else { + throw new Error("Cannot clear timer: timer created with set" + ttype + "() but cleared with clear" + timerType(timer) + "()"); + } + } + } + + function uninstall(clock, target) { + var method, + i, + l; + + for (i = 0, l = clock.methods.length; i < l; i++) { + method = clock.methods[i]; + + if (target[method].hadOwnProperty) { + target[method] = clock["_" + method]; + } else { + try { + delete target[method]; + } catch (ignore) {} + } + } + + // Prevent multiple executions which will completely remove these props + clock.methods = []; + } + + function hijackMethod(target, method, clock) { + var prop; + + clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method); + clock["_" + method] = target[method]; + + if (method === "Date") { + var date = mirrorDateProperties(clock[method], target[method]); + target[method] = date; + } else { + target[method] = function () { + return clock[method].apply(clock, arguments); + }; + + for (prop in clock[method]) { + if (clock[method].hasOwnProperty(prop)) { + target[method][prop] = clock[method][prop]; + } + } + } + + target[method].clock = clock; + } + + var timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: global.setImmediate, + clearImmediate: global.clearImmediate, + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + + var keys = Object.keys || function (obj) { + var ks = [], + key; + + for (key in obj) { + if (obj.hasOwnProperty(key)) { + ks.push(key); + } + } + + return ks; + }; + + exports.timers = timers; + + function createClock(now) { + var clock = { + now: getEpoch(now), + timeouts: {}, + Date: createDate() + }; + + clock.Date.clock = clock; + + clock.setTimeout = function setTimeout(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout + }); + }; + + clock.clearTimeout = function clearTimeout(timerId) { + return clearTimer(clock, timerId, "Timeout"); + }; + + clock.setInterval = function setInterval(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout, + interval: timeout + }); + }; + + clock.clearInterval = function clearInterval(timerId) { + return clearTimer(clock, timerId, "Interval"); + }; + + clock.setImmediate = function setImmediate(func) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 1), + immediate: true + }); + }; + + clock.clearImmediate = function clearImmediate(timerId) { + return clearTimer(clock, timerId, "Immediate"); + }; + + clock.tick = function tick(ms) { + ms = typeof ms === "number" ? ms : parseTime(ms); + var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now; + var timer = firstTimerInRange(clock, tickFrom, tickTo); + var oldNow; + + clock.duringTick = true; + + var firstException; + while (timer && tickFrom <= tickTo) { + if (clock.timers[timer.id]) { + tickFrom = clock.now = timer.callAt; + try { + oldNow = clock.now; + callTimer(clock, timer); + // compensate for any setSystemTime() call during timer callback + if (oldNow !== clock.now) { + tickFrom += clock.now - oldNow; + tickTo += clock.now - oldNow; + previous += clock.now - oldNow; + } + } catch (e) { + firstException = firstException || e; + } + } + + timer = firstTimerInRange(clock, previous, tickTo); + previous = tickFrom; + } + + clock.duringTick = false; + clock.now = tickTo; + + if (firstException) { + throw firstException; + } + + return clock.now; + }; + + clock.reset = function reset() { + clock.timers = {}; + }; + + clock.setSystemTime = function setSystemTime(now) { + // determine time difference + var newNow = getEpoch(now); + var difference = newNow - clock.now; + + // update 'system clock' + clock.now = newNow; + + // update timers and intervals to keep them stable + for (var id in clock.timers) { + if (clock.timers.hasOwnProperty(id)) { + var timer = clock.timers[id]; + timer.createdAt += difference; + timer.callAt += difference; + } + } + }; + + return clock; + } + exports.createClock = createClock; + + exports.install = function install(target, now, toFake) { + var i, + l; + + if (typeof target === "number") { + toFake = now; + now = target; + target = null; + } + + if (!target) { + target = global; + } + + var clock = createClock(now); + + clock.uninstall = function () { + uninstall(clock, target); + }; + + clock.methods = toFake || []; + + if (clock.methods.length === 0) { + clock.methods = keys(timers); + } + + for (i = 0, l = clock.methods.length; i < l; i++) { + hijackMethod(target, clock.methods[i], clock); + } + + return clock; + }; + +}(global || this)); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}]},{},[1])(1) +}); + })(); + var define; +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +var sinon = (function () { +"use strict"; + // eslint-disable-line no-unused-vars + + var sinonModule; + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + sinonModule = module.exports = require("./sinon/util/core"); + require("./sinon/extend"); + require("./sinon/walk"); + require("./sinon/typeOf"); + require("./sinon/times_in_words"); + require("./sinon/spy"); + require("./sinon/call"); + require("./sinon/behavior"); + require("./sinon/stub"); + require("./sinon/mock"); + require("./sinon/collection"); + require("./sinon/assert"); + require("./sinon/sandbox"); + require("./sinon/test"); + require("./sinon/test_case"); + require("./sinon/match"); + require("./sinon/format"); + require("./sinon/log_error"); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + sinonModule = module.exports; + } else { + sinonModule = {}; + } + + return sinonModule; +}()); + +/** + * @depend ../../sinon.js + */ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + var div = typeof document !== "undefined" && document.createElement("div"); + var hasOwn = Object.prototype.hasOwnProperty; + + function isDOMNode(obj) { + var success = false; + + try { + obj.appendChild(div); + success = div.parentNode === obj; + } catch (e) { + return false; + } finally { + try { + obj.removeChild(div); + } catch (e) { + // Remove failed, not much we can do about that + } + } + + return success; + } + + function isElement(obj) { + return div && obj && obj.nodeType === 1 && isDOMNode(obj); + } + + function isFunction(obj) { + return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply); + } + + function isReallyNaN(val) { + return typeof val === "number" && isNaN(val); + } + + function mirrorProperties(target, source) { + for (var prop in source) { + if (!hasOwn.call(target, prop)) { + target[prop] = source[prop]; + } + } + } + + function isRestorable(obj) { + return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon; + } + + // Cheap way to detect if we have ES5 support. + var hasES5Support = "keys" in Object; + + function makeApi(sinon) { + sinon.wrapMethod = function wrapMethod(object, property, method) { + if (!object) { + throw new TypeError("Should wrap property of object"); + } + + if (typeof method !== "function" && typeof method !== "object") { + throw new TypeError("Method wrapper should be a function or a property descriptor"); + } + + function checkWrappedMethod(wrappedMethod) { + var error; + + if (!isFunction(wrappedMethod)) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } else if (wrappedMethod.calledBefore) { + var verb = wrappedMethod.returns ? "stubbed" : "spied on"; + error = new TypeError("Attempted to wrap " + property + " which is already " + verb); + } + + if (error) { + if (wrappedMethod && wrappedMethod.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethod.stackTrace; + } + throw error; + } + } + + var error, wrappedMethod, i; + + // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem + // when using hasOwn.call on objects from other frames. + var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property); + + if (hasES5Support) { + var methodDesc = (typeof method === "function") ? {value: method} : method; + var wrappedMethodDesc = sinon.getPropertyDescriptor(object, property); + + if (!wrappedMethodDesc) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } + if (error) { + if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace; + } + throw error; + } + + var types = sinon.objectKeys(methodDesc); + for (i = 0; i < types.length; i++) { + wrappedMethod = wrappedMethodDesc[types[i]]; + checkWrappedMethod(wrappedMethod); + } + + mirrorProperties(methodDesc, wrappedMethodDesc); + for (i = 0; i < types.length; i++) { + mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]); + } + Object.defineProperty(object, property, methodDesc); + } else { + wrappedMethod = object[property]; + checkWrappedMethod(wrappedMethod); + object[property] = method; + method.displayName = property; + } + + method.displayName = property; + + // Set up a stack trace which can be used later to find what line of + // code the original method was created on. + method.stackTrace = (new Error("Stack Trace for original")).stack; + + method.restore = function () { + // For prototype properties try to reset by delete first. + // If this fails (ex: localStorage on mobile safari) then force a reset + // via direct assignment. + if (!owned) { + // In some cases `delete` may throw an error + try { + delete object[property]; + } catch (e) {} // eslint-disable-line no-empty + // For native code functions `delete` fails without throwing an error + // on Chrome < 43, PhantomJS, etc. + } else if (hasES5Support) { + Object.defineProperty(object, property, wrappedMethodDesc); + } + + // Use strict equality comparison to check failures then force a reset + // via direct assignment. + if (object[property] === method) { + object[property] = wrappedMethod; + } + }; + + method.restore.sinon = true; + + if (!hasES5Support) { + mirrorProperties(method, wrappedMethod); + } + + return method; + }; + + sinon.create = function create(proto) { + var F = function () {}; + F.prototype = proto; + return new F(); + }; + + sinon.deepEqual = function deepEqual(a, b) { + if (sinon.match && sinon.match.isMatcher(a)) { + return a.test(b); + } + + if (typeof a !== "object" || typeof b !== "object") { + return isReallyNaN(a) && isReallyNaN(b) || a === b; + } + + if (isElement(a) || isElement(b)) { + return a === b; + } + + if (a === b) { + return true; + } + + if ((a === null && b !== null) || (a !== null && b === null)) { + return false; + } + + if (a instanceof RegExp && b instanceof RegExp) { + return (a.source === b.source) && (a.global === b.global) && + (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline); + } + + var aString = Object.prototype.toString.call(a); + if (aString !== Object.prototype.toString.call(b)) { + return false; + } + + if (aString === "[object Date]") { + return a.valueOf() === b.valueOf(); + } + + var prop; + var aLength = 0; + var bLength = 0; + + if (aString === "[object Array]" && a.length !== b.length) { + return false; + } + + for (prop in a) { + if (a.hasOwnProperty(prop)) { + aLength += 1; + + if (!(prop in b)) { + return false; + } + + if (!deepEqual(a[prop], b[prop])) { + return false; + } + } + } + + for (prop in b) { + if (b.hasOwnProperty(prop)) { + bLength += 1; + } + } + + return aLength === bLength; + }; + + sinon.functionName = function functionName(func) { + var name = func.displayName || func.name; + + // Use function decomposition as a last resort to get function + // name. Does not rely on function decomposition to work - if it + // doesn't debugging will be slightly less informative + // (i.e. toString will say 'spy' rather than 'myFunc'). + if (!name) { + var matches = func.toString().match(/function ([^\s\(]+)/); + name = matches && matches[1]; + } + + return name; + }; + + sinon.functionToString = function toString() { + if (this.getCall && this.callCount) { + var thisValue, + prop; + var i = this.callCount; + + while (i--) { + thisValue = this.getCall(i).thisValue; + + for (prop in thisValue) { + if (thisValue[prop] === this) { + return prop; + } + } + } + } + + return this.displayName || "sinon fake"; + }; + + sinon.objectKeys = function objectKeys(obj) { + if (obj !== Object(obj)) { + throw new TypeError("sinon.objectKeys called on a non-object"); + } + + var keys = []; + var key; + for (key in obj) { + if (hasOwn.call(obj, key)) { + keys.push(key); + } + } + + return keys; + }; + + sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) { + var proto = object; + var descriptor; + + while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) { + proto = Object.getPrototypeOf(proto); + } + return descriptor; + }; + + sinon.getConfig = function (custom) { + var config = {}; + custom = custom || {}; + var defaults = sinon.defaultConfig; + + for (var prop in defaults) { + if (defaults.hasOwnProperty(prop)) { + config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; + } + } + + return config; + }; + + sinon.defaultConfig = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.timesInWords = function timesInWords(count) { + return count === 1 && "once" || + count === 2 && "twice" || + count === 3 && "thrice" || + (count || 0) + " times"; + }; + + sinon.calledInOrder = function (spies) { + for (var i = 1, l = spies.length; i < l; i++) { + if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) { + return false; + } + } + + return true; + }; + + sinon.orderByFirstCall = function (spies) { + return spies.sort(function (a, b) { + // uuid, won't ever be equal + var aCall = a.getCall(0); + var bCall = b.getCall(0); + var aId = aCall && aCall.callId || -1; + var bId = bCall && bCall.callId || -1; + + return aId < bId ? -1 : 1; + }); + }; + + sinon.createStubInstance = function (constructor) { + if (typeof constructor !== "function") { + throw new TypeError("The constructor should be a function."); + } + return sinon.stub(sinon.create(constructor.prototype)); + }; + + sinon.restore = function (object) { + if (object !== null && typeof object === "object") { + for (var prop in object) { + if (isRestorable(object[prop])) { + object[prop].restore(); + } + } + } else if (isRestorable(object)) { + object.restore(); + } + }; + + return sinon; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports) { + makeApi(exports); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + + // Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + var hasDontEnumBug = (function () { + var obj = { + constructor: function () { + return "0"; + }, + toString: function () { + return "1"; + }, + valueOf: function () { + return "2"; + }, + toLocaleString: function () { + return "3"; + }, + prototype: function () { + return "4"; + }, + isPrototypeOf: function () { + return "5"; + }, + propertyIsEnumerable: function () { + return "6"; + }, + hasOwnProperty: function () { + return "7"; + }, + length: function () { + return "8"; + }, + unique: function () { + return "9"; + } + }; + + var result = []; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + result.push(obj[prop]()); + } + } + return result.join("") !== "0123456789"; + })(); + + /* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will + * override properties in previous sources. + * + * target - The Object to extend + * sources - Objects to copy properties from. + * + * Returns the extended target + */ + function extend(target /*, sources */) { + var sources = Array.prototype.slice.call(arguments, 1); + var source, i, prop; + + for (i = 0; i < sources.length; i++) { + source = sources[i]; + + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // Make sure we copy (own) toString method even when in JScript with DontEnum bug + // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) { + target.toString = source.toString; + } + } + + return target; + } + + sinon.extend = extend; + return sinon.extend; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + + function timesInWords(count) { + switch (count) { + case 1: + return "once"; + case 2: + return "twice"; + case 3: + return "thrice"; + default: + return (count || 0) + " times"; + } + } + + sinon.timesInWords = timesInWords; + return sinon.timesInWords; + } + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + module.exports = makeApi(core); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + function typeOf(value) { + if (value === null) { + return "null"; + } else if (value === undefined) { + return "undefined"; + } + var string = Object.prototype.toString.call(value); + return string.substring(8, string.length - 1).toLowerCase(); + } + + sinon.typeOf = typeOf; + return sinon.typeOf; + } + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + module.exports = makeApi(core); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend typeOf.js + */ +/*jslint eqeqeq: false, onevar: false, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Match functions + * + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2012 Maximilian Antoni + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + function assertType(value, type, name) { + var actual = sinon.typeOf(value); + if (actual !== type) { + throw new TypeError("Expected type of " + name + " to be " + + type + ", but was " + actual); + } + } + + var matcher = { + toString: function () { + return this.message; + } + }; + + function isMatcher(object) { + return matcher.isPrototypeOf(object); + } + + function matchObject(expectation, actual) { + if (actual === null || actual === undefined) { + return false; + } + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + var exp = expectation[key]; + var act = actual[key]; + if (isMatcher(exp)) { + if (!exp.test(act)) { + return false; + } + } else if (sinon.typeOf(exp) === "object") { + if (!matchObject(exp, act)) { + return false; + } + } else if (!sinon.deepEqual(exp, act)) { + return false; + } + } + } + return true; + } + + function match(expectation, message) { + var m = sinon.create(matcher); + var type = sinon.typeOf(expectation); + switch (type) { + case "object": + if (typeof expectation.test === "function") { + m.test = function (actual) { + return expectation.test(actual) === true; + }; + m.message = "match(" + sinon.functionName(expectation.test) + ")"; + return m; + } + var str = []; + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + str.push(key + ": " + expectation[key]); + } + } + m.test = function (actual) { + return matchObject(expectation, actual); + }; + m.message = "match(" + str.join(", ") + ")"; + break; + case "number": + m.test = function (actual) { + // we need type coercion here + return expectation == actual; // eslint-disable-line eqeqeq + }; + break; + case "string": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return actual.indexOf(expectation) !== -1; + }; + m.message = "match(\"" + expectation + "\")"; + break; + case "regexp": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return expectation.test(actual); + }; + break; + case "function": + m.test = expectation; + if (message) { + m.message = message; + } else { + m.message = "match(" + sinon.functionName(expectation) + ")"; + } + break; + default: + m.test = function (actual) { + return sinon.deepEqual(expectation, actual); + }; + } + if (!m.message) { + m.message = "match(" + expectation + ")"; + } + return m; + } + + matcher.or = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var or = sinon.create(matcher); + or.test = function (actual) { + return m1.test(actual) || m2.test(actual); + }; + or.message = m1.message + ".or(" + m2.message + ")"; + return or; + }; + + matcher.and = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var and = sinon.create(matcher); + and.test = function (actual) { + return m1.test(actual) && m2.test(actual); + }; + and.message = m1.message + ".and(" + m2.message + ")"; + return and; + }; + + match.isMatcher = isMatcher; + + match.any = match(function () { + return true; + }, "any"); + + match.defined = match(function (actual) { + return actual !== null && actual !== undefined; + }, "defined"); + + match.truthy = match(function (actual) { + return !!actual; + }, "truthy"); + + match.falsy = match(function (actual) { + return !actual; + }, "falsy"); + + match.same = function (expectation) { + return match(function (actual) { + return expectation === actual; + }, "same(" + expectation + ")"); + }; + + match.typeOf = function (type) { + assertType(type, "string", "type"); + return match(function (actual) { + return sinon.typeOf(actual) === type; + }, "typeOf(\"" + type + "\")"); + }; + + match.instanceOf = function (type) { + assertType(type, "function", "type"); + return match(function (actual) { + return actual instanceof type; + }, "instanceOf(" + sinon.functionName(type) + ")"); + }; + + function createPropertyMatcher(propertyTest, messagePrefix) { + return function (property, value) { + assertType(property, "string", "property"); + var onlyProperty = arguments.length === 1; + var message = messagePrefix + "(\"" + property + "\""; + if (!onlyProperty) { + message += ", " + value; + } + message += ")"; + return match(function (actual) { + if (actual === undefined || actual === null || + !propertyTest(actual, property)) { + return false; + } + return onlyProperty || sinon.deepEqual(value, actual[property]); + }, message); + }; + } + + match.has = createPropertyMatcher(function (actual, property) { + if (typeof actual === "object") { + return property in actual; + } + return actual[property] !== undefined; + }, "has"); + + match.hasOwn = createPropertyMatcher(function (actual, property) { + return actual.hasOwnProperty(property); + }, "hasOwn"); + + match.bool = match.typeOf("boolean"); + match.number = match.typeOf("number"); + match.string = match.typeOf("string"); + match.object = match.typeOf("object"); + match.func = match.typeOf("function"); + match.array = match.typeOf("array"); + match.regexp = match.typeOf("regexp"); + match.date = match.typeOf("date"); + + sinon.match = match; + return match; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./typeOf"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal, formatio) { + + function makeApi(sinon) { + function valueFormatter(value) { + return "" + value; + } + + function getFormatioFormatter() { + var formatter = formatio.configure({ + quoteStrings: false, + limitChildrenCount: 250 + }); + + function format() { + return formatter.ascii.apply(formatter, arguments); + } + + return format; + } + + function getNodeFormatter() { + try { + var util = require("util"); + } catch (e) { + /* Node, but no util module - would be very old, but better safe than sorry */ + } + + function format(v) { + var isObjectWithNativeToString = typeof v === "object" && v.toString === Object.prototype.toString; + return isObjectWithNativeToString ? util.inspect(v) : v; + } + + return util ? format : valueFormatter; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var formatter; + + if (isNode) { + try { + formatio = require("formatio"); + } + catch (e) {} // eslint-disable-line no-empty + } + + if (formatio) { + formatter = getFormatioFormatter(); + } else if (isNode) { + formatter = getNodeFormatter(); + } else { + formatter = valueFormatter; + } + + sinon.format = formatter; + return sinon.format; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof formatio === "object" && formatio // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend match.js + * @depend format.js + */ +/** + * Spy calls + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + * Copyright (c) 2013 Maximilian Antoni + */ +(function (sinonGlobal) { + + var slice = Array.prototype.slice; + + function makeApi(sinon) { + function throwYieldError(proxy, text, args) { + var msg = sinon.functionName(proxy) + text; + if (args.length) { + msg += " Received [" + slice.call(args).join(", ") + "]"; + } + throw new Error(msg); + } + + var callProto = { + calledOn: function calledOn(thisValue) { + if (sinon.match && sinon.match.isMatcher(thisValue)) { + return thisValue.test(this.thisValue); + } + return this.thisValue === thisValue; + }, + + calledWith: function calledWith() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + if (!sinon.deepEqual(arguments[i], this.args[i])) { + return false; + } + } + + return true; + }, + + calledWithMatch: function calledWithMatch() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + var actual = this.args[i]; + var expectation = arguments[i]; + if (!sinon.match || !sinon.match(expectation).test(actual)) { + return false; + } + } + return true; + }, + + calledWithExactly: function calledWithExactly() { + return arguments.length === this.args.length && + this.calledWith.apply(this, arguments); + }, + + notCalledWith: function notCalledWith() { + return !this.calledWith.apply(this, arguments); + }, + + notCalledWithMatch: function notCalledWithMatch() { + return !this.calledWithMatch.apply(this, arguments); + }, + + returned: function returned(value) { + return sinon.deepEqual(value, this.returnValue); + }, + + threw: function threw(error) { + if (typeof error === "undefined" || !this.exception) { + return !!this.exception; + } + + return this.exception === error || this.exception.name === error; + }, + + calledWithNew: function calledWithNew() { + return this.proxy.prototype && this.thisValue instanceof this.proxy; + }, + + calledBefore: function (other) { + return this.callId < other.callId; + }, + + calledAfter: function (other) { + return this.callId > other.callId; + }, + + callArg: function (pos) { + this.args[pos](); + }, + + callArgOn: function (pos, thisValue) { + this.args[pos].apply(thisValue); + }, + + callArgWith: function (pos) { + this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1))); + }, + + callArgOnWith: function (pos, thisValue) { + var args = slice.call(arguments, 2); + this.args[pos].apply(thisValue, args); + }, + + "yield": function () { + this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0))); + }, + + yieldOn: function (thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (typeof args[i] === "function") { + args[i].apply(thisValue, slice.call(arguments, 1)); + return; + } + } + throwYieldError(this.proxy, " cannot yield since no callback was passed.", args); + }, + + yieldTo: function (prop) { + this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1))); + }, + + yieldToOn: function (prop, thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (args[i] && typeof args[i][prop] === "function") { + args[i][prop].apply(thisValue, slice.call(arguments, 2)); + return; + } + } + throwYieldError(this.proxy, " cannot yield to '" + prop + + "' since no callback was passed.", args); + }, + + getStackFrames: function () { + // Omit the error message and the two top stack frames in sinon itself: + return this.stack && this.stack.split("\n").slice(3); + }, + + toString: function () { + var callStr = this.proxy.toString() + "("; + var args = []; + + for (var i = 0, l = this.args.length; i < l; ++i) { + args.push(sinon.format(this.args[i])); + } + + callStr = callStr + args.join(", ") + ")"; + + if (typeof this.returnValue !== "undefined") { + callStr += " => " + sinon.format(this.returnValue); + } + + if (this.exception) { + callStr += " !" + this.exception.name; + + if (this.exception.message) { + callStr += "(" + this.exception.message + ")"; + } + } + if (this.stack) { + callStr += this.getStackFrames()[0].replace(/^\s*(?:at\s+|@)?/, " at "); + + } + + return callStr; + } + }; + + callProto.invokeCallback = callProto.yield; + + function createSpyCall(spy, thisValue, args, returnValue, exception, id, stack) { + if (typeof id !== "number") { + throw new TypeError("Call id is not a number"); + } + var proxyCall = sinon.create(callProto); + proxyCall.proxy = spy; + proxyCall.thisValue = thisValue; + proxyCall.args = args; + proxyCall.returnValue = returnValue; + proxyCall.exception = exception; + proxyCall.callId = id; + proxyCall.stack = stack; + + return proxyCall; + } + createSpyCall.toString = callProto.toString; // used by mocks + + sinon.spyCall = createSpyCall; + return createSpyCall; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./match"); + require("./format"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend extend.js + * @depend call.js + * @depend format.js + */ +/** + * Spy functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + var push = Array.prototype.push; + var slice = Array.prototype.slice; + var callId = 0; + + function spy(object, property, types) { + if (!property && typeof object === "function") { + return spy.create(object); + } + + if (!object && !property) { + return spy.create(function () { }); + } + + if (types) { + var methodDesc = sinon.getPropertyDescriptor(object, property); + for (var i = 0; i < types.length; i++) { + methodDesc[types[i]] = spy.create(methodDesc[types[i]]); + } + return sinon.wrapMethod(object, property, methodDesc); + } + + return sinon.wrapMethod(object, property, spy.create(object[property])); + } + + function matchingFake(fakes, args, strict) { + if (!fakes) { + return undefined; + } + + for (var i = 0, l = fakes.length; i < l; i++) { + if (fakes[i].matches(args, strict)) { + return fakes[i]; + } + } + } + + function incrementCallCount() { + this.called = true; + this.callCount += 1; + this.notCalled = false; + this.calledOnce = this.callCount === 1; + this.calledTwice = this.callCount === 2; + this.calledThrice = this.callCount === 3; + } + + function createCallProperties() { + this.firstCall = this.getCall(0); + this.secondCall = this.getCall(1); + this.thirdCall = this.getCall(2); + this.lastCall = this.getCall(this.callCount - 1); + } + + var vars = "a,b,c,d,e,f,g,h,i,j,k,l"; + function createProxy(func, proxyLength) { + // Retain the function length: + var p; + if (proxyLength) { + eval("p = (function proxy(" + vars.substring(0, proxyLength * 2 - 1) + // eslint-disable-line no-eval + ") { return p.invoke(func, this, slice.call(arguments)); });"); + } else { + p = function proxy() { + return p.invoke(func, this, slice.call(arguments)); + }; + } + p.isSinonProxy = true; + return p; + } + + var uuid = 0; + + // Public API + var spyApi = { + reset: function () { + if (this.invoking) { + var err = new Error("Cannot reset Sinon function while invoking it. " + + "Move the call to .reset outside of the callback."); + err.name = "InvalidResetException"; + throw err; + } + + this.called = false; + this.notCalled = true; + this.calledOnce = false; + this.calledTwice = false; + this.calledThrice = false; + this.callCount = 0; + this.firstCall = null; + this.secondCall = null; + this.thirdCall = null; + this.lastCall = null; + this.args = []; + this.returnValues = []; + this.thisValues = []; + this.exceptions = []; + this.callIds = []; + this.stacks = []; + if (this.fakes) { + for (var i = 0; i < this.fakes.length; i++) { + this.fakes[i].reset(); + } + } + + return this; + }, + + create: function create(func, spyLength) { + var name; + + if (typeof func !== "function") { + func = function () { }; + } else { + name = sinon.functionName(func); + } + + if (!spyLength) { + spyLength = func.length; + } + + var proxy = createProxy(func, spyLength); + + sinon.extend(proxy, spy); + delete proxy.create; + sinon.extend(proxy, func); + + proxy.reset(); + proxy.prototype = func.prototype; + proxy.displayName = name || "spy"; + proxy.toString = sinon.functionToString; + proxy.instantiateFake = sinon.spy.create; + proxy.id = "spy#" + uuid++; + + return proxy; + }, + + invoke: function invoke(func, thisValue, args) { + var matching = matchingFake(this.fakes, args); + var exception, returnValue; + + incrementCallCount.call(this); + push.call(this.thisValues, thisValue); + push.call(this.args, args); + push.call(this.callIds, callId++); + + // Make call properties available from within the spied function: + createCallProperties.call(this); + + try { + this.invoking = true; + + if (matching) { + returnValue = matching.invoke(func, thisValue, args); + } else { + returnValue = (this.func || func).apply(thisValue, args); + } + + var thisCall = this.getCall(this.callCount - 1); + if (thisCall.calledWithNew() && typeof returnValue !== "object") { + returnValue = thisValue; + } + } catch (e) { + exception = e; + } finally { + delete this.invoking; + } + + push.call(this.exceptions, exception); + push.call(this.returnValues, returnValue); + push.call(this.stacks, new Error().stack); + + // Make return value and exception available in the calls: + createCallProperties.call(this); + + if (exception !== undefined) { + throw exception; + } + + return returnValue; + }, + + named: function named(name) { + this.displayName = name; + return this; + }, + + getCall: function getCall(i) { + if (i < 0 || i >= this.callCount) { + return null; + } + + return sinon.spyCall(this, this.thisValues[i], this.args[i], + this.returnValues[i], this.exceptions[i], + this.callIds[i], this.stacks[i]); + }, + + getCalls: function () { + var calls = []; + var i; + + for (i = 0; i < this.callCount; i++) { + calls.push(this.getCall(i)); + } + + return calls; + }, + + calledBefore: function calledBefore(spyFn) { + if (!this.called) { + return false; + } + + if (!spyFn.called) { + return true; + } + + return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1]; + }, + + calledAfter: function calledAfter(spyFn) { + if (!this.called || !spyFn.called) { + return false; + } + + return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1]; + }, + + withArgs: function () { + var args = slice.call(arguments); + + if (this.fakes) { + var match = matchingFake(this.fakes, args, true); + + if (match) { + return match; + } + } else { + this.fakes = []; + } + + var original = this; + var fake = this.instantiateFake(); + fake.matchingAguments = args; + fake.parent = this; + push.call(this.fakes, fake); + + fake.withArgs = function () { + return original.withArgs.apply(original, arguments); + }; + + for (var i = 0; i < this.args.length; i++) { + if (fake.matches(this.args[i])) { + incrementCallCount.call(fake); + push.call(fake.thisValues, this.thisValues[i]); + push.call(fake.args, this.args[i]); + push.call(fake.returnValues, this.returnValues[i]); + push.call(fake.exceptions, this.exceptions[i]); + push.call(fake.callIds, this.callIds[i]); + } + } + createCallProperties.call(fake); + + return fake; + }, + + matches: function (args, strict) { + var margs = this.matchingAguments; + + if (margs.length <= args.length && + sinon.deepEqual(margs, args.slice(0, margs.length))) { + return !strict || margs.length === args.length; + } + }, + + printf: function (format) { + var spyInstance = this; + var args = slice.call(arguments, 1); + var formatter; + + return (format || "").replace(/%(.)/g, function (match, specifyer) { + formatter = spyApi.formatters[specifyer]; + + if (typeof formatter === "function") { + return formatter.call(null, spyInstance, args); + } else if (!isNaN(parseInt(specifyer, 10))) { + return sinon.format(args[specifyer - 1]); + } + + return "%" + specifyer; + }); + } + }; + + function delegateToCalls(method, matchAny, actual, notCalled) { + spyApi[method] = function () { + if (!this.called) { + if (notCalled) { + return notCalled.apply(this, arguments); + } + return false; + } + + var currentCall; + var matches = 0; + + for (var i = 0, l = this.callCount; i < l; i += 1) { + currentCall = this.getCall(i); + + if (currentCall[actual || method].apply(currentCall, arguments)) { + matches += 1; + + if (matchAny) { + return true; + } + } + } + + return matches === this.callCount; + }; + } + + delegateToCalls("calledOn", true); + delegateToCalls("alwaysCalledOn", false, "calledOn"); + delegateToCalls("calledWith", true); + delegateToCalls("calledWithMatch", true); + delegateToCalls("alwaysCalledWith", false, "calledWith"); + delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch"); + delegateToCalls("calledWithExactly", true); + delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly"); + delegateToCalls("neverCalledWith", false, "notCalledWith", function () { + return true; + }); + delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch", function () { + return true; + }); + delegateToCalls("threw", true); + delegateToCalls("alwaysThrew", false, "threw"); + delegateToCalls("returned", true); + delegateToCalls("alwaysReturned", false, "returned"); + delegateToCalls("calledWithNew", true); + delegateToCalls("alwaysCalledWithNew", false, "calledWithNew"); + delegateToCalls("callArg", false, "callArgWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgWith = spyApi.callArg; + delegateToCalls("callArgOn", false, "callArgOnWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgOnWith = spyApi.callArgOn; + delegateToCalls("yield", false, "yield", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode. + spyApi.invokeCallback = spyApi.yield; + delegateToCalls("yieldOn", false, "yieldOn", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + delegateToCalls("yieldTo", false, "yieldTo", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + delegateToCalls("yieldToOn", false, "yieldToOn", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + + spyApi.formatters = { + c: function (spyInstance) { + return sinon.timesInWords(spyInstance.callCount); + }, + + n: function (spyInstance) { + return spyInstance.toString(); + }, + + C: function (spyInstance) { + var calls = []; + + for (var i = 0, l = spyInstance.callCount; i < l; ++i) { + var stringifiedCall = " " + spyInstance.getCall(i).toString(); + if (/\n/.test(calls[i - 1])) { + stringifiedCall = "\n" + stringifiedCall; + } + push.call(calls, stringifiedCall); + } + + return calls.length > 0 ? "\n" + calls.join("\n") : ""; + }, + + t: function (spyInstance) { + var objects = []; + + for (var i = 0, l = spyInstance.callCount; i < l; ++i) { + push.call(objects, sinon.format(spyInstance.thisValues[i])); + } + + return objects.join(", "); + }, + + "*": function (spyInstance, args) { + var formatted = []; + + for (var i = 0, l = args.length; i < l; ++i) { + push.call(formatted, sinon.format(args[i])); + } + + return formatted.join(", "); + } + }; + + sinon.extend(spy, spyApi); + + spy.spyCall = sinon.spyCall; + sinon.spy = spy; + + return spy; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./call"); + require("./extend"); + require("./times_in_words"); + require("./format"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend extend.js + */ +/** + * Stub behavior + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Tim Fischbach (mail@timfischbach.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + var slice = Array.prototype.slice; + var join = Array.prototype.join; + var useLeftMostCallback = -1; + var useRightMostCallback = -2; + + var nextTick = (function () { + if (typeof process === "object" && typeof process.nextTick === "function") { + return process.nextTick; + } + + if (typeof setImmediate === "function") { + return setImmediate; + } + + return function (callback) { + setTimeout(callback, 0); + }; + })(); + + function throwsException(error, message) { + if (typeof error === "string") { + this.exception = new Error(message || ""); + this.exception.name = error; + } else if (!error) { + this.exception = new Error("Error"); + } else { + this.exception = error; + } + + return this; + } + + function getCallback(behavior, args) { + var callArgAt = behavior.callArgAt; + + if (callArgAt >= 0) { + return args[callArgAt]; + } + + var argumentList; + + if (callArgAt === useLeftMostCallback) { + argumentList = args; + } + + if (callArgAt === useRightMostCallback) { + argumentList = slice.call(args).reverse(); + } + + var callArgProp = behavior.callArgProp; + + for (var i = 0, l = argumentList.length; i < l; ++i) { + if (!callArgProp && typeof argumentList[i] === "function") { + return argumentList[i]; + } + + if (callArgProp && argumentList[i] && + typeof argumentList[i][callArgProp] === "function") { + return argumentList[i][callArgProp]; + } + } + + return null; + } + + function makeApi(sinon) { + function getCallbackError(behavior, func, args) { + if (behavior.callArgAt < 0) { + var msg; + + if (behavior.callArgProp) { + msg = sinon.functionName(behavior.stub) + + " expected to yield to '" + behavior.callArgProp + + "', but no object with such a property was passed."; + } else { + msg = sinon.functionName(behavior.stub) + + " expected to yield, but no callback was passed."; + } + + if (args.length > 0) { + msg += " Received [" + join.call(args, ", ") + "]"; + } + + return msg; + } + + return "argument at index " + behavior.callArgAt + " is not a function: " + func; + } + + function callCallback(behavior, args) { + if (typeof behavior.callArgAt === "number") { + var func = getCallback(behavior, args); + + if (typeof func !== "function") { + throw new TypeError(getCallbackError(behavior, func, args)); + } + + if (behavior.callbackAsync) { + nextTick(function () { + func.apply(behavior.callbackContext, behavior.callbackArguments); + }); + } else { + func.apply(behavior.callbackContext, behavior.callbackArguments); + } + } + } + + var proto = { + create: function create(stub) { + var behavior = sinon.extend({}, sinon.behavior); + delete behavior.create; + behavior.stub = stub; + + return behavior; + }, + + isPresent: function isPresent() { + return (typeof this.callArgAt === "number" || + this.exception || + typeof this.returnArgAt === "number" || + this.returnThis || + this.returnValueDefined); + }, + + invoke: function invoke(context, args) { + callCallback(this, args); + + if (this.exception) { + throw this.exception; + } else if (typeof this.returnArgAt === "number") { + return args[this.returnArgAt]; + } else if (this.returnThis) { + return context; + } + + return this.returnValue; + }, + + onCall: function onCall(index) { + return this.stub.onCall(index); + }, + + onFirstCall: function onFirstCall() { + return this.stub.onFirstCall(); + }, + + onSecondCall: function onSecondCall() { + return this.stub.onSecondCall(); + }, + + onThirdCall: function onThirdCall() { + return this.stub.onThirdCall(); + }, + + withArgs: function withArgs(/* arguments */) { + throw new Error( + "Defining a stub by invoking \"stub.onCall(...).withArgs(...)\" " + + "is not supported. Use \"stub.withArgs(...).onCall(...)\" " + + "to define sequential behavior for calls with certain arguments." + ); + }, + + callsArg: function callsArg(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOn: function callsArgOn(pos, context) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgWith: function callsArgWith(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOnWith: function callsArgWith(pos, context) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yields: function () { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsRight: function () { + this.callArgAt = useRightMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsOn: function (context) { + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsTo: function (prop) { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + yieldsToOn: function (prop, context) { + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + throws: throwsException, + throwsException: throwsException, + + returns: function returns(value) { + this.returnValue = value; + this.returnValueDefined = true; + this.exception = undefined; + + return this; + }, + + returnsArg: function returnsArg(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.returnArgAt = pos; + + return this; + }, + + returnsThis: function returnsThis() { + this.returnThis = true; + + return this; + } + }; + + function createAsyncVersion(syncFnName) { + return function () { + var result = this[syncFnName].apply(this, arguments); + this.callbackAsync = true; + return result; + }; + } + + // create asynchronous versions of callsArg* and yields* methods + for (var method in proto) { + // need to avoid creating anotherasync versions of the newly added async methods + if (proto.hasOwnProperty(method) && method.match(/^(callsArg|yields)/) && !method.match(/Async/)) { + proto[method + "Async"] = createAsyncVersion(method); + } + } + + sinon.behavior = proto; + return proto; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./extend"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + function walkInternal(obj, iterator, context, originalObj, seen) { + var proto, prop; + + if (typeof Object.getOwnPropertyNames !== "function") { + // We explicitly want to enumerate through all of the prototype's properties + // in this case, therefore we deliberately leave out an own property check. + /* eslint-disable guard-for-in */ + for (prop in obj) { + iterator.call(context, obj[prop], prop, obj); + } + /* eslint-enable guard-for-in */ + + return; + } + + Object.getOwnPropertyNames(obj).forEach(function (k) { + if (!seen[k]) { + seen[k] = true; + var target = typeof Object.getOwnPropertyDescriptor(obj, k).get === "function" ? + originalObj : obj; + iterator.call(context, target[k], k, target); + } + }); + + proto = Object.getPrototypeOf(obj); + if (proto) { + walkInternal(proto, iterator, context, originalObj, seen); + } + } + + /* Public: walks the prototype chain of an object and iterates over every own property + * name encountered. The iterator is called in the same fashion that Array.prototype.forEach + * works, where it is passed the value, key, and own object as the 1st, 2nd, and 3rd positional + * argument, respectively. In cases where Object.getOwnPropertyNames is not available, walk will + * default to using a simple for..in loop. + * + * obj - The object to walk the prototype chain for. + * iterator - The function to be called on each pass of the walk. + * context - (Optional) When given, the iterator will be called with this object as the receiver. + */ + function walk(obj, iterator, context) { + return walkInternal(obj, iterator, context, obj, {}); + } + + sinon.walk = walk; + return sinon.walk; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend extend.js + * @depend spy.js + * @depend behavior.js + * @depend walk.js + */ +/** + * Stub functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + function stub(object, property, func) { + if (!!func && typeof func !== "function" && typeof func !== "object") { + throw new TypeError("Custom stub should be a function or a property descriptor"); + } + + var wrapper; + + if (func) { + if (typeof func === "function") { + wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func; + } else { + wrapper = func; + if (sinon.spy && sinon.spy.create) { + var types = sinon.objectKeys(wrapper); + for (var i = 0; i < types.length; i++) { + wrapper[types[i]] = sinon.spy.create(wrapper[types[i]]); + } + } + } + } else { + var stubLength = 0; + if (typeof object === "object" && typeof object[property] === "function") { + stubLength = object[property].length; + } + wrapper = stub.create(stubLength); + } + + if (!object && typeof property === "undefined") { + return sinon.stub.create(); + } + + if (typeof property === "undefined" && typeof object === "object") { + sinon.walk(object || {}, function (value, prop, propOwner) { + // we don't want to stub things like toString(), valueOf(), etc. so we only stub if the object + // is not Object.prototype + if ( + propOwner !== Object.prototype && + prop !== "constructor" && + typeof sinon.getPropertyDescriptor(propOwner, prop).value === "function" + ) { + stub(object, prop); + } + }); + + return object; + } + + return sinon.wrapMethod(object, property, wrapper); + } + + + /*eslint-disable no-use-before-define*/ + function getParentBehaviour(stubInstance) { + return (stubInstance.parent && getCurrentBehavior(stubInstance.parent)); + } + + function getDefaultBehavior(stubInstance) { + return stubInstance.defaultBehavior || + getParentBehaviour(stubInstance) || + sinon.behavior.create(stubInstance); + } + + function getCurrentBehavior(stubInstance) { + var behavior = stubInstance.behaviors[stubInstance.callCount - 1]; + return behavior && behavior.isPresent() ? behavior : getDefaultBehavior(stubInstance); + } + /*eslint-enable no-use-before-define*/ + + var uuid = 0; + + var proto = { + create: function create(stubLength) { + var functionStub = function () { + return getCurrentBehavior(functionStub).invoke(this, arguments); + }; + + functionStub.id = "stub#" + uuid++; + var orig = functionStub; + functionStub = sinon.spy.create(functionStub, stubLength); + functionStub.func = orig; + + sinon.extend(functionStub, stub); + functionStub.instantiateFake = sinon.stub.create; + functionStub.displayName = "stub"; + functionStub.toString = sinon.functionToString; + + functionStub.defaultBehavior = null; + functionStub.behaviors = []; + + return functionStub; + }, + + resetBehavior: function () { + var i; + + this.defaultBehavior = null; + this.behaviors = []; + + delete this.returnValue; + delete this.returnArgAt; + this.returnThis = false; + + if (this.fakes) { + for (i = 0; i < this.fakes.length; i++) { + this.fakes[i].resetBehavior(); + } + } + }, + + onCall: function onCall(index) { + if (!this.behaviors[index]) { + this.behaviors[index] = sinon.behavior.create(this); + } + + return this.behaviors[index]; + }, + + onFirstCall: function onFirstCall() { + return this.onCall(0); + }, + + onSecondCall: function onSecondCall() { + return this.onCall(1); + }, + + onThirdCall: function onThirdCall() { + return this.onCall(2); + } + }; + + function createBehavior(behaviorMethod) { + return function () { + this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this); + this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments); + return this; + }; + } + + for (var method in sinon.behavior) { + if (sinon.behavior.hasOwnProperty(method) && + !proto.hasOwnProperty(method) && + method !== "create" && + method !== "withArgs" && + method !== "invoke") { + proto[method] = createBehavior(method); + } + } + + sinon.extend(stub, proto); + sinon.stub = stub; + + return stub; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./behavior"); + require("./spy"); + require("./extend"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend call.js + * @depend extend.js + * @depend match.js + * @depend spy.js + * @depend stub.js + * @depend format.js + */ +/** + * Mock functions. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + var push = [].push; + var match = sinon.match; + + function mock(object) { + // if (typeof console !== undefined && console.warn) { + // console.warn("mock will be removed from Sinon.JS v2.0"); + // } + + if (!object) { + return sinon.expectation.create("Anonymous mock"); + } + + return mock.create(object); + } + + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + + function arrayEquals(arr1, arr2, compareLength) { + if (compareLength && (arr1.length !== arr2.length)) { + return false; + } + + for (var i = 0, l = arr1.length; i < l; i++) { + if (!sinon.deepEqual(arr1[i], arr2[i])) { + return false; + } + } + return true; + } + + sinon.extend(mock, { + create: function create(object) { + if (!object) { + throw new TypeError("object is null"); + } + + var mockObject = sinon.extend({}, mock); + mockObject.object = object; + delete mockObject.create; + + return mockObject; + }, + + expects: function expects(method) { + if (!method) { + throw new TypeError("method is falsy"); + } + + if (!this.expectations) { + this.expectations = {}; + this.proxies = []; + } + + if (!this.expectations[method]) { + this.expectations[method] = []; + var mockObject = this; + + sinon.wrapMethod(this.object, method, function () { + return mockObject.invokeMethod(method, this, arguments); + }); + + push.call(this.proxies, method); + } + + var expectation = sinon.expectation.create(method); + push.call(this.expectations[method], expectation); + + return expectation; + }, + + restore: function restore() { + var object = this.object; + + each(this.proxies, function (proxy) { + if (typeof object[proxy].restore === "function") { + object[proxy].restore(); + } + }); + }, + + verify: function verify() { + var expectations = this.expectations || {}; + var messages = []; + var met = []; + + each(this.proxies, function (proxy) { + each(expectations[proxy], function (expectation) { + if (!expectation.met()) { + push.call(messages, expectation.toString()); + } else { + push.call(met, expectation.toString()); + } + }); + }); + + this.restore(); + + if (messages.length > 0) { + sinon.expectation.fail(messages.concat(met).join("\n")); + } else if (met.length > 0) { + sinon.expectation.pass(messages.concat(met).join("\n")); + } + + return true; + }, + + invokeMethod: function invokeMethod(method, thisValue, args) { + var expectations = this.expectations && this.expectations[method] ? this.expectations[method] : []; + var expectationsWithMatchingArgs = []; + var currentArgs = args || []; + var i, available; + + for (i = 0; i < expectations.length; i += 1) { + var expectedArgs = expectations[i].expectedArguments || []; + if (arrayEquals(expectedArgs, currentArgs, expectations[i].expectsExactArgCount)) { + expectationsWithMatchingArgs.push(expectations[i]); + } + } + + for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) { + if (!expectationsWithMatchingArgs[i].met() && + expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) { + return expectationsWithMatchingArgs[i].apply(thisValue, args); + } + } + + var messages = []; + var exhausted = 0; + + for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) { + if (expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) { + available = available || expectationsWithMatchingArgs[i]; + } else { + exhausted += 1; + } + } + + if (available && exhausted === 0) { + return available.apply(thisValue, args); + } + + for (i = 0; i < expectations.length; i += 1) { + push.call(messages, " " + expectations[i].toString()); + } + + messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({ + proxy: method, + args: args + })); + + sinon.expectation.fail(messages.join("\n")); + } + }); + + var times = sinon.timesInWords; + var slice = Array.prototype.slice; + + function callCountInWords(callCount) { + if (callCount === 0) { + return "never called"; + } + + return "called " + times(callCount); + } + + function expectedCallCountInWords(expectation) { + var min = expectation.minCalls; + var max = expectation.maxCalls; + + if (typeof min === "number" && typeof max === "number") { + var str = times(min); + + if (min !== max) { + str = "at least " + str + " and at most " + times(max); + } + + return str; + } + + if (typeof min === "number") { + return "at least " + times(min); + } + + return "at most " + times(max); + } + + function receivedMinCalls(expectation) { + var hasMinLimit = typeof expectation.minCalls === "number"; + return !hasMinLimit || expectation.callCount >= expectation.minCalls; + } + + function receivedMaxCalls(expectation) { + if (typeof expectation.maxCalls !== "number") { + return false; + } + + return expectation.callCount === expectation.maxCalls; + } + + function verifyMatcher(possibleMatcher, arg) { + var isMatcher = match && match.isMatcher(possibleMatcher); + + return isMatcher && possibleMatcher.test(arg) || true; + } + + sinon.expectation = { + minCalls: 1, + maxCalls: 1, + + create: function create(methodName) { + var expectation = sinon.extend(sinon.stub.create(), sinon.expectation); + delete expectation.create; + expectation.method = methodName; + + return expectation; + }, + + invoke: function invoke(func, thisValue, args) { + this.verifyCallAllowed(thisValue, args); + + return sinon.spy.invoke.apply(this, arguments); + }, + + atLeast: function atLeast(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.maxCalls = null; + this.limitsSet = true; + } + + this.minCalls = num; + + return this; + }, + + atMost: function atMost(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.minCalls = null; + this.limitsSet = true; + } + + this.maxCalls = num; + + return this; + }, + + never: function never() { + return this.exactly(0); + }, + + once: function once() { + return this.exactly(1); + }, + + twice: function twice() { + return this.exactly(2); + }, + + thrice: function thrice() { + return this.exactly(3); + }, + + exactly: function exactly(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not a number"); + } + + this.atLeast(num); + return this.atMost(num); + }, + + met: function met() { + return !this.failed && receivedMinCalls(this); + }, + + verifyCallAllowed: function verifyCallAllowed(thisValue, args) { + if (receivedMaxCalls(this)) { + this.failed = true; + sinon.expectation.fail(this.method + " already called " + times(this.maxCalls)); + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " + + this.expectedThis); + } + + if (!("expectedArguments" in this)) { + return; + } + + if (!args) { + sinon.expectation.fail(this.method + " received no arguments, expected " + + sinon.format(this.expectedArguments)); + } + + if (args.length < this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + if (this.expectsExactArgCount && + args.length !== this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", didn't match " + this.expectedArguments.toString()); + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", expected " + sinon.format(this.expectedArguments)); + } + } + }, + + allowsCall: function allowsCall(thisValue, args) { + if (this.met() && receivedMaxCalls(this)) { + return false; + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + return false; + } + + if (!("expectedArguments" in this)) { + return true; + } + + args = args || []; + + if (args.length < this.expectedArguments.length) { + return false; + } + + if (this.expectsExactArgCount && + args.length !== this.expectedArguments.length) { + return false; + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + return false; + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + return false; + } + } + + return true; + }, + + withArgs: function withArgs() { + this.expectedArguments = slice.call(arguments); + return this; + }, + + withExactArgs: function withExactArgs() { + this.withArgs.apply(this, arguments); + this.expectsExactArgCount = true; + return this; + }, + + on: function on(thisValue) { + this.expectedThis = thisValue; + return this; + }, + + toString: function () { + var args = (this.expectedArguments || []).slice(); + + if (!this.expectsExactArgCount) { + push.call(args, "[...]"); + } + + var callStr = sinon.spyCall.toString.call({ + proxy: this.method || "anonymous mock expectation", + args: args + }); + + var message = callStr.replace(", [...", "[, ...") + " " + + expectedCallCountInWords(this); + + if (this.met()) { + return "Expectation met: " + message; + } + + return "Expected " + message + " (" + + callCountInWords(this.callCount) + ")"; + }, + + verify: function verify() { + if (!this.met()) { + sinon.expectation.fail(this.toString()); + } else { + sinon.expectation.pass(this.toString()); + } + + return true; + }, + + pass: function pass(message) { + sinon.assert.pass(message); + }, + + fail: function fail(message) { + var exception = new Error(message); + exception.name = "ExpectationError"; + + throw exception; + } + }; + + sinon.mock = mock; + return mock; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./times_in_words"); + require("./call"); + require("./extend"); + require("./match"); + require("./spy"); + require("./stub"); + require("./format"); + + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend spy.js + * @depend stub.js + * @depend mock.js + */ +/** + * Collections of stubs, spies and mocks. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + var push = [].push; + var hasOwnProperty = Object.prototype.hasOwnProperty; + + function getFakes(fakeCollection) { + if (!fakeCollection.fakes) { + fakeCollection.fakes = []; + } + + return fakeCollection.fakes; + } + + function each(fakeCollection, method) { + var fakes = getFakes(fakeCollection); + + for (var i = 0, l = fakes.length; i < l; i += 1) { + if (typeof fakes[i][method] === "function") { + fakes[i][method](); + } + } + } + + function compact(fakeCollection) { + var fakes = getFakes(fakeCollection); + var i = 0; + while (i < fakes.length) { + fakes.splice(i, 1); + } + } + + function makeApi(sinon) { + var collection = { + verify: function resolve() { + each(this, "verify"); + }, + + restore: function restore() { + each(this, "restore"); + compact(this); + }, + + reset: function restore() { + each(this, "reset"); + }, + + verifyAndRestore: function verifyAndRestore() { + var exception; + + try { + this.verify(); + } catch (e) { + exception = e; + } + + this.restore(); + + if (exception) { + throw exception; + } + }, + + add: function add(fake) { + push.call(getFakes(this), fake); + return fake; + }, + + spy: function spy() { + return this.add(sinon.spy.apply(sinon, arguments)); + }, + + stub: function stub(object, property, value) { + if (property) { + var original = object[property]; + + if (typeof original !== "function") { + if (!hasOwnProperty.call(object, property)) { + throw new TypeError("Cannot stub non-existent own property " + property); + } + + object[property] = value; + + return this.add({ + restore: function () { + object[property] = original; + } + }); + } + } + if (!property && !!object && typeof object === "object") { + var stubbedObj = sinon.stub.apply(sinon, arguments); + + for (var prop in stubbedObj) { + if (typeof stubbedObj[prop] === "function") { + this.add(stubbedObj[prop]); + } + } + + return stubbedObj; + } + + return this.add(sinon.stub.apply(sinon, arguments)); + }, + + mock: function mock() { + return this.add(sinon.mock.apply(sinon, arguments)); + }, + + inject: function inject(obj) { + var col = this; + + obj.spy = function () { + return col.spy.apply(col, arguments); + }; + + obj.stub = function () { + return col.stub.apply(col, arguments); + }; + + obj.mock = function () { + return col.mock.apply(col, arguments); + }; + + return obj; + } + }; + + sinon.collection = collection; + return collection; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./mock"); + require("./spy"); + require("./stub"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * Fake timer API + * setTimeout + * setInterval + * clearTimeout + * clearInterval + * tick + * reset + * Date + * + * Inspired by jsUnitMockTimeOut from JsUnit + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + function makeApi(s, lol) { + /*global lolex */ + var llx = typeof lolex !== "undefined" ? lolex : lol; + + s.useFakeTimers = function () { + var now; + var methods = Array.prototype.slice.call(arguments); + + if (typeof methods[0] === "string") { + now = 0; + } else { + now = methods.shift(); + } + + var clock = llx.install(now || 0, methods); + clock.restore = clock.uninstall; + return clock; + }; + + s.clock = { + create: function (now) { + return llx.createClock(now); + } + }; + + s.timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), + clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined), + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, epxorts, module, lolex) { + var core = require("./core"); + makeApi(core, lolex); + module.exports = core; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module, require("lolex")); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * Minimal Event interface implementation + * + * Original implementation by Sven Fuchs: https://gist.github.com/995028 + * Modifications and tests by Christian Johansen. + * + * @author Sven Fuchs (svenfuchs@artweb-design.de) + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2011 Sven Fuchs, Christian Johansen + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +(function () { + + var push = [].push; + + function makeApi(sinon) { + sinon.Event = function Event(type, bubbles, cancelable, target) { + this.initEvent(type, bubbles, cancelable, target); + }; + + sinon.Event.prototype = { + initEvent: function (type, bubbles, cancelable, target) { + this.type = type; + this.bubbles = bubbles; + this.cancelable = cancelable; + this.target = target; + }, + + stopPropagation: function () {}, + + preventDefault: function () { + this.defaultPrevented = true; + } + }; + + sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) { + this.initEvent(type, false, false, target); + this.loaded = progressEventRaw.loaded || null; + this.total = progressEventRaw.total || null; + this.lengthComputable = !!progressEventRaw.total; + }; + + sinon.ProgressEvent.prototype = new sinon.Event(); + + sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent; + + sinon.CustomEvent = function CustomEvent(type, customData, target) { + this.initEvent(type, false, false, target); + this.detail = customData.detail || null; + }; + + sinon.CustomEvent.prototype = new sinon.Event(); + + sinon.CustomEvent.prototype.constructor = sinon.CustomEvent; + + sinon.EventTarget = { + addEventListener: function addEventListener(event, listener) { + this.eventListeners = this.eventListeners || {}; + this.eventListeners[event] = this.eventListeners[event] || []; + push.call(this.eventListeners[event], listener); + }, + + removeEventListener: function removeEventListener(event, listener) { + var listeners = this.eventListeners && this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }, + + dispatchEvent: function dispatchEvent(event) { + var type = event.type; + var listeners = this.eventListeners && this.eventListeners[type] || []; + + for (var i = 0; i < listeners.length; i++) { + if (typeof listeners[i] === "function") { + listeners[i].call(this, event); + } else { + listeners[i].handleEvent(event); + } + } + + return !!event.defaultPrevented; + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend util/core.js + */ +/** + * Logs errors + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal) { + + // cache a reference to setTimeout, so that our reference won't be stubbed out + // when using fake timers and errors will still get logged + // https://github.com/cjohansen/Sinon.JS/issues/381 + var realSetTimeout = setTimeout; + + function makeApi(sinon) { + + function log() {} + + function logError(label, err) { + var msg = label + " threw exception: "; + + function throwLoggedError() { + err.message = msg + err.message; + throw err; + } + + sinon.log(msg + "[" + err.name + "] " + err.message); + + if (err.stack) { + sinon.log(err.stack); + } + + if (logError.useImmediateExceptions) { + throwLoggedError(); + } else { + logError.setTimeout(throwLoggedError, 0); + } + } + + // When set to true, any errors logged will be thrown immediately; + // If set to false, the errors will be thrown in separate execution frame. + logError.useImmediateExceptions = false; + + // wrap realSetTimeout with something we can stub in tests + logError.setTimeout = function (func, timeout) { + realSetTimeout(func, timeout); + }; + + var exports = {}; + exports.log = sinon.log = log; + exports.logError = sinon.logError = logError; + + return exports; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XDomainRequest object + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +// wrapper for global +(function (global) { + + var xdr = { XDomainRequest: global.XDomainRequest }; + xdr.GlobalXDomainRequest = global.XDomainRequest; + xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== "undefined"; + xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false; + + function makeApi(sinon) { + sinon.xdr = xdr; + + function FakeXDomainRequest() { + this.readyState = FakeXDomainRequest.UNSENT; + this.requestBody = null; + this.requestHeaders = {}; + this.status = 0; + this.timeout = null; + + if (typeof FakeXDomainRequest.onCreate === "function") { + FakeXDomainRequest.onCreate(this); + } + } + + function verifyState(x) { + if (x.readyState !== FakeXDomainRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (x.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function verifyRequestSent(x) { + if (x.readyState === FakeXDomainRequest.UNSENT) { + throw new Error("Request not sent"); + } + if (x.readyState === FakeXDomainRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XDomainRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, { + open: function open(method, url) { + this.method = method; + this.url = url; + + this.responseText = null; + this.sendFlag = false; + + this.readyStateChange(FakeXDomainRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + var eventName = ""; + switch (this.readyState) { + case FakeXDomainRequest.UNSENT: + break; + case FakeXDomainRequest.OPENED: + break; + case FakeXDomainRequest.LOADING: + if (this.sendFlag) { + //raise the progress event + eventName = "onprogress"; + } + break; + case FakeXDomainRequest.DONE: + if (this.isTimeout) { + eventName = "ontimeout"; + } else if (this.errorFlag || (this.status < 200 || this.status > 299)) { + eventName = "onerror"; + } else { + eventName = "onload"; + } + break; + } + + // raising event (if defined) + if (eventName) { + if (typeof this[eventName] === "function") { + try { + this[eventName](); + } catch (e) { + sinon.logError("Fake XHR " + eventName + " handler", e); + } + } + } + }, + + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + this.requestBody = data; + } + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + + this.errorFlag = false; + this.sendFlag = true; + this.readyStateChange(FakeXDomainRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + + if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) { + this.readyStateChange(sinon.FakeXDomainRequest.DONE); + this.sendFlag = false; + } + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyResponseBodyType(body); + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + this.readyStateChange(FakeXDomainRequest.LOADING); + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + this.readyStateChange(FakeXDomainRequest.DONE); + }, + + respond: function respond(status, contentType, body) { + // content-type ignored, since XDomainRequest does not carry this + // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease + // test integration across browsers + this.status = typeof status === "number" ? status : 200; + this.setResponseBody(body || ""); + }, + + simulatetimeout: function simulatetimeout() { + this.status = 0; + this.isTimeout = true; + // Access to this should actually throw an error + this.responseText = undefined; + this.readyStateChange(FakeXDomainRequest.DONE); + } + }); + + sinon.extend(FakeXDomainRequest, { + UNSENT: 0, + OPENED: 1, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXDomainRequest = function useFakeXDomainRequest() { + sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) { + if (xdr.supportsXDR) { + global.XDomainRequest = xdr.GlobalXDomainRequest; + } + + delete sinon.FakeXDomainRequest.restore; + + if (keepOnCreate !== true) { + delete sinon.FakeXDomainRequest.onCreate; + } + }; + if (xdr.supportsXDR) { + global.XDomainRequest = sinon.FakeXDomainRequest; + } + return sinon.FakeXDomainRequest; + }; + + sinon.FakeXDomainRequest = FakeXDomainRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +})(typeof global !== "undefined" ? global : self); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XMLHttpRequest object + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal, global) { + + function getWorkingXHR(globalScope) { + var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined"; + if (supportsXHR) { + return globalScope.XMLHttpRequest; + } + + var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined"; + if (supportsActiveX) { + return function () { + return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0"); + }; + } + + return false; + } + + var supportsProgress = typeof ProgressEvent !== "undefined"; + var supportsCustomEvent = typeof CustomEvent !== "undefined"; + var supportsFormData = typeof FormData !== "undefined"; + var supportsArrayBuffer = typeof ArrayBuffer !== "undefined"; + var supportsBlob = typeof Blob === "function"; + var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest }; + sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest; + sinonXhr.GlobalActiveXObject = global.ActiveXObject; + sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== "undefined"; + sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined"; + sinonXhr.workingXHR = getWorkingXHR(global); + sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest()); + + var unsafeHeaders = { + "Accept-Charset": true, + "Accept-Encoding": true, + Connection: true, + "Content-Length": true, + Cookie: true, + Cookie2: true, + "Content-Transfer-Encoding": true, + Date: true, + Expect: true, + Host: true, + "Keep-Alive": true, + Referer: true, + TE: true, + Trailer: true, + "Transfer-Encoding": true, + Upgrade: true, + "User-Agent": true, + Via: true + }; + + // An upload object is created for each + // FakeXMLHttpRequest and allows upload + // events to be simulated using uploadProgress + // and uploadError. + function UploadProgress() { + this.eventListeners = { + progress: [], + load: [], + abort: [], + error: [] + }; + } + + UploadProgress.prototype.addEventListener = function addEventListener(event, listener) { + this.eventListeners[event].push(listener); + }; + + UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) { + var listeners = this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }; + + UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) { + var listeners = this.eventListeners[event.type] || []; + + for (var i = 0, listener; (listener = listeners[i]) != null; i++) { + listener(event); + } + }; + + // Note that for FakeXMLHttpRequest to work pre ES5 + // we lose some of the alignment with the spec. + // To ensure as close a match as possible, + // set responseType before calling open, send or respond; + function FakeXMLHttpRequest() { + this.readyState = FakeXMLHttpRequest.UNSENT; + this.requestHeaders = {}; + this.requestBody = null; + this.status = 0; + this.statusText = ""; + this.upload = new UploadProgress(); + this.responseType = ""; + this.response = ""; + if (sinonXhr.supportsCORS) { + this.withCredentials = false; + } + + var xhr = this; + var events = ["loadstart", "load", "abort", "loadend"]; + + function addEventListener(eventName) { + xhr.addEventListener(eventName, function (event) { + var listener = xhr["on" + eventName]; + + if (listener && typeof listener === "function") { + listener.call(this, event); + } + }); + } + + for (var i = events.length - 1; i >= 0; i--) { + addEventListener(events[i]); + } + + if (typeof FakeXMLHttpRequest.onCreate === "function") { + FakeXMLHttpRequest.onCreate(this); + } + } + + function verifyState(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xhr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function getHeader(headers, header) { + header = header.toLowerCase(); + + for (var h in headers) { + if (h.toLowerCase() === header) { + return h; + } + } + + return null; + } + + // filtering to enable a white-list version of Sinon FakeXhr, + // where whitelisted requests are passed through to real XHR + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + function some(collection, callback) { + for (var index = 0; index < collection.length; index++) { + if (callback(collection[index]) === true) { + return true; + } + } + return false; + } + // largest arity in XHR is 5 - XHR#open + var apply = function (obj, method, args) { + switch (args.length) { + case 0: return obj[method](); + case 1: return obj[method](args[0]); + case 2: return obj[method](args[0], args[1]); + case 3: return obj[method](args[0], args[1], args[2]); + case 4: return obj[method](args[0], args[1], args[2], args[3]); + case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]); + } + }; + + FakeXMLHttpRequest.filters = []; + FakeXMLHttpRequest.addFilter = function addFilter(fn) { + this.filters.push(fn); + }; + var IE6Re = /MSIE 6/; + FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) { + var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap + + each([ + "open", + "setRequestHeader", + "send", + "abort", + "getResponseHeader", + "getAllResponseHeaders", + "addEventListener", + "overrideMimeType", + "removeEventListener" + ], function (method) { + fakeXhr[method] = function () { + return apply(xhr, method, arguments); + }; + }); + + var copyAttrs = function (args) { + each(args, function (attr) { + try { + fakeXhr[attr] = xhr[attr]; + } catch (e) { + if (!IE6Re.test(navigator.userAgent)) { + throw e; + } + } + }); + }; + + var stateChange = function stateChange() { + fakeXhr.readyState = xhr.readyState; + if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { + copyAttrs(["status", "statusText"]); + } + if (xhr.readyState >= FakeXMLHttpRequest.LOADING) { + copyAttrs(["responseText", "response"]); + } + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + copyAttrs(["responseXML"]); + } + if (fakeXhr.onreadystatechange) { + fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr }); + } + }; + + if (xhr.addEventListener) { + for (var event in fakeXhr.eventListeners) { + if (fakeXhr.eventListeners.hasOwnProperty(event)) { + + /*eslint-disable no-loop-func*/ + each(fakeXhr.eventListeners[event], function (handler) { + xhr.addEventListener(event, handler); + }); + /*eslint-enable no-loop-func*/ + } + } + xhr.addEventListener("readystatechange", stateChange); + } else { + xhr.onreadystatechange = stateChange; + } + apply(xhr, "open", xhrArgs); + }; + FakeXMLHttpRequest.useFilters = false; + + function verifyRequestOpened(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR - " + xhr.readyState); + } + } + + function verifyRequestSent(xhr) { + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyHeadersReceived(xhr) { + if (xhr.async && xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) { + throw new Error("No headers received"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XMLHttpRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + function convertToArrayBuffer(body) { + var buffer = new ArrayBuffer(body.length); + var view = new Uint8Array(buffer); + for (var i = 0; i < body.length; i++) { + var charCode = body.charCodeAt(i); + if (charCode >= 256) { + throw new TypeError("arraybuffer or blob responseTypes require binary string, " + + "invalid character " + body[i] + " found."); + } + view[i] = charCode; + } + return buffer; + } + + function isXmlContentType(contentType) { + return !contentType || /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType); + } + + function convertResponseBody(responseType, contentType, body) { + if (responseType === "" || responseType === "text") { + return body; + } else if (supportsArrayBuffer && responseType === "arraybuffer") { + return convertToArrayBuffer(body); + } else if (responseType === "json") { + try { + return JSON.parse(body); + } catch (e) { + // Return parsing failure as null + return null; + } + } else if (supportsBlob && responseType === "blob") { + var blobOptions = {}; + if (contentType) { + blobOptions.type = contentType; + } + return new Blob([convertToArrayBuffer(body)], blobOptions); + } else if (responseType === "document") { + if (isXmlContentType(contentType)) { + return FakeXMLHttpRequest.parseXML(body); + } + return null; + } + throw new Error("Invalid responseType " + responseType); + } + + function clearResponse(xhr) { + if (xhr.responseType === "" || xhr.responseType === "text") { + xhr.response = xhr.responseText = ""; + } else { + xhr.response = xhr.responseText = null; + } + xhr.responseXML = null; + } + + FakeXMLHttpRequest.parseXML = function parseXML(text) { + // Treat empty string as parsing failure + if (text !== "") { + try { + if (typeof DOMParser !== "undefined") { + var parser = new DOMParser(); + return parser.parseFromString(text, "text/xml"); + } + var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(text); + return xmlDoc; + } catch (e) { + // Unable to parse XML - no biggie + } + } + + return null; + }; + + FakeXMLHttpRequest.statusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 300: "Multiple Choice", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 422: "Unprocessable Entity", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" + }; + + function makeApi(sinon) { + sinon.xhr = sinonXhr; + + sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { + async: true, + + open: function open(method, url, async, username, password) { + this.method = method; + this.url = url; + this.async = typeof async === "boolean" ? async : true; + this.username = username; + this.password = password; + clearResponse(this); + this.requestHeaders = {}; + this.sendFlag = false; + + if (FakeXMLHttpRequest.useFilters === true) { + var xhrArgs = arguments; + var defake = some(FakeXMLHttpRequest.filters, function (filter) { + return filter.apply(this, xhrArgs); + }); + if (defake) { + return FakeXMLHttpRequest.defake(this, arguments); + } + } + this.readyStateChange(FakeXMLHttpRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + + var readyStateChangeEvent = new sinon.Event("readystatechange", false, false, this); + + if (typeof this.onreadystatechange === "function") { + try { + this.onreadystatechange(readyStateChangeEvent); + } catch (e) { + sinon.logError("Fake XHR onreadystatechange handler", e); + } + } + + switch (this.readyState) { + case FakeXMLHttpRequest.DONE: + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + this.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + } + this.upload.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("loadend", false, false, this)); + break; + } + + this.dispatchEvent(readyStateChangeEvent); + }, + + setRequestHeader: function setRequestHeader(header, value) { + verifyState(this); + + if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { + throw new Error("Refused to set unsafe header \"" + header + "\""); + } + + if (this.requestHeaders[header]) { + this.requestHeaders[header] += "," + value; + } else { + this.requestHeaders[header] = value; + } + }, + + // Helps testing + setResponseHeaders: function setResponseHeaders(headers) { + verifyRequestOpened(this); + this.responseHeaders = {}; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + this.responseHeaders[header] = headers[header]; + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); + } else { + this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; + } + }, + + // Currently treats ALL data as a DOMString (i.e. no Document) + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + var contentType = getHeader(this.requestHeaders, "Content-Type"); + if (this.requestHeaders[contentType]) { + var value = this.requestHeaders[contentType].split(";"); + this.requestHeaders[contentType] = value[0] + ";charset=utf-8"; + } else if (supportsFormData && !(data instanceof FormData)) { + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + } + + this.requestBody = data; + } + + this.errorFlag = false; + this.sendFlag = this.async; + clearResponse(this); + this.readyStateChange(FakeXMLHttpRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + + this.dispatchEvent(new sinon.Event("loadstart", false, false, this)); + }, + + abort: function abort() { + this.aborted = true; + clearResponse(this); + this.errorFlag = true; + this.requestHeaders = {}; + this.responseHeaders = {}; + + if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) { + this.readyStateChange(FakeXMLHttpRequest.DONE); + this.sendFlag = false; + } + + this.readyState = FakeXMLHttpRequest.UNSENT; + + this.dispatchEvent(new sinon.Event("abort", false, false, this)); + + this.upload.dispatchEvent(new sinon.Event("abort", false, false, this)); + + if (typeof this.onerror === "function") { + this.onerror(); + } + }, + + getResponseHeader: function getResponseHeader(header) { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return null; + } + + if (/^Set-Cookie2?$/i.test(header)) { + return null; + } + + header = getHeader(this.responseHeaders, header); + + return this.responseHeaders[header] || null; + }, + + getAllResponseHeaders: function getAllResponseHeaders() { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return ""; + } + + var headers = ""; + + for (var header in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(header) && + !/^Set-Cookie2?$/i.test(header)) { + headers += header + ": " + this.responseHeaders[header] + "\r\n"; + } + } + + return headers; + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyHeadersReceived(this); + verifyResponseBodyType(body); + var contentType = this.getResponseHeader("Content-Type"); + + var isTextResponse = this.responseType === "" || this.responseType === "text"; + clearResponse(this); + if (this.async) { + var chunkSize = this.chunkSize || 10; + var index = 0; + + do { + this.readyStateChange(FakeXMLHttpRequest.LOADING); + + if (isTextResponse) { + this.responseText = this.response += body.substring(index, index + chunkSize); + } + index += chunkSize; + } while (index < body.length); + } + + this.response = convertResponseBody(this.responseType, contentType, body); + if (isTextResponse) { + this.responseText = this.response; + } + + if (this.responseType === "document") { + this.responseXML = this.response; + } else if (this.responseType === "" && isXmlContentType(contentType)) { + this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); + } + this.readyStateChange(FakeXMLHttpRequest.DONE); + }, + + respond: function respond(status, headers, body) { + this.status = typeof status === "number" ? status : 200; + this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; + this.setResponseHeaders(headers || {}); + this.setResponseBody(body || ""); + }, + + uploadProgress: function uploadProgress(progressEventRaw) { + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + downloadProgress: function downloadProgress(progressEventRaw) { + if (supportsProgress) { + this.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + uploadError: function uploadError(error) { + if (supportsCustomEvent) { + this.upload.dispatchEvent(new sinon.CustomEvent("error", {detail: error})); + } + } + }); + + sinon.extend(FakeXMLHttpRequest, { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXMLHttpRequest = function () { + FakeXMLHttpRequest.restore = function restore(keepOnCreate) { + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = sinonXhr.GlobalActiveXObject; + } + + delete FakeXMLHttpRequest.restore; + + if (keepOnCreate !== true) { + delete FakeXMLHttpRequest.onCreate; + } + }; + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = FakeXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = function ActiveXObject(objId) { + if (objId === "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { + + return new FakeXMLHttpRequest(); + } + + return new sinonXhr.GlobalActiveXObject(objId); + }; + } + + return FakeXMLHttpRequest; + }; + + sinon.FakeXMLHttpRequest = FakeXMLHttpRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof global !== "undefined" ? global : self +)); + +/** + * @depend fake_xdomain_request.js + * @depend fake_xml_http_request.js + * @depend ../format.js + * @depend ../log_error.js + */ +/** + * The Sinon "server" mimics a web server that receives requests from + * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, + * both synchronously and asynchronously. To respond synchronuously, canned + * answers have to be provided upfront. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + var push = [].push; + + function responseArray(handler) { + var response = handler; + + if (Object.prototype.toString.call(handler) !== "[object Array]") { + response = [200, {}, handler]; + } + + if (typeof response[2] !== "string") { + throw new TypeError("Fake server response body should be string, but was " + + typeof response[2]); + } + + return response; + } + + var wloc = typeof window !== "undefined" ? window.location : {}; + var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host); + + function matchOne(response, reqMethod, reqUrl) { + var rmeth = response.method; + var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase(); + var url = response.url; + var matchUrl = !url || url === reqUrl || (typeof url.test === "function" && url.test(reqUrl)); + + return matchMethod && matchUrl; + } + + function match(response, request) { + var requestUrl = request.url; + + if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { + requestUrl = requestUrl.replace(rCurrLoc, ""); + } + + if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { + if (typeof response.response === "function") { + var ru = response.url; + var args = [request].concat(ru && typeof ru.exec === "function" ? ru.exec(requestUrl).slice(1) : []); + return response.response.apply(response, args); + } + + return true; + } + + return false; + } + + function makeApi(sinon) { + sinon.fakeServer = { + create: function (config) { + var server = sinon.create(this); + server.configure(config); + if (!sinon.xhr.supportsCORS) { + this.xhr = sinon.useFakeXDomainRequest(); + } else { + this.xhr = sinon.useFakeXMLHttpRequest(); + } + server.requests = []; + + this.xhr.onCreate = function (xhrObj) { + server.addRequest(xhrObj); + }; + + return server; + }, + configure: function (config) { + var whitelist = { + "autoRespond": true, + "autoRespondAfter": true, + "respondImmediately": true, + "fakeHTTPMethods": true + }; + var setting; + + config = config || {}; + for (setting in config) { + if (whitelist.hasOwnProperty(setting) && config.hasOwnProperty(setting)) { + this[setting] = config[setting]; + } + } + }, + addRequest: function addRequest(xhrObj) { + var server = this; + push.call(this.requests, xhrObj); + + xhrObj.onSend = function () { + server.handleRequest(this); + + if (server.respondImmediately) { + server.respond(); + } else if (server.autoRespond && !server.responding) { + setTimeout(function () { + server.responding = false; + server.respond(); + }, server.autoRespondAfter || 10); + + server.responding = true; + } + }; + }, + + getHTTPMethod: function getHTTPMethod(request) { + if (this.fakeHTTPMethods && /post/i.test(request.method)) { + var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); + return matches ? matches[1] : request.method; + } + + return request.method; + }, + + handleRequest: function handleRequest(xhr) { + if (xhr.async) { + if (!this.queue) { + this.queue = []; + } + + push.call(this.queue, xhr); + } else { + this.processRequest(xhr); + } + }, + + log: function log(response, request) { + var str; + + str = "Request:\n" + sinon.format(request) + "\n\n"; + str += "Response:\n" + sinon.format(response) + "\n\n"; + + sinon.log(str); + }, + + respondWith: function respondWith(method, url, body) { + if (arguments.length === 1 && typeof method !== "function") { + this.response = responseArray(method); + return; + } + + if (!this.responses) { + this.responses = []; + } + + if (arguments.length === 1) { + body = method; + url = method = null; + } + + if (arguments.length === 2) { + body = url; + url = method; + method = null; + } + + push.call(this.responses, { + method: method, + url: url, + response: typeof body === "function" ? body : responseArray(body) + }); + }, + + respond: function respond() { + if (arguments.length > 0) { + this.respondWith.apply(this, arguments); + } + + var queue = this.queue || []; + var requests = queue.splice(0, queue.length); + + for (var i = 0; i < requests.length; i++) { + this.processRequest(requests[i]); + } + }, + + processRequest: function processRequest(request) { + try { + if (request.aborted) { + return; + } + + var response = this.response || [404, {}, ""]; + + if (this.responses) { + for (var l = this.responses.length, i = l - 1; i >= 0; i--) { + if (match.call(this, this.responses[i], request)) { + response = this.responses[i].response; + break; + } + } + } + + if (request.readyState !== 4) { + this.log(response, request); + + request.respond(response[0], response[1], response[2]); + } + } catch (e) { + sinon.logError("Fake server request processing", e); + } + }, + + restore: function restore() { + return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("./fake_xdomain_request"); + require("./fake_xml_http_request"); + require("../format"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend fake_server.js + * @depend fake_timers.js + */ +/** + * Add-on for sinon.fakeServer that automatically handles a fake timer along with + * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery + * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, + * it polls the object for completion with setInterval. Dispite the direct + * motivation, there is nothing jQuery-specific in this file, so it can be used + * in any environment where the ajax implementation depends on setInterval or + * setTimeout. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + function makeApi(sinon) { + function Server() {} + Server.prototype = sinon.fakeServer; + + sinon.fakeServerWithClock = new Server(); + + sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { + if (xhr.async) { + if (typeof setTimeout.clock === "object") { + this.clock = setTimeout.clock; + } else { + this.clock = sinon.useFakeTimers(); + this.resetClock = true; + } + + if (!this.longestTimeout) { + var clockSetTimeout = this.clock.setTimeout; + var clockSetInterval = this.clock.setInterval; + var server = this; + + this.clock.setTimeout = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetTimeout.apply(this, arguments); + }; + + this.clock.setInterval = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetInterval.apply(this, arguments); + }; + } + } + + return sinon.fakeServer.addRequest.call(this, xhr); + }; + + sinon.fakeServerWithClock.respond = function respond() { + var returnVal = sinon.fakeServer.respond.apply(this, arguments); + + if (this.clock) { + this.clock.tick(this.longestTimeout || 0); + this.longestTimeout = 0; + + if (this.resetClock) { + this.clock.restore(); + this.resetClock = false; + } + } + + return returnVal; + }; + + sinon.fakeServerWithClock.restore = function restore() { + if (this.clock) { + this.clock.restore(); + } + + return sinon.fakeServer.restore.apply(this, arguments); + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + require("./fake_server"); + require("./fake_timers"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend util/core.js + * @depend extend.js + * @depend collection.js + * @depend util/fake_timers.js + * @depend util/fake_server_with_clock.js + */ +/** + * Manages fake collections as well as fake utilities such as Sinon's + * timers and fake XHR implementation in one convenient object. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + var push = [].push; + + function exposeValue(sandbox, config, key, value) { + if (!value) { + return; + } + + if (config.injectInto && !(key in config.injectInto)) { + config.injectInto[key] = value; + sandbox.injectedKeys.push(key); + } else { + push.call(sandbox.args, value); + } + } + + function prepareSandboxFromConfig(config) { + var sandbox = sinon.create(sinon.sandbox); + + if (config.useFakeServer) { + if (typeof config.useFakeServer === "object") { + sandbox.serverPrototype = config.useFakeServer; + } + + sandbox.useFakeServer(); + } + + if (config.useFakeTimers) { + if (typeof config.useFakeTimers === "object") { + sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers); + } else { + sandbox.useFakeTimers(); + } + } + + return sandbox; + } + + sinon.sandbox = sinon.extend(sinon.create(sinon.collection), { + useFakeTimers: function useFakeTimers() { + this.clock = sinon.useFakeTimers.apply(sinon, arguments); + + return this.add(this.clock); + }, + + serverPrototype: sinon.fakeServer, + + useFakeServer: function useFakeServer() { + var proto = this.serverPrototype || sinon.fakeServer; + + if (!proto || !proto.create) { + return null; + } + + this.server = proto.create(); + return this.add(this.server); + }, + + inject: function (obj) { + sinon.collection.inject.call(this, obj); + + if (this.clock) { + obj.clock = this.clock; + } + + if (this.server) { + obj.server = this.server; + obj.requests = this.server.requests; + } + + obj.match = sinon.match; + + return obj; + }, + + restore: function () { + sinon.collection.restore.apply(this, arguments); + this.restoreContext(); + }, + + restoreContext: function () { + if (this.injectedKeys) { + for (var i = 0, j = this.injectedKeys.length; i < j; i++) { + delete this.injectInto[this.injectedKeys[i]]; + } + this.injectedKeys = []; + } + }, + + create: function (config) { + if (!config) { + return sinon.create(sinon.sandbox); + } + + var sandbox = prepareSandboxFromConfig(config); + sandbox.args = sandbox.args || []; + sandbox.injectedKeys = []; + sandbox.injectInto = config.injectInto; + var prop, + value; + var exposed = sandbox.inject({}); + + if (config.properties) { + for (var i = 0, l = config.properties.length; i < l; i++) { + prop = config.properties[i]; + value = exposed[prop] || prop === "sandbox" && sandbox; + exposeValue(sandbox, config, prop, value); + } + } else { + exposeValue(sandbox, config, "sandbox", value); + } + + return sandbox; + }, + + match: sinon.match + }); + + sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer; + + return sinon.sandbox; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./extend"); + require("./util/fake_server_with_clock"); + require("./util/fake_timers"); + require("./collection"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend sandbox.js + */ +/** + * Test function, sandboxes fakes + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + var slice = Array.prototype.slice; + + function test(callback) { + var type = typeof callback; + + if (type !== "function") { + throw new TypeError("sinon.test needs to wrap a test function, got " + type); + } + + function sinonSandboxedTest() { + var config = sinon.getConfig(sinon.config); + config.injectInto = config.injectIntoThis && this || config.injectInto; + var sandbox = sinon.sandbox.create(config); + var args = slice.call(arguments); + var oldDone = args.length && args[args.length - 1]; + var exception, result; + + if (typeof oldDone === "function") { + args[args.length - 1] = function sinonDone(res) { + if (res) { + sandbox.restore(); + } else { + sandbox.verifyAndRestore(); + } + oldDone(res); + }; + } + + try { + result = callback.apply(this, args.concat(sandbox.args)); + } catch (e) { + exception = e; + } + + if (typeof oldDone !== "function") { + if (typeof exception !== "undefined") { + sandbox.restore(); + throw exception; + } else { + sandbox.verifyAndRestore(); + } + } + + return result; + } + + if (callback.length) { + return function sinonAsyncSandboxedTest(done) { // eslint-disable-line no-unused-vars + return sinonSandboxedTest.apply(this, arguments); + }; + } + + return sinonSandboxedTest; + } + + test.config = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.test = test; + return test; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./sandbox"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (sinonGlobal) { + makeApi(sinonGlobal); + } +}(typeof sinon === "object" && sinon || null)); // eslint-disable-line no-undef + +/** + * @depend util/core.js + * @depend test.js + */ +/** + * Test case, sandboxes all test functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function createTest(property, setUp, tearDown) { + return function () { + if (setUp) { + setUp.apply(this, arguments); + } + + var exception, result; + + try { + result = property.apply(this, arguments); + } catch (e) { + exception = e; + } + + if (tearDown) { + tearDown.apply(this, arguments); + } + + if (exception) { + throw exception; + } + + return result; + }; + } + + function makeApi(sinon) { + function testCase(tests, prefix) { + if (!tests || typeof tests !== "object") { + throw new TypeError("sinon.testCase needs an object with test functions"); + } + + prefix = prefix || "test"; + var rPrefix = new RegExp("^" + prefix); + var methods = {}; + var setUp = tests.setUp; + var tearDown = tests.tearDown; + var testName, + property, + method; + + for (testName in tests) { + if (tests.hasOwnProperty(testName) && !/^(setUp|tearDown)$/.test(testName)) { + property = tests[testName]; + + if (typeof property === "function" && rPrefix.test(testName)) { + method = property; + + if (setUp || tearDown) { + method = createTest(property, setUp, tearDown); + } + + methods[testName] = sinon.test(method); + } else { + methods[testName] = tests[testName]; + } + } + } + + return methods; + } + + sinon.testCase = testCase; + return testCase; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./test"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend match.js + * @depend format.js + */ +/** + * Assertions matching the test spy retrieval interface. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal, global) { + + var slice = Array.prototype.slice; + + function makeApi(sinon) { + var assert; + + function verifyIsStub() { + var method; + + for (var i = 0, l = arguments.length; i < l; ++i) { + method = arguments[i]; + + if (!method) { + assert.fail("fake is not a spy"); + } + + if (method.proxy && method.proxy.isSinonProxy) { + verifyIsStub(method.proxy); + } else { + if (typeof method !== "function") { + assert.fail(method + " is not a function"); + } + + if (typeof method.getCall !== "function") { + assert.fail(method + " is not stubbed"); + } + } + + } + } + + function failAssertion(object, msg) { + object = object || global; + var failMethod = object.fail || assert.fail; + failMethod.call(object, msg); + } + + function mirrorPropAsAssertion(name, method, message) { + if (arguments.length === 2) { + message = method; + method = name; + } + + assert[name] = function (fake) { + verifyIsStub(fake); + + var args = slice.call(arguments, 1); + var failed = false; + + if (typeof method === "function") { + failed = !method(fake); + } else { + failed = typeof fake[method] === "function" ? + !fake[method].apply(fake, args) : !fake[method]; + } + + if (failed) { + failAssertion(this, (fake.printf || fake.proxy.printf).apply(fake, [message].concat(args))); + } else { + assert.pass(name); + } + }; + } + + function exposedName(prefix, prop) { + return !prefix || /^fail/.test(prop) ? prop : + prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1); + } + + assert = { + failException: "AssertError", + + fail: function fail(message) { + var error = new Error(message); + error.name = this.failException || assert.failException; + + throw error; + }, + + pass: function pass() {}, + + callOrder: function assertCallOrder() { + verifyIsStub.apply(null, arguments); + var expected = ""; + var actual = ""; + + if (!sinon.calledInOrder(arguments)) { + try { + expected = [].join.call(arguments, ", "); + var calls = slice.call(arguments); + var i = calls.length; + while (i) { + if (!calls[--i].called) { + calls.splice(i, 1); + } + } + actual = sinon.orderByFirstCall(calls).join(", "); + } catch (e) { + // If this fails, we'll just fall back to the blank string + } + + failAssertion(this, "expected " + expected + " to be " + + "called in order but were called as " + actual); + } else { + assert.pass("callOrder"); + } + }, + + callCount: function assertCallCount(method, count) { + verifyIsStub(method); + + if (method.callCount !== count) { + var msg = "expected %n to be called " + sinon.timesInWords(count) + + " but was called %c%C"; + failAssertion(this, method.printf(msg)); + } else { + assert.pass("callCount"); + } + }, + + expose: function expose(target, options) { + if (!target) { + throw new TypeError("target is null or undefined"); + } + + var o = options || {}; + var prefix = typeof o.prefix === "undefined" && "assert" || o.prefix; + var includeFail = typeof o.includeFail === "undefined" || !!o.includeFail; + + for (var method in this) { + if (method !== "expose" && (includeFail || !/^(fail)/.test(method))) { + target[exposedName(prefix, method)] = this[method]; + } + } + + return target; + }, + + match: function match(actual, expectation) { + var matcher = sinon.match(expectation); + if (matcher.test(actual)) { + assert.pass("match"); + } else { + var formatted = [ + "expected value to match", + " expected = " + sinon.format(expectation), + " actual = " + sinon.format(actual) + ]; + + failAssertion(this, formatted.join("\n")); + } + } + }; + + mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called"); + mirrorPropAsAssertion("notCalled", function (spy) { + return !spy.called; + }, "expected %n to not have been called but was called %c%C"); + mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C"); + mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C"); + mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C"); + mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t"); + mirrorPropAsAssertion( + "alwaysCalledOn", + "expected %n to always be called with %1 as this but was called with %t" + ); + mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new"); + mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new"); + mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C"); + mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C"); + mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C"); + mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C"); + mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C"); + mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C"); + mirrorPropAsAssertion("threw", "%n did not throw exception%C"); + mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C"); + + sinon.assert = assert; + return assert; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./match"); + require("./format"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof global !== "undefined" ? global : self +)); + + return sinon; +})); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-ie-1.16.1.js b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-ie-1.16.1.js new file mode 100644 index 0000000..cbe3547 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-ie-1.16.1.js @@ -0,0 +1,112 @@ +/** + * Sinon.JS 1.16.1, 2015/09/22 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Helps IE run the fake timers. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake timers to work in IE, don't include this file. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +/*eslint-disable strict, no-inner-declarations, no-unused-vars*/ +if (typeof window !== "undefined") { + function setTimeout() {} + function clearTimeout() {} + function setImmediate() {} + function clearImmediate() {} + function setInterval() {} + function clearInterval() {} + function Date() {} + + // Reassign the original functions. Now their writable attribute + // should be true. Hackish, I know, but it works. + /*global sinon*/ + setTimeout = sinon.timers.setTimeout; + clearTimeout = sinon.timers.clearTimeout; + setImmediate = sinon.timers.setImmediate; + clearImmediate = sinon.timers.clearImmediate; + setInterval = sinon.timers.setInterval; + clearInterval = sinon.timers.clearInterval; + Date = sinon.timers.Date; // eslint-disable-line no-native-reassign +} +/*eslint-enable no-inner-declarations*/ + +/** + * Helps IE run the fake XMLHttpRequest. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake XHR to work in IE, don't include this file. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +/*eslint-disable strict*/ +if (typeof window !== "undefined") { + function XMLHttpRequest() {} // eslint-disable-line no-unused-vars, no-inner-declarations + + // Reassign the original function. Now its writable attribute + // should be true. Hackish, I know, but it works. + /*global sinon*/ + XMLHttpRequest = sinon.xhr.XMLHttpRequest || undefined; +} +/*eslint-enable strict*/ +/** + * Helps IE run the fake XDomainRequest. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake XDR to work in IE, don't include this file. + */ +/*eslint-disable strict*/ +if (typeof window !== "undefined") { + function XDomainRequest() {} // eslint-disable-line no-unused-vars, no-inner-declarations + + // Reassign the original function. Now its writable attribute + // should be true. Hackish, I know, but it works. + /*global sinon*/ + XDomainRequest = sinon.xdr.XDomainRequest || undefined; +} +/*eslint-enable strict*/ diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-ie-1.17.0.js b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-ie-1.17.0.js new file mode 100644 index 0000000..371fd88 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-ie-1.17.0.js @@ -0,0 +1,103 @@ +/** + * Sinon.JS 1.17.0, 2015/10/21 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Helps IE run the fake timers. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake timers to work in IE, don't include this file. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +/*eslint-disable no-func-assign, no-inner-declarations, no-unused-vars, strict*/ +function setTimeout() {} +function clearTimeout() {} +function setImmediate() {} +function clearImmediate() {} +function setInterval() {} +function clearInterval() {} +function Date() {} + +// Reassign the original functions. Now their writable attribute +// should be true. Hackish, I know, but it works. +/*global sinon*/ +setTimeout = sinon.timers.setTimeout; +clearTimeout = sinon.timers.clearTimeout; +setImmediate = sinon.timers.setImmediate; +clearImmediate = sinon.timers.clearImmediate; +setInterval = sinon.timers.setInterval; +clearInterval = sinon.timers.clearInterval; +Date = sinon.timers.Date; // eslint-disable-line no-native-reassign + +/** + * Helps IE run the fake XMLHttpRequest. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake XHR to work in IE, don't include this file. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +/*eslint-disable no-func-assign, strict*/ +function XMLHttpRequest() {} // eslint-disable-line no-unused-vars, no-inner-declarations + +// Reassign the original function. Now its writable attribute +// should be true. Hackish, I know, but it works. +/*global sinon*/ +XMLHttpRequest = sinon.xhr.XMLHttpRequest || undefined; +/** + * Helps IE run the fake XDomainRequest. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake XDR to work in IE, don't include this file. + */ +/*eslint-disable no-func-assign, strict*/ +function XDomainRequest() {} // eslint-disable-line no-unused-vars, no-inner-declarations + +// Reassign the original function. Now its writable attribute +// should be true. Hackish, I know, but it works. +/*global sinon*/ +XDomainRequest = sinon.xdr.XDomainRequest || undefined; diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-ie-1.17.2.js b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-ie-1.17.2.js new file mode 100644 index 0000000..2d95220 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-ie-1.17.2.js @@ -0,0 +1,112 @@ +/** + * Sinon.JS 1.17.2, 2015/10/21 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Helps IE run the fake timers. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake timers to work in IE, don't include this file. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +/*eslint-disable strict, no-inner-declarations, no-unused-vars*/ +if (typeof window !== "undefined") { + function setTimeout() {} + function clearTimeout() {} + function setImmediate() {} + function clearImmediate() {} + function setInterval() {} + function clearInterval() {} + function Date() {} + + // Reassign the original functions. Now their writable attribute + // should be true. Hackish, I know, but it works. + /*global sinon*/ + setTimeout = sinon.timers.setTimeout; + clearTimeout = sinon.timers.clearTimeout; + setImmediate = sinon.timers.setImmediate; + clearImmediate = sinon.timers.clearImmediate; + setInterval = sinon.timers.setInterval; + clearInterval = sinon.timers.clearInterval; + Date = sinon.timers.Date; // eslint-disable-line no-native-reassign +} +/*eslint-enable no-inner-declarations*/ + +/** + * Helps IE run the fake XMLHttpRequest. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake XHR to work in IE, don't include this file. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +/*eslint-disable strict*/ +if (typeof window !== "undefined") { + function XMLHttpRequest() {} // eslint-disable-line no-unused-vars, no-inner-declarations + + // Reassign the original function. Now its writable attribute + // should be true. Hackish, I know, but it works. + /*global sinon*/ + XMLHttpRequest = sinon.xhr.XMLHttpRequest || undefined; +} +/*eslint-enable strict*/ +/** + * Helps IE run the fake XDomainRequest. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake XDR to work in IE, don't include this file. + */ +/*eslint-disable strict*/ +if (typeof window !== "undefined") { + function XDomainRequest() {} // eslint-disable-line no-unused-vars, no-inner-declarations + + // Reassign the original function. Now its writable attribute + // should be true. Hackish, I know, but it works. + /*global sinon*/ + XDomainRequest = sinon.xdr.XDomainRequest || undefined; +} +/*eslint-enable strict*/ diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-ie.js b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-ie.js new file mode 100644 index 0000000..2d95220 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-ie.js @@ -0,0 +1,112 @@ +/** + * Sinon.JS 1.17.2, 2015/10/21 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Helps IE run the fake timers. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake timers to work in IE, don't include this file. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +/*eslint-disable strict, no-inner-declarations, no-unused-vars*/ +if (typeof window !== "undefined") { + function setTimeout() {} + function clearTimeout() {} + function setImmediate() {} + function clearImmediate() {} + function setInterval() {} + function clearInterval() {} + function Date() {} + + // Reassign the original functions. Now their writable attribute + // should be true. Hackish, I know, but it works. + /*global sinon*/ + setTimeout = sinon.timers.setTimeout; + clearTimeout = sinon.timers.clearTimeout; + setImmediate = sinon.timers.setImmediate; + clearImmediate = sinon.timers.clearImmediate; + setInterval = sinon.timers.setInterval; + clearInterval = sinon.timers.clearInterval; + Date = sinon.timers.Date; // eslint-disable-line no-native-reassign +} +/*eslint-enable no-inner-declarations*/ + +/** + * Helps IE run the fake XMLHttpRequest. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake XHR to work in IE, don't include this file. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +/*eslint-disable strict*/ +if (typeof window !== "undefined") { + function XMLHttpRequest() {} // eslint-disable-line no-unused-vars, no-inner-declarations + + // Reassign the original function. Now its writable attribute + // should be true. Hackish, I know, but it works. + /*global sinon*/ + XMLHttpRequest = sinon.xhr.XMLHttpRequest || undefined; +} +/*eslint-enable strict*/ +/** + * Helps IE run the fake XDomainRequest. By defining global functions, IE allows + * them to be overwritten at a later point. If these are not defined like + * this, overwriting them will result in anything from an exception to browser + * crash. + * + * If you don't require fake XDR to work in IE, don't include this file. + */ +/*eslint-disable strict*/ +if (typeof window !== "undefined") { + function XDomainRequest() {} // eslint-disable-line no-unused-vars, no-inner-declarations + + // Reassign the original function. Now its writable attribute + // should be true. Hackish, I know, but it works. + /*global sinon*/ + XDomainRequest = sinon.xdr.XDomainRequest || undefined; +} +/*eslint-enable strict*/ diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-server-1.16.1.js b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-server-1.16.1.js new file mode 100644 index 0000000..1eadd40 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-server-1.16.1.js @@ -0,0 +1,2245 @@ +/** + * Sinon.JS 1.16.1, 2015/09/22 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +var sinon = (function () { +"use strict"; + // eslint-disable-line no-unused-vars + + var sinonModule; + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + sinonModule = module.exports = require("./sinon/util/core"); + require("./sinon/extend"); + require("./sinon/walk"); + require("./sinon/typeOf"); + require("./sinon/times_in_words"); + require("./sinon/spy"); + require("./sinon/call"); + require("./sinon/behavior"); + require("./sinon/stub"); + require("./sinon/mock"); + require("./sinon/collection"); + require("./sinon/assert"); + require("./sinon/sandbox"); + require("./sinon/test"); + require("./sinon/test_case"); + require("./sinon/match"); + require("./sinon/format"); + require("./sinon/log_error"); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + sinonModule = module.exports; + } else { + sinonModule = {}; + } + + return sinonModule; +}()); + +/** + * @depend ../../sinon.js + */ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + var div = typeof document !== "undefined" && document.createElement("div"); + var hasOwn = Object.prototype.hasOwnProperty; + + function isDOMNode(obj) { + var success = false; + + try { + obj.appendChild(div); + success = div.parentNode === obj; + } catch (e) { + return false; + } finally { + try { + obj.removeChild(div); + } catch (e) { + // Remove failed, not much we can do about that + } + } + + return success; + } + + function isElement(obj) { + return div && obj && obj.nodeType === 1 && isDOMNode(obj); + } + + function isFunction(obj) { + return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply); + } + + function isReallyNaN(val) { + return typeof val === "number" && isNaN(val); + } + + function mirrorProperties(target, source) { + for (var prop in source) { + if (!hasOwn.call(target, prop)) { + target[prop] = source[prop]; + } + } + } + + function isRestorable(obj) { + return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon; + } + + // Cheap way to detect if we have ES5 support. + var hasES5Support = "keys" in Object; + + function makeApi(sinon) { + sinon.wrapMethod = function wrapMethod(object, property, method) { + if (!object) { + throw new TypeError("Should wrap property of object"); + } + + if (typeof method !== "function" && typeof method !== "object") { + throw new TypeError("Method wrapper should be a function or a property descriptor"); + } + + function checkWrappedMethod(wrappedMethod) { + var error; + + if (!isFunction(wrappedMethod)) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } else if (wrappedMethod.calledBefore) { + var verb = wrappedMethod.returns ? "stubbed" : "spied on"; + error = new TypeError("Attempted to wrap " + property + " which is already " + verb); + } + + if (error) { + if (wrappedMethod && wrappedMethod.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethod.stackTrace; + } + throw error; + } + } + + var error, wrappedMethod, i; + + // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem + // when using hasOwn.call on objects from other frames. + var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property); + + if (hasES5Support) { + var methodDesc = (typeof method === "function") ? {value: method} : method; + var wrappedMethodDesc = sinon.getPropertyDescriptor(object, property); + + if (!wrappedMethodDesc) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } + if (error) { + if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace; + } + throw error; + } + + var types = sinon.objectKeys(methodDesc); + for (i = 0; i < types.length; i++) { + wrappedMethod = wrappedMethodDesc[types[i]]; + checkWrappedMethod(wrappedMethod); + } + + mirrorProperties(methodDesc, wrappedMethodDesc); + for (i = 0; i < types.length; i++) { + mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]); + } + Object.defineProperty(object, property, methodDesc); + } else { + wrappedMethod = object[property]; + checkWrappedMethod(wrappedMethod); + object[property] = method; + method.displayName = property; + } + + method.displayName = property; + + // Set up a stack trace which can be used later to find what line of + // code the original method was created on. + method.stackTrace = (new Error("Stack Trace for original")).stack; + + method.restore = function () { + // For prototype properties try to reset by delete first. + // If this fails (ex: localStorage on mobile safari) then force a reset + // via direct assignment. + if (!owned) { + // In some cases `delete` may throw an error + try { + delete object[property]; + } catch (e) {} // eslint-disable-line no-empty + // For native code functions `delete` fails without throwing an error + // on Chrome < 43, PhantomJS, etc. + } else if (hasES5Support) { + Object.defineProperty(object, property, wrappedMethodDesc); + } + + // Use strict equality comparison to check failures then force a reset + // via direct assignment. + if (object[property] === method) { + object[property] = wrappedMethod; + } + }; + + method.restore.sinon = true; + + if (!hasES5Support) { + mirrorProperties(method, wrappedMethod); + } + + return method; + }; + + sinon.create = function create(proto) { + var F = function () {}; + F.prototype = proto; + return new F(); + }; + + sinon.deepEqual = function deepEqual(a, b) { + if (sinon.match && sinon.match.isMatcher(a)) { + return a.test(b); + } + + if (typeof a !== "object" || typeof b !== "object") { + return isReallyNaN(a) && isReallyNaN(b) || a === b; + } + + if (isElement(a) || isElement(b)) { + return a === b; + } + + if (a === b) { + return true; + } + + if ((a === null && b !== null) || (a !== null && b === null)) { + return false; + } + + if (a instanceof RegExp && b instanceof RegExp) { + return (a.source === b.source) && (a.global === b.global) && + (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline); + } + + var aString = Object.prototype.toString.call(a); + if (aString !== Object.prototype.toString.call(b)) { + return false; + } + + if (aString === "[object Date]") { + return a.valueOf() === b.valueOf(); + } + + var prop; + var aLength = 0; + var bLength = 0; + + if (aString === "[object Array]" && a.length !== b.length) { + return false; + } + + for (prop in a) { + if (a.hasOwnProperty(prop)) { + aLength += 1; + + if (!(prop in b)) { + return false; + } + + if (!deepEqual(a[prop], b[prop])) { + return false; + } + } + } + + for (prop in b) { + if (b.hasOwnProperty(prop)) { + bLength += 1; + } + } + + return aLength === bLength; + }; + + sinon.functionName = function functionName(func) { + var name = func.displayName || func.name; + + // Use function decomposition as a last resort to get function + // name. Does not rely on function decomposition to work - if it + // doesn't debugging will be slightly less informative + // (i.e. toString will say 'spy' rather than 'myFunc'). + if (!name) { + var matches = func.toString().match(/function ([^\s\(]+)/); + name = matches && matches[1]; + } + + return name; + }; + + sinon.functionToString = function toString() { + if (this.getCall && this.callCount) { + var thisValue, + prop; + var i = this.callCount; + + while (i--) { + thisValue = this.getCall(i).thisValue; + + for (prop in thisValue) { + if (thisValue[prop] === this) { + return prop; + } + } + } + } + + return this.displayName || "sinon fake"; + }; + + sinon.objectKeys = function objectKeys(obj) { + if (obj !== Object(obj)) { + throw new TypeError("sinon.objectKeys called on a non-object"); + } + + var keys = []; + var key; + for (key in obj) { + if (hasOwn.call(obj, key)) { + keys.push(key); + } + } + + return keys; + }; + + sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) { + var proto = object; + var descriptor; + + while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) { + proto = Object.getPrototypeOf(proto); + } + return descriptor; + }; + + sinon.getConfig = function (custom) { + var config = {}; + custom = custom || {}; + var defaults = sinon.defaultConfig; + + for (var prop in defaults) { + if (defaults.hasOwnProperty(prop)) { + config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; + } + } + + return config; + }; + + sinon.defaultConfig = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.timesInWords = function timesInWords(count) { + return count === 1 && "once" || + count === 2 && "twice" || + count === 3 && "thrice" || + (count || 0) + " times"; + }; + + sinon.calledInOrder = function (spies) { + for (var i = 1, l = spies.length; i < l; i++) { + if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) { + return false; + } + } + + return true; + }; + + sinon.orderByFirstCall = function (spies) { + return spies.sort(function (a, b) { + // uuid, won't ever be equal + var aCall = a.getCall(0); + var bCall = b.getCall(0); + var aId = aCall && aCall.callId || -1; + var bId = bCall && bCall.callId || -1; + + return aId < bId ? -1 : 1; + }); + }; + + sinon.createStubInstance = function (constructor) { + if (typeof constructor !== "function") { + throw new TypeError("The constructor should be a function."); + } + return sinon.stub(sinon.create(constructor.prototype)); + }; + + sinon.restore = function (object) { + if (object !== null && typeof object === "object") { + for (var prop in object) { + if (isRestorable(object[prop])) { + object[prop].restore(); + } + } + } else if (isRestorable(object)) { + object.restore(); + } + }; + + return sinon; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports) { + makeApi(exports); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + + // Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + var hasDontEnumBug = (function () { + var obj = { + constructor: function () { + return "0"; + }, + toString: function () { + return "1"; + }, + valueOf: function () { + return "2"; + }, + toLocaleString: function () { + return "3"; + }, + prototype: function () { + return "4"; + }, + isPrototypeOf: function () { + return "5"; + }, + propertyIsEnumerable: function () { + return "6"; + }, + hasOwnProperty: function () { + return "7"; + }, + length: function () { + return "8"; + }, + unique: function () { + return "9"; + } + }; + + var result = []; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + result.push(obj[prop]()); + } + } + return result.join("") !== "0123456789"; + })(); + + /* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will + * override properties in previous sources. + * + * target - The Object to extend + * sources - Objects to copy properties from. + * + * Returns the extended target + */ + function extend(target /*, sources */) { + var sources = Array.prototype.slice.call(arguments, 1); + var source, i, prop; + + for (i = 0; i < sources.length; i++) { + source = sources[i]; + + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // Make sure we copy (own) toString method even when in JScript with DontEnum bug + // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) { + target.toString = source.toString; + } + } + + return target; + } + + sinon.extend = extend; + return sinon.extend; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * Minimal Event interface implementation + * + * Original implementation by Sven Fuchs: https://gist.github.com/995028 + * Modifications and tests by Christian Johansen. + * + * @author Sven Fuchs (svenfuchs@artweb-design.de) + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2011 Sven Fuchs, Christian Johansen + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +(function () { + + var push = [].push; + + function makeApi(sinon) { + sinon.Event = function Event(type, bubbles, cancelable, target) { + this.initEvent(type, bubbles, cancelable, target); + }; + + sinon.Event.prototype = { + initEvent: function (type, bubbles, cancelable, target) { + this.type = type; + this.bubbles = bubbles; + this.cancelable = cancelable; + this.target = target; + }, + + stopPropagation: function () {}, + + preventDefault: function () { + this.defaultPrevented = true; + } + }; + + sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) { + this.initEvent(type, false, false, target); + this.loaded = progressEventRaw.loaded || null; + this.total = progressEventRaw.total || null; + this.lengthComputable = !!progressEventRaw.total; + }; + + sinon.ProgressEvent.prototype = new sinon.Event(); + + sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent; + + sinon.CustomEvent = function CustomEvent(type, customData, target) { + this.initEvent(type, false, false, target); + this.detail = customData.detail || null; + }; + + sinon.CustomEvent.prototype = new sinon.Event(); + + sinon.CustomEvent.prototype.constructor = sinon.CustomEvent; + + sinon.EventTarget = { + addEventListener: function addEventListener(event, listener) { + this.eventListeners = this.eventListeners || {}; + this.eventListeners[event] = this.eventListeners[event] || []; + push.call(this.eventListeners[event], listener); + }, + + removeEventListener: function removeEventListener(event, listener) { + var listeners = this.eventListeners && this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }, + + dispatchEvent: function dispatchEvent(event) { + var type = event.type; + var listeners = this.eventListeners && this.eventListeners[type] || []; + + for (var i = 0; i < listeners.length; i++) { + if (typeof listeners[i] === "function") { + listeners[i].call(this, event); + } else { + listeners[i].handleEvent(event); + } + } + + return !!event.defaultPrevented; + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend util/core.js + */ +/** + * Logs errors + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal) { + + // cache a reference to setTimeout, so that our reference won't be stubbed out + // when using fake timers and errors will still get logged + // https://github.com/cjohansen/Sinon.JS/issues/381 + var realSetTimeout = setTimeout; + + function makeApi(sinon) { + + function log() {} + + function logError(label, err) { + var msg = label + " threw exception: "; + + function throwLoggedError() { + err.message = msg + err.message; + throw err; + } + + sinon.log(msg + "[" + err.name + "] " + err.message); + + if (err.stack) { + sinon.log(err.stack); + } + + if (logError.useImmediateExceptions) { + throwLoggedError(); + } else { + logError.setTimeout(throwLoggedError, 0); + } + } + + // When set to true, any errors logged will be thrown immediately; + // If set to false, the errors will be thrown in separate execution frame. + logError.useImmediateExceptions = false; + + // wrap realSetTimeout with something we can stub in tests + logError.setTimeout = function (func, timeout) { + realSetTimeout(func, timeout); + }; + + var exports = {}; + exports.log = sinon.log = log; + exports.logError = sinon.logError = logError; + + return exports; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XDomainRequest object + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +// wrapper for global +(function (global) { + + var xdr = { XDomainRequest: global.XDomainRequest }; + xdr.GlobalXDomainRequest = global.XDomainRequest; + xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== "undefined"; + xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false; + + function makeApi(sinon) { + sinon.xdr = xdr; + + function FakeXDomainRequest() { + this.readyState = FakeXDomainRequest.UNSENT; + this.requestBody = null; + this.requestHeaders = {}; + this.status = 0; + this.timeout = null; + + if (typeof FakeXDomainRequest.onCreate === "function") { + FakeXDomainRequest.onCreate(this); + } + } + + function verifyState(x) { + if (x.readyState !== FakeXDomainRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (x.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function verifyRequestSent(x) { + if (x.readyState === FakeXDomainRequest.UNSENT) { + throw new Error("Request not sent"); + } + if (x.readyState === FakeXDomainRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XDomainRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, { + open: function open(method, url) { + this.method = method; + this.url = url; + + this.responseText = null; + this.sendFlag = false; + + this.readyStateChange(FakeXDomainRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + var eventName = ""; + switch (this.readyState) { + case FakeXDomainRequest.UNSENT: + break; + case FakeXDomainRequest.OPENED: + break; + case FakeXDomainRequest.LOADING: + if (this.sendFlag) { + //raise the progress event + eventName = "onprogress"; + } + break; + case FakeXDomainRequest.DONE: + if (this.isTimeout) { + eventName = "ontimeout"; + } else if (this.errorFlag || (this.status < 200 || this.status > 299)) { + eventName = "onerror"; + } else { + eventName = "onload"; + } + break; + } + + // raising event (if defined) + if (eventName) { + if (typeof this[eventName] === "function") { + try { + this[eventName](); + } catch (e) { + sinon.logError("Fake XHR " + eventName + " handler", e); + } + } + } + }, + + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + this.requestBody = data; + } + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + + this.errorFlag = false; + this.sendFlag = true; + this.readyStateChange(FakeXDomainRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + + if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) { + this.readyStateChange(sinon.FakeXDomainRequest.DONE); + this.sendFlag = false; + } + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyResponseBodyType(body); + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + this.readyStateChange(FakeXDomainRequest.LOADING); + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + this.readyStateChange(FakeXDomainRequest.DONE); + }, + + respond: function respond(status, contentType, body) { + // content-type ignored, since XDomainRequest does not carry this + // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease + // test integration across browsers + this.status = typeof status === "number" ? status : 200; + this.setResponseBody(body || ""); + }, + + simulatetimeout: function simulatetimeout() { + this.status = 0; + this.isTimeout = true; + // Access to this should actually throw an error + this.responseText = undefined; + this.readyStateChange(FakeXDomainRequest.DONE); + } + }); + + sinon.extend(FakeXDomainRequest, { + UNSENT: 0, + OPENED: 1, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXDomainRequest = function useFakeXDomainRequest() { + sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) { + if (xdr.supportsXDR) { + global.XDomainRequest = xdr.GlobalXDomainRequest; + } + + delete sinon.FakeXDomainRequest.restore; + + if (keepOnCreate !== true) { + delete sinon.FakeXDomainRequest.onCreate; + } + }; + if (xdr.supportsXDR) { + global.XDomainRequest = sinon.FakeXDomainRequest; + } + return sinon.FakeXDomainRequest; + }; + + sinon.FakeXDomainRequest = FakeXDomainRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +})(typeof global !== "undefined" ? global : self); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XMLHttpRequest object + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal, global) { + + function getWorkingXHR(globalScope) { + var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined"; + if (supportsXHR) { + return globalScope.XMLHttpRequest; + } + + var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined"; + if (supportsActiveX) { + return function () { + return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0"); + }; + } + + return false; + } + + var supportsProgress = typeof ProgressEvent !== "undefined"; + var supportsCustomEvent = typeof CustomEvent !== "undefined"; + var supportsFormData = typeof FormData !== "undefined"; + var supportsArrayBuffer = typeof ArrayBuffer !== "undefined"; + var supportsBlob = typeof Blob === "function"; + var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest }; + sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest; + sinonXhr.GlobalActiveXObject = global.ActiveXObject; + sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== "undefined"; + sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined"; + sinonXhr.workingXHR = getWorkingXHR(global); + sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest()); + + var unsafeHeaders = { + "Accept-Charset": true, + "Accept-Encoding": true, + Connection: true, + "Content-Length": true, + Cookie: true, + Cookie2: true, + "Content-Transfer-Encoding": true, + Date: true, + Expect: true, + Host: true, + "Keep-Alive": true, + Referer: true, + TE: true, + Trailer: true, + "Transfer-Encoding": true, + Upgrade: true, + "User-Agent": true, + Via: true + }; + + // An upload object is created for each + // FakeXMLHttpRequest and allows upload + // events to be simulated using uploadProgress + // and uploadError. + function UploadProgress() { + this.eventListeners = { + progress: [], + load: [], + abort: [], + error: [] + }; + } + + UploadProgress.prototype.addEventListener = function addEventListener(event, listener) { + this.eventListeners[event].push(listener); + }; + + UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) { + var listeners = this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }; + + UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) { + var listeners = this.eventListeners[event.type] || []; + + for (var i = 0, listener; (listener = listeners[i]) != null; i++) { + listener(event); + } + }; + + // Note that for FakeXMLHttpRequest to work pre ES5 + // we lose some of the alignment with the spec. + // To ensure as close a match as possible, + // set responseType before calling open, send or respond; + function FakeXMLHttpRequest() { + this.readyState = FakeXMLHttpRequest.UNSENT; + this.requestHeaders = {}; + this.requestBody = null; + this.status = 0; + this.statusText = ""; + this.upload = new UploadProgress(); + this.responseType = ""; + this.response = ""; + if (sinonXhr.supportsCORS) { + this.withCredentials = false; + } + + var xhr = this; + var events = ["loadstart", "load", "abort", "loadend"]; + + function addEventListener(eventName) { + xhr.addEventListener(eventName, function (event) { + var listener = xhr["on" + eventName]; + + if (listener && typeof listener === "function") { + listener.call(this, event); + } + }); + } + + for (var i = events.length - 1; i >= 0; i--) { + addEventListener(events[i]); + } + + if (typeof FakeXMLHttpRequest.onCreate === "function") { + FakeXMLHttpRequest.onCreate(this); + } + } + + function verifyState(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xhr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function getHeader(headers, header) { + header = header.toLowerCase(); + + for (var h in headers) { + if (h.toLowerCase() === header) { + return h; + } + } + + return null; + } + + // filtering to enable a white-list version of Sinon FakeXhr, + // where whitelisted requests are passed through to real XHR + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + function some(collection, callback) { + for (var index = 0; index < collection.length; index++) { + if (callback(collection[index]) === true) { + return true; + } + } + return false; + } + // largest arity in XHR is 5 - XHR#open + var apply = function (obj, method, args) { + switch (args.length) { + case 0: return obj[method](); + case 1: return obj[method](args[0]); + case 2: return obj[method](args[0], args[1]); + case 3: return obj[method](args[0], args[1], args[2]); + case 4: return obj[method](args[0], args[1], args[2], args[3]); + case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]); + } + }; + + FakeXMLHttpRequest.filters = []; + FakeXMLHttpRequest.addFilter = function addFilter(fn) { + this.filters.push(fn); + }; + var IE6Re = /MSIE 6/; + FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) { + var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap + + each([ + "open", + "setRequestHeader", + "send", + "abort", + "getResponseHeader", + "getAllResponseHeaders", + "addEventListener", + "overrideMimeType", + "removeEventListener" + ], function (method) { + fakeXhr[method] = function () { + return apply(xhr, method, arguments); + }; + }); + + var copyAttrs = function (args) { + each(args, function (attr) { + try { + fakeXhr[attr] = xhr[attr]; + } catch (e) { + if (!IE6Re.test(navigator.userAgent)) { + throw e; + } + } + }); + }; + + var stateChange = function stateChange() { + fakeXhr.readyState = xhr.readyState; + if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { + copyAttrs(["status", "statusText"]); + } + if (xhr.readyState >= FakeXMLHttpRequest.LOADING) { + copyAttrs(["responseText", "response"]); + } + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + copyAttrs(["responseXML"]); + } + if (fakeXhr.onreadystatechange) { + fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr }); + } + }; + + if (xhr.addEventListener) { + for (var event in fakeXhr.eventListeners) { + if (fakeXhr.eventListeners.hasOwnProperty(event)) { + + /*eslint-disable no-loop-func*/ + each(fakeXhr.eventListeners[event], function (handler) { + xhr.addEventListener(event, handler); + }); + /*eslint-enable no-loop-func*/ + } + } + xhr.addEventListener("readystatechange", stateChange); + } else { + xhr.onreadystatechange = stateChange; + } + apply(xhr, "open", xhrArgs); + }; + FakeXMLHttpRequest.useFilters = false; + + function verifyRequestOpened(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR - " + xhr.readyState); + } + } + + function verifyRequestSent(xhr) { + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyHeadersReceived(xhr) { + if (xhr.async && xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) { + throw new Error("No headers received"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XMLHttpRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + function convertToArrayBuffer(body) { + var buffer = new ArrayBuffer(body.length); + var view = new Uint8Array(buffer); + for (var i = 0; i < body.length; i++) { + var charCode = body.charCodeAt(i); + if (charCode >= 256) { + throw new TypeError("arraybuffer or blob responseTypes require binary string, " + + "invalid character " + body[i] + " found."); + } + view[i] = charCode; + } + return buffer; + } + + function isXmlContentType(contentType) { + return !contentType || /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType); + } + + function convertResponseBody(responseType, contentType, body) { + if (responseType === "" || responseType === "text") { + return body; + } else if (supportsArrayBuffer && responseType === "arraybuffer") { + return convertToArrayBuffer(body); + } else if (responseType === "json") { + try { + return JSON.parse(body); + } catch (e) { + // Return parsing failure as null + return null; + } + } else if (supportsBlob && responseType === "blob") { + var blobOptions = {}; + if (contentType) { + blobOptions.type = contentType; + } + return new Blob([convertToArrayBuffer(body)], blobOptions); + } else if (responseType === "document") { + if (isXmlContentType(contentType)) { + return FakeXMLHttpRequest.parseXML(body); + } + return null; + } + throw new Error("Invalid responseType " + responseType); + } + + function clearResponse(xhr) { + if (xhr.responseType === "" || xhr.responseType === "text") { + xhr.response = xhr.responseText = ""; + } else { + xhr.response = xhr.responseText = null; + } + xhr.responseXML = null; + } + + FakeXMLHttpRequest.parseXML = function parseXML(text) { + // Treat empty string as parsing failure + if (text !== "") { + try { + if (typeof DOMParser !== "undefined") { + var parser = new DOMParser(); + return parser.parseFromString(text, "text/xml"); + } + var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(text); + return xmlDoc; + } catch (e) { + // Unable to parse XML - no biggie + } + } + + return null; + }; + + FakeXMLHttpRequest.statusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 300: "Multiple Choice", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 422: "Unprocessable Entity", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" + }; + + function makeApi(sinon) { + sinon.xhr = sinonXhr; + + sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { + async: true, + + open: function open(method, url, async, username, password) { + this.method = method; + this.url = url; + this.async = typeof async === "boolean" ? async : true; + this.username = username; + this.password = password; + clearResponse(this); + this.requestHeaders = {}; + this.sendFlag = false; + + if (FakeXMLHttpRequest.useFilters === true) { + var xhrArgs = arguments; + var defake = some(FakeXMLHttpRequest.filters, function (filter) { + return filter.apply(this, xhrArgs); + }); + if (defake) { + return FakeXMLHttpRequest.defake(this, arguments); + } + } + this.readyStateChange(FakeXMLHttpRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + + var readyStateChangeEvent = new sinon.Event("readystatechange", false, false, this); + + if (typeof this.onreadystatechange === "function") { + try { + this.onreadystatechange(readyStateChangeEvent); + } catch (e) { + sinon.logError("Fake XHR onreadystatechange handler", e); + } + } + + switch (this.readyState) { + case FakeXMLHttpRequest.DONE: + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + this.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + } + this.upload.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("loadend", false, false, this)); + break; + } + + this.dispatchEvent(readyStateChangeEvent); + }, + + setRequestHeader: function setRequestHeader(header, value) { + verifyState(this); + + if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { + throw new Error("Refused to set unsafe header \"" + header + "\""); + } + + if (this.requestHeaders[header]) { + this.requestHeaders[header] += "," + value; + } else { + this.requestHeaders[header] = value; + } + }, + + // Helps testing + setResponseHeaders: function setResponseHeaders(headers) { + verifyRequestOpened(this); + this.responseHeaders = {}; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + this.responseHeaders[header] = headers[header]; + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); + } else { + this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; + } + }, + + // Currently treats ALL data as a DOMString (i.e. no Document) + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + var contentType = getHeader(this.requestHeaders, "Content-Type"); + if (this.requestHeaders[contentType]) { + var value = this.requestHeaders[contentType].split(";"); + this.requestHeaders[contentType] = value[0] + ";charset=utf-8"; + } else if (supportsFormData && !(data instanceof FormData)) { + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + } + + this.requestBody = data; + } + + this.errorFlag = false; + this.sendFlag = this.async; + clearResponse(this); + this.readyStateChange(FakeXMLHttpRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + + this.dispatchEvent(new sinon.Event("loadstart", false, false, this)); + }, + + abort: function abort() { + this.aborted = true; + clearResponse(this); + this.errorFlag = true; + this.requestHeaders = {}; + this.responseHeaders = {}; + + if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) { + this.readyStateChange(FakeXMLHttpRequest.DONE); + this.sendFlag = false; + } + + this.readyState = FakeXMLHttpRequest.UNSENT; + + this.dispatchEvent(new sinon.Event("abort", false, false, this)); + + this.upload.dispatchEvent(new sinon.Event("abort", false, false, this)); + + if (typeof this.onerror === "function") { + this.onerror(); + } + }, + + getResponseHeader: function getResponseHeader(header) { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return null; + } + + if (/^Set-Cookie2?$/i.test(header)) { + return null; + } + + header = getHeader(this.responseHeaders, header); + + return this.responseHeaders[header] || null; + }, + + getAllResponseHeaders: function getAllResponseHeaders() { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return ""; + } + + var headers = ""; + + for (var header in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(header) && + !/^Set-Cookie2?$/i.test(header)) { + headers += header + ": " + this.responseHeaders[header] + "\r\n"; + } + } + + return headers; + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyHeadersReceived(this); + verifyResponseBodyType(body); + var contentType = this.getResponseHeader("Content-Type"); + + var isTextResponse = this.responseType === "" || this.responseType === "text"; + clearResponse(this); + if (this.async) { + var chunkSize = this.chunkSize || 10; + var index = 0; + + do { + this.readyStateChange(FakeXMLHttpRequest.LOADING); + + if (isTextResponse) { + this.responseText = this.response += body.substring(index, index + chunkSize); + } + index += chunkSize; + } while (index < body.length); + } + + this.response = convertResponseBody(this.responseType, contentType, body); + if (isTextResponse) { + this.responseText = this.response; + } + + if (this.responseType === "document") { + this.responseXML = this.response; + } else if (this.responseType === "" && isXmlContentType(contentType)) { + this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); + } + this.readyStateChange(FakeXMLHttpRequest.DONE); + }, + + respond: function respond(status, headers, body) { + this.status = typeof status === "number" ? status : 200; + this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; + this.setResponseHeaders(headers || {}); + this.setResponseBody(body || ""); + }, + + uploadProgress: function uploadProgress(progressEventRaw) { + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + downloadProgress: function downloadProgress(progressEventRaw) { + if (supportsProgress) { + this.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + uploadError: function uploadError(error) { + if (supportsCustomEvent) { + this.upload.dispatchEvent(new sinon.CustomEvent("error", {detail: error})); + } + } + }); + + sinon.extend(FakeXMLHttpRequest, { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXMLHttpRequest = function () { + FakeXMLHttpRequest.restore = function restore(keepOnCreate) { + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = sinonXhr.GlobalActiveXObject; + } + + delete FakeXMLHttpRequest.restore; + + if (keepOnCreate !== true) { + delete FakeXMLHttpRequest.onCreate; + } + }; + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = FakeXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = function ActiveXObject(objId) { + if (objId === "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { + + return new FakeXMLHttpRequest(); + } + + return new sinonXhr.GlobalActiveXObject(objId); + }; + } + + return FakeXMLHttpRequest; + }; + + sinon.FakeXMLHttpRequest = FakeXMLHttpRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof global !== "undefined" ? global : self +)); + +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal, formatio) { + + function makeApi(sinon) { + function valueFormatter(value) { + return "" + value; + } + + function getFormatioFormatter() { + var formatter = formatio.configure({ + quoteStrings: false, + limitChildrenCount: 250 + }); + + function format() { + return formatter.ascii.apply(formatter, arguments); + } + + return format; + } + + function getNodeFormatter() { + try { + var util = require("util"); + } catch (e) { + /* Node, but no util module - would be very old, but better safe than sorry */ + } + + function format(v) { + var isObjectWithNativeToString = typeof v === "object" && v.toString === Object.prototype.toString; + return isObjectWithNativeToString ? util.inspect(v) : v; + } + + return util ? format : valueFormatter; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var formatter; + + if (isNode) { + try { + formatio = require("formatio"); + } + catch (e) {} // eslint-disable-line no-empty + } + + if (formatio) { + formatter = getFormatioFormatter(); + } else if (isNode) { + formatter = getNodeFormatter(); + } else { + formatter = valueFormatter; + } + + sinon.format = formatter; + return sinon.format; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof formatio === "object" && formatio // eslint-disable-line no-undef +)); + +/** + * @depend fake_xdomain_request.js + * @depend fake_xml_http_request.js + * @depend ../format.js + * @depend ../log_error.js + */ +/** + * The Sinon "server" mimics a web server that receives requests from + * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, + * both synchronously and asynchronously. To respond synchronuously, canned + * answers have to be provided upfront. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + var push = [].push; + + function responseArray(handler) { + var response = handler; + + if (Object.prototype.toString.call(handler) !== "[object Array]") { + response = [200, {}, handler]; + } + + if (typeof response[2] !== "string") { + throw new TypeError("Fake server response body should be string, but was " + + typeof response[2]); + } + + return response; + } + + var wloc = typeof window !== "undefined" ? window.location : {}; + var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host); + + function matchOne(response, reqMethod, reqUrl) { + var rmeth = response.method; + var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase(); + var url = response.url; + var matchUrl = !url || url === reqUrl || (typeof url.test === "function" && url.test(reqUrl)); + + return matchMethod && matchUrl; + } + + function match(response, request) { + var requestUrl = request.url; + + if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { + requestUrl = requestUrl.replace(rCurrLoc, ""); + } + + if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { + if (typeof response.response === "function") { + var ru = response.url; + var args = [request].concat(ru && typeof ru.exec === "function" ? ru.exec(requestUrl).slice(1) : []); + return response.response.apply(response, args); + } + + return true; + } + + return false; + } + + function makeApi(sinon) { + sinon.fakeServer = { + create: function (config) { + var server = sinon.create(this); + server.configure(config); + if (!sinon.xhr.supportsCORS) { + this.xhr = sinon.useFakeXDomainRequest(); + } else { + this.xhr = sinon.useFakeXMLHttpRequest(); + } + server.requests = []; + + this.xhr.onCreate = function (xhrObj) { + server.addRequest(xhrObj); + }; + + return server; + }, + configure: function (config) { + var whitelist = { + "autoRespond": true, + "autoRespondAfter": true, + "respondImmediately": true, + "fakeHTTPMethods": true + }; + var setting; + + config = config || {}; + for (setting in config) { + if (whitelist.hasOwnProperty(setting) && config.hasOwnProperty(setting)) { + this[setting] = config[setting]; + } + } + }, + addRequest: function addRequest(xhrObj) { + var server = this; + push.call(this.requests, xhrObj); + + xhrObj.onSend = function () { + server.handleRequest(this); + + if (server.respondImmediately) { + server.respond(); + } else if (server.autoRespond && !server.responding) { + setTimeout(function () { + server.responding = false; + server.respond(); + }, server.autoRespondAfter || 10); + + server.responding = true; + } + }; + }, + + getHTTPMethod: function getHTTPMethod(request) { + if (this.fakeHTTPMethods && /post/i.test(request.method)) { + var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); + return matches ? matches[1] : request.method; + } + + return request.method; + }, + + handleRequest: function handleRequest(xhr) { + if (xhr.async) { + if (!this.queue) { + this.queue = []; + } + + push.call(this.queue, xhr); + } else { + this.processRequest(xhr); + } + }, + + log: function log(response, request) { + var str; + + str = "Request:\n" + sinon.format(request) + "\n\n"; + str += "Response:\n" + sinon.format(response) + "\n\n"; + + sinon.log(str); + }, + + respondWith: function respondWith(method, url, body) { + if (arguments.length === 1 && typeof method !== "function") { + this.response = responseArray(method); + return; + } + + if (!this.responses) { + this.responses = []; + } + + if (arguments.length === 1) { + body = method; + url = method = null; + } + + if (arguments.length === 2) { + body = url; + url = method; + method = null; + } + + push.call(this.responses, { + method: method, + url: url, + response: typeof body === "function" ? body : responseArray(body) + }); + }, + + respond: function respond() { + if (arguments.length > 0) { + this.respondWith.apply(this, arguments); + } + + var queue = this.queue || []; + var requests = queue.splice(0, queue.length); + + for (var i = 0; i < requests.length; i++) { + this.processRequest(requests[i]); + } + }, + + processRequest: function processRequest(request) { + try { + if (request.aborted) { + return; + } + + var response = this.response || [404, {}, ""]; + + if (this.responses) { + for (var l = this.responses.length, i = l - 1; i >= 0; i--) { + if (match.call(this, this.responses[i], request)) { + response = this.responses[i].response; + break; + } + } + } + + if (request.readyState !== 4) { + this.log(response, request); + + request.respond(response[0], response[1], response[2]); + } + } catch (e) { + sinon.logError("Fake server request processing", e); + } + }, + + restore: function restore() { + return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("./fake_xdomain_request"); + require("./fake_xml_http_request"); + require("../format"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * Fake timer API + * setTimeout + * setInterval + * clearTimeout + * clearInterval + * tick + * reset + * Date + * + * Inspired by jsUnitMockTimeOut from JsUnit + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + function makeApi(s, lol) { + /*global lolex */ + var llx = typeof lolex !== "undefined" ? lolex : lol; + + s.useFakeTimers = function () { + var now; + var methods = Array.prototype.slice.call(arguments); + + if (typeof methods[0] === "string") { + now = 0; + } else { + now = methods.shift(); + } + + var clock = llx.install(now || 0, methods); + clock.restore = clock.uninstall; + return clock; + }; + + s.clock = { + create: function (now) { + return llx.createClock(now); + } + }; + + s.timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), + clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined), + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, epxorts, module, lolex) { + var core = require("./core"); + makeApi(core, lolex); + module.exports = core; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module, require("lolex")); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend fake_server.js + * @depend fake_timers.js + */ +/** + * Add-on for sinon.fakeServer that automatically handles a fake timer along with + * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery + * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, + * it polls the object for completion with setInterval. Dispite the direct + * motivation, there is nothing jQuery-specific in this file, so it can be used + * in any environment where the ajax implementation depends on setInterval or + * setTimeout. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + function makeApi(sinon) { + function Server() {} + Server.prototype = sinon.fakeServer; + + sinon.fakeServerWithClock = new Server(); + + sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { + if (xhr.async) { + if (typeof setTimeout.clock === "object") { + this.clock = setTimeout.clock; + } else { + this.clock = sinon.useFakeTimers(); + this.resetClock = true; + } + + if (!this.longestTimeout) { + var clockSetTimeout = this.clock.setTimeout; + var clockSetInterval = this.clock.setInterval; + var server = this; + + this.clock.setTimeout = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetTimeout.apply(this, arguments); + }; + + this.clock.setInterval = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetInterval.apply(this, arguments); + }; + } + } + + return sinon.fakeServer.addRequest.call(this, xhr); + }; + + sinon.fakeServerWithClock.respond = function respond() { + var returnVal = sinon.fakeServer.respond.apply(this, arguments); + + if (this.clock) { + this.clock.tick(this.longestTimeout || 0); + this.longestTimeout = 0; + + if (this.resetClock) { + this.clock.restore(); + this.resetClock = false; + } + } + + return returnVal; + }; + + sinon.fakeServerWithClock.restore = function restore() { + if (this.clock) { + this.clock.restore(); + } + + return sinon.fakeServer.restore.apply(this, arguments); + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + require("./fake_server"); + require("./fake_timers"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-server-1.17.0.js b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-server-1.17.0.js new file mode 100644 index 0000000..7042806 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-server-1.17.0.js @@ -0,0 +1,3123 @@ +/** + * Sinon.JS 1.17.0, 2015/10/21 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.sinon = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) { + this.respondWith.apply(this, arguments); + } + + var queue = this.queue || []; + var requests = queue.splice(0, queue.length); + + for (var i = 0; i < requests.length; i++) { + this.processRequest(requests[i]); + } + }, + + processRequest: function processRequest(request) { + try { + if (request.aborted) { + return; + } + + var response = this.response || [404, {}, ""]; + + if (this.responses) { + for (var l = this.responses.length, i = l - 1; i >= 0; i--) { + if (match.call(this, this.responses[i], request)) { + response = this.responses[i].response; + break; + } + } + } + + if (request.readyState !== 4) { + this.log(response, request); + + request.respond(response[0], response[1], response[2]); + } + } catch (e) { + sinon.logError("Fake server request processing", e); + } + }, + + restore: function restore() { + return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); + } +}; + +},{"../format":2,"../log_error":3,"./core":12,"./fake_xdomain_request":22,"./fake_xml_http_request":23}],20:[function(require,module,exports){ +/** + * Add-on for sinon.fakeServer that automatically handles a fake timer along with + * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery + * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, + * it polls the object for completion with setInterval. Dispite the direct + * motivation, there is nothing jQuery-specific in this file, so it can be used + * in any environment where the ajax implementation depends on setInterval or + * setTimeout. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +require("./fake_server"); +require("./fake_timers"); +var sinon = require("./core"); + +function Server() {} +Server.prototype = sinon.fakeServer; + +sinon.fakeServerWithClock = new Server(); + +sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { + if (xhr.async) { + if (typeof setTimeout.clock === "object") { + this.clock = setTimeout.clock; + } else { + this.clock = sinon.useFakeTimers(); + this.resetClock = true; + } + + if (!this.longestTimeout) { + var clockSetTimeout = this.clock.setTimeout; + var clockSetInterval = this.clock.setInterval; + var server = this; + + this.clock.setTimeout = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetTimeout.apply(this, arguments); + }; + + this.clock.setInterval = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetInterval.apply(this, arguments); + }; + } + } + + return sinon.fakeServer.addRequest.call(this, xhr); +}; + +sinon.fakeServerWithClock.respond = function respond() { + var returnVal = sinon.fakeServer.respond.apply(this, arguments); + + if (this.clock) { + this.clock.tick(this.longestTimeout || 0); + this.longestTimeout = 0; + + if (this.resetClock) { + this.clock.restore(); + this.resetClock = false; + } + } + + return returnVal; +}; + +sinon.fakeServerWithClock.restore = function restore() { + if (this.clock) { + this.clock.restore(); + } + + return sinon.fakeServer.restore.apply(this, arguments); +}; + +},{"./core":12,"./fake_server":19,"./fake_timers":21}],21:[function(require,module,exports){ +/** + * Fake timer API + * setTimeout + * setInterval + * clearTimeout + * clearInterval + * tick + * reset + * Date + * + * Inspired by jsUnitMockTimeOut from JsUnit + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +var s = require("./core"); +var llx = require("lolex"); + +s.useFakeTimers = function () { + var now; + var methods = Array.prototype.slice.call(arguments); + + if (typeof methods[0] === "string") { + now = 0; + } else { + now = methods.shift(); + } + + var clock = llx.install(now || 0, methods); + clock.restore = clock.uninstall; + return clock; +}; + +s.clock = { + create: function (now) { + return llx.createClock(now); + } +}; + +s.timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), + clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined), + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date +}; + +},{"./core":12,"lolex":25}],22:[function(require,module,exports){ +(function (global){ +/** + * Fake XDomainRequest object + */ + +"use strict"; + +require("../extend"); +require("./event"); +require("../log_error"); + +var xdr = { XDomainRequest: global.XDomainRequest }; +xdr.GlobalXDomainRequest = global.XDomainRequest; +xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== "undefined"; +xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false; + +var sinon = require("./core"); +sinon.xdr = xdr; + +function FakeXDomainRequest() { + this.readyState = FakeXDomainRequest.UNSENT; + this.requestBody = null; + this.requestHeaders = {}; + this.status = 0; + this.timeout = null; + + if (typeof FakeXDomainRequest.onCreate === "function") { + FakeXDomainRequest.onCreate(this); + } +} + +function verifyState(x) { + if (x.readyState !== FakeXDomainRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (x.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } +} + +function verifyRequestSent(x) { + if (x.readyState === FakeXDomainRequest.UNSENT) { + throw new Error("Request not sent"); + } + if (x.readyState === FakeXDomainRequest.DONE) { + throw new Error("Request done"); + } +} + +function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XDomainRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } +} + +sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, { + open: function open(method, url) { + this.method = method; + this.url = url; + + this.responseText = null; + this.sendFlag = false; + + this.readyStateChange(FakeXDomainRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + var eventName = ""; + switch (this.readyState) { + case FakeXDomainRequest.UNSENT: + break; + case FakeXDomainRequest.OPENED: + break; + case FakeXDomainRequest.LOADING: + if (this.sendFlag) { + //raise the progress event + eventName = "onprogress"; + } + break; + case FakeXDomainRequest.DONE: + if (this.isTimeout) { + eventName = "ontimeout"; + } else if (this.errorFlag || (this.status < 200 || this.status > 299)) { + eventName = "onerror"; + } else { + eventName = "onload"; + } + break; + } + + // raising event (if defined) + if (eventName) { + if (typeof this[eventName] === "function") { + try { + this[eventName](); + } catch (e) { + sinon.logError("Fake XHR " + eventName + " handler", e); + } + } + } + }, + + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + this.requestBody = data; + } + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + + this.errorFlag = false; + this.sendFlag = true; + this.readyStateChange(FakeXDomainRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + + if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) { + this.readyStateChange(sinon.FakeXDomainRequest.DONE); + this.sendFlag = false; + } + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyResponseBodyType(body); + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + this.readyStateChange(FakeXDomainRequest.LOADING); + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + this.readyStateChange(FakeXDomainRequest.DONE); + }, + + respond: function respond(status, contentType, body) { + // content-type ignored, since XDomainRequest does not carry this + // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease + // test integration across browsers + this.status = typeof status === "number" ? status : 200; + this.setResponseBody(body || ""); + }, + + simulatetimeout: function simulatetimeout() { + this.status = 0; + this.isTimeout = true; + // Access to this should actually throw an error + this.responseText = undefined; + this.readyStateChange(FakeXDomainRequest.DONE); + } +}); + +sinon.extend(FakeXDomainRequest, { + UNSENT: 0, + OPENED: 1, + LOADING: 3, + DONE: 4 +}); + +sinon.useFakeXDomainRequest = function useFakeXDomainRequest() { + sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) { + if (xdr.supportsXDR) { + global.XDomainRequest = xdr.GlobalXDomainRequest; + } + + delete sinon.FakeXDomainRequest.restore; + + if (keepOnCreate !== true) { + delete sinon.FakeXDomainRequest.onCreate; + } + }; + if (xdr.supportsXDR) { + global.XDomainRequest = sinon.FakeXDomainRequest; + } + return sinon.FakeXDomainRequest; +}; + +sinon.FakeXDomainRequest = FakeXDomainRequest; + + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../extend":1,"../log_error":3,"./core":12,"./event":18}],23:[function(require,module,exports){ +(function (global){ +/** + * Fake XMLHttpRequest object + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +"use strict"; + +require("../extend"); +require("./event"); +require("../log_error"); + +var sinon = require("./core"); + +function getWorkingXHR(globalScope) { + var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined"; + if (supportsXHR) { + return globalScope.XMLHttpRequest; + } + + var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined"; + if (supportsActiveX) { + return function () { + return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0"); + }; + } + + return false; +} + +var supportsProgress = typeof ProgressEvent !== "undefined"; +var supportsCustomEvent = typeof CustomEvent !== "undefined"; +var supportsFormData = typeof FormData !== "undefined"; +var supportsArrayBuffer = typeof ArrayBuffer !== "undefined"; +var supportsBlob = typeof Blob === "function"; +var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest }; +sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest; +sinonXhr.GlobalActiveXObject = global.ActiveXObject; +sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== "undefined"; +sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined"; +sinonXhr.workingXHR = getWorkingXHR(global); +sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest()); + +var unsafeHeaders = { + "Accept-Charset": true, + "Accept-Encoding": true, + Connection: true, + "Content-Length": true, + Cookie: true, + Cookie2: true, + "Content-Transfer-Encoding": true, + Date: true, + Expect: true, + Host: true, + "Keep-Alive": true, + Referer: true, + TE: true, + Trailer: true, + "Transfer-Encoding": true, + Upgrade: true, + "User-Agent": true, + Via: true +}; + +// An upload object is created for each +// FakeXMLHttpRequest and allows upload +// events to be simulated using uploadProgress +// and uploadError. +function UploadProgress() { + this.eventListeners = { + abort: [], + error: [], + load: [], + loadend: [], + progress: [] + }; +} + +UploadProgress.prototype.addEventListener = function addEventListener(event, listener) { + this.eventListeners[event].push(listener); +}; + +UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) { + var listeners = this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } +}; + +UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) { + var listeners = this.eventListeners[event.type] || []; + + for (var i = 0, listener; (listener = listeners[i]) != null; i++) { + listener(event); + } +}; + +// Note that for FakeXMLHttpRequest to work pre ES5 +// we lose some of the alignment with the spec. +// To ensure as close a match as possible, +// set responseType before calling open, send or respond; +function FakeXMLHttpRequest() { + this.readyState = FakeXMLHttpRequest.UNSENT; + this.requestHeaders = {}; + this.requestBody = null; + this.status = 0; + this.statusText = ""; + this.upload = new UploadProgress(); + this.responseType = ""; + this.response = ""; + if (sinonXhr.supportsCORS) { + this.withCredentials = false; + } + + var xhr = this; + var events = ["loadstart", "load", "abort", "loadend"]; + + function addEventListener(eventName) { + xhr.addEventListener(eventName, function (event) { + var listener = xhr["on" + eventName]; + + if (listener && typeof listener === "function") { + listener.call(this, event); + } + }); + } + + for (var i = events.length - 1; i >= 0; i--) { + addEventListener(events[i]); + } + + if (typeof FakeXMLHttpRequest.onCreate === "function") { + FakeXMLHttpRequest.onCreate(this); + } +} + +function verifyState(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xhr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } +} + +function getHeader(headers, header) { + header = header.toLowerCase(); + + for (var h in headers) { + if (h.toLowerCase() === header) { + return h; + } + } + + return null; +} + +// filtering to enable a white-list version of Sinon FakeXhr, +// where whitelisted requests are passed through to real XHR +function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } +} +function some(collection, callback) { + for (var index = 0; index < collection.length; index++) { + if (callback(collection[index]) === true) { + return true; + } + } + return false; +} +// largest arity in XHR is 5 - XHR#open +var apply = function (obj, method, args) { + switch (args.length) { + case 0: return obj[method](); + case 1: return obj[method](args[0]); + case 2: return obj[method](args[0], args[1]); + case 3: return obj[method](args[0], args[1], args[2]); + case 4: return obj[method](args[0], args[1], args[2], args[3]); + case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]); + } +}; + +FakeXMLHttpRequest.filters = []; +FakeXMLHttpRequest.addFilter = function addFilter(fn) { + this.filters.push(fn); +}; +var IE6Re = /MSIE 6/; +FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) { + var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap + + each([ + "open", + "setRequestHeader", + "send", + "abort", + "getResponseHeader", + "getAllResponseHeaders", + "addEventListener", + "overrideMimeType", + "removeEventListener" + ], function (method) { + fakeXhr[method] = function () { + return apply(xhr, method, arguments); + }; + }); + + var copyAttrs = function (args) { + each(args, function (attr) { + try { + fakeXhr[attr] = xhr[attr]; + } catch (e) { + if (!IE6Re.test(navigator.userAgent)) { + throw e; + } + } + }); + }; + + var stateChange = function stateChange() { + fakeXhr.readyState = xhr.readyState; + if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { + copyAttrs(["status", "statusText"]); + } + if (xhr.readyState >= FakeXMLHttpRequest.LOADING) { + copyAttrs(["responseText", "response"]); + } + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + copyAttrs(["responseXML"]); + } + if (fakeXhr.onreadystatechange) { + fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr }); + } + }; + + if (xhr.addEventListener) { + for (var event in fakeXhr.eventListeners) { + if (fakeXhr.eventListeners.hasOwnProperty(event)) { + + /*eslint-disable no-loop-func*/ + each(fakeXhr.eventListeners[event], function (handler) { + xhr.addEventListener(event, handler); + }); + /*eslint-enable no-loop-func*/ + } + } + xhr.addEventListener("readystatechange", stateChange); + } else { + xhr.onreadystatechange = stateChange; + } + apply(xhr, "open", xhrArgs); +}; +FakeXMLHttpRequest.useFilters = false; + +function verifyRequestOpened(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR - " + xhr.readyState); + } +} + +function verifyRequestSent(xhr) { + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + throw new Error("Request done"); + } +} + +function verifyHeadersReceived(xhr) { + if (xhr.async && xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) { + throw new Error("No headers received"); + } +} + +function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XMLHttpRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } +} + +function convertToArrayBuffer(body) { + var buffer = new ArrayBuffer(body.length); + var view = new Uint8Array(buffer); + for (var i = 0; i < body.length; i++) { + var charCode = body.charCodeAt(i); + if (charCode >= 256) { + throw new TypeError("arraybuffer or blob responseTypes require binary string, " + + "invalid character " + body[i] + " found."); + } + view[i] = charCode; + } + return buffer; +} + +function isXmlContentType(contentType) { + return !contentType || /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType); +} + +function convertResponseBody(responseType, contentType, body) { + if (responseType === "" || responseType === "text") { + return body; + } else if (supportsArrayBuffer && responseType === "arraybuffer") { + return convertToArrayBuffer(body); + } else if (responseType === "json") { + try { + return JSON.parse(body); + } catch (e) { + // Return parsing failure as null + return null; + } + } else if (supportsBlob && responseType === "blob") { + var blobOptions = {}; + if (contentType) { + blobOptions.type = contentType; + } + return new Blob([convertToArrayBuffer(body)], blobOptions); + } else if (responseType === "document") { + if (isXmlContentType(contentType)) { + return FakeXMLHttpRequest.parseXML(body); + } + return null; + } + throw new Error("Invalid responseType " + responseType); +} + +function clearResponse(xhr) { + if (xhr.responseType === "" || xhr.responseType === "text") { + xhr.response = xhr.responseText = ""; + } else { + xhr.response = xhr.responseText = null; + } + xhr.responseXML = null; +} + +FakeXMLHttpRequest.parseXML = function parseXML(text) { + // Treat empty string as parsing failure + if (text !== "") { + try { + if (typeof DOMParser !== "undefined") { + var parser = new DOMParser(); + return parser.parseFromString(text, "text/xml"); + } + var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(text); + return xmlDoc; + } catch (e) { + // Unable to parse XML - no biggie + } + } + + return null; +}; + +FakeXMLHttpRequest.statusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 300: "Multiple Choice", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 422: "Unprocessable Entity", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" +}; + +sinon.xhr = sinonXhr; + +sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { + async: true, + + open: function open(method, url, async, username, password) { + this.method = method; + this.url = url; + this.async = typeof async === "boolean" ? async : true; + this.username = username; + this.password = password; + clearResponse(this); + this.requestHeaders = {}; + this.sendFlag = false; + + if (FakeXMLHttpRequest.useFilters === true) { + var xhrArgs = arguments; + var defake = some(FakeXMLHttpRequest.filters, function (filter) { + return filter.apply(this, xhrArgs); + }); + if (defake) { + return FakeXMLHttpRequest.defake(this, arguments); + } + } + this.readyStateChange(FakeXMLHttpRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + + var readyStateChangeEvent = new sinon.Event("readystatechange", false, false, this); + var event, progress; + + if (typeof this.onreadystatechange === "function") { + try { + this.onreadystatechange(readyStateChangeEvent); + } catch (e) { + sinon.logError("Fake XHR onreadystatechange handler", e); + } + } + + if (this.readyState === FakeXMLHttpRequest.DONE) { + if (this.status < 200 || this.status > 299) { + progress = {loaded: 0, total: 0}; + event = this.aborted ? "abort" : "error"; + } + else { + progress = {loaded: 100, total: 100}; + event = "load"; + } + + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progress, this)); + this.upload.dispatchEvent(new sinon.ProgressEvent(event, progress, this)); + this.upload.dispatchEvent(new sinon.ProgressEvent("loadend", progress, this)); + } + + this.dispatchEvent(new sinon.ProgressEvent("progress", progress, this)); + this.dispatchEvent(new sinon.ProgressEvent(event, progress, this)); + this.dispatchEvent(new sinon.ProgressEvent("loadend", progress, this)); + } + + this.dispatchEvent(readyStateChangeEvent); + }, + + setRequestHeader: function setRequestHeader(header, value) { + verifyState(this); + + if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { + throw new Error("Refused to set unsafe header \"" + header + "\""); + } + + if (this.requestHeaders[header]) { + this.requestHeaders[header] += "," + value; + } else { + this.requestHeaders[header] = value; + } + }, + + // Helps testing + setResponseHeaders: function setResponseHeaders(headers) { + verifyRequestOpened(this); + this.responseHeaders = {}; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + this.responseHeaders[header] = headers[header]; + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); + } else { + this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; + } + }, + + // Currently treats ALL data as a DOMString (i.e. no Document) + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + var contentType = getHeader(this.requestHeaders, "Content-Type"); + if (this.requestHeaders[contentType]) { + var value = this.requestHeaders[contentType].split(";"); + this.requestHeaders[contentType] = value[0] + ";charset=utf-8"; + } else if (supportsFormData && !(data instanceof FormData)) { + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + } + + this.requestBody = data; + } + + this.errorFlag = false; + this.sendFlag = this.async; + clearResponse(this); + this.readyStateChange(FakeXMLHttpRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + + this.dispatchEvent(new sinon.Event("loadstart", false, false, this)); + }, + + abort: function abort() { + this.aborted = true; + clearResponse(this); + this.errorFlag = true; + this.requestHeaders = {}; + this.responseHeaders = {}; + + if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) { + this.readyStateChange(FakeXMLHttpRequest.DONE); + this.sendFlag = false; + } + + this.readyState = FakeXMLHttpRequest.UNSENT; + }, + + getResponseHeader: function getResponseHeader(header) { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return null; + } + + if (/^Set-Cookie2?$/i.test(header)) { + return null; + } + + header = getHeader(this.responseHeaders, header); + + return this.responseHeaders[header] || null; + }, + + getAllResponseHeaders: function getAllResponseHeaders() { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return ""; + } + + var headers = ""; + + for (var header in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(header) && + !/^Set-Cookie2?$/i.test(header)) { + headers += header + ": " + this.responseHeaders[header] + "\r\n"; + } + } + + return headers; + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyHeadersReceived(this); + verifyResponseBodyType(body); + var contentType = this.getResponseHeader("Content-Type"); + + var isTextResponse = this.responseType === "" || this.responseType === "text"; + clearResponse(this); + if (this.async) { + var chunkSize = this.chunkSize || 10; + var index = 0; + + do { + this.readyStateChange(FakeXMLHttpRequest.LOADING); + + if (isTextResponse) { + this.responseText = this.response += body.substring(index, index + chunkSize); + } + index += chunkSize; + } while (index < body.length); + } + + this.response = convertResponseBody(this.responseType, contentType, body); + if (isTextResponse) { + this.responseText = this.response; + } + + if (this.responseType === "document") { + this.responseXML = this.response; + } else if (this.responseType === "" && isXmlContentType(contentType)) { + this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); + } + this.readyStateChange(FakeXMLHttpRequest.DONE); + }, + + respond: function respond(status, headers, body) { + this.status = typeof status === "number" ? status : 200; + this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; + this.setResponseHeaders(headers || {}); + this.setResponseBody(body || ""); + }, + + uploadProgress: function uploadProgress(progressEventRaw) { + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + downloadProgress: function downloadProgress(progressEventRaw) { + if (supportsProgress) { + this.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + uploadError: function uploadError(error) { + if (supportsCustomEvent) { + this.upload.dispatchEvent(new sinon.CustomEvent("error", {detail: error})); + } + } +}); + +sinon.extend(FakeXMLHttpRequest, { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 +}); + +sinon.useFakeXMLHttpRequest = function () { + FakeXMLHttpRequest.restore = function restore(keepOnCreate) { + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = sinonXhr.GlobalActiveXObject; + } + + delete FakeXMLHttpRequest.restore; + + if (keepOnCreate !== true) { + delete FakeXMLHttpRequest.onCreate; + } + }; + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = FakeXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = function ActiveXObject(objId) { + if (objId === "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { + + return new FakeXMLHttpRequest(); + } + + return new sinonXhr.GlobalActiveXObject(objId); + }; + } + + return FakeXMLHttpRequest; +}; + +sinon.FakeXMLHttpRequest = FakeXMLHttpRequest; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../extend":1,"../log_error":3,"./core":12,"./event":18}],24:[function(require,module,exports){ +(function (global){ +((typeof define === "function" && define.amd && function (m) { + define("formatio", ["samsam"], m); +}) || (typeof module === "object" && function (m) { + module.exports = m(require("samsam")); +}) || function (m) { this.formatio = m(this.samsam); } +)(function (samsam) { + "use strict"; + + var formatio = { + excludeConstructors: ["Object", /^.$/], + quoteStrings: true, + limitChildrenCount: 0 + }; + + var hasOwn = Object.prototype.hasOwnProperty; + + var specialObjects = []; + if (typeof global !== "undefined") { + specialObjects.push({ object: global, value: "[object global]" }); + } + if (typeof document !== "undefined") { + specialObjects.push({ + object: document, + value: "[object HTMLDocument]" + }); + } + if (typeof window !== "undefined") { + specialObjects.push({ object: window, value: "[object Window]" }); + } + + function functionName(func) { + if (!func) { return ""; } + if (func.displayName) { return func.displayName; } + if (func.name) { return func.name; } + var matches = func.toString().match(/function\s+([^\(]+)/m); + return (matches && matches[1]) || ""; + } + + function constructorName(f, object) { + var name = functionName(object && object.constructor); + var excludes = f.excludeConstructors || + formatio.excludeConstructors || []; + + var i, l; + for (i = 0, l = excludes.length; i < l; ++i) { + if (typeof excludes[i] === "string" && excludes[i] === name) { + return ""; + } else if (excludes[i].test && excludes[i].test(name)) { + return ""; + } + } + + return name; + } + + function isCircular(object, objects) { + if (typeof object !== "object") { return false; } + var i, l; + for (i = 0, l = objects.length; i < l; ++i) { + if (objects[i] === object) { return true; } + } + return false; + } + + function ascii(f, object, processed, indent) { + if (typeof object === "string") { + var qs = f.quoteStrings; + var quote = typeof qs !== "boolean" || qs; + return processed || quote ? '"' + object + '"' : object; + } + + if (typeof object === "function" && !(object instanceof RegExp)) { + return ascii.func(object); + } + + processed = processed || []; + + if (isCircular(object, processed)) { return "[Circular]"; } + + if (Object.prototype.toString.call(object) === "[object Array]") { + return ascii.array.call(f, object, processed); + } + + if (!object) { return String((1/object) === -Infinity ? "-0" : object); } + if (samsam.isElement(object)) { return ascii.element(object); } + + if (typeof object.toString === "function" && + object.toString !== Object.prototype.toString) { + return object.toString(); + } + + var i, l; + for (i = 0, l = specialObjects.length; i < l; i++) { + if (object === specialObjects[i].object) { + return specialObjects[i].value; + } + } + + return ascii.object.call(f, object, processed, indent); + } + + ascii.func = function (func) { + return "function " + functionName(func) + "() {}"; + }; + + ascii.array = function (array, processed) { + processed = processed || []; + processed.push(array); + var pieces = []; + var i, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, array.length) : array.length; + + for (i = 0; i < l; ++i) { + pieces.push(ascii(this, array[i], processed)); + } + + if(l < array.length) + pieces.push("[... " + (array.length - l) + " more elements]"); + + return "[" + pieces.join(", ") + "]"; + }; + + ascii.object = function (object, processed, indent) { + processed = processed || []; + processed.push(object); + indent = indent || 0; + var pieces = [], properties = samsam.keys(object).sort(); + var length = 3; + var prop, str, obj, i, k, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, properties.length) : properties.length; + + for (i = 0; i < l; ++i) { + prop = properties[i]; + obj = object[prop]; + + if (isCircular(obj, processed)) { + str = "[Circular]"; + } else { + str = ascii(this, obj, processed, indent + 2); + } + + str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str; + length += str.length; + pieces.push(str); + } + + var cons = constructorName(this, object); + var prefix = cons ? "[" + cons + "] " : ""; + var is = ""; + for (i = 0, k = indent; i < k; ++i) { is += " "; } + + if(l < properties.length) + pieces.push("[... " + (properties.length - l) + " more elements]"); + + if (length + indent > 80) { + return prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" + + is + "}"; + } + return prefix + "{ " + pieces.join(", ") + " }"; + }; + + ascii.element = function (element) { + var tagName = element.tagName.toLowerCase(); + var attrs = element.attributes, attr, pairs = [], attrName, i, l, val; + + for (i = 0, l = attrs.length; i < l; ++i) { + attr = attrs.item(i); + attrName = attr.nodeName.toLowerCase().replace("html:", ""); + val = attr.nodeValue; + if (attrName !== "contenteditable" || val !== "inherit") { + if (!!val) { pairs.push(attrName + "=\"" + val + "\""); } + } + } + + var formatted = "<" + tagName + (pairs.length > 0 ? " " : ""); + var content = element.innerHTML; + + if (content.length > 20) { + content = content.substr(0, 20) + "[...]"; + } + + var res = formatted + pairs.join(" ") + ">" + content + + ""; + + return res.replace(/ contentEditable="inherit"/, ""); + }; + + function Formatio(options) { + for (var opt in options) { + this[opt] = options[opt]; + } + } + + Formatio.prototype = { + functionName: functionName, + + configure: function (options) { + return new Formatio(options); + }, + + constructorName: function (object) { + return constructorName(this, object); + }, + + ascii: function (object, processed, indent) { + return ascii(this, object, processed, indent); + } + }; + + return Formatio.prototype; +}); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"samsam":26}],25:[function(require,module,exports){ +(function (global){ +/*global global, window*/ +/** + * @author Christian Johansen (christian@cjohansen.no) and contributors + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ + +(function (global) { + "use strict"; + + // Make properties writable in IE, as per + // http://www.adequatelygood.com/Replacing-setTimeout-Globally.html + // JSLint being anal + var glbl = global; + + global.setTimeout = glbl.setTimeout; + global.clearTimeout = glbl.clearTimeout; + global.setInterval = glbl.setInterval; + global.clearInterval = glbl.clearInterval; + global.Date = glbl.Date; + + // setImmediate is not a standard function + // avoid adding the prop to the window object if not present + if('setImmediate' in global) { + global.setImmediate = glbl.setImmediate; + global.clearImmediate = glbl.clearImmediate; + } + + // node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref() + // browsers, a number. + // see https://github.com/cjohansen/Sinon.JS/pull/436 + + var NOOP = function () { return undefined; }; + var timeoutResult = setTimeout(NOOP, 0); + var addTimerReturnsObject = typeof timeoutResult === "object"; + clearTimeout(timeoutResult); + + var NativeDate = Date; + var uniqueTimerId = 1; + + /** + * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into + * number of milliseconds. This is used to support human-readable strings passed + * to clock.tick() + */ + function parseTime(str) { + if (!str) { + return 0; + } + + var strings = str.split(":"); + var l = strings.length, i = l; + var ms = 0, parsed; + + if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { + throw new Error("tick only understands numbers and 'h:m:s'"); + } + + while (i--) { + parsed = parseInt(strings[i], 10); + + if (parsed >= 60) { + throw new Error("Invalid time " + str); + } + + ms += parsed * Math.pow(60, (l - i - 1)); + } + + return ms * 1000; + } + + /** + * Used to grok the `now` parameter to createClock. + */ + function getEpoch(epoch) { + if (!epoch) { return 0; } + if (typeof epoch.getTime === "function") { return epoch.getTime(); } + if (typeof epoch === "number") { return epoch; } + throw new TypeError("now should be milliseconds since UNIX epoch"); + } + + function inRange(from, to, timer) { + return timer && timer.callAt >= from && timer.callAt <= to; + } + + function mirrorDateProperties(target, source) { + var prop; + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // set special now implementation + if (source.now) { + target.now = function now() { + return target.clock.now; + }; + } else { + delete target.now; + } + + // set special toSource implementation + if (source.toSource) { + target.toSource = function toSource() { + return source.toSource(); + }; + } else { + delete target.toSource; + } + + // set special toString implementation + target.toString = function toString() { + return source.toString(); + }; + + target.prototype = source.prototype; + target.parse = source.parse; + target.UTC = source.UTC; + target.prototype.toUTCString = source.prototype.toUTCString; + + return target; + } + + function createDate() { + function ClockDate(year, month, date, hour, minute, second, ms) { + // Defensive and verbose to avoid potential harm in passing + // explicit undefined when user does not pass argument + switch (arguments.length) { + case 0: + return new NativeDate(ClockDate.clock.now); + case 1: + return new NativeDate(year); + case 2: + return new NativeDate(year, month); + case 3: + return new NativeDate(year, month, date); + case 4: + return new NativeDate(year, month, date, hour); + case 5: + return new NativeDate(year, month, date, hour, minute); + case 6: + return new NativeDate(year, month, date, hour, minute, second); + default: + return new NativeDate(year, month, date, hour, minute, second, ms); + } + } + + return mirrorDateProperties(ClockDate, NativeDate); + } + + function addTimer(clock, timer) { + if (timer.func === undefined) { + throw new Error("Callback must be provided to timer calls"); + } + + if (!clock.timers) { + clock.timers = {}; + } + + timer.id = uniqueTimerId++; + timer.createdAt = clock.now; + timer.callAt = clock.now + (timer.delay || (clock.duringTick ? 1 : 0)); + + clock.timers[timer.id] = timer; + + if (addTimerReturnsObject) { + return { + id: timer.id, + ref: NOOP, + unref: NOOP + }; + } + + return timer.id; + } + + + function compareTimers(a, b) { + // Sort first by absolute timing + if (a.callAt < b.callAt) { + return -1; + } + if (a.callAt > b.callAt) { + return 1; + } + + // Sort next by immediate, immediate timers take precedence + if (a.immediate && !b.immediate) { + return -1; + } + if (!a.immediate && b.immediate) { + return 1; + } + + // Sort next by creation time, earlier-created timers take precedence + if (a.createdAt < b.createdAt) { + return -1; + } + if (a.createdAt > b.createdAt) { + return 1; + } + + // Sort next by id, lower-id timers take precedence + if (a.id < b.id) { + return -1; + } + if (a.id > b.id) { + return 1; + } + + // As timer ids are unique, no fallback `0` is necessary + } + + function firstTimerInRange(clock, from, to) { + var timers = clock.timers, + timer = null, + id, + isInRange; + + for (id in timers) { + if (timers.hasOwnProperty(id)) { + isInRange = inRange(from, to, timers[id]); + + if (isInRange && (!timer || compareTimers(timer, timers[id]) === 1)) { + timer = timers[id]; + } + } + } + + return timer; + } + + function callTimer(clock, timer) { + var exception; + + if (typeof timer.interval === "number") { + clock.timers[timer.id].callAt += timer.interval; + } else { + delete clock.timers[timer.id]; + } + + try { + if (typeof timer.func === "function") { + timer.func.apply(null, timer.args); + } else { + eval(timer.func); + } + } catch (e) { + exception = e; + } + + if (!clock.timers[timer.id]) { + if (exception) { + throw exception; + } + return; + } + + if (exception) { + throw exception; + } + } + + function timerType(timer) { + if (timer.immediate) { + return "Immediate"; + } else if (typeof timer.interval !== "undefined") { + return "Interval"; + } else { + return "Timeout"; + } + } + + function clearTimer(clock, timerId, ttype) { + if (!timerId) { + // null appears to be allowed in most browsers, and appears to be + // relied upon by some libraries, like Bootstrap carousel + return; + } + + if (!clock.timers) { + clock.timers = []; + } + + // in Node, timerId is an object with .ref()/.unref(), and + // its .id field is the actual timer id. + if (typeof timerId === "object") { + timerId = timerId.id; + } + + if (clock.timers.hasOwnProperty(timerId)) { + // check that the ID matches a timer of the correct type + var timer = clock.timers[timerId]; + if (timerType(timer) === ttype) { + delete clock.timers[timerId]; + } else { + throw new Error("Cannot clear timer: timer created with set" + ttype + "() but cleared with clear" + timerType(timer) + "()"); + } + } + } + + function uninstall(clock, target) { + var method, + i, + l; + + for (i = 0, l = clock.methods.length; i < l; i++) { + method = clock.methods[i]; + + if (target[method].hadOwnProperty) { + target[method] = clock["_" + method]; + } else { + try { + delete target[method]; + } catch (ignore) {} + } + } + + // Prevent multiple executions which will completely remove these props + clock.methods = []; + } + + function hijackMethod(target, method, clock) { + var prop; + + clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method); + clock["_" + method] = target[method]; + + if (method === "Date") { + var date = mirrorDateProperties(clock[method], target[method]); + target[method] = date; + } else { + target[method] = function () { + return clock[method].apply(clock, arguments); + }; + + for (prop in clock[method]) { + if (clock[method].hasOwnProperty(prop)) { + target[method][prop] = clock[method][prop]; + } + } + } + + target[method].clock = clock; + } + + var timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: global.setImmediate, + clearImmediate: global.clearImmediate, + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + + var keys = Object.keys || function (obj) { + var ks = [], + key; + + for (key in obj) { + if (obj.hasOwnProperty(key)) { + ks.push(key); + } + } + + return ks; + }; + + exports.timers = timers; + + function createClock(now) { + var clock = { + now: getEpoch(now), + timeouts: {}, + Date: createDate() + }; + + clock.Date.clock = clock; + + clock.setTimeout = function setTimeout(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout + }); + }; + + clock.clearTimeout = function clearTimeout(timerId) { + return clearTimer(clock, timerId, "Timeout"); + }; + + clock.setInterval = function setInterval(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout, + interval: timeout + }); + }; + + clock.clearInterval = function clearInterval(timerId) { + return clearTimer(clock, timerId, "Interval"); + }; + + clock.setImmediate = function setImmediate(func) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 1), + immediate: true + }); + }; + + clock.clearImmediate = function clearImmediate(timerId) { + return clearTimer(clock, timerId, "Immediate"); + }; + + clock.tick = function tick(ms) { + ms = typeof ms === "number" ? ms : parseTime(ms); + var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now; + var timer = firstTimerInRange(clock, tickFrom, tickTo); + var oldNow; + + clock.duringTick = true; + + var firstException; + while (timer && tickFrom <= tickTo) { + if (clock.timers[timer.id]) { + tickFrom = clock.now = timer.callAt; + try { + oldNow = clock.now; + callTimer(clock, timer); + // compensate for any setSystemTime() call during timer callback + if (oldNow !== clock.now) { + tickFrom += clock.now - oldNow; + tickTo += clock.now - oldNow; + previous += clock.now - oldNow; + } + } catch (e) { + firstException = firstException || e; + } + } + + timer = firstTimerInRange(clock, previous, tickTo); + previous = tickFrom; + } + + clock.duringTick = false; + clock.now = tickTo; + + if (firstException) { + throw firstException; + } + + return clock.now; + }; + + clock.reset = function reset() { + clock.timers = {}; + }; + + clock.setSystemTime = function setSystemTime(now) { + // determine time difference + var newNow = getEpoch(now); + var difference = newNow - clock.now; + + // update 'system clock' + clock.now = newNow; + + // update timers and intervals to keep them stable + for (var id in clock.timers) { + if (clock.timers.hasOwnProperty(id)) { + var timer = clock.timers[id]; + timer.createdAt += difference; + timer.callAt += difference; + } + } + }; + + return clock; + } + exports.createClock = createClock; + + exports.install = function install(target, now, toFake) { + var i, + l; + + if (typeof target === "number") { + toFake = now; + now = target; + target = null; + } + + if (!target) { + target = global; + } + + var clock = createClock(now); + + clock.uninstall = function () { + uninstall(clock, target); + }; + + clock.methods = toFake || []; + + if (clock.methods.length === 0) { + clock.methods = keys(timers); + } + + for (i = 0, l = clock.methods.length; i < l; i++) { + hijackMethod(target, clock.methods[i], clock); + } + + return clock; + }; + +}(global || this)); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],26:[function(require,module,exports){ +((typeof define === "function" && define.amd && function (m) { define("samsam", m); }) || + (typeof module === "object" && + function (m) { module.exports = m(); }) || // Node + function (m) { this.samsam = m(); } // Browser globals +)(function () { + var o = Object.prototype; + var div = typeof document !== "undefined" && document.createElement("div"); + + function isNaN(value) { + // Unlike global isNaN, this avoids type coercion + // typeof check avoids IE host object issues, hat tip to + // lodash + var val = value; // JsLint thinks value !== value is "weird" + return typeof value === "number" && value !== val; + } + + function getClass(value) { + // Returns the internal [[Class]] by calling Object.prototype.toString + // with the provided value as this. Return value is a string, naming the + // internal class, e.g. "Array" + return o.toString.call(value).split(/[ \]]/)[1]; + } + + /** + * @name samsam.isArguments + * @param Object object + * + * Returns ``true`` if ``object`` is an ``arguments`` object, + * ``false`` otherwise. + */ + function isArguments(object) { + if (getClass(object) === 'Arguments') { return true; } + if (typeof object !== "object" || typeof object.length !== "number" || + getClass(object) === "Array") { + return false; + } + if (typeof object.callee == "function") { return true; } + try { + object[object.length] = 6; + delete object[object.length]; + } catch (e) { + return true; + } + return false; + } + + /** + * @name samsam.isElement + * @param Object object + * + * Returns ``true`` if ``object`` is a DOM element node. Unlike + * Underscore.js/lodash, this function will return ``false`` if ``object`` + * is an *element-like* object, i.e. a regular object with a ``nodeType`` + * property that holds the value ``1``. + */ + function isElement(object) { + if (!object || object.nodeType !== 1 || !div) { return false; } + try { + object.appendChild(div); + object.removeChild(div); + } catch (e) { + return false; + } + return true; + } + + /** + * @name samsam.keys + * @param Object object + * + * Return an array of own property names. + */ + function keys(object) { + var ks = [], prop; + for (prop in object) { + if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); } + } + return ks; + } + + /** + * @name samsam.isDate + * @param Object value + * + * Returns true if the object is a ``Date``, or *date-like*. Duck typing + * of date objects work by checking that the object has a ``getTime`` + * function whose return value equals the return value from the object's + * ``valueOf``. + */ + function isDate(value) { + return typeof value.getTime == "function" && + value.getTime() == value.valueOf(); + } + + /** + * @name samsam.isNegZero + * @param Object value + * + * Returns ``true`` if ``value`` is ``-0``. + */ + function isNegZero(value) { + return value === 0 && 1 / value === -Infinity; + } + + /** + * @name samsam.equal + * @param Object obj1 + * @param Object obj2 + * + * Returns ``true`` if two objects are strictly equal. Compared to + * ``===`` there are two exceptions: + * + * - NaN is considered equal to NaN + * - -0 and +0 are not considered equal + */ + function identical(obj1, obj2) { + if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) { + return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2); + } + } + + + /** + * @name samsam.deepEqual + * @param Object obj1 + * @param Object obj2 + * + * Deep equal comparison. Two values are "deep equal" if: + * + * - They are equal, according to samsam.identical + * - They are both date objects representing the same time + * - They are both arrays containing elements that are all deepEqual + * - They are objects with the same set of properties, and each property + * in ``obj1`` is deepEqual to the corresponding property in ``obj2`` + * + * Supports cyclic objects. + */ + function deepEqualCyclic(obj1, obj2) { + + // used for cyclic comparison + // contain already visited objects + var objects1 = [], + objects2 = [], + // contain pathes (position in the object structure) + // of the already visited objects + // indexes same as in objects arrays + paths1 = [], + paths2 = [], + // contains combinations of already compared objects + // in the manner: { "$1['ref']$2['ref']": true } + compared = {}; + + /** + * used to check, if the value of a property is an object + * (cyclic logic is only needed for objects) + * only needed for cyclic logic + */ + function isObject(value) { + + if (typeof value === 'object' && value !== null && + !(value instanceof Boolean) && + !(value instanceof Date) && + !(value instanceof Number) && + !(value instanceof RegExp) && + !(value instanceof String)) { + + return true; + } + + return false; + } + + /** + * returns the index of the given object in the + * given objects array, -1 if not contained + * only needed for cyclic logic + */ + function getIndex(objects, obj) { + + var i; + for (i = 0; i < objects.length; i++) { + if (objects[i] === obj) { + return i; + } + } + + return -1; + } + + // does the recursion for the deep equal check + return (function deepEqual(obj1, obj2, path1, path2) { + var type1 = typeof obj1; + var type2 = typeof obj2; + + // == null also matches undefined + if (obj1 === obj2 || + isNaN(obj1) || isNaN(obj2) || + obj1 == null || obj2 == null || + type1 !== "object" || type2 !== "object") { + + return identical(obj1, obj2); + } + + // Elements are only equal if identical(expected, actual) + if (isElement(obj1) || isElement(obj2)) { return false; } + + var isDate1 = isDate(obj1), isDate2 = isDate(obj2); + if (isDate1 || isDate2) { + if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) { + return false; + } + } + + if (obj1 instanceof RegExp && obj2 instanceof RegExp) { + if (obj1.toString() !== obj2.toString()) { return false; } + } + + var class1 = getClass(obj1); + var class2 = getClass(obj2); + var keys1 = keys(obj1); + var keys2 = keys(obj2); + + if (isArguments(obj1) || isArguments(obj2)) { + if (obj1.length !== obj2.length) { return false; } + } else { + if (type1 !== type2 || class1 !== class2 || + keys1.length !== keys2.length) { + return false; + } + } + + var key, i, l, + // following vars are used for the cyclic logic + value1, value2, + isObject1, isObject2, + index1, index2, + newPath1, newPath2; + + for (i = 0, l = keys1.length; i < l; i++) { + key = keys1[i]; + if (!o.hasOwnProperty.call(obj2, key)) { + return false; + } + + // Start of the cyclic logic + + value1 = obj1[key]; + value2 = obj2[key]; + + isObject1 = isObject(value1); + isObject2 = isObject(value2); + + // determine, if the objects were already visited + // (it's faster to check for isObject first, than to + // get -1 from getIndex for non objects) + index1 = isObject1 ? getIndex(objects1, value1) : -1; + index2 = isObject2 ? getIndex(objects2, value2) : -1; + + // determine the new pathes of the objects + // - for non cyclic objects the current path will be extended + // by current property name + // - for cyclic objects the stored path is taken + newPath1 = index1 !== -1 + ? paths1[index1] + : path1 + '[' + JSON.stringify(key) + ']'; + newPath2 = index2 !== -1 + ? paths2[index2] + : path2 + '[' + JSON.stringify(key) + ']'; + + // stop recursion if current objects are already compared + if (compared[newPath1 + newPath2]) { + return true; + } + + // remember the current objects and their pathes + if (index1 === -1 && isObject1) { + objects1.push(value1); + paths1.push(newPath1); + } + if (index2 === -1 && isObject2) { + objects2.push(value2); + paths2.push(newPath2); + } + + // remember that the current objects are already compared + if (isObject1 && isObject2) { + compared[newPath1 + newPath2] = true; + } + + // End of cyclic logic + + // neither value1 nor value2 is a cycle + // continue with next level + if (!deepEqual(value1, value2, newPath1, newPath2)) { + return false; + } + } + + return true; + + }(obj1, obj2, '$1', '$2')); + } + + var match; + + function arrayContains(array, subset) { + if (subset.length === 0) { return true; } + var i, l, j, k; + for (i = 0, l = array.length; i < l; ++i) { + if (match(array[i], subset[0])) { + for (j = 0, k = subset.length; j < k; ++j) { + if (!match(array[i + j], subset[j])) { return false; } + } + return true; + } + } + return false; + } + + /** + * @name samsam.match + * @param Object object + * @param Object matcher + * + * Compare arbitrary value ``object`` with matcher. + */ + match = function match(object, matcher) { + if (matcher && typeof matcher.test === "function") { + return matcher.test(object); + } + + if (typeof matcher === "function") { + return matcher(object) === true; + } + + if (typeof matcher === "string") { + matcher = matcher.toLowerCase(); + var notNull = typeof object === "string" || !!object; + return notNull && + (String(object)).toLowerCase().indexOf(matcher) >= 0; + } + + if (typeof matcher === "number") { + return matcher === object; + } + + if (typeof matcher === "boolean") { + return matcher === object; + } + + if (typeof(matcher) === "undefined") { + return typeof(object) === "undefined"; + } + + if (matcher === null) { + return object === null; + } + + if (getClass(object) === "Array" && getClass(matcher) === "Array") { + return arrayContains(object, matcher); + } + + if (matcher && typeof matcher === "object") { + if (matcher === object) { + return true; + } + var prop; + for (prop in matcher) { + var value = object[prop]; + if (typeof value === "undefined" && + typeof object.getAttribute === "function") { + value = object.getAttribute(prop); + } + if (matcher[prop] === null || typeof matcher[prop] === 'undefined') { + if (value !== matcher[prop]) { + return false; + } + } else if (typeof value === "undefined" || !match(value, matcher[prop])) { + return false; + } + } + return true; + } + + throw new Error("Matcher was not a string, a number, a " + + "function, a boolean or an object"); + }; + + return { + isArguments: isArguments, + isElement: isElement, + isDate: isDate, + isNegZero: isNegZero, + identical: identical, + deepEqual: deepEqualCyclic, + match: match, + keys: keys + }; +}); + +},{}]},{},[20])(20) +}); diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-server-1.17.2.js b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-server-1.17.2.js new file mode 100644 index 0000000..7d3678a --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-server-1.17.2.js @@ -0,0 +1,2245 @@ +/** + * Sinon.JS 1.17.2, 2015/10/21 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +var sinon = (function () { +"use strict"; + // eslint-disable-line no-unused-vars + + var sinonModule; + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + sinonModule = module.exports = require("./sinon/util/core"); + require("./sinon/extend"); + require("./sinon/walk"); + require("./sinon/typeOf"); + require("./sinon/times_in_words"); + require("./sinon/spy"); + require("./sinon/call"); + require("./sinon/behavior"); + require("./sinon/stub"); + require("./sinon/mock"); + require("./sinon/collection"); + require("./sinon/assert"); + require("./sinon/sandbox"); + require("./sinon/test"); + require("./sinon/test_case"); + require("./sinon/match"); + require("./sinon/format"); + require("./sinon/log_error"); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + sinonModule = module.exports; + } else { + sinonModule = {}; + } + + return sinonModule; +}()); + +/** + * @depend ../../sinon.js + */ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + var div = typeof document !== "undefined" && document.createElement("div"); + var hasOwn = Object.prototype.hasOwnProperty; + + function isDOMNode(obj) { + var success = false; + + try { + obj.appendChild(div); + success = div.parentNode === obj; + } catch (e) { + return false; + } finally { + try { + obj.removeChild(div); + } catch (e) { + // Remove failed, not much we can do about that + } + } + + return success; + } + + function isElement(obj) { + return div && obj && obj.nodeType === 1 && isDOMNode(obj); + } + + function isFunction(obj) { + return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply); + } + + function isReallyNaN(val) { + return typeof val === "number" && isNaN(val); + } + + function mirrorProperties(target, source) { + for (var prop in source) { + if (!hasOwn.call(target, prop)) { + target[prop] = source[prop]; + } + } + } + + function isRestorable(obj) { + return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon; + } + + // Cheap way to detect if we have ES5 support. + var hasES5Support = "keys" in Object; + + function makeApi(sinon) { + sinon.wrapMethod = function wrapMethod(object, property, method) { + if (!object) { + throw new TypeError("Should wrap property of object"); + } + + if (typeof method !== "function" && typeof method !== "object") { + throw new TypeError("Method wrapper should be a function or a property descriptor"); + } + + function checkWrappedMethod(wrappedMethod) { + var error; + + if (!isFunction(wrappedMethod)) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } else if (wrappedMethod.calledBefore) { + var verb = wrappedMethod.returns ? "stubbed" : "spied on"; + error = new TypeError("Attempted to wrap " + property + " which is already " + verb); + } + + if (error) { + if (wrappedMethod && wrappedMethod.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethod.stackTrace; + } + throw error; + } + } + + var error, wrappedMethod, i; + + // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem + // when using hasOwn.call on objects from other frames. + var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property); + + if (hasES5Support) { + var methodDesc = (typeof method === "function") ? {value: method} : method; + var wrappedMethodDesc = sinon.getPropertyDescriptor(object, property); + + if (!wrappedMethodDesc) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } + if (error) { + if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace; + } + throw error; + } + + var types = sinon.objectKeys(methodDesc); + for (i = 0; i < types.length; i++) { + wrappedMethod = wrappedMethodDesc[types[i]]; + checkWrappedMethod(wrappedMethod); + } + + mirrorProperties(methodDesc, wrappedMethodDesc); + for (i = 0; i < types.length; i++) { + mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]); + } + Object.defineProperty(object, property, methodDesc); + } else { + wrappedMethod = object[property]; + checkWrappedMethod(wrappedMethod); + object[property] = method; + method.displayName = property; + } + + method.displayName = property; + + // Set up a stack trace which can be used later to find what line of + // code the original method was created on. + method.stackTrace = (new Error("Stack Trace for original")).stack; + + method.restore = function () { + // For prototype properties try to reset by delete first. + // If this fails (ex: localStorage on mobile safari) then force a reset + // via direct assignment. + if (!owned) { + // In some cases `delete` may throw an error + try { + delete object[property]; + } catch (e) {} // eslint-disable-line no-empty + // For native code functions `delete` fails without throwing an error + // on Chrome < 43, PhantomJS, etc. + } else if (hasES5Support) { + Object.defineProperty(object, property, wrappedMethodDesc); + } + + // Use strict equality comparison to check failures then force a reset + // via direct assignment. + if (object[property] === method) { + object[property] = wrappedMethod; + } + }; + + method.restore.sinon = true; + + if (!hasES5Support) { + mirrorProperties(method, wrappedMethod); + } + + return method; + }; + + sinon.create = function create(proto) { + var F = function () {}; + F.prototype = proto; + return new F(); + }; + + sinon.deepEqual = function deepEqual(a, b) { + if (sinon.match && sinon.match.isMatcher(a)) { + return a.test(b); + } + + if (typeof a !== "object" || typeof b !== "object") { + return isReallyNaN(a) && isReallyNaN(b) || a === b; + } + + if (isElement(a) || isElement(b)) { + return a === b; + } + + if (a === b) { + return true; + } + + if ((a === null && b !== null) || (a !== null && b === null)) { + return false; + } + + if (a instanceof RegExp && b instanceof RegExp) { + return (a.source === b.source) && (a.global === b.global) && + (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline); + } + + var aString = Object.prototype.toString.call(a); + if (aString !== Object.prototype.toString.call(b)) { + return false; + } + + if (aString === "[object Date]") { + return a.valueOf() === b.valueOf(); + } + + var prop; + var aLength = 0; + var bLength = 0; + + if (aString === "[object Array]" && a.length !== b.length) { + return false; + } + + for (prop in a) { + if (a.hasOwnProperty(prop)) { + aLength += 1; + + if (!(prop in b)) { + return false; + } + + if (!deepEqual(a[prop], b[prop])) { + return false; + } + } + } + + for (prop in b) { + if (b.hasOwnProperty(prop)) { + bLength += 1; + } + } + + return aLength === bLength; + }; + + sinon.functionName = function functionName(func) { + var name = func.displayName || func.name; + + // Use function decomposition as a last resort to get function + // name. Does not rely on function decomposition to work - if it + // doesn't debugging will be slightly less informative + // (i.e. toString will say 'spy' rather than 'myFunc'). + if (!name) { + var matches = func.toString().match(/function ([^\s\(]+)/); + name = matches && matches[1]; + } + + return name; + }; + + sinon.functionToString = function toString() { + if (this.getCall && this.callCount) { + var thisValue, + prop; + var i = this.callCount; + + while (i--) { + thisValue = this.getCall(i).thisValue; + + for (prop in thisValue) { + if (thisValue[prop] === this) { + return prop; + } + } + } + } + + return this.displayName || "sinon fake"; + }; + + sinon.objectKeys = function objectKeys(obj) { + if (obj !== Object(obj)) { + throw new TypeError("sinon.objectKeys called on a non-object"); + } + + var keys = []; + var key; + for (key in obj) { + if (hasOwn.call(obj, key)) { + keys.push(key); + } + } + + return keys; + }; + + sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) { + var proto = object; + var descriptor; + + while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) { + proto = Object.getPrototypeOf(proto); + } + return descriptor; + }; + + sinon.getConfig = function (custom) { + var config = {}; + custom = custom || {}; + var defaults = sinon.defaultConfig; + + for (var prop in defaults) { + if (defaults.hasOwnProperty(prop)) { + config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; + } + } + + return config; + }; + + sinon.defaultConfig = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.timesInWords = function timesInWords(count) { + return count === 1 && "once" || + count === 2 && "twice" || + count === 3 && "thrice" || + (count || 0) + " times"; + }; + + sinon.calledInOrder = function (spies) { + for (var i = 1, l = spies.length; i < l; i++) { + if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) { + return false; + } + } + + return true; + }; + + sinon.orderByFirstCall = function (spies) { + return spies.sort(function (a, b) { + // uuid, won't ever be equal + var aCall = a.getCall(0); + var bCall = b.getCall(0); + var aId = aCall && aCall.callId || -1; + var bId = bCall && bCall.callId || -1; + + return aId < bId ? -1 : 1; + }); + }; + + sinon.createStubInstance = function (constructor) { + if (typeof constructor !== "function") { + throw new TypeError("The constructor should be a function."); + } + return sinon.stub(sinon.create(constructor.prototype)); + }; + + sinon.restore = function (object) { + if (object !== null && typeof object === "object") { + for (var prop in object) { + if (isRestorable(object[prop])) { + object[prop].restore(); + } + } + } else if (isRestorable(object)) { + object.restore(); + } + }; + + return sinon; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports) { + makeApi(exports); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + + // Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + var hasDontEnumBug = (function () { + var obj = { + constructor: function () { + return "0"; + }, + toString: function () { + return "1"; + }, + valueOf: function () { + return "2"; + }, + toLocaleString: function () { + return "3"; + }, + prototype: function () { + return "4"; + }, + isPrototypeOf: function () { + return "5"; + }, + propertyIsEnumerable: function () { + return "6"; + }, + hasOwnProperty: function () { + return "7"; + }, + length: function () { + return "8"; + }, + unique: function () { + return "9"; + } + }; + + var result = []; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + result.push(obj[prop]()); + } + } + return result.join("") !== "0123456789"; + })(); + + /* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will + * override properties in previous sources. + * + * target - The Object to extend + * sources - Objects to copy properties from. + * + * Returns the extended target + */ + function extend(target /*, sources */) { + var sources = Array.prototype.slice.call(arguments, 1); + var source, i, prop; + + for (i = 0; i < sources.length; i++) { + source = sources[i]; + + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // Make sure we copy (own) toString method even when in JScript with DontEnum bug + // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) { + target.toString = source.toString; + } + } + + return target; + } + + sinon.extend = extend; + return sinon.extend; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * Minimal Event interface implementation + * + * Original implementation by Sven Fuchs: https://gist.github.com/995028 + * Modifications and tests by Christian Johansen. + * + * @author Sven Fuchs (svenfuchs@artweb-design.de) + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2011 Sven Fuchs, Christian Johansen + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +(function () { + + var push = [].push; + + function makeApi(sinon) { + sinon.Event = function Event(type, bubbles, cancelable, target) { + this.initEvent(type, bubbles, cancelable, target); + }; + + sinon.Event.prototype = { + initEvent: function (type, bubbles, cancelable, target) { + this.type = type; + this.bubbles = bubbles; + this.cancelable = cancelable; + this.target = target; + }, + + stopPropagation: function () {}, + + preventDefault: function () { + this.defaultPrevented = true; + } + }; + + sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) { + this.initEvent(type, false, false, target); + this.loaded = progressEventRaw.loaded || null; + this.total = progressEventRaw.total || null; + this.lengthComputable = !!progressEventRaw.total; + }; + + sinon.ProgressEvent.prototype = new sinon.Event(); + + sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent; + + sinon.CustomEvent = function CustomEvent(type, customData, target) { + this.initEvent(type, false, false, target); + this.detail = customData.detail || null; + }; + + sinon.CustomEvent.prototype = new sinon.Event(); + + sinon.CustomEvent.prototype.constructor = sinon.CustomEvent; + + sinon.EventTarget = { + addEventListener: function addEventListener(event, listener) { + this.eventListeners = this.eventListeners || {}; + this.eventListeners[event] = this.eventListeners[event] || []; + push.call(this.eventListeners[event], listener); + }, + + removeEventListener: function removeEventListener(event, listener) { + var listeners = this.eventListeners && this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }, + + dispatchEvent: function dispatchEvent(event) { + var type = event.type; + var listeners = this.eventListeners && this.eventListeners[type] || []; + + for (var i = 0; i < listeners.length; i++) { + if (typeof listeners[i] === "function") { + listeners[i].call(this, event); + } else { + listeners[i].handleEvent(event); + } + } + + return !!event.defaultPrevented; + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend util/core.js + */ +/** + * Logs errors + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal) { + + // cache a reference to setTimeout, so that our reference won't be stubbed out + // when using fake timers and errors will still get logged + // https://github.com/cjohansen/Sinon.JS/issues/381 + var realSetTimeout = setTimeout; + + function makeApi(sinon) { + + function log() {} + + function logError(label, err) { + var msg = label + " threw exception: "; + + function throwLoggedError() { + err.message = msg + err.message; + throw err; + } + + sinon.log(msg + "[" + err.name + "] " + err.message); + + if (err.stack) { + sinon.log(err.stack); + } + + if (logError.useImmediateExceptions) { + throwLoggedError(); + } else { + logError.setTimeout(throwLoggedError, 0); + } + } + + // When set to true, any errors logged will be thrown immediately; + // If set to false, the errors will be thrown in separate execution frame. + logError.useImmediateExceptions = false; + + // wrap realSetTimeout with something we can stub in tests + logError.setTimeout = function (func, timeout) { + realSetTimeout(func, timeout); + }; + + var exports = {}; + exports.log = sinon.log = log; + exports.logError = sinon.logError = logError; + + return exports; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XDomainRequest object + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +// wrapper for global +(function (global) { + + var xdr = { XDomainRequest: global.XDomainRequest }; + xdr.GlobalXDomainRequest = global.XDomainRequest; + xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== "undefined"; + xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false; + + function makeApi(sinon) { + sinon.xdr = xdr; + + function FakeXDomainRequest() { + this.readyState = FakeXDomainRequest.UNSENT; + this.requestBody = null; + this.requestHeaders = {}; + this.status = 0; + this.timeout = null; + + if (typeof FakeXDomainRequest.onCreate === "function") { + FakeXDomainRequest.onCreate(this); + } + } + + function verifyState(x) { + if (x.readyState !== FakeXDomainRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (x.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function verifyRequestSent(x) { + if (x.readyState === FakeXDomainRequest.UNSENT) { + throw new Error("Request not sent"); + } + if (x.readyState === FakeXDomainRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XDomainRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, { + open: function open(method, url) { + this.method = method; + this.url = url; + + this.responseText = null; + this.sendFlag = false; + + this.readyStateChange(FakeXDomainRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + var eventName = ""; + switch (this.readyState) { + case FakeXDomainRequest.UNSENT: + break; + case FakeXDomainRequest.OPENED: + break; + case FakeXDomainRequest.LOADING: + if (this.sendFlag) { + //raise the progress event + eventName = "onprogress"; + } + break; + case FakeXDomainRequest.DONE: + if (this.isTimeout) { + eventName = "ontimeout"; + } else if (this.errorFlag || (this.status < 200 || this.status > 299)) { + eventName = "onerror"; + } else { + eventName = "onload"; + } + break; + } + + // raising event (if defined) + if (eventName) { + if (typeof this[eventName] === "function") { + try { + this[eventName](); + } catch (e) { + sinon.logError("Fake XHR " + eventName + " handler", e); + } + } + } + }, + + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + this.requestBody = data; + } + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + + this.errorFlag = false; + this.sendFlag = true; + this.readyStateChange(FakeXDomainRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + + if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) { + this.readyStateChange(sinon.FakeXDomainRequest.DONE); + this.sendFlag = false; + } + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyResponseBodyType(body); + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + this.readyStateChange(FakeXDomainRequest.LOADING); + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + this.readyStateChange(FakeXDomainRequest.DONE); + }, + + respond: function respond(status, contentType, body) { + // content-type ignored, since XDomainRequest does not carry this + // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease + // test integration across browsers + this.status = typeof status === "number" ? status : 200; + this.setResponseBody(body || ""); + }, + + simulatetimeout: function simulatetimeout() { + this.status = 0; + this.isTimeout = true; + // Access to this should actually throw an error + this.responseText = undefined; + this.readyStateChange(FakeXDomainRequest.DONE); + } + }); + + sinon.extend(FakeXDomainRequest, { + UNSENT: 0, + OPENED: 1, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXDomainRequest = function useFakeXDomainRequest() { + sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) { + if (xdr.supportsXDR) { + global.XDomainRequest = xdr.GlobalXDomainRequest; + } + + delete sinon.FakeXDomainRequest.restore; + + if (keepOnCreate !== true) { + delete sinon.FakeXDomainRequest.onCreate; + } + }; + if (xdr.supportsXDR) { + global.XDomainRequest = sinon.FakeXDomainRequest; + } + return sinon.FakeXDomainRequest; + }; + + sinon.FakeXDomainRequest = FakeXDomainRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +})(typeof global !== "undefined" ? global : self); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XMLHttpRequest object + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal, global) { + + function getWorkingXHR(globalScope) { + var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined"; + if (supportsXHR) { + return globalScope.XMLHttpRequest; + } + + var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined"; + if (supportsActiveX) { + return function () { + return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0"); + }; + } + + return false; + } + + var supportsProgress = typeof ProgressEvent !== "undefined"; + var supportsCustomEvent = typeof CustomEvent !== "undefined"; + var supportsFormData = typeof FormData !== "undefined"; + var supportsArrayBuffer = typeof ArrayBuffer !== "undefined"; + var supportsBlob = typeof Blob === "function"; + var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest }; + sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest; + sinonXhr.GlobalActiveXObject = global.ActiveXObject; + sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== "undefined"; + sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined"; + sinonXhr.workingXHR = getWorkingXHR(global); + sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest()); + + var unsafeHeaders = { + "Accept-Charset": true, + "Accept-Encoding": true, + Connection: true, + "Content-Length": true, + Cookie: true, + Cookie2: true, + "Content-Transfer-Encoding": true, + Date: true, + Expect: true, + Host: true, + "Keep-Alive": true, + Referer: true, + TE: true, + Trailer: true, + "Transfer-Encoding": true, + Upgrade: true, + "User-Agent": true, + Via: true + }; + + // An upload object is created for each + // FakeXMLHttpRequest and allows upload + // events to be simulated using uploadProgress + // and uploadError. + function UploadProgress() { + this.eventListeners = { + progress: [], + load: [], + abort: [], + error: [] + }; + } + + UploadProgress.prototype.addEventListener = function addEventListener(event, listener) { + this.eventListeners[event].push(listener); + }; + + UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) { + var listeners = this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }; + + UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) { + var listeners = this.eventListeners[event.type] || []; + + for (var i = 0, listener; (listener = listeners[i]) != null; i++) { + listener(event); + } + }; + + // Note that for FakeXMLHttpRequest to work pre ES5 + // we lose some of the alignment with the spec. + // To ensure as close a match as possible, + // set responseType before calling open, send or respond; + function FakeXMLHttpRequest() { + this.readyState = FakeXMLHttpRequest.UNSENT; + this.requestHeaders = {}; + this.requestBody = null; + this.status = 0; + this.statusText = ""; + this.upload = new UploadProgress(); + this.responseType = ""; + this.response = ""; + if (sinonXhr.supportsCORS) { + this.withCredentials = false; + } + + var xhr = this; + var events = ["loadstart", "load", "abort", "loadend"]; + + function addEventListener(eventName) { + xhr.addEventListener(eventName, function (event) { + var listener = xhr["on" + eventName]; + + if (listener && typeof listener === "function") { + listener.call(this, event); + } + }); + } + + for (var i = events.length - 1; i >= 0; i--) { + addEventListener(events[i]); + } + + if (typeof FakeXMLHttpRequest.onCreate === "function") { + FakeXMLHttpRequest.onCreate(this); + } + } + + function verifyState(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xhr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function getHeader(headers, header) { + header = header.toLowerCase(); + + for (var h in headers) { + if (h.toLowerCase() === header) { + return h; + } + } + + return null; + } + + // filtering to enable a white-list version of Sinon FakeXhr, + // where whitelisted requests are passed through to real XHR + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + function some(collection, callback) { + for (var index = 0; index < collection.length; index++) { + if (callback(collection[index]) === true) { + return true; + } + } + return false; + } + // largest arity in XHR is 5 - XHR#open + var apply = function (obj, method, args) { + switch (args.length) { + case 0: return obj[method](); + case 1: return obj[method](args[0]); + case 2: return obj[method](args[0], args[1]); + case 3: return obj[method](args[0], args[1], args[2]); + case 4: return obj[method](args[0], args[1], args[2], args[3]); + case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]); + } + }; + + FakeXMLHttpRequest.filters = []; + FakeXMLHttpRequest.addFilter = function addFilter(fn) { + this.filters.push(fn); + }; + var IE6Re = /MSIE 6/; + FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) { + var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap + + each([ + "open", + "setRequestHeader", + "send", + "abort", + "getResponseHeader", + "getAllResponseHeaders", + "addEventListener", + "overrideMimeType", + "removeEventListener" + ], function (method) { + fakeXhr[method] = function () { + return apply(xhr, method, arguments); + }; + }); + + var copyAttrs = function (args) { + each(args, function (attr) { + try { + fakeXhr[attr] = xhr[attr]; + } catch (e) { + if (!IE6Re.test(navigator.userAgent)) { + throw e; + } + } + }); + }; + + var stateChange = function stateChange() { + fakeXhr.readyState = xhr.readyState; + if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { + copyAttrs(["status", "statusText"]); + } + if (xhr.readyState >= FakeXMLHttpRequest.LOADING) { + copyAttrs(["responseText", "response"]); + } + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + copyAttrs(["responseXML"]); + } + if (fakeXhr.onreadystatechange) { + fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr }); + } + }; + + if (xhr.addEventListener) { + for (var event in fakeXhr.eventListeners) { + if (fakeXhr.eventListeners.hasOwnProperty(event)) { + + /*eslint-disable no-loop-func*/ + each(fakeXhr.eventListeners[event], function (handler) { + xhr.addEventListener(event, handler); + }); + /*eslint-enable no-loop-func*/ + } + } + xhr.addEventListener("readystatechange", stateChange); + } else { + xhr.onreadystatechange = stateChange; + } + apply(xhr, "open", xhrArgs); + }; + FakeXMLHttpRequest.useFilters = false; + + function verifyRequestOpened(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR - " + xhr.readyState); + } + } + + function verifyRequestSent(xhr) { + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyHeadersReceived(xhr) { + if (xhr.async && xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) { + throw new Error("No headers received"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XMLHttpRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + function convertToArrayBuffer(body) { + var buffer = new ArrayBuffer(body.length); + var view = new Uint8Array(buffer); + for (var i = 0; i < body.length; i++) { + var charCode = body.charCodeAt(i); + if (charCode >= 256) { + throw new TypeError("arraybuffer or blob responseTypes require binary string, " + + "invalid character " + body[i] + " found."); + } + view[i] = charCode; + } + return buffer; + } + + function isXmlContentType(contentType) { + return !contentType || /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType); + } + + function convertResponseBody(responseType, contentType, body) { + if (responseType === "" || responseType === "text") { + return body; + } else if (supportsArrayBuffer && responseType === "arraybuffer") { + return convertToArrayBuffer(body); + } else if (responseType === "json") { + try { + return JSON.parse(body); + } catch (e) { + // Return parsing failure as null + return null; + } + } else if (supportsBlob && responseType === "blob") { + var blobOptions = {}; + if (contentType) { + blobOptions.type = contentType; + } + return new Blob([convertToArrayBuffer(body)], blobOptions); + } else if (responseType === "document") { + if (isXmlContentType(contentType)) { + return FakeXMLHttpRequest.parseXML(body); + } + return null; + } + throw new Error("Invalid responseType " + responseType); + } + + function clearResponse(xhr) { + if (xhr.responseType === "" || xhr.responseType === "text") { + xhr.response = xhr.responseText = ""; + } else { + xhr.response = xhr.responseText = null; + } + xhr.responseXML = null; + } + + FakeXMLHttpRequest.parseXML = function parseXML(text) { + // Treat empty string as parsing failure + if (text !== "") { + try { + if (typeof DOMParser !== "undefined") { + var parser = new DOMParser(); + return parser.parseFromString(text, "text/xml"); + } + var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(text); + return xmlDoc; + } catch (e) { + // Unable to parse XML - no biggie + } + } + + return null; + }; + + FakeXMLHttpRequest.statusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 300: "Multiple Choice", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 422: "Unprocessable Entity", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" + }; + + function makeApi(sinon) { + sinon.xhr = sinonXhr; + + sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { + async: true, + + open: function open(method, url, async, username, password) { + this.method = method; + this.url = url; + this.async = typeof async === "boolean" ? async : true; + this.username = username; + this.password = password; + clearResponse(this); + this.requestHeaders = {}; + this.sendFlag = false; + + if (FakeXMLHttpRequest.useFilters === true) { + var xhrArgs = arguments; + var defake = some(FakeXMLHttpRequest.filters, function (filter) { + return filter.apply(this, xhrArgs); + }); + if (defake) { + return FakeXMLHttpRequest.defake(this, arguments); + } + } + this.readyStateChange(FakeXMLHttpRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + + var readyStateChangeEvent = new sinon.Event("readystatechange", false, false, this); + + if (typeof this.onreadystatechange === "function") { + try { + this.onreadystatechange(readyStateChangeEvent); + } catch (e) { + sinon.logError("Fake XHR onreadystatechange handler", e); + } + } + + switch (this.readyState) { + case FakeXMLHttpRequest.DONE: + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + this.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + } + this.upload.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("loadend", false, false, this)); + break; + } + + this.dispatchEvent(readyStateChangeEvent); + }, + + setRequestHeader: function setRequestHeader(header, value) { + verifyState(this); + + if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { + throw new Error("Refused to set unsafe header \"" + header + "\""); + } + + if (this.requestHeaders[header]) { + this.requestHeaders[header] += "," + value; + } else { + this.requestHeaders[header] = value; + } + }, + + // Helps testing + setResponseHeaders: function setResponseHeaders(headers) { + verifyRequestOpened(this); + this.responseHeaders = {}; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + this.responseHeaders[header] = headers[header]; + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); + } else { + this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; + } + }, + + // Currently treats ALL data as a DOMString (i.e. no Document) + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + var contentType = getHeader(this.requestHeaders, "Content-Type"); + if (this.requestHeaders[contentType]) { + var value = this.requestHeaders[contentType].split(";"); + this.requestHeaders[contentType] = value[0] + ";charset=utf-8"; + } else if (supportsFormData && !(data instanceof FormData)) { + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + } + + this.requestBody = data; + } + + this.errorFlag = false; + this.sendFlag = this.async; + clearResponse(this); + this.readyStateChange(FakeXMLHttpRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + + this.dispatchEvent(new sinon.Event("loadstart", false, false, this)); + }, + + abort: function abort() { + this.aborted = true; + clearResponse(this); + this.errorFlag = true; + this.requestHeaders = {}; + this.responseHeaders = {}; + + if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) { + this.readyStateChange(FakeXMLHttpRequest.DONE); + this.sendFlag = false; + } + + this.readyState = FakeXMLHttpRequest.UNSENT; + + this.dispatchEvent(new sinon.Event("abort", false, false, this)); + + this.upload.dispatchEvent(new sinon.Event("abort", false, false, this)); + + if (typeof this.onerror === "function") { + this.onerror(); + } + }, + + getResponseHeader: function getResponseHeader(header) { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return null; + } + + if (/^Set-Cookie2?$/i.test(header)) { + return null; + } + + header = getHeader(this.responseHeaders, header); + + return this.responseHeaders[header] || null; + }, + + getAllResponseHeaders: function getAllResponseHeaders() { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return ""; + } + + var headers = ""; + + for (var header in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(header) && + !/^Set-Cookie2?$/i.test(header)) { + headers += header + ": " + this.responseHeaders[header] + "\r\n"; + } + } + + return headers; + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyHeadersReceived(this); + verifyResponseBodyType(body); + var contentType = this.getResponseHeader("Content-Type"); + + var isTextResponse = this.responseType === "" || this.responseType === "text"; + clearResponse(this); + if (this.async) { + var chunkSize = this.chunkSize || 10; + var index = 0; + + do { + this.readyStateChange(FakeXMLHttpRequest.LOADING); + + if (isTextResponse) { + this.responseText = this.response += body.substring(index, index + chunkSize); + } + index += chunkSize; + } while (index < body.length); + } + + this.response = convertResponseBody(this.responseType, contentType, body); + if (isTextResponse) { + this.responseText = this.response; + } + + if (this.responseType === "document") { + this.responseXML = this.response; + } else if (this.responseType === "" && isXmlContentType(contentType)) { + this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); + } + this.readyStateChange(FakeXMLHttpRequest.DONE); + }, + + respond: function respond(status, headers, body) { + this.status = typeof status === "number" ? status : 200; + this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; + this.setResponseHeaders(headers || {}); + this.setResponseBody(body || ""); + }, + + uploadProgress: function uploadProgress(progressEventRaw) { + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + downloadProgress: function downloadProgress(progressEventRaw) { + if (supportsProgress) { + this.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + uploadError: function uploadError(error) { + if (supportsCustomEvent) { + this.upload.dispatchEvent(new sinon.CustomEvent("error", {detail: error})); + } + } + }); + + sinon.extend(FakeXMLHttpRequest, { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXMLHttpRequest = function () { + FakeXMLHttpRequest.restore = function restore(keepOnCreate) { + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = sinonXhr.GlobalActiveXObject; + } + + delete FakeXMLHttpRequest.restore; + + if (keepOnCreate !== true) { + delete FakeXMLHttpRequest.onCreate; + } + }; + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = FakeXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = function ActiveXObject(objId) { + if (objId === "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { + + return new FakeXMLHttpRequest(); + } + + return new sinonXhr.GlobalActiveXObject(objId); + }; + } + + return FakeXMLHttpRequest; + }; + + sinon.FakeXMLHttpRequest = FakeXMLHttpRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof global !== "undefined" ? global : self +)); + +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal, formatio) { + + function makeApi(sinon) { + function valueFormatter(value) { + return "" + value; + } + + function getFormatioFormatter() { + var formatter = formatio.configure({ + quoteStrings: false, + limitChildrenCount: 250 + }); + + function format() { + return formatter.ascii.apply(formatter, arguments); + } + + return format; + } + + function getNodeFormatter() { + try { + var util = require("util"); + } catch (e) { + /* Node, but no util module - would be very old, but better safe than sorry */ + } + + function format(v) { + var isObjectWithNativeToString = typeof v === "object" && v.toString === Object.prototype.toString; + return isObjectWithNativeToString ? util.inspect(v) : v; + } + + return util ? format : valueFormatter; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var formatter; + + if (isNode) { + try { + formatio = require("formatio"); + } + catch (e) {} // eslint-disable-line no-empty + } + + if (formatio) { + formatter = getFormatioFormatter(); + } else if (isNode) { + formatter = getNodeFormatter(); + } else { + formatter = valueFormatter; + } + + sinon.format = formatter; + return sinon.format; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof formatio === "object" && formatio // eslint-disable-line no-undef +)); + +/** + * @depend fake_xdomain_request.js + * @depend fake_xml_http_request.js + * @depend ../format.js + * @depend ../log_error.js + */ +/** + * The Sinon "server" mimics a web server that receives requests from + * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, + * both synchronously and asynchronously. To respond synchronuously, canned + * answers have to be provided upfront. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + var push = [].push; + + function responseArray(handler) { + var response = handler; + + if (Object.prototype.toString.call(handler) !== "[object Array]") { + response = [200, {}, handler]; + } + + if (typeof response[2] !== "string") { + throw new TypeError("Fake server response body should be string, but was " + + typeof response[2]); + } + + return response; + } + + var wloc = typeof window !== "undefined" ? window.location : {}; + var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host); + + function matchOne(response, reqMethod, reqUrl) { + var rmeth = response.method; + var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase(); + var url = response.url; + var matchUrl = !url || url === reqUrl || (typeof url.test === "function" && url.test(reqUrl)); + + return matchMethod && matchUrl; + } + + function match(response, request) { + var requestUrl = request.url; + + if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { + requestUrl = requestUrl.replace(rCurrLoc, ""); + } + + if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { + if (typeof response.response === "function") { + var ru = response.url; + var args = [request].concat(ru && typeof ru.exec === "function" ? ru.exec(requestUrl).slice(1) : []); + return response.response.apply(response, args); + } + + return true; + } + + return false; + } + + function makeApi(sinon) { + sinon.fakeServer = { + create: function (config) { + var server = sinon.create(this); + server.configure(config); + if (!sinon.xhr.supportsCORS) { + this.xhr = sinon.useFakeXDomainRequest(); + } else { + this.xhr = sinon.useFakeXMLHttpRequest(); + } + server.requests = []; + + this.xhr.onCreate = function (xhrObj) { + server.addRequest(xhrObj); + }; + + return server; + }, + configure: function (config) { + var whitelist = { + "autoRespond": true, + "autoRespondAfter": true, + "respondImmediately": true, + "fakeHTTPMethods": true + }; + var setting; + + config = config || {}; + for (setting in config) { + if (whitelist.hasOwnProperty(setting) && config.hasOwnProperty(setting)) { + this[setting] = config[setting]; + } + } + }, + addRequest: function addRequest(xhrObj) { + var server = this; + push.call(this.requests, xhrObj); + + xhrObj.onSend = function () { + server.handleRequest(this); + + if (server.respondImmediately) { + server.respond(); + } else if (server.autoRespond && !server.responding) { + setTimeout(function () { + server.responding = false; + server.respond(); + }, server.autoRespondAfter || 10); + + server.responding = true; + } + }; + }, + + getHTTPMethod: function getHTTPMethod(request) { + if (this.fakeHTTPMethods && /post/i.test(request.method)) { + var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); + return matches ? matches[1] : request.method; + } + + return request.method; + }, + + handleRequest: function handleRequest(xhr) { + if (xhr.async) { + if (!this.queue) { + this.queue = []; + } + + push.call(this.queue, xhr); + } else { + this.processRequest(xhr); + } + }, + + log: function log(response, request) { + var str; + + str = "Request:\n" + sinon.format(request) + "\n\n"; + str += "Response:\n" + sinon.format(response) + "\n\n"; + + sinon.log(str); + }, + + respondWith: function respondWith(method, url, body) { + if (arguments.length === 1 && typeof method !== "function") { + this.response = responseArray(method); + return; + } + + if (!this.responses) { + this.responses = []; + } + + if (arguments.length === 1) { + body = method; + url = method = null; + } + + if (arguments.length === 2) { + body = url; + url = method; + method = null; + } + + push.call(this.responses, { + method: method, + url: url, + response: typeof body === "function" ? body : responseArray(body) + }); + }, + + respond: function respond() { + if (arguments.length > 0) { + this.respondWith.apply(this, arguments); + } + + var queue = this.queue || []; + var requests = queue.splice(0, queue.length); + + for (var i = 0; i < requests.length; i++) { + this.processRequest(requests[i]); + } + }, + + processRequest: function processRequest(request) { + try { + if (request.aborted) { + return; + } + + var response = this.response || [404, {}, ""]; + + if (this.responses) { + for (var l = this.responses.length, i = l - 1; i >= 0; i--) { + if (match.call(this, this.responses[i], request)) { + response = this.responses[i].response; + break; + } + } + } + + if (request.readyState !== 4) { + this.log(response, request); + + request.respond(response[0], response[1], response[2]); + } + } catch (e) { + sinon.logError("Fake server request processing", e); + } + }, + + restore: function restore() { + return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("./fake_xdomain_request"); + require("./fake_xml_http_request"); + require("../format"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * Fake timer API + * setTimeout + * setInterval + * clearTimeout + * clearInterval + * tick + * reset + * Date + * + * Inspired by jsUnitMockTimeOut from JsUnit + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + function makeApi(s, lol) { + /*global lolex */ + var llx = typeof lolex !== "undefined" ? lolex : lol; + + s.useFakeTimers = function () { + var now; + var methods = Array.prototype.slice.call(arguments); + + if (typeof methods[0] === "string") { + now = 0; + } else { + now = methods.shift(); + } + + var clock = llx.install(now || 0, methods); + clock.restore = clock.uninstall; + return clock; + }; + + s.clock = { + create: function (now) { + return llx.createClock(now); + } + }; + + s.timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), + clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined), + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, epxorts, module, lolex) { + var core = require("./core"); + makeApi(core, lolex); + module.exports = core; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module, require("lolex")); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend fake_server.js + * @depend fake_timers.js + */ +/** + * Add-on for sinon.fakeServer that automatically handles a fake timer along with + * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery + * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, + * it polls the object for completion with setInterval. Dispite the direct + * motivation, there is nothing jQuery-specific in this file, so it can be used + * in any environment where the ajax implementation depends on setInterval or + * setTimeout. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + function makeApi(sinon) { + function Server() {} + Server.prototype = sinon.fakeServer; + + sinon.fakeServerWithClock = new Server(); + + sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { + if (xhr.async) { + if (typeof setTimeout.clock === "object") { + this.clock = setTimeout.clock; + } else { + this.clock = sinon.useFakeTimers(); + this.resetClock = true; + } + + if (!this.longestTimeout) { + var clockSetTimeout = this.clock.setTimeout; + var clockSetInterval = this.clock.setInterval; + var server = this; + + this.clock.setTimeout = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetTimeout.apply(this, arguments); + }; + + this.clock.setInterval = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetInterval.apply(this, arguments); + }; + } + } + + return sinon.fakeServer.addRequest.call(this, xhr); + }; + + sinon.fakeServerWithClock.respond = function respond() { + var returnVal = sinon.fakeServer.respond.apply(this, arguments); + + if (this.clock) { + this.clock.tick(this.longestTimeout || 0); + this.longestTimeout = 0; + + if (this.resetClock) { + this.clock.restore(); + this.resetClock = false; + } + } + + return returnVal; + }; + + sinon.fakeServerWithClock.restore = function restore() { + if (this.clock) { + this.clock.restore(); + } + + return sinon.fakeServer.restore.apply(this, arguments); + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + require("./fake_server"); + require("./fake_timers"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-server.js b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-server.js new file mode 100644 index 0000000..7d3678a --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon-server.js @@ -0,0 +1,2245 @@ +/** + * Sinon.JS 1.17.2, 2015/10/21 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +var sinon = (function () { +"use strict"; + // eslint-disable-line no-unused-vars + + var sinonModule; + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + sinonModule = module.exports = require("./sinon/util/core"); + require("./sinon/extend"); + require("./sinon/walk"); + require("./sinon/typeOf"); + require("./sinon/times_in_words"); + require("./sinon/spy"); + require("./sinon/call"); + require("./sinon/behavior"); + require("./sinon/stub"); + require("./sinon/mock"); + require("./sinon/collection"); + require("./sinon/assert"); + require("./sinon/sandbox"); + require("./sinon/test"); + require("./sinon/test_case"); + require("./sinon/match"); + require("./sinon/format"); + require("./sinon/log_error"); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + sinonModule = module.exports; + } else { + sinonModule = {}; + } + + return sinonModule; +}()); + +/** + * @depend ../../sinon.js + */ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + var div = typeof document !== "undefined" && document.createElement("div"); + var hasOwn = Object.prototype.hasOwnProperty; + + function isDOMNode(obj) { + var success = false; + + try { + obj.appendChild(div); + success = div.parentNode === obj; + } catch (e) { + return false; + } finally { + try { + obj.removeChild(div); + } catch (e) { + // Remove failed, not much we can do about that + } + } + + return success; + } + + function isElement(obj) { + return div && obj && obj.nodeType === 1 && isDOMNode(obj); + } + + function isFunction(obj) { + return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply); + } + + function isReallyNaN(val) { + return typeof val === "number" && isNaN(val); + } + + function mirrorProperties(target, source) { + for (var prop in source) { + if (!hasOwn.call(target, prop)) { + target[prop] = source[prop]; + } + } + } + + function isRestorable(obj) { + return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon; + } + + // Cheap way to detect if we have ES5 support. + var hasES5Support = "keys" in Object; + + function makeApi(sinon) { + sinon.wrapMethod = function wrapMethod(object, property, method) { + if (!object) { + throw new TypeError("Should wrap property of object"); + } + + if (typeof method !== "function" && typeof method !== "object") { + throw new TypeError("Method wrapper should be a function or a property descriptor"); + } + + function checkWrappedMethod(wrappedMethod) { + var error; + + if (!isFunction(wrappedMethod)) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } else if (wrappedMethod.calledBefore) { + var verb = wrappedMethod.returns ? "stubbed" : "spied on"; + error = new TypeError("Attempted to wrap " + property + " which is already " + verb); + } + + if (error) { + if (wrappedMethod && wrappedMethod.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethod.stackTrace; + } + throw error; + } + } + + var error, wrappedMethod, i; + + // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem + // when using hasOwn.call on objects from other frames. + var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property); + + if (hasES5Support) { + var methodDesc = (typeof method === "function") ? {value: method} : method; + var wrappedMethodDesc = sinon.getPropertyDescriptor(object, property); + + if (!wrappedMethodDesc) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } + if (error) { + if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace; + } + throw error; + } + + var types = sinon.objectKeys(methodDesc); + for (i = 0; i < types.length; i++) { + wrappedMethod = wrappedMethodDesc[types[i]]; + checkWrappedMethod(wrappedMethod); + } + + mirrorProperties(methodDesc, wrappedMethodDesc); + for (i = 0; i < types.length; i++) { + mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]); + } + Object.defineProperty(object, property, methodDesc); + } else { + wrappedMethod = object[property]; + checkWrappedMethod(wrappedMethod); + object[property] = method; + method.displayName = property; + } + + method.displayName = property; + + // Set up a stack trace which can be used later to find what line of + // code the original method was created on. + method.stackTrace = (new Error("Stack Trace for original")).stack; + + method.restore = function () { + // For prototype properties try to reset by delete first. + // If this fails (ex: localStorage on mobile safari) then force a reset + // via direct assignment. + if (!owned) { + // In some cases `delete` may throw an error + try { + delete object[property]; + } catch (e) {} // eslint-disable-line no-empty + // For native code functions `delete` fails without throwing an error + // on Chrome < 43, PhantomJS, etc. + } else if (hasES5Support) { + Object.defineProperty(object, property, wrappedMethodDesc); + } + + // Use strict equality comparison to check failures then force a reset + // via direct assignment. + if (object[property] === method) { + object[property] = wrappedMethod; + } + }; + + method.restore.sinon = true; + + if (!hasES5Support) { + mirrorProperties(method, wrappedMethod); + } + + return method; + }; + + sinon.create = function create(proto) { + var F = function () {}; + F.prototype = proto; + return new F(); + }; + + sinon.deepEqual = function deepEqual(a, b) { + if (sinon.match && sinon.match.isMatcher(a)) { + return a.test(b); + } + + if (typeof a !== "object" || typeof b !== "object") { + return isReallyNaN(a) && isReallyNaN(b) || a === b; + } + + if (isElement(a) || isElement(b)) { + return a === b; + } + + if (a === b) { + return true; + } + + if ((a === null && b !== null) || (a !== null && b === null)) { + return false; + } + + if (a instanceof RegExp && b instanceof RegExp) { + return (a.source === b.source) && (a.global === b.global) && + (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline); + } + + var aString = Object.prototype.toString.call(a); + if (aString !== Object.prototype.toString.call(b)) { + return false; + } + + if (aString === "[object Date]") { + return a.valueOf() === b.valueOf(); + } + + var prop; + var aLength = 0; + var bLength = 0; + + if (aString === "[object Array]" && a.length !== b.length) { + return false; + } + + for (prop in a) { + if (a.hasOwnProperty(prop)) { + aLength += 1; + + if (!(prop in b)) { + return false; + } + + if (!deepEqual(a[prop], b[prop])) { + return false; + } + } + } + + for (prop in b) { + if (b.hasOwnProperty(prop)) { + bLength += 1; + } + } + + return aLength === bLength; + }; + + sinon.functionName = function functionName(func) { + var name = func.displayName || func.name; + + // Use function decomposition as a last resort to get function + // name. Does not rely on function decomposition to work - if it + // doesn't debugging will be slightly less informative + // (i.e. toString will say 'spy' rather than 'myFunc'). + if (!name) { + var matches = func.toString().match(/function ([^\s\(]+)/); + name = matches && matches[1]; + } + + return name; + }; + + sinon.functionToString = function toString() { + if (this.getCall && this.callCount) { + var thisValue, + prop; + var i = this.callCount; + + while (i--) { + thisValue = this.getCall(i).thisValue; + + for (prop in thisValue) { + if (thisValue[prop] === this) { + return prop; + } + } + } + } + + return this.displayName || "sinon fake"; + }; + + sinon.objectKeys = function objectKeys(obj) { + if (obj !== Object(obj)) { + throw new TypeError("sinon.objectKeys called on a non-object"); + } + + var keys = []; + var key; + for (key in obj) { + if (hasOwn.call(obj, key)) { + keys.push(key); + } + } + + return keys; + }; + + sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) { + var proto = object; + var descriptor; + + while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) { + proto = Object.getPrototypeOf(proto); + } + return descriptor; + }; + + sinon.getConfig = function (custom) { + var config = {}; + custom = custom || {}; + var defaults = sinon.defaultConfig; + + for (var prop in defaults) { + if (defaults.hasOwnProperty(prop)) { + config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; + } + } + + return config; + }; + + sinon.defaultConfig = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.timesInWords = function timesInWords(count) { + return count === 1 && "once" || + count === 2 && "twice" || + count === 3 && "thrice" || + (count || 0) + " times"; + }; + + sinon.calledInOrder = function (spies) { + for (var i = 1, l = spies.length; i < l; i++) { + if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) { + return false; + } + } + + return true; + }; + + sinon.orderByFirstCall = function (spies) { + return spies.sort(function (a, b) { + // uuid, won't ever be equal + var aCall = a.getCall(0); + var bCall = b.getCall(0); + var aId = aCall && aCall.callId || -1; + var bId = bCall && bCall.callId || -1; + + return aId < bId ? -1 : 1; + }); + }; + + sinon.createStubInstance = function (constructor) { + if (typeof constructor !== "function") { + throw new TypeError("The constructor should be a function."); + } + return sinon.stub(sinon.create(constructor.prototype)); + }; + + sinon.restore = function (object) { + if (object !== null && typeof object === "object") { + for (var prop in object) { + if (isRestorable(object[prop])) { + object[prop].restore(); + } + } + } else if (isRestorable(object)) { + object.restore(); + } + }; + + return sinon; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports) { + makeApi(exports); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + + // Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + var hasDontEnumBug = (function () { + var obj = { + constructor: function () { + return "0"; + }, + toString: function () { + return "1"; + }, + valueOf: function () { + return "2"; + }, + toLocaleString: function () { + return "3"; + }, + prototype: function () { + return "4"; + }, + isPrototypeOf: function () { + return "5"; + }, + propertyIsEnumerable: function () { + return "6"; + }, + hasOwnProperty: function () { + return "7"; + }, + length: function () { + return "8"; + }, + unique: function () { + return "9"; + } + }; + + var result = []; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + result.push(obj[prop]()); + } + } + return result.join("") !== "0123456789"; + })(); + + /* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will + * override properties in previous sources. + * + * target - The Object to extend + * sources - Objects to copy properties from. + * + * Returns the extended target + */ + function extend(target /*, sources */) { + var sources = Array.prototype.slice.call(arguments, 1); + var source, i, prop; + + for (i = 0; i < sources.length; i++) { + source = sources[i]; + + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // Make sure we copy (own) toString method even when in JScript with DontEnum bug + // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) { + target.toString = source.toString; + } + } + + return target; + } + + sinon.extend = extend; + return sinon.extend; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * Minimal Event interface implementation + * + * Original implementation by Sven Fuchs: https://gist.github.com/995028 + * Modifications and tests by Christian Johansen. + * + * @author Sven Fuchs (svenfuchs@artweb-design.de) + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2011 Sven Fuchs, Christian Johansen + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +(function () { + + var push = [].push; + + function makeApi(sinon) { + sinon.Event = function Event(type, bubbles, cancelable, target) { + this.initEvent(type, bubbles, cancelable, target); + }; + + sinon.Event.prototype = { + initEvent: function (type, bubbles, cancelable, target) { + this.type = type; + this.bubbles = bubbles; + this.cancelable = cancelable; + this.target = target; + }, + + stopPropagation: function () {}, + + preventDefault: function () { + this.defaultPrevented = true; + } + }; + + sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) { + this.initEvent(type, false, false, target); + this.loaded = progressEventRaw.loaded || null; + this.total = progressEventRaw.total || null; + this.lengthComputable = !!progressEventRaw.total; + }; + + sinon.ProgressEvent.prototype = new sinon.Event(); + + sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent; + + sinon.CustomEvent = function CustomEvent(type, customData, target) { + this.initEvent(type, false, false, target); + this.detail = customData.detail || null; + }; + + sinon.CustomEvent.prototype = new sinon.Event(); + + sinon.CustomEvent.prototype.constructor = sinon.CustomEvent; + + sinon.EventTarget = { + addEventListener: function addEventListener(event, listener) { + this.eventListeners = this.eventListeners || {}; + this.eventListeners[event] = this.eventListeners[event] || []; + push.call(this.eventListeners[event], listener); + }, + + removeEventListener: function removeEventListener(event, listener) { + var listeners = this.eventListeners && this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }, + + dispatchEvent: function dispatchEvent(event) { + var type = event.type; + var listeners = this.eventListeners && this.eventListeners[type] || []; + + for (var i = 0; i < listeners.length; i++) { + if (typeof listeners[i] === "function") { + listeners[i].call(this, event); + } else { + listeners[i].handleEvent(event); + } + } + + return !!event.defaultPrevented; + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend util/core.js + */ +/** + * Logs errors + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal) { + + // cache a reference to setTimeout, so that our reference won't be stubbed out + // when using fake timers and errors will still get logged + // https://github.com/cjohansen/Sinon.JS/issues/381 + var realSetTimeout = setTimeout; + + function makeApi(sinon) { + + function log() {} + + function logError(label, err) { + var msg = label + " threw exception: "; + + function throwLoggedError() { + err.message = msg + err.message; + throw err; + } + + sinon.log(msg + "[" + err.name + "] " + err.message); + + if (err.stack) { + sinon.log(err.stack); + } + + if (logError.useImmediateExceptions) { + throwLoggedError(); + } else { + logError.setTimeout(throwLoggedError, 0); + } + } + + // When set to true, any errors logged will be thrown immediately; + // If set to false, the errors will be thrown in separate execution frame. + logError.useImmediateExceptions = false; + + // wrap realSetTimeout with something we can stub in tests + logError.setTimeout = function (func, timeout) { + realSetTimeout(func, timeout); + }; + + var exports = {}; + exports.log = sinon.log = log; + exports.logError = sinon.logError = logError; + + return exports; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XDomainRequest object + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +// wrapper for global +(function (global) { + + var xdr = { XDomainRequest: global.XDomainRequest }; + xdr.GlobalXDomainRequest = global.XDomainRequest; + xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== "undefined"; + xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false; + + function makeApi(sinon) { + sinon.xdr = xdr; + + function FakeXDomainRequest() { + this.readyState = FakeXDomainRequest.UNSENT; + this.requestBody = null; + this.requestHeaders = {}; + this.status = 0; + this.timeout = null; + + if (typeof FakeXDomainRequest.onCreate === "function") { + FakeXDomainRequest.onCreate(this); + } + } + + function verifyState(x) { + if (x.readyState !== FakeXDomainRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (x.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function verifyRequestSent(x) { + if (x.readyState === FakeXDomainRequest.UNSENT) { + throw new Error("Request not sent"); + } + if (x.readyState === FakeXDomainRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XDomainRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, { + open: function open(method, url) { + this.method = method; + this.url = url; + + this.responseText = null; + this.sendFlag = false; + + this.readyStateChange(FakeXDomainRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + var eventName = ""; + switch (this.readyState) { + case FakeXDomainRequest.UNSENT: + break; + case FakeXDomainRequest.OPENED: + break; + case FakeXDomainRequest.LOADING: + if (this.sendFlag) { + //raise the progress event + eventName = "onprogress"; + } + break; + case FakeXDomainRequest.DONE: + if (this.isTimeout) { + eventName = "ontimeout"; + } else if (this.errorFlag || (this.status < 200 || this.status > 299)) { + eventName = "onerror"; + } else { + eventName = "onload"; + } + break; + } + + // raising event (if defined) + if (eventName) { + if (typeof this[eventName] === "function") { + try { + this[eventName](); + } catch (e) { + sinon.logError("Fake XHR " + eventName + " handler", e); + } + } + } + }, + + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + this.requestBody = data; + } + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + + this.errorFlag = false; + this.sendFlag = true; + this.readyStateChange(FakeXDomainRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + + if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) { + this.readyStateChange(sinon.FakeXDomainRequest.DONE); + this.sendFlag = false; + } + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyResponseBodyType(body); + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + this.readyStateChange(FakeXDomainRequest.LOADING); + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + this.readyStateChange(FakeXDomainRequest.DONE); + }, + + respond: function respond(status, contentType, body) { + // content-type ignored, since XDomainRequest does not carry this + // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease + // test integration across browsers + this.status = typeof status === "number" ? status : 200; + this.setResponseBody(body || ""); + }, + + simulatetimeout: function simulatetimeout() { + this.status = 0; + this.isTimeout = true; + // Access to this should actually throw an error + this.responseText = undefined; + this.readyStateChange(FakeXDomainRequest.DONE); + } + }); + + sinon.extend(FakeXDomainRequest, { + UNSENT: 0, + OPENED: 1, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXDomainRequest = function useFakeXDomainRequest() { + sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) { + if (xdr.supportsXDR) { + global.XDomainRequest = xdr.GlobalXDomainRequest; + } + + delete sinon.FakeXDomainRequest.restore; + + if (keepOnCreate !== true) { + delete sinon.FakeXDomainRequest.onCreate; + } + }; + if (xdr.supportsXDR) { + global.XDomainRequest = sinon.FakeXDomainRequest; + } + return sinon.FakeXDomainRequest; + }; + + sinon.FakeXDomainRequest = FakeXDomainRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +})(typeof global !== "undefined" ? global : self); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XMLHttpRequest object + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal, global) { + + function getWorkingXHR(globalScope) { + var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined"; + if (supportsXHR) { + return globalScope.XMLHttpRequest; + } + + var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined"; + if (supportsActiveX) { + return function () { + return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0"); + }; + } + + return false; + } + + var supportsProgress = typeof ProgressEvent !== "undefined"; + var supportsCustomEvent = typeof CustomEvent !== "undefined"; + var supportsFormData = typeof FormData !== "undefined"; + var supportsArrayBuffer = typeof ArrayBuffer !== "undefined"; + var supportsBlob = typeof Blob === "function"; + var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest }; + sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest; + sinonXhr.GlobalActiveXObject = global.ActiveXObject; + sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== "undefined"; + sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined"; + sinonXhr.workingXHR = getWorkingXHR(global); + sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest()); + + var unsafeHeaders = { + "Accept-Charset": true, + "Accept-Encoding": true, + Connection: true, + "Content-Length": true, + Cookie: true, + Cookie2: true, + "Content-Transfer-Encoding": true, + Date: true, + Expect: true, + Host: true, + "Keep-Alive": true, + Referer: true, + TE: true, + Trailer: true, + "Transfer-Encoding": true, + Upgrade: true, + "User-Agent": true, + Via: true + }; + + // An upload object is created for each + // FakeXMLHttpRequest and allows upload + // events to be simulated using uploadProgress + // and uploadError. + function UploadProgress() { + this.eventListeners = { + progress: [], + load: [], + abort: [], + error: [] + }; + } + + UploadProgress.prototype.addEventListener = function addEventListener(event, listener) { + this.eventListeners[event].push(listener); + }; + + UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) { + var listeners = this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }; + + UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) { + var listeners = this.eventListeners[event.type] || []; + + for (var i = 0, listener; (listener = listeners[i]) != null; i++) { + listener(event); + } + }; + + // Note that for FakeXMLHttpRequest to work pre ES5 + // we lose some of the alignment with the spec. + // To ensure as close a match as possible, + // set responseType before calling open, send or respond; + function FakeXMLHttpRequest() { + this.readyState = FakeXMLHttpRequest.UNSENT; + this.requestHeaders = {}; + this.requestBody = null; + this.status = 0; + this.statusText = ""; + this.upload = new UploadProgress(); + this.responseType = ""; + this.response = ""; + if (sinonXhr.supportsCORS) { + this.withCredentials = false; + } + + var xhr = this; + var events = ["loadstart", "load", "abort", "loadend"]; + + function addEventListener(eventName) { + xhr.addEventListener(eventName, function (event) { + var listener = xhr["on" + eventName]; + + if (listener && typeof listener === "function") { + listener.call(this, event); + } + }); + } + + for (var i = events.length - 1; i >= 0; i--) { + addEventListener(events[i]); + } + + if (typeof FakeXMLHttpRequest.onCreate === "function") { + FakeXMLHttpRequest.onCreate(this); + } + } + + function verifyState(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xhr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function getHeader(headers, header) { + header = header.toLowerCase(); + + for (var h in headers) { + if (h.toLowerCase() === header) { + return h; + } + } + + return null; + } + + // filtering to enable a white-list version of Sinon FakeXhr, + // where whitelisted requests are passed through to real XHR + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + function some(collection, callback) { + for (var index = 0; index < collection.length; index++) { + if (callback(collection[index]) === true) { + return true; + } + } + return false; + } + // largest arity in XHR is 5 - XHR#open + var apply = function (obj, method, args) { + switch (args.length) { + case 0: return obj[method](); + case 1: return obj[method](args[0]); + case 2: return obj[method](args[0], args[1]); + case 3: return obj[method](args[0], args[1], args[2]); + case 4: return obj[method](args[0], args[1], args[2], args[3]); + case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]); + } + }; + + FakeXMLHttpRequest.filters = []; + FakeXMLHttpRequest.addFilter = function addFilter(fn) { + this.filters.push(fn); + }; + var IE6Re = /MSIE 6/; + FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) { + var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap + + each([ + "open", + "setRequestHeader", + "send", + "abort", + "getResponseHeader", + "getAllResponseHeaders", + "addEventListener", + "overrideMimeType", + "removeEventListener" + ], function (method) { + fakeXhr[method] = function () { + return apply(xhr, method, arguments); + }; + }); + + var copyAttrs = function (args) { + each(args, function (attr) { + try { + fakeXhr[attr] = xhr[attr]; + } catch (e) { + if (!IE6Re.test(navigator.userAgent)) { + throw e; + } + } + }); + }; + + var stateChange = function stateChange() { + fakeXhr.readyState = xhr.readyState; + if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { + copyAttrs(["status", "statusText"]); + } + if (xhr.readyState >= FakeXMLHttpRequest.LOADING) { + copyAttrs(["responseText", "response"]); + } + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + copyAttrs(["responseXML"]); + } + if (fakeXhr.onreadystatechange) { + fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr }); + } + }; + + if (xhr.addEventListener) { + for (var event in fakeXhr.eventListeners) { + if (fakeXhr.eventListeners.hasOwnProperty(event)) { + + /*eslint-disable no-loop-func*/ + each(fakeXhr.eventListeners[event], function (handler) { + xhr.addEventListener(event, handler); + }); + /*eslint-enable no-loop-func*/ + } + } + xhr.addEventListener("readystatechange", stateChange); + } else { + xhr.onreadystatechange = stateChange; + } + apply(xhr, "open", xhrArgs); + }; + FakeXMLHttpRequest.useFilters = false; + + function verifyRequestOpened(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR - " + xhr.readyState); + } + } + + function verifyRequestSent(xhr) { + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyHeadersReceived(xhr) { + if (xhr.async && xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) { + throw new Error("No headers received"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XMLHttpRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + function convertToArrayBuffer(body) { + var buffer = new ArrayBuffer(body.length); + var view = new Uint8Array(buffer); + for (var i = 0; i < body.length; i++) { + var charCode = body.charCodeAt(i); + if (charCode >= 256) { + throw new TypeError("arraybuffer or blob responseTypes require binary string, " + + "invalid character " + body[i] + " found."); + } + view[i] = charCode; + } + return buffer; + } + + function isXmlContentType(contentType) { + return !contentType || /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType); + } + + function convertResponseBody(responseType, contentType, body) { + if (responseType === "" || responseType === "text") { + return body; + } else if (supportsArrayBuffer && responseType === "arraybuffer") { + return convertToArrayBuffer(body); + } else if (responseType === "json") { + try { + return JSON.parse(body); + } catch (e) { + // Return parsing failure as null + return null; + } + } else if (supportsBlob && responseType === "blob") { + var blobOptions = {}; + if (contentType) { + blobOptions.type = contentType; + } + return new Blob([convertToArrayBuffer(body)], blobOptions); + } else if (responseType === "document") { + if (isXmlContentType(contentType)) { + return FakeXMLHttpRequest.parseXML(body); + } + return null; + } + throw new Error("Invalid responseType " + responseType); + } + + function clearResponse(xhr) { + if (xhr.responseType === "" || xhr.responseType === "text") { + xhr.response = xhr.responseText = ""; + } else { + xhr.response = xhr.responseText = null; + } + xhr.responseXML = null; + } + + FakeXMLHttpRequest.parseXML = function parseXML(text) { + // Treat empty string as parsing failure + if (text !== "") { + try { + if (typeof DOMParser !== "undefined") { + var parser = new DOMParser(); + return parser.parseFromString(text, "text/xml"); + } + var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(text); + return xmlDoc; + } catch (e) { + // Unable to parse XML - no biggie + } + } + + return null; + }; + + FakeXMLHttpRequest.statusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 300: "Multiple Choice", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 422: "Unprocessable Entity", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" + }; + + function makeApi(sinon) { + sinon.xhr = sinonXhr; + + sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { + async: true, + + open: function open(method, url, async, username, password) { + this.method = method; + this.url = url; + this.async = typeof async === "boolean" ? async : true; + this.username = username; + this.password = password; + clearResponse(this); + this.requestHeaders = {}; + this.sendFlag = false; + + if (FakeXMLHttpRequest.useFilters === true) { + var xhrArgs = arguments; + var defake = some(FakeXMLHttpRequest.filters, function (filter) { + return filter.apply(this, xhrArgs); + }); + if (defake) { + return FakeXMLHttpRequest.defake(this, arguments); + } + } + this.readyStateChange(FakeXMLHttpRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + + var readyStateChangeEvent = new sinon.Event("readystatechange", false, false, this); + + if (typeof this.onreadystatechange === "function") { + try { + this.onreadystatechange(readyStateChangeEvent); + } catch (e) { + sinon.logError("Fake XHR onreadystatechange handler", e); + } + } + + switch (this.readyState) { + case FakeXMLHttpRequest.DONE: + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + this.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + } + this.upload.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("loadend", false, false, this)); + break; + } + + this.dispatchEvent(readyStateChangeEvent); + }, + + setRequestHeader: function setRequestHeader(header, value) { + verifyState(this); + + if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { + throw new Error("Refused to set unsafe header \"" + header + "\""); + } + + if (this.requestHeaders[header]) { + this.requestHeaders[header] += "," + value; + } else { + this.requestHeaders[header] = value; + } + }, + + // Helps testing + setResponseHeaders: function setResponseHeaders(headers) { + verifyRequestOpened(this); + this.responseHeaders = {}; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + this.responseHeaders[header] = headers[header]; + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); + } else { + this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; + } + }, + + // Currently treats ALL data as a DOMString (i.e. no Document) + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + var contentType = getHeader(this.requestHeaders, "Content-Type"); + if (this.requestHeaders[contentType]) { + var value = this.requestHeaders[contentType].split(";"); + this.requestHeaders[contentType] = value[0] + ";charset=utf-8"; + } else if (supportsFormData && !(data instanceof FormData)) { + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + } + + this.requestBody = data; + } + + this.errorFlag = false; + this.sendFlag = this.async; + clearResponse(this); + this.readyStateChange(FakeXMLHttpRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + + this.dispatchEvent(new sinon.Event("loadstart", false, false, this)); + }, + + abort: function abort() { + this.aborted = true; + clearResponse(this); + this.errorFlag = true; + this.requestHeaders = {}; + this.responseHeaders = {}; + + if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) { + this.readyStateChange(FakeXMLHttpRequest.DONE); + this.sendFlag = false; + } + + this.readyState = FakeXMLHttpRequest.UNSENT; + + this.dispatchEvent(new sinon.Event("abort", false, false, this)); + + this.upload.dispatchEvent(new sinon.Event("abort", false, false, this)); + + if (typeof this.onerror === "function") { + this.onerror(); + } + }, + + getResponseHeader: function getResponseHeader(header) { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return null; + } + + if (/^Set-Cookie2?$/i.test(header)) { + return null; + } + + header = getHeader(this.responseHeaders, header); + + return this.responseHeaders[header] || null; + }, + + getAllResponseHeaders: function getAllResponseHeaders() { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return ""; + } + + var headers = ""; + + for (var header in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(header) && + !/^Set-Cookie2?$/i.test(header)) { + headers += header + ": " + this.responseHeaders[header] + "\r\n"; + } + } + + return headers; + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyHeadersReceived(this); + verifyResponseBodyType(body); + var contentType = this.getResponseHeader("Content-Type"); + + var isTextResponse = this.responseType === "" || this.responseType === "text"; + clearResponse(this); + if (this.async) { + var chunkSize = this.chunkSize || 10; + var index = 0; + + do { + this.readyStateChange(FakeXMLHttpRequest.LOADING); + + if (isTextResponse) { + this.responseText = this.response += body.substring(index, index + chunkSize); + } + index += chunkSize; + } while (index < body.length); + } + + this.response = convertResponseBody(this.responseType, contentType, body); + if (isTextResponse) { + this.responseText = this.response; + } + + if (this.responseType === "document") { + this.responseXML = this.response; + } else if (this.responseType === "" && isXmlContentType(contentType)) { + this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); + } + this.readyStateChange(FakeXMLHttpRequest.DONE); + }, + + respond: function respond(status, headers, body) { + this.status = typeof status === "number" ? status : 200; + this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; + this.setResponseHeaders(headers || {}); + this.setResponseBody(body || ""); + }, + + uploadProgress: function uploadProgress(progressEventRaw) { + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + downloadProgress: function downloadProgress(progressEventRaw) { + if (supportsProgress) { + this.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + uploadError: function uploadError(error) { + if (supportsCustomEvent) { + this.upload.dispatchEvent(new sinon.CustomEvent("error", {detail: error})); + } + } + }); + + sinon.extend(FakeXMLHttpRequest, { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXMLHttpRequest = function () { + FakeXMLHttpRequest.restore = function restore(keepOnCreate) { + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = sinonXhr.GlobalActiveXObject; + } + + delete FakeXMLHttpRequest.restore; + + if (keepOnCreate !== true) { + delete FakeXMLHttpRequest.onCreate; + } + }; + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = FakeXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = function ActiveXObject(objId) { + if (objId === "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { + + return new FakeXMLHttpRequest(); + } + + return new sinonXhr.GlobalActiveXObject(objId); + }; + } + + return FakeXMLHttpRequest; + }; + + sinon.FakeXMLHttpRequest = FakeXMLHttpRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof global !== "undefined" ? global : self +)); + +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal, formatio) { + + function makeApi(sinon) { + function valueFormatter(value) { + return "" + value; + } + + function getFormatioFormatter() { + var formatter = formatio.configure({ + quoteStrings: false, + limitChildrenCount: 250 + }); + + function format() { + return formatter.ascii.apply(formatter, arguments); + } + + return format; + } + + function getNodeFormatter() { + try { + var util = require("util"); + } catch (e) { + /* Node, but no util module - would be very old, but better safe than sorry */ + } + + function format(v) { + var isObjectWithNativeToString = typeof v === "object" && v.toString === Object.prototype.toString; + return isObjectWithNativeToString ? util.inspect(v) : v; + } + + return util ? format : valueFormatter; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var formatter; + + if (isNode) { + try { + formatio = require("formatio"); + } + catch (e) {} // eslint-disable-line no-empty + } + + if (formatio) { + formatter = getFormatioFormatter(); + } else if (isNode) { + formatter = getNodeFormatter(); + } else { + formatter = valueFormatter; + } + + sinon.format = formatter; + return sinon.format; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof formatio === "object" && formatio // eslint-disable-line no-undef +)); + +/** + * @depend fake_xdomain_request.js + * @depend fake_xml_http_request.js + * @depend ../format.js + * @depend ../log_error.js + */ +/** + * The Sinon "server" mimics a web server that receives requests from + * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, + * both synchronously and asynchronously. To respond synchronuously, canned + * answers have to be provided upfront. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + var push = [].push; + + function responseArray(handler) { + var response = handler; + + if (Object.prototype.toString.call(handler) !== "[object Array]") { + response = [200, {}, handler]; + } + + if (typeof response[2] !== "string") { + throw new TypeError("Fake server response body should be string, but was " + + typeof response[2]); + } + + return response; + } + + var wloc = typeof window !== "undefined" ? window.location : {}; + var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host); + + function matchOne(response, reqMethod, reqUrl) { + var rmeth = response.method; + var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase(); + var url = response.url; + var matchUrl = !url || url === reqUrl || (typeof url.test === "function" && url.test(reqUrl)); + + return matchMethod && matchUrl; + } + + function match(response, request) { + var requestUrl = request.url; + + if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { + requestUrl = requestUrl.replace(rCurrLoc, ""); + } + + if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { + if (typeof response.response === "function") { + var ru = response.url; + var args = [request].concat(ru && typeof ru.exec === "function" ? ru.exec(requestUrl).slice(1) : []); + return response.response.apply(response, args); + } + + return true; + } + + return false; + } + + function makeApi(sinon) { + sinon.fakeServer = { + create: function (config) { + var server = sinon.create(this); + server.configure(config); + if (!sinon.xhr.supportsCORS) { + this.xhr = sinon.useFakeXDomainRequest(); + } else { + this.xhr = sinon.useFakeXMLHttpRequest(); + } + server.requests = []; + + this.xhr.onCreate = function (xhrObj) { + server.addRequest(xhrObj); + }; + + return server; + }, + configure: function (config) { + var whitelist = { + "autoRespond": true, + "autoRespondAfter": true, + "respondImmediately": true, + "fakeHTTPMethods": true + }; + var setting; + + config = config || {}; + for (setting in config) { + if (whitelist.hasOwnProperty(setting) && config.hasOwnProperty(setting)) { + this[setting] = config[setting]; + } + } + }, + addRequest: function addRequest(xhrObj) { + var server = this; + push.call(this.requests, xhrObj); + + xhrObj.onSend = function () { + server.handleRequest(this); + + if (server.respondImmediately) { + server.respond(); + } else if (server.autoRespond && !server.responding) { + setTimeout(function () { + server.responding = false; + server.respond(); + }, server.autoRespondAfter || 10); + + server.responding = true; + } + }; + }, + + getHTTPMethod: function getHTTPMethod(request) { + if (this.fakeHTTPMethods && /post/i.test(request.method)) { + var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); + return matches ? matches[1] : request.method; + } + + return request.method; + }, + + handleRequest: function handleRequest(xhr) { + if (xhr.async) { + if (!this.queue) { + this.queue = []; + } + + push.call(this.queue, xhr); + } else { + this.processRequest(xhr); + } + }, + + log: function log(response, request) { + var str; + + str = "Request:\n" + sinon.format(request) + "\n\n"; + str += "Response:\n" + sinon.format(response) + "\n\n"; + + sinon.log(str); + }, + + respondWith: function respondWith(method, url, body) { + if (arguments.length === 1 && typeof method !== "function") { + this.response = responseArray(method); + return; + } + + if (!this.responses) { + this.responses = []; + } + + if (arguments.length === 1) { + body = method; + url = method = null; + } + + if (arguments.length === 2) { + body = url; + url = method; + method = null; + } + + push.call(this.responses, { + method: method, + url: url, + response: typeof body === "function" ? body : responseArray(body) + }); + }, + + respond: function respond() { + if (arguments.length > 0) { + this.respondWith.apply(this, arguments); + } + + var queue = this.queue || []; + var requests = queue.splice(0, queue.length); + + for (var i = 0; i < requests.length; i++) { + this.processRequest(requests[i]); + } + }, + + processRequest: function processRequest(request) { + try { + if (request.aborted) { + return; + } + + var response = this.response || [404, {}, ""]; + + if (this.responses) { + for (var l = this.responses.length, i = l - 1; i >= 0; i--) { + if (match.call(this, this.responses[i], request)) { + response = this.responses[i].response; + break; + } + } + } + + if (request.readyState !== 4) { + this.log(response, request); + + request.respond(response[0], response[1], response[2]); + } + } catch (e) { + sinon.logError("Fake server request processing", e); + } + }, + + restore: function restore() { + return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("./fake_xdomain_request"); + require("./fake_xml_http_request"); + require("../format"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * Fake timer API + * setTimeout + * setInterval + * clearTimeout + * clearInterval + * tick + * reset + * Date + * + * Inspired by jsUnitMockTimeOut from JsUnit + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + function makeApi(s, lol) { + /*global lolex */ + var llx = typeof lolex !== "undefined" ? lolex : lol; + + s.useFakeTimers = function () { + var now; + var methods = Array.prototype.slice.call(arguments); + + if (typeof methods[0] === "string") { + now = 0; + } else { + now = methods.shift(); + } + + var clock = llx.install(now || 0, methods); + clock.restore = clock.uninstall; + return clock; + }; + + s.clock = { + create: function (now) { + return llx.createClock(now); + } + }; + + s.timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), + clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined), + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, epxorts, module, lolex) { + var core = require("./core"); + makeApi(core, lolex); + module.exports = core; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module, require("lolex")); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend fake_server.js + * @depend fake_timers.js + */ +/** + * Add-on for sinon.fakeServer that automatically handles a fake timer along with + * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery + * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, + * it polls the object for completion with setInterval. Dispite the direct + * motivation, there is nothing jQuery-specific in this file, so it can be used + * in any environment where the ajax implementation depends on setInterval or + * setTimeout. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + function makeApi(sinon) { + function Server() {} + Server.prototype = sinon.fakeServer; + + sinon.fakeServerWithClock = new Server(); + + sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { + if (xhr.async) { + if (typeof setTimeout.clock === "object") { + this.clock = setTimeout.clock; + } else { + this.clock = sinon.useFakeTimers(); + this.resetClock = true; + } + + if (!this.longestTimeout) { + var clockSetTimeout = this.clock.setTimeout; + var clockSetInterval = this.clock.setInterval; + var server = this; + + this.clock.setTimeout = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetTimeout.apply(this, arguments); + }; + + this.clock.setInterval = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetInterval.apply(this, arguments); + }; + } + } + + return sinon.fakeServer.addRequest.call(this, xhr); + }; + + sinon.fakeServerWithClock.respond = function respond() { + var returnVal = sinon.fakeServer.respond.apply(this, arguments); + + if (this.clock) { + this.clock.tick(this.longestTimeout || 0); + this.longestTimeout = 0; + + if (this.resetClock) { + this.clock.restore(); + this.resetClock = false; + } + } + + return returnVal; + }; + + sinon.fakeServerWithClock.restore = function restore() { + if (this.clock) { + this.clock.restore(); + } + + return sinon.fakeServer.restore.apply(this, arguments); + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + require("./fake_server"); + require("./fake_timers"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + diff --git a/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon.js b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon.js new file mode 100644 index 0000000..f2305ef --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/sinon/pkg/sinon.js @@ -0,0 +1,6386 @@ +/** + * Sinon.JS 1.17.2, 2015/10/21 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +(function (root, factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + define('sinon', [], function () { + return (root.sinon = factory()); + }); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.sinon = factory(); + } +}(this, function () { + 'use strict'; + var samsam, formatio, lolex; + (function () { + function define(mod, deps, fn) { + if (mod == "samsam") { + samsam = deps(); + } else if (typeof deps === "function" && mod.length === 0) { + lolex = deps(); + } else if (typeof fn === "function") { + formatio = fn(samsam); + } + } + define.amd = {}; +((typeof define === "function" && define.amd && function (m) { define("samsam", m); }) || + (typeof module === "object" && + function (m) { module.exports = m(); }) || // Node + function (m) { this.samsam = m(); } // Browser globals +)(function () { + var o = Object.prototype; + var div = typeof document !== "undefined" && document.createElement("div"); + + function isNaN(value) { + // Unlike global isNaN, this avoids type coercion + // typeof check avoids IE host object issues, hat tip to + // lodash + var val = value; // JsLint thinks value !== value is "weird" + return typeof value === "number" && value !== val; + } + + function getClass(value) { + // Returns the internal [[Class]] by calling Object.prototype.toString + // with the provided value as this. Return value is a string, naming the + // internal class, e.g. "Array" + return o.toString.call(value).split(/[ \]]/)[1]; + } + + /** + * @name samsam.isArguments + * @param Object object + * + * Returns ``true`` if ``object`` is an ``arguments`` object, + * ``false`` otherwise. + */ + function isArguments(object) { + if (getClass(object) === 'Arguments') { return true; } + if (typeof object !== "object" || typeof object.length !== "number" || + getClass(object) === "Array") { + return false; + } + if (typeof object.callee == "function") { return true; } + try { + object[object.length] = 6; + delete object[object.length]; + } catch (e) { + return true; + } + return false; + } + + /** + * @name samsam.isElement + * @param Object object + * + * Returns ``true`` if ``object`` is a DOM element node. Unlike + * Underscore.js/lodash, this function will return ``false`` if ``object`` + * is an *element-like* object, i.e. a regular object with a ``nodeType`` + * property that holds the value ``1``. + */ + function isElement(object) { + if (!object || object.nodeType !== 1 || !div) { return false; } + try { + object.appendChild(div); + object.removeChild(div); + } catch (e) { + return false; + } + return true; + } + + /** + * @name samsam.keys + * @param Object object + * + * Return an array of own property names. + */ + function keys(object) { + var ks = [], prop; + for (prop in object) { + if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); } + } + return ks; + } + + /** + * @name samsam.isDate + * @param Object value + * + * Returns true if the object is a ``Date``, or *date-like*. Duck typing + * of date objects work by checking that the object has a ``getTime`` + * function whose return value equals the return value from the object's + * ``valueOf``. + */ + function isDate(value) { + return typeof value.getTime == "function" && + value.getTime() == value.valueOf(); + } + + /** + * @name samsam.isNegZero + * @param Object value + * + * Returns ``true`` if ``value`` is ``-0``. + */ + function isNegZero(value) { + return value === 0 && 1 / value === -Infinity; + } + + /** + * @name samsam.equal + * @param Object obj1 + * @param Object obj2 + * + * Returns ``true`` if two objects are strictly equal. Compared to + * ``===`` there are two exceptions: + * + * - NaN is considered equal to NaN + * - -0 and +0 are not considered equal + */ + function identical(obj1, obj2) { + if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) { + return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2); + } + } + + + /** + * @name samsam.deepEqual + * @param Object obj1 + * @param Object obj2 + * + * Deep equal comparison. Two values are "deep equal" if: + * + * - They are equal, according to samsam.identical + * - They are both date objects representing the same time + * - They are both arrays containing elements that are all deepEqual + * - They are objects with the same set of properties, and each property + * in ``obj1`` is deepEqual to the corresponding property in ``obj2`` + * + * Supports cyclic objects. + */ + function deepEqualCyclic(obj1, obj2) { + + // used for cyclic comparison + // contain already visited objects + var objects1 = [], + objects2 = [], + // contain pathes (position in the object structure) + // of the already visited objects + // indexes same as in objects arrays + paths1 = [], + paths2 = [], + // contains combinations of already compared objects + // in the manner: { "$1['ref']$2['ref']": true } + compared = {}; + + /** + * used to check, if the value of a property is an object + * (cyclic logic is only needed for objects) + * only needed for cyclic logic + */ + function isObject(value) { + + if (typeof value === 'object' && value !== null && + !(value instanceof Boolean) && + !(value instanceof Date) && + !(value instanceof Number) && + !(value instanceof RegExp) && + !(value instanceof String)) { + + return true; + } + + return false; + } + + /** + * returns the index of the given object in the + * given objects array, -1 if not contained + * only needed for cyclic logic + */ + function getIndex(objects, obj) { + + var i; + for (i = 0; i < objects.length; i++) { + if (objects[i] === obj) { + return i; + } + } + + return -1; + } + + // does the recursion for the deep equal check + return (function deepEqual(obj1, obj2, path1, path2) { + var type1 = typeof obj1; + var type2 = typeof obj2; + + // == null also matches undefined + if (obj1 === obj2 || + isNaN(obj1) || isNaN(obj2) || + obj1 == null || obj2 == null || + type1 !== "object" || type2 !== "object") { + + return identical(obj1, obj2); + } + + // Elements are only equal if identical(expected, actual) + if (isElement(obj1) || isElement(obj2)) { return false; } + + var isDate1 = isDate(obj1), isDate2 = isDate(obj2); + if (isDate1 || isDate2) { + if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) { + return false; + } + } + + if (obj1 instanceof RegExp && obj2 instanceof RegExp) { + if (obj1.toString() !== obj2.toString()) { return false; } + } + + var class1 = getClass(obj1); + var class2 = getClass(obj2); + var keys1 = keys(obj1); + var keys2 = keys(obj2); + + if (isArguments(obj1) || isArguments(obj2)) { + if (obj1.length !== obj2.length) { return false; } + } else { + if (type1 !== type2 || class1 !== class2 || + keys1.length !== keys2.length) { + return false; + } + } + + var key, i, l, + // following vars are used for the cyclic logic + value1, value2, + isObject1, isObject2, + index1, index2, + newPath1, newPath2; + + for (i = 0, l = keys1.length; i < l; i++) { + key = keys1[i]; + if (!o.hasOwnProperty.call(obj2, key)) { + return false; + } + + // Start of the cyclic logic + + value1 = obj1[key]; + value2 = obj2[key]; + + isObject1 = isObject(value1); + isObject2 = isObject(value2); + + // determine, if the objects were already visited + // (it's faster to check for isObject first, than to + // get -1 from getIndex for non objects) + index1 = isObject1 ? getIndex(objects1, value1) : -1; + index2 = isObject2 ? getIndex(objects2, value2) : -1; + + // determine the new pathes of the objects + // - for non cyclic objects the current path will be extended + // by current property name + // - for cyclic objects the stored path is taken + newPath1 = index1 !== -1 + ? paths1[index1] + : path1 + '[' + JSON.stringify(key) + ']'; + newPath2 = index2 !== -1 + ? paths2[index2] + : path2 + '[' + JSON.stringify(key) + ']'; + + // stop recursion if current objects are already compared + if (compared[newPath1 + newPath2]) { + return true; + } + + // remember the current objects and their pathes + if (index1 === -1 && isObject1) { + objects1.push(value1); + paths1.push(newPath1); + } + if (index2 === -1 && isObject2) { + objects2.push(value2); + paths2.push(newPath2); + } + + // remember that the current objects are already compared + if (isObject1 && isObject2) { + compared[newPath1 + newPath2] = true; + } + + // End of cyclic logic + + // neither value1 nor value2 is a cycle + // continue with next level + if (!deepEqual(value1, value2, newPath1, newPath2)) { + return false; + } + } + + return true; + + }(obj1, obj2, '$1', '$2')); + } + + var match; + + function arrayContains(array, subset) { + if (subset.length === 0) { return true; } + var i, l, j, k; + for (i = 0, l = array.length; i < l; ++i) { + if (match(array[i], subset[0])) { + for (j = 0, k = subset.length; j < k; ++j) { + if (!match(array[i + j], subset[j])) { return false; } + } + return true; + } + } + return false; + } + + /** + * @name samsam.match + * @param Object object + * @param Object matcher + * + * Compare arbitrary value ``object`` with matcher. + */ + match = function match(object, matcher) { + if (matcher && typeof matcher.test === "function") { + return matcher.test(object); + } + + if (typeof matcher === "function") { + return matcher(object) === true; + } + + if (typeof matcher === "string") { + matcher = matcher.toLowerCase(); + var notNull = typeof object === "string" || !!object; + return notNull && + (String(object)).toLowerCase().indexOf(matcher) >= 0; + } + + if (typeof matcher === "number") { + return matcher === object; + } + + if (typeof matcher === "boolean") { + return matcher === object; + } + + if (typeof(matcher) === "undefined") { + return typeof(object) === "undefined"; + } + + if (matcher === null) { + return object === null; + } + + if (getClass(object) === "Array" && getClass(matcher) === "Array") { + return arrayContains(object, matcher); + } + + if (matcher && typeof matcher === "object") { + if (matcher === object) { + return true; + } + var prop; + for (prop in matcher) { + var value = object[prop]; + if (typeof value === "undefined" && + typeof object.getAttribute === "function") { + value = object.getAttribute(prop); + } + if (matcher[prop] === null || typeof matcher[prop] === 'undefined') { + if (value !== matcher[prop]) { + return false; + } + } else if (typeof value === "undefined" || !match(value, matcher[prop])) { + return false; + } + } + return true; + } + + throw new Error("Matcher was not a string, a number, a " + + "function, a boolean or an object"); + }; + + return { + isArguments: isArguments, + isElement: isElement, + isDate: isDate, + isNegZero: isNegZero, + identical: identical, + deepEqual: deepEqualCyclic, + match: match, + keys: keys + }; +}); +((typeof define === "function" && define.amd && function (m) { + define("formatio", ["samsam"], m); +}) || (typeof module === "object" && function (m) { + module.exports = m(require("samsam")); +}) || function (m) { this.formatio = m(this.samsam); } +)(function (samsam) { + + var formatio = { + excludeConstructors: ["Object", /^.$/], + quoteStrings: true, + limitChildrenCount: 0 + }; + + var hasOwn = Object.prototype.hasOwnProperty; + + var specialObjects = []; + if (typeof global !== "undefined") { + specialObjects.push({ object: global, value: "[object global]" }); + } + if (typeof document !== "undefined") { + specialObjects.push({ + object: document, + value: "[object HTMLDocument]" + }); + } + if (typeof window !== "undefined") { + specialObjects.push({ object: window, value: "[object Window]" }); + } + + function functionName(func) { + if (!func) { return ""; } + if (func.displayName) { return func.displayName; } + if (func.name) { return func.name; } + var matches = func.toString().match(/function\s+([^\(]+)/m); + return (matches && matches[1]) || ""; + } + + function constructorName(f, object) { + var name = functionName(object && object.constructor); + var excludes = f.excludeConstructors || + formatio.excludeConstructors || []; + + var i, l; + for (i = 0, l = excludes.length; i < l; ++i) { + if (typeof excludes[i] === "string" && excludes[i] === name) { + return ""; + } else if (excludes[i].test && excludes[i].test(name)) { + return ""; + } + } + + return name; + } + + function isCircular(object, objects) { + if (typeof object !== "object") { return false; } + var i, l; + for (i = 0, l = objects.length; i < l; ++i) { + if (objects[i] === object) { return true; } + } + return false; + } + + function ascii(f, object, processed, indent) { + if (typeof object === "string") { + var qs = f.quoteStrings; + var quote = typeof qs !== "boolean" || qs; + return processed || quote ? '"' + object + '"' : object; + } + + if (typeof object === "function" && !(object instanceof RegExp)) { + return ascii.func(object); + } + + processed = processed || []; + + if (isCircular(object, processed)) { return "[Circular]"; } + + if (Object.prototype.toString.call(object) === "[object Array]") { + return ascii.array.call(f, object, processed); + } + + if (!object) { return String((1/object) === -Infinity ? "-0" : object); } + if (samsam.isElement(object)) { return ascii.element(object); } + + if (typeof object.toString === "function" && + object.toString !== Object.prototype.toString) { + return object.toString(); + } + + var i, l; + for (i = 0, l = specialObjects.length; i < l; i++) { + if (object === specialObjects[i].object) { + return specialObjects[i].value; + } + } + + return ascii.object.call(f, object, processed, indent); + } + + ascii.func = function (func) { + return "function " + functionName(func) + "() {}"; + }; + + ascii.array = function (array, processed) { + processed = processed || []; + processed.push(array); + var pieces = []; + var i, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, array.length) : array.length; + + for (i = 0; i < l; ++i) { + pieces.push(ascii(this, array[i], processed)); + } + + if(l < array.length) + pieces.push("[... " + (array.length - l) + " more elements]"); + + return "[" + pieces.join(", ") + "]"; + }; + + ascii.object = function (object, processed, indent) { + processed = processed || []; + processed.push(object); + indent = indent || 0; + var pieces = [], properties = samsam.keys(object).sort(); + var length = 3; + var prop, str, obj, i, k, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, properties.length) : properties.length; + + for (i = 0; i < l; ++i) { + prop = properties[i]; + obj = object[prop]; + + if (isCircular(obj, processed)) { + str = "[Circular]"; + } else { + str = ascii(this, obj, processed, indent + 2); + } + + str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str; + length += str.length; + pieces.push(str); + } + + var cons = constructorName(this, object); + var prefix = cons ? "[" + cons + "] " : ""; + var is = ""; + for (i = 0, k = indent; i < k; ++i) { is += " "; } + + if(l < properties.length) + pieces.push("[... " + (properties.length - l) + " more elements]"); + + if (length + indent > 80) { + return prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" + + is + "}"; + } + return prefix + "{ " + pieces.join(", ") + " }"; + }; + + ascii.element = function (element) { + var tagName = element.tagName.toLowerCase(); + var attrs = element.attributes, attr, pairs = [], attrName, i, l, val; + + for (i = 0, l = attrs.length; i < l; ++i) { + attr = attrs.item(i); + attrName = attr.nodeName.toLowerCase().replace("html:", ""); + val = attr.nodeValue; + if (attrName !== "contenteditable" || val !== "inherit") { + if (!!val) { pairs.push(attrName + "=\"" + val + "\""); } + } + } + + var formatted = "<" + tagName + (pairs.length > 0 ? " " : ""); + var content = element.innerHTML; + + if (content.length > 20) { + content = content.substr(0, 20) + "[...]"; + } + + var res = formatted + pairs.join(" ") + ">" + content + + ""; + + return res.replace(/ contentEditable="inherit"/, ""); + }; + + function Formatio(options) { + for (var opt in options) { + this[opt] = options[opt]; + } + } + + Formatio.prototype = { + functionName: functionName, + + configure: function (options) { + return new Formatio(options); + }, + + constructorName: function (object) { + return constructorName(this, object); + }, + + ascii: function (object, processed, indent) { + return ascii(this, object, processed, indent); + } + }; + + return Formatio.prototype; +}); +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.lolex=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { + throw new Error("tick only understands numbers and 'h:m:s'"); + } + + while (i--) { + parsed = parseInt(strings[i], 10); + + if (parsed >= 60) { + throw new Error("Invalid time " + str); + } + + ms += parsed * Math.pow(60, (l - i - 1)); + } + + return ms * 1000; + } + + /** + * Used to grok the `now` parameter to createClock. + */ + function getEpoch(epoch) { + if (!epoch) { return 0; } + if (typeof epoch.getTime === "function") { return epoch.getTime(); } + if (typeof epoch === "number") { return epoch; } + throw new TypeError("now should be milliseconds since UNIX epoch"); + } + + function inRange(from, to, timer) { + return timer && timer.callAt >= from && timer.callAt <= to; + } + + function mirrorDateProperties(target, source) { + var prop; + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // set special now implementation + if (source.now) { + target.now = function now() { + return target.clock.now; + }; + } else { + delete target.now; + } + + // set special toSource implementation + if (source.toSource) { + target.toSource = function toSource() { + return source.toSource(); + }; + } else { + delete target.toSource; + } + + // set special toString implementation + target.toString = function toString() { + return source.toString(); + }; + + target.prototype = source.prototype; + target.parse = source.parse; + target.UTC = source.UTC; + target.prototype.toUTCString = source.prototype.toUTCString; + + return target; + } + + function createDate() { + function ClockDate(year, month, date, hour, minute, second, ms) { + // Defensive and verbose to avoid potential harm in passing + // explicit undefined when user does not pass argument + switch (arguments.length) { + case 0: + return new NativeDate(ClockDate.clock.now); + case 1: + return new NativeDate(year); + case 2: + return new NativeDate(year, month); + case 3: + return new NativeDate(year, month, date); + case 4: + return new NativeDate(year, month, date, hour); + case 5: + return new NativeDate(year, month, date, hour, minute); + case 6: + return new NativeDate(year, month, date, hour, minute, second); + default: + return new NativeDate(year, month, date, hour, minute, second, ms); + } + } + + return mirrorDateProperties(ClockDate, NativeDate); + } + + function addTimer(clock, timer) { + if (timer.func === undefined) { + throw new Error("Callback must be provided to timer calls"); + } + + if (!clock.timers) { + clock.timers = {}; + } + + timer.id = uniqueTimerId++; + timer.createdAt = clock.now; + timer.callAt = clock.now + (timer.delay || (clock.duringTick ? 1 : 0)); + + clock.timers[timer.id] = timer; + + if (addTimerReturnsObject) { + return { + id: timer.id, + ref: NOOP, + unref: NOOP + }; + } + + return timer.id; + } + + + function compareTimers(a, b) { + // Sort first by absolute timing + if (a.callAt < b.callAt) { + return -1; + } + if (a.callAt > b.callAt) { + return 1; + } + + // Sort next by immediate, immediate timers take precedence + if (a.immediate && !b.immediate) { + return -1; + } + if (!a.immediate && b.immediate) { + return 1; + } + + // Sort next by creation time, earlier-created timers take precedence + if (a.createdAt < b.createdAt) { + return -1; + } + if (a.createdAt > b.createdAt) { + return 1; + } + + // Sort next by id, lower-id timers take precedence + if (a.id < b.id) { + return -1; + } + if (a.id > b.id) { + return 1; + } + + // As timer ids are unique, no fallback `0` is necessary + } + + function firstTimerInRange(clock, from, to) { + var timers = clock.timers, + timer = null, + id, + isInRange; + + for (id in timers) { + if (timers.hasOwnProperty(id)) { + isInRange = inRange(from, to, timers[id]); + + if (isInRange && (!timer || compareTimers(timer, timers[id]) === 1)) { + timer = timers[id]; + } + } + } + + return timer; + } + + function callTimer(clock, timer) { + var exception; + + if (typeof timer.interval === "number") { + clock.timers[timer.id].callAt += timer.interval; + } else { + delete clock.timers[timer.id]; + } + + try { + if (typeof timer.func === "function") { + timer.func.apply(null, timer.args); + } else { + eval(timer.func); + } + } catch (e) { + exception = e; + } + + if (!clock.timers[timer.id]) { + if (exception) { + throw exception; + } + return; + } + + if (exception) { + throw exception; + } + } + + function timerType(timer) { + if (timer.immediate) { + return "Immediate"; + } else if (typeof timer.interval !== "undefined") { + return "Interval"; + } else { + return "Timeout"; + } + } + + function clearTimer(clock, timerId, ttype) { + if (!timerId) { + // null appears to be allowed in most browsers, and appears to be + // relied upon by some libraries, like Bootstrap carousel + return; + } + + if (!clock.timers) { + clock.timers = []; + } + + // in Node, timerId is an object with .ref()/.unref(), and + // its .id field is the actual timer id. + if (typeof timerId === "object") { + timerId = timerId.id; + } + + if (clock.timers.hasOwnProperty(timerId)) { + // check that the ID matches a timer of the correct type + var timer = clock.timers[timerId]; + if (timerType(timer) === ttype) { + delete clock.timers[timerId]; + } else { + throw new Error("Cannot clear timer: timer created with set" + ttype + "() but cleared with clear" + timerType(timer) + "()"); + } + } + } + + function uninstall(clock, target) { + var method, + i, + l; + + for (i = 0, l = clock.methods.length; i < l; i++) { + method = clock.methods[i]; + + if (target[method].hadOwnProperty) { + target[method] = clock["_" + method]; + } else { + try { + delete target[method]; + } catch (ignore) {} + } + } + + // Prevent multiple executions which will completely remove these props + clock.methods = []; + } + + function hijackMethod(target, method, clock) { + var prop; + + clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method); + clock["_" + method] = target[method]; + + if (method === "Date") { + var date = mirrorDateProperties(clock[method], target[method]); + target[method] = date; + } else { + target[method] = function () { + return clock[method].apply(clock, arguments); + }; + + for (prop in clock[method]) { + if (clock[method].hasOwnProperty(prop)) { + target[method][prop] = clock[method][prop]; + } + } + } + + target[method].clock = clock; + } + + var timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: global.setImmediate, + clearImmediate: global.clearImmediate, + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + + var keys = Object.keys || function (obj) { + var ks = [], + key; + + for (key in obj) { + if (obj.hasOwnProperty(key)) { + ks.push(key); + } + } + + return ks; + }; + + exports.timers = timers; + + function createClock(now) { + var clock = { + now: getEpoch(now), + timeouts: {}, + Date: createDate() + }; + + clock.Date.clock = clock; + + clock.setTimeout = function setTimeout(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout + }); + }; + + clock.clearTimeout = function clearTimeout(timerId) { + return clearTimer(clock, timerId, "Timeout"); + }; + + clock.setInterval = function setInterval(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout, + interval: timeout + }); + }; + + clock.clearInterval = function clearInterval(timerId) { + return clearTimer(clock, timerId, "Interval"); + }; + + clock.setImmediate = function setImmediate(func) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 1), + immediate: true + }); + }; + + clock.clearImmediate = function clearImmediate(timerId) { + return clearTimer(clock, timerId, "Immediate"); + }; + + clock.tick = function tick(ms) { + ms = typeof ms === "number" ? ms : parseTime(ms); + var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now; + var timer = firstTimerInRange(clock, tickFrom, tickTo); + var oldNow; + + clock.duringTick = true; + + var firstException; + while (timer && tickFrom <= tickTo) { + if (clock.timers[timer.id]) { + tickFrom = clock.now = timer.callAt; + try { + oldNow = clock.now; + callTimer(clock, timer); + // compensate for any setSystemTime() call during timer callback + if (oldNow !== clock.now) { + tickFrom += clock.now - oldNow; + tickTo += clock.now - oldNow; + previous += clock.now - oldNow; + } + } catch (e) { + firstException = firstException || e; + } + } + + timer = firstTimerInRange(clock, previous, tickTo); + previous = tickFrom; + } + + clock.duringTick = false; + clock.now = tickTo; + + if (firstException) { + throw firstException; + } + + return clock.now; + }; + + clock.reset = function reset() { + clock.timers = {}; + }; + + clock.setSystemTime = function setSystemTime(now) { + // determine time difference + var newNow = getEpoch(now); + var difference = newNow - clock.now; + + // update 'system clock' + clock.now = newNow; + + // update timers and intervals to keep them stable + for (var id in clock.timers) { + if (clock.timers.hasOwnProperty(id)) { + var timer = clock.timers[id]; + timer.createdAt += difference; + timer.callAt += difference; + } + } + }; + + return clock; + } + exports.createClock = createClock; + + exports.install = function install(target, now, toFake) { + var i, + l; + + if (typeof target === "number") { + toFake = now; + now = target; + target = null; + } + + if (!target) { + target = global; + } + + var clock = createClock(now); + + clock.uninstall = function () { + uninstall(clock, target); + }; + + clock.methods = toFake || []; + + if (clock.methods.length === 0) { + clock.methods = keys(timers); + } + + for (i = 0, l = clock.methods.length; i < l; i++) { + hijackMethod(target, clock.methods[i], clock); + } + + return clock; + }; + +}(global || this)); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}]},{},[1])(1) +}); + })(); + var define; +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +var sinon = (function () { +"use strict"; + // eslint-disable-line no-unused-vars + + var sinonModule; + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + sinonModule = module.exports = require("./sinon/util/core"); + require("./sinon/extend"); + require("./sinon/walk"); + require("./sinon/typeOf"); + require("./sinon/times_in_words"); + require("./sinon/spy"); + require("./sinon/call"); + require("./sinon/behavior"); + require("./sinon/stub"); + require("./sinon/mock"); + require("./sinon/collection"); + require("./sinon/assert"); + require("./sinon/sandbox"); + require("./sinon/test"); + require("./sinon/test_case"); + require("./sinon/match"); + require("./sinon/format"); + require("./sinon/log_error"); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + sinonModule = module.exports; + } else { + sinonModule = {}; + } + + return sinonModule; +}()); + +/** + * @depend ../../sinon.js + */ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + var div = typeof document !== "undefined" && document.createElement("div"); + var hasOwn = Object.prototype.hasOwnProperty; + + function isDOMNode(obj) { + var success = false; + + try { + obj.appendChild(div); + success = div.parentNode === obj; + } catch (e) { + return false; + } finally { + try { + obj.removeChild(div); + } catch (e) { + // Remove failed, not much we can do about that + } + } + + return success; + } + + function isElement(obj) { + return div && obj && obj.nodeType === 1 && isDOMNode(obj); + } + + function isFunction(obj) { + return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply); + } + + function isReallyNaN(val) { + return typeof val === "number" && isNaN(val); + } + + function mirrorProperties(target, source) { + for (var prop in source) { + if (!hasOwn.call(target, prop)) { + target[prop] = source[prop]; + } + } + } + + function isRestorable(obj) { + return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon; + } + + // Cheap way to detect if we have ES5 support. + var hasES5Support = "keys" in Object; + + function makeApi(sinon) { + sinon.wrapMethod = function wrapMethod(object, property, method) { + if (!object) { + throw new TypeError("Should wrap property of object"); + } + + if (typeof method !== "function" && typeof method !== "object") { + throw new TypeError("Method wrapper should be a function or a property descriptor"); + } + + function checkWrappedMethod(wrappedMethod) { + var error; + + if (!isFunction(wrappedMethod)) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } else if (wrappedMethod.calledBefore) { + var verb = wrappedMethod.returns ? "stubbed" : "spied on"; + error = new TypeError("Attempted to wrap " + property + " which is already " + verb); + } + + if (error) { + if (wrappedMethod && wrappedMethod.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethod.stackTrace; + } + throw error; + } + } + + var error, wrappedMethod, i; + + // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem + // when using hasOwn.call on objects from other frames. + var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property); + + if (hasES5Support) { + var methodDesc = (typeof method === "function") ? {value: method} : method; + var wrappedMethodDesc = sinon.getPropertyDescriptor(object, property); + + if (!wrappedMethodDesc) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } + if (error) { + if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace; + } + throw error; + } + + var types = sinon.objectKeys(methodDesc); + for (i = 0; i < types.length; i++) { + wrappedMethod = wrappedMethodDesc[types[i]]; + checkWrappedMethod(wrappedMethod); + } + + mirrorProperties(methodDesc, wrappedMethodDesc); + for (i = 0; i < types.length; i++) { + mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]); + } + Object.defineProperty(object, property, methodDesc); + } else { + wrappedMethod = object[property]; + checkWrappedMethod(wrappedMethod); + object[property] = method; + method.displayName = property; + } + + method.displayName = property; + + // Set up a stack trace which can be used later to find what line of + // code the original method was created on. + method.stackTrace = (new Error("Stack Trace for original")).stack; + + method.restore = function () { + // For prototype properties try to reset by delete first. + // If this fails (ex: localStorage on mobile safari) then force a reset + // via direct assignment. + if (!owned) { + // In some cases `delete` may throw an error + try { + delete object[property]; + } catch (e) {} // eslint-disable-line no-empty + // For native code functions `delete` fails without throwing an error + // on Chrome < 43, PhantomJS, etc. + } else if (hasES5Support) { + Object.defineProperty(object, property, wrappedMethodDesc); + } + + // Use strict equality comparison to check failures then force a reset + // via direct assignment. + if (object[property] === method) { + object[property] = wrappedMethod; + } + }; + + method.restore.sinon = true; + + if (!hasES5Support) { + mirrorProperties(method, wrappedMethod); + } + + return method; + }; + + sinon.create = function create(proto) { + var F = function () {}; + F.prototype = proto; + return new F(); + }; + + sinon.deepEqual = function deepEqual(a, b) { + if (sinon.match && sinon.match.isMatcher(a)) { + return a.test(b); + } + + if (typeof a !== "object" || typeof b !== "object") { + return isReallyNaN(a) && isReallyNaN(b) || a === b; + } + + if (isElement(a) || isElement(b)) { + return a === b; + } + + if (a === b) { + return true; + } + + if ((a === null && b !== null) || (a !== null && b === null)) { + return false; + } + + if (a instanceof RegExp && b instanceof RegExp) { + return (a.source === b.source) && (a.global === b.global) && + (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline); + } + + var aString = Object.prototype.toString.call(a); + if (aString !== Object.prototype.toString.call(b)) { + return false; + } + + if (aString === "[object Date]") { + return a.valueOf() === b.valueOf(); + } + + var prop; + var aLength = 0; + var bLength = 0; + + if (aString === "[object Array]" && a.length !== b.length) { + return false; + } + + for (prop in a) { + if (a.hasOwnProperty(prop)) { + aLength += 1; + + if (!(prop in b)) { + return false; + } + + if (!deepEqual(a[prop], b[prop])) { + return false; + } + } + } + + for (prop in b) { + if (b.hasOwnProperty(prop)) { + bLength += 1; + } + } + + return aLength === bLength; + }; + + sinon.functionName = function functionName(func) { + var name = func.displayName || func.name; + + // Use function decomposition as a last resort to get function + // name. Does not rely on function decomposition to work - if it + // doesn't debugging will be slightly less informative + // (i.e. toString will say 'spy' rather than 'myFunc'). + if (!name) { + var matches = func.toString().match(/function ([^\s\(]+)/); + name = matches && matches[1]; + } + + return name; + }; + + sinon.functionToString = function toString() { + if (this.getCall && this.callCount) { + var thisValue, + prop; + var i = this.callCount; + + while (i--) { + thisValue = this.getCall(i).thisValue; + + for (prop in thisValue) { + if (thisValue[prop] === this) { + return prop; + } + } + } + } + + return this.displayName || "sinon fake"; + }; + + sinon.objectKeys = function objectKeys(obj) { + if (obj !== Object(obj)) { + throw new TypeError("sinon.objectKeys called on a non-object"); + } + + var keys = []; + var key; + for (key in obj) { + if (hasOwn.call(obj, key)) { + keys.push(key); + } + } + + return keys; + }; + + sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) { + var proto = object; + var descriptor; + + while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) { + proto = Object.getPrototypeOf(proto); + } + return descriptor; + }; + + sinon.getConfig = function (custom) { + var config = {}; + custom = custom || {}; + var defaults = sinon.defaultConfig; + + for (var prop in defaults) { + if (defaults.hasOwnProperty(prop)) { + config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; + } + } + + return config; + }; + + sinon.defaultConfig = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.timesInWords = function timesInWords(count) { + return count === 1 && "once" || + count === 2 && "twice" || + count === 3 && "thrice" || + (count || 0) + " times"; + }; + + sinon.calledInOrder = function (spies) { + for (var i = 1, l = spies.length; i < l; i++) { + if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) { + return false; + } + } + + return true; + }; + + sinon.orderByFirstCall = function (spies) { + return spies.sort(function (a, b) { + // uuid, won't ever be equal + var aCall = a.getCall(0); + var bCall = b.getCall(0); + var aId = aCall && aCall.callId || -1; + var bId = bCall && bCall.callId || -1; + + return aId < bId ? -1 : 1; + }); + }; + + sinon.createStubInstance = function (constructor) { + if (typeof constructor !== "function") { + throw new TypeError("The constructor should be a function."); + } + return sinon.stub(sinon.create(constructor.prototype)); + }; + + sinon.restore = function (object) { + if (object !== null && typeof object === "object") { + for (var prop in object) { + if (isRestorable(object[prop])) { + object[prop].restore(); + } + } + } else if (isRestorable(object)) { + object.restore(); + } + }; + + return sinon; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports) { + makeApi(exports); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + + // Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + var hasDontEnumBug = (function () { + var obj = { + constructor: function () { + return "0"; + }, + toString: function () { + return "1"; + }, + valueOf: function () { + return "2"; + }, + toLocaleString: function () { + return "3"; + }, + prototype: function () { + return "4"; + }, + isPrototypeOf: function () { + return "5"; + }, + propertyIsEnumerable: function () { + return "6"; + }, + hasOwnProperty: function () { + return "7"; + }, + length: function () { + return "8"; + }, + unique: function () { + return "9"; + } + }; + + var result = []; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + result.push(obj[prop]()); + } + } + return result.join("") !== "0123456789"; + })(); + + /* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will + * override properties in previous sources. + * + * target - The Object to extend + * sources - Objects to copy properties from. + * + * Returns the extended target + */ + function extend(target /*, sources */) { + var sources = Array.prototype.slice.call(arguments, 1); + var source, i, prop; + + for (i = 0; i < sources.length; i++) { + source = sources[i]; + + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // Make sure we copy (own) toString method even when in JScript with DontEnum bug + // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) { + target.toString = source.toString; + } + } + + return target; + } + + sinon.extend = extend; + return sinon.extend; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + + function timesInWords(count) { + switch (count) { + case 1: + return "once"; + case 2: + return "twice"; + case 3: + return "thrice"; + default: + return (count || 0) + " times"; + } + } + + sinon.timesInWords = timesInWords; + return sinon.timesInWords; + } + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + module.exports = makeApi(core); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + function typeOf(value) { + if (value === null) { + return "null"; + } else if (value === undefined) { + return "undefined"; + } + var string = Object.prototype.toString.call(value); + return string.substring(8, string.length - 1).toLowerCase(); + } + + sinon.typeOf = typeOf; + return sinon.typeOf; + } + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + module.exports = makeApi(core); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend typeOf.js + */ +/*jslint eqeqeq: false, onevar: false, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Match functions + * + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2012 Maximilian Antoni + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + function assertType(value, type, name) { + var actual = sinon.typeOf(value); + if (actual !== type) { + throw new TypeError("Expected type of " + name + " to be " + + type + ", but was " + actual); + } + } + + var matcher = { + toString: function () { + return this.message; + } + }; + + function isMatcher(object) { + return matcher.isPrototypeOf(object); + } + + function matchObject(expectation, actual) { + if (actual === null || actual === undefined) { + return false; + } + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + var exp = expectation[key]; + var act = actual[key]; + if (isMatcher(exp)) { + if (!exp.test(act)) { + return false; + } + } else if (sinon.typeOf(exp) === "object") { + if (!matchObject(exp, act)) { + return false; + } + } else if (!sinon.deepEqual(exp, act)) { + return false; + } + } + } + return true; + } + + function match(expectation, message) { + var m = sinon.create(matcher); + var type = sinon.typeOf(expectation); + switch (type) { + case "object": + if (typeof expectation.test === "function") { + m.test = function (actual) { + return expectation.test(actual) === true; + }; + m.message = "match(" + sinon.functionName(expectation.test) + ")"; + return m; + } + var str = []; + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + str.push(key + ": " + expectation[key]); + } + } + m.test = function (actual) { + return matchObject(expectation, actual); + }; + m.message = "match(" + str.join(", ") + ")"; + break; + case "number": + m.test = function (actual) { + // we need type coercion here + return expectation == actual; // eslint-disable-line eqeqeq + }; + break; + case "string": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return actual.indexOf(expectation) !== -1; + }; + m.message = "match(\"" + expectation + "\")"; + break; + case "regexp": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return expectation.test(actual); + }; + break; + case "function": + m.test = expectation; + if (message) { + m.message = message; + } else { + m.message = "match(" + sinon.functionName(expectation) + ")"; + } + break; + default: + m.test = function (actual) { + return sinon.deepEqual(expectation, actual); + }; + } + if (!m.message) { + m.message = "match(" + expectation + ")"; + } + return m; + } + + matcher.or = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var or = sinon.create(matcher); + or.test = function (actual) { + return m1.test(actual) || m2.test(actual); + }; + or.message = m1.message + ".or(" + m2.message + ")"; + return or; + }; + + matcher.and = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var and = sinon.create(matcher); + and.test = function (actual) { + return m1.test(actual) && m2.test(actual); + }; + and.message = m1.message + ".and(" + m2.message + ")"; + return and; + }; + + match.isMatcher = isMatcher; + + match.any = match(function () { + return true; + }, "any"); + + match.defined = match(function (actual) { + return actual !== null && actual !== undefined; + }, "defined"); + + match.truthy = match(function (actual) { + return !!actual; + }, "truthy"); + + match.falsy = match(function (actual) { + return !actual; + }, "falsy"); + + match.same = function (expectation) { + return match(function (actual) { + return expectation === actual; + }, "same(" + expectation + ")"); + }; + + match.typeOf = function (type) { + assertType(type, "string", "type"); + return match(function (actual) { + return sinon.typeOf(actual) === type; + }, "typeOf(\"" + type + "\")"); + }; + + match.instanceOf = function (type) { + assertType(type, "function", "type"); + return match(function (actual) { + return actual instanceof type; + }, "instanceOf(" + sinon.functionName(type) + ")"); + }; + + function createPropertyMatcher(propertyTest, messagePrefix) { + return function (property, value) { + assertType(property, "string", "property"); + var onlyProperty = arguments.length === 1; + var message = messagePrefix + "(\"" + property + "\""; + if (!onlyProperty) { + message += ", " + value; + } + message += ")"; + return match(function (actual) { + if (actual === undefined || actual === null || + !propertyTest(actual, property)) { + return false; + } + return onlyProperty || sinon.deepEqual(value, actual[property]); + }, message); + }; + } + + match.has = createPropertyMatcher(function (actual, property) { + if (typeof actual === "object") { + return property in actual; + } + return actual[property] !== undefined; + }, "has"); + + match.hasOwn = createPropertyMatcher(function (actual, property) { + return actual.hasOwnProperty(property); + }, "hasOwn"); + + match.bool = match.typeOf("boolean"); + match.number = match.typeOf("number"); + match.string = match.typeOf("string"); + match.object = match.typeOf("object"); + match.func = match.typeOf("function"); + match.array = match.typeOf("array"); + match.regexp = match.typeOf("regexp"); + match.date = match.typeOf("date"); + + sinon.match = match; + return match; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./typeOf"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal, formatio) { + + function makeApi(sinon) { + function valueFormatter(value) { + return "" + value; + } + + function getFormatioFormatter() { + var formatter = formatio.configure({ + quoteStrings: false, + limitChildrenCount: 250 + }); + + function format() { + return formatter.ascii.apply(formatter, arguments); + } + + return format; + } + + function getNodeFormatter() { + try { + var util = require("util"); + } catch (e) { + /* Node, but no util module - would be very old, but better safe than sorry */ + } + + function format(v) { + var isObjectWithNativeToString = typeof v === "object" && v.toString === Object.prototype.toString; + return isObjectWithNativeToString ? util.inspect(v) : v; + } + + return util ? format : valueFormatter; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var formatter; + + if (isNode) { + try { + formatio = require("formatio"); + } + catch (e) {} // eslint-disable-line no-empty + } + + if (formatio) { + formatter = getFormatioFormatter(); + } else if (isNode) { + formatter = getNodeFormatter(); + } else { + formatter = valueFormatter; + } + + sinon.format = formatter; + return sinon.format; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof formatio === "object" && formatio // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend match.js + * @depend format.js + */ +/** + * Spy calls + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + * Copyright (c) 2013 Maximilian Antoni + */ +(function (sinonGlobal) { + + var slice = Array.prototype.slice; + + function makeApi(sinon) { + function throwYieldError(proxy, text, args) { + var msg = sinon.functionName(proxy) + text; + if (args.length) { + msg += " Received [" + slice.call(args).join(", ") + "]"; + } + throw new Error(msg); + } + + var callProto = { + calledOn: function calledOn(thisValue) { + if (sinon.match && sinon.match.isMatcher(thisValue)) { + return thisValue.test(this.thisValue); + } + return this.thisValue === thisValue; + }, + + calledWith: function calledWith() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + if (!sinon.deepEqual(arguments[i], this.args[i])) { + return false; + } + } + + return true; + }, + + calledWithMatch: function calledWithMatch() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + var actual = this.args[i]; + var expectation = arguments[i]; + if (!sinon.match || !sinon.match(expectation).test(actual)) { + return false; + } + } + return true; + }, + + calledWithExactly: function calledWithExactly() { + return arguments.length === this.args.length && + this.calledWith.apply(this, arguments); + }, + + notCalledWith: function notCalledWith() { + return !this.calledWith.apply(this, arguments); + }, + + notCalledWithMatch: function notCalledWithMatch() { + return !this.calledWithMatch.apply(this, arguments); + }, + + returned: function returned(value) { + return sinon.deepEqual(value, this.returnValue); + }, + + threw: function threw(error) { + if (typeof error === "undefined" || !this.exception) { + return !!this.exception; + } + + return this.exception === error || this.exception.name === error; + }, + + calledWithNew: function calledWithNew() { + return this.proxy.prototype && this.thisValue instanceof this.proxy; + }, + + calledBefore: function (other) { + return this.callId < other.callId; + }, + + calledAfter: function (other) { + return this.callId > other.callId; + }, + + callArg: function (pos) { + this.args[pos](); + }, + + callArgOn: function (pos, thisValue) { + this.args[pos].apply(thisValue); + }, + + callArgWith: function (pos) { + this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1))); + }, + + callArgOnWith: function (pos, thisValue) { + var args = slice.call(arguments, 2); + this.args[pos].apply(thisValue, args); + }, + + "yield": function () { + this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0))); + }, + + yieldOn: function (thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (typeof args[i] === "function") { + args[i].apply(thisValue, slice.call(arguments, 1)); + return; + } + } + throwYieldError(this.proxy, " cannot yield since no callback was passed.", args); + }, + + yieldTo: function (prop) { + this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1))); + }, + + yieldToOn: function (prop, thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (args[i] && typeof args[i][prop] === "function") { + args[i][prop].apply(thisValue, slice.call(arguments, 2)); + return; + } + } + throwYieldError(this.proxy, " cannot yield to '" + prop + + "' since no callback was passed.", args); + }, + + getStackFrames: function () { + // Omit the error message and the two top stack frames in sinon itself: + return this.stack && this.stack.split("\n").slice(3); + }, + + toString: function () { + var callStr = this.proxy.toString() + "("; + var args = []; + + for (var i = 0, l = this.args.length; i < l; ++i) { + args.push(sinon.format(this.args[i])); + } + + callStr = callStr + args.join(", ") + ")"; + + if (typeof this.returnValue !== "undefined") { + callStr += " => " + sinon.format(this.returnValue); + } + + if (this.exception) { + callStr += " !" + this.exception.name; + + if (this.exception.message) { + callStr += "(" + this.exception.message + ")"; + } + } + if (this.stack) { + callStr += this.getStackFrames()[0].replace(/^\s*(?:at\s+|@)?/, " at "); + + } + + return callStr; + } + }; + + callProto.invokeCallback = callProto.yield; + + function createSpyCall(spy, thisValue, args, returnValue, exception, id, stack) { + if (typeof id !== "number") { + throw new TypeError("Call id is not a number"); + } + var proxyCall = sinon.create(callProto); + proxyCall.proxy = spy; + proxyCall.thisValue = thisValue; + proxyCall.args = args; + proxyCall.returnValue = returnValue; + proxyCall.exception = exception; + proxyCall.callId = id; + proxyCall.stack = stack; + + return proxyCall; + } + createSpyCall.toString = callProto.toString; // used by mocks + + sinon.spyCall = createSpyCall; + return createSpyCall; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./match"); + require("./format"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend extend.js + * @depend call.js + * @depend format.js + */ +/** + * Spy functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + var push = Array.prototype.push; + var slice = Array.prototype.slice; + var callId = 0; + + function spy(object, property, types) { + if (!property && typeof object === "function") { + return spy.create(object); + } + + if (!object && !property) { + return spy.create(function () { }); + } + + if (types) { + var methodDesc = sinon.getPropertyDescriptor(object, property); + for (var i = 0; i < types.length; i++) { + methodDesc[types[i]] = spy.create(methodDesc[types[i]]); + } + return sinon.wrapMethod(object, property, methodDesc); + } + + return sinon.wrapMethod(object, property, spy.create(object[property])); + } + + function matchingFake(fakes, args, strict) { + if (!fakes) { + return undefined; + } + + for (var i = 0, l = fakes.length; i < l; i++) { + if (fakes[i].matches(args, strict)) { + return fakes[i]; + } + } + } + + function incrementCallCount() { + this.called = true; + this.callCount += 1; + this.notCalled = false; + this.calledOnce = this.callCount === 1; + this.calledTwice = this.callCount === 2; + this.calledThrice = this.callCount === 3; + } + + function createCallProperties() { + this.firstCall = this.getCall(0); + this.secondCall = this.getCall(1); + this.thirdCall = this.getCall(2); + this.lastCall = this.getCall(this.callCount - 1); + } + + var vars = "a,b,c,d,e,f,g,h,i,j,k,l"; + function createProxy(func, proxyLength) { + // Retain the function length: + var p; + if (proxyLength) { + eval("p = (function proxy(" + vars.substring(0, proxyLength * 2 - 1) + // eslint-disable-line no-eval + ") { return p.invoke(func, this, slice.call(arguments)); });"); + } else { + p = function proxy() { + return p.invoke(func, this, slice.call(arguments)); + }; + } + p.isSinonProxy = true; + return p; + } + + var uuid = 0; + + // Public API + var spyApi = { + reset: function () { + if (this.invoking) { + var err = new Error("Cannot reset Sinon function while invoking it. " + + "Move the call to .reset outside of the callback."); + err.name = "InvalidResetException"; + throw err; + } + + this.called = false; + this.notCalled = true; + this.calledOnce = false; + this.calledTwice = false; + this.calledThrice = false; + this.callCount = 0; + this.firstCall = null; + this.secondCall = null; + this.thirdCall = null; + this.lastCall = null; + this.args = []; + this.returnValues = []; + this.thisValues = []; + this.exceptions = []; + this.callIds = []; + this.stacks = []; + if (this.fakes) { + for (var i = 0; i < this.fakes.length; i++) { + this.fakes[i].reset(); + } + } + + return this; + }, + + create: function create(func, spyLength) { + var name; + + if (typeof func !== "function") { + func = function () { }; + } else { + name = sinon.functionName(func); + } + + if (!spyLength) { + spyLength = func.length; + } + + var proxy = createProxy(func, spyLength); + + sinon.extend(proxy, spy); + delete proxy.create; + sinon.extend(proxy, func); + + proxy.reset(); + proxy.prototype = func.prototype; + proxy.displayName = name || "spy"; + proxy.toString = sinon.functionToString; + proxy.instantiateFake = sinon.spy.create; + proxy.id = "spy#" + uuid++; + + return proxy; + }, + + invoke: function invoke(func, thisValue, args) { + var matching = matchingFake(this.fakes, args); + var exception, returnValue; + + incrementCallCount.call(this); + push.call(this.thisValues, thisValue); + push.call(this.args, args); + push.call(this.callIds, callId++); + + // Make call properties available from within the spied function: + createCallProperties.call(this); + + try { + this.invoking = true; + + if (matching) { + returnValue = matching.invoke(func, thisValue, args); + } else { + returnValue = (this.func || func).apply(thisValue, args); + } + + var thisCall = this.getCall(this.callCount - 1); + if (thisCall.calledWithNew() && typeof returnValue !== "object") { + returnValue = thisValue; + } + } catch (e) { + exception = e; + } finally { + delete this.invoking; + } + + push.call(this.exceptions, exception); + push.call(this.returnValues, returnValue); + push.call(this.stacks, new Error().stack); + + // Make return value and exception available in the calls: + createCallProperties.call(this); + + if (exception !== undefined) { + throw exception; + } + + return returnValue; + }, + + named: function named(name) { + this.displayName = name; + return this; + }, + + getCall: function getCall(i) { + if (i < 0 || i >= this.callCount) { + return null; + } + + return sinon.spyCall(this, this.thisValues[i], this.args[i], + this.returnValues[i], this.exceptions[i], + this.callIds[i], this.stacks[i]); + }, + + getCalls: function () { + var calls = []; + var i; + + for (i = 0; i < this.callCount; i++) { + calls.push(this.getCall(i)); + } + + return calls; + }, + + calledBefore: function calledBefore(spyFn) { + if (!this.called) { + return false; + } + + if (!spyFn.called) { + return true; + } + + return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1]; + }, + + calledAfter: function calledAfter(spyFn) { + if (!this.called || !spyFn.called) { + return false; + } + + return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1]; + }, + + withArgs: function () { + var args = slice.call(arguments); + + if (this.fakes) { + var match = matchingFake(this.fakes, args, true); + + if (match) { + return match; + } + } else { + this.fakes = []; + } + + var original = this; + var fake = this.instantiateFake(); + fake.matchingAguments = args; + fake.parent = this; + push.call(this.fakes, fake); + + fake.withArgs = function () { + return original.withArgs.apply(original, arguments); + }; + + for (var i = 0; i < this.args.length; i++) { + if (fake.matches(this.args[i])) { + incrementCallCount.call(fake); + push.call(fake.thisValues, this.thisValues[i]); + push.call(fake.args, this.args[i]); + push.call(fake.returnValues, this.returnValues[i]); + push.call(fake.exceptions, this.exceptions[i]); + push.call(fake.callIds, this.callIds[i]); + } + } + createCallProperties.call(fake); + + return fake; + }, + + matches: function (args, strict) { + var margs = this.matchingAguments; + + if (margs.length <= args.length && + sinon.deepEqual(margs, args.slice(0, margs.length))) { + return !strict || margs.length === args.length; + } + }, + + printf: function (format) { + var spyInstance = this; + var args = slice.call(arguments, 1); + var formatter; + + return (format || "").replace(/%(.)/g, function (match, specifyer) { + formatter = spyApi.formatters[specifyer]; + + if (typeof formatter === "function") { + return formatter.call(null, spyInstance, args); + } else if (!isNaN(parseInt(specifyer, 10))) { + return sinon.format(args[specifyer - 1]); + } + + return "%" + specifyer; + }); + } + }; + + function delegateToCalls(method, matchAny, actual, notCalled) { + spyApi[method] = function () { + if (!this.called) { + if (notCalled) { + return notCalled.apply(this, arguments); + } + return false; + } + + var currentCall; + var matches = 0; + + for (var i = 0, l = this.callCount; i < l; i += 1) { + currentCall = this.getCall(i); + + if (currentCall[actual || method].apply(currentCall, arguments)) { + matches += 1; + + if (matchAny) { + return true; + } + } + } + + return matches === this.callCount; + }; + } + + delegateToCalls("calledOn", true); + delegateToCalls("alwaysCalledOn", false, "calledOn"); + delegateToCalls("calledWith", true); + delegateToCalls("calledWithMatch", true); + delegateToCalls("alwaysCalledWith", false, "calledWith"); + delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch"); + delegateToCalls("calledWithExactly", true); + delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly"); + delegateToCalls("neverCalledWith", false, "notCalledWith", function () { + return true; + }); + delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch", function () { + return true; + }); + delegateToCalls("threw", true); + delegateToCalls("alwaysThrew", false, "threw"); + delegateToCalls("returned", true); + delegateToCalls("alwaysReturned", false, "returned"); + delegateToCalls("calledWithNew", true); + delegateToCalls("alwaysCalledWithNew", false, "calledWithNew"); + delegateToCalls("callArg", false, "callArgWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgWith = spyApi.callArg; + delegateToCalls("callArgOn", false, "callArgOnWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgOnWith = spyApi.callArgOn; + delegateToCalls("yield", false, "yield", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode. + spyApi.invokeCallback = spyApi.yield; + delegateToCalls("yieldOn", false, "yieldOn", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + delegateToCalls("yieldTo", false, "yieldTo", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + delegateToCalls("yieldToOn", false, "yieldToOn", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + + spyApi.formatters = { + c: function (spyInstance) { + return sinon.timesInWords(spyInstance.callCount); + }, + + n: function (spyInstance) { + return spyInstance.toString(); + }, + + C: function (spyInstance) { + var calls = []; + + for (var i = 0, l = spyInstance.callCount; i < l; ++i) { + var stringifiedCall = " " + spyInstance.getCall(i).toString(); + if (/\n/.test(calls[i - 1])) { + stringifiedCall = "\n" + stringifiedCall; + } + push.call(calls, stringifiedCall); + } + + return calls.length > 0 ? "\n" + calls.join("\n") : ""; + }, + + t: function (spyInstance) { + var objects = []; + + for (var i = 0, l = spyInstance.callCount; i < l; ++i) { + push.call(objects, sinon.format(spyInstance.thisValues[i])); + } + + return objects.join(", "); + }, + + "*": function (spyInstance, args) { + var formatted = []; + + for (var i = 0, l = args.length; i < l; ++i) { + push.call(formatted, sinon.format(args[i])); + } + + return formatted.join(", "); + } + }; + + sinon.extend(spy, spyApi); + + spy.spyCall = sinon.spyCall; + sinon.spy = spy; + + return spy; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./call"); + require("./extend"); + require("./times_in_words"); + require("./format"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend extend.js + */ +/** + * Stub behavior + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Tim Fischbach (mail@timfischbach.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + var slice = Array.prototype.slice; + var join = Array.prototype.join; + var useLeftMostCallback = -1; + var useRightMostCallback = -2; + + var nextTick = (function () { + if (typeof process === "object" && typeof process.nextTick === "function") { + return process.nextTick; + } + + if (typeof setImmediate === "function") { + return setImmediate; + } + + return function (callback) { + setTimeout(callback, 0); + }; + })(); + + function throwsException(error, message) { + if (typeof error === "string") { + this.exception = new Error(message || ""); + this.exception.name = error; + } else if (!error) { + this.exception = new Error("Error"); + } else { + this.exception = error; + } + + return this; + } + + function getCallback(behavior, args) { + var callArgAt = behavior.callArgAt; + + if (callArgAt >= 0) { + return args[callArgAt]; + } + + var argumentList; + + if (callArgAt === useLeftMostCallback) { + argumentList = args; + } + + if (callArgAt === useRightMostCallback) { + argumentList = slice.call(args).reverse(); + } + + var callArgProp = behavior.callArgProp; + + for (var i = 0, l = argumentList.length; i < l; ++i) { + if (!callArgProp && typeof argumentList[i] === "function") { + return argumentList[i]; + } + + if (callArgProp && argumentList[i] && + typeof argumentList[i][callArgProp] === "function") { + return argumentList[i][callArgProp]; + } + } + + return null; + } + + function makeApi(sinon) { + function getCallbackError(behavior, func, args) { + if (behavior.callArgAt < 0) { + var msg; + + if (behavior.callArgProp) { + msg = sinon.functionName(behavior.stub) + + " expected to yield to '" + behavior.callArgProp + + "', but no object with such a property was passed."; + } else { + msg = sinon.functionName(behavior.stub) + + " expected to yield, but no callback was passed."; + } + + if (args.length > 0) { + msg += " Received [" + join.call(args, ", ") + "]"; + } + + return msg; + } + + return "argument at index " + behavior.callArgAt + " is not a function: " + func; + } + + function callCallback(behavior, args) { + if (typeof behavior.callArgAt === "number") { + var func = getCallback(behavior, args); + + if (typeof func !== "function") { + throw new TypeError(getCallbackError(behavior, func, args)); + } + + if (behavior.callbackAsync) { + nextTick(function () { + func.apply(behavior.callbackContext, behavior.callbackArguments); + }); + } else { + func.apply(behavior.callbackContext, behavior.callbackArguments); + } + } + } + + var proto = { + create: function create(stub) { + var behavior = sinon.extend({}, sinon.behavior); + delete behavior.create; + behavior.stub = stub; + + return behavior; + }, + + isPresent: function isPresent() { + return (typeof this.callArgAt === "number" || + this.exception || + typeof this.returnArgAt === "number" || + this.returnThis || + this.returnValueDefined); + }, + + invoke: function invoke(context, args) { + callCallback(this, args); + + if (this.exception) { + throw this.exception; + } else if (typeof this.returnArgAt === "number") { + return args[this.returnArgAt]; + } else if (this.returnThis) { + return context; + } + + return this.returnValue; + }, + + onCall: function onCall(index) { + return this.stub.onCall(index); + }, + + onFirstCall: function onFirstCall() { + return this.stub.onFirstCall(); + }, + + onSecondCall: function onSecondCall() { + return this.stub.onSecondCall(); + }, + + onThirdCall: function onThirdCall() { + return this.stub.onThirdCall(); + }, + + withArgs: function withArgs(/* arguments */) { + throw new Error( + "Defining a stub by invoking \"stub.onCall(...).withArgs(...)\" " + + "is not supported. Use \"stub.withArgs(...).onCall(...)\" " + + "to define sequential behavior for calls with certain arguments." + ); + }, + + callsArg: function callsArg(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOn: function callsArgOn(pos, context) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgWith: function callsArgWith(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOnWith: function callsArgWith(pos, context) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yields: function () { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsRight: function () { + this.callArgAt = useRightMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsOn: function (context) { + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsTo: function (prop) { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + yieldsToOn: function (prop, context) { + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + throws: throwsException, + throwsException: throwsException, + + returns: function returns(value) { + this.returnValue = value; + this.returnValueDefined = true; + this.exception = undefined; + + return this; + }, + + returnsArg: function returnsArg(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.returnArgAt = pos; + + return this; + }, + + returnsThis: function returnsThis() { + this.returnThis = true; + + return this; + } + }; + + function createAsyncVersion(syncFnName) { + return function () { + var result = this[syncFnName].apply(this, arguments); + this.callbackAsync = true; + return result; + }; + } + + // create asynchronous versions of callsArg* and yields* methods + for (var method in proto) { + // need to avoid creating anotherasync versions of the newly added async methods + if (proto.hasOwnProperty(method) && method.match(/^(callsArg|yields)/) && !method.match(/Async/)) { + proto[method + "Async"] = createAsyncVersion(method); + } + } + + sinon.behavior = proto; + return proto; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./extend"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + function walkInternal(obj, iterator, context, originalObj, seen) { + var proto, prop; + + if (typeof Object.getOwnPropertyNames !== "function") { + // We explicitly want to enumerate through all of the prototype's properties + // in this case, therefore we deliberately leave out an own property check. + /* eslint-disable guard-for-in */ + for (prop in obj) { + iterator.call(context, obj[prop], prop, obj); + } + /* eslint-enable guard-for-in */ + + return; + } + + Object.getOwnPropertyNames(obj).forEach(function (k) { + if (!seen[k]) { + seen[k] = true; + var target = typeof Object.getOwnPropertyDescriptor(obj, k).get === "function" ? + originalObj : obj; + iterator.call(context, target[k], k, target); + } + }); + + proto = Object.getPrototypeOf(obj); + if (proto) { + walkInternal(proto, iterator, context, originalObj, seen); + } + } + + /* Public: walks the prototype chain of an object and iterates over every own property + * name encountered. The iterator is called in the same fashion that Array.prototype.forEach + * works, where it is passed the value, key, and own object as the 1st, 2nd, and 3rd positional + * argument, respectively. In cases where Object.getOwnPropertyNames is not available, walk will + * default to using a simple for..in loop. + * + * obj - The object to walk the prototype chain for. + * iterator - The function to be called on each pass of the walk. + * context - (Optional) When given, the iterator will be called with this object as the receiver. + */ + function walk(obj, iterator, context) { + return walkInternal(obj, iterator, context, obj, {}); + } + + sinon.walk = walk; + return sinon.walk; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend extend.js + * @depend spy.js + * @depend behavior.js + * @depend walk.js + */ +/** + * Stub functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + function stub(object, property, func) { + if (!!func && typeof func !== "function" && typeof func !== "object") { + throw new TypeError("Custom stub should be a function or a property descriptor"); + } + + var wrapper; + + if (func) { + if (typeof func === "function") { + wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func; + } else { + wrapper = func; + if (sinon.spy && sinon.spy.create) { + var types = sinon.objectKeys(wrapper); + for (var i = 0; i < types.length; i++) { + wrapper[types[i]] = sinon.spy.create(wrapper[types[i]]); + } + } + } + } else { + var stubLength = 0; + if (typeof object === "object" && typeof object[property] === "function") { + stubLength = object[property].length; + } + wrapper = stub.create(stubLength); + } + + if (!object && typeof property === "undefined") { + return sinon.stub.create(); + } + + if (typeof property === "undefined" && typeof object === "object") { + sinon.walk(object || {}, function (value, prop, propOwner) { + // we don't want to stub things like toString(), valueOf(), etc. so we only stub if the object + // is not Object.prototype + if ( + propOwner !== Object.prototype && + prop !== "constructor" && + typeof sinon.getPropertyDescriptor(propOwner, prop).value === "function" + ) { + stub(object, prop); + } + }); + + return object; + } + + return sinon.wrapMethod(object, property, wrapper); + } + + + /*eslint-disable no-use-before-define*/ + function getParentBehaviour(stubInstance) { + return (stubInstance.parent && getCurrentBehavior(stubInstance.parent)); + } + + function getDefaultBehavior(stubInstance) { + return stubInstance.defaultBehavior || + getParentBehaviour(stubInstance) || + sinon.behavior.create(stubInstance); + } + + function getCurrentBehavior(stubInstance) { + var behavior = stubInstance.behaviors[stubInstance.callCount - 1]; + return behavior && behavior.isPresent() ? behavior : getDefaultBehavior(stubInstance); + } + /*eslint-enable no-use-before-define*/ + + var uuid = 0; + + var proto = { + create: function create(stubLength) { + var functionStub = function () { + return getCurrentBehavior(functionStub).invoke(this, arguments); + }; + + functionStub.id = "stub#" + uuid++; + var orig = functionStub; + functionStub = sinon.spy.create(functionStub, stubLength); + functionStub.func = orig; + + sinon.extend(functionStub, stub); + functionStub.instantiateFake = sinon.stub.create; + functionStub.displayName = "stub"; + functionStub.toString = sinon.functionToString; + + functionStub.defaultBehavior = null; + functionStub.behaviors = []; + + return functionStub; + }, + + resetBehavior: function () { + var i; + + this.defaultBehavior = null; + this.behaviors = []; + + delete this.returnValue; + delete this.returnArgAt; + this.returnThis = false; + + if (this.fakes) { + for (i = 0; i < this.fakes.length; i++) { + this.fakes[i].resetBehavior(); + } + } + }, + + onCall: function onCall(index) { + if (!this.behaviors[index]) { + this.behaviors[index] = sinon.behavior.create(this); + } + + return this.behaviors[index]; + }, + + onFirstCall: function onFirstCall() { + return this.onCall(0); + }, + + onSecondCall: function onSecondCall() { + return this.onCall(1); + }, + + onThirdCall: function onThirdCall() { + return this.onCall(2); + } + }; + + function createBehavior(behaviorMethod) { + return function () { + this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this); + this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments); + return this; + }; + } + + for (var method in sinon.behavior) { + if (sinon.behavior.hasOwnProperty(method) && + !proto.hasOwnProperty(method) && + method !== "create" && + method !== "withArgs" && + method !== "invoke") { + proto[method] = createBehavior(method); + } + } + + sinon.extend(stub, proto); + sinon.stub = stub; + + return stub; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./behavior"); + require("./spy"); + require("./extend"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend call.js + * @depend extend.js + * @depend match.js + * @depend spy.js + * @depend stub.js + * @depend format.js + */ +/** + * Mock functions. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + var push = [].push; + var match = sinon.match; + + function mock(object) { + // if (typeof console !== undefined && console.warn) { + // console.warn("mock will be removed from Sinon.JS v2.0"); + // } + + if (!object) { + return sinon.expectation.create("Anonymous mock"); + } + + return mock.create(object); + } + + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + + function arrayEquals(arr1, arr2, compareLength) { + if (compareLength && (arr1.length !== arr2.length)) { + return false; + } + + for (var i = 0, l = arr1.length; i < l; i++) { + if (!sinon.deepEqual(arr1[i], arr2[i])) { + return false; + } + } + return true; + } + + sinon.extend(mock, { + create: function create(object) { + if (!object) { + throw new TypeError("object is null"); + } + + var mockObject = sinon.extend({}, mock); + mockObject.object = object; + delete mockObject.create; + + return mockObject; + }, + + expects: function expects(method) { + if (!method) { + throw new TypeError("method is falsy"); + } + + if (!this.expectations) { + this.expectations = {}; + this.proxies = []; + } + + if (!this.expectations[method]) { + this.expectations[method] = []; + var mockObject = this; + + sinon.wrapMethod(this.object, method, function () { + return mockObject.invokeMethod(method, this, arguments); + }); + + push.call(this.proxies, method); + } + + var expectation = sinon.expectation.create(method); + push.call(this.expectations[method], expectation); + + return expectation; + }, + + restore: function restore() { + var object = this.object; + + each(this.proxies, function (proxy) { + if (typeof object[proxy].restore === "function") { + object[proxy].restore(); + } + }); + }, + + verify: function verify() { + var expectations = this.expectations || {}; + var messages = []; + var met = []; + + each(this.proxies, function (proxy) { + each(expectations[proxy], function (expectation) { + if (!expectation.met()) { + push.call(messages, expectation.toString()); + } else { + push.call(met, expectation.toString()); + } + }); + }); + + this.restore(); + + if (messages.length > 0) { + sinon.expectation.fail(messages.concat(met).join("\n")); + } else if (met.length > 0) { + sinon.expectation.pass(messages.concat(met).join("\n")); + } + + return true; + }, + + invokeMethod: function invokeMethod(method, thisValue, args) { + var expectations = this.expectations && this.expectations[method] ? this.expectations[method] : []; + var expectationsWithMatchingArgs = []; + var currentArgs = args || []; + var i, available; + + for (i = 0; i < expectations.length; i += 1) { + var expectedArgs = expectations[i].expectedArguments || []; + if (arrayEquals(expectedArgs, currentArgs, expectations[i].expectsExactArgCount)) { + expectationsWithMatchingArgs.push(expectations[i]); + } + } + + for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) { + if (!expectationsWithMatchingArgs[i].met() && + expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) { + return expectationsWithMatchingArgs[i].apply(thisValue, args); + } + } + + var messages = []; + var exhausted = 0; + + for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) { + if (expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) { + available = available || expectationsWithMatchingArgs[i]; + } else { + exhausted += 1; + } + } + + if (available && exhausted === 0) { + return available.apply(thisValue, args); + } + + for (i = 0; i < expectations.length; i += 1) { + push.call(messages, " " + expectations[i].toString()); + } + + messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({ + proxy: method, + args: args + })); + + sinon.expectation.fail(messages.join("\n")); + } + }); + + var times = sinon.timesInWords; + var slice = Array.prototype.slice; + + function callCountInWords(callCount) { + if (callCount === 0) { + return "never called"; + } + + return "called " + times(callCount); + } + + function expectedCallCountInWords(expectation) { + var min = expectation.minCalls; + var max = expectation.maxCalls; + + if (typeof min === "number" && typeof max === "number") { + var str = times(min); + + if (min !== max) { + str = "at least " + str + " and at most " + times(max); + } + + return str; + } + + if (typeof min === "number") { + return "at least " + times(min); + } + + return "at most " + times(max); + } + + function receivedMinCalls(expectation) { + var hasMinLimit = typeof expectation.minCalls === "number"; + return !hasMinLimit || expectation.callCount >= expectation.minCalls; + } + + function receivedMaxCalls(expectation) { + if (typeof expectation.maxCalls !== "number") { + return false; + } + + return expectation.callCount === expectation.maxCalls; + } + + function verifyMatcher(possibleMatcher, arg) { + var isMatcher = match && match.isMatcher(possibleMatcher); + + return isMatcher && possibleMatcher.test(arg) || true; + } + + sinon.expectation = { + minCalls: 1, + maxCalls: 1, + + create: function create(methodName) { + var expectation = sinon.extend(sinon.stub.create(), sinon.expectation); + delete expectation.create; + expectation.method = methodName; + + return expectation; + }, + + invoke: function invoke(func, thisValue, args) { + this.verifyCallAllowed(thisValue, args); + + return sinon.spy.invoke.apply(this, arguments); + }, + + atLeast: function atLeast(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.maxCalls = null; + this.limitsSet = true; + } + + this.minCalls = num; + + return this; + }, + + atMost: function atMost(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.minCalls = null; + this.limitsSet = true; + } + + this.maxCalls = num; + + return this; + }, + + never: function never() { + return this.exactly(0); + }, + + once: function once() { + return this.exactly(1); + }, + + twice: function twice() { + return this.exactly(2); + }, + + thrice: function thrice() { + return this.exactly(3); + }, + + exactly: function exactly(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not a number"); + } + + this.atLeast(num); + return this.atMost(num); + }, + + met: function met() { + return !this.failed && receivedMinCalls(this); + }, + + verifyCallAllowed: function verifyCallAllowed(thisValue, args) { + if (receivedMaxCalls(this)) { + this.failed = true; + sinon.expectation.fail(this.method + " already called " + times(this.maxCalls)); + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " + + this.expectedThis); + } + + if (!("expectedArguments" in this)) { + return; + } + + if (!args) { + sinon.expectation.fail(this.method + " received no arguments, expected " + + sinon.format(this.expectedArguments)); + } + + if (args.length < this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + if (this.expectsExactArgCount && + args.length !== this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", didn't match " + this.expectedArguments.toString()); + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", expected " + sinon.format(this.expectedArguments)); + } + } + }, + + allowsCall: function allowsCall(thisValue, args) { + if (this.met() && receivedMaxCalls(this)) { + return false; + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + return false; + } + + if (!("expectedArguments" in this)) { + return true; + } + + args = args || []; + + if (args.length < this.expectedArguments.length) { + return false; + } + + if (this.expectsExactArgCount && + args.length !== this.expectedArguments.length) { + return false; + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + return false; + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + return false; + } + } + + return true; + }, + + withArgs: function withArgs() { + this.expectedArguments = slice.call(arguments); + return this; + }, + + withExactArgs: function withExactArgs() { + this.withArgs.apply(this, arguments); + this.expectsExactArgCount = true; + return this; + }, + + on: function on(thisValue) { + this.expectedThis = thisValue; + return this; + }, + + toString: function () { + var args = (this.expectedArguments || []).slice(); + + if (!this.expectsExactArgCount) { + push.call(args, "[...]"); + } + + var callStr = sinon.spyCall.toString.call({ + proxy: this.method || "anonymous mock expectation", + args: args + }); + + var message = callStr.replace(", [...", "[, ...") + " " + + expectedCallCountInWords(this); + + if (this.met()) { + return "Expectation met: " + message; + } + + return "Expected " + message + " (" + + callCountInWords(this.callCount) + ")"; + }, + + verify: function verify() { + if (!this.met()) { + sinon.expectation.fail(this.toString()); + } else { + sinon.expectation.pass(this.toString()); + } + + return true; + }, + + pass: function pass(message) { + sinon.assert.pass(message); + }, + + fail: function fail(message) { + var exception = new Error(message); + exception.name = "ExpectationError"; + + throw exception; + } + }; + + sinon.mock = mock; + return mock; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./times_in_words"); + require("./call"); + require("./extend"); + require("./match"); + require("./spy"); + require("./stub"); + require("./format"); + + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend spy.js + * @depend stub.js + * @depend mock.js + */ +/** + * Collections of stubs, spies and mocks. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + var push = [].push; + var hasOwnProperty = Object.prototype.hasOwnProperty; + + function getFakes(fakeCollection) { + if (!fakeCollection.fakes) { + fakeCollection.fakes = []; + } + + return fakeCollection.fakes; + } + + function each(fakeCollection, method) { + var fakes = getFakes(fakeCollection); + + for (var i = 0, l = fakes.length; i < l; i += 1) { + if (typeof fakes[i][method] === "function") { + fakes[i][method](); + } + } + } + + function compact(fakeCollection) { + var fakes = getFakes(fakeCollection); + var i = 0; + while (i < fakes.length) { + fakes.splice(i, 1); + } + } + + function makeApi(sinon) { + var collection = { + verify: function resolve() { + each(this, "verify"); + }, + + restore: function restore() { + each(this, "restore"); + compact(this); + }, + + reset: function restore() { + each(this, "reset"); + }, + + verifyAndRestore: function verifyAndRestore() { + var exception; + + try { + this.verify(); + } catch (e) { + exception = e; + } + + this.restore(); + + if (exception) { + throw exception; + } + }, + + add: function add(fake) { + push.call(getFakes(this), fake); + return fake; + }, + + spy: function spy() { + return this.add(sinon.spy.apply(sinon, arguments)); + }, + + stub: function stub(object, property, value) { + if (property) { + var original = object[property]; + + if (typeof original !== "function") { + if (!hasOwnProperty.call(object, property)) { + throw new TypeError("Cannot stub non-existent own property " + property); + } + + object[property] = value; + + return this.add({ + restore: function () { + object[property] = original; + } + }); + } + } + if (!property && !!object && typeof object === "object") { + var stubbedObj = sinon.stub.apply(sinon, arguments); + + for (var prop in stubbedObj) { + if (typeof stubbedObj[prop] === "function") { + this.add(stubbedObj[prop]); + } + } + + return stubbedObj; + } + + return this.add(sinon.stub.apply(sinon, arguments)); + }, + + mock: function mock() { + return this.add(sinon.mock.apply(sinon, arguments)); + }, + + inject: function inject(obj) { + var col = this; + + obj.spy = function () { + return col.spy.apply(col, arguments); + }; + + obj.stub = function () { + return col.stub.apply(col, arguments); + }; + + obj.mock = function () { + return col.mock.apply(col, arguments); + }; + + return obj; + } + }; + + sinon.collection = collection; + return collection; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./mock"); + require("./spy"); + require("./stub"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * Fake timer API + * setTimeout + * setInterval + * clearTimeout + * clearInterval + * tick + * reset + * Date + * + * Inspired by jsUnitMockTimeOut from JsUnit + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + function makeApi(s, lol) { + /*global lolex */ + var llx = typeof lolex !== "undefined" ? lolex : lol; + + s.useFakeTimers = function () { + var now; + var methods = Array.prototype.slice.call(arguments); + + if (typeof methods[0] === "string") { + now = 0; + } else { + now = methods.shift(); + } + + var clock = llx.install(now || 0, methods); + clock.restore = clock.uninstall; + return clock; + }; + + s.clock = { + create: function (now) { + return llx.createClock(now); + } + }; + + s.timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), + clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined), + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, epxorts, module, lolex) { + var core = require("./core"); + makeApi(core, lolex); + module.exports = core; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module, require("lolex")); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * Minimal Event interface implementation + * + * Original implementation by Sven Fuchs: https://gist.github.com/995028 + * Modifications and tests by Christian Johansen. + * + * @author Sven Fuchs (svenfuchs@artweb-design.de) + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2011 Sven Fuchs, Christian Johansen + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +(function () { + + var push = [].push; + + function makeApi(sinon) { + sinon.Event = function Event(type, bubbles, cancelable, target) { + this.initEvent(type, bubbles, cancelable, target); + }; + + sinon.Event.prototype = { + initEvent: function (type, bubbles, cancelable, target) { + this.type = type; + this.bubbles = bubbles; + this.cancelable = cancelable; + this.target = target; + }, + + stopPropagation: function () {}, + + preventDefault: function () { + this.defaultPrevented = true; + } + }; + + sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) { + this.initEvent(type, false, false, target); + this.loaded = progressEventRaw.loaded || null; + this.total = progressEventRaw.total || null; + this.lengthComputable = !!progressEventRaw.total; + }; + + sinon.ProgressEvent.prototype = new sinon.Event(); + + sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent; + + sinon.CustomEvent = function CustomEvent(type, customData, target) { + this.initEvent(type, false, false, target); + this.detail = customData.detail || null; + }; + + sinon.CustomEvent.prototype = new sinon.Event(); + + sinon.CustomEvent.prototype.constructor = sinon.CustomEvent; + + sinon.EventTarget = { + addEventListener: function addEventListener(event, listener) { + this.eventListeners = this.eventListeners || {}; + this.eventListeners[event] = this.eventListeners[event] || []; + push.call(this.eventListeners[event], listener); + }, + + removeEventListener: function removeEventListener(event, listener) { + var listeners = this.eventListeners && this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }, + + dispatchEvent: function dispatchEvent(event) { + var type = event.type; + var listeners = this.eventListeners && this.eventListeners[type] || []; + + for (var i = 0; i < listeners.length; i++) { + if (typeof listeners[i] === "function") { + listeners[i].call(this, event); + } else { + listeners[i].handleEvent(event); + } + } + + return !!event.defaultPrevented; + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend util/core.js + */ +/** + * Logs errors + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal) { + + // cache a reference to setTimeout, so that our reference won't be stubbed out + // when using fake timers and errors will still get logged + // https://github.com/cjohansen/Sinon.JS/issues/381 + var realSetTimeout = setTimeout; + + function makeApi(sinon) { + + function log() {} + + function logError(label, err) { + var msg = label + " threw exception: "; + + function throwLoggedError() { + err.message = msg + err.message; + throw err; + } + + sinon.log(msg + "[" + err.name + "] " + err.message); + + if (err.stack) { + sinon.log(err.stack); + } + + if (logError.useImmediateExceptions) { + throwLoggedError(); + } else { + logError.setTimeout(throwLoggedError, 0); + } + } + + // When set to true, any errors logged will be thrown immediately; + // If set to false, the errors will be thrown in separate execution frame. + logError.useImmediateExceptions = false; + + // wrap realSetTimeout with something we can stub in tests + logError.setTimeout = function (func, timeout) { + realSetTimeout(func, timeout); + }; + + var exports = {}; + exports.log = sinon.log = log; + exports.logError = sinon.logError = logError; + + return exports; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XDomainRequest object + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +// wrapper for global +(function (global) { + + var xdr = { XDomainRequest: global.XDomainRequest }; + xdr.GlobalXDomainRequest = global.XDomainRequest; + xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== "undefined"; + xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false; + + function makeApi(sinon) { + sinon.xdr = xdr; + + function FakeXDomainRequest() { + this.readyState = FakeXDomainRequest.UNSENT; + this.requestBody = null; + this.requestHeaders = {}; + this.status = 0; + this.timeout = null; + + if (typeof FakeXDomainRequest.onCreate === "function") { + FakeXDomainRequest.onCreate(this); + } + } + + function verifyState(x) { + if (x.readyState !== FakeXDomainRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (x.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function verifyRequestSent(x) { + if (x.readyState === FakeXDomainRequest.UNSENT) { + throw new Error("Request not sent"); + } + if (x.readyState === FakeXDomainRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XDomainRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, { + open: function open(method, url) { + this.method = method; + this.url = url; + + this.responseText = null; + this.sendFlag = false; + + this.readyStateChange(FakeXDomainRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + var eventName = ""; + switch (this.readyState) { + case FakeXDomainRequest.UNSENT: + break; + case FakeXDomainRequest.OPENED: + break; + case FakeXDomainRequest.LOADING: + if (this.sendFlag) { + //raise the progress event + eventName = "onprogress"; + } + break; + case FakeXDomainRequest.DONE: + if (this.isTimeout) { + eventName = "ontimeout"; + } else if (this.errorFlag || (this.status < 200 || this.status > 299)) { + eventName = "onerror"; + } else { + eventName = "onload"; + } + break; + } + + // raising event (if defined) + if (eventName) { + if (typeof this[eventName] === "function") { + try { + this[eventName](); + } catch (e) { + sinon.logError("Fake XHR " + eventName + " handler", e); + } + } + } + }, + + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + this.requestBody = data; + } + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + + this.errorFlag = false; + this.sendFlag = true; + this.readyStateChange(FakeXDomainRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + + if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) { + this.readyStateChange(sinon.FakeXDomainRequest.DONE); + this.sendFlag = false; + } + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyResponseBodyType(body); + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + this.readyStateChange(FakeXDomainRequest.LOADING); + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + this.readyStateChange(FakeXDomainRequest.DONE); + }, + + respond: function respond(status, contentType, body) { + // content-type ignored, since XDomainRequest does not carry this + // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease + // test integration across browsers + this.status = typeof status === "number" ? status : 200; + this.setResponseBody(body || ""); + }, + + simulatetimeout: function simulatetimeout() { + this.status = 0; + this.isTimeout = true; + // Access to this should actually throw an error + this.responseText = undefined; + this.readyStateChange(FakeXDomainRequest.DONE); + } + }); + + sinon.extend(FakeXDomainRequest, { + UNSENT: 0, + OPENED: 1, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXDomainRequest = function useFakeXDomainRequest() { + sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) { + if (xdr.supportsXDR) { + global.XDomainRequest = xdr.GlobalXDomainRequest; + } + + delete sinon.FakeXDomainRequest.restore; + + if (keepOnCreate !== true) { + delete sinon.FakeXDomainRequest.onCreate; + } + }; + if (xdr.supportsXDR) { + global.XDomainRequest = sinon.FakeXDomainRequest; + } + return sinon.FakeXDomainRequest; + }; + + sinon.FakeXDomainRequest = FakeXDomainRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +})(typeof global !== "undefined" ? global : self); + +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XMLHttpRequest object + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal, global) { + + function getWorkingXHR(globalScope) { + var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined"; + if (supportsXHR) { + return globalScope.XMLHttpRequest; + } + + var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined"; + if (supportsActiveX) { + return function () { + return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0"); + }; + } + + return false; + } + + var supportsProgress = typeof ProgressEvent !== "undefined"; + var supportsCustomEvent = typeof CustomEvent !== "undefined"; + var supportsFormData = typeof FormData !== "undefined"; + var supportsArrayBuffer = typeof ArrayBuffer !== "undefined"; + var supportsBlob = typeof Blob === "function"; + var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest }; + sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest; + sinonXhr.GlobalActiveXObject = global.ActiveXObject; + sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== "undefined"; + sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined"; + sinonXhr.workingXHR = getWorkingXHR(global); + sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest()); + + var unsafeHeaders = { + "Accept-Charset": true, + "Accept-Encoding": true, + Connection: true, + "Content-Length": true, + Cookie: true, + Cookie2: true, + "Content-Transfer-Encoding": true, + Date: true, + Expect: true, + Host: true, + "Keep-Alive": true, + Referer: true, + TE: true, + Trailer: true, + "Transfer-Encoding": true, + Upgrade: true, + "User-Agent": true, + Via: true + }; + + // An upload object is created for each + // FakeXMLHttpRequest and allows upload + // events to be simulated using uploadProgress + // and uploadError. + function UploadProgress() { + this.eventListeners = { + progress: [], + load: [], + abort: [], + error: [] + }; + } + + UploadProgress.prototype.addEventListener = function addEventListener(event, listener) { + this.eventListeners[event].push(listener); + }; + + UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) { + var listeners = this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }; + + UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) { + var listeners = this.eventListeners[event.type] || []; + + for (var i = 0, listener; (listener = listeners[i]) != null; i++) { + listener(event); + } + }; + + // Note that for FakeXMLHttpRequest to work pre ES5 + // we lose some of the alignment with the spec. + // To ensure as close a match as possible, + // set responseType before calling open, send or respond; + function FakeXMLHttpRequest() { + this.readyState = FakeXMLHttpRequest.UNSENT; + this.requestHeaders = {}; + this.requestBody = null; + this.status = 0; + this.statusText = ""; + this.upload = new UploadProgress(); + this.responseType = ""; + this.response = ""; + if (sinonXhr.supportsCORS) { + this.withCredentials = false; + } + + var xhr = this; + var events = ["loadstart", "load", "abort", "loadend"]; + + function addEventListener(eventName) { + xhr.addEventListener(eventName, function (event) { + var listener = xhr["on" + eventName]; + + if (listener && typeof listener === "function") { + listener.call(this, event); + } + }); + } + + for (var i = events.length - 1; i >= 0; i--) { + addEventListener(events[i]); + } + + if (typeof FakeXMLHttpRequest.onCreate === "function") { + FakeXMLHttpRequest.onCreate(this); + } + } + + function verifyState(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xhr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function getHeader(headers, header) { + header = header.toLowerCase(); + + for (var h in headers) { + if (h.toLowerCase() === header) { + return h; + } + } + + return null; + } + + // filtering to enable a white-list version of Sinon FakeXhr, + // where whitelisted requests are passed through to real XHR + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + function some(collection, callback) { + for (var index = 0; index < collection.length; index++) { + if (callback(collection[index]) === true) { + return true; + } + } + return false; + } + // largest arity in XHR is 5 - XHR#open + var apply = function (obj, method, args) { + switch (args.length) { + case 0: return obj[method](); + case 1: return obj[method](args[0]); + case 2: return obj[method](args[0], args[1]); + case 3: return obj[method](args[0], args[1], args[2]); + case 4: return obj[method](args[0], args[1], args[2], args[3]); + case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]); + } + }; + + FakeXMLHttpRequest.filters = []; + FakeXMLHttpRequest.addFilter = function addFilter(fn) { + this.filters.push(fn); + }; + var IE6Re = /MSIE 6/; + FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) { + var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap + + each([ + "open", + "setRequestHeader", + "send", + "abort", + "getResponseHeader", + "getAllResponseHeaders", + "addEventListener", + "overrideMimeType", + "removeEventListener" + ], function (method) { + fakeXhr[method] = function () { + return apply(xhr, method, arguments); + }; + }); + + var copyAttrs = function (args) { + each(args, function (attr) { + try { + fakeXhr[attr] = xhr[attr]; + } catch (e) { + if (!IE6Re.test(navigator.userAgent)) { + throw e; + } + } + }); + }; + + var stateChange = function stateChange() { + fakeXhr.readyState = xhr.readyState; + if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { + copyAttrs(["status", "statusText"]); + } + if (xhr.readyState >= FakeXMLHttpRequest.LOADING) { + copyAttrs(["responseText", "response"]); + } + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + copyAttrs(["responseXML"]); + } + if (fakeXhr.onreadystatechange) { + fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr }); + } + }; + + if (xhr.addEventListener) { + for (var event in fakeXhr.eventListeners) { + if (fakeXhr.eventListeners.hasOwnProperty(event)) { + + /*eslint-disable no-loop-func*/ + each(fakeXhr.eventListeners[event], function (handler) { + xhr.addEventListener(event, handler); + }); + /*eslint-enable no-loop-func*/ + } + } + xhr.addEventListener("readystatechange", stateChange); + } else { + xhr.onreadystatechange = stateChange; + } + apply(xhr, "open", xhrArgs); + }; + FakeXMLHttpRequest.useFilters = false; + + function verifyRequestOpened(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR - " + xhr.readyState); + } + } + + function verifyRequestSent(xhr) { + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyHeadersReceived(xhr) { + if (xhr.async && xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) { + throw new Error("No headers received"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XMLHttpRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + function convertToArrayBuffer(body) { + var buffer = new ArrayBuffer(body.length); + var view = new Uint8Array(buffer); + for (var i = 0; i < body.length; i++) { + var charCode = body.charCodeAt(i); + if (charCode >= 256) { + throw new TypeError("arraybuffer or blob responseTypes require binary string, " + + "invalid character " + body[i] + " found."); + } + view[i] = charCode; + } + return buffer; + } + + function isXmlContentType(contentType) { + return !contentType || /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType); + } + + function convertResponseBody(responseType, contentType, body) { + if (responseType === "" || responseType === "text") { + return body; + } else if (supportsArrayBuffer && responseType === "arraybuffer") { + return convertToArrayBuffer(body); + } else if (responseType === "json") { + try { + return JSON.parse(body); + } catch (e) { + // Return parsing failure as null + return null; + } + } else if (supportsBlob && responseType === "blob") { + var blobOptions = {}; + if (contentType) { + blobOptions.type = contentType; + } + return new Blob([convertToArrayBuffer(body)], blobOptions); + } else if (responseType === "document") { + if (isXmlContentType(contentType)) { + return FakeXMLHttpRequest.parseXML(body); + } + return null; + } + throw new Error("Invalid responseType " + responseType); + } + + function clearResponse(xhr) { + if (xhr.responseType === "" || xhr.responseType === "text") { + xhr.response = xhr.responseText = ""; + } else { + xhr.response = xhr.responseText = null; + } + xhr.responseXML = null; + } + + FakeXMLHttpRequest.parseXML = function parseXML(text) { + // Treat empty string as parsing failure + if (text !== "") { + try { + if (typeof DOMParser !== "undefined") { + var parser = new DOMParser(); + return parser.parseFromString(text, "text/xml"); + } + var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(text); + return xmlDoc; + } catch (e) { + // Unable to parse XML - no biggie + } + } + + return null; + }; + + FakeXMLHttpRequest.statusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 300: "Multiple Choice", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 422: "Unprocessable Entity", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" + }; + + function makeApi(sinon) { + sinon.xhr = sinonXhr; + + sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { + async: true, + + open: function open(method, url, async, username, password) { + this.method = method; + this.url = url; + this.async = typeof async === "boolean" ? async : true; + this.username = username; + this.password = password; + clearResponse(this); + this.requestHeaders = {}; + this.sendFlag = false; + + if (FakeXMLHttpRequest.useFilters === true) { + var xhrArgs = arguments; + var defake = some(FakeXMLHttpRequest.filters, function (filter) { + return filter.apply(this, xhrArgs); + }); + if (defake) { + return FakeXMLHttpRequest.defake(this, arguments); + } + } + this.readyStateChange(FakeXMLHttpRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + + var readyStateChangeEvent = new sinon.Event("readystatechange", false, false, this); + + if (typeof this.onreadystatechange === "function") { + try { + this.onreadystatechange(readyStateChangeEvent); + } catch (e) { + sinon.logError("Fake XHR onreadystatechange handler", e); + } + } + + switch (this.readyState) { + case FakeXMLHttpRequest.DONE: + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + this.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100})); + } + this.upload.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("loadend", false, false, this)); + break; + } + + this.dispatchEvent(readyStateChangeEvent); + }, + + setRequestHeader: function setRequestHeader(header, value) { + verifyState(this); + + if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { + throw new Error("Refused to set unsafe header \"" + header + "\""); + } + + if (this.requestHeaders[header]) { + this.requestHeaders[header] += "," + value; + } else { + this.requestHeaders[header] = value; + } + }, + + // Helps testing + setResponseHeaders: function setResponseHeaders(headers) { + verifyRequestOpened(this); + this.responseHeaders = {}; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + this.responseHeaders[header] = headers[header]; + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); + } else { + this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; + } + }, + + // Currently treats ALL data as a DOMString (i.e. no Document) + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + var contentType = getHeader(this.requestHeaders, "Content-Type"); + if (this.requestHeaders[contentType]) { + var value = this.requestHeaders[contentType].split(";"); + this.requestHeaders[contentType] = value[0] + ";charset=utf-8"; + } else if (supportsFormData && !(data instanceof FormData)) { + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + } + + this.requestBody = data; + } + + this.errorFlag = false; + this.sendFlag = this.async; + clearResponse(this); + this.readyStateChange(FakeXMLHttpRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + + this.dispatchEvent(new sinon.Event("loadstart", false, false, this)); + }, + + abort: function abort() { + this.aborted = true; + clearResponse(this); + this.errorFlag = true; + this.requestHeaders = {}; + this.responseHeaders = {}; + + if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) { + this.readyStateChange(FakeXMLHttpRequest.DONE); + this.sendFlag = false; + } + + this.readyState = FakeXMLHttpRequest.UNSENT; + + this.dispatchEvent(new sinon.Event("abort", false, false, this)); + + this.upload.dispatchEvent(new sinon.Event("abort", false, false, this)); + + if (typeof this.onerror === "function") { + this.onerror(); + } + }, + + getResponseHeader: function getResponseHeader(header) { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return null; + } + + if (/^Set-Cookie2?$/i.test(header)) { + return null; + } + + header = getHeader(this.responseHeaders, header); + + return this.responseHeaders[header] || null; + }, + + getAllResponseHeaders: function getAllResponseHeaders() { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return ""; + } + + var headers = ""; + + for (var header in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(header) && + !/^Set-Cookie2?$/i.test(header)) { + headers += header + ": " + this.responseHeaders[header] + "\r\n"; + } + } + + return headers; + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyHeadersReceived(this); + verifyResponseBodyType(body); + var contentType = this.getResponseHeader("Content-Type"); + + var isTextResponse = this.responseType === "" || this.responseType === "text"; + clearResponse(this); + if (this.async) { + var chunkSize = this.chunkSize || 10; + var index = 0; + + do { + this.readyStateChange(FakeXMLHttpRequest.LOADING); + + if (isTextResponse) { + this.responseText = this.response += body.substring(index, index + chunkSize); + } + index += chunkSize; + } while (index < body.length); + } + + this.response = convertResponseBody(this.responseType, contentType, body); + if (isTextResponse) { + this.responseText = this.response; + } + + if (this.responseType === "document") { + this.responseXML = this.response; + } else if (this.responseType === "" && isXmlContentType(contentType)) { + this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); + } + this.readyStateChange(FakeXMLHttpRequest.DONE); + }, + + respond: function respond(status, headers, body) { + this.status = typeof status === "number" ? status : 200; + this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; + this.setResponseHeaders(headers || {}); + this.setResponseBody(body || ""); + }, + + uploadProgress: function uploadProgress(progressEventRaw) { + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + downloadProgress: function downloadProgress(progressEventRaw) { + if (supportsProgress) { + this.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + uploadError: function uploadError(error) { + if (supportsCustomEvent) { + this.upload.dispatchEvent(new sinon.CustomEvent("error", {detail: error})); + } + } + }); + + sinon.extend(FakeXMLHttpRequest, { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXMLHttpRequest = function () { + FakeXMLHttpRequest.restore = function restore(keepOnCreate) { + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = sinonXhr.GlobalActiveXObject; + } + + delete FakeXMLHttpRequest.restore; + + if (keepOnCreate !== true) { + delete FakeXMLHttpRequest.onCreate; + } + }; + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = FakeXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = function ActiveXObject(objId) { + if (objId === "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { + + return new FakeXMLHttpRequest(); + } + + return new sinonXhr.GlobalActiveXObject(objId); + }; + } + + return FakeXMLHttpRequest; + }; + + sinon.FakeXMLHttpRequest = FakeXMLHttpRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof global !== "undefined" ? global : self +)); + +/** + * @depend fake_xdomain_request.js + * @depend fake_xml_http_request.js + * @depend ../format.js + * @depend ../log_error.js + */ +/** + * The Sinon "server" mimics a web server that receives requests from + * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, + * both synchronously and asynchronously. To respond synchronuously, canned + * answers have to be provided upfront. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + var push = [].push; + + function responseArray(handler) { + var response = handler; + + if (Object.prototype.toString.call(handler) !== "[object Array]") { + response = [200, {}, handler]; + } + + if (typeof response[2] !== "string") { + throw new TypeError("Fake server response body should be string, but was " + + typeof response[2]); + } + + return response; + } + + var wloc = typeof window !== "undefined" ? window.location : {}; + var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host); + + function matchOne(response, reqMethod, reqUrl) { + var rmeth = response.method; + var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase(); + var url = response.url; + var matchUrl = !url || url === reqUrl || (typeof url.test === "function" && url.test(reqUrl)); + + return matchMethod && matchUrl; + } + + function match(response, request) { + var requestUrl = request.url; + + if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { + requestUrl = requestUrl.replace(rCurrLoc, ""); + } + + if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { + if (typeof response.response === "function") { + var ru = response.url; + var args = [request].concat(ru && typeof ru.exec === "function" ? ru.exec(requestUrl).slice(1) : []); + return response.response.apply(response, args); + } + + return true; + } + + return false; + } + + function makeApi(sinon) { + sinon.fakeServer = { + create: function (config) { + var server = sinon.create(this); + server.configure(config); + if (!sinon.xhr.supportsCORS) { + this.xhr = sinon.useFakeXDomainRequest(); + } else { + this.xhr = sinon.useFakeXMLHttpRequest(); + } + server.requests = []; + + this.xhr.onCreate = function (xhrObj) { + server.addRequest(xhrObj); + }; + + return server; + }, + configure: function (config) { + var whitelist = { + "autoRespond": true, + "autoRespondAfter": true, + "respondImmediately": true, + "fakeHTTPMethods": true + }; + var setting; + + config = config || {}; + for (setting in config) { + if (whitelist.hasOwnProperty(setting) && config.hasOwnProperty(setting)) { + this[setting] = config[setting]; + } + } + }, + addRequest: function addRequest(xhrObj) { + var server = this; + push.call(this.requests, xhrObj); + + xhrObj.onSend = function () { + server.handleRequest(this); + + if (server.respondImmediately) { + server.respond(); + } else if (server.autoRespond && !server.responding) { + setTimeout(function () { + server.responding = false; + server.respond(); + }, server.autoRespondAfter || 10); + + server.responding = true; + } + }; + }, + + getHTTPMethod: function getHTTPMethod(request) { + if (this.fakeHTTPMethods && /post/i.test(request.method)) { + var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); + return matches ? matches[1] : request.method; + } + + return request.method; + }, + + handleRequest: function handleRequest(xhr) { + if (xhr.async) { + if (!this.queue) { + this.queue = []; + } + + push.call(this.queue, xhr); + } else { + this.processRequest(xhr); + } + }, + + log: function log(response, request) { + var str; + + str = "Request:\n" + sinon.format(request) + "\n\n"; + str += "Response:\n" + sinon.format(response) + "\n\n"; + + sinon.log(str); + }, + + respondWith: function respondWith(method, url, body) { + if (arguments.length === 1 && typeof method !== "function") { + this.response = responseArray(method); + return; + } + + if (!this.responses) { + this.responses = []; + } + + if (arguments.length === 1) { + body = method; + url = method = null; + } + + if (arguments.length === 2) { + body = url; + url = method; + method = null; + } + + push.call(this.responses, { + method: method, + url: url, + response: typeof body === "function" ? body : responseArray(body) + }); + }, + + respond: function respond() { + if (arguments.length > 0) { + this.respondWith.apply(this, arguments); + } + + var queue = this.queue || []; + var requests = queue.splice(0, queue.length); + + for (var i = 0; i < requests.length; i++) { + this.processRequest(requests[i]); + } + }, + + processRequest: function processRequest(request) { + try { + if (request.aborted) { + return; + } + + var response = this.response || [404, {}, ""]; + + if (this.responses) { + for (var l = this.responses.length, i = l - 1; i >= 0; i--) { + if (match.call(this, this.responses[i], request)) { + response = this.responses[i].response; + break; + } + } + } + + if (request.readyState !== 4) { + this.log(response, request); + + request.respond(response[0], response[1], response[2]); + } + } catch (e) { + sinon.logError("Fake server request processing", e); + } + }, + + restore: function restore() { + return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("./fake_xdomain_request"); + require("./fake_xml_http_request"); + require("../format"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend fake_server.js + * @depend fake_timers.js + */ +/** + * Add-on for sinon.fakeServer that automatically handles a fake timer along with + * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery + * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, + * it polls the object for completion with setInterval. Dispite the direct + * motivation, there is nothing jQuery-specific in this file, so it can be used + * in any environment where the ajax implementation depends on setInterval or + * setTimeout. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + + function makeApi(sinon) { + function Server() {} + Server.prototype = sinon.fakeServer; + + sinon.fakeServerWithClock = new Server(); + + sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { + if (xhr.async) { + if (typeof setTimeout.clock === "object") { + this.clock = setTimeout.clock; + } else { + this.clock = sinon.useFakeTimers(); + this.resetClock = true; + } + + if (!this.longestTimeout) { + var clockSetTimeout = this.clock.setTimeout; + var clockSetInterval = this.clock.setInterval; + var server = this; + + this.clock.setTimeout = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetTimeout.apply(this, arguments); + }; + + this.clock.setInterval = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetInterval.apply(this, arguments); + }; + } + } + + return sinon.fakeServer.addRequest.call(this, xhr); + }; + + sinon.fakeServerWithClock.respond = function respond() { + var returnVal = sinon.fakeServer.respond.apply(this, arguments); + + if (this.clock) { + this.clock.tick(this.longestTimeout || 0); + this.longestTimeout = 0; + + if (this.resetClock) { + this.clock.restore(); + this.resetClock = false; + } + } + + return returnVal; + }; + + sinon.fakeServerWithClock.restore = function restore() { + if (this.clock) { + this.clock.restore(); + } + + return sinon.fakeServer.restore.apply(this, arguments); + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + require("./fake_server"); + require("./fake_timers"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +/** + * @depend util/core.js + * @depend extend.js + * @depend collection.js + * @depend util/fake_timers.js + * @depend util/fake_server_with_clock.js + */ +/** + * Manages fake collections as well as fake utilities such as Sinon's + * timers and fake XHR implementation in one convenient object. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + var push = [].push; + + function exposeValue(sandbox, config, key, value) { + if (!value) { + return; + } + + if (config.injectInto && !(key in config.injectInto)) { + config.injectInto[key] = value; + sandbox.injectedKeys.push(key); + } else { + push.call(sandbox.args, value); + } + } + + function prepareSandboxFromConfig(config) { + var sandbox = sinon.create(sinon.sandbox); + + if (config.useFakeServer) { + if (typeof config.useFakeServer === "object") { + sandbox.serverPrototype = config.useFakeServer; + } + + sandbox.useFakeServer(); + } + + if (config.useFakeTimers) { + if (typeof config.useFakeTimers === "object") { + sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers); + } else { + sandbox.useFakeTimers(); + } + } + + return sandbox; + } + + sinon.sandbox = sinon.extend(sinon.create(sinon.collection), { + useFakeTimers: function useFakeTimers() { + this.clock = sinon.useFakeTimers.apply(sinon, arguments); + + return this.add(this.clock); + }, + + serverPrototype: sinon.fakeServer, + + useFakeServer: function useFakeServer() { + var proto = this.serverPrototype || sinon.fakeServer; + + if (!proto || !proto.create) { + return null; + } + + this.server = proto.create(); + return this.add(this.server); + }, + + inject: function (obj) { + sinon.collection.inject.call(this, obj); + + if (this.clock) { + obj.clock = this.clock; + } + + if (this.server) { + obj.server = this.server; + obj.requests = this.server.requests; + } + + obj.match = sinon.match; + + return obj; + }, + + restore: function () { + sinon.collection.restore.apply(this, arguments); + this.restoreContext(); + }, + + restoreContext: function () { + if (this.injectedKeys) { + for (var i = 0, j = this.injectedKeys.length; i < j; i++) { + delete this.injectInto[this.injectedKeys[i]]; + } + this.injectedKeys = []; + } + }, + + create: function (config) { + if (!config) { + return sinon.create(sinon.sandbox); + } + + var sandbox = prepareSandboxFromConfig(config); + sandbox.args = sandbox.args || []; + sandbox.injectedKeys = []; + sandbox.injectInto = config.injectInto; + var prop, + value; + var exposed = sandbox.inject({}); + + if (config.properties) { + for (var i = 0, l = config.properties.length; i < l; i++) { + prop = config.properties[i]; + value = exposed[prop] || prop === "sandbox" && sandbox; + exposeValue(sandbox, config, prop, value); + } + } else { + exposeValue(sandbox, config, "sandbox", value); + } + + return sandbox; + }, + + match: sinon.match + }); + + sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer; + + return sinon.sandbox; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./extend"); + require("./util/fake_server_with_clock"); + require("./util/fake_timers"); + require("./collection"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend util/core.js + * @depend sandbox.js + */ +/** + * Test function, sandboxes fakes + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function makeApi(sinon) { + var slice = Array.prototype.slice; + + function test(callback) { + var type = typeof callback; + + if (type !== "function") { + throw new TypeError("sinon.test needs to wrap a test function, got " + type); + } + + function sinonSandboxedTest() { + var config = sinon.getConfig(sinon.config); + config.injectInto = config.injectIntoThis && this || config.injectInto; + var sandbox = sinon.sandbox.create(config); + var args = slice.call(arguments); + var oldDone = args.length && args[args.length - 1]; + var exception, result; + + if (typeof oldDone === "function") { + args[args.length - 1] = function sinonDone(res) { + if (res) { + sandbox.restore(); + } else { + sandbox.verifyAndRestore(); + } + oldDone(res); + }; + } + + try { + result = callback.apply(this, args.concat(sandbox.args)); + } catch (e) { + exception = e; + } + + if (typeof oldDone !== "function") { + if (typeof exception !== "undefined") { + sandbox.restore(); + throw exception; + } else { + sandbox.verifyAndRestore(); + } + } + + return result; + } + + if (callback.length) { + return function sinonAsyncSandboxedTest(done) { // eslint-disable-line no-unused-vars + return sinonSandboxedTest.apply(this, arguments); + }; + } + + return sinonSandboxedTest; + } + + test.config = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.test = test; + return test; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./sandbox"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (sinonGlobal) { + makeApi(sinonGlobal); + } +}(typeof sinon === "object" && sinon || null)); // eslint-disable-line no-undef + +/** + * @depend util/core.js + * @depend test.js + */ +/** + * Test case, sandboxes all test functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + + function createTest(property, setUp, tearDown) { + return function () { + if (setUp) { + setUp.apply(this, arguments); + } + + var exception, result; + + try { + result = property.apply(this, arguments); + } catch (e) { + exception = e; + } + + if (tearDown) { + tearDown.apply(this, arguments); + } + + if (exception) { + throw exception; + } + + return result; + }; + } + + function makeApi(sinon) { + function testCase(tests, prefix) { + if (!tests || typeof tests !== "object") { + throw new TypeError("sinon.testCase needs an object with test functions"); + } + + prefix = prefix || "test"; + var rPrefix = new RegExp("^" + prefix); + var methods = {}; + var setUp = tests.setUp; + var tearDown = tests.tearDown; + var testName, + property, + method; + + for (testName in tests) { + if (tests.hasOwnProperty(testName) && !/^(setUp|tearDown)$/.test(testName)) { + property = tests[testName]; + + if (typeof property === "function" && rPrefix.test(testName)) { + method = property; + + if (setUp || tearDown) { + method = createTest(property, setUp, tearDown); + } + + methods[testName] = sinon.test(method); + } else { + methods[testName] = tests[testName]; + } + } + } + + return methods; + } + + sinon.testCase = testCase; + return testCase; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./test"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend match.js + * @depend format.js + */ +/** + * Assertions matching the test spy retrieval interface. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal, global) { + + var slice = Array.prototype.slice; + + function makeApi(sinon) { + var assert; + + function verifyIsStub() { + var method; + + for (var i = 0, l = arguments.length; i < l; ++i) { + method = arguments[i]; + + if (!method) { + assert.fail("fake is not a spy"); + } + + if (method.proxy && method.proxy.isSinonProxy) { + verifyIsStub(method.proxy); + } else { + if (typeof method !== "function") { + assert.fail(method + " is not a function"); + } + + if (typeof method.getCall !== "function") { + assert.fail(method + " is not stubbed"); + } + } + + } + } + + function failAssertion(object, msg) { + object = object || global; + var failMethod = object.fail || assert.fail; + failMethod.call(object, msg); + } + + function mirrorPropAsAssertion(name, method, message) { + if (arguments.length === 2) { + message = method; + method = name; + } + + assert[name] = function (fake) { + verifyIsStub(fake); + + var args = slice.call(arguments, 1); + var failed = false; + + if (typeof method === "function") { + failed = !method(fake); + } else { + failed = typeof fake[method] === "function" ? + !fake[method].apply(fake, args) : !fake[method]; + } + + if (failed) { + failAssertion(this, (fake.printf || fake.proxy.printf).apply(fake, [message].concat(args))); + } else { + assert.pass(name); + } + }; + } + + function exposedName(prefix, prop) { + return !prefix || /^fail/.test(prop) ? prop : + prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1); + } + + assert = { + failException: "AssertError", + + fail: function fail(message) { + var error = new Error(message); + error.name = this.failException || assert.failException; + + throw error; + }, + + pass: function pass() {}, + + callOrder: function assertCallOrder() { + verifyIsStub.apply(null, arguments); + var expected = ""; + var actual = ""; + + if (!sinon.calledInOrder(arguments)) { + try { + expected = [].join.call(arguments, ", "); + var calls = slice.call(arguments); + var i = calls.length; + while (i) { + if (!calls[--i].called) { + calls.splice(i, 1); + } + } + actual = sinon.orderByFirstCall(calls).join(", "); + } catch (e) { + // If this fails, we'll just fall back to the blank string + } + + failAssertion(this, "expected " + expected + " to be " + + "called in order but were called as " + actual); + } else { + assert.pass("callOrder"); + } + }, + + callCount: function assertCallCount(method, count) { + verifyIsStub(method); + + if (method.callCount !== count) { + var msg = "expected %n to be called " + sinon.timesInWords(count) + + " but was called %c%C"; + failAssertion(this, method.printf(msg)); + } else { + assert.pass("callCount"); + } + }, + + expose: function expose(target, options) { + if (!target) { + throw new TypeError("target is null or undefined"); + } + + var o = options || {}; + var prefix = typeof o.prefix === "undefined" && "assert" || o.prefix; + var includeFail = typeof o.includeFail === "undefined" || !!o.includeFail; + + for (var method in this) { + if (method !== "expose" && (includeFail || !/^(fail)/.test(method))) { + target[exposedName(prefix, method)] = this[method]; + } + } + + return target; + }, + + match: function match(actual, expectation) { + var matcher = sinon.match(expectation); + if (matcher.test(actual)) { + assert.pass("match"); + } else { + var formatted = [ + "expected value to match", + " expected = " + sinon.format(expectation), + " actual = " + sinon.format(actual) + ]; + + failAssertion(this, formatted.join("\n")); + } + } + }; + + mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called"); + mirrorPropAsAssertion("notCalled", function (spy) { + return !spy.called; + }, "expected %n to not have been called but was called %c%C"); + mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C"); + mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C"); + mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C"); + mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t"); + mirrorPropAsAssertion( + "alwaysCalledOn", + "expected %n to always be called with %1 as this but was called with %t" + ); + mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new"); + mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new"); + mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C"); + mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C"); + mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C"); + mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C"); + mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C"); + mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C"); + mirrorPropAsAssertion("threw", "%n did not throw exception%C"); + mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C"); + + sinon.assert = assert; + return assert; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./match"); + require("./format"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof global !== "undefined" ? global : self +)); + + return sinon; +})); diff --git a/node_modules/promises-aplus-tests/node_modules/underscore/LICENSE b/node_modules/promises-aplus-tests/node_modules/underscore/LICENSE new file mode 100644 index 0000000..0d6b873 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/underscore/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative +Reporters & Editors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/promises-aplus-tests/node_modules/underscore/README.md b/node_modules/promises-aplus-tests/node_modules/underscore/README.md new file mode 100644 index 0000000..c2ba259 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/underscore/README.md @@ -0,0 +1,22 @@ + __ + /\ \ __ + __ __ ___ \_\ \ __ _ __ ____ ___ ___ _ __ __ /\_\ ____ + /\ \/\ \ /' _ `\ /'_ \ /'__`\/\ __\/ ,__\ / ___\ / __`\/\ __\/'__`\ \/\ \ /',__\ + \ \ \_\ \/\ \/\ \/\ \ \ \/\ __/\ \ \//\__, `\/\ \__//\ \ \ \ \ \//\ __/ __ \ \ \/\__, `\ + \ \____/\ \_\ \_\ \___,_\ \____\\ \_\\/\____/\ \____\ \____/\ \_\\ \____\/\_\ _\ \ \/\____/ + \/___/ \/_/\/_/\/__,_ /\/____/ \/_/ \/___/ \/____/\/___/ \/_/ \/____/\/_//\ \_\ \/___/ + \ \____/ + \/___/ + +Underscore.js is a utility-belt library for JavaScript that provides +support for the usual functional suspects (each, map, reduce, filter...) +without extending any core JavaScript objects. + +For Docs, License, Tests, and pre-packed downloads, see: +http://underscorejs.org + +Underscore is an open-sourced component of DocumentCloud: +https://github.com/documentcloud + +Many thanks to our contributors: +https://github.com/jashkenas/underscore/contributors diff --git a/node_modules/promises-aplus-tests/node_modules/underscore/package.json b/node_modules/promises-aplus-tests/node_modules/underscore/package.json new file mode 100644 index 0000000..fb45655 --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/underscore/package.json @@ -0,0 +1,66 @@ +{ + "name": "underscore", + "description": "JavaScript's functional programming helper library.", + "homepage": "http://underscorejs.org", + "keywords": [ + "util", + "functional", + "server", + "client", + "browser" + ], + "author": { + "name": "Jeremy Ashkenas", + "email": "jeremy@documentcloud.org" + }, + "repository": { + "type": "git", + "url": "git://github.com/jashkenas/underscore.git" + }, + "main": "underscore.js", + "version": "1.6.0", + "devDependencies": { + "docco": "0.6.x", + "phantomjs": "1.9.0-1", + "uglify-js": "2.4.x" + }, + "scripts": { + "test": "phantomjs test/vendor/runner.js test/index.html?noglobals=true", + "build": "uglifyjs underscore.js -c \"evaluate=false\" --comments \"/ .*/\" -m --source-map underscore-min.map -o underscore-min.js", + "doc": "docco underscore.js" + }, + "licenses": [ + { + "type": "MIT", + "url": "https://raw.github.com/jashkenas/underscore/master/LICENSE" + } + ], + "files": [ + "underscore.js", + "underscore-min.js", + "LICENSE" + ], + "bugs": { + "url": "https://github.com/jashkenas/underscore/issues" + }, + "_id": "underscore@1.6.0", + "dist": { + "shasum": "8b38b10cacdef63337b8b24e4ff86d45aea529a8", + "tarball": "http://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + }, + "_from": "underscore@>=1.6.0 <1.7.0", + "_npmVersion": "1.3.21", + "_npmUser": { + "name": "jashkenas", + "email": "jashkenas@gmail.com" + }, + "maintainers": [ + { + "name": "jashkenas", + "email": "jashkenas@gmail.com" + } + ], + "directories": {}, + "_shasum": "8b38b10cacdef63337b8b24e4ff86d45aea529a8", + "_resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" +} diff --git a/node_modules/promises-aplus-tests/node_modules/underscore/underscore-min.js b/node_modules/promises-aplus-tests/node_modules/underscore/underscore-min.js new file mode 100644 index 0000000..3434d6c --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/underscore/underscore-min.js @@ -0,0 +1,6 @@ +// Underscore.js 1.6.0 +// http://underscorejs.org +// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Underscore may be freely distributed under the MIT license. +(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,w=Object.keys,_=i.bind,j=function(n){return n instanceof j?n:this instanceof j?void(this._wrapped=n):new j(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=j),exports._=j):n._=j,j.VERSION="1.6.0";var A=j.each=j.forEach=function(n,t,e){if(null==n)return n;if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a=j.keys(n),u=0,i=a.length;i>u;u++)if(t.call(e,n[a[u]],a[u],n)===r)return;return n};j.map=j.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e.push(t.call(r,n,u,i))}),e)};var O="Reduce of empty array with no initial value";j.reduce=j.foldl=j.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=j.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},j.reduceRight=j.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=j.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=j.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},j.find=j.detect=function(n,t,r){var e;return k(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},j.filter=j.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&e.push(n)}),e)},j.reject=function(n,t,r){return j.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},j.every=j.all=function(n,t,e){t||(t=j.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var k=j.some=j.any=function(n,t,e){t||(t=j.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};j.contains=j.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:k(n,function(n){return n===t})},j.invoke=function(n,t){var r=o.call(arguments,2),e=j.isFunction(t);return j.map(n,function(n){return(e?t:n[t]).apply(n,r)})},j.pluck=function(n,t){return j.map(n,j.property(t))},j.where=function(n,t){return j.filter(n,j.matches(t))},j.findWhere=function(n,t){return j.find(n,j.matches(t))},j.max=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.max.apply(Math,n);var e=-1/0,u=-1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;o>u&&(e=n,u=o)}),e},j.min=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.min.apply(Math,n);var e=1/0,u=1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;u>o&&(e=n,u=o)}),e},j.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=j.random(r++),e[r-1]=e[t],e[t]=n}),e},j.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=j.values(n)),n[j.random(n.length-1)]):j.shuffle(n).slice(0,Math.max(0,t))};var E=function(n){return null==n?j.identity:j.isFunction(n)?n:j.property(n)};j.sortBy=function(n,t,r){return t=E(t),j.pluck(j.map(n,function(n,e,u){return{value:n,index:e,criteria:t.call(r,n,e,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=E(r),A(t,function(i,a){var o=r.call(e,i,a,t);n(u,o,i)}),u}};j.groupBy=F(function(n,t,r){j.has(n,t)?n[t].push(r):n[t]=[r]}),j.indexBy=F(function(n,t,r){n[t]=r}),j.countBy=F(function(n,t){j.has(n,t)?n[t]++:n[t]=1}),j.sortedIndex=function(n,t,r,e){r=E(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;r.call(e,n[o])t?[]:o.call(n,0,t)},j.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},j.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},j.rest=j.tail=j.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},j.compact=function(n){return j.filter(n,j.identity)};var M=function(n,t,r){return t&&j.every(n,j.isArray)?c.apply(r,n):(A(n,function(n){j.isArray(n)||j.isArguments(n)?t?a.apply(r,n):M(n,t,r):r.push(n)}),r)};j.flatten=function(n,t){return M(n,t,[])},j.without=function(n){return j.difference(n,o.call(arguments,1))},j.partition=function(n,t){var r=[],e=[];return A(n,function(n){(t(n)?r:e).push(n)}),[r,e]},j.uniq=j.unique=function(n,t,r,e){j.isFunction(t)&&(e=r,r=t,t=!1);var u=r?j.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:j.contains(a,r))||(a.push(r),i.push(n[e]))}),i},j.union=function(){return j.uniq(j.flatten(arguments,!0))},j.intersection=function(n){var t=o.call(arguments,1);return j.filter(j.uniq(n),function(n){return j.every(t,function(t){return j.contains(t,n)})})},j.difference=function(n){var t=c.apply(e,o.call(arguments,1));return j.filter(n,function(n){return!j.contains(t,n)})},j.zip=function(){for(var n=j.max(j.pluck(arguments,"length").concat(0)),t=new Array(n),r=0;n>r;r++)t[r]=j.pluck(arguments,""+r);return t},j.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},j.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=j.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},j.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},j.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=new Array(e);e>u;)i[u++]=n,n+=r;return i};var R=function(){};j.bind=function(n,t){var r,e;if(_&&n.bind===_)return _.apply(n,o.call(arguments,1));if(!j.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));R.prototype=n.prototype;var u=new R;R.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},j.partial=function(n){var t=o.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===j&&(e[u]=arguments[r++]);for(;r=f?(clearTimeout(a),a=null,o=l,i=n.apply(e,u),e=u=null):a||r.trailing===!1||(a=setTimeout(c,f)),i}},j.debounce=function(n,t,r){var e,u,i,a,o,c=function(){var l=j.now()-a;t>l?e=setTimeout(c,t-l):(e=null,r||(o=n.apply(i,u),i=u=null))};return function(){i=this,u=arguments,a=j.now();var l=r&&!e;return e||(e=setTimeout(c,t)),l&&(o=n.apply(i,u),i=u=null),o}},j.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},j.wrap=function(n,t){return j.partial(t,n)},j.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},j.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},j.keys=function(n){if(!j.isObject(n))return[];if(w)return w(n);var t=[];for(var r in n)j.has(n,r)&&t.push(r);return t},j.values=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},j.pairs=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},j.invert=function(n){for(var t={},r=j.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},j.functions=j.methods=function(n){var t=[];for(var r in n)j.isFunction(n[r])&&t.push(r);return t.sort()},j.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},j.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},j.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)j.contains(r,u)||(t[u]=n[u]);return t},j.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]===void 0&&(n[r]=t[r])}),n},j.clone=function(n){return j.isObject(n)?j.isArray(n)?n.slice():j.extend({},n):n},j.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof j&&(n=n._wrapped),t instanceof j&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==String(t);case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;var a=n.constructor,o=t.constructor;if(a!==o&&!(j.isFunction(a)&&a instanceof a&&j.isFunction(o)&&o instanceof o)&&"constructor"in n&&"constructor"in t)return!1;r.push(n),e.push(t);var c=0,f=!0;if("[object Array]"==u){if(c=n.length,f=c==t.length)for(;c--&&(f=S(n[c],t[c],r,e)););}else{for(var s in n)if(j.has(n,s)&&(c++,!(f=j.has(t,s)&&S(n[s],t[s],r,e))))break;if(f){for(s in t)if(j.has(t,s)&&!c--)break;f=!c}}return r.pop(),e.pop(),f};j.isEqual=function(n,t){return S(n,t,[],[])},j.isEmpty=function(n){if(null==n)return!0;if(j.isArray(n)||j.isString(n))return 0===n.length;for(var t in n)if(j.has(n,t))return!1;return!0},j.isElement=function(n){return!(!n||1!==n.nodeType)},j.isArray=x||function(n){return"[object Array]"==l.call(n)},j.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){j["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),j.isArguments(arguments)||(j.isArguments=function(n){return!(!n||!j.has(n,"callee"))}),"function"!=typeof/./&&(j.isFunction=function(n){return"function"==typeof n}),j.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},j.isNaN=function(n){return j.isNumber(n)&&n!=+n},j.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},j.isNull=function(n){return null===n},j.isUndefined=function(n){return n===void 0},j.has=function(n,t){return f.call(n,t)},j.noConflict=function(){return n._=t,this},j.identity=function(n){return n},j.constant=function(n){return function(){return n}},j.property=function(n){return function(t){return t[n]}},j.matches=function(n){return function(t){if(t===n)return!0;for(var r in n)if(n[r]!==t[r])return!1;return!0}},j.times=function(n,t,r){for(var e=Array(Math.max(0,n)),u=0;n>u;u++)e[u]=t.call(r,u);return e},j.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},j.now=Date.now||function(){return(new Date).getTime()};var T={escape:{"&":"&","<":"<",">":">",'"':""","'":"'"}};T.unescape=j.invert(T.escape);var I={escape:new RegExp("["+j.keys(T.escape).join("")+"]","g"),unescape:new RegExp("("+j.keys(T.unescape).join("|")+")","g")};j.each(["escape","unescape"],function(n){j[n]=function(t){return null==t?"":(""+t).replace(I[n],function(t){return T[n][t]})}}),j.result=function(n,t){if(null==n)return void 0;var r=n[t];return j.isFunction(r)?r.call(n):r},j.mixin=function(n){A(j.functions(n),function(t){var r=j[t]=n[t];j.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(j,n))}})};var N=0;j.uniqueId=function(n){var t=++N+"";return n?n+t:t},j.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;j.template=function(n,t,r){var e;r=j.defaults({},r,j.templateSettings);var u=new RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(D,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=new Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,j);var c=function(n){return e.call(this,n,j)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},j.chain=function(n){return j(n).chain()};var z=function(n){return this._chain?j(n).chain():n};j.mixin(j),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];j.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];j.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),j.extend(j.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}}),"function"==typeof define&&define.amd&&define("underscore",[],function(){return j})}).call(this); +//# sourceMappingURL=underscore-min.map \ No newline at end of file diff --git a/node_modules/promises-aplus-tests/node_modules/underscore/underscore.js b/node_modules/promises-aplus-tests/node_modules/underscore/underscore.js new file mode 100644 index 0000000..9a4cabe --- /dev/null +++ b/node_modules/promises-aplus-tests/node_modules/underscore/underscore.js @@ -0,0 +1,1343 @@ +// Underscore.js 1.6.0 +// http://underscorejs.org +// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Underscore may be freely distributed under the MIT license. + +(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `exports` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Establish the object that gets returned to break out of a loop iteration. + var breaker = {}; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var + push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object via a string identifier, + // for Closure Compiler "advanced" mode. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root._ = _; + } + + // Current version. + _.VERSION = '1.6.0'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return obj; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, length = obj.length; i < length; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return; + } + } + return obj; + }; + + // Return the results of applying the iterator to each element. + // Delegates to **ECMAScript 5**'s native `map` if available. + _.map = _.collect = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results.push(iterator.call(context, value, index, list)); + }); + return results; + }; + + var reduceError = 'Reduce of empty array with no initial value'; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + // Delegates to **ECMAScript 5**'s native `reduceRight` if available. + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var length = obj.length; + if (length !== +length) { + var keys = _.keys(obj); + length = keys.length; + } + each(obj, function(value, index, list) { + index = keys ? keys[--length] : --length; + if (!initial) { + memo = obj[index]; + initial = true; + } else { + memo = iterator.call(context, memo, obj[index], index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, predicate, context) { + var result; + any(obj, function(value, index, list) { + if (predicate.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Delegates to **ECMAScript 5**'s native `filter` if available. + // Aliased as `select`. + _.filter = _.select = function(obj, predicate, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context); + each(obj, function(value, index, list) { + if (predicate.call(context, value, index, list)) results.push(value); + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, predicate, context) { + return _.filter(obj, function(value, index, list) { + return !predicate.call(context, value, index, list); + }, context); + }; + + // Determine whether all of the elements match a truth test. + // Delegates to **ECMAScript 5**'s native `every` if available. + // Aliased as `all`. + _.every = _.all = function(obj, predicate, context) { + predicate || (predicate = _.identity); + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context); + each(obj, function(value, index, list) { + if (!(result = result && predicate.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if at least one element in the object matches a truth test. + // Delegates to **ECMAScript 5**'s native `some` if available. + // Aliased as `any`. + var any = _.some = _.any = function(obj, predicate, context) { + predicate || (predicate = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context); + each(obj, function(value, index, list) { + if (result || (result = predicate.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if the array or object contains a given value (using `===`). + // Aliased as `include`. + _.contains = _.include = function(obj, target) { + if (obj == null) return false; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + return any(obj, function(value) { + return value === target; + }); + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + var isFunc = _.isFunction(method); + return _.map(obj, function(value) { + return (isFunc ? method : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, _.property(key)); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // containing specific `key:value` pairs. + _.where = function(obj, attrs) { + return _.filter(obj, _.matches(attrs)); + }; + + // Convenience version of a common use case of `find`: getting the first object + // containing specific `key:value` pairs. + _.findWhere = function(obj, attrs) { + return _.find(obj, _.matches(attrs)); + }; + + // Return the maximum element or (element-based computation). + // Can't optimize arrays of integers longer than 65,535 elements. + // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797) + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.max.apply(Math, obj); + } + var result = -Infinity, lastComputed = -Infinity; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + if (computed > lastComputed) { + result = value; + lastComputed = computed; + } + }); + return result; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.min.apply(Math, obj); + } + var result = Infinity, lastComputed = Infinity; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + if (computed < lastComputed) { + result = value; + lastComputed = computed; + } + }); + return result; + }; + + // Shuffle an array, using the modern version of the + // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). + _.shuffle = function(obj) { + var rand; + var index = 0; + var shuffled = []; + each(obj, function(value) { + rand = _.random(index++); + shuffled[index - 1] = shuffled[rand]; + shuffled[rand] = value; + }); + return shuffled; + }; + + // Sample **n** random values from a collection. + // If **n** is not specified, returns a single random element. + // The internal `guard` argument allows it to work with `map`. + _.sample = function(obj, n, guard) { + if (n == null || guard) { + if (obj.length !== +obj.length) obj = _.values(obj); + return obj[_.random(obj.length - 1)]; + } + return _.shuffle(obj).slice(0, Math.max(0, n)); + }; + + // An internal function to generate lookup iterators. + var lookupIterator = function(value) { + if (value == null) return _.identity; + if (_.isFunction(value)) return value; + return _.property(value); + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, iterator, context) { + iterator = lookupIterator(iterator); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value: value, + index: index, + criteria: iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index - right.index; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(behavior) { + return function(obj, iterator, context) { + var result = {}; + iterator = lookupIterator(iterator); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = group(function(result, key, value) { + _.has(result, key) ? result[key].push(value) : result[key] = [value]; + }); + + // Indexes the object's values by a criterion, similar to `groupBy`, but for + // when you know that your index values will be unique. + _.indexBy = group(function(result, key, value) { + result[key] = value; + }); + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = group(function(result, key) { + _.has(result, key) ? result[key]++ : result[key] = 1; + }); + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator, context) { + iterator = lookupIterator(iterator); + var value = iterator.call(context, obj); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >>> 1; + iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely create a real, live array from anything iterable. + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (obj.length === +obj.length) return _.map(obj, _.identity); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + if (obj == null) return 0; + return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + if (array == null) return void 0; + if ((n == null) || guard) return array[0]; + if (n < 0) return []; + return slice.call(array, 0, n); + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. The **guard** check allows it to work with + // `_.map`. + _.initial = function(array, n, guard) { + return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. The **guard** check allows it to work with `_.map`. + _.last = function(array, n, guard) { + if (array == null) return void 0; + if ((n == null) || guard) return array[array.length - 1]; + return slice.call(array, Math.max(array.length - n, 0)); + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, (n == null) || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, _.identity); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, output) { + if (shallow && _.every(input, _.isArray)) { + return concat.apply(output, input); + } + each(input, function(value) { + if (_.isArray(value) || _.isArguments(value)) { + shallow ? push.apply(output, value) : flatten(value, shallow, output); + } else { + output.push(value); + } + }); + return output; + }; + + // Flatten out an array, either recursively (by default), or just one level. + _.flatten = function(array, shallow) { + return flatten(array, shallow, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Split an array into two arrays: one whose elements all satisfy the given + // predicate, and one whose elements all do not satisfy the predicate. + _.partition = function(array, predicate) { + var pass = [], fail = []; + each(array, function(elem) { + (predicate(elem) ? pass : fail).push(elem); + }); + return [pass, fail]; + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iterator, context) { + if (_.isFunction(isSorted)) { + context = iterator; + iterator = isSorted; + isSorted = false; + } + var initial = iterator ? _.map(array, iterator, context) : array; + var results = []; + var seen = []; + each(initial, function(value, index) { + if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { + seen.push(value); + results.push(array[index]); + } + }); + return results; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(_.flatten(arguments, true)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.contains(other, item); + }); + }); + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); + return _.filter(array, function(value){ return !_.contains(rest, value); }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + var length = _.max(_.pluck(arguments, 'length').concat(0)); + var results = new Array(length); + for (var i = 0; i < length; i++) { + results[i] = _.pluck(arguments, '' + i); + } + return results; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + if (list == null) return {}; + var result = {}; + for (var i = 0, length = list.length; i < length; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), + // we need this function. Return the position of the first occurrence of an + // item in an array, or -1 if the item is not included in the array. + // Delegates to **ECMAScript 5**'s native `indexOf` if available. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i = 0, length = array.length; + if (isSorted) { + if (typeof isSorted == 'number') { + i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted); + } else { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); + for (; i < length; i++) if (array[i] === item) return i; + return -1; + }; + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item, from) { + if (array == null) return -1; + var hasIndex = from != null; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { + return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); + } + var i = (hasIndex ? from : array.length); + while (i--) if (array[i] === item) return i; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var length = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(length); + + while(idx < length) { + range[idx++] = start; + start += step; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Reusable constructor function for prototype setting. + var ctor = function(){}; + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if + // available. + _.bind = function(func, context) { + var args, bound; + if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError; + args = slice.call(arguments, 2); + return bound = function() { + if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); + ctor.prototype = func.prototype; + var self = new ctor; + ctor.prototype = null; + var result = func.apply(self, args.concat(slice.call(arguments))); + if (Object(result) === result) return result; + return self; + }; + }; + + // Partially apply a function by creating a version that has had some of its + // arguments pre-filled, without changing its dynamic `this` context. _ acts + // as a placeholder, allowing any combination of arguments to be pre-filled. + _.partial = function(func) { + var boundArgs = slice.call(arguments, 1); + return function() { + var position = 0; + var args = boundArgs.slice(); + for (var i = 0, length = args.length; i < length; i++) { + if (args[i] === _) args[i] = arguments[position++]; + } + while (position < arguments.length) args.push(arguments[position++]); + return func.apply(this, args); + }; + }; + + // Bind a number of an object's methods to that object. Remaining arguments + // are the method names to be bound. Useful for ensuring that all callbacks + // defined on an object belong to it. + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length === 0) throw new Error('bindAll must be passed function names'); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(null, args); }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. Normally, the throttled function will run + // as much as it can, without ever going more than once per `wait` duration; + // but if you'd like to disable the execution on the leading edge, pass + // `{leading: false}`. To disable execution on the trailing edge, ditto. + _.throttle = function(func, wait, options) { + var context, args, result; + var timeout = null; + var previous = 0; + options || (options = {}); + var later = function() { + previous = options.leading === false ? 0 : _.now(); + timeout = null; + result = func.apply(context, args); + context = args = null; + }; + return function() { + var now = _.now(); + if (!previous && options.leading === false) previous = now; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + context = args = null; + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, args, context, timestamp, result; + + var later = function() { + var last = _.now() - timestamp; + if (last < wait) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + context = args = null; + } + } + }; + + return function() { + context = this; + args = arguments; + timestamp = _.now(); + var callNow = immediate && !timeout; + if (!timeout) { + timeout = setTimeout(later, wait); + } + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + + return result; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + memo = func.apply(this, arguments); + func = null; + return memo; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return _.partial(wrapper, func); + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var funcs = arguments; + return function() { + var args = arguments; + for (var i = funcs.length - 1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = function(obj) { + if (!_.isObject(obj)) return []; + if (nativeKeys) return nativeKeys(obj); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys.push(key); + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var values = new Array(length); + for (var i = 0; i < length; i++) { + values[i] = obj[keys[i]]; + } + return values; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var pairs = new Array(length); + for (var i = 0; i < length; i++) { + pairs[i] = [keys[i], obj[keys[i]]]; + } + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + result[obj[keys[i]]] = keys[i]; + } + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + each(keys, function(key) { + if (key in obj) copy[key] = obj[key]; + }); + return copy; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + for (var key in obj) { + if (!_.contains(keys, key)) copy[key] = obj[key]; + } + return copy; + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + if (obj[prop] === void 0) obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). + if (a === b) return a !== 0 || 1 / a == 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className != toString.call(b)) return false; + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') return false; + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) return bStack[length] == b; + } + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor)) + && ('constructor' in a && 'constructor' in b)) { + return false; + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0, result = true; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) break; + } + } + } else { + // Deep compare objects. + for (var key in a) { + if (_.has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (_.has(b, key) && !(size--)) break; + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b, [], []); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (_.has(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) == '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + return obj === Object(obj); + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. + each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) == '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return !!(obj && _.has(obj, 'callee')); + }; + } + + // Optimize `isFunction` if appropriate. + if (typeof (/./) !== 'function') { + _.isFunction = function(obj) { + return typeof obj === 'function'; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return isFinite(obj) && !isNaN(parseFloat(obj)); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj != +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iterators. + _.identity = function(value) { + return value; + }; + + _.constant = function(value) { + return function () { + return value; + }; + }; + + _.property = function(key) { + return function(obj) { + return obj[key]; + }; + }; + + // Returns a predicate for checking whether an object has a given set of `key:value` pairs. + _.matches = function(attrs) { + return function(obj) { + if (obj === attrs) return true; //avoid comparing an object to itself. + for (var key in attrs) { + if (attrs[key] !== obj[key]) + return false; + } + return true; + } + }; + + // Run a function **n** times. + _.times = function(n, iterator, context) { + var accum = Array(Math.max(0, n)); + for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); + return accum; + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + Math.floor(Math.random() * (max - min + 1)); + }; + + // A (possibly faster) way to get the current timestamp as an integer. + _.now = Date.now || function() { return new Date().getTime(); }; + + // List of HTML entities for escaping. + var entityMap = { + escape: { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + } + }; + entityMap.unescape = _.invert(entityMap.escape); + + // Regexes containing the keys and values listed immediately above. + var entityRegexes = { + escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), + unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + }; + + // Functions for escaping and unescaping strings to/from HTML interpolation. + _.each(['escape', 'unescape'], function(method) { + _[method] = function(string) { + if (string == null) return ''; + return ('' + string).replace(entityRegexes[method], function(match) { + return entityMap[method][match]; + }); + }; + }); + + // If the value of the named `property` is a function then invoke it with the + // `object` as context; otherwise, return it. + _.result = function(object, property) { + if (object == null) return void 0; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + each(_.functions(obj), function(name) { + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result.call(this, func.apply(_, args)); + }; + }); + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = ++idCounter + ''; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + var render; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } + if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } + if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + index = offset + match.length; + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function, which will delegate to the wrapper. + _.chain = function(obj) { + return _(obj).chain(); + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(obj) { + return this._chain ? _(obj).chain() : obj; + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; + return result.call(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result.call(this, method.apply(this._wrapped, arguments)); + }; + }); + + _.extend(_.prototype, { + + // Start chaining a wrapped Underscore object. + chain: function() { + this._chain = true; + return this; + }, + + // Extracts the result from a wrapped and chained object. + value: function() { + return this._wrapped; + } + + }); + + // AMD registration happens at the end for compatibility with AMD loaders + // that may not enforce next-turn semantics on modules. Even though general + // practice for AMD registration is to be anonymous, underscore registers + // as a named module because, like jQuery, it is a base library that is + // popular enough to be bundled in a third party lib, but not be part of + // an AMD load request. Those cases could generate an error when an + // anonymous define() is called outside of a loader request. + if (typeof define === 'function' && define.amd) { + define('underscore', [], function() { + return _; + }); + } +}).call(this); diff --git a/node_modules/promises-aplus-tests/package.json b/node_modules/promises-aplus-tests/package.json new file mode 100644 index 0000000..8db3e16 --- /dev/null +++ b/node_modules/promises-aplus-tests/package.json @@ -0,0 +1,68 @@ +{ + "name": "promises-aplus-tests", + "description": "Compliance test suite for Promises/A+", + "keywords": [ + "promises", + "promises-aplus" + ], + "version": "2.1.1", + "implements": [ + "Promises/A+ 1.1.0" + ], + "author": { + "name": "Domenic Denicola", + "email": "domenic@domenicdenicola.com", + "url": "http://domenic.me" + }, + "license": "WTFPL", + "repository": { + "type": "git", + "url": "git://github.com/promises-aplus/promises-tests.git" + }, + "bugs": { + "url": "https://github.com/promises-aplus/promises-tests/issues" + }, + "main": "lib/programmaticRunner.js", + "bin": { + "promises-aplus-tests": "lib/cli.js" + }, + "scripts": { + "lint": "jshint lib", + "test": "mocha", + "prepublish": "node ./scripts/generateTestFiles.js" + }, + "dependencies": { + "mocha": "~1.21.4", + "sinon": "^1.10.3", + "underscore": "~1.6.0" + }, + "devDependencies": { + "jshint": "~2.4.4" + }, + "browser": { + "mocha": false + }, + "gitHead": "481b36c735b3a7c6ca78636037dfc41d9e051ac3", + "homepage": "https://github.com/promises-aplus/promises-tests", + "_id": "promises-aplus-tests@2.1.1", + "_shasum": "43fc528beb150d14c5918e687480dc088bd3078e", + "_from": "promises-aplus-tests@>=2.1.0 <3.0.0", + "_npmVersion": "2.7.6", + "_nodeVersion": "1.7.1", + "_npmUser": { + "name": "domenic", + "email": "domenic@domenicdenicola.com" + }, + "maintainers": [ + { + "name": "domenic", + "email": "domenic@domenicdenicola.com" + } + ], + "dist": { + "shasum": "43fc528beb150d14c5918e687480dc088bd3078e", + "tarball": "http://registry.npmjs.org/promises-aplus-tests/-/promises-aplus-tests-2.1.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/promises-aplus-tests/-/promises-aplus-tests-2.1.1.tgz" +} diff --git a/node_modules/promises-aplus-tests/scripts/generateTestFiles.js b/node_modules/promises-aplus-tests/scripts/generateTestFiles.js new file mode 100644 index 0000000..84d7c40 --- /dev/null +++ b/node_modules/promises-aplus-tests/scripts/generateTestFiles.js @@ -0,0 +1,24 @@ +"use strict"; +var fs = require("fs"); +var path = require("path"); + +var testsDir = path.resolve(__dirname, "../lib/tests"); +var testDirFiles = fs.readdirSync(testsDir); + +var outFile = fs.createWriteStream(path.resolve(__dirname, "../lib/testFiles.js"), { encoding: "utf-8" }); + +testDirFiles.forEach(function (file) { + if (path.extname(file) !== ".js") { + return; + } + + outFile.write("require(\"./"); + outFile.write("tests/" + path.basename(file, ".js")); + outFile.write("\");\n"); +}); + +outFile.end(function (err) { + if (err) { + throw err; + } +}); diff --git a/node_modules/promises-aplus-tests/test/getMochaOptsTest.js b/node_modules/promises-aplus-tests/test/getMochaOptsTest.js new file mode 100644 index 0000000..8b7f175 --- /dev/null +++ b/node_modules/promises-aplus-tests/test/getMochaOptsTest.js @@ -0,0 +1,32 @@ +"use strict"; + +var assert = require("assert"); +var getMochaOpts = require("../lib/getMochaOpts"); + +describe("getMochaOpts", function() { + function test(argsString, expectedOpts) { + var opts = getMochaOpts(argsString.split(" ")); + assert.deepEqual(opts, expectedOpts); + } + + it("parses the provided options to an object", function () { + test("--reporter spec --ui bdd", { + reporter: "spec", + ui: "bdd" + }); + }); + + it("sets the value for no-arg options to true", function () { + test("--bail --ui bdd", { + bail: true, + ui: "bdd" + }); + }); + + it("supports no-arg options as the last option", function () { + test("--reporter spec --bail", { + reporter: "spec", + bail: true + }); + }); +}); diff --git a/node_modules/rimraf/LICENSE b/node_modules/rimraf/LICENSE new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/node_modules/rimraf/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/rimraf/README.md b/node_modules/rimraf/README.md new file mode 100644 index 0000000..de7c4bb --- /dev/null +++ b/node_modules/rimraf/README.md @@ -0,0 +1,43 @@ +[![Build Status](https://travis-ci.org/isaacs/rimraf.svg?branch=master)](https://travis-ci.org/isaacs/rimraf) [![Dependency Status](https://david-dm.org/isaacs/rimraf.svg)](https://david-dm.org/isaacs/rimraf) [![devDependency Status](https://david-dm.org/isaacs/rimraf/dev-status.svg)](https://david-dm.org/isaacs/rimraf#info=devDependencies) + +The [UNIX command](http://en.wikipedia.org/wiki/Rm_(Unix)) `rm -rf` for node. + +Install with `npm install rimraf`, or just drop rimraf.js somewhere. + +## API + +`rimraf(f, [opts], callback)` + +The first parameter will be interpreted as a globbing pattern for files. If you +want to disable globbing you can do so with `opts.disableGlob` (defaults to +`false`). This might be handy, for instance, if you have filenames that contain +globbing wildcard characters. + +The callback will be called with an error if there is one. Certain +errors are handled for you: + +* Windows: `EBUSY` and `ENOTEMPTY` - rimraf will back off a maximum of + `opts.maxBusyTries` times before giving up, adding 100ms of wait + between each attempt. The default `maxBusyTries` is 3. +* `ENOENT` - If the file doesn't exist, rimraf will return + successfully, since your desired outcome is already the case. +* `EMFILE` - Since `readdir` requires opening a file descriptor, it's + possible to hit `EMFILE` if too many file descriptors are in use. + In the sync case, there's nothing to be done for this. But in the + async case, rimraf will gradually back off with timeouts up to + `opts.emfileWait` ms, which defaults to 1000. + +## rimraf.sync + +It can remove stuff synchronously, too. But that's not so good. Use +the async API. It's better. + +## CLI + +If installed with `npm install rimraf -g` it can be used as a global +command `rimraf [ ...]` which is useful for cross platform support. + +## mkdirp + +If you need to create a directory recursively, check out +[mkdirp](https://github.com/substack/node-mkdirp). diff --git a/node_modules/rimraf/bin.js b/node_modules/rimraf/bin.js new file mode 100755 index 0000000..1bd5a0d --- /dev/null +++ b/node_modules/rimraf/bin.js @@ -0,0 +1,40 @@ +#!/usr/bin/env node + +var rimraf = require('./') + +var help = false +var dashdash = false +var args = process.argv.slice(2).filter(function(arg) { + if (dashdash) + return !!arg + else if (arg === '--') + dashdash = true + else if (arg.match(/^(-+|\/)(h(elp)?|\?)$/)) + help = true + else + return !!arg +}); + +if (help || args.length === 0) { + // If they didn't ask for help, then this is not a "success" + var log = help ? console.log : console.error + log('Usage: rimraf [ ...]') + log('') + log(' Deletes all files and folders at "path" recursively.') + log('') + log('Options:') + log('') + log(' -h, --help Display this usage info') + process.exit(help ? 0 : 1) +} else + go(0) + +function go (n) { + if (n >= args.length) + return + rimraf(args[n], function (er) { + if (er) + throw er + go(n+1) + }) +} diff --git a/node_modules/rimraf/node_modules/glob/LICENSE b/node_modules/rimraf/node_modules/glob/LICENSE new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/rimraf/node_modules/glob/README.md b/node_modules/rimraf/node_modules/glob/README.md new file mode 100644 index 0000000..063cf95 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/README.md @@ -0,0 +1,377 @@ +[![Build Status](https://travis-ci.org/isaacs/node-glob.svg?branch=master)](https://travis-ci.org/isaacs/node-glob/) [![Dependency Status](https://david-dm.org/isaacs/node-glob.svg)](https://david-dm.org/isaacs/node-glob) [![devDependency Status](https://david-dm.org/isaacs/node-glob/dev-status.svg)](https://david-dm.org/isaacs/node-glob#info=devDependencies) [![optionalDependency Status](https://david-dm.org/isaacs/node-glob/optional-status.svg)](https://david-dm.org/isaacs/node-glob#info=optionalDependencies) + +# Glob + +Match files using the patterns the shell uses, like stars and stuff. + +This is a glob implementation in JavaScript. It uses the `minimatch` +library to do its matching. + +![](oh-my-glob.gif) + +## Usage + +```javascript +var glob = require("glob") + +// options is optional +glob("**/*.js", options, function (er, files) { + // files is an array of filenames. + // If the `nonull` option is set, and nothing + // was found, then files is ["**/*.js"] + // er is an error object or null. +}) +``` + +## Glob Primer + +"Globs" are the patterns you type when you do stuff like `ls *.js` on +the command line, or put `build/*` in a `.gitignore` file. + +Before parsing the path part patterns, braced sections are expanded +into a set. Braced sections start with `{` and end with `}`, with any +number of comma-delimited sections within. Braced sections may contain +slash characters, so `a{/b/c,bcd}` would expand into `a/b/c` and `abcd`. + +The following characters have special magic meaning when used in a +path portion: + +* `*` Matches 0 or more characters in a single path portion +* `?` Matches 1 character +* `[...]` Matches a range of characters, similar to a RegExp range. + If the first character of the range is `!` or `^` then it matches + any character not in the range. +* `!(pattern|pattern|pattern)` Matches anything that does not match + any of the patterns provided. +* `?(pattern|pattern|pattern)` Matches zero or one occurrence of the + patterns provided. +* `+(pattern|pattern|pattern)` Matches one or more occurrences of the + patterns provided. +* `*(a|b|c)` Matches zero or more occurrences of the patterns provided +* `@(pattern|pat*|pat?erN)` Matches exactly one of the patterns + provided +* `**` If a "globstar" is alone in a path portion, then it matches + zero or more directories and subdirectories searching for matches. + It does not crawl symlinked directories. + +### Dots + +If a file or directory path portion has a `.` as the first character, +then it will not match any glob pattern unless that pattern's +corresponding path part also has a `.` as its first character. + +For example, the pattern `a/.*/c` would match the file at `a/.b/c`. +However the pattern `a/*/c` would not, because `*` does not start with +a dot character. + +You can make glob treat dots as normal characters by setting +`dot:true` in the options. + +### Basename Matching + +If you set `matchBase:true` in the options, and the pattern has no +slashes in it, then it will seek for any file anywhere in the tree +with a matching basename. For example, `*.js` would match +`test/simple/basic.js`. + +### Negation + +The intent for negation would be for a pattern starting with `!` to +match everything that *doesn't* match the supplied pattern. However, +the implementation is weird, and for the time being, this should be +avoided. The behavior is deprecated in version 5, and will be removed +entirely in version 6. + +### Empty Sets + +If no matching files are found, then an empty array is returned. This +differs from the shell, where the pattern itself is returned. For +example: + + $ echo a*s*d*f + a*s*d*f + +To get the bash-style behavior, set the `nonull:true` in the options. + +### See Also: + +* `man sh` +* `man bash` (Search for "Pattern Matching") +* `man 3 fnmatch` +* `man 5 gitignore` +* [minimatch documentation](https://github.com/isaacs/minimatch) + +## glob.hasMagic(pattern, [options]) + +Returns `true` if there are any special characters in the pattern, and +`false` otherwise. + +Note that the options affect the results. If `noext:true` is set in +the options object, then `+(a|b)` will not be considered a magic +pattern. If the pattern has a brace expansion, like `a/{b/c,x/y}` +then that is considered magical, unless `nobrace:true` is set in the +options. + +## glob(pattern, [options], cb) + +* `pattern` {String} Pattern to be matched +* `options` {Object} +* `cb` {Function} + * `err` {Error | null} + * `matches` {Array} filenames found matching the pattern + +Perform an asynchronous glob search. + +## glob.sync(pattern, [options]) + +* `pattern` {String} Pattern to be matched +* `options` {Object} +* return: {Array} filenames found matching the pattern + +Perform a synchronous glob search. + +## Class: glob.Glob + +Create a Glob object by instantiating the `glob.Glob` class. + +```javascript +var Glob = require("glob").Glob +var mg = new Glob(pattern, options, cb) +``` + +It's an EventEmitter, and starts walking the filesystem to find matches +immediately. + +### new glob.Glob(pattern, [options], [cb]) + +* `pattern` {String} pattern to search for +* `options` {Object} +* `cb` {Function} Called when an error occurs, or matches are found + * `err` {Error | null} + * `matches` {Array} filenames found matching the pattern + +Note that if the `sync` flag is set in the options, then matches will +be immediately available on the `g.found` member. + +### Properties + +* `minimatch` The minimatch object that the glob uses. +* `options` The options object passed in. +* `aborted` Boolean which is set to true when calling `abort()`. There + is no way at this time to continue a glob search after aborting, but + you can re-use the statCache to avoid having to duplicate syscalls. +* `cache` Convenience object. Each field has the following possible + values: + * `false` - Path does not exist + * `true` - Path exists + * `'DIR'` - Path exists, and is not a directory + * `'FILE'` - Path exists, and is a directory + * `[file, entries, ...]` - Path exists, is a directory, and the + array value is the results of `fs.readdir` +* `statCache` Cache of `fs.stat` results, to prevent statting the same + path multiple times. +* `symlinks` A record of which paths are symbolic links, which is + relevant in resolving `**` patterns. +* `realpathCache` An optional object which is passed to `fs.realpath` + to minimize unnecessary syscalls. It is stored on the instantiated + Glob object, and may be re-used. + +### Events + +* `end` When the matching is finished, this is emitted with all the + matches found. If the `nonull` option is set, and no match was found, + then the `matches` list contains the original pattern. The matches + are sorted, unless the `nosort` flag is set. +* `match` Every time a match is found, this is emitted with the matched. +* `error` Emitted when an unexpected error is encountered, or whenever + any fs error occurs if `options.strict` is set. +* `abort` When `abort()` is called, this event is raised. + +### Methods + +* `pause` Temporarily stop the search +* `resume` Resume the search +* `abort` Stop the search forever + +### Options + +All the options that can be passed to Minimatch can also be passed to +Glob to change pattern matching behavior. Also, some have been added, +or have glob-specific ramifications. + +All options are false by default, unless otherwise noted. + +All options are added to the Glob object, as well. + +If you are running many `glob` operations, you can pass a Glob object +as the `options` argument to a subsequent operation to shortcut some +`stat` and `readdir` calls. At the very least, you may pass in shared +`symlinks`, `statCache`, `realpathCache`, and `cache` options, so that +parallel glob operations will be sped up by sharing information about +the filesystem. + +* `cwd` The current working directory in which to search. Defaults + to `process.cwd()`. +* `root` The place where patterns starting with `/` will be mounted + onto. Defaults to `path.resolve(options.cwd, "/")` (`/` on Unix + systems, and `C:\` or some such on Windows.) +* `dot` Include `.dot` files in normal matches and `globstar` matches. + Note that an explicit dot in a portion of the pattern will always + match dot files. +* `nomount` By default, a pattern starting with a forward-slash will be + "mounted" onto the root setting, so that a valid filesystem path is + returned. Set this flag to disable that behavior. +* `mark` Add a `/` character to directory matches. Note that this + requires additional stat calls. +* `nosort` Don't sort the results. +* `stat` Set to true to stat *all* results. This reduces performance + somewhat, and is completely unnecessary, unless `readdir` is presumed + to be an untrustworthy indicator of file existence. +* `silent` When an unusual error is encountered when attempting to + read a directory, a warning will be printed to stderr. Set the + `silent` option to true to suppress these warnings. +* `strict` When an unusual error is encountered when attempting to + read a directory, the process will just continue on in search of + other matches. Set the `strict` option to raise an error in these + cases. +* `cache` See `cache` property above. Pass in a previously generated + cache object to save some fs calls. +* `statCache` A cache of results of filesystem information, to prevent + unnecessary stat calls. While it should not normally be necessary + to set this, you may pass the statCache from one glob() call to the + options object of another, if you know that the filesystem will not + change between calls. (See "Race Conditions" below.) +* `symlinks` A cache of known symbolic links. You may pass in a + previously generated `symlinks` object to save `lstat` calls when + resolving `**` matches. +* `sync` DEPRECATED: use `glob.sync(pattern, opts)` instead. +* `nounique` In some cases, brace-expanded patterns can result in the + same file showing up multiple times in the result set. By default, + this implementation prevents duplicates in the result set. Set this + flag to disable that behavior. +* `nonull` Set to never return an empty set, instead returning a set + containing the pattern itself. This is the default in glob(3). +* `debug` Set to enable debug logging in minimatch and glob. +* `nobrace` Do not expand `{a,b}` and `{1..3}` brace sets. +* `noglobstar` Do not match `**` against multiple filenames. (Ie, + treat it as a normal `*` instead.) +* `noext` Do not match `+(a|b)` "extglob" patterns. +* `nocase` Perform a case-insensitive match. Note: on + case-insensitive filesystems, non-magic patterns will match by + default, since `stat` and `readdir` will not raise errors. +* `matchBase` Perform a basename-only match if the pattern does not + contain any slash characters. That is, `*.js` would be treated as + equivalent to `**/*.js`, matching all js files in all directories. +* `nodir` Do not match directories, only files. (Note: to match + *only* directories, simply put a `/` at the end of the pattern.) +* `ignore` Add a pattern or an array of patterns to exclude matches. +* `follow` Follow symlinked directories when expanding `**` patterns. + Note that this can result in a lot of duplicate references in the + presence of cyclic links. +* `realpath` Set to true to call `fs.realpath` on all of the results. + In the case of a symlink that cannot be resolved, the full absolute + path to the matched entry is returned (though it will usually be a + broken symlink) +* `nonegate` Suppress deprecated `negate` behavior. (See below.) + Default=true +* `nocomment` Suppress deprecated `comment` behavior. (See below.) + Default=true + +## Comparisons to other fnmatch/glob implementations + +While strict compliance with the existing standards is a worthwhile +goal, some discrepancies exist between node-glob and other +implementations, and are intentional. + +The double-star character `**` is supported by default, unless the +`noglobstar` flag is set. This is supported in the manner of bsdglob +and bash 4.3, where `**` only has special significance if it is the only +thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but +`a/**b` will not. + +Note that symlinked directories are not crawled as part of a `**`, +though their contents may match against subsequent portions of the +pattern. This prevents infinite loops and duplicates and the like. + +If an escaped pattern has no matches, and the `nonull` flag is set, +then glob returns the pattern as-provided, rather than +interpreting the character escapes. For example, +`glob.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than +`"*a?"`. This is akin to setting the `nullglob` option in bash, except +that it does not resolve escaped pattern characters. + +If brace expansion is not disabled, then it is performed before any +other interpretation of the glob pattern. Thus, a pattern like +`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded +**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are +checked for validity. Since those two are valid, matching proceeds. + +### Comments and Negation + +**Note**: In version 5 of this module, negation and comments are +**disabled** by default. You can explicitly set `nonegate:false` or +`nocomment:false` to re-enable them. They are going away entirely in +version 6. + +The intent for negation would be for a pattern starting with `!` to +match everything that *doesn't* match the supplied pattern. However, +the implementation is weird. It is better to use the `ignore` option +to set a pattern or set of patterns to exclude from matches. If you +want the "everything except *x*" type of behavior, you can use `**` as +the main pattern, and set an `ignore` for the things to exclude. + +The comments feature is added in minimatch, primarily to more easily +support use cases like ignore files, where a `#` at the start of a +line makes the pattern "empty". However, in the context of a +straightforward filesystem globber, "comments" don't make much sense. + +## Windows + +**Please only use forward-slashes in glob expressions.** + +Though windows uses either `/` or `\` as its path separator, only `/` +characters are used by this glob implementation. You must use +forward-slashes **only** in glob expressions. Back-slashes will always +be interpreted as escape characters, not path separators. + +Results from absolute patterns such as `/foo/*` are mounted onto the +root setting using `path.join`. On windows, this will by default result +in `/foo/*` matching `C:\foo\bar.txt`. + +## Race Conditions + +Glob searching, by its very nature, is susceptible to race conditions, +since it relies on directory walking and such. + +As a result, it is possible that a file that exists when glob looks for +it may have been deleted or modified by the time it returns the result. + +As part of its internal implementation, this program caches all stat +and readdir calls that it makes, in order to cut down on system +overhead. However, this also makes it even more susceptible to races, +especially if the cache or statCache objects are reused between glob +calls. + +Users are thus advised not to use a glob result as a guarantee of +filesystem state in the face of rapid changes. For the vast majority +of operations, this is never a problem. + +## Contributing + +Any change to behavior (including bugfixes) must come with a test. + +Patches that fail tests or reduce performance will be rejected. + +``` +# to run tests +npm test + +# to re-generate test fixtures +npm run test-regen + +# to benchmark against bash/zsh +npm run bench + +# to profile javascript +npm run prof +``` diff --git a/node_modules/rimraf/node_modules/glob/common.js b/node_modules/rimraf/node_modules/glob/common.js new file mode 100644 index 0000000..e36a631 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/common.js @@ -0,0 +1,245 @@ +exports.alphasort = alphasort +exports.alphasorti = alphasorti +exports.setopts = setopts +exports.ownProp = ownProp +exports.makeAbs = makeAbs +exports.finish = finish +exports.mark = mark +exports.isIgnored = isIgnored +exports.childrenIgnored = childrenIgnored + +function ownProp (obj, field) { + return Object.prototype.hasOwnProperty.call(obj, field) +} + +var path = require("path") +var minimatch = require("minimatch") +var isAbsolute = require("path-is-absolute") +var Minimatch = minimatch.Minimatch + +function alphasorti (a, b) { + return a.toLowerCase().localeCompare(b.toLowerCase()) +} + +function alphasort (a, b) { + return a.localeCompare(b) +} + +function setupIgnores (self, options) { + self.ignore = options.ignore || [] + + if (!Array.isArray(self.ignore)) + self.ignore = [self.ignore] + + if (self.ignore.length) { + self.ignore = self.ignore.map(ignoreMap) + } +} + +function ignoreMap (pattern) { + var gmatcher = null + if (pattern.slice(-3) === '/**') { + var gpattern = pattern.replace(/(\/\*\*)+$/, '') + gmatcher = new Minimatch(gpattern) + } + + return { + matcher: new Minimatch(pattern), + gmatcher: gmatcher + } +} + +function setopts (self, pattern, options) { + if (!options) + options = {} + + // base-matching: just use globstar for that. + if (options.matchBase && -1 === pattern.indexOf("/")) { + if (options.noglobstar) { + throw new Error("base matching requires globstar") + } + pattern = "**/" + pattern + } + + self.silent = !!options.silent + self.pattern = pattern + self.strict = options.strict !== false + self.realpath = !!options.realpath + self.realpathCache = options.realpathCache || Object.create(null) + self.follow = !!options.follow + self.dot = !!options.dot + self.mark = !!options.mark + self.nodir = !!options.nodir + if (self.nodir) + self.mark = true + self.sync = !!options.sync + self.nounique = !!options.nounique + self.nonull = !!options.nonull + self.nosort = !!options.nosort + self.nocase = !!options.nocase + self.stat = !!options.stat + self.noprocess = !!options.noprocess + + self.maxLength = options.maxLength || Infinity + self.cache = options.cache || Object.create(null) + self.statCache = options.statCache || Object.create(null) + self.symlinks = options.symlinks || Object.create(null) + + setupIgnores(self, options) + + self.changedCwd = false + var cwd = process.cwd() + if (!ownProp(options, "cwd")) + self.cwd = cwd + else { + self.cwd = options.cwd + self.changedCwd = path.resolve(options.cwd) !== cwd + } + + self.root = options.root || path.resolve(self.cwd, "/") + self.root = path.resolve(self.root) + if (process.platform === "win32") + self.root = self.root.replace(/\\/g, "/") + + self.nomount = !!options.nomount + + // disable comments and negation unless the user explicitly + // passes in false as the option. + options.nonegate = options.nonegate === false ? false : true + options.nocomment = options.nocomment === false ? false : true + deprecationWarning(options) + + self.minimatch = new Minimatch(pattern, options) + self.options = self.minimatch.options +} + +// TODO(isaacs): remove entirely in v6 +// exported to reset in tests +exports.deprecationWarned +function deprecationWarning(options) { + if (!options.nonegate || !options.nocomment) { + if (process.noDeprecation !== true && !exports.deprecationWarned) { + var msg = 'glob WARNING: comments and negation will be disabled in v6' + if (process.throwDeprecation) + throw new Error(msg) + else if (process.traceDeprecation) + console.trace(msg) + else + console.error(msg) + + exports.deprecationWarned = true + } + } +} + +function finish (self) { + var nou = self.nounique + var all = nou ? [] : Object.create(null) + + for (var i = 0, l = self.matches.length; i < l; i ++) { + var matches = self.matches[i] + if (!matches || Object.keys(matches).length === 0) { + if (self.nonull) { + // do like the shell, and spit out the literal glob + var literal = self.minimatch.globSet[i] + if (nou) + all.push(literal) + else + all[literal] = true + } + } else { + // had matches + var m = Object.keys(matches) + if (nou) + all.push.apply(all, m) + else + m.forEach(function (m) { + all[m] = true + }) + } + } + + if (!nou) + all = Object.keys(all) + + if (!self.nosort) + all = all.sort(self.nocase ? alphasorti : alphasort) + + // at *some* point we statted all of these + if (self.mark) { + for (var i = 0; i < all.length; i++) { + all[i] = self._mark(all[i]) + } + if (self.nodir) { + all = all.filter(function (e) { + return !(/\/$/.test(e)) + }) + } + } + + if (self.ignore.length) + all = all.filter(function(m) { + return !isIgnored(self, m) + }) + + self.found = all +} + +function mark (self, p) { + var abs = makeAbs(self, p) + var c = self.cache[abs] + var m = p + if (c) { + var isDir = c === 'DIR' || Array.isArray(c) + var slash = p.slice(-1) === '/' + + if (isDir && !slash) + m += '/' + else if (!isDir && slash) + m = m.slice(0, -1) + + if (m !== p) { + var mabs = makeAbs(self, m) + self.statCache[mabs] = self.statCache[abs] + self.cache[mabs] = self.cache[abs] + } + } + + return m +} + +// lotta situps... +function makeAbs (self, f) { + var abs = f + if (f.charAt(0) === '/') { + abs = path.join(self.root, f) + } else if (isAbsolute(f) || f === '') { + abs = f + } else if (self.changedCwd) { + abs = path.resolve(self.cwd, f) + } else { + abs = path.resolve(f) + } + return abs +} + + +// Return true, if pattern ends with globstar '**', for the accompanying parent directory. +// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents +function isIgnored (self, path) { + if (!self.ignore.length) + return false + + return self.ignore.some(function(item) { + return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path)) + }) +} + +function childrenIgnored (self, path) { + if (!self.ignore.length) + return false + + return self.ignore.some(function(item) { + return !!(item.gmatcher && item.gmatcher.match(path)) + }) +} diff --git a/node_modules/rimraf/node_modules/glob/glob.js b/node_modules/rimraf/node_modules/glob/glob.js new file mode 100644 index 0000000..022d2ac --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/glob.js @@ -0,0 +1,752 @@ +// Approach: +// +// 1. Get the minimatch set +// 2. For each pattern in the set, PROCESS(pattern, false) +// 3. Store matches per-set, then uniq them +// +// PROCESS(pattern, inGlobStar) +// Get the first [n] items from pattern that are all strings +// Join these together. This is PREFIX. +// If there is no more remaining, then stat(PREFIX) and +// add to matches if it succeeds. END. +// +// If inGlobStar and PREFIX is symlink and points to dir +// set ENTRIES = [] +// else readdir(PREFIX) as ENTRIES +// If fail, END +// +// with ENTRIES +// If pattern[n] is GLOBSTAR +// // handle the case where the globstar match is empty +// // by pruning it out, and testing the resulting pattern +// PROCESS(pattern[0..n] + pattern[n+1 .. $], false) +// // handle other cases. +// for ENTRY in ENTRIES (not dotfiles) +// // attach globstar + tail onto the entry +// // Mark that this entry is a globstar match +// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true) +// +// else // not globstar +// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot) +// Test ENTRY against pattern[n] +// If fails, continue +// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $]) +// +// Caveat: +// Cache all stats and readdirs results to minimize syscall. Since all +// we ever care about is existence and directory-ness, we can just keep +// `true` for files, and [children,...] for directories, or `false` for +// things that don't exist. + +module.exports = glob + +var fs = require('fs') +var minimatch = require('minimatch') +var Minimatch = minimatch.Minimatch +var inherits = require('inherits') +var EE = require('events').EventEmitter +var path = require('path') +var assert = require('assert') +var isAbsolute = require('path-is-absolute') +var globSync = require('./sync.js') +var common = require('./common.js') +var alphasort = common.alphasort +var alphasorti = common.alphasorti +var setopts = common.setopts +var ownProp = common.ownProp +var inflight = require('inflight') +var util = require('util') +var childrenIgnored = common.childrenIgnored +var isIgnored = common.isIgnored + +var once = require('once') + +function glob (pattern, options, cb) { + if (typeof options === 'function') cb = options, options = {} + if (!options) options = {} + + if (options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return globSync(pattern, options) + } + + return new Glob(pattern, options, cb) +} + +glob.sync = globSync +var GlobSync = glob.GlobSync = globSync.GlobSync + +// old api surface +glob.glob = glob + +glob.hasMagic = function (pattern, options_) { + var options = util._extend({}, options_) + options.noprocess = true + + var g = new Glob(pattern, options) + var set = g.minimatch.set + if (set.length > 1) + return true + + for (var j = 0; j < set[0].length; j++) { + if (typeof set[0][j] !== 'string') + return true + } + + return false +} + +glob.Glob = Glob +inherits(Glob, EE) +function Glob (pattern, options, cb) { + if (typeof options === 'function') { + cb = options + options = null + } + + if (options && options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return new GlobSync(pattern, options) + } + + if (!(this instanceof Glob)) + return new Glob(pattern, options, cb) + + setopts(this, pattern, options) + this._didRealPath = false + + // process each pattern in the minimatch set + var n = this.minimatch.set.length + + // The matches are stored as {: true,...} so that + // duplicates are automagically pruned. + // Later, we do an Object.keys() on these. + // Keep them as a list so we can fill in when nonull is set. + this.matches = new Array(n) + + if (typeof cb === 'function') { + cb = once(cb) + this.on('error', cb) + this.on('end', function (matches) { + cb(null, matches) + }) + } + + var self = this + var n = this.minimatch.set.length + this._processing = 0 + this.matches = new Array(n) + + this._emitQueue = [] + this._processQueue = [] + this.paused = false + + if (this.noprocess) + return this + + if (n === 0) + return done() + + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false, done) + } + + function done () { + --self._processing + if (self._processing <= 0) + self._finish() + } +} + +Glob.prototype._finish = function () { + assert(this instanceof Glob) + if (this.aborted) + return + + if (this.realpath && !this._didRealpath) + return this._realpath() + + common.finish(this) + this.emit('end', this.found) +} + +Glob.prototype._realpath = function () { + if (this._didRealpath) + return + + this._didRealpath = true + + var n = this.matches.length + if (n === 0) + return this._finish() + + var self = this + for (var i = 0; i < this.matches.length; i++) + this._realpathSet(i, next) + + function next () { + if (--n === 0) + self._finish() + } +} + +Glob.prototype._realpathSet = function (index, cb) { + var matchset = this.matches[index] + if (!matchset) + return cb() + + var found = Object.keys(matchset) + var self = this + var n = found.length + + if (n === 0) + return cb() + + var set = this.matches[index] = Object.create(null) + found.forEach(function (p, i) { + // If there's a problem with the stat, then it means that + // one or more of the links in the realpath couldn't be + // resolved. just return the abs value in that case. + p = self._makeAbs(p) + fs.realpath(p, self.realpathCache, function (er, real) { + if (!er) + set[real] = true + else if (er.syscall === 'stat') + set[p] = true + else + self.emit('error', er) // srsly wtf right here + + if (--n === 0) { + self.matches[index] = set + cb() + } + }) + }) +} + +Glob.prototype._mark = function (p) { + return common.mark(this, p) +} + +Glob.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) +} + +Glob.prototype.abort = function () { + this.aborted = true + this.emit('abort') +} + +Glob.prototype.pause = function () { + if (!this.paused) { + this.paused = true + this.emit('pause') + } +} + +Glob.prototype.resume = function () { + if (this.paused) { + this.emit('resume') + this.paused = false + if (this._emitQueue.length) { + var eq = this._emitQueue.slice(0) + this._emitQueue.length = 0 + for (var i = 0; i < eq.length; i ++) { + var e = eq[i] + this._emitMatch(e[0], e[1]) + } + } + if (this._processQueue.length) { + var pq = this._processQueue.slice(0) + this._processQueue.length = 0 + for (var i = 0; i < pq.length; i ++) { + var p = pq[i] + this._processing-- + this._process(p[0], p[1], p[2], p[3]) + } + } + } +} + +Glob.prototype._process = function (pattern, index, inGlobStar, cb) { + assert(this instanceof Glob) + assert(typeof cb === 'function') + + if (this.aborted) + return + + this._processing++ + if (this.paused) { + this._processQueue.push([pattern, index, inGlobStar, cb]) + return + } + + //console.error('PROCESS %d', this._processing, pattern) + + // Get the first [n] parts of pattern that are all strings. + var n = 0 + while (typeof pattern[n] === 'string') { + n ++ + } + // now n is the index of the first one that is *not* a string. + + // see if there's anything else + var prefix + switch (n) { + // if not, then this is rather simple + case pattern.length: + this._processSimple(pattern.join('/'), index, cb) + return + + case 0: + // pattern *starts* with some non-trivial item. + // going to readdir(cwd), but not include the prefix in matches. + prefix = null + break + + default: + // pattern has some string bits in the front. + // whatever it starts with, whether that's 'absolute' like /foo/bar, + // or 'relative' like '../baz' + prefix = pattern.slice(0, n).join('/') + break + } + + var remain = pattern.slice(n) + + // get the list of entries. + var read + if (prefix === null) + read = '.' + else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { + if (!prefix || !isAbsolute(prefix)) + prefix = '/' + prefix + read = prefix + } else + read = prefix + + var abs = this._makeAbs(read) + + //if ignored, skip _processing + if (childrenIgnored(this, read)) + return cb() + + var isGlobStar = remain[0] === minimatch.GLOBSTAR + if (isGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb) + else + this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb) +} + +Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + }) +} + +Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { + + // if the abs isn't a dir, then nothing can match! + if (!entries) + return cb() + + // It will only match dot entries if it starts with a dot, or if + // dot is set. Stuff like @(.foo|.bar) isn't allowed. + var pn = remain[0] + var negate = !!this.minimatch.negate + var rawGlob = pn._glob + var dotOk = this.dot || rawGlob.charAt(0) === '.' + + var matchedEntries = [] + for (var i = 0; i < entries.length; i++) { + var e = entries[i] + if (e.charAt(0) !== '.' || dotOk) { + var m + if (negate && !prefix) { + m = !e.match(pn) + } else { + m = e.match(pn) + } + if (m) + matchedEntries.push(e) + } + } + + //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries) + + var len = matchedEntries.length + // If there are no matched entries, then nothing matches. + if (len === 0) + return cb() + + // if this is the last remaining pattern bit, then no need for + // an additional stat *unless* the user has specified mark or + // stat explicitly. We know they exist, since readdir returned + // them. + + if (remain.length === 1 && !this.mark && !this.stat) { + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + if (prefix) { + if (prefix !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + + if (e.charAt(0) === '/' && !this.nomount) { + e = path.join(this.root, e) + } + this._emitMatch(index, e) + } + // This was the last one, and no stats were needed + return cb() + } + + // now test all matched entries as stand-ins for that part + // of the pattern. + remain.shift() + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + var newPattern + if (prefix) { + if (prefix !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + this._process([e].concat(remain), index, inGlobStar, cb) + } + cb() +} + +Glob.prototype._emitMatch = function (index, e) { + if (this.aborted) + return + + if (this.matches[index][e]) + return + + if (isIgnored(this, e)) + return + + if (this.paused) { + this._emitQueue.push([index, e]) + return + } + + var abs = this._makeAbs(e) + + if (this.nodir) { + var c = this.cache[abs] + if (c === 'DIR' || Array.isArray(c)) + return + } + + if (this.mark) + e = this._mark(e) + + this.matches[index][e] = true + + var st = this.statCache[abs] + if (st) + this.emit('stat', e, st) + + this.emit('match', e) +} + +Glob.prototype._readdirInGlobStar = function (abs, cb) { + if (this.aborted) + return + + // follow all symlinked directories forever + // just proceed as if this is a non-globstar situation + if (this.follow) + return this._readdir(abs, false, cb) + + var lstatkey = 'lstat\0' + abs + var self = this + var lstatcb = inflight(lstatkey, lstatcb_) + + if (lstatcb) + fs.lstat(abs, lstatcb) + + function lstatcb_ (er, lstat) { + if (er) + return cb() + + var isSym = lstat.isSymbolicLink() + self.symlinks[abs] = isSym + + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && !lstat.isDirectory()) { + self.cache[abs] = 'FILE' + cb() + } else + self._readdir(abs, false, cb) + } +} + +Glob.prototype._readdir = function (abs, inGlobStar, cb) { + if (this.aborted) + return + + cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb) + if (!cb) + return + + //console.error('RD %j %j', +inGlobStar, abs) + if (inGlobStar && !ownProp(this.symlinks, abs)) + return this._readdirInGlobStar(abs, cb) + + if (ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (!c || c === 'FILE') + return cb() + + if (Array.isArray(c)) + return cb(null, c) + } + + var self = this + fs.readdir(abs, readdirCb(this, abs, cb)) +} + +function readdirCb (self, abs, cb) { + return function (er, entries) { + if (er) + self._readdirError(abs, er, cb) + else + self._readdirEntries(abs, entries, cb) + } +} + +Glob.prototype._readdirEntries = function (abs, entries, cb) { + if (this.aborted) + return + + // if we haven't asked to stat everything, then just + // assume that everything in there exists, so we can avoid + // having to stat it a second time. + if (!this.mark && !this.stat) { + for (var i = 0; i < entries.length; i ++) { + var e = entries[i] + if (abs === '/') + e = abs + e + else + e = abs + '/' + e + this.cache[e] = true + } + } + + this.cache[abs] = entries + return cb(null, entries) +} + +Glob.prototype._readdirError = function (f, er, cb) { + if (this.aborted) + return + + // handle errors, and cache the information + switch (er.code) { + case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 + case 'ENOTDIR': // totally normal. means it *does* exist. + this.cache[this._makeAbs(f)] = 'FILE' + break + + case 'ENOENT': // not terribly unusual + case 'ELOOP': + case 'ENAMETOOLONG': + case 'UNKNOWN': + this.cache[this._makeAbs(f)] = false + break + + default: // some unusual error. Treat as failure. + this.cache[this._makeAbs(f)] = false + if (this.strict) { + this.emit('error', er) + // If the error is handled, then we abort + // if not, we threw out of here + this.abort() + } + if (!this.silent) + console.error('glob error', er) + break + } + + return cb() +} + +Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + }) +} + + +Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { + //console.error('pgs2', prefix, remain[0], entries) + + // no entries means not a dir, so it can never have matches + // foo.txt/** doesn't match foo.txt + if (!entries) + return cb() + + // test without the globstar, and with every child both below + // and replacing the globstar. + var remainWithoutGlobStar = remain.slice(1) + var gspref = prefix ? [ prefix ] : [] + var noGlobStar = gspref.concat(remainWithoutGlobStar) + + // the noGlobStar pattern exits the inGlobStar state + this._process(noGlobStar, index, false, cb) + + var isSym = this.symlinks[abs] + var len = entries.length + + // If it's a symlink, and we're in a globstar, then stop + if (isSym && inGlobStar) + return cb() + + for (var i = 0; i < len; i++) { + var e = entries[i] + if (e.charAt(0) === '.' && !this.dot) + continue + + // these two cases enter the inGlobStar state + var instead = gspref.concat(entries[i], remainWithoutGlobStar) + this._process(instead, index, true, cb) + + var below = gspref.concat(entries[i], remain) + this._process(below, index, true, cb) + } + + cb() +} + +Glob.prototype._processSimple = function (prefix, index, cb) { + // XXX review this. Shouldn't it be doing the mounting etc + // before doing stat? kinda weird? + var self = this + this._stat(prefix, function (er, exists) { + self._processSimple2(prefix, index, er, exists, cb) + }) +} +Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) { + + //console.error('ps2', prefix, exists) + + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + // If it doesn't exist, then just mark the lack of results + if (!exists) + return cb() + + if (prefix && isAbsolute(prefix) && !this.nomount) { + var trail = /[\/\\]$/.test(prefix) + if (prefix.charAt(0) === '/') { + prefix = path.join(this.root, prefix) + } else { + prefix = path.resolve(this.root, prefix) + if (trail) + prefix += '/' + } + } + + if (process.platform === 'win32') + prefix = prefix.replace(/\\/g, '/') + + // Mark this as a match + this._emitMatch(index, prefix) + cb() +} + +// Returns either 'DIR', 'FILE', or false +Glob.prototype._stat = function (f, cb) { + var abs = this._makeAbs(f) + var needDir = f.slice(-1) === '/' + + if (f.length > this.maxLength) + return cb() + + if (!this.stat && ownProp(this.cache, abs)) { + var c = this.cache[abs] + + if (Array.isArray(c)) + c = 'DIR' + + // It exists, but maybe not how we need it + if (!needDir || c === 'DIR') + return cb(null, c) + + if (needDir && c === 'FILE') + return cb() + + // otherwise we have to stat, because maybe c=true + // if we know it exists, but not what it is. + } + + var exists + var stat = this.statCache[abs] + if (stat !== undefined) { + if (stat === false) + return cb(null, stat) + else { + var type = stat.isDirectory() ? 'DIR' : 'FILE' + if (needDir && type === 'FILE') + return cb() + else + return cb(null, type, stat) + } + } + + var self = this + var statcb = inflight('stat\0' + abs, lstatcb_) + if (statcb) + fs.lstat(abs, statcb) + + function lstatcb_ (er, lstat) { + if (lstat && lstat.isSymbolicLink()) { + // If it's a symlink, then treat it as the target, unless + // the target does not exist, then treat it as a file. + return fs.stat(abs, function (er, stat) { + if (er) + self._stat2(f, abs, null, lstat, cb) + else + self._stat2(f, abs, er, stat, cb) + }) + } else { + self._stat2(f, abs, er, lstat, cb) + } + } +} + +Glob.prototype._stat2 = function (f, abs, er, stat, cb) { + if (er) { + this.statCache[abs] = false + return cb() + } + + var needDir = f.slice(-1) === '/' + this.statCache[abs] = stat + + if (abs.slice(-1) === '/' && !stat.isDirectory()) + return cb(null, false, stat) + + var c = stat.isDirectory() ? 'DIR' : 'FILE' + this.cache[abs] = this.cache[abs] || c + + if (needDir && c !== 'DIR') + return cb() + + return cb(null, c, stat) +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inflight/.eslintrc b/node_modules/rimraf/node_modules/glob/node_modules/inflight/.eslintrc new file mode 100644 index 0000000..b7a1550 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inflight/.eslintrc @@ -0,0 +1,17 @@ +{ + "env" : { + "node" : true + }, + "rules" : { + "semi": [2, "never"], + "strict": 0, + "quotes": [1, "single", "avoid-escape"], + "no-use-before-define": 0, + "curly": 0, + "no-underscore-dangle": 0, + "no-lonely-if": 1, + "no-unused-vars": [2, {"vars" : "all", "args" : "after-used"}], + "no-mixed-requires": 0, + "space-infix-ops": 0 + } +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inflight/LICENSE b/node_modules/rimraf/node_modules/glob/node_modules/inflight/LICENSE new file mode 100644 index 0000000..05eeeb8 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inflight/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inflight/README.md b/node_modules/rimraf/node_modules/glob/node_modules/inflight/README.md new file mode 100644 index 0000000..6dc8929 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inflight/README.md @@ -0,0 +1,37 @@ +# inflight + +Add callbacks to requests in flight to avoid async duplication + +## USAGE + +```javascript +var inflight = require('inflight') + +// some request that does some stuff +function req(key, callback) { + // key is any random string. like a url or filename or whatever. + // + // will return either a falsey value, indicating that the + // request for this key is already in flight, or a new callback + // which when called will call all callbacks passed to inflightk + // with the same key + callback = inflight(key, callback) + + // If we got a falsey value back, then there's already a req going + if (!callback) return + + // this is where you'd fetch the url or whatever + // callback is also once()-ified, so it can safely be assigned + // to multiple events etc. First call wins. + setTimeout(function() { + callback(null, key) + }, 100) +} + +// only assigns a single setTimeout +// when it dings, all cbs get called +req('foo', cb1) +req('foo', cb2) +req('foo', cb3) +req('foo', cb4) +``` diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inflight/inflight.js b/node_modules/rimraf/node_modules/glob/node_modules/inflight/inflight.js new file mode 100644 index 0000000..8bc96cb --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inflight/inflight.js @@ -0,0 +1,44 @@ +var wrappy = require('wrappy') +var reqs = Object.create(null) +var once = require('once') + +module.exports = wrappy(inflight) + +function inflight (key, cb) { + if (reqs[key]) { + reqs[key].push(cb) + return null + } else { + reqs[key] = [cb] + return makeres(key) + } +} + +function makeres (key) { + return once(function RES () { + var cbs = reqs[key] + var len = cbs.length + var args = slice(arguments) + for (var i = 0; i < len; i++) { + cbs[i].apply(null, args) + } + if (cbs.length > len) { + // added more in the interim. + // de-zalgo, just in case, but don't call again. + cbs.splice(0, len) + process.nextTick(function () { + RES.apply(null, args) + }) + } else { + delete reqs[key] + } + }) +} + +function slice (args) { + var length = args.length + var array = [] + + for (var i = 0; i < length; i++) array[i] = args[i] + return array +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/LICENSE b/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/LICENSE new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/README.md b/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/README.md new file mode 100644 index 0000000..98eab25 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/README.md @@ -0,0 +1,36 @@ +# wrappy + +Callback wrapping utility + +## USAGE + +```javascript +var wrappy = require("wrappy") + +// var wrapper = wrappy(wrapperFunction) + +// make sure a cb is called only once +// See also: http://npm.im/once for this specific use case +var once = wrappy(function (cb) { + var called = false + return function () { + if (called) return + called = true + return cb.apply(this, arguments) + } +}) + +function printBoo () { + console.log('boo') +} +// has some rando property +printBoo.iAmBooPrinter = true + +var onlyPrintOnce = once(printBoo) + +onlyPrintOnce() // prints 'boo' +onlyPrintOnce() // does nothing + +// random property is retained! +assert.equal(onlyPrintOnce.iAmBooPrinter, true) +``` diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/package.json b/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/package.json new file mode 100644 index 0000000..5a07040 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/package.json @@ -0,0 +1,52 @@ +{ + "name": "wrappy", + "version": "1.0.1", + "description": "Callback wrapping utility", + "main": "wrappy.js", + "directories": { + "test": "test" + }, + "dependencies": {}, + "devDependencies": { + "tap": "^0.4.12" + }, + "scripts": { + "test": "tap test/*.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/npm/wrappy.git" + }, + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "license": "ISC", + "bugs": { + "url": "https://github.com/npm/wrappy/issues" + }, + "homepage": "https://github.com/npm/wrappy", + "gitHead": "006a8cbac6b99988315834c207896eed71fd069a", + "_id": "wrappy@1.0.1", + "_shasum": "1e65969965ccbc2db4548c6b84a6f2c5aedd4739", + "_from": "wrappy@>=1.0.0 <2.0.0", + "_npmVersion": "2.0.0", + "_nodeVersion": "0.10.31", + "_npmUser": { + "name": "isaacs", + "email": "i@izs.me" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + } + ], + "dist": { + "shasum": "1e65969965ccbc2db4548c6b84a6f2c5aedd4739", + "tarball": "http://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + }, + "_resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/test/basic.js b/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/test/basic.js new file mode 100644 index 0000000..5ed0fcd --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/test/basic.js @@ -0,0 +1,51 @@ +var test = require('tap').test +var wrappy = require('../wrappy.js') + +test('basic', function (t) { + function onceifier (cb) { + var called = false + return function () { + if (called) return + called = true + return cb.apply(this, arguments) + } + } + onceifier.iAmOnce = {} + var once = wrappy(onceifier) + t.equal(once.iAmOnce, onceifier.iAmOnce) + + var called = 0 + function boo () { + t.equal(called, 0) + called++ + } + // has some rando property + boo.iAmBoo = true + + var onlyPrintOnce = once(boo) + + onlyPrintOnce() // prints 'boo' + onlyPrintOnce() // does nothing + t.equal(called, 1) + + // random property is retained! + t.equal(onlyPrintOnce.iAmBoo, true) + + var logs = [] + var logwrap = wrappy(function (msg, cb) { + logs.push(msg + ' wrapping cb') + return function () { + logs.push(msg + ' before cb') + var ret = cb.apply(this, arguments) + logs.push(msg + ' after cb') + } + }) + + var c = logwrap('foo', function () { + t.same(logs, [ 'foo wrapping cb', 'foo before cb' ]) + }) + c() + t.same(logs, [ 'foo wrapping cb', 'foo before cb', 'foo after cb' ]) + + t.end() +}) diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/wrappy.js b/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/wrappy.js new file mode 100644 index 0000000..bb7e7d6 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy/wrappy.js @@ -0,0 +1,33 @@ +// Returns a wrapper function that returns a wrapped callback +// The wrapper function should do some stuff, and return a +// presumably different callback function. +// This makes sure that own properties are retained, so that +// decorations and such are not lost along the way. +module.exports = wrappy +function wrappy (fn, cb) { + if (fn && cb) return wrappy(fn)(cb) + + if (typeof fn !== 'function') + throw new TypeError('need wrapper function') + + Object.keys(fn).forEach(function (k) { + wrapper[k] = fn[k] + }) + + return wrapper + + function wrapper() { + var args = new Array(arguments.length) + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i] + } + var ret = fn.apply(this, args) + var cb = args[args.length-1] + if (typeof ret === 'function' && ret !== cb) { + Object.keys(cb).forEach(function (k) { + ret[k] = cb[k] + }) + } + return ret + } +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inflight/package.json b/node_modules/rimraf/node_modules/glob/node_modules/inflight/package.json new file mode 100644 index 0000000..fd0da2d --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inflight/package.json @@ -0,0 +1,61 @@ +{ + "name": "inflight", + "version": "1.0.4", + "description": "Add callbacks to requests in flight to avoid async duplication", + "main": "inflight.js", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + }, + "devDependencies": { + "tap": "^0.4.10" + }, + "scripts": { + "test": "tap test.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/isaacs/inflight.git" + }, + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "bugs": { + "url": "https://github.com/isaacs/inflight/issues" + }, + "homepage": "https://github.com/isaacs/inflight", + "license": "ISC", + "gitHead": "c7b5531d572a867064d4a1da9e013e8910b7d1ba", + "_id": "inflight@1.0.4", + "_shasum": "6cbb4521ebd51ce0ec0a936bfd7657ef7e9b172a", + "_from": "inflight@>=1.0.4 <2.0.0", + "_npmVersion": "2.1.3", + "_nodeVersion": "0.10.32", + "_npmUser": { + "name": "othiym23", + "email": "ogd@aoaioxxysz.net" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + }, + { + "name": "othiym23", + "email": "ogd@aoaioxxysz.net" + }, + { + "name": "iarna", + "email": "me@re-becca.org" + } + ], + "dist": { + "shasum": "6cbb4521ebd51ce0ec0a936bfd7657ef7e9b172a", + "tarball": "http://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inflight/test.js b/node_modules/rimraf/node_modules/glob/node_modules/inflight/test.js new file mode 100644 index 0000000..2bb75b3 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inflight/test.js @@ -0,0 +1,97 @@ +var test = require('tap').test +var inf = require('./inflight.js') + + +function req (key, cb) { + cb = inf(key, cb) + if (cb) setTimeout(function () { + cb(key) + cb(key) + }) + return cb +} + +test('basic', function (t) { + var calleda = false + var a = req('key', function (k) { + t.notOk(calleda) + calleda = true + t.equal(k, 'key') + if (calledb) t.end() + }) + t.ok(a, 'first returned cb function') + + var calledb = false + var b = req('key', function (k) { + t.notOk(calledb) + calledb = true + t.equal(k, 'key') + if (calleda) t.end() + }) + + t.notOk(b, 'second should get falsey inflight response') +}) + +test('timing', function (t) { + var expect = [ + 'method one', + 'start one', + 'end one', + 'two', + 'tick', + 'three' + ] + var i = 0 + + function log (m) { + t.equal(m, expect[i], m + ' === ' + expect[i]) + ++i + if (i === expect.length) + t.end() + } + + function method (name, cb) { + log('method ' + name) + process.nextTick(cb) + } + + var one = inf('foo', function () { + log('start one') + var three = inf('foo', function () { + log('three') + }) + if (three) method('three', three) + log('end one') + }) + + method('one', one) + + var two = inf('foo', function () { + log('two') + }) + if (two) method('one', two) + + process.nextTick(log.bind(null, 'tick')) +}) + +test('parameters', function (t) { + t.plan(8) + + var a = inf('key', function (first, second, third) { + t.equal(first, 1) + t.equal(second, 2) + t.equal(third, 3) + }) + t.ok(a, 'first returned cb function') + + var b = inf('key', function (first, second, third) { + t.equal(first, 1) + t.equal(second, 2) + t.equal(third, 3) + }) + t.notOk(b, 'second should get falsey inflight response') + + setTimeout(function () { + a(1, 2, 3) + }) +}) diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inherits/LICENSE b/node_modules/rimraf/node_modules/glob/node_modules/inherits/LICENSE new file mode 100644 index 0000000..dea3013 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inherits/LICENSE @@ -0,0 +1,16 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inherits/README.md b/node_modules/rimraf/node_modules/glob/node_modules/inherits/README.md new file mode 100644 index 0000000..b1c5665 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inherits/README.md @@ -0,0 +1,42 @@ +Browser-friendly inheritance fully compatible with standard node.js +[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor). + +This package exports standard `inherits` from node.js `util` module in +node environment, but also provides alternative browser-friendly +implementation through [browser +field](https://gist.github.com/shtylman/4339901). Alternative +implementation is a literal copy of standard one located in standalone +module to avoid requiring of `util`. It also has a shim for old +browsers with no `Object.create` support. + +While keeping you sure you are using standard `inherits` +implementation in node.js environment, it allows bundlers such as +[browserify](https://github.com/substack/node-browserify) to not +include full `util` package to your client code if all you need is +just `inherits` function. It worth, because browser shim for `util` +package is large and `inherits` is often the single function you need +from it. + +It's recommended to use this package instead of +`require('util').inherits` for any code that has chances to be used +not only in node.js but in browser too. + +## usage + +```js +var inherits = require('inherits'); +// then use exactly as the standard one +``` + +## note on version ~1.0 + +Version ~1.0 had completely different motivation and is not compatible +neither with 2.0 nor with standard node.js `inherits`. + +If you are using version ~1.0 and planning to switch to ~2.0, be +careful: + +* new version uses `super_` instead of `super` for referencing + superclass +* new version overwrites current prototype while old one preserves any + existing fields on it diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inherits/inherits.js b/node_modules/rimraf/node_modules/glob/node_modules/inherits/inherits.js new file mode 100644 index 0000000..29f5e24 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inherits/inherits.js @@ -0,0 +1 @@ +module.exports = require('util').inherits diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inherits/inherits_browser.js b/node_modules/rimraf/node_modules/glob/node_modules/inherits/inherits_browser.js new file mode 100644 index 0000000..c1e78a7 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inherits/inherits_browser.js @@ -0,0 +1,23 @@ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inherits/package.json b/node_modules/rimraf/node_modules/glob/node_modules/inherits/package.json new file mode 100644 index 0000000..bb245d2 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inherits/package.json @@ -0,0 +1,50 @@ +{ + "name": "inherits", + "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()", + "version": "2.0.1", + "keywords": [ + "inheritance", + "class", + "klass", + "oop", + "object-oriented", + "inherits", + "browser", + "browserify" + ], + "main": "./inherits.js", + "browser": "./inherits_browser.js", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/inherits.git" + }, + "license": "ISC", + "scripts": { + "test": "node test" + }, + "bugs": { + "url": "https://github.com/isaacs/inherits/issues" + }, + "_id": "inherits@2.0.1", + "dist": { + "shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1", + "tarball": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "_from": "inherits@>=2.0.0 <3.0.0", + "_npmVersion": "1.3.8", + "_npmUser": { + "name": "isaacs", + "email": "i@izs.me" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + } + ], + "directories": {}, + "_shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1", + "_resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "readme": "ERROR: No README data found!", + "homepage": "https://github.com/isaacs/inherits#readme" +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/inherits/test.js b/node_modules/rimraf/node_modules/glob/node_modules/inherits/test.js new file mode 100644 index 0000000..fc53012 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/inherits/test.js @@ -0,0 +1,25 @@ +var inherits = require('./inherits.js') +var assert = require('assert') + +function test(c) { + assert(c.constructor === Child) + assert(c.constructor.super_ === Parent) + assert(Object.getPrototypeOf(c) === Child.prototype) + assert(Object.getPrototypeOf(Object.getPrototypeOf(c)) === Parent.prototype) + assert(c instanceof Child) + assert(c instanceof Parent) +} + +function Child() { + Parent.call(this) + test(this) +} + +function Parent() {} + +inherits(Child, Parent) + +var c = new Child +test(c) + +console.log('ok') diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/LICENSE b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/LICENSE new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/README.md b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/README.md new file mode 100644 index 0000000..d458bc2 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/README.md @@ -0,0 +1,216 @@ +# minimatch + +A minimal matching utility. + +[![Build Status](https://secure.travis-ci.org/isaacs/minimatch.png)](http://travis-ci.org/isaacs/minimatch) + + +This is the matching library used internally by npm. + +It works by converting glob expressions into JavaScript `RegExp` +objects. + +## Usage + +```javascript +var minimatch = require("minimatch") + +minimatch("bar.foo", "*.foo") // true! +minimatch("bar.foo", "*.bar") // false! +minimatch("bar.foo", "*.+(bar|foo)", { debug: true }) // true, and noisy! +``` + +## Features + +Supports these glob features: + +* Brace Expansion +* Extended glob matching +* "Globstar" `**` matching + +See: + +* `man sh` +* `man bash` +* `man 3 fnmatch` +* `man 5 gitignore` + +## Minimatch Class + +Create a minimatch object by instanting the `minimatch.Minimatch` class. + +```javascript +var Minimatch = require("minimatch").Minimatch +var mm = new Minimatch(pattern, options) +``` + +### Properties + +* `pattern` The original pattern the minimatch object represents. +* `options` The options supplied to the constructor. +* `set` A 2-dimensional array of regexp or string expressions. + Each row in the + array corresponds to a brace-expanded pattern. Each item in the row + corresponds to a single path-part. For example, the pattern + `{a,b/c}/d` would expand to a set of patterns like: + + [ [ a, d ] + , [ b, c, d ] ] + + If a portion of the pattern doesn't have any "magic" in it + (that is, it's something like `"foo"` rather than `fo*o?`), then it + will be left as a string rather than converted to a regular + expression. + +* `regexp` Created by the `makeRe` method. A single regular expression + expressing the entire pattern. This is useful in cases where you wish + to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled. +* `negate` True if the pattern is negated. +* `comment` True if the pattern is a comment. +* `empty` True if the pattern is `""`. + +### Methods + +* `makeRe` Generate the `regexp` member if necessary, and return it. + Will return `false` if the pattern is invalid. +* `match(fname)` Return true if the filename matches the pattern, or + false otherwise. +* `matchOne(fileArray, patternArray, partial)` Take a `/`-split + filename, and match it against a single row in the `regExpSet`. This + method is mainly for internal use, but is exposed so that it can be + used by a glob-walker that needs to avoid excessive filesystem calls. + +All other methods are internal, and will be called as necessary. + +## Functions + +The top-level exported function has a `cache` property, which is an LRU +cache set to store 100 items. So, calling these methods repeatedly +with the same pattern and options will use the same Minimatch object, +saving the cost of parsing it multiple times. + +### minimatch(path, pattern, options) + +Main export. Tests a path against the pattern using the options. + +```javascript +var isJS = minimatch(file, "*.js", { matchBase: true }) +``` + +### minimatch.filter(pattern, options) + +Returns a function that tests its +supplied argument, suitable for use with `Array.filter`. Example: + +```javascript +var javascripts = fileList.filter(minimatch.filter("*.js", {matchBase: true})) +``` + +### minimatch.match(list, pattern, options) + +Match against the list of +files, in the style of fnmatch or glob. If nothing is matched, and +options.nonull is set, then return a list containing the pattern itself. + +```javascript +var javascripts = minimatch.match(fileList, "*.js", {matchBase: true})) +``` + +### minimatch.makeRe(pattern, options) + +Make a regular expression object from the pattern. + +## Options + +All options are `false` by default. + +### debug + +Dump a ton of stuff to stderr. + +### nobrace + +Do not expand `{a,b}` and `{1..3}` brace sets. + +### noglobstar + +Disable `**` matching against multiple folder names. + +### dot + +Allow patterns to match filenames starting with a period, even if +the pattern does not explicitly have a period in that spot. + +Note that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot` +is set. + +### noext + +Disable "extglob" style patterns like `+(a|b)`. + +### nocase + +Perform a case-insensitive match. + +### nonull + +When a match is not found by `minimatch.match`, return a list containing +the pattern itself if this option is set. When not set, an empty list +is returned if there are no matches. + +### matchBase + +If set, then patterns without slashes will be matched +against the basename of the path if it contains slashes. For example, +`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`. + +### nocomment + +Suppress the behavior of treating `#` at the start of a pattern as a +comment. + +### nonegate + +Suppress the behavior of treating a leading `!` character as negation. + +### flipNegate + +Returns from negate expressions the same as if they were not negated. +(Ie, true on a hit, false on a miss.) + + +## Comparisons to other fnmatch/glob implementations + +While strict compliance with the existing standards is a worthwhile +goal, some discrepancies exist between minimatch and other +implementations, and are intentional. + +If the pattern starts with a `!` character, then it is negated. Set the +`nonegate` flag to suppress this behavior, and treat leading `!` +characters normally. This is perhaps relevant if you wish to start the +pattern with a negative extglob pattern like `!(a|B)`. Multiple `!` +characters at the start of a pattern will negate the pattern multiple +times. + +If a pattern starts with `#`, then it is treated as a comment, and +will not match anything. Use `\#` to match a literal `#` at the +start of a line, or set the `nocomment` flag to suppress this behavior. + +The double-star character `**` is supported by default, unless the +`noglobstar` flag is set. This is supported in the manner of bsdglob +and bash 4.1, where `**` only has special significance if it is the only +thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but +`a/**b` will not. + +If an escaped pattern has no matches, and the `nonull` flag is set, +then minimatch.match returns the pattern as-provided, rather than +interpreting the character escapes. For example, +`minimatch.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than +`"*a?"`. This is akin to setting the `nullglob` option in bash, except +that it does not resolve escaped pattern characters. + +If brace expansion is not disabled, then it is performed before any +other interpretation of the glob pattern. Thus, a pattern like +`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded +**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are +checked for validity. Since those two are valid, matching proceeds. diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/minimatch.js b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/minimatch.js new file mode 100644 index 0000000..ec4c05c --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/minimatch.js @@ -0,0 +1,912 @@ +module.exports = minimatch +minimatch.Minimatch = Minimatch + +var path = { sep: '/' } +try { + path = require('path') +} catch (er) {} + +var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} +var expand = require('brace-expansion') + +// any single thing other than / +// don't need to escape / when using new RegExp() +var qmark = '[^/]' + +// * => any number of characters +var star = qmark + '*?' + +// ** when dots are allowed. Anything goes, except .. and . +// not (^ or / followed by one or two dots followed by $ or /), +// followed by anything, any number of times. +var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?' + +// not a ^ or / followed by a dot, +// followed by anything, any number of times. +var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?' + +// characters that need to be escaped in RegExp. +var reSpecials = charSet('().*{}+?[]^$\\!') + +// "abc" -> { a:true, b:true, c:true } +function charSet (s) { + return s.split('').reduce(function (set, c) { + set[c] = true + return set + }, {}) +} + +// normalizes slashes. +var slashSplit = /\/+/ + +minimatch.filter = filter +function filter (pattern, options) { + options = options || {} + return function (p, i, list) { + return minimatch(p, pattern, options) + } +} + +function ext (a, b) { + a = a || {} + b = b || {} + var t = {} + Object.keys(b).forEach(function (k) { + t[k] = b[k] + }) + Object.keys(a).forEach(function (k) { + t[k] = a[k] + }) + return t +} + +minimatch.defaults = function (def) { + if (!def || !Object.keys(def).length) return minimatch + + var orig = minimatch + + var m = function minimatch (p, pattern, options) { + return orig.minimatch(p, pattern, ext(def, options)) + } + + m.Minimatch = function Minimatch (pattern, options) { + return new orig.Minimatch(pattern, ext(def, options)) + } + + return m +} + +Minimatch.defaults = function (def) { + if (!def || !Object.keys(def).length) return Minimatch + return minimatch.defaults(def).Minimatch +} + +function minimatch (p, pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('glob pattern string required') + } + + if (!options) options = {} + + // shortcut: comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + return false + } + + // "" only matches "" + if (pattern.trim() === '') return p === '' + + return new Minimatch(pattern, options).match(p) +} + +function Minimatch (pattern, options) { + if (!(this instanceof Minimatch)) { + return new Minimatch(pattern, options) + } + + if (typeof pattern !== 'string') { + throw new TypeError('glob pattern string required') + } + + if (!options) options = {} + pattern = pattern.trim() + + // windows support: need to use /, not \ + if (path.sep !== '/') { + pattern = pattern.split(path.sep).join('/') + } + + this.options = options + this.set = [] + this.pattern = pattern + this.regexp = null + this.negate = false + this.comment = false + this.empty = false + + // make the set of regexps etc. + this.make() +} + +Minimatch.prototype.debug = function () {} + +Minimatch.prototype.make = make +function make () { + // don't do it more than once. + if (this._made) return + + var pattern = this.pattern + var options = this.options + + // empty patterns and comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + this.comment = true + return + } + if (!pattern) { + this.empty = true + return + } + + // step 1: figure out negation, etc. + this.parseNegate() + + // step 2: expand braces + var set = this.globSet = this.braceExpand() + + if (options.debug) this.debug = console.error + + this.debug(this.pattern, set) + + // step 3: now we have a set, so turn each one into a series of path-portion + // matching patterns. + // These will be regexps, except in the case of "**", which is + // set to the GLOBSTAR object for globstar behavior, + // and will not contain any / characters + set = this.globParts = set.map(function (s) { + return s.split(slashSplit) + }) + + this.debug(this.pattern, set) + + // glob --> regexps + set = set.map(function (s, si, set) { + return s.map(this.parse, this) + }, this) + + this.debug(this.pattern, set) + + // filter out everything that didn't compile properly. + set = set.filter(function (s) { + return s.indexOf(false) === -1 + }) + + this.debug(this.pattern, set) + + this.set = set +} + +Minimatch.prototype.parseNegate = parseNegate +function parseNegate () { + var pattern = this.pattern + var negate = false + var options = this.options + var negateOffset = 0 + + if (options.nonegate) return + + for (var i = 0, l = pattern.length + ; i < l && pattern.charAt(i) === '!' + ; i++) { + negate = !negate + negateOffset++ + } + + if (negateOffset) this.pattern = pattern.substr(negateOffset) + this.negate = negate +} + +// Brace expansion: +// a{b,c}d -> abd acd +// a{b,}c -> abc ac +// a{0..3}d -> a0d a1d a2d a3d +// a{b,c{d,e}f}g -> abg acdfg acefg +// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg +// +// Invalid sets are not expanded. +// a{2..}b -> a{2..}b +// a{b}c -> a{b}c +minimatch.braceExpand = function (pattern, options) { + return braceExpand(pattern, options) +} + +Minimatch.prototype.braceExpand = braceExpand + +function braceExpand (pattern, options) { + if (!options) { + if (this instanceof Minimatch) { + options = this.options + } else { + options = {} + } + } + + pattern = typeof pattern === 'undefined' + ? this.pattern : pattern + + if (typeof pattern === 'undefined') { + throw new Error('undefined pattern') + } + + if (options.nobrace || + !pattern.match(/\{.*\}/)) { + // shortcut. no need to expand. + return [pattern] + } + + return expand(pattern) +} + +// parse a component of the expanded set. +// At this point, no pattern may contain "/" in it +// so we're going to return a 2d array, where each entry is the full +// pattern, split on '/', and then turned into a regular expression. +// A regexp is made at the end which joins each array with an +// escaped /, and another full one which joins each regexp with |. +// +// Following the lead of Bash 4.1, note that "**" only has special meaning +// when it is the *only* thing in a path portion. Otherwise, any series +// of * is equivalent to a single *. Globstar behavior is enabled by +// default, and can be disabled by setting options.noglobstar. +Minimatch.prototype.parse = parse +var SUBPARSE = {} +function parse (pattern, isSub) { + var options = this.options + + // shortcuts + if (!options.noglobstar && pattern === '**') return GLOBSTAR + if (pattern === '') return '' + + var re = '' + var hasMagic = !!options.nocase + var escaping = false + // ? => one single character + var patternListStack = [] + var negativeLists = [] + var plType + var stateChar + var inClass = false + var reClassStart = -1 + var classStart = -1 + // . and .. never match anything that doesn't start with ., + // even when options.dot is set. + var patternStart = pattern.charAt(0) === '.' ? '' // anything + // not (start or / followed by . or .. followed by / or end) + : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))' + : '(?!\\.)' + var self = this + + function clearStateChar () { + if (stateChar) { + // we had some state-tracking character + // that wasn't consumed by this pass. + switch (stateChar) { + case '*': + re += star + hasMagic = true + break + case '?': + re += qmark + hasMagic = true + break + default: + re += '\\' + stateChar + break + } + self.debug('clearStateChar %j %j', stateChar, re) + stateChar = false + } + } + + for (var i = 0, len = pattern.length, c + ; (i < len) && (c = pattern.charAt(i)) + ; i++) { + this.debug('%s\t%s %s %j', pattern, i, re, c) + + // skip over any that are escaped. + if (escaping && reSpecials[c]) { + re += '\\' + c + escaping = false + continue + } + + switch (c) { + case '/': + // completely not allowed, even escaped. + // Should already be path-split by now. + return false + + case '\\': + clearStateChar() + escaping = true + continue + + // the various stateChar values + // for the "extglob" stuff. + case '?': + case '*': + case '+': + case '@': + case '!': + this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c) + + // all of those are literals inside a class, except that + // the glob [!a] means [^a] in regexp + if (inClass) { + this.debug(' in class') + if (c === '!' && i === classStart + 1) c = '^' + re += c + continue + } + + // if we already have a stateChar, then it means + // that there was something like ** or +? in there. + // Handle the stateChar, then proceed with this one. + self.debug('call clearStateChar %j', stateChar) + clearStateChar() + stateChar = c + // if extglob is disabled, then +(asdf|foo) isn't a thing. + // just clear the statechar *now*, rather than even diving into + // the patternList stuff. + if (options.noext) clearStateChar() + continue + + case '(': + if (inClass) { + re += '(' + continue + } + + if (!stateChar) { + re += '\\(' + continue + } + + plType = stateChar + patternListStack.push({ + type: plType, + start: i - 1, + reStart: re.length + }) + // negation is (?:(?!js)[^/]*) + re += stateChar === '!' ? '(?:(?!(?:' : '(?:' + this.debug('plType %j %j', stateChar, re) + stateChar = false + continue + + case ')': + if (inClass || !patternListStack.length) { + re += '\\)' + continue + } + + clearStateChar() + hasMagic = true + re += ')' + var pl = patternListStack.pop() + plType = pl.type + // negation is (?:(?!js)[^/]*) + // The others are (?:) + switch (plType) { + case '!': + negativeLists.push(pl) + re += ')[^/]*?)' + pl.reEnd = re.length + break + case '?': + case '+': + case '*': + re += plType + break + case '@': break // the default anyway + } + continue + + case '|': + if (inClass || !patternListStack.length || escaping) { + re += '\\|' + escaping = false + continue + } + + clearStateChar() + re += '|' + continue + + // these are mostly the same in regexp and glob + case '[': + // swallow any state-tracking char before the [ + clearStateChar() + + if (inClass) { + re += '\\' + c + continue + } + + inClass = true + classStart = i + reClassStart = re.length + re += c + continue + + case ']': + // a right bracket shall lose its special + // meaning and represent itself in + // a bracket expression if it occurs + // first in the list. -- POSIX.2 2.8.3.2 + if (i === classStart + 1 || !inClass) { + re += '\\' + c + escaping = false + continue + } + + // handle the case where we left a class open. + // "[z-a]" is valid, equivalent to "\[z-a\]" + if (inClass) { + // split where the last [ was, make sure we don't have + // an invalid re. if so, re-walk the contents of the + // would-be class to re-translate any characters that + // were passed through as-is + // TODO: It would probably be faster to determine this + // without a try/catch and a new RegExp, but it's tricky + // to do safely. For now, this is safe and works. + var cs = pattern.substring(classStart + 1, i) + try { + RegExp('[' + cs + ']') + } catch (er) { + // not a valid class! + var sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]' + hasMagic = hasMagic || sp[1] + inClass = false + continue + } + } + + // finish up the class. + hasMagic = true + inClass = false + re += c + continue + + default: + // swallow any state char that wasn't consumed + clearStateChar() + + if (escaping) { + // no need + escaping = false + } else if (reSpecials[c] + && !(c === '^' && inClass)) { + re += '\\' + } + + re += c + + } // switch + } // for + + // handle the case where we left a class open. + // "[abc" is valid, equivalent to "\[abc" + if (inClass) { + // split where the last [ was, and escape it + // this is a huge pita. We now have to re-walk + // the contents of the would-be class to re-translate + // any characters that were passed through as-is + cs = pattern.substr(classStart + 1) + sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + hasMagic = hasMagic || sp[1] + } + + // handle the case where we had a +( thing at the *end* + // of the pattern. + // each pattern list stack adds 3 chars, and we need to go through + // and escape any | chars that were passed through as-is for the regexp. + // Go through and escape them, taking care not to double-escape any + // | chars that were already escaped. + for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) { + var tail = re.slice(pl.reStart + 3) + // maybe some even number of \, then maybe 1 \, followed by a | + tail = tail.replace(/((?:\\{2})*)(\\?)\|/g, function (_, $1, $2) { + if (!$2) { + // the | isn't already escaped, so escape it. + $2 = '\\' + } + + // need to escape all those slashes *again*, without escaping the + // one that we need for escaping the | character. As it works out, + // escaping an even number of slashes can be done by simply repeating + // it exactly after itself. That's why this trick works. + // + // I am sorry that you have to see this. + return $1 + $1 + $2 + '|' + }) + + this.debug('tail=%j\n %s', tail, tail) + var t = pl.type === '*' ? star + : pl.type === '?' ? qmark + : '\\' + pl.type + + hasMagic = true + re = re.slice(0, pl.reStart) + t + '\\(' + tail + } + + // handle trailing things that only matter at the very end. + clearStateChar() + if (escaping) { + // trailing \\ + re += '\\\\' + } + + // only need to apply the nodot start if the re starts with + // something that could conceivably capture a dot + var addPatternStart = false + switch (re.charAt(0)) { + case '.': + case '[': + case '(': addPatternStart = true + } + + // Hack to work around lack of negative lookbehind in JS + // A pattern like: *.!(x).!(y|z) needs to ensure that a name + // like 'a.xyz.yz' doesn't match. So, the first negative + // lookahead, has to look ALL the way ahead, to the end of + // the pattern. + for (var n = negativeLists.length - 1; n > -1; n--) { + var nl = negativeLists[n] + + var nlBefore = re.slice(0, nl.reStart) + var nlFirst = re.slice(nl.reStart, nl.reEnd - 8) + var nlLast = re.slice(nl.reEnd - 8, nl.reEnd) + var nlAfter = re.slice(nl.reEnd) + + nlLast += nlAfter + + // Handle nested stuff like *(*.js|!(*.json)), where open parens + // mean that we should *not* include the ) in the bit that is considered + // "after" the negated section. + var openParensBefore = nlBefore.split('(').length - 1 + var cleanAfter = nlAfter + for (i = 0; i < openParensBefore; i++) { + cleanAfter = cleanAfter.replace(/\)[+*?]?/, '') + } + nlAfter = cleanAfter + + var dollar = '' + if (nlAfter === '' && isSub !== SUBPARSE) { + dollar = '$' + } + var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast + re = newRe + } + + // if the re is not "" at this point, then we need to make sure + // it doesn't match against an empty path part. + // Otherwise a/* will match a/, which it should not. + if (re !== '' && hasMagic) { + re = '(?=.)' + re + } + + if (addPatternStart) { + re = patternStart + re + } + + // parsing just a piece of a larger pattern. + if (isSub === SUBPARSE) { + return [re, hasMagic] + } + + // skip the regexp for non-magical patterns + // unescape anything in it, though, so that it'll be + // an exact match against a file etc. + if (!hasMagic) { + return globUnescape(pattern) + } + + var flags = options.nocase ? 'i' : '' + var regExp = new RegExp('^' + re + '$', flags) + + regExp._glob = pattern + regExp._src = re + + return regExp +} + +minimatch.makeRe = function (pattern, options) { + return new Minimatch(pattern, options || {}).makeRe() +} + +Minimatch.prototype.makeRe = makeRe +function makeRe () { + if (this.regexp || this.regexp === false) return this.regexp + + // at this point, this.set is a 2d array of partial + // pattern strings, or "**". + // + // It's better to use .match(). This function shouldn't + // be used, really, but it's pretty convenient sometimes, + // when you just want to work with a regex. + var set = this.set + + if (!set.length) { + this.regexp = false + return this.regexp + } + var options = this.options + + var twoStar = options.noglobstar ? star + : options.dot ? twoStarDot + : twoStarNoDot + var flags = options.nocase ? 'i' : '' + + var re = set.map(function (pattern) { + return pattern.map(function (p) { + return (p === GLOBSTAR) ? twoStar + : (typeof p === 'string') ? regExpEscape(p) + : p._src + }).join('\\\/') + }).join('|') + + // must match entire pattern + // ending in a * or ** will make it less strict. + re = '^(?:' + re + ')$' + + // can match anything, as long as it's not this. + if (this.negate) re = '^(?!' + re + ').*$' + + try { + this.regexp = new RegExp(re, flags) + } catch (ex) { + this.regexp = false + } + return this.regexp +} + +minimatch.match = function (list, pattern, options) { + options = options || {} + var mm = new Minimatch(pattern, options) + list = list.filter(function (f) { + return mm.match(f) + }) + if (mm.options.nonull && !list.length) { + list.push(pattern) + } + return list +} + +Minimatch.prototype.match = match +function match (f, partial) { + this.debug('match', f, this.pattern) + // short-circuit in the case of busted things. + // comments, etc. + if (this.comment) return false + if (this.empty) return f === '' + + if (f === '/' && partial) return true + + var options = this.options + + // windows: need to use /, not \ + if (path.sep !== '/') { + f = f.split(path.sep).join('/') + } + + // treat the test path as a set of pathparts. + f = f.split(slashSplit) + this.debug(this.pattern, 'split', f) + + // just ONE of the pattern sets in this.set needs to match + // in order for it to be valid. If negating, then just one + // match means that we have failed. + // Either way, return on the first hit. + + var set = this.set + this.debug(this.pattern, 'set', set) + + // Find the basename of the path by looking for the last non-empty segment + var filename + var i + for (i = f.length - 1; i >= 0; i--) { + filename = f[i] + if (filename) break + } + + for (i = 0; i < set.length; i++) { + var pattern = set[i] + var file = f + if (options.matchBase && pattern.length === 1) { + file = [filename] + } + var hit = this.matchOne(file, pattern, partial) + if (hit) { + if (options.flipNegate) return true + return !this.negate + } + } + + // didn't get any hits. this is success if it's a negative + // pattern, failure otherwise. + if (options.flipNegate) return false + return this.negate +} + +// set partial to true to test if, for example, +// "/a/b" matches the start of "/*/b/*/d" +// Partial means, if you run out of file before you run +// out of pattern, then that's fine, as long as all +// the parts match. +Minimatch.prototype.matchOne = function (file, pattern, partial) { + var options = this.options + + this.debug('matchOne', + { 'this': this, file: file, pattern: pattern }) + + this.debug('matchOne', file.length, pattern.length) + + for (var fi = 0, + pi = 0, + fl = file.length, + pl = pattern.length + ; (fi < fl) && (pi < pl) + ; fi++, pi++) { + this.debug('matchOne loop') + var p = pattern[pi] + var f = file[fi] + + this.debug(pattern, p, f) + + // should be impossible. + // some invalid regexp stuff in the set. + if (p === false) return false + + if (p === GLOBSTAR) { + this.debug('GLOBSTAR', [pattern, p, f]) + + // "**" + // a/**/b/**/c would match the following: + // a/b/x/y/z/c + // a/x/y/z/b/c + // a/b/x/b/x/c + // a/b/c + // To do this, take the rest of the pattern after + // the **, and see if it would match the file remainder. + // If so, return success. + // If not, the ** "swallows" a segment, and try again. + // This is recursively awful. + // + // a/**/b/**/c matching a/b/x/y/z/c + // - a matches a + // - doublestar + // - matchOne(b/x/y/z/c, b/**/c) + // - b matches b + // - doublestar + // - matchOne(x/y/z/c, c) -> no + // - matchOne(y/z/c, c) -> no + // - matchOne(z/c, c) -> no + // - matchOne(c, c) yes, hit + var fr = fi + var pr = pi + 1 + if (pr === pl) { + this.debug('** at the end') + // a ** at the end will just swallow the rest. + // We have found a match. + // however, it will not swallow /.x, unless + // options.dot is set. + // . and .. are *never* matched by **, for explosively + // exponential reasons. + for (; fi < fl; fi++) { + if (file[fi] === '.' || file[fi] === '..' || + (!options.dot && file[fi].charAt(0) === '.')) return false + } + return true + } + + // ok, let's see if we can swallow whatever we can. + while (fr < fl) { + var swallowee = file[fr] + + this.debug('\nglobstar while', file, fr, pattern, pr, swallowee) + + // XXX remove this slice. Just pass the start index. + if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { + this.debug('globstar found match!', fr, fl, swallowee) + // found a match. + return true + } else { + // can't swallow "." or ".." ever. + // can only swallow ".foo" when explicitly asked. + if (swallowee === '.' || swallowee === '..' || + (!options.dot && swallowee.charAt(0) === '.')) { + this.debug('dot detected!', file, fr, pattern, pr) + break + } + + // ** swallows a segment, and continue. + this.debug('globstar swallow a segment, and continue') + fr++ + } + } + + // no match was found. + // However, in partial mode, we can't say this is necessarily over. + // If there's more *pattern* left, then + if (partial) { + // ran out of file + this.debug('\n>>> no match, partial?', file, fr, pattern, pr) + if (fr === fl) return true + } + return false + } + + // something other than ** + // non-magic patterns just have to match exactly + // patterns with magic have been turned into regexps. + var hit + if (typeof p === 'string') { + if (options.nocase) { + hit = f.toLowerCase() === p.toLowerCase() + } else { + hit = f === p + } + this.debug('string match', p, f, hit) + } else { + hit = f.match(p) + this.debug('pattern match', p, f, hit) + } + + if (!hit) return false + } + + // Note: ending in / means that we'll get a final "" + // at the end of the pattern. This can only match a + // corresponding "" at the end of the file. + // If the file ends in /, then it can only match a + // a pattern that ends in /, unless the pattern just + // doesn't have any more for it. But, a/b/ should *not* + // match "a/b/*", even though "" matches against the + // [^/]*? pattern, except in partial mode, where it might + // simply not be reached yet. + // However, a/b/ should still satisfy a/* + + // now either we fell off the end of the pattern, or we're done. + if (fi === fl && pi === pl) { + // ran out of pattern and filename at the same time. + // an exact hit! + return true + } else if (fi === fl) { + // ran out of file, but still had pattern left. + // this is ok if we're doing the match as part of + // a glob fs traversal. + return partial + } else if (pi === pl) { + // ran out of pattern, still have file left. + // this is only acceptable if we're on the very last + // empty segment of a file with a trailing slash. + // a/* should match a/b/ + var emptyFileEnd = (fi === fl - 1) && (file[fi] === '') + return emptyFileEnd + } + + // should be unreachable. + throw new Error('wtf?') +} + +// replace stuff like \* with * +function globUnescape (s) { + return s.replace(/\\(.)/g, '$1') +} + +function regExpEscape (s) { + return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/.npmignore b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/.npmignore new file mode 100644 index 0000000..353546a --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/.npmignore @@ -0,0 +1,3 @@ +test +.gitignore +.travis.yml diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/README.md b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/README.md new file mode 100644 index 0000000..1793929 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/README.md @@ -0,0 +1,122 @@ +# brace-expansion + +[Brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html), +as known from sh/bash, in JavaScript. + +[![build status](https://secure.travis-ci.org/juliangruber/brace-expansion.svg)](http://travis-ci.org/juliangruber/brace-expansion) +[![downloads](https://img.shields.io/npm/dm/brace-expansion.svg)](https://www.npmjs.org/package/brace-expansion) + +[![testling badge](https://ci.testling.com/juliangruber/brace-expansion.png)](https://ci.testling.com/juliangruber/brace-expansion) + +## Example + +```js +var expand = require('brace-expansion'); + +expand('file-{a,b,c}.jpg') +// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg'] + +expand('-v{,,}') +// => ['-v', '-v', '-v'] + +expand('file{0..2}.jpg') +// => ['file0.jpg', 'file1.jpg', 'file2.jpg'] + +expand('file-{a..c}.jpg') +// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg'] + +expand('file{2..0}.jpg') +// => ['file2.jpg', 'file1.jpg', 'file0.jpg'] + +expand('file{0..4..2}.jpg') +// => ['file0.jpg', 'file2.jpg', 'file4.jpg'] + +expand('file-{a..e..2}.jpg') +// => ['file-a.jpg', 'file-c.jpg', 'file-e.jpg'] + +expand('file{00..10..5}.jpg') +// => ['file00.jpg', 'file05.jpg', 'file10.jpg'] + +expand('{{A..C},{a..c}}') +// => ['A', 'B', 'C', 'a', 'b', 'c'] + +expand('ppp{,config,oe{,conf}}') +// => ['ppp', 'pppconfig', 'pppoe', 'pppoeconf'] +``` + +## API + +```js +var expand = require('brace-expansion'); +``` + +### var expanded = expand(str) + +Return an array of all possible and valid expansions of `str`. If none are +found, `[str]` is returned. + +Valid expansions are: + +```js +/^(.*,)+(.+)?$/ +// {a,b,...} +``` + +A comma seperated list of options, like `{a,b}` or `{a,{b,c}}` or `{,a,}`. + +```js +/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/ +// {x..y[..incr]} +``` + +A numeric sequence from `x` to `y` inclusive, with optional increment. +If `x` or `y` start with a leading `0`, all the numbers will be padded +to have equal length. Negative numbers and backwards iteration work too. + +```js +/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/ +// {x..y[..incr]} +``` + +An alphabetic sequence from `x` to `y` inclusive, with optional increment. +`x` and `y` must be exactly one character, and if given, `incr` must be a +number. + +For compatibility reasons, the string `${` is not eligible for brace expansion. + +## Installation + +With [npm](https://npmjs.org) do: + +```bash +npm install brace-expansion +``` + +## Contributors + +- [Julian Gruber](https://github.com/juliangruber) +- [Isaac Z. Schlueter](https://github.com/isaacs) + +## License + +(MIT) + +Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/example.js b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/example.js new file mode 100644 index 0000000..60ecfc7 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/example.js @@ -0,0 +1,8 @@ +var expand = require('./'); + +console.log(expand('http://any.org/archive{1996..1999}/vol{1..4}/part{a,b,c}.html')); +console.log(expand('http://www.numericals.com/file{1..100..10}.txt')); +console.log(expand('http://www.letters.com/file{a..z..2}.txt')); +console.log(expand('mkdir /usr/local/src/bash/{old,new,dist,bugs}')); +console.log(expand('chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}')); + diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/index.js b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/index.js new file mode 100644 index 0000000..a23104e --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/index.js @@ -0,0 +1,191 @@ +var concatMap = require('concat-map'); +var balanced = require('balanced-match'); + +module.exports = expandTop; + +var escSlash = '\0SLASH'+Math.random()+'\0'; +var escOpen = '\0OPEN'+Math.random()+'\0'; +var escClose = '\0CLOSE'+Math.random()+'\0'; +var escComma = '\0COMMA'+Math.random()+'\0'; +var escPeriod = '\0PERIOD'+Math.random()+'\0'; + +function numeric(str) { + return parseInt(str, 10) == str + ? parseInt(str, 10) + : str.charCodeAt(0); +} + +function escapeBraces(str) { + return str.split('\\\\').join(escSlash) + .split('\\{').join(escOpen) + .split('\\}').join(escClose) + .split('\\,').join(escComma) + .split('\\.').join(escPeriod); +} + +function unescapeBraces(str) { + return str.split(escSlash).join('\\') + .split(escOpen).join('{') + .split(escClose).join('}') + .split(escComma).join(',') + .split(escPeriod).join('.'); +} + + +// Basically just str.split(","), but handling cases +// where we have nested braced sections, which should be +// treated as individual members, like {a,{b,c},d} +function parseCommaParts(str) { + if (!str) + return ['']; + + var parts = []; + var m = balanced('{', '}', str); + + if (!m) + return str.split(','); + + var pre = m.pre; + var body = m.body; + var post = m.post; + var p = pre.split(','); + + p[p.length-1] += '{' + body + '}'; + var postParts = parseCommaParts(post); + if (post.length) { + p[p.length-1] += postParts.shift(); + p.push.apply(p, postParts); + } + + parts.push.apply(parts, p); + + return parts; +} + +function expandTop(str) { + if (!str) + return []; + + return expand(escapeBraces(str), true).map(unescapeBraces); +} + +function identity(e) { + return e; +} + +function embrace(str) { + return '{' + str + '}'; +} +function isPadded(el) { + return /^-?0\d/.test(el); +} + +function lte(i, y) { + return i <= y; +} +function gte(i, y) { + return i >= y; +} + +function expand(str, isTop) { + var expansions = []; + + var m = balanced('{', '}', str); + if (!m || /\$$/.test(m.pre)) return [str]; + + var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); + var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); + var isSequence = isNumericSequence || isAlphaSequence; + var isOptions = /^(.*,)+(.+)?$/.test(m.body); + if (!isSequence && !isOptions) { + // {a},b} + if (m.post.match(/,.*}/)) { + str = m.pre + '{' + m.body + escClose + m.post; + return expand(str); + } + return [str]; + } + + var n; + if (isSequence) { + n = m.body.split(/\.\./); + } else { + n = parseCommaParts(m.body); + if (n.length === 1) { + // x{{a,b}}y ==> x{a}y x{b}y + n = expand(n[0], false).map(embrace); + if (n.length === 1) { + var post = m.post.length + ? expand(m.post, false) + : ['']; + return post.map(function(p) { + return m.pre + n[0] + p; + }); + } + } + } + + // at this point, n is the parts, and we know it's not a comma set + // with a single entry. + + // no need to expand pre, since it is guaranteed to be free of brace-sets + var pre = m.pre; + var post = m.post.length + ? expand(m.post, false) + : ['']; + + var N; + + if (isSequence) { + var x = numeric(n[0]); + var y = numeric(n[1]); + var width = Math.max(n[0].length, n[1].length) + var incr = n.length == 3 + ? Math.abs(numeric(n[2])) + : 1; + var test = lte; + var reverse = y < x; + if (reverse) { + incr *= -1; + test = gte; + } + var pad = n.some(isPadded); + + N = []; + + for (var i = x; test(i, y); i += incr) { + var c; + if (isAlphaSequence) { + c = String.fromCharCode(i); + if (c === '\\') + c = ''; + } else { + c = String(i); + if (pad) { + var need = width - c.length; + if (need > 0) { + var z = new Array(need + 1).join('0'); + if (i < 0) + c = '-' + z + c.slice(1); + else + c = z + c; + } + } + } + N.push(c); + } + } else { + N = concatMap(n, function(el) { return expand(el, false) }); + } + + for (var j = 0; j < N.length; j++) { + for (var k = 0; k < post.length; k++) { + var expansion = pre + N[j] + post[k]; + if (!isTop || isSequence || expansion) + expansions.push(expansion); + } + } + + return expansions; +} + diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/.npmignore b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/.npmignore new file mode 100644 index 0000000..fd4f2b0 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/.npmignore @@ -0,0 +1,2 @@ +node_modules +.DS_Store diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/.travis.yml b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/.travis.yml new file mode 100644 index 0000000..6e5919d --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - "0.10" diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/LICENSE.md b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/LICENSE.md new file mode 100644 index 0000000..2cdc8e4 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/LICENSE.md @@ -0,0 +1,21 @@ +(MIT) + +Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/Makefile b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/Makefile new file mode 100644 index 0000000..fa5da71 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/Makefile @@ -0,0 +1,6 @@ + +test: + @node_modules/.bin/tape test/*.js + +.PHONY: test + diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/README.md b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/README.md new file mode 100644 index 0000000..421f3aa --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/README.md @@ -0,0 +1,89 @@ +# balanced-match + +Match balanced string pairs, like `{` and `}` or `` and ``. + +[![build status](https://secure.travis-ci.org/juliangruber/balanced-match.svg)](http://travis-ci.org/juliangruber/balanced-match) +[![downloads](https://img.shields.io/npm/dm/balanced-match.svg)](https://www.npmjs.org/package/balanced-match) + +[![testling badge](https://ci.testling.com/juliangruber/balanced-match.png)](https://ci.testling.com/juliangruber/balanced-match) + +## Example + +Get the first matching pair of braces: + +```js +var balanced = require('balanced-match'); + +console.log(balanced('{', '}', 'pre{in{nested}}post')); +console.log(balanced('{', '}', 'pre{first}between{second}post')); +``` + +The matches are: + +```bash +$ node example.js +{ start: 3, end: 14, pre: 'pre', body: 'in{nested}', post: 'post' } +{ start: 3, + end: 9, + pre: 'pre', + body: 'first', + post: 'between{second}post' } +``` + +## API + +### var m = balanced(a, b, str) + +For the first non-nested matching pair of `a` and `b` in `str`, return an +object with those keys: + +* **start** the index of the first match of `a` +* **end** the index of the matching `b` +* **pre** the preamble, `a` and `b` not included +* **body** the match, `a` and `b` not included +* **post** the postscript, `a` and `b` not included + +If there's no match, `undefined` will be returned. + +If the `str` contains more `a` than `b` / there are unmatched pairs, the first match that was closed will be used. For example, `{{a}` will match `['{', 'a', '']`. + +### var r = balanced.range(a, b, str) + +For the first non-nested matching pair of `a` and `b` in `str`, return an +array with indexes: `[ , ]`. + +If there's no match, `undefined` will be returned. + +If the `str` contains more `a` than `b` / there are unmatched pairs, the first match that was closed will be used. For example, `{{a}` will match `[ 1, 3 ]`. + +## Installation + +With [npm](https://npmjs.org) do: + +```bash +npm install balanced-match +``` + +## License + +(MIT) + +Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/example.js b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/example.js new file mode 100644 index 0000000..c02ad34 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/example.js @@ -0,0 +1,5 @@ +var balanced = require('./'); + +console.log(balanced('{', '}', 'pre{in{nested}}post')); +console.log(balanced('{', '}', 'pre{first}between{second}post')); + diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/index.js b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/index.js new file mode 100644 index 0000000..75f3d71 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/index.js @@ -0,0 +1,50 @@ +module.exports = balanced; +function balanced(a, b, str) { + var r = range(a, b, str); + + return r && { + start: r[0], + end: r[1], + pre: str.slice(0, r[0]), + body: str.slice(r[0] + a.length, r[1]), + post: str.slice(r[1] + b.length) + }; +} + +balanced.range = range; +function range(a, b, str) { + var begs, beg, left, right, result; + var ai = str.indexOf(a); + var bi = str.indexOf(b, ai + 1); + var i = ai; + + if (ai >= 0 && bi > 0) { + begs = []; + left = str.length; + + while (i < str.length && i >= 0 && ! result) { + if (i == ai) { + begs.push(i); + ai = str.indexOf(a, i + 1); + } else if (begs.length == 1) { + result = [ begs.pop(), bi ]; + } else { + beg = begs.pop(); + if (beg < left) { + left = beg; + right = bi; + } + + bi = str.indexOf(b, i + 1); + } + + i = ai < bi && ai >= 0 ? ai : bi; + } + + if (begs.length) { + result = [ left, right ]; + } + } + + return result; +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/package.json b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/package.json new file mode 100644 index 0000000..7eb3457 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/package.json @@ -0,0 +1,73 @@ +{ + "name": "balanced-match", + "description": "Match balanced character pairs, like \"{\" and \"}\"", + "version": "0.3.0", + "repository": { + "type": "git", + "url": "git://github.com/juliangruber/balanced-match.git" + }, + "homepage": "https://github.com/juliangruber/balanced-match", + "main": "index.js", + "scripts": { + "test": "make test" + }, + "dependencies": {}, + "devDependencies": { + "tape": "~4.2.2" + }, + "keywords": [ + "match", + "regexp", + "test", + "balanced", + "parse" + ], + "author": { + "name": "Julian Gruber", + "email": "mail@juliangruber.com", + "url": "http://juliangruber.com" + }, + "license": "MIT", + "testling": { + "files": "test/*.js", + "browsers": [ + "ie/8..latest", + "firefox/20..latest", + "firefox/nightly", + "chrome/25..latest", + "chrome/canary", + "opera/12..latest", + "opera/next", + "safari/5.1..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2..latest" + ] + }, + "gitHead": "a7114b0986554787e90b7ac595a043ca75ea77e5", + "bugs": { + "url": "https://github.com/juliangruber/balanced-match/issues" + }, + "_id": "balanced-match@0.3.0", + "_shasum": "a91cdd1ebef1a86659e70ff4def01625fc2d6756", + "_from": "balanced-match@>=0.3.0 <0.4.0", + "_npmVersion": "2.14.7", + "_nodeVersion": "4.2.1", + "_npmUser": { + "name": "juliangruber", + "email": "julian@juliangruber.com" + }, + "dist": { + "shasum": "a91cdd1ebef1a86659e70ff4def01625fc2d6756", + "tarball": "http://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" + }, + "maintainers": [ + { + "name": "juliangruber", + "email": "julian@juliangruber.com" + } + ], + "directories": {}, + "_resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/test/balanced.js b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/test/balanced.js new file mode 100644 index 0000000..f5e98e3 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match/test/balanced.js @@ -0,0 +1,84 @@ +var test = require('tape'); +var balanced = require('..'); + +test('balanced', function(t) { + t.deepEqual(balanced('{', '}', 'pre{in{nest}}post'), { + start: 3, + end: 12, + pre: 'pre', + body: 'in{nest}', + post: 'post' + }); + t.deepEqual(balanced('{', '}', '{{{{{{{{{in}post'), { + start: 8, + end: 11, + pre: '{{{{{{{{', + body: 'in', + post: 'post' + }); + t.deepEqual(balanced('{', '}', 'pre{body{in}post'), { + start: 8, + end: 11, + pre: 'pre{body', + body: 'in', + post: 'post' + }); + t.deepEqual(balanced('{', '}', 'pre}{in{nest}}post'), { + start: 4, + end: 13, + pre: 'pre}', + body: 'in{nest}', + post: 'post' + }); + t.deepEqual(balanced('{', '}', 'pre{body}between{body2}post'), { + start: 3, + end: 8, + pre: 'pre', + body: 'body', + post: 'between{body2}post' + }); + t.notOk(balanced('{', '}', 'nope'), 'should be notOk'); + t.deepEqual(balanced('', '', 'preinnestpost'), { + start: 3, + end: 19, + pre: 'pre', + body: 'innest', + post: 'post' + }); + t.deepEqual(balanced('', '', 'preinnestpost'), { + start: 7, + end: 23, + pre: 'pre', + body: 'innest', + post: 'post' + }); + t.deepEqual(balanced('{{', '}}', 'pre{{{in}}}post'), { + start: 3, + end: 9, + pre: 'pre', + body: '{in}', + post: 'post' + }); + t.deepEqual(balanced('{{{', '}}', 'pre{{{in}}}post'), { + start: 3, + end: 8, + pre: 'pre', + body: 'in', + post: '}post' + }); + t.deepEqual(balanced('{', '}', 'pre{{first}in{second}post'), { + start: 4, + end: 10, + pre: 'pre{', + body: 'first', + post: 'in{second}post' + }); + t.deepEqual(balanced('', 'prepost'), { + start: 3, + end: 4, + pre: 'pre', + body: '', + post: 'post' + }); + t.end(); +}); diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/.travis.yml b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/.travis.yml new file mode 100644 index 0000000..f1d0f13 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.4 + - 0.6 diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/LICENSE b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/LICENSE new file mode 100644 index 0000000..ee27ba4 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/LICENSE @@ -0,0 +1,18 @@ +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/README.markdown b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/README.markdown new file mode 100644 index 0000000..408f70a --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/README.markdown @@ -0,0 +1,62 @@ +concat-map +========== + +Concatenative mapdashery. + +[![browser support](http://ci.testling.com/substack/node-concat-map.png)](http://ci.testling.com/substack/node-concat-map) + +[![build status](https://secure.travis-ci.org/substack/node-concat-map.png)](http://travis-ci.org/substack/node-concat-map) + +example +======= + +``` js +var concatMap = require('concat-map'); +var xs = [ 1, 2, 3, 4, 5, 6 ]; +var ys = concatMap(xs, function (x) { + return x % 2 ? [ x - 0.1, x, x + 0.1 ] : []; +}); +console.dir(ys); +``` + +*** + +``` +[ 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1 ] +``` + +methods +======= + +``` js +var concatMap = require('concat-map') +``` + +concatMap(xs, fn) +----------------- + +Return an array of concatenated elements by calling `fn(x, i)` for each element +`x` and each index `i` in the array `xs`. + +When `fn(x, i)` returns an array, its result will be concatenated with the +result array. If `fn(x, i)` returns anything else, that value will be pushed +onto the end of the result array. + +install +======= + +With [npm](http://npmjs.org) do: + +``` +npm install concat-map +``` + +license +======= + +MIT + +notes +===== + +This module was written while sitting high above the ground in a tree. diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/example/map.js b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/example/map.js new file mode 100644 index 0000000..3365621 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/example/map.js @@ -0,0 +1,6 @@ +var concatMap = require('../'); +var xs = [ 1, 2, 3, 4, 5, 6 ]; +var ys = concatMap(xs, function (x) { + return x % 2 ? [ x - 0.1, x, x + 0.1 ] : []; +}); +console.dir(ys); diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/index.js b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/index.js new file mode 100644 index 0000000..b29a781 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/index.js @@ -0,0 +1,13 @@ +module.exports = function (xs, fn) { + var res = []; + for (var i = 0; i < xs.length; i++) { + var x = fn(xs[i], i); + if (isArray(x)) res.push.apply(res, x); + else res.push(x); + } + return res; +}; + +var isArray = Array.isArray || function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]'; +}; diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/package.json b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/package.json new file mode 100644 index 0000000..b516138 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/package.json @@ -0,0 +1,83 @@ +{ + "name": "concat-map", + "description": "concatenative mapdashery", + "version": "0.0.1", + "repository": { + "type": "git", + "url": "git://github.com/substack/node-concat-map.git" + }, + "main": "index.js", + "keywords": [ + "concat", + "concatMap", + "map", + "functional", + "higher-order" + ], + "directories": { + "example": "example", + "test": "test" + }, + "scripts": { + "test": "tape test/*.js" + }, + "devDependencies": { + "tape": "~2.4.0" + }, + "license": "MIT", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "testling": { + "files": "test/*.js", + "browsers": { + "ie": [ + 6, + 7, + 8, + 9 + ], + "ff": [ + 3.5, + 10, + 15 + ], + "chrome": [ + 10, + 22 + ], + "safari": [ + 5.1 + ], + "opera": [ + 12 + ] + } + }, + "bugs": { + "url": "https://github.com/substack/node-concat-map/issues" + }, + "homepage": "https://github.com/substack/node-concat-map", + "_id": "concat-map@0.0.1", + "dist": { + "shasum": "d8a96bd77fd68df7793a73036a3ba0d5405d477b", + "tarball": "http://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + }, + "_from": "concat-map@0.0.1", + "_npmVersion": "1.3.21", + "_npmUser": { + "name": "substack", + "email": "mail@substack.net" + }, + "maintainers": [ + { + "name": "substack", + "email": "mail@substack.net" + } + ], + "_shasum": "d8a96bd77fd68df7793a73036a3ba0d5405d477b", + "_resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/test/map.js b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/test/map.js new file mode 100644 index 0000000..fdbd702 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map/test/map.js @@ -0,0 +1,39 @@ +var concatMap = require('../'); +var test = require('tape'); + +test('empty or not', function (t) { + var xs = [ 1, 2, 3, 4, 5, 6 ]; + var ixes = []; + var ys = concatMap(xs, function (x, ix) { + ixes.push(ix); + return x % 2 ? [ x - 0.1, x, x + 0.1 ] : []; + }); + t.same(ys, [ 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1 ]); + t.same(ixes, [ 0, 1, 2, 3, 4, 5 ]); + t.end(); +}); + +test('always something', function (t) { + var xs = [ 'a', 'b', 'c', 'd' ]; + var ys = concatMap(xs, function (x) { + return x === 'b' ? [ 'B', 'B', 'B' ] : [ x ]; + }); + t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]); + t.end(); +}); + +test('scalars', function (t) { + var xs = [ 'a', 'b', 'c', 'd' ]; + var ys = concatMap(xs, function (x) { + return x === 'b' ? [ 'B', 'B', 'B' ] : x; + }); + t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]); + t.end(); +}); + +test('undefs', function (t) { + var xs = [ 'a', 'b', 'c', 'd' ]; + var ys = concatMap(xs, function () {}); + t.same(ys, [ undefined, undefined, undefined, undefined ]); + t.end(); +}); diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/package.json b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/package.json new file mode 100644 index 0000000..fe96726 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/package.json @@ -0,0 +1,75 @@ +{ + "name": "brace-expansion", + "description": "Brace expansion as known from sh/bash", + "version": "1.1.2", + "repository": { + "type": "git", + "url": "git://github.com/juliangruber/brace-expansion.git" + }, + "homepage": "https://github.com/juliangruber/brace-expansion", + "main": "index.js", + "scripts": { + "test": "tape test/*.js", + "gentest": "bash test/generate.sh" + }, + "dependencies": { + "balanced-match": "^0.3.0", + "concat-map": "0.0.1" + }, + "devDependencies": { + "tape": "4.2.2" + }, + "keywords": [], + "author": { + "name": "Julian Gruber", + "email": "mail@juliangruber.com", + "url": "http://juliangruber.com" + }, + "license": "MIT", + "testling": { + "files": "test/*.js", + "browsers": [ + "ie/8..latest", + "firefox/20..latest", + "firefox/nightly", + "chrome/25..latest", + "chrome/canary", + "opera/12..latest", + "opera/next", + "safari/5.1..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2..latest" + ] + }, + "gitHead": "b03773a30fa516b1374945b68e9acb6253d595fa", + "bugs": { + "url": "https://github.com/juliangruber/brace-expansion/issues" + }, + "_id": "brace-expansion@1.1.2", + "_shasum": "f21445d0488b658e2771efd870eff51df29f04ef", + "_from": "brace-expansion@>=1.0.0 <2.0.0", + "_npmVersion": "2.14.7", + "_nodeVersion": "4.2.1", + "_npmUser": { + "name": "juliangruber", + "email": "julian@juliangruber.com" + }, + "dist": { + "shasum": "f21445d0488b658e2771efd870eff51df29f04ef", + "tarball": "http://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.2.tgz" + }, + "maintainers": [ + { + "name": "juliangruber", + "email": "julian@juliangruber.com" + }, + { + "name": "isaacs", + "email": "isaacs@npmjs.com" + } + ], + "directories": {}, + "_resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.2.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/minimatch/package.json b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/package.json new file mode 100644 index 0000000..4944eb0 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/minimatch/package.json @@ -0,0 +1,60 @@ +{ + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me" + }, + "name": "minimatch", + "description": "a glob matcher in javascript", + "version": "3.0.0", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/minimatch.git" + }, + "main": "minimatch.js", + "scripts": { + "posttest": "standard minimatch.js test/*.js", + "test": "tap test/*.js" + }, + "engines": { + "node": "*" + }, + "dependencies": { + "brace-expansion": "^1.0.0" + }, + "devDependencies": { + "standard": "^3.7.2", + "tap": "^1.2.0" + }, + "license": "ISC", + "files": [ + "minimatch.js" + ], + "gitHead": "270dbea567f0af6918cb18103e98c612aa717a20", + "bugs": { + "url": "https://github.com/isaacs/minimatch/issues" + }, + "homepage": "https://github.com/isaacs/minimatch#readme", + "_id": "minimatch@3.0.0", + "_shasum": "5236157a51e4f004c177fb3c527ff7dd78f0ef83", + "_from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", + "_npmVersion": "3.3.2", + "_nodeVersion": "4.0.0", + "_npmUser": { + "name": "isaacs", + "email": "isaacs@npmjs.com" + }, + "dist": { + "shasum": "5236157a51e4f004c177fb3c527ff7dd78f0ef83", + "tarball": "http://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + } + ], + "directories": {}, + "_resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/once/LICENSE b/node_modules/rimraf/node_modules/glob/node_modules/once/LICENSE new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/once/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/rimraf/node_modules/glob/node_modules/once/README.md b/node_modules/rimraf/node_modules/glob/node_modules/once/README.md new file mode 100644 index 0000000..a2981ea --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/once/README.md @@ -0,0 +1,51 @@ +# once + +Only call a function once. + +## usage + +```javascript +var once = require('once') + +function load (file, cb) { + cb = once(cb) + loader.load('file') + loader.once('load', cb) + loader.once('error', cb) +} +``` + +Or add to the Function.prototype in a responsible way: + +```javascript +// only has to be done once +require('once').proto() + +function load (file, cb) { + cb = cb.once() + loader.load('file') + loader.once('load', cb) + loader.once('error', cb) +} +``` + +Ironically, the prototype feature makes this module twice as +complicated as necessary. + +To check whether you function has been called, use `fn.called`. Once the +function is called for the first time the return value of the original +function is saved in `fn.value` and subsequent calls will continue to +return this value. + +```javascript +var once = require('once') + +function load (cb) { + cb = once(cb) + var stream = createStream() + stream.once('data', cb) + stream.once('end', function () { + if (!cb.called) cb(new Error('not found')) + }) +} +``` diff --git a/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/LICENSE b/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/LICENSE new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/README.md b/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/README.md new file mode 100644 index 0000000..98eab25 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/README.md @@ -0,0 +1,36 @@ +# wrappy + +Callback wrapping utility + +## USAGE + +```javascript +var wrappy = require("wrappy") + +// var wrapper = wrappy(wrapperFunction) + +// make sure a cb is called only once +// See also: http://npm.im/once for this specific use case +var once = wrappy(function (cb) { + var called = false + return function () { + if (called) return + called = true + return cb.apply(this, arguments) + } +}) + +function printBoo () { + console.log('boo') +} +// has some rando property +printBoo.iAmBooPrinter = true + +var onlyPrintOnce = once(printBoo) + +onlyPrintOnce() // prints 'boo' +onlyPrintOnce() // does nothing + +// random property is retained! +assert.equal(onlyPrintOnce.iAmBooPrinter, true) +``` diff --git a/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/package.json b/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/package.json new file mode 100644 index 0000000..5a07040 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/package.json @@ -0,0 +1,52 @@ +{ + "name": "wrappy", + "version": "1.0.1", + "description": "Callback wrapping utility", + "main": "wrappy.js", + "directories": { + "test": "test" + }, + "dependencies": {}, + "devDependencies": { + "tap": "^0.4.12" + }, + "scripts": { + "test": "tap test/*.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/npm/wrappy.git" + }, + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "license": "ISC", + "bugs": { + "url": "https://github.com/npm/wrappy/issues" + }, + "homepage": "https://github.com/npm/wrappy", + "gitHead": "006a8cbac6b99988315834c207896eed71fd069a", + "_id": "wrappy@1.0.1", + "_shasum": "1e65969965ccbc2db4548c6b84a6f2c5aedd4739", + "_from": "wrappy@>=1.0.0 <2.0.0", + "_npmVersion": "2.0.0", + "_nodeVersion": "0.10.31", + "_npmUser": { + "name": "isaacs", + "email": "i@izs.me" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + } + ], + "dist": { + "shasum": "1e65969965ccbc2db4548c6b84a6f2c5aedd4739", + "tarball": "http://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + }, + "_resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/test/basic.js b/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/test/basic.js new file mode 100644 index 0000000..5ed0fcd --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/test/basic.js @@ -0,0 +1,51 @@ +var test = require('tap').test +var wrappy = require('../wrappy.js') + +test('basic', function (t) { + function onceifier (cb) { + var called = false + return function () { + if (called) return + called = true + return cb.apply(this, arguments) + } + } + onceifier.iAmOnce = {} + var once = wrappy(onceifier) + t.equal(once.iAmOnce, onceifier.iAmOnce) + + var called = 0 + function boo () { + t.equal(called, 0) + called++ + } + // has some rando property + boo.iAmBoo = true + + var onlyPrintOnce = once(boo) + + onlyPrintOnce() // prints 'boo' + onlyPrintOnce() // does nothing + t.equal(called, 1) + + // random property is retained! + t.equal(onlyPrintOnce.iAmBoo, true) + + var logs = [] + var logwrap = wrappy(function (msg, cb) { + logs.push(msg + ' wrapping cb') + return function () { + logs.push(msg + ' before cb') + var ret = cb.apply(this, arguments) + logs.push(msg + ' after cb') + } + }) + + var c = logwrap('foo', function () { + t.same(logs, [ 'foo wrapping cb', 'foo before cb' ]) + }) + c() + t.same(logs, [ 'foo wrapping cb', 'foo before cb', 'foo after cb' ]) + + t.end() +}) diff --git a/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/wrappy.js b/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/wrappy.js new file mode 100644 index 0000000..bb7e7d6 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/once/node_modules/wrappy/wrappy.js @@ -0,0 +1,33 @@ +// Returns a wrapper function that returns a wrapped callback +// The wrapper function should do some stuff, and return a +// presumably different callback function. +// This makes sure that own properties are retained, so that +// decorations and such are not lost along the way. +module.exports = wrappy +function wrappy (fn, cb) { + if (fn && cb) return wrappy(fn)(cb) + + if (typeof fn !== 'function') + throw new TypeError('need wrapper function') + + Object.keys(fn).forEach(function (k) { + wrapper[k] = fn[k] + }) + + return wrapper + + function wrapper() { + var args = new Array(arguments.length) + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i] + } + var ret = fn.apply(this, args) + var cb = args[args.length-1] + if (typeof ret === 'function' && ret !== cb) { + Object.keys(cb).forEach(function (k) { + ret[k] = cb[k] + }) + } + return ret + } +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/once/once.js b/node_modules/rimraf/node_modules/glob/node_modules/once/once.js new file mode 100644 index 0000000..2e1e721 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/once/once.js @@ -0,0 +1,21 @@ +var wrappy = require('wrappy') +module.exports = wrappy(once) + +once.proto = once(function () { + Object.defineProperty(Function.prototype, 'once', { + value: function () { + return once(this) + }, + configurable: true + }) +}) + +function once (fn) { + var f = function () { + if (f.called) return f.value + f.called = true + return f.value = fn.apply(this, arguments) + } + f.called = false + return f +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/once/package.json b/node_modules/rimraf/node_modules/glob/node_modules/once/package.json new file mode 100644 index 0000000..72f1388 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/once/package.json @@ -0,0 +1,63 @@ +{ + "name": "once", + "version": "1.3.3", + "description": "Run a function exactly one time", + "main": "once.js", + "directories": { + "test": "test" + }, + "dependencies": { + "wrappy": "1" + }, + "devDependencies": { + "tap": "^1.2.0" + }, + "scripts": { + "test": "tap test/*.js" + }, + "files": [ + "once.js" + ], + "repository": { + "type": "git", + "url": "git://github.com/isaacs/once.git" + }, + "keywords": [ + "once", + "function", + "one", + "single" + ], + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "license": "ISC", + "gitHead": "2ad558657e17fafd24803217ba854762842e4178", + "bugs": { + "url": "https://github.com/isaacs/once/issues" + }, + "homepage": "https://github.com/isaacs/once#readme", + "_id": "once@1.3.3", + "_shasum": "b2e261557ce4c314ec8304f3fa82663e4297ca20", + "_from": "once@>=1.3.0 <2.0.0", + "_npmVersion": "3.3.2", + "_nodeVersion": "4.0.0", + "_npmUser": { + "name": "isaacs", + "email": "i@izs.me" + }, + "dist": { + "shasum": "b2e261557ce4c314ec8304f3fa82663e4297ca20", + "tarball": "http://registry.npmjs.org/once/-/once-1.3.3.tgz" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + } + ], + "_resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/path-is-absolute/index.js b/node_modules/rimraf/node_modules/glob/node_modules/path-is-absolute/index.js new file mode 100644 index 0000000..19f103f --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/path-is-absolute/index.js @@ -0,0 +1,20 @@ +'use strict'; + +function posix(path) { + return path.charAt(0) === '/'; +}; + +function win32(path) { + // https://github.com/joyent/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56 + var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; + var result = splitDeviceRe.exec(path); + var device = result[1] || ''; + var isUnc = !!device && device.charAt(1) !== ':'; + + // UNC paths are always absolute + return !!result[2] || isUnc; +}; + +module.exports = process.platform === 'win32' ? win32 : posix; +module.exports.posix = posix; +module.exports.win32 = win32; diff --git a/node_modules/rimraf/node_modules/glob/node_modules/path-is-absolute/license b/node_modules/rimraf/node_modules/glob/node_modules/path-is-absolute/license new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/path-is-absolute/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/rimraf/node_modules/glob/node_modules/path-is-absolute/package.json b/node_modules/rimraf/node_modules/glob/node_modules/path-is-absolute/package.json new file mode 100644 index 0000000..3937263 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/path-is-absolute/package.json @@ -0,0 +1,70 @@ +{ + "name": "path-is-absolute", + "version": "1.0.0", + "description": "Node.js 0.12 path.isAbsolute() ponyfill", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/path-is-absolute.git" + }, + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "node test.js" + }, + "files": [ + "index.js" + ], + "keywords": [ + "path", + "paths", + "file", + "dir", + "absolute", + "isabsolute", + "is-absolute", + "built-in", + "util", + "utils", + "core", + "ponyfill", + "polyfill", + "shim", + "is", + "detect", + "check" + ], + "gitHead": "7a76a0c9f2263192beedbe0a820e4d0baee5b7a1", + "bugs": { + "url": "https://github.com/sindresorhus/path-is-absolute/issues" + }, + "homepage": "https://github.com/sindresorhus/path-is-absolute", + "_id": "path-is-absolute@1.0.0", + "_shasum": "263dada66ab3f2fb10bf7f9d24dd8f3e570ef912", + "_from": "path-is-absolute@>=1.0.0 <2.0.0", + "_npmVersion": "2.5.1", + "_nodeVersion": "0.12.0", + "_npmUser": { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + } + ], + "dist": { + "shasum": "263dada66ab3f2fb10bf7f9d24dd8f3e570ef912", + "tarball": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/rimraf/node_modules/glob/node_modules/path-is-absolute/readme.md b/node_modules/rimraf/node_modules/glob/node_modules/path-is-absolute/readme.md new file mode 100644 index 0000000..cdf94f4 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/node_modules/path-is-absolute/readme.md @@ -0,0 +1,51 @@ +# path-is-absolute [![Build Status](https://travis-ci.org/sindresorhus/path-is-absolute.svg?branch=master)](https://travis-ci.org/sindresorhus/path-is-absolute) + +> Node.js 0.12 [`path.isAbsolute()`](http://nodejs.org/api/path.html#path_path_isabsolute_path) ponyfill + +> Ponyfill: A polyfill that doesn't overwrite the native method + + +## Install + +``` +$ npm install --save path-is-absolute +``` + + +## Usage + +```js +var pathIsAbsolute = require('path-is-absolute'); + +// Linux +pathIsAbsolute('/home/foo'); +//=> true + +// Windows +pathIsAbsolute('C:/Users/'); +//=> true + +// Any OS +pathIsAbsolute.posix('/home/foo'); +//=> true +``` + + +## API + +See the [`path.isAbsolute()` docs](http://nodejs.org/api/path.html#path_path_isabsolute_path). + +### pathIsAbsolute(path) + +### pathIsAbsolute.posix(path) + +The Posix specific version. + +### pathIsAbsolute.win32(path) + +The Windows specific version. + + +## License + +MIT © [Sindre Sorhus](http://sindresorhus.com) diff --git a/node_modules/rimraf/node_modules/glob/package.json b/node_modules/rimraf/node_modules/glob/package.json new file mode 100644 index 0000000..bc35810 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/package.json @@ -0,0 +1,73 @@ +{ + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "name": "glob", + "description": "a little globber", + "version": "5.0.15", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/node-glob.git" + }, + "main": "glob.js", + "files": [ + "glob.js", + "sync.js", + "common.js" + ], + "engines": { + "node": "*" + }, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "devDependencies": { + "mkdirp": "0", + "rimraf": "^2.2.8", + "tap": "^1.1.4", + "tick": "0.0.6" + }, + "scripts": { + "prepublish": "npm run benchclean", + "profclean": "rm -f v8.log profile.txt", + "test": "tap test/*.js --cov", + "test-regen": "npm run profclean && TEST_REGEN=1 node test/00-setup.js", + "bench": "bash benchmark.sh", + "prof": "bash prof.sh && cat profile.txt", + "benchclean": "node benchclean.js" + }, + "license": "ISC", + "gitHead": "3a7e71d453dd80e75b196fd262dd23ed54beeceb", + "bugs": { + "url": "https://github.com/isaacs/node-glob/issues" + }, + "homepage": "https://github.com/isaacs/node-glob#readme", + "_id": "glob@5.0.15", + "_shasum": "1bc936b9e02f4a603fcc222ecf7633d30b8b93b1", + "_from": "glob@>=5.0.14 <6.0.0", + "_npmVersion": "3.3.2", + "_nodeVersion": "4.0.0", + "_npmUser": { + "name": "isaacs", + "email": "isaacs@npmjs.com" + }, + "dist": { + "shasum": "1bc936b9e02f4a603fcc222ecf7633d30b8b93b1", + "tarball": "http://registry.npmjs.org/glob/-/glob-5.0.15.tgz" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + } + ], + "directories": {}, + "_resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/rimraf/node_modules/glob/sync.js b/node_modules/rimraf/node_modules/glob/sync.js new file mode 100644 index 0000000..09883d2 --- /dev/null +++ b/node_modules/rimraf/node_modules/glob/sync.js @@ -0,0 +1,460 @@ +module.exports = globSync +globSync.GlobSync = GlobSync + +var fs = require('fs') +var minimatch = require('minimatch') +var Minimatch = minimatch.Minimatch +var Glob = require('./glob.js').Glob +var util = require('util') +var path = require('path') +var assert = require('assert') +var isAbsolute = require('path-is-absolute') +var common = require('./common.js') +var alphasort = common.alphasort +var alphasorti = common.alphasorti +var setopts = common.setopts +var ownProp = common.ownProp +var childrenIgnored = common.childrenIgnored + +function globSync (pattern, options) { + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') + + return new GlobSync(pattern, options).found +} + +function GlobSync (pattern, options) { + if (!pattern) + throw new Error('must provide pattern') + + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') + + if (!(this instanceof GlobSync)) + return new GlobSync(pattern, options) + + setopts(this, pattern, options) + + if (this.noprocess) + return this + + var n = this.minimatch.set.length + this.matches = new Array(n) + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false) + } + this._finish() +} + +GlobSync.prototype._finish = function () { + assert(this instanceof GlobSync) + if (this.realpath) { + var self = this + this.matches.forEach(function (matchset, index) { + var set = self.matches[index] = Object.create(null) + for (var p in matchset) { + try { + p = self._makeAbs(p) + var real = fs.realpathSync(p, self.realpathCache) + set[real] = true + } catch (er) { + if (er.syscall === 'stat') + set[self._makeAbs(p)] = true + else + throw er + } + } + }) + } + common.finish(this) +} + + +GlobSync.prototype._process = function (pattern, index, inGlobStar) { + assert(this instanceof GlobSync) + + // Get the first [n] parts of pattern that are all strings. + var n = 0 + while (typeof pattern[n] === 'string') { + n ++ + } + // now n is the index of the first one that is *not* a string. + + // See if there's anything else + var prefix + switch (n) { + // if not, then this is rather simple + case pattern.length: + this._processSimple(pattern.join('/'), index) + return + + case 0: + // pattern *starts* with some non-trivial item. + // going to readdir(cwd), but not include the prefix in matches. + prefix = null + break + + default: + // pattern has some string bits in the front. + // whatever it starts with, whether that's 'absolute' like /foo/bar, + // or 'relative' like '../baz' + prefix = pattern.slice(0, n).join('/') + break + } + + var remain = pattern.slice(n) + + // get the list of entries. + var read + if (prefix === null) + read = '.' + else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { + if (!prefix || !isAbsolute(prefix)) + prefix = '/' + prefix + read = prefix + } else + read = prefix + + var abs = this._makeAbs(read) + + //if ignored, skip processing + if (childrenIgnored(this, read)) + return + + var isGlobStar = remain[0] === minimatch.GLOBSTAR + if (isGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar) + else + this._processReaddir(prefix, read, abs, remain, index, inGlobStar) +} + + +GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) { + var entries = this._readdir(abs, inGlobStar) + + // if the abs isn't a dir, then nothing can match! + if (!entries) + return + + // It will only match dot entries if it starts with a dot, or if + // dot is set. Stuff like @(.foo|.bar) isn't allowed. + var pn = remain[0] + var negate = !!this.minimatch.negate + var rawGlob = pn._glob + var dotOk = this.dot || rawGlob.charAt(0) === '.' + + var matchedEntries = [] + for (var i = 0; i < entries.length; i++) { + var e = entries[i] + if (e.charAt(0) !== '.' || dotOk) { + var m + if (negate && !prefix) { + m = !e.match(pn) + } else { + m = e.match(pn) + } + if (m) + matchedEntries.push(e) + } + } + + var len = matchedEntries.length + // If there are no matched entries, then nothing matches. + if (len === 0) + return + + // if this is the last remaining pattern bit, then no need for + // an additional stat *unless* the user has specified mark or + // stat explicitly. We know they exist, since readdir returned + // them. + + if (remain.length === 1 && !this.mark && !this.stat) { + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + if (prefix) { + if (prefix.slice(-1) !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + + if (e.charAt(0) === '/' && !this.nomount) { + e = path.join(this.root, e) + } + this.matches[index][e] = true + } + // This was the last one, and no stats were needed + return + } + + // now test all matched entries as stand-ins for that part + // of the pattern. + remain.shift() + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + var newPattern + if (prefix) + newPattern = [prefix, e] + else + newPattern = [e] + this._process(newPattern.concat(remain), index, inGlobStar) + } +} + + +GlobSync.prototype._emitMatch = function (index, e) { + var abs = this._makeAbs(e) + if (this.mark) + e = this._mark(e) + + if (this.matches[index][e]) + return + + if (this.nodir) { + var c = this.cache[this._makeAbs(e)] + if (c === 'DIR' || Array.isArray(c)) + return + } + + this.matches[index][e] = true + if (this.stat) + this._stat(e) +} + + +GlobSync.prototype._readdirInGlobStar = function (abs) { + // follow all symlinked directories forever + // just proceed as if this is a non-globstar situation + if (this.follow) + return this._readdir(abs, false) + + var entries + var lstat + var stat + try { + lstat = fs.lstatSync(abs) + } catch (er) { + // lstat failed, doesn't exist + return null + } + + var isSym = lstat.isSymbolicLink() + this.symlinks[abs] = isSym + + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && !lstat.isDirectory()) + this.cache[abs] = 'FILE' + else + entries = this._readdir(abs, false) + + return entries +} + +GlobSync.prototype._readdir = function (abs, inGlobStar) { + var entries + + if (inGlobStar && !ownProp(this.symlinks, abs)) + return this._readdirInGlobStar(abs) + + if (ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (!c || c === 'FILE') + return null + + if (Array.isArray(c)) + return c + } + + try { + return this._readdirEntries(abs, fs.readdirSync(abs)) + } catch (er) { + this._readdirError(abs, er) + return null + } +} + +GlobSync.prototype._readdirEntries = function (abs, entries) { + // if we haven't asked to stat everything, then just + // assume that everything in there exists, so we can avoid + // having to stat it a second time. + if (!this.mark && !this.stat) { + for (var i = 0; i < entries.length; i ++) { + var e = entries[i] + if (abs === '/') + e = abs + e + else + e = abs + '/' + e + this.cache[e] = true + } + } + + this.cache[abs] = entries + + // mark and cache dir-ness + return entries +} + +GlobSync.prototype._readdirError = function (f, er) { + // handle errors, and cache the information + switch (er.code) { + case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 + case 'ENOTDIR': // totally normal. means it *does* exist. + this.cache[this._makeAbs(f)] = 'FILE' + break + + case 'ENOENT': // not terribly unusual + case 'ELOOP': + case 'ENAMETOOLONG': + case 'UNKNOWN': + this.cache[this._makeAbs(f)] = false + break + + default: // some unusual error. Treat as failure. + this.cache[this._makeAbs(f)] = false + if (this.strict) + throw er + if (!this.silent) + console.error('glob error', er) + break + } +} + +GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) { + + var entries = this._readdir(abs, inGlobStar) + + // no entries means not a dir, so it can never have matches + // foo.txt/** doesn't match foo.txt + if (!entries) + return + + // test without the globstar, and with every child both below + // and replacing the globstar. + var remainWithoutGlobStar = remain.slice(1) + var gspref = prefix ? [ prefix ] : [] + var noGlobStar = gspref.concat(remainWithoutGlobStar) + + // the noGlobStar pattern exits the inGlobStar state + this._process(noGlobStar, index, false) + + var len = entries.length + var isSym = this.symlinks[abs] + + // If it's a symlink, and we're in a globstar, then stop + if (isSym && inGlobStar) + return + + for (var i = 0; i < len; i++) { + var e = entries[i] + if (e.charAt(0) === '.' && !this.dot) + continue + + // these two cases enter the inGlobStar state + var instead = gspref.concat(entries[i], remainWithoutGlobStar) + this._process(instead, index, true) + + var below = gspref.concat(entries[i], remain) + this._process(below, index, true) + } +} + +GlobSync.prototype._processSimple = function (prefix, index) { + // XXX review this. Shouldn't it be doing the mounting etc + // before doing stat? kinda weird? + var exists = this._stat(prefix) + + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + // If it doesn't exist, then just mark the lack of results + if (!exists) + return + + if (prefix && isAbsolute(prefix) && !this.nomount) { + var trail = /[\/\\]$/.test(prefix) + if (prefix.charAt(0) === '/') { + prefix = path.join(this.root, prefix) + } else { + prefix = path.resolve(this.root, prefix) + if (trail) + prefix += '/' + } + } + + if (process.platform === 'win32') + prefix = prefix.replace(/\\/g, '/') + + // Mark this as a match + this.matches[index][prefix] = true +} + +// Returns either 'DIR', 'FILE', or false +GlobSync.prototype._stat = function (f) { + var abs = this._makeAbs(f) + var needDir = f.slice(-1) === '/' + + if (f.length > this.maxLength) + return false + + if (!this.stat && ownProp(this.cache, abs)) { + var c = this.cache[abs] + + if (Array.isArray(c)) + c = 'DIR' + + // It exists, but maybe not how we need it + if (!needDir || c === 'DIR') + return c + + if (needDir && c === 'FILE') + return false + + // otherwise we have to stat, because maybe c=true + // if we know it exists, but not what it is. + } + + var exists + var stat = this.statCache[abs] + if (!stat) { + var lstat + try { + lstat = fs.lstatSync(abs) + } catch (er) { + return false + } + + if (lstat.isSymbolicLink()) { + try { + stat = fs.statSync(abs) + } catch (er) { + stat = lstat + } + } else { + stat = lstat + } + } + + this.statCache[abs] = stat + + var c = stat.isDirectory() ? 'DIR' : 'FILE' + this.cache[abs] = this.cache[abs] || c + + if (needDir && c !== 'DIR') + return false + + return c +} + +GlobSync.prototype._mark = function (p) { + return common.mark(this, p) +} + +GlobSync.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) +} diff --git a/node_modules/rimraf/package.json b/node_modules/rimraf/package.json new file mode 100644 index 0000000..b8cac60 --- /dev/null +++ b/node_modules/rimraf/package.json @@ -0,0 +1,62 @@ +{ + "name": "rimraf", + "version": "2.4.4", + "main": "rimraf.js", + "description": "A deep deletion module for node (like `rm -rf`)", + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "license": "ISC", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/rimraf.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "bin": { + "rimraf": "./bin.js" + }, + "dependencies": { + "glob": "^5.0.14" + }, + "files": [ + "LICENSE", + "README.md", + "bin.js", + "rimraf.js" + ], + "devDependencies": { + "mkdirp": "^0.5.1", + "tap": "^1.3.1" + }, + "gitHead": "62ae8a4037e7190691eeab35265aed1c768d23e3", + "bugs": { + "url": "https://github.com/isaacs/rimraf/issues" + }, + "homepage": "https://github.com/isaacs/rimraf#readme", + "_id": "rimraf@2.4.4", + "_shasum": "b528ce2ebe0e6d89fb03b265de11d61da0dbcf82", + "_from": "rimraf@>=2.2.8 <3.0.0", + "_npmVersion": "3.3.2", + "_nodeVersion": "4.0.0", + "_npmUser": { + "name": "isaacs", + "email": "i@izs.me" + }, + "dist": { + "shasum": "b528ce2ebe0e6d89fb03b265de11d61da0dbcf82", + "tarball": "http://registry.npmjs.org/rimraf/-/rimraf-2.4.4.tgz" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + } + ], + "directories": {}, + "_resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.4.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/rimraf/rimraf.js b/node_modules/rimraf/rimraf.js new file mode 100644 index 0000000..7771b53 --- /dev/null +++ b/node_modules/rimraf/rimraf.js @@ -0,0 +1,333 @@ +module.exports = rimraf +rimraf.sync = rimrafSync + +var assert = require("assert") +var path = require("path") +var fs = require("fs") +var glob = require("glob") + +var globOpts = { + nosort: true, + nocomment: true, + nonegate: true, + silent: true +} + +// for EMFILE handling +var timeout = 0 + +var isWindows = (process.platform === "win32") + +function defaults (options) { + var methods = [ + 'unlink', + 'chmod', + 'stat', + 'lstat', + 'rmdir', + 'readdir' + ] + methods.forEach(function(m) { + options[m] = options[m] || fs[m] + m = m + 'Sync' + options[m] = options[m] || fs[m] + }) + + options.maxBusyTries = options.maxBusyTries || 3 + options.emfileWait = options.emfileWait || 1000 + options.disableGlob = options.disableGlob || false +} + +function rimraf (p, options, cb) { + if (typeof options === 'function') { + cb = options + options = {} + } + + assert(p, 'rimraf: missing path') + assert.equal(typeof p, 'string', 'rimraf: path should be a string') + assert(options, 'rimraf: missing options') + assert.equal(typeof options, 'object', 'rimraf: options should be object') + assert.equal(typeof cb, 'function', 'rimraf: callback function required') + + defaults(options) + + var busyTries = 0 + var errState = null + var n = 0 + + if (options.disableGlob || !glob.hasMagic(p)) + return afterGlob(null, [p]) + + fs.lstat(p, function (er, stat) { + if (!er) + return afterGlob(null, [p]) + + glob(p, globOpts, afterGlob) + }) + + function next (er) { + errState = errState || er + if (--n === 0) + cb(errState) + } + + function afterGlob (er, results) { + if (er) + return cb(er) + + n = results.length + if (n === 0) + return cb() + + results.forEach(function (p) { + rimraf_(p, options, function CB (er) { + if (er) { + if (isWindows && (er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") && + busyTries < options.maxBusyTries) { + busyTries ++ + var time = busyTries * 100 + // try again, with the same exact callback as this one. + return setTimeout(function () { + rimraf_(p, options, CB) + }, time) + } + + // this one won't happen if graceful-fs is used. + if (er.code === "EMFILE" && timeout < options.emfileWait) { + return setTimeout(function () { + rimraf_(p, options, CB) + }, timeout ++) + } + + // already gone + if (er.code === "ENOENT") er = null + } + + timeout = 0 + next(er) + }) + }) + } +} + +// Two possible strategies. +// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR +// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR +// +// Both result in an extra syscall when you guess wrong. However, there +// are likely far more normal files in the world than directories. This +// is based on the assumption that a the average number of files per +// directory is >= 1. +// +// If anyone ever complains about this, then I guess the strategy could +// be made configurable somehow. But until then, YAGNI. +function rimraf_ (p, options, cb) { + assert(p) + assert(options) + assert(typeof cb === 'function') + + // sunos lets the root user unlink directories, which is... weird. + // so we have to lstat here and make sure it's not a dir. + options.lstat(p, function (er, st) { + if (er && er.code === "ENOENT") + return cb(null) + + if (st && st.isDirectory()) + return rmdir(p, options, er, cb) + + options.unlink(p, function (er) { + if (er) { + if (er.code === "ENOENT") + return cb(null) + if (er.code === "EPERM") + return (isWindows) + ? fixWinEPERM(p, options, er, cb) + : rmdir(p, options, er, cb) + if (er.code === "EISDIR") + return rmdir(p, options, er, cb) + } + return cb(er) + }) + }) +} + +function fixWinEPERM (p, options, er, cb) { + assert(p) + assert(options) + assert(typeof cb === 'function') + if (er) + assert(er instanceof Error) + + options.chmod(p, 666, function (er2) { + if (er2) + cb(er2.code === "ENOENT" ? null : er) + else + options.stat(p, function(er3, stats) { + if (er3) + cb(er3.code === "ENOENT" ? null : er) + else if (stats.isDirectory()) + rmdir(p, options, er, cb) + else + options.unlink(p, cb) + }) + }) +} + +function fixWinEPERMSync (p, options, er) { + assert(p) + assert(options) + if (er) + assert(er instanceof Error) + + try { + options.chmodSync(p, 666) + } catch (er2) { + if (er2.code === "ENOENT") + return + else + throw er + } + + try { + var stats = options.statSync(p) + } catch (er3) { + if (er3.code === "ENOENT") + return + else + throw er + } + + if (stats.isDirectory()) + rmdirSync(p, options, er) + else + options.unlinkSync(p) +} + +function rmdir (p, options, originalEr, cb) { + assert(p) + assert(options) + if (originalEr) + assert(originalEr instanceof Error) + assert(typeof cb === 'function') + + // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS) + // if we guessed wrong, and it's not a directory, then + // raise the original error. + options.rmdir(p, function (er) { + if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")) + rmkids(p, options, cb) + else if (er && er.code === "ENOTDIR") + cb(originalEr) + else + cb(er) + }) +} + +function rmkids(p, options, cb) { + assert(p) + assert(options) + assert(typeof cb === 'function') + + options.readdir(p, function (er, files) { + if (er) + return cb(er) + var n = files.length + if (n === 0) + return options.rmdir(p, cb) + var errState + files.forEach(function (f) { + rimraf(path.join(p, f), options, function (er) { + if (errState) + return + if (er) + return cb(errState = er) + if (--n === 0) + options.rmdir(p, cb) + }) + }) + }) +} + +// this looks simpler, and is strictly *faster*, but will +// tie up the JavaScript thread and fail on excessively +// deep directory trees. +function rimrafSync (p, options) { + options = options || {} + defaults(options) + + assert(p, 'rimraf: missing path') + assert.equal(typeof p, 'string', 'rimraf: path should be a string') + assert(options, 'rimraf: missing options') + assert.equal(typeof options, 'object', 'rimraf: options should be object') + + var results + + if (options.disableGlob || !glob.hasMagic(p)) { + results = [p] + } else { + try { + fs.lstatSync(p) + results = [p] + } catch (er) { + results = glob.sync(p, globOpts) + } + } + + if (!results.length) + return + + for (var i = 0; i < results.length; i++) { + var p = results[i] + + try { + var st = options.lstatSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + } + + try { + // sunos lets the root user unlink directories, which is... weird. + if (st && st.isDirectory()) + rmdirSync(p, options, null) + else + options.unlinkSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + if (er.code === "EPERM") + return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er) + if (er.code !== "EISDIR") + throw er + rmdirSync(p, options, er) + } + } +} + +function rmdirSync (p, options, originalEr) { + assert(p) + assert(options) + if (originalEr) + assert(originalEr instanceof Error) + + try { + options.rmdirSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + if (er.code === "ENOTDIR") + throw originalEr + if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM") + rmkidsSync(p, options) + } +} + +function rmkidsSync (p, options) { + assert(p) + assert(options) + options.readdirSync(p).forEach(function (f) { + rimrafSync(path.join(p, f), options) + }) + options.rmdirSync(p, options) +} diff --git a/node_modules/serve-index/HISTORY.md b/node_modules/serve-index/HISTORY.md new file mode 100644 index 0000000..391d220 --- /dev/null +++ b/node_modules/serve-index/HISTORY.md @@ -0,0 +1,235 @@ +1.7.2 / 2015-07-30 +================== + + * deps: accepts@~1.2.12 + - deps: mime-types@~2.1.4 + * deps: mime-types@~2.1.4 + - Add new mime types + +1.7.1 / 2015-07-05 +================== + + * deps: accepts@~1.2.10 + - deps: mime-types@~2.1.2 + * deps: mime-types@~2.1.2 + - Add new mime types + +1.7.0 / 2015-06-15 +================== + + * Accept `function` value for `template` option + * Send non-chunked response for `OPTIONS` + * Stat parent directory when necessary + * Use `Date.prototype.toLocaleDateString` to format date + * deps: accepts@~1.2.9 + - deps: mime-types@~2.1.1 + - deps: negotiator@0.5.3 + - perf: avoid argument reassignment & argument slice + - perf: avoid negotiator recursive construction + - perf: enable strict mode + - perf: remove unnecessary bitwise operator + * deps: escape-html@1.0.2 + * deps: mime-types@~2.1.1 + - Add new mime types + * perf: enable strict mode + * perf: remove argument reassignment + +1.6.4 / 2015-05-12 +================== + + * deps: accepts@~1.2.7 + - deps: mime-types@~2.0.11 + - deps: negotiator@0.5.3 + * deps: debug@~2.2.0 + - deps: ms@0.7.1 + * deps: mime-types@~2.0.11 + - Add new mime types + +1.6.3 / 2015-03-13 +================== + + * Properly escape file names in HTML + * deps: accepts@~1.2.5 + - deps: mime-types@~2.0.10 + * deps: debug@~2.1.3 + - Fix high intensity foreground color for bold + - deps: ms@0.7.0 + * deps: escape-html@1.0.1 + * deps: mime-types@~2.0.10 + - Add new mime types + +1.6.2 / 2015-02-16 +================== + + * deps: accepts@~1.2.4 + - deps: mime-types@~2.0.9 + - deps: negotiator@0.5.1 + * deps: http-errors@~1.3.1 + - Construct errors using defined constructors from `createError` + - Fix error names that are not identifiers + - Set a meaningful `name` property on constructed errors + * deps: mime-types@~2.0.9 + - Add new mime types + - deps: mime-db@~1.7.0 + +1.6.1 / 2015-01-31 +================== + + * deps: accepts@~1.2.3 + - deps: mime-types@~2.0.8 + * deps: mime-types@~2.0.8 + - Add new mime types + - deps: mime-db@~1.6.0 + +1.6.0 / 2015-01-01 +================== + + * Add link to root directory + * deps: accepts@~1.2.2 + - deps: mime-types@~2.0.7 + - deps: negotiator@0.5.0 + * deps: batch@0.5.2 + * deps: debug@~2.1.1 + * deps: mime-types@~2.0.7 + - Add new mime types + - Fix missing extensions + - Fix various invalid MIME type entries + - Remove example template MIME types + - deps: mime-db@~1.5.0 + +1.5.3 / 2014-12-10 +================== + + * deps: accepts@~1.1.4 + - deps: mime-types@~2.0.4 + * deps: http-errors@~1.2.8 + - Fix stack trace from exported function + * deps: mime-types@~2.0.4 + - Add new mime types + - deps: mime-db@~1.3.0 + +1.5.2 / 2014-12-03 +================== + + * Fix icon name background alignment on mobile view + +1.5.1 / 2014-11-22 +================== + + * deps: accepts@~1.1.3 + - deps: mime-types@~2.0.3 + * deps: mime-types@~2.0.3 + - Add new mime types + - deps: mime-db@~1.2.0 + +1.5.0 / 2014-10-16 +================== + + * Create errors with `http-errors` + * deps: debug@~2.1.0 + - Implement `DEBUG_FD` env variable support + * deps: mime-types@~2.0.2 + - deps: mime-db@~1.1.0 + +1.4.1 / 2014-10-15 +================== + + * deps: accepts@~1.1.2 + - Fix error when media type has invalid parameter + - deps: negotiator@0.4.9 + +1.4.0 / 2014-10-03 +================== + + * Add `dir` argument to `filter` function + * Support using tokens multiple times + +1.3.1 / 2014-10-01 +================== + + * Fix incorrect 403 on Windows and Node.js 0.11 + * deps: accepts@~1.1.1 + - deps: mime-types@~2.0.2 + - deps: negotiator@0.4.8 + +1.3.0 / 2014-09-20 +================== + + * Add icon for mkv files + * Lookup icon by mime type for greater icon support + +1.2.1 / 2014-09-05 +================== + + * deps: accepts@~1.1.0 + * deps: debug@~2.0.0 + +1.2.0 / 2014-08-25 +================== + + * Add `debug` messages + * Resolve relative paths at middleware setup + +1.1.6 / 2014-08-10 +================== + + * Fix URL parsing + * deps: parseurl@~1.3.0 + +1.1.5 / 2014-07-27 +================== + + * Fix Content-Length calculation for multi-byte file names + * deps: accepts@~1.0.7 + - deps: negotiator@0.4.7 + +1.1.4 / 2014-06-20 +================== + + * deps: accepts@~1.0.5 + +1.1.3 / 2014-06-20 +================== + + * deps: accepts@~1.0.4 + - use `mime-types` + +1.1.2 / 2014-06-19 +================== + + * deps: batch@0.5.1 + +1.1.1 / 2014-06-11 +================== + + * deps: accepts@1.0.3 + +1.1.0 / 2014-05-29 +================== + + * Fix content negotiation when no `Accept` header + * Properly support all HTTP methods + * Support vanilla node.js http servers + * Treat `ENAMETOOLONG` as code 414 + * Use accepts for negotiation + +1.0.3 / 2014-05-20 +================== + + * Fix error from non-statable files in HTML view + +1.0.2 / 2014-04-28 +================== + + * Add `stylesheet` option + * deps: negotiator@0.4.3 + +1.0.1 / 2014-03-05 +================== + + * deps: negotiator@0.4.2 + +1.0.0 / 2014-03-05 +================== + + * Genesis from connect diff --git a/node_modules/serve-index/LICENSE b/node_modules/serve-index/LICENSE new file mode 100644 index 0000000..d8cce67 --- /dev/null +++ b/node_modules/serve-index/LICENSE @@ -0,0 +1,25 @@ +(The MIT License) + +Copyright (c) 2010 Sencha Inc. +Copyright (c) 2011 LearnBoost +Copyright (c) 2011 TJ Holowaychuk +Copyright (c) 2014-2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/serve-index/README.md b/node_modules/serve-index/README.md new file mode 100644 index 0000000..5dde837 --- /dev/null +++ b/node_modules/serve-index/README.md @@ -0,0 +1,146 @@ +# serve-index + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Linux Build][travis-image]][travis-url] +[![Windows Build][appveyor-image]][appveyor-url] +[![Test Coverage][coveralls-image]][coveralls-url] +[![Gratipay][gratipay-image]][gratipay-url] + + Serves pages that contain directory listings for a given path. + +## Install + +```sh +$ npm install serve-index +``` + +## API + +```js +var serveIndex = require('serve-index') +``` + +### serveIndex(path, options) + +Returns middlware that serves an index of the directory in the given `path`. + +The `path` is based off the `req.url` value, so a `req.url` of `'/some/dir` +with a `path` of `'public'` will look at `'public/some/dir'`. If you are using +something like `express`, you can change the URL "base" with `app.use` (see +the express example). + +#### Options + +Serve index accepts these properties in the options object. + +##### filter + +Apply this filter function to files. Defaults to `false`. The `filter` function +is called for each file, with the signature `filter(filename, index, files, dir)` +where `filename` is the name of the file, `index` is the array index, `files` is +the array of files and `dir` is the absolute path the file is located (and thus, +the directory the listing is for). + +##### hidden + +Display hidden (dot) files. Defaults to `false`. + +##### icons + +Display icons. Defaults to `false`. + +##### stylesheet + +Optional path to a CSS stylesheet. Defaults to a built-in stylesheet. + +##### template + +Optional path to an HTML template or a function that will render a HTML +string. Defaults to a built-in template. + +When given a string, the string is used as a file path to load and then the +following tokens are replaced in templates: + + * `{directory}` with the name of the directory. + * `{files}` with the HTML of an unordered list of file links. + * `{linked-path}` with the HTML of a link to the directory. + * `{style}` with the specified stylesheet and embedded images. + +When given as a function, the function is called as `template(locals, callback)` +and it needs to invoke `callback(error, htmlString)`. The following are the +provided locals: + + * `directory` is the directory being displayed (where `/` is the root). + * `displayIcons` is a Boolean for if icons should be rendered or not. + * `fileList` is a sorted array of files in the directory. The array contains + objects with the following properties: + - `name` is the relative name for the file. + - `stat` is a `fs.Stats` object for the file. + * `path` is the full filesystem path to `directory`. + * `style` is the default stylesheet or the contents of the `stylesheet` option. + * `viewName` is the view name provided by the `view` option. + +##### view + +Display mode. `tiles` and `details` are available. Defaults to `tiles`. + +## Examples + +### Serve directory indexes with vanilla node.js http server + +```js +var finalhandler = require('finalhandler') +var http = require('http') +var serveIndex = require('serve-index') +var serveStatic = require('serve-static') + +// Serve directory indexes for public/ftp folder (with icons) +var index = serveIndex('public/ftp', {'icons': true}) + +// Serve up public/ftp folder files +var serve = serveStatic('public/ftp') + +// Create server +var server = http.createServer(function onRequest(req, res){ + var done = finalhandler(req, res) + serve(req, res, function onNext(err) { + if (err) return done(err) + index(req, res, done) + }) +}) + +// Listen +server.listen(3000) +``` + +### Serve directory indexes with express + +```js +var express = require('express') +var serveIndex = require('serve-index') + +var app = express() + +// Serve URLs like /ftp/thing as public/ftp/thing +app.use('/ftp', serveIndex('public/ftp', {'icons': true})) +app.listen() +``` + +## License + +[MIT](LICENSE). The [Silk](http://www.famfamfam.com/lab/icons/silk/) icons +are created by/copyright of [FAMFAMFAM](http://www.famfamfam.com/). + +[npm-image]: https://img.shields.io/npm/v/serve-index.svg +[npm-url]: https://npmjs.org/package/serve-index +[travis-image]: https://img.shields.io/travis/expressjs/serve-index/master.svg?label=linux +[travis-url]: https://travis-ci.org/expressjs/serve-index +[appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/serve-index/master.svg?label=windows +[appveyor-url]: https://ci.appveyor.com/project/dougwilson/serve-index +[coveralls-image]: https://img.shields.io/coveralls/expressjs/serve-index/master.svg +[coveralls-url]: https://coveralls.io/r/expressjs/serve-index?branch=master +[downloads-image]: https://img.shields.io/npm/dm/serve-index.svg +[downloads-url]: https://npmjs.org/package/serve-index +[gratipay-image]: https://img.shields.io/gratipay/dougwilson.svg +[gratipay-url]: https://www.gratipay.com/dougwilson/ diff --git a/node_modules/serve-index/index.js b/node_modules/serve-index/index.js new file mode 100644 index 0000000..7b9d856 --- /dev/null +++ b/node_modules/serve-index/index.js @@ -0,0 +1,645 @@ +/*! + * serve-index + * Copyright(c) 2011 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Module dependencies. + * @private + */ + +var accepts = require('accepts'); +var createError = require('http-errors'); +var debug = require('debug')('serve-index'); +var escapeHtml = require('escape-html'); +var fs = require('fs') + , path = require('path') + , normalize = path.normalize + , sep = path.sep + , extname = path.extname + , join = path.join; +var Batch = require('batch'); +var mime = require('mime-types'); +var parseUrl = require('parseurl'); +var resolve = require('path').resolve; + +/** + * Module exports. + * @public + */ + +module.exports = serveIndex; + +/*! + * Icon cache. + */ + +var cache = {}; + +/*! + * Default template. + */ + +var defaultTemplate = join(__dirname, 'public', 'directory.html'); + +/*! + * Stylesheet. + */ + +var defaultStylesheet = join(__dirname, 'public', 'style.css'); + +/** + * Media types and the map for content negotiation. + */ + +var mediaTypes = [ + 'text/html', + 'text/plain', + 'application/json' +]; + +var mediaType = { + 'text/html': 'html', + 'text/plain': 'plain', + 'application/json': 'json' +}; + +/** + * Serve directory listings with the given `root` path. + * + * See Readme.md for documentation of options. + * + * @param {String} root + * @param {Object} options + * @return {Function} middleware + * @public + */ + +function serveIndex(root, options) { + var opts = options || {}; + + // root required + if (!root) { + throw new TypeError('serveIndex() root path required'); + } + + // resolve root to absolute and normalize + var rootPath = normalize(resolve(root) + sep); + + var filter = opts.filter; + var hidden = opts.hidden; + var icons = opts.icons; + var stylesheet = opts.stylesheet || defaultStylesheet; + var template = opts.template || defaultTemplate; + var view = opts.view || 'tiles'; + + return function (req, res, next) { + if (req.method !== 'GET' && req.method !== 'HEAD') { + res.statusCode = 'OPTIONS' === req.method ? 200 : 405; + res.setHeader('Allow', 'GET, HEAD, OPTIONS'); + res.setHeader('Content-Length', '0'); + res.end(); + return; + } + + // parse URLs + var url = parseUrl(req); + var originalUrl = parseUrl.original(req); + var dir = decodeURIComponent(url.pathname); + var originalDir = decodeURIComponent(originalUrl.pathname); + + // join / normalize from root dir + var path = normalize(join(rootPath, dir)); + + // null byte(s), bad request + if (~path.indexOf('\0')) return next(createError(400)); + + // malicious path + if ((path + sep).substr(0, rootPath.length) !== rootPath) { + debug('malicious path "%s"', path); + return next(createError(403)); + } + + // determine ".." display + var showUp = normalize(resolve(path) + sep) !== rootPath; + + // check if we have a directory + debug('stat "%s"', path); + fs.stat(path, function(err, stat){ + if (err && err.code === 'ENOENT') { + return next(); + } + + if (err) { + err.status = err.code === 'ENAMETOOLONG' + ? 414 + : 500; + return next(err); + } + + if (!stat.isDirectory()) return next(); + + // fetch files + debug('readdir "%s"', path); + fs.readdir(path, function(err, files){ + if (err) return next(err); + if (!hidden) files = removeHidden(files); + if (filter) files = files.filter(function(filename, index, list) { + return filter(filename, index, list, path); + }); + files.sort(); + + // content-negotiation + var accept = accepts(req); + var type = accept.type(mediaTypes); + + // not acceptable + if (!type) return next(createError(406)); + serveIndex[mediaType[type]](req, res, files, next, originalDir, showUp, icons, path, view, template, stylesheet); + }); + }); + }; +}; + +/** + * Respond with text/html. + */ + +serveIndex.html = function _html(req, res, files, next, dir, showUp, icons, path, view, template, stylesheet) { + var render = typeof template !== 'function' + ? createHtmlRender(template) + : template + + if (showUp) { + files.unshift('..'); + } + + // stat all files + stat(path, files, function (err, stats) { + if (err) return next(err); + + // combine the stats into the file list + var fileList = files.map(function (file, i) { + return { name: file, stat: stats[i] }; + }); + + // sort file list + fileList.sort(fileSort); + + // read stylesheet + fs.readFile(stylesheet, 'utf8', function (err, style) { + if (err) return next(err); + + // create locals for rendering + var locals = { + directory: dir, + displayIcons: Boolean(icons), + fileList: fileList, + path: path, + style: style, + viewName: view + }; + + // render html + render(locals, function (err, body) { + if (err) return next(err); + + var buf = new Buffer(body, 'utf8'); + res.setHeader('Content-Type', 'text/html; charset=utf-8'); + res.setHeader('Content-Length', buf.length); + res.end(buf); + }); + }); + }); +}; + +/** + * Respond with application/json. + */ + +serveIndex.json = function _json(req, res, files) { + var body = JSON.stringify(files); + var buf = new Buffer(body, 'utf8'); + + res.setHeader('Content-Type', 'application/json; charset=utf-8'); + res.setHeader('Content-Length', buf.length); + res.end(buf); +}; + +/** + * Respond with text/plain. + */ + +serveIndex.plain = function _plain(req, res, files) { + var body = files.join('\n') + '\n'; + var buf = new Buffer(body, 'utf8'); + + res.setHeader('Content-Type', 'text/plain; charset=utf-8'); + res.setHeader('Content-Length', buf.length); + res.end(buf); +}; + +/** + * Map html `files`, returning an html unordered list. + * @private + */ + +function createHtmlFileList(files, dir, useIcons, view) { + var html = ''; + + return html; +} + +/** + * Create function to render html. + */ + +function createHtmlRender(template) { + return function render(locals, callback) { + // read template + fs.readFile(template, 'utf8', function (err, str) { + if (err) return callback(err); + + var body = str + .replace(/\{style\}/g, locals.style.concat(iconStyle(locals.fileList, locals.displayIcons))) + .replace(/\{files\}/g, createHtmlFileList(locals.fileList, locals.directory, locals.displayIcons, locals.viewName)) + .replace(/\{directory\}/g, escapeHtml(locals.directory)) + .replace(/\{linked-path\}/g, htmlPath(locals.directory)); + + callback(null, body); + }); + }; +} + +/** + * Sort function for with directories first. + */ + +function fileSort(a, b) { + // sort ".." to the top + if (a.name === '..' || b.name === '..') { + return a.name === b.name ? 0 + : a.name === '..' ? -1 : 1; + } + + return Number(b.stat && b.stat.isDirectory()) - Number(a.stat && a.stat.isDirectory()) || + String(a.name).toLocaleLowerCase().localeCompare(String(b.name).toLocaleLowerCase()); +} + +/** + * Map html `dir`, returning a linked path. + */ + +function htmlPath(dir) { + var parts = dir.split('/'); + var crumb = new Array(parts.length); + + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + + if (part) { + parts[i] = encodeURIComponent(part); + crumb[i] = '' + escapeHtml(part) + ''; + } + } + + return crumb.join(' / '); +} + +/** + * Get the icon data for the file name. + */ + +function iconLookup(filename) { + var ext = extname(filename); + + // try by extension + if (icons[ext]) { + return { + className: 'icon-' + ext.substring(1), + fileName: icons[ext] + }; + } + + var mimetype = mime.lookup(ext); + + // default if no mime type + if (mimetype === false) { + return { + className: 'icon-default', + fileName: icons.default + }; + } + + // try by mime type + if (icons[mimetype]) { + return { + className: 'icon-' + mimetype.replace('/', '-'), + fileName: icons[mimetype] + }; + } + + var suffix = mimetype.split('+')[1]; + + if (suffix && icons['+' + suffix]) { + return { + className: 'icon-' + suffix, + fileName: icons['+' + suffix] + }; + } + + var type = mimetype.split('/')[0]; + + // try by type only + if (icons[type]) { + return { + className: 'icon-' + type, + fileName: icons[type] + }; + } + + return { + className: 'icon-default', + fileName: icons.default + }; +} + +/** + * Load icon images, return css string. + */ + +function iconStyle(files, useIcons) { + if (!useIcons) return ''; + var className; + var i; + var iconName; + var list = []; + var rules = {}; + var selector; + var selectors = {}; + var style = ''; + + for (i = 0; i < files.length; i++) { + var file = files[i]; + + var isDir = file.stat && file.stat.isDirectory(); + var icon = isDir + ? { className: 'icon-directory', fileName: icons.folder } + : iconLookup(file.name); + var iconName = icon.fileName; + + selector = '#files .' + icon.className + ' .name'; + + if (!rules[iconName]) { + rules[iconName] = 'background-image: url(data:image/png;base64,' + load(iconName) + ');' + selectors[iconName] = []; + list.push(iconName); + } + + if (selectors[iconName].indexOf(selector) === -1) { + selectors[iconName].push(selector); + } + } + + for (i = 0; i < list.length; i++) { + iconName = list[i]; + style += selectors[iconName].join(',\n') + ' {\n ' + rules[iconName] + '\n}\n'; + } + + return style; +} + +/** + * Load and cache the given `icon`. + * + * @param {String} icon + * @return {String} + * @api private + */ + +function load(icon) { + if (cache[icon]) return cache[icon]; + return cache[icon] = fs.readFileSync(__dirname + '/public/icons/' + icon, 'base64'); +} + +/** + * Normalizes the path separator from system separator + * to URL separator, aka `/`. + * + * @param {String} path + * @return {String} + * @api private + */ + +function normalizeSlashes(path) { + return path.split(sep).join('/'); +}; + +/** + * Filter "hidden" `files`, aka files + * beginning with a `.`. + * + * @param {Array} files + * @return {Array} + * @api private + */ + +function removeHidden(files) { + return files.filter(function(file){ + return '.' != file[0]; + }); +} + +/** + * Stat all files and return array of stat + * in same order. + */ + +function stat(dir, files, cb) { + var batch = new Batch(); + + batch.concurrency(10); + + files.forEach(function(file){ + batch.push(function(done){ + fs.stat(join(dir, file), function(err, stat){ + if (err && err.code !== 'ENOENT') return done(err); + + // pass ENOENT as null stat, not error + done(null, stat || null); + }); + }); + }); + + batch.end(cb); +} + +/** + * Icon map. + */ + +var icons = { + // base icons + 'default': 'page_white.png', + 'folder': 'folder.png', + + // generic mime type icons + 'image': 'image.png', + 'text': 'page_white_text.png', + 'video': 'film.png', + + // generic mime suffix icons + '+json': 'page_white_code.png', + '+xml': 'page_white_code.png', + '+zip': 'box.png', + + // specific mime type icons + 'application/font-woff': 'font.png', + 'application/javascript': 'page_white_code_red.png', + 'application/json': 'page_white_code.png', + 'application/msword': 'page_white_word.png', + 'application/pdf': 'page_white_acrobat.png', + 'application/postscript': 'page_white_vector.png', + 'application/rtf': 'page_white_word.png', + 'application/vnd.ms-excel': 'page_white_excel.png', + 'application/vnd.ms-powerpoint': 'page_white_powerpoint.png', + 'application/vnd.oasis.opendocument.presentation': 'page_white_powerpoint.png', + 'application/vnd.oasis.opendocument.spreadsheet': 'page_white_excel.png', + 'application/vnd.oasis.opendocument.text': 'page_white_word.png', + 'application/x-7z-compressed': 'box.png', + 'application/x-sh': 'application_xp_terminal.png', + 'application/x-font-ttf': 'font.png', + 'application/x-msaccess': 'page_white_database.png', + 'application/x-shockwave-flash': 'page_white_flash.png', + 'application/x-sql': 'page_white_database.png', + 'application/x-tar': 'box.png', + 'application/x-xz': 'box.png', + 'application/xml': 'page_white_code.png', + 'application/zip': 'box.png', + 'image/svg+xml': 'page_white_vector.png', + 'text/css': 'page_white_code.png', + 'text/html': 'page_white_code.png', + 'text/less': 'page_white_code.png', + + // other, extension-specific icons + '.accdb': 'page_white_database.png', + '.apk': 'box.png', + '.app': 'application_xp.png', + '.as': 'page_white_actionscript.png', + '.asp': 'page_white_code.png', + '.aspx': 'page_white_code.png', + '.bat': 'application_xp_terminal.png', + '.bz2': 'box.png', + '.c': 'page_white_c.png', + '.cab': 'box.png', + '.cfm': 'page_white_coldfusion.png', + '.clj': 'page_white_code.png', + '.cc': 'page_white_cplusplus.png', + '.cgi': 'application_xp_terminal.png', + '.cpp': 'page_white_cplusplus.png', + '.cs': 'page_white_csharp.png', + '.db': 'page_white_database.png', + '.dbf': 'page_white_database.png', + '.deb': 'box.png', + '.dll': 'page_white_gear.png', + '.dmg': 'drive.png', + '.docx': 'page_white_word.png', + '.erb': 'page_white_ruby.png', + '.exe': 'application_xp.png', + '.fnt': 'font.png', + '.gam': 'controller.png', + '.gz': 'box.png', + '.h': 'page_white_h.png', + '.ini': 'page_white_gear.png', + '.iso': 'cd.png', + '.jar': 'box.png', + '.java': 'page_white_cup.png', + '.jsp': 'page_white_cup.png', + '.lua': 'page_white_code.png', + '.lz': 'box.png', + '.lzma': 'box.png', + '.m': 'page_white_code.png', + '.map': 'map.png', + '.msi': 'box.png', + '.mv4': 'film.png', + '.otf': 'font.png', + '.pdb': 'page_white_database.png', + '.php': 'page_white_php.png', + '.pl': 'page_white_code.png', + '.pkg': 'box.png', + '.pptx': 'page_white_powerpoint.png', + '.psd': 'page_white_picture.png', + '.py': 'page_white_code.png', + '.rar': 'box.png', + '.rb': 'page_white_ruby.png', + '.rm': 'film.png', + '.rom': 'controller.png', + '.rpm': 'box.png', + '.sass': 'page_white_code.png', + '.sav': 'controller.png', + '.scss': 'page_white_code.png', + '.srt': 'page_white_text.png', + '.tbz2': 'box.png', + '.tgz': 'box.png', + '.tlz': 'box.png', + '.vb': 'page_white_code.png', + '.vbs': 'page_white_code.png', + '.xcf': 'page_white_picture.png', + '.xlsx': 'page_white_excel.png', + '.yaws': 'page_white_code.png' +}; diff --git a/node_modules/serve-index/node_modules/accepts/HISTORY.md b/node_modules/serve-index/node_modules/accepts/HISTORY.md new file mode 100644 index 0000000..397636e --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/HISTORY.md @@ -0,0 +1,170 @@ +1.2.13 / 2015-09-06 +=================== + + * deps: mime-types@~2.1.6 + - deps: mime-db@~1.18.0 + +1.2.12 / 2015-07-30 +=================== + + * deps: mime-types@~2.1.4 + - deps: mime-db@~1.16.0 + +1.2.11 / 2015-07-16 +=================== + + * deps: mime-types@~2.1.3 + - deps: mime-db@~1.15.0 + +1.2.10 / 2015-07-01 +=================== + + * deps: mime-types@~2.1.2 + - deps: mime-db@~1.14.0 + +1.2.9 / 2015-06-08 +================== + + * deps: mime-types@~2.1.1 + - perf: fix deopt during mapping + +1.2.8 / 2015-06-07 +================== + + * deps: mime-types@~2.1.0 + - deps: mime-db@~1.13.0 + * perf: avoid argument reassignment & argument slice + * perf: avoid negotiator recursive construction + * perf: enable strict mode + * perf: remove unnecessary bitwise operator + +1.2.7 / 2015-05-10 +================== + + * deps: negotiator@0.5.3 + - Fix media type parameter matching to be case-insensitive + +1.2.6 / 2015-05-07 +================== + + * deps: mime-types@~2.0.11 + - deps: mime-db@~1.9.1 + * deps: negotiator@0.5.2 + - Fix comparing media types with quoted values + - Fix splitting media types with quoted commas + +1.2.5 / 2015-03-13 +================== + + * deps: mime-types@~2.0.10 + - deps: mime-db@~1.8.0 + +1.2.4 / 2015-02-14 +================== + + * Support Node.js 0.6 + * deps: mime-types@~2.0.9 + - deps: mime-db@~1.7.0 + * deps: negotiator@0.5.1 + - Fix preference sorting to be stable for long acceptable lists + +1.2.3 / 2015-01-31 +================== + + * deps: mime-types@~2.0.8 + - deps: mime-db@~1.6.0 + +1.2.2 / 2014-12-30 +================== + + * deps: mime-types@~2.0.7 + - deps: mime-db@~1.5.0 + +1.2.1 / 2014-12-30 +================== + + * deps: mime-types@~2.0.5 + - deps: mime-db@~1.3.1 + +1.2.0 / 2014-12-19 +================== + + * deps: negotiator@0.5.0 + - Fix list return order when large accepted list + - Fix missing identity encoding when q=0 exists + - Remove dynamic building of Negotiator class + +1.1.4 / 2014-12-10 +================== + + * deps: mime-types@~2.0.4 + - deps: mime-db@~1.3.0 + +1.1.3 / 2014-11-09 +================== + + * deps: mime-types@~2.0.3 + - deps: mime-db@~1.2.0 + +1.1.2 / 2014-10-14 +================== + + * deps: negotiator@0.4.9 + - Fix error when media type has invalid parameter + +1.1.1 / 2014-09-28 +================== + + * deps: mime-types@~2.0.2 + - deps: mime-db@~1.1.0 + * deps: negotiator@0.4.8 + - Fix all negotiations to be case-insensitive + - Stable sort preferences of same quality according to client order + +1.1.0 / 2014-09-02 +================== + + * update `mime-types` + +1.0.7 / 2014-07-04 +================== + + * Fix wrong type returned from `type` when match after unknown extension + +1.0.6 / 2014-06-24 +================== + + * deps: negotiator@0.4.7 + +1.0.5 / 2014-06-20 +================== + + * fix crash when unknown extension given + +1.0.4 / 2014-06-19 +================== + + * use `mime-types` + +1.0.3 / 2014-06-11 +================== + + * deps: negotiator@0.4.6 + - Order by specificity when quality is the same + +1.0.2 / 2014-05-29 +================== + + * Fix interpretation when header not in request + * deps: pin negotiator@0.4.5 + +1.0.1 / 2014-01-18 +================== + + * Identity encoding isn't always acceptable + * deps: negotiator@~0.4.0 + +1.0.0 / 2013-12-27 +================== + + * Genesis diff --git a/node_modules/serve-index/node_modules/accepts/LICENSE b/node_modules/serve-index/node_modules/accepts/LICENSE new file mode 100644 index 0000000..0616607 --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/LICENSE @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/serve-index/node_modules/accepts/README.md b/node_modules/serve-index/node_modules/accepts/README.md new file mode 100644 index 0000000..ae36676 --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/README.md @@ -0,0 +1,135 @@ +# accepts + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Higher level content negotiation based on [negotiator](https://www.npmjs.com/package/negotiator). Extracted from [koa](https://www.npmjs.com/package/koa) for general use. + +In addition to negotiator, it allows: + +- Allows types as an array or arguments list, ie `(['text/html', 'application/json'])` as well as `('text/html', 'application/json')`. +- Allows type shorthands such as `json`. +- Returns `false` when no types match +- Treats non-existent headers as `*` + +## Installation + +```sh +npm install accepts +``` + +## API + +```js +var accepts = require('accepts') +``` + +### accepts(req) + +Create a new `Accepts` object for the given `req`. + +#### .charset(charsets) + +Return the first accepted charset. If nothing in `charsets` is accepted, +then `false` is returned. + +#### .charsets() + +Return the charsets that the request accepts, in the order of the client's +preference (most preferred first). + +#### .encoding(encodings) + +Return the first accepted encoding. If nothing in `encodings` is accepted, +then `false` is returned. + +#### .encodings() + +Return the encodings that the request accepts, in the order of the client's +preference (most preferred first). + +#### .language(languages) + +Return the first accepted language. If nothing in `languages` is accepted, +then `false` is returned. + +#### .languages() + +Return the languages that the request accepts, in the order of the client's +preference (most preferred first). + +#### .type(types) + +Return the first accepted type (and it is returned as the same text as what +appears in the `types` array). If nothing in `types` is accepted, then `false` +is returned. + +The `types` array can contain full MIME types or file extensions. Any value +that is not a full MIME types is passed to `require('mime-types').lookup`. + +#### .types() + +Return the types that the request accepts, in the order of the client's +preference (most preferred first). + +## Examples + +### Simple type negotiation + +This simple example shows how to use `accepts` to return a different typed +respond body based on what the client wants to accept. The server lists it's +preferences in order and will get back the best match between the client and +server. + +```js +var accepts = require('accepts') +var http = require('http') + +function app(req, res) { + var accept = accepts(req) + + // the order of this list is significant; should be server preferred order + switch(accept.type(['json', 'html'])) { + case 'json': + res.setHeader('Content-Type', 'application/json') + res.write('{"hello":"world!"}') + break + case 'html': + res.setHeader('Content-Type', 'text/html') + res.write('hello, world!') + break + default: + // the fallback is text/plain, so no need to specify it above + res.setHeader('Content-Type', 'text/plain') + res.write('hello, world!') + break + } + + res.end() +} + +http.createServer(app).listen(3000) +``` + +You can test this out with the cURL program: +```sh +curl -I -H'Accept: text/html' http://localhost:3000/ +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/accepts.svg +[npm-url]: https://npmjs.org/package/accepts +[node-version-image]: https://img.shields.io/node/v/accepts.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/accepts/master.svg +[travis-url]: https://travis-ci.org/jshttp/accepts +[coveralls-image]: https://img.shields.io/coveralls/jshttp/accepts/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/accepts +[downloads-image]: https://img.shields.io/npm/dm/accepts.svg +[downloads-url]: https://npmjs.org/package/accepts diff --git a/node_modules/serve-index/node_modules/accepts/index.js b/node_modules/serve-index/node_modules/accepts/index.js new file mode 100644 index 0000000..e80192a --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/index.js @@ -0,0 +1,231 @@ +/*! + * accepts + * Copyright(c) 2014 Jonathan Ong + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module dependencies. + * @private + */ + +var Negotiator = require('negotiator') +var mime = require('mime-types') + +/** + * Module exports. + * @public + */ + +module.exports = Accepts + +/** + * Create a new Accepts object for the given req. + * + * @param {object} req + * @public + */ + +function Accepts(req) { + if (!(this instanceof Accepts)) + return new Accepts(req) + + this.headers = req.headers + this.negotiator = new Negotiator(req) +} + +/** + * Check if the given `type(s)` is acceptable, returning + * the best match when true, otherwise `undefined`, in which + * case you should respond with 406 "Not Acceptable". + * + * The `type` value may be a single mime type string + * such as "application/json", the extension name + * such as "json" or an array `["json", "html", "text/plain"]`. When a list + * or array is given the _best_ match, if any is returned. + * + * Examples: + * + * // Accept: text/html + * this.types('html'); + * // => "html" + * + * // Accept: text/*, application/json + * this.types('html'); + * // => "html" + * this.types('text/html'); + * // => "text/html" + * this.types('json', 'text'); + * // => "json" + * this.types('application/json'); + * // => "application/json" + * + * // Accept: text/*, application/json + * this.types('image/png'); + * this.types('png'); + * // => undefined + * + * // Accept: text/*;q=.5, application/json + * this.types(['html', 'json']); + * this.types('html', 'json'); + * // => "json" + * + * @param {String|Array} types... + * @return {String|Array|Boolean} + * @public + */ + +Accepts.prototype.type = +Accepts.prototype.types = function (types_) { + var types = types_ + + // support flattened arguments + if (types && !Array.isArray(types)) { + types = new Array(arguments.length) + for (var i = 0; i < types.length; i++) { + types[i] = arguments[i] + } + } + + // no types, return all requested types + if (!types || types.length === 0) { + return this.negotiator.mediaTypes() + } + + if (!this.headers.accept) return types[0]; + var mimes = types.map(extToMime); + var accepts = this.negotiator.mediaTypes(mimes.filter(validMime)); + var first = accepts[0]; + if (!first) return false; + return types[mimes.indexOf(first)]; +} + +/** + * Return accepted encodings or best fit based on `encodings`. + * + * Given `Accept-Encoding: gzip, deflate` + * an array sorted by quality is returned: + * + * ['gzip', 'deflate'] + * + * @param {String|Array} encodings... + * @return {String|Array} + * @public + */ + +Accepts.prototype.encoding = +Accepts.prototype.encodings = function (encodings_) { + var encodings = encodings_ + + // support flattened arguments + if (encodings && !Array.isArray(encodings)) { + encodings = new Array(arguments.length) + for (var i = 0; i < encodings.length; i++) { + encodings[i] = arguments[i] + } + } + + // no encodings, return all requested encodings + if (!encodings || encodings.length === 0) { + return this.negotiator.encodings() + } + + return this.negotiator.encodings(encodings)[0] || false +} + +/** + * Return accepted charsets or best fit based on `charsets`. + * + * Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5` + * an array sorted by quality is returned: + * + * ['utf-8', 'utf-7', 'iso-8859-1'] + * + * @param {String|Array} charsets... + * @return {String|Array} + * @public + */ + +Accepts.prototype.charset = +Accepts.prototype.charsets = function (charsets_) { + var charsets = charsets_ + + // support flattened arguments + if (charsets && !Array.isArray(charsets)) { + charsets = new Array(arguments.length) + for (var i = 0; i < charsets.length; i++) { + charsets[i] = arguments[i] + } + } + + // no charsets, return all requested charsets + if (!charsets || charsets.length === 0) { + return this.negotiator.charsets() + } + + return this.negotiator.charsets(charsets)[0] || false +} + +/** + * Return accepted languages or best fit based on `langs`. + * + * Given `Accept-Language: en;q=0.8, es, pt` + * an array sorted by quality is returned: + * + * ['es', 'pt', 'en'] + * + * @param {String|Array} langs... + * @return {Array|String} + * @public + */ + +Accepts.prototype.lang = +Accepts.prototype.langs = +Accepts.prototype.language = +Accepts.prototype.languages = function (languages_) { + var languages = languages_ + + // support flattened arguments + if (languages && !Array.isArray(languages)) { + languages = new Array(arguments.length) + for (var i = 0; i < languages.length; i++) { + languages[i] = arguments[i] + } + } + + // no languages, return all requested languages + if (!languages || languages.length === 0) { + return this.negotiator.languages() + } + + return this.negotiator.languages(languages)[0] || false +} + +/** + * Convert extnames to mime. + * + * @param {String} type + * @return {String} + * @private + */ + +function extToMime(type) { + return type.indexOf('/') === -1 + ? mime.lookup(type) + : type +} + +/** + * Check if mime is valid. + * + * @param {String} type + * @return {String} + * @private + */ + +function validMime(type) { + return typeof type === 'string'; +} diff --git a/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/HISTORY.md b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/HISTORY.md new file mode 100644 index 0000000..aa2a7c4 --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/HISTORY.md @@ -0,0 +1,76 @@ +0.5.3 / 2015-05-10 +================== + + * Fix media type parameter matching to be case-insensitive + +0.5.2 / 2015-05-06 +================== + + * Fix comparing media types with quoted values + * Fix splitting media types with quoted commas + +0.5.1 / 2015-02-14 +================== + + * Fix preference sorting to be stable for long acceptable lists + +0.5.0 / 2014-12-18 +================== + + * Fix list return order when large accepted list + * Fix missing identity encoding when q=0 exists + * Remove dynamic building of Negotiator class + +0.4.9 / 2014-10-14 +================== + + * Fix error when media type has invalid parameter + +0.4.8 / 2014-09-28 +================== + + * Fix all negotiations to be case-insensitive + * Stable sort preferences of same quality according to client order + * Support Node.js 0.6 + +0.4.7 / 2014-06-24 +================== + + * Handle invalid provided languages + * Handle invalid provided media types + +0.4.6 / 2014-06-11 +================== + + * Order by specificity when quality is the same + +0.4.5 / 2014-05-29 +================== + + * Fix regression in empty header handling + +0.4.4 / 2014-05-29 +================== + + * Fix behaviors when headers are not present + +0.4.3 / 2014-04-16 +================== + + * Handle slashes on media params correctly + +0.4.2 / 2014-02-28 +================== + + * Fix media type sorting + * Handle media types params strictly + +0.4.1 / 2014-01-16 +================== + + * Use most specific matches + +0.4.0 / 2014-01-09 +================== + + * Remove preferred prefix from methods diff --git a/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/LICENSE b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/LICENSE new file mode 100644 index 0000000..ea6b9e2 --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/LICENSE @@ -0,0 +1,24 @@ +(The MIT License) + +Copyright (c) 2012-2014 Federico Romero +Copyright (c) 2012-2014 Isaac Z. Schlueter +Copyright (c) 2014-2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/README.md b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/README.md new file mode 100644 index 0000000..ef507fa --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/README.md @@ -0,0 +1,203 @@ +# negotiator + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +An HTTP content negotiator for Node.js + +## Installation + +```sh +$ npm install negotiator +``` + +## API + +```js +var Negotiator = require('negotiator') +``` + +### Accept Negotiation + +```js +availableMediaTypes = ['text/html', 'text/plain', 'application/json'] + +// The negotiator constructor receives a request object +negotiator = new Negotiator(request) + +// Let's say Accept header is 'text/html, application/*;q=0.2, image/jpeg;q=0.8' + +negotiator.mediaTypes() +// -> ['text/html', 'image/jpeg', 'application/*'] + +negotiator.mediaTypes(availableMediaTypes) +// -> ['text/html', 'application/json'] + +negotiator.mediaType(availableMediaTypes) +// -> 'text/html' +``` + +You can check a working example at `examples/accept.js`. + +#### Methods + +##### mediaType() + +Returns the most preferred media type from the client. + +##### mediaType(availableMediaType) + +Returns the most preferred media type from a list of available media types. + +##### mediaTypes() + +Returns an array of preferred media types ordered by the client preference. + +##### mediaTypes(availableMediaTypes) + +Returns an array of preferred media types ordered by priority from a list of +available media types. + +### Accept-Language Negotiation + +```js +negotiator = new Negotiator(request) + +availableLanguages = 'en', 'es', 'fr' + +// Let's say Accept-Language header is 'en;q=0.8, es, pt' + +negotiator.languages() +// -> ['es', 'pt', 'en'] + +negotiator.languages(availableLanguages) +// -> ['es', 'en'] + +language = negotiator.language(availableLanguages) +// -> 'es' +``` + +You can check a working example at `examples/language.js`. + +#### Methods + +##### language() + +Returns the most preferred language from the client. + +##### language(availableLanguages) + +Returns the most preferred language from a list of available languages. + +##### languages() + +Returns an array of preferred languages ordered by the client preference. + +##### languages(availableLanguages) + +Returns an array of preferred languages ordered by priority from a list of +available languages. + +### Accept-Charset Negotiation + +```js +availableCharsets = ['utf-8', 'iso-8859-1', 'iso-8859-5'] + +negotiator = new Negotiator(request) + +// Let's say Accept-Charset header is 'utf-8, iso-8859-1;q=0.8, utf-7;q=0.2' + +negotiator.charsets() +// -> ['utf-8', 'iso-8859-1', 'utf-7'] + +negotiator.charsets(availableCharsets) +// -> ['utf-8', 'iso-8859-1'] + +negotiator.charset(availableCharsets) +// -> 'utf-8' +``` + +You can check a working example at `examples/charset.js`. + +#### Methods + +##### charset() + +Returns the most preferred charset from the client. + +##### charset(availableCharsets) + +Returns the most preferred charset from a list of available charsets. + +##### charsets() + +Returns an array of preferred charsets ordered by the client preference. + +##### charsets(availableCharsets) + +Returns an array of preferred charsets ordered by priority from a list of +available charsets. + +### Accept-Encoding Negotiation + +```js +availableEncodings = ['identity', 'gzip'] + +negotiator = new Negotiator(request) + +// Let's say Accept-Encoding header is 'gzip, compress;q=0.2, identity;q=0.5' + +negotiator.encodings() +// -> ['gzip', 'identity', 'compress'] + +negotiator.encodings(availableEncodings) +// -> ['gzip', 'identity'] + +negotiator.encoding(availableEncodings) +// -> 'gzip' +``` + +You can check a working example at `examples/encoding.js`. + +#### Methods + +##### encoding() + +Returns the most preferred encoding from the client. + +##### encoding(availableEncodings) + +Returns the most preferred encoding from a list of available encodings. + +##### encodings() + +Returns an array of preferred encodings ordered by the client preference. + +##### encodings(availableEncodings) + +Returns an array of preferred encodings ordered by priority from a list of +available encodings. + +## See Also + +The [accepts](https://npmjs.org/package/accepts#readme) module builds on +this module and provides an alternative interface, mime type validation, +and more. + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/negotiator.svg +[npm-url]: https://npmjs.org/package/negotiator +[node-version-image]: https://img.shields.io/node/v/negotiator.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/negotiator/master.svg +[travis-url]: https://travis-ci.org/jshttp/negotiator +[coveralls-image]: https://img.shields.io/coveralls/jshttp/negotiator/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/negotiator?branch=master +[downloads-image]: https://img.shields.io/npm/dm/negotiator.svg +[downloads-url]: https://npmjs.org/package/negotiator diff --git a/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/index.js b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/index.js new file mode 100644 index 0000000..edae9cf --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/index.js @@ -0,0 +1,62 @@ + +var preferredCharsets = require('./lib/charset'); +var preferredEncodings = require('./lib/encoding'); +var preferredLanguages = require('./lib/language'); +var preferredMediaTypes = require('./lib/mediaType'); + +module.exports = Negotiator; +Negotiator.Negotiator = Negotiator; + +function Negotiator(request) { + if (!(this instanceof Negotiator)) { + return new Negotiator(request); + } + + this.request = request; +} + +Negotiator.prototype.charset = function charset(available) { + var set = this.charsets(available); + return set && set[0]; +}; + +Negotiator.prototype.charsets = function charsets(available) { + return preferredCharsets(this.request.headers['accept-charset'], available); +}; + +Negotiator.prototype.encoding = function encoding(available) { + var set = this.encodings(available); + return set && set[0]; +}; + +Negotiator.prototype.encodings = function encodings(available) { + return preferredEncodings(this.request.headers['accept-encoding'], available); +}; + +Negotiator.prototype.language = function language(available) { + var set = this.languages(available); + return set && set[0]; +}; + +Negotiator.prototype.languages = function languages(available) { + return preferredLanguages(this.request.headers['accept-language'], available); +}; + +Negotiator.prototype.mediaType = function mediaType(available) { + var set = this.mediaTypes(available); + return set && set[0]; +}; + +Negotiator.prototype.mediaTypes = function mediaTypes(available) { + return preferredMediaTypes(this.request.headers.accept, available); +}; + +// Backwards compatibility +Negotiator.prototype.preferredCharset = Negotiator.prototype.charset; +Negotiator.prototype.preferredCharsets = Negotiator.prototype.charsets; +Negotiator.prototype.preferredEncoding = Negotiator.prototype.encoding; +Negotiator.prototype.preferredEncodings = Negotiator.prototype.encodings; +Negotiator.prototype.preferredLanguage = Negotiator.prototype.language; +Negotiator.prototype.preferredLanguages = Negotiator.prototype.languages; +Negotiator.prototype.preferredMediaType = Negotiator.prototype.mediaType; +Negotiator.prototype.preferredMediaTypes = Negotiator.prototype.mediaTypes; diff --git a/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/lib/charset.js b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/lib/charset.js new file mode 100644 index 0000000..7abd17c --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/lib/charset.js @@ -0,0 +1,102 @@ +module.exports = preferredCharsets; +preferredCharsets.preferredCharsets = preferredCharsets; + +function parseAcceptCharset(accept) { + var accepts = accept.split(','); + + for (var i = 0, j = 0; i < accepts.length; i++) { + var charset = parseCharset(accepts[i].trim(), i); + + if (charset) { + accepts[j++] = charset; + } + } + + // trim accepts + accepts.length = j; + + return accepts; +} + +function parseCharset(s, i) { + var match = s.match(/^\s*(\S+?)\s*(?:;(.*))?$/); + if (!match) return null; + + var charset = match[1]; + var q = 1; + if (match[2]) { + var params = match[2].split(';') + for (var i = 0; i < params.length; i ++) { + var p = params[i].trim().split('='); + if (p[0] === 'q') { + q = parseFloat(p[1]); + break; + } + } + } + + return { + charset: charset, + q: q, + i: i + }; +} + +function getCharsetPriority(charset, accepted, index) { + var priority = {o: -1, q: 0, s: 0}; + + for (var i = 0; i < accepted.length; i++) { + var spec = specify(charset, accepted[i], index); + + if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) { + priority = spec; + } + } + + return priority; +} + +function specify(charset, spec, index) { + var s = 0; + if(spec.charset.toLowerCase() === charset.toLowerCase()){ + s |= 1; + } else if (spec.charset !== '*' ) { + return null + } + + return { + i: index, + o: spec.i, + q: spec.q, + s: s + } +} + +function preferredCharsets(accept, provided) { + // RFC 2616 sec 14.2: no header = * + var accepts = parseAcceptCharset(accept === undefined ? '*' : accept || ''); + + if (!provided) { + // sorted list of all charsets + return accepts.filter(isQuality).sort(compareSpecs).map(function getCharset(spec) { + return spec.charset; + }); + } + + var priorities = provided.map(function getPriority(type, index) { + return getCharsetPriority(type, accepts, index); + }); + + // sorted list of accepted charsets + return priorities.filter(isQuality).sort(compareSpecs).map(function getCharset(priority) { + return provided[priorities.indexOf(priority)]; + }); +} + +function compareSpecs(a, b) { + return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0; +} + +function isQuality(spec) { + return spec.q > 0; +} diff --git a/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/lib/encoding.js b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/lib/encoding.js new file mode 100644 index 0000000..7fed673 --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/lib/encoding.js @@ -0,0 +1,118 @@ +module.exports = preferredEncodings; +preferredEncodings.preferredEncodings = preferredEncodings; + +function parseAcceptEncoding(accept) { + var accepts = accept.split(','); + var hasIdentity = false; + var minQuality = 1; + + for (var i = 0, j = 0; i < accepts.length; i++) { + var encoding = parseEncoding(accepts[i].trim(), i); + + if (encoding) { + accepts[j++] = encoding; + hasIdentity = hasIdentity || specify('identity', encoding); + minQuality = Math.min(minQuality, encoding.q || 1); + } + } + + if (!hasIdentity) { + /* + * If identity doesn't explicitly appear in the accept-encoding header, + * it's added to the list of acceptable encoding with the lowest q + */ + accepts[j++] = { + encoding: 'identity', + q: minQuality, + i: i + }; + } + + // trim accepts + accepts.length = j; + + return accepts; +} + +function parseEncoding(s, i) { + var match = s.match(/^\s*(\S+?)\s*(?:;(.*))?$/); + + if (!match) return null; + + var encoding = match[1]; + var q = 1; + if (match[2]) { + var params = match[2].split(';'); + for (var i = 0; i < params.length; i ++) { + var p = params[i].trim().split('='); + if (p[0] === 'q') { + q = parseFloat(p[1]); + break; + } + } + } + + return { + encoding: encoding, + q: q, + i: i + }; +} + +function getEncodingPriority(encoding, accepted, index) { + var priority = {o: -1, q: 0, s: 0}; + + for (var i = 0; i < accepted.length; i++) { + var spec = specify(encoding, accepted[i], index); + + if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) { + priority = spec; + } + } + + return priority; +} + +function specify(encoding, spec, index) { + var s = 0; + if(spec.encoding.toLowerCase() === encoding.toLowerCase()){ + s |= 1; + } else if (spec.encoding !== '*' ) { + return null + } + + return { + i: index, + o: spec.i, + q: spec.q, + s: s + } +}; + +function preferredEncodings(accept, provided) { + var accepts = parseAcceptEncoding(accept || ''); + + if (!provided) { + // sorted list of all encodings + return accepts.filter(isQuality).sort(compareSpecs).map(function getEncoding(spec) { + return spec.encoding; + }); + } + + var priorities = provided.map(function getPriority(type, index) { + return getEncodingPriority(type, accepts, index); + }); + + // sorted list of accepted encodings + return priorities.filter(isQuality).sort(compareSpecs).map(function getEncoding(priority) { + return provided[priorities.indexOf(priority)]; + }); +} + +function compareSpecs(a, b) { + return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0; +} + +function isQuality(spec) { + return spec.q > 0; +} diff --git a/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/lib/language.js b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/lib/language.js new file mode 100644 index 0000000..ed9e1ec --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/lib/language.js @@ -0,0 +1,112 @@ +module.exports = preferredLanguages; +preferredLanguages.preferredLanguages = preferredLanguages; + +function parseAcceptLanguage(accept) { + var accepts = accept.split(','); + + for (var i = 0, j = 0; i < accepts.length; i++) { + var langauge = parseLanguage(accepts[i].trim(), i); + + if (langauge) { + accepts[j++] = langauge; + } + } + + // trim accepts + accepts.length = j; + + return accepts; +} + +function parseLanguage(s, i) { + var match = s.match(/^\s*(\S+?)(?:-(\S+?))?\s*(?:;(.*))?$/); + if (!match) return null; + + var prefix = match[1], + suffix = match[2], + full = prefix; + + if (suffix) full += "-" + suffix; + + var q = 1; + if (match[3]) { + var params = match[3].split(';') + for (var i = 0; i < params.length; i ++) { + var p = params[i].split('='); + if (p[0] === 'q') q = parseFloat(p[1]); + } + } + + return { + prefix: prefix, + suffix: suffix, + q: q, + i: i, + full: full + }; +} + +function getLanguagePriority(language, accepted, index) { + var priority = {o: -1, q: 0, s: 0}; + + for (var i = 0; i < accepted.length; i++) { + var spec = specify(language, accepted[i], index); + + if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) { + priority = spec; + } + } + + return priority; +} + +function specify(language, spec, index) { + var p = parseLanguage(language) + if (!p) return null; + var s = 0; + if(spec.full.toLowerCase() === p.full.toLowerCase()){ + s |= 4; + } else if (spec.prefix.toLowerCase() === p.full.toLowerCase()) { + s |= 2; + } else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) { + s |= 1; + } else if (spec.full !== '*' ) { + return null + } + + return { + i: index, + o: spec.i, + q: spec.q, + s: s + } +}; + +function preferredLanguages(accept, provided) { + // RFC 2616 sec 14.4: no header = * + var accepts = parseAcceptLanguage(accept === undefined ? '*' : accept || ''); + + if (!provided) { + // sorted list of all languages + return accepts.filter(isQuality).sort(compareSpecs).map(function getLanguage(spec) { + return spec.full; + }); + } + + var priorities = provided.map(function getPriority(type, index) { + return getLanguagePriority(type, accepts, index); + }); + + // sorted list of accepted languages + return priorities.filter(isQuality).sort(compareSpecs).map(function getLanguage(priority) { + return provided[priorities.indexOf(priority)]; + }); +} + +function compareSpecs(a, b) { + return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0; +} + +function isQuality(spec) { + return spec.q > 0; +} diff --git a/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/lib/mediaType.js b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/lib/mediaType.js new file mode 100644 index 0000000..4170c25 --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/lib/mediaType.js @@ -0,0 +1,179 @@ +/** + * negotiator + * Copyright(c) 2012 Isaac Z. Schlueter + * Copyright(c) 2014 Federico Romero + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +module.exports = preferredMediaTypes; +preferredMediaTypes.preferredMediaTypes = preferredMediaTypes; + +function parseAccept(accept) { + var accepts = splitMediaTypes(accept); + + for (var i = 0, j = 0; i < accepts.length; i++) { + var mediaType = parseMediaType(accepts[i].trim(), i); + + if (mediaType) { + accepts[j++] = mediaType; + } + } + + // trim accepts + accepts.length = j; + + return accepts; +}; + +function parseMediaType(s, i) { + var match = s.match(/\s*(\S+?)\/([^;\s]+)\s*(?:;(.*))?/); + if (!match) return null; + + var type = match[1], + subtype = match[2], + full = "" + type + "/" + subtype, + params = {}, + q = 1; + + if (match[3]) { + params = match[3].split(';').map(function(s) { + return s.trim().split('='); + }).reduce(function (set, p) { + var name = p[0].toLowerCase(); + var value = p[1]; + + set[name] = value && value[0] === '"' && value[value.length - 1] === '"' + ? value.substr(1, value.length - 2) + : value; + + return set; + }, params); + + if (params.q != null) { + q = parseFloat(params.q); + delete params.q; + } + } + + return { + type: type, + subtype: subtype, + params: params, + q: q, + i: i, + full: full + }; +} + +function getMediaTypePriority(type, accepted, index) { + var priority = {o: -1, q: 0, s: 0}; + + for (var i = 0; i < accepted.length; i++) { + var spec = specify(type, accepted[i], index); + + if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) { + priority = spec; + } + } + + return priority; +} + +function specify(type, spec, index) { + var p = parseMediaType(type); + var s = 0; + + if (!p) { + return null; + } + + if(spec.type.toLowerCase() == p.type.toLowerCase()) { + s |= 4 + } else if(spec.type != '*') { + return null; + } + + if(spec.subtype.toLowerCase() == p.subtype.toLowerCase()) { + s |= 2 + } else if(spec.subtype != '*') { + return null; + } + + var keys = Object.keys(spec.params); + if (keys.length > 0) { + if (keys.every(function (k) { + return spec.params[k] == '*' || (spec.params[k] || '').toLowerCase() == (p.params[k] || '').toLowerCase(); + })) { + s |= 1 + } else { + return null + } + } + + return { + i: index, + o: spec.i, + q: spec.q, + s: s, + } + +} + +function preferredMediaTypes(accept, provided) { + // RFC 2616 sec 14.2: no header = */* + var accepts = parseAccept(accept === undefined ? '*/*' : accept || ''); + + if (!provided) { + // sorted list of all types + return accepts.filter(isQuality).sort(compareSpecs).map(function getType(spec) { + return spec.full; + }); + } + + var priorities = provided.map(function getPriority(type, index) { + return getMediaTypePriority(type, accepts, index); + }); + + // sorted list of accepted types + return priorities.filter(isQuality).sort(compareSpecs).map(function getType(priority) { + return provided[priorities.indexOf(priority)]; + }); +} + +function compareSpecs(a, b) { + return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0; +} + +function isQuality(spec) { + return spec.q > 0; +} + +function quoteCount(string) { + var count = 0; + var index = 0; + + while ((index = string.indexOf('"', index)) !== -1) { + count++; + index++; + } + + return count; +} + +function splitMediaTypes(accept) { + var accepts = accept.split(','); + + for (var i = 1, j = 0; i < accepts.length; i++) { + if (quoteCount(accepts[j]) % 2 == 0) { + accepts[++j] = accepts[i]; + } else { + accepts[j] += ',' + accepts[i]; + } + } + + // trim accepts + accepts.length = j + 1; + + return accepts; +} diff --git a/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/package.json b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/package.json new file mode 100644 index 0000000..f257e1d --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/node_modules/negotiator/package.json @@ -0,0 +1,85 @@ +{ + "name": "negotiator", + "description": "HTTP content negotiation", + "version": "0.5.3", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Federico Romero", + "email": "federico.romero@outboxlabs.com" + }, + { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + } + ], + "license": "MIT", + "keywords": [ + "http", + "content negotiation", + "accept", + "accept-language", + "accept-encoding", + "accept-charset" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/negotiator" + }, + "devDependencies": { + "istanbul": "0.3.9", + "mocha": "~1.21.5" + }, + "files": [ + "lib/", + "HISTORY.md", + "LICENSE", + "index.js", + "README.md" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec --check-leaks --bail test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "gitHead": "cbb717b3f164f25820f90b160cda6d0166b9d922", + "bugs": { + "url": "https://github.com/jshttp/negotiator/issues" + }, + "homepage": "https://github.com/jshttp/negotiator", + "_id": "negotiator@0.5.3", + "_shasum": "269d5c476810ec92edbe7b6c2f28316384f9a7e8", + "_from": "negotiator@0.5.3", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "federomero", + "email": "federomero@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + } + ], + "dist": { + "shasum": "269d5c476810ec92edbe7b6c2f28316384f9a7e8", + "tarball": "http://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz" +} diff --git a/node_modules/serve-index/node_modules/accepts/package.json b/node_modules/serve-index/node_modules/accepts/package.json new file mode 100644 index 0000000..abd0b16 --- /dev/null +++ b/node_modules/serve-index/node_modules/accepts/package.json @@ -0,0 +1,97 @@ +{ + "name": "accepts", + "description": "Higher-level content negotiation", + "version": "1.2.13", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/jshttp/accepts" + }, + "dependencies": { + "mime-types": "~2.1.6", + "negotiator": "0.5.3" + }, + "devDependencies": { + "istanbul": "0.3.19", + "mocha": "~1.21.5" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec --check-leaks --bail test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "keywords": [ + "content", + "negotiation", + "accept", + "accepts" + ], + "gitHead": "b7e15ecb25dacc0b2133ed0553d64f8a79537e01", + "bugs": { + "url": "https://github.com/jshttp/accepts/issues" + }, + "homepage": "https://github.com/jshttp/accepts", + "_id": "accepts@1.2.13", + "_shasum": "e5f1f3928c6d95fd96558c36ec3d9d0de4a6ecea", + "_from": "accepts@>=1.2.12 <1.3.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "federomero", + "email": "federomero@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + }, + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + { + "name": "defunctzombie", + "email": "shtylman@gmail.com" + } + ], + "dist": { + "shasum": "e5f1f3928c6d95fd96558c36ec3d9d0de4a6ecea", + "tarball": "http://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz" +} diff --git a/node_modules/serve-index/node_modules/batch/.npmignore b/node_modules/serve-index/node_modules/batch/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/node_modules/serve-index/node_modules/batch/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/node_modules/serve-index/node_modules/batch/History.md b/node_modules/serve-index/node_modules/batch/History.md new file mode 100644 index 0000000..424324a --- /dev/null +++ b/node_modules/serve-index/node_modules/batch/History.md @@ -0,0 +1,71 @@ + +0.5.1 / 2014-06-19 +================== + + * add repository field to readme (exciting) + +0.5.0 / 2013-07-29 +================== + + * add `.throws(true)` to opt-in to responding with an array of error objects + * make `new` optional + +0.4.0 / 2013-06-05 +================== + + * add catching of immediate callback errors + +0.3.2 / 2013-03-15 +================== + + * remove Emitter call in constructor + +0.3.1 / 2013-03-13 +================== + + * add Emitter() mixin for client. Closes #8 + +0.3.0 / 2013-03-13 +================== + + * add component.json + * add result example + * add .concurrency support + * add concurrency example + * add parallel example + +0.2.1 / 2012-11-08 +================== + + * add .start, .end, and .duration properties + * change dependencies to devDependencies + +0.2.0 / 2012-10-04 +================== + + * add progress events. Closes #5 (__BREAKING CHANGE__) + +0.1.1 / 2012-07-03 +================== + + * change "complete" event to "progress" + +0.1.0 / 2012-07-03 +================== + + * add Emitter inheritance and emit "complete" [burcu] + +0.0.3 / 2012-06-02 +================== + + * Callback results should be in the order of the queued functions. + +0.0.2 / 2012-02-12 +================== + + * any node + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/node_modules/serve-index/node_modules/batch/Makefile b/node_modules/serve-index/node_modules/batch/Makefile new file mode 100644 index 0000000..634e372 --- /dev/null +++ b/node_modules/serve-index/node_modules/batch/Makefile @@ -0,0 +1,6 @@ + +test: + @./node_modules/.bin/mocha \ + --require should + +.PHONY: test \ No newline at end of file diff --git a/node_modules/serve-index/node_modules/batch/Readme.md b/node_modules/serve-index/node_modules/batch/Readme.md new file mode 100644 index 0000000..f2345c6 --- /dev/null +++ b/node_modules/serve-index/node_modules/batch/Readme.md @@ -0,0 +1,74 @@ + +# batch + + Simple async batch with concurrency control and progress reporting. + +## Installation + +``` +$ npm install batch +``` + +## API + +```js +var Batch = require('batch') + , batch = new Batch; + +batch.concurrency(4); + +ids.forEach(function(id){ + batch.push(function(done){ + User.get(id, done); + }); +}); + +batch.on('progress', function(e){ + +}); + +batch.end(function(err, users){ + +}); +``` + +### Progress events + + Contain the "job" index, response value, duration information, and completion data. + +```js +{ index: 1, + value: 'bar', + pending: 2, + total: 3, + complete: 2, + percent: 66, + start: Thu Oct 04 2012 12:25:53 GMT-0700 (PDT), + end: Thu Oct 04 2012 12:25:53 GMT-0700 (PDT), + duration: 0 } +``` + +## License + +(The MIT License) + +Copyright (c) 2013 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/serve-index/node_modules/batch/component.json b/node_modules/serve-index/node_modules/batch/component.json new file mode 100644 index 0000000..9bd3e45 --- /dev/null +++ b/node_modules/serve-index/node_modules/batch/component.json @@ -0,0 +1,14 @@ +{ + "name": "batch", + "repo": "visionmedia/batch", + "description": "Async task batching", + "version": "0.5.2", + "keywords": ["batch", "async", "utility", "concurrency", "concurrent"], + "dependencies": { + "component/emitter": "*" + }, + "development": {}, + "scripts": [ + "index.js" + ] +} diff --git a/node_modules/serve-index/node_modules/batch/index.js b/node_modules/serve-index/node_modules/batch/index.js new file mode 100644 index 0000000..c2cbe46 --- /dev/null +++ b/node_modules/serve-index/node_modules/batch/index.js @@ -0,0 +1,158 @@ +/** + * Module dependencies. + */ + +try { + var EventEmitter = require('events').EventEmitter; +} catch (err) { + var Emitter = require('emitter'); +} + +/** + * Noop. + */ + +function noop(){} + +/** + * Expose `Batch`. + */ + +module.exports = Batch; + +/** + * Create a new Batch. + */ + +function Batch() { + if (!(this instanceof Batch)) return new Batch; + this.fns = []; + this.concurrency(Infinity); + this.throws(true); + for (var i = 0, len = arguments.length; i < len; ++i) { + this.push(arguments[i]); + } +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +if (EventEmitter) { + Batch.prototype.__proto__ = EventEmitter.prototype; +} else { + Emitter(Batch.prototype); +} + +/** + * Set concurrency to `n`. + * + * @param {Number} n + * @return {Batch} + * @api public + */ + +Batch.prototype.concurrency = function(n){ + this.n = n; + return this; +}; + +/** + * Queue a function. + * + * @param {Function} fn + * @return {Batch} + * @api public + */ + +Batch.prototype.push = function(fn){ + this.fns.push(fn); + return this; +}; + +/** + * Set wether Batch will or will not throw up. + * + * @param {Boolean} throws + * @return {Batch} + * @api public + */ +Batch.prototype.throws = function(throws) { + this.e = !!throws; + return this; +}; + +/** + * Execute all queued functions in parallel, + * executing `cb(err, results)`. + * + * @param {Function} cb + * @return {Batch} + * @api public + */ + +Batch.prototype.end = function(cb){ + var self = this + , total = this.fns.length + , pending = total + , results = [] + , errors = [] + , cb = cb || noop + , fns = this.fns + , max = this.n + , throws = this.e + , index = 0 + , done; + + // empty + if (!fns.length) return cb(null, results); + + // process + function next() { + var i = index++; + var fn = fns[i]; + if (!fn) return; + var start = new Date; + + try { + fn(callback); + } catch (err) { + callback(err); + } + + function callback(err, res){ + if (done) return; + if (err && throws) return done = true, cb(err); + var complete = total - pending + 1; + var end = new Date; + + results[i] = res; + errors[i] = err; + + self.emit('progress', { + index: i, + value: res, + error: err, + pending: pending, + total: total, + complete: complete, + percent: complete / total * 100 | 0, + start: start, + end: end, + duration: end - start + }); + + if (--pending) next(); + else if(!throws) cb(errors, results); + else cb(null, results); + } + } + + // concurrency + for (var i = 0; i < fns.length; i++) { + if (i == max) break; + next(); + } + + return this; +}; diff --git a/node_modules/serve-index/node_modules/batch/package.json b/node_modules/serve-index/node_modules/batch/package.json new file mode 100644 index 0000000..3d58ce4 --- /dev/null +++ b/node_modules/serve-index/node_modules/batch/package.json @@ -0,0 +1,53 @@ +{ + "name": "batch", + "version": "0.5.2", + "licenses": [ + { + "type": "MIT" + } + ], + "description": "Simple async batch", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "main": "index", + "browser": { + "emitter": "component-emitter" + }, + "repository": { + "type": "git", + "url": "https://github.com/visionmedia/batch.git" + }, + "gitHead": "cd69ea173754c0fbd3a7ab33e0a678e6909f3bf3", + "bugs": { + "url": "https://github.com/visionmedia/batch/issues" + }, + "homepage": "https://github.com/visionmedia/batch", + "_id": "batch@0.5.2", + "scripts": {}, + "_shasum": "546543dbe32118c83c7c7ca33a1f5c5d5ea963e9", + "_from": "batch@0.5.2", + "_npmVersion": "2.1.14", + "_nodeVersion": "0.11.14", + "_npmUser": { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + } + ], + "dist": { + "shasum": "546543dbe32118c83c7c7ca33a1f5c5d5ea963e9", + "tarball": "http://registry.npmjs.org/batch/-/batch-0.5.2.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/batch/-/batch-0.5.2.tgz" +} diff --git a/node_modules/serve-index/node_modules/debug/.jshintrc b/node_modules/serve-index/node_modules/debug/.jshintrc new file mode 100644 index 0000000..299877f --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/.jshintrc @@ -0,0 +1,3 @@ +{ + "laxbreak": true +} diff --git a/node_modules/serve-index/node_modules/debug/.npmignore b/node_modules/serve-index/node_modules/debug/.npmignore new file mode 100644 index 0000000..7e6163d --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/.npmignore @@ -0,0 +1,6 @@ +support +test +examples +example +*.sock +dist diff --git a/node_modules/serve-index/node_modules/debug/History.md b/node_modules/serve-index/node_modules/debug/History.md new file mode 100644 index 0000000..854c971 --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/History.md @@ -0,0 +1,195 @@ + +2.2.0 / 2015-05-09 +================== + + * package: update "ms" to v0.7.1 (#202, @dougwilson) + * README: add logging to file example (#193, @DanielOchoa) + * README: fixed a typo (#191, @amir-s) + * browser: expose `storage` (#190, @stephenmathieson) + * Makefile: add a `distclean` target (#189, @stephenmathieson) + +2.1.3 / 2015-03-13 +================== + + * Updated stdout/stderr example (#186) + * Updated example/stdout.js to match debug current behaviour + * Renamed example/stderr.js to stdout.js + * Update Readme.md (#184) + * replace high intensity foreground color for bold (#182, #183) + +2.1.2 / 2015-03-01 +================== + + * dist: recompile + * update "ms" to v0.7.0 + * package: update "browserify" to v9.0.3 + * component: fix "ms.js" repo location + * changed bower package name + * updated documentation about using debug in a browser + * fix: security error on safari (#167, #168, @yields) + +2.1.1 / 2014-12-29 +================== + + * browser: use `typeof` to check for `console` existence + * browser: check for `console.log` truthiness (fix IE 8/9) + * browser: add support for Chrome apps + * Readme: added Windows usage remarks + * Add `bower.json` to properly support bower install + +2.1.0 / 2014-10-15 +================== + + * node: implement `DEBUG_FD` env variable support + * package: update "browserify" to v6.1.0 + * package: add "license" field to package.json (#135, @panuhorsmalahti) + +2.0.0 / 2014-09-01 +================== + + * package: update "browserify" to v5.11.0 + * node: use stderr rather than stdout for logging (#29, @stephenmathieson) + +1.0.4 / 2014-07-15 +================== + + * dist: recompile + * example: remove `console.info()` log usage + * example: add "Content-Type" UTF-8 header to browser example + * browser: place %c marker after the space character + * browser: reset the "content" color via `color: inherit` + * browser: add colors support for Firefox >= v31 + * debug: prefer an instance `log()` function over the global one (#119) + * Readme: update documentation about styled console logs for FF v31 (#116, @wryk) + +1.0.3 / 2014-07-09 +================== + + * Add support for multiple wildcards in namespaces (#122, @seegno) + * browser: fix lint + +1.0.2 / 2014-06-10 +================== + + * browser: update color palette (#113, @gscottolson) + * common: make console logging function configurable (#108, @timoxley) + * node: fix %o colors on old node <= 0.8.x + * Makefile: find node path using shell/which (#109, @timoxley) + +1.0.1 / 2014-06-06 +================== + + * browser: use `removeItem()` to clear localStorage + * browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777) + * package: add "contributors" section + * node: fix comment typo + * README: list authors + +1.0.0 / 2014-06-04 +================== + + * make ms diff be global, not be scope + * debug: ignore empty strings in enable() + * node: make DEBUG_COLORS able to disable coloring + * *: export the `colors` array + * npmignore: don't publish the `dist` dir + * Makefile: refactor to use browserify + * package: add "browserify" as a dev dependency + * Readme: add Web Inspector Colors section + * node: reset terminal color for the debug content + * node: map "%o" to `util.inspect()` + * browser: map "%j" to `JSON.stringify()` + * debug: add custom "formatters" + * debug: use "ms" module for humanizing the diff + * Readme: add "bash" syntax highlighting + * browser: add Firebug color support + * browser: add colors for WebKit browsers + * node: apply log to `console` + * rewrite: abstract common logic for Node & browsers + * add .jshintrc file + +0.8.1 / 2014-04-14 +================== + + * package: re-add the "component" section + +0.8.0 / 2014-03-30 +================== + + * add `enable()` method for nodejs. Closes #27 + * change from stderr to stdout + * remove unnecessary index.js file + +0.7.4 / 2013-11-13 +================== + + * remove "browserify" key from package.json (fixes something in browserify) + +0.7.3 / 2013-10-30 +================== + + * fix: catch localStorage security error when cookies are blocked (Chrome) + * add debug(err) support. Closes #46 + * add .browser prop to package.json. Closes #42 + +0.7.2 / 2013-02-06 +================== + + * fix package.json + * fix: Mobile Safari (private mode) is broken with debug + * fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript + +0.7.1 / 2013-02-05 +================== + + * add repository URL to package.json + * add DEBUG_COLORED to force colored output + * add browserify support + * fix component. Closes #24 + +0.7.0 / 2012-05-04 +================== + + * Added .component to package.json + * Added debug.component.js build + +0.6.0 / 2012-03-16 +================== + + * Added support for "-" prefix in DEBUG [Vinay Pulim] + * Added `.enabled` flag to the node version [TooTallNate] + +0.5.0 / 2012-02-02 +================== + + * Added: humanize diffs. Closes #8 + * Added `debug.disable()` to the CS variant + * Removed padding. Closes #10 + * Fixed: persist client-side variant again. Closes #9 + +0.4.0 / 2012-02-01 +================== + + * Added browser variant support for older browsers [TooTallNate] + * Added `debug.enable('project:*')` to browser variant [TooTallNate] + * Added padding to diff (moved it to the right) + +0.3.0 / 2012-01-26 +================== + + * Added millisecond diff when isatty, otherwise UTC string + +0.2.0 / 2012-01-22 +================== + + * Added wildcard support + +0.1.0 / 2011-12-02 +================== + + * Added: remove colors unless stderr isatty [TooTallNate] + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/node_modules/serve-index/node_modules/debug/Makefile b/node_modules/serve-index/node_modules/debug/Makefile new file mode 100644 index 0000000..5cf4a59 --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/Makefile @@ -0,0 +1,36 @@ + +# get Makefile directory name: http://stackoverflow.com/a/5982798/376773 +THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd) + +# BIN directory +BIN := $(THIS_DIR)/node_modules/.bin + +# applications +NODE ?= $(shell which node) +NPM ?= $(NODE) $(shell which npm) +BROWSERIFY ?= $(NODE) $(BIN)/browserify + +all: dist/debug.js + +install: node_modules + +clean: + @rm -rf dist + +dist: + @mkdir -p $@ + +dist/debug.js: node_modules browser.js debug.js dist + @$(BROWSERIFY) \ + --standalone debug \ + . > $@ + +distclean: clean + @rm -rf node_modules + +node_modules: package.json + @NODE_ENV= $(NPM) install + @touch node_modules + +.PHONY: all install clean distclean diff --git a/node_modules/serve-index/node_modules/debug/Readme.md b/node_modules/serve-index/node_modules/debug/Readme.md new file mode 100644 index 0000000..b4f45e3 --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/Readme.md @@ -0,0 +1,188 @@ +# debug + + tiny node.js debugging utility modelled after node core's debugging technique. + +## Installation + +```bash +$ npm install debug +``` + +## Usage + + With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility. + +Example _app.js_: + +```js +var debug = require('debug')('http') + , http = require('http') + , name = 'My App'; + +// fake app + +debug('booting %s', name); + +http.createServer(function(req, res){ + debug(req.method + ' ' + req.url); + res.end('hello\n'); +}).listen(3000, function(){ + debug('listening'); +}); + +// fake worker of some kind + +require('./worker'); +``` + +Example _worker.js_: + +```js +var debug = require('debug')('worker'); + +setInterval(function(){ + debug('doing some work'); +}, 1000); +``` + + The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples: + + ![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png) + + ![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png) + +#### Windows note + + On Windows the environment variable is set using the `set` command. + + ```cmd + set DEBUG=*,-not_this + ``` + +Then, run the program to be debugged as usual. + +## Millisecond diff + + When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls. + + ![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png) + + When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below: + + ![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png) + +## Conventions + + If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". + +## Wildcards + + The `*` character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`. + + You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with "connect:". + +## Browser support + + Debug works in the browser as well, currently persisted by `localStorage`. Consider the situation shown below where you have `worker:a` and `worker:b`, and wish to debug both. Somewhere in the code on your page, include: + +```js +window.myDebug = require("debug"); +``` + + ("debug" is a global object in the browser so we give this object a different name.) When your page is open in the browser, type the following in the console: + +```js +myDebug.enable("worker:*") +``` + + Refresh the page. Debug output will continue to be sent to the console until it is disabled by typing `myDebug.disable()` in the console. + +```js +a = debug('worker:a'); +b = debug('worker:b'); + +setInterval(function(){ + a('doing some work'); +}, 1000); + +setInterval(function(){ + b('doing some work'); +}, 1200); +``` + +#### Web Inspector Colors + + Colors are also enabled on "Web Inspectors" that understand the `%c` formatting + option. These are WebKit web inspectors, Firefox ([since version + 31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/)) + and the Firebug plugin for Firefox (any version). + + Colored output looks something like: + + ![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png) + +### stderr vs stdout + +You can set an alternative logging method per-namespace by overriding the `log` method on a per-namespace or globally: + +Example _stdout.js_: + +```js +var debug = require('debug'); +var error = debug('app:error'); + +// by default stderr is used +error('goes to stderr!'); + +var log = debug('app:log'); +// set this namespace to log via console.log +log.log = console.log.bind(console); // don't forget to bind to console! +log('goes to stdout'); +error('still goes to stderr!'); + +// set all output to go via console.info +// overrides all per-namespace log settings +debug.log = console.info.bind(console); +error('now goes to stdout via console.info'); +log('still goes to stdout, but via console.info now'); +``` + +### Save debug output to a file + +You can save all debug statements to a file by piping them. + +Example: + +```bash +$ DEBUG_FD=3 node your-app.js 3> whatever.log +``` + +## Authors + + - TJ Holowaychuk + - Nathan Rajlich + +## License + +(The MIT License) + +Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/serve-index/node_modules/debug/bower.json b/node_modules/serve-index/node_modules/debug/bower.json new file mode 100644 index 0000000..6af573f --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/bower.json @@ -0,0 +1,28 @@ +{ + "name": "visionmedia-debug", + "main": "dist/debug.js", + "version": "2.2.0", + "homepage": "https://github.com/visionmedia/debug", + "authors": [ + "TJ Holowaychuk " + ], + "description": "visionmedia-debug", + "moduleType": [ + "amd", + "es6", + "globals", + "node" + ], + "keywords": [ + "visionmedia", + "debug" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/node_modules/serve-index/node_modules/debug/browser.js b/node_modules/serve-index/node_modules/debug/browser.js new file mode 100644 index 0000000..7c76452 --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/browser.js @@ -0,0 +1,168 @@ + +/** + * This is the web browser implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = require('./debug'); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +exports.storage = 'undefined' != typeof chrome + && 'undefined' != typeof chrome.storage + ? chrome.storage.local + : localstorage(); + +/** + * Colors. + */ + +exports.colors = [ + 'lightseagreen', + 'forestgreen', + 'goldenrod', + 'dodgerblue', + 'darkorchid', + 'crimson' +]; + +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + +function useColors() { + // is webkit? http://stackoverflow.com/a/16459606/376773 + return ('WebkitAppearance' in document.documentElement.style) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (window.console && (console.firebug || (console.exception && console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); +} + +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +exports.formatters.j = function(v) { + return JSON.stringify(v); +}; + + +/** + * Colorize log arguments if enabled. + * + * @api public + */ + +function formatArgs() { + var args = arguments; + var useColors = this.useColors; + + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); + + if (!useColors) return args; + + var c = 'color: ' + this.color; + args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); + + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); + return args; +} + +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + +function log() { + // this hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return 'object' === typeof console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + try { + if (null == namespaces) { + exports.storage.removeItem('debug'); + } else { + exports.storage.debug = namespaces; + } + } catch(e) {} +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + var r; + try { + r = exports.storage.debug; + } catch(e) {} + return r; +} + +/** + * Enable namespaces listed in `localStorage.debug` initially. + */ + +exports.enable(load()); + +/** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + +function localstorage(){ + try { + return window.localStorage; + } catch (e) {} +} diff --git a/node_modules/serve-index/node_modules/debug/component.json b/node_modules/serve-index/node_modules/debug/component.json new file mode 100644 index 0000000..ca10637 --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/component.json @@ -0,0 +1,19 @@ +{ + "name": "debug", + "repo": "visionmedia/debug", + "description": "small debugging utility", + "version": "2.2.0", + "keywords": [ + "debug", + "log", + "debugger" + ], + "main": "browser.js", + "scripts": [ + "browser.js", + "debug.js" + ], + "dependencies": { + "rauchg/ms.js": "0.7.1" + } +} diff --git a/node_modules/serve-index/node_modules/debug/debug.js b/node_modules/serve-index/node_modules/debug/debug.js new file mode 100644 index 0000000..7571a86 --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/debug.js @@ -0,0 +1,197 @@ + +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = debug; +exports.coerce = coerce; +exports.disable = disable; +exports.enable = enable; +exports.enabled = enabled; +exports.humanize = require('ms'); + +/** + * The currently active debug mode names, and names to skip. + */ + +exports.names = []; +exports.skips = []; + +/** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lowercased letter, i.e. "n". + */ + +exports.formatters = {}; + +/** + * Previously assigned color. + */ + +var prevColor = 0; + +/** + * Previous log timestamp. + */ + +var prevTime; + +/** + * Select a color. + * + * @return {Number} + * @api private + */ + +function selectColor() { + return exports.colors[prevColor++ % exports.colors.length]; +} + +/** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + +function debug(namespace) { + + // define the `disabled` version + function disabled() { + } + disabled.enabled = false; + + // define the `enabled` version + function enabled() { + + var self = enabled; + + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + // add the `color` if not set + if (null == self.useColors) self.useColors = exports.useColors(); + if (null == self.color && self.useColors) self.color = selectColor(); + + var args = Array.prototype.slice.call(arguments); + + args[0] = exports.coerce(args[0]); + + if ('string' !== typeof args[0]) { + // anything else let's inspect with %o + args = ['%o'].concat(args); + } + + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); + + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + if ('function' === typeof exports.formatArgs) { + args = exports.formatArgs.apply(self, args); + } + var logFn = enabled.log || exports.log || console.log.bind(console); + logFn.apply(self, args); + } + enabled.enabled = true; + + var fn = exports.enabled(namespace) ? enabled : disabled; + + fn.namespace = namespace; + + return fn; +} + +/** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + +function enable(namespaces) { + exports.save(namespaces); + + var split = (namespaces || '').split(/[\s,]+/); + var len = split.length; + + for (var i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } + } +} + +/** + * Disable debug output. + * + * @api public + */ + +function disable() { + exports.enable(''); +} + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +function enabled(name) { + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; + } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; + } + } + return false; +} + +/** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} diff --git a/node_modules/serve-index/node_modules/debug/node.js b/node_modules/serve-index/node_modules/debug/node.js new file mode 100644 index 0000000..1d392a8 --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/node.js @@ -0,0 +1,209 @@ + +/** + * Module dependencies. + */ + +var tty = require('tty'); +var util = require('util'); + +/** + * This is the Node.js implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = require('./debug'); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; + +/** + * Colors. + */ + +exports.colors = [6, 2, 3, 4, 5, 1]; + +/** + * The file descriptor to write the `debug()` calls to. + * Set the `DEBUG_FD` env variable to override with another value. i.e.: + * + * $ DEBUG_FD=3 node script.js 3>debug.log + */ + +var fd = parseInt(process.env.DEBUG_FD, 10) || 2; +var stream = 1 === fd ? process.stdout : + 2 === fd ? process.stderr : + createWritableStdioStream(fd); + +/** + * Is stdout a TTY? Colored output is enabled when `true`. + */ + +function useColors() { + var debugColors = (process.env.DEBUG_COLORS || '').trim().toLowerCase(); + if (0 === debugColors.length) { + return tty.isatty(fd); + } else { + return '0' !== debugColors + && 'no' !== debugColors + && 'false' !== debugColors + && 'disabled' !== debugColors; + } +} + +/** + * Map %o to `util.inspect()`, since Node doesn't do that out of the box. + */ + +var inspect = (4 === util.inspect.length ? + // node <= 0.8.x + function (v, colors) { + return util.inspect(v, void 0, void 0, colors); + } : + // node > 0.8.x + function (v, colors) { + return util.inspect(v, { colors: colors }); + } +); + +exports.formatters.o = function(v) { + return inspect(v, this.useColors) + .replace(/\s*\n\s*/g, ' '); +}; + +/** + * Adds ANSI color escape codes if enabled. + * + * @api public + */ + +function formatArgs() { + var args = arguments; + var useColors = this.useColors; + var name = this.namespace; + + if (useColors) { + var c = this.color; + + args[0] = ' \u001b[3' + c + ';1m' + name + ' ' + + '\u001b[0m' + + args[0] + '\u001b[3' + c + 'm' + + ' +' + exports.humanize(this.diff) + '\u001b[0m'; + } else { + args[0] = new Date().toUTCString() + + ' ' + name + ' ' + args[0]; + } + return args; +} + +/** + * Invokes `console.error()` with the specified arguments. + */ + +function log() { + return stream.write(util.format.apply(this, arguments) + '\n'); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + if (null == namespaces) { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } else { + process.env.DEBUG = namespaces; + } +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + return process.env.DEBUG; +} + +/** + * Copied from `node/src/node.js`. + * + * XXX: It's lame that node doesn't expose this API out-of-the-box. It also + * relies on the undocumented `tty_wrap.guessHandleType()` which is also lame. + */ + +function createWritableStdioStream (fd) { + var stream; + var tty_wrap = process.binding('tty_wrap'); + + // Note stream._type is used for test-module-load-list.js + + switch (tty_wrap.guessHandleType(fd)) { + case 'TTY': + stream = new tty.WriteStream(fd); + stream._type = 'tty'; + + // Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; + + case 'FILE': + var fs = require('fs'); + stream = new fs.SyncWriteStream(fd, { autoClose: false }); + stream._type = 'fs'; + break; + + case 'PIPE': + case 'TCP': + var net = require('net'); + stream = new net.Socket({ + fd: fd, + readable: false, + writable: true + }); + + // FIXME Should probably have an option in net.Socket to create a + // stream from an existing fd which is writable only. But for now + // we'll just add this hack and set the `readable` member to false. + // Test: ./node test/fixtures/echo.js < /etc/passwd + stream.readable = false; + stream.read = null; + stream._type = 'pipe'; + + // FIXME Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; + + default: + // Probably an error on in uv_guess_handle() + throw new Error('Implement me. Unknown stream file type!'); + } + + // For supporting legacy API we put the FD here. + stream.fd = fd; + + stream._isStdio = true; + + return stream; +} + +/** + * Enable namespaces listed in `process.env.DEBUG` initially. + */ + +exports.enable(load()); diff --git a/node_modules/serve-index/node_modules/debug/node_modules/ms/.npmignore b/node_modules/serve-index/node_modules/debug/node_modules/ms/.npmignore new file mode 100644 index 0000000..d1aa0ce --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/node_modules/ms/.npmignore @@ -0,0 +1,5 @@ +node_modules +test +History.md +Makefile +component.json diff --git a/node_modules/serve-index/node_modules/debug/node_modules/ms/History.md b/node_modules/serve-index/node_modules/debug/node_modules/ms/History.md new file mode 100644 index 0000000..32fdfc1 --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/node_modules/ms/History.md @@ -0,0 +1,66 @@ + +0.7.1 / 2015-04-20 +================== + + * prevent extraordinary long inputs (@evilpacket) + * Fixed broken readme link + +0.7.0 / 2014-11-24 +================== + + * add time abbreviations, updated tests and readme for the new units + * fix example in the readme. + * add LICENSE file + +0.6.2 / 2013-12-05 +================== + + * Adding repository section to package.json to suppress warning from NPM. + +0.6.1 / 2013-05-10 +================== + + * fix singularization [visionmedia] + +0.6.0 / 2013-03-15 +================== + + * fix minutes + +0.5.1 / 2013-02-24 +================== + + * add component namespace + +0.5.0 / 2012-11-09 +================== + + * add short formatting as default and .long option + * add .license property to component.json + * add version to component.json + +0.4.0 / 2012-10-22 +================== + + * add rounding to fix crazy decimals + +0.3.0 / 2012-09-07 +================== + + * fix `ms()` [visionmedia] + +0.2.0 / 2012-09-03 +================== + + * add component.json [visionmedia] + * add days support [visionmedia] + * add hours support [visionmedia] + * add minutes support [visionmedia] + * add seconds support [visionmedia] + * add ms string support [visionmedia] + * refactor tests to facilitate ms(number) [visionmedia] + +0.1.0 / 2012-03-07 +================== + + * Initial release diff --git a/node_modules/serve-index/node_modules/debug/node_modules/ms/LICENSE b/node_modules/serve-index/node_modules/debug/node_modules/ms/LICENSE new file mode 100644 index 0000000..6c07561 --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/node_modules/ms/LICENSE @@ -0,0 +1,20 @@ +(The MIT License) + +Copyright (c) 2014 Guillermo Rauch + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/serve-index/node_modules/debug/node_modules/ms/README.md b/node_modules/serve-index/node_modules/debug/node_modules/ms/README.md new file mode 100644 index 0000000..9b4fd03 --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/node_modules/ms/README.md @@ -0,0 +1,35 @@ +# ms.js: miliseconds conversion utility + +```js +ms('2 days') // 172800000 +ms('1d') // 86400000 +ms('10h') // 36000000 +ms('2.5 hrs') // 9000000 +ms('2h') // 7200000 +ms('1m') // 60000 +ms('5s') // 5000 +ms('100') // 100 +``` + +```js +ms(60000) // "1m" +ms(2 * 60000) // "2m" +ms(ms('10 hours')) // "10h" +``` + +```js +ms(60000, { long: true }) // "1 minute" +ms(2 * 60000, { long: true }) // "2 minutes" +ms(ms('10 hours'), { long: true }) // "10 hours" +``` + +- Node/Browser compatible. Published as [`ms`](https://www.npmjs.org/package/ms) in [NPM](http://nodejs.org/download). +- If a number is supplied to `ms`, a string with a unit is returned. +- If a string that contains the number is supplied, it returns it as +a number (e.g: it returns `100` for `'100'`). +- If you pass a string with a number and a valid unit, the number of +equivalent ms is returned. + +## License + +MIT diff --git a/node_modules/serve-index/node_modules/debug/node_modules/ms/index.js b/node_modules/serve-index/node_modules/debug/node_modules/ms/index.js new file mode 100644 index 0000000..4f92771 --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/node_modules/ms/index.js @@ -0,0 +1,125 @@ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} options + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options){ + options = options || {}; + if ('string' == typeof val) return parse(val); + return options.long + ? long(val) + : short(val); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + str = '' + str; + if (str.length > 10000) return; + var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str); + if (!match) return; + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function short(ms) { + if (ms >= d) return Math.round(ms / d) + 'd'; + if (ms >= h) return Math.round(ms / h) + 'h'; + if (ms >= m) return Math.round(ms / m) + 'm'; + if (ms >= s) return Math.round(ms / s) + 's'; + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function long(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) return; + if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; + return Math.ceil(ms / n) + ' ' + name + 's'; +} diff --git a/node_modules/serve-index/node_modules/debug/node_modules/ms/package.json b/node_modules/serve-index/node_modules/debug/node_modules/ms/package.json new file mode 100644 index 0000000..b12c4a0 --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/node_modules/ms/package.json @@ -0,0 +1,47 @@ +{ + "name": "ms", + "version": "0.7.1", + "description": "Tiny ms conversion utility", + "repository": { + "type": "git", + "url": "git://github.com/guille/ms.js.git" + }, + "main": "./index", + "devDependencies": { + "mocha": "*", + "expect.js": "*", + "serve": "*" + }, + "component": { + "scripts": { + "ms/index.js": "index.js" + } + }, + "gitHead": "713dcf26d9e6fd9dbc95affe7eff9783b7f1b909", + "bugs": { + "url": "https://github.com/guille/ms.js/issues" + }, + "homepage": "https://github.com/guille/ms.js", + "_id": "ms@0.7.1", + "scripts": {}, + "_shasum": "9cd13c03adbff25b65effde7ce864ee952017098", + "_from": "ms@0.7.1", + "_npmVersion": "2.7.5", + "_nodeVersion": "0.12.2", + "_npmUser": { + "name": "rauchg", + "email": "rauchg@gmail.com" + }, + "maintainers": [ + { + "name": "rauchg", + "email": "rauchg@gmail.com" + } + ], + "dist": { + "shasum": "9cd13c03adbff25b65effde7ce864ee952017098", + "tarball": "http://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" +} diff --git a/node_modules/serve-index/node_modules/debug/package.json b/node_modules/serve-index/node_modules/debug/package.json new file mode 100644 index 0000000..91c6b99 --- /dev/null +++ b/node_modules/serve-index/node_modules/debug/package.json @@ -0,0 +1,72 @@ +{ + "name": "debug", + "version": "2.2.0", + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/debug.git" + }, + "description": "small debugging utility", + "keywords": [ + "debug", + "log", + "debugger" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "contributors": [ + { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net", + "url": "http://n8.io" + } + ], + "license": "MIT", + "dependencies": { + "ms": "0.7.1" + }, + "devDependencies": { + "browserify": "9.0.3", + "mocha": "*" + }, + "main": "./node.js", + "browser": "./browser.js", + "component": { + "scripts": { + "debug/index.js": "browser.js", + "debug/debug.js": "debug.js" + } + }, + "gitHead": "b38458422b5aa8aa6d286b10dfe427e8a67e2b35", + "bugs": { + "url": "https://github.com/visionmedia/debug/issues" + }, + "homepage": "https://github.com/visionmedia/debug", + "_id": "debug@2.2.0", + "scripts": {}, + "_shasum": "f87057e995b1a1f6ae6a4960664137bc56f039da", + "_from": "debug@>=2.2.0 <2.3.0", + "_npmVersion": "2.7.4", + "_nodeVersion": "0.12.2", + "_npmUser": { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + } + ], + "dist": { + "shasum": "f87057e995b1a1f6ae6a4960664137bc56f039da", + "tarball": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" +} diff --git a/node_modules/serve-index/node_modules/escape-html/LICENSE b/node_modules/serve-index/node_modules/escape-html/LICENSE new file mode 100644 index 0000000..a3f0274 --- /dev/null +++ b/node_modules/serve-index/node_modules/escape-html/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2012-2013 TJ Holowaychuk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/serve-index/node_modules/escape-html/Readme.md b/node_modules/serve-index/node_modules/escape-html/Readme.md new file mode 100644 index 0000000..2cfcc99 --- /dev/null +++ b/node_modules/serve-index/node_modules/escape-html/Readme.md @@ -0,0 +1,15 @@ + +# escape-html + + Escape HTML entities + +## Example + +```js +var escape = require('escape-html'); +escape(str); +``` + +## License + + MIT \ No newline at end of file diff --git a/node_modules/serve-index/node_modules/escape-html/index.js b/node_modules/serve-index/node_modules/escape-html/index.js new file mode 100644 index 0000000..d0f9256 --- /dev/null +++ b/node_modules/serve-index/node_modules/escape-html/index.js @@ -0,0 +1,29 @@ +/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module exports. + * @public + */ + +module.exports = escapeHtml; + +/** + * Escape special characters in the given string of html. + * + * @param {string} str The string to escape for inserting into HTML + * @return {string} + * @public + */ + +function escapeHtml(html) { + return String(html) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(//g, '>'); +} diff --git a/node_modules/serve-index/node_modules/escape-html/package.json b/node_modules/serve-index/node_modules/escape-html/package.json new file mode 100644 index 0000000..c0cda2d --- /dev/null +++ b/node_modules/serve-index/node_modules/escape-html/package.json @@ -0,0 +1,50 @@ +{ + "name": "escape-html", + "description": "Escape HTML entities", + "version": "1.0.2", + "license": "MIT", + "keywords": [ + "escape", + "html", + "utility" + ], + "repository": { + "type": "git", + "url": "https://github.com/component/escape-html" + }, + "files": [ + "LICENSE", + "Readme.md", + "index.js" + ], + "gitHead": "2477a23ae56f75e0a5622a20b5b55da00de3a23b", + "bugs": { + "url": "https://github.com/component/escape-html/issues" + }, + "homepage": "https://github.com/component/escape-html", + "_id": "escape-html@1.0.2", + "scripts": {}, + "_shasum": "d77d32fa98e38c2f41ae85e9278e0e0e6ba1022c", + "_from": "escape-html@1.0.2", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "d77d32fa98e38c2f41ae85e9278e0e0e6ba1022c", + "tarball": "http://registry.npmjs.org/escape-html/-/escape-html-1.0.2.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.2.tgz" +} diff --git a/node_modules/serve-index/node_modules/http-errors/HISTORY.md b/node_modules/serve-index/node_modules/http-errors/HISTORY.md new file mode 100644 index 0000000..4c7087d --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/HISTORY.md @@ -0,0 +1,76 @@ +2015-02-02 / 1.3.1 +================== + + * Fix regression where status can be overwritten in `createError` `props` + +2015-02-01 / 1.3.0 +================== + + * Construct errors using defined constructors from `createError` + * Fix error names that are not identifiers + - `createError["I'mateapot"]` is now `createError.ImATeapot` + * Set a meaningful `name` property on constructed errors + +2014-12-09 / 1.2.8 +================== + + * Fix stack trace from exported function + * Remove `arguments.callee` usage + +2014-10-14 / 1.2.7 +================== + + * Remove duplicate line + +2014-10-02 / 1.2.6 +================== + + * Fix `expose` to be `true` for `ClientError` constructor + +2014-09-28 / 1.2.5 +================== + + * deps: statuses@1 + +2014-09-21 / 1.2.4 +================== + + * Fix dependency version to work with old `npm`s + +2014-09-21 / 1.2.3 +================== + + * deps: statuses@~1.1.0 + +2014-09-21 / 1.2.2 +================== + + * Fix publish error + +2014-09-21 / 1.2.1 +================== + + * Support Node.js 0.6 + * Use `inherits` instead of `util` + +2014-09-09 / 1.2.0 +================== + + * Fix the way inheriting functions + * Support `expose` being provided in properties argument + +2014-09-08 / 1.1.0 +================== + + * Default status to 500 + * Support provided `error` to extend + +2014-09-08 / 1.0.1 +================== + + * Fix accepting string message + +2014-09-08 / 1.0.0 +================== + + * Initial release diff --git a/node_modules/serve-index/node_modules/http-errors/LICENSE b/node_modules/serve-index/node_modules/http-errors/LICENSE new file mode 100644 index 0000000..a7ae8ee --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/serve-index/node_modules/http-errors/README.md b/node_modules/serve-index/node_modules/http-errors/README.md new file mode 100644 index 0000000..520271e --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/README.md @@ -0,0 +1,63 @@ +# http-errors + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Create HTTP errors for Express, Koa, Connect, etc. with ease. + +## Example + +```js +var createError = require('http-errors'); + +app.use(function (req, res, next) { + if (!req.user) return next(createError(401, 'Please login to view this page.')); + next(); +}) +``` + +## API + +This is the current API, currently extracted from Koa and subject to change. + +### Error Properties + +- `message` +- `status` and `statusCode` - the status code of the error, defaulting to `500` + +### createError([status], [message], [properties]) + +```js +var err = createError(404, 'This video does not exist!'); +``` + +- `status: 500` - the status code as a number +- `message` - the message of the error, defaulting to node's text for that status code. +- `properties` - custom properties to attach to the object + +### new createError\[code || name\](\[msg]\)) + +```js +var err = new createError.NotFound(); +``` + +- `code` - the status code as a number +- `name` - the name of the error as a "bumpy case", i.e. `NotFound` or `InternalServerError`. + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/http-errors.svg?style=flat +[npm-url]: https://npmjs.org/package/http-errors +[node-version-image]: https://img.shields.io/node/v/http-errors.svg?style=flat +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/http-errors.svg?style=flat +[travis-url]: https://travis-ci.org/jshttp/http-errors +[coveralls-image]: https://img.shields.io/coveralls/jshttp/http-errors.svg?style=flat +[coveralls-url]: https://coveralls.io/r/jshttp/http-errors +[downloads-image]: https://img.shields.io/npm/dm/http-errors.svg?style=flat +[downloads-url]: https://npmjs.org/package/http-errors diff --git a/node_modules/serve-index/node_modules/http-errors/index.js b/node_modules/serve-index/node_modules/http-errors/index.js new file mode 100644 index 0000000..d84b114 --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/index.js @@ -0,0 +1,120 @@ + +var statuses = require('statuses'); +var inherits = require('inherits'); + +function toIdentifier(str) { + return str.split(' ').map(function (token) { + return token.slice(0, 1).toUpperCase() + token.slice(1) + }).join('').replace(/[^ _0-9a-z]/gi, '') +} + +exports = module.exports = function httpError() { + // so much arity going on ~_~ + var err; + var msg; + var status = 500; + var props = {}; + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i]; + if (arg instanceof Error) { + err = arg; + status = err.status || err.statusCode || status; + continue; + } + switch (typeof arg) { + case 'string': + msg = arg; + break; + case 'number': + status = arg; + break; + case 'object': + props = arg; + break; + } + } + + if (typeof status !== 'number' || !statuses[status]) { + status = 500 + } + + // constructor + var HttpError = exports[status] + + if (!err) { + // create error + err = HttpError + ? new HttpError(msg) + : new Error(msg || statuses[status]) + Error.captureStackTrace(err, httpError) + } + + if (!HttpError || !(err instanceof HttpError)) { + // add properties to generic error + err.expose = status < 500 + err.status = err.statusCode = status + } + + for (var key in props) { + if (key !== 'status' && key !== 'statusCode') { + err[key] = props[key] + } + } + + return err; +}; + +// create generic error objects +var codes = statuses.codes.filter(function (num) { + return num >= 400; +}); + +codes.forEach(function (code) { + var name = toIdentifier(statuses[code]) + var className = name.match(/Error$/) ? name : name + 'Error' + + if (code >= 500) { + var ServerError = function ServerError(msg) { + var self = new Error(msg != null ? msg : statuses[code]) + Error.captureStackTrace(self, ServerError) + self.__proto__ = ServerError.prototype + Object.defineProperty(self, 'name', { + enumerable: false, + configurable: true, + value: className, + writable: true + }) + return self + } + inherits(ServerError, Error); + ServerError.prototype.status = + ServerError.prototype.statusCode = code; + ServerError.prototype.expose = false; + exports[code] = + exports[name] = ServerError + return; + } + + var ClientError = function ClientError(msg) { + var self = new Error(msg != null ? msg : statuses[code]) + Error.captureStackTrace(self, ClientError) + self.__proto__ = ClientError.prototype + Object.defineProperty(self, 'name', { + enumerable: false, + configurable: true, + value: className, + writable: true + }) + return self + } + inherits(ClientError, Error); + ClientError.prototype.status = + ClientError.prototype.statusCode = code; + ClientError.prototype.expose = true; + exports[code] = + exports[name] = ClientError + return; +}); + +// backwards-compatibility +exports["I'mateapot"] = exports.ImATeapot diff --git a/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/LICENSE b/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/LICENSE new file mode 100644 index 0000000..dea3013 --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/LICENSE @@ -0,0 +1,16 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + diff --git a/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/README.md b/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/README.md new file mode 100644 index 0000000..b1c5665 --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/README.md @@ -0,0 +1,42 @@ +Browser-friendly inheritance fully compatible with standard node.js +[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor). + +This package exports standard `inherits` from node.js `util` module in +node environment, but also provides alternative browser-friendly +implementation through [browser +field](https://gist.github.com/shtylman/4339901). Alternative +implementation is a literal copy of standard one located in standalone +module to avoid requiring of `util`. It also has a shim for old +browsers with no `Object.create` support. + +While keeping you sure you are using standard `inherits` +implementation in node.js environment, it allows bundlers such as +[browserify](https://github.com/substack/node-browserify) to not +include full `util` package to your client code if all you need is +just `inherits` function. It worth, because browser shim for `util` +package is large and `inherits` is often the single function you need +from it. + +It's recommended to use this package instead of +`require('util').inherits` for any code that has chances to be used +not only in node.js but in browser too. + +## usage + +```js +var inherits = require('inherits'); +// then use exactly as the standard one +``` + +## note on version ~1.0 + +Version ~1.0 had completely different motivation and is not compatible +neither with 2.0 nor with standard node.js `inherits`. + +If you are using version ~1.0 and planning to switch to ~2.0, be +careful: + +* new version uses `super_` instead of `super` for referencing + superclass +* new version overwrites current prototype while old one preserves any + existing fields on it diff --git a/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/inherits.js b/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/inherits.js new file mode 100644 index 0000000..29f5e24 --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/inherits.js @@ -0,0 +1 @@ +module.exports = require('util').inherits diff --git a/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/inherits_browser.js b/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/inherits_browser.js new file mode 100644 index 0000000..c1e78a7 --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/inherits_browser.js @@ -0,0 +1,23 @@ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} diff --git a/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/package.json b/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/package.json new file mode 100644 index 0000000..93d5078 --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/package.json @@ -0,0 +1,50 @@ +{ + "name": "inherits", + "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()", + "version": "2.0.1", + "keywords": [ + "inheritance", + "class", + "klass", + "oop", + "object-oriented", + "inherits", + "browser", + "browserify" + ], + "main": "./inherits.js", + "browser": "./inherits_browser.js", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/inherits.git" + }, + "license": "ISC", + "scripts": { + "test": "node test" + }, + "bugs": { + "url": "https://github.com/isaacs/inherits/issues" + }, + "_id": "inherits@2.0.1", + "dist": { + "shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1", + "tarball": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "_from": "inherits@>=2.0.1 <2.1.0", + "_npmVersion": "1.3.8", + "_npmUser": { + "name": "isaacs", + "email": "i@izs.me" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + } + ], + "directories": {}, + "_shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1", + "_resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "readme": "ERROR: No README data found!", + "homepage": "https://github.com/isaacs/inherits#readme" +} diff --git a/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/test.js b/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/test.js new file mode 100644 index 0000000..fc53012 --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/node_modules/inherits/test.js @@ -0,0 +1,25 @@ +var inherits = require('./inherits.js') +var assert = require('assert') + +function test(c) { + assert(c.constructor === Child) + assert(c.constructor.super_ === Parent) + assert(Object.getPrototypeOf(c) === Child.prototype) + assert(Object.getPrototypeOf(Object.getPrototypeOf(c)) === Parent.prototype) + assert(c instanceof Child) + assert(c instanceof Parent) +} + +function Child() { + Parent.call(this) + test(this) +} + +function Parent() {} + +inherits(Child, Parent) + +var c = new Child +test(c) + +console.log('ok') diff --git a/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/LICENSE b/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/LICENSE new file mode 100644 index 0000000..a7ae8ee --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/README.md b/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/README.md new file mode 100644 index 0000000..f6ae24c --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/README.md @@ -0,0 +1,114 @@ +# Statuses + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +HTTP status utility for node. + +## API + +```js +var status = require('statuses'); +``` + +### var code = status(Integer || String) + +If `Integer` or `String` is a valid HTTP code or status message, then the appropriate `code` will be returned. Otherwise, an error will be thrown. + +```js +status(403) // => 'Forbidden' +status('403') // => 'Forbidden' +status('forbidden') // => 403 +status('Forbidden') // => 403 +status(306) // throws, as it's not supported by node.js +``` + +### status.codes + +Returns an array of all the status codes as `Integer`s. + +### var msg = status[code] + +Map of `code` to `status message`. `undefined` for invalid `code`s. + +```js +status[404] // => 'Not Found' +``` + +### var code = status[msg] + +Map of `status message` to `code`. `msg` can either be title-cased or lower-cased. `undefined` for invalid `status message`s. + +```js +status['not found'] // => 404 +status['Not Found'] // => 404 +``` + +### status.redirect[code] + +Returns `true` if a status code is a valid redirect status. + +```js +status.redirect[200] // => undefined +status.redirect[301] // => true +``` + +### status.empty[code] + +Returns `true` if a status code expects an empty body. + +```js +status.empty[200] // => undefined +status.empty[204] // => true +status.empty[304] // => true +``` + +### status.retry[code] + +Returns `true` if you should retry the rest. + +```js +status.retry[501] // => undefined +status.retry[503] // => true +``` + +### statuses/codes.json + +```js +var codes = require('statuses/codes.json'); +``` + +This is a JSON file of the status codes +taken from `require('http').STATUS_CODES`. +This is saved so that codes are consistent even in older node.js versions. +For example, `308` will be added in v0.12. + +## Adding Status Codes + +The status codes are primarily sourced from http://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv. +Additionally, custom codes are added from http://en.wikipedia.org/wiki/List_of_HTTP_status_codes. +These are added manually in the `lib/*.json` files. +If you would like to add a status code, add it to the appropriate JSON file. + +To rebuild `codes.json`, run the following: + +```bash +# update src/iana.json +npm run update +# build codes.json +npm run build +``` + +[npm-image]: https://img.shields.io/npm/v/statuses.svg?style=flat +[npm-url]: https://npmjs.org/package/statuses +[node-version-image]: http://img.shields.io/badge/node.js-%3E%3D_0.6-brightgreen.svg?style=flat +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/statuses.svg?style=flat +[travis-url]: https://travis-ci.org/jshttp/statuses +[coveralls-image]: https://img.shields.io/coveralls/jshttp/statuses.svg?style=flat +[coveralls-url]: https://coveralls.io/r/jshttp/statuses?branch=master +[downloads-image]: http://img.shields.io/npm/dm/statuses.svg?style=flat +[downloads-url]: https://npmjs.org/package/statuses diff --git a/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/codes.json b/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/codes.json new file mode 100644 index 0000000..4c45a88 --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/codes.json @@ -0,0 +1,64 @@ +{ + "100": "Continue", + "101": "Switching Protocols", + "102": "Processing", + "200": "OK", + "201": "Created", + "202": "Accepted", + "203": "Non-Authoritative Information", + "204": "No Content", + "205": "Reset Content", + "206": "Partial Content", + "207": "Multi-Status", + "208": "Already Reported", + "226": "IM Used", + "300": "Multiple Choices", + "301": "Moved Permanently", + "302": "Found", + "303": "See Other", + "304": "Not Modified", + "305": "Use Proxy", + "306": "(Unused)", + "307": "Temporary Redirect", + "308": "Permanent Redirect", + "400": "Bad Request", + "401": "Unauthorized", + "402": "Payment Required", + "403": "Forbidden", + "404": "Not Found", + "405": "Method Not Allowed", + "406": "Not Acceptable", + "407": "Proxy Authentication Required", + "408": "Request Timeout", + "409": "Conflict", + "410": "Gone", + "411": "Length Required", + "412": "Precondition Failed", + "413": "Payload Too Large", + "414": "URI Too Long", + "415": "Unsupported Media Type", + "416": "Range Not Satisfiable", + "417": "Expectation Failed", + "418": "I'm a teapot", + "422": "Unprocessable Entity", + "423": "Locked", + "424": "Failed Dependency", + "425": "Unordered Collection", + "426": "Upgrade Required", + "428": "Precondition Required", + "429": "Too Many Requests", + "431": "Request Header Fields Too Large", + "451": "Unavailable For Legal Reasons", + "500": "Internal Server Error", + "501": "Not Implemented", + "502": "Bad Gateway", + "503": "Service Unavailable", + "504": "Gateway Timeout", + "505": "HTTP Version Not Supported", + "506": "Variant Also Negotiates", + "507": "Insufficient Storage", + "508": "Loop Detected", + "509": "Bandwidth Limit Exceeded", + "510": "Not Extended", + "511": "Network Authentication Required" +} \ No newline at end of file diff --git a/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/index.js b/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/index.js new file mode 100644 index 0000000..b06182d --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/index.js @@ -0,0 +1,60 @@ + +var codes = require('./codes.json'); + +module.exports = status; + +// [Integer...] +status.codes = Object.keys(codes).map(function (code) { + code = ~~code; + var msg = codes[code]; + status[code] = msg; + status[msg] = status[msg.toLowerCase()] = code; + return code; +}); + +// status codes for redirects +status.redirect = { + 300: true, + 301: true, + 302: true, + 303: true, + 305: true, + 307: true, + 308: true, +}; + +// status codes for empty bodies +status.empty = { + 204: true, + 205: true, + 304: true, +}; + +// status codes for when you should retry the request +status.retry = { + 502: true, + 503: true, + 504: true, +}; + +function status(code) { + if (typeof code === 'number') { + if (!status[code]) throw new Error('invalid status code: ' + code); + return code; + } + + if (typeof code !== 'string') { + throw new TypeError('code must be a number or string'); + } + + // '403' + var n = parseInt(code, 10) + if (!isNaN(n)) { + if (!status[n]) throw new Error('invalid status code: ' + n); + return n; + } + + n = status[code.toLowerCase()]; + if (!n) throw new Error('invalid status message: "' + code + '"'); + return n; +} diff --git a/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/package.json b/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/package.json new file mode 100644 index 0000000..f542bd1 --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/node_modules/statuses/package.json @@ -0,0 +1,83 @@ +{ + "name": "statuses", + "description": "HTTP status utility", + "version": "1.2.1", + "author": { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/jshttp/statuses" + }, + "license": "MIT", + "keywords": [ + "http", + "status", + "code" + ], + "files": [ + "index.js", + "codes.json", + "LICENSE" + ], + "devDependencies": { + "csv-parse": "0.0.6", + "istanbul": "0", + "mocha": "1", + "stream-to-array": "2" + }, + "scripts": { + "build": "node scripts/build.js", + "update": "node scripts/update.js", + "test": "mocha --reporter spec --bail --check-leaks", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks" + }, + "gitHead": "49e6ac7ae4c63ee8186f56cb52112a7eeda28ed7", + "bugs": { + "url": "https://github.com/jshttp/statuses/issues" + }, + "homepage": "https://github.com/jshttp/statuses", + "_id": "statuses@1.2.1", + "_shasum": "dded45cc18256d51ed40aec142489d5c61026d28", + "_from": "statuses@>=1.2.1 <1.3.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "shtylman", + "email": "shtylman@gmail.com" + }, + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + } + ], + "dist": { + "shasum": "dded45cc18256d51ed40aec142489d5c61026d28", + "tarball": "http://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" +} diff --git a/node_modules/serve-index/node_modules/http-errors/package.json b/node_modules/serve-index/node_modules/http-errors/package.json new file mode 100644 index 0000000..3f88eda --- /dev/null +++ b/node_modules/serve-index/node_modules/http-errors/package.json @@ -0,0 +1,84 @@ +{ + "name": "http-errors", + "description": "Create HTTP error objects", + "version": "1.3.1", + "author": { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + "contributors": [ + { + "name": "Alan Plum", + "email": "me@pluma.io" + }, + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/jshttp/http-errors" + }, + "dependencies": { + "inherits": "~2.0.1", + "statuses": "1" + }, + "devDependencies": { + "istanbul": "0", + "mocha": "1" + }, + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec --bail", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter dot" + }, + "keywords": [ + "http", + "error" + ], + "files": [ + "index.js", + "HISTORY.md", + "LICENSE", + "README.md" + ], + "gitHead": "89a8502b40d5dd42da2908f265275e2eeb8d0699", + "bugs": { + "url": "https://github.com/jshttp/http-errors/issues" + }, + "homepage": "https://github.com/jshttp/http-errors", + "_id": "http-errors@1.3.1", + "_shasum": "197e22cdebd4198585e8694ef6786197b91ed942", + "_from": "http-errors@>=1.3.1 <1.4.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "egeste", + "email": "npm@egeste.net" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "197e22cdebd4198585e8694ef6786197b91ed942", + "tarball": "http://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz" +} diff --git a/node_modules/serve-index/node_modules/mime-types/HISTORY.md b/node_modules/serve-index/node_modules/mime-types/HISTORY.md new file mode 100644 index 0000000..64241d9 --- /dev/null +++ b/node_modules/serve-index/node_modules/mime-types/HISTORY.md @@ -0,0 +1,177 @@ +2.1.8 / 2015-11-30 +================== + + * deps: mime-db@~1.20.0 + - Add new mime types + +2.1.7 / 2015-09-20 +================== + + * deps: mime-db@~1.19.0 + - Add new mime types + +2.1.6 / 2015-09-03 +================== + + * deps: mime-db@~1.18.0 + - Add new mime types + +2.1.5 / 2015-08-20 +================== + + * deps: mime-db@~1.17.0 + - Add new mime types + +2.1.4 / 2015-07-30 +================== + + * deps: mime-db@~1.16.0 + - Add new mime types + +2.1.3 / 2015-07-13 +================== + + * deps: mime-db@~1.15.0 + - Add new mime types + +2.1.2 / 2015-06-25 +================== + + * deps: mime-db@~1.14.0 + - Add new mime types + +2.1.1 / 2015-06-08 +================== + + * perf: fix deopt during mapping + +2.1.0 / 2015-06-07 +================== + + * Fix incorrectly treating extension-less file name as extension + - i.e. `'path/to/json'` will no longer return `application/json` + * Fix `.charset(type)` to accept parameters + * Fix `.charset(type)` to match case-insensitive + * Improve generation of extension to MIME mapping + * Refactor internals for readability and no argument reassignment + * Prefer `application/*` MIME types from the same source + * Prefer any type over `application/octet-stream` + * deps: mime-db@~1.13.0 + - Add nginx as a source + - Add new mime types + +2.0.14 / 2015-06-06 +=================== + + * deps: mime-db@~1.12.0 + - Add new mime types + +2.0.13 / 2015-05-31 +=================== + + * deps: mime-db@~1.11.0 + - Add new mime types + +2.0.12 / 2015-05-19 +=================== + + * deps: mime-db@~1.10.0 + - Add new mime types + +2.0.11 / 2015-05-05 +=================== + + * deps: mime-db@~1.9.1 + - Add new mime types + +2.0.10 / 2015-03-13 +=================== + + * deps: mime-db@~1.8.0 + - Add new mime types + +2.0.9 / 2015-02-09 +================== + + * deps: mime-db@~1.7.0 + - Add new mime types + - Community extensions ownership transferred from `node-mime` + +2.0.8 / 2015-01-29 +================== + + * deps: mime-db@~1.6.0 + - Add new mime types + +2.0.7 / 2014-12-30 +================== + + * deps: mime-db@~1.5.0 + - Add new mime types + - Fix various invalid MIME type entries + +2.0.6 / 2014-12-30 +================== + + * deps: mime-db@~1.4.0 + - Add new mime types + - Fix various invalid MIME type entries + - Remove example template MIME types + +2.0.5 / 2014-12-29 +================== + + * deps: mime-db@~1.3.1 + - Fix missing extensions + +2.0.4 / 2014-12-10 +================== + + * deps: mime-db@~1.3.0 + - Add new mime types + +2.0.3 / 2014-11-09 +================== + + * deps: mime-db@~1.2.0 + - Add new mime types + +2.0.2 / 2014-09-28 +================== + + * deps: mime-db@~1.1.0 + - Add new mime types + - Add additional compressible + - Update charsets + +2.0.1 / 2014-09-07 +================== + + * Support Node.js 0.6 + +2.0.0 / 2014-09-02 +================== + + * Use `mime-db` + * Remove `.define()` + +1.0.2 / 2014-08-04 +================== + + * Set charset=utf-8 for `text/javascript` + +1.0.1 / 2014-06-24 +================== + + * Add `text/jsx` type + +1.0.0 / 2014-05-12 +================== + + * Return `false` for unknown types + * Set charset=utf-8 for `application/json` + +0.1.0 / 2014-05-02 +================== + + * Initial release diff --git a/node_modules/serve-index/node_modules/mime-types/LICENSE b/node_modules/serve-index/node_modules/mime-types/LICENSE new file mode 100644 index 0000000..0616607 --- /dev/null +++ b/node_modules/serve-index/node_modules/mime-types/LICENSE @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/serve-index/node_modules/mime-types/README.md b/node_modules/serve-index/node_modules/mime-types/README.md new file mode 100644 index 0000000..e26295d --- /dev/null +++ b/node_modules/serve-index/node_modules/mime-types/README.md @@ -0,0 +1,103 @@ +# mime-types + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +The ultimate javascript content-type utility. + +Similar to [node-mime](https://github.com/broofa/node-mime), except: + +- __No fallbacks.__ Instead of naively returning the first available type, `mime-types` simply returns `false`, + so do `var type = mime.lookup('unrecognized') || 'application/octet-stream'`. +- No `new Mime()` business, so you could do `var lookup = require('mime-types').lookup`. +- Additional mime types are added such as jade and stylus via [mime-db](https://github.com/jshttp/mime-db) +- No `.define()` functionality + +Otherwise, the API is compatible. + +## Install + +```sh +$ npm install mime-types +``` + +## Adding Types + +All mime types are based on [mime-db](https://github.com/jshttp/mime-db), +so open a PR there if you'd like to add mime types. + +## API + +```js +var mime = require('mime-types') +``` + +All functions return `false` if input is invalid or not found. + +### mime.lookup(path) + +Lookup the content-type associated with a file. + +```js +mime.lookup('json') // 'application/json' +mime.lookup('.md') // 'text/x-markdown' +mime.lookup('file.html') // 'text/html' +mime.lookup('folder/file.js') // 'application/javascript' +mime.lookup('folder/.htaccess') // false + +mime.lookup('cats') // false +``` + +### mime.contentType(type) + +Create a full content-type header given a content-type or extension. + +```js +mime.contentType('markdown') // 'text/x-markdown; charset=utf-8' +mime.contentType('file.json') // 'application/json; charset=utf-8' + +// from a full path +mime.contentType(path.extname('/path/to/file.json')) // 'application/json; charset=utf-8' +``` + +### mime.extension(type) + +Get the default extension for a content-type. + +```js +mime.extension('application/octet-stream') // 'bin' +``` + +### mime.charset(type) + +Lookup the implied default charset of a content-type. + +```js +mime.charset('text/x-markdown') // 'UTF-8' +``` + +### var type = mime.types[extension] + +A map of content-types by extension. + +### [extensions...] = mime.extensions[type] + +A map of extensions by content-type. + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/mime-types.svg +[npm-url]: https://npmjs.org/package/mime-types +[node-version-image]: https://img.shields.io/node/v/mime-types.svg +[node-version-url]: http://nodejs.org/download/ +[travis-image]: https://img.shields.io/travis/jshttp/mime-types/master.svg +[travis-url]: https://travis-ci.org/jshttp/mime-types +[coveralls-image]: https://img.shields.io/coveralls/jshttp/mime-types/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/mime-types +[downloads-image]: https://img.shields.io/npm/dm/mime-types.svg +[downloads-url]: https://npmjs.org/package/mime-types diff --git a/node_modules/serve-index/node_modules/mime-types/index.js b/node_modules/serve-index/node_modules/mime-types/index.js new file mode 100644 index 0000000..f7008b2 --- /dev/null +++ b/node_modules/serve-index/node_modules/mime-types/index.js @@ -0,0 +1,188 @@ +/*! + * mime-types + * Copyright(c) 2014 Jonathan Ong + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module dependencies. + * @private + */ + +var db = require('mime-db') +var extname = require('path').extname + +/** + * Module variables. + * @private + */ + +var extractTypeRegExp = /^\s*([^;\s]*)(?:;|\s|$)/ +var textTypeRegExp = /^text\//i + +/** + * Module exports. + * @public + */ + +exports.charset = charset +exports.charsets = { lookup: charset } +exports.contentType = contentType +exports.extension = extension +exports.extensions = Object.create(null) +exports.lookup = lookup +exports.types = Object.create(null) + +// Populate the extensions/types maps +populateMaps(exports.extensions, exports.types) + +/** + * Get the default charset for a MIME type. + * + * @param {string} type + * @return {boolean|string} + */ + +function charset(type) { + if (!type || typeof type !== 'string') { + return false + } + + // TODO: use media-typer + var match = extractTypeRegExp.exec(type) + var mime = match && db[match[1].toLowerCase()] + + if (mime && mime.charset) { + return mime.charset + } + + // default text/* to utf-8 + if (match && textTypeRegExp.test(match[1])) { + return 'UTF-8' + } + + return false +} + +/** + * Create a full Content-Type header given a MIME type or extension. + * + * @param {string} str + * @return {boolean|string} + */ + +function contentType(str) { + // TODO: should this even be in this module? + if (!str || typeof str !== 'string') { + return false + } + + var mime = str.indexOf('/') === -1 + ? exports.lookup(str) + : str + + if (!mime) { + return false + } + + // TODO: use content-type or other module + if (mime.indexOf('charset') === -1) { + var charset = exports.charset(mime) + if (charset) mime += '; charset=' + charset.toLowerCase() + } + + return mime +} + +/** + * Get the default extension for a MIME type. + * + * @param {string} type + * @return {boolean|string} + */ + +function extension(type) { + if (!type || typeof type !== 'string') { + return false + } + + // TODO: use media-typer + var match = extractTypeRegExp.exec(type) + + // get extensions + var exts = match && exports.extensions[match[1].toLowerCase()] + + if (!exts || !exts.length) { + return false + } + + return exts[0] +} + +/** + * Lookup the MIME type for a file path/extension. + * + * @param {string} path + * @return {boolean|string} + */ + +function lookup(path) { + if (!path || typeof path !== 'string') { + return false + } + + // get the extension ("ext" or ".ext" or full path) + var extension = extname('x.' + path) + .toLowerCase() + .substr(1) + + if (!extension) { + return false + } + + return exports.types[extension] || false +} + +/** + * Populate the extensions and types maps. + * @private + */ + +function populateMaps(extensions, types) { + // source preference (least -> most) + var preference = ['nginx', 'apache', undefined, 'iana'] + + Object.keys(db).forEach(function forEachMimeType(type) { + var mime = db[type] + var exts = mime.extensions + + if (!exts || !exts.length) { + return + } + + // mime -> extensions + extensions[type] = exts + + // extension -> mime + for (var i = 0; i < exts.length; i++) { + var extension = exts[i] + + if (types[extension]) { + var from = preference.indexOf(db[types[extension]].source) + var to = preference.indexOf(mime.source) + + if (types[extension] !== 'application/octet-stream' + && from > to || (from === to && types[extension].substr(0, 12) === 'application/')) { + // skip the remapping + continue + } + } + + // set the extension -> mime + types[extension] = type + } + }) +} diff --git a/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/HISTORY.md b/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/HISTORY.md new file mode 100644 index 0000000..c7f8b5a --- /dev/null +++ b/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/HISTORY.md @@ -0,0 +1,287 @@ +1.20.0 / 2015-11-10 +=================== + + * Add `application/cdni` + * Add `application/csvm+json` + * Add `application/rfc+xml` + * Add `application/vnd.3gpp.access-transfer-events+xml` + * Add `application/vnd.3gpp.srvcc-ext+xml` + * Add `application/vnd.ms-windows.wsd.oob` + * Add `application/vnd.oxli.countgraph` + * Add `application/vnd.pagerduty+json` + * Add `text/x-suse-ymp` + +1.19.0 / 2015-09-17 +=================== + + * Add `application/vnd.3gpp-prose-pc3ch+xml` + * Add `application/vnd.3gpp.srvcc-info+xml` + * Add `application/vnd.apple.pkpass` + * Add `application/vnd.drive+json` + +1.18.0 / 2015-09-03 +=================== + + * Add `application/pkcs12` + * Add `application/vnd.3gpp-prose+xml` + * Add `application/vnd.3gpp.mid-call+xml` + * Add `application/vnd.3gpp.state-and-event-info+xml` + * Add `application/vnd.anki` + * Add `application/vnd.firemonkeys.cloudcell` + * Add `application/vnd.openblox.game+xml` + * Add `application/vnd.openblox.game-binary` + +1.17.0 / 2015-08-13 +=================== + + * Add `application/x-msdos-program` + * Add `audio/g711-0` + * Add `image/vnd.mozilla.apng` + * Add extension `.exe` to `application/x-msdos-program` + +1.16.0 / 2015-07-29 +=================== + + * Add `application/vnd.uri-map` + +1.15.0 / 2015-07-13 +=================== + + * Add `application/x-httpd-php` + +1.14.0 / 2015-06-25 +=================== + + * Add `application/scim+json` + * Add `application/vnd.3gpp.ussd+xml` + * Add `application/vnd.biopax.rdf+xml` + * Add `text/x-processing` + +1.13.0 / 2015-06-07 +=================== + + * Add nginx as a source + * Add `application/x-cocoa` + * Add `application/x-java-archive-diff` + * Add `application/x-makeself` + * Add `application/x-perl` + * Add `application/x-pilot` + * Add `application/x-redhat-package-manager` + * Add `application/x-sea` + * Add `audio/x-m4a` + * Add `audio/x-realaudio` + * Add `image/x-jng` + * Add `text/mathml` + +1.12.0 / 2015-06-05 +=================== + + * Add `application/bdoc` + * Add `application/vnd.hyperdrive+json` + * Add `application/x-bdoc` + * Add extension `.rtf` to `text/rtf` + +1.11.0 / 2015-05-31 +=================== + + * Add `audio/wav` + * Add `audio/wave` + * Add extension `.litcoffee` to `text/coffeescript` + * Add extension `.sfd-hdstx` to `application/vnd.hydrostatix.sof-data` + * Add extension `.n-gage` to `application/vnd.nokia.n-gage.symbian.install` + +1.10.0 / 2015-05-19 +=================== + + * Add `application/vnd.balsamiq.bmpr` + * Add `application/vnd.microsoft.portable-executable` + * Add `application/x-ns-proxy-autoconfig` + +1.9.1 / 2015-04-19 +================== + + * Remove `.json` extension from `application/manifest+json` + - This is causing bugs downstream + +1.9.0 / 2015-04-19 +================== + + * Add `application/manifest+json` + * Add `application/vnd.micro+json` + * Add `image/vnd.zbrush.pcx` + * Add `image/x-ms-bmp` + +1.8.0 / 2015-03-13 +================== + + * Add `application/vnd.citationstyles.style+xml` + * Add `application/vnd.fastcopy-disk-image` + * Add `application/vnd.gov.sk.xmldatacontainer+xml` + * Add extension `.jsonld` to `application/ld+json` + +1.7.0 / 2015-02-08 +================== + + * Add `application/vnd.gerber` + * Add `application/vnd.msa-disk-image` + +1.6.1 / 2015-02-05 +================== + + * Community extensions ownership transferred from `node-mime` + +1.6.0 / 2015-01-29 +================== + + * Add `application/jose` + * Add `application/jose+json` + * Add `application/json-seq` + * Add `application/jwk+json` + * Add `application/jwk-set+json` + * Add `application/jwt` + * Add `application/rdap+json` + * Add `application/vnd.gov.sk.e-form+xml` + * Add `application/vnd.ims.imsccv1p3` + +1.5.0 / 2014-12-30 +================== + + * Add `application/vnd.oracle.resource+json` + * Fix various invalid MIME type entries + - `application/mbox+xml` + - `application/oscp-response` + - `application/vwg-multiplexed` + - `audio/g721` + +1.4.0 / 2014-12-21 +================== + + * Add `application/vnd.ims.imsccv1p2` + * Fix various invalid MIME type entries + - `application/vnd-acucobol` + - `application/vnd-curl` + - `application/vnd-dart` + - `application/vnd-dxr` + - `application/vnd-fdf` + - `application/vnd-mif` + - `application/vnd-sema` + - `application/vnd-wap-wmlc` + - `application/vnd.adobe.flash-movie` + - `application/vnd.dece-zip` + - `application/vnd.dvb_service` + - `application/vnd.micrografx-igx` + - `application/vnd.sealed-doc` + - `application/vnd.sealed-eml` + - `application/vnd.sealed-mht` + - `application/vnd.sealed-ppt` + - `application/vnd.sealed-tiff` + - `application/vnd.sealed-xls` + - `application/vnd.sealedmedia.softseal-html` + - `application/vnd.sealedmedia.softseal-pdf` + - `application/vnd.wap-slc` + - `application/vnd.wap-wbxml` + - `audio/vnd.sealedmedia.softseal-mpeg` + - `image/vnd-djvu` + - `image/vnd-svf` + - `image/vnd-wap-wbmp` + - `image/vnd.sealed-png` + - `image/vnd.sealedmedia.softseal-gif` + - `image/vnd.sealedmedia.softseal-jpg` + - `model/vnd-dwf` + - `model/vnd.parasolid.transmit-binary` + - `model/vnd.parasolid.transmit-text` + - `text/vnd-a` + - `text/vnd-curl` + - `text/vnd.wap-wml` + * Remove example template MIME types + - `application/example` + - `audio/example` + - `image/example` + - `message/example` + - `model/example` + - `multipart/example` + - `text/example` + - `video/example` + +1.3.1 / 2014-12-16 +================== + + * Fix missing extensions + - `application/json5` + - `text/hjson` + +1.3.0 / 2014-12-07 +================== + + * Add `application/a2l` + * Add `application/aml` + * Add `application/atfx` + * Add `application/atxml` + * Add `application/cdfx+xml` + * Add `application/dii` + * Add `application/json5` + * Add `application/lxf` + * Add `application/mf4` + * Add `application/vnd.apache.thrift.compact` + * Add `application/vnd.apache.thrift.json` + * Add `application/vnd.coffeescript` + * Add `application/vnd.enphase.envoy` + * Add `application/vnd.ims.imsccv1p1` + * Add `text/csv-schema` + * Add `text/hjson` + * Add `text/markdown` + * Add `text/yaml` + +1.2.0 / 2014-11-09 +================== + + * Add `application/cea` + * Add `application/dit` + * Add `application/vnd.gov.sk.e-form+zip` + * Add `application/vnd.tmd.mediaflex.api+xml` + * Type `application/epub+zip` is now IANA-registered + +1.1.2 / 2014-10-23 +================== + + * Rebuild database for `application/x-www-form-urlencoded` change + +1.1.1 / 2014-10-20 +================== + + * Mark `application/x-www-form-urlencoded` as compressible. + +1.1.0 / 2014-09-28 +================== + + * Add `application/font-woff2` + +1.0.3 / 2014-09-25 +================== + + * Fix engine requirement in package + +1.0.2 / 2014-09-25 +================== + + * Add `application/coap-group+json` + * Add `application/dcd` + * Add `application/vnd.apache.thrift.binary` + * Add `image/vnd.tencent.tap` + * Mark all JSON-derived types as compressible + * Update `text/vtt` data + +1.0.1 / 2014-08-30 +================== + + * Fix extension ordering + +1.0.0 / 2014-08-30 +================== + + * Add `application/atf` + * Add `application/merge-patch+json` + * Add `multipart/x-mixed-replace` + * Add `source: 'apache'` metadata + * Add `source: 'iana'` metadata + * Remove badly-assumed charset data diff --git a/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/LICENSE b/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/LICENSE new file mode 100644 index 0000000..a7ae8ee --- /dev/null +++ b/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/README.md b/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/README.md new file mode 100644 index 0000000..164cca0 --- /dev/null +++ b/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/README.md @@ -0,0 +1,82 @@ +# mime-db + +[![NPM Version][npm-version-image]][npm-url] +[![NPM Downloads][npm-downloads-image]][npm-url] +[![Node.js Version][node-image]][node-url] +[![Build Status][travis-image]][travis-url] +[![Coverage Status][coveralls-image]][coveralls-url] + +This is a database of all mime types. +It consists of a single, public JSON file and does not include any logic, +allowing it to remain as un-opinionated as possible with an API. +It aggregates data from the following sources: + +- http://www.iana.org/assignments/media-types/media-types.xhtml +- http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types +- http://hg.nginx.org/nginx/raw-file/default/conf/mime.types + +## Installation + +```bash +npm install mime-db +``` + +### Database Download + +If you're crazy enough to use this in the browser, you can just grab the +JSON file using [RawGit](https://rawgit.com/). It is recommended to replace +`master` with [a release tag](https://github.com/jshttp/mime-db/tags) as the +JSON format may change in the future. + +``` +https://cdn.rawgit.com/jshttp/mime-db/master/db.json +``` + +## Usage + +```js +var db = require('mime-db'); + +// grab data on .js files +var data = db['application/javascript']; +``` + +## Data Structure + +The JSON file is a map lookup for lowercased mime types. +Each mime type has the following properties: + +- `.source` - where the mime type is defined. + If not set, it's probably a custom media type. + - `apache` - [Apache common media types](http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types) + - `iana` - [IANA-defined media types](http://www.iana.org/assignments/media-types/media-types.xhtml) + - `nginx` - [nginx media types](http://hg.nginx.org/nginx/raw-file/default/conf/mime.types) +- `.extensions[]` - known extensions associated with this mime type. +- `.compressible` - whether a file of this type is can be gzipped. +- `.charset` - the default charset associated with this type, if any. + +If unknown, every property could be `undefined`. + +## Contributing + +To edit the database, only make PRs against `src/custom.json` or +`src/custom-suffix.json`. + +To update the build, run `npm run build`. + +## Adding Custom Media Types + +The best way to get new media types included in this library is to register +them with the IANA. The community registration procedure is outlined in +[RFC 6838 section 5](http://tools.ietf.org/html/rfc6838#section-5). Types +registered with the IANA are automatically pulled into this library. + +[npm-version-image]: https://img.shields.io/npm/v/mime-db.svg +[npm-downloads-image]: https://img.shields.io/npm/dm/mime-db.svg +[npm-url]: https://npmjs.org/package/mime-db +[travis-image]: https://img.shields.io/travis/jshttp/mime-db/master.svg +[travis-url]: https://travis-ci.org/jshttp/mime-db +[coveralls-image]: https://img.shields.io/coveralls/jshttp/mime-db/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/mime-db?branch=master +[node-image]: https://img.shields.io/node/v/mime-db.svg +[node-url]: http://nodejs.org/download/ diff --git a/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/db.json b/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/db.json new file mode 100644 index 0000000..123e7f9 --- /dev/null +++ b/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/db.json @@ -0,0 +1,6504 @@ +{ + "application/1d-interleaved-parityfec": { + "source": "iana" + }, + "application/3gpdash-qoe-report+xml": { + "source": "iana" + }, + "application/3gpp-ims+xml": { + "source": "iana" + }, + "application/a2l": { + "source": "iana" + }, + "application/activemessage": { + "source": "iana" + }, + "application/alto-costmap+json": { + "source": "iana", + "compressible": true + }, + "application/alto-costmapfilter+json": { + "source": "iana", + "compressible": true + }, + "application/alto-directory+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointcost+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointcostparams+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointprop+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointpropparams+json": { + "source": "iana", + "compressible": true + }, + "application/alto-error+json": { + "source": "iana", + "compressible": true + }, + "application/alto-networkmap+json": { + "source": "iana", + "compressible": true + }, + "application/alto-networkmapfilter+json": { + "source": "iana", + "compressible": true + }, + "application/aml": { + "source": "iana" + }, + "application/andrew-inset": { + "source": "iana", + "extensions": ["ez"] + }, + "application/applefile": { + "source": "iana" + }, + "application/applixware": { + "source": "apache", + "extensions": ["aw"] + }, + "application/atf": { + "source": "iana" + }, + "application/atfx": { + "source": "iana" + }, + "application/atom+xml": { + "source": "iana", + "compressible": true, + "extensions": ["atom"] + }, + "application/atomcat+xml": { + "source": "iana", + "extensions": ["atomcat"] + }, + "application/atomdeleted+xml": { + "source": "iana" + }, + "application/atomicmail": { + "source": "iana" + }, + "application/atomsvc+xml": { + "source": "iana", + "extensions": ["atomsvc"] + }, + "application/atxml": { + "source": "iana" + }, + "application/auth-policy+xml": { + "source": "iana" + }, + "application/bacnet-xdd+zip": { + "source": "iana" + }, + "application/batch-smtp": { + "source": "iana" + }, + "application/bdoc": { + "compressible": false, + "extensions": ["bdoc"] + }, + "application/beep+xml": { + "source": "iana" + }, + "application/calendar+json": { + "source": "iana", + "compressible": true + }, + "application/calendar+xml": { + "source": "iana" + }, + "application/call-completion": { + "source": "iana" + }, + "application/cals-1840": { + "source": "iana" + }, + "application/cbor": { + "source": "iana" + }, + "application/ccmp+xml": { + "source": "iana" + }, + "application/ccxml+xml": { + "source": "iana", + "extensions": ["ccxml"] + }, + "application/cdfx+xml": { + "source": "iana" + }, + "application/cdmi-capability": { + "source": "iana", + "extensions": ["cdmia"] + }, + "application/cdmi-container": { + "source": "iana", + "extensions": ["cdmic"] + }, + "application/cdmi-domain": { + "source": "iana", + "extensions": ["cdmid"] + }, + "application/cdmi-object": { + "source": "iana", + "extensions": ["cdmio"] + }, + "application/cdmi-queue": { + "source": "iana", + "extensions": ["cdmiq"] + }, + "application/cdni": { + "source": "iana" + }, + "application/cea": { + "source": "iana" + }, + "application/cea-2018+xml": { + "source": "iana" + }, + "application/cellml+xml": { + "source": "iana" + }, + "application/cfw": { + "source": "iana" + }, + "application/cms": { + "source": "iana" + }, + "application/cnrp+xml": { + "source": "iana" + }, + "application/coap-group+json": { + "source": "iana", + "compressible": true + }, + "application/commonground": { + "source": "iana" + }, + "application/conference-info+xml": { + "source": "iana" + }, + "application/cpl+xml": { + "source": "iana" + }, + "application/csrattrs": { + "source": "iana" + }, + "application/csta+xml": { + "source": "iana" + }, + "application/cstadata+xml": { + "source": "iana" + }, + "application/csvm+json": { + "source": "iana", + "compressible": true + }, + "application/cu-seeme": { + "source": "apache", + "extensions": ["cu"] + }, + "application/cybercash": { + "source": "iana" + }, + "application/dart": { + "compressible": true + }, + "application/dash+xml": { + "source": "iana", + "extensions": ["mdp"] + }, + "application/dashdelta": { + "source": "iana" + }, + "application/davmount+xml": { + "source": "iana", + "extensions": ["davmount"] + }, + "application/dca-rft": { + "source": "iana" + }, + "application/dcd": { + "source": "iana" + }, + "application/dec-dx": { + "source": "iana" + }, + "application/dialog-info+xml": { + "source": "iana" + }, + "application/dicom": { + "source": "iana" + }, + "application/dii": { + "source": "iana" + }, + "application/dit": { + "source": "iana" + }, + "application/dns": { + "source": "iana" + }, + "application/docbook+xml": { + "source": "apache", + "extensions": ["dbk"] + }, + "application/dskpp+xml": { + "source": "iana" + }, + "application/dssc+der": { + "source": "iana", + "extensions": ["dssc"] + }, + "application/dssc+xml": { + "source": "iana", + "extensions": ["xdssc"] + }, + "application/dvcs": { + "source": "iana" + }, + "application/ecmascript": { + "source": "iana", + "compressible": true, + "extensions": ["ecma"] + }, + "application/edi-consent": { + "source": "iana" + }, + "application/edi-x12": { + "source": "iana", + "compressible": false + }, + "application/edifact": { + "source": "iana", + "compressible": false + }, + "application/emma+xml": { + "source": "iana", + "extensions": ["emma"] + }, + "application/emotionml+xml": { + "source": "iana" + }, + "application/encaprtp": { + "source": "iana" + }, + "application/epp+xml": { + "source": "iana" + }, + "application/epub+zip": { + "source": "iana", + "extensions": ["epub"] + }, + "application/eshop": { + "source": "iana" + }, + "application/exi": { + "source": "iana", + "extensions": ["exi"] + }, + "application/fastinfoset": { + "source": "iana" + }, + "application/fastsoap": { + "source": "iana" + }, + "application/fdt+xml": { + "source": "iana" + }, + "application/fits": { + "source": "iana" + }, + "application/font-sfnt": { + "source": "iana" + }, + "application/font-tdpfr": { + "source": "iana", + "extensions": ["pfr"] + }, + "application/font-woff": { + "source": "iana", + "compressible": false, + "extensions": ["woff"] + }, + "application/font-woff2": { + "compressible": false, + "extensions": ["woff2"] + }, + "application/framework-attributes+xml": { + "source": "iana" + }, + "application/gml+xml": { + "source": "apache", + "extensions": ["gml"] + }, + "application/gpx+xml": { + "source": "apache", + "extensions": ["gpx"] + }, + "application/gxf": { + "source": "apache", + "extensions": ["gxf"] + }, + "application/gzip": { + "source": "iana", + "compressible": false + }, + "application/h224": { + "source": "iana" + }, + "application/held+xml": { + "source": "iana" + }, + "application/http": { + "source": "iana" + }, + "application/hyperstudio": { + "source": "iana", + "extensions": ["stk"] + }, + "application/ibe-key-request+xml": { + "source": "iana" + }, + "application/ibe-pkg-reply+xml": { + "source": "iana" + }, + "application/ibe-pp-data": { + "source": "iana" + }, + "application/iges": { + "source": "iana" + }, + "application/im-iscomposing+xml": { + "source": "iana" + }, + "application/index": { + "source": "iana" + }, + "application/index.cmd": { + "source": "iana" + }, + "application/index.obj": { + "source": "iana" + }, + "application/index.response": { + "source": "iana" + }, + "application/index.vnd": { + "source": "iana" + }, + "application/inkml+xml": { + "source": "iana", + "extensions": ["ink","inkml"] + }, + "application/iotp": { + "source": "iana" + }, + "application/ipfix": { + "source": "iana", + "extensions": ["ipfix"] + }, + "application/ipp": { + "source": "iana" + }, + "application/isup": { + "source": "iana" + }, + "application/its+xml": { + "source": "iana" + }, + "application/java-archive": { + "source": "apache", + "compressible": false, + "extensions": ["jar","war","ear"] + }, + "application/java-serialized-object": { + "source": "apache", + "compressible": false, + "extensions": ["ser"] + }, + "application/java-vm": { + "source": "apache", + "compressible": false, + "extensions": ["class"] + }, + "application/javascript": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["js"] + }, + "application/jose": { + "source": "iana" + }, + "application/jose+json": { + "source": "iana", + "compressible": true + }, + "application/jrd+json": { + "source": "iana", + "compressible": true + }, + "application/json": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["json","map"] + }, + "application/json-patch+json": { + "source": "iana", + "compressible": true + }, + "application/json-seq": { + "source": "iana" + }, + "application/json5": { + "extensions": ["json5"] + }, + "application/jsonml+json": { + "source": "apache", + "compressible": true, + "extensions": ["jsonml"] + }, + "application/jwk+json": { + "source": "iana", + "compressible": true + }, + "application/jwk-set+json": { + "source": "iana", + "compressible": true + }, + "application/jwt": { + "source": "iana" + }, + "application/kpml-request+xml": { + "source": "iana" + }, + "application/kpml-response+xml": { + "source": "iana" + }, + "application/ld+json": { + "source": "iana", + "compressible": true, + "extensions": ["jsonld"] + }, + "application/link-format": { + "source": "iana" + }, + "application/load-control+xml": { + "source": "iana" + }, + "application/lost+xml": { + "source": "iana", + "extensions": ["lostxml"] + }, + "application/lostsync+xml": { + "source": "iana" + }, + "application/lxf": { + "source": "iana" + }, + "application/mac-binhex40": { + "source": "iana", + "extensions": ["hqx"] + }, + "application/mac-compactpro": { + "source": "apache", + "extensions": ["cpt"] + }, + "application/macwriteii": { + "source": "iana" + }, + "application/mads+xml": { + "source": "iana", + "extensions": ["mads"] + }, + "application/manifest+json": { + "charset": "UTF-8", + "compressible": true, + "extensions": ["webmanifest"] + }, + "application/marc": { + "source": "iana", + "extensions": ["mrc"] + }, + "application/marcxml+xml": { + "source": "iana", + "extensions": ["mrcx"] + }, + "application/mathematica": { + "source": "iana", + "extensions": ["ma","nb","mb"] + }, + "application/mathml+xml": { + "source": "iana", + "extensions": ["mathml"] + }, + "application/mathml-content+xml": { + "source": "iana" + }, + "application/mathml-presentation+xml": { + "source": "iana" + }, + "application/mbms-associated-procedure-description+xml": { + "source": "iana" + }, + "application/mbms-deregister+xml": { + "source": "iana" + }, + "application/mbms-envelope+xml": { + "source": "iana" + }, + "application/mbms-msk+xml": { + "source": "iana" + }, + "application/mbms-msk-response+xml": { + "source": "iana" + }, + "application/mbms-protection-description+xml": { + "source": "iana" + }, + "application/mbms-reception-report+xml": { + "source": "iana" + }, + "application/mbms-register+xml": { + "source": "iana" + }, + "application/mbms-register-response+xml": { + "source": "iana" + }, + "application/mbms-schedule+xml": { + "source": "iana" + }, + "application/mbms-user-service-description+xml": { + "source": "iana" + }, + "application/mbox": { + "source": "iana", + "extensions": ["mbox"] + }, + "application/media-policy-dataset+xml": { + "source": "iana" + }, + "application/media_control+xml": { + "source": "iana" + }, + "application/mediaservercontrol+xml": { + "source": "iana", + "extensions": ["mscml"] + }, + "application/merge-patch+json": { + "source": "iana", + "compressible": true + }, + "application/metalink+xml": { + "source": "apache", + "extensions": ["metalink"] + }, + "application/metalink4+xml": { + "source": "iana", + "extensions": ["meta4"] + }, + "application/mets+xml": { + "source": "iana", + "extensions": ["mets"] + }, + "application/mf4": { + "source": "iana" + }, + "application/mikey": { + "source": "iana" + }, + "application/mods+xml": { + "source": "iana", + "extensions": ["mods"] + }, + "application/moss-keys": { + "source": "iana" + }, + "application/moss-signature": { + "source": "iana" + }, + "application/mosskey-data": { + "source": "iana" + }, + "application/mosskey-request": { + "source": "iana" + }, + "application/mp21": { + "source": "iana", + "extensions": ["m21","mp21"] + }, + "application/mp4": { + "source": "iana", + "extensions": ["mp4s","m4p"] + }, + "application/mpeg4-generic": { + "source": "iana" + }, + "application/mpeg4-iod": { + "source": "iana" + }, + "application/mpeg4-iod-xmt": { + "source": "iana" + }, + "application/mrb-consumer+xml": { + "source": "iana" + }, + "application/mrb-publish+xml": { + "source": "iana" + }, + "application/msc-ivr+xml": { + "source": "iana" + }, + "application/msc-mixer+xml": { + "source": "iana" + }, + "application/msword": { + "source": "iana", + "compressible": false, + "extensions": ["doc","dot"] + }, + "application/mxf": { + "source": "iana", + "extensions": ["mxf"] + }, + "application/nasdata": { + "source": "iana" + }, + "application/news-checkgroups": { + "source": "iana" + }, + "application/news-groupinfo": { + "source": "iana" + }, + "application/news-transmission": { + "source": "iana" + }, + "application/nlsml+xml": { + "source": "iana" + }, + "application/nss": { + "source": "iana" + }, + "application/ocsp-request": { + "source": "iana" + }, + "application/ocsp-response": { + "source": "iana" + }, + "application/octet-stream": { + "source": "iana", + "compressible": false, + "extensions": ["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","exe","dll","deb","dmg","iso","img","msi","msp","msm","buffer"] + }, + "application/oda": { + "source": "iana", + "extensions": ["oda"] + }, + "application/odx": { + "source": "iana" + }, + "application/oebps-package+xml": { + "source": "iana", + "extensions": ["opf"] + }, + "application/ogg": { + "source": "iana", + "compressible": false, + "extensions": ["ogx"] + }, + "application/omdoc+xml": { + "source": "apache", + "extensions": ["omdoc"] + }, + "application/onenote": { + "source": "apache", + "extensions": ["onetoc","onetoc2","onetmp","onepkg"] + }, + "application/oxps": { + "source": "iana", + "extensions": ["oxps"] + }, + "application/p2p-overlay+xml": { + "source": "iana" + }, + "application/parityfec": { + "source": "iana" + }, + "application/patch-ops-error+xml": { + "source": "iana", + "extensions": ["xer"] + }, + "application/pdf": { + "source": "iana", + "compressible": false, + "extensions": ["pdf"] + }, + "application/pdx": { + "source": "iana" + }, + "application/pgp-encrypted": { + "source": "iana", + "compressible": false, + "extensions": ["pgp"] + }, + "application/pgp-keys": { + "source": "iana" + }, + "application/pgp-signature": { + "source": "iana", + "extensions": ["asc","sig"] + }, + "application/pics-rules": { + "source": "apache", + "extensions": ["prf"] + }, + "application/pidf+xml": { + "source": "iana" + }, + "application/pidf-diff+xml": { + "source": "iana" + }, + "application/pkcs10": { + "source": "iana", + "extensions": ["p10"] + }, + "application/pkcs12": { + "source": "iana" + }, + "application/pkcs7-mime": { + "source": "iana", + "extensions": ["p7m","p7c"] + }, + "application/pkcs7-signature": { + "source": "iana", + "extensions": ["p7s"] + }, + "application/pkcs8": { + "source": "iana", + "extensions": ["p8"] + }, + "application/pkix-attr-cert": { + "source": "iana", + "extensions": ["ac"] + }, + "application/pkix-cert": { + "source": "iana", + "extensions": ["cer"] + }, + "application/pkix-crl": { + "source": "iana", + "extensions": ["crl"] + }, + "application/pkix-pkipath": { + "source": "iana", + "extensions": ["pkipath"] + }, + "application/pkixcmp": { + "source": "iana", + "extensions": ["pki"] + }, + "application/pls+xml": { + "source": "iana", + "extensions": ["pls"] + }, + "application/poc-settings+xml": { + "source": "iana" + }, + "application/postscript": { + "source": "iana", + "compressible": true, + "extensions": ["ai","eps","ps"] + }, + "application/provenance+xml": { + "source": "iana" + }, + "application/prs.alvestrand.titrax-sheet": { + "source": "iana" + }, + "application/prs.cww": { + "source": "iana", + "extensions": ["cww"] + }, + "application/prs.hpub+zip": { + "source": "iana" + }, + "application/prs.nprend": { + "source": "iana" + }, + "application/prs.plucker": { + "source": "iana" + }, + "application/prs.rdf-xml-crypt": { + "source": "iana" + }, + "application/prs.xsf+xml": { + "source": "iana" + }, + "application/pskc+xml": { + "source": "iana", + "extensions": ["pskcxml"] + }, + "application/qsig": { + "source": "iana" + }, + "application/raptorfec": { + "source": "iana" + }, + "application/rdap+json": { + "source": "iana", + "compressible": true + }, + "application/rdf+xml": { + "source": "iana", + "compressible": true, + "extensions": ["rdf"] + }, + "application/reginfo+xml": { + "source": "iana", + "extensions": ["rif"] + }, + "application/relax-ng-compact-syntax": { + "source": "iana", + "extensions": ["rnc"] + }, + "application/remote-printing": { + "source": "iana" + }, + "application/reputon+json": { + "source": "iana", + "compressible": true + }, + "application/resource-lists+xml": { + "source": "iana", + "extensions": ["rl"] + }, + "application/resource-lists-diff+xml": { + "source": "iana", + "extensions": ["rld"] + }, + "application/rfc+xml": { + "source": "iana" + }, + "application/riscos": { + "source": "iana" + }, + "application/rlmi+xml": { + "source": "iana" + }, + "application/rls-services+xml": { + "source": "iana", + "extensions": ["rs"] + }, + "application/rpki-ghostbusters": { + "source": "iana", + "extensions": ["gbr"] + }, + "application/rpki-manifest": { + "source": "iana", + "extensions": ["mft"] + }, + "application/rpki-roa": { + "source": "iana", + "extensions": ["roa"] + }, + "application/rpki-updown": { + "source": "iana" + }, + "application/rsd+xml": { + "source": "apache", + "extensions": ["rsd"] + }, + "application/rss+xml": { + "source": "apache", + "compressible": true, + "extensions": ["rss"] + }, + "application/rtf": { + "source": "iana", + "compressible": true, + "extensions": ["rtf"] + }, + "application/rtploopback": { + "source": "iana" + }, + "application/rtx": { + "source": "iana" + }, + "application/samlassertion+xml": { + "source": "iana" + }, + "application/samlmetadata+xml": { + "source": "iana" + }, + "application/sbml+xml": { + "source": "iana", + "extensions": ["sbml"] + }, + "application/scaip+xml": { + "source": "iana" + }, + "application/scim+json": { + "source": "iana", + "compressible": true + }, + "application/scvp-cv-request": { + "source": "iana", + "extensions": ["scq"] + }, + "application/scvp-cv-response": { + "source": "iana", + "extensions": ["scs"] + }, + "application/scvp-vp-request": { + "source": "iana", + "extensions": ["spq"] + }, + "application/scvp-vp-response": { + "source": "iana", + "extensions": ["spp"] + }, + "application/sdp": { + "source": "iana", + "extensions": ["sdp"] + }, + "application/sep+xml": { + "source": "iana" + }, + "application/sep-exi": { + "source": "iana" + }, + "application/session-info": { + "source": "iana" + }, + "application/set-payment": { + "source": "iana" + }, + "application/set-payment-initiation": { + "source": "iana", + "extensions": ["setpay"] + }, + "application/set-registration": { + "source": "iana" + }, + "application/set-registration-initiation": { + "source": "iana", + "extensions": ["setreg"] + }, + "application/sgml": { + "source": "iana" + }, + "application/sgml-open-catalog": { + "source": "iana" + }, + "application/shf+xml": { + "source": "iana", + "extensions": ["shf"] + }, + "application/sieve": { + "source": "iana" + }, + "application/simple-filter+xml": { + "source": "iana" + }, + "application/simple-message-summary": { + "source": "iana" + }, + "application/simplesymbolcontainer": { + "source": "iana" + }, + "application/slate": { + "source": "iana" + }, + "application/smil": { + "source": "iana" + }, + "application/smil+xml": { + "source": "iana", + "extensions": ["smi","smil"] + }, + "application/smpte336m": { + "source": "iana" + }, + "application/soap+fastinfoset": { + "source": "iana" + }, + "application/soap+xml": { + "source": "iana", + "compressible": true + }, + "application/sparql-query": { + "source": "iana", + "extensions": ["rq"] + }, + "application/sparql-results+xml": { + "source": "iana", + "extensions": ["srx"] + }, + "application/spirits-event+xml": { + "source": "iana" + }, + "application/sql": { + "source": "iana" + }, + "application/srgs": { + "source": "iana", + "extensions": ["gram"] + }, + "application/srgs+xml": { + "source": "iana", + "extensions": ["grxml"] + }, + "application/sru+xml": { + "source": "iana", + "extensions": ["sru"] + }, + "application/ssdl+xml": { + "source": "apache", + "extensions": ["ssdl"] + }, + "application/ssml+xml": { + "source": "iana", + "extensions": ["ssml"] + }, + "application/tamp-apex-update": { + "source": "iana" + }, + "application/tamp-apex-update-confirm": { + "source": "iana" + }, + "application/tamp-community-update": { + "source": "iana" + }, + "application/tamp-community-update-confirm": { + "source": "iana" + }, + "application/tamp-error": { + "source": "iana" + }, + "application/tamp-sequence-adjust": { + "source": "iana" + }, + "application/tamp-sequence-adjust-confirm": { + "source": "iana" + }, + "application/tamp-status-query": { + "source": "iana" + }, + "application/tamp-status-response": { + "source": "iana" + }, + "application/tamp-update": { + "source": "iana" + }, + "application/tamp-update-confirm": { + "source": "iana" + }, + "application/tar": { + "compressible": true + }, + "application/tei+xml": { + "source": "iana", + "extensions": ["tei","teicorpus"] + }, + "application/thraud+xml": { + "source": "iana", + "extensions": ["tfi"] + }, + "application/timestamp-query": { + "source": "iana" + }, + "application/timestamp-reply": { + "source": "iana" + }, + "application/timestamped-data": { + "source": "iana", + "extensions": ["tsd"] + }, + "application/ttml+xml": { + "source": "iana" + }, + "application/tve-trigger": { + "source": "iana" + }, + "application/ulpfec": { + "source": "iana" + }, + "application/urc-grpsheet+xml": { + "source": "iana" + }, + "application/urc-ressheet+xml": { + "source": "iana" + }, + "application/urc-targetdesc+xml": { + "source": "iana" + }, + "application/urc-uisocketdesc+xml": { + "source": "iana" + }, + "application/vcard+json": { + "source": "iana", + "compressible": true + }, + "application/vcard+xml": { + "source": "iana" + }, + "application/vemmi": { + "source": "iana" + }, + "application/vividence.scriptfile": { + "source": "apache" + }, + "application/vnd.3gpp-prose+xml": { + "source": "iana" + }, + "application/vnd.3gpp-prose-pc3ch+xml": { + "source": "iana" + }, + "application/vnd.3gpp.access-transfer-events+xml": { + "source": "iana" + }, + "application/vnd.3gpp.bsf+xml": { + "source": "iana" + }, + "application/vnd.3gpp.mid-call+xml": { + "source": "iana" + }, + "application/vnd.3gpp.pic-bw-large": { + "source": "iana", + "extensions": ["plb"] + }, + "application/vnd.3gpp.pic-bw-small": { + "source": "iana", + "extensions": ["psb"] + }, + "application/vnd.3gpp.pic-bw-var": { + "source": "iana", + "extensions": ["pvb"] + }, + "application/vnd.3gpp.sms": { + "source": "iana" + }, + "application/vnd.3gpp.srvcc-ext+xml": { + "source": "iana" + }, + "application/vnd.3gpp.srvcc-info+xml": { + "source": "iana" + }, + "application/vnd.3gpp.state-and-event-info+xml": { + "source": "iana" + }, + "application/vnd.3gpp.ussd+xml": { + "source": "iana" + }, + "application/vnd.3gpp2.bcmcsinfo+xml": { + "source": "iana" + }, + "application/vnd.3gpp2.sms": { + "source": "iana" + }, + "application/vnd.3gpp2.tcap": { + "source": "iana", + "extensions": ["tcap"] + }, + "application/vnd.3m.post-it-notes": { + "source": "iana", + "extensions": ["pwn"] + }, + "application/vnd.accpac.simply.aso": { + "source": "iana", + "extensions": ["aso"] + }, + "application/vnd.accpac.simply.imp": { + "source": "iana", + "extensions": ["imp"] + }, + "application/vnd.acucobol": { + "source": "iana", + "extensions": ["acu"] + }, + "application/vnd.acucorp": { + "source": "iana", + "extensions": ["atc","acutc"] + }, + "application/vnd.adobe.air-application-installer-package+zip": { + "source": "apache", + "extensions": ["air"] + }, + "application/vnd.adobe.flash.movie": { + "source": "iana" + }, + "application/vnd.adobe.formscentral.fcdt": { + "source": "iana", + "extensions": ["fcdt"] + }, + "application/vnd.adobe.fxp": { + "source": "iana", + "extensions": ["fxp","fxpl"] + }, + "application/vnd.adobe.partial-upload": { + "source": "iana" + }, + "application/vnd.adobe.xdp+xml": { + "source": "iana", + "extensions": ["xdp"] + }, + "application/vnd.adobe.xfdf": { + "source": "iana", + "extensions": ["xfdf"] + }, + "application/vnd.aether.imp": { + "source": "iana" + }, + "application/vnd.ah-barcode": { + "source": "iana" + }, + "application/vnd.ahead.space": { + "source": "iana", + "extensions": ["ahead"] + }, + "application/vnd.airzip.filesecure.azf": { + "source": "iana", + "extensions": ["azf"] + }, + "application/vnd.airzip.filesecure.azs": { + "source": "iana", + "extensions": ["azs"] + }, + "application/vnd.amazon.ebook": { + "source": "apache", + "extensions": ["azw"] + }, + "application/vnd.americandynamics.acc": { + "source": "iana", + "extensions": ["acc"] + }, + "application/vnd.amiga.ami": { + "source": "iana", + "extensions": ["ami"] + }, + "application/vnd.amundsen.maze+xml": { + "source": "iana" + }, + "application/vnd.android.package-archive": { + "source": "apache", + "compressible": false, + "extensions": ["apk"] + }, + "application/vnd.anki": { + "source": "iana" + }, + "application/vnd.anser-web-certificate-issue-initiation": { + "source": "iana", + "extensions": ["cii"] + }, + "application/vnd.anser-web-funds-transfer-initiation": { + "source": "apache", + "extensions": ["fti"] + }, + "application/vnd.antix.game-component": { + "source": "iana", + "extensions": ["atx"] + }, + "application/vnd.apache.thrift.binary": { + "source": "iana" + }, + "application/vnd.apache.thrift.compact": { + "source": "iana" + }, + "application/vnd.apache.thrift.json": { + "source": "iana" + }, + "application/vnd.api+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.apple.installer+xml": { + "source": "iana", + "extensions": ["mpkg"] + }, + "application/vnd.apple.mpegurl": { + "source": "iana", + "extensions": ["m3u8"] + }, + "application/vnd.apple.pkpass": { + "compressible": false, + "extensions": ["pkpass"] + }, + "application/vnd.arastra.swi": { + "source": "iana" + }, + "application/vnd.aristanetworks.swi": { + "source": "iana", + "extensions": ["swi"] + }, + "application/vnd.artsquare": { + "source": "iana" + }, + "application/vnd.astraea-software.iota": { + "source": "iana", + "extensions": ["iota"] + }, + "application/vnd.audiograph": { + "source": "iana", + "extensions": ["aep"] + }, + "application/vnd.autopackage": { + "source": "iana" + }, + "application/vnd.avistar+xml": { + "source": "iana" + }, + "application/vnd.balsamiq.bmml+xml": { + "source": "iana" + }, + "application/vnd.balsamiq.bmpr": { + "source": "iana" + }, + "application/vnd.bekitzur-stech+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.biopax.rdf+xml": { + "source": "iana" + }, + "application/vnd.blueice.multipass": { + "source": "iana", + "extensions": ["mpm"] + }, + "application/vnd.bluetooth.ep.oob": { + "source": "iana" + }, + "application/vnd.bluetooth.le.oob": { + "source": "iana" + }, + "application/vnd.bmi": { + "source": "iana", + "extensions": ["bmi"] + }, + "application/vnd.businessobjects": { + "source": "iana", + "extensions": ["rep"] + }, + "application/vnd.cab-jscript": { + "source": "iana" + }, + "application/vnd.canon-cpdl": { + "source": "iana" + }, + "application/vnd.canon-lips": { + "source": "iana" + }, + "application/vnd.cendio.thinlinc.clientconf": { + "source": "iana" + }, + "application/vnd.century-systems.tcp_stream": { + "source": "iana" + }, + "application/vnd.chemdraw+xml": { + "source": "iana", + "extensions": ["cdxml"] + }, + "application/vnd.chipnuts.karaoke-mmd": { + "source": "iana", + "extensions": ["mmd"] + }, + "application/vnd.cinderella": { + "source": "iana", + "extensions": ["cdy"] + }, + "application/vnd.cirpack.isdn-ext": { + "source": "iana" + }, + "application/vnd.citationstyles.style+xml": { + "source": "iana" + }, + "application/vnd.claymore": { + "source": "iana", + "extensions": ["cla"] + }, + "application/vnd.cloanto.rp9": { + "source": "iana", + "extensions": ["rp9"] + }, + "application/vnd.clonk.c4group": { + "source": "iana", + "extensions": ["c4g","c4d","c4f","c4p","c4u"] + }, + "application/vnd.cluetrust.cartomobile-config": { + "source": "iana", + "extensions": ["c11amc"] + }, + "application/vnd.cluetrust.cartomobile-config-pkg": { + "source": "iana", + "extensions": ["c11amz"] + }, + "application/vnd.coffeescript": { + "source": "iana" + }, + "application/vnd.collection+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.collection.doc+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.collection.next+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.commerce-battelle": { + "source": "iana" + }, + "application/vnd.commonspace": { + "source": "iana", + "extensions": ["csp"] + }, + "application/vnd.contact.cmsg": { + "source": "iana", + "extensions": ["cdbcmsg"] + }, + "application/vnd.cosmocaller": { + "source": "iana", + "extensions": ["cmc"] + }, + "application/vnd.crick.clicker": { + "source": "iana", + "extensions": ["clkx"] + }, + "application/vnd.crick.clicker.keyboard": { + "source": "iana", + "extensions": ["clkk"] + }, + "application/vnd.crick.clicker.palette": { + "source": "iana", + "extensions": ["clkp"] + }, + "application/vnd.crick.clicker.template": { + "source": "iana", + "extensions": ["clkt"] + }, + "application/vnd.crick.clicker.wordbank": { + "source": "iana", + "extensions": ["clkw"] + }, + "application/vnd.criticaltools.wbs+xml": { + "source": "iana", + "extensions": ["wbs"] + }, + "application/vnd.ctc-posml": { + "source": "iana", + "extensions": ["pml"] + }, + "application/vnd.ctct.ws+xml": { + "source": "iana" + }, + "application/vnd.cups-pdf": { + "source": "iana" + }, + "application/vnd.cups-postscript": { + "source": "iana" + }, + "application/vnd.cups-ppd": { + "source": "iana", + "extensions": ["ppd"] + }, + "application/vnd.cups-raster": { + "source": "iana" + }, + "application/vnd.cups-raw": { + "source": "iana" + }, + "application/vnd.curl": { + "source": "iana" + }, + "application/vnd.curl.car": { + "source": "apache", + "extensions": ["car"] + }, + "application/vnd.curl.pcurl": { + "source": "apache", + "extensions": ["pcurl"] + }, + "application/vnd.cyan.dean.root+xml": { + "source": "iana" + }, + "application/vnd.cybank": { + "source": "iana" + }, + "application/vnd.dart": { + "source": "iana", + "compressible": true, + "extensions": ["dart"] + }, + "application/vnd.data-vision.rdz": { + "source": "iana", + "extensions": ["rdz"] + }, + "application/vnd.debian.binary-package": { + "source": "iana" + }, + "application/vnd.dece.data": { + "source": "iana", + "extensions": ["uvf","uvvf","uvd","uvvd"] + }, + "application/vnd.dece.ttml+xml": { + "source": "iana", + "extensions": ["uvt","uvvt"] + }, + "application/vnd.dece.unspecified": { + "source": "iana", + "extensions": ["uvx","uvvx"] + }, + "application/vnd.dece.zip": { + "source": "iana", + "extensions": ["uvz","uvvz"] + }, + "application/vnd.denovo.fcselayout-link": { + "source": "iana", + "extensions": ["fe_launch"] + }, + "application/vnd.desmume-movie": { + "source": "iana" + }, + "application/vnd.dir-bi.plate-dl-nosuffix": { + "source": "iana" + }, + "application/vnd.dm.delegation+xml": { + "source": "iana" + }, + "application/vnd.dna": { + "source": "iana", + "extensions": ["dna"] + }, + "application/vnd.document+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.dolby.mlp": { + "source": "apache", + "extensions": ["mlp"] + }, + "application/vnd.dolby.mobile.1": { + "source": "iana" + }, + "application/vnd.dolby.mobile.2": { + "source": "iana" + }, + "application/vnd.doremir.scorecloud-binary-document": { + "source": "iana" + }, + "application/vnd.dpgraph": { + "source": "iana", + "extensions": ["dpg"] + }, + "application/vnd.dreamfactory": { + "source": "iana", + "extensions": ["dfac"] + }, + "application/vnd.drive+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ds-keypoint": { + "source": "apache", + "extensions": ["kpxx"] + }, + "application/vnd.dtg.local": { + "source": "iana" + }, + "application/vnd.dtg.local.flash": { + "source": "iana" + }, + "application/vnd.dtg.local.html": { + "source": "iana" + }, + "application/vnd.dvb.ait": { + "source": "iana", + "extensions": ["ait"] + }, + "application/vnd.dvb.dvbj": { + "source": "iana" + }, + "application/vnd.dvb.esgcontainer": { + "source": "iana" + }, + "application/vnd.dvb.ipdcdftnotifaccess": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgaccess": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgaccess2": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgpdd": { + "source": "iana" + }, + "application/vnd.dvb.ipdcroaming": { + "source": "iana" + }, + "application/vnd.dvb.iptv.alfec-base": { + "source": "iana" + }, + "application/vnd.dvb.iptv.alfec-enhancement": { + "source": "iana" + }, + "application/vnd.dvb.notif-aggregate-root+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-container+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-generic+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-ia-msglist+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-ia-registration-request+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-ia-registration-response+xml": { + "source": "iana" + }, + "application/vnd.dvb.notif-init+xml": { + "source": "iana" + }, + "application/vnd.dvb.pfr": { + "source": "iana" + }, + "application/vnd.dvb.service": { + "source": "iana", + "extensions": ["svc"] + }, + "application/vnd.dxr": { + "source": "iana" + }, + "application/vnd.dynageo": { + "source": "iana", + "extensions": ["geo"] + }, + "application/vnd.dzr": { + "source": "iana" + }, + "application/vnd.easykaraoke.cdgdownload": { + "source": "iana" + }, + "application/vnd.ecdis-update": { + "source": "iana" + }, + "application/vnd.ecowin.chart": { + "source": "iana", + "extensions": ["mag"] + }, + "application/vnd.ecowin.filerequest": { + "source": "iana" + }, + "application/vnd.ecowin.fileupdate": { + "source": "iana" + }, + "application/vnd.ecowin.series": { + "source": "iana" + }, + "application/vnd.ecowin.seriesrequest": { + "source": "iana" + }, + "application/vnd.ecowin.seriesupdate": { + "source": "iana" + }, + "application/vnd.emclient.accessrequest+xml": { + "source": "iana" + }, + "application/vnd.enliven": { + "source": "iana", + "extensions": ["nml"] + }, + "application/vnd.enphase.envoy": { + "source": "iana" + }, + "application/vnd.eprints.data+xml": { + "source": "iana" + }, + "application/vnd.epson.esf": { + "source": "iana", + "extensions": ["esf"] + }, + "application/vnd.epson.msf": { + "source": "iana", + "extensions": ["msf"] + }, + "application/vnd.epson.quickanime": { + "source": "iana", + "extensions": ["qam"] + }, + "application/vnd.epson.salt": { + "source": "iana", + "extensions": ["slt"] + }, + "application/vnd.epson.ssf": { + "source": "iana", + "extensions": ["ssf"] + }, + "application/vnd.ericsson.quickcall": { + "source": "iana" + }, + "application/vnd.eszigno3+xml": { + "source": "iana", + "extensions": ["es3","et3"] + }, + "application/vnd.etsi.aoc+xml": { + "source": "iana" + }, + "application/vnd.etsi.asic-e+zip": { + "source": "iana" + }, + "application/vnd.etsi.asic-s+zip": { + "source": "iana" + }, + "application/vnd.etsi.cug+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvcommand+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvdiscovery+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvprofile+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsad-bc+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsad-cod+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsad-npvr+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvservice+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvsync+xml": { + "source": "iana" + }, + "application/vnd.etsi.iptvueprofile+xml": { + "source": "iana" + }, + "application/vnd.etsi.mcid+xml": { + "source": "iana" + }, + "application/vnd.etsi.mheg5": { + "source": "iana" + }, + "application/vnd.etsi.overload-control-policy-dataset+xml": { + "source": "iana" + }, + "application/vnd.etsi.pstn+xml": { + "source": "iana" + }, + "application/vnd.etsi.sci+xml": { + "source": "iana" + }, + "application/vnd.etsi.simservs+xml": { + "source": "iana" + }, + "application/vnd.etsi.timestamp-token": { + "source": "iana" + }, + "application/vnd.etsi.tsl+xml": { + "source": "iana" + }, + "application/vnd.etsi.tsl.der": { + "source": "iana" + }, + "application/vnd.eudora.data": { + "source": "iana" + }, + "application/vnd.ezpix-album": { + "source": "iana", + "extensions": ["ez2"] + }, + "application/vnd.ezpix-package": { + "source": "iana", + "extensions": ["ez3"] + }, + "application/vnd.f-secure.mobile": { + "source": "iana" + }, + "application/vnd.fastcopy-disk-image": { + "source": "iana" + }, + "application/vnd.fdf": { + "source": "iana", + "extensions": ["fdf"] + }, + "application/vnd.fdsn.mseed": { + "source": "iana", + "extensions": ["mseed"] + }, + "application/vnd.fdsn.seed": { + "source": "iana", + "extensions": ["seed","dataless"] + }, + "application/vnd.ffsns": { + "source": "iana" + }, + "application/vnd.fints": { + "source": "iana" + }, + "application/vnd.firemonkeys.cloudcell": { + "source": "iana" + }, + "application/vnd.flographit": { + "source": "iana", + "extensions": ["gph"] + }, + "application/vnd.fluxtime.clip": { + "source": "iana", + "extensions": ["ftc"] + }, + "application/vnd.font-fontforge-sfd": { + "source": "iana" + }, + "application/vnd.framemaker": { + "source": "iana", + "extensions": ["fm","frame","maker","book"] + }, + "application/vnd.frogans.fnc": { + "source": "iana", + "extensions": ["fnc"] + }, + "application/vnd.frogans.ltf": { + "source": "iana", + "extensions": ["ltf"] + }, + "application/vnd.fsc.weblaunch": { + "source": "iana", + "extensions": ["fsc"] + }, + "application/vnd.fujitsu.oasys": { + "source": "iana", + "extensions": ["oas"] + }, + "application/vnd.fujitsu.oasys2": { + "source": "iana", + "extensions": ["oa2"] + }, + "application/vnd.fujitsu.oasys3": { + "source": "iana", + "extensions": ["oa3"] + }, + "application/vnd.fujitsu.oasysgp": { + "source": "iana", + "extensions": ["fg5"] + }, + "application/vnd.fujitsu.oasysprs": { + "source": "iana", + "extensions": ["bh2"] + }, + "application/vnd.fujixerox.art-ex": { + "source": "iana" + }, + "application/vnd.fujixerox.art4": { + "source": "iana" + }, + "application/vnd.fujixerox.ddd": { + "source": "iana", + "extensions": ["ddd"] + }, + "application/vnd.fujixerox.docuworks": { + "source": "iana", + "extensions": ["xdw"] + }, + "application/vnd.fujixerox.docuworks.binder": { + "source": "iana", + "extensions": ["xbd"] + }, + "application/vnd.fujixerox.docuworks.container": { + "source": "iana" + }, + "application/vnd.fujixerox.hbpl": { + "source": "iana" + }, + "application/vnd.fut-misnet": { + "source": "iana" + }, + "application/vnd.fuzzysheet": { + "source": "iana", + "extensions": ["fzs"] + }, + "application/vnd.genomatix.tuxedo": { + "source": "iana", + "extensions": ["txd"] + }, + "application/vnd.geo+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.geocube+xml": { + "source": "iana" + }, + "application/vnd.geogebra.file": { + "source": "iana", + "extensions": ["ggb"] + }, + "application/vnd.geogebra.tool": { + "source": "iana", + "extensions": ["ggt"] + }, + "application/vnd.geometry-explorer": { + "source": "iana", + "extensions": ["gex","gre"] + }, + "application/vnd.geonext": { + "source": "iana", + "extensions": ["gxt"] + }, + "application/vnd.geoplan": { + "source": "iana", + "extensions": ["g2w"] + }, + "application/vnd.geospace": { + "source": "iana", + "extensions": ["g3w"] + }, + "application/vnd.gerber": { + "source": "iana" + }, + "application/vnd.globalplatform.card-content-mgt": { + "source": "iana" + }, + "application/vnd.globalplatform.card-content-mgt-response": { + "source": "iana" + }, + "application/vnd.gmx": { + "source": "iana", + "extensions": ["gmx"] + }, + "application/vnd.google-earth.kml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["kml"] + }, + "application/vnd.google-earth.kmz": { + "source": "iana", + "compressible": false, + "extensions": ["kmz"] + }, + "application/vnd.gov.sk.e-form+xml": { + "source": "iana" + }, + "application/vnd.gov.sk.e-form+zip": { + "source": "iana" + }, + "application/vnd.gov.sk.xmldatacontainer+xml": { + "source": "iana" + }, + "application/vnd.grafeq": { + "source": "iana", + "extensions": ["gqf","gqs"] + }, + "application/vnd.gridmp": { + "source": "iana" + }, + "application/vnd.groove-account": { + "source": "iana", + "extensions": ["gac"] + }, + "application/vnd.groove-help": { + "source": "iana", + "extensions": ["ghf"] + }, + "application/vnd.groove-identity-message": { + "source": "iana", + "extensions": ["gim"] + }, + "application/vnd.groove-injector": { + "source": "iana", + "extensions": ["grv"] + }, + "application/vnd.groove-tool-message": { + "source": "iana", + "extensions": ["gtm"] + }, + "application/vnd.groove-tool-template": { + "source": "iana", + "extensions": ["tpl"] + }, + "application/vnd.groove-vcard": { + "source": "iana", + "extensions": ["vcg"] + }, + "application/vnd.hal+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hal+xml": { + "source": "iana", + "extensions": ["hal"] + }, + "application/vnd.handheld-entertainment+xml": { + "source": "iana", + "extensions": ["zmm"] + }, + "application/vnd.hbci": { + "source": "iana", + "extensions": ["hbci"] + }, + "application/vnd.hcl-bireports": { + "source": "iana" + }, + "application/vnd.heroku+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hhe.lesson-player": { + "source": "iana", + "extensions": ["les"] + }, + "application/vnd.hp-hpgl": { + "source": "iana", + "extensions": ["hpgl"] + }, + "application/vnd.hp-hpid": { + "source": "iana", + "extensions": ["hpid"] + }, + "application/vnd.hp-hps": { + "source": "iana", + "extensions": ["hps"] + }, + "application/vnd.hp-jlyt": { + "source": "iana", + "extensions": ["jlt"] + }, + "application/vnd.hp-pcl": { + "source": "iana", + "extensions": ["pcl"] + }, + "application/vnd.hp-pclxl": { + "source": "iana", + "extensions": ["pclxl"] + }, + "application/vnd.httphone": { + "source": "iana" + }, + "application/vnd.hydrostatix.sof-data": { + "source": "iana", + "extensions": ["sfd-hdstx"] + }, + "application/vnd.hyperdrive+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hzn-3d-crossword": { + "source": "iana" + }, + "application/vnd.ibm.afplinedata": { + "source": "iana" + }, + "application/vnd.ibm.electronic-media": { + "source": "iana" + }, + "application/vnd.ibm.minipay": { + "source": "iana", + "extensions": ["mpy"] + }, + "application/vnd.ibm.modcap": { + "source": "iana", + "extensions": ["afp","listafp","list3820"] + }, + "application/vnd.ibm.rights-management": { + "source": "iana", + "extensions": ["irm"] + }, + "application/vnd.ibm.secure-container": { + "source": "iana", + "extensions": ["sc"] + }, + "application/vnd.iccprofile": { + "source": "iana", + "extensions": ["icc","icm"] + }, + "application/vnd.ieee.1905": { + "source": "iana" + }, + "application/vnd.igloader": { + "source": "iana", + "extensions": ["igl"] + }, + "application/vnd.immervision-ivp": { + "source": "iana", + "extensions": ["ivp"] + }, + "application/vnd.immervision-ivu": { + "source": "iana", + "extensions": ["ivu"] + }, + "application/vnd.ims.imsccv1p1": { + "source": "iana" + }, + "application/vnd.ims.imsccv1p2": { + "source": "iana" + }, + "application/vnd.ims.imsccv1p3": { + "source": "iana" + }, + "application/vnd.ims.lis.v2.result+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolconsumerprofile+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolproxy+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolproxy.id+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolsettings+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolsettings.simple+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.informedcontrol.rms+xml": { + "source": "iana" + }, + "application/vnd.informix-visionary": { + "source": "iana" + }, + "application/vnd.infotech.project": { + "source": "iana" + }, + "application/vnd.infotech.project+xml": { + "source": "iana" + }, + "application/vnd.innopath.wamp.notification": { + "source": "iana" + }, + "application/vnd.insors.igm": { + "source": "iana", + "extensions": ["igm"] + }, + "application/vnd.intercon.formnet": { + "source": "iana", + "extensions": ["xpw","xpx"] + }, + "application/vnd.intergeo": { + "source": "iana", + "extensions": ["i2g"] + }, + "application/vnd.intertrust.digibox": { + "source": "iana" + }, + "application/vnd.intertrust.nncp": { + "source": "iana" + }, + "application/vnd.intu.qbo": { + "source": "iana", + "extensions": ["qbo"] + }, + "application/vnd.intu.qfx": { + "source": "iana", + "extensions": ["qfx"] + }, + "application/vnd.iptc.g2.catalogitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.conceptitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.knowledgeitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.newsitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.newsmessage+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.packageitem+xml": { + "source": "iana" + }, + "application/vnd.iptc.g2.planningitem+xml": { + "source": "iana" + }, + "application/vnd.ipunplugged.rcprofile": { + "source": "iana", + "extensions": ["rcprofile"] + }, + "application/vnd.irepository.package+xml": { + "source": "iana", + "extensions": ["irp"] + }, + "application/vnd.is-xpr": { + "source": "iana", + "extensions": ["xpr"] + }, + "application/vnd.isac.fcs": { + "source": "iana", + "extensions": ["fcs"] + }, + "application/vnd.jam": { + "source": "iana", + "extensions": ["jam"] + }, + "application/vnd.japannet-directory-service": { + "source": "iana" + }, + "application/vnd.japannet-jpnstore-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-payment-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-registration": { + "source": "iana" + }, + "application/vnd.japannet-registration-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-setstore-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-verification": { + "source": "iana" + }, + "application/vnd.japannet-verification-wakeup": { + "source": "iana" + }, + "application/vnd.jcp.javame.midlet-rms": { + "source": "iana", + "extensions": ["rms"] + }, + "application/vnd.jisp": { + "source": "iana", + "extensions": ["jisp"] + }, + "application/vnd.joost.joda-archive": { + "source": "iana", + "extensions": ["joda"] + }, + "application/vnd.jsk.isdn-ngn": { + "source": "iana" + }, + "application/vnd.kahootz": { + "source": "iana", + "extensions": ["ktz","ktr"] + }, + "application/vnd.kde.karbon": { + "source": "iana", + "extensions": ["karbon"] + }, + "application/vnd.kde.kchart": { + "source": "iana", + "extensions": ["chrt"] + }, + "application/vnd.kde.kformula": { + "source": "iana", + "extensions": ["kfo"] + }, + "application/vnd.kde.kivio": { + "source": "iana", + "extensions": ["flw"] + }, + "application/vnd.kde.kontour": { + "source": "iana", + "extensions": ["kon"] + }, + "application/vnd.kde.kpresenter": { + "source": "iana", + "extensions": ["kpr","kpt"] + }, + "application/vnd.kde.kspread": { + "source": "iana", + "extensions": ["ksp"] + }, + "application/vnd.kde.kword": { + "source": "iana", + "extensions": ["kwd","kwt"] + }, + "application/vnd.kenameaapp": { + "source": "iana", + "extensions": ["htke"] + }, + "application/vnd.kidspiration": { + "source": "iana", + "extensions": ["kia"] + }, + "application/vnd.kinar": { + "source": "iana", + "extensions": ["kne","knp"] + }, + "application/vnd.koan": { + "source": "iana", + "extensions": ["skp","skd","skt","skm"] + }, + "application/vnd.kodak-descriptor": { + "source": "iana", + "extensions": ["sse"] + }, + "application/vnd.las.las+xml": { + "source": "iana", + "extensions": ["lasxml"] + }, + "application/vnd.liberty-request+xml": { + "source": "iana" + }, + "application/vnd.llamagraphics.life-balance.desktop": { + "source": "iana", + "extensions": ["lbd"] + }, + "application/vnd.llamagraphics.life-balance.exchange+xml": { + "source": "iana", + "extensions": ["lbe"] + }, + "application/vnd.lotus-1-2-3": { + "source": "iana", + "extensions": ["123"] + }, + "application/vnd.lotus-approach": { + "source": "iana", + "extensions": ["apr"] + }, + "application/vnd.lotus-freelance": { + "source": "iana", + "extensions": ["pre"] + }, + "application/vnd.lotus-notes": { + "source": "iana", + "extensions": ["nsf"] + }, + "application/vnd.lotus-organizer": { + "source": "iana", + "extensions": ["org"] + }, + "application/vnd.lotus-screencam": { + "source": "iana", + "extensions": ["scm"] + }, + "application/vnd.lotus-wordpro": { + "source": "iana", + "extensions": ["lwp"] + }, + "application/vnd.macports.portpkg": { + "source": "iana", + "extensions": ["portpkg"] + }, + "application/vnd.marlin.drm.actiontoken+xml": { + "source": "iana" + }, + "application/vnd.marlin.drm.conftoken+xml": { + "source": "iana" + }, + "application/vnd.marlin.drm.license+xml": { + "source": "iana" + }, + "application/vnd.marlin.drm.mdcf": { + "source": "iana" + }, + "application/vnd.mason+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.maxmind.maxmind-db": { + "source": "iana" + }, + "application/vnd.mcd": { + "source": "iana", + "extensions": ["mcd"] + }, + "application/vnd.medcalcdata": { + "source": "iana", + "extensions": ["mc1"] + }, + "application/vnd.mediastation.cdkey": { + "source": "iana", + "extensions": ["cdkey"] + }, + "application/vnd.meridian-slingshot": { + "source": "iana" + }, + "application/vnd.mfer": { + "source": "iana", + "extensions": ["mwf"] + }, + "application/vnd.mfmp": { + "source": "iana", + "extensions": ["mfm"] + }, + "application/vnd.micro+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.micrografx.flo": { + "source": "iana", + "extensions": ["flo"] + }, + "application/vnd.micrografx.igx": { + "source": "iana", + "extensions": ["igx"] + }, + "application/vnd.microsoft.portable-executable": { + "source": "iana" + }, + "application/vnd.miele+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.mif": { + "source": "iana", + "extensions": ["mif"] + }, + "application/vnd.minisoft-hp3000-save": { + "source": "iana" + }, + "application/vnd.mitsubishi.misty-guard.trustweb": { + "source": "iana" + }, + "application/vnd.mobius.daf": { + "source": "iana", + "extensions": ["daf"] + }, + "application/vnd.mobius.dis": { + "source": "iana", + "extensions": ["dis"] + }, + "application/vnd.mobius.mbk": { + "source": "iana", + "extensions": ["mbk"] + }, + "application/vnd.mobius.mqy": { + "source": "iana", + "extensions": ["mqy"] + }, + "application/vnd.mobius.msl": { + "source": "iana", + "extensions": ["msl"] + }, + "application/vnd.mobius.plc": { + "source": "iana", + "extensions": ["plc"] + }, + "application/vnd.mobius.txf": { + "source": "iana", + "extensions": ["txf"] + }, + "application/vnd.mophun.application": { + "source": "iana", + "extensions": ["mpn"] + }, + "application/vnd.mophun.certificate": { + "source": "iana", + "extensions": ["mpc"] + }, + "application/vnd.motorola.flexsuite": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.adsi": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.fis": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.gotap": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.kmr": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.ttc": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.wem": { + "source": "iana" + }, + "application/vnd.motorola.iprm": { + "source": "iana" + }, + "application/vnd.mozilla.xul+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xul"] + }, + "application/vnd.ms-3mfdocument": { + "source": "iana" + }, + "application/vnd.ms-artgalry": { + "source": "iana", + "extensions": ["cil"] + }, + "application/vnd.ms-asf": { + "source": "iana" + }, + "application/vnd.ms-cab-compressed": { + "source": "iana", + "extensions": ["cab"] + }, + "application/vnd.ms-color.iccprofile": { + "source": "apache" + }, + "application/vnd.ms-excel": { + "source": "iana", + "compressible": false, + "extensions": ["xls","xlm","xla","xlc","xlt","xlw"] + }, + "application/vnd.ms-excel.addin.macroenabled.12": { + "source": "iana", + "extensions": ["xlam"] + }, + "application/vnd.ms-excel.sheet.binary.macroenabled.12": { + "source": "iana", + "extensions": ["xlsb"] + }, + "application/vnd.ms-excel.sheet.macroenabled.12": { + "source": "iana", + "extensions": ["xlsm"] + }, + "application/vnd.ms-excel.template.macroenabled.12": { + "source": "iana", + "extensions": ["xltm"] + }, + "application/vnd.ms-fontobject": { + "source": "iana", + "compressible": true, + "extensions": ["eot"] + }, + "application/vnd.ms-htmlhelp": { + "source": "iana", + "extensions": ["chm"] + }, + "application/vnd.ms-ims": { + "source": "iana", + "extensions": ["ims"] + }, + "application/vnd.ms-lrm": { + "source": "iana", + "extensions": ["lrm"] + }, + "application/vnd.ms-office.activex+xml": { + "source": "iana" + }, + "application/vnd.ms-officetheme": { + "source": "iana", + "extensions": ["thmx"] + }, + "application/vnd.ms-opentype": { + "source": "apache", + "compressible": true + }, + "application/vnd.ms-package.obfuscated-opentype": { + "source": "apache" + }, + "application/vnd.ms-pki.seccat": { + "source": "apache", + "extensions": ["cat"] + }, + "application/vnd.ms-pki.stl": { + "source": "apache", + "extensions": ["stl"] + }, + "application/vnd.ms-playready.initiator+xml": { + "source": "iana" + }, + "application/vnd.ms-powerpoint": { + "source": "iana", + "compressible": false, + "extensions": ["ppt","pps","pot"] + }, + "application/vnd.ms-powerpoint.addin.macroenabled.12": { + "source": "iana", + "extensions": ["ppam"] + }, + "application/vnd.ms-powerpoint.presentation.macroenabled.12": { + "source": "iana", + "extensions": ["pptm"] + }, + "application/vnd.ms-powerpoint.slide.macroenabled.12": { + "source": "iana", + "extensions": ["sldm"] + }, + "application/vnd.ms-powerpoint.slideshow.macroenabled.12": { + "source": "iana", + "extensions": ["ppsm"] + }, + "application/vnd.ms-powerpoint.template.macroenabled.12": { + "source": "iana", + "extensions": ["potm"] + }, + "application/vnd.ms-printing.printticket+xml": { + "source": "apache" + }, + "application/vnd.ms-project": { + "source": "iana", + "extensions": ["mpp","mpt"] + }, + "application/vnd.ms-tnef": { + "source": "iana" + }, + "application/vnd.ms-windows.printerpairing": { + "source": "iana" + }, + "application/vnd.ms-windows.wsd.oob": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.lic-chlg-req": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.lic-resp": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.meter-chlg-req": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.meter-resp": { + "source": "iana" + }, + "application/vnd.ms-word.document.macroenabled.12": { + "source": "iana", + "extensions": ["docm"] + }, + "application/vnd.ms-word.template.macroenabled.12": { + "source": "iana", + "extensions": ["dotm"] + }, + "application/vnd.ms-works": { + "source": "iana", + "extensions": ["wps","wks","wcm","wdb"] + }, + "application/vnd.ms-wpl": { + "source": "iana", + "extensions": ["wpl"] + }, + "application/vnd.ms-xpsdocument": { + "source": "iana", + "compressible": false, + "extensions": ["xps"] + }, + "application/vnd.msa-disk-image": { + "source": "iana" + }, + "application/vnd.mseq": { + "source": "iana", + "extensions": ["mseq"] + }, + "application/vnd.msign": { + "source": "iana" + }, + "application/vnd.multiad.creator": { + "source": "iana" + }, + "application/vnd.multiad.creator.cif": { + "source": "iana" + }, + "application/vnd.music-niff": { + "source": "iana" + }, + "application/vnd.musician": { + "source": "iana", + "extensions": ["mus"] + }, + "application/vnd.muvee.style": { + "source": "iana", + "extensions": ["msty"] + }, + "application/vnd.mynfc": { + "source": "iana", + "extensions": ["taglet"] + }, + "application/vnd.ncd.control": { + "source": "iana" + }, + "application/vnd.ncd.reference": { + "source": "iana" + }, + "application/vnd.nervana": { + "source": "iana" + }, + "application/vnd.netfpx": { + "source": "iana" + }, + "application/vnd.neurolanguage.nlu": { + "source": "iana", + "extensions": ["nlu"] + }, + "application/vnd.nintendo.nitro.rom": { + "source": "iana" + }, + "application/vnd.nintendo.snes.rom": { + "source": "iana" + }, + "application/vnd.nitf": { + "source": "iana", + "extensions": ["ntf","nitf"] + }, + "application/vnd.noblenet-directory": { + "source": "iana", + "extensions": ["nnd"] + }, + "application/vnd.noblenet-sealer": { + "source": "iana", + "extensions": ["nns"] + }, + "application/vnd.noblenet-web": { + "source": "iana", + "extensions": ["nnw"] + }, + "application/vnd.nokia.catalogs": { + "source": "iana" + }, + "application/vnd.nokia.conml+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.conml+xml": { + "source": "iana" + }, + "application/vnd.nokia.iptv.config+xml": { + "source": "iana" + }, + "application/vnd.nokia.isds-radio-presets": { + "source": "iana" + }, + "application/vnd.nokia.landmark+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.landmark+xml": { + "source": "iana" + }, + "application/vnd.nokia.landmarkcollection+xml": { + "source": "iana" + }, + "application/vnd.nokia.n-gage.ac+xml": { + "source": "iana" + }, + "application/vnd.nokia.n-gage.data": { + "source": "iana", + "extensions": ["ngdat"] + }, + "application/vnd.nokia.n-gage.symbian.install": { + "source": "iana", + "extensions": ["n-gage"] + }, + "application/vnd.nokia.ncd": { + "source": "iana" + }, + "application/vnd.nokia.pcd+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.pcd+xml": { + "source": "iana" + }, + "application/vnd.nokia.radio-preset": { + "source": "iana", + "extensions": ["rpst"] + }, + "application/vnd.nokia.radio-presets": { + "source": "iana", + "extensions": ["rpss"] + }, + "application/vnd.novadigm.edm": { + "source": "iana", + "extensions": ["edm"] + }, + "application/vnd.novadigm.edx": { + "source": "iana", + "extensions": ["edx"] + }, + "application/vnd.novadigm.ext": { + "source": "iana", + "extensions": ["ext"] + }, + "application/vnd.ntt-local.content-share": { + "source": "iana" + }, + "application/vnd.ntt-local.file-transfer": { + "source": "iana" + }, + "application/vnd.ntt-local.ogw_remote-access": { + "source": "iana" + }, + "application/vnd.ntt-local.sip-ta_remote": { + "source": "iana" + }, + "application/vnd.ntt-local.sip-ta_tcp_stream": { + "source": "iana" + }, + "application/vnd.oasis.opendocument.chart": { + "source": "iana", + "extensions": ["odc"] + }, + "application/vnd.oasis.opendocument.chart-template": { + "source": "iana", + "extensions": ["otc"] + }, + "application/vnd.oasis.opendocument.database": { + "source": "iana", + "extensions": ["odb"] + }, + "application/vnd.oasis.opendocument.formula": { + "source": "iana", + "extensions": ["odf"] + }, + "application/vnd.oasis.opendocument.formula-template": { + "source": "iana", + "extensions": ["odft"] + }, + "application/vnd.oasis.opendocument.graphics": { + "source": "iana", + "compressible": false, + "extensions": ["odg"] + }, + "application/vnd.oasis.opendocument.graphics-template": { + "source": "iana", + "extensions": ["otg"] + }, + "application/vnd.oasis.opendocument.image": { + "source": "iana", + "extensions": ["odi"] + }, + "application/vnd.oasis.opendocument.image-template": { + "source": "iana", + "extensions": ["oti"] + }, + "application/vnd.oasis.opendocument.presentation": { + "source": "iana", + "compressible": false, + "extensions": ["odp"] + }, + "application/vnd.oasis.opendocument.presentation-template": { + "source": "iana", + "extensions": ["otp"] + }, + "application/vnd.oasis.opendocument.spreadsheet": { + "source": "iana", + "compressible": false, + "extensions": ["ods"] + }, + "application/vnd.oasis.opendocument.spreadsheet-template": { + "source": "iana", + "extensions": ["ots"] + }, + "application/vnd.oasis.opendocument.text": { + "source": "iana", + "compressible": false, + "extensions": ["odt"] + }, + "application/vnd.oasis.opendocument.text-master": { + "source": "iana", + "extensions": ["odm"] + }, + "application/vnd.oasis.opendocument.text-template": { + "source": "iana", + "extensions": ["ott"] + }, + "application/vnd.oasis.opendocument.text-web": { + "source": "iana", + "extensions": ["oth"] + }, + "application/vnd.obn": { + "source": "iana" + }, + "application/vnd.oftn.l10n+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.oipf.contentaccessdownload+xml": { + "source": "iana" + }, + "application/vnd.oipf.contentaccessstreaming+xml": { + "source": "iana" + }, + "application/vnd.oipf.cspg-hexbinary": { + "source": "iana" + }, + "application/vnd.oipf.dae.svg+xml": { + "source": "iana" + }, + "application/vnd.oipf.dae.xhtml+xml": { + "source": "iana" + }, + "application/vnd.oipf.mippvcontrolmessage+xml": { + "source": "iana" + }, + "application/vnd.oipf.pae.gem": { + "source": "iana" + }, + "application/vnd.oipf.spdiscovery+xml": { + "source": "iana" + }, + "application/vnd.oipf.spdlist+xml": { + "source": "iana" + }, + "application/vnd.oipf.ueprofile+xml": { + "source": "iana" + }, + "application/vnd.oipf.userprofile+xml": { + "source": "iana" + }, + "application/vnd.olpc-sugar": { + "source": "iana", + "extensions": ["xo"] + }, + "application/vnd.oma-scws-config": { + "source": "iana" + }, + "application/vnd.oma-scws-http-request": { + "source": "iana" + }, + "application/vnd.oma-scws-http-response": { + "source": "iana" + }, + "application/vnd.oma.bcast.associated-procedure-parameter+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.drm-trigger+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.imd+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.ltkm": { + "source": "iana" + }, + "application/vnd.oma.bcast.notification+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.provisioningtrigger": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgboot": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgdd+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgdu": { + "source": "iana" + }, + "application/vnd.oma.bcast.simple-symbol-container": { + "source": "iana" + }, + "application/vnd.oma.bcast.smartcard-trigger+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.sprov+xml": { + "source": "iana" + }, + "application/vnd.oma.bcast.stkm": { + "source": "iana" + }, + "application/vnd.oma.cab-address-book+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-feature-handler+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-pcc+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-subs-invite+xml": { + "source": "iana" + }, + "application/vnd.oma.cab-user-prefs+xml": { + "source": "iana" + }, + "application/vnd.oma.dcd": { + "source": "iana" + }, + "application/vnd.oma.dcdc": { + "source": "iana" + }, + "application/vnd.oma.dd2+xml": { + "source": "iana", + "extensions": ["dd2"] + }, + "application/vnd.oma.drm.risd+xml": { + "source": "iana" + }, + "application/vnd.oma.group-usage-list+xml": { + "source": "iana" + }, + "application/vnd.oma.pal+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.detailed-progress-report+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.final-report+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.groups+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.invocation-descriptor+xml": { + "source": "iana" + }, + "application/vnd.oma.poc.optimized-progress-report+xml": { + "source": "iana" + }, + "application/vnd.oma.push": { + "source": "iana" + }, + "application/vnd.oma.scidm.messages+xml": { + "source": "iana" + }, + "application/vnd.oma.xcap-directory+xml": { + "source": "iana" + }, + "application/vnd.omads-email+xml": { + "source": "iana" + }, + "application/vnd.omads-file+xml": { + "source": "iana" + }, + "application/vnd.omads-folder+xml": { + "source": "iana" + }, + "application/vnd.omaloc-supl-init": { + "source": "iana" + }, + "application/vnd.openblox.game+xml": { + "source": "iana" + }, + "application/vnd.openblox.game-binary": { + "source": "iana" + }, + "application/vnd.openeye.oeb": { + "source": "iana" + }, + "application/vnd.openofficeorg.extension": { + "source": "apache", + "extensions": ["oxt"] + }, + "application/vnd.openxmlformats-officedocument.custom-properties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.customxmlproperties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawing+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.chart+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.extended-properties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml-template": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.comments+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.presentation": { + "source": "iana", + "compressible": false, + "extensions": ["pptx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.presprops+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slide": { + "source": "iana", + "extensions": ["sldx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.slide+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideshow": { + "source": "iana", + "extensions": ["ppsx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.tags+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.template": { + "source": "apache", + "extensions": ["potx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml-template": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "source": "iana", + "compressible": false, + "extensions": ["xlsx"] + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.template": { + "source": "apache", + "extensions": ["xltx"] + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.theme+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.themeoverride+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.vmldrawing": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml-template": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document": { + "source": "iana", + "compressible": false, + "extensions": ["docx"] + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.template": { + "source": "apache", + "extensions": ["dotx"] + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-package.core-properties+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml": { + "source": "iana" + }, + "application/vnd.openxmlformats-package.relationships+xml": { + "source": "iana" + }, + "application/vnd.oracle.resource+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.orange.indata": { + "source": "iana" + }, + "application/vnd.osa.netdeploy": { + "source": "iana" + }, + "application/vnd.osgeo.mapguide.package": { + "source": "iana", + "extensions": ["mgp"] + }, + "application/vnd.osgi.bundle": { + "source": "iana" + }, + "application/vnd.osgi.dp": { + "source": "iana", + "extensions": ["dp"] + }, + "application/vnd.osgi.subsystem": { + "source": "iana", + "extensions": ["esa"] + }, + "application/vnd.otps.ct-kip+xml": { + "source": "iana" + }, + "application/vnd.oxli.countgraph": { + "source": "iana" + }, + "application/vnd.pagerduty+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.palm": { + "source": "iana", + "extensions": ["pdb","pqa","oprc"] + }, + "application/vnd.panoply": { + "source": "iana" + }, + "application/vnd.paos+xml": { + "source": "iana" + }, + "application/vnd.paos.xml": { + "source": "apache" + }, + "application/vnd.pawaafile": { + "source": "iana", + "extensions": ["paw"] + }, + "application/vnd.pcos": { + "source": "iana" + }, + "application/vnd.pg.format": { + "source": "iana", + "extensions": ["str"] + }, + "application/vnd.pg.osasli": { + "source": "iana", + "extensions": ["ei6"] + }, + "application/vnd.piaccess.application-licence": { + "source": "iana" + }, + "application/vnd.picsel": { + "source": "iana", + "extensions": ["efif"] + }, + "application/vnd.pmi.widget": { + "source": "iana", + "extensions": ["wg"] + }, + "application/vnd.poc.group-advertisement+xml": { + "source": "iana" + }, + "application/vnd.pocketlearn": { + "source": "iana", + "extensions": ["plf"] + }, + "application/vnd.powerbuilder6": { + "source": "iana", + "extensions": ["pbd"] + }, + "application/vnd.powerbuilder6-s": { + "source": "iana" + }, + "application/vnd.powerbuilder7": { + "source": "iana" + }, + "application/vnd.powerbuilder7-s": { + "source": "iana" + }, + "application/vnd.powerbuilder75": { + "source": "iana" + }, + "application/vnd.powerbuilder75-s": { + "source": "iana" + }, + "application/vnd.preminet": { + "source": "iana" + }, + "application/vnd.previewsystems.box": { + "source": "iana", + "extensions": ["box"] + }, + "application/vnd.proteus.magazine": { + "source": "iana", + "extensions": ["mgz"] + }, + "application/vnd.publishare-delta-tree": { + "source": "iana", + "extensions": ["qps"] + }, + "application/vnd.pvi.ptid1": { + "source": "iana", + "extensions": ["ptid"] + }, + "application/vnd.pwg-multiplexed": { + "source": "iana" + }, + "application/vnd.pwg-xhtml-print+xml": { + "source": "iana" + }, + "application/vnd.qualcomm.brew-app-res": { + "source": "iana" + }, + "application/vnd.quark.quarkxpress": { + "source": "iana", + "extensions": ["qxd","qxt","qwd","qwt","qxl","qxb"] + }, + "application/vnd.quobject-quoxdocument": { + "source": "iana" + }, + "application/vnd.radisys.moml+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-conf+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-conn+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-dialog+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-audit-stream+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-conf+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-base+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-fax-detect+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-fax-sendrecv+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-group+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-speech+xml": { + "source": "iana" + }, + "application/vnd.radisys.msml-dialog-transform+xml": { + "source": "iana" + }, + "application/vnd.rainstor.data": { + "source": "iana" + }, + "application/vnd.rapid": { + "source": "iana" + }, + "application/vnd.realvnc.bed": { + "source": "iana", + "extensions": ["bed"] + }, + "application/vnd.recordare.musicxml": { + "source": "iana", + "extensions": ["mxl"] + }, + "application/vnd.recordare.musicxml+xml": { + "source": "iana", + "extensions": ["musicxml"] + }, + "application/vnd.renlearn.rlprint": { + "source": "iana" + }, + "application/vnd.rig.cryptonote": { + "source": "iana", + "extensions": ["cryptonote"] + }, + "application/vnd.rim.cod": { + "source": "apache", + "extensions": ["cod"] + }, + "application/vnd.rn-realmedia": { + "source": "apache", + "extensions": ["rm"] + }, + "application/vnd.rn-realmedia-vbr": { + "source": "apache", + "extensions": ["rmvb"] + }, + "application/vnd.route66.link66+xml": { + "source": "iana", + "extensions": ["link66"] + }, + "application/vnd.rs-274x": { + "source": "iana" + }, + "application/vnd.ruckus.download": { + "source": "iana" + }, + "application/vnd.s3sms": { + "source": "iana" + }, + "application/vnd.sailingtracker.track": { + "source": "iana", + "extensions": ["st"] + }, + "application/vnd.sbm.cid": { + "source": "iana" + }, + "application/vnd.sbm.mid2": { + "source": "iana" + }, + "application/vnd.scribus": { + "source": "iana" + }, + "application/vnd.sealed.3df": { + "source": "iana" + }, + "application/vnd.sealed.csf": { + "source": "iana" + }, + "application/vnd.sealed.doc": { + "source": "iana" + }, + "application/vnd.sealed.eml": { + "source": "iana" + }, + "application/vnd.sealed.mht": { + "source": "iana" + }, + "application/vnd.sealed.net": { + "source": "iana" + }, + "application/vnd.sealed.ppt": { + "source": "iana" + }, + "application/vnd.sealed.tiff": { + "source": "iana" + }, + "application/vnd.sealed.xls": { + "source": "iana" + }, + "application/vnd.sealedmedia.softseal.html": { + "source": "iana" + }, + "application/vnd.sealedmedia.softseal.pdf": { + "source": "iana" + }, + "application/vnd.seemail": { + "source": "iana", + "extensions": ["see"] + }, + "application/vnd.sema": { + "source": "iana", + "extensions": ["sema"] + }, + "application/vnd.semd": { + "source": "iana", + "extensions": ["semd"] + }, + "application/vnd.semf": { + "source": "iana", + "extensions": ["semf"] + }, + "application/vnd.shana.informed.formdata": { + "source": "iana", + "extensions": ["ifm"] + }, + "application/vnd.shana.informed.formtemplate": { + "source": "iana", + "extensions": ["itp"] + }, + "application/vnd.shana.informed.interchange": { + "source": "iana", + "extensions": ["iif"] + }, + "application/vnd.shana.informed.package": { + "source": "iana", + "extensions": ["ipk"] + }, + "application/vnd.simtech-mindmapper": { + "source": "iana", + "extensions": ["twd","twds"] + }, + "application/vnd.siren+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.smaf": { + "source": "iana", + "extensions": ["mmf"] + }, + "application/vnd.smart.notebook": { + "source": "iana" + }, + "application/vnd.smart.teacher": { + "source": "iana", + "extensions": ["teacher"] + }, + "application/vnd.software602.filler.form+xml": { + "source": "iana" + }, + "application/vnd.software602.filler.form-xml-zip": { + "source": "iana" + }, + "application/vnd.solent.sdkm+xml": { + "source": "iana", + "extensions": ["sdkm","sdkd"] + }, + "application/vnd.spotfire.dxp": { + "source": "iana", + "extensions": ["dxp"] + }, + "application/vnd.spotfire.sfs": { + "source": "iana", + "extensions": ["sfs"] + }, + "application/vnd.sss-cod": { + "source": "iana" + }, + "application/vnd.sss-dtf": { + "source": "iana" + }, + "application/vnd.sss-ntf": { + "source": "iana" + }, + "application/vnd.stardivision.calc": { + "source": "apache", + "extensions": ["sdc"] + }, + "application/vnd.stardivision.draw": { + "source": "apache", + "extensions": ["sda"] + }, + "application/vnd.stardivision.impress": { + "source": "apache", + "extensions": ["sdd"] + }, + "application/vnd.stardivision.math": { + "source": "apache", + "extensions": ["smf"] + }, + "application/vnd.stardivision.writer": { + "source": "apache", + "extensions": ["sdw","vor"] + }, + "application/vnd.stardivision.writer-global": { + "source": "apache", + "extensions": ["sgl"] + }, + "application/vnd.stepmania.package": { + "source": "iana", + "extensions": ["smzip"] + }, + "application/vnd.stepmania.stepchart": { + "source": "iana", + "extensions": ["sm"] + }, + "application/vnd.street-stream": { + "source": "iana" + }, + "application/vnd.sun.wadl+xml": { + "source": "iana" + }, + "application/vnd.sun.xml.calc": { + "source": "apache", + "extensions": ["sxc"] + }, + "application/vnd.sun.xml.calc.template": { + "source": "apache", + "extensions": ["stc"] + }, + "application/vnd.sun.xml.draw": { + "source": "apache", + "extensions": ["sxd"] + }, + "application/vnd.sun.xml.draw.template": { + "source": "apache", + "extensions": ["std"] + }, + "application/vnd.sun.xml.impress": { + "source": "apache", + "extensions": ["sxi"] + }, + "application/vnd.sun.xml.impress.template": { + "source": "apache", + "extensions": ["sti"] + }, + "application/vnd.sun.xml.math": { + "source": "apache", + "extensions": ["sxm"] + }, + "application/vnd.sun.xml.writer": { + "source": "apache", + "extensions": ["sxw"] + }, + "application/vnd.sun.xml.writer.global": { + "source": "apache", + "extensions": ["sxg"] + }, + "application/vnd.sun.xml.writer.template": { + "source": "apache", + "extensions": ["stw"] + }, + "application/vnd.sus-calendar": { + "source": "iana", + "extensions": ["sus","susp"] + }, + "application/vnd.svd": { + "source": "iana", + "extensions": ["svd"] + }, + "application/vnd.swiftview-ics": { + "source": "iana" + }, + "application/vnd.symbian.install": { + "source": "apache", + "extensions": ["sis","sisx"] + }, + "application/vnd.syncml+xml": { + "source": "iana", + "extensions": ["xsm"] + }, + "application/vnd.syncml.dm+wbxml": { + "source": "iana", + "extensions": ["bdm"] + }, + "application/vnd.syncml.dm+xml": { + "source": "iana", + "extensions": ["xdm"] + }, + "application/vnd.syncml.dm.notification": { + "source": "iana" + }, + "application/vnd.syncml.dmddf+wbxml": { + "source": "iana" + }, + "application/vnd.syncml.dmddf+xml": { + "source": "iana" + }, + "application/vnd.syncml.dmtnds+wbxml": { + "source": "iana" + }, + "application/vnd.syncml.dmtnds+xml": { + "source": "iana" + }, + "application/vnd.syncml.ds.notification": { + "source": "iana" + }, + "application/vnd.tao.intent-module-archive": { + "source": "iana", + "extensions": ["tao"] + }, + "application/vnd.tcpdump.pcap": { + "source": "iana", + "extensions": ["pcap","cap","dmp"] + }, + "application/vnd.tmd.mediaflex.api+xml": { + "source": "iana" + }, + "application/vnd.tmobile-livetv": { + "source": "iana", + "extensions": ["tmo"] + }, + "application/vnd.trid.tpt": { + "source": "iana", + "extensions": ["tpt"] + }, + "application/vnd.triscape.mxs": { + "source": "iana", + "extensions": ["mxs"] + }, + "application/vnd.trueapp": { + "source": "iana", + "extensions": ["tra"] + }, + "application/vnd.truedoc": { + "source": "iana" + }, + "application/vnd.ubisoft.webplayer": { + "source": "iana" + }, + "application/vnd.ufdl": { + "source": "iana", + "extensions": ["ufd","ufdl"] + }, + "application/vnd.uiq.theme": { + "source": "iana", + "extensions": ["utz"] + }, + "application/vnd.umajin": { + "source": "iana", + "extensions": ["umj"] + }, + "application/vnd.unity": { + "source": "iana", + "extensions": ["unityweb"] + }, + "application/vnd.uoml+xml": { + "source": "iana", + "extensions": ["uoml"] + }, + "application/vnd.uplanet.alert": { + "source": "iana" + }, + "application/vnd.uplanet.alert-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.bearer-choice": { + "source": "iana" + }, + "application/vnd.uplanet.bearer-choice-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.cacheop": { + "source": "iana" + }, + "application/vnd.uplanet.cacheop-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.channel": { + "source": "iana" + }, + "application/vnd.uplanet.channel-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.list": { + "source": "iana" + }, + "application/vnd.uplanet.list-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.listcmd": { + "source": "iana" + }, + "application/vnd.uplanet.listcmd-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.signal": { + "source": "iana" + }, + "application/vnd.uri-map": { + "source": "iana" + }, + "application/vnd.valve.source.material": { + "source": "iana" + }, + "application/vnd.vcx": { + "source": "iana", + "extensions": ["vcx"] + }, + "application/vnd.vd-study": { + "source": "iana" + }, + "application/vnd.vectorworks": { + "source": "iana" + }, + "application/vnd.verimatrix.vcas": { + "source": "iana" + }, + "application/vnd.vidsoft.vidconference": { + "source": "iana" + }, + "application/vnd.visio": { + "source": "iana", + "extensions": ["vsd","vst","vss","vsw"] + }, + "application/vnd.visionary": { + "source": "iana", + "extensions": ["vis"] + }, + "application/vnd.vividence.scriptfile": { + "source": "iana" + }, + "application/vnd.vsf": { + "source": "iana", + "extensions": ["vsf"] + }, + "application/vnd.wap.sic": { + "source": "iana" + }, + "application/vnd.wap.slc": { + "source": "iana" + }, + "application/vnd.wap.wbxml": { + "source": "iana", + "extensions": ["wbxml"] + }, + "application/vnd.wap.wmlc": { + "source": "iana", + "extensions": ["wmlc"] + }, + "application/vnd.wap.wmlscriptc": { + "source": "iana", + "extensions": ["wmlsc"] + }, + "application/vnd.webturbo": { + "source": "iana", + "extensions": ["wtb"] + }, + "application/vnd.wfa.p2p": { + "source": "iana" + }, + "application/vnd.wfa.wsc": { + "source": "iana" + }, + "application/vnd.windows.devicepairing": { + "source": "iana" + }, + "application/vnd.wmc": { + "source": "iana" + }, + "application/vnd.wmf.bootstrap": { + "source": "iana" + }, + "application/vnd.wolfram.mathematica": { + "source": "iana" + }, + "application/vnd.wolfram.mathematica.package": { + "source": "iana" + }, + "application/vnd.wolfram.player": { + "source": "iana", + "extensions": ["nbp"] + }, + "application/vnd.wordperfect": { + "source": "iana", + "extensions": ["wpd"] + }, + "application/vnd.wqd": { + "source": "iana", + "extensions": ["wqd"] + }, + "application/vnd.wrq-hp3000-labelled": { + "source": "iana" + }, + "application/vnd.wt.stf": { + "source": "iana", + "extensions": ["stf"] + }, + "application/vnd.wv.csp+wbxml": { + "source": "iana" + }, + "application/vnd.wv.csp+xml": { + "source": "iana" + }, + "application/vnd.wv.ssp+xml": { + "source": "iana" + }, + "application/vnd.xacml+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.xara": { + "source": "iana", + "extensions": ["xar"] + }, + "application/vnd.xfdl": { + "source": "iana", + "extensions": ["xfdl"] + }, + "application/vnd.xfdl.webform": { + "source": "iana" + }, + "application/vnd.xmi+xml": { + "source": "iana" + }, + "application/vnd.xmpie.cpkg": { + "source": "iana" + }, + "application/vnd.xmpie.dpkg": { + "source": "iana" + }, + "application/vnd.xmpie.plan": { + "source": "iana" + }, + "application/vnd.xmpie.ppkg": { + "source": "iana" + }, + "application/vnd.xmpie.xlim": { + "source": "iana" + }, + "application/vnd.yamaha.hv-dic": { + "source": "iana", + "extensions": ["hvd"] + }, + "application/vnd.yamaha.hv-script": { + "source": "iana", + "extensions": ["hvs"] + }, + "application/vnd.yamaha.hv-voice": { + "source": "iana", + "extensions": ["hvp"] + }, + "application/vnd.yamaha.openscoreformat": { + "source": "iana", + "extensions": ["osf"] + }, + "application/vnd.yamaha.openscoreformat.osfpvg+xml": { + "source": "iana", + "extensions": ["osfpvg"] + }, + "application/vnd.yamaha.remote-setup": { + "source": "iana" + }, + "application/vnd.yamaha.smaf-audio": { + "source": "iana", + "extensions": ["saf"] + }, + "application/vnd.yamaha.smaf-phrase": { + "source": "iana", + "extensions": ["spf"] + }, + "application/vnd.yamaha.through-ngn": { + "source": "iana" + }, + "application/vnd.yamaha.tunnel-udpencap": { + "source": "iana" + }, + "application/vnd.yaoweme": { + "source": "iana" + }, + "application/vnd.yellowriver-custom-menu": { + "source": "iana", + "extensions": ["cmp"] + }, + "application/vnd.zul": { + "source": "iana", + "extensions": ["zir","zirz"] + }, + "application/vnd.zzazz.deck+xml": { + "source": "iana", + "extensions": ["zaz"] + }, + "application/voicexml+xml": { + "source": "iana", + "extensions": ["vxml"] + }, + "application/vq-rtcpxr": { + "source": "iana" + }, + "application/watcherinfo+xml": { + "source": "iana" + }, + "application/whoispp-query": { + "source": "iana" + }, + "application/whoispp-response": { + "source": "iana" + }, + "application/widget": { + "source": "iana", + "extensions": ["wgt"] + }, + "application/winhlp": { + "source": "apache", + "extensions": ["hlp"] + }, + "application/wita": { + "source": "iana" + }, + "application/wordperfect5.1": { + "source": "iana" + }, + "application/wsdl+xml": { + "source": "iana", + "extensions": ["wsdl"] + }, + "application/wspolicy+xml": { + "source": "iana", + "extensions": ["wspolicy"] + }, + "application/x-7z-compressed": { + "source": "apache", + "compressible": false, + "extensions": ["7z"] + }, + "application/x-abiword": { + "source": "apache", + "extensions": ["abw"] + }, + "application/x-ace-compressed": { + "source": "apache", + "extensions": ["ace"] + }, + "application/x-amf": { + "source": "apache" + }, + "application/x-apple-diskimage": { + "source": "apache", + "extensions": ["dmg"] + }, + "application/x-authorware-bin": { + "source": "apache", + "extensions": ["aab","x32","u32","vox"] + }, + "application/x-authorware-map": { + "source": "apache", + "extensions": ["aam"] + }, + "application/x-authorware-seg": { + "source": "apache", + "extensions": ["aas"] + }, + "application/x-bcpio": { + "source": "apache", + "extensions": ["bcpio"] + }, + "application/x-bdoc": { + "compressible": false, + "extensions": ["bdoc"] + }, + "application/x-bittorrent": { + "source": "apache", + "extensions": ["torrent"] + }, + "application/x-blorb": { + "source": "apache", + "extensions": ["blb","blorb"] + }, + "application/x-bzip": { + "source": "apache", + "compressible": false, + "extensions": ["bz"] + }, + "application/x-bzip2": { + "source": "apache", + "compressible": false, + "extensions": ["bz2","boz"] + }, + "application/x-cbr": { + "source": "apache", + "extensions": ["cbr","cba","cbt","cbz","cb7"] + }, + "application/x-cdlink": { + "source": "apache", + "extensions": ["vcd"] + }, + "application/x-cfs-compressed": { + "source": "apache", + "extensions": ["cfs"] + }, + "application/x-chat": { + "source": "apache", + "extensions": ["chat"] + }, + "application/x-chess-pgn": { + "source": "apache", + "extensions": ["pgn"] + }, + "application/x-chrome-extension": { + "extensions": ["crx"] + }, + "application/x-cocoa": { + "source": "nginx", + "extensions": ["cco"] + }, + "application/x-compress": { + "source": "apache" + }, + "application/x-conference": { + "source": "apache", + "extensions": ["nsc"] + }, + "application/x-cpio": { + "source": "apache", + "extensions": ["cpio"] + }, + "application/x-csh": { + "source": "apache", + "extensions": ["csh"] + }, + "application/x-deb": { + "compressible": false + }, + "application/x-debian-package": { + "source": "apache", + "extensions": ["deb","udeb"] + }, + "application/x-dgc-compressed": { + "source": "apache", + "extensions": ["dgc"] + }, + "application/x-director": { + "source": "apache", + "extensions": ["dir","dcr","dxr","cst","cct","cxt","w3d","fgd","swa"] + }, + "application/x-doom": { + "source": "apache", + "extensions": ["wad"] + }, + "application/x-dtbncx+xml": { + "source": "apache", + "extensions": ["ncx"] + }, + "application/x-dtbook+xml": { + "source": "apache", + "extensions": ["dtb"] + }, + "application/x-dtbresource+xml": { + "source": "apache", + "extensions": ["res"] + }, + "application/x-dvi": { + "source": "apache", + "compressible": false, + "extensions": ["dvi"] + }, + "application/x-envoy": { + "source": "apache", + "extensions": ["evy"] + }, + "application/x-eva": { + "source": "apache", + "extensions": ["eva"] + }, + "application/x-font-bdf": { + "source": "apache", + "extensions": ["bdf"] + }, + "application/x-font-dos": { + "source": "apache" + }, + "application/x-font-framemaker": { + "source": "apache" + }, + "application/x-font-ghostscript": { + "source": "apache", + "extensions": ["gsf"] + }, + "application/x-font-libgrx": { + "source": "apache" + }, + "application/x-font-linux-psf": { + "source": "apache", + "extensions": ["psf"] + }, + "application/x-font-otf": { + "source": "apache", + "compressible": true, + "extensions": ["otf"] + }, + "application/x-font-pcf": { + "source": "apache", + "extensions": ["pcf"] + }, + "application/x-font-snf": { + "source": "apache", + "extensions": ["snf"] + }, + "application/x-font-speedo": { + "source": "apache" + }, + "application/x-font-sunos-news": { + "source": "apache" + }, + "application/x-font-ttf": { + "source": "apache", + "compressible": true, + "extensions": ["ttf","ttc"] + }, + "application/x-font-type1": { + "source": "apache", + "extensions": ["pfa","pfb","pfm","afm"] + }, + "application/x-font-vfont": { + "source": "apache" + }, + "application/x-freearc": { + "source": "apache", + "extensions": ["arc"] + }, + "application/x-futuresplash": { + "source": "apache", + "extensions": ["spl"] + }, + "application/x-gca-compressed": { + "source": "apache", + "extensions": ["gca"] + }, + "application/x-glulx": { + "source": "apache", + "extensions": ["ulx"] + }, + "application/x-gnumeric": { + "source": "apache", + "extensions": ["gnumeric"] + }, + "application/x-gramps-xml": { + "source": "apache", + "extensions": ["gramps"] + }, + "application/x-gtar": { + "source": "apache", + "extensions": ["gtar"] + }, + "application/x-gzip": { + "source": "apache" + }, + "application/x-hdf": { + "source": "apache", + "extensions": ["hdf"] + }, + "application/x-httpd-php": { + "compressible": true, + "extensions": ["php"] + }, + "application/x-install-instructions": { + "source": "apache", + "extensions": ["install"] + }, + "application/x-iso9660-image": { + "source": "apache", + "extensions": ["iso"] + }, + "application/x-java-archive-diff": { + "source": "nginx", + "extensions": ["jardiff"] + }, + "application/x-java-jnlp-file": { + "source": "apache", + "compressible": false, + "extensions": ["jnlp"] + }, + "application/x-javascript": { + "compressible": true + }, + "application/x-latex": { + "source": "apache", + "compressible": false, + "extensions": ["latex"] + }, + "application/x-lua-bytecode": { + "extensions": ["luac"] + }, + "application/x-lzh-compressed": { + "source": "apache", + "extensions": ["lzh","lha"] + }, + "application/x-makeself": { + "source": "nginx", + "extensions": ["run"] + }, + "application/x-mie": { + "source": "apache", + "extensions": ["mie"] + }, + "application/x-mobipocket-ebook": { + "source": "apache", + "extensions": ["prc","mobi"] + }, + "application/x-mpegurl": { + "compressible": false + }, + "application/x-ms-application": { + "source": "apache", + "extensions": ["application"] + }, + "application/x-ms-shortcut": { + "source": "apache", + "extensions": ["lnk"] + }, + "application/x-ms-wmd": { + "source": "apache", + "extensions": ["wmd"] + }, + "application/x-ms-wmz": { + "source": "apache", + "extensions": ["wmz"] + }, + "application/x-ms-xbap": { + "source": "apache", + "extensions": ["xbap"] + }, + "application/x-msaccess": { + "source": "apache", + "extensions": ["mdb"] + }, + "application/x-msbinder": { + "source": "apache", + "extensions": ["obd"] + }, + "application/x-mscardfile": { + "source": "apache", + "extensions": ["crd"] + }, + "application/x-msclip": { + "source": "apache", + "extensions": ["clp"] + }, + "application/x-msdos-program": { + "extensions": ["exe"] + }, + "application/x-msdownload": { + "source": "apache", + "extensions": ["exe","dll","com","bat","msi"] + }, + "application/x-msmediaview": { + "source": "apache", + "extensions": ["mvb","m13","m14"] + }, + "application/x-msmetafile": { + "source": "apache", + "extensions": ["wmf","wmz","emf","emz"] + }, + "application/x-msmoney": { + "source": "apache", + "extensions": ["mny"] + }, + "application/x-mspublisher": { + "source": "apache", + "extensions": ["pub"] + }, + "application/x-msschedule": { + "source": "apache", + "extensions": ["scd"] + }, + "application/x-msterminal": { + "source": "apache", + "extensions": ["trm"] + }, + "application/x-mswrite": { + "source": "apache", + "extensions": ["wri"] + }, + "application/x-netcdf": { + "source": "apache", + "extensions": ["nc","cdf"] + }, + "application/x-ns-proxy-autoconfig": { + "compressible": true, + "extensions": ["pac"] + }, + "application/x-nzb": { + "source": "apache", + "extensions": ["nzb"] + }, + "application/x-perl": { + "source": "nginx", + "extensions": ["pl","pm"] + }, + "application/x-pilot": { + "source": "nginx", + "extensions": ["prc","pdb"] + }, + "application/x-pkcs12": { + "source": "apache", + "compressible": false, + "extensions": ["p12","pfx"] + }, + "application/x-pkcs7-certificates": { + "source": "apache", + "extensions": ["p7b","spc"] + }, + "application/x-pkcs7-certreqresp": { + "source": "apache", + "extensions": ["p7r"] + }, + "application/x-rar-compressed": { + "source": "apache", + "compressible": false, + "extensions": ["rar"] + }, + "application/x-redhat-package-manager": { + "source": "nginx", + "extensions": ["rpm"] + }, + "application/x-research-info-systems": { + "source": "apache", + "extensions": ["ris"] + }, + "application/x-sea": { + "source": "nginx", + "extensions": ["sea"] + }, + "application/x-sh": { + "source": "apache", + "compressible": true, + "extensions": ["sh"] + }, + "application/x-shar": { + "source": "apache", + "extensions": ["shar"] + }, + "application/x-shockwave-flash": { + "source": "apache", + "compressible": false, + "extensions": ["swf"] + }, + "application/x-silverlight-app": { + "source": "apache", + "extensions": ["xap"] + }, + "application/x-sql": { + "source": "apache", + "extensions": ["sql"] + }, + "application/x-stuffit": { + "source": "apache", + "compressible": false, + "extensions": ["sit"] + }, + "application/x-stuffitx": { + "source": "apache", + "extensions": ["sitx"] + }, + "application/x-subrip": { + "source": "apache", + "extensions": ["srt"] + }, + "application/x-sv4cpio": { + "source": "apache", + "extensions": ["sv4cpio"] + }, + "application/x-sv4crc": { + "source": "apache", + "extensions": ["sv4crc"] + }, + "application/x-t3vm-image": { + "source": "apache", + "extensions": ["t3"] + }, + "application/x-tads": { + "source": "apache", + "extensions": ["gam"] + }, + "application/x-tar": { + "source": "apache", + "compressible": true, + "extensions": ["tar"] + }, + "application/x-tcl": { + "source": "apache", + "extensions": ["tcl","tk"] + }, + "application/x-tex": { + "source": "apache", + "extensions": ["tex"] + }, + "application/x-tex-tfm": { + "source": "apache", + "extensions": ["tfm"] + }, + "application/x-texinfo": { + "source": "apache", + "extensions": ["texinfo","texi"] + }, + "application/x-tgif": { + "source": "apache", + "extensions": ["obj"] + }, + "application/x-ustar": { + "source": "apache", + "extensions": ["ustar"] + }, + "application/x-wais-source": { + "source": "apache", + "extensions": ["src"] + }, + "application/x-web-app-manifest+json": { + "compressible": true, + "extensions": ["webapp"] + }, + "application/x-www-form-urlencoded": { + "source": "iana", + "compressible": true + }, + "application/x-x509-ca-cert": { + "source": "apache", + "extensions": ["der","crt","pem"] + }, + "application/x-xfig": { + "source": "apache", + "extensions": ["fig"] + }, + "application/x-xliff+xml": { + "source": "apache", + "extensions": ["xlf"] + }, + "application/x-xpinstall": { + "source": "apache", + "compressible": false, + "extensions": ["xpi"] + }, + "application/x-xz": { + "source": "apache", + "extensions": ["xz"] + }, + "application/x-zmachine": { + "source": "apache", + "extensions": ["z1","z2","z3","z4","z5","z6","z7","z8"] + }, + "application/x400-bp": { + "source": "iana" + }, + "application/xacml+xml": { + "source": "iana" + }, + "application/xaml+xml": { + "source": "apache", + "extensions": ["xaml"] + }, + "application/xcap-att+xml": { + "source": "iana" + }, + "application/xcap-caps+xml": { + "source": "iana" + }, + "application/xcap-diff+xml": { + "source": "iana", + "extensions": ["xdf"] + }, + "application/xcap-el+xml": { + "source": "iana" + }, + "application/xcap-error+xml": { + "source": "iana" + }, + "application/xcap-ns+xml": { + "source": "iana" + }, + "application/xcon-conference-info+xml": { + "source": "iana" + }, + "application/xcon-conference-info-diff+xml": { + "source": "iana" + }, + "application/xenc+xml": { + "source": "iana", + "extensions": ["xenc"] + }, + "application/xhtml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xhtml","xht"] + }, + "application/xhtml-voice+xml": { + "source": "apache" + }, + "application/xml": { + "source": "iana", + "compressible": true, + "extensions": ["xml","xsl","xsd"] + }, + "application/xml-dtd": { + "source": "iana", + "compressible": true, + "extensions": ["dtd"] + }, + "application/xml-external-parsed-entity": { + "source": "iana" + }, + "application/xml-patch+xml": { + "source": "iana" + }, + "application/xmpp+xml": { + "source": "iana" + }, + "application/xop+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xop"] + }, + "application/xproc+xml": { + "source": "apache", + "extensions": ["xpl"] + }, + "application/xslt+xml": { + "source": "iana", + "extensions": ["xslt"] + }, + "application/xspf+xml": { + "source": "apache", + "extensions": ["xspf"] + }, + "application/xv+xml": { + "source": "iana", + "extensions": ["mxml","xhvml","xvml","xvm"] + }, + "application/yang": { + "source": "iana", + "extensions": ["yang"] + }, + "application/yin+xml": { + "source": "iana", + "extensions": ["yin"] + }, + "application/zip": { + "source": "iana", + "compressible": false, + "extensions": ["zip"] + }, + "application/zlib": { + "source": "iana" + }, + "audio/1d-interleaved-parityfec": { + "source": "iana" + }, + "audio/32kadpcm": { + "source": "iana" + }, + "audio/3gpp": { + "source": "iana" + }, + "audio/3gpp2": { + "source": "iana" + }, + "audio/ac3": { + "source": "iana" + }, + "audio/adpcm": { + "source": "apache", + "extensions": ["adp"] + }, + "audio/amr": { + "source": "iana" + }, + "audio/amr-wb": { + "source": "iana" + }, + "audio/amr-wb+": { + "source": "iana" + }, + "audio/aptx": { + "source": "iana" + }, + "audio/asc": { + "source": "iana" + }, + "audio/atrac-advanced-lossless": { + "source": "iana" + }, + "audio/atrac-x": { + "source": "iana" + }, + "audio/atrac3": { + "source": "iana" + }, + "audio/basic": { + "source": "iana", + "compressible": false, + "extensions": ["au","snd"] + }, + "audio/bv16": { + "source": "iana" + }, + "audio/bv32": { + "source": "iana" + }, + "audio/clearmode": { + "source": "iana" + }, + "audio/cn": { + "source": "iana" + }, + "audio/dat12": { + "source": "iana" + }, + "audio/dls": { + "source": "iana" + }, + "audio/dsr-es201108": { + "source": "iana" + }, + "audio/dsr-es202050": { + "source": "iana" + }, + "audio/dsr-es202211": { + "source": "iana" + }, + "audio/dsr-es202212": { + "source": "iana" + }, + "audio/dv": { + "source": "iana" + }, + "audio/dvi4": { + "source": "iana" + }, + "audio/eac3": { + "source": "iana" + }, + "audio/encaprtp": { + "source": "iana" + }, + "audio/evrc": { + "source": "iana" + }, + "audio/evrc-qcp": { + "source": "iana" + }, + "audio/evrc0": { + "source": "iana" + }, + "audio/evrc1": { + "source": "iana" + }, + "audio/evrcb": { + "source": "iana" + }, + "audio/evrcb0": { + "source": "iana" + }, + "audio/evrcb1": { + "source": "iana" + }, + "audio/evrcnw": { + "source": "iana" + }, + "audio/evrcnw0": { + "source": "iana" + }, + "audio/evrcnw1": { + "source": "iana" + }, + "audio/evrcwb": { + "source": "iana" + }, + "audio/evrcwb0": { + "source": "iana" + }, + "audio/evrcwb1": { + "source": "iana" + }, + "audio/fwdred": { + "source": "iana" + }, + "audio/g711-0": { + "source": "iana" + }, + "audio/g719": { + "source": "iana" + }, + "audio/g722": { + "source": "iana" + }, + "audio/g7221": { + "source": "iana" + }, + "audio/g723": { + "source": "iana" + }, + "audio/g726-16": { + "source": "iana" + }, + "audio/g726-24": { + "source": "iana" + }, + "audio/g726-32": { + "source": "iana" + }, + "audio/g726-40": { + "source": "iana" + }, + "audio/g728": { + "source": "iana" + }, + "audio/g729": { + "source": "iana" + }, + "audio/g7291": { + "source": "iana" + }, + "audio/g729d": { + "source": "iana" + }, + "audio/g729e": { + "source": "iana" + }, + "audio/gsm": { + "source": "iana" + }, + "audio/gsm-efr": { + "source": "iana" + }, + "audio/gsm-hr-08": { + "source": "iana" + }, + "audio/ilbc": { + "source": "iana" + }, + "audio/ip-mr_v2.5": { + "source": "iana" + }, + "audio/isac": { + "source": "apache" + }, + "audio/l16": { + "source": "iana" + }, + "audio/l20": { + "source": "iana" + }, + "audio/l24": { + "source": "iana", + "compressible": false + }, + "audio/l8": { + "source": "iana" + }, + "audio/lpc": { + "source": "iana" + }, + "audio/midi": { + "source": "apache", + "extensions": ["mid","midi","kar","rmi"] + }, + "audio/mobile-xmf": { + "source": "iana" + }, + "audio/mp4": { + "source": "iana", + "compressible": false, + "extensions": ["mp4a","m4a"] + }, + "audio/mp4a-latm": { + "source": "iana" + }, + "audio/mpa": { + "source": "iana" + }, + "audio/mpa-robust": { + "source": "iana" + }, + "audio/mpeg": { + "source": "iana", + "compressible": false, + "extensions": ["mpga","mp2","mp2a","mp3","m2a","m3a"] + }, + "audio/mpeg4-generic": { + "source": "iana" + }, + "audio/musepack": { + "source": "apache" + }, + "audio/ogg": { + "source": "iana", + "compressible": false, + "extensions": ["oga","ogg","spx"] + }, + "audio/opus": { + "source": "iana" + }, + "audio/parityfec": { + "source": "iana" + }, + "audio/pcma": { + "source": "iana" + }, + "audio/pcma-wb": { + "source": "iana" + }, + "audio/pcmu": { + "source": "iana" + }, + "audio/pcmu-wb": { + "source": "iana" + }, + "audio/prs.sid": { + "source": "iana" + }, + "audio/qcelp": { + "source": "iana" + }, + "audio/raptorfec": { + "source": "iana" + }, + "audio/red": { + "source": "iana" + }, + "audio/rtp-enc-aescm128": { + "source": "iana" + }, + "audio/rtp-midi": { + "source": "iana" + }, + "audio/rtploopback": { + "source": "iana" + }, + "audio/rtx": { + "source": "iana" + }, + "audio/s3m": { + "source": "apache", + "extensions": ["s3m"] + }, + "audio/silk": { + "source": "apache", + "extensions": ["sil"] + }, + "audio/smv": { + "source": "iana" + }, + "audio/smv-qcp": { + "source": "iana" + }, + "audio/smv0": { + "source": "iana" + }, + "audio/sp-midi": { + "source": "iana" + }, + "audio/speex": { + "source": "iana" + }, + "audio/t140c": { + "source": "iana" + }, + "audio/t38": { + "source": "iana" + }, + "audio/telephone-event": { + "source": "iana" + }, + "audio/tone": { + "source": "iana" + }, + "audio/uemclip": { + "source": "iana" + }, + "audio/ulpfec": { + "source": "iana" + }, + "audio/vdvi": { + "source": "iana" + }, + "audio/vmr-wb": { + "source": "iana" + }, + "audio/vnd.3gpp.iufp": { + "source": "iana" + }, + "audio/vnd.4sb": { + "source": "iana" + }, + "audio/vnd.audiokoz": { + "source": "iana" + }, + "audio/vnd.celp": { + "source": "iana" + }, + "audio/vnd.cisco.nse": { + "source": "iana" + }, + "audio/vnd.cmles.radio-events": { + "source": "iana" + }, + "audio/vnd.cns.anp1": { + "source": "iana" + }, + "audio/vnd.cns.inf1": { + "source": "iana" + }, + "audio/vnd.dece.audio": { + "source": "iana", + "extensions": ["uva","uvva"] + }, + "audio/vnd.digital-winds": { + "source": "iana", + "extensions": ["eol"] + }, + "audio/vnd.dlna.adts": { + "source": "iana" + }, + "audio/vnd.dolby.heaac.1": { + "source": "iana" + }, + "audio/vnd.dolby.heaac.2": { + "source": "iana" + }, + "audio/vnd.dolby.mlp": { + "source": "iana" + }, + "audio/vnd.dolby.mps": { + "source": "iana" + }, + "audio/vnd.dolby.pl2": { + "source": "iana" + }, + "audio/vnd.dolby.pl2x": { + "source": "iana" + }, + "audio/vnd.dolby.pl2z": { + "source": "iana" + }, + "audio/vnd.dolby.pulse.1": { + "source": "iana" + }, + "audio/vnd.dra": { + "source": "iana", + "extensions": ["dra"] + }, + "audio/vnd.dts": { + "source": "iana", + "extensions": ["dts"] + }, + "audio/vnd.dts.hd": { + "source": "iana", + "extensions": ["dtshd"] + }, + "audio/vnd.dvb.file": { + "source": "iana" + }, + "audio/vnd.everad.plj": { + "source": "iana" + }, + "audio/vnd.hns.audio": { + "source": "iana" + }, + "audio/vnd.lucent.voice": { + "source": "iana", + "extensions": ["lvp"] + }, + "audio/vnd.ms-playready.media.pya": { + "source": "iana", + "extensions": ["pya"] + }, + "audio/vnd.nokia.mobile-xmf": { + "source": "iana" + }, + "audio/vnd.nortel.vbk": { + "source": "iana" + }, + "audio/vnd.nuera.ecelp4800": { + "source": "iana", + "extensions": ["ecelp4800"] + }, + "audio/vnd.nuera.ecelp7470": { + "source": "iana", + "extensions": ["ecelp7470"] + }, + "audio/vnd.nuera.ecelp9600": { + "source": "iana", + "extensions": ["ecelp9600"] + }, + "audio/vnd.octel.sbc": { + "source": "iana" + }, + "audio/vnd.qcelp": { + "source": "iana" + }, + "audio/vnd.rhetorex.32kadpcm": { + "source": "iana" + }, + "audio/vnd.rip": { + "source": "iana", + "extensions": ["rip"] + }, + "audio/vnd.rn-realaudio": { + "compressible": false + }, + "audio/vnd.sealedmedia.softseal.mpeg": { + "source": "iana" + }, + "audio/vnd.vmx.cvsd": { + "source": "iana" + }, + "audio/vnd.wave": { + "compressible": false + }, + "audio/vorbis": { + "source": "iana", + "compressible": false + }, + "audio/vorbis-config": { + "source": "iana" + }, + "audio/wav": { + "compressible": false, + "extensions": ["wav"] + }, + "audio/wave": { + "compressible": false, + "extensions": ["wav"] + }, + "audio/webm": { + "source": "apache", + "compressible": false, + "extensions": ["weba"] + }, + "audio/x-aac": { + "source": "apache", + "compressible": false, + "extensions": ["aac"] + }, + "audio/x-aiff": { + "source": "apache", + "extensions": ["aif","aiff","aifc"] + }, + "audio/x-caf": { + "source": "apache", + "compressible": false, + "extensions": ["caf"] + }, + "audio/x-flac": { + "source": "apache", + "extensions": ["flac"] + }, + "audio/x-m4a": { + "source": "nginx", + "extensions": ["m4a"] + }, + "audio/x-matroska": { + "source": "apache", + "extensions": ["mka"] + }, + "audio/x-mpegurl": { + "source": "apache", + "extensions": ["m3u"] + }, + "audio/x-ms-wax": { + "source": "apache", + "extensions": ["wax"] + }, + "audio/x-ms-wma": { + "source": "apache", + "extensions": ["wma"] + }, + "audio/x-pn-realaudio": { + "source": "apache", + "extensions": ["ram","ra"] + }, + "audio/x-pn-realaudio-plugin": { + "source": "apache", + "extensions": ["rmp"] + }, + "audio/x-realaudio": { + "source": "nginx", + "extensions": ["ra"] + }, + "audio/x-tta": { + "source": "apache" + }, + "audio/x-wav": { + "source": "apache", + "extensions": ["wav"] + }, + "audio/xm": { + "source": "apache", + "extensions": ["xm"] + }, + "chemical/x-cdx": { + "source": "apache", + "extensions": ["cdx"] + }, + "chemical/x-cif": { + "source": "apache", + "extensions": ["cif"] + }, + "chemical/x-cmdf": { + "source": "apache", + "extensions": ["cmdf"] + }, + "chemical/x-cml": { + "source": "apache", + "extensions": ["cml"] + }, + "chemical/x-csml": { + "source": "apache", + "extensions": ["csml"] + }, + "chemical/x-pdb": { + "source": "apache" + }, + "chemical/x-xyz": { + "source": "apache", + "extensions": ["xyz"] + }, + "font/opentype": { + "compressible": true, + "extensions": ["otf"] + }, + "image/bmp": { + "source": "apache", + "compressible": true, + "extensions": ["bmp"] + }, + "image/cgm": { + "source": "iana", + "extensions": ["cgm"] + }, + "image/fits": { + "source": "iana" + }, + "image/g3fax": { + "source": "iana", + "extensions": ["g3"] + }, + "image/gif": { + "source": "iana", + "compressible": false, + "extensions": ["gif"] + }, + "image/ief": { + "source": "iana", + "extensions": ["ief"] + }, + "image/jp2": { + "source": "iana" + }, + "image/jpeg": { + "source": "iana", + "compressible": false, + "extensions": ["jpeg","jpg","jpe"] + }, + "image/jpm": { + "source": "iana" + }, + "image/jpx": { + "source": "iana" + }, + "image/ktx": { + "source": "iana", + "extensions": ["ktx"] + }, + "image/naplps": { + "source": "iana" + }, + "image/pjpeg": { + "compressible": false + }, + "image/png": { + "source": "iana", + "compressible": false, + "extensions": ["png"] + }, + "image/prs.btif": { + "source": "iana", + "extensions": ["btif"] + }, + "image/prs.pti": { + "source": "iana" + }, + "image/pwg-raster": { + "source": "iana" + }, + "image/sgi": { + "source": "apache", + "extensions": ["sgi"] + }, + "image/svg+xml": { + "source": "iana", + "compressible": true, + "extensions": ["svg","svgz"] + }, + "image/t38": { + "source": "iana" + }, + "image/tiff": { + "source": "iana", + "compressible": false, + "extensions": ["tiff","tif"] + }, + "image/tiff-fx": { + "source": "iana" + }, + "image/vnd.adobe.photoshop": { + "source": "iana", + "compressible": true, + "extensions": ["psd"] + }, + "image/vnd.airzip.accelerator.azv": { + "source": "iana" + }, + "image/vnd.cns.inf2": { + "source": "iana" + }, + "image/vnd.dece.graphic": { + "source": "iana", + "extensions": ["uvi","uvvi","uvg","uvvg"] + }, + "image/vnd.djvu": { + "source": "iana", + "extensions": ["djvu","djv"] + }, + "image/vnd.dvb.subtitle": { + "source": "iana", + "extensions": ["sub"] + }, + "image/vnd.dwg": { + "source": "iana", + "extensions": ["dwg"] + }, + "image/vnd.dxf": { + "source": "iana", + "extensions": ["dxf"] + }, + "image/vnd.fastbidsheet": { + "source": "iana", + "extensions": ["fbs"] + }, + "image/vnd.fpx": { + "source": "iana", + "extensions": ["fpx"] + }, + "image/vnd.fst": { + "source": "iana", + "extensions": ["fst"] + }, + "image/vnd.fujixerox.edmics-mmr": { + "source": "iana", + "extensions": ["mmr"] + }, + "image/vnd.fujixerox.edmics-rlc": { + "source": "iana", + "extensions": ["rlc"] + }, + "image/vnd.globalgraphics.pgb": { + "source": "iana" + }, + "image/vnd.microsoft.icon": { + "source": "iana" + }, + "image/vnd.mix": { + "source": "iana" + }, + "image/vnd.mozilla.apng": { + "source": "iana" + }, + "image/vnd.ms-modi": { + "source": "iana", + "extensions": ["mdi"] + }, + "image/vnd.ms-photo": { + "source": "apache", + "extensions": ["wdp"] + }, + "image/vnd.net-fpx": { + "source": "iana", + "extensions": ["npx"] + }, + "image/vnd.radiance": { + "source": "iana" + }, + "image/vnd.sealed.png": { + "source": "iana" + }, + "image/vnd.sealedmedia.softseal.gif": { + "source": "iana" + }, + "image/vnd.sealedmedia.softseal.jpg": { + "source": "iana" + }, + "image/vnd.svf": { + "source": "iana" + }, + "image/vnd.tencent.tap": { + "source": "iana" + }, + "image/vnd.valve.source.texture": { + "source": "iana" + }, + "image/vnd.wap.wbmp": { + "source": "iana", + "extensions": ["wbmp"] + }, + "image/vnd.xiff": { + "source": "iana", + "extensions": ["xif"] + }, + "image/vnd.zbrush.pcx": { + "source": "iana" + }, + "image/webp": { + "source": "apache", + "extensions": ["webp"] + }, + "image/x-3ds": { + "source": "apache", + "extensions": ["3ds"] + }, + "image/x-cmu-raster": { + "source": "apache", + "extensions": ["ras"] + }, + "image/x-cmx": { + "source": "apache", + "extensions": ["cmx"] + }, + "image/x-freehand": { + "source": "apache", + "extensions": ["fh","fhc","fh4","fh5","fh7"] + }, + "image/x-icon": { + "source": "apache", + "compressible": true, + "extensions": ["ico"] + }, + "image/x-jng": { + "source": "nginx", + "extensions": ["jng"] + }, + "image/x-mrsid-image": { + "source": "apache", + "extensions": ["sid"] + }, + "image/x-ms-bmp": { + "source": "nginx", + "compressible": true, + "extensions": ["bmp"] + }, + "image/x-pcx": { + "source": "apache", + "extensions": ["pcx"] + }, + "image/x-pict": { + "source": "apache", + "extensions": ["pic","pct"] + }, + "image/x-portable-anymap": { + "source": "apache", + "extensions": ["pnm"] + }, + "image/x-portable-bitmap": { + "source": "apache", + "extensions": ["pbm"] + }, + "image/x-portable-graymap": { + "source": "apache", + "extensions": ["pgm"] + }, + "image/x-portable-pixmap": { + "source": "apache", + "extensions": ["ppm"] + }, + "image/x-rgb": { + "source": "apache", + "extensions": ["rgb"] + }, + "image/x-tga": { + "source": "apache", + "extensions": ["tga"] + }, + "image/x-xbitmap": { + "source": "apache", + "extensions": ["xbm"] + }, + "image/x-xcf": { + "compressible": false + }, + "image/x-xpixmap": { + "source": "apache", + "extensions": ["xpm"] + }, + "image/x-xwindowdump": { + "source": "apache", + "extensions": ["xwd"] + }, + "message/cpim": { + "source": "iana" + }, + "message/delivery-status": { + "source": "iana" + }, + "message/disposition-notification": { + "source": "iana" + }, + "message/external-body": { + "source": "iana" + }, + "message/feedback-report": { + "source": "iana" + }, + "message/global": { + "source": "iana" + }, + "message/global-delivery-status": { + "source": "iana" + }, + "message/global-disposition-notification": { + "source": "iana" + }, + "message/global-headers": { + "source": "iana" + }, + "message/http": { + "source": "iana", + "compressible": false + }, + "message/imdn+xml": { + "source": "iana", + "compressible": true + }, + "message/news": { + "source": "iana" + }, + "message/partial": { + "source": "iana", + "compressible": false + }, + "message/rfc822": { + "source": "iana", + "compressible": true, + "extensions": ["eml","mime"] + }, + "message/s-http": { + "source": "iana" + }, + "message/sip": { + "source": "iana" + }, + "message/sipfrag": { + "source": "iana" + }, + "message/tracking-status": { + "source": "iana" + }, + "message/vnd.si.simp": { + "source": "iana" + }, + "message/vnd.wfa.wsc": { + "source": "iana" + }, + "model/iges": { + "source": "iana", + "compressible": false, + "extensions": ["igs","iges"] + }, + "model/mesh": { + "source": "iana", + "compressible": false, + "extensions": ["msh","mesh","silo"] + }, + "model/vnd.collada+xml": { + "source": "iana", + "extensions": ["dae"] + }, + "model/vnd.dwf": { + "source": "iana", + "extensions": ["dwf"] + }, + "model/vnd.flatland.3dml": { + "source": "iana" + }, + "model/vnd.gdl": { + "source": "iana", + "extensions": ["gdl"] + }, + "model/vnd.gs-gdl": { + "source": "apache" + }, + "model/vnd.gs.gdl": { + "source": "iana" + }, + "model/vnd.gtw": { + "source": "iana", + "extensions": ["gtw"] + }, + "model/vnd.moml+xml": { + "source": "iana" + }, + "model/vnd.mts": { + "source": "iana", + "extensions": ["mts"] + }, + "model/vnd.opengex": { + "source": "iana" + }, + "model/vnd.parasolid.transmit.binary": { + "source": "iana" + }, + "model/vnd.parasolid.transmit.text": { + "source": "iana" + }, + "model/vnd.valve.source.compiled-map": { + "source": "iana" + }, + "model/vnd.vtu": { + "source": "iana", + "extensions": ["vtu"] + }, + "model/vrml": { + "source": "iana", + "compressible": false, + "extensions": ["wrl","vrml"] + }, + "model/x3d+binary": { + "source": "apache", + "compressible": false, + "extensions": ["x3db","x3dbz"] + }, + "model/x3d+fastinfoset": { + "source": "iana" + }, + "model/x3d+vrml": { + "source": "apache", + "compressible": false, + "extensions": ["x3dv","x3dvz"] + }, + "model/x3d+xml": { + "source": "iana", + "compressible": true, + "extensions": ["x3d","x3dz"] + }, + "model/x3d-vrml": { + "source": "iana" + }, + "multipart/alternative": { + "source": "iana", + "compressible": false + }, + "multipart/appledouble": { + "source": "iana" + }, + "multipart/byteranges": { + "source": "iana" + }, + "multipart/digest": { + "source": "iana" + }, + "multipart/encrypted": { + "source": "iana", + "compressible": false + }, + "multipart/form-data": { + "source": "iana", + "compressible": false + }, + "multipart/header-set": { + "source": "iana" + }, + "multipart/mixed": { + "source": "iana", + "compressible": false + }, + "multipart/parallel": { + "source": "iana" + }, + "multipart/related": { + "source": "iana", + "compressible": false + }, + "multipart/report": { + "source": "iana" + }, + "multipart/signed": { + "source": "iana", + "compressible": false + }, + "multipart/voice-message": { + "source": "iana" + }, + "multipart/x-mixed-replace": { + "source": "iana" + }, + "text/1d-interleaved-parityfec": { + "source": "iana" + }, + "text/cache-manifest": { + "source": "iana", + "compressible": true, + "extensions": ["appcache","manifest"] + }, + "text/calendar": { + "source": "iana", + "extensions": ["ics","ifb"] + }, + "text/calender": { + "compressible": true + }, + "text/cmd": { + "compressible": true + }, + "text/coffeescript": { + "extensions": ["coffee","litcoffee"] + }, + "text/css": { + "source": "iana", + "compressible": true, + "extensions": ["css"] + }, + "text/csv": { + "source": "iana", + "compressible": true, + "extensions": ["csv"] + }, + "text/csv-schema": { + "source": "iana" + }, + "text/directory": { + "source": "iana" + }, + "text/dns": { + "source": "iana" + }, + "text/ecmascript": { + "source": "iana" + }, + "text/encaprtp": { + "source": "iana" + }, + "text/enriched": { + "source": "iana" + }, + "text/fwdred": { + "source": "iana" + }, + "text/grammar-ref-list": { + "source": "iana" + }, + "text/hjson": { + "extensions": ["hjson"] + }, + "text/html": { + "source": "iana", + "compressible": true, + "extensions": ["html","htm","shtml"] + }, + "text/jade": { + "extensions": ["jade"] + }, + "text/javascript": { + "source": "iana", + "compressible": true + }, + "text/jcr-cnd": { + "source": "iana" + }, + "text/jsx": { + "compressible": true, + "extensions": ["jsx"] + }, + "text/less": { + "extensions": ["less"] + }, + "text/markdown": { + "source": "iana" + }, + "text/mathml": { + "source": "nginx", + "extensions": ["mml"] + }, + "text/mizar": { + "source": "iana" + }, + "text/n3": { + "source": "iana", + "compressible": true, + "extensions": ["n3"] + }, + "text/parameters": { + "source": "iana" + }, + "text/parityfec": { + "source": "iana" + }, + "text/plain": { + "source": "iana", + "compressible": true, + "extensions": ["txt","text","conf","def","list","log","in","ini"] + }, + "text/provenance-notation": { + "source": "iana" + }, + "text/prs.fallenstein.rst": { + "source": "iana" + }, + "text/prs.lines.tag": { + "source": "iana", + "extensions": ["dsc"] + }, + "text/raptorfec": { + "source": "iana" + }, + "text/red": { + "source": "iana" + }, + "text/rfc822-headers": { + "source": "iana" + }, + "text/richtext": { + "source": "iana", + "compressible": true, + "extensions": ["rtx"] + }, + "text/rtf": { + "source": "iana", + "compressible": true, + "extensions": ["rtf"] + }, + "text/rtp-enc-aescm128": { + "source": "iana" + }, + "text/rtploopback": { + "source": "iana" + }, + "text/rtx": { + "source": "iana" + }, + "text/sgml": { + "source": "iana", + "extensions": ["sgml","sgm"] + }, + "text/stylus": { + "extensions": ["stylus","styl"] + }, + "text/t140": { + "source": "iana" + }, + "text/tab-separated-values": { + "source": "iana", + "compressible": true, + "extensions": ["tsv"] + }, + "text/troff": { + "source": "iana", + "extensions": ["t","tr","roff","man","me","ms"] + }, + "text/turtle": { + "source": "iana", + "extensions": ["ttl"] + }, + "text/ulpfec": { + "source": "iana" + }, + "text/uri-list": { + "source": "iana", + "compressible": true, + "extensions": ["uri","uris","urls"] + }, + "text/vcard": { + "source": "iana", + "compressible": true, + "extensions": ["vcard"] + }, + "text/vnd.a": { + "source": "iana" + }, + "text/vnd.abc": { + "source": "iana" + }, + "text/vnd.curl": { + "source": "iana", + "extensions": ["curl"] + }, + "text/vnd.curl.dcurl": { + "source": "apache", + "extensions": ["dcurl"] + }, + "text/vnd.curl.mcurl": { + "source": "apache", + "extensions": ["mcurl"] + }, + "text/vnd.curl.scurl": { + "source": "apache", + "extensions": ["scurl"] + }, + "text/vnd.debian.copyright": { + "source": "iana" + }, + "text/vnd.dmclientscript": { + "source": "iana" + }, + "text/vnd.dvb.subtitle": { + "source": "iana", + "extensions": ["sub"] + }, + "text/vnd.esmertec.theme-descriptor": { + "source": "iana" + }, + "text/vnd.fly": { + "source": "iana", + "extensions": ["fly"] + }, + "text/vnd.fmi.flexstor": { + "source": "iana", + "extensions": ["flx"] + }, + "text/vnd.graphviz": { + "source": "iana", + "extensions": ["gv"] + }, + "text/vnd.in3d.3dml": { + "source": "iana", + "extensions": ["3dml"] + }, + "text/vnd.in3d.spot": { + "source": "iana", + "extensions": ["spot"] + }, + "text/vnd.iptc.newsml": { + "source": "iana" + }, + "text/vnd.iptc.nitf": { + "source": "iana" + }, + "text/vnd.latex-z": { + "source": "iana" + }, + "text/vnd.motorola.reflex": { + "source": "iana" + }, + "text/vnd.ms-mediapackage": { + "source": "iana" + }, + "text/vnd.net2phone.commcenter.command": { + "source": "iana" + }, + "text/vnd.radisys.msml-basic-layout": { + "source": "iana" + }, + "text/vnd.si.uricatalogue": { + "source": "iana" + }, + "text/vnd.sun.j2me.app-descriptor": { + "source": "iana", + "extensions": ["jad"] + }, + "text/vnd.trolltech.linguist": { + "source": "iana" + }, + "text/vnd.wap.si": { + "source": "iana" + }, + "text/vnd.wap.sl": { + "source": "iana" + }, + "text/vnd.wap.wml": { + "source": "iana", + "extensions": ["wml"] + }, + "text/vnd.wap.wmlscript": { + "source": "iana", + "extensions": ["wmls"] + }, + "text/vtt": { + "charset": "UTF-8", + "compressible": true, + "extensions": ["vtt"] + }, + "text/x-asm": { + "source": "apache", + "extensions": ["s","asm"] + }, + "text/x-c": { + "source": "apache", + "extensions": ["c","cc","cxx","cpp","h","hh","dic"] + }, + "text/x-component": { + "source": "nginx", + "extensions": ["htc"] + }, + "text/x-fortran": { + "source": "apache", + "extensions": ["f","for","f77","f90"] + }, + "text/x-gwt-rpc": { + "compressible": true + }, + "text/x-handlebars-template": { + "extensions": ["hbs"] + }, + "text/x-java-source": { + "source": "apache", + "extensions": ["java"] + }, + "text/x-jquery-tmpl": { + "compressible": true + }, + "text/x-lua": { + "extensions": ["lua"] + }, + "text/x-markdown": { + "compressible": true, + "extensions": ["markdown","md","mkd"] + }, + "text/x-nfo": { + "source": "apache", + "extensions": ["nfo"] + }, + "text/x-opml": { + "source": "apache", + "extensions": ["opml"] + }, + "text/x-pascal": { + "source": "apache", + "extensions": ["p","pas"] + }, + "text/x-processing": { + "compressible": true, + "extensions": ["pde"] + }, + "text/x-sass": { + "extensions": ["sass"] + }, + "text/x-scss": { + "extensions": ["scss"] + }, + "text/x-setext": { + "source": "apache", + "extensions": ["etx"] + }, + "text/x-sfv": { + "source": "apache", + "extensions": ["sfv"] + }, + "text/x-suse-ymp": { + "compressible": true, + "extensions": ["ymp"] + }, + "text/x-uuencode": { + "source": "apache", + "extensions": ["uu"] + }, + "text/x-vcalendar": { + "source": "apache", + "extensions": ["vcs"] + }, + "text/x-vcard": { + "source": "apache", + "extensions": ["vcf"] + }, + "text/xml": { + "source": "iana", + "compressible": true, + "extensions": ["xml"] + }, + "text/xml-external-parsed-entity": { + "source": "iana" + }, + "text/yaml": { + "extensions": ["yaml","yml"] + }, + "video/1d-interleaved-parityfec": { + "source": "apache" + }, + "video/3gpp": { + "source": "apache", + "extensions": ["3gp","3gpp"] + }, + "video/3gpp-tt": { + "source": "apache" + }, + "video/3gpp2": { + "source": "apache", + "extensions": ["3g2"] + }, + "video/bmpeg": { + "source": "apache" + }, + "video/bt656": { + "source": "apache" + }, + "video/celb": { + "source": "apache" + }, + "video/dv": { + "source": "apache" + }, + "video/h261": { + "source": "apache", + "extensions": ["h261"] + }, + "video/h263": { + "source": "apache", + "extensions": ["h263"] + }, + "video/h263-1998": { + "source": "apache" + }, + "video/h263-2000": { + "source": "apache" + }, + "video/h264": { + "source": "apache", + "extensions": ["h264"] + }, + "video/h264-rcdo": { + "source": "apache" + }, + "video/h264-svc": { + "source": "apache" + }, + "video/jpeg": { + "source": "apache", + "extensions": ["jpgv"] + }, + "video/jpeg2000": { + "source": "apache" + }, + "video/jpm": { + "source": "apache", + "extensions": ["jpm","jpgm"] + }, + "video/mj2": { + "source": "apache", + "extensions": ["mj2","mjp2"] + }, + "video/mp1s": { + "source": "apache" + }, + "video/mp2p": { + "source": "apache" + }, + "video/mp2t": { + "source": "apache", + "extensions": ["ts"] + }, + "video/mp4": { + "source": "apache", + "compressible": false, + "extensions": ["mp4","mp4v","mpg4"] + }, + "video/mp4v-es": { + "source": "apache" + }, + "video/mpeg": { + "source": "apache", + "compressible": false, + "extensions": ["mpeg","mpg","mpe","m1v","m2v"] + }, + "video/mpeg4-generic": { + "source": "apache" + }, + "video/mpv": { + "source": "apache" + }, + "video/nv": { + "source": "apache" + }, + "video/ogg": { + "source": "apache", + "compressible": false, + "extensions": ["ogv"] + }, + "video/parityfec": { + "source": "apache" + }, + "video/pointer": { + "source": "apache" + }, + "video/quicktime": { + "source": "apache", + "compressible": false, + "extensions": ["qt","mov"] + }, + "video/raw": { + "source": "apache" + }, + "video/rtp-enc-aescm128": { + "source": "apache" + }, + "video/rtx": { + "source": "apache" + }, + "video/smpte292m": { + "source": "apache" + }, + "video/ulpfec": { + "source": "apache" + }, + "video/vc1": { + "source": "apache" + }, + "video/vnd.cctv": { + "source": "apache" + }, + "video/vnd.dece.hd": { + "source": "apache", + "extensions": ["uvh","uvvh"] + }, + "video/vnd.dece.mobile": { + "source": "apache", + "extensions": ["uvm","uvvm"] + }, + "video/vnd.dece.mp4": { + "source": "apache" + }, + "video/vnd.dece.pd": { + "source": "apache", + "extensions": ["uvp","uvvp"] + }, + "video/vnd.dece.sd": { + "source": "apache", + "extensions": ["uvs","uvvs"] + }, + "video/vnd.dece.video": { + "source": "apache", + "extensions": ["uvv","uvvv"] + }, + "video/vnd.directv.mpeg": { + "source": "apache" + }, + "video/vnd.directv.mpeg-tts": { + "source": "apache" + }, + "video/vnd.dlna.mpeg-tts": { + "source": "apache" + }, + "video/vnd.dvb.file": { + "source": "apache", + "extensions": ["dvb"] + }, + "video/vnd.fvt": { + "source": "apache", + "extensions": ["fvt"] + }, + "video/vnd.hns.video": { + "source": "apache" + }, + "video/vnd.iptvforum.1dparityfec-1010": { + "source": "apache" + }, + "video/vnd.iptvforum.1dparityfec-2005": { + "source": "apache" + }, + "video/vnd.iptvforum.2dparityfec-1010": { + "source": "apache" + }, + "video/vnd.iptvforum.2dparityfec-2005": { + "source": "apache" + }, + "video/vnd.iptvforum.ttsavc": { + "source": "apache" + }, + "video/vnd.iptvforum.ttsmpeg2": { + "source": "apache" + }, + "video/vnd.motorola.video": { + "source": "apache" + }, + "video/vnd.motorola.videop": { + "source": "apache" + }, + "video/vnd.mpegurl": { + "source": "apache", + "extensions": ["mxu","m4u"] + }, + "video/vnd.ms-playready.media.pyv": { + "source": "apache", + "extensions": ["pyv"] + }, + "video/vnd.nokia.interleaved-multimedia": { + "source": "apache" + }, + "video/vnd.nokia.videovoip": { + "source": "apache" + }, + "video/vnd.objectvideo": { + "source": "apache" + }, + "video/vnd.sealed.mpeg1": { + "source": "apache" + }, + "video/vnd.sealed.mpeg4": { + "source": "apache" + }, + "video/vnd.sealed.swf": { + "source": "apache" + }, + "video/vnd.sealedmedia.softseal.mov": { + "source": "apache" + }, + "video/vnd.uvvu.mp4": { + "source": "apache", + "extensions": ["uvu","uvvu"] + }, + "video/vnd.vivo": { + "source": "apache", + "extensions": ["viv"] + }, + "video/webm": { + "source": "apache", + "compressible": false, + "extensions": ["webm"] + }, + "video/x-f4v": { + "source": "apache", + "extensions": ["f4v"] + }, + "video/x-fli": { + "source": "apache", + "extensions": ["fli"] + }, + "video/x-flv": { + "source": "apache", + "compressible": false, + "extensions": ["flv"] + }, + "video/x-m4v": { + "source": "apache", + "extensions": ["m4v"] + }, + "video/x-matroska": { + "source": "apache", + "compressible": false, + "extensions": ["mkv","mk3d","mks"] + }, + "video/x-mng": { + "source": "apache", + "extensions": ["mng"] + }, + "video/x-ms-asf": { + "source": "apache", + "extensions": ["asf","asx"] + }, + "video/x-ms-vob": { + "source": "apache", + "extensions": ["vob"] + }, + "video/x-ms-wm": { + "source": "apache", + "extensions": ["wm"] + }, + "video/x-ms-wmv": { + "source": "apache", + "compressible": false, + "extensions": ["wmv"] + }, + "video/x-ms-wmx": { + "source": "apache", + "extensions": ["wmx"] + }, + "video/x-ms-wvx": { + "source": "apache", + "extensions": ["wvx"] + }, + "video/x-msvideo": { + "source": "apache", + "extensions": ["avi"] + }, + "video/x-sgi-movie": { + "source": "apache", + "extensions": ["movie"] + }, + "video/x-smv": { + "source": "apache", + "extensions": ["smv"] + }, + "x-conference/x-cooltalk": { + "source": "apache", + "extensions": ["ice"] + }, + "x-shader/x-fragment": { + "compressible": true + }, + "x-shader/x-vertex": { + "compressible": true + } +} diff --git a/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/index.js b/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/index.js new file mode 100644 index 0000000..551031f --- /dev/null +++ b/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/index.js @@ -0,0 +1,11 @@ +/*! + * mime-db + * Copyright(c) 2014 Jonathan Ong + * MIT Licensed + */ + +/** + * Module exports. + */ + +module.exports = require('./db.json') diff --git a/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/package.json b/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/package.json new file mode 100644 index 0000000..509af96 --- /dev/null +++ b/node_modules/serve-index/node_modules/mime-types/node_modules/mime-db/package.json @@ -0,0 +1,93 @@ +{ + "name": "mime-db", + "description": "Media Type Database", + "version": "1.20.0", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + { + "name": "Robert Kieffer", + "email": "robert@broofa.com", + "url": "http://github.com/broofa" + } + ], + "license": "MIT", + "keywords": [ + "mime", + "db", + "type", + "types", + "database", + "charset", + "charsets" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/mime-db" + }, + "devDependencies": { + "bluebird": "2.10.0", + "co": "4.6.0", + "cogent": "1.0.1", + "csv-parse": "1.0.0", + "gnode": "0.1.1", + "istanbul": "0.4.0", + "mocha": "1.21.5", + "raw-body": "2.1.4", + "stream-to-array": "2.2.0" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "README.md", + "db.json", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "build": "node scripts/build", + "fetch": "gnode scripts/fetch-apache && gnode scripts/fetch-iana && gnode scripts/fetch-nginx", + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/", + "update": "npm run fetch && npm run build" + }, + "gitHead": "20c99312645c05ab8466701ede01bd5cd3ac7bc4", + "bugs": { + "url": "https://github.com/jshttp/mime-db/issues" + }, + "homepage": "https://github.com/jshttp/mime-db", + "_id": "mime-db@1.20.0", + "_shasum": "496f90fd01fe0e031c8823ec3aa9450ffda18ed8", + "_from": "mime-db@>=1.20.0 <1.21.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "496f90fd01fe0e031c8823ec3aa9450ffda18ed8", + "tarball": "http://registry.npmjs.org/mime-db/-/mime-db-1.20.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.20.0.tgz" +} diff --git a/node_modules/serve-index/node_modules/mime-types/package.json b/node_modules/serve-index/node_modules/mime-types/package.json new file mode 100644 index 0000000..7b90c97 --- /dev/null +++ b/node_modules/serve-index/node_modules/mime-types/package.json @@ -0,0 +1,83 @@ +{ + "name": "mime-types", + "description": "The ultimate javascript content-type utility.", + "version": "2.1.8", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jeremiah Senkpiel", + "email": "fishrock123@rocketmail.com", + "url": "https://searchbeam.jit.su" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + } + ], + "license": "MIT", + "keywords": [ + "mime", + "types" + ], + "repository": { + "type": "git", + "url": "https://github.com/jshttp/mime-types" + }, + "dependencies": { + "mime-db": "~1.20.0" + }, + "devDependencies": { + "istanbul": "0.4.1", + "mocha": "~1.21.5" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec test/test.js", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot test/test.js", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter dot test/test.js" + }, + "gitHead": "100876a23fab896d8cf0d904fc9778dbdfc1695b", + "bugs": { + "url": "https://github.com/jshttp/mime-types/issues" + }, + "homepage": "https://github.com/jshttp/mime-types", + "_id": "mime-types@2.1.8", + "_shasum": "faf57823de04bc7cbff4ee82c6b63946e812ae72", + "_from": "mime-types@>=2.1.4 <2.2.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + } + ], + "dist": { + "shasum": "faf57823de04bc7cbff4ee82c6b63946e812ae72", + "tarball": "http://registry.npmjs.org/mime-types/-/mime-types-2.1.8.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.8.tgz" +} diff --git a/node_modules/serve-index/node_modules/parseurl/.npmignore b/node_modules/serve-index/node_modules/parseurl/.npmignore new file mode 100644 index 0000000..85c82a5 --- /dev/null +++ b/node_modules/serve-index/node_modules/parseurl/.npmignore @@ -0,0 +1,4 @@ +benchmark/ +coverage/ +test/ +.travis.yml diff --git a/node_modules/serve-index/node_modules/parseurl/HISTORY.md b/node_modules/serve-index/node_modules/parseurl/HISTORY.md new file mode 100644 index 0000000..65a0860 --- /dev/null +++ b/node_modules/serve-index/node_modules/parseurl/HISTORY.md @@ -0,0 +1,42 @@ +1.3.0 / 2014-08-09 +================== + + * Add `parseurl.original` for parsing `req.originalUrl` with fallback + * Return `undefined` if `req.url` is `undefined` + +1.2.0 / 2014-07-21 +================== + + * Cache URLs based on original value + * Remove no-longer-needed URL mis-parse work-around + * Simplify the "fast-path" `RegExp` + +1.1.3 / 2014-07-08 +================== + + * Fix typo + +1.1.2 / 2014-07-08 +================== + + * Seriously fix Node.js 0.8 compatibility + +1.1.1 / 2014-07-08 +================== + + * Fix Node.js 0.8 compatibility + +1.1.0 / 2014-07-08 +================== + + * Incorporate URL href-only parse fast-path + +1.0.1 / 2014-03-08 +================== + + * Add missing `require` + +1.0.0 / 2014-03-08 +================== + + * Genesis from `connect` diff --git a/node_modules/serve-index/node_modules/parseurl/LICENSE b/node_modules/serve-index/node_modules/parseurl/LICENSE new file mode 100644 index 0000000..ec7dfe7 --- /dev/null +++ b/node_modules/serve-index/node_modules/parseurl/LICENSE @@ -0,0 +1,24 @@ + +(The MIT License) + +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2014 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/serve-index/node_modules/parseurl/README.md b/node_modules/serve-index/node_modules/parseurl/README.md new file mode 100644 index 0000000..0db1d02 --- /dev/null +++ b/node_modules/serve-index/node_modules/parseurl/README.md @@ -0,0 +1,107 @@ +# parseurl + +[![NPM version](https://badge.fury.io/js/parseurl.svg)](http://badge.fury.io/js/parseurl) +[![Build Status](https://travis-ci.org/expressjs/parseurl.svg?branch=master)](https://travis-ci.org/expressjs/parseurl) +[![Coverage Status](https://img.shields.io/coveralls/expressjs/parseurl.svg?branch=master)](https://coveralls.io/r/expressjs/parseurl) + +Parse a URL with memoization. + +## Install + +```bash +$ npm install parseurl +``` + +## API + +```js +var parseurl = require('parseurl') +``` + +### parseurl(req) + +Parse the URL of the given request object (looks at the `req.url` property) +and return the result. The result is the same as `url.parse` in Node.js core. +Calling this function multiple times on the same `req` where `req.url` does +not change will return a cached parsed object, rather than parsing again. + +### parseurl.original(req) + +Parse the original URL of the given request object and return the result. +This works by trying to parse `req.originalUrl` if it is a string, otherwise +parses `req.url`. The result is the same as `url.parse` in Node.js core. +Calling this function multiple times on the same `req` where `req.originalUrl` +does not change will return a cached parsed object, rather than parsing again. + +## Benchmark + +```bash +$ npm run-script bench + +> parseurl@1.3.0 bench nodejs-parseurl +> node benchmark/index.js + +> node benchmark/fullurl.js + + Parsing URL "http://localhost:8888/foo/bar?user=tj&pet=fluffy" + + 1 test completed. + 2 tests completed. + 3 tests completed. + + fasturl x 1,290,780 ops/sec ±0.46% (195 runs sampled) + nativeurl x 56,401 ops/sec ±0.22% (196 runs sampled) + parseurl x 55,231 ops/sec ±0.22% (194 runs sampled) + +> node benchmark/pathquery.js + + Parsing URL "/foo/bar?user=tj&pet=fluffy" + + 1 test completed. + 2 tests completed. + 3 tests completed. + + fasturl x 1,986,668 ops/sec ±0.27% (190 runs sampled) + nativeurl x 98,740 ops/sec ±0.21% (195 runs sampled) + parseurl x 2,628,171 ops/sec ±0.36% (195 runs sampled) + +> node benchmark/samerequest.js + + Parsing URL "/foo/bar?user=tj&pet=fluffy" on same request object + + 1 test completed. + 2 tests completed. + 3 tests completed. + + fasturl x 2,184,468 ops/sec ±0.40% (194 runs sampled) + nativeurl x 99,437 ops/sec ±0.71% (194 runs sampled) + parseurl x 10,498,005 ops/sec ±0.61% (186 runs sampled) + +> node benchmark/simplepath.js + + Parsing URL "/foo/bar" + + 1 test completed. + 2 tests completed. + 3 tests completed. + + fasturl x 4,535,825 ops/sec ±0.27% (191 runs sampled) + nativeurl x 98,769 ops/sec ±0.54% (191 runs sampled) + parseurl x 4,164,865 ops/sec ±0.34% (192 runs sampled) + +> node benchmark/slash.js + + Parsing URL "/" + + 1 test completed. + 2 tests completed. + 3 tests completed. + + fasturl x 4,908,405 ops/sec ±0.42% (191 runs sampled) + nativeurl x 100,945 ops/sec ±0.59% (188 runs sampled) + parseurl x 4,333,208 ops/sec ±0.27% (194 runs sampled) +``` + +## License + + [MIT](LICENSE) diff --git a/node_modules/serve-index/node_modules/parseurl/index.js b/node_modules/serve-index/node_modules/parseurl/index.js new file mode 100644 index 0000000..8632347 --- /dev/null +++ b/node_modules/serve-index/node_modules/parseurl/index.js @@ -0,0 +1,136 @@ +/*! + * parseurl + * Copyright(c) 2014 Jonathan Ong + * Copyright(c) 2014 Douglas Christopher Wilson + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var url = require('url') +var parse = url.parse +var Url = url.Url + +/** + * Pattern for a simple path case. + * See: https://github.com/joyent/node/pull/7878 + */ + +var simplePathRegExp = /^(\/\/?(?!\/)[^\?#\s]*)(\?[^#\s]*)?$/ + +/** + * Exports. + */ + +module.exports = parseurl +module.exports.original = originalurl + +/** + * Parse the `req` url with memoization. + * + * @param {ServerRequest} req + * @return {Object} + * @api public + */ + +function parseurl(req) { + var url = req.url + + if (url === undefined) { + // URL is undefined + return undefined + } + + var parsed = req._parsedUrl + + if (fresh(url, parsed)) { + // Return cached URL parse + return parsed + } + + // Parse the URL + parsed = fastparse(url) + parsed._raw = url + + return req._parsedUrl = parsed +}; + +/** + * Parse the `req` original url with fallback and memoization. + * + * @param {ServerRequest} req + * @return {Object} + * @api public + */ + +function originalurl(req) { + var url = req.originalUrl + + if (typeof url !== 'string') { + // Fallback + return parseurl(req) + } + + var parsed = req._parsedOriginalUrl + + if (fresh(url, parsed)) { + // Return cached URL parse + return parsed + } + + // Parse the URL + parsed = fastparse(url) + parsed._raw = url + + return req._parsedOriginalUrl = parsed +}; + +/** + * Parse the `str` url with fast-path short-cut. + * + * @param {string} str + * @return {Object} + * @api private + */ + +function fastparse(str) { + // Try fast path regexp + // See: https://github.com/joyent/node/pull/7878 + var simplePath = typeof str === 'string' && simplePathRegExp.exec(str) + + // Construct simple URL + if (simplePath) { + var pathname = simplePath[1] + var search = simplePath[2] || null + var url = Url !== undefined + ? new Url() + : {} + url.path = str + url.href = str + url.pathname = pathname + url.search = search + url.query = search && search.substr(1) + + return url + } + + return parse(str) +} + +/** + * Determine if parsed is still fresh for url. + * + * @param {string} url + * @param {object} parsedUrl + * @return {boolean} + * @api private + */ + +function fresh(url, parsedUrl) { + return typeof parsedUrl === 'object' + && parsedUrl !== null + && (Url === undefined || parsedUrl instanceof Url) + && parsedUrl._raw === url +} diff --git a/node_modules/serve-index/node_modules/parseurl/package.json b/node_modules/serve-index/node_modules/parseurl/package.json new file mode 100644 index 0000000..7d567c6 --- /dev/null +++ b/node_modules/serve-index/node_modules/parseurl/package.json @@ -0,0 +1,79 @@ +{ + "name": "parseurl", + "description": "parse a url with memoization", + "version": "1.3.0", + "author": { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + } + ], + "repository": { + "type": "git", + "url": "https://github.com/expressjs/parseurl" + }, + "license": "MIT", + "devDependencies": { + "benchmark": "1.0.0", + "beautify-benchmark": "0.2.4", + "fast-url-parser": "~1.0.0", + "istanbul": "0.3.0", + "mocha": "~1.21.4" + }, + "scripts": { + "bench": "node benchmark/index.js", + "test": "mocha --check-leaks --bail --reporter spec test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --check-leaks --reporter dot test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --check-leaks --reporter spec test/" + }, + "gitHead": "03b7ccca240e2bef5df6c25797e99175d28fb2cb", + "bugs": { + "url": "https://github.com/expressjs/parseurl/issues" + }, + "homepage": "https://github.com/expressjs/parseurl", + "_id": "parseurl@1.3.0", + "_shasum": "b58046db4223e145afa76009e61bac87cc2281b3", + "_from": "parseurl@>=1.3.0 <1.4.0", + "_npmVersion": "1.4.21", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "shtylman", + "email": "shtylman@gmail.com" + }, + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + } + ], + "dist": { + "shasum": "b58046db4223e145afa76009e61bac87cc2281b3", + "tarball": "http://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz" +} diff --git a/node_modules/serve-index/package.json b/node_modules/serve-index/package.json new file mode 100644 index 0000000..0800f0f --- /dev/null +++ b/node_modules/serve-index/package.json @@ -0,0 +1,88 @@ +{ + "name": "serve-index", + "description": "Serve directory listings", + "version": "1.7.2", + "author": { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/expressjs/serve-index" + }, + "dependencies": { + "accepts": "~1.2.12", + "batch": "0.5.2", + "debug": "~2.2.0", + "escape-html": "1.0.2", + "http-errors": "~1.3.1", + "mime-types": "~2.1.4", + "parseurl": "~1.3.0" + }, + "devDependencies": { + "after": "0.8.1", + "istanbul": "0.3.9", + "mocha": "2.2.5", + "supertest": "1.0.1" + }, + "files": [ + "public/", + "LICENSE", + "HISTORY.md", + "index.js" + ], + "engines": { + "node": ">= 0.8.0" + }, + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/" + }, + "gitHead": "f39ad778ed5546fb94456544f92938f4b09b7a34", + "bugs": { + "url": "https://github.com/expressjs/serve-index/issues" + }, + "homepage": "https://github.com/expressjs/serve-index", + "_id": "serve-index@1.7.2", + "_shasum": "9a155d9c4f9d391e463970e7b4eb16c7672141c0", + "_from": "serve-index@>=1.6.1 <2.0.0", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + "maintainers": [ + { + "name": "dougwilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "jongleberry", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "mscdex", + "email": "mscdex@mscdex.net" + }, + { + "name": "fishrock123", + "email": "fishrock123@rocketmail.com" + }, + { + "name": "defunctzombie", + "email": "shtylman@gmail.com" + } + ], + "dist": { + "shasum": "9a155d9c4f9d391e463970e7b4eb16c7672141c0", + "tarball": "http://registry.npmjs.org/serve-index/-/serve-index-1.7.2.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.7.2.tgz" +} diff --git a/node_modules/serve-index/public/directory.html b/node_modules/serve-index/public/directory.html new file mode 100644 index 0000000..cda1601 --- /dev/null +++ b/node_modules/serve-index/public/directory.html @@ -0,0 +1,82 @@ + + + + + + listing directory {directory} + + + + + +
            +

            ~{linked-path}

            + {files} +
            + + \ No newline at end of file diff --git a/node_modules/serve-index/public/icons/application_xp.png b/node_modules/serve-index/public/icons/application_xp.png new file mode 100644 index 0000000..d22860a Binary files /dev/null and b/node_modules/serve-index/public/icons/application_xp.png differ diff --git a/node_modules/serve-index/public/icons/application_xp_terminal.png b/node_modules/serve-index/public/icons/application_xp_terminal.png new file mode 100644 index 0000000..c28dd63 Binary files /dev/null and b/node_modules/serve-index/public/icons/application_xp_terminal.png differ diff --git a/node_modules/serve-index/public/icons/box.png b/node_modules/serve-index/public/icons/box.png new file mode 100644 index 0000000..8443c23 Binary files /dev/null and b/node_modules/serve-index/public/icons/box.png differ diff --git a/node_modules/serve-index/public/icons/cd.png b/node_modules/serve-index/public/icons/cd.png new file mode 100644 index 0000000..ef43223 Binary files /dev/null and b/node_modules/serve-index/public/icons/cd.png differ diff --git a/node_modules/serve-index/public/icons/controller.png b/node_modules/serve-index/public/icons/controller.png new file mode 100644 index 0000000..5cf76ed Binary files /dev/null and b/node_modules/serve-index/public/icons/controller.png differ diff --git a/node_modules/serve-index/public/icons/drive.png b/node_modules/serve-index/public/icons/drive.png new file mode 100644 index 0000000..37b7c9b Binary files /dev/null and b/node_modules/serve-index/public/icons/drive.png differ diff --git a/node_modules/serve-index/public/icons/film.png b/node_modules/serve-index/public/icons/film.png new file mode 100644 index 0000000..b0ce7bb Binary files /dev/null and b/node_modules/serve-index/public/icons/film.png differ diff --git a/node_modules/serve-index/public/icons/folder.png b/node_modules/serve-index/public/icons/folder.png new file mode 100644 index 0000000..698f3d3 Binary files /dev/null and b/node_modules/serve-index/public/icons/folder.png differ diff --git a/node_modules/serve-index/public/icons/font.png b/node_modules/serve-index/public/icons/font.png new file mode 100644 index 0000000..b7960db Binary files /dev/null and b/node_modules/serve-index/public/icons/font.png differ diff --git a/node_modules/serve-index/public/icons/image.png b/node_modules/serve-index/public/icons/image.png new file mode 100644 index 0000000..fc3c393 Binary files /dev/null and b/node_modules/serve-index/public/icons/image.png differ diff --git a/node_modules/serve-index/public/icons/map.png b/node_modules/serve-index/public/icons/map.png new file mode 100644 index 0000000..f90ef25 Binary files /dev/null and b/node_modules/serve-index/public/icons/map.png differ diff --git a/node_modules/serve-index/public/icons/page.png b/node_modules/serve-index/public/icons/page.png new file mode 100644 index 0000000..03ddd79 Binary files /dev/null and b/node_modules/serve-index/public/icons/page.png differ diff --git a/node_modules/serve-index/public/icons/page_add.png b/node_modules/serve-index/public/icons/page_add.png new file mode 100644 index 0000000..d5bfa07 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_add.png differ diff --git a/node_modules/serve-index/public/icons/page_attach.png b/node_modules/serve-index/public/icons/page_attach.png new file mode 100644 index 0000000..89ee2da Binary files /dev/null and b/node_modules/serve-index/public/icons/page_attach.png differ diff --git a/node_modules/serve-index/public/icons/page_code.png b/node_modules/serve-index/public/icons/page_code.png new file mode 100644 index 0000000..f7ea904 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_code.png differ diff --git a/node_modules/serve-index/public/icons/page_copy.png b/node_modules/serve-index/public/icons/page_copy.png new file mode 100644 index 0000000..195dc6d Binary files /dev/null and b/node_modules/serve-index/public/icons/page_copy.png differ diff --git a/node_modules/serve-index/public/icons/page_delete.png b/node_modules/serve-index/public/icons/page_delete.png new file mode 100644 index 0000000..3141467 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_delete.png differ diff --git a/node_modules/serve-index/public/icons/page_edit.png b/node_modules/serve-index/public/icons/page_edit.png new file mode 100644 index 0000000..046811e Binary files /dev/null and b/node_modules/serve-index/public/icons/page_edit.png differ diff --git a/node_modules/serve-index/public/icons/page_error.png b/node_modules/serve-index/public/icons/page_error.png new file mode 100644 index 0000000..f07f449 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_error.png differ diff --git a/node_modules/serve-index/public/icons/page_excel.png b/node_modules/serve-index/public/icons/page_excel.png new file mode 100644 index 0000000..eb6158e Binary files /dev/null and b/node_modules/serve-index/public/icons/page_excel.png differ diff --git a/node_modules/serve-index/public/icons/page_find.png b/node_modules/serve-index/public/icons/page_find.png new file mode 100644 index 0000000..2f19388 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_find.png differ diff --git a/node_modules/serve-index/public/icons/page_gear.png b/node_modules/serve-index/public/icons/page_gear.png new file mode 100644 index 0000000..8e83281 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_gear.png differ diff --git a/node_modules/serve-index/public/icons/page_go.png b/node_modules/serve-index/public/icons/page_go.png new file mode 100644 index 0000000..80fe1ed Binary files /dev/null and b/node_modules/serve-index/public/icons/page_go.png differ diff --git a/node_modules/serve-index/public/icons/page_green.png b/node_modules/serve-index/public/icons/page_green.png new file mode 100644 index 0000000..de8e003 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_green.png differ diff --git a/node_modules/serve-index/public/icons/page_key.png b/node_modules/serve-index/public/icons/page_key.png new file mode 100644 index 0000000..d6626cb Binary files /dev/null and b/node_modules/serve-index/public/icons/page_key.png differ diff --git a/node_modules/serve-index/public/icons/page_lightning.png b/node_modules/serve-index/public/icons/page_lightning.png new file mode 100644 index 0000000..7e56870 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_lightning.png differ diff --git a/node_modules/serve-index/public/icons/page_link.png b/node_modules/serve-index/public/icons/page_link.png new file mode 100644 index 0000000..312eab0 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_link.png differ diff --git a/node_modules/serve-index/public/icons/page_paintbrush.png b/node_modules/serve-index/public/icons/page_paintbrush.png new file mode 100644 index 0000000..246a2f0 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_paintbrush.png differ diff --git a/node_modules/serve-index/public/icons/page_paste.png b/node_modules/serve-index/public/icons/page_paste.png new file mode 100644 index 0000000..968f073 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_paste.png differ diff --git a/node_modules/serve-index/public/icons/page_red.png b/node_modules/serve-index/public/icons/page_red.png new file mode 100644 index 0000000..0b18247 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_red.png differ diff --git a/node_modules/serve-index/public/icons/page_refresh.png b/node_modules/serve-index/public/icons/page_refresh.png new file mode 100644 index 0000000..cf347c7 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_refresh.png differ diff --git a/node_modules/serve-index/public/icons/page_save.png b/node_modules/serve-index/public/icons/page_save.png new file mode 100644 index 0000000..caea546 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_save.png differ diff --git a/node_modules/serve-index/public/icons/page_white.png b/node_modules/serve-index/public/icons/page_white.png new file mode 100644 index 0000000..8b8b1ca Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white.png differ diff --git a/node_modules/serve-index/public/icons/page_white_acrobat.png b/node_modules/serve-index/public/icons/page_white_acrobat.png new file mode 100644 index 0000000..8f8095e Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_acrobat.png differ diff --git a/node_modules/serve-index/public/icons/page_white_actionscript.png b/node_modules/serve-index/public/icons/page_white_actionscript.png new file mode 100644 index 0000000..159b240 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_actionscript.png differ diff --git a/node_modules/serve-index/public/icons/page_white_add.png b/node_modules/serve-index/public/icons/page_white_add.png new file mode 100644 index 0000000..aa23dde Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_add.png differ diff --git a/node_modules/serve-index/public/icons/page_white_c.png b/node_modules/serve-index/public/icons/page_white_c.png new file mode 100644 index 0000000..34a05cc Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_c.png differ diff --git a/node_modules/serve-index/public/icons/page_white_camera.png b/node_modules/serve-index/public/icons/page_white_camera.png new file mode 100644 index 0000000..f501a59 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_camera.png differ diff --git a/node_modules/serve-index/public/icons/page_white_cd.png b/node_modules/serve-index/public/icons/page_white_cd.png new file mode 100644 index 0000000..848bdaf Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_cd.png differ diff --git a/node_modules/serve-index/public/icons/page_white_code.png b/node_modules/serve-index/public/icons/page_white_code.png new file mode 100644 index 0000000..0c76bd1 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_code.png differ diff --git a/node_modules/serve-index/public/icons/page_white_code_red.png b/node_modules/serve-index/public/icons/page_white_code_red.png new file mode 100644 index 0000000..87a6914 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_code_red.png differ diff --git a/node_modules/serve-index/public/icons/page_white_coldfusion.png b/node_modules/serve-index/public/icons/page_white_coldfusion.png new file mode 100644 index 0000000..c66011f Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_coldfusion.png differ diff --git a/node_modules/serve-index/public/icons/page_white_compressed.png b/node_modules/serve-index/public/icons/page_white_compressed.png new file mode 100644 index 0000000..2b6b100 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_compressed.png differ diff --git a/node_modules/serve-index/public/icons/page_white_copy.png b/node_modules/serve-index/public/icons/page_white_copy.png new file mode 100644 index 0000000..a9f31a2 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_copy.png differ diff --git a/node_modules/serve-index/public/icons/page_white_cplusplus.png b/node_modules/serve-index/public/icons/page_white_cplusplus.png new file mode 100644 index 0000000..a87cf84 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_cplusplus.png differ diff --git a/node_modules/serve-index/public/icons/page_white_csharp.png b/node_modules/serve-index/public/icons/page_white_csharp.png new file mode 100644 index 0000000..ffb8fc9 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_csharp.png differ diff --git a/node_modules/serve-index/public/icons/page_white_cup.png b/node_modules/serve-index/public/icons/page_white_cup.png new file mode 100644 index 0000000..0a7d6f4 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_cup.png differ diff --git a/node_modules/serve-index/public/icons/page_white_database.png b/node_modules/serve-index/public/icons/page_white_database.png new file mode 100644 index 0000000..bddba1f Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_database.png differ diff --git a/node_modules/serve-index/public/icons/page_white_delete.png b/node_modules/serve-index/public/icons/page_white_delete.png new file mode 100644 index 0000000..af1ecaf Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_delete.png differ diff --git a/node_modules/serve-index/public/icons/page_white_dvd.png b/node_modules/serve-index/public/icons/page_white_dvd.png new file mode 100644 index 0000000..4cc537a Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_dvd.png differ diff --git a/node_modules/serve-index/public/icons/page_white_edit.png b/node_modules/serve-index/public/icons/page_white_edit.png new file mode 100644 index 0000000..b93e776 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_edit.png differ diff --git a/node_modules/serve-index/public/icons/page_white_error.png b/node_modules/serve-index/public/icons/page_white_error.png new file mode 100644 index 0000000..9fc5a0a Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_error.png differ diff --git a/node_modules/serve-index/public/icons/page_white_excel.png b/node_modules/serve-index/public/icons/page_white_excel.png new file mode 100644 index 0000000..b977d7e Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_excel.png differ diff --git a/node_modules/serve-index/public/icons/page_white_find.png b/node_modules/serve-index/public/icons/page_white_find.png new file mode 100644 index 0000000..5818436 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_find.png differ diff --git a/node_modules/serve-index/public/icons/page_white_flash.png b/node_modules/serve-index/public/icons/page_white_flash.png new file mode 100644 index 0000000..5769120 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_flash.png differ diff --git a/node_modules/serve-index/public/icons/page_white_freehand.png b/node_modules/serve-index/public/icons/page_white_freehand.png new file mode 100644 index 0000000..8d719df Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_freehand.png differ diff --git a/node_modules/serve-index/public/icons/page_white_gear.png b/node_modules/serve-index/public/icons/page_white_gear.png new file mode 100644 index 0000000..106f5aa Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_gear.png differ diff --git a/node_modules/serve-index/public/icons/page_white_get.png b/node_modules/serve-index/public/icons/page_white_get.png new file mode 100644 index 0000000..e4a1ecb Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_get.png differ diff --git a/node_modules/serve-index/public/icons/page_white_go.png b/node_modules/serve-index/public/icons/page_white_go.png new file mode 100644 index 0000000..7e62a92 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_go.png differ diff --git a/node_modules/serve-index/public/icons/page_white_h.png b/node_modules/serve-index/public/icons/page_white_h.png new file mode 100644 index 0000000..e902abb Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_h.png differ diff --git a/node_modules/serve-index/public/icons/page_white_horizontal.png b/node_modules/serve-index/public/icons/page_white_horizontal.png new file mode 100644 index 0000000..1d2d0a4 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_horizontal.png differ diff --git a/node_modules/serve-index/public/icons/page_white_key.png b/node_modules/serve-index/public/icons/page_white_key.png new file mode 100644 index 0000000..d616484 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_key.png differ diff --git a/node_modules/serve-index/public/icons/page_white_lightning.png b/node_modules/serve-index/public/icons/page_white_lightning.png new file mode 100644 index 0000000..7215d1e Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_lightning.png differ diff --git a/node_modules/serve-index/public/icons/page_white_link.png b/node_modules/serve-index/public/icons/page_white_link.png new file mode 100644 index 0000000..bf7bd1c Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_link.png differ diff --git a/node_modules/serve-index/public/icons/page_white_magnify.png b/node_modules/serve-index/public/icons/page_white_magnify.png new file mode 100644 index 0000000..f6b74cc Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_magnify.png differ diff --git a/node_modules/serve-index/public/icons/page_white_medal.png b/node_modules/serve-index/public/icons/page_white_medal.png new file mode 100644 index 0000000..d3fffb6 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_medal.png differ diff --git a/node_modules/serve-index/public/icons/page_white_office.png b/node_modules/serve-index/public/icons/page_white_office.png new file mode 100644 index 0000000..a65bcb3 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_office.png differ diff --git a/node_modules/serve-index/public/icons/page_white_paint.png b/node_modules/serve-index/public/icons/page_white_paint.png new file mode 100644 index 0000000..23a37b8 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_paint.png differ diff --git a/node_modules/serve-index/public/icons/page_white_paintbrush.png b/node_modules/serve-index/public/icons/page_white_paintbrush.png new file mode 100644 index 0000000..f907e44 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_paintbrush.png differ diff --git a/node_modules/serve-index/public/icons/page_white_paste.png b/node_modules/serve-index/public/icons/page_white_paste.png new file mode 100644 index 0000000..5b2cbb3 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_paste.png differ diff --git a/node_modules/serve-index/public/icons/page_white_php.png b/node_modules/serve-index/public/icons/page_white_php.png new file mode 100644 index 0000000..7868a25 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_php.png differ diff --git a/node_modules/serve-index/public/icons/page_white_picture.png b/node_modules/serve-index/public/icons/page_white_picture.png new file mode 100644 index 0000000..134b669 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_picture.png differ diff --git a/node_modules/serve-index/public/icons/page_white_powerpoint.png b/node_modules/serve-index/public/icons/page_white_powerpoint.png new file mode 100644 index 0000000..c4eff03 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_powerpoint.png differ diff --git a/node_modules/serve-index/public/icons/page_white_put.png b/node_modules/serve-index/public/icons/page_white_put.png new file mode 100644 index 0000000..884ffd6 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_put.png differ diff --git a/node_modules/serve-index/public/icons/page_white_ruby.png b/node_modules/serve-index/public/icons/page_white_ruby.png new file mode 100644 index 0000000..f59b7c4 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_ruby.png differ diff --git a/node_modules/serve-index/public/icons/page_white_stack.png b/node_modules/serve-index/public/icons/page_white_stack.png new file mode 100644 index 0000000..44084ad Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_stack.png differ diff --git a/node_modules/serve-index/public/icons/page_white_star.png b/node_modules/serve-index/public/icons/page_white_star.png new file mode 100644 index 0000000..3a1441c Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_star.png differ diff --git a/node_modules/serve-index/public/icons/page_white_swoosh.png b/node_modules/serve-index/public/icons/page_white_swoosh.png new file mode 100644 index 0000000..e770829 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_swoosh.png differ diff --git a/node_modules/serve-index/public/icons/page_white_text.png b/node_modules/serve-index/public/icons/page_white_text.png new file mode 100644 index 0000000..813f712 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_text.png differ diff --git a/node_modules/serve-index/public/icons/page_white_text_width.png b/node_modules/serve-index/public/icons/page_white_text_width.png new file mode 100644 index 0000000..d9cf132 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_text_width.png differ diff --git a/node_modules/serve-index/public/icons/page_white_tux.png b/node_modules/serve-index/public/icons/page_white_tux.png new file mode 100644 index 0000000..52699bf Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_tux.png differ diff --git a/node_modules/serve-index/public/icons/page_white_vector.png b/node_modules/serve-index/public/icons/page_white_vector.png new file mode 100644 index 0000000..4a05955 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_vector.png differ diff --git a/node_modules/serve-index/public/icons/page_white_visualstudio.png b/node_modules/serve-index/public/icons/page_white_visualstudio.png new file mode 100644 index 0000000..a0a433d Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_visualstudio.png differ diff --git a/node_modules/serve-index/public/icons/page_white_width.png b/node_modules/serve-index/public/icons/page_white_width.png new file mode 100644 index 0000000..1eb8809 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_width.png differ diff --git a/node_modules/serve-index/public/icons/page_white_word.png b/node_modules/serve-index/public/icons/page_white_word.png new file mode 100644 index 0000000..ae8ecbf Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_word.png differ diff --git a/node_modules/serve-index/public/icons/page_white_world.png b/node_modules/serve-index/public/icons/page_white_world.png new file mode 100644 index 0000000..6ed2490 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_world.png differ diff --git a/node_modules/serve-index/public/icons/page_white_wrench.png b/node_modules/serve-index/public/icons/page_white_wrench.png new file mode 100644 index 0000000..fecadd0 Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_wrench.png differ diff --git a/node_modules/serve-index/public/icons/page_white_zip.png b/node_modules/serve-index/public/icons/page_white_zip.png new file mode 100644 index 0000000..fd4bbcc Binary files /dev/null and b/node_modules/serve-index/public/icons/page_white_zip.png differ diff --git a/node_modules/serve-index/public/icons/page_word.png b/node_modules/serve-index/public/icons/page_word.png new file mode 100644 index 0000000..834cdfa Binary files /dev/null and b/node_modules/serve-index/public/icons/page_word.png differ diff --git a/node_modules/serve-index/public/icons/page_world.png b/node_modules/serve-index/public/icons/page_world.png new file mode 100644 index 0000000..b8895dd Binary files /dev/null and b/node_modules/serve-index/public/icons/page_world.png differ diff --git a/node_modules/serve-index/public/style.css b/node_modules/serve-index/public/style.css new file mode 100644 index 0000000..eb99dc9 --- /dev/null +++ b/node_modules/serve-index/public/style.css @@ -0,0 +1,257 @@ +* { + margin: 0; + padding: 0; + outline: 0; +} + +body { + padding: 80px 100px; + font: 13px "Helvetica Neue", "Lucida Grande", "Arial"; + background: #ECE9E9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ECE9E9)); + background: #ECE9E9 -moz-linear-gradient(top, #fff, #ECE9E9); + background-repeat: no-repeat; + color: #555; + -webkit-font-smoothing: antialiased; +} +h1, h2, h3 { + font-size: 22px; + color: #343434; +} +h1 em, h2 em { + padding: 0 5px; + font-weight: normal; +} +h1 { + font-size: 60px; +} +h2 { + margin-top: 10px; +} +h3 { + margin: 5px 0 10px 0; + padding-bottom: 5px; + border-bottom: 1px solid #eee; + font-size: 18px; +} +ul li { + list-style: none; +} +ul li:hover { + cursor: pointer; + color: #2e2e2e; +} +ul li .path { + padding-left: 5px; + font-weight: bold; +} +ul li .line { + padding-right: 5px; + font-style: italic; +} +ul li:first-child .path { + padding-left: 0; +} +p { + line-height: 1.5; +} +a { + color: #555; + text-decoration: none; +} +a:hover { + color: #303030; +} +#stacktrace { + margin-top: 15px; +} +.directory h1 { + margin-bottom: 15px; + font-size: 18px; +} +ul#files { + width: 100%; + height: 100%; + overflow: hidden; +} +ul#files li { + float: left; + width: 30%; + line-height: 25px; + margin: 1px; +} +ul#files li a { + display: block; + height: 25px; + border: 1px solid transparent; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + overflow: hidden; + white-space: nowrap; +} +ul#files li a:focus, +ul#files li a:hover { + background: rgba(255,255,255,0.65); + border: 1px solid #ececec; +} +ul#files li a.highlight { + -webkit-transition: background .4s ease-in-out; + background: #ffff4f; + border-color: #E9DC51; +} +#search { + display: block; + position: fixed; + top: 20px; + right: 20px; + width: 90px; + -webkit-transition: width ease 0.2s, opacity ease 0.4s; + -moz-transition: width ease 0.2s, opacity ease 0.4s; + -webkit-border-radius: 32px; + -moz-border-radius: 32px; + -webkit-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03); + -moz-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03); + -webkit-font-smoothing: antialiased; + text-align: left; + font: 13px "Helvetica Neue", Arial, sans-serif; + padding: 4px 10px; + border: none; + background: transparent; + margin-bottom: 0; + outline: none; + opacity: 0.7; + color: #888; +} +#search:focus { + width: 120px; + opacity: 1.0; +} + +/*views*/ +#files span { + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + text-indent: 10px; +} +#files .name { + background-repeat: no-repeat; +} +#files .icon .name { + text-indent: 28px; +} + +/*tiles*/ +.view-tiles .name { + width: 100%; + background-position: 8px 5px; +} +.view-tiles .size, +.view-tiles .date { + display: none; +} + +/*details*/ +ul#files.view-details li { + float: none; + display: block; + width: 90%; +} +ul#files.view-details li.header { + height: 25px; + background: #000; + color: #fff; + font-weight: bold; +} +.view-details .header { + border-radius: 5px; +} +.view-details .name { + width: 60%; + background-position: 8px 5px; +} +.view-details .size { + width: 10%; +} +.view-details .date { + width: 30%; +} +.view-details .size, +.view-details .date { + text-align: right; + direction: rtl; +} + +/*mobile*/ +@media (max-width: 768px) { + body { + font-size: 13px; + line-height: 16px; + padding: 0; + } + #search { + position: static; + width: 100%; + font-size: 2em; + line-height: 1.8em; + text-indent: 10px; + border: 0; + border-radius: 0; + padding: 10px 0; + margin: 0; + } + #search:focus { + width: 100%; + border: 0; + opacity: 1; + } + .directory h1 { + font-size: 2em; + line-height: 1.5em; + color: #fff; + background: #000; + padding: 15px 10px; + margin: 0; + } + ul#files { + border-top: 1px solid #cacaca; + } + ul#files li { + float: none; + width: auto !important; + display: block; + border-bottom: 1px solid #cacaca; + font-size: 2em; + line-height: 1.2em; + text-indent: 0; + margin: 0; + } + ul#files li:nth-child(odd) { + background: #e0e0e0; + } + ul#files li a { + height: auto; + border: 0; + border-radius: 0; + padding: 15px 10px; + } + ul#files li a:focus, + ul#files li a:hover { + border: 0; + } + #files .header, + #files .size, + #files .date { + display: none !important; + } + #files .name { + float: none; + display: inline-block; + width: 100%; + text-indent: 0; + background-position: 0 50%; + } + #files .icon .name { + text-indent: 41px; + } +} diff --git a/node_modules/tmp/.npmignore b/node_modules/tmp/.npmignore new file mode 100644 index 0000000..78f2710 --- /dev/null +++ b/node_modules/tmp/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +.idea/ diff --git a/node_modules/tmp/.travis.yml b/node_modules/tmp/.travis.yml new file mode 100644 index 0000000..0175d82 --- /dev/null +++ b/node_modules/tmp/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - "0.6" + - "0.8" + - "0.10" diff --git a/node_modules/tmp/README.md b/node_modules/tmp/README.md new file mode 100644 index 0000000..3a1a509 --- /dev/null +++ b/node_modules/tmp/README.md @@ -0,0 +1,162 @@ +# Tmp + +A simple temporary file and directory creator for [node.js.][1] + +[![Build Status](https://secure.travis-ci.org/raszi/node-tmp.png?branch=master)](http://travis-ci.org/raszi/node-tmp) + +## About + +The main difference between bruce's [node-temp][2] is that mine more +aggressively checks for the existence of the newly created temporary file and +creates the new file with `O_EXCL` instead of simple `O_CREAT | O_RDRW`, so it +is safer. + +The API is slightly different as well, Tmp does not yet provide synchronous +calls and all the parameters are optional. + +You can set whether you want to remove the temporary file on process exit or +not, and the destination directory can also be set. + +## How to install + +```bash +npm install tmp +``` + +## Usage + +### File creation + +Simple temporary file creation, the file will be unlinked on process exit. + +```javascript +var tmp = require('tmp'); + +tmp.file(function _tempFileCreated(err, path, fd) { + if (err) throw err; + + console.log("File: ", path); + console.log("Filedescriptor: ", fd); +}); +``` + +### Directory creation + +Simple temporary directory creation, it will be removed on process exit. + +If the directory still contains items on process exit, then it won't be removed. + +```javascript +var tmp = require('tmp'); + +tmp.dir(function _tempDirCreated(err, path) { + if (err) throw err; + + console.log("Dir: ", path); +}); +``` + +If you want to cleanup the directory even when there are entries in it, then +you can pass the `unsafeCleanup` option when creating it. + +### Filename generation + +It is possible with this library to generate a unique filename in the specified +directory. + +```javascript +var tmp = require('tmp'); + +tmp.tmpName(function _tempNameGenerated(err, path) { + if (err) throw err; + + console.log("Created temporary filename: ", path); +}); +``` + +## Advanced usage + +### File creation + +Creates a file with mode `0644`, prefix will be `prefix-` and postfix will be `.txt`. + +```javascript +var tmp = require('tmp'); + +tmp.file({ mode: 0644, prefix: 'prefix-', postfix: '.txt' }, function _tempFileCreated(err, path, fd) { + if (err) throw err; + + console.log("File: ", path); + console.log("Filedescriptor: ", fd); +}); +``` + +### Directory creation + +Creates a directory with mode `0755`, prefix will be `myTmpDir_`. + +```javascript +var tmp = require('tmp'); + +tmp.dir({ mode: 0750, prefix: 'myTmpDir_' }, function _tempDirCreated(err, path) { + if (err) throw err; + + console.log("Dir: ", path); +}); +``` + +### mkstemps like + +Creates a new temporary directory with mode `0700` and filename like `/tmp/tmp-nk2J1u`. + +```javascript +var tmp = require('tmp'); + +tmp.dir({ template: '/tmp/tmp-XXXXXX' }, function _tempDirCreated(err, path) { + if (err) throw err; + + console.log("Dir: ", path); +}); +``` + +### Filename generation + +The `tmpName()` function accepts the `prefix`, `postfix`, `dir`, etc. parameters also: + +```javascript +var tmp = require('tmp'); + +tmp.tmpName({ template: '/tmp/tmp-XXXXXX' }, function _tempNameGenerated(err, path) { + if (err) throw err; + + console.log("Created temporary filename: ", path); +}); +``` + +## Graceful cleanup + +One may want to cleanup the temporary files even when an uncaught exception +occurs. To enforce this, you can call the `setGracefulCleanup()` method: + +```javascript +var tmp = require('tmp'); + +tmp.setGracefulCleanup(); +``` + +## Options + +All options are optional :) + + * `mode`: the file mode to create with, it fallbacks to `0600` on file creation and `0700` on directory creation + * `prefix`: the optional prefix, fallbacks to `tmp-` if not provided + * `postfix`: the optional postfix, fallbacks to `.tmp` on file creation + * `template`: [`mkstemps`][3] like filename template, no default + * `dir`: the optional temporary directory, fallbacks to system default (guesses from environment) + * `tries`: how many times should the function try to get a unique filename before giving up, default `3` + * `keep`: signals that the temporary file or directory should not be deleted on exit, default is `false`, means delete + * `unsafeCleanup`: recursively removes the created temporary directory, even when it's not empty. default is `false` + +[1]: http://nodejs.org/ +[2]: https://github.com/bruce/node-temp +[3]: http://www.kernel.org/doc/man-pages/online/pages/man3/mkstemp.3.html diff --git a/node_modules/tmp/domain-test.js b/node_modules/tmp/domain-test.js new file mode 100644 index 0000000..47221bc --- /dev/null +++ b/node_modules/tmp/domain-test.js @@ -0,0 +1,13 @@ +var domain = require('domain'); + +//throw new Error('bazz'); + +var d = domain.create(); +d.on('error', function ( e ) { + console.log('error!!!', e); +}); + +d.run(function () { + console.log('hey'); + throw new Error('bazz'); +}); diff --git a/node_modules/tmp/lib/tmp.js b/node_modules/tmp/lib/tmp.js new file mode 100644 index 0000000..ea84faa --- /dev/null +++ b/node_modules/tmp/lib/tmp.js @@ -0,0 +1,307 @@ +/*! + * Tmp + * + * Copyright (c) 2011-2013 KARASZI Istvan + * + * MIT Licensed + */ + +/** + * Module dependencies. + */ +var + fs = require('fs'), + path = require('path'), + os = require('os'), + exists = fs.exists || path.exists, + tmpDir = os.tmpDir || _getTMPDir, + _c = require('constants'); + +/** + * The working inner variables. + */ +var + // store the actual TMP directory + _TMP = tmpDir(), + + // the random characters to choose from + randomChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz", + randomCharsLength = randomChars.length, + + // this will hold the objects need to be removed on exit + _removeObjects = [], + + _gracefulCleanup = false, + _uncaughtException = false; + +/** + * Gets the temp directory. + * + * @return {String} + * @api private + */ +function _getTMPDir() { + var tmpNames = [ 'TMPDIR', 'TMP', 'TEMP' ]; + + for (var i = 0, length = tmpNames.length; i < length; i++) { + if (_isUndefined(process.env[tmpNames[i]])) continue; + + return process.env[tmpNames[i]]; + } + + // fallback to the default + return '/tmp'; +} + +/** + * Checks whether the `obj` parameter is defined or not. + * + * @param {Object} obj + * @return {Boolean} + * @api private + */ +function _isUndefined(obj) { + return typeof obj === 'undefined'; +} + +/** + * Parses the function arguments. + * + * This function helps to have optional arguments. + * + * @param {Object} options + * @param {Function} callback + * @api private + */ +function _parseArguments(options, callback) { + if (!callback || typeof callback != "function") { + callback = options; + options = {}; + } + + return [ options, callback ]; +} + +/** + * Gets a temporary file name. + * + * @param {Object} opts + * @param {Function} cb + * @api private + */ +function _getTmpName(options, callback) { + var + args = _parseArguments(options, callback), + opts = args[0], + cb = args[1], + template = opts.template, + templateDefined = !_isUndefined(template), + tries = opts.tries || 3; + + if (isNaN(tries) || tries < 0) + return cb(new Error('Invalid tries')); + + if (templateDefined && !template.match(/XXXXXX/)) + return cb(new Error('Invalid template provided')); + + function _getName() { + + // prefix and postfix + if (!templateDefined) { + var name = [ + (_isUndefined(opts.prefix)) ? 'tmp-' : opts.prefix, + process.pid, + (Math.random() * 0x1000000000).toString(36), + opts.postfix + ].join(''); + + return path.join(opts.dir || _TMP, name); + } + + // mkstemps like template + var chars = []; + + for (var i = 0; i < 6; i++) { + chars.push(randomChars.substr(Math.floor(Math.random() * randomCharsLength), 1)); + } + + return template.replace(/XXXXXX/, chars.join('')); + } + + (function _getUniqueName() { + var name = _getName(); + + // check whether the path exists then retry if needed + exists(name, function _pathExists(pathExists) { + if (pathExists) { + if (tries-- > 0) return _getUniqueName(); + + return cb(new Error('Could not get a unique tmp filename, max tries reached')); + } + + cb(null, name); + }); + }()); +} + +/** + * Creates and opens a temporary file. + * + * @param {Object} options + * @param {Function} callback + * @api public + */ +function _createTmpFile(options, callback) { + var + args = _parseArguments(options, callback), + opts = args[0], + cb = args[1]; + + opts.postfix = (_isUndefined(opts.postfix)) ? '.tmp' : opts.postfix; + + // gets a temporary filename + _getTmpName(opts, function _tmpNameCreated(err, name) { + if (err) return cb(err); + + // create and open the file + fs.open(name, _c.O_CREAT | _c.O_EXCL | _c.O_RDWR, opts.mode || 0600, function _fileCreated(err, fd) { + if (err) return cb(err); + + var removeCallback = _prepareRemoveCallback(fs.unlinkSync.bind(fs), name); + + if (!opts.keep) { + _removeObjects.unshift(removeCallback); + } + + cb(null, name, fd, removeCallback); + }); + }); +} + +/** + * Removes files and folders in a directory recursively. + * + * @param {String} dir + */ +function _rmdirRecursiveSync(dir) { + var files = fs.readdirSync(dir); + + for (var i = 0, length = files.length; i < length; i++) { + var file = path.join(dir, files[i]); + // lstat so we don't recurse into symlinked directories. + var stat = fs.lstatSync(file); + + if (stat.isDirectory()) { + _rmdirRecursiveSync(file); + } else { + fs.unlinkSync(file); + } + } + + fs.rmdirSync(dir); +} + +/** + * + * @param {Function} removeFunction + * @param {String} path + * @returns {Function} + * @private + */ +function _prepareRemoveCallback(removeFunction, path) { + var called = false; + return function() { + if (called) { + return; + } + + removeFunction(path); + + called = true; + }; +} + +/** + * Creates a temporary directory. + * + * @param {Object} options + * @param {Function} callback + * @api public + */ +function _createTmpDir(options, callback) { + var + args = _parseArguments(options, callback), + opts = args[0], + cb = args[1]; + + // gets a temporary filename + _getTmpName(opts, function _tmpNameCreated(err, name) { + if (err) return cb(err); + + // create the directory + fs.mkdir(name, opts.mode || 0700, function _dirCreated(err) { + if (err) return cb(err); + + var removeCallback = _prepareRemoveCallback( + opts.unsafeCleanup + ? _rmdirRecursiveSync + : fs.rmdirSync.bind(fs), + name + ); + + if (!opts.keep) { + _removeObjects.unshift(removeCallback); + } + + cb(null, name, removeCallback); + }); + }); +} + +/** + * The garbage collector. + * + * @api private + */ +function _garbageCollector() { + if (_uncaughtException && !_gracefulCleanup) { + return; + } + + for (var i = 0, length = _removeObjects.length; i < length; i++) { + try { + _removeObjects[i].call(null); + } catch (e) { + // already removed? + } + } +} + +function _setGracefulCleanup() { + _gracefulCleanup = true; +} + +var version = process.versions.node.split('.').map(function (value) { + return parseInt(value, 10); +}); + +if (version[0] === 0 && (version[1] < 9 || version[1] === 9 && version[2] < 5)) { + process.addListener('uncaughtException', function _uncaughtExceptionThrown( err ) { + _uncaughtException = true; + _garbageCollector(); + + throw err; + }); +} + +process.addListener('exit', function _exit(code) { + if (code) _uncaughtException = true; + _garbageCollector(); +}); + +// exporting all the needed methods +module.exports.tmpdir = _TMP; +module.exports.dir = _createTmpDir; +module.exports.file = _createTmpFile; +module.exports.tmpName = _getTmpName; +module.exports.setGracefulCleanup = _setGracefulCleanup; diff --git a/node_modules/tmp/package.json b/node_modules/tmp/package.json new file mode 100644 index 0000000..7d2c361 --- /dev/null +++ b/node_modules/tmp/package.json @@ -0,0 +1,48 @@ +{ + "name": "tmp", + "version": "0.0.24", + "description": "Temporary file and directory creator", + "author": { + "name": "KARASZI István", + "email": "github@spam.raszi.hu", + "url": "http://raszi.hu/" + }, + "homepage": "http://github.com/raszi/node-tmp", + "keywords": [ + "temporary", + "tmp", + "temp", + "tempdir", + "tempfile", + "tmpdir", + "tmpfile" + ], + "licenses": [ + { + "type": "MIT", + "url": "http://opensource.org/licenses/MIT" + } + ], + "repository": { + "type": "git", + "url": "git://github.com/raszi/node-tmp.git" + }, + "bugs": { + "url": "http://github.com/raszi/node-tmp/issues" + }, + "main": "lib/tmp.js", + "scripts": { + "test": "vows test/*-test.js" + }, + "engines": { + "node": ">=0.4.0" + }, + "dependencies": {}, + "devDependencies": { + "vows": "~0.7.0" + }, + "readme": "# Tmp\n\nA simple temporary file and directory creator for [node.js.][1]\n\n[![Build Status](https://secure.travis-ci.org/raszi/node-tmp.png?branch=master)](http://travis-ci.org/raszi/node-tmp)\n\n## About\n\nThe main difference between bruce's [node-temp][2] is that mine more\naggressively checks for the existence of the newly created temporary file and\ncreates the new file with `O_EXCL` instead of simple `O_CREAT | O_RDRW`, so it\nis safer.\n\nThe API is slightly different as well, Tmp does not yet provide synchronous\ncalls and all the parameters are optional.\n\nYou can set whether you want to remove the temporary file on process exit or\nnot, and the destination directory can also be set.\n\n## How to install\n\n```bash\nnpm install tmp\n```\n\n## Usage\n\n### File creation\n\nSimple temporary file creation, the file will be unlinked on process exit.\n\n```javascript\nvar tmp = require('tmp');\n\ntmp.file(function _tempFileCreated(err, path, fd) {\n if (err) throw err;\n\n console.log(\"File: \", path);\n console.log(\"Filedescriptor: \", fd);\n});\n```\n\n### Directory creation\n\nSimple temporary directory creation, it will be removed on process exit.\n\nIf the directory still contains items on process exit, then it won't be removed.\n\n```javascript\nvar tmp = require('tmp');\n\ntmp.dir(function _tempDirCreated(err, path) {\n if (err) throw err;\n\n console.log(\"Dir: \", path);\n});\n```\n\nIf you want to cleanup the directory even when there are entries in it, then\nyou can pass the `unsafeCleanup` option when creating it.\n\n### Filename generation\n\nIt is possible with this library to generate a unique filename in the specified\ndirectory.\n\n```javascript\nvar tmp = require('tmp');\n\ntmp.tmpName(function _tempNameGenerated(err, path) {\n if (err) throw err;\n\n console.log(\"Created temporary filename: \", path);\n});\n```\n\n## Advanced usage\n\n### File creation\n\nCreates a file with mode `0644`, prefix will be `prefix-` and postfix will be `.txt`.\n\n```javascript\nvar tmp = require('tmp');\n\ntmp.file({ mode: 0644, prefix: 'prefix-', postfix: '.txt' }, function _tempFileCreated(err, path, fd) {\n if (err) throw err;\n\n console.log(\"File: \", path);\n console.log(\"Filedescriptor: \", fd);\n});\n```\n\n### Directory creation\n\nCreates a directory with mode `0755`, prefix will be `myTmpDir_`.\n\n```javascript\nvar tmp = require('tmp');\n\ntmp.dir({ mode: 0750, prefix: 'myTmpDir_' }, function _tempDirCreated(err, path) {\n if (err) throw err;\n\n console.log(\"Dir: \", path);\n});\n```\n\n### mkstemps like\n\nCreates a new temporary directory with mode `0700` and filename like `/tmp/tmp-nk2J1u`.\n\n```javascript\nvar tmp = require('tmp');\n\ntmp.dir({ template: '/tmp/tmp-XXXXXX' }, function _tempDirCreated(err, path) {\n if (err) throw err;\n\n console.log(\"Dir: \", path);\n});\n```\n\n### Filename generation\n\nThe `tmpName()` function accepts the `prefix`, `postfix`, `dir`, etc. parameters also:\n\n```javascript\nvar tmp = require('tmp');\n\ntmp.tmpName({ template: '/tmp/tmp-XXXXXX' }, function _tempNameGenerated(err, path) {\n if (err) throw err;\n\n console.log(\"Created temporary filename: \", path);\n});\n```\n\n## Graceful cleanup\n\nOne may want to cleanup the temporary files even when an uncaught exception\noccurs. To enforce this, you can call the `setGracefulCleanup()` method:\n\n```javascript\nvar tmp = require('tmp');\n\ntmp.setGracefulCleanup();\n```\n\n## Options\n\nAll options are optional :)\n\n * `mode`: the file mode to create with, it fallbacks to `0600` on file creation and `0700` on directory creation\n * `prefix`: the optional prefix, fallbacks to `tmp-` if not provided\n * `postfix`: the optional postfix, fallbacks to `.tmp` on file creation\n * `template`: [`mkstemps`][3] like filename template, no default\n * `dir`: the optional temporary directory, fallbacks to system default (guesses from environment)\n * `tries`: how many times should the function try to get a unique filename before giving up, default `3`\n * `keep`: signals that the temporary file or directory should not be deleted on exit, default is `false`, means delete\n * `unsafeCleanup`: recursively removes the created temporary directory, even when it's not empty. default is `false`\n\n[1]: http://nodejs.org/\n[2]: https://github.com/bruce/node-temp\n[3]: http://www.kernel.org/doc/man-pages/online/pages/man3/mkstemp.3.html\n", + "readmeFilename": "README.md", + "_id": "tmp@0.0.24", + "_from": "tmp@0.0.24" +} diff --git a/node_modules/tmp/test-all.sh b/node_modules/tmp/test-all.sh new file mode 100755 index 0000000..4734d60 --- /dev/null +++ b/node_modules/tmp/test-all.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +#node06 +for node in node08 node; do + command -v ${node} > /dev/null 2>&1 || continue + + echo "Testing with $(${node} --version)..." + ${node} node_modules/vows/bin/vows test/*test.js +done diff --git a/node_modules/tmp/test.js b/node_modules/tmp/test.js new file mode 100644 index 0000000..8058221 --- /dev/null +++ b/node_modules/tmp/test.js @@ -0,0 +1,6 @@ +process.on('uncaughtException', function ( err ) { + console.log('blah'); + throw err; +}); + +throw "on purpose" diff --git a/node_modules/tmp/test/base.js b/node_modules/tmp/test/base.js new file mode 100644 index 0000000..498d8fb --- /dev/null +++ b/node_modules/tmp/test/base.js @@ -0,0 +1,74 @@ +var + assert = require('assert'), + path = require('path'), + exec = require('child_process').exec; + +function _spawnTestWithError(testFile, params, cb) { + _spawnTest(true, testFile, params, cb); +} + +function _spawnTestWithoutError(testFile, params, cb) { + _spawnTest(false, testFile, params, cb); +} + +function _spawnTest(passError, testFile, params, cb) { + var + filename, + node_path = process.argv[0], + command = [ node_path, path.join(__dirname, testFile) ].concat(params).join(' '); + + exec(command, function _execDone(err, stdout, stderr) { + if (passError) { + if (err) { + return cb(err); + } else if (stderr.length > 0) { + return cb(stderr.toString()); + } + } + + return cb(null, stdout.toString()); + }); +} + +function _testStat(stat, mode) { + assert.equal(stat.uid, process.getuid(), 'should have the same UID'); + assert.equal(stat.gid, process.getgid(), 'should have the same GUID'); + assert.equal(stat.mode, mode); +} + +function _testPrefix(prefix) { + return function _testPrefixGenerated(err, name, fd) { + assert.equal(path.basename(name).slice(0, prefix.length), prefix, 'should have the provided prefix'); + }; +} + +function _testPostfix(postfix) { + return function _testPostfixGenerated(err, name, fd) { + assert.equal(name.slice(name.length - postfix.length, name.length), postfix, 'should have the provided postfix'); + }; +} + +function _testKeep(type, keep, cb) { + _spawnTestWithError('keep.js', [ type, keep ], cb); +} + +function _testGraceful(type, graceful, cb) { + _spawnTestWithoutError('graceful.js', [ type, graceful ], cb); +} + +function _assertName(err, name) { + assert.isString(name); + assert.isNotZero(name.length); +} + +function _testUnsafeCleanup(unsafe, cb) { + _spawnTestWithoutError('unsafe.js', [ 'dir', unsafe ], cb); +} + +module.exports.testStat = _testStat; +module.exports.testPrefix = _testPrefix; +module.exports.testPostfix = _testPostfix; +module.exports.testKeep = _testKeep; +module.exports.testGraceful = _testGraceful; +module.exports.assertName = _assertName; +module.exports.testUnsafeCleanup = _testUnsafeCleanup; diff --git a/node_modules/tmp/test/dir-test.js b/node_modules/tmp/test/dir-test.js new file mode 100644 index 0000000..2e4e529 --- /dev/null +++ b/node_modules/tmp/test/dir-test.js @@ -0,0 +1,196 @@ +var + vows = require('vows'), + assert = require('assert'), + + path = require('path'), + fs = require('fs'), + existsSync = fs.existsSync || path.existsSync, + + tmp = require('../lib/tmp.js'), + Test = require('./base.js'); + + +function _testDir(mode) { + return function _testDirGenerated(err, name) { + assert.ok(existsSync(name), 'should exist'); + + var stat = fs.statSync(name); + assert.ok(stat.isDirectory(), 'should be a directory'); + + Test.testStat(stat, mode); + }; +} + +vows.describe('Directory creation').addBatch({ + 'when using without parameters': { + topic: function () { + tmp.dir(this.callback); + }, + + 'should be a directory': _testDir(040700), + 'should have the default prefix': Test.testPrefix('tmp-') + }, + + 'when using with prefix': { + topic: function () { + tmp.dir({ prefix: 'something' }, this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a directory': _testDir(040700), + 'should have the provided prefix': Test.testPrefix('something') + }, + + 'when using with postfix': { + topic: function () { + tmp.dir({ postfix: '.txt' }, this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a directory': _testDir(040700), + 'should have the provided postfix': Test.testPostfix('.txt') + }, + + 'when using template': { + topic: function () { + tmp.dir({ template: path.join(tmp.tmpdir, 'clike-XXXXXX-postfix') }, this.callback); + }, + + 'should not return with error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a file': _testDir(040700), + 'should have the provided prefix': Test.testPrefix('clike-'), + 'should have the provided postfix': Test.testPostfix('-postfix') + }, + + 'when using multiple options': { + topic: function () { + tmp.dir({ prefix: 'foo', postfix: 'bar', mode: 0750 }, this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a directory': _testDir(040750), + 'should have the provided prefix': Test.testPrefix('foo'), + 'should have the provided postfix': Test.testPostfix('bar') + }, + + 'when using multiple options and mode': { + topic: function () { + tmp.dir({ prefix: 'complicated', postfix: 'options', mode: 0755 }, this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a directory': _testDir(040755), + 'should have the provided prefix': Test.testPrefix('complicated'), + 'should have the provided postfix': Test.testPostfix('options') + }, + + 'no tries': { + topic: function () { + tmp.dir({ tries: -1 }, this.callback); + }, + + 'should return with an error': assert.isObject + }, + + 'keep testing': { + topic: function () { + Test.testKeep('dir', '1', this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a dir': function (err, name) { + _testDir(040700)(err, name); + fs.rmdirSync(name); + } + }, + + 'unlink testing': { + topic: function () { + Test.testKeep('dir', '0', this.callback); + }, + + 'should not return with error': assert.isNull, + 'should return with a name': Test.assertName, + 'should not exist': function (err, name) { + assert.ok(!existsSync(name), "Directory should be removed"); + } + }, + + 'non graceful testing': { + topic: function () { + Test.testGraceful('dir', '0', this.callback); + }, + + 'should not return with error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a dir': function (err, name) { + _testDir(040700)(err, name); + fs.rmdirSync(name); + } + }, + + 'graceful testing': { + topic: function () { + Test.testGraceful('dir', '1', this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should not exist': function (err, name) { + assert.ok(!existsSync(name), "Directory should be removed"); + } + }, + + 'unsafeCleanup === true': { + topic: function () { + Test.testUnsafeCleanup('1', this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should not exist': function (err, name) { + assert.ok(!existsSync(name), "Directory should be removed"); + }, + 'should remove symlinked dir': function(err, name) { + assert.ok( + !existsSync(name + '/symlinkme-target'), + 'should remove target' + ); + }, + 'should not remove contents of symlink dir': function(err, name) { + assert.ok( + existsSync(__dirname + '/symlinkme/file.js'), + 'should not remove symlinked directory\'s content' + ); + } + }, + + 'unsafeCleanup === false': { + topic: function () { + Test.testUnsafeCleanup('0', this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a directory': _testDir(040700) + }, + + 'remove callback': { + topic: function () { + tmp.dir(this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'removeCallback should remove directory': function (_err, name, removeCallback) { + removeCallback(); + assert.ok(!existsSync(name), "Directory should be removed"); + } + } +}).exportTo(module); diff --git a/node_modules/tmp/test/file-test.js b/node_modules/tmp/test/file-test.js new file mode 100644 index 0000000..d9605b3 --- /dev/null +++ b/node_modules/tmp/test/file-test.js @@ -0,0 +1,177 @@ +var + vows = require('vows'), + assert = require('assert'), + + path = require('path'), + fs = require('fs'), + existsSync = fs.existsSync || path.existsSync, + + tmp = require('../lib/tmp.js'), + Test = require('./base.js'); + + +function _testFile(mode, fdTest) { + return function _testFileGenerated(err, name, fd) { + assert.ok(existsSync(name), 'should exist'); + + var stat = fs.statSync(name); + assert.equal(stat.size, 0, 'should have zero size'); + assert.ok(stat.isFile(), 'should be a file'); + + Test.testStat(stat, mode); + + // check with fstat as well (fd checking) + if (fdTest) { + var fstat = fs.fstatSync(fd); + assert.deepEqual(fstat, stat, 'fstat results should be the same'); + + var data = new Buffer('something'); + assert.equal(fs.writeSync(fd, data, 0, data.length, 0), data.length, 'should be writable'); + assert.ok(!fs.closeSync(fd), 'should not return with error'); + } + }; +} + +vows.describe('File creation').addBatch({ + 'when using without parameters': { + topic: function () { + tmp.file(this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a file': _testFile(0100600, true), + 'should have the default prefix': Test.testPrefix('tmp-'), + 'should have the default postfix': Test.testPostfix('.tmp') + }, + + 'when using with prefix': { + topic: function () { + tmp.file({ prefix: 'something' }, this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a file': _testFile(0100600, true), + 'should have the provided prefix': Test.testPrefix('something') + }, + + 'when using with postfix': { + topic: function () { + tmp.file({ postfix: '.txt' }, this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a file': _testFile(0100600, true), + 'should have the provided postfix': Test.testPostfix('.txt') + }, + + 'when using template': { + topic: function () { + tmp.file({ template: path.join(tmp.tmpdir, 'clike-XXXXXX-postfix') }, this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a file': _testFile(0100600, true), + 'should have the provided prefix': Test.testPrefix('clike-'), + 'should have the provided postfix': Test.testPostfix('-postfix') + }, + + 'when using multiple options': { + topic: function () { + tmp.file({ prefix: 'foo', postfix: 'bar', mode: 0640 }, this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a file': _testFile(0100640, true), + 'should have the provided prefix': Test.testPrefix('foo'), + 'should have the provided postfix': Test.testPostfix('bar') + }, + + 'when using multiple options and mode': { + topic: function () { + tmp.file({ prefix: 'complicated', postfix: 'options', mode: 0644 }, this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a file': _testFile(0100644, true), + 'should have the provided prefix': Test.testPrefix('complicated'), + 'should have the provided postfix': Test.testPostfix('options') + }, + + 'no tries': { + topic: function () { + tmp.file({ tries: -1 }, this.callback); + }, + + 'should not be created': assert.isObject + }, + + 'keep testing': { + topic: function () { + Test.testKeep('file', '1', this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a file': function (err, name) { + _testFile(0100600, false)(err, name, null); + fs.unlinkSync(name); + } + }, + + 'unlink testing': { + topic: function () { + Test.testKeep('file', '0', this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should not exist': function (err, name) { + assert.ok(!existsSync(name), "File should be removed"); + } + }, + + 'non graceful testing': { + topic: function () { + Test.testGraceful('file', '0', this.callback); + }, + + 'should not return with error': assert.isNull, + 'should return with a name': Test.assertName, + 'should be a file': function (err, name) { + _testFile(0100600, false)(err, name, null); + fs.unlinkSync(name); + } + }, + + 'graceful testing': { + topic: function () { + Test.testGraceful('file', '1', this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'should not exist': function (err, name) { + assert.ok(!existsSync(name), "File should be removed"); + } + }, + + 'remove callback': { + topic: function () { + tmp.file(this.callback); + }, + + 'should not return with an error': assert.isNull, + 'should return with a name': Test.assertName, + 'removeCallback should remove file': function (_err, name, _fd, removeCallback) { + removeCallback(); + assert.ok(!existsSync(name), "File should be removed"); + } + } + +}).exportTo(module); diff --git a/node_modules/tmp/test/graceful.js b/node_modules/tmp/test/graceful.js new file mode 100644 index 0000000..c898656 --- /dev/null +++ b/node_modules/tmp/test/graceful.js @@ -0,0 +1,15 @@ +var + tmp = require('../lib/tmp'), + spawn = require('./spawn'); + +var graceful = spawn.arg; + +if (graceful) { + tmp.setGracefulCleanup(); +} + +spawn.tmpFunction(function (err, name) { + spawn.out(name, function () { + throw new Error("Thrown on purpose"); + }); +}); diff --git a/node_modules/tmp/test/keep.js b/node_modules/tmp/test/keep.js new file mode 100644 index 0000000..9538605 --- /dev/null +++ b/node_modules/tmp/test/keep.js @@ -0,0 +1,11 @@ +var spawn = require('./spawn'); + +var keep = spawn.arg; + +spawn.tmpFunction({ keep: keep }, function (err, name) { + if (err) { + spawn.err(err, spawn.exit); + } else { + spawn.out(name, spawn.exit); + } +}); diff --git a/node_modules/tmp/test/name-test.js b/node_modules/tmp/test/name-test.js new file mode 100644 index 0000000..a242c21 --- /dev/null +++ b/node_modules/tmp/test/name-test.js @@ -0,0 +1,82 @@ +var + vows = require('vows'), + assert = require('assert'), + + path = require('path'), + + tmp = require('../lib/tmp.js'), + Test = require('./base.js'); + +vows.describe('Name creation').addBatch({ + 'when using without parameters': { + topic: function () { + tmp.tmpName(this.callback); + }, + + 'should not return with error': assert.isNull, + 'should have the default prefix': Test.testPrefix('tmp-') + }, + + 'when using with prefix': { + topic: function () { + tmp.tmpName({ prefix: 'something' }, this.callback); + }, + + 'should not return with error': assert.isNull, + 'should have the provided prefix': Test.testPrefix('something') + }, + + 'when using with postfix': { + topic: function () { + tmp.tmpName({ postfix: '.txt' }, this.callback); + }, + + 'should not return with error': assert.isNull, + 'should have the provided postfix': Test.testPostfix('.txt') + + }, + + 'when using template': { + topic: function () { + tmp.tmpName({ template: path.join(tmp.tmpdir, 'clike-XXXXXX-postfix') }, this.callback); + }, + + 'should not return with error': assert.isNull, + 'should have the provided prefix': Test.testPrefix('clike-'), + 'should have the provided postfix': Test.testPostfix('-postfix'), + 'should have template filled': function (err, name) { + assert.isTrue(/[a-zA-Z0-9]{6}/.test(name)); + } + }, + + 'when using multiple options': { + topic: function () { + tmp.tmpName({ prefix: 'foo', postfix: 'bar', tries: 5 }, this.callback); + }, + + 'should not return with error': assert.isNull, + 'should have the provided prefix': Test.testPrefix('foo'), + 'should have the provided postfix': Test.testPostfix('bar') + }, + + 'no tries': { + topic: function () { + tmp.tmpName({ tries: -1 }, this.callback); + }, + + 'should fail': function (err, name) { + assert.isObject(err); + } + }, + + 'tries not numeric': { + topic: function () { + tmp.tmpName({ tries: 'hello'}, this.callback); + }, + + 'should fail': function (err, name) { + assert.isObject(err); + } + } + +}).exportTo(module); diff --git a/node_modules/tmp/test/spawn.js b/node_modules/tmp/test/spawn.js new file mode 100644 index 0000000..6468eb3 --- /dev/null +++ b/node_modules/tmp/test/spawn.js @@ -0,0 +1,32 @@ +var + fs = require('fs'), + tmp = require('../lib/tmp'); + +function _writeSync(stream, str, cb) { + var flushed = stream.write(str); + if (flushed) { + return cb(null); + } + + stream.once('drain', function _flushed() { + cb(null); + }); +} + +module.exports.out = function (str, cb) { + _writeSync(process.stdout, str, cb); +}; + +module.exports.err = function (str, cb) { + _writeSync(process.stderr, str, cb); +}; + +module.exports.exit = function () { + process.exit(0); +}; + +var type = process.argv[2]; +module.exports.tmpFunction = (type == 'file') ? tmp.file : tmp.dir; + +var arg = (process.argv[3] && parseInt(process.argv[3], 10) === 1) ? true : false; +module.exports.arg = arg; diff --git a/node_modules/tmp/test/symlinkme/file.js b/node_modules/tmp/test/symlinkme/file.js new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/tmp/test/unsafe.js b/node_modules/tmp/test/unsafe.js new file mode 100644 index 0000000..73e4fb3 --- /dev/null +++ b/node_modules/tmp/test/unsafe.js @@ -0,0 +1,30 @@ +var + fs = require('fs'), + join = require('path').join, + spawn = require('./spawn'); + +var unsafe = spawn.arg; +spawn.tmpFunction({ unsafeCleanup: unsafe }, function (err, name) { + if (err) { + spawn.err(err, spawn.exit); + return; + } + + try { + // file that should be removed + var fd = fs.openSync(join(name, 'should-be-removed.file'), 'w'); + fs.closeSync(fd); + + // in tree source + var symlinkSource = join(__dirname, 'symlinkme'); + // testing target + var symlinkTarget = join(name, 'symlinkme-target'); + + // symlink that should be removed but the contents should be preserved. + fs.symlinkSync(symlinkSource, symlinkTarget, 'dir'); + + spawn.out(name, spawn.exit); + } catch (e) { + spawn.err(e.toString(), spawn.exit); + } +}); diff --git a/node_modules/ws/.npmignore b/node_modules/ws/.npmignore new file mode 100644 index 0000000..1eba800 --- /dev/null +++ b/node_modules/ws/.npmignore @@ -0,0 +1,11 @@ +npm-debug.log +node_modules +.*.swp +.lock-* +build + +bench +doc +examples +test + diff --git a/node_modules/ws/.travis.yml b/node_modules/ws/.travis.yml new file mode 100644 index 0000000..ccb864f --- /dev/null +++ b/node_modules/ws/.travis.yml @@ -0,0 +1,29 @@ +language: node_js +sudo: false +npm_args: --ws:native +node_js: + - "4" + - "3" + - "2" + - "1" + - "0.12" + - "0.11" + - "0.10" + - "0.9" + - "0.8" +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-4.9 + - g++-4.9 +before_install: + - export CC="gcc-4.9" CXX="g++-4.9" + - "if [[ $(node --version) == v0.8.* ]]; then npm install -g npm@2.1.18; fi" +matrix: + fast_finish: true + allow_failures: + - node_js: "0.11" + - node_js: "0.9" + - node_js: "0.8" diff --git a/node_modules/ws/Makefile b/node_modules/ws/Makefile new file mode 100644 index 0000000..00f19fa --- /dev/null +++ b/node_modules/ws/Makefile @@ -0,0 +1,40 @@ +ALL_TESTS = $(shell find test/ -name '*.test.js') +ALL_INTEGRATION = $(shell find test/ -name '*.integration.js') + +all: + node-gyp configure build + +clean: + node-gyp clean + +run-tests: + @./node_modules/.bin/mocha \ + -t 5000 \ + -s 2400 \ + $(TESTFLAGS) \ + $(TESTS) + +run-integrationtests: + @./node_modules/.bin/mocha \ + -t 5000 \ + -s 6000 \ + $(TESTFLAGS) \ + $(TESTS) + +test: + @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests + +integrationtest: + @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_INTEGRATION)" run-integrationtests + +benchmark: + @node bench/sender.benchmark.js + @node bench/parser.benchmark.js + +autobahn: + @NODE_PATH=lib node test/autobahn.js + +autobahn-server: + @NODE_PATH=lib node test/autobahn-server.js + +.PHONY: test diff --git a/node_modules/ws/README.md b/node_modules/ws/README.md new file mode 100644 index 0000000..9647d08 --- /dev/null +++ b/node_modules/ws/README.md @@ -0,0 +1,225 @@ +# ws: a node.js websocket library + +[![Build Status](https://travis-ci.org/websockets/ws.svg?branch=master)](https://travis-ci.org/websockets/ws) + +`ws` is a simple to use WebSocket implementation, up-to-date against RFC-6455, +and [probably the fastest WebSocket library for node.js][archive]. + +Passes the quite extensive Autobahn test suite. See http://websockets.github.com/ws +for the full reports. + +## Protocol support + +* **Hixie draft 76** (Old and deprecated, but still in use by Safari and Opera. + Added to ws version 0.4.2, but server only. Can be disabled by setting the + `disableHixie` option to true.) +* **HyBi drafts 07-12** (Use the option `protocolVersion: 8`) +* **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`) + +### Installing + +``` +npm install --save ws +``` + +### Sending and receiving text data + +```js +var WebSocket = require('ws'); +var ws = new WebSocket('ws://www.host.com/path'); + +ws.on('open', function open() { + ws.send('something'); +}); + +ws.on('message', function(data, flags) { + // flags.binary will be set if a binary data is received. + // flags.masked will be set if the data was masked. +}); +``` + +### Sending binary data + +```js +var WebSocket = require('ws'); +var ws = new WebSocket('ws://www.host.com/path'); + +ws.on('open', function open() { + var array = new Float32Array(5); + + for (var i = 0; i < array.length; ++i) { + array[i] = i / 2; + } + + ws.send(array, { binary: true, mask: true }); +}); +``` + +Setting `mask`, as done for the send options above, will cause the data to be +masked according to the WebSocket protocol. The same option applies for text +data. + +### Server example + +```js +var WebSocketServer = require('ws').Server + , wss = new WebSocketServer({ port: 8080 }); + +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(message) { + console.log('received: %s', message); + }); + + ws.send('something'); +}); +``` + +### ExpressJS example + +```js +var server = require('http').createServer() + , url = require('url') + , WebSocketServer = require('ws').Server + , wss = new WebSocketServer({ server: server }) + , express = require('express') + , app = express() + , port = 4080; + +app.use(function (req, res) { + res.send({ msg: "hello" }); +}); + +wss.on('connection', function connection(ws) { + var location = url.parse(ws.upgradeReq.url, true); + // you might use location.query.access_token to authenticate or share sessions + // or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312) + + ws.on('message', function incoming(message) { + console.log('received: %s', message); + }); + + ws.send('something'); +}); + +server.on('request', app); +server.listen(port, function () { console.log('Listening on ' + server.address().port) }); +``` + +### Server sending broadcast data + +```js +var WebSocketServer = require('ws').Server + , wss = new WebSocketServer({ port: 8080 }); + +wss.broadcast = function broadcast(data) { + wss.clients.forEach(function each(client) { + client.send(data); + }); +}; +``` + +### Error handling best practices + +```js +// If the WebSocket is closed before the following send is attempted +ws.send('something'); + +// Errors (both immediate and async write errors) can be detected in an optional +// callback. The callback is also the only way of being notified that data has +// actually been sent. +ws.send('something', function ack(error) { + // if error is not defined, the send has been completed, + // otherwise the error object will indicate what failed. +}); + +// Immediate errors can also be handled with try/catch-blocks, but **note** that +// since sends are inherently asynchronous, socket write failures will *not* be +// captured when this technique is used. +try { ws.send('something'); } +catch (e) { /* handle error */ } +``` + +### echo.websocket.org demo + +```js +var WebSocket = require('ws'); +var ws = new WebSocket('ws://echo.websocket.org/', { + protocolVersion: 8, + origin: 'http://websocket.org' +}); + +ws.on('open', function open() { + console.log('connected'); + ws.send(Date.now().toString(), {mask: true}); +}); + +ws.on('close', function close() { + console.log('disconnected'); +}); + +ws.on('message', function message(data, flags) { + console.log('Roundtrip time: ' + (Date.now() - parseInt(data)) + 'ms', flags); + + setTimeout(function timeout() { + ws.send(Date.now().toString(), {mask: true}); + }, 500); +}); +``` + +### Browserify users +When including ws via a browserify bundle, ws returns global.WebSocket which has slightly different API. +You should use the standard WebSockets API instead. + +https://developer.mozilla.org/en-US/docs/WebSockets/Writing_WebSocket_client_applications#Availability_of_WebSockets + + +### Other examples + +For a full example with a browser client communicating with a ws server, see the +examples folder. + +Note that the usage together with Express 3.0 is quite different from Express +2.x. The difference is expressed in the two different serverstats-examples. + +Otherwise, see the test cases. + +### Running the tests + +``` +make test +``` + +## API Docs + +See [`/doc/ws.md`](https://github.com/websockets/ws/blob/master/doc/ws.md) for Node.js-like docs for the ws classes. + +## Changelog + +We're using the GitHub [`releases`](https://github.com/websockets/ws/releases) for changelog entries. + +## License + +(The MIT License) + +Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +[archive]: http://web.archive.org/web/20130314230536/http://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs diff --git a/node_modules/ws/index.js b/node_modules/ws/index.js new file mode 100644 index 0000000..a7e8644 --- /dev/null +++ b/node_modules/ws/index.js @@ -0,0 +1,49 @@ +'use strict'; + +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var WS = module.exports = require('./lib/WebSocket'); + +WS.Server = require('./lib/WebSocketServer'); +WS.Sender = require('./lib/Sender'); +WS.Receiver = require('./lib/Receiver'); + +/** + * Create a new WebSocket server. + * + * @param {Object} options Server options + * @param {Function} fn Optional connection listener. + * @returns {WS.Server} + * @api public + */ +WS.createServer = function createServer(options, fn) { + var server = new WS.Server(options); + + if (typeof fn === 'function') { + server.on('connection', fn); + } + + return server; +}; + +/** + * Create a new WebSocket connection. + * + * @param {String} address The URL/address we need to connect to. + * @param {Function} fn Open listener. + * @returns {WS} + * @api public + */ +WS.connect = WS.createConnection = function connect(address, fn) { + var client = new WS(address); + + if (typeof fn === 'function') { + client.on('open', fn); + } + + return client; +}; diff --git a/node_modules/ws/lib/BufferPool.js b/node_modules/ws/lib/BufferPool.js new file mode 100644 index 0000000..8ee5990 --- /dev/null +++ b/node_modules/ws/lib/BufferPool.js @@ -0,0 +1,63 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util'); + +function BufferPool(initialSize, growStrategy, shrinkStrategy) { + if (this instanceof BufferPool === false) { + throw new TypeError("Classes can't be function-called"); + } + + if (typeof initialSize === 'function') { + shrinkStrategy = growStrategy; + growStrategy = initialSize; + initialSize = 0; + } + else if (typeof initialSize === 'undefined') { + initialSize = 0; + } + this._growStrategy = (growStrategy || function(db, size) { + return db.used + size; + }).bind(null, this); + this._shrinkStrategy = (shrinkStrategy || function(db) { + return initialSize; + }).bind(null, this); + this._buffer = initialSize ? new Buffer(initialSize) : null; + this._offset = 0; + this._used = 0; + this._changeFactor = 0; + this.__defineGetter__('size', function(){ + return this._buffer == null ? 0 : this._buffer.length; + }); + this.__defineGetter__('used', function(){ + return this._used; + }); +} + +BufferPool.prototype.get = function(length) { + if (this._buffer == null || this._offset + length > this._buffer.length) { + var newBuf = new Buffer(this._growStrategy(length)); + this._buffer = newBuf; + this._offset = 0; + } + this._used += length; + var buf = this._buffer.slice(this._offset, this._offset + length); + this._offset += length; + return buf; +} + +BufferPool.prototype.reset = function(forceNewBuffer) { + var len = this._shrinkStrategy(); + if (len < this.size) this._changeFactor -= 1; + if (forceNewBuffer || this._changeFactor < -2) { + this._changeFactor = 0; + this._buffer = len ? new Buffer(len) : null; + } + this._offset = 0; + this._used = 0; +} + +module.exports = BufferPool; diff --git a/node_modules/ws/lib/BufferUtil.fallback.js b/node_modules/ws/lib/BufferUtil.fallback.js new file mode 100644 index 0000000..508542c --- /dev/null +++ b/node_modules/ws/lib/BufferUtil.fallback.js @@ -0,0 +1,47 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +module.exports.BufferUtil = { + merge: function(mergedBuffer, buffers) { + var offset = 0; + for (var i = 0, l = buffers.length; i < l; ++i) { + var buf = buffers[i]; + buf.copy(mergedBuffer, offset); + offset += buf.length; + } + }, + mask: function(source, mask, output, offset, length) { + var maskNum = mask.readUInt32LE(0, true); + var i = 0; + for (; i < length - 3; i += 4) { + var num = maskNum ^ source.readUInt32LE(i, true); + if (num < 0) num = 4294967296 + num; + output.writeUInt32LE(num, offset + i, true); + } + switch (length % 4) { + case 3: output[offset + i + 2] = source[i + 2] ^ mask[2]; + case 2: output[offset + i + 1] = source[i + 1] ^ mask[1]; + case 1: output[offset + i] = source[i] ^ mask[0]; + case 0:; + } + }, + unmask: function(data, mask) { + var maskNum = mask.readUInt32LE(0, true); + var length = data.length; + var i = 0; + for (; i < length - 3; i += 4) { + var num = maskNum ^ data.readUInt32LE(i, true); + if (num < 0) num = 4294967296 + num; + data.writeUInt32LE(num, i, true); + } + switch (length % 4) { + case 3: data[i + 2] = data[i + 2] ^ mask[2]; + case 2: data[i + 1] = data[i + 1] ^ mask[1]; + case 1: data[i] = data[i] ^ mask[0]; + case 0:; + } + } +} diff --git a/node_modules/ws/lib/BufferUtil.js b/node_modules/ws/lib/BufferUtil.js new file mode 100644 index 0000000..18c6998 --- /dev/null +++ b/node_modules/ws/lib/BufferUtil.js @@ -0,0 +1,13 @@ +'use strict'; + +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +try { + module.exports = require('bufferutil'); +} catch (e) { + module.exports = require('./BufferUtil.fallback'); +} diff --git a/node_modules/ws/lib/ErrorCodes.js b/node_modules/ws/lib/ErrorCodes.js new file mode 100644 index 0000000..55ebd52 --- /dev/null +++ b/node_modules/ws/lib/ErrorCodes.js @@ -0,0 +1,24 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +module.exports = { + isValidErrorCode: function(code) { + return (code >= 1000 && code <= 1011 && code != 1004 && code != 1005 && code != 1006) || + (code >= 3000 && code <= 4999); + }, + 1000: 'normal', + 1001: 'going away', + 1002: 'protocol error', + 1003: 'unsupported data', + 1004: 'reserved', + 1005: 'reserved for extensions', + 1006: 'reserved for extensions', + 1007: 'inconsistent or invalid data', + 1008: 'policy violation', + 1009: 'message too big', + 1010: 'extension handshake missing', + 1011: 'an unexpected condition prevented the request from being fulfilled', +}; \ No newline at end of file diff --git a/node_modules/ws/lib/Extensions.js b/node_modules/ws/lib/Extensions.js new file mode 100644 index 0000000..a465ace --- /dev/null +++ b/node_modules/ws/lib/Extensions.js @@ -0,0 +1,70 @@ + +var util = require('util'); + +/** + * Module exports. + */ + +exports.parse = parse; +exports.format = format; + +/** + * Parse extensions header value + */ + +function parse(value) { + value = value || ''; + + var extensions = {}; + + value.split(',').forEach(function(v) { + var params = v.split(';'); + var token = params.shift().trim(); + var paramsList = extensions[token] = extensions[token] || []; + var parsedParams = {}; + + params.forEach(function(param) { + var parts = param.trim().split('='); + var key = parts[0]; + var value = parts[1]; + if (typeof value === 'undefined') { + value = true; + } else { + // unquote value + if (value[0] === '"') { + value = value.slice(1); + } + if (value[value.length - 1] === '"') { + value = value.slice(0, value.length - 1); + } + } + (parsedParams[key] = parsedParams[key] || []).push(value); + }); + + paramsList.push(parsedParams); + }); + + return extensions; +} + +/** + * Format extensions header value + */ + +function format(value) { + return Object.keys(value).map(function(token) { + var paramsList = value[token]; + if (!util.isArray(paramsList)) { + paramsList = [paramsList]; + } + return paramsList.map(function(params) { + return [token].concat(Object.keys(params).map(function(k) { + var p = params[k]; + if (!util.isArray(p)) p = [p]; + return p.map(function(v) { + return v === true ? k : k + '=' + v; + }).join('; '); + })).join('; '); + }).join(', '); + }).join(', '); +} diff --git a/node_modules/ws/lib/PerMessageDeflate.js b/node_modules/ws/lib/PerMessageDeflate.js new file mode 100644 index 0000000..0060c5f --- /dev/null +++ b/node_modules/ws/lib/PerMessageDeflate.js @@ -0,0 +1,313 @@ + +var zlib = require('zlib'); + +var AVAILABLE_WINDOW_BITS = [8, 9, 10, 11, 12, 13, 14, 15]; +var DEFAULT_WINDOW_BITS = 15; +var DEFAULT_MEM_LEVEL = 8; + +PerMessageDeflate.extensionName = 'permessage-deflate'; + +/** + * Per-message Compression Extensions implementation + */ + +function PerMessageDeflate(options, isServer) { + if (this instanceof PerMessageDeflate === false) { + throw new TypeError("Classes can't be function-called"); + } + + this._options = options || {}; + this._isServer = !!isServer; + this._inflate = null; + this._deflate = null; + this.params = null; +} + +/** + * Create extension parameters offer + * + * @api public + */ + +PerMessageDeflate.prototype.offer = function() { + var params = {}; + if (this._options.serverNoContextTakeover) { + params.server_no_context_takeover = true; + } + if (this._options.clientNoContextTakeover) { + params.client_no_context_takeover = true; + } + if (this._options.serverMaxWindowBits) { + params.server_max_window_bits = this._options.serverMaxWindowBits; + } + if (this._options.clientMaxWindowBits) { + params.client_max_window_bits = this._options.clientMaxWindowBits; + } else if (this._options.clientMaxWindowBits == null) { + params.client_max_window_bits = true; + } + return params; +}; + +/** + * Accept extension offer + * + * @api public + */ + +PerMessageDeflate.prototype.accept = function(paramsList) { + paramsList = this.normalizeParams(paramsList); + + var params; + if (this._isServer) { + params = this.acceptAsServer(paramsList); + } else { + params = this.acceptAsClient(paramsList); + } + + this.params = params; + return params; +}; + +/** + * Releases all resources used by the extension + * + * @api public + */ + +PerMessageDeflate.prototype.cleanup = function() { + if (this._inflate) { + this._inflate.close(); + this._inflate = null; + } + if (this._deflate) { + this._deflate.close(); + this._deflate = null; + } +}; + +/** + * Accept extension offer from client + * + * @api private + */ + +PerMessageDeflate.prototype.acceptAsServer = function(paramsList) { + var accepted = {}; + var result = paramsList.some(function(params) { + accepted = {}; + if (this._options.serverNoContextTakeover === false && params.server_no_context_takeover) { + return; + } + if (this._options.serverMaxWindowBits === false && params.server_max_window_bits) { + return; + } + if (typeof this._options.serverMaxWindowBits === 'number' && + typeof params.server_max_window_bits === 'number' && + this._options.serverMaxWindowBits > params.server_max_window_bits) { + return; + } + if (typeof this._options.clientMaxWindowBits === 'number' && !params.client_max_window_bits) { + return; + } + + if (this._options.serverNoContextTakeover || params.server_no_context_takeover) { + accepted.server_no_context_takeover = true; + } + if (this._options.clientNoContextTakeover) { + accepted.client_no_context_takeover = true; + } + if (this._options.clientNoContextTakeover !== false && params.client_no_context_takeover) { + accepted.client_no_context_takeover = true; + } + if (typeof this._options.serverMaxWindowBits === 'number') { + accepted.server_max_window_bits = this._options.serverMaxWindowBits; + } else if (typeof params.server_max_window_bits === 'number') { + accepted.server_max_window_bits = params.server_max_window_bits; + } + if (typeof this._options.clientMaxWindowBits === 'number') { + accepted.client_max_window_bits = this._options.clientMaxWindowBits; + } else if (this._options.clientMaxWindowBits !== false && typeof params.client_max_window_bits === 'number') { + accepted.client_max_window_bits = params.client_max_window_bits; + } + return true; + }, this); + + if (!result) { + throw new Error('Doesn\'t support the offered configuration'); + } + + return accepted; +}; + +/** + * Accept extension response from server + * + * @api privaye + */ + +PerMessageDeflate.prototype.acceptAsClient = function(paramsList) { + var params = paramsList[0]; + if (this._options.clientNoContextTakeover != null) { + if (this._options.clientNoContextTakeover === false && params.client_no_context_takeover) { + throw new Error('Invalid value for "client_no_context_takeover"'); + } + } + if (this._options.clientMaxWindowBits != null) { + if (this._options.clientMaxWindowBits === false && params.client_max_window_bits) { + throw new Error('Invalid value for "client_max_window_bits"'); + } + if (typeof this._options.clientMaxWindowBits === 'number' && + (!params.client_max_window_bits || params.client_max_window_bits > this._options.clientMaxWindowBits)) { + throw new Error('Invalid value for "client_max_window_bits"'); + } + } + return params; +}; + +/** + * Normalize extensions parameters + * + * @api private + */ + +PerMessageDeflate.prototype.normalizeParams = function(paramsList) { + return paramsList.map(function(params) { + Object.keys(params).forEach(function(key) { + var value = params[key]; + if (value.length > 1) { + throw new Error('Multiple extension parameters for ' + key); + } + + value = value[0]; + + switch (key) { + case 'server_no_context_takeover': + case 'client_no_context_takeover': + if (value !== true) { + throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')'); + } + params[key] = true; + break; + case 'server_max_window_bits': + case 'client_max_window_bits': + if (typeof value === 'string') { + value = parseInt(value, 10); + if (!~AVAILABLE_WINDOW_BITS.indexOf(value)) { + throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')'); + } + } + if (!this._isServer && value === true) { + throw new Error('Missing extension parameter value for ' + key); + } + params[key] = value; + break; + default: + throw new Error('Not defined extension parameter (' + key + ')'); + } + }, this); + return params; + }, this); +}; + +/** + * Decompress message + * + * @api public + */ + +PerMessageDeflate.prototype.decompress = function (data, fin, callback) { + var endpoint = this._isServer ? 'client' : 'server'; + + if (!this._inflate) { + var maxWindowBits = this.params[endpoint + '_max_window_bits']; + this._inflate = zlib.createInflateRaw({ + windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS + }); + } + + var self = this; + var buffers = []; + + this._inflate.on('error', onError).on('data', onData); + this._inflate.write(data); + if (fin) { + this._inflate.write(new Buffer([0x00, 0x00, 0xff, 0xff])); + } + this._inflate.flush(function() { + cleanup(); + callback(null, Buffer.concat(buffers)); + }); + + function onError(err) { + cleanup(); + callback(err); + } + + function onData(data) { + buffers.push(data); + } + + function cleanup() { + if (!self._inflate) return; + self._inflate.removeListener('error', onError); + self._inflate.removeListener('data', onData); + if (fin && self.params[endpoint + '_no_context_takeover']) { + self._inflate.close(); + self._inflate = null; + } + } +}; + +/** + * Compress message + * + * @api public + */ + +PerMessageDeflate.prototype.compress = function (data, fin, callback) { + var endpoint = this._isServer ? 'server' : 'client'; + + if (!this._deflate) { + var maxWindowBits = this.params[endpoint + '_max_window_bits']; + this._deflate = zlib.createDeflateRaw({ + flush: zlib.Z_SYNC_FLUSH, + windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS, + memLevel: this._options.memLevel || DEFAULT_MEM_LEVEL + }); + } + + var self = this; + var buffers = []; + + this._deflate.on('error', onError).on('data', onData); + this._deflate.write(data); + this._deflate.flush(function() { + cleanup(); + var data = Buffer.concat(buffers); + if (fin) { + data = data.slice(0, data.length - 4); + } + callback(null, data); + }); + + function onError(err) { + cleanup(); + callback(err); + } + + function onData(data) { + buffers.push(data); + } + + function cleanup() { + if (!self._deflate) return; + self._deflate.removeListener('error', onError); + self._deflate.removeListener('data', onData); + if (fin && self.params[endpoint + '_no_context_takeover']) { + self._deflate.close(); + self._deflate = null; + } + } +}; + +module.exports = PerMessageDeflate; diff --git a/node_modules/ws/lib/Receiver.hixie.js b/node_modules/ws/lib/Receiver.hixie.js new file mode 100644 index 0000000..66bc561 --- /dev/null +++ b/node_modules/ws/lib/Receiver.hixie.js @@ -0,0 +1,184 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util'); + +/** + * State constants + */ + +var EMPTY = 0 + , BODY = 1; +var BINARYLENGTH = 2 + , BINARYBODY = 3; + +/** + * Hixie Receiver implementation + */ + +function Receiver () { + if (this instanceof Receiver === false) { + throw new TypeError("Classes can't be function-called"); + } + + this.state = EMPTY; + this.buffers = []; + this.messageEnd = -1; + this.spanLength = 0; + this.dead = false; + + this.onerror = function() {}; + this.ontext = function() {}; + this.onbinary = function() {}; + this.onclose = function() {}; + this.onping = function() {}; + this.onpong = function() {}; +} + +module.exports = Receiver; + +/** + * Add new data to the parser. + * + * @api public + */ + +Receiver.prototype.add = function(data) { + var self = this; + function doAdd() { + if (self.state === EMPTY) { + if (data.length == 2 && data[0] == 0xFF && data[1] == 0x00) { + self.reset(); + self.onclose(); + return; + } + if (data[0] === 0x80) { + self.messageEnd = 0; + self.state = BINARYLENGTH; + data = data.slice(1); + } else { + + if (data[0] !== 0x00) { + self.error('payload must start with 0x00 byte', true); + return; + } + data = data.slice(1); + self.state = BODY; + + } + } + if (self.state === BINARYLENGTH) { + var i = 0; + while ((i < data.length) && (data[i] & 0x80)) { + self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f); + ++i; + } + if (i < data.length) { + self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f); + self.state = BINARYBODY; + ++i; + } + if (i > 0) + data = data.slice(i); + } + if (self.state === BINARYBODY) { + var dataleft = self.messageEnd - self.spanLength; + if (data.length >= dataleft) { + // consume the whole buffer to finish the frame + self.buffers.push(data); + self.spanLength += dataleft; + self.messageEnd = dataleft; + return self.parse(); + } + // frame's not done even if we consume it all + self.buffers.push(data); + self.spanLength += data.length; + return; + } + self.buffers.push(data); + if ((self.messageEnd = bufferIndex(data, 0xFF)) != -1) { + self.spanLength += self.messageEnd; + return self.parse(); + } + else self.spanLength += data.length; + } + while(data) data = doAdd(); +}; + +/** + * Releases all resources used by the receiver. + * + * @api public + */ + +Receiver.prototype.cleanup = function() { + this.dead = true; + this.state = EMPTY; + this.buffers = []; +}; + +/** + * Process buffered data. + * + * @api public + */ + +Receiver.prototype.parse = function() { + var output = new Buffer(this.spanLength); + var outputIndex = 0; + for (var bi = 0, bl = this.buffers.length; bi < bl - 1; ++bi) { + var buffer = this.buffers[bi]; + buffer.copy(output, outputIndex); + outputIndex += buffer.length; + } + var lastBuffer = this.buffers[this.buffers.length - 1]; + if (this.messageEnd > 0) lastBuffer.copy(output, outputIndex, 0, this.messageEnd); + if (this.state !== BODY) --this.messageEnd; + var tail = null; + if (this.messageEnd < lastBuffer.length - 1) { + tail = lastBuffer.slice(this.messageEnd + 1); + } + this.reset(); + this.ontext(output.toString('utf8')); + return tail; +}; + +/** + * Handles an error + * + * @api private + */ + +Receiver.prototype.error = function (reason, terminate) { + this.reset(); + this.onerror(reason, terminate); + return this; +}; + +/** + * Reset parser state + * + * @api private + */ + +Receiver.prototype.reset = function (reason) { + if (this.dead) return; + this.state = EMPTY; + this.buffers = []; + this.messageEnd = -1; + this.spanLength = 0; +}; + +/** + * Internal api + */ + +function bufferIndex(buffer, byte) { + for (var i = 0, l = buffer.length; i < l; ++i) { + if (buffer[i] === byte) return i; + } + return -1; +} diff --git a/node_modules/ws/lib/Receiver.js b/node_modules/ws/lib/Receiver.js new file mode 100644 index 0000000..999af0a --- /dev/null +++ b/node_modules/ws/lib/Receiver.js @@ -0,0 +1,702 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util') + , Validation = require('./Validation').Validation + , ErrorCodes = require('./ErrorCodes') + , BufferPool = require('./BufferPool') + , bufferUtil = require('./BufferUtil').BufferUtil + , PerMessageDeflate = require('./PerMessageDeflate'); + +/** + * HyBi Receiver implementation + */ + +function Receiver (extensions) { + if (this instanceof Receiver === false) { + throw new TypeError("Classes can't be function-called"); + } + + // memory pool for fragmented messages + var fragmentedPoolPrevUsed = -1; + this.fragmentedBufferPool = new BufferPool(1024, function(db, length) { + return db.used + length; + }, function(db) { + return fragmentedPoolPrevUsed = fragmentedPoolPrevUsed >= 0 ? + (fragmentedPoolPrevUsed + db.used) / 2 : + db.used; + }); + + // memory pool for unfragmented messages + var unfragmentedPoolPrevUsed = -1; + this.unfragmentedBufferPool = new BufferPool(1024, function(db, length) { + return db.used + length; + }, function(db) { + return unfragmentedPoolPrevUsed = unfragmentedPoolPrevUsed >= 0 ? + (unfragmentedPoolPrevUsed + db.used) / 2 : + db.used; + }); + + this.extensions = extensions || {}; + this.state = { + activeFragmentedOperation: null, + lastFragment: false, + masked: false, + opcode: 0, + fragmentedOperation: false + }; + this.overflow = []; + this.headerBuffer = new Buffer(10); + this.expectOffset = 0; + this.expectBuffer = null; + this.expectHandler = null; + this.currentMessage = []; + this.messageHandlers = []; + this.expectHeader(2, this.processPacket); + this.dead = false; + this.processing = false; + + this.onerror = function() {}; + this.ontext = function() {}; + this.onbinary = function() {}; + this.onclose = function() {}; + this.onping = function() {}; + this.onpong = function() {}; +} + +module.exports = Receiver; + +/** + * Add new data to the parser. + * + * @api public + */ + +Receiver.prototype.add = function(data) { + var dataLength = data.length; + if (dataLength == 0) return; + if (this.expectBuffer == null) { + this.overflow.push(data); + return; + } + var toRead = Math.min(dataLength, this.expectBuffer.length - this.expectOffset); + fastCopy(toRead, data, this.expectBuffer, this.expectOffset); + this.expectOffset += toRead; + if (toRead < dataLength) { + this.overflow.push(data.slice(toRead)); + } + while (this.expectBuffer && this.expectOffset == this.expectBuffer.length) { + var bufferForHandler = this.expectBuffer; + this.expectBuffer = null; + this.expectOffset = 0; + this.expectHandler.call(this, bufferForHandler); + } +}; + +/** + * Releases all resources used by the receiver. + * + * @api public + */ + +Receiver.prototype.cleanup = function() { + this.dead = true; + this.overflow = null; + this.headerBuffer = null; + this.expectBuffer = null; + this.expectHandler = null; + this.unfragmentedBufferPool = null; + this.fragmentedBufferPool = null; + this.state = null; + this.currentMessage = null; + this.onerror = null; + this.ontext = null; + this.onbinary = null; + this.onclose = null; + this.onping = null; + this.onpong = null; +}; + +/** + * Waits for a certain amount of header bytes to be available, then fires a callback. + * + * @api private + */ + +Receiver.prototype.expectHeader = function(length, handler) { + if (length == 0) { + handler(null); + return; + } + this.expectBuffer = this.headerBuffer.slice(this.expectOffset, this.expectOffset + length); + this.expectHandler = handler; + var toRead = length; + while (toRead > 0 && this.overflow.length > 0) { + var fromOverflow = this.overflow.pop(); + if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead)); + var read = Math.min(fromOverflow.length, toRead); + fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset); + this.expectOffset += read; + toRead -= read; + } +}; + +/** + * Waits for a certain amount of data bytes to be available, then fires a callback. + * + * @api private + */ + +Receiver.prototype.expectData = function(length, handler) { + if (length == 0) { + handler(null); + return; + } + this.expectBuffer = this.allocateFromPool(length, this.state.fragmentedOperation); + this.expectHandler = handler; + var toRead = length; + while (toRead > 0 && this.overflow.length > 0) { + var fromOverflow = this.overflow.pop(); + if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead)); + var read = Math.min(fromOverflow.length, toRead); + fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset); + this.expectOffset += read; + toRead -= read; + } +}; + +/** + * Allocates memory from the buffer pool. + * + * @api private + */ + +Receiver.prototype.allocateFromPool = function(length, isFragmented) { + return (isFragmented ? this.fragmentedBufferPool : this.unfragmentedBufferPool).get(length); +}; + +/** + * Start processing a new packet. + * + * @api private + */ + +Receiver.prototype.processPacket = function (data) { + if (this.extensions[PerMessageDeflate.extensionName]) { + if ((data[0] & 0x30) != 0) { + this.error('reserved fields (2, 3) must be empty', 1002); + return; + } + } else { + if ((data[0] & 0x70) != 0) { + this.error('reserved fields must be empty', 1002); + return; + } + } + this.state.lastFragment = (data[0] & 0x80) == 0x80; + this.state.masked = (data[1] & 0x80) == 0x80; + var compressed = (data[0] & 0x40) == 0x40; + var opcode = data[0] & 0xf; + if (opcode === 0) { + if (compressed) { + this.error('continuation frame cannot have the Per-message Compressed bits', 1002); + return; + } + // continuation frame + this.state.fragmentedOperation = true; + this.state.opcode = this.state.activeFragmentedOperation; + if (!(this.state.opcode == 1 || this.state.opcode == 2)) { + this.error('continuation frame cannot follow current opcode', 1002); + return; + } + } + else { + if (opcode < 3 && this.state.activeFragmentedOperation != null) { + this.error('data frames after the initial data frame must have opcode 0', 1002); + return; + } + if (opcode >= 8 && compressed) { + this.error('control frames cannot have the Per-message Compressed bits', 1002); + return; + } + this.state.compressed = compressed; + this.state.opcode = opcode; + if (this.state.lastFragment === false) { + this.state.fragmentedOperation = true; + this.state.activeFragmentedOperation = opcode; + } + else this.state.fragmentedOperation = false; + } + var handler = opcodes[this.state.opcode]; + if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode, 1002); + else { + handler.start.call(this, data); + } +}; + +/** + * Endprocessing a packet. + * + * @api private + */ + +Receiver.prototype.endPacket = function() { + if (!this.state.fragmentedOperation) this.unfragmentedBufferPool.reset(true); + else if (this.state.lastFragment) this.fragmentedBufferPool.reset(true); + this.expectOffset = 0; + this.expectBuffer = null; + this.expectHandler = null; + if (this.state.lastFragment && this.state.opcode === this.state.activeFragmentedOperation) { + // end current fragmented operation + this.state.activeFragmentedOperation = null; + } + this.state.lastFragment = false; + this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0; + this.state.masked = false; + this.expectHeader(2, this.processPacket); +}; + +/** + * Reset the parser state. + * + * @api private + */ + +Receiver.prototype.reset = function() { + if (this.dead) return; + this.state = { + activeFragmentedOperation: null, + lastFragment: false, + masked: false, + opcode: 0, + fragmentedOperation: false + }; + this.fragmentedBufferPool.reset(true); + this.unfragmentedBufferPool.reset(true); + this.expectOffset = 0; + this.expectBuffer = null; + this.expectHandler = null; + this.overflow = []; + this.currentMessage = []; + this.messageHandlers = []; +}; + +/** + * Unmask received data. + * + * @api private + */ + +Receiver.prototype.unmask = function (mask, buf, binary) { + if (mask != null && buf != null) bufferUtil.unmask(buf, mask); + if (binary) return buf; + return buf != null ? buf.toString('utf8') : ''; +}; + +/** + * Concatenates a list of buffers. + * + * @api private + */ + +Receiver.prototype.concatBuffers = function(buffers) { + var length = 0; + for (var i = 0, l = buffers.length; i < l; ++i) length += buffers[i].length; + var mergedBuffer = new Buffer(length); + bufferUtil.merge(mergedBuffer, buffers); + return mergedBuffer; +}; + +/** + * Handles an error + * + * @api private + */ + +Receiver.prototype.error = function (reason, protocolErrorCode) { + this.reset(); + this.onerror(reason, protocolErrorCode); + return this; +}; + +/** + * Execute message handler buffers + * + * @api private + */ + +Receiver.prototype.flush = function() { + if (this.processing || this.dead) return; + + var handler = this.messageHandlers.shift(); + if (!handler) return; + + this.processing = true; + var self = this; + + handler(function() { + self.processing = false; + self.flush(); + }); +}; + +/** + * Apply extensions to message + * + * @api private + */ + +Receiver.prototype.applyExtensions = function(messageBuffer, fin, compressed, callback) { + var self = this; + if (compressed) { + this.extensions[PerMessageDeflate.extensionName].decompress(messageBuffer, fin, function(err, buffer) { + if (self.dead) return; + if (err) { + callback(new Error('invalid compressed data')); + return; + } + callback(null, buffer); + }); + } else { + callback(null, messageBuffer); + } +}; + +/** + * Buffer utilities + */ + +function readUInt16BE(start) { + return (this[start]<<8) + + this[start+1]; +} + +function readUInt32BE(start) { + return (this[start]<<24) + + (this[start+1]<<16) + + (this[start+2]<<8) + + this[start+3]; +} + +function fastCopy(length, srcBuffer, dstBuffer, dstOffset) { + switch (length) { + default: srcBuffer.copy(dstBuffer, dstOffset, 0, length); break; + case 16: dstBuffer[dstOffset+15] = srcBuffer[15]; + case 15: dstBuffer[dstOffset+14] = srcBuffer[14]; + case 14: dstBuffer[dstOffset+13] = srcBuffer[13]; + case 13: dstBuffer[dstOffset+12] = srcBuffer[12]; + case 12: dstBuffer[dstOffset+11] = srcBuffer[11]; + case 11: dstBuffer[dstOffset+10] = srcBuffer[10]; + case 10: dstBuffer[dstOffset+9] = srcBuffer[9]; + case 9: dstBuffer[dstOffset+8] = srcBuffer[8]; + case 8: dstBuffer[dstOffset+7] = srcBuffer[7]; + case 7: dstBuffer[dstOffset+6] = srcBuffer[6]; + case 6: dstBuffer[dstOffset+5] = srcBuffer[5]; + case 5: dstBuffer[dstOffset+4] = srcBuffer[4]; + case 4: dstBuffer[dstOffset+3] = srcBuffer[3]; + case 3: dstBuffer[dstOffset+2] = srcBuffer[2]; + case 2: dstBuffer[dstOffset+1] = srcBuffer[1]; + case 1: dstBuffer[dstOffset] = srcBuffer[0]; + } +} + +function clone(obj) { + var cloned = {}; + for (var k in obj) { + if (obj.hasOwnProperty(k)) { + cloned[k] = obj[k]; + } + } + return cloned; +} + +/** + * Opcode handlers + */ + +var opcodes = { + // text + '1': { + start: function(data) { + var self = this; + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + opcodes['1'].getData.call(self, firstLength); + } + else if (firstLength == 126) { + self.expectHeader(2, function(data) { + opcodes['1'].getData.call(self, readUInt16BE.call(data, 0)); + }); + } + else if (firstLength == 127) { + self.expectHeader(8, function(data) { + if (readUInt32BE.call(data, 0) != 0) { + self.error('packets with length spanning more than 32 bit is currently not supported', 1008); + return; + } + opcodes['1'].getData.call(self, readUInt32BE.call(data, 4)); + }); + } + }, + getData: function(length) { + var self = this; + if (self.state.masked) { + self.expectHeader(4, function(data) { + var mask = data; + self.expectData(length, function(data) { + opcodes['1'].finish.call(self, mask, data); + }); + }); + } + else { + self.expectData(length, function(data) { + opcodes['1'].finish.call(self, null, data); + }); + } + }, + finish: function(mask, data) { + var self = this; + var packet = this.unmask(mask, data, true) || new Buffer(0); + var state = clone(this.state); + this.messageHandlers.push(function(callback) { + self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) { + if (err) return self.error(err.message, 1007); + if (buffer != null) self.currentMessage.push(buffer); + + if (state.lastFragment) { + var messageBuffer = self.concatBuffers(self.currentMessage); + self.currentMessage = []; + if (!Validation.isValidUTF8(messageBuffer)) { + self.error('invalid utf8 sequence', 1007); + return; + } + self.ontext(messageBuffer.toString('utf8'), {masked: state.masked, buffer: messageBuffer}); + } + callback(); + }); + }); + this.flush(); + this.endPacket(); + } + }, + // binary + '2': { + start: function(data) { + var self = this; + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + opcodes['2'].getData.call(self, firstLength); + } + else if (firstLength == 126) { + self.expectHeader(2, function(data) { + opcodes['2'].getData.call(self, readUInt16BE.call(data, 0)); + }); + } + else if (firstLength == 127) { + self.expectHeader(8, function(data) { + if (readUInt32BE.call(data, 0) != 0) { + self.error('packets with length spanning more than 32 bit is currently not supported', 1008); + return; + } + opcodes['2'].getData.call(self, readUInt32BE.call(data, 4, true)); + }); + } + }, + getData: function(length) { + var self = this; + if (self.state.masked) { + self.expectHeader(4, function(data) { + var mask = data; + self.expectData(length, function(data) { + opcodes['2'].finish.call(self, mask, data); + }); + }); + } + else { + self.expectData(length, function(data) { + opcodes['2'].finish.call(self, null, data); + }); + } + }, + finish: function(mask, data) { + var self = this; + var packet = this.unmask(mask, data, true) || new Buffer(0); + var state = clone(this.state); + this.messageHandlers.push(function(callback) { + self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) { + if (err) return self.error(err.message, 1007); + if (buffer != null) self.currentMessage.push(buffer); + if (state.lastFragment) { + var messageBuffer = self.concatBuffers(self.currentMessage); + self.currentMessage = []; + self.onbinary(messageBuffer, {masked: state.masked, buffer: messageBuffer}); + } + callback(); + }); + }); + this.flush(); + this.endPacket(); + } + }, + // close + '8': { + start: function(data) { + var self = this; + if (self.state.lastFragment == false) { + self.error('fragmented close is not supported', 1002); + return; + } + + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + opcodes['8'].getData.call(self, firstLength); + } + else { + self.error('control frames cannot have more than 125 bytes of data', 1002); + } + }, + getData: function(length) { + var self = this; + if (self.state.masked) { + self.expectHeader(4, function(data) { + var mask = data; + self.expectData(length, function(data) { + opcodes['8'].finish.call(self, mask, data); + }); + }); + } + else { + self.expectData(length, function(data) { + opcodes['8'].finish.call(self, null, data); + }); + } + }, + finish: function(mask, data) { + var self = this; + data = self.unmask(mask, data, true); + + var state = clone(this.state); + this.messageHandlers.push(function() { + if (data && data.length == 1) { + self.error('close packets with data must be at least two bytes long', 1002); + return; + } + var code = data && data.length > 1 ? readUInt16BE.call(data, 0) : 1000; + if (!ErrorCodes.isValidErrorCode(code)) { + self.error('invalid error code', 1002); + return; + } + var message = ''; + if (data && data.length > 2) { + var messageBuffer = data.slice(2); + if (!Validation.isValidUTF8(messageBuffer)) { + self.error('invalid utf8 sequence', 1007); + return; + } + message = messageBuffer.toString('utf8'); + } + self.onclose(code, message, {masked: state.masked}); + self.reset(); + }); + this.flush(); + }, + }, + // ping + '9': { + start: function(data) { + var self = this; + if (self.state.lastFragment == false) { + self.error('fragmented ping is not supported', 1002); + return; + } + + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + opcodes['9'].getData.call(self, firstLength); + } + else { + self.error('control frames cannot have more than 125 bytes of data', 1002); + } + }, + getData: function(length) { + var self = this; + if (self.state.masked) { + self.expectHeader(4, function(data) { + var mask = data; + self.expectData(length, function(data) { + opcodes['9'].finish.call(self, mask, data); + }); + }); + } + else { + self.expectData(length, function(data) { + opcodes['9'].finish.call(self, null, data); + }); + } + }, + finish: function(mask, data) { + var self = this; + data = this.unmask(mask, data, true); + var state = clone(this.state); + this.messageHandlers.push(function(callback) { + self.onping(data, {masked: state.masked, binary: true}); + callback(); + }); + this.flush(); + this.endPacket(); + } + }, + // pong + '10': { + start: function(data) { + var self = this; + if (self.state.lastFragment == false) { + self.error('fragmented pong is not supported', 1002); + return; + } + + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + opcodes['10'].getData.call(self, firstLength); + } + else { + self.error('control frames cannot have more than 125 bytes of data', 1002); + } + }, + getData: function(length) { + var self = this; + if (this.state.masked) { + this.expectHeader(4, function(data) { + var mask = data; + self.expectData(length, function(data) { + opcodes['10'].finish.call(self, mask, data); + }); + }); + } + else { + this.expectData(length, function(data) { + opcodes['10'].finish.call(self, null, data); + }); + } + }, + finish: function(mask, data) { + var self = this; + data = self.unmask(mask, data, true); + var state = clone(this.state); + this.messageHandlers.push(function(callback) { + self.onpong(data, {masked: state.masked, binary: true}); + callback(); + }); + this.flush(); + this.endPacket(); + } + } +} diff --git a/node_modules/ws/lib/Sender.hixie.js b/node_modules/ws/lib/Sender.hixie.js new file mode 100644 index 0000000..b87d9dd --- /dev/null +++ b/node_modules/ws/lib/Sender.hixie.js @@ -0,0 +1,124 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var events = require('events') + , util = require('util') + , EventEmitter = events.EventEmitter; + +/** + * Hixie Sender implementation + */ + +function Sender(socket) { + if (this instanceof Sender === false) { + throw new TypeError("Classes can't be function-called"); + } + + events.EventEmitter.call(this); + + this.socket = socket; + this.continuationFrame = false; + this.isClosed = false; +} + +module.exports = Sender; + +/** + * Inherits from EventEmitter. + */ + +util.inherits(Sender, events.EventEmitter); + +/** + * Frames and writes data. + * + * @api public + */ + +Sender.prototype.send = function(data, options, cb) { + if (this.isClosed) return; + + var isString = typeof data == 'string' + , length = isString ? Buffer.byteLength(data) : data.length + , lengthbytes = (length > 127) ? 2 : 1 // assume less than 2**14 bytes + , writeStartMarker = this.continuationFrame == false + , writeEndMarker = !options || !(typeof options.fin != 'undefined' && !options.fin) + , buffer = new Buffer((writeStartMarker ? ((options && options.binary) ? (1 + lengthbytes) : 1) : 0) + length + ((writeEndMarker && !(options && options.binary)) ? 1 : 0)) + , offset = writeStartMarker ? 1 : 0; + + if (writeStartMarker) { + if (options && options.binary) { + buffer.write('\x80', 'binary'); + // assume length less than 2**14 bytes + if (lengthbytes > 1) + buffer.write(String.fromCharCode(128+length/128), offset++, 'binary'); + buffer.write(String.fromCharCode(length&0x7f), offset++, 'binary'); + } else + buffer.write('\x00', 'binary'); + } + + if (isString) buffer.write(data, offset, 'utf8'); + else data.copy(buffer, offset, 0); + + if (writeEndMarker) { + if (options && options.binary) { + // sending binary, not writing end marker + } else + buffer.write('\xff', offset + length, 'binary'); + this.continuationFrame = false; + } + else this.continuationFrame = true; + + try { + this.socket.write(buffer, 'binary', cb); + } catch (e) { + this.error(e.toString()); + } +}; + +/** + * Sends a close instruction to the remote party. + * + * @api public + */ + +Sender.prototype.close = function(code, data, mask, cb) { + if (this.isClosed) return; + this.isClosed = true; + try { + if (this.continuationFrame) this.socket.write(new Buffer([0xff], 'binary')); + this.socket.write(new Buffer([0xff, 0x00]), 'binary', cb); + } catch (e) { + this.error(e.toString()); + } +}; + +/** + * Sends a ping message to the remote party. Not available for hixie. + * + * @api public + */ + +Sender.prototype.ping = function(data, options) {}; + +/** + * Sends a pong message to the remote party. Not available for hixie. + * + * @api public + */ + +Sender.prototype.pong = function(data, options) {}; + +/** + * Handles an error + * + * @api private + */ + +Sender.prototype.error = function (reason) { + this.emit('error', reason); + return this; +}; diff --git a/node_modules/ws/lib/Sender.js b/node_modules/ws/lib/Sender.js new file mode 100644 index 0000000..2f8f7c4 --- /dev/null +++ b/node_modules/ws/lib/Sender.js @@ -0,0 +1,316 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var events = require('events') + , util = require('util') + , EventEmitter = events.EventEmitter + , ErrorCodes = require('./ErrorCodes') + , bufferUtil = require('./BufferUtil').BufferUtil + , PerMessageDeflate = require('./PerMessageDeflate'); + +/** + * HyBi Sender implementation + */ + +function Sender(socket, extensions) { + if (this instanceof Sender === false) { + throw new TypeError("Classes can't be function-called"); + } + + events.EventEmitter.call(this); + + this._socket = socket; + this.extensions = extensions || {}; + this.firstFragment = true; + this.compress = false; + this.messageHandlers = []; + this.processing = false; +} + +/** + * Inherits from EventEmitter. + */ + +util.inherits(Sender, events.EventEmitter); + +/** + * Sends a close instruction to the remote party. + * + * @api public + */ + +Sender.prototype.close = function(code, data, mask, cb) { + if (typeof code !== 'undefined') { + if (typeof code !== 'number' || + !ErrorCodes.isValidErrorCode(code)) throw new Error('first argument must be a valid error code number'); + } + code = code || 1000; + var dataBuffer = new Buffer(2 + (data ? Buffer.byteLength(data) : 0)); + writeUInt16BE.call(dataBuffer, code, 0); + if (dataBuffer.length > 2) dataBuffer.write(data, 2); + + var self = this; + this.messageHandlers.push(function(callback) { + self.frameAndSend(0x8, dataBuffer, true, mask); + callback(); + if (typeof cb == 'function') cb(); + }); + this.flush(); +}; + +/** + * Sends a ping message to the remote party. + * + * @api public + */ + +Sender.prototype.ping = function(data, options) { + var mask = options && options.mask; + var self = this; + this.messageHandlers.push(function(callback) { + self.frameAndSend(0x9, data || '', true, mask); + callback(); + }); + this.flush(); +}; + +/** + * Sends a pong message to the remote party. + * + * @api public + */ + +Sender.prototype.pong = function(data, options) { + var mask = options && options.mask; + var self = this; + this.messageHandlers.push(function(callback) { + self.frameAndSend(0xa, data || '', true, mask); + callback(); + }); + this.flush(); +}; + +/** + * Sends text or binary data to the remote party. + * + * @api public + */ + +Sender.prototype.send = function(data, options, cb) { + var finalFragment = options && options.fin === false ? false : true; + var mask = options && options.mask; + var compress = options && options.compress; + var opcode = options && options.binary ? 2 : 1; + if (this.firstFragment === false) { + opcode = 0; + compress = false; + } else { + this.firstFragment = false; + this.compress = compress; + } + if (finalFragment) this.firstFragment = true + + var compressFragment = this.compress; + + var self = this; + this.messageHandlers.push(function(callback) { + self.applyExtensions(data, finalFragment, compressFragment, function(err, data) { + if (err) { + if (typeof cb == 'function') cb(err); + else self.emit('error', err); + return; + } + self.frameAndSend(opcode, data, finalFragment, mask, compress, cb); + callback(); + }); + }); + this.flush(); +}; + +/** + * Frames and sends a piece of data according to the HyBi WebSocket protocol. + * + * @api private + */ + +Sender.prototype.frameAndSend = function(opcode, data, finalFragment, maskData, compressed, cb) { + var canModifyData = false; + + if (!data) { + try { + this._socket.write(new Buffer([opcode | (finalFragment ? 0x80 : 0), 0 | (maskData ? 0x80 : 0)].concat(maskData ? [0, 0, 0, 0] : [])), 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + return; + } + + if (!Buffer.isBuffer(data)) { + canModifyData = true; + if (data && (typeof data.byteLength !== 'undefined' || typeof data.buffer !== 'undefined')) { + data = getArrayBuffer(data); + } else { + data = new Buffer(data); + } + } + + var dataLength = data.length + , dataOffset = maskData ? 6 : 2 + , secondByte = dataLength; + + if (dataLength >= 65536) { + dataOffset += 8; + secondByte = 127; + } + else if (dataLength > 125) { + dataOffset += 2; + secondByte = 126; + } + + var mergeBuffers = dataLength < 32768 || (maskData && !canModifyData); + var totalLength = mergeBuffers ? dataLength + dataOffset : dataOffset; + var outputBuffer = new Buffer(totalLength); + outputBuffer[0] = finalFragment ? opcode | 0x80 : opcode; + if (compressed) outputBuffer[0] |= 0x40; + + switch (secondByte) { + case 126: + writeUInt16BE.call(outputBuffer, dataLength, 2); + break; + case 127: + writeUInt32BE.call(outputBuffer, 0, 2); + writeUInt32BE.call(outputBuffer, dataLength, 6); + } + + if (maskData) { + outputBuffer[1] = secondByte | 0x80; + var mask = this._randomMask || (this._randomMask = getRandomMask()); + outputBuffer[dataOffset - 4] = mask[0]; + outputBuffer[dataOffset - 3] = mask[1]; + outputBuffer[dataOffset - 2] = mask[2]; + outputBuffer[dataOffset - 1] = mask[3]; + if (mergeBuffers) { + bufferUtil.mask(data, mask, outputBuffer, dataOffset, dataLength); + try { + this._socket.write(outputBuffer, 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + } + else { + bufferUtil.mask(data, mask, data, 0, dataLength); + try { + this._socket.write(outputBuffer, 'binary'); + this._socket.write(data, 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + } + } + else { + outputBuffer[1] = secondByte; + if (mergeBuffers) { + data.copy(outputBuffer, dataOffset); + try { + this._socket.write(outputBuffer, 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + } + else { + try { + this._socket.write(outputBuffer, 'binary'); + this._socket.write(data, 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + } + } +}; + +/** + * Execute message handler buffers + * + * @api private + */ + +Sender.prototype.flush = function() { + if (this.processing) return; + + var handler = this.messageHandlers.shift(); + if (!handler) return; + + this.processing = true; + + var self = this; + + handler(function() { + self.processing = false; + self.flush(); + }); +}; + +/** + * Apply extensions to message + * + * @api private + */ + +Sender.prototype.applyExtensions = function(data, fin, compress, callback) { + if (compress && data) { + if ((data.buffer || data) instanceof ArrayBuffer) { + data = getArrayBuffer(data); + } + this.extensions[PerMessageDeflate.extensionName].compress(data, fin, callback); + } else { + callback(null, data); + } +}; + +module.exports = Sender; + +function writeUInt16BE(value, offset) { + this[offset] = (value & 0xff00)>>8; + this[offset+1] = value & 0xff; +} + +function writeUInt32BE(value, offset) { + this[offset] = (value & 0xff000000)>>24; + this[offset+1] = (value & 0xff0000)>>16; + this[offset+2] = (value & 0xff00)>>8; + this[offset+3] = value & 0xff; +} + +function getArrayBuffer(data) { + // data is either an ArrayBuffer or ArrayBufferView. + var array = new Uint8Array(data.buffer || data) + , l = data.byteLength || data.length + , o = data.byteOffset || 0 + , buffer = new Buffer(l); + for (var i = 0; i < l; ++i) { + buffer[i] = array[o+i]; + } + return buffer; +} + +function getRandomMask() { + return new Buffer([ + ~~(Math.random() * 255), + ~~(Math.random() * 255), + ~~(Math.random() * 255), + ~~(Math.random() * 255) + ]); +} diff --git a/node_modules/ws/lib/Validation.fallback.js b/node_modules/ws/lib/Validation.fallback.js new file mode 100644 index 0000000..2c7c4fd --- /dev/null +++ b/node_modules/ws/lib/Validation.fallback.js @@ -0,0 +1,12 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +module.exports.Validation = { + isValidUTF8: function(buffer) { + return true; + } +}; + diff --git a/node_modules/ws/lib/Validation.js b/node_modules/ws/lib/Validation.js new file mode 100644 index 0000000..0795fb7 --- /dev/null +++ b/node_modules/ws/lib/Validation.js @@ -0,0 +1,13 @@ +'use strict'; + +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +try { + module.exports = require('utf-8-validate'); +} catch (e) { + module.exports = require('./Validation.fallback'); +} diff --git a/node_modules/ws/lib/WebSocket.js b/node_modules/ws/lib/WebSocket.js new file mode 100644 index 0000000..27144c0 --- /dev/null +++ b/node_modules/ws/lib/WebSocket.js @@ -0,0 +1,964 @@ +'use strict'; + +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var url = require('url') + , util = require('util') + , http = require('http') + , https = require('https') + , crypto = require('crypto') + , stream = require('stream') + , Ultron = require('ultron') + , Options = require('options') + , Sender = require('./Sender') + , Receiver = require('./Receiver') + , SenderHixie = require('./Sender.hixie') + , ReceiverHixie = require('./Receiver.hixie') + , Extensions = require('./Extensions') + , PerMessageDeflate = require('./PerMessageDeflate') + , EventEmitter = require('events').EventEmitter; + +/** + * Constants + */ + +// Default protocol version + +var protocolVersion = 13; + +// Close timeout + +var closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly + +/** + * WebSocket implementation + * + * @constructor + * @param {String} address Connection address. + * @param {String|Array} protocols WebSocket protocols. + * @param {Object} options Additional connection options. + * @api public + */ +function WebSocket(address, protocols, options) { + if (this instanceof WebSocket === false) { + return new WebSocket(address, protocols, options); + } + + EventEmitter.call(this); + + if (protocols && !Array.isArray(protocols) && 'object' === typeof protocols) { + // accept the "options" Object as the 2nd argument + options = protocols; + protocols = null; + } + + if ('string' === typeof protocols) { + protocols = [ protocols ]; + } + + if (!Array.isArray(protocols)) { + protocols = []; + } + + this._socket = null; + this._ultron = null; + this._closeReceived = false; + this.bytesReceived = 0; + this.readyState = null; + this.supports = {}; + this.extensions = {}; + + if (Array.isArray(address)) { + initAsServerClient.apply(this, address.concat(options)); + } else { + initAsClient.apply(this, [address, protocols, options]); + } +} + +/** + * Inherits from EventEmitter. + */ +util.inherits(WebSocket, EventEmitter); + +/** + * Ready States + */ +["CONNECTING", "OPEN", "CLOSING", "CLOSED"].forEach(function each(state, index) { + WebSocket.prototype[state] = WebSocket[state] = index; +}); + +/** + * Gracefully closes the connection, after sending a description message to the server + * + * @param {Object} data to be sent to the server + * @api public + */ +WebSocket.prototype.close = function close(code, data) { + if (this.readyState === WebSocket.CLOSED) return; + + if (this.readyState === WebSocket.CONNECTING) { + this.readyState = WebSocket.CLOSED; + return; + } + + if (this.readyState === WebSocket.CLOSING) { + if (this._closeReceived && this._isServer) { + this.terminate(); + } + return; + } + + var self = this; + try { + this.readyState = WebSocket.CLOSING; + this._closeCode = code; + this._closeMessage = data; + var mask = !this._isServer; + this._sender.close(code, data, mask, function(err) { + if (err) self.emit('error', err); + + if (self._closeReceived && self._isServer) { + self.terminate(); + } else { + // ensure that the connection is cleaned up even when no response of closing handshake. + clearTimeout(self._closeTimer); + self._closeTimer = setTimeout(cleanupWebsocketResources.bind(self, true), closeTimeout); + } + }); + } catch (e) { + this.emit('error', e); + } +}; + +/** + * Pause the client stream + * + * @api public + */ +WebSocket.prototype.pause = function pauser() { + if (this.readyState !== WebSocket.OPEN) throw new Error('not opened'); + + return this._socket.pause(); +}; + +/** + * Sends a ping + * + * @param {Object} data to be sent to the server + * @param {Object} Members - mask: boolean, binary: boolean + * @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open + * @api public + */ +WebSocket.prototype.ping = function ping(data, options, dontFailWhenClosed) { + if (this.readyState !== WebSocket.OPEN) { + if (dontFailWhenClosed === true) return; + throw new Error('not opened'); + } + + options = options || {}; + + if (typeof options.mask === 'undefined') options.mask = !this._isServer; + + this._sender.ping(data, options); +}; + +/** + * Sends a pong + * + * @param {Object} data to be sent to the server + * @param {Object} Members - mask: boolean, binary: boolean + * @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open + * @api public + */ +WebSocket.prototype.pong = function(data, options, dontFailWhenClosed) { + if (this.readyState !== WebSocket.OPEN) { + if (dontFailWhenClosed === true) return; + throw new Error('not opened'); + } + + options = options || {}; + + if (typeof options.mask === 'undefined') options.mask = !this._isServer; + + this._sender.pong(data, options); +}; + +/** + * Resume the client stream + * + * @api public + */ +WebSocket.prototype.resume = function resume() { + if (this.readyState !== WebSocket.OPEN) throw new Error('not opened'); + + return this._socket.resume(); +}; + +/** + * Sends a piece of data + * + * @param {Object} data to be sent to the server + * @param {Object} Members - mask: boolean, binary: boolean, compress: boolean + * @param {function} Optional callback which is executed after the send completes + * @api public + */ + +WebSocket.prototype.send = function send(data, options, cb) { + if (typeof options === 'function') { + cb = options; + options = {}; + } + + if (this.readyState !== WebSocket.OPEN) { + if (typeof cb === 'function') cb(new Error('not opened')); + else throw new Error('not opened'); + return; + } + + if (!data) data = ''; + if (this._queue) { + var self = this; + this._queue.push(function() { self.send(data, options, cb); }); + return; + } + + options = options || {}; + options.fin = true; + + if (typeof options.binary === 'undefined') { + options.binary = (data instanceof ArrayBuffer || data instanceof Buffer || + data instanceof Uint8Array || + data instanceof Uint16Array || + data instanceof Uint32Array || + data instanceof Int8Array || + data instanceof Int16Array || + data instanceof Int32Array || + data instanceof Float32Array || + data instanceof Float64Array); + } + + if (typeof options.mask === 'undefined') options.mask = !this._isServer; + if (typeof options.compress === 'undefined') options.compress = true; + if (!this.extensions[PerMessageDeflate.extensionName]) { + options.compress = false; + } + + var readable = typeof stream.Readable === 'function' + ? stream.Readable + : stream.Stream; + + if (data instanceof readable) { + startQueue(this); + var self = this; + + sendStream(this, data, options, function send(error) { + process.nextTick(function tock() { + executeQueueSends(self); + }); + + if (typeof cb === 'function') cb(error); + }); + } else { + this._sender.send(data, options, cb); + } +}; + +/** + * Streams data through calls to a user supplied function + * + * @param {Object} Members - mask: boolean, binary: boolean, compress: boolean + * @param {function} 'function (error, send)' which is executed on successive ticks of which send is 'function (data, final)'. + * @api public + */ +WebSocket.prototype.stream = function stream(options, cb) { + if (typeof options === 'function') { + cb = options; + options = {}; + } + + var self = this; + + if (typeof cb !== 'function') throw new Error('callback must be provided'); + + if (this.readyState !== WebSocket.OPEN) { + if (typeof cb === 'function') cb(new Error('not opened')); + else throw new Error('not opened'); + return; + } + + if (this._queue) { + this._queue.push(function () { self.stream(options, cb); }); + return; + } + + options = options || {}; + + if (typeof options.mask === 'undefined') options.mask = !this._isServer; + if (typeof options.compress === 'undefined') options.compress = true; + if (!this.extensions[PerMessageDeflate.extensionName]) { + options.compress = false; + } + + startQueue(this); + + function send(data, final) { + try { + if (self.readyState !== WebSocket.OPEN) throw new Error('not opened'); + options.fin = final === true; + self._sender.send(data, options); + if (!final) process.nextTick(cb.bind(null, null, send)); + else executeQueueSends(self); + } catch (e) { + if (typeof cb === 'function') cb(e); + else { + delete self._queue; + self.emit('error', e); + } + } + } + + process.nextTick(cb.bind(null, null, send)); +}; + +/** + * Immediately shuts down the connection + * + * @api public + */ +WebSocket.prototype.terminate = function terminate() { + if (this.readyState === WebSocket.CLOSED) return; + + if (this._socket) { + this.readyState = WebSocket.CLOSING; + + // End the connection + try { this._socket.end(); } + catch (e) { + // Socket error during end() call, so just destroy it right now + cleanupWebsocketResources.call(this, true); + return; + } + + // Add a timeout to ensure that the connection is completely + // cleaned up within 30 seconds, even if the clean close procedure + // fails for whatever reason + // First cleanup any pre-existing timeout from an earlier "terminate" call, + // if one exists. Otherwise terminate calls in quick succession will leak timeouts + // and hold the program open for `closeTimout` time. + if (this._closeTimer) { clearTimeout(this._closeTimer); } + this._closeTimer = setTimeout(cleanupWebsocketResources.bind(this, true), closeTimeout); + } else if (this.readyState === WebSocket.CONNECTING) { + cleanupWebsocketResources.call(this, true); + } +}; + +/** + * Expose bufferedAmount + * + * @api public + */ +Object.defineProperty(WebSocket.prototype, 'bufferedAmount', { + get: function get() { + var amount = 0; + if (this._socket) { + amount = this._socket.bufferSize || 0; + } + return amount; + } +}); + +/** + * Emulates the W3C Browser based WebSocket interface using function members. + * + * @see http://dev.w3.org/html5/websockets/#the-websocket-interface + * @api public + */ +['open', 'error', 'close', 'message'].forEach(function(method) { + Object.defineProperty(WebSocket.prototype, 'on' + method, { + /** + * Returns the current listener + * + * @returns {Mixed} the set function or undefined + * @api public + */ + get: function get() { + var listener = this.listeners(method)[0]; + return listener ? (listener._listener ? listener._listener : listener) : undefined; + }, + + /** + * Start listening for events + * + * @param {Function} listener the listener + * @returns {Mixed} the set function or undefined + * @api public + */ + set: function set(listener) { + this.removeAllListeners(method); + this.addEventListener(method, listener); + } + }); +}); + +/** + * Emulates the W3C Browser based WebSocket interface using addEventListener. + * + * @see https://developer.mozilla.org/en/DOM/element.addEventListener + * @see http://dev.w3.org/html5/websockets/#the-websocket-interface + * @api public + */ +WebSocket.prototype.addEventListener = function(method, listener) { + var target = this; + + function onMessage (data, flags) { + listener.call(target, new MessageEvent(data, !!flags.binary, target)); + } + + function onClose (code, message) { + listener.call(target, new CloseEvent(code, message, target)); + } + + function onError (event) { + event.type = 'error'; + event.target = target; + listener.call(target, event); + } + + function onOpen () { + listener.call(target, new OpenEvent(target)); + } + + if (typeof listener === 'function') { + if (method === 'message') { + // store a reference so we can return the original function from the + // addEventListener hook + onMessage._listener = listener; + this.on(method, onMessage); + } else if (method === 'close') { + // store a reference so we can return the original function from the + // addEventListener hook + onClose._listener = listener; + this.on(method, onClose); + } else if (method === 'error') { + // store a reference so we can return the original function from the + // addEventListener hook + onError._listener = listener; + this.on(method, onError); + } else if (method === 'open') { + // store a reference so we can return the original function from the + // addEventListener hook + onOpen._listener = listener; + this.on(method, onOpen); + } else { + this.on(method, listener); + } + } +}; + +module.exports = WebSocket; +module.exports.buildHostHeader = buildHostHeader + +/** + * W3C MessageEvent + * + * @see http://www.w3.org/TR/html5/comms.html + * @constructor + * @api private + */ +function MessageEvent(dataArg, isBinary, target) { + this.type = 'message'; + this.data = dataArg; + this.target = target; + this.binary = isBinary; // non-standard. +} + +/** + * W3C CloseEvent + * + * @see http://www.w3.org/TR/html5/comms.html + * @constructor + * @api private + */ +function CloseEvent(code, reason, target) { + this.type = 'close'; + this.wasClean = (typeof code === 'undefined' || code === 1000); + this.code = code; + this.reason = reason; + this.target = target; +} + +/** + * W3C OpenEvent + * + * @see http://www.w3.org/TR/html5/comms.html + * @constructor + * @api private + */ +function OpenEvent(target) { + this.type = 'open'; + this.target = target; +} + +// Append port number to Host header, only if specified in the url +// and non-default +function buildHostHeader(isSecure, hostname, port) { + var headerHost = hostname; + if (hostname) { + if ((isSecure && (port != 443)) || (!isSecure && (port != 80))){ + headerHost = headerHost + ':' + port; + } + } + return headerHost; +} + +/** + * Entirely private apis, + * which may or may not be bound to a sepcific WebSocket instance. + */ +function initAsServerClient(req, socket, upgradeHead, options) { + options = new Options({ + protocolVersion: protocolVersion, + protocol: null, + extensions: {} + }).merge(options); + + // expose state properties + this.protocol = options.value.protocol; + this.protocolVersion = options.value.protocolVersion; + this.extensions = options.value.extensions; + this.supports.binary = (this.protocolVersion !== 'hixie-76'); + this.upgradeReq = req; + this.readyState = WebSocket.CONNECTING; + this._isServer = true; + + // establish connection + if (options.value.protocolVersion === 'hixie-76') { + establishConnection.call(this, ReceiverHixie, SenderHixie, socket, upgradeHead); + } else { + establishConnection.call(this, Receiver, Sender, socket, upgradeHead); + } +} + +function initAsClient(address, protocols, options) { + options = new Options({ + origin: null, + protocolVersion: protocolVersion, + host: null, + headers: null, + protocol: protocols.join(','), + agent: null, + + // ssl-related options + pfx: null, + key: null, + passphrase: null, + cert: null, + ca: null, + ciphers: null, + rejectUnauthorized: null, + perMessageDeflate: true, + localAddress: null + }).merge(options); + + if (options.value.protocolVersion !== 8 && options.value.protocolVersion !== 13) { + throw new Error('unsupported protocol version'); + } + + // verify URL and establish http class + var serverUrl = url.parse(address); + var isUnixSocket = serverUrl.protocol === 'ws+unix:'; + if (!serverUrl.host && !isUnixSocket) throw new Error('invalid url'); + var isSecure = serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:'; + var httpObj = isSecure ? https : http; + var port = serverUrl.port || (isSecure ? 443 : 80); + var auth = serverUrl.auth; + + // prepare extensions + var extensionsOffer = {}; + var perMessageDeflate; + if (options.value.perMessageDeflate) { + perMessageDeflate = new PerMessageDeflate(typeof options.value.perMessageDeflate !== true ? options.value.perMessageDeflate : {}, false); + extensionsOffer[PerMessageDeflate.extensionName] = perMessageDeflate.offer(); + } + + // expose state properties + this._isServer = false; + this.url = address; + this.protocolVersion = options.value.protocolVersion; + this.supports.binary = (this.protocolVersion !== 'hixie-76'); + + // begin handshake + var key = new Buffer(options.value.protocolVersion + '-' + Date.now()).toString('base64'); + var shasum = crypto.createHash('sha1'); + shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'); + var expectedServerKey = shasum.digest('base64'); + + var agent = options.value.agent; + + var headerHost = buildHostHeader(isSecure, serverUrl.hostname, port) + + var requestOptions = { + port: port, + host: serverUrl.hostname, + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Host': headerHost, + 'Sec-WebSocket-Version': options.value.protocolVersion, + 'Sec-WebSocket-Key': key + } + }; + + // If we have basic auth. + if (auth) { + requestOptions.headers.Authorization = 'Basic ' + new Buffer(auth).toString('base64'); + } + + if (options.value.protocol) { + requestOptions.headers['Sec-WebSocket-Protocol'] = options.value.protocol; + } + + if (options.value.host) { + requestOptions.headers.Host = options.value.host; + } + + if (options.value.headers) { + for (var header in options.value.headers) { + if (options.value.headers.hasOwnProperty(header)) { + requestOptions.headers[header] = options.value.headers[header]; + } + } + } + + if (Object.keys(extensionsOffer).length) { + requestOptions.headers['Sec-WebSocket-Extensions'] = Extensions.format(extensionsOffer); + } + + if (options.isDefinedAndNonNull('pfx') + || options.isDefinedAndNonNull('key') + || options.isDefinedAndNonNull('passphrase') + || options.isDefinedAndNonNull('cert') + || options.isDefinedAndNonNull('ca') + || options.isDefinedAndNonNull('ciphers') + || options.isDefinedAndNonNull('rejectUnauthorized')) { + + if (options.isDefinedAndNonNull('pfx')) requestOptions.pfx = options.value.pfx; + if (options.isDefinedAndNonNull('key')) requestOptions.key = options.value.key; + if (options.isDefinedAndNonNull('passphrase')) requestOptions.passphrase = options.value.passphrase; + if (options.isDefinedAndNonNull('cert')) requestOptions.cert = options.value.cert; + if (options.isDefinedAndNonNull('ca')) requestOptions.ca = options.value.ca; + if (options.isDefinedAndNonNull('ciphers')) requestOptions.ciphers = options.value.ciphers; + if (options.isDefinedAndNonNull('rejectUnauthorized')) requestOptions.rejectUnauthorized = options.value.rejectUnauthorized; + + if (!agent) { + // global agent ignores client side certificates + agent = new httpObj.Agent(requestOptions); + } + } + + requestOptions.path = serverUrl.path || '/'; + + if (agent) { + requestOptions.agent = agent; + } + + if (isUnixSocket) { + requestOptions.socketPath = serverUrl.pathname; + } + + if (options.value.localAddress) { + requestOptions.localAddress = options.value.localAddress; + } + + if (options.value.origin) { + if (options.value.protocolVersion < 13) requestOptions.headers['Sec-WebSocket-Origin'] = options.value.origin; + else requestOptions.headers.Origin = options.value.origin; + } + + var self = this; + var req = httpObj.request(requestOptions); + + req.on('error', function onerror(error) { + self.emit('error', error); + cleanupWebsocketResources.call(self, error); + }); + + req.once('response', function response(res) { + var error; + + if (!self.emit('unexpected-response', req, res)) { + error = new Error('unexpected server response (' + res.statusCode + ')'); + req.abort(); + self.emit('error', error); + } + + cleanupWebsocketResources.call(self, error); + }); + + req.once('upgrade', function upgrade(res, socket, upgradeHead) { + if (self.readyState === WebSocket.CLOSED) { + // client closed before server accepted connection + self.emit('close'); + self.removeAllListeners(); + socket.end(); + return; + } + + var serverKey = res.headers['sec-websocket-accept']; + if (typeof serverKey === 'undefined' || serverKey !== expectedServerKey) { + self.emit('error', 'invalid server key'); + self.removeAllListeners(); + socket.end(); + return; + } + + var serverProt = res.headers['sec-websocket-protocol']; + var protList = (options.value.protocol || "").split(/, */); + var protError = null; + + if (!options.value.protocol && serverProt) { + protError = 'server sent a subprotocol even though none requested'; + } else if (options.value.protocol && !serverProt) { + protError = 'server sent no subprotocol even though requested'; + } else if (serverProt && protList.indexOf(serverProt) === -1) { + protError = 'server responded with an invalid protocol'; + } + + if (protError) { + self.emit('error', protError); + self.removeAllListeners(); + socket.end(); + return; + } else if (serverProt) { + self.protocol = serverProt; + } + + var serverExtensions = Extensions.parse(res.headers['sec-websocket-extensions']); + if (perMessageDeflate && serverExtensions[PerMessageDeflate.extensionName]) { + try { + perMessageDeflate.accept(serverExtensions[PerMessageDeflate.extensionName]); + } catch (err) { + self.emit('error', 'invalid extension parameter'); + self.removeAllListeners(); + socket.end(); + return; + } + self.extensions[PerMessageDeflate.extensionName] = perMessageDeflate; + } + + establishConnection.call(self, Receiver, Sender, socket, upgradeHead); + + // perform cleanup on http resources + req.removeAllListeners(); + req = null; + agent = null; + }); + + req.end(); + this.readyState = WebSocket.CONNECTING; +} + +function establishConnection(ReceiverClass, SenderClass, socket, upgradeHead) { + var ultron = this._ultron = new Ultron(socket) + , called = false + , self = this; + + socket.setTimeout(0); + socket.setNoDelay(true); + + this._receiver = new ReceiverClass(this.extensions); + this._socket = socket; + + // socket cleanup handlers + ultron.on('end', cleanupWebsocketResources.bind(this)); + ultron.on('close', cleanupWebsocketResources.bind(this)); + ultron.on('error', cleanupWebsocketResources.bind(this)); + + // ensure that the upgradeHead is added to the receiver + function firstHandler(data) { + if (called || self.readyState === WebSocket.CLOSED) return; + + called = true; + socket.removeListener('data', firstHandler); + ultron.on('data', realHandler); + + if (upgradeHead && upgradeHead.length > 0) { + realHandler(upgradeHead); + upgradeHead = null; + } + + if (data) realHandler(data); + } + + // subsequent packets are pushed straight to the receiver + function realHandler(data) { + self.bytesReceived += data.length; + self._receiver.add(data); + } + + ultron.on('data', firstHandler); + + // if data was passed along with the http upgrade, + // this will schedule a push of that on to the receiver. + // this has to be done on next tick, since the caller + // hasn't had a chance to set event handlers on this client + // object yet. + process.nextTick(firstHandler); + + // receiver event handlers + self._receiver.ontext = function ontext(data, flags) { + flags = flags || {}; + + self.emit('message', data, flags); + }; + + self._receiver.onbinary = function onbinary(data, flags) { + flags = flags || {}; + + flags.binary = true; + self.emit('message', data, flags); + }; + + self._receiver.onping = function onping(data, flags) { + flags = flags || {}; + + self.pong(data, { + mask: !self._isServer, + binary: flags.binary === true + }, true); + + self.emit('ping', data, flags); + }; + + self._receiver.onpong = function onpong(data, flags) { + self.emit('pong', data, flags || {}); + }; + + self._receiver.onclose = function onclose(code, data, flags) { + flags = flags || {}; + + self._closeReceived = true; + self.close(code, data); + }; + + self._receiver.onerror = function onerror(reason, errorCode) { + // close the connection when the receiver reports a HyBi error code + self.close(typeof errorCode !== 'undefined' ? errorCode : 1002, ''); + self.emit('error', reason, errorCode); + }; + + // finalize the client + this._sender = new SenderClass(socket, this.extensions); + this._sender.on('error', function onerror(error) { + self.close(1002, ''); + self.emit('error', error); + }); + + this.readyState = WebSocket.OPEN; + this.emit('open'); +} + +function startQueue(instance) { + instance._queue = instance._queue || []; +} + +function executeQueueSends(instance) { + var queue = instance._queue; + if (typeof queue === 'undefined') return; + + delete instance._queue; + for (var i = 0, l = queue.length; i < l; ++i) { + queue[i](); + } +} + +function sendStream(instance, stream, options, cb) { + stream.on('data', function incoming(data) { + if (instance.readyState !== WebSocket.OPEN) { + if (typeof cb === 'function') cb(new Error('not opened')); + else { + delete instance._queue; + instance.emit('error', new Error('not opened')); + } + return; + } + + options.fin = false; + instance._sender.send(data, options); + }); + + stream.on('end', function end() { + if (instance.readyState !== WebSocket.OPEN) { + if (typeof cb === 'function') cb(new Error('not opened')); + else { + delete instance._queue; + instance.emit('error', new Error('not opened')); + } + return; + } + + options.fin = true; + instance._sender.send(null, options); + + if (typeof cb === 'function') cb(null); + }); +} + +function cleanupWebsocketResources(error) { + if (this.readyState === WebSocket.CLOSED) return; + + var emitClose = this.readyState !== WebSocket.CONNECTING; + this.readyState = WebSocket.CLOSED; + + clearTimeout(this._closeTimer); + this._closeTimer = null; + + if (emitClose) { + // If the connection was closed abnormally (with an error), + // then the close code must default to 1006. + if (error) { + this._closeCode = 1006; + } + this.emit('close', this._closeCode || 1000, this._closeMessage || ''); + } + + if (this._socket) { + if (this._ultron) this._ultron.destroy(); + this._socket.on('error', function onerror() { + try { this.destroy(); } + catch (e) {} + }); + + try { + if (!error) this._socket.end(); + else this._socket.destroy(); + } catch (e) { /* Ignore termination errors */ } + + this._socket = null; + this._ultron = null; + } + + if (this._sender) { + this._sender.removeAllListeners(); + this._sender = null; + } + + if (this._receiver) { + this._receiver.cleanup(); + this._receiver = null; + } + + if (this.extensions[PerMessageDeflate.extensionName]) { + this.extensions[PerMessageDeflate.extensionName].cleanup(); + } + + this.extensions = null; + + this.removeAllListeners(); + this.on('error', function onerror() {}); // catch all errors after this + delete this._queue; +} diff --git a/node_modules/ws/lib/WebSocketServer.js b/node_modules/ws/lib/WebSocketServer.js new file mode 100644 index 0000000..ba0e4c0 --- /dev/null +++ b/node_modules/ws/lib/WebSocketServer.js @@ -0,0 +1,513 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util') + , events = require('events') + , http = require('http') + , crypto = require('crypto') + , Options = require('options') + , WebSocket = require('./WebSocket') + , Extensions = require('./Extensions') + , PerMessageDeflate = require('./PerMessageDeflate') + , tls = require('tls') + , url = require('url'); + +/** + * WebSocket Server implementation + */ + +function WebSocketServer(options, callback) { + if (this instanceof WebSocketServer === false) { + return new WebSocketServer(options, callback); + } + + events.EventEmitter.call(this); + + options = new Options({ + host: '0.0.0.0', + port: null, + server: null, + verifyClient: null, + handleProtocols: null, + path: null, + noServer: false, + disableHixie: false, + clientTracking: true, + perMessageDeflate: true + }).merge(options); + + if (!options.isDefinedAndNonNull('port') && !options.isDefinedAndNonNull('server') && !options.value.noServer) { + throw new TypeError('`port` or a `server` must be provided'); + } + + var self = this; + + if (options.isDefinedAndNonNull('port')) { + this._server = http.createServer(function (req, res) { + var body = http.STATUS_CODES[426]; + res.writeHead(426, { + 'Content-Length': body.length, + 'Content-Type': 'text/plain' + }); + res.end(body); + }); + this._server.allowHalfOpen = false; + this._server.listen(options.value.port, options.value.host, callback); + this._closeServer = function() { if (self._server) self._server.close(); }; + } + else if (options.value.server) { + this._server = options.value.server; + if (options.value.path) { + // take note of the path, to avoid collisions when multiple websocket servers are + // listening on the same http server + if (this._server._webSocketPaths && options.value.server._webSocketPaths[options.value.path]) { + throw new Error('two instances of WebSocketServer cannot listen on the same http server path'); + } + if (typeof this._server._webSocketPaths !== 'object') { + this._server._webSocketPaths = {}; + } + this._server._webSocketPaths[options.value.path] = 1; + } + } + if (this._server) this._server.once('listening', function() { self.emit('listening'); }); + + if (typeof this._server != 'undefined') { + this._server.on('error', function(error) { + self.emit('error', error) + }); + this._server.on('upgrade', function(req, socket, upgradeHead) { + //copy upgradeHead to avoid retention of large slab buffers used in node core + var head = new Buffer(upgradeHead.length); + upgradeHead.copy(head); + + self.handleUpgrade(req, socket, head, function(client) { + self.emit('connection'+req.url, client); + self.emit('connection', client); + }); + }); + } + + this.options = options.value; + this.path = options.value.path; + this.clients = []; +} + +/** + * Inherits from EventEmitter. + */ + +util.inherits(WebSocketServer, events.EventEmitter); + +/** + * Immediately shuts down the connection. + * + * @api public + */ + +WebSocketServer.prototype.close = function(callback) { + // terminate all associated clients + var error = null; + try { + for (var i = 0, l = this.clients.length; i < l; ++i) { + this.clients[i].terminate(); + } + } + catch (e) { + error = e; + } + + // remove path descriptor, if any + if (this.path && this._server._webSocketPaths) { + delete this._server._webSocketPaths[this.path]; + if (Object.keys(this._server._webSocketPaths).length == 0) { + delete this._server._webSocketPaths; + } + } + + // close the http server if it was internally created + try { + if (typeof this._closeServer !== 'undefined') { + this._closeServer(); + } + } + finally { + delete this._server; + } + if(callback) + callback(error); + else if(error) + throw error; +} + +/** + * Handle a HTTP Upgrade request. + * + * @api public + */ + +WebSocketServer.prototype.handleUpgrade = function(req, socket, upgradeHead, cb) { + // check for wrong path + if (this.options.path) { + var u = url.parse(req.url); + if (u && u.pathname !== this.options.path) return; + } + + if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') { + abortConnection(socket, 400, 'Bad Request'); + return; + } + + if (req.headers['sec-websocket-key1']) handleHixieUpgrade.apply(this, arguments); + else handleHybiUpgrade.apply(this, arguments); +} + +module.exports = WebSocketServer; + +/** + * Entirely private apis, + * which may or may not be bound to a sepcific WebSocket instance. + */ + +function handleHybiUpgrade(req, socket, upgradeHead, cb) { + // handle premature socket errors + var errorHandler = function() { + try { socket.destroy(); } catch (e) {} + } + socket.on('error', errorHandler); + + // verify key presence + if (!req.headers['sec-websocket-key']) { + abortConnection(socket, 400, 'Bad Request'); + return; + } + + // verify version + var version = parseInt(req.headers['sec-websocket-version']); + if ([8, 13].indexOf(version) === -1) { + abortConnection(socket, 400, 'Bad Request'); + return; + } + + // verify protocol + var protocols = req.headers['sec-websocket-protocol']; + + // verify client + var origin = version < 13 ? + req.headers['sec-websocket-origin'] : + req.headers['origin']; + + // handle extensions offer + var extensionsOffer = Extensions.parse(req.headers['sec-websocket-extensions']); + + // handler to call when the connection sequence completes + var self = this; + var completeHybiUpgrade2 = function(protocol) { + + // calc key + var key = req.headers['sec-websocket-key']; + var shasum = crypto.createHash('sha1'); + shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + key = shasum.digest('base64'); + + var headers = [ + 'HTTP/1.1 101 Switching Protocols' + , 'Upgrade: websocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Accept: ' + key + ]; + + if (typeof protocol != 'undefined') { + headers.push('Sec-WebSocket-Protocol: ' + protocol); + } + + var extensions = {}; + try { + extensions = acceptExtensions.call(self, extensionsOffer); + } catch (err) { + abortConnection(socket, 400, 'Bad Request'); + return; + } + + if (Object.keys(extensions).length) { + var serverExtensions = {}; + Object.keys(extensions).forEach(function(token) { + serverExtensions[token] = [extensions[token].params] + }); + headers.push('Sec-WebSocket-Extensions: ' + Extensions.format(serverExtensions)); + } + + // allows external modification/inspection of handshake headers + self.emit('headers', headers); + + socket.setTimeout(0); + socket.setNoDelay(true); + try { + socket.write(headers.concat('', '').join('\r\n')); + } + catch (e) { + // if the upgrade write fails, shut the connection down hard + try { socket.destroy(); } catch (e) {} + return; + } + + var client = new WebSocket([req, socket, upgradeHead], { + protocolVersion: version, + protocol: protocol, + extensions: extensions + }); + + if (self.options.clientTracking) { + self.clients.push(client); + client.on('close', function() { + var index = self.clients.indexOf(client); + if (index != -1) { + self.clients.splice(index, 1); + } + }); + } + + // signal upgrade complete + socket.removeListener('error', errorHandler); + cb(client); + } + + // optionally call external protocol selection handler before + // calling completeHybiUpgrade2 + var completeHybiUpgrade1 = function() { + // choose from the sub-protocols + if (typeof self.options.handleProtocols == 'function') { + var protList = (protocols || "").split(/, */); + var callbackCalled = false; + var res = self.options.handleProtocols(protList, function(result, protocol) { + callbackCalled = true; + if (!result) abortConnection(socket, 401, 'Unauthorized'); + else completeHybiUpgrade2(protocol); + }); + if (!callbackCalled) { + // the handleProtocols handler never called our callback + abortConnection(socket, 501, 'Could not process protocols'); + } + return; + } else { + if (typeof protocols !== 'undefined') { + completeHybiUpgrade2(protocols.split(/, */)[0]); + } + else { + completeHybiUpgrade2(); + } + } + } + + // optionally call external client verification handler + if (typeof this.options.verifyClient == 'function') { + var info = { + origin: origin, + secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined', + req: req + }; + if (this.options.verifyClient.length == 2) { + this.options.verifyClient(info, function(result, code, name) { + if (typeof code === 'undefined') code = 401; + if (typeof name === 'undefined') name = http.STATUS_CODES[code]; + + if (!result) abortConnection(socket, code, name); + else completeHybiUpgrade1(); + }); + return; + } + else if (!this.options.verifyClient(info)) { + abortConnection(socket, 401, 'Unauthorized'); + return; + } + } + + completeHybiUpgrade1(); +} + +function handleHixieUpgrade(req, socket, upgradeHead, cb) { + // handle premature socket errors + var errorHandler = function() { + try { socket.destroy(); } catch (e) {} + } + socket.on('error', errorHandler); + + // bail if options prevent hixie + if (this.options.disableHixie) { + abortConnection(socket, 401, 'Hixie support disabled'); + return; + } + + // verify key presence + if (!req.headers['sec-websocket-key2']) { + abortConnection(socket, 400, 'Bad Request'); + return; + } + + var origin = req.headers['origin'] + , self = this; + + // setup handshake completion to run after client has been verified + var onClientVerified = function() { + var wshost; + if (!req.headers['x-forwarded-host']) + wshost = req.headers.host; + else + wshost = req.headers['x-forwarded-host']; + var location = ((req.headers['x-forwarded-proto'] === 'https' || socket.encrypted) ? 'wss' : 'ws') + '://' + wshost + req.url + , protocol = req.headers['sec-websocket-protocol']; + + // handshake completion code to run once nonce has been successfully retrieved + var completeHandshake = function(nonce, rest) { + // calculate key + var k1 = req.headers['sec-websocket-key1'] + , k2 = req.headers['sec-websocket-key2'] + , md5 = crypto.createHash('md5'); + + [k1, k2].forEach(function (k) { + var n = parseInt(k.replace(/[^\d]/g, '')) + , spaces = k.replace(/[^ ]/g, '').length; + if (spaces === 0 || n % spaces !== 0){ + abortConnection(socket, 400, 'Bad Request'); + return; + } + n /= spaces; + md5.update(String.fromCharCode( + n >> 24 & 0xFF, + n >> 16 & 0xFF, + n >> 8 & 0xFF, + n & 0xFF)); + }); + md5.update(nonce.toString('binary')); + + var headers = [ + 'HTTP/1.1 101 Switching Protocols' + , 'Upgrade: WebSocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Location: ' + location + ]; + if (typeof protocol != 'undefined') headers.push('Sec-WebSocket-Protocol: ' + protocol); + if (typeof origin != 'undefined') headers.push('Sec-WebSocket-Origin: ' + origin); + + socket.setTimeout(0); + socket.setNoDelay(true); + try { + // merge header and hash buffer + var headerBuffer = new Buffer(headers.concat('', '').join('\r\n')); + var hashBuffer = new Buffer(md5.digest('binary'), 'binary'); + var handshakeBuffer = new Buffer(headerBuffer.length + hashBuffer.length); + headerBuffer.copy(handshakeBuffer, 0); + hashBuffer.copy(handshakeBuffer, headerBuffer.length); + + // do a single write, which - upon success - causes a new client websocket to be setup + socket.write(handshakeBuffer, 'binary', function(err) { + if (err) return; // do not create client if an error happens + var client = new WebSocket([req, socket, rest], { + protocolVersion: 'hixie-76', + protocol: protocol + }); + if (self.options.clientTracking) { + self.clients.push(client); + client.on('close', function() { + var index = self.clients.indexOf(client); + if (index != -1) { + self.clients.splice(index, 1); + } + }); + } + + // signal upgrade complete + socket.removeListener('error', errorHandler); + cb(client); + }); + } + catch (e) { + try { socket.destroy(); } catch (e) {} + return; + } + } + + // retrieve nonce + var nonceLength = 8; + if (upgradeHead && upgradeHead.length >= nonceLength) { + var nonce = upgradeHead.slice(0, nonceLength); + var rest = upgradeHead.length > nonceLength ? upgradeHead.slice(nonceLength) : null; + completeHandshake.call(self, nonce, rest); + } + else { + // nonce not present in upgradeHead, so we must wait for enough data + // data to arrive before continuing + var nonce = new Buffer(nonceLength); + upgradeHead.copy(nonce, 0); + var received = upgradeHead.length; + var rest = null; + var handler = function (data) { + var toRead = Math.min(data.length, nonceLength - received); + if (toRead === 0) return; + data.copy(nonce, received, 0, toRead); + received += toRead; + if (received == nonceLength) { + socket.removeListener('data', handler); + if (toRead < data.length) rest = data.slice(toRead); + completeHandshake.call(self, nonce, rest); + } + } + socket.on('data', handler); + } + } + + // verify client + if (typeof this.options.verifyClient == 'function') { + var info = { + origin: origin, + secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined', + req: req + }; + if (this.options.verifyClient.length == 2) { + var self = this; + this.options.verifyClient(info, function(result, code, name) { + if (typeof code === 'undefined') code = 401; + if (typeof name === 'undefined') name = http.STATUS_CODES[code]; + + if (!result) abortConnection(socket, code, name); + else onClientVerified.apply(self); + }); + return; + } + else if (!this.options.verifyClient(info)) { + abortConnection(socket, 401, 'Unauthorized'); + return; + } + } + + // no client verification required + onClientVerified(); +} + +function acceptExtensions(offer) { + var extensions = {}; + var options = this.options.perMessageDeflate; + if (options && offer[PerMessageDeflate.extensionName]) { + var perMessageDeflate = new PerMessageDeflate(options !== true ? options : {}, true); + perMessageDeflate.accept(offer[PerMessageDeflate.extensionName]); + extensions[PerMessageDeflate.extensionName] = perMessageDeflate; + } + return extensions; +} + +function abortConnection(socket, code, name) { + try { + var response = [ + 'HTTP/1.1 ' + code + ' ' + name, + 'Content-type: text/html' + ]; + socket.write(response.concat('', '').join('\r\n')); + } + catch (e) { /* ignore errors - we've aborted this connection */ } + finally { + // ensure that an early aborted connection is shut down completely + try { socket.destroy(); } catch (e) {} + } +} diff --git a/node_modules/ws/lib/browser.js b/node_modules/ws/lib/browser.js new file mode 100644 index 0000000..8d3a755 --- /dev/null +++ b/node_modules/ws/lib/browser.js @@ -0,0 +1,43 @@ + +/** + * Module dependencies. + */ + +var global = (function() { return this; })(); + +/** + * WebSocket constructor. + */ + +var WebSocket = global.WebSocket || global.MozWebSocket; + +/** + * Module exports. + */ + +module.exports = WebSocket ? ws : null; + +/** + * WebSocket constructor. + * + * The third `opts` options object gets ignored in web browsers, since it's + * non-standard, and throws a TypeError if passed to the constructor. + * See: https://github.com/einaros/ws/issues/227 + * + * @param {String} uri + * @param {Array} protocols (optional) + * @param {Object) opts (optional) + * @api public + */ + +function ws(uri, protocols, opts) { + var instance; + if (protocols) { + instance = new WebSocket(uri, protocols); + } else { + instance = new WebSocket(uri); + } + return instance; +} + +if (WebSocket) ws.prototype = WebSocket.prototype; diff --git a/node_modules/ws/node_modules/bufferutil/.npmignore b/node_modules/ws/node_modules/bufferutil/.npmignore new file mode 100644 index 0000000..0c90f67 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/.npmignore @@ -0,0 +1,3 @@ +npm-debug.log +node_modules +build diff --git a/node_modules/ws/node_modules/bufferutil/.travis.yml b/node_modules/ws/node_modules/bufferutil/.travis.yml new file mode 100644 index 0000000..cd09de2 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/.travis.yml @@ -0,0 +1,16 @@ +language: node_js +node_js: + - "iojs-v3" + - "iojs-v2" + - "iojs-v1" + - "0.12" + - "0.10" +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-4.9 + - g++-4.9 +before_install: + - export CC="gcc-4.9" CXX="g++-4.9" diff --git a/node_modules/ws/node_modules/bufferutil/README.md b/node_modules/ws/node_modules/bufferutil/README.md new file mode 100644 index 0000000..777e996 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/README.md @@ -0,0 +1,62 @@ +# bufferutil + +[![Build Status](https://travis-ci.org/websockets/bufferutil.svg?branch=master)](https://travis-ci.org/websockets/bufferutil) + +Buffer utils is one of the modules that makes `ws` fast. It's optimized for +certain buffer based operations such as merging buffers, generating WebSocket +masks and unmasking. + +As the module consists of binary components, it should be used an +`optionalDependency` so when installation fails, it doesn't halt the +installation of your module. There are fallback files available in this +repository. See `fallback.js` for the suggest fallback implementation if +installation fails. + +## Installation + +``` +npm install bufferutil +``` + +## API + +In all examples we assume that you've already required the BufferUtil as +followed: + +```js +'use strict'; + +var bu = require('bufferutil').BufferUtil; +``` + +The module exposes 3 different functions: + +#### merge + +Merge multiple buffers in the first supplied buffer argument: + +```js +bu.merge(buffer, [buffer1, buffer2]); +``` + +This merges buffer1 and buffer2 which are in an array into buffer. + +#### mask + +Apply a WebSocket mask on the given data. + +```js +bu.mask(buffer, mask); +``` + +#### unmask + +Remove a WebSocket mask on the given data.;w + +```js +bu.unmask(buffer, mask); +``` + +## License + +MIT diff --git a/node_modules/ws/node_modules/bufferutil/binding.gyp b/node_modules/ws/node_modules/bufferutil/binding.gyp new file mode 100644 index 0000000..636f324 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/binding.gyp @@ -0,0 +1,11 @@ +{ + 'targets': [ + { + 'target_name': 'bufferutil', + 'include_dirs': ["> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_objc = CXX($(TOOLSET)) $@ +cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< + +quiet_cmd_objcxx = CXX($(TOOLSET)) $@ +cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# Commands for precompiled header files. +quiet_cmd_pch_c = CXX($(TOOLSET)) $@ +cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_cc = CXX($(TOOLSET)) $@ +cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_m = CXX($(TOOLSET)) $@ +cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< +quiet_cmd_pch_mm = CXX($(TOOLSET)) $@ +cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# gyp-mac-tool is written next to the root Makefile by gyp. +# Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd +# already. +quiet_cmd_mac_tool = MACTOOL $(4) $< +cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@" + +quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@ +cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4) + +quiet_cmd_infoplist = INFOPLIST $@ +cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@" + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = rm -rf "$@" && cp -af "$<" "$@" + +quiet_cmd_alink = LIBTOOL-STATIC $@ +cmd_alink = rm -f $@ && ./gyp-mac-tool filter-libtool libtool $(GYP_LIBTOOLFLAGS) -static -o $@ $(filter %.o,$^) + +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -bundle $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# Helper that executes all postbuilds until one fails. +define do_postbuilds + @E=0;\ + for p in $(POSTBUILDS); do\ + eval $$p;\ + E=$$?;\ + if [ $$E -ne 0 ]; then\ + break;\ + fi;\ + done;\ + if [ $$E -ne 0 ]; then\ + rm -rf "$@";\ + exit $$E;\ + fi +endef + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 2,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + $(call do_postbuilds) + ) +) +endef + +# Declare the "all" target first so it is the default, +# even though we don't have the deps yet. +.PHONY: all +all: + +# make looks for ways to re-generate included makefiles, but in our case, we +# don't have a direct way. Explicitly telling make that it has nothing to do +# for them makes it go faster. +%.d: ; + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,bufferutil.target.mk)))),) + include bufferutil.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = cd $(srcdir); /Users/Oracle/.nvm/versions/node/v0.12.7/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "--toplevel-dir=." -I/Users/Oracle/dev/selenium-webdriver-nodejs/node_modules/ws/node_modules/bufferutil/build/config.gypi -I/Users/Oracle/.nvm/versions/node/v0.12.7/lib/node_modules/npm/node_modules/node-gyp/addon.gypi -I/Users/Oracle/.node-gyp/0.12.7/common.gypi "--depth=." "-Goutput_dir=." "--generator-output=build" "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/Oracle/.node-gyp/0.12.7" "-Dmodule_root_dir=/Users/Oracle/dev/selenium-webdriver-nodejs/node_modules/ws/node_modules/bufferutil" binding.gyp +Makefile: $(srcdir)/../../../../../../.node-gyp/0.12.7/common.gypi $(srcdir)/../../../../../../.nvm/versions/node/v0.12.7/lib/node_modules/npm/node_modules/node-gyp/addon.gypi $(srcdir)/build/config.gypi $(srcdir)/binding.gyp + $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + include $(d_files) +endif diff --git a/node_modules/ws/node_modules/bufferutil/build/Release/.deps/Release/bufferutil.node.d b/node_modules/ws/node_modules/bufferutil/build/Release/.deps/Release/bufferutil.node.d new file mode 100644 index 0000000..0dc664f --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/build/Release/.deps/Release/bufferutil.node.d @@ -0,0 +1 @@ +cmd_Release/bufferutil.node := c++ -bundle -undefined dynamic_lookup -Wl,-search_paths_first -mmacosx-version-min=10.5 -arch x86_64 -L./Release -o Release/bufferutil.node Release/obj.target/bufferutil/src/bufferutil.o diff --git a/node_modules/ws/node_modules/bufferutil/build/Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d b/node_modules/ws/node_modules/bufferutil/build/Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d new file mode 100644 index 0000000..6f4cd74 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/build/Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d @@ -0,0 +1,54 @@ +cmd_Release/obj.target/bufferutil/src/bufferutil.o := c++ '-DNODE_GYP_MODULE_NAME=bufferutil' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DBUILDING_NODE_EXTENSION' -I/Users/Oracle/.node-gyp/0.12.7/src -I/Users/Oracle/.node-gyp/0.12.7/deps/uv/include -I/Users/Oracle/.node-gyp/0.12.7/deps/v8/include -I../node_modules/nan -Os -gdwarf-2 -mmacosx-version-min=10.5 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d.raw -c -o Release/obj.target/bufferutil/src/bufferutil.o ../src/bufferutil.cc +Release/obj.target/bufferutil/src/bufferutil.o: ../src/bufferutil.cc \ + /Users/Oracle/.node-gyp/0.12.7/deps/v8/include/v8.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/v8/include/v8stdint.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/v8/include/v8config.h \ + /Users/Oracle/.node-gyp/0.12.7/src/node.h \ + /Users/Oracle/.node-gyp/0.12.7/src/node_version.h \ + /Users/Oracle/.node-gyp/0.12.7/src/node_buffer.h \ + /Users/Oracle/.node-gyp/0.12.7/src/smalloc.h \ + /Users/Oracle/.node-gyp/0.12.7/src/node_object_wrap.h \ + ../node_modules/nan/nan.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-errno.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-version.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-unix.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-threadpool.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-darwin.h \ + ../node_modules/nan/nan_callbacks.h \ + ../node_modules/nan/nan_callbacks_12_inl.h \ + ../node_modules/nan/nan_maybe_pre_43_inl.h \ + ../node_modules/nan/nan_converters.h \ + ../node_modules/nan/nan_converters_pre_43_inl.h \ + ../node_modules/nan/nan_new.h \ + ../node_modules/nan/nan_implementation_12_inl.h \ + ../node_modules/nan/nan_persistent_12_inl.h \ + ../node_modules/nan/nan_weak.h ../node_modules/nan/nan_object_wrap.h \ + ../node_modules/nan/nan_typedarray_contents.h +../src/bufferutil.cc: +/Users/Oracle/.node-gyp/0.12.7/deps/v8/include/v8.h: +/Users/Oracle/.node-gyp/0.12.7/deps/v8/include/v8stdint.h: +/Users/Oracle/.node-gyp/0.12.7/deps/v8/include/v8config.h: +/Users/Oracle/.node-gyp/0.12.7/src/node.h: +/Users/Oracle/.node-gyp/0.12.7/src/node_version.h: +/Users/Oracle/.node-gyp/0.12.7/src/node_buffer.h: +/Users/Oracle/.node-gyp/0.12.7/src/smalloc.h: +/Users/Oracle/.node-gyp/0.12.7/src/node_object_wrap.h: +../node_modules/nan/nan.h: +/Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv.h: +/Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-errno.h: +/Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-version.h: +/Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-unix.h: +/Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-threadpool.h: +/Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-darwin.h: +../node_modules/nan/nan_callbacks.h: +../node_modules/nan/nan_callbacks_12_inl.h: +../node_modules/nan/nan_maybe_pre_43_inl.h: +../node_modules/nan/nan_converters.h: +../node_modules/nan/nan_converters_pre_43_inl.h: +../node_modules/nan/nan_new.h: +../node_modules/nan/nan_implementation_12_inl.h: +../node_modules/nan/nan_persistent_12_inl.h: +../node_modules/nan/nan_weak.h: +../node_modules/nan/nan_object_wrap.h: +../node_modules/nan/nan_typedarray_contents.h: diff --git a/node_modules/ws/node_modules/bufferutil/build/Release/bufferutil.node b/node_modules/ws/node_modules/bufferutil/build/Release/bufferutil.node new file mode 100755 index 0000000..cced14c Binary files /dev/null and b/node_modules/ws/node_modules/bufferutil/build/Release/bufferutil.node differ diff --git a/node_modules/ws/node_modules/bufferutil/build/Release/obj.target/bufferutil/src/bufferutil.o b/node_modules/ws/node_modules/bufferutil/build/Release/obj.target/bufferutil/src/bufferutil.o new file mode 100644 index 0000000..e536814 Binary files /dev/null and b/node_modules/ws/node_modules/bufferutil/build/Release/obj.target/bufferutil/src/bufferutil.o differ diff --git a/node_modules/ws/node_modules/bufferutil/build/binding.Makefile b/node_modules/ws/node_modules/bufferutil/build/binding.Makefile new file mode 100644 index 0000000..b532496 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/build/binding.Makefile @@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= ./build/. +.PHONY: all +all: + $(MAKE) bufferutil diff --git a/node_modules/ws/node_modules/bufferutil/build/bufferutil.target.mk b/node_modules/ws/node_modules/bufferutil/build/bufferutil.target.mk new file mode 100644 index 0000000..a2a4a43 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/build/bufferutil.target.mk @@ -0,0 +1,161 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := bufferutil +DEFS_Debug := \ + '-DNODE_GYP_MODULE_NAME=bufferutil' \ + '-D_DARWIN_USE_64_BIT_INODE=1' \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-DBUILDING_NODE_EXTENSION' \ + '-DDEBUG' \ + '-D_DEBUG' + +# Flags passed to all source files. +CFLAGS_Debug := \ + -O0 \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Debug := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Debug := \ + -fno-rtti \ + -fno-exceptions \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Debug := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Debug := + +INCS_Debug := \ + -I/Users/Oracle/.node-gyp/0.12.7/src \ + -I/Users/Oracle/.node-gyp/0.12.7/deps/uv/include \ + -I/Users/Oracle/.node-gyp/0.12.7/deps/v8/include \ + -I$(srcdir)/node_modules/nan + +DEFS_Release := \ + '-DNODE_GYP_MODULE_NAME=bufferutil' \ + '-D_DARWIN_USE_64_BIT_INODE=1' \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-DBUILDING_NODE_EXTENSION' + +# Flags passed to all source files. +CFLAGS_Release := \ + -Os \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Release := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Release := \ + -fno-rtti \ + -fno-exceptions \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Release := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Release := + +INCS_Release := \ + -I/Users/Oracle/.node-gyp/0.12.7/src \ + -I/Users/Oracle/.node-gyp/0.12.7/deps/uv/include \ + -I/Users/Oracle/.node-gyp/0.12.7/deps/v8/include \ + -I$(srcdir)/node_modules/nan + +OBJS := \ + $(obj).target/$(TARGET)/src/bufferutil.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Debug := \ + -undefined dynamic_lookup \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) + +LIBTOOLFLAGS_Debug := \ + -undefined dynamic_lookup \ + -Wl,-search_paths_first + +LDFLAGS_Release := \ + -undefined dynamic_lookup \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) + +LIBTOOLFLAGS_Release := \ + -undefined dynamic_lookup \ + -Wl,-search_paths_first + +LIBS := + +$(builddir)/bufferutil.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/bufferutil.node: LIBS := $(LIBS) +$(builddir)/bufferutil.node: GYP_LIBTOOLFLAGS := $(LIBTOOLFLAGS_$(BUILDTYPE)) +$(builddir)/bufferutil.node: TOOLSET := $(TOOLSET) +$(builddir)/bufferutil.node: $(OBJS) FORCE_DO_CMD + $(call do_cmd,solink_module) + +all_deps += $(builddir)/bufferutil.node +# Add target alias +.PHONY: bufferutil +bufferutil: $(builddir)/bufferutil.node + +# Short alias for building this executable. +.PHONY: bufferutil.node +bufferutil.node: $(builddir)/bufferutil.node + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/bufferutil.node + diff --git a/node_modules/ws/node_modules/bufferutil/build/config.gypi b/node_modules/ws/node_modules/bufferutil/build/config.gypi new file mode 100644 index 0000000..842860a --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/build/config.gypi @@ -0,0 +1,137 @@ +# Do not edit. File was generated by node-gyp's "configure" step +{ + "target_defaults": { + "cflags": [], + "default_configuration": "Release", + "defines": [], + "include_dirs": [], + "libraries": [] + }, + "variables": { + "clang": 1, + "host_arch": "x64", + "icu_data_file": "icudt54l.dat", + "icu_data_in": "../../deps/icu/source/data/in/icudt54l.dat", + "icu_endianness": "l", + "icu_gyp_path": "tools/icu/icu-generic.gyp", + "icu_locales": "en,root", + "icu_path": "./deps/icu", + "icu_small": "true", + "icu_ver_major": "54", + "node_install_npm": "true", + "node_prefix": "/", + "node_shared_cares": "false", + "node_shared_http_parser": "false", + "node_shared_libuv": "false", + "node_shared_openssl": "false", + "node_shared_v8": "false", + "node_shared_zlib": "false", + "node_tag": "", + "node_use_dtrace": "true", + "node_use_etw": "false", + "node_use_mdb": "false", + "node_use_openssl": "true", + "node_use_perfctr": "false", + "openssl_no_asm": 0, + "python": "/usr/bin/python", + "target_arch": "x64", + "uv_library": "static_library", + "uv_parent_path": "/deps/uv/", + "uv_use_dtrace": "true", + "v8_enable_gdbjit": 0, + "v8_enable_i18n_support": 1, + "v8_no_strict_aliasing": 1, + "v8_optimized_debug": 0, + "v8_random_seed": 0, + "v8_use_snapshot": "false", + "want_separate_host_toolset": 0, + "nodedir": "/Users/Oracle/.node-gyp/0.12.7", + "copy_dev_lib": "true", + "standalone_static_library": 1, + "save_dev": "", + "browser": "", + "viewer": "man", + "rollback": "true", + "usage": "", + "globalignorefile": "/Users/Oracle/.nvm/versions/node/v0.12.7/etc/npmignore", + "init_author_url": "", + "shell": "/bin/bash", + "parseable": "", + "shrinkwrap": "true", + "init_license": "ISC", + "if_present": "", + "cache_max": "Infinity", + "init_author_email": "", + "sign_git_tag": "", + "cert": "", + "git_tag_version": "true", + "local_address": "", + "long": "", + "fetch_retries": "2", + "npat": "", + "registry": "https://registry.npmjs.org/", + "key": "", + "message": "%s", + "versions": "", + "globalconfig": "/Users/Oracle/.nvm/versions/node/v0.12.7/etc/npmrc", + "always_auth": "", + "spin": "true", + "cache_lock_retries": "10", + "cafile": "", + "heading": "npm", + "fetch_retry_mintimeout": "10000", + "proprietary_attribs": "true", + "access": "", + "json": "", + "description": "true", + "engine_strict": "", + "https_proxy": "", + "init_module": "/Users/Oracle/.npm-init.js", + "userconfig": "/Users/Oracle/.npmrc", + "user": "akhil", + "node_version": "0.12.7", + "editor": "vi", + "save": "", + "tag": "latest", + "global": "", + "optional": "true", + "bin_links": "true", + "force": "", + "searchopts": "", + "depth": "Infinity", + "rebuild_bundle": "true", + "searchsort": "name", + "unicode": "true", + "fetch_retry_maxtimeout": "60000", + "ca": "", + "save_prefix": "^", + "strict_ssl": "true", + "tag_version_prefix": "v", + "dev": "", + "fetch_retry_factor": "10", + "group": "20", + "save_exact": "", + "cache_lock_stale": "60000", + "version": "", + "cache_min": "10", + "cache": "/Users/Oracle/.npm", + "searchexclude": "", + "color": "true", + "save_optional": "", + "user_agent": "npm/2.11.3 node/v0.12.7 darwin x64", + "ignore_scripts": "", + "cache_lock_wait": "10000", + "production": "", + "save_bundle": "", + "init_version": "1.0.0", + "umask": "0022", + "git": "git", + "init_author_name": "", + "scope": "", + "onload_script": "", + "tmp": "/var/folders/62/d6lvgp5s4hldcbsxbwb67y1w0000gn/T", + "unsafe_perm": "true", + "link": "", + "prefix": "/Users/Oracle/.nvm/versions/node/v0.12.7" + } +} diff --git a/node_modules/ws/node_modules/bufferutil/build/gyp-mac-tool b/node_modules/ws/node_modules/bufferutil/build/gyp-mac-tool new file mode 100755 index 0000000..976c598 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/build/gyp-mac-tool @@ -0,0 +1,612 @@ +#!/usr/bin/env python +# Generated by gyp. Do not edit. +# Copyright (c) 2012 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Utility functions to perform Xcode-style build steps. + +These functions are executed via gyp-mac-tool when using the Makefile generator. +""" + +import fcntl +import fnmatch +import glob +import json +import os +import plistlib +import re +import shutil +import string +import subprocess +import sys +import tempfile + + +def main(args): + executor = MacTool() + exit_code = executor.Dispatch(args) + if exit_code is not None: + sys.exit(exit_code) + + +class MacTool(object): + """This class performs all the Mac tooling steps. The methods can either be + executed directly, or dispatched from an argument list.""" + + def Dispatch(self, args): + """Dispatches a string command to a method.""" + if len(args) < 1: + raise Exception("Not enough arguments") + + method = "Exec%s" % self._CommandifyName(args[0]) + return getattr(self, method)(*args[1:]) + + def _CommandifyName(self, name_string): + """Transforms a tool name like copy-info-plist to CopyInfoPlist""" + return name_string.title().replace('-', '') + + def ExecCopyBundleResource(self, source, dest, convert_to_binary): + """Copies a resource file to the bundle/Resources directory, performing any + necessary compilation on each resource.""" + extension = os.path.splitext(source)[1].lower() + if os.path.isdir(source): + # Copy tree. + # TODO(thakis): This copies file attributes like mtime, while the + # single-file branch below doesn't. This should probably be changed to + # be consistent with the single-file branch. + if os.path.exists(dest): + shutil.rmtree(dest) + shutil.copytree(source, dest) + elif extension == '.xib': + return self._CopyXIBFile(source, dest) + elif extension == '.storyboard': + return self._CopyXIBFile(source, dest) + elif extension == '.strings': + self._CopyStringsFile(source, dest, convert_to_binary) + else: + shutil.copy(source, dest) + + def _CopyXIBFile(self, source, dest): + """Compiles a XIB file with ibtool into a binary plist in the bundle.""" + + # ibtool sometimes crashes with relative paths. See crbug.com/314728. + base = os.path.dirname(os.path.realpath(__file__)) + if os.path.relpath(source): + source = os.path.join(base, source) + if os.path.relpath(dest): + dest = os.path.join(base, dest) + + args = ['xcrun', 'ibtool', '--errors', '--warnings', '--notices', + '--output-format', 'human-readable-text', '--compile', dest, source] + ibtool_section_re = re.compile(r'/\*.*\*/') + ibtool_re = re.compile(r'.*note:.*is clipping its content') + ibtoolout = subprocess.Popen(args, stdout=subprocess.PIPE) + current_section_header = None + for line in ibtoolout.stdout: + if ibtool_section_re.match(line): + current_section_header = line + elif not ibtool_re.match(line): + if current_section_header: + sys.stdout.write(current_section_header) + current_section_header = None + sys.stdout.write(line) + return ibtoolout.returncode + + def _ConvertToBinary(self, dest): + subprocess.check_call([ + 'xcrun', 'plutil', '-convert', 'binary1', '-o', dest, dest]) + + def _CopyStringsFile(self, source, dest, convert_to_binary): + """Copies a .strings file using iconv to reconvert the input into UTF-16.""" + input_code = self._DetectInputEncoding(source) or "UTF-8" + + # Xcode's CpyCopyStringsFile / builtin-copyStrings seems to call + # CFPropertyListCreateFromXMLData() behind the scenes; at least it prints + # CFPropertyListCreateFromXMLData(): Old-style plist parser: missing + # semicolon in dictionary. + # on invalid files. Do the same kind of validation. + import CoreFoundation + s = open(source, 'rb').read() + d = CoreFoundation.CFDataCreate(None, s, len(s)) + _, error = CoreFoundation.CFPropertyListCreateFromXMLData(None, d, 0, None) + if error: + return + + fp = open(dest, 'wb') + fp.write(s.decode(input_code).encode('UTF-16')) + fp.close() + + if convert_to_binary == 'True': + self._ConvertToBinary(dest) + + def _DetectInputEncoding(self, file_name): + """Reads the first few bytes from file_name and tries to guess the text + encoding. Returns None as a guess if it can't detect it.""" + fp = open(file_name, 'rb') + try: + header = fp.read(3) + except e: + fp.close() + return None + fp.close() + if header.startswith("\xFE\xFF"): + return "UTF-16" + elif header.startswith("\xFF\xFE"): + return "UTF-16" + elif header.startswith("\xEF\xBB\xBF"): + return "UTF-8" + else: + return None + + def ExecCopyInfoPlist(self, source, dest, convert_to_binary, *keys): + """Copies the |source| Info.plist to the destination directory |dest|.""" + # Read the source Info.plist into memory. + fd = open(source, 'r') + lines = fd.read() + fd.close() + + # Insert synthesized key/value pairs (e.g. BuildMachineOSBuild). + plist = plistlib.readPlistFromString(lines) + if keys: + plist = dict(plist.items() + json.loads(keys[0]).items()) + lines = plistlib.writePlistToString(plist) + + # Go through all the environment variables and replace them as variables in + # the file. + IDENT_RE = re.compile(r'[/\s]') + for key in os.environ: + if key.startswith('_'): + continue + evar = '${%s}' % key + evalue = os.environ[key] + lines = string.replace(lines, evar, evalue) + + # Xcode supports various suffices on environment variables, which are + # all undocumented. :rfc1034identifier is used in the standard project + # template these days, and :identifier was used earlier. They are used to + # convert non-url characters into things that look like valid urls -- + # except that the replacement character for :identifier, '_' isn't valid + # in a URL either -- oops, hence :rfc1034identifier was born. + evar = '${%s:identifier}' % key + evalue = IDENT_RE.sub('_', os.environ[key]) + lines = string.replace(lines, evar, evalue) + + evar = '${%s:rfc1034identifier}' % key + evalue = IDENT_RE.sub('-', os.environ[key]) + lines = string.replace(lines, evar, evalue) + + # Remove any keys with values that haven't been replaced. + lines = lines.split('\n') + for i in range(len(lines)): + if lines[i].strip().startswith("${"): + lines[i] = None + lines[i - 1] = None + lines = '\n'.join(filter(lambda x: x is not None, lines)) + + # Write out the file with variables replaced. + fd = open(dest, 'w') + fd.write(lines) + fd.close() + + # Now write out PkgInfo file now that the Info.plist file has been + # "compiled". + self._WritePkgInfo(dest) + + if convert_to_binary == 'True': + self._ConvertToBinary(dest) + + def _WritePkgInfo(self, info_plist): + """This writes the PkgInfo file from the data stored in Info.plist.""" + plist = plistlib.readPlist(info_plist) + if not plist: + return + + # Only create PkgInfo for executable types. + package_type = plist['CFBundlePackageType'] + if package_type != 'APPL': + return + + # The format of PkgInfo is eight characters, representing the bundle type + # and bundle signature, each four characters. If that is missing, four + # '?' characters are used instead. + signature_code = plist.get('CFBundleSignature', '????') + if len(signature_code) != 4: # Wrong length resets everything, too. + signature_code = '?' * 4 + + dest = os.path.join(os.path.dirname(info_plist), 'PkgInfo') + fp = open(dest, 'w') + fp.write('%s%s' % (package_type, signature_code)) + fp.close() + + def ExecFlock(self, lockfile, *cmd_list): + """Emulates the most basic behavior of Linux's flock(1).""" + # Rely on exception handling to report errors. + fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666) + fcntl.flock(fd, fcntl.LOCK_EX) + return subprocess.call(cmd_list) + + def ExecFilterLibtool(self, *cmd_list): + """Calls libtool and filters out '/path/to/libtool: file: foo.o has no + symbols'.""" + libtool_re = re.compile(r'^.*libtool: file: .* has no symbols$') + libtool_re5 = re.compile( + r'^.*libtool: warning for library: ' + + r'.* the table of contents is empty ' + + r'\(no object file members in the library define global symbols\)$') + env = os.environ.copy() + # Ref: + # http://www.opensource.apple.com/source/cctools/cctools-809/misc/libtool.c + # The problem with this flag is that it resets the file mtime on the file to + # epoch=0, e.g. 1970-1-1 or 1969-12-31 depending on timezone. + env['ZERO_AR_DATE'] = '1' + libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env) + _, err = libtoolout.communicate() + for line in err.splitlines(): + if not libtool_re.match(line) and not libtool_re5.match(line): + print >>sys.stderr, line + # Unconditionally touch the output .a file on the command line if present + # and the command succeeded. A bit hacky. + if not libtoolout.returncode: + for i in range(len(cmd_list) - 1): + if cmd_list[i] == "-o" and cmd_list[i+1].endswith('.a'): + os.utime(cmd_list[i+1], None) + break + return libtoolout.returncode + + def ExecPackageFramework(self, framework, version): + """Takes a path to Something.framework and the Current version of that and + sets up all the symlinks.""" + # Find the name of the binary based on the part before the ".framework". + binary = os.path.basename(framework).split('.')[0] + + CURRENT = 'Current' + RESOURCES = 'Resources' + VERSIONS = 'Versions' + + if not os.path.exists(os.path.join(framework, VERSIONS, version, binary)): + # Binary-less frameworks don't seem to contain symlinks (see e.g. + # chromium's out/Debug/org.chromium.Chromium.manifest/ bundle). + return + + # Move into the framework directory to set the symlinks correctly. + pwd = os.getcwd() + os.chdir(framework) + + # Set up the Current version. + self._Relink(version, os.path.join(VERSIONS, CURRENT)) + + # Set up the root symlinks. + self._Relink(os.path.join(VERSIONS, CURRENT, binary), binary) + self._Relink(os.path.join(VERSIONS, CURRENT, RESOURCES), RESOURCES) + + # Back to where we were before! + os.chdir(pwd) + + def _Relink(self, dest, link): + """Creates a symlink to |dest| named |link|. If |link| already exists, + it is overwritten.""" + if os.path.lexists(link): + os.remove(link) + os.symlink(dest, link) + + def ExecCompileXcassets(self, keys, *inputs): + """Compiles multiple .xcassets files into a single .car file. + + This invokes 'actool' to compile all the inputs .xcassets files. The + |keys| arguments is a json-encoded dictionary of extra arguments to + pass to 'actool' when the asset catalogs contains an application icon + or a launch image. + + Note that 'actool' does not create the Assets.car file if the asset + catalogs does not contains imageset. + """ + command_line = [ + 'xcrun', 'actool', '--output-format', 'human-readable-text', + '--compress-pngs', '--notices', '--warnings', '--errors', + ] + is_iphone_target = 'IPHONEOS_DEPLOYMENT_TARGET' in os.environ + if is_iphone_target: + platform = os.environ['CONFIGURATION'].split('-')[-1] + if platform not in ('iphoneos', 'iphonesimulator'): + platform = 'iphonesimulator' + command_line.extend([ + '--platform', platform, '--target-device', 'iphone', + '--target-device', 'ipad', '--minimum-deployment-target', + os.environ['IPHONEOS_DEPLOYMENT_TARGET'], '--compile', + os.path.abspath(os.environ['CONTENTS_FOLDER_PATH']), + ]) + else: + command_line.extend([ + '--platform', 'macosx', '--target-device', 'mac', + '--minimum-deployment-target', os.environ['MACOSX_DEPLOYMENT_TARGET'], + '--compile', + os.path.abspath(os.environ['UNLOCALIZED_RESOURCES_FOLDER_PATH']), + ]) + if keys: + keys = json.loads(keys) + for key, value in keys.iteritems(): + arg_name = '--' + key + if isinstance(value, bool): + if value: + command_line.append(arg_name) + elif isinstance(value, list): + for v in value: + command_line.append(arg_name) + command_line.append(str(v)) + else: + command_line.append(arg_name) + command_line.append(str(value)) + # Note: actool crashes if inputs path are relative, so use os.path.abspath + # to get absolute path name for inputs. + command_line.extend(map(os.path.abspath, inputs)) + subprocess.check_call(command_line) + + def ExecMergeInfoPlist(self, output, *inputs): + """Merge multiple .plist files into a single .plist file.""" + merged_plist = {} + for path in inputs: + plist = self._LoadPlistMaybeBinary(path) + self._MergePlist(merged_plist, plist) + plistlib.writePlist(merged_plist, output) + + def ExecCodeSignBundle(self, key, resource_rules, entitlements, provisioning): + """Code sign a bundle. + + This function tries to code sign an iOS bundle, following the same + algorithm as Xcode: + 1. copy ResourceRules.plist from the user or the SDK into the bundle, + 2. pick the provisioning profile that best match the bundle identifier, + and copy it into the bundle as embedded.mobileprovision, + 3. copy Entitlements.plist from user or SDK next to the bundle, + 4. code sign the bundle. + """ + resource_rules_path = self._InstallResourceRules(resource_rules) + substitutions, overrides = self._InstallProvisioningProfile( + provisioning, self._GetCFBundleIdentifier()) + entitlements_path = self._InstallEntitlements( + entitlements, substitutions, overrides) + subprocess.check_call([ + 'codesign', '--force', '--sign', key, '--resource-rules', + resource_rules_path, '--entitlements', entitlements_path, + os.path.join( + os.environ['TARGET_BUILD_DIR'], + os.environ['FULL_PRODUCT_NAME'])]) + + def _InstallResourceRules(self, resource_rules): + """Installs ResourceRules.plist from user or SDK into the bundle. + + Args: + resource_rules: string, optional, path to the ResourceRules.plist file + to use, default to "${SDKROOT}/ResourceRules.plist" + + Returns: + Path to the copy of ResourceRules.plist into the bundle. + """ + source_path = resource_rules + target_path = os.path.join( + os.environ['BUILT_PRODUCTS_DIR'], + os.environ['CONTENTS_FOLDER_PATH'], + 'ResourceRules.plist') + if not source_path: + source_path = os.path.join( + os.environ['SDKROOT'], 'ResourceRules.plist') + shutil.copy2(source_path, target_path) + return target_path + + def _InstallProvisioningProfile(self, profile, bundle_identifier): + """Installs embedded.mobileprovision into the bundle. + + Args: + profile: string, optional, short name of the .mobileprovision file + to use, if empty or the file is missing, the best file installed + will be used + bundle_identifier: string, value of CFBundleIdentifier from Info.plist + + Returns: + A tuple containing two dictionary: variables substitutions and values + to overrides when generating the entitlements file. + """ + source_path, provisioning_data, team_id = self._FindProvisioningProfile( + profile, bundle_identifier) + target_path = os.path.join( + os.environ['BUILT_PRODUCTS_DIR'], + os.environ['CONTENTS_FOLDER_PATH'], + 'embedded.mobileprovision') + shutil.copy2(source_path, target_path) + substitutions = self._GetSubstitutions(bundle_identifier, team_id + '.') + return substitutions, provisioning_data['Entitlements'] + + def _FindProvisioningProfile(self, profile, bundle_identifier): + """Finds the .mobileprovision file to use for signing the bundle. + + Checks all the installed provisioning profiles (or if the user specified + the PROVISIONING_PROFILE variable, only consult it) and select the most + specific that correspond to the bundle identifier. + + Args: + profile: string, optional, short name of the .mobileprovision file + to use, if empty or the file is missing, the best file installed + will be used + bundle_identifier: string, value of CFBundleIdentifier from Info.plist + + Returns: + A tuple of the path to the selected provisioning profile, the data of + the embedded plist in the provisioning profile and the team identifier + to use for code signing. + + Raises: + SystemExit: if no .mobileprovision can be used to sign the bundle. + """ + profiles_dir = os.path.join( + os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles') + if not os.path.isdir(profiles_dir): + print >>sys.stderr, ( + 'cannot find mobile provisioning for %s' % bundle_identifier) + sys.exit(1) + provisioning_profiles = None + if profile: + profile_path = os.path.join(profiles_dir, profile + '.mobileprovision') + if os.path.exists(profile_path): + provisioning_profiles = [profile_path] + if not provisioning_profiles: + provisioning_profiles = glob.glob( + os.path.join(profiles_dir, '*.mobileprovision')) + valid_provisioning_profiles = {} + for profile_path in provisioning_profiles: + profile_data = self._LoadProvisioningProfile(profile_path) + app_id_pattern = profile_data.get( + 'Entitlements', {}).get('application-identifier', '') + for team_identifier in profile_data.get('TeamIdentifier', []): + app_id = '%s.%s' % (team_identifier, bundle_identifier) + if fnmatch.fnmatch(app_id, app_id_pattern): + valid_provisioning_profiles[app_id_pattern] = ( + profile_path, profile_data, team_identifier) + if not valid_provisioning_profiles: + print >>sys.stderr, ( + 'cannot find mobile provisioning for %s' % bundle_identifier) + sys.exit(1) + # If the user has multiple provisioning profiles installed that can be + # used for ${bundle_identifier}, pick the most specific one (ie. the + # provisioning profile whose pattern is the longest). + selected_key = max(valid_provisioning_profiles, key=lambda v: len(v)) + return valid_provisioning_profiles[selected_key] + + def _LoadProvisioningProfile(self, profile_path): + """Extracts the plist embedded in a provisioning profile. + + Args: + profile_path: string, path to the .mobileprovision file + + Returns: + Content of the plist embedded in the provisioning profile as a dictionary. + """ + with tempfile.NamedTemporaryFile() as temp: + subprocess.check_call([ + 'security', 'cms', '-D', '-i', profile_path, '-o', temp.name]) + return self._LoadPlistMaybeBinary(temp.name) + + def _MergePlist(self, merged_plist, plist): + """Merge |plist| into |merged_plist|.""" + for key, value in plist.iteritems(): + if isinstance(value, dict): + merged_value = merged_plist.get(key, {}) + if isinstance(merged_value, dict): + self._MergePlist(merged_value, value) + merged_plist[key] = merged_value + else: + merged_plist[key] = value + else: + merged_plist[key] = value + + def _LoadPlistMaybeBinary(self, plist_path): + """Loads into a memory a plist possibly encoded in binary format. + + This is a wrapper around plistlib.readPlist that tries to convert the + plist to the XML format if it can't be parsed (assuming that it is in + the binary format). + + Args: + plist_path: string, path to a plist file, in XML or binary format + + Returns: + Content of the plist as a dictionary. + """ + try: + # First, try to read the file using plistlib that only supports XML, + # and if an exception is raised, convert a temporary copy to XML and + # load that copy. + return plistlib.readPlist(plist_path) + except: + pass + with tempfile.NamedTemporaryFile() as temp: + shutil.copy2(plist_path, temp.name) + subprocess.check_call(['plutil', '-convert', 'xml1', temp.name]) + return plistlib.readPlist(temp.name) + + def _GetSubstitutions(self, bundle_identifier, app_identifier_prefix): + """Constructs a dictionary of variable substitutions for Entitlements.plist. + + Args: + bundle_identifier: string, value of CFBundleIdentifier from Info.plist + app_identifier_prefix: string, value for AppIdentifierPrefix + + Returns: + Dictionary of substitutions to apply when generating Entitlements.plist. + """ + return { + 'CFBundleIdentifier': bundle_identifier, + 'AppIdentifierPrefix': app_identifier_prefix, + } + + def _GetCFBundleIdentifier(self): + """Extracts CFBundleIdentifier value from Info.plist in the bundle. + + Returns: + Value of CFBundleIdentifier in the Info.plist located in the bundle. + """ + info_plist_path = os.path.join( + os.environ['TARGET_BUILD_DIR'], + os.environ['INFOPLIST_PATH']) + info_plist_data = self._LoadPlistMaybeBinary(info_plist_path) + return info_plist_data['CFBundleIdentifier'] + + def _InstallEntitlements(self, entitlements, substitutions, overrides): + """Generates and install the ${BundleName}.xcent entitlements file. + + Expands variables "$(variable)" pattern in the source entitlements file, + add extra entitlements defined in the .mobileprovision file and the copy + the generated plist to "${BundlePath}.xcent". + + Args: + entitlements: string, optional, path to the Entitlements.plist template + to use, defaults to "${SDKROOT}/Entitlements.plist" + substitutions: dictionary, variable substitutions + overrides: dictionary, values to add to the entitlements + + Returns: + Path to the generated entitlements file. + """ + source_path = entitlements + target_path = os.path.join( + os.environ['BUILT_PRODUCTS_DIR'], + os.environ['PRODUCT_NAME'] + '.xcent') + if not source_path: + source_path = os.path.join( + os.environ['SDKROOT'], + 'Entitlements.plist') + shutil.copy2(source_path, target_path) + data = self._LoadPlistMaybeBinary(target_path) + data = self._ExpandVariables(data, substitutions) + if overrides: + for key in overrides: + if key not in data: + data[key] = overrides[key] + plistlib.writePlist(data, target_path) + return target_path + + def _ExpandVariables(self, data, substitutions): + """Expands variables "$(variable)" in data. + + Args: + data: object, can be either string, list or dictionary + substitutions: dictionary, variable substitutions to perform + + Returns: + Copy of data where each references to "$(variable)" has been replaced + by the corresponding value found in substitutions, or left intact if + the key was not found. + """ + if isinstance(data, str): + for key, value in substitutions.iteritems(): + data = data.replace('$(%s)' % key, value) + return data + if isinstance(data, list): + return [self._ExpandVariables(v, substitutions) for v in data] + if isinstance(data, dict): + return dict((k, self._ExpandVariables(data[k], + substitutions)) for k in data) + return data + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/node_modules/ws/node_modules/bufferutil/fallback.js b/node_modules/ws/node_modules/bufferutil/fallback.js new file mode 100644 index 0000000..52d6379 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/fallback.js @@ -0,0 +1,57 @@ +'use strict'; + +/*! + * bufferutil: WebSocket buffer utils + * Copyright(c) 2015 Einar Otto Stangvik + * MIT Licensed + */ + +module.exports.BufferUtil = { + merge: function(mergedBuffer, buffers) { + for (var i = 0, offset = 0, l = buffers.length; i < l; ++i) { + var buf = buffers[i]; + + buf.copy(mergedBuffer, offset); + offset += buf.length; + } + }, + + mask: function(source, mask, output, offset, length) { + var maskNum = mask.readUInt32LE(0, true) + , i = 0 + , num; + + for (; i < length - 3; i += 4) { + num = maskNum ^ source.readUInt32LE(i, true); + + if (num < 0) num = 4294967296 + num; + output.writeUInt32LE(num, offset + i, true); + } + + switch (length % 4) { + case 3: output[offset + i + 2] = source[i + 2] ^ mask[2]; + case 2: output[offset + i + 1] = source[i + 1] ^ mask[1]; + case 1: output[offset + i] = source[i] ^ mask[0]; + } + }, + + unmask: function(data, mask) { + var maskNum = mask.readUInt32LE(0, true) + , length = data.length + , i = 0 + , num; + + for (; i < length - 3; i += 4) { + num = maskNum ^ data.readUInt32LE(i, true); + + if (num < 0) num = 4294967296 + num; + data.writeUInt32LE(num, i, true); + } + + switch (length % 4) { + case 3: data[i + 2] = data[i + 2] ^ mask[2]; + case 2: data[i + 1] = data[i + 1] ^ mask[1]; + case 1: data[i] = data[i] ^ mask[0]; + } + } +}; diff --git a/node_modules/ws/node_modules/bufferutil/index.js b/node_modules/ws/node_modules/bufferutil/index.js new file mode 100644 index 0000000..00c607c --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/index.js @@ -0,0 +1,7 @@ +'use strict'; + +try { + module.exports = require('bindings')('bufferutil'); +} catch (e) { + module.exports = require('./fallback'); +} diff --git a/node_modules/ws/node_modules/bufferutil/node_modules/bindings/README.md b/node_modules/ws/node_modules/bufferutil/node_modules/bindings/README.md new file mode 100644 index 0000000..585cf51 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/node_modules/bindings/README.md @@ -0,0 +1,97 @@ +node-bindings +============= +### Helper module for loading your native module's .node file + +This is a helper module for authors of Node.js native addon modules. +It is basically the "swiss army knife" of `require()`ing your native module's +`.node` file. + +Throughout the course of Node's native addon history, addons have ended up being +compiled in a variety of different places, depending on which build tool and which +version of node was used. To make matters worse, now the _gyp_ build tool can +produce either a _Release_ or _Debug_ build, each being built into different +locations. + +This module checks _all_ the possible locations that a native addon would be built +at, and returns the first one that loads successfully. + + +Installation +------------ + +Install with `npm`: + +``` bash +$ npm install bindings +``` + +Or add it to the `"dependencies"` section of your _package.json_ file. + + +Example +------- + +`require()`ing the proper bindings file for the current node version, platform +and architecture is as simple as: + +``` js +var bindings = require('bindings')('binding.node') + +// Use your bindings defined in your C files +bindings.your_c_function() +``` + + +Nice Error Output +----------------- + +When the `.node` file could not be loaded, `node-bindings` throws an Error with +a nice error message telling you exactly what was tried. You can also check the +`err.tries` Array property. + +``` +Error: Could not load the bindings file. Tried: + → /Users/nrajlich/ref/build/binding.node + → /Users/nrajlich/ref/build/Debug/binding.node + → /Users/nrajlich/ref/build/Release/binding.node + → /Users/nrajlich/ref/out/Debug/binding.node + → /Users/nrajlich/ref/Debug/binding.node + → /Users/nrajlich/ref/out/Release/binding.node + → /Users/nrajlich/ref/Release/binding.node + → /Users/nrajlich/ref/build/default/binding.node + → /Users/nrajlich/ref/compiled/0.8.2/darwin/x64/binding.node + at bindings (/Users/nrajlich/ref/node_modules/bindings/bindings.js:84:13) + at Object. (/Users/nrajlich/ref/lib/ref.js:5:47) + at Module._compile (module.js:449:26) + at Object.Module._extensions..js (module.js:467:10) + at Module.load (module.js:356:32) + at Function.Module._load (module.js:312:12) + ... +``` + + +License +------- + +(The MIT License) + +Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/ws/node_modules/bufferutil/node_modules/bindings/bindings.js b/node_modules/ws/node_modules/bufferutil/node_modules/bindings/bindings.js new file mode 100644 index 0000000..93dcf85 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/node_modules/bindings/bindings.js @@ -0,0 +1,166 @@ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , path = require('path') + , join = path.join + , dirname = path.dirname + , exists = fs.existsSync || path.existsSync + , defaults = { + arrow: process.env.NODE_BINDINGS_ARROW || ' → ' + , compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled' + , platform: process.platform + , arch: process.arch + , version: process.versions.node + , bindings: 'bindings.node' + , try: [ + // node-gyp's linked version in the "build" dir + [ 'module_root', 'build', 'bindings' ] + // node-waf and gyp_addon (a.k.a node-gyp) + , [ 'module_root', 'build', 'Debug', 'bindings' ] + , [ 'module_root', 'build', 'Release', 'bindings' ] + // Debug files, for development (legacy behavior, remove for node v0.9) + , [ 'module_root', 'out', 'Debug', 'bindings' ] + , [ 'module_root', 'Debug', 'bindings' ] + // Release files, but manually compiled (legacy behavior, remove for node v0.9) + , [ 'module_root', 'out', 'Release', 'bindings' ] + , [ 'module_root', 'Release', 'bindings' ] + // Legacy from node-waf, node <= 0.4.x + , [ 'module_root', 'build', 'default', 'bindings' ] + // Production "Release" buildtype binary (meh...) + , [ 'module_root', 'compiled', 'version', 'platform', 'arch', 'bindings' ] + ] + } + +/** + * The main `bindings()` function loads the compiled bindings for a given module. + * It uses V8's Error API to determine the parent filename that this function is + * being invoked from, which is then used to find the root directory. + */ + +function bindings (opts) { + + // Argument surgery + if (typeof opts == 'string') { + opts = { bindings: opts } + } else if (!opts) { + opts = {} + } + opts.__proto__ = defaults + + // Get the module root + if (!opts.module_root) { + opts.module_root = exports.getRoot(exports.getFileName()) + } + + // Ensure the given bindings name ends with .node + if (path.extname(opts.bindings) != '.node') { + opts.bindings += '.node' + } + + var tries = [] + , i = 0 + , l = opts.try.length + , n + , b + , err + + for (; i=1.2.0 <1.3.0", + "_npmVersion": "1.4.14", + "_npmUser": { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + }, + "maintainers": [ + { + "name": "TooTallNate", + "email": "nathan@tootallnate.net" + }, + { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + } + ], + "dist": { + "shasum": "14ad6113812d2d37d72e67b4cacb4bb726505f11", + "tarball": "http://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/ws/node_modules/bufferutil/node_modules/nan/.dntrc b/node_modules/ws/node_modules/bufferutil/node_modules/nan/.dntrc new file mode 100644 index 0000000..47971da --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/node_modules/nan/.dntrc @@ -0,0 +1,30 @@ +## DNT config file +## see https://github.com/rvagg/dnt + +NODE_VERSIONS="\ + master \ + v0.11.13 \ + v0.10.30 \ + v0.10.29 \ + v0.10.28 \ + v0.10.26 \ + v0.10.25 \ + v0.10.24 \ + v0.10.23 \ + v0.10.22 \ + v0.10.21 \ + v0.10.20 \ + v0.10.19 \ + v0.8.28 \ + v0.8.27 \ + v0.8.26 \ + v0.8.24 \ +" +OUTPUT_PREFIX="nan-" +TEST_CMD=" \ + cd /dnt/ && \ + npm install && \ + node_modules/.bin/node-gyp --nodedir /usr/src/node/ rebuild --directory test && \ + node_modules/.bin/tap --gc test/js/*-test.js \ +" + diff --git a/node_modules/ws/node_modules/bufferutil/node_modules/nan/CHANGELOG.md b/node_modules/ws/node_modules/bufferutil/node_modules/nan/CHANGELOG.md new file mode 100644 index 0000000..8d3c618 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/node_modules/nan/CHANGELOG.md @@ -0,0 +1,384 @@ +# NAN ChangeLog + +**Version 2.1.0: current Node 4.1.2, Node 12: 0.12.7, Node 10: 0.10.40, iojs: 3.3.1** + +### 2.1.0 Oct 8 2015 + + - Deprecation: Deprecate NanErrnoException in favor of ErrnoException 0af1ca4cf8b3f0f65ed31bc63a663ab3319da55c + - Feature: added helper class for accessing contents of typedarrays 17b51294c801e534479d5463697a73462d0ca555 + - Feature: [Maybe types] Add MakeMaybe(...) 48d7b53d9702b0c7a060e69ea10fea8fb48d814d + - Feature: new: allow utf16 string with length 66ac6e65c8ab9394ef588adfc59131b3b9d8347b + - Feature: Introduce SetCallHandler and SetCallAsFunctionHandler 7764a9a115d60ba10dc24d86feb0fbc9b4f75537 + - Bugfix: Enable creating Locals from Globals under Node 0.10. 9bf9b8b190821af889790fdc18ace57257e4f9ff + - Bugfix: Fix issue #462 where PropertyCallbackInfo data is not stored safely. 55f50adedd543098526c7b9f4fffd607d3f9861f + +### 2.0.9 Sep 8 2015 + + - Bugfix: EscapableHandleScope in Nan::NewBuffer for Node 0.8 and 0.10 b1654d7 + +### 2.0.8 Aug 28 2015 + + - Work around duplicate linking bug in clang 11902da + +### 2.0.7 Aug 26 2015 + + - Build: Repackage + +### 2.0.6 Aug 26 2015 + + - Bugfix: Properly handle null callback in FunctionTemplate factory 6e99cb1 + - Bugfix: Remove unused static std::map instances 525bddc + - Bugfix: Make better use of maybe versions of APIs bfba85b + - Bugfix: Fix shadowing issues with handle in ObjectWrap 0a9072d + +### 2.0.5 Aug 10 2015 + + - Bugfix: Reimplement weak callback in ObjectWrap 98d38c1 + - Bugfix: Make sure callback classes are not assignable, copyable or movable 81f9b1d + +### 2.0.4 Aug 6 2015 + + - Build: Repackage + +### 2.0.3 Aug 6 2015 + + - Bugfix: Don't use clang++ / g++ syntax extension. 231450e + +### 2.0.2 Aug 6 2015 + + - Build: Repackage + +### 2.0.1 Aug 6 2015 + + - Bugfix: Add workaround for missing REPLACE_INVALID_UTF8 60d6687 + - Bugfix: Reimplement ObjectWrap from scratch to prevent memory leaks 6484601 + - Bugfix: Fix Persistent leak in FunctionCallbackInfo and PropertyCallbackInfo 641ef5f + - Bugfix: Add missing overload for Nan::NewInstance that takes argc/argv 29450ed + +### 2.0.0 Jul 31 2015 + + - Change: Renamed identifiers with leading underscores b5932b4 + - Change: Replaced NanObjectWrapHandle with class NanObjectWrap 464f1e1 + - Change: Replace NanScope and NanEscpableScope macros with classes 47751c4 + - Change: Rename NanNewBufferHandle to NanNewBuffer 6745f99 + - Change: Rename NanBufferUse to NanNewBuffer 3e8b0a5 + - Change: Rename NanNewBuffer to NanCopyBuffer d6af78d + - Change: Remove Nan prefix from all names 72d1f67 + - Change: Update Buffer API for new upstream changes d5d3291 + - Change: Rename Scope and EscapableScope to HandleScope and EscapableHandleScope 21a7a6a + - Change: Get rid of Handles e6c0daf + - Feature: Support io.js 3 with V8 4.4 + - Feature: Introduce NanPersistent 7fed696 + - Feature: Introduce NanGlobal 4408da1 + - Feature: Added NanTryCatch 10f1ca4 + - Feature: Update for V8 v4.3 4b6404a + - Feature: Introduce NanNewOneByteString c543d32 + - Feature: Introduce namespace Nan 67ed1b1 + - Removal: Remove NanLocker and NanUnlocker dd6e401 + - Removal: Remove string converters, except NanUtf8String, which now follows the node implementation b5d00a9 + - Removal: Remove NanReturn* macros d90a25c + - Removal: Remove HasInstance e8f84fe + + +### 1.9.0 Jul 31 2015 + + - Feature: Added `NanFatalException` 81d4a2c + - Feature: Added more error types 4265f06 + - Feature: Added dereference and function call operators to NanCallback c4b2ed0 + - Feature: Added indexed GetFromPersistent and SaveToPersistent edd510c + - Feature: Added more overloads of SaveToPersistent and GetFromPersistent 8b1cef6 + - Feature: Added NanErrnoException dd87d9e + - Correctness: Prevent assign, copy, and move for classes that do not support it 1f55c59, 4b808cb, c96d9b2, fba4a29, 3357130 + - Deprecation: Deprecate `NanGetPointerSafe` and `NanSetPointerSafe` 81d4a2c + - Deprecation: Deprecate `NanBooleanOptionValue` and `NanUInt32OptionValue` 0ad254b + +### 1.8.4 Apr 26 2015 + + - Build: Repackage + +### 1.8.3 Apr 26 2015 + + - Bugfix: Include missing header 1af8648 + +### 1.8.2 Apr 23 2015 + + - Build: Repackage + +### 1.8.1 Apr 23 2015 + + - Bugfix: NanObjectWrapHandle should take a pointer 155f1d3 + +### 1.8.0 Apr 23 2015 + + - Feature: Allow primitives with NanReturnValue 2e4475e + - Feature: Added comparison operators to NanCallback 55b075e + - Feature: Backport thread local storage 15bb7fa + - Removal: Remove support for signatures with arguments 8a2069d + - Correcteness: Replaced NanObjectWrapHandle macro with function 0bc6d59 + +### 1.7.0 Feb 28 2015 + + - Feature: Made NanCallback::Call accept optional target 8d54da7 + - Feature: Support atom-shell 0.21 0b7f1bb + +### 1.6.2 Feb 6 2015 + + - Bugfix: NanEncode: fix argument type for node::Encode on io.js 2be8639 + +### 1.6.1 Jan 23 2015 + + - Build: version bump + +### 1.5.3 Jan 23 2015 + + - Build: repackage + +### 1.6.0 Jan 23 2015 + + - Deprecated `NanNewContextHandle` in favor of `NanNew` 49259af + - Support utility functions moved in newer v8 versions (Node 0.11.15, io.js 1.0) a0aa179 + - Added `NanEncode`, `NanDecodeBytes` and `NanDecodeWrite` 75e6fb9 + +### 1.5.2 Jan 23 2015 + + - Bugfix: Fix non-inline definition build error with clang++ 21d96a1, 60fadd4 + - Bugfix: Readded missing String constructors 18d828f + - Bugfix: Add overload handling NanNew(..) 5ef813b + - Bugfix: Fix uv_work_cb versioning 997e4ae + - Bugfix: Add function factory and test 4eca89c + - Bugfix: Add object template factory and test cdcb951 + - Correctness: Lifted an io.js related typedef c9490be + - Correctness: Make explicit downcasts of String lengths 00074e6 + - Windows: Limit the scope of disabled warning C4530 83d7deb + +### 1.5.1 Jan 15 2015 + + - Build: version bump + +### 1.4.3 Jan 15 2015 + + - Build: version bump + +### 1.4.2 Jan 15 2015 + + - Feature: Support io.js 0dbc5e8 + +### 1.5.0 Jan 14 2015 + + - Feature: Support io.js b003843 + - Correctness: Improved NanNew internals 9cd4f6a + - Feature: Implement progress to NanAsyncWorker 8d6a160 + +### 1.4.1 Nov 8 2014 + + - Bugfix: Handle DEBUG definition correctly + - Bugfix: Accept int as Boolean + +### 1.4.0 Nov 1 2014 + + - Feature: Added NAN_GC_CALLBACK 6a5c245 + - Performance: Removed unnecessary local handle creation 18a7243, 41fe2f8 + - Correctness: Added constness to references in NanHasInstance 02c61cd + - Warnings: Fixed spurious warnings from -Wundef and -Wshadow, 541b122, 99d8cb6 + - Windoze: Shut Visual Studio up when compiling 8d558c1 + - License: Switch to plain MIT from custom hacked MIT license 11de983 + - Build: Added test target to Makefile e232e46 + - Performance: Removed superfluous scope in NanAsyncWorker f4b7821 + - Sugar/Feature: Added NanReturnThis() and NanReturnHolder() shorthands 237a5ff, d697208 + - Feature: Added suitable overload of NanNew for v8::Integer::NewFromUnsigned b27b450 + +### 1.3.0 Aug 2 2014 + + - Added NanNew(std::string) + - Added NanNew(std::string&) + - Added NanAsciiString helper class + - Added NanUtf8String helper class + - Added NanUcs2String helper class + - Deprecated NanRawString() + - Deprecated NanCString() + - Added NanGetIsolateData(v8::Isolate *isolate) + - Added NanMakeCallback(v8::Handle target, v8::Handle func, int argc, v8::Handle* argv) + - Added NanMakeCallback(v8::Handle target, v8::Handle symbol, int argc, v8::Handle* argv) + - Added NanMakeCallback(v8::Handle target, const char* method, int argc, v8::Handle* argv) + - Added NanSetTemplate(v8::Handle templ, v8::Handle name , v8::Handle value, v8::PropertyAttribute attributes) + - Added NanSetPrototypeTemplate(v8::Local templ, v8::Handle name, v8::Handle value, v8::PropertyAttribute attributes) + - Added NanSetInstanceTemplate(v8::Local templ, const char *name, v8::Handle value) + - Added NanSetInstanceTemplate(v8::Local templ, v8::Handle name, v8::Handle value, v8::PropertyAttribute attributes) + +### 1.2.0 Jun 5 2014 + + - Add NanSetPrototypeTemplate + - Changed NAN_WEAK_CALLBACK internals, switched _NanWeakCallbackData to class, + introduced _NanWeakCallbackDispatcher + - Removed -Wno-unused-local-typedefs from test builds + - Made test builds Windows compatible ('Sleep()') + +### 1.1.2 May 28 2014 + + - Release to fix more stuff-ups in 1.1.1 + +### 1.1.1 May 28 2014 + + - Release to fix version mismatch in nan.h and lack of changelog entry for 1.1.0 + +### 1.1.0 May 25 2014 + + - Remove nan_isolate, use v8::Isolate::GetCurrent() internally instead + - Additional explicit overloads for NanNew(): (char*,int), (uint8_t*[,int]), + (uint16_t*[,int), double, int, unsigned int, bool, v8::String::ExternalStringResource*, + v8::String::ExternalAsciiStringResource* + - Deprecate NanSymbol() + - Added SetErrorMessage() and ErrorMessage() to NanAsyncWorker + +### 1.0.0 May 4 2014 + + - Heavy API changes for V8 3.25 / Node 0.11.13 + - Use cpplint.py + - Removed NanInitPersistent + - Removed NanPersistentToLocal + - Removed NanFromV8String + - Removed NanMakeWeak + - Removed NanNewLocal + - Removed NAN_WEAK_CALLBACK_OBJECT + - Removed NAN_WEAK_CALLBACK_DATA + - Introduce NanNew, replaces NanNewLocal, NanPersistentToLocal, adds many overloaded typed versions + - Introduce NanUndefined, NanNull, NanTrue and NanFalse + - Introduce NanEscapableScope and NanEscapeScope + - Introduce NanMakeWeakPersistent (requires a special callback to work on both old and new node) + - Introduce NanMakeCallback for node::MakeCallback + - Introduce NanSetTemplate + - Introduce NanGetCurrentContext + - Introduce NanCompileScript and NanRunScript + - Introduce NanAdjustExternalMemory + - Introduce NanAddGCEpilogueCallback, NanAddGCPrologueCallback, NanRemoveGCEpilogueCallback, NanRemoveGCPrologueCallback + - Introduce NanGetHeapStatistics + - Rename NanAsyncWorker#SavePersistent() to SaveToPersistent() + +### 0.8.0 Jan 9 2014 + + - NanDispose -> NanDisposePersistent, deprecate NanDispose + - Extract _NAN_*_RETURN_TYPE, pull up NAN_*() + +### 0.7.1 Jan 9 2014 + + - Fixes to work against debug builds of Node + - Safer NanPersistentToLocal (avoid reinterpret_cast) + - Speed up common NanRawString case by only extracting flattened string when necessary + +### 0.7.0 Dec 17 2013 + + - New no-arg form of NanCallback() constructor. + - NanCallback#Call takes Handle rather than Local + - Removed deprecated NanCallback#Run method, use NanCallback#Call instead + - Split off _NAN_*_ARGS_TYPE from _NAN_*_ARGS + - Restore (unofficial) Node 0.6 compatibility at NanCallback#Call() + - Introduce NanRawString() for char* (or appropriate void*) from v8::String + (replacement for NanFromV8String) + - Introduce NanCString() for null-terminated char* from v8::String + +### 0.6.0 Nov 21 2013 + + - Introduce NanNewLocal(v8::Handle value) for use in place of + v8::Local::New(...) since v8 started requiring isolate in Node 0.11.9 + +### 0.5.2 Nov 16 2013 + + - Convert SavePersistent and GetFromPersistent in NanAsyncWorker from protected and public + +### 0.5.1 Nov 12 2013 + + - Use node::MakeCallback() instead of direct v8::Function::Call() + +### 0.5.0 Nov 11 2013 + + - Added @TooTallNate as collaborator + - New, much simpler, "include_dirs" for binding.gyp + - Added full range of NAN_INDEX_* macros to match NAN_PROPERTY_* macros + +### 0.4.4 Nov 2 2013 + + - Isolate argument from v8::Persistent::MakeWeak removed for 0.11.8+ + +### 0.4.3 Nov 2 2013 + + - Include node_object_wrap.h, removed from node.h for Node 0.11.8. + +### 0.4.2 Nov 2 2013 + + - Handle deprecation of v8::Persistent::Dispose(v8::Isolate* isolate)) for + Node 0.11.8 release. + +### 0.4.1 Sep 16 2013 + + - Added explicit `#include ` as it was removed from node.h for v0.11.8 + +### 0.4.0 Sep 2 2013 + + - Added NAN_INLINE and NAN_DEPRECATED and made use of them + - Added NanError, NanTypeError and NanRangeError + - Cleaned up code + +### 0.3.2 Aug 30 2013 + + - Fix missing scope declaration in GetFromPersistent() and SaveToPersistent + in NanAsyncWorker + +### 0.3.1 Aug 20 2013 + + - fix "not all control paths return a value" compile warning on some platforms + +### 0.3.0 Aug 19 2013 + + - Made NAN work with NPM + - Lots of fixes to NanFromV8String, pulling in features from new Node core + - Changed node::encoding to Nan::Encoding in NanFromV8String to unify the API + - Added optional error number argument for NanThrowError() + - Added NanInitPersistent() + - Added NanReturnNull() and NanReturnEmptyString() + - Added NanLocker and NanUnlocker + - Added missing scopes + - Made sure to clear disposed Persistent handles + - Changed NanAsyncWorker to allocate error messages on the heap + - Changed NanThrowError(Local) to NanThrowError(Handle) + - Fixed leak in NanAsyncWorker when errmsg is used + +### 0.2.2 Aug 5 2013 + + - Fixed usage of undefined variable with node::BASE64 in NanFromV8String() + +### 0.2.1 Aug 5 2013 + + - Fixed 0.8 breakage, node::BUFFER encoding type not available in 0.8 for + NanFromV8String() + +### 0.2.0 Aug 5 2013 + + - Added NAN_PROPERTY_GETTER, NAN_PROPERTY_SETTER, NAN_PROPERTY_ENUMERATOR, + NAN_PROPERTY_DELETER, NAN_PROPERTY_QUERY + - Extracted _NAN_METHOD_ARGS, _NAN_GETTER_ARGS, _NAN_SETTER_ARGS, + _NAN_PROPERTY_GETTER_ARGS, _NAN_PROPERTY_SETTER_ARGS, + _NAN_PROPERTY_ENUMERATOR_ARGS, _NAN_PROPERTY_DELETER_ARGS, + _NAN_PROPERTY_QUERY_ARGS + - Added NanGetInternalFieldPointer, NanSetInternalFieldPointer + - Added NAN_WEAK_CALLBACK, NAN_WEAK_CALLBACK_OBJECT, + NAN_WEAK_CALLBACK_DATA, NanMakeWeak + - Renamed THROW_ERROR to _NAN_THROW_ERROR + - Added NanNewBufferHandle(char*, size_t, node::smalloc::FreeCallback, void*) + - Added NanBufferUse(char*, uint32_t) + - Added NanNewContextHandle(v8::ExtensionConfiguration*, + v8::Handle, v8::Handle) + - Fixed broken NanCallback#GetFunction() + - Added optional encoding and size arguments to NanFromV8String() + - Added NanGetPointerSafe() and NanSetPointerSafe() + - Added initial test suite (to be expanded) + - Allow NanUInt32OptionValue to convert any Number object + +### 0.1.0 Jul 21 2013 + + - Added `NAN_GETTER`, `NAN_SETTER` + - Added `NanThrowError` with single Local argument + - Added `NanNewBufferHandle` with single uint32_t argument + - Added `NanHasInstance(Persistent&, Handle)` + - Added `Local NanCallback#GetFunction()` + - Added `NanCallback#Call(int, Local[])` + - Deprecated `NanCallback#Run(int, Local[])` in favour of Call diff --git a/node_modules/ws/node_modules/bufferutil/node_modules/nan/LICENSE.md b/node_modules/ws/node_modules/bufferutil/node_modules/nan/LICENSE.md new file mode 100644 index 0000000..77666cd --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/node_modules/nan/LICENSE.md @@ -0,0 +1,13 @@ +The MIT License (MIT) +===================== + +Copyright (c) 2015 NAN contributors +----------------------------------- + +*NAN contributors listed at * + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/ws/node_modules/bufferutil/node_modules/nan/README.md b/node_modules/ws/node_modules/bufferutil/node_modules/nan/README.md new file mode 100644 index 0000000..7167ae3 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/node_modules/nan/README.md @@ -0,0 +1,371 @@ +Native Abstractions for Node.js +=============================== + +**A header file filled with macro and utility goodness for making add-on development for Node.js easier across versions 0.8, 0.10, 0.12 and 4.** + +***Current version: 2.1.0*** + +*(See [CHANGELOG.md](https://github.com/nodejs/nan/blob/master/CHANGELOG.md) for complete ChangeLog)* + +[![NPM](https://nodei.co/npm/nan.png?downloads=true&downloadRank=true)](https://nodei.co/npm/nan/) [![NPM](https://nodei.co/npm-dl/nan.png?months=6&height=3)](https://nodei.co/npm/nan/) + +[![Build Status](https://api.travis-ci.org/nodejs/nan.svg?branch=master)](http://travis-ci.org/nodejs/nan) +[![Build status](https://ci.appveyor.com/api/projects/status/kh73pbm9dsju7fgh)](https://ci.appveyor.com/project/RodVagg/nan) + +Thanks to the crazy changes in V8 (and some in Node core), keeping native addons compiling happily across versions, particularly 0.10 to 0.12 to 4.0, is a minor nightmare. The goal of this project is to store all logic necessary to develop native Node.js addons without having to inspect `NODE_MODULE_VERSION` and get yourself into a macro-tangle. + +This project also contains some helper utilities that make addon development a bit more pleasant. + + * **[News & Updates](#news)** + * **[Usage](#usage)** + * **[Example](#example)** + * **[API](#api)** + * **[Tests](#tests)** + * **[Governance & Contributing](#governance)** + + +## News & Updates + + +## Usage + +Simply add **NAN** as a dependency in the *package.json* of your Node addon: + +``` bash +$ npm install --save nan +``` + +Pull in the path to **NAN** in your *binding.gyp* so that you can use `#include ` in your *.cpp* files: + +``` python +"include_dirs" : [ + "` when compiling your addon. + + +## Example + +Just getting started with Nan? Refer to a [quick-start **Nan** Boilerplate](https://github.com/fcanas/node-native-boilerplate) for a ready-to-go project that utilizes basic Nan functionality. + +For a simpler example, see the **[async pi estimation example](https://github.com/nodejs/nan/tree/master/examples/async_pi_estimate)** in the examples directory for full code and an explanation of what this Monte Carlo Pi estimation example does. Below are just some parts of the full example that illustrate the use of **NAN**. + +For another example, see **[nan-example-eol](https://github.com/CodeCharmLtd/nan-example-eol)**. It shows newline detection implemented as a native addon. + + +## API + +Additional to the NAN documentation below, please consult: + +* [The V8 Getting Started Guide](https://developers.google.com/v8/get_started) +* [The V8 Embedders Guide](https://developers.google.com/v8/embed) +* [V8 API Documentation](http://v8docs.nodesource.com/) + + + +### JavaScript-accessible methods + +A _template_ is a blueprint for JavaScript functions and objects in a context. You can use a template to wrap C++ functions and data structures within JavaScript objects so that they can be manipulated from JavaScript. See the V8 Embedders Guide section on [Templates](https://developers.google.com/v8/embed#templates) for further information. + +In order to expose functionality to JavaScript via a template, you must provide it to V8 in a form that it understands. Across the versions of V8 supported by NAN, JavaScript-accessible method signatures vary widely, NAN fully abstracts method declaration and provides you with an interface that is similar to the most recent V8 API but is backward-compatible with older versions that still use the now-deceased `v8::Argument` type. + +* **Method argument types** + - Nan::FunctionCallbackInfo + - Nan::PropertyCallbackInfo + - Nan::ReturnValue +* **Method declarations** + - Method declaration + - Getter declaration + - Setter declaration + - Property getter declaration + - Property setter declaration + - Property enumerator declaration + - Property deleter declaration + - Property query declaration + - Index getter declaration + - Index setter declaration + - Index enumerator declaration + - Index deleter declaration + - Index query declaration +* Method and template helpers + - Nan::SetMethod() + - Nan::SetNamedPropertyHandler() + - Nan::SetIndexedPropertyHandler() + - Nan::SetPrototypeMethod() + - Nan::SetTemplate() + - Nan::SetPrototypeTemplate() + - Nan::SetInstanceTemplate() + - Nan::SetCallHandler() + - Nan::SetCallAsFunctionHandler() + +### Scopes + +A _local handle_ is a pointer to an object. All V8 objects are accessed using handles, they are necessary because of the way the V8 garbage collector works. + +A handle scope can be thought of as a container for any number of handles. When you've finished with your handles, instead of deleting each one individually you can simply delete their scope. + +The creation of `HandleScope` objects is different across the supported versions of V8. Therefore, NAN provides its own implementations that can be used safely across these. + + - Nan::HandleScope + - Nan::EscapableHandleScope + +Also see the V8 Embedders Guide section on [Handles and Garbage Collection](https://developers.google.com/v8/embed#handles). + +### Persistent references + +An object reference that is independent of any `HandleScope` is a _persistent_ reference. Where a `Local` handle only lives as long as the `HandleScope` in which it was allocated, a `Persistent` handle remains valid until it is explicitly disposed. + +Due to the evolution of the V8 API, it is necessary for NAN to provide a wrapper implementation of the `Persistent` classes to supply compatibility across the V8 versions supported. + + - Nan::PersistentBase & v8::PersistentBase + - Nan::NonCopyablePersistentTraits & v8::NonCopyablePersistentTraits + - Nan::CopyablePersistentTraits & v8::CopyablePersistentTraits + - Nan::Persistent + - Nan::Global + - Nan::WeakCallbackInfo + - Nan::WeakCallbackType + +Also see the V8 Embedders Guide section on [Handles and Garbage Collection](https://developers.google.com/v8/embed#handles). + +### New + +NAN provides a `Nan::New()` helper for the creation of new JavaScript objects in a way that's compatible across the supported versions of V8. + + - Nan::New() + - Nan::Undefined() + - Nan::Null() + - Nan::True() + - Nan::False() + - Nan::EmptyString() + + +### Converters + +NAN contains functions that convert `v8::Value`s to other `v8::Value` types and native types. Since type conversion is not guaranteed to succeed, they return `Nan::Maybe` types. These converters can be used in place of `value->ToX()` and `value->XValue()` (where `X` is one of the types, e.g. `Boolean`) in a way that provides a consistent interface across V8 versions. Newer versions of V8 use the new `v8::Maybe` and `v8::MaybeLocal` types for these conversions, older versions don't have this functionality so it is provided by NAN. + + - Nan::To() + +### Maybe Types + +The `Nan::MaybeLocal` and `Nan::Maybe` types are monads that encapsulate `v8::Local` handles that _may be empty_. + +* **Maybe Types** + - Nan::MaybeLocal + - Nan::Maybe + - Nan::Nothing + - Nan::Just +* **Maybe Helpers** + - Nan::ToDetailString() + - Nan::ToArrayIndex() + - Nan::Equals() + - Nan::NewInstance() + - Nan::GetFunction() + - Nan::Set() + - Nan::ForceSet() + - Nan::Get() + - Nan::GetPropertyAttributes() + - Nan::Has() + - Nan::Delete() + - Nan::GetPropertyNames() + - Nan::GetOwnPropertyNames() + - Nan::SetPrototype() + - Nan::ObjectProtoToString() + - Nan::HasOwnProperty() + - Nan::HasRealNamedProperty() + - Nan::HasRealIndexedProperty() + - Nan::HasRealNamedCallbackProperty() + - Nan::GetRealNamedPropertyInPrototypeChain() + - Nan::GetRealNamedProperty() + - Nan::CallAsFunction() + - Nan::CallAsConstructor() + - Nan::GetSourceLine() + - Nan::GetLineNumber() + - Nan::GetStartColumn() + - Nan::GetEndColumn() + - Nan::CloneElementAt() + - Nan::MakeMaybe() + +### Script + +NAN provides a `v8::Script` helpers as the API has changed over the supported versions of V8. + + - Nan::CompileScript() + - Nan::RunScript() + + +### Errors + +NAN includes helpers for creating, throwing and catching Errors as much of this functionality varies across the supported versions of V8 and must be abstracted. + +Note that an Error object is simply a specialized form of `v8::Value`. + +Also consult the V8 Embedders Guide section on [Exceptions](https://developers.google.com/v8/embed#exceptions) for more information. + + - Nan::Error() + - Nan::RangeError() + - Nan::ReferenceError() + - Nan::SyntaxError() + - Nan::TypeError() + - Nan::ThrowError() + - Nan::ThrowRangeError() + - Nan::ThrowReferenceError() + - Nan::ThrowSyntaxError() + - Nan::ThrowTypeError() + - Nan::FatalException() + - Nan::ErrnoException() + - Nan::TryCatch + + +### Buffers + +NAN's `node::Buffer` helpers exist as the API has changed across supported Node versions. Use these methods to ensure compatibility. + + - Nan::NewBuffer() + - Nan::CopyBuffer() + - Nan::FreeCallback() + +### Nan::Callback + +`Nan::Callback` makes it easier to use `v8::Function` handles as callbacks. A class that wraps a `v8::Function` handle, protecting it from garbage collection and making it particularly useful for storage and use across asynchronous execution. + + - Nan::Callback + +### Asynchronous work helpers + +`Nan::AsyncWorker` and `Nan::AsyncProgressWorker` are helper classes that make working with asynchronous code easier. + + - Nan::AsyncWorker + - Nan::AsyncProgressWorker + - Nan::AsyncQueueWorker + +### Strings & Bytes + +Miscellaneous string & byte encoding and decoding functionality provided for compatibility across supported versions of V8 and Node. Implemented by NAN to ensure that all encoding types are supported, even for older versions of Node where they are missing. + + - Nan::Encoding + - Nan::Encode() + - Nan::DecodeBytes() + - Nan::DecodeWrite() + + +### V8 internals + +The hooks to access V8 internals—including GC and statistics—are different across the supported versions of V8, therefore NAN provides its own hooks that call the appropriate V8 methods. + + - NAN_GC_CALLBACK() + - Nan::AddGCEpilogueCallback() + - Nan::RemoveGCEpilogueCallback() + - Nan::AddGCPrologueCallback() + - Nan::RemoveGCPrologueCallback() + - Nan::GetHeapStatistics() + - Nan::SetCounterFunction() + - Nan::SetCreateHistogramFunction() + - Nan::SetAddHistogramSampleFunction() + - Nan::IdleNotification() + - Nan::LowMemoryNotification() + - Nan::ContextDisposedNotification() + - Nan::GetInternalFieldPointer() + - Nan::SetInternalFieldPointer() + - Nan::AdjustExternalMemory() + + +### Miscellaneous V8 Helpers + + - Nan::Utf8String + - Nan::GetCurrentContext() + - Nan::SetIsolateData() + - Nan::GetIsolateData() + - Nan::TypedArrayContents + + +### Miscellaneous Node Helpers + + - Nan::MakeCallback() + - Nan::ObjectWrap + - NAN_MODULE_INIT() + - Nan::Export() + + + + + +### Tests + +To run the NAN tests do: + +``` sh +npm install +npm run-script rebuild-tests +npm test +``` + +Or just: + +``` sh +npm install +make test +``` + + +## Governance & Contributing + +NAN is governed by the [io.js](https://iojs.org/) Addon API Working Group + +### Addon API Working Group (WG) + +The NAN project is jointly governed by a Working Group which is responsible for high-level guidance of the project. + +Members of the WG are also known as Collaborators, there is no distinction between the two, unlike other io.js projects. + +The WG has final authority over this project including: + +* Technical direction +* Project governance and process (including this policy) +* Contribution policy +* GitHub repository hosting +* Maintaining the list of additional Collaborators + +For the current list of WG members, see the project [README.md](./README.md#collaborators). + +Individuals making significant and valuable contributions are made members of the WG and given commit-access to the project. These individuals are identified by the WG and their addition to the WG is discussed via GitHub and requires unanimous consensus amongst those WG members participating in the discussion with a quorum of 50% of WG members required for acceptance of the vote. + +_Note:_ If you make a significant contribution and are not considered for commit-access log an issue or contact a WG member directly. + +For the current list of WG members / Collaborators, see the project [README.md](./README.md#collaborators). + +### Consensus Seeking Process + +The WG follows a [Consensus Seeking](http://en.wikipedia.org/wiki/Consensus-seeking_decision-making) decision making model. + +Modifications of the contents of the NAN repository are made on a collaborative basis. Anybody with a GitHub account may propose a modification via pull request and it will be considered by the WG. All pull requests must be reviewed and accepted by a WG member with sufficient expertise who is able to take full responsibility for the change. In the case of pull requests proposed by an existing WG member, an additional WG member is required for sign-off. Consensus should be sought if additional WG members participate and there is disagreement around a particular modification. + +If a change proposal cannot reach a consensus, a WG member can call for a vote amongst the members of the WG. Simple majority wins. + +### Developer's Certificate of Origin 1.0 + +By making a contribution to this project, I certify that: + +* (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or +* (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or +* (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. + + +### WG Members / Collaborators + + + + + + + + + +
            Rod VaggGitHub/rvaggTwitter/@rvagg
            Benjamin ByholmGitHub/kkoopa-
            Trevor NorrisGitHub/trevnorrisTwitter/@trevnorris
            Nathan RajlichGitHub/TooTallNateTwitter/@TooTallNate
            Brett LawsonGitHub/brett19Twitter/@brett19x
            Ben NoordhuisGitHub/bnoordhuisTwitter/@bnoordhuis
            David SiegelGitHub/agnat-
            + +## Licence & copyright + +Copyright (c) 2015 NAN WG Members / Collaborators (listed above). + +Native Abstractions for Node.js is licensed under an MIT license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE file for more details. diff --git a/node_modules/ws/node_modules/bufferutil/node_modules/nan/appveyor.yml b/node_modules/ws/node_modules/bufferutil/node_modules/nan/appveyor.yml new file mode 100644 index 0000000..694f84e --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/node_modules/nan/appveyor.yml @@ -0,0 +1,37 @@ +# http://www.appveyor.com/docs/appveyor-yml + +# Test against these versions of Io.js and Node.js. +environment: + matrix: + # node.js + - nodejs_version: "0.8" + - nodejs_version: "0.10" + - nodejs_version: "0.12" + - nodejs_version: "3" + - nodejs_version: "4" + +# Install scripts. (runs after repo cloning) +install: + # Get the latest stable version of Node 0.STABLE.latest + - ps: Install-Product node $env:nodejs_version + - IF %nodejs_version% EQU 0.8 npm -g install npm@2 + - IF %nodejs_version% EQU 0.8 set PATH=%APPDATA%\npm;%PATH% + - npm -g install npm + - IF %nodejs_version% NEQ 0.8 set PATH=%APPDATA%\npm;%PATH% + # Typical npm stuff. + - npm install + - npm run rebuild-tests + +# Post-install test scripts. +test_script: + # Output useful info for debugging. + - node --version + - npm --version + # run tests + - IF %nodejs_version% LSS 1 (npm test) ELSE (IF %nodejs_version% LSS 4 (iojs node_modules\tap\bin\tap.js --gc test/js/*-test.js) ELSE (node node_modules\tap\bin\tap.js --gc test/js/*-test.js)) + +# Don't actually build. +build: off + +# Set build version format here instead of in the admin panel. +version: "{build}" diff --git a/node_modules/ws/node_modules/bufferutil/node_modules/nan/doc/.build.sh b/node_modules/ws/node_modules/bufferutil/node_modules/nan/doc/.build.sh new file mode 100755 index 0000000..75a975a --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/node_modules/nan/doc/.build.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +files=" \ + methods.md \ + scopes.md \ + persistent.md \ + new.md \ + converters.md \ + maybe_types.md \ + script.md \ + errors.md \ + buffers.md \ + callback.md \ + asyncworker.md \ + string_bytes.md \ + v8_internals.md \ + v8_misc.md \ + node_misc.md \ +" + +__dirname=$(dirname "${BASH_SOURCE[0]}") +head=$(perl -e 'while (<>) { if (!$en){print;} if ($_=~/ NanNew("foo").ToLocalChecked() */ + if (arguments[groups[3][0]] === 'NanNew') { + return [arguments[0], '.ToLocalChecked()'].join(''); + } + + /* insert warning for removed functions as comment on new line above */ + switch (arguments[groups[4][0]]) { + case 'GetIndexedPropertiesExternalArrayData': + case 'GetIndexedPropertiesExternalArrayDataLength': + case 'GetIndexedPropertiesExternalArrayDataType': + case 'GetIndexedPropertiesPixelData': + case 'GetIndexedPropertiesPixelDataLength': + case 'HasIndexedPropertiesInExternalArrayData': + case 'HasIndexedPropertiesInPixelData': + case 'SetIndexedPropertiesToExternalArrayData': + case 'SetIndexedPropertiesToPixelData': + return arguments[groups[4][0] - 1] ? arguments[0] : [warning1, arguments[0]].join(''); + default: + } + + /* remove unnecessary NanScope() */ + switch (arguments[groups[5][0]]) { + case 'NAN_GETTER': + case 'NAN_METHOD': + case 'NAN_SETTER': + case 'NAN_INDEX_DELETER': + case 'NAN_INDEX_ENUMERATOR': + case 'NAN_INDEX_GETTER': + case 'NAN_INDEX_QUERY': + case 'NAN_INDEX_SETTER': + case 'NAN_PROPERTY_DELETER': + case 'NAN_PROPERTY_ENUMERATOR': + case 'NAN_PROPERTY_GETTER': + case 'NAN_PROPERTY_QUERY': + case 'NAN_PROPERTY_SETTER': + return arguments[groups[5][0] - 1]; + default: + } + + /* Value converstion */ + switch (arguments[groups[6][0]]) { + case 'Boolean': + case 'Int32': + case 'Integer': + case 'Number': + case 'Object': + case 'String': + case 'Uint32': + return [arguments[groups[6][0] - 2], 'NanTo(', arguments[groups[6][0] - 1]].join(''); + default: + } + + /* other value conversion */ + switch (arguments[groups[7][0]]) { + case 'BooleanValue': + return [arguments[groups[7][0] - 2], 'NanTo(', arguments[groups[7][0] - 1]].join(''); + case 'Int32Value': + return [arguments[groups[7][0] - 2], 'NanTo(', arguments[groups[7][0] - 1]].join(''); + case 'IntegerValue': + return [arguments[groups[7][0] - 2], 'NanTo(', arguments[groups[7][0] - 1]].join(''); + case 'Uint32Value': + return [arguments[groups[7][0] - 2], 'NanTo(', arguments[groups[7][0] - 1]].join(''); + default: + } + + /* NAN_WEAK_CALLBACK */ + if (arguments[groups[8][0]] === 'NAN_WEAK_CALLBACK') { + return ['template\nvoid ', + arguments[groups[8][0] + 1], '(const NanWeakCallbackInfo &data)'].join(''); + } + + /* use methods on NAN classes instead */ + switch (arguments[groups[9][0]]) { + case 'NanDisposePersistent': + return [arguments[groups[9][0] + 1], '.Reset('].join(''); + case 'NanObjectWrapHandle': + return [arguments[groups[9][0] + 1], '->handle('].join(''); + default: + } + + /* use method on NanPersistent instead */ + if (arguments[groups[10][0]] === 'NanMakeWeakPersistent') { + return arguments[groups[10][0] + 1] + '.SetWeak('; + } + + /* These return Maybes, the upper ones take no arguments */ + switch (arguments[groups[11][0]]) { + case 'GetEndColumn': + case 'GetFunction': + case 'GetLineNumber': + case 'GetOwnPropertyNames': + case 'GetPropertyNames': + case 'GetSourceLine': + case 'GetStartColumn': + case 'NewInstance': + case 'ObjectProtoToString': + case 'ToArrayIndex': + case 'ToDetailString': + return [arguments[groups[11][0] - 2], 'Nan', arguments[groups[11][0]], '(', arguments[groups[11][0] - 1]].join(''); + case 'CallAsConstructor': + case 'CallAsFunction': + case 'CloneElementAt': + case 'Delete': + case 'ForceSet': + case 'Get': + case 'GetPropertyAttributes': + case 'GetRealNamedProperty': + case 'GetRealNamedPropertyInPrototypeChain': + case 'Has': + case 'HasOwnProperty': + case 'HasRealIndexedProperty': + case 'HasRealNamedCallbackProperty': + case 'HasRealNamedProperty': + case 'Set': + case 'SetAccessor': + case 'SetIndexedPropertyHandler': + case 'SetNamedPropertyHandler': + case 'SetPrototype': + return [arguments[groups[11][0] - 2], 'Nan', arguments[groups[11][0]], '(', arguments[groups[11][0] - 1], ', '].join(''); + default: + } + + /* Automatic ToLocalChecked(), take it or leave it */ + switch (arguments[groups[12][0]]) { + case 'Date': + case 'String': + case 'RegExp': + return ['NanNew', arguments[groups[12][0] - 1], arguments[groups[12][0] + 1], '.ToLocalChecked()'].join(''); + default: + } + + /* NanEquals is now required for uniformity */ + if (arguments[groups[13][0]] === 'Equals') { + return [arguments[groups[13][0] - 1], 'NanEquals(', arguments[groups[13][0] - 1], ', ', arguments[groups[13][0] + 1]].join(''); + } + + /* use method on replacement class instead */ + if (arguments[groups[14][0]] === 'NanAssignPersistent') { + return [arguments[groups[14][0] + 1], '.Reset('].join(''); + } + + /* args --> info */ + if (arguments[groups[15][0]] === 'args') { + return [arguments[groups[15][0] - 1], 'info', arguments[groups[15][0] + 1]].join(''); + } + + /* ObjectWrap --> NanObjectWrap */ + if (arguments[groups[16][0]] === 'ObjectWrap') { + return [arguments[groups[16][0] - 1], 'NanObjectWrap', arguments[groups[16][0] + 1]].join(''); + } + + /* Persistent --> NanPersistent */ + if (arguments[groups[17][0]] === 'Persistent') { + return [arguments[groups[17][0] - 1], 'NanPersistent', arguments[groups[17][0] + 1]].join(''); + } + + /* This should not happen. A switch is probably missing a case if it does. */ + throw 'Unhandled match: ' + arguments[0]; +} + +/* reads a file, runs replacement and writes it back */ +function processFile(file) { + fs.readFile(file, {encoding: 'utf8'}, function (err, data) { + if (err) { + throw err; + } + + /* run replacement twice, might need more runs */ + fs.writeFile(file, data.replace(master, replace).replace(master, replace), function (err) { + if (err) { + throw err; + } + }); + }); +} + +/* process file names from command line and process the identified files */ +for (i = 2, length = process.argv.length; i < length; i++) { + glob(process.argv[i], function (err, matches) { + if (err) { + throw err; + } + matches.forEach(processFile); + }); +} diff --git a/node_modules/ws/node_modules/bufferutil/node_modules/nan/tools/README.md b/node_modules/ws/node_modules/bufferutil/node_modules/nan/tools/README.md new file mode 100644 index 0000000..7f07e4b --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/node_modules/nan/tools/README.md @@ -0,0 +1,14 @@ +1to2 naively converts source code files from NAN 1 to NAN 2. There will be erroneous conversions, +false positives and missed opportunities. The input files are rewritten in place. Make sure that +you have backups. You will have to manually review the changes afterwards and do some touchups. + +```sh +$ tools/1to2.js + + Usage: 1to2 [options] + + Options: + + -h, --help output usage information + -V, --version output the version number +``` diff --git a/node_modules/ws/node_modules/bufferutil/node_modules/nan/tools/package.json b/node_modules/ws/node_modules/bufferutil/node_modules/nan/tools/package.json new file mode 100644 index 0000000..2dcdd78 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/node_modules/nan/tools/package.json @@ -0,0 +1,19 @@ +{ + "name": "1to2", + "version": "1.0.0", + "description": "NAN 1 -> 2 Migration Script", + "main": "1to2.js", + "repository": { + "type": "git", + "url": "git://github.com/nodejs/nan.git" + }, + "contributors": [ + "Benjamin Byholm (https://github.com/kkoopa/)", + "Mathias Küsel (https://github.com/mathiask88/)" + ], + "dependencies": { + "glob": "~5.0.10", + "commander": "~2.8.1" + }, + "license": "MIT" +} diff --git a/node_modules/ws/node_modules/bufferutil/package.json b/node_modules/ws/node_modules/bufferutil/package.json new file mode 100644 index 0000000..504cdb9 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/package.json @@ -0,0 +1,59 @@ +{ + "name": "bufferutil", + "version": "1.2.1", + "description": "WebSocket buffer utils", + "main": "index.js", + "scripts": { + "test": "echo \"Only testing builds, test have to be extraced from `ws`\" && exit 0", + "install": "node-gyp rebuild" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/websockets/bufferutil.git" + }, + "keywords": [ + "bufferutil" + ], + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/websockets/bufferutil/issues" + }, + "homepage": "https://github.com/websockets/bufferutil", + "dependencies": { + "bindings": "1.2.x", + "nan": "^2.0.5" + }, + "gypfile": true, + "gitHead": "cb7163377b8032fb79ddd835a549c83488585859", + "_id": "bufferutil@1.2.1", + "_shasum": "37be5d36e1e06492221e68d474b1ac58e510cbd7", + "_from": "bufferutil@>=1.2.0 <1.3.0", + "_npmVersion": "2.9.1", + "_nodeVersion": "0.12.3", + "_npmUser": { + "name": "3rdeden", + "email": "npm@3rd-Eden.com" + }, + "maintainers": [ + { + "name": "3rdeden", + "email": "npm@3rd-Eden.com" + }, + { + "name": "einaros", + "email": "einaros@gmail.com" + } + ], + "dist": { + "shasum": "37be5d36e1e06492221e68d474b1ac58e510cbd7", + "tarball": "http://registry.npmjs.org/bufferutil/-/bufferutil-1.2.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-1.2.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/ws/node_modules/bufferutil/src/bufferutil.cc b/node_modules/ws/node_modules/bufferutil/src/bufferutil.cc new file mode 100644 index 0000000..1e826b3 --- /dev/null +++ b/node_modules/ws/node_modules/bufferutil/src/bufferutil.cc @@ -0,0 +1,120 @@ +/*! + * bufferutil: WebSocket buffer utils + * Copyright(c) 2015 Einar Otto Stangvik + * MIT Licensed + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nan.h" + +using namespace v8; +using namespace node; + +class BufferUtil : public ObjectWrap +{ +public: + + static void Initialize(v8::Handle target) + { + Nan::HandleScope scope; + Local t = Nan::New(New); + t->InstanceTemplate()->SetInternalFieldCount(1); + Nan::SetMethod(t, "unmask", BufferUtil::Unmask); + Nan::SetMethod(t, "mask", BufferUtil::Mask); + Nan::SetMethod(t, "merge", BufferUtil::Merge); + Nan::Set(target, Nan::New("BufferUtil").ToLocalChecked(), t->GetFunction()); + } + +protected: + + static NAN_METHOD(New) + { + Nan::HandleScope scope; + BufferUtil* bufferUtil = new BufferUtil(); + bufferUtil->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + } + + static NAN_METHOD(Merge) + { + Nan::HandleScope scope; + Local bufferObj = info[0]->ToObject(); + char* buffer = Buffer::Data(bufferObj); + Local array = Local::Cast(info[1]); + unsigned int arrayLength = array->Length(); + size_t offset = 0; + unsigned int i; + for (i = 0; i < arrayLength; ++i) { + Local src = array->Get(i)->ToObject(); + size_t length = Buffer::Length(src); + memcpy(buffer + offset, Buffer::Data(src), length); + offset += length; + } + info.GetReturnValue().Set(Nan::True()); + } + + static NAN_METHOD(Unmask) + { + Nan::HandleScope scope; + Local buffer_obj = info[0]->ToObject(); + size_t length = Buffer::Length(buffer_obj); + Local mask_obj = info[1]->ToObject(); + unsigned int *mask = (unsigned int*)Buffer::Data(mask_obj); + unsigned int* from = (unsigned int*)Buffer::Data(buffer_obj); + size_t len32 = length / 4; + unsigned int i; + for (i = 0; i < len32; ++i) *(from + i) ^= *mask; + from += i; + switch (length % 4) { + case 3: *((unsigned char*)from+2) = *((unsigned char*)from+2) ^ ((unsigned char*)mask)[2]; + case 2: *((unsigned char*)from+1) = *((unsigned char*)from+1) ^ ((unsigned char*)mask)[1]; + case 1: *((unsigned char*)from ) = *((unsigned char*)from ) ^ ((unsigned char*)mask)[0]; + case 0:; + } + info.GetReturnValue().Set(Nan::True()); + } + + static NAN_METHOD(Mask) + { + Nan::HandleScope scope; + Local buffer_obj = info[0]->ToObject(); + Local mask_obj = info[1]->ToObject(); + unsigned int *mask = (unsigned int*)Buffer::Data(mask_obj); + Local output_obj = info[2]->ToObject(); + unsigned int dataOffset = info[3]->Int32Value(); + unsigned int length = info[4]->Int32Value(); + unsigned int* to = (unsigned int*)(Buffer::Data(output_obj) + dataOffset); + unsigned int* from = (unsigned int*)Buffer::Data(buffer_obj); + unsigned int len32 = length / 4; + unsigned int i; + for (i = 0; i < len32; ++i) *(to + i) = *(from + i) ^ *mask; + to += i; + from += i; + switch (length % 4) { + case 3: *((unsigned char*)to+2) = *((unsigned char*)from+2) ^ *((unsigned char*)mask+2); + case 2: *((unsigned char*)to+1) = *((unsigned char*)from+1) ^ *((unsigned char*)mask+1); + case 1: *((unsigned char*)to ) = *((unsigned char*)from ) ^ *((unsigned char*)mask); + case 0:; + } + info.GetReturnValue().Set(Nan::True()); + } +}; + +#if !NODE_VERSION_AT_LEAST(0,10,0) +extern "C" +#endif +void init (Handle target) +{ + Nan::HandleScope scope; + BufferUtil::Initialize(target); +} + +NODE_MODULE(bufferutil, init) diff --git a/node_modules/ws/node_modules/options/.npmignore b/node_modules/ws/node_modules/options/.npmignore new file mode 100644 index 0000000..1b18fb3 --- /dev/null +++ b/node_modules/ws/node_modules/options/.npmignore @@ -0,0 +1,7 @@ +npm-debug.log +node_modules +.*.swp +.lock-* +build/ + +test diff --git a/node_modules/ws/node_modules/options/Makefile b/node_modules/ws/node_modules/options/Makefile new file mode 100644 index 0000000..7496b6f --- /dev/null +++ b/node_modules/ws/node_modules/options/Makefile @@ -0,0 +1,12 @@ +ALL_TESTS = $(shell find test/ -name '*.test.js') + +run-tests: + @./node_modules/.bin/mocha \ + -t 2000 \ + $(TESTFLAGS) \ + $(TESTS) + +test: + @$(MAKE) NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests + +.PHONY: test diff --git a/node_modules/ws/node_modules/options/README.md b/node_modules/ws/node_modules/options/README.md new file mode 100644 index 0000000..0dabc75 --- /dev/null +++ b/node_modules/ws/node_modules/options/README.md @@ -0,0 +1,69 @@ +# options.js # + +A very light-weight in-code option parsers for node.js. + +## Usage ## + +``` js +var Options = require("options"); + +// Create an Options object +function foo(options) { + var default_options = { + foo : "bar" + }; + + // Create an option object with default value + var opts = new Options(default_options); + + // Merge options + opts = opts.merge(options); + + // Reset to default value + opts.reset(); + + // Copy selected attributes out + var seled_att = opts.copy("foo"); + + // Read json options from a file. + opts.read("options.file"); // Sync + opts.read("options.file", function(err){ // Async + if(err){ // If error occurs + console.log("File error."); + }else{ + // No error + } + }); + + // Attributes defined or not + opts.isDefinedAndNonNull("foobar"); + opts.isDefined("foobar"); +} + +``` + + +## License ## + +(The MIT License) + +Copyright (c) 2012 Einar Otto Stangvik <einaros@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/ws/node_modules/options/lib/options.js b/node_modules/ws/node_modules/options/lib/options.js new file mode 100644 index 0000000..4fc45e9 --- /dev/null +++ b/node_modules/ws/node_modules/options/lib/options.js @@ -0,0 +1,86 @@ +/*! + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var fs = require('fs'); + +function Options(defaults) { + var internalValues = {}; + var values = this.value = {}; + Object.keys(defaults).forEach(function(key) { + internalValues[key] = defaults[key]; + Object.defineProperty(values, key, { + get: function() { return internalValues[key]; }, + configurable: false, + enumerable: true + }); + }); + this.reset = function() { + Object.keys(defaults).forEach(function(key) { + internalValues[key] = defaults[key]; + }); + return this; + }; + this.merge = function(options, required) { + options = options || {}; + if (Object.prototype.toString.call(required) === '[object Array]') { + var missing = []; + for (var i = 0, l = required.length; i < l; ++i) { + var key = required[i]; + if (!(key in options)) { + missing.push(key); + } + } + if (missing.length > 0) { + if (missing.length > 1) { + throw new Error('options ' + + missing.slice(0, missing.length - 1).join(', ') + ' and ' + + missing[missing.length - 1] + ' must be defined'); + } + else throw new Error('option ' + missing[0] + ' must be defined'); + } + } + Object.keys(options).forEach(function(key) { + if (key in internalValues) { + internalValues[key] = options[key]; + } + }); + return this; + }; + this.copy = function(keys) { + var obj = {}; + Object.keys(defaults).forEach(function(key) { + if (keys.indexOf(key) !== -1) { + obj[key] = values[key]; + } + }); + return obj; + }; + this.read = function(filename, cb) { + if (typeof cb == 'function') { + var self = this; + fs.readFile(filename, function(error, data) { + if (error) return cb(error); + var conf = JSON.parse(data); + self.merge(conf); + cb(); + }); + } + else { + var conf = JSON.parse(fs.readFileSync(filename)); + this.merge(conf); + } + return this; + }; + this.isDefined = function(key) { + return typeof values[key] != 'undefined'; + }; + this.isDefinedAndNonNull = function(key) { + return typeof values[key] != 'undefined' && values[key] !== null; + }; + Object.freeze(values); + Object.freeze(this); +} + +module.exports = Options; diff --git a/node_modules/ws/node_modules/options/package.json b/node_modules/ws/node_modules/options/package.json new file mode 100644 index 0000000..7a62d8e --- /dev/null +++ b/node_modules/ws/node_modules/options/package.json @@ -0,0 +1,51 @@ +{ + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "name": "options", + "description": "A very light-weight in-code option parsers for node.js.", + "version": "0.0.6", + "repository": { + "type": "git", + "url": "git://github.com/einaros/options.js.git" + }, + "main": "lib/options", + "scripts": { + "test": "make test" + }, + "engines": { + "node": ">=0.4.0" + }, + "dependencies": {}, + "devDependencies": { + "mocha": "latest" + }, + "gitHead": "ff53d0a092c897cb95964232a96fe17da65c11af", + "bugs": { + "url": "https://github.com/einaros/options.js/issues" + }, + "homepage": "https://github.com/einaros/options.js", + "_id": "options@0.0.6", + "_shasum": "ec22d312806bb53e731773e7cdaefcf1c643128f", + "_from": "options@>=0.0.5", + "_npmVersion": "1.4.21", + "_npmUser": { + "name": "einaros", + "email": "einaros@gmail.com" + }, + "maintainers": [ + { + "name": "einaros", + "email": "einaros@gmail.com" + } + ], + "dist": { + "shasum": "ec22d312806bb53e731773e7cdaefcf1c643128f", + "tarball": "http://registry.npmjs.org/options/-/options-0.0.6.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/ws/node_modules/ultron/.npmignore b/node_modules/ws/node_modules/ultron/.npmignore new file mode 100644 index 0000000..66210a2 --- /dev/null +++ b/node_modules/ws/node_modules/ultron/.npmignore @@ -0,0 +1,3 @@ +node_modules +coverage +.tern-port diff --git a/node_modules/ws/node_modules/ultron/.travis.yml b/node_modules/ws/node_modules/ultron/.travis.yml new file mode 100644 index 0000000..a505004 --- /dev/null +++ b/node_modules/ws/node_modules/ultron/.travis.yml @@ -0,0 +1,21 @@ +sudo: false +language: node_js +node_js: + - "0.12" + - "0.10" + - "0.8" + - "iojs" +before_install: + - 'if [ "${TRAVIS_NODE_VERSION}" == "0.8" ]; then npm install -g npm@2.11.1; fi' +script: + - "npm run test-travis" +after_script: + - "npm install coveralls@2.11.x && cat coverage/lcov.info | coveralls" +matrix: + fast_finish: true +notifications: + irc: + channels: + - "irc.freenode.org#unshift" + on_success: change + on_failure: change diff --git a/node_modules/ws/node_modules/ultron/LICENSE b/node_modules/ws/node_modules/ultron/LICENSE new file mode 100644 index 0000000..6dc9316 --- /dev/null +++ b/node_modules/ws/node_modules/ultron/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Unshift.io, Arnout Kazemier, the Contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/node_modules/ws/node_modules/ultron/README.md b/node_modules/ws/node_modules/ultron/README.md new file mode 100644 index 0000000..84fa3f2 --- /dev/null +++ b/node_modules/ws/node_modules/ultron/README.md @@ -0,0 +1,97 @@ +# Ultron + +[![Made by unshift](https://img.shields.io/badge/made%20by-unshift-00ffcc.svg?style=flat-square)](http://unshift.io)[![Version npm](http://img.shields.io/npm/v/ultron.svg?style=flat-square)](http://browsenpm.org/package/ultron)[![Build Status](http://img.shields.io/travis/unshiftio/ultron/master.svg?style=flat-square)](https://travis-ci.org/unshiftio/ultron)[![Dependencies](https://img.shields.io/david/unshiftio/ultron.svg?style=flat-square)](https://david-dm.org/unshiftio/ultron)[![Coverage Status](http://img.shields.io/coveralls/unshiftio/ultron/master.svg?style=flat-square)](https://coveralls.io/r/unshiftio/ultron?branch=master)[![IRC channel](http://img.shields.io/badge/IRC-irc.freenode.net%23unshift-00a8ff.svg?style=flat-square)](http://webchat.freenode.net/?channels=unshift) + +Ultron is a high-intelligence robot. It gathers intelligence so it can start +improving upon his rudimentary design. It will learn your event emitting +patterns and find ways to exterminate them. Allowing you to remove only the +event emitters that **you** assigned and not the ones that your users or +developers assigned. This can prevent race conditions, memory leaks and even file +descriptor leaks from ever happening as you won't remove clean up processes. + +## Installation + +The module is designed to be used in browsers using browserify and in Node.js. +You can install the module through the public npm registry by running the +following command in CLI: + +``` +npm install --save ultron +``` + +## Usage + +In all examples we assume that you've required the library as following: + +```js +'use strict'; + +var Ultron = require('ultron'); +``` + +Now that we've required the library we can construct our first `Ultron` instance. +The constructor requires one argument which should be the `EventEmitter` +instance that we need to operate upon. This can be the `EventEmitter` module +that ships with Node.js or `EventEmitter3` or anything else as long as it +follow the same API and internal structure as these 2. So with that in mind we +can create the instance: + +```js +// +// For the sake of this example we're going to construct an empty EventEmitter +// +var EventEmitter = require('events').EventEmitter; // or require('eventmitter3'); +var events = new EventEmitter(); + +var ultron = new Ultron(events); +``` + +You can now use the following API's from the Ultron instance: + +### Ultron.on + +Register a new event listener for the given event. It follows the exact same API +as `EventEmitter.on` but it will return itself instead of returning the +EventEmitter instance. If you are using EventEmitter3 it also supports the +context param: + +```js +ultron.on('event-name', handler, { custom: 'function context' }); +``` + +### Ultron.once + +Exactly the same as the [Ultron.on](#ultronon) but it only allows the execution +once. + +### Ultron.remove + +This is where all the magic happens and the safe removal starts. This function +accepts different argument styles: + +- No arguments, assume that all events need to be removed so it will work as + `removeAllListeners()` API. +- 1 argument, when it's a string it will be split on ` ` and `,` to create a + list of events that need to be cleared. +- Multiple arguments, we assume that they are all names of events that need to + be cleared. + +```js +ultron.remove('foo, bar baz'); // Removes foo, bar and baz. +ultron.remove('foo', 'bar', 'baz'); // Removes foo, bar and baz. +ultron.remove(); // Removes everything. +``` + +If you just want to remove a single event listener using a function reference +you can still use the EventEmitter's `removeListener(event, fn)` API: + +```js +function foo() {} + +ulton.on('foo', foo); +events.removeListener('foo', foo); +``` + +## License + +MIT diff --git a/node_modules/ws/node_modules/ultron/index.js b/node_modules/ws/node_modules/ultron/index.js new file mode 100644 index 0000000..af17ab7 --- /dev/null +++ b/node_modules/ws/node_modules/ultron/index.js @@ -0,0 +1,129 @@ +'use strict'; + +var has = Object.prototype.hasOwnProperty; + +/** + * An auto incrementing id which we can use to create "unique" Ultron instances + * so we can track the event emitters that are added through the Ultron + * interface. + * + * @type {Number} + * @private + */ +var id = 0; + +/** + * Ultron is high-intelligence robot. It gathers intelligence so it can start improving + * upon his rudimentary design. It will learn from your EventEmitting patterns + * and exterminate them. + * + * @constructor + * @param {EventEmitter} ee EventEmitter instance we need to wrap. + * @api public + */ +function Ultron(ee) { + if (!(this instanceof Ultron)) return new Ultron(ee); + + this.id = id++; + this.ee = ee; +} + +/** + * Register a new EventListener for the given event. + * + * @param {String} event Name of the event. + * @param {Functon} fn Callback function. + * @param {Mixed} context The context of the function. + * @returns {Ultron} + * @api public + */ +Ultron.prototype.on = function on(event, fn, context) { + fn.__ultron = this.id; + this.ee.on(event, fn, context); + + return this; +}; +/** + * Add an EventListener that's only called once. + * + * @param {String} event Name of the event. + * @param {Function} fn Callback function. + * @param {Mixed} context The context of the function. + * @returns {Ultron} + * @api public + */ +Ultron.prototype.once = function once(event, fn, context) { + fn.__ultron = this.id; + this.ee.once(event, fn, context); + + return this; +}; + +/** + * Remove the listeners we assigned for the given event. + * + * @returns {Ultron} + * @api public + */ +Ultron.prototype.remove = function remove() { + var args = arguments + , event; + + // + // When no event names are provided we assume that we need to clear all the + // events that were assigned through us. + // + if (args.length === 1 && 'string' === typeof args[0]) { + args = args[0].split(/[, ]+/); + } else if (!args.length) { + args = []; + + for (event in this.ee._events) { + if (has.call(this.ee._events, event)) args.push(event); + } + } + + for (var i = 0; i < args.length; i++) { + var listeners = this.ee.listeners(args[i]); + + for (var j = 0; j < listeners.length; j++) { + event = listeners[j]; + + // + // Once listeners have a `listener` property that stores the real listener + // in the EventEmitter that ships with Node.js. + // + if (event.listener) { + if (event.listener.__ultron !== this.id) continue; + delete event.listener.__ultron; + } else { + if (event.__ultron !== this.id) continue; + delete event.__ultron; + } + + this.ee.removeListener(args[i], event); + } + } + + return this; +}; + +/** + * Destroy the Ultron instance, remove all listeners and release all references. + * + * @returns {Boolean} + * @api public + */ +Ultron.prototype.destroy = function destroy() { + if (!this.ee) return false; + + this.remove(); + this.ee = null; + + return true; +}; + +// +// Expose the module. +// +module.exports = Ultron; diff --git a/node_modules/ws/node_modules/ultron/package.json b/node_modules/ws/node_modules/ultron/package.json new file mode 100644 index 0000000..d68bc58 --- /dev/null +++ b/node_modules/ws/node_modules/ultron/package.json @@ -0,0 +1,74 @@ +{ + "name": "ultron", + "version": "1.0.2", + "description": "Ultron is high-intelligence robot. It gathers intel so it can start improving upon his rudimentary design", + "main": "index.js", + "scripts": { + "100%": "istanbul check-coverage --statements 100 --functions 100 --lines 100 --branches 100", + "test": "mocha test.js", + "watch": "mocha --watch test.js", + "coverage": "istanbul cover ./node_modules/.bin/_mocha -- test.js", + "test-travis": "istanbul cover node_modules/.bin/_mocha --report lcovonly -- test.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/unshiftio/ultron.git" + }, + "keywords": [ + "Ultron", + "robot", + "gather", + "intelligence", + "event", + "events", + "eventemitter", + "emitter", + "cleanup" + ], + "author": { + "name": "Arnout Kazemier" + }, + "license": "MIT", + "devDependencies": { + "assume": "1.2.x", + "eventemitter3": "1.1.x", + "istanbul": "0.3.x", + "mocha": "2.2.x", + "pre-commit": "1.0.x" + }, + "bugs": { + "url": "https://github.com/unshiftio/ultron/issues" + }, + "homepage": "https://github.com/unshiftio/ultron", + "gitHead": "a10482ae98a09120821545456c90c6d60d540f7c", + "_id": "ultron@1.0.2", + "_shasum": "ace116ab557cd197386a4e88f4685378c8b2e4fa", + "_from": "ultron@>=1.0.0 <1.1.0", + "_npmVersion": "2.9.1", + "_nodeVersion": "0.12.3", + "_npmUser": { + "name": "3rdeden", + "email": "npm@3rd-Eden.com" + }, + "maintainers": [ + { + "name": "unshift", + "email": "npm@unshift.io" + }, + { + "name": "v1", + "email": "info@3rd-Eden.com" + }, + { + "name": "3rdeden", + "email": "npm@3rd-Eden.com" + } + ], + "dist": { + "shasum": "ace116ab557cd197386a4e88f4685378c8b2e4fa", + "tarball": "http://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/ws/node_modules/ultron/test.js b/node_modules/ws/node_modules/ultron/test.js new file mode 100644 index 0000000..1fd4f1b --- /dev/null +++ b/node_modules/ws/node_modules/ultron/test.js @@ -0,0 +1,327 @@ +/* istanbul ignore next */ +describe('Ultron', function () { + 'use strict'; + + var EventEmitter = require('eventemitter3') + , EE = require('events').EventEmitter + , assume = require('assume') + , Ultron = require('./') + , ultron + , ee; + + beforeEach(function () { + ee = new EventEmitter(); + ultron = new Ultron(ee); + }); + + afterEach(function () { + ultron.destroy(); + ee.removeAllListeners(); + }); + + it('is exposed as a function', function () { + assume(Ultron).is.a('function'); + }); + + it('can be initialized without the new keyword', function () { + assume(Ultron(ee)).is.instanceOf(Ultron); + }); + + it('assigns a unique id to every instance', function () { + for (var i = 0; i < 100; i++) { + assume(ultron.id).does.not.equal((new Ultron()).id); + } + }); + + it('allows removal through the event emitter', function () { + function foo() {} + function bar() {} + + ultron.on('foo', foo); + ultron.once('foo', bar); + + assume(foo.__ultron).equals(ultron.id); + assume(bar.__ultron).equals(ultron.id); + assume(ee.listeners('foo').length).equals(2); + + ee.removeListener('foo', foo); + assume(ee.listeners('foo').length).equals(1); + + ee.removeListener('foo', bar); + assume(ee.listeners('foo').length).equals(0); + }); + + describe('#on', function () { + it('assigns a listener', function () { + assume(ee.listeners('foo').length).equals(0); + + function foo() {} + + ultron.on('foo', foo); + assume(ee.listeners('foo').length).equals(1); + assume(ee.listeners('foo')[0]).equals(foo); + }); + + it('tags the assigned function', function () { + assume(ee.listeners('foo').length).equals(0); + + ultron.on('foo', function () {}); + assume(ee.listeners('foo')[0].__ultron).equals(ultron.id); + }); + + it('also passes in the context', function (next) { + var context = 1313; + + ultron.on('foo', function (a, b, c) { + assume(a).equals('a'); + assume(b).equals('b'); + assume(c).equals('c'); + + assume(this).equals(context); + + next(); + }, context); + + ee.emit('foo', 'a', 'b', 'c'); + }); + + it('works with regular eventemitters as well', function (next) { + var ee = new EE() + , ultron = new Ultron(ee); + + ultron.on('foo', function (a, b, c) { + assume(a).equals('a'); + assume(b).equals('b'); + assume(c).equals('c'); + + next(); + }); + + ee.emit('foo', 'a', 'b', 'c'); + }); + }); + + describe('#once', function () { + it('assigns a listener', function () { + assume(ee.listeners('foo').length).equals(0); + + function foo() {} + ultron.once('foo', foo); + assume(ee.listeners('foo').length).equals(1); + assume(ee.listeners('foo')[0]).equals(foo); + }); + + it('tags the assigned function', function () { + assume(ee.listeners('foo').length).equals(0); + + ultron.once('foo', function () {}); + assume(ee.listeners('foo')[0].__ultron).equals(ultron.id); + }); + + it('also passes in the context', function (next) { + var context = 1313; + + ultron.once('foo', function (a, b, c) { + assume(a).equals('a'); + assume(b).equals('b'); + assume(c).equals('c'); + + assume(this).equals(context); + + next(); + }, context); + + ee.emit('foo', 'a', 'b', 'c'); + ee.emit('foo', 'a', 'b', 'c'); // Ensure that we don't double execute + }); + + it('works with regular eventemitters as well', function (next) { + var ee = new EE() + , ultron = new Ultron(ee); + + ultron.once('foo', function (a, b, c) { + assume(a).equals('a'); + assume(b).equals('b'); + assume(c).equals('c'); + + next(); + }); + + ee.emit('foo', 'a', 'b', 'c'); + ee.emit('foo', 'a', 'b', 'c'); // Ensure that we don't double execute + }); + }); + + describe('#remove', function () { + it('removes only our assigned `on` listeners', function () { + function foo() {} + function bar() {} + + ee.on('foo', foo); + ultron.on('foo', bar); + assume(ee.listeners('foo').length).equals(2); + + ultron.remove('foo'); + assume(ee.listeners('foo').length).equals(1); + assume(ee.listeners('foo')[0]).equals(foo); + }); + + it('removes our private __ultron references', function () { + function once() {} + function on() {} + + assume('__ultron' in once).is.false(); + assume('__ultron' in on).is.false(); + + ultron.on('foo', on); + ultron.once('bar', once); + + assume('__ultron' in once).is.true(); + assume('__ultron' in on).is.true(); + + ultron.remove('foo, bar'); + + assume('__ultron' in once).is.false(); + assume('__ultron' in on).is.false(); + + ultron.destroy(); + + ee = new EE(); + ultron = new Ultron(ee); + + assume('__ultron' in once).is.false(); + assume('__ultron' in on).is.false(); + + ultron.on('foo', on); + ultron.once('bar', once); + + assume('__ultron' in once).is.true(); + assume('__ultron' in on).is.true(); + + ultron.remove('foo, bar'); + + assume('__ultron' in once).is.false(); + assume('__ultron' in on).is.false(); + }); + + it('removes only our assigned `once` listeners', function () { + function foo() {} + function bar() {} + + ee.once('foo', foo); + ultron.once('foo', bar); + assume(ee.listeners('foo').length).equals(2); + + ultron.remove('foo'); + assume(ee.listeners('foo').length).equals(1); + assume(ee.listeners('foo')[0]).equals(foo); + }); + + it('removes only our assigned `once` listeners from regular EE', function () { + var ee = new EE() + , ultron = new Ultron(ee); + + function foo() {} + function bar() {} + + ee.once('foo', foo); + ultron.once('foo', bar); + assume(ee.listeners('foo').length).equals(2); + + ultron.remove('foo'); + assume(ee.listeners('foo').length).equals(1); + assume(ee.listeners('foo')[0].listener).equals(foo); + }); + + it('removes all assigned events if called without args', function () { + function foo() {} + function bar() {} + + ultron.on('foo', foo); + ultron.on('bar', bar); + + assume(ee.listeners('foo').length).equals(1); + assume(ee.listeners('bar').length).equals(1); + + ultron.remove(); + + assume(ee.listeners('foo').length).equals(0); + assume(ee.listeners('bar').length).equals(0); + }); + + it('removes multiple listeners based on args', function () { + function foo() {} + function bar() {} + function baz() {} + + ultron.on('foo', foo); + ultron.on('bar', bar); + ultron.on('baz', baz); + + assume(ee.listeners('foo').length).equals(1); + assume(ee.listeners('bar').length).equals(1); + assume(ee.listeners('baz').length).equals(1); + + ultron.remove('foo', 'bar'); + + assume(ee.listeners('foo').length).equals(0); + assume(ee.listeners('bar').length).equals(0); + assume(ee.listeners('baz').length).equals(1); + }); + + it('removes multiple listeners if first arg is seperated string', function () { + function foo() {} + function bar() {} + function baz() {} + + ultron.on('foo', foo); + ultron.on('bar', bar); + ultron.on('baz', baz); + + assume(ee.listeners('foo').length).equals(1); + assume(ee.listeners('bar').length).equals(1); + assume(ee.listeners('baz').length).equals(1); + + ultron.remove('foo, bar'); + + assume(ee.listeners('foo').length).equals(0); + assume(ee.listeners('bar').length).equals(0); + assume(ee.listeners('baz').length).equals(1); + }); + }); + + describe('#destroy', function () { + it('removes all listeners', function () { + function foo() {} + function bar() {} + function baz() {} + + ultron.on('foo', foo); + ultron.on('bar', bar); + ultron.on('baz', baz); + + assume(ee.listeners('foo').length).equals(1); + assume(ee.listeners('bar').length).equals(1); + assume(ee.listeners('baz').length).equals(1); + + ultron.destroy(); + + assume(ee.listeners('foo').length).equals(0); + assume(ee.listeners('bar').length).equals(0); + assume(ee.listeners('baz').length).equals(0); + }); + + it('removes the .ee reference', function () { + assume(ultron.ee).equals(ee); + ultron.destroy(); + assume(ultron.ee).equals(null); + }); + + it('returns booleans for state indication', function () { + assume(ultron.destroy()).is.true(); + assume(ultron.destroy()).is.false(); + assume(ultron.destroy()).is.false(); + assume(ultron.destroy()).is.false(); + }); + }); +}); diff --git a/node_modules/ws/node_modules/utf-8-validate/.npmignore b/node_modules/ws/node_modules/utf-8-validate/.npmignore new file mode 100644 index 0000000..0c90f67 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/.npmignore @@ -0,0 +1,3 @@ +npm-debug.log +node_modules +build diff --git a/node_modules/ws/node_modules/utf-8-validate/.travis.yml b/node_modules/ws/node_modules/utf-8-validate/.travis.yml new file mode 100644 index 0000000..cd09de2 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/.travis.yml @@ -0,0 +1,16 @@ +language: node_js +node_js: + - "iojs-v3" + - "iojs-v2" + - "iojs-v1" + - "0.12" + - "0.10" +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-4.9 + - g++-4.9 +before_install: + - export CC="gcc-4.9" CXX="g++-4.9" diff --git a/node_modules/ws/node_modules/utf-8-validate/README.md b/node_modules/ws/node_modules/utf-8-validate/README.md new file mode 100644 index 0000000..7e6d7e6 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/README.md @@ -0,0 +1,44 @@ +# utf-8-validate + +[![Build Status](https://travis-ci.org/websockets/utf-8-validate.svg?branch=master)](https://travis-ci.org/websockets/utf-8-validate) + +WebSocket connections require extensive UTF-8 validation in order to confirm to +the specification. This was unfortunately not possible in JavaScript, hence the +need for a binary addon. + +As the module consists of binary components, it should be used an +`optionalDependency` so when installation fails, it doesn't halt the +installation of your module. There are fallback files available in this +repository. See `fallback.js` for the suggest fallback implementation if +installation fails. + +## Installation + +``` +npm install utf-8-validate +``` + +## API + +In all examples we assume that you've already required the mdoule as +followed: + +```js +'use strict'; + +var isValid = require('utf-8-validate').isValidUTF8; +``` + +The module exposes 1 function: + +#### isValidUTF8 + +Validate if the passed in buffer contains valid UTF-8 chars. + +```js +bu.isValidUTF8(buffer); +``` + +## License + +MIT diff --git a/node_modules/ws/node_modules/utf-8-validate/binding.gyp b/node_modules/ws/node_modules/utf-8-validate/binding.gyp new file mode 100644 index 0000000..34222db --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/binding.gyp @@ -0,0 +1,11 @@ +{ + 'targets': [ + { + 'target_name': 'validation', + 'include_dirs': ["> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_objc = CXX($(TOOLSET)) $@ +cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< + +quiet_cmd_objcxx = CXX($(TOOLSET)) $@ +cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# Commands for precompiled header files. +quiet_cmd_pch_c = CXX($(TOOLSET)) $@ +cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_cc = CXX($(TOOLSET)) $@ +cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_m = CXX($(TOOLSET)) $@ +cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< +quiet_cmd_pch_mm = CXX($(TOOLSET)) $@ +cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# gyp-mac-tool is written next to the root Makefile by gyp. +# Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd +# already. +quiet_cmd_mac_tool = MACTOOL $(4) $< +cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@" + +quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@ +cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4) + +quiet_cmd_infoplist = INFOPLIST $@ +cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@" + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = rm -rf "$@" && cp -af "$<" "$@" + +quiet_cmd_alink = LIBTOOL-STATIC $@ +cmd_alink = rm -f $@ && ./gyp-mac-tool filter-libtool libtool $(GYP_LIBTOOLFLAGS) -static -o $@ $(filter %.o,$^) + +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -bundle $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# Helper that executes all postbuilds until one fails. +define do_postbuilds + @E=0;\ + for p in $(POSTBUILDS); do\ + eval $$p;\ + E=$$?;\ + if [ $$E -ne 0 ]; then\ + break;\ + fi;\ + done;\ + if [ $$E -ne 0 ]; then\ + rm -rf "$@";\ + exit $$E;\ + fi +endef + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 2,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + $(call do_postbuilds) + ) +) +endef + +# Declare the "all" target first so it is the default, +# even though we don't have the deps yet. +.PHONY: all +all: + +# make looks for ways to re-generate included makefiles, but in our case, we +# don't have a direct way. Explicitly telling make that it has nothing to do +# for them makes it go faster. +%.d: ; + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,validation.target.mk)))),) + include validation.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = cd $(srcdir); /Users/Oracle/.nvm/versions/node/v0.12.7/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "--toplevel-dir=." -I/Users/Oracle/dev/selenium-webdriver-nodejs/node_modules/ws/node_modules/utf-8-validate/build/config.gypi -I/Users/Oracle/.nvm/versions/node/v0.12.7/lib/node_modules/npm/node_modules/node-gyp/addon.gypi -I/Users/Oracle/.node-gyp/0.12.7/common.gypi "--depth=." "-Goutput_dir=." "--generator-output=build" "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/Oracle/.node-gyp/0.12.7" "-Dmodule_root_dir=/Users/Oracle/dev/selenium-webdriver-nodejs/node_modules/ws/node_modules/utf-8-validate" binding.gyp +Makefile: $(srcdir)/../../../../../../.node-gyp/0.12.7/common.gypi $(srcdir)/../../../../../../.nvm/versions/node/v0.12.7/lib/node_modules/npm/node_modules/node-gyp/addon.gypi $(srcdir)/build/config.gypi $(srcdir)/binding.gyp + $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + include $(d_files) +endif diff --git a/node_modules/ws/node_modules/utf-8-validate/build/Release/.deps/Release/obj.target/validation/src/validation.o.d b/node_modules/ws/node_modules/utf-8-validate/build/Release/.deps/Release/obj.target/validation/src/validation.o.d new file mode 100644 index 0000000..7a51d81 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/build/Release/.deps/Release/obj.target/validation/src/validation.o.d @@ -0,0 +1,54 @@ +cmd_Release/obj.target/validation/src/validation.o := c++ '-DNODE_GYP_MODULE_NAME=validation' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DBUILDING_NODE_EXTENSION' -I/Users/Oracle/.node-gyp/0.12.7/src -I/Users/Oracle/.node-gyp/0.12.7/deps/uv/include -I/Users/Oracle/.node-gyp/0.12.7/deps/v8/include -I../node_modules/nan -Os -gdwarf-2 -mmacosx-version-min=10.5 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/validation/src/validation.o.d.raw -c -o Release/obj.target/validation/src/validation.o ../src/validation.cc +Release/obj.target/validation/src/validation.o: ../src/validation.cc \ + /Users/Oracle/.node-gyp/0.12.7/deps/v8/include/v8.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/v8/include/v8stdint.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/v8/include/v8config.h \ + /Users/Oracle/.node-gyp/0.12.7/src/node.h \ + /Users/Oracle/.node-gyp/0.12.7/src/node_version.h \ + /Users/Oracle/.node-gyp/0.12.7/src/node_buffer.h \ + /Users/Oracle/.node-gyp/0.12.7/src/smalloc.h \ + /Users/Oracle/.node-gyp/0.12.7/src/node_object_wrap.h \ + ../node_modules/nan/nan.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-errno.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-version.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-unix.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-threadpool.h \ + /Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-darwin.h \ + ../node_modules/nan/nan_callbacks.h \ + ../node_modules/nan/nan_callbacks_12_inl.h \ + ../node_modules/nan/nan_maybe_pre_43_inl.h \ + ../node_modules/nan/nan_converters.h \ + ../node_modules/nan/nan_converters_pre_43_inl.h \ + ../node_modules/nan/nan_new.h \ + ../node_modules/nan/nan_implementation_12_inl.h \ + ../node_modules/nan/nan_persistent_12_inl.h \ + ../node_modules/nan/nan_weak.h ../node_modules/nan/nan_object_wrap.h \ + ../node_modules/nan/nan_typedarray_contents.h +../src/validation.cc: +/Users/Oracle/.node-gyp/0.12.7/deps/v8/include/v8.h: +/Users/Oracle/.node-gyp/0.12.7/deps/v8/include/v8stdint.h: +/Users/Oracle/.node-gyp/0.12.7/deps/v8/include/v8config.h: +/Users/Oracle/.node-gyp/0.12.7/src/node.h: +/Users/Oracle/.node-gyp/0.12.7/src/node_version.h: +/Users/Oracle/.node-gyp/0.12.7/src/node_buffer.h: +/Users/Oracle/.node-gyp/0.12.7/src/smalloc.h: +/Users/Oracle/.node-gyp/0.12.7/src/node_object_wrap.h: +../node_modules/nan/nan.h: +/Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv.h: +/Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-errno.h: +/Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-version.h: +/Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-unix.h: +/Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-threadpool.h: +/Users/Oracle/.node-gyp/0.12.7/deps/uv/include/uv-darwin.h: +../node_modules/nan/nan_callbacks.h: +../node_modules/nan/nan_callbacks_12_inl.h: +../node_modules/nan/nan_maybe_pre_43_inl.h: +../node_modules/nan/nan_converters.h: +../node_modules/nan/nan_converters_pre_43_inl.h: +../node_modules/nan/nan_new.h: +../node_modules/nan/nan_implementation_12_inl.h: +../node_modules/nan/nan_persistent_12_inl.h: +../node_modules/nan/nan_weak.h: +../node_modules/nan/nan_object_wrap.h: +../node_modules/nan/nan_typedarray_contents.h: diff --git a/node_modules/ws/node_modules/utf-8-validate/build/Release/.deps/Release/validation.node.d b/node_modules/ws/node_modules/utf-8-validate/build/Release/.deps/Release/validation.node.d new file mode 100644 index 0000000..77e9ea2 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/build/Release/.deps/Release/validation.node.d @@ -0,0 +1 @@ +cmd_Release/validation.node := c++ -bundle -undefined dynamic_lookup -Wl,-search_paths_first -mmacosx-version-min=10.5 -arch x86_64 -L./Release -o Release/validation.node Release/obj.target/validation/src/validation.o diff --git a/node_modules/ws/node_modules/utf-8-validate/build/Release/obj.target/validation/src/validation.o b/node_modules/ws/node_modules/utf-8-validate/build/Release/obj.target/validation/src/validation.o new file mode 100644 index 0000000..bb6a649 Binary files /dev/null and b/node_modules/ws/node_modules/utf-8-validate/build/Release/obj.target/validation/src/validation.o differ diff --git a/node_modules/ws/node_modules/utf-8-validate/build/Release/validation.node b/node_modules/ws/node_modules/utf-8-validate/build/Release/validation.node new file mode 100755 index 0000000..fa31319 Binary files /dev/null and b/node_modules/ws/node_modules/utf-8-validate/build/Release/validation.node differ diff --git a/node_modules/ws/node_modules/utf-8-validate/build/binding.Makefile b/node_modules/ws/node_modules/utf-8-validate/build/binding.Makefile new file mode 100644 index 0000000..c7aead9 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/build/binding.Makefile @@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= ./build/. +.PHONY: all +all: + $(MAKE) validation diff --git a/node_modules/ws/node_modules/utf-8-validate/build/config.gypi b/node_modules/ws/node_modules/utf-8-validate/build/config.gypi new file mode 100644 index 0000000..842860a --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/build/config.gypi @@ -0,0 +1,137 @@ +# Do not edit. File was generated by node-gyp's "configure" step +{ + "target_defaults": { + "cflags": [], + "default_configuration": "Release", + "defines": [], + "include_dirs": [], + "libraries": [] + }, + "variables": { + "clang": 1, + "host_arch": "x64", + "icu_data_file": "icudt54l.dat", + "icu_data_in": "../../deps/icu/source/data/in/icudt54l.dat", + "icu_endianness": "l", + "icu_gyp_path": "tools/icu/icu-generic.gyp", + "icu_locales": "en,root", + "icu_path": "./deps/icu", + "icu_small": "true", + "icu_ver_major": "54", + "node_install_npm": "true", + "node_prefix": "/", + "node_shared_cares": "false", + "node_shared_http_parser": "false", + "node_shared_libuv": "false", + "node_shared_openssl": "false", + "node_shared_v8": "false", + "node_shared_zlib": "false", + "node_tag": "", + "node_use_dtrace": "true", + "node_use_etw": "false", + "node_use_mdb": "false", + "node_use_openssl": "true", + "node_use_perfctr": "false", + "openssl_no_asm": 0, + "python": "/usr/bin/python", + "target_arch": "x64", + "uv_library": "static_library", + "uv_parent_path": "/deps/uv/", + "uv_use_dtrace": "true", + "v8_enable_gdbjit": 0, + "v8_enable_i18n_support": 1, + "v8_no_strict_aliasing": 1, + "v8_optimized_debug": 0, + "v8_random_seed": 0, + "v8_use_snapshot": "false", + "want_separate_host_toolset": 0, + "nodedir": "/Users/Oracle/.node-gyp/0.12.7", + "copy_dev_lib": "true", + "standalone_static_library": 1, + "save_dev": "", + "browser": "", + "viewer": "man", + "rollback": "true", + "usage": "", + "globalignorefile": "/Users/Oracle/.nvm/versions/node/v0.12.7/etc/npmignore", + "init_author_url": "", + "shell": "/bin/bash", + "parseable": "", + "shrinkwrap": "true", + "init_license": "ISC", + "if_present": "", + "cache_max": "Infinity", + "init_author_email": "", + "sign_git_tag": "", + "cert": "", + "git_tag_version": "true", + "local_address": "", + "long": "", + "fetch_retries": "2", + "npat": "", + "registry": "https://registry.npmjs.org/", + "key": "", + "message": "%s", + "versions": "", + "globalconfig": "/Users/Oracle/.nvm/versions/node/v0.12.7/etc/npmrc", + "always_auth": "", + "spin": "true", + "cache_lock_retries": "10", + "cafile": "", + "heading": "npm", + "fetch_retry_mintimeout": "10000", + "proprietary_attribs": "true", + "access": "", + "json": "", + "description": "true", + "engine_strict": "", + "https_proxy": "", + "init_module": "/Users/Oracle/.npm-init.js", + "userconfig": "/Users/Oracle/.npmrc", + "user": "akhil", + "node_version": "0.12.7", + "editor": "vi", + "save": "", + "tag": "latest", + "global": "", + "optional": "true", + "bin_links": "true", + "force": "", + "searchopts": "", + "depth": "Infinity", + "rebuild_bundle": "true", + "searchsort": "name", + "unicode": "true", + "fetch_retry_maxtimeout": "60000", + "ca": "", + "save_prefix": "^", + "strict_ssl": "true", + "tag_version_prefix": "v", + "dev": "", + "fetch_retry_factor": "10", + "group": "20", + "save_exact": "", + "cache_lock_stale": "60000", + "version": "", + "cache_min": "10", + "cache": "/Users/Oracle/.npm", + "searchexclude": "", + "color": "true", + "save_optional": "", + "user_agent": "npm/2.11.3 node/v0.12.7 darwin x64", + "ignore_scripts": "", + "cache_lock_wait": "10000", + "production": "", + "save_bundle": "", + "init_version": "1.0.0", + "umask": "0022", + "git": "git", + "init_author_name": "", + "scope": "", + "onload_script": "", + "tmp": "/var/folders/62/d6lvgp5s4hldcbsxbwb67y1w0000gn/T", + "unsafe_perm": "true", + "link": "", + "prefix": "/Users/Oracle/.nvm/versions/node/v0.12.7" + } +} diff --git a/node_modules/ws/node_modules/utf-8-validate/build/gyp-mac-tool b/node_modules/ws/node_modules/utf-8-validate/build/gyp-mac-tool new file mode 100755 index 0000000..976c598 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/build/gyp-mac-tool @@ -0,0 +1,612 @@ +#!/usr/bin/env python +# Generated by gyp. Do not edit. +# Copyright (c) 2012 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Utility functions to perform Xcode-style build steps. + +These functions are executed via gyp-mac-tool when using the Makefile generator. +""" + +import fcntl +import fnmatch +import glob +import json +import os +import plistlib +import re +import shutil +import string +import subprocess +import sys +import tempfile + + +def main(args): + executor = MacTool() + exit_code = executor.Dispatch(args) + if exit_code is not None: + sys.exit(exit_code) + + +class MacTool(object): + """This class performs all the Mac tooling steps. The methods can either be + executed directly, or dispatched from an argument list.""" + + def Dispatch(self, args): + """Dispatches a string command to a method.""" + if len(args) < 1: + raise Exception("Not enough arguments") + + method = "Exec%s" % self._CommandifyName(args[0]) + return getattr(self, method)(*args[1:]) + + def _CommandifyName(self, name_string): + """Transforms a tool name like copy-info-plist to CopyInfoPlist""" + return name_string.title().replace('-', '') + + def ExecCopyBundleResource(self, source, dest, convert_to_binary): + """Copies a resource file to the bundle/Resources directory, performing any + necessary compilation on each resource.""" + extension = os.path.splitext(source)[1].lower() + if os.path.isdir(source): + # Copy tree. + # TODO(thakis): This copies file attributes like mtime, while the + # single-file branch below doesn't. This should probably be changed to + # be consistent with the single-file branch. + if os.path.exists(dest): + shutil.rmtree(dest) + shutil.copytree(source, dest) + elif extension == '.xib': + return self._CopyXIBFile(source, dest) + elif extension == '.storyboard': + return self._CopyXIBFile(source, dest) + elif extension == '.strings': + self._CopyStringsFile(source, dest, convert_to_binary) + else: + shutil.copy(source, dest) + + def _CopyXIBFile(self, source, dest): + """Compiles a XIB file with ibtool into a binary plist in the bundle.""" + + # ibtool sometimes crashes with relative paths. See crbug.com/314728. + base = os.path.dirname(os.path.realpath(__file__)) + if os.path.relpath(source): + source = os.path.join(base, source) + if os.path.relpath(dest): + dest = os.path.join(base, dest) + + args = ['xcrun', 'ibtool', '--errors', '--warnings', '--notices', + '--output-format', 'human-readable-text', '--compile', dest, source] + ibtool_section_re = re.compile(r'/\*.*\*/') + ibtool_re = re.compile(r'.*note:.*is clipping its content') + ibtoolout = subprocess.Popen(args, stdout=subprocess.PIPE) + current_section_header = None + for line in ibtoolout.stdout: + if ibtool_section_re.match(line): + current_section_header = line + elif not ibtool_re.match(line): + if current_section_header: + sys.stdout.write(current_section_header) + current_section_header = None + sys.stdout.write(line) + return ibtoolout.returncode + + def _ConvertToBinary(self, dest): + subprocess.check_call([ + 'xcrun', 'plutil', '-convert', 'binary1', '-o', dest, dest]) + + def _CopyStringsFile(self, source, dest, convert_to_binary): + """Copies a .strings file using iconv to reconvert the input into UTF-16.""" + input_code = self._DetectInputEncoding(source) or "UTF-8" + + # Xcode's CpyCopyStringsFile / builtin-copyStrings seems to call + # CFPropertyListCreateFromXMLData() behind the scenes; at least it prints + # CFPropertyListCreateFromXMLData(): Old-style plist parser: missing + # semicolon in dictionary. + # on invalid files. Do the same kind of validation. + import CoreFoundation + s = open(source, 'rb').read() + d = CoreFoundation.CFDataCreate(None, s, len(s)) + _, error = CoreFoundation.CFPropertyListCreateFromXMLData(None, d, 0, None) + if error: + return + + fp = open(dest, 'wb') + fp.write(s.decode(input_code).encode('UTF-16')) + fp.close() + + if convert_to_binary == 'True': + self._ConvertToBinary(dest) + + def _DetectInputEncoding(self, file_name): + """Reads the first few bytes from file_name and tries to guess the text + encoding. Returns None as a guess if it can't detect it.""" + fp = open(file_name, 'rb') + try: + header = fp.read(3) + except e: + fp.close() + return None + fp.close() + if header.startswith("\xFE\xFF"): + return "UTF-16" + elif header.startswith("\xFF\xFE"): + return "UTF-16" + elif header.startswith("\xEF\xBB\xBF"): + return "UTF-8" + else: + return None + + def ExecCopyInfoPlist(self, source, dest, convert_to_binary, *keys): + """Copies the |source| Info.plist to the destination directory |dest|.""" + # Read the source Info.plist into memory. + fd = open(source, 'r') + lines = fd.read() + fd.close() + + # Insert synthesized key/value pairs (e.g. BuildMachineOSBuild). + plist = plistlib.readPlistFromString(lines) + if keys: + plist = dict(plist.items() + json.loads(keys[0]).items()) + lines = plistlib.writePlistToString(plist) + + # Go through all the environment variables and replace them as variables in + # the file. + IDENT_RE = re.compile(r'[/\s]') + for key in os.environ: + if key.startswith('_'): + continue + evar = '${%s}' % key + evalue = os.environ[key] + lines = string.replace(lines, evar, evalue) + + # Xcode supports various suffices on environment variables, which are + # all undocumented. :rfc1034identifier is used in the standard project + # template these days, and :identifier was used earlier. They are used to + # convert non-url characters into things that look like valid urls -- + # except that the replacement character for :identifier, '_' isn't valid + # in a URL either -- oops, hence :rfc1034identifier was born. + evar = '${%s:identifier}' % key + evalue = IDENT_RE.sub('_', os.environ[key]) + lines = string.replace(lines, evar, evalue) + + evar = '${%s:rfc1034identifier}' % key + evalue = IDENT_RE.sub('-', os.environ[key]) + lines = string.replace(lines, evar, evalue) + + # Remove any keys with values that haven't been replaced. + lines = lines.split('\n') + for i in range(len(lines)): + if lines[i].strip().startswith("${"): + lines[i] = None + lines[i - 1] = None + lines = '\n'.join(filter(lambda x: x is not None, lines)) + + # Write out the file with variables replaced. + fd = open(dest, 'w') + fd.write(lines) + fd.close() + + # Now write out PkgInfo file now that the Info.plist file has been + # "compiled". + self._WritePkgInfo(dest) + + if convert_to_binary == 'True': + self._ConvertToBinary(dest) + + def _WritePkgInfo(self, info_plist): + """This writes the PkgInfo file from the data stored in Info.plist.""" + plist = plistlib.readPlist(info_plist) + if not plist: + return + + # Only create PkgInfo for executable types. + package_type = plist['CFBundlePackageType'] + if package_type != 'APPL': + return + + # The format of PkgInfo is eight characters, representing the bundle type + # and bundle signature, each four characters. If that is missing, four + # '?' characters are used instead. + signature_code = plist.get('CFBundleSignature', '????') + if len(signature_code) != 4: # Wrong length resets everything, too. + signature_code = '?' * 4 + + dest = os.path.join(os.path.dirname(info_plist), 'PkgInfo') + fp = open(dest, 'w') + fp.write('%s%s' % (package_type, signature_code)) + fp.close() + + def ExecFlock(self, lockfile, *cmd_list): + """Emulates the most basic behavior of Linux's flock(1).""" + # Rely on exception handling to report errors. + fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666) + fcntl.flock(fd, fcntl.LOCK_EX) + return subprocess.call(cmd_list) + + def ExecFilterLibtool(self, *cmd_list): + """Calls libtool and filters out '/path/to/libtool: file: foo.o has no + symbols'.""" + libtool_re = re.compile(r'^.*libtool: file: .* has no symbols$') + libtool_re5 = re.compile( + r'^.*libtool: warning for library: ' + + r'.* the table of contents is empty ' + + r'\(no object file members in the library define global symbols\)$') + env = os.environ.copy() + # Ref: + # http://www.opensource.apple.com/source/cctools/cctools-809/misc/libtool.c + # The problem with this flag is that it resets the file mtime on the file to + # epoch=0, e.g. 1970-1-1 or 1969-12-31 depending on timezone. + env['ZERO_AR_DATE'] = '1' + libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env) + _, err = libtoolout.communicate() + for line in err.splitlines(): + if not libtool_re.match(line) and not libtool_re5.match(line): + print >>sys.stderr, line + # Unconditionally touch the output .a file on the command line if present + # and the command succeeded. A bit hacky. + if not libtoolout.returncode: + for i in range(len(cmd_list) - 1): + if cmd_list[i] == "-o" and cmd_list[i+1].endswith('.a'): + os.utime(cmd_list[i+1], None) + break + return libtoolout.returncode + + def ExecPackageFramework(self, framework, version): + """Takes a path to Something.framework and the Current version of that and + sets up all the symlinks.""" + # Find the name of the binary based on the part before the ".framework". + binary = os.path.basename(framework).split('.')[0] + + CURRENT = 'Current' + RESOURCES = 'Resources' + VERSIONS = 'Versions' + + if not os.path.exists(os.path.join(framework, VERSIONS, version, binary)): + # Binary-less frameworks don't seem to contain symlinks (see e.g. + # chromium's out/Debug/org.chromium.Chromium.manifest/ bundle). + return + + # Move into the framework directory to set the symlinks correctly. + pwd = os.getcwd() + os.chdir(framework) + + # Set up the Current version. + self._Relink(version, os.path.join(VERSIONS, CURRENT)) + + # Set up the root symlinks. + self._Relink(os.path.join(VERSIONS, CURRENT, binary), binary) + self._Relink(os.path.join(VERSIONS, CURRENT, RESOURCES), RESOURCES) + + # Back to where we were before! + os.chdir(pwd) + + def _Relink(self, dest, link): + """Creates a symlink to |dest| named |link|. If |link| already exists, + it is overwritten.""" + if os.path.lexists(link): + os.remove(link) + os.symlink(dest, link) + + def ExecCompileXcassets(self, keys, *inputs): + """Compiles multiple .xcassets files into a single .car file. + + This invokes 'actool' to compile all the inputs .xcassets files. The + |keys| arguments is a json-encoded dictionary of extra arguments to + pass to 'actool' when the asset catalogs contains an application icon + or a launch image. + + Note that 'actool' does not create the Assets.car file if the asset + catalogs does not contains imageset. + """ + command_line = [ + 'xcrun', 'actool', '--output-format', 'human-readable-text', + '--compress-pngs', '--notices', '--warnings', '--errors', + ] + is_iphone_target = 'IPHONEOS_DEPLOYMENT_TARGET' in os.environ + if is_iphone_target: + platform = os.environ['CONFIGURATION'].split('-')[-1] + if platform not in ('iphoneos', 'iphonesimulator'): + platform = 'iphonesimulator' + command_line.extend([ + '--platform', platform, '--target-device', 'iphone', + '--target-device', 'ipad', '--minimum-deployment-target', + os.environ['IPHONEOS_DEPLOYMENT_TARGET'], '--compile', + os.path.abspath(os.environ['CONTENTS_FOLDER_PATH']), + ]) + else: + command_line.extend([ + '--platform', 'macosx', '--target-device', 'mac', + '--minimum-deployment-target', os.environ['MACOSX_DEPLOYMENT_TARGET'], + '--compile', + os.path.abspath(os.environ['UNLOCALIZED_RESOURCES_FOLDER_PATH']), + ]) + if keys: + keys = json.loads(keys) + for key, value in keys.iteritems(): + arg_name = '--' + key + if isinstance(value, bool): + if value: + command_line.append(arg_name) + elif isinstance(value, list): + for v in value: + command_line.append(arg_name) + command_line.append(str(v)) + else: + command_line.append(arg_name) + command_line.append(str(value)) + # Note: actool crashes if inputs path are relative, so use os.path.abspath + # to get absolute path name for inputs. + command_line.extend(map(os.path.abspath, inputs)) + subprocess.check_call(command_line) + + def ExecMergeInfoPlist(self, output, *inputs): + """Merge multiple .plist files into a single .plist file.""" + merged_plist = {} + for path in inputs: + plist = self._LoadPlistMaybeBinary(path) + self._MergePlist(merged_plist, plist) + plistlib.writePlist(merged_plist, output) + + def ExecCodeSignBundle(self, key, resource_rules, entitlements, provisioning): + """Code sign a bundle. + + This function tries to code sign an iOS bundle, following the same + algorithm as Xcode: + 1. copy ResourceRules.plist from the user or the SDK into the bundle, + 2. pick the provisioning profile that best match the bundle identifier, + and copy it into the bundle as embedded.mobileprovision, + 3. copy Entitlements.plist from user or SDK next to the bundle, + 4. code sign the bundle. + """ + resource_rules_path = self._InstallResourceRules(resource_rules) + substitutions, overrides = self._InstallProvisioningProfile( + provisioning, self._GetCFBundleIdentifier()) + entitlements_path = self._InstallEntitlements( + entitlements, substitutions, overrides) + subprocess.check_call([ + 'codesign', '--force', '--sign', key, '--resource-rules', + resource_rules_path, '--entitlements', entitlements_path, + os.path.join( + os.environ['TARGET_BUILD_DIR'], + os.environ['FULL_PRODUCT_NAME'])]) + + def _InstallResourceRules(self, resource_rules): + """Installs ResourceRules.plist from user or SDK into the bundle. + + Args: + resource_rules: string, optional, path to the ResourceRules.plist file + to use, default to "${SDKROOT}/ResourceRules.plist" + + Returns: + Path to the copy of ResourceRules.plist into the bundle. + """ + source_path = resource_rules + target_path = os.path.join( + os.environ['BUILT_PRODUCTS_DIR'], + os.environ['CONTENTS_FOLDER_PATH'], + 'ResourceRules.plist') + if not source_path: + source_path = os.path.join( + os.environ['SDKROOT'], 'ResourceRules.plist') + shutil.copy2(source_path, target_path) + return target_path + + def _InstallProvisioningProfile(self, profile, bundle_identifier): + """Installs embedded.mobileprovision into the bundle. + + Args: + profile: string, optional, short name of the .mobileprovision file + to use, if empty or the file is missing, the best file installed + will be used + bundle_identifier: string, value of CFBundleIdentifier from Info.plist + + Returns: + A tuple containing two dictionary: variables substitutions and values + to overrides when generating the entitlements file. + """ + source_path, provisioning_data, team_id = self._FindProvisioningProfile( + profile, bundle_identifier) + target_path = os.path.join( + os.environ['BUILT_PRODUCTS_DIR'], + os.environ['CONTENTS_FOLDER_PATH'], + 'embedded.mobileprovision') + shutil.copy2(source_path, target_path) + substitutions = self._GetSubstitutions(bundle_identifier, team_id + '.') + return substitutions, provisioning_data['Entitlements'] + + def _FindProvisioningProfile(self, profile, bundle_identifier): + """Finds the .mobileprovision file to use for signing the bundle. + + Checks all the installed provisioning profiles (or if the user specified + the PROVISIONING_PROFILE variable, only consult it) and select the most + specific that correspond to the bundle identifier. + + Args: + profile: string, optional, short name of the .mobileprovision file + to use, if empty or the file is missing, the best file installed + will be used + bundle_identifier: string, value of CFBundleIdentifier from Info.plist + + Returns: + A tuple of the path to the selected provisioning profile, the data of + the embedded plist in the provisioning profile and the team identifier + to use for code signing. + + Raises: + SystemExit: if no .mobileprovision can be used to sign the bundle. + """ + profiles_dir = os.path.join( + os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles') + if not os.path.isdir(profiles_dir): + print >>sys.stderr, ( + 'cannot find mobile provisioning for %s' % bundle_identifier) + sys.exit(1) + provisioning_profiles = None + if profile: + profile_path = os.path.join(profiles_dir, profile + '.mobileprovision') + if os.path.exists(profile_path): + provisioning_profiles = [profile_path] + if not provisioning_profiles: + provisioning_profiles = glob.glob( + os.path.join(profiles_dir, '*.mobileprovision')) + valid_provisioning_profiles = {} + for profile_path in provisioning_profiles: + profile_data = self._LoadProvisioningProfile(profile_path) + app_id_pattern = profile_data.get( + 'Entitlements', {}).get('application-identifier', '') + for team_identifier in profile_data.get('TeamIdentifier', []): + app_id = '%s.%s' % (team_identifier, bundle_identifier) + if fnmatch.fnmatch(app_id, app_id_pattern): + valid_provisioning_profiles[app_id_pattern] = ( + profile_path, profile_data, team_identifier) + if not valid_provisioning_profiles: + print >>sys.stderr, ( + 'cannot find mobile provisioning for %s' % bundle_identifier) + sys.exit(1) + # If the user has multiple provisioning profiles installed that can be + # used for ${bundle_identifier}, pick the most specific one (ie. the + # provisioning profile whose pattern is the longest). + selected_key = max(valid_provisioning_profiles, key=lambda v: len(v)) + return valid_provisioning_profiles[selected_key] + + def _LoadProvisioningProfile(self, profile_path): + """Extracts the plist embedded in a provisioning profile. + + Args: + profile_path: string, path to the .mobileprovision file + + Returns: + Content of the plist embedded in the provisioning profile as a dictionary. + """ + with tempfile.NamedTemporaryFile() as temp: + subprocess.check_call([ + 'security', 'cms', '-D', '-i', profile_path, '-o', temp.name]) + return self._LoadPlistMaybeBinary(temp.name) + + def _MergePlist(self, merged_plist, plist): + """Merge |plist| into |merged_plist|.""" + for key, value in plist.iteritems(): + if isinstance(value, dict): + merged_value = merged_plist.get(key, {}) + if isinstance(merged_value, dict): + self._MergePlist(merged_value, value) + merged_plist[key] = merged_value + else: + merged_plist[key] = value + else: + merged_plist[key] = value + + def _LoadPlistMaybeBinary(self, plist_path): + """Loads into a memory a plist possibly encoded in binary format. + + This is a wrapper around plistlib.readPlist that tries to convert the + plist to the XML format if it can't be parsed (assuming that it is in + the binary format). + + Args: + plist_path: string, path to a plist file, in XML or binary format + + Returns: + Content of the plist as a dictionary. + """ + try: + # First, try to read the file using plistlib that only supports XML, + # and if an exception is raised, convert a temporary copy to XML and + # load that copy. + return plistlib.readPlist(plist_path) + except: + pass + with tempfile.NamedTemporaryFile() as temp: + shutil.copy2(plist_path, temp.name) + subprocess.check_call(['plutil', '-convert', 'xml1', temp.name]) + return plistlib.readPlist(temp.name) + + def _GetSubstitutions(self, bundle_identifier, app_identifier_prefix): + """Constructs a dictionary of variable substitutions for Entitlements.plist. + + Args: + bundle_identifier: string, value of CFBundleIdentifier from Info.plist + app_identifier_prefix: string, value for AppIdentifierPrefix + + Returns: + Dictionary of substitutions to apply when generating Entitlements.plist. + """ + return { + 'CFBundleIdentifier': bundle_identifier, + 'AppIdentifierPrefix': app_identifier_prefix, + } + + def _GetCFBundleIdentifier(self): + """Extracts CFBundleIdentifier value from Info.plist in the bundle. + + Returns: + Value of CFBundleIdentifier in the Info.plist located in the bundle. + """ + info_plist_path = os.path.join( + os.environ['TARGET_BUILD_DIR'], + os.environ['INFOPLIST_PATH']) + info_plist_data = self._LoadPlistMaybeBinary(info_plist_path) + return info_plist_data['CFBundleIdentifier'] + + def _InstallEntitlements(self, entitlements, substitutions, overrides): + """Generates and install the ${BundleName}.xcent entitlements file. + + Expands variables "$(variable)" pattern in the source entitlements file, + add extra entitlements defined in the .mobileprovision file and the copy + the generated plist to "${BundlePath}.xcent". + + Args: + entitlements: string, optional, path to the Entitlements.plist template + to use, defaults to "${SDKROOT}/Entitlements.plist" + substitutions: dictionary, variable substitutions + overrides: dictionary, values to add to the entitlements + + Returns: + Path to the generated entitlements file. + """ + source_path = entitlements + target_path = os.path.join( + os.environ['BUILT_PRODUCTS_DIR'], + os.environ['PRODUCT_NAME'] + '.xcent') + if not source_path: + source_path = os.path.join( + os.environ['SDKROOT'], + 'Entitlements.plist') + shutil.copy2(source_path, target_path) + data = self._LoadPlistMaybeBinary(target_path) + data = self._ExpandVariables(data, substitutions) + if overrides: + for key in overrides: + if key not in data: + data[key] = overrides[key] + plistlib.writePlist(data, target_path) + return target_path + + def _ExpandVariables(self, data, substitutions): + """Expands variables "$(variable)" in data. + + Args: + data: object, can be either string, list or dictionary + substitutions: dictionary, variable substitutions to perform + + Returns: + Copy of data where each references to "$(variable)" has been replaced + by the corresponding value found in substitutions, or left intact if + the key was not found. + """ + if isinstance(data, str): + for key, value in substitutions.iteritems(): + data = data.replace('$(%s)' % key, value) + return data + if isinstance(data, list): + return [self._ExpandVariables(v, substitutions) for v in data] + if isinstance(data, dict): + return dict((k, self._ExpandVariables(data[k], + substitutions)) for k in data) + return data + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/node_modules/ws/node_modules/utf-8-validate/build/validation.target.mk b/node_modules/ws/node_modules/utf-8-validate/build/validation.target.mk new file mode 100644 index 0000000..d0392c5 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/build/validation.target.mk @@ -0,0 +1,161 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := validation +DEFS_Debug := \ + '-DNODE_GYP_MODULE_NAME=validation' \ + '-D_DARWIN_USE_64_BIT_INODE=1' \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-DBUILDING_NODE_EXTENSION' \ + '-DDEBUG' \ + '-D_DEBUG' + +# Flags passed to all source files. +CFLAGS_Debug := \ + -O0 \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Debug := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Debug := \ + -fno-rtti \ + -fno-exceptions \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Debug := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Debug := + +INCS_Debug := \ + -I/Users/Oracle/.node-gyp/0.12.7/src \ + -I/Users/Oracle/.node-gyp/0.12.7/deps/uv/include \ + -I/Users/Oracle/.node-gyp/0.12.7/deps/v8/include \ + -I$(srcdir)/node_modules/nan + +DEFS_Release := \ + '-DNODE_GYP_MODULE_NAME=validation' \ + '-D_DARWIN_USE_64_BIT_INODE=1' \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-DBUILDING_NODE_EXTENSION' + +# Flags passed to all source files. +CFLAGS_Release := \ + -Os \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Release := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Release := \ + -fno-rtti \ + -fno-exceptions \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Release := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Release := + +INCS_Release := \ + -I/Users/Oracle/.node-gyp/0.12.7/src \ + -I/Users/Oracle/.node-gyp/0.12.7/deps/uv/include \ + -I/Users/Oracle/.node-gyp/0.12.7/deps/v8/include \ + -I$(srcdir)/node_modules/nan + +OBJS := \ + $(obj).target/$(TARGET)/src/validation.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Debug := \ + -undefined dynamic_lookup \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) + +LIBTOOLFLAGS_Debug := \ + -undefined dynamic_lookup \ + -Wl,-search_paths_first + +LDFLAGS_Release := \ + -undefined dynamic_lookup \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) + +LIBTOOLFLAGS_Release := \ + -undefined dynamic_lookup \ + -Wl,-search_paths_first + +LIBS := + +$(builddir)/validation.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/validation.node: LIBS := $(LIBS) +$(builddir)/validation.node: GYP_LIBTOOLFLAGS := $(LIBTOOLFLAGS_$(BUILDTYPE)) +$(builddir)/validation.node: TOOLSET := $(TOOLSET) +$(builddir)/validation.node: $(OBJS) FORCE_DO_CMD + $(call do_cmd,solink_module) + +all_deps += $(builddir)/validation.node +# Add target alias +.PHONY: validation +validation: $(builddir)/validation.node + +# Short alias for building this executable. +.PHONY: validation.node +validation.node: $(builddir)/validation.node + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/validation.node + diff --git a/node_modules/ws/node_modules/utf-8-validate/fallback.js b/node_modules/ws/node_modules/utf-8-validate/fallback.js new file mode 100644 index 0000000..f929d77 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/fallback.js @@ -0,0 +1,13 @@ +'use strict'; + +/*! + * UTF-8 validate: UTF-8 validation for WebSockets. + * Copyright(c) 2015 Einar Otto Stangvik + * MIT Licensed + */ + +module.exports.Validation = { + isValidUTF8: function(buffer) { + return true; + } +}; diff --git a/node_modules/ws/node_modules/utf-8-validate/index.js b/node_modules/ws/node_modules/utf-8-validate/index.js new file mode 100644 index 0000000..e7bfde8 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/index.js @@ -0,0 +1,7 @@ +'use strict'; + +try { + module.exports = require('bindings')('validation'); +} catch (e) { + module.exports = require('./fallback'); +} diff --git a/node_modules/ws/node_modules/utf-8-validate/node_modules/bindings/README.md b/node_modules/ws/node_modules/utf-8-validate/node_modules/bindings/README.md new file mode 100644 index 0000000..585cf51 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/node_modules/bindings/README.md @@ -0,0 +1,97 @@ +node-bindings +============= +### Helper module for loading your native module's .node file + +This is a helper module for authors of Node.js native addon modules. +It is basically the "swiss army knife" of `require()`ing your native module's +`.node` file. + +Throughout the course of Node's native addon history, addons have ended up being +compiled in a variety of different places, depending on which build tool and which +version of node was used. To make matters worse, now the _gyp_ build tool can +produce either a _Release_ or _Debug_ build, each being built into different +locations. + +This module checks _all_ the possible locations that a native addon would be built +at, and returns the first one that loads successfully. + + +Installation +------------ + +Install with `npm`: + +``` bash +$ npm install bindings +``` + +Or add it to the `"dependencies"` section of your _package.json_ file. + + +Example +------- + +`require()`ing the proper bindings file for the current node version, platform +and architecture is as simple as: + +``` js +var bindings = require('bindings')('binding.node') + +// Use your bindings defined in your C files +bindings.your_c_function() +``` + + +Nice Error Output +----------------- + +When the `.node` file could not be loaded, `node-bindings` throws an Error with +a nice error message telling you exactly what was tried. You can also check the +`err.tries` Array property. + +``` +Error: Could not load the bindings file. Tried: + → /Users/nrajlich/ref/build/binding.node + → /Users/nrajlich/ref/build/Debug/binding.node + → /Users/nrajlich/ref/build/Release/binding.node + → /Users/nrajlich/ref/out/Debug/binding.node + → /Users/nrajlich/ref/Debug/binding.node + → /Users/nrajlich/ref/out/Release/binding.node + → /Users/nrajlich/ref/Release/binding.node + → /Users/nrajlich/ref/build/default/binding.node + → /Users/nrajlich/ref/compiled/0.8.2/darwin/x64/binding.node + at bindings (/Users/nrajlich/ref/node_modules/bindings/bindings.js:84:13) + at Object. (/Users/nrajlich/ref/lib/ref.js:5:47) + at Module._compile (module.js:449:26) + at Object.Module._extensions..js (module.js:467:10) + at Module.load (module.js:356:32) + at Function.Module._load (module.js:312:12) + ... +``` + + +License +------- + +(The MIT License) + +Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/ws/node_modules/utf-8-validate/node_modules/bindings/bindings.js b/node_modules/ws/node_modules/utf-8-validate/node_modules/bindings/bindings.js new file mode 100644 index 0000000..93dcf85 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/node_modules/bindings/bindings.js @@ -0,0 +1,166 @@ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , path = require('path') + , join = path.join + , dirname = path.dirname + , exists = fs.existsSync || path.existsSync + , defaults = { + arrow: process.env.NODE_BINDINGS_ARROW || ' → ' + , compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled' + , platform: process.platform + , arch: process.arch + , version: process.versions.node + , bindings: 'bindings.node' + , try: [ + // node-gyp's linked version in the "build" dir + [ 'module_root', 'build', 'bindings' ] + // node-waf and gyp_addon (a.k.a node-gyp) + , [ 'module_root', 'build', 'Debug', 'bindings' ] + , [ 'module_root', 'build', 'Release', 'bindings' ] + // Debug files, for development (legacy behavior, remove for node v0.9) + , [ 'module_root', 'out', 'Debug', 'bindings' ] + , [ 'module_root', 'Debug', 'bindings' ] + // Release files, but manually compiled (legacy behavior, remove for node v0.9) + , [ 'module_root', 'out', 'Release', 'bindings' ] + , [ 'module_root', 'Release', 'bindings' ] + // Legacy from node-waf, node <= 0.4.x + , [ 'module_root', 'build', 'default', 'bindings' ] + // Production "Release" buildtype binary (meh...) + , [ 'module_root', 'compiled', 'version', 'platform', 'arch', 'bindings' ] + ] + } + +/** + * The main `bindings()` function loads the compiled bindings for a given module. + * It uses V8's Error API to determine the parent filename that this function is + * being invoked from, which is then used to find the root directory. + */ + +function bindings (opts) { + + // Argument surgery + if (typeof opts == 'string') { + opts = { bindings: opts } + } else if (!opts) { + opts = {} + } + opts.__proto__ = defaults + + // Get the module root + if (!opts.module_root) { + opts.module_root = exports.getRoot(exports.getFileName()) + } + + // Ensure the given bindings name ends with .node + if (path.extname(opts.bindings) != '.node') { + opts.bindings += '.node' + } + + var tries = [] + , i = 0 + , l = opts.try.length + , n + , b + , err + + for (; i=1.2.0 <1.3.0", + "_npmVersion": "1.4.14", + "_npmUser": { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + }, + "maintainers": [ + { + "name": "TooTallNate", + "email": "nathan@tootallnate.net" + }, + { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + } + ], + "dist": { + "shasum": "14ad6113812d2d37d72e67b4cacb4bb726505f11", + "tarball": "http://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/.dntrc b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/.dntrc new file mode 100644 index 0000000..47971da --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/.dntrc @@ -0,0 +1,30 @@ +## DNT config file +## see https://github.com/rvagg/dnt + +NODE_VERSIONS="\ + master \ + v0.11.13 \ + v0.10.30 \ + v0.10.29 \ + v0.10.28 \ + v0.10.26 \ + v0.10.25 \ + v0.10.24 \ + v0.10.23 \ + v0.10.22 \ + v0.10.21 \ + v0.10.20 \ + v0.10.19 \ + v0.8.28 \ + v0.8.27 \ + v0.8.26 \ + v0.8.24 \ +" +OUTPUT_PREFIX="nan-" +TEST_CMD=" \ + cd /dnt/ && \ + npm install && \ + node_modules/.bin/node-gyp --nodedir /usr/src/node/ rebuild --directory test && \ + node_modules/.bin/tap --gc test/js/*-test.js \ +" + diff --git a/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/CHANGELOG.md b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/CHANGELOG.md new file mode 100644 index 0000000..8d3c618 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/CHANGELOG.md @@ -0,0 +1,384 @@ +# NAN ChangeLog + +**Version 2.1.0: current Node 4.1.2, Node 12: 0.12.7, Node 10: 0.10.40, iojs: 3.3.1** + +### 2.1.0 Oct 8 2015 + + - Deprecation: Deprecate NanErrnoException in favor of ErrnoException 0af1ca4cf8b3f0f65ed31bc63a663ab3319da55c + - Feature: added helper class for accessing contents of typedarrays 17b51294c801e534479d5463697a73462d0ca555 + - Feature: [Maybe types] Add MakeMaybe(...) 48d7b53d9702b0c7a060e69ea10fea8fb48d814d + - Feature: new: allow utf16 string with length 66ac6e65c8ab9394ef588adfc59131b3b9d8347b + - Feature: Introduce SetCallHandler and SetCallAsFunctionHandler 7764a9a115d60ba10dc24d86feb0fbc9b4f75537 + - Bugfix: Enable creating Locals from Globals under Node 0.10. 9bf9b8b190821af889790fdc18ace57257e4f9ff + - Bugfix: Fix issue #462 where PropertyCallbackInfo data is not stored safely. 55f50adedd543098526c7b9f4fffd607d3f9861f + +### 2.0.9 Sep 8 2015 + + - Bugfix: EscapableHandleScope in Nan::NewBuffer for Node 0.8 and 0.10 b1654d7 + +### 2.0.8 Aug 28 2015 + + - Work around duplicate linking bug in clang 11902da + +### 2.0.7 Aug 26 2015 + + - Build: Repackage + +### 2.0.6 Aug 26 2015 + + - Bugfix: Properly handle null callback in FunctionTemplate factory 6e99cb1 + - Bugfix: Remove unused static std::map instances 525bddc + - Bugfix: Make better use of maybe versions of APIs bfba85b + - Bugfix: Fix shadowing issues with handle in ObjectWrap 0a9072d + +### 2.0.5 Aug 10 2015 + + - Bugfix: Reimplement weak callback in ObjectWrap 98d38c1 + - Bugfix: Make sure callback classes are not assignable, copyable or movable 81f9b1d + +### 2.0.4 Aug 6 2015 + + - Build: Repackage + +### 2.0.3 Aug 6 2015 + + - Bugfix: Don't use clang++ / g++ syntax extension. 231450e + +### 2.0.2 Aug 6 2015 + + - Build: Repackage + +### 2.0.1 Aug 6 2015 + + - Bugfix: Add workaround for missing REPLACE_INVALID_UTF8 60d6687 + - Bugfix: Reimplement ObjectWrap from scratch to prevent memory leaks 6484601 + - Bugfix: Fix Persistent leak in FunctionCallbackInfo and PropertyCallbackInfo 641ef5f + - Bugfix: Add missing overload for Nan::NewInstance that takes argc/argv 29450ed + +### 2.0.0 Jul 31 2015 + + - Change: Renamed identifiers with leading underscores b5932b4 + - Change: Replaced NanObjectWrapHandle with class NanObjectWrap 464f1e1 + - Change: Replace NanScope and NanEscpableScope macros with classes 47751c4 + - Change: Rename NanNewBufferHandle to NanNewBuffer 6745f99 + - Change: Rename NanBufferUse to NanNewBuffer 3e8b0a5 + - Change: Rename NanNewBuffer to NanCopyBuffer d6af78d + - Change: Remove Nan prefix from all names 72d1f67 + - Change: Update Buffer API for new upstream changes d5d3291 + - Change: Rename Scope and EscapableScope to HandleScope and EscapableHandleScope 21a7a6a + - Change: Get rid of Handles e6c0daf + - Feature: Support io.js 3 with V8 4.4 + - Feature: Introduce NanPersistent 7fed696 + - Feature: Introduce NanGlobal 4408da1 + - Feature: Added NanTryCatch 10f1ca4 + - Feature: Update for V8 v4.3 4b6404a + - Feature: Introduce NanNewOneByteString c543d32 + - Feature: Introduce namespace Nan 67ed1b1 + - Removal: Remove NanLocker and NanUnlocker dd6e401 + - Removal: Remove string converters, except NanUtf8String, which now follows the node implementation b5d00a9 + - Removal: Remove NanReturn* macros d90a25c + - Removal: Remove HasInstance e8f84fe + + +### 1.9.0 Jul 31 2015 + + - Feature: Added `NanFatalException` 81d4a2c + - Feature: Added more error types 4265f06 + - Feature: Added dereference and function call operators to NanCallback c4b2ed0 + - Feature: Added indexed GetFromPersistent and SaveToPersistent edd510c + - Feature: Added more overloads of SaveToPersistent and GetFromPersistent 8b1cef6 + - Feature: Added NanErrnoException dd87d9e + - Correctness: Prevent assign, copy, and move for classes that do not support it 1f55c59, 4b808cb, c96d9b2, fba4a29, 3357130 + - Deprecation: Deprecate `NanGetPointerSafe` and `NanSetPointerSafe` 81d4a2c + - Deprecation: Deprecate `NanBooleanOptionValue` and `NanUInt32OptionValue` 0ad254b + +### 1.8.4 Apr 26 2015 + + - Build: Repackage + +### 1.8.3 Apr 26 2015 + + - Bugfix: Include missing header 1af8648 + +### 1.8.2 Apr 23 2015 + + - Build: Repackage + +### 1.8.1 Apr 23 2015 + + - Bugfix: NanObjectWrapHandle should take a pointer 155f1d3 + +### 1.8.0 Apr 23 2015 + + - Feature: Allow primitives with NanReturnValue 2e4475e + - Feature: Added comparison operators to NanCallback 55b075e + - Feature: Backport thread local storage 15bb7fa + - Removal: Remove support for signatures with arguments 8a2069d + - Correcteness: Replaced NanObjectWrapHandle macro with function 0bc6d59 + +### 1.7.0 Feb 28 2015 + + - Feature: Made NanCallback::Call accept optional target 8d54da7 + - Feature: Support atom-shell 0.21 0b7f1bb + +### 1.6.2 Feb 6 2015 + + - Bugfix: NanEncode: fix argument type for node::Encode on io.js 2be8639 + +### 1.6.1 Jan 23 2015 + + - Build: version bump + +### 1.5.3 Jan 23 2015 + + - Build: repackage + +### 1.6.0 Jan 23 2015 + + - Deprecated `NanNewContextHandle` in favor of `NanNew` 49259af + - Support utility functions moved in newer v8 versions (Node 0.11.15, io.js 1.0) a0aa179 + - Added `NanEncode`, `NanDecodeBytes` and `NanDecodeWrite` 75e6fb9 + +### 1.5.2 Jan 23 2015 + + - Bugfix: Fix non-inline definition build error with clang++ 21d96a1, 60fadd4 + - Bugfix: Readded missing String constructors 18d828f + - Bugfix: Add overload handling NanNew(..) 5ef813b + - Bugfix: Fix uv_work_cb versioning 997e4ae + - Bugfix: Add function factory and test 4eca89c + - Bugfix: Add object template factory and test cdcb951 + - Correctness: Lifted an io.js related typedef c9490be + - Correctness: Make explicit downcasts of String lengths 00074e6 + - Windows: Limit the scope of disabled warning C4530 83d7deb + +### 1.5.1 Jan 15 2015 + + - Build: version bump + +### 1.4.3 Jan 15 2015 + + - Build: version bump + +### 1.4.2 Jan 15 2015 + + - Feature: Support io.js 0dbc5e8 + +### 1.5.0 Jan 14 2015 + + - Feature: Support io.js b003843 + - Correctness: Improved NanNew internals 9cd4f6a + - Feature: Implement progress to NanAsyncWorker 8d6a160 + +### 1.4.1 Nov 8 2014 + + - Bugfix: Handle DEBUG definition correctly + - Bugfix: Accept int as Boolean + +### 1.4.0 Nov 1 2014 + + - Feature: Added NAN_GC_CALLBACK 6a5c245 + - Performance: Removed unnecessary local handle creation 18a7243, 41fe2f8 + - Correctness: Added constness to references in NanHasInstance 02c61cd + - Warnings: Fixed spurious warnings from -Wundef and -Wshadow, 541b122, 99d8cb6 + - Windoze: Shut Visual Studio up when compiling 8d558c1 + - License: Switch to plain MIT from custom hacked MIT license 11de983 + - Build: Added test target to Makefile e232e46 + - Performance: Removed superfluous scope in NanAsyncWorker f4b7821 + - Sugar/Feature: Added NanReturnThis() and NanReturnHolder() shorthands 237a5ff, d697208 + - Feature: Added suitable overload of NanNew for v8::Integer::NewFromUnsigned b27b450 + +### 1.3.0 Aug 2 2014 + + - Added NanNew(std::string) + - Added NanNew(std::string&) + - Added NanAsciiString helper class + - Added NanUtf8String helper class + - Added NanUcs2String helper class + - Deprecated NanRawString() + - Deprecated NanCString() + - Added NanGetIsolateData(v8::Isolate *isolate) + - Added NanMakeCallback(v8::Handle target, v8::Handle func, int argc, v8::Handle* argv) + - Added NanMakeCallback(v8::Handle target, v8::Handle symbol, int argc, v8::Handle* argv) + - Added NanMakeCallback(v8::Handle target, const char* method, int argc, v8::Handle* argv) + - Added NanSetTemplate(v8::Handle templ, v8::Handle name , v8::Handle value, v8::PropertyAttribute attributes) + - Added NanSetPrototypeTemplate(v8::Local templ, v8::Handle name, v8::Handle value, v8::PropertyAttribute attributes) + - Added NanSetInstanceTemplate(v8::Local templ, const char *name, v8::Handle value) + - Added NanSetInstanceTemplate(v8::Local templ, v8::Handle name, v8::Handle value, v8::PropertyAttribute attributes) + +### 1.2.0 Jun 5 2014 + + - Add NanSetPrototypeTemplate + - Changed NAN_WEAK_CALLBACK internals, switched _NanWeakCallbackData to class, + introduced _NanWeakCallbackDispatcher + - Removed -Wno-unused-local-typedefs from test builds + - Made test builds Windows compatible ('Sleep()') + +### 1.1.2 May 28 2014 + + - Release to fix more stuff-ups in 1.1.1 + +### 1.1.1 May 28 2014 + + - Release to fix version mismatch in nan.h and lack of changelog entry for 1.1.0 + +### 1.1.0 May 25 2014 + + - Remove nan_isolate, use v8::Isolate::GetCurrent() internally instead + - Additional explicit overloads for NanNew(): (char*,int), (uint8_t*[,int]), + (uint16_t*[,int), double, int, unsigned int, bool, v8::String::ExternalStringResource*, + v8::String::ExternalAsciiStringResource* + - Deprecate NanSymbol() + - Added SetErrorMessage() and ErrorMessage() to NanAsyncWorker + +### 1.0.0 May 4 2014 + + - Heavy API changes for V8 3.25 / Node 0.11.13 + - Use cpplint.py + - Removed NanInitPersistent + - Removed NanPersistentToLocal + - Removed NanFromV8String + - Removed NanMakeWeak + - Removed NanNewLocal + - Removed NAN_WEAK_CALLBACK_OBJECT + - Removed NAN_WEAK_CALLBACK_DATA + - Introduce NanNew, replaces NanNewLocal, NanPersistentToLocal, adds many overloaded typed versions + - Introduce NanUndefined, NanNull, NanTrue and NanFalse + - Introduce NanEscapableScope and NanEscapeScope + - Introduce NanMakeWeakPersistent (requires a special callback to work on both old and new node) + - Introduce NanMakeCallback for node::MakeCallback + - Introduce NanSetTemplate + - Introduce NanGetCurrentContext + - Introduce NanCompileScript and NanRunScript + - Introduce NanAdjustExternalMemory + - Introduce NanAddGCEpilogueCallback, NanAddGCPrologueCallback, NanRemoveGCEpilogueCallback, NanRemoveGCPrologueCallback + - Introduce NanGetHeapStatistics + - Rename NanAsyncWorker#SavePersistent() to SaveToPersistent() + +### 0.8.0 Jan 9 2014 + + - NanDispose -> NanDisposePersistent, deprecate NanDispose + - Extract _NAN_*_RETURN_TYPE, pull up NAN_*() + +### 0.7.1 Jan 9 2014 + + - Fixes to work against debug builds of Node + - Safer NanPersistentToLocal (avoid reinterpret_cast) + - Speed up common NanRawString case by only extracting flattened string when necessary + +### 0.7.0 Dec 17 2013 + + - New no-arg form of NanCallback() constructor. + - NanCallback#Call takes Handle rather than Local + - Removed deprecated NanCallback#Run method, use NanCallback#Call instead + - Split off _NAN_*_ARGS_TYPE from _NAN_*_ARGS + - Restore (unofficial) Node 0.6 compatibility at NanCallback#Call() + - Introduce NanRawString() for char* (or appropriate void*) from v8::String + (replacement for NanFromV8String) + - Introduce NanCString() for null-terminated char* from v8::String + +### 0.6.0 Nov 21 2013 + + - Introduce NanNewLocal(v8::Handle value) for use in place of + v8::Local::New(...) since v8 started requiring isolate in Node 0.11.9 + +### 0.5.2 Nov 16 2013 + + - Convert SavePersistent and GetFromPersistent in NanAsyncWorker from protected and public + +### 0.5.1 Nov 12 2013 + + - Use node::MakeCallback() instead of direct v8::Function::Call() + +### 0.5.0 Nov 11 2013 + + - Added @TooTallNate as collaborator + - New, much simpler, "include_dirs" for binding.gyp + - Added full range of NAN_INDEX_* macros to match NAN_PROPERTY_* macros + +### 0.4.4 Nov 2 2013 + + - Isolate argument from v8::Persistent::MakeWeak removed for 0.11.8+ + +### 0.4.3 Nov 2 2013 + + - Include node_object_wrap.h, removed from node.h for Node 0.11.8. + +### 0.4.2 Nov 2 2013 + + - Handle deprecation of v8::Persistent::Dispose(v8::Isolate* isolate)) for + Node 0.11.8 release. + +### 0.4.1 Sep 16 2013 + + - Added explicit `#include ` as it was removed from node.h for v0.11.8 + +### 0.4.0 Sep 2 2013 + + - Added NAN_INLINE and NAN_DEPRECATED and made use of them + - Added NanError, NanTypeError and NanRangeError + - Cleaned up code + +### 0.3.2 Aug 30 2013 + + - Fix missing scope declaration in GetFromPersistent() and SaveToPersistent + in NanAsyncWorker + +### 0.3.1 Aug 20 2013 + + - fix "not all control paths return a value" compile warning on some platforms + +### 0.3.0 Aug 19 2013 + + - Made NAN work with NPM + - Lots of fixes to NanFromV8String, pulling in features from new Node core + - Changed node::encoding to Nan::Encoding in NanFromV8String to unify the API + - Added optional error number argument for NanThrowError() + - Added NanInitPersistent() + - Added NanReturnNull() and NanReturnEmptyString() + - Added NanLocker and NanUnlocker + - Added missing scopes + - Made sure to clear disposed Persistent handles + - Changed NanAsyncWorker to allocate error messages on the heap + - Changed NanThrowError(Local) to NanThrowError(Handle) + - Fixed leak in NanAsyncWorker when errmsg is used + +### 0.2.2 Aug 5 2013 + + - Fixed usage of undefined variable with node::BASE64 in NanFromV8String() + +### 0.2.1 Aug 5 2013 + + - Fixed 0.8 breakage, node::BUFFER encoding type not available in 0.8 for + NanFromV8String() + +### 0.2.0 Aug 5 2013 + + - Added NAN_PROPERTY_GETTER, NAN_PROPERTY_SETTER, NAN_PROPERTY_ENUMERATOR, + NAN_PROPERTY_DELETER, NAN_PROPERTY_QUERY + - Extracted _NAN_METHOD_ARGS, _NAN_GETTER_ARGS, _NAN_SETTER_ARGS, + _NAN_PROPERTY_GETTER_ARGS, _NAN_PROPERTY_SETTER_ARGS, + _NAN_PROPERTY_ENUMERATOR_ARGS, _NAN_PROPERTY_DELETER_ARGS, + _NAN_PROPERTY_QUERY_ARGS + - Added NanGetInternalFieldPointer, NanSetInternalFieldPointer + - Added NAN_WEAK_CALLBACK, NAN_WEAK_CALLBACK_OBJECT, + NAN_WEAK_CALLBACK_DATA, NanMakeWeak + - Renamed THROW_ERROR to _NAN_THROW_ERROR + - Added NanNewBufferHandle(char*, size_t, node::smalloc::FreeCallback, void*) + - Added NanBufferUse(char*, uint32_t) + - Added NanNewContextHandle(v8::ExtensionConfiguration*, + v8::Handle, v8::Handle) + - Fixed broken NanCallback#GetFunction() + - Added optional encoding and size arguments to NanFromV8String() + - Added NanGetPointerSafe() and NanSetPointerSafe() + - Added initial test suite (to be expanded) + - Allow NanUInt32OptionValue to convert any Number object + +### 0.1.0 Jul 21 2013 + + - Added `NAN_GETTER`, `NAN_SETTER` + - Added `NanThrowError` with single Local argument + - Added `NanNewBufferHandle` with single uint32_t argument + - Added `NanHasInstance(Persistent&, Handle)` + - Added `Local NanCallback#GetFunction()` + - Added `NanCallback#Call(int, Local[])` + - Deprecated `NanCallback#Run(int, Local[])` in favour of Call diff --git a/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/LICENSE.md b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/LICENSE.md new file mode 100644 index 0000000..77666cd --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/LICENSE.md @@ -0,0 +1,13 @@ +The MIT License (MIT) +===================== + +Copyright (c) 2015 NAN contributors +----------------------------------- + +*NAN contributors listed at * + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/README.md b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/README.md new file mode 100644 index 0000000..7167ae3 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/README.md @@ -0,0 +1,371 @@ +Native Abstractions for Node.js +=============================== + +**A header file filled with macro and utility goodness for making add-on development for Node.js easier across versions 0.8, 0.10, 0.12 and 4.** + +***Current version: 2.1.0*** + +*(See [CHANGELOG.md](https://github.com/nodejs/nan/blob/master/CHANGELOG.md) for complete ChangeLog)* + +[![NPM](https://nodei.co/npm/nan.png?downloads=true&downloadRank=true)](https://nodei.co/npm/nan/) [![NPM](https://nodei.co/npm-dl/nan.png?months=6&height=3)](https://nodei.co/npm/nan/) + +[![Build Status](https://api.travis-ci.org/nodejs/nan.svg?branch=master)](http://travis-ci.org/nodejs/nan) +[![Build status](https://ci.appveyor.com/api/projects/status/kh73pbm9dsju7fgh)](https://ci.appveyor.com/project/RodVagg/nan) + +Thanks to the crazy changes in V8 (and some in Node core), keeping native addons compiling happily across versions, particularly 0.10 to 0.12 to 4.0, is a minor nightmare. The goal of this project is to store all logic necessary to develop native Node.js addons without having to inspect `NODE_MODULE_VERSION` and get yourself into a macro-tangle. + +This project also contains some helper utilities that make addon development a bit more pleasant. + + * **[News & Updates](#news)** + * **[Usage](#usage)** + * **[Example](#example)** + * **[API](#api)** + * **[Tests](#tests)** + * **[Governance & Contributing](#governance)** + + +## News & Updates + + +## Usage + +Simply add **NAN** as a dependency in the *package.json* of your Node addon: + +``` bash +$ npm install --save nan +``` + +Pull in the path to **NAN** in your *binding.gyp* so that you can use `#include ` in your *.cpp* files: + +``` python +"include_dirs" : [ + "` when compiling your addon. + + +## Example + +Just getting started with Nan? Refer to a [quick-start **Nan** Boilerplate](https://github.com/fcanas/node-native-boilerplate) for a ready-to-go project that utilizes basic Nan functionality. + +For a simpler example, see the **[async pi estimation example](https://github.com/nodejs/nan/tree/master/examples/async_pi_estimate)** in the examples directory for full code and an explanation of what this Monte Carlo Pi estimation example does. Below are just some parts of the full example that illustrate the use of **NAN**. + +For another example, see **[nan-example-eol](https://github.com/CodeCharmLtd/nan-example-eol)**. It shows newline detection implemented as a native addon. + + +## API + +Additional to the NAN documentation below, please consult: + +* [The V8 Getting Started Guide](https://developers.google.com/v8/get_started) +* [The V8 Embedders Guide](https://developers.google.com/v8/embed) +* [V8 API Documentation](http://v8docs.nodesource.com/) + + + +### JavaScript-accessible methods + +A _template_ is a blueprint for JavaScript functions and objects in a context. You can use a template to wrap C++ functions and data structures within JavaScript objects so that they can be manipulated from JavaScript. See the V8 Embedders Guide section on [Templates](https://developers.google.com/v8/embed#templates) for further information. + +In order to expose functionality to JavaScript via a template, you must provide it to V8 in a form that it understands. Across the versions of V8 supported by NAN, JavaScript-accessible method signatures vary widely, NAN fully abstracts method declaration and provides you with an interface that is similar to the most recent V8 API but is backward-compatible with older versions that still use the now-deceased `v8::Argument` type. + +* **Method argument types** + - Nan::FunctionCallbackInfo + - Nan::PropertyCallbackInfo + - Nan::ReturnValue +* **Method declarations** + - Method declaration + - Getter declaration + - Setter declaration + - Property getter declaration + - Property setter declaration + - Property enumerator declaration + - Property deleter declaration + - Property query declaration + - Index getter declaration + - Index setter declaration + - Index enumerator declaration + - Index deleter declaration + - Index query declaration +* Method and template helpers + - Nan::SetMethod() + - Nan::SetNamedPropertyHandler() + - Nan::SetIndexedPropertyHandler() + - Nan::SetPrototypeMethod() + - Nan::SetTemplate() + - Nan::SetPrototypeTemplate() + - Nan::SetInstanceTemplate() + - Nan::SetCallHandler() + - Nan::SetCallAsFunctionHandler() + +### Scopes + +A _local handle_ is a pointer to an object. All V8 objects are accessed using handles, they are necessary because of the way the V8 garbage collector works. + +A handle scope can be thought of as a container for any number of handles. When you've finished with your handles, instead of deleting each one individually you can simply delete their scope. + +The creation of `HandleScope` objects is different across the supported versions of V8. Therefore, NAN provides its own implementations that can be used safely across these. + + - Nan::HandleScope + - Nan::EscapableHandleScope + +Also see the V8 Embedders Guide section on [Handles and Garbage Collection](https://developers.google.com/v8/embed#handles). + +### Persistent references + +An object reference that is independent of any `HandleScope` is a _persistent_ reference. Where a `Local` handle only lives as long as the `HandleScope` in which it was allocated, a `Persistent` handle remains valid until it is explicitly disposed. + +Due to the evolution of the V8 API, it is necessary for NAN to provide a wrapper implementation of the `Persistent` classes to supply compatibility across the V8 versions supported. + + - Nan::PersistentBase & v8::PersistentBase + - Nan::NonCopyablePersistentTraits & v8::NonCopyablePersistentTraits + - Nan::CopyablePersistentTraits & v8::CopyablePersistentTraits + - Nan::Persistent + - Nan::Global + - Nan::WeakCallbackInfo + - Nan::WeakCallbackType + +Also see the V8 Embedders Guide section on [Handles and Garbage Collection](https://developers.google.com/v8/embed#handles). + +### New + +NAN provides a `Nan::New()` helper for the creation of new JavaScript objects in a way that's compatible across the supported versions of V8. + + - Nan::New() + - Nan::Undefined() + - Nan::Null() + - Nan::True() + - Nan::False() + - Nan::EmptyString() + + +### Converters + +NAN contains functions that convert `v8::Value`s to other `v8::Value` types and native types. Since type conversion is not guaranteed to succeed, they return `Nan::Maybe` types. These converters can be used in place of `value->ToX()` and `value->XValue()` (where `X` is one of the types, e.g. `Boolean`) in a way that provides a consistent interface across V8 versions. Newer versions of V8 use the new `v8::Maybe` and `v8::MaybeLocal` types for these conversions, older versions don't have this functionality so it is provided by NAN. + + - Nan::To() + +### Maybe Types + +The `Nan::MaybeLocal` and `Nan::Maybe` types are monads that encapsulate `v8::Local` handles that _may be empty_. + +* **Maybe Types** + - Nan::MaybeLocal + - Nan::Maybe + - Nan::Nothing + - Nan::Just +* **Maybe Helpers** + - Nan::ToDetailString() + - Nan::ToArrayIndex() + - Nan::Equals() + - Nan::NewInstance() + - Nan::GetFunction() + - Nan::Set() + - Nan::ForceSet() + - Nan::Get() + - Nan::GetPropertyAttributes() + - Nan::Has() + - Nan::Delete() + - Nan::GetPropertyNames() + - Nan::GetOwnPropertyNames() + - Nan::SetPrototype() + - Nan::ObjectProtoToString() + - Nan::HasOwnProperty() + - Nan::HasRealNamedProperty() + - Nan::HasRealIndexedProperty() + - Nan::HasRealNamedCallbackProperty() + - Nan::GetRealNamedPropertyInPrototypeChain() + - Nan::GetRealNamedProperty() + - Nan::CallAsFunction() + - Nan::CallAsConstructor() + - Nan::GetSourceLine() + - Nan::GetLineNumber() + - Nan::GetStartColumn() + - Nan::GetEndColumn() + - Nan::CloneElementAt() + - Nan::MakeMaybe() + +### Script + +NAN provides a `v8::Script` helpers as the API has changed over the supported versions of V8. + + - Nan::CompileScript() + - Nan::RunScript() + + +### Errors + +NAN includes helpers for creating, throwing and catching Errors as much of this functionality varies across the supported versions of V8 and must be abstracted. + +Note that an Error object is simply a specialized form of `v8::Value`. + +Also consult the V8 Embedders Guide section on [Exceptions](https://developers.google.com/v8/embed#exceptions) for more information. + + - Nan::Error() + - Nan::RangeError() + - Nan::ReferenceError() + - Nan::SyntaxError() + - Nan::TypeError() + - Nan::ThrowError() + - Nan::ThrowRangeError() + - Nan::ThrowReferenceError() + - Nan::ThrowSyntaxError() + - Nan::ThrowTypeError() + - Nan::FatalException() + - Nan::ErrnoException() + - Nan::TryCatch + + +### Buffers + +NAN's `node::Buffer` helpers exist as the API has changed across supported Node versions. Use these methods to ensure compatibility. + + - Nan::NewBuffer() + - Nan::CopyBuffer() + - Nan::FreeCallback() + +### Nan::Callback + +`Nan::Callback` makes it easier to use `v8::Function` handles as callbacks. A class that wraps a `v8::Function` handle, protecting it from garbage collection and making it particularly useful for storage and use across asynchronous execution. + + - Nan::Callback + +### Asynchronous work helpers + +`Nan::AsyncWorker` and `Nan::AsyncProgressWorker` are helper classes that make working with asynchronous code easier. + + - Nan::AsyncWorker + - Nan::AsyncProgressWorker + - Nan::AsyncQueueWorker + +### Strings & Bytes + +Miscellaneous string & byte encoding and decoding functionality provided for compatibility across supported versions of V8 and Node. Implemented by NAN to ensure that all encoding types are supported, even for older versions of Node where they are missing. + + - Nan::Encoding + - Nan::Encode() + - Nan::DecodeBytes() + - Nan::DecodeWrite() + + +### V8 internals + +The hooks to access V8 internals—including GC and statistics—are different across the supported versions of V8, therefore NAN provides its own hooks that call the appropriate V8 methods. + + - NAN_GC_CALLBACK() + - Nan::AddGCEpilogueCallback() + - Nan::RemoveGCEpilogueCallback() + - Nan::AddGCPrologueCallback() + - Nan::RemoveGCPrologueCallback() + - Nan::GetHeapStatistics() + - Nan::SetCounterFunction() + - Nan::SetCreateHistogramFunction() + - Nan::SetAddHistogramSampleFunction() + - Nan::IdleNotification() + - Nan::LowMemoryNotification() + - Nan::ContextDisposedNotification() + - Nan::GetInternalFieldPointer() + - Nan::SetInternalFieldPointer() + - Nan::AdjustExternalMemory() + + +### Miscellaneous V8 Helpers + + - Nan::Utf8String + - Nan::GetCurrentContext() + - Nan::SetIsolateData() + - Nan::GetIsolateData() + - Nan::TypedArrayContents + + +### Miscellaneous Node Helpers + + - Nan::MakeCallback() + - Nan::ObjectWrap + - NAN_MODULE_INIT() + - Nan::Export() + + + + + +### Tests + +To run the NAN tests do: + +``` sh +npm install +npm run-script rebuild-tests +npm test +``` + +Or just: + +``` sh +npm install +make test +``` + + +## Governance & Contributing + +NAN is governed by the [io.js](https://iojs.org/) Addon API Working Group + +### Addon API Working Group (WG) + +The NAN project is jointly governed by a Working Group which is responsible for high-level guidance of the project. + +Members of the WG are also known as Collaborators, there is no distinction between the two, unlike other io.js projects. + +The WG has final authority over this project including: + +* Technical direction +* Project governance and process (including this policy) +* Contribution policy +* GitHub repository hosting +* Maintaining the list of additional Collaborators + +For the current list of WG members, see the project [README.md](./README.md#collaborators). + +Individuals making significant and valuable contributions are made members of the WG and given commit-access to the project. These individuals are identified by the WG and their addition to the WG is discussed via GitHub and requires unanimous consensus amongst those WG members participating in the discussion with a quorum of 50% of WG members required for acceptance of the vote. + +_Note:_ If you make a significant contribution and are not considered for commit-access log an issue or contact a WG member directly. + +For the current list of WG members / Collaborators, see the project [README.md](./README.md#collaborators). + +### Consensus Seeking Process + +The WG follows a [Consensus Seeking](http://en.wikipedia.org/wiki/Consensus-seeking_decision-making) decision making model. + +Modifications of the contents of the NAN repository are made on a collaborative basis. Anybody with a GitHub account may propose a modification via pull request and it will be considered by the WG. All pull requests must be reviewed and accepted by a WG member with sufficient expertise who is able to take full responsibility for the change. In the case of pull requests proposed by an existing WG member, an additional WG member is required for sign-off. Consensus should be sought if additional WG members participate and there is disagreement around a particular modification. + +If a change proposal cannot reach a consensus, a WG member can call for a vote amongst the members of the WG. Simple majority wins. + +### Developer's Certificate of Origin 1.0 + +By making a contribution to this project, I certify that: + +* (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or +* (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or +* (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. + + +### WG Members / Collaborators + + + + + + + + + +
            Rod VaggGitHub/rvaggTwitter/@rvagg
            Benjamin ByholmGitHub/kkoopa-
            Trevor NorrisGitHub/trevnorrisTwitter/@trevnorris
            Nathan RajlichGitHub/TooTallNateTwitter/@TooTallNate
            Brett LawsonGitHub/brett19Twitter/@brett19x
            Ben NoordhuisGitHub/bnoordhuisTwitter/@bnoordhuis
            David SiegelGitHub/agnat-
            + +## Licence & copyright + +Copyright (c) 2015 NAN WG Members / Collaborators (listed above). + +Native Abstractions for Node.js is licensed under an MIT license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE file for more details. diff --git a/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/appveyor.yml b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/appveyor.yml new file mode 100644 index 0000000..694f84e --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/appveyor.yml @@ -0,0 +1,37 @@ +# http://www.appveyor.com/docs/appveyor-yml + +# Test against these versions of Io.js and Node.js. +environment: + matrix: + # node.js + - nodejs_version: "0.8" + - nodejs_version: "0.10" + - nodejs_version: "0.12" + - nodejs_version: "3" + - nodejs_version: "4" + +# Install scripts. (runs after repo cloning) +install: + # Get the latest stable version of Node 0.STABLE.latest + - ps: Install-Product node $env:nodejs_version + - IF %nodejs_version% EQU 0.8 npm -g install npm@2 + - IF %nodejs_version% EQU 0.8 set PATH=%APPDATA%\npm;%PATH% + - npm -g install npm + - IF %nodejs_version% NEQ 0.8 set PATH=%APPDATA%\npm;%PATH% + # Typical npm stuff. + - npm install + - npm run rebuild-tests + +# Post-install test scripts. +test_script: + # Output useful info for debugging. + - node --version + - npm --version + # run tests + - IF %nodejs_version% LSS 1 (npm test) ELSE (IF %nodejs_version% LSS 4 (iojs node_modules\tap\bin\tap.js --gc test/js/*-test.js) ELSE (node node_modules\tap\bin\tap.js --gc test/js/*-test.js)) + +# Don't actually build. +build: off + +# Set build version format here instead of in the admin panel. +version: "{build}" diff --git a/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/doc/.build.sh b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/doc/.build.sh new file mode 100755 index 0000000..75a975a --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/doc/.build.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +files=" \ + methods.md \ + scopes.md \ + persistent.md \ + new.md \ + converters.md \ + maybe_types.md \ + script.md \ + errors.md \ + buffers.md \ + callback.md \ + asyncworker.md \ + string_bytes.md \ + v8_internals.md \ + v8_misc.md \ + node_misc.md \ +" + +__dirname=$(dirname "${BASH_SOURCE[0]}") +head=$(perl -e 'while (<>) { if (!$en){print;} if ($_=~/ NanNew("foo").ToLocalChecked() */ + if (arguments[groups[3][0]] === 'NanNew') { + return [arguments[0], '.ToLocalChecked()'].join(''); + } + + /* insert warning for removed functions as comment on new line above */ + switch (arguments[groups[4][0]]) { + case 'GetIndexedPropertiesExternalArrayData': + case 'GetIndexedPropertiesExternalArrayDataLength': + case 'GetIndexedPropertiesExternalArrayDataType': + case 'GetIndexedPropertiesPixelData': + case 'GetIndexedPropertiesPixelDataLength': + case 'HasIndexedPropertiesInExternalArrayData': + case 'HasIndexedPropertiesInPixelData': + case 'SetIndexedPropertiesToExternalArrayData': + case 'SetIndexedPropertiesToPixelData': + return arguments[groups[4][0] - 1] ? arguments[0] : [warning1, arguments[0]].join(''); + default: + } + + /* remove unnecessary NanScope() */ + switch (arguments[groups[5][0]]) { + case 'NAN_GETTER': + case 'NAN_METHOD': + case 'NAN_SETTER': + case 'NAN_INDEX_DELETER': + case 'NAN_INDEX_ENUMERATOR': + case 'NAN_INDEX_GETTER': + case 'NAN_INDEX_QUERY': + case 'NAN_INDEX_SETTER': + case 'NAN_PROPERTY_DELETER': + case 'NAN_PROPERTY_ENUMERATOR': + case 'NAN_PROPERTY_GETTER': + case 'NAN_PROPERTY_QUERY': + case 'NAN_PROPERTY_SETTER': + return arguments[groups[5][0] - 1]; + default: + } + + /* Value converstion */ + switch (arguments[groups[6][0]]) { + case 'Boolean': + case 'Int32': + case 'Integer': + case 'Number': + case 'Object': + case 'String': + case 'Uint32': + return [arguments[groups[6][0] - 2], 'NanTo(', arguments[groups[6][0] - 1]].join(''); + default: + } + + /* other value conversion */ + switch (arguments[groups[7][0]]) { + case 'BooleanValue': + return [arguments[groups[7][0] - 2], 'NanTo(', arguments[groups[7][0] - 1]].join(''); + case 'Int32Value': + return [arguments[groups[7][0] - 2], 'NanTo(', arguments[groups[7][0] - 1]].join(''); + case 'IntegerValue': + return [arguments[groups[7][0] - 2], 'NanTo(', arguments[groups[7][0] - 1]].join(''); + case 'Uint32Value': + return [arguments[groups[7][0] - 2], 'NanTo(', arguments[groups[7][0] - 1]].join(''); + default: + } + + /* NAN_WEAK_CALLBACK */ + if (arguments[groups[8][0]] === 'NAN_WEAK_CALLBACK') { + return ['template\nvoid ', + arguments[groups[8][0] + 1], '(const NanWeakCallbackInfo &data)'].join(''); + } + + /* use methods on NAN classes instead */ + switch (arguments[groups[9][0]]) { + case 'NanDisposePersistent': + return [arguments[groups[9][0] + 1], '.Reset('].join(''); + case 'NanObjectWrapHandle': + return [arguments[groups[9][0] + 1], '->handle('].join(''); + default: + } + + /* use method on NanPersistent instead */ + if (arguments[groups[10][0]] === 'NanMakeWeakPersistent') { + return arguments[groups[10][0] + 1] + '.SetWeak('; + } + + /* These return Maybes, the upper ones take no arguments */ + switch (arguments[groups[11][0]]) { + case 'GetEndColumn': + case 'GetFunction': + case 'GetLineNumber': + case 'GetOwnPropertyNames': + case 'GetPropertyNames': + case 'GetSourceLine': + case 'GetStartColumn': + case 'NewInstance': + case 'ObjectProtoToString': + case 'ToArrayIndex': + case 'ToDetailString': + return [arguments[groups[11][0] - 2], 'Nan', arguments[groups[11][0]], '(', arguments[groups[11][0] - 1]].join(''); + case 'CallAsConstructor': + case 'CallAsFunction': + case 'CloneElementAt': + case 'Delete': + case 'ForceSet': + case 'Get': + case 'GetPropertyAttributes': + case 'GetRealNamedProperty': + case 'GetRealNamedPropertyInPrototypeChain': + case 'Has': + case 'HasOwnProperty': + case 'HasRealIndexedProperty': + case 'HasRealNamedCallbackProperty': + case 'HasRealNamedProperty': + case 'Set': + case 'SetAccessor': + case 'SetIndexedPropertyHandler': + case 'SetNamedPropertyHandler': + case 'SetPrototype': + return [arguments[groups[11][0] - 2], 'Nan', arguments[groups[11][0]], '(', arguments[groups[11][0] - 1], ', '].join(''); + default: + } + + /* Automatic ToLocalChecked(), take it or leave it */ + switch (arguments[groups[12][0]]) { + case 'Date': + case 'String': + case 'RegExp': + return ['NanNew', arguments[groups[12][0] - 1], arguments[groups[12][0] + 1], '.ToLocalChecked()'].join(''); + default: + } + + /* NanEquals is now required for uniformity */ + if (arguments[groups[13][0]] === 'Equals') { + return [arguments[groups[13][0] - 1], 'NanEquals(', arguments[groups[13][0] - 1], ', ', arguments[groups[13][0] + 1]].join(''); + } + + /* use method on replacement class instead */ + if (arguments[groups[14][0]] === 'NanAssignPersistent') { + return [arguments[groups[14][0] + 1], '.Reset('].join(''); + } + + /* args --> info */ + if (arguments[groups[15][0]] === 'args') { + return [arguments[groups[15][0] - 1], 'info', arguments[groups[15][0] + 1]].join(''); + } + + /* ObjectWrap --> NanObjectWrap */ + if (arguments[groups[16][0]] === 'ObjectWrap') { + return [arguments[groups[16][0] - 1], 'NanObjectWrap', arguments[groups[16][0] + 1]].join(''); + } + + /* Persistent --> NanPersistent */ + if (arguments[groups[17][0]] === 'Persistent') { + return [arguments[groups[17][0] - 1], 'NanPersistent', arguments[groups[17][0] + 1]].join(''); + } + + /* This should not happen. A switch is probably missing a case if it does. */ + throw 'Unhandled match: ' + arguments[0]; +} + +/* reads a file, runs replacement and writes it back */ +function processFile(file) { + fs.readFile(file, {encoding: 'utf8'}, function (err, data) { + if (err) { + throw err; + } + + /* run replacement twice, might need more runs */ + fs.writeFile(file, data.replace(master, replace).replace(master, replace), function (err) { + if (err) { + throw err; + } + }); + }); +} + +/* process file names from command line and process the identified files */ +for (i = 2, length = process.argv.length; i < length; i++) { + glob(process.argv[i], function (err, matches) { + if (err) { + throw err; + } + matches.forEach(processFile); + }); +} diff --git a/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/tools/README.md b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/tools/README.md new file mode 100644 index 0000000..7f07e4b --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/tools/README.md @@ -0,0 +1,14 @@ +1to2 naively converts source code files from NAN 1 to NAN 2. There will be erroneous conversions, +false positives and missed opportunities. The input files are rewritten in place. Make sure that +you have backups. You will have to manually review the changes afterwards and do some touchups. + +```sh +$ tools/1to2.js + + Usage: 1to2 [options] + + Options: + + -h, --help output usage information + -V, --version output the version number +``` diff --git a/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/tools/package.json b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/tools/package.json new file mode 100644 index 0000000..2dcdd78 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/node_modules/nan/tools/package.json @@ -0,0 +1,19 @@ +{ + "name": "1to2", + "version": "1.0.0", + "description": "NAN 1 -> 2 Migration Script", + "main": "1to2.js", + "repository": { + "type": "git", + "url": "git://github.com/nodejs/nan.git" + }, + "contributors": [ + "Benjamin Byholm (https://github.com/kkoopa/)", + "Mathias Küsel (https://github.com/mathiask88/)" + ], + "dependencies": { + "glob": "~5.0.10", + "commander": "~2.8.1" + }, + "license": "MIT" +} diff --git a/node_modules/ws/node_modules/utf-8-validate/package.json b/node_modules/ws/node_modules/utf-8-validate/package.json new file mode 100644 index 0000000..482c3c7 --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/package.json @@ -0,0 +1,63 @@ +{ + "name": "utf-8-validate", + "version": "1.2.1", + "description": "Validate UTF-8 for Web", + "main": "index.js", + "scripts": { + "test": "echo \"Only testing builds, test have to be extraced from `ws`\" && exit 0", + "install": "node-gyp rebuild" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/websockets/utf-8-validate.git" + }, + "keywords": [ + "utf-8-validate" + ], + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/websockets/utf-8-validate/issues" + }, + "homepage": "https://github.com/websockets/utf-8-validate", + "dependencies": { + "bindings": "1.2.x", + "nan": "^2.0.5" + }, + "gypfile": true, + "gitHead": "8067ecff68899b9a1bb31d6906c80e1d5e88bcc7", + "_id": "utf-8-validate@1.2.1", + "_shasum": "44cb7c6eead73d6b40448f71f745904357b9f72c", + "_from": "utf-8-validate@>=1.2.0 <1.3.0", + "_npmVersion": "2.9.1", + "_nodeVersion": "0.12.3", + "_npmUser": { + "name": "3rdeden", + "email": "npm@3rd-Eden.com" + }, + "maintainers": [ + { + "name": "einaros", + "email": "einaros@gmail.com" + }, + { + "name": "3rdeden", + "email": "npm@3rd-Eden.com" + }, + { + "name": "v1", + "email": "info@3rd-Eden.com" + } + ], + "dist": { + "shasum": "44cb7c6eead73d6b40448f71f745904357b9f72c", + "tarball": "http://registry.npmjs.org/utf-8-validate/-/utf-8-validate-1.2.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-1.2.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/ws/node_modules/utf-8-validate/src/validation.cc b/node_modules/ws/node_modules/utf-8-validate/src/validation.cc new file mode 100644 index 0000000..305ebaf --- /dev/null +++ b/node_modules/ws/node_modules/utf-8-validate/src/validation.cc @@ -0,0 +1,148 @@ +/*! + * UTF-8 validate: UTF-8 validation for WebSockets. + * Copyright(c) 2015 Einar Otto Stangvik + * MIT Licensed + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "nan.h" + +using namespace v8; +using namespace node; + +#define UNI_SUR_HIGH_START (uint32_t) 0xD800 +#define UNI_SUR_LOW_END (uint32_t) 0xDFFF +#define UNI_REPLACEMENT_CHAR (uint32_t) 0x0000FFFD +#define UNI_MAX_LEGAL_UTF32 (uint32_t) 0x0010FFFF + +static const uint8_t trailingBytesForUTF8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +static const uint32_t offsetsFromUTF8[6] = { + 0x00000000, 0x00003080, 0x000E2080, + 0x03C82080, 0xFA082080, 0x82082080 +}; + +static int isLegalUTF8(const uint8_t *source, const int length) +{ + uint8_t a; + const uint8_t *srcptr = source+length; + switch (length) { + default: return 0; + /* Everything else falls through when "true"... */ + /* RFC3629 makes 5 & 6 bytes UTF-8 illegal + case 6: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; + case 5: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; */ + case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; + case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; + case 2: if ((a = (*--srcptr)) > 0xBF) return 0; + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: if (a < 0xA0) return 0; break; + case 0xED: if (a > 0x9F) return 0; break; + case 0xF0: if (a < 0x90) return 0; break; + case 0xF4: if (a > 0x8F) return 0; break; + default: if (a < 0x80) return 0; + } + + case 1: if (*source >= 0x80 && *source < 0xC2) return 0; + } + if (*source > 0xF4) return 0; + return 1; +} + +int is_valid_utf8 (size_t len, char *value) +{ + /* is the string valid UTF-8? */ + for (unsigned int i = 0; i < len; i++) { + uint32_t ch = 0; + uint8_t extrabytes = trailingBytesForUTF8[(uint8_t) value[i]]; + + if (extrabytes + i >= len) + return 0; + + if (isLegalUTF8 ((uint8_t *) (value + i), extrabytes + 1) == 0) return 0; + + switch (extrabytes) { + case 5 : ch += (uint8_t) value[i++]; ch <<= 6; + case 4 : ch += (uint8_t) value[i++]; ch <<= 6; + case 3 : ch += (uint8_t) value[i++]; ch <<= 6; + case 2 : ch += (uint8_t) value[i++]; ch <<= 6; + case 1 : ch += (uint8_t) value[i++]; ch <<= 6; + case 0 : ch += (uint8_t) value[i]; + } + + ch -= offsetsFromUTF8[extrabytes]; + + if (ch <= UNI_MAX_LEGAL_UTF32) { + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) + return 0; + } else { + return 0; + } + } + + return 1; +} + +class Validation : public ObjectWrap +{ +public: + + static void Initialize(v8::Handle target) + { + Nan::HandleScope scope; + Local t = Nan::New(New); + t->InstanceTemplate()->SetInternalFieldCount(1); + Nan::SetMethod(t, "isValidUTF8", Validation::IsValidUTF8); + Nan::Set(target, Nan::New("Validation").ToLocalChecked(), t->GetFunction()); + } + +protected: + + static NAN_METHOD(New) + { + Nan::HandleScope scope; + Validation* validation = new Validation(); + validation->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + } + + static NAN_METHOD(IsValidUTF8) + { + Nan::HandleScope scope; + if (!Buffer::HasInstance(info[0])) { + return Nan::ThrowTypeError("First argument needs to be a buffer"); + } + Local buffer_obj = info[0]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); + info.GetReturnValue().Set(is_valid_utf8(buffer_length, buffer_data) == 1 ? Nan::True() : Nan::False()); + } +}; +#if !NODE_VERSION_AT_LEAST(0,10,0) +extern "C" +#endif +void init (Handle target) +{ + Nan::HandleScope scope; + Validation::Initialize(target); +} + +NODE_MODULE(validation, init) + diff --git a/node_modules/ws/package.json b/node_modules/ws/package.json new file mode 100644 index 0000000..7fa6186 --- /dev/null +++ b/node_modules/ws/package.json @@ -0,0 +1,87 @@ +{ + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "name": "ws", + "description": "simple to use, blazing fast and thoroughly tested websocket client, server and console for node.js, up-to-date against RFC-6455", + "version": "0.8.1", + "license": "MIT", + "keywords": [ + "Hixie", + "HyBi", + "Push", + "RFC-6455", + "WebSocket", + "WebSockets", + "real-time" + ], + "repository": { + "type": "git", + "url": "git://github.com/websockets/ws.git" + }, + "scripts": { + "test": "make test" + }, + "dependencies": { + "options": ">=0.0.5", + "ultron": "1.0.x", + "bufferutil": "1.2.x", + "utf-8-validate": "1.2.x" + }, + "optionalDependencies": { + "bufferutil": "1.2.x", + "utf-8-validate": "1.2.x" + }, + "devDependencies": { + "ansi": "0.3.x", + "benchmark": "0.3.x", + "expect.js": "0.3.x", + "mocha": "2.2.x", + "should": "4.3.x", + "tinycolor": "0.0.x" + }, + "browser": "./lib/browser.js", + "component": { + "scripts": { + "ws/index.js": "./lib/browser.js" + } + }, + "gypfile": true, + "gitHead": "74f567e0221a14071bb40eb1902e946524a11862", + "bugs": { + "url": "https://github.com/websockets/ws/issues" + }, + "homepage": "https://github.com/websockets/ws#readme", + "_id": "ws@0.8.1", + "_shasum": "6b65273b99193c5f067a4cf5809598f777e3b759", + "_from": "ws@>=0.8.0 <0.9.0", + "_npmVersion": "2.14.7", + "_nodeVersion": "4.2.1", + "_npmUser": { + "name": "3rdeden", + "email": "npm@3rd-Eden.com" + }, + "maintainers": [ + { + "name": "einaros", + "email": "einaros@gmail.com" + }, + { + "name": "v1", + "email": "info@3rd-Eden.com" + }, + { + "name": "3rdeden", + "email": "npm@3rd-Eden.com" + } + ], + "dist": { + "shasum": "6b65273b99193c5f067a4cf5809598f777e3b759", + "tarball": "http://registry.npmjs.org/ws/-/ws-0.8.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/ws/-/ws-0.8.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/xml2js/.npmignore b/node_modules/xml2js/.npmignore new file mode 100644 index 0000000..ef7b9b9 --- /dev/null +++ b/node_modules/xml2js/.npmignore @@ -0,0 +1,6 @@ +*.swp +.idea +node_modules +src +test +Cakefile \ No newline at end of file diff --git a/node_modules/xml2js/.travis.yml b/node_modules/xml2js/.travis.yml new file mode 100644 index 0000000..755a6b7 --- /dev/null +++ b/node_modules/xml2js/.travis.yml @@ -0,0 +1,5 @@ +language: node_js + +node_js: + - "0.8" + - "0.10" diff --git a/node_modules/xml2js/83.coffee b/node_modules/xml2js/83.coffee new file mode 100644 index 0000000..3443540 --- /dev/null +++ b/node_modules/xml2js/83.coffee @@ -0,0 +1,6 @@ +xml2js = require 'xml2js' +util = require 'util' + +body = 'Character data here!' +xml2js.parseString body, (err, result) -> + console.log util.inspect result, false, null diff --git a/node_modules/xml2js/CONTRIBUTING.md b/node_modules/xml2js/CONTRIBUTING.md new file mode 100644 index 0000000..2209adf --- /dev/null +++ b/node_modules/xml2js/CONTRIBUTING.md @@ -0,0 +1,19 @@ +# How to contribute + +We're always happy about useful new pull requests. Keep in mind that the better +your pull request is, the easier it can be added to `xml2js`. As such please +make sure your patch is ok: + + * `xml2js` is written in CoffeeScript. Please don't send patches to + the JavaScript source, as it get's overwritten by the CoffeeScript + compiler. The reason we have the JS code in the repository is for easier + use with eg. `git submodule` + * Make sure that the unit tests still all pass. Failing unit tests mean that + someone *will* run into a bug, if we accept your pull request. + * Please, add a unit test with your pull request, to show what was broken and + is now fixed or what was impossible and now works due to your new code. + * If you add a new feature, please add some documentation that it exists. + +If you like, you can add yourself in the `package.json` as contributor if you +deem your contribution significant enough. Otherwise, we will decide and maybe +add you. diff --git a/node_modules/xml2js/LICENSE b/node_modules/xml2js/LICENSE new file mode 100644 index 0000000..e3b4222 --- /dev/null +++ b/node_modules/xml2js/LICENSE @@ -0,0 +1,19 @@ +Copyright 2010, 2011, 2012, 2013. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/node_modules/xml2js/README.md b/node_modules/xml2js/README.md new file mode 100644 index 0000000..4e01478 --- /dev/null +++ b/node_modules/xml2js/README.md @@ -0,0 +1,343 @@ +node-xml2js +=========== + +Ever had the urge to parse XML? And wanted to access the data in some sane, +easy way? Don't want to compile a C parser, for whatever reason? Then xml2js is +what you're looking for! + +Description +=========== + +Simple XML to JavaScript object converter. It supports bi-directional conversion. +Uses [sax-js](https://github.com/isaacs/sax-js/) and +[xmlbuilder-js](https://github.com/oozcitak/xmlbuilder-js/). + +Note: If you're looking for a full DOM parser, you probably want +[JSDom](https://github.com/tmpvar/jsdom). + +Installation +============ + +Simplest way to install `xml2js` is to use [npm](http://npmjs.org), just `npm +install xml2js` which will download xml2js and all dependencies. + +Usage +===== + +No extensive tutorials required because you are a smart developer! The task of +parsing XML should be an easy one, so let's make it so! Here's some examples. + +Shoot-and-forget usage +---------------------- + +You want to parse XML as simple and easy as possible? It's dangerous to go +alone, take this: + +```javascript +var parseString = require('xml2js').parseString; +var xml = "Hello xml2js!" +parseString(xml, function (err, result) { + console.dir(result); +}); +``` + +Can't get easier than this, right? This works starting with `xml2js` 0.2.3. +With CoffeeScript it looks like this: + +```coffeescript +{parseString} = require 'xml2js' +xml = "Hello xml2js!" +parseString xml, (err, result) -> + console.dir result +``` + +If you need some special options, fear not, `xml2js` supports a number of +options (see below), you can specify these as second argument: + +```javascript +parseString(xml, {trim: true}, function (err, result) { +}); +``` + +Simple as pie usage +------------------- + +That's right, if you have been using xml-simple or a home-grown +wrapper, this was added in 0.1.11 just for you: + +```javascript +var fs = require('fs'), + xml2js = require('xml2js'); + +var parser = new xml2js.Parser(); +fs.readFile(__dirname + '/foo.xml', function(err, data) { + parser.parseString(data, function (err, result) { + console.dir(result); + console.log('Done'); + }); +}); +``` + +Look ma, no event listeners! + +You can also use `xml2js` from +[CoffeeScript](http://jashkenas.github.com/coffee-script/), further reducing +the clutter: + +```coffeescript +fs = require 'fs', +xml2js = require 'xml2js' + +parser = new xml2js.Parser() +fs.readFile __dirname + '/foo.xml', (err, data) -> + parser.parseString data, (err, result) -> + console.dir result + console.log 'Done.' +``` + +But what happens if you forget the `new` keyword to create a new `Parser`? In +the middle of a nightly coding session, it might get lost, after all. Worry +not, we got you covered! Starting with 0.2.8 you can also leave it out, in +which case `xml2js` will helpfully add it for you, no bad surprises and +inexplicable bugs! + +"Traditional" usage +------------------- + +Alternatively you can still use the traditional `addListener` variant that was +supported since forever: + +```javascript +var fs = require('fs'), + xml2js = require('xml2js'); + +var parser = new xml2js.Parser(); +parser.addListener('end', function(result) { + console.dir(result); + console.log('Done.'); +}); +fs.readFile(__dirname + '/foo.xml', function(err, data) { + parser.parseString(data); +}); +``` + +If you want to parse multiple files, you have multiple possibilites: + + * You can create one `xml2js.Parser` per file. That's the recommended one + and is promised to always *just work*. + * You can call `reset()` on your parser object. + * You can hope everything goes well anyway. This behaviour is not + guaranteed work always, if ever. Use option #1 if possible. Thanks! + +So you wanna some JSON? +----------------------- + +Just wrap the `result` object in a call to `JSON.stringify` like this +`JSON.stringify(result)`. You get a string containing the JSON representation +of the parsed object that you can feed to JSON-hungry consumers. + +Displaying results +------------------ + +You might wonder why, using `console.dir` or `console.log` the output at some +level is only `[Object]`. Don't worry, this is not because xml2js got lazy. +That's because Node uses `util.inspect` to convert the object into strings and +that function stops after `depth=2` which is a bit low for most XML. + +To display the whole deal, you can use `console.log(util.inspect(result, false, +null))`, which displays the whole result. + +So much for that, but what if you use +[eyes](https://github.com/cloudhead/eyes.js) for nice colored output and it +truncates the output with `…`? Don't fear, there's also a solution for that, +you just need to increase the `maxLength` limit by creating a custom inspector +`var inspect = require('eyes').inspector({maxLength: false})` and then you can +easily `inspect(result)`. + +XML builder usage +----------------- + +Since 0.4.0, objects can be also be used to build XML: + +```javascript +var fs = require('fs'), + xml2js = require('xml2js'); + +var obj = {name: "Super", Surname: "Man", age: 23}; + +var builder = new xml2js.Builder(); +var xml = builder.buildObject(obj); +``` + +At the moment, a one to one bi-directional conversion is guaranteed only for +default configuration, except for `attrkey`, `charkey` and `explicitArray` options +you can redefine to your taste. Writing CDATA is not currently supported. + +Processing attribute and tag names +---------------------------------- + +Since 0.4.1 you can optionally provide the parser with attribute and tag name processors: + +```javascript + +function nameToUpperCase(name){ + return name.toUpperCase(); +} + +//transform all attribute and tag names to uppercase +parseString(xml, {tagNameProcessors: [nameToUpperCase], attrNameProcessors: [nameToUpperCase]}, function (err, result) { +}); +``` + +The `tagNameProcessors` and `attrNameProcessors` options both accept an `Array` of functions with the following signature: +```javascript +function (name){ + //do something with `name` + return name +} +``` + +Some processors are provided out-of-the-box and can be found in `lib/processors.js`: + +- `normalize`: transforms the name to lowercase. +(Automatically used when `options.normalize` is set to `true`) + +- `firstCharLowerCase`: transforms the first character to lower case. +E.g. 'MyTagName' becomes 'myTagName' + +- `stripPrefix`: strips the xml namespace prefix. E.g `` will become 'Bar'. +(N.B.: the `xmlns` prefix is NOT stripped.) + +Options +======= + +Apart from the default settings, there are a number of options that can be +specified for the parser. Options are specified by ``new Parser({optionName: +value})``. Possible options are: + + * `attrkey` (default: `$`): Prefix that is used to access the attributes. + Version 0.1 default was `@`. + * `charkey` (default: `_`): Prefix that is used to access the character + content. Version 0.1 default was `#`. + * `explicitCharkey` (default: `false`) + * `trim` (default: `false`): Trim the whitespace at the beginning and end of + text nodes. + * `normalizeTags` (default: `false`): Normalize all tag names to lowercase. + * `normalize` (default: `false`): Trim whitespaces inside text nodes. + * `explicitRoot` (default: `true`): Set this if you want to get the root + node in the resulting object. + * `emptyTag` (default: `undefined`): what will the value of empty nodes be. + Default is `{}`. + * `explicitArray` (default: `true`): Always put child nodes in an array if + true; otherwise an array is created only if there is more than one. + * `ignoreAttrs` (default: `false`): Ignore all XML attributes and only create + text nodes. + * `mergeAttrs` (default: `false`): Merge attributes and child elements as + properties of the parent, instead of keying attributes off a child + attribute object. This option is ignored if `ignoreAttrs` is `false`. + * `validator` (default `null`): You can specify a callable that validates + the resulting structure somehow, however you want. See unit tests + for an example. + * `xmlns` (default `false`): Give each element a field usually called '$ns' + (the first character is the same as attrkey) that contains its local name + and namespace URI. + * `explicitChildren` (default `false`): Put child elements to separate + property. Doesn't work with `mergeAttrs = true`. If element has no children + then "children" won't be created. Added in 0.2.5. + * `childkey` (default `$$`): Prefix that is used to access child elements if + `explicitChildren` is set to `true`. Added in 0.2.5. + * `charsAsChildren` (default `false`): Determines whether chars should be + considered children if `explicitChildren` is on. Added in 0.2.5. + * `async` (default `false`): Should the callbacks be async? This *might* be + an incompatible change if your code depends on sync execution of callbacks. + xml2js 0.3 might change this default, so the recommendation is to not + depend on sync execution anyway. Added in 0.2.6. + * `strict` (default `true`): Set sax-js to strict or non-strict parsing mode. + Defaults to `true` which is *highly* recommended, since parsing HTML which + is not well-formed XML might yield just about anything. Added in 0.2.7. + * `attrNameProcessors` (default: `null`): Allows the addition of attribute name processing functions. + Accepts an `Array` of functions with following signature: + ```javascript + function (name){ + //do something with `name` + return name + } + ``` + Added in 0.4.1 + * `tagNameProcessors` (default: `null`):Allows the addition of tag name processing functions. + Accepts an `Array` of functions with following signature: + ```javascript + function (name){ + //do something with `name` + return name + } + ``` + Added in 0.4.1 + +Options for the `Builder` class +------------------------------- + + * `rootName` (default `root`): root element name to be used in case + `explicitRoot` is `false` or to override the root element name. + * `renderOpts` (default `{ 'pretty': true, 'indent': ' ', 'newline': '\n' }`): + Rendering options for xmlbuilder-js. + * pretty: prettify generated XML + * indent: whitespace for indentation (only when pretty) + * newline: newline char (only when pretty) + * `xmldec` (default `{ 'version': '1.0', 'encoding': 'UTF-8', 'standalone': true }`: + XML declaration attributes. + * `xmldec.version` A version number string, e.g. 1.0 + * `xmldec.encoding` Encoding declaration, e.g. UTF-8 + * `xmldec.standalone` standalone document declaration: true or false + * `doctype` (default `null`): optional DTD. Eg. `{'ext': 'hello.dtd'}` + * `headless` (default: `false`): omit the XML header. Added in 0.4.3. + +`renderOpts`, `xmldec`,`doctype` and `headless` pass through to +[xmlbuilder-js](https://github.com/oozcitak/xmlbuilder-js). + +Updating to new version +======================= + +Version 0.2 changed the default parsing settings, but version 0.1.14 introduced +the default settings for version 0.2, so these settings can be tried before the +migration. + +```javascript +var xml2js = require('xml2js'); +var parser = new xml2js.Parser(xml2js.defaults["0.2"]); +``` + +To get the 0.1 defaults in version 0.2 you can just use +`xml2js.defaults["0.1"]` in the same place. This provides you with enough time +to migrate to the saner way of parsing in xml2js 0.2. We try to make the +migration as simple and gentle as possible, but some breakage cannot be +avoided. + +So, what exactly did change and why? In 0.2 we changed some defaults to parse +the XML in a more universal and sane way. So we disabled `normalize` and `trim` +so xml2js does not cut out any text content. You can reenable this at will of +course. A more important change is that we return the root tag in the resulting +JavaScript structure via the `explicitRoot` setting, so you need to access the +first element. This is useful for anybody who wants to know what the root node +is and preserves more information. The last major change was to enable +`explicitArray`, so everytime it is possible that one might embed more than one +sub-tag into a tag, xml2js >= 0.2 returns an array even if the array just +includes one element. This is useful when dealing with APIs that return +variable amounts of subtags. + +Running tests, development +========================== + +[![Build Status](https://secure.travis-ci.org/Leonidas-from-XIV/node-xml2js.png?branch=master)](https://travis-ci.org/Leonidas-from-XIV/node-xml2js) +[![Dependency Status](https://david-dm.org/Leonidas-from-XIV/node-xml2js.png)](https://david-dm.org/Leonidas-from-XIV/node-xml2js) + +The development requirements are handled by npm, you just need to install them. +We also have a number of unit tests, they can be run using `npm test` directly +from the project root. This runs zap to discover all the tests and execute +them. + +If you like to contribute, keep in mind that xml2js is written in CoffeeScript, +so don't develop on the JavaScript files that are checked into the repository +for convenience reasons. Also, please write some unit test to check your +behaviour and if it is some user-facing thing, add some documentation to this +README, so people will know it exists. Thanks in advance! diff --git a/node_modules/xml2js/canon.xml b/node_modules/xml2js/canon.xml new file mode 100644 index 0000000..f24ddd1 --- /dev/null +++ b/node_modules/xml2js/canon.xml @@ -0,0 +1,482 @@ + + + AF485783 + 14758 + double + DNA + circular + SYN + 15-MAY-2003 + 21-MAR-2002 + Binary vector pBI121, complete sequence + AF485783 + AF485783.1 + + gb|AF485783.1| + gi|19569229 + + Binary vector pBI121 + Binary vector pBI121 + other sequences; artificial sequences; vectors + + + 1 + 1..14758 + + Chen,P.Y. + Wang,C.K. + Soong,S.C. + To,K.Y. + + Complete sequence of the binary vector pBI121 and its application in cloning T-DNA insertion from transgenic plants + Mol. Breed. 11, 287-293 (2003) + + + 2 + 1..14758 + + To,K.Y. + + Direct Submission + Submitted (20-FEB-2002) Institute of BioAgricultural Sciences, Academia Sinica, Taipei 11529, Taiwan + + + + + source + 1..14758 + + + 1 + 14758 + AF485783.1 + + + + + organism + Binary vector pBI121 + + + mol_type + genomic DNA + + + db_xref + taxon:189807 + + + note + constructed using pB221 from Clontech Laboratories and Bin19 described in GenBank Accession Number U09365 + + + + + misc_feature + complement(13..796) + + + 796 + 13 + + AF485783.1 + + + + + note + similar to traF in GenBank Accession Number X54459 + + + + + rep_origin + complement(790..1168) + + + 1168 + 790 + + AF485783.1 + + + + + note + ColE1 ori; similar to sequence in GenBank Accession Number V00268 + + + + + misc_feature + complement(1161..2344) + + + 2344 + 1161 + + AF485783.1 + + + + + note + similar to tetA in GenBank Accession Number X75761 + + + + + misc_feature + complement(2454..2478) + + + 2478 + 2454 + + AF485783.1 + + + + + note + T-DNA right border + + + + + promoter + 2519..2825 + + + 2519 + 2825 + AF485783.1 + + + + + note + NOS + + + + + gene + 2838..3632 + + + 2838 + 3632 + AF485783.1 + + + + + gene + nptII + + + + + CDS + 2838..3632 + + + 2838 + 3632 + AF485783.1 + + + + + gene + nptII + + + codon_start + 1 + + + transl_table + 1 + + + product + neomycin phosphotransferase II + + + protein_id + AAL92039.1 + + + db_xref + GI:19569230 + + + translation + MIEQDGLHAGSPAAWVERLFGYDWAQQTIGCSDAAVFRLSAQGRPVLFVKTDLSGALNELQDEAARLSWLATTGVPCAAVLDVVTEAGRDWLLLGEVPGQDLLSSHLAPAEKVSIMADAMRRLHTLDPATCPFDHQAKHRIERARTRMEAGLVDQDDLDEEHQGLAPAELFARLKARMPDGDDLVVTHGDACLPNIMVENGRFSGFIDCGRLGVADRYQDIALATRDIAEELGGEWADRFLVLYGIAAPDSQRIAFYRLLDEFF + + + + + terminator + 4022..4277 + + + 4022 + 4277 + AF485783.1 + + + + + note + NOS + + + + + promoter + 4974..5808 + + + 4974 + 5808 + AF485783.1 + + + + + note + CaMV 35S + + + + + gene + 5845..7656 + + + 5845 + 7656 + AF485783.1 + + + + + gene + gusA + + + + + CDS + 5845..7656 + + + 5845 + 7656 + AF485783.1 + + + + + gene + gusA + + + note + GUS + + + codon_start + 1 + + + transl_table + 1 + + + product + beta-glucuronidase + + + protein_id + AAL92040.1 + + + db_xref + GI:19569231 + + + translation + MLRPVETPTREIKKLDGLWAFSLDRENCGIDQRWWESALQESRAIAVPGSFNDQFADADIRNYAGNVWYQREVFIPKGWAGQRIVLRFDAVTHYGKVWVNNQEVMEHQGGYTPFEADVTPYVIAGKSVRITVCVNNELNWQTIPPGMVITDENGKKKQSYFHDFFNYAGIHRSVMLYTTPNTWVDDITVVTHVAQDCNHASVDWQVVANGDVSVELRDADQQVVATGQGTSGTLQVVNPHLWQPGEGYLYELCVTAKSQTECDIYPLRVGIRSVAVKGEQFLINHKPFYFTGFGRHEDADLRGKGFDNVLMVHDHALMDWIGANSYRTSHYPYAEEMLDWADEHGIVVIDETAAVGFNLSLGIGFEAGNKPKELYSEEAVNGETQQAHLQAIKELIARDKNHPSVVMWSIANEPDTRPQGAREYFAPLAEATRKLDPTRPITCVNVMFCDAHTDTISDLFDVLCLNRYYGWYVQSGDLETAEKVLEKELLAWQEKLHQPIIITEYGVDTLAGLHSMYTDMWSEEYQCAWLDMYHRVFDRVSAVVGEQVWNFADFATSQGILRVGGNKKGIFTRDRKPKSAAFLLQKRWTGMNFGEKPQQGGKQ + + + + + terminator + 7727..7979 + + + 7727 + 7979 + AF485783.1 + + + + + note + NOS + + + + + misc_feature + complement(8621..8646) + + + 8646 + 8621 + + AF485783.1 + + + + + note + T-DNA left border + + + + + misc_feature + complement(9156..10198) + + + 10198 + 9156 + + AF485783.1 + + + + + note + similar to tetA in GenBank Accession Number L13842 + + + + + misc_feature + complement(10199..11680) + + + 11680 + 10199 + + AF485783.1 + + + + + note + similar to trfA in GenBank Accession Number X00713 + + + + + misc_feature + complement(11681..12673) + + + 12673 + 11681 + + AF485783.1 + + + + + note + similar to NPTIII gene in GenBank Accession Number V01547 + + + + + misc_feature + complement(12674..13443) + + + 13443 + 12674 + + AF485783.1 + + + + + note + similar to transposable element IS1 in GenBank Accession Number X58999 + + + + + misc_feature + complement(13444..13794) + + + 13794 + 13444 + + AF485783.1 + + + + + note + similarity to NPT III gene in GenBank Accession Number V01547 + + + + + misc_feature + complement(13795..14066) + + + 14066 + 13795 + + AF485783.1 + + + + + note + similar to kilA in GenBank Accession Number M62846 + + + + + rep_origin + complement(14141..14758) + + + 14758 + 14141 + + AF485783.1 + + + + + note + ori V; similar to sequence in GenBank Accession Number M20134 + + + + + tgagcgtcgcaaaggcgctcggtcttgccttgctcgtcggtgatgtacttcaccagctccgcgaagtcgctcttcttgatggagcgcatggggacgtgcttggcaatcacgcgcaccccccggccgttttagcggctaaaaaagtcatggctctgccctcgggcggaccacgcccatcatgaccttgccaagctcgtcctgcttctcttcgatcttcgccagcagggcgaggatcgtggcatcaccgaaccgcgccgtgcgcgggtcgtcggtgagccagagtttcagcaggccgcccaggcggcccaggtcgccattgatgcgggccagctcgcggacgtgctcatagtccacgacgcccgtgattttgtagccctggccgacggccagcaggtaggccgacaggctcatgccggccgccgccgccttttcctcaatcgctcttcgttcgtctggaaggcagtacaccttgataggtgggctgcccttcctggttggcttggtttcatcagccatccgcttgccctcatctgttacgccggcggtagccggccagcctcgcagagcaggattcccgttgagcaccgccaggtgcgaataagggacagtgaagaaggaacacccgctcgcgggtgggcctacttcacctatcctgcccggctgacgccgttggatacaccaaggaaagtctacacgaaccctttggcaaaatcctgtatatcgtgcgaaaaaggatggatataccgaaaaaatcgctataatgaccccgaagcagggttatgcagcggaaaagcgccacgcttcccgaagggagaaaggcggacaggtatccggtaagcggcagggtcggaacaggagagcgcacgagggagcttccagggggaaacgcctggtatctttatagtcctgtcgggtttcgccacctctgacttgagcgtcgatttttgtgatgctcgtcaggggggcggagcctatggaaaaacgccagcaacgcggcctttttacggttcctggccttttgctggccttttgctcacatgttctttcctgcgttatcccctgattctgtggataaccgtattaccgcctttgagtgagctgataccgctcgccgcagccgaacgaccgagcgcagcgagtcagtgagcgaggaagcggaagagcgccagaaggccgccagagaggccgagcgcggccgtgaggcttggacgctagggcagggcatgaaaaagcccgtagcgggctgctacgggcgtctgacgcggtggaaagggggaggggatgttgtctacatggctctgctgtagtgagtgggttgcgctccggcagcggtcctgatcaatcgtcaccctttctcggtccttcaacgttcctgacaacgagcctccttttcgccaatccatcgacaatcaccgcgagtccctgctcgaacgctgcgtccggaccggcttcgtcgaaggcgtctatcgcggcccgcaacagcggcgagagcggagcctgttcaacggtgccgccgcgctcgccggcatcgctgtcgccggcctgctcctcaagcacggccccaacagtgaagtagctgattgtcatcagcgcattgacggcgtccccggccgaaaaacccgcctcgcagaggaagcgaagctgcgcgtcggccgtttccatctgcggtgcgcccggtcgcgtgccggcatggatgcgcgcgccatcgcggtaggcgagcagcgcctgcctgaagctgcgggcattcccgatcagaaatgagcgccagtcgtcgtcggctctcggcaccgaatgcgtatgattctccgccagcatggcttcggccagtgcgtcgagcagcgcccgcttgttcctgaagtgccagtaaagcgccggctgctgaacccccaaccgttccgccagtttgcgtgtcgtcagaccgtctacgccgacctcgttcaacaggtccagggcggcacggatcactgtattcggctgcaactttgtcatgcttgacactttatcactgataaacataatatgtccaccaacttatcagtgataaagaatccgcgcgttcaatcggaccagcggaggctggtccggaggccagacgtgaaacccaacatacccctgatcgtaattctgagcactgtcgcgctcgacgctgtcggcatcggcctgattatgccggtgctgccgggcctcctgcgcgatctggttcactcgaacgacgtcaccgcccactatggcattctgctggcgctgtatgcgttggtgcaatttgcctgcgcacctgtgctgggcgcgctgtcggatcgtttcgggcggcggccaatcttgctcgtctcgctggccggcgccagatctggggaaccctgtggttggcatgcacatacaaatggacgaacggataaaccttttcacgcccttttaaatatccgattattctaataaacgctcttttctcttaggtttacccgccaatatatcctgtcaaacactgatagtttaaactgaaggcgggaaacgacaatctgatcatgagcggagaattaagggagtcacgttatgacccccgccgatgacgcgggacaagccgttttacgtttggaactgacagaaccgcaacgttgaaggagccactcagccgcgggtttctggagtttaatgagctaagcacatacgtcagaaaccattattgcgcgttcaaaagtcgcctaaggtcactatcagctagcaaatatttcttgtcaaaaatgctccactgacgttccataaattcccctcggtatccaattagagtctcatattcactctcaatccaaataatctgcaccggatctggatcgtttcgcatgattgaacaagatggattgcacgcaggttctccggccgcttgggtggagaggctattcggctatgactgggcacaacagacaatcggctgctctgatgccgccgtgttccggctgtcagcgcaggggcgcccggttctttttgtcaagaccgacctgtccggtgccctgaatgaactgcaggacgaggcagcgcggctatcgtggctggccacgacgggcgttccttgcgcagctgtgctcgacgttgtcactgaagcgggaagggactggctgctattgggcgaagtgccggggcaggatctcctgtcatctcaccttgctcctgccgagaaagtatccatcatggctgatgcaatgcggcggctgcatacgcttgatccggctacctgcccattcgaccaccaagcgaaacatcgcatcgagcgagcacgtactcggatggaagccggtcttgtcgatcaggatgatctggacgaagagcatcaggggctcgcgccagccgaactgttcgccaggctcaaggcgcgcatgcccgacggcgatgatctcgtcgtgacccatggcgatgcctgcttgccgaatatcatggtggaaaatggccgcttttctggattcatcgactgtggccggctgggtgtggcggaccgctatcaggacatagcgttggctacccgtgatattgctgaagagcttggcggcgaatgggctgaccgcttcctcgtgctttacggtatcgccgctcccgattcgcagcgcatcgccttctatcgccttcttgacgagttcttctgagcgggactctggggttcgaaatgaccgaccaagcgacgcccaacctgccatcacgagatttcgattccaccgccgccttctatgaaaggttgggcttcggaatcgttttccgggacgccggctggatgatcctccagcgcggggatctcatgctggagttcttcgcccacgggatctctgcggaacaggcggtcgaaggtgccgatatcattacgacagcaacggccgacaagcacaacgccacgatcctgagcgacaatatgatcgggcccggcgtccacatcaacggcgtcggcggcgactgcccaggcaagaccgagatgcaccgcgatatcttgctgcgttcggatattttcgtggagttcccgccacagacccggatgatccccgatcgttcaaacatttggcaataaagtttcttaagattgaatcctgttgccggtcttgcgatgattatcatataatttctgttgaattacgttaagcatgtaataattaacatgtaatgcatgacgttatttatgagatgggtttttatgattagagtcccgcaattatacatttaatacgcgatagaaaacaaaatatagcgcgcaaactaggataaattatcgcgcgcggtgtcatctatgttactagatcgggcctcctgtcaatgctggcggcggctctggtggtggttctggtggcggctctgagggtggtggctctgagggtggcggttctgagggtggcggctctgagggaggcggttccggtggtggctctggttccggtgattttgattatgaaaagatggcaaacgctaataagggggctatgaccgaaaatgccgatgaaaacgcgctacagtctgacgctaaaggcaaacttgattctgtcgctactgattacggtgctgctatcgatggtttcattggtgacgtttccggccttgctaatggtaatggtgctactggtgattttgctggctctaattcccaaatggctcaagtcggtgacggtgataattcacctttaatgaataatttccgtcaatatttaccttccctccctcaatcggttgaatgtcgcccttttgtctttggcccaatacgcaaaccgcctctccccgcgcgttggccgattcattaatgcagctggcacgacaggtttcccgactggaaagcgggcagtgagcgcaacgcaattaatgtgagttagctcactcattaggcaccccaggctttacactttatgcttccggctcgtatgttgtgtggaattgtgagcggataacaatttcacacaggaaacagctatgaccatgattacgccaagcttgcatgcctgcaggtccccagattagccttttcaatttcagaaagaatgctaacccacagatggttagagaggcttacgcagcaggtctcatcaagacgatctacccgagcaataatctccaggaaatcaaataccttcccaagaaggttaaagatgcagtcaaaagattcaggactaactgcatcaagaacacagagaaagatatatttctcaagatcagaagtactattccagtatggacgattcaaggcttgcttcacaaaccaaggcaagtaatagagattggagtctctaaaaaggtagttcccactgaatcaaaggccatggagtcaaagattcaaatagaggacctaacagaactcgccgtaaagactggcgaacagttcatacagagtctcttacgactcaatgacaagaagaaaatcttcgtcaacatggtggagcacgacacacttgtctactccaaaaatatcaaagatacagtctcagaagaccaaagggcaattgagacttttcaacaaagggtaatatccggaaacctcctcggattccattgcccagctatctgtcactttattgtgaagatagtggaaaaggaaggtggctcctacaaatgccatcattgcgataaaggaaaggccatcgttgaagatgcctctgccgacagtggtcccaaagatggacccccacccacgaggagcatcgtggaaaaagaagacgttccaaccacgtcttcaaagcaagtggattgatgtgatatctccactgacgtaagggatgacgcacaatcccactatccttcgcaagacccttcctctatataaggaagttcatttcatttggagagaacacgggggactctagaggatccccgggtggtcagtcccttatgttacgtcctgtagaaaccccaacccgtgaaatcaaaaaactcgacggcctgtgggcattcagtctggatcgcgaaaactgtggaattgatcagcgttggtgggaaagcgcgttacaagaaagccgggcaattgctgtgccaggcagttttaacgatcagttcgccgatgcagatattcgtaattatgcgggcaacgtctggtatcagcgcgaagtctttataccgaaaggttgggcaggccagcgtatcgtgctgcgtttcgatgcggtcactcattacggcaaagtgtgggtcaataatcaggaagtgatggagcatcagggcggctatacgccatttgaagccgatgtcacgccgtatgttattgccgggaaaagtgtacgtatcaccgtttgtgtgaacaacgaactgaactggcagactatcccgccgggaatggtgattaccgacgaaaacggcaagaaaaagcagtcttacttccatgatttctttaactatgccggaatccatcgcagcgtaatgctctacaccacgccgaacacctgggtggacgatatcaccgtggtgacgcatgtcgcgcaagactgtaaccacgcgtctgttgactggcaggtggtggccaatggtgatgtcagcgttgaactgcgtgatgcggatcaacaggtggttgcaactggacaaggcactagcgggactttgcaagtggtgaatccgcacctctggcaaccgggtgaaggttatctctatgaactgtgcgtcacagccaaaagccagacagagtgtgatatctacccgcttcgcgtcggcatccggtcagtggcagtgaagggcgaacagttcctgattaaccacaaaccgttctactttactggctttggtcgtcatgaagatgcggacttgcgtggcaaaggattcgataacgtgctgatggtgcacgaccacgcattaatggactggattggggccaactcctaccgtacctcgcattacccttacgctgaagagatgctcgactgggcagatgaacatggcatcgtggtgattgatgaaactgctgctgtcggctttaacctctctttaggcattggtttcgaagcgggcaacaagccgaaagaactgtacagcgaagaggcagtcaacggggaaactcagcaagcgcacttacaggcgattaaagagctgatagcgcgtgacaaaaaccacccaagcgtggtgatgtggagtattgccaacgaaccggatacccgtccgcaaggtgcacgggaatatttcgcgccactggcggaagcaacgcgtaaactcgacccgacgcgtccgatcacctgcgtcaatgtaatgttctgcgacgctcacaccgataccatcagcgatctctttgatgtgctgtgcctgaaccgttattacggatggtatgtccaaagcggcgatttggaaacggcagagaaggtactggaaaaagaacttctggcctggcaggagaaactgcatcagccgattatcatcaccgaatacggcgtggatacgttagccgggctgcactcaatgtacaccgacatgtggagtgaagagtatcagtgtgcatggctggatatgtatcaccgcgtctttgatcgcgtcagcgccgtcgtcggtgaacaggtatggaatttcgccgattttgcgacctcgcaaggcatattgcgcgttggcggtaacaagaaagggatcttcactcgcgaccgcaaaccgaagtcggcggcttttctgctgcaaaaacgctggactggcatgaacttcggtgaaaaaccgcagcagggaggcaaacaatgaatcaacaactctcctggcgcaccatcgtcggctacagcctcgggaattgctaccgagctcgaatttccccgatcgttcaaacatttggcaataaagtttcttaagattgaatcctgttgccggtcttgcgatgattatcatataatttctgttgaattacgttaagcatgtaataattaacatgtaatgcatgacgttatttatgagatgggtttttatgattagagtcccgcaattatacatttaatacgcgatagaaaacaaaatatagcgcgcaaactaggataaattatcgcgcgcggtgtcatctatgttactagatcgggaattcactggccgtcgttttacaacgtcgtgactgggaaaaccctggcgttacccaacttaatcgccttgcagcacatccccctttcgccagctggcgtaatagcgaagaggcccgcaccgatcgcccttcccaacagttgcgcagcctgaatggcgcccgctcctttcgctttcttcccttcctttctcgccacgttcgccggctttccccgtcaagctctaaatcgggggctccctttagggttccgatttagtgctttacggcacctcgaccccaaaaaacttgatttgggtgatggttcacgtagtgggccatcgccctgatagacggtttttcgccctttgacgttggagtccacgttctttaatagtggactcttgttccaaactggaacaacactcaaccctatctcgggctattcttttgatttataagggattttgccgatttcggaaccaccatcaaacaggattttcgcctgctggggcaaaccagcgtggaccgcttgctgcaactctctcagggccaggcggtgaagggcaatcagctgttgcccgtctcactggtgaaaagaaaaaccaccccagtacattaaaaacgtccgcaatgtgttattaagttgtctaagcgtcaatttgtttacaccacaatatatcctgccaccagccagccaacagctccccgaccggcagctcggcacaaaatcaccactcgatacaggcagcccatcagtccgggacggcgtcagcgggagagccgttgtaaggcggcagactttgctcatgttaccgatgctattcggaagaacggcaactaagctgccgggtttgaaacacggatgatctcgcggagggtagcatgttgattgtaacgatgacagagcgttgctgcctgtgatcaaatatcatctccctcgcagagatccgaattatcagccttcttattcatttctcgcttaaccgtgacaggctgtcgatcttgagaactatgccgacataataggaaatcgctggataaagccgctgaggaagctgagtggcgctatttctttagaagtgaacgttgacgatatcaactcccctatccattgctcaccgaatggtacaggtcggggacccgaagttccgactgtcggcctgatgcatccccggctgatcgaccccagatctggggctgagaaagcccagtaaggaaacaactgtaggttcgagtcgcgagatcccccggaaccaaaggaagtaggttaaacccgctccgatcaggccgagccacgccaggccgagaacattggttcctgtaggcatcgggattggcggatcaaacactaaagctactggaacgagcagaagtcctccggccgccagttgccaggcggtaaaggtgagcagaggcacgggaggttgccacttgcgggtcagcacggttccgaacgccatggaaaccgcccccgccaggcccgctgcgacgccgacaggatctagcgctgcgtttggtgtcaacaccaacagcgccacgcccgcagttccgcaaatagcccccaggaccgccatcaatcgtatcgggctacctagcagagcggcagagatgaacacgaccatcagcggctgcacagcgcctaccgtcgccgcgaccccgcccggcaggcggtagaccgaaataaacaacaagctccagaatagcgaaatattaagtgcgccgaggatgaagatgcgcatccaccagattcccgttggaatctgtcggacgatcatcacgagcaataaacccgccggcaacgcccgcagcagcataccggcgacccctcggcctcgctgttcgggctccacgaaaacgccggacagatgcgccttgtgagcgtccttggggccgtcctcctgtttgaagaccgacagcccaatgatctcgccgtcgatgtaggcgccgaatgccacggcatctcgcaaccgttcagcgaacgcctccatgggctttttctcctcgtgctcgtaaacggacccgaacatctctggagctttcttcagggccgacaatcggatctcgcggaaatcctgcacgtcggccgctccaagccgtcgaatctgagccttaatcacaattgtcaattttaatcctctgtttatcggcagttcgtagagcgcgccgtgcgtcccgagcgatactgagcgaagcaagtgcgtcgagcagtgcccgcttgttcctgaaatgccagtaaagcgctggctgctgaacccccagccggaactgaccccacaaggccctagcgtttgcaatgcaccaggtcatcattgacccaggcgtgttccaccaggccgctgcctcgcaactcttcgcaggcttcgccgacctgctcgcgccacttcttcacgcgggtggaatccgatccgcacatgaggcggaaggtttccagcttgagcgggtacggctcccggtgcgagctgaaatagtcgaacatccgtcgggccgtcggcgacagcttgcggtacttctcccatatgaatttcgtgtagtggtcgccagcaaacagcacgacgatttcctcgtcgatcaggacctggcaacgggacgttttcttgccacggtccaggacgcggaagcggtgcagcagcgacaccgattccaggtgcccaacgcggtcggacgtgaagcccatcgccgtcgcctgtaggcgcgacaggcattcctcggccttcgtgtaataccggccattgatcgaccagcccaggtcctggcaaagctcgtagaacgtgaaggtgatcggctcgccgataggggtgcgcttcgcgtactccaacacctgctgccacaccagttcgtcatcgtcggcccgcagctcgacgccggtgtaggtgatcttcacgtccttgttgacgtggaaaatgaccttgttttgcagcgcctcgcgcgggattttcttgttgcgcgtggtgaacagggcagagcgggccgtgtcgtttggcatcgctcgcatcgtgtccggccacggcgcaatatcgaacaaggaaagctgcatttccttgatctgctgcttcgtgtgtttcagcaacgcggcctgcttggcctcgctgacctgttttgccaggtcctcgccggcggtttttcgcttcttggtcgtcatagttcctcgcgtgtcgatggtcatcgacttcgccaaacctgccgcctcctgttcgagacgacgcgaacgctccacggcggccgatggcgcgggcagggcagggggagccagttgcacgctgtcgcgctcgatcttggccgtagcttgctggaccatcgagccgacggactggaaggtttcgcggggcgcacgcatgacggtgcggcttgcgatggtttcggcatcctcggcggaaaaccccgcgtcgatcagttcttgcctgtatgccttccggtcaaacgtccgattcattcaccctccttgcgggattgccccgactcacgccggggcaatgtgcccttattcctgatttgacccgcctggtgccttggtgtccagataatccaccttatcggcaatgaagtcggtcccgtagaccgtctggccgtccttctcgtacttggtattccgaatcttgccctgcacgaataccagcgaccccttgcccaaatacttgccgtgggcctcggcctgagagccaaaacacttgatgcggaagaagtcggtgcgctcctgcttgtcgccggcatcgttgcgccacatctaggtactaaaacaattcatccagtaaaatataatattttattttctcccaatcaggcttgatccccagtaagtcaaaaaatagctcgacatactgttcttccccgatatcctccctgatcgaccggacgcagaaggcaatgtcataccacttgtccgccctgccgcttctcccaagatcaataaagccacttactttgccatctttcacaaagatgttgctgtctcccaggtcgccgtgggaaaagacaagttcctcttcgggcttttccgtctttaaaaaatcatacagctcgcgcggatctttaaatggagtgtcttcttcccagttttcgcaatccacatcggccagatcgttattcagtaagtaatccaattcggctaagcggctgtctaagctattcgtatagggacaatccgatatgtcgatggagtgaaagagcctgatgcactccgcatacagctcgataatcttttcagggctttgttcatcttcatactcttccgagcaaaggacgccatcggcctcactcatgagcagattgctccagccatcatgccgttcaaagtgcaggacctttggaacaggcagctttccttccagccatagcatcatgtccttttcccgttccacatcataggtggtccctttataccggctgtccgtcatttttaaatataggttttcattttctcccaccagcttatataccttagcaggagacattccttccgtatcttttacgcagcggtatttttcgatcagttttttcaattccggtgatattctcattttagccatttattatttccttcctcttttctacagtatttaaagataccccaagaagctaattataacaagacgaactccaattcactgttccttgcattctaaaaccttaaataccagaaaacagctttttcaaagttgttttcaaagttggcgtataacatagtatcgacggagccgattttgaaaccacaattatgggtgatgctgccaacttactgatttagtgtatgatggtgtttttgaggtgctccagtggcttctgtgtctatcagctgtccctcctgttcagctactgacggggtggtgcgtaacggcaaaagcaccgccggacatcagcgctatctctgctctcactgccgtaaaacatggcaactgcagttcacttacaccgcttctcaacccggtacgcaccagaaaatcattgatatggccatgaatggcgttggatgccgggcaacagcccgcattatgggcgttggcctcaacacgattttacgtcacttaaaaaactcaggccgcagtcggtaacctcgcgcatacagccgggcagtgacgtcatcgtctgcgcggaaatggacgaacagtggggctatgtcggggctaaatcgcgccagcgctggctgttttacgcgtatgacagtctccggaagacggttgttgcgcacgtattcggtgaacgcactatggcgacgctggggcgtcttatgagcctgctgtcaccctttgacgtggtgatatggatgacggatggctggccgctgtatgaatcccgcctgaagggaaagctgcacgtaatcagcaagcgatatacgcagcgaattgagcggcataacctgaatctgaggcagcacctggcacggctgggacggaagtcgctgtcgttctcaaaatcggtggagctgcatgacaaagtcatcgggcattatctgaacataaaacactatcaataagttggagtcattacccaattatgatagaatttacaagctataaggttattgtcctgggtttcaagcattagtccatgcaagtttttatgctttgcccattctatagatatattgataagcgcgctgcctatgccttgccccctgaaatccttacatacggcgatatcttctatataaaagatatattatcttatcagtattgtcaatatattcaaggcaatctgcctcctcatcctcttcatcctcttcgtcttggtagctttttaaatatggcgcttcatagagtaattctgtaaaggtccaattctcgttttcatacctcggtataatcttacctatcacctcaaatggttcgctgggtttatcgcacccccgaacacgagcacggcacccgcgaccactatgccaagaatgcccaaggtaaaaattgccggccccgccatgaagtccgtgaatgccccgacggccgaagtgaagggcaggccgccacccaggccgccgccctcactgcccggcacctggtcgctgaatgtcgatgccagcacctgcggcacgtcaatgcttccgggcgtcgcgctcgggctgatcgcccatcccgttactgccccgatcccggcaatggcaaggactgccagcgctgccatttttggggtgaggccgttcgcggccgaggggcgcagcccctggggggatgggaggcccgcgttagcgggccgggagggttcgagaagggggggcaccccccttcggcgtgcgcggtcacgcgcacagggcgcagccctggttaaaaacaaggtttataaatattggtttaaaagcaggttaaaagacaggttagcggtggccgaaaaacgggcggaaacccttgcaaatgctggattttctgcctgtggacagcccctcaaatgtcaataggtgcgcccctcatctgtcagcactctgcccctcaagtgtcaaggatcgcgcccctcatctgtcagtagtcgcgcccctcaagtgtcaataccgcagggcacttatccccaggcttgtccacatcatctgtgggaaactcgcgtaaaatcaggcgttttcgccgatttgcgaggctggccagctccacgtcgccggccgaaatcgagcctgcccctcatctgtcaacgccgcgccgggtgagtcggcccctcaagtgtcaacgtccgcccctcatctgtcagtgagggccaagttttccgcgaggtatccacaacgccggcggccgcggtgtctcgcacacggcttcgacggcgtttctggcgcgtttgcagggccatagacggccgccagcccagcggcgagggcaaccagcccgg + + + \ No newline at end of file diff --git a/node_modules/xml2js/incompat.coffee b/node_modules/xml2js/incompat.coffee new file mode 100644 index 0000000..5533965 --- /dev/null +++ b/node_modules/xml2js/incompat.coffee @@ -0,0 +1,5 @@ +{parseString} = require './lib/xml2js' +xml = '' +parseString xml, (err, result) -> + console.dir result + diff --git a/node_modules/xml2js/incompat2.js b/node_modules/xml2js/incompat2.js new file mode 100644 index 0000000..31cfbc8 --- /dev/null +++ b/node_modules/xml2js/incompat2.js @@ -0,0 +1,7 @@ +var xml2js = require('xml2js'); +var parser = new xml2js.Parser({ + mergeAttrs: true +}); +parser.parseString('', function (err, result) { + console.dir(result); +}); diff --git a/node_modules/xml2js/lib/bom.js b/node_modules/xml2js/lib/bom.js new file mode 100644 index 0000000..d7f226e --- /dev/null +++ b/node_modules/xml2js/lib/bom.js @@ -0,0 +1,15 @@ +// Generated by CoffeeScript 1.7.1 +(function() { + var xml2js; + + xml2js = require('../lib/xml2js'); + + exports.stripBOM = function(str) { + if (str[0] === '\uFEFF') { + return str.substring(1); + } else { + return str; + } + }; + +}).call(this); diff --git a/node_modules/xml2js/lib/processors.js b/node_modules/xml2js/lib/processors.js new file mode 100644 index 0000000..aeadaef --- /dev/null +++ b/node_modules/xml2js/lib/processors.js @@ -0,0 +1,19 @@ +// Generated by CoffeeScript 1.7.1 +(function() { + var prefixMatch; + + prefixMatch = new RegExp(/(?!xmlns)^.*:/); + + exports.normalize = function(str) { + return str.toLowerCase(); + }; + + exports.firstCharLowerCase = function(str) { + return str.charAt(0).toLowerCase() + str.slice(1); + }; + + exports.stripPrefix = function(str) { + return str.replace(prefixMatch, ''); + }; + +}).call(this); diff --git a/node_modules/xml2js/lib/xml2js.js b/node_modules/xml2js/lib/xml2js.js new file mode 100644 index 0000000..7c1cad3 --- /dev/null +++ b/node_modules/xml2js/lib/xml2js.js @@ -0,0 +1,436 @@ +// Generated by CoffeeScript 1.7.1 +(function() { + var bom, builder, events, isEmpty, processName, processors, sax, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + + sax = require('sax'); + + events = require('events'); + + builder = require('xmlbuilder'); + + bom = require('./bom'); + + processors = require('./processors'); + + isEmpty = function(thing) { + return typeof thing === "object" && (thing != null) && Object.keys(thing).length === 0; + }; + + processName = function(processors, processedName) { + var process, _i, _len; + for (_i = 0, _len = processors.length; _i < _len; _i++) { + process = processors[_i]; + processedName = process(processedName); + } + return processedName; + }; + + exports.processors = processors; + + exports.defaults = { + "0.1": { + explicitCharkey: false, + trim: true, + normalize: true, + normalizeTags: false, + attrkey: "@", + charkey: "#", + explicitArray: false, + ignoreAttrs: false, + mergeAttrs: false, + explicitRoot: false, + validator: null, + xmlns: false, + explicitChildren: false, + childkey: '@@', + charsAsChildren: false, + async: false, + strict: true, + attrNameProcessors: null, + tagNameProcessors: null + }, + "0.2": { + explicitCharkey: false, + trim: false, + normalize: false, + normalizeTags: false, + attrkey: "$", + charkey: "_", + explicitArray: true, + ignoreAttrs: false, + mergeAttrs: false, + explicitRoot: true, + validator: null, + xmlns: false, + explicitChildren: false, + childkey: '$$', + charsAsChildren: false, + async: false, + strict: true, + attrNameProcessors: null, + tagNameProcessors: null, + rootName: 'root', + xmldec: { + 'version': '1.0', + 'encoding': 'UTF-8', + 'standalone': true + }, + doctype: null, + renderOpts: { + 'pretty': true, + 'indent': ' ', + 'newline': '\n' + }, + headless: false + } + }; + + exports.ValidationError = (function(_super) { + __extends(ValidationError, _super); + + function ValidationError(message) { + this.message = message; + } + + return ValidationError; + + })(Error); + + exports.Builder = (function() { + function Builder(opts) { + var key, value, _ref; + this.options = {}; + _ref = exports.defaults["0.2"]; + for (key in _ref) { + if (!__hasProp.call(_ref, key)) continue; + value = _ref[key]; + this.options[key] = value; + } + for (key in opts) { + if (!__hasProp.call(opts, key)) continue; + value = opts[key]; + this.options[key] = value; + } + } + + Builder.prototype.buildObject = function(rootObj) { + var attrkey, charkey, render, rootElement, rootName; + attrkey = this.options.attrkey; + charkey = this.options.charkey; + if ((Object.keys(rootObj).length === 1) && (this.options.rootName === exports.defaults['0.2'].rootName)) { + rootName = Object.keys(rootObj)[0]; + rootObj = rootObj[rootName]; + } else { + rootName = this.options.rootName; + } + render = function(element, obj) { + var attr, child, entry, index, key, value, _ref, _ref1; + if (typeof obj !== 'object') { + element.txt(obj); + } else { + for (key in obj) { + if (!__hasProp.call(obj, key)) continue; + child = obj[key]; + if (key === attrkey) { + if (typeof child === "object") { + for (attr in child) { + value = child[attr]; + element = element.att(attr, value); + } + } + } else if (key === charkey) { + element = element.txt(child); + } else if (typeof child === 'object' && ((child != null ? child.constructor : void 0) != null) && ((child != null ? (_ref = child.constructor) != null ? _ref.name : void 0 : void 0) != null) && (child != null ? (_ref1 = child.constructor) != null ? _ref1.name : void 0 : void 0) === 'Array') { + for (index in child) { + if (!__hasProp.call(child, index)) continue; + entry = child[index]; + if (typeof entry === 'string') { + element = element.ele(key, entry).up(); + } else { + element = arguments.callee(element.ele(key), entry).up(); + } + } + } else if (typeof child === "object") { + element = arguments.callee(element.ele(key), child).up(); + } else { + element = element.ele(key, child.toString()).up(); + } + } + } + return element; + }; + rootElement = builder.create(rootName, this.options.xmldec, this.options.doctype, { + headless: this.options.headless + }); + return render(rootElement, rootObj).end(this.options.renderOpts); + }; + + return Builder; + + })(); + + exports.Parser = (function(_super) { + __extends(Parser, _super); + + function Parser(opts) { + this.parseString = __bind(this.parseString, this); + this.reset = __bind(this.reset, this); + this.assignOrPush = __bind(this.assignOrPush, this); + var key, value, _ref; + if (!(this instanceof exports.Parser)) { + return new exports.Parser(opts); + } + this.options = {}; + _ref = exports.defaults["0.2"]; + for (key in _ref) { + if (!__hasProp.call(_ref, key)) continue; + value = _ref[key]; + this.options[key] = value; + } + for (key in opts) { + if (!__hasProp.call(opts, key)) continue; + value = opts[key]; + this.options[key] = value; + } + if (this.options.xmlns) { + this.options.xmlnskey = this.options.attrkey + "ns"; + } + if (this.options.normalizeTags) { + if (!this.options.tagNameProcessors) { + this.options.tagNameProcessors = []; + } + this.options.tagNameProcessors.unshift(processors.normalize); + } + this.reset(); + } + + Parser.prototype.assignOrPush = function(obj, key, newValue) { + if (!(key in obj)) { + if (!this.options.explicitArray) { + return obj[key] = newValue; + } else { + return obj[key] = [newValue]; + } + } else { + if (!(obj[key] instanceof Array)) { + obj[key] = [obj[key]]; + } + return obj[key].push(newValue); + } + }; + + Parser.prototype.reset = function() { + var attrkey, charkey, ontext, stack; + this.removeAllListeners(); + this.saxParser = sax.parser(this.options.strict, { + trim: false, + normalize: false, + xmlns: this.options.xmlns + }); + this.saxParser.errThrown = false; + this.saxParser.onerror = (function(_this) { + return function(error) { + _this.saxParser.resume(); + if (!_this.saxParser.errThrown) { + _this.saxParser.errThrown = true; + return _this.emit("error", error); + } + }; + })(this); + this.saxParser.ended = false; + this.EXPLICIT_CHARKEY = this.options.explicitCharkey; + this.resultObject = null; + stack = []; + attrkey = this.options.attrkey; + charkey = this.options.charkey; + this.saxParser.onopentag = (function(_this) { + return function(node) { + var key, newValue, obj, processedKey, _ref; + obj = {}; + obj[charkey] = ""; + if (!_this.options.ignoreAttrs) { + _ref = node.attributes; + for (key in _ref) { + if (!__hasProp.call(_ref, key)) continue; + if (!(attrkey in obj) && !_this.options.mergeAttrs) { + obj[attrkey] = {}; + } + newValue = node.attributes[key]; + processedKey = _this.options.attrNameProcessors ? processName(_this.options.attrNameProcessors, key) : key; + if (_this.options.mergeAttrs) { + _this.assignOrPush(obj, processedKey, newValue); + } else { + obj[attrkey][processedKey] = newValue; + } + } + } + obj["#name"] = _this.options.tagNameProcessors ? processName(_this.options.tagNameProcessors, node.name) : node.name; + if (_this.options.xmlns) { + obj[_this.options.xmlnskey] = { + uri: node.uri, + local: node.local + }; + } + return stack.push(obj); + }; + })(this); + this.saxParser.onclosetag = (function(_this) { + return function() { + var cdata, emptyStr, err, node, nodeName, obj, old, s, xpath; + obj = stack.pop(); + nodeName = obj["#name"]; + delete obj["#name"]; + cdata = obj.cdata; + delete obj.cdata; + s = stack[stack.length - 1]; + if (obj[charkey].match(/^\s*$/) && !cdata) { + emptyStr = obj[charkey]; + delete obj[charkey]; + } else { + if (_this.options.trim) { + obj[charkey] = obj[charkey].trim(); + } + if (_this.options.normalize) { + obj[charkey] = obj[charkey].replace(/\s{2,}/g, " ").trim(); + } + if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) { + obj = obj[charkey]; + } + } + if (isEmpty(obj)) { + obj = _this.options.emptyTag !== void 0 ? _this.options.emptyTag : emptyStr; + } + if (_this.options.validator != null) { + xpath = "/" + ((function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = stack.length; _i < _len; _i++) { + node = stack[_i]; + _results.push(node["#name"]); + } + return _results; + })()).concat(nodeName).join("/"); + try { + obj = _this.options.validator(xpath, s && s[nodeName], obj); + } catch (_error) { + err = _error; + _this.emit("error", err); + } + } + if (_this.options.explicitChildren && !_this.options.mergeAttrs && typeof obj === 'object') { + node = {}; + if (_this.options.attrkey in obj) { + node[_this.options.attrkey] = obj[_this.options.attrkey]; + delete obj[_this.options.attrkey]; + } + if (!_this.options.charsAsChildren && _this.options.charkey in obj) { + node[_this.options.charkey] = obj[_this.options.charkey]; + delete obj[_this.options.charkey]; + } + if (Object.getOwnPropertyNames(obj).length > 0) { + node[_this.options.childkey] = obj; + } + obj = node; + } + if (stack.length > 0) { + return _this.assignOrPush(s, nodeName, obj); + } else { + if (_this.options.explicitRoot) { + old = obj; + obj = {}; + obj[nodeName] = old; + } + _this.resultObject = obj; + _this.saxParser.ended = true; + return _this.emit("end", _this.resultObject); + } + }; + })(this); + ontext = (function(_this) { + return function(text) { + var s; + s = stack[stack.length - 1]; + if (s) { + s[charkey] += text; + return s; + } + }; + })(this); + this.saxParser.ontext = ontext; + return this.saxParser.oncdata = (function(_this) { + return function(text) { + var s; + s = ontext(text); + if (s) { + return s.cdata = true; + } + }; + })(this); + }; + + Parser.prototype.parseString = function(str, cb) { + var err; + if ((cb != null) && typeof cb === "function") { + this.on("end", function(result) { + this.reset(); + if (this.options.async) { + return process.nextTick(function() { + return cb(null, result); + }); + } else { + return cb(null, result); + } + }); + this.on("error", function(err) { + this.reset(); + if (this.options.async) { + return process.nextTick(function() { + return cb(err); + }); + } else { + return cb(err); + } + }); + } + if (str.toString().trim() === '') { + this.emit("end", null); + return true; + } + try { + return this.saxParser.write(bom.stripBOM(str.toString())).close(); + } catch (_error) { + err = _error; + if (!(this.saxParser.errThrown || this.saxParser.ended)) { + this.emit('error', err); + return this.saxParser.errThrown = true; + } + } + }; + + return Parser; + + })(events.EventEmitter); + + exports.parseString = function(str, a, b) { + var cb, options, parser; + if (b != null) { + if (typeof b === 'function') { + cb = b; + } + if (typeof a === 'object') { + options = a; + } + } else { + if (typeof a === 'function') { + cb = a; + } + options = {}; + } + parser = new exports.Parser(options); + return parser.parseString(str, cb); + }; + +}).call(this); diff --git a/node_modules/xml2js/node_modules/sax/AUTHORS b/node_modules/xml2js/node_modules/sax/AUTHORS new file mode 100644 index 0000000..7145cbc --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/AUTHORS @@ -0,0 +1,10 @@ +# contributors sorted by whether or not they're me. +Isaac Z. Schlueter +Stein Martin Hustad +Mikeal Rogers +Laurie Harper +Jann Horn +Elijah Insua +Henry Rawas +Justin Makeig +Mike Schilling diff --git a/node_modules/xml2js/node_modules/sax/LICENSE b/node_modules/xml2js/node_modules/sax/LICENSE new file mode 100644 index 0000000..9824864 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/LICENSE @@ -0,0 +1,57 @@ +Copyright (c) Isaac Z. Schlueter ("Author") +All rights reserved. + +The BSD License + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +The file "examples/strict.dtd" is licensed by the W3C and used according +to the terms of the W3C SOFTWARE NOTICE AND LICENSE. See LICENSE-W3C.html +for details. + + +"String.fromCodePoint" used under the terms of the MIT license. Its license +follows: + + Copyright Mathias Bynens + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/xml2js/node_modules/sax/LICENSE-W3C.html b/node_modules/xml2js/node_modules/sax/LICENSE-W3C.html new file mode 100644 index 0000000..a611e3f --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/LICENSE-W3C.html @@ -0,0 +1,188 @@ + +W3C Software Notice and License
            + + + +
            +

            + W3C + +

            + +
            + + + +
            +
            + +
            + + +
            +
            + +
            + + +
            +
            +
            + +
            +
            +

            W3C Software Notice and License

            +
            +
            +

            This work (and included software, documentation such as READMEs, or other +related items) is being provided by the copyright holders under the following +license.

            +

            License

            + +

            +By obtaining, using and/or copying this work, you (the licensee) +agree that you have read, understood, and will comply with the following +terms and conditions.

            + +

            Permission to copy, modify, and distribute this software and its +documentation, with or without modification, for any purpose and without +fee or royalty is hereby granted, provided that you include the following on +ALL copies of the software and documentation or portions thereof, including +modifications:

            + +
            • The full text of this NOTICE in a location viewable to users of the + redistributed or derivative work.
            • Any pre-existing intellectual property disclaimers, notices, or terms + and conditions. If none exist, the W3C Software Short + Notice should be included (hypertext is preferred, text is permitted) + within the body of any redistributed or derivative code.
            • Notice of any changes or modifications to the files, including the date + changes were made. (We recommend you provide URIs to the location from + which the code is derived.)
            + +

            Disclaimers

            + +

            THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS +MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR +PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE +ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.

            + +

            COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR +DOCUMENTATION.

            + +

            The name and trademarks of copyright holders may NOT be used in +advertising or publicity pertaining to the software without specific, written +prior permission. Title to copyright in this software and any associated +documentation will at all times remain with copyright holders.

            + +

            Notes

            + +

            This version: http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231

            + +

            This formulation of W3C's notice and license became active on December 31 +2002. This version removes the copyright ownership notice such that this +license can be used with materials other than those owned by the W3C, +reflects that ERCIM is now a host of the W3C, includes references to this +specific dated version of the license, and removes the ambiguous grant of +"use". Otherwise, this version is the same as the previous +version and is written so as to preserve the Free +Software Foundation's assessment of GPL compatibility and OSI's certification +under the Open Source +Definition.

            +
            +
            +
            +
            + + + +
            + +
            diff --git a/node_modules/xml2js/node_modules/sax/README.md b/node_modules/xml2js/node_modules/sax/README.md new file mode 100644 index 0000000..c965242 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/README.md @@ -0,0 +1,216 @@ +# sax js + +A sax-style parser for XML and HTML. + +Designed with [node](http://nodejs.org/) in mind, but should work fine in +the browser or other CommonJS implementations. + +## What This Is + +* A very simple tool to parse through an XML string. +* A stepping stone to a streaming HTML parser. +* A handy way to deal with RSS and other mostly-ok-but-kinda-broken XML + docs. + +## What This Is (probably) Not + +* An HTML Parser - That's a fine goal, but this isn't it. It's just + XML. +* A DOM Builder - You can use it to build an object model out of XML, + but it doesn't do that out of the box. +* XSLT - No DOM = no querying. +* 100% Compliant with (some other SAX implementation) - Most SAX + implementations are in Java and do a lot more than this does. +* An XML Validator - It does a little validation when in strict mode, but + not much. +* A Schema-Aware XSD Thing - Schemas are an exercise in fetishistic + masochism. +* A DTD-aware Thing - Fetching DTDs is a much bigger job. + +## Regarding `Hello, world!').close(); + + // stream usage + // takes the same options as the parser + var saxStream = require("sax").createStream(strict, options) + saxStream.on("error", function (e) { + // unhandled errors will throw, since this is a proper node + // event emitter. + console.error("error!", e) + // clear the error + this._parser.error = null + this._parser.resume() + }) + saxStream.on("opentag", function (node) { + // same object as above + }) + // pipe is supported, and it's readable/writable + // same chunks coming in also go out. + fs.createReadStream("file.xml") + .pipe(saxStream) + .pipe(fs.createWriteStream("file-copy.xml")) + + + +## Arguments + +Pass the following arguments to the parser function. All are optional. + +`strict` - Boolean. Whether or not to be a jerk. Default: `false`. + +`opt` - Object bag of settings regarding string formatting. All default to `false`. + +Settings supported: + +* `trim` - Boolean. Whether or not to trim text and comment nodes. +* `normalize` - Boolean. If true, then turn any whitespace into a single + space. +* `lowercase` - Boolean. If true, then lowercase tag names and attribute names + in loose mode, rather than uppercasing them. +* `xmlns` - Boolean. If true, then namespaces are supported. +* `position` - Boolean. If false, then don't track line/col/position. + +## Methods + +`write` - Write bytes onto the stream. You don't have to do this all at +once. You can keep writing as much as you want. + +`close` - Close the stream. Once closed, no more data may be written until +it is done processing the buffer, which is signaled by the `end` event. + +`resume` - To gracefully handle errors, assign a listener to the `error` +event. Then, when the error is taken care of, you can call `resume` to +continue parsing. Otherwise, the parser will not continue while in an error +state. + +## Members + +At all times, the parser object will have the following members: + +`line`, `column`, `position` - Indications of the position in the XML +document where the parser currently is looking. + +`startTagPosition` - Indicates the position where the current tag starts. + +`closed` - Boolean indicating whether or not the parser can be written to. +If it's `true`, then wait for the `ready` event to write again. + +`strict` - Boolean indicating whether or not the parser is a jerk. + +`opt` - Any options passed into the constructor. + +`tag` - The current tag being dealt with. + +And a bunch of other stuff that you probably shouldn't touch. + +## Events + +All events emit with a single argument. To listen to an event, assign a +function to `on`. Functions get executed in the this-context of +the parser object. The list of supported events are also in the exported +`EVENTS` array. + +When using the stream interface, assign handlers using the EventEmitter +`on` function in the normal fashion. + +`error` - Indication that something bad happened. The error will be hanging +out on `parser.error`, and must be deleted before parsing can continue. By +listening to this event, you can keep an eye on that kind of stuff. Note: +this happens *much* more in strict mode. Argument: instance of `Error`. + +`text` - Text node. Argument: string of text. + +`doctype` - The ``. Argument: +object with `name` and `body` members. Attributes are not parsed, as +processing instructions have implementation dependent semantics. + +`sgmldeclaration` - Random SGML declarations. Stuff like `` +would trigger this kind of event. This is a weird thing to support, so it +might go away at some point. SAX isn't intended to be used to parse SGML, +after all. + +`opentag` - An opening tag. Argument: object with `name` and `attributes`. +In non-strict mode, tag names are uppercased, unless the `lowercase` +option is set. If the `xmlns` option is set, then it will contain +namespace binding information on the `ns` member, and will have a +`local`, `prefix`, and `uri` member. + +`closetag` - A closing tag. In loose mode, tags are auto-closed if their +parent closes. In strict mode, well-formedness is enforced. Note that +self-closing tags will have `closeTag` emitted immediately after `openTag`. +Argument: tag name. + +`attribute` - An attribute node. Argument: object with `name` and `value`. +In non-strict mode, attribute names are uppercased, unless the `lowercase` +option is set. If the `xmlns` option is set, it will also contains namespace +information. + +`comment` - A comment node. Argument: the string of the comment. + +`opencdata` - The opening tag of a ``) of a `` tags trigger a `"script"` +event, and their contents are not checked for special xml characters. +If you pass `noscript: true`, then this behavior is suppressed. + +## Reporting Problems + +It's best to write a failing test if you find an issue. I will always +accept pull requests with failing tests if they demonstrate intended +behavior, but it is very hard to figure out what issue you're describing +without a test. Writing a test is also the best way for you yourself +to figure out if you really understand the issue you think you have with +sax-js. diff --git a/node_modules/xml2js/node_modules/sax/examples/big-not-pretty.xml b/node_modules/xml2js/node_modules/sax/examples/big-not-pretty.xml new file mode 100644 index 0000000..fb5265d --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/examples/big-not-pretty.xml @@ -0,0 +1,8002 @@ + + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + + something blerm a bit down here + diff --git a/node_modules/xml2js/node_modules/sax/examples/example.js b/node_modules/xml2js/node_modules/sax/examples/example.js new file mode 100644 index 0000000..7b0246e --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/examples/example.js @@ -0,0 +1,29 @@ + +var fs = require("fs"), + util = require("util"), + path = require("path"), + xml = fs.readFileSync(path.join(__dirname, "test.xml"), "utf8"), + sax = require("../lib/sax"), + strict = sax.parser(true), + loose = sax.parser(false, {trim:true}), + inspector = function (ev) { return function (data) { + console.error("%s %s %j", this.line+":"+this.column, ev, data); + }}; + +sax.EVENTS.forEach(function (ev) { + loose["on"+ev] = inspector(ev); +}); +loose.onend = function () { + console.error("end"); + console.error(loose); +}; + +// do this in random bits at a time to verify that it works. +(function () { + if (xml) { + var c = Math.ceil(Math.random() * 1000) + loose.write(xml.substr(0,c)); + xml = xml.substr(c); + process.nextTick(arguments.callee); + } else loose.close(); +})(); diff --git a/node_modules/xml2js/node_modules/sax/examples/get-products.js b/node_modules/xml2js/node_modules/sax/examples/get-products.js new file mode 100644 index 0000000..9e8d74a --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/examples/get-products.js @@ -0,0 +1,58 @@ +// pull out /GeneralSearchResponse/categories/category/items/product tags +// the rest we don't care about. + +var sax = require("../lib/sax.js") +var fs = require("fs") +var path = require("path") +var xmlFile = path.resolve(__dirname, "shopping.xml") +var util = require("util") +var http = require("http") + +fs.readFile(xmlFile, function (er, d) { + http.createServer(function (req, res) { + if (er) throw er + var xmlstr = d.toString("utf8") + + var parser = sax.parser(true) + var products = [] + var product = null + var currentTag = null + + parser.onclosetag = function (tagName) { + if (tagName === "product") { + products.push(product) + currentTag = product = null + return + } + if (currentTag && currentTag.parent) { + var p = currentTag.parent + delete currentTag.parent + currentTag = p + } + } + + parser.onopentag = function (tag) { + if (tag.name !== "product" && !product) return + if (tag.name === "product") { + product = tag + } + tag.parent = currentTag + tag.children = [] + tag.parent && tag.parent.children.push(tag) + currentTag = tag + } + + parser.ontext = function (text) { + if (currentTag) currentTag.children.push(text) + } + + parser.onend = function () { + var out = util.inspect(products, false, 3, true) + res.writeHead(200, {"content-type":"application/json"}) + res.end("{\"ok\":true}") + // res.end(JSON.stringify(products)) + } + + parser.write(xmlstr).end() + }).listen(1337) +}) diff --git a/node_modules/xml2js/node_modules/sax/examples/hello-world.js b/node_modules/xml2js/node_modules/sax/examples/hello-world.js new file mode 100644 index 0000000..cbfa518 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/examples/hello-world.js @@ -0,0 +1,4 @@ +require("http").createServer(function (req, res) { + res.writeHead(200, {"content-type":"application/json"}) + res.end(JSON.stringify({ok: true})) +}).listen(1337) diff --git a/node_modules/xml2js/node_modules/sax/examples/not-pretty.xml b/node_modules/xml2js/node_modules/sax/examples/not-pretty.xml new file mode 100644 index 0000000..9592852 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/examples/not-pretty.xml @@ -0,0 +1,8 @@ + + something blerm a bit down here diff --git a/node_modules/xml2js/node_modules/sax/examples/pretty-print.js b/node_modules/xml2js/node_modules/sax/examples/pretty-print.js new file mode 100644 index 0000000..cd6aca9 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/examples/pretty-print.js @@ -0,0 +1,74 @@ +var sax = require("../lib/sax") + , printer = sax.createStream(false, {lowercasetags:true, trim:true}) + , fs = require("fs") + +function entity (str) { + return str.replace('"', '"') +} + +printer.tabstop = 2 +printer.level = 0 +printer.indent = function () { + print("\n") + for (var i = this.level; i > 0; i --) { + for (var j = this.tabstop; j > 0; j --) { + print(" ") + } + } +} +printer.on("opentag", function (tag) { + this.indent() + this.level ++ + print("<"+tag.name) + for (var i in tag.attributes) { + print(" "+i+"=\""+entity(tag.attributes[i])+"\"") + } + print(">") +}) + +printer.on("text", ontext) +printer.on("doctype", ontext) +function ontext (text) { + this.indent() + print(text) +} + +printer.on("closetag", function (tag) { + this.level -- + this.indent() + print("") +}) + +printer.on("cdata", function (data) { + this.indent() + print("") +}) + +printer.on("comment", function (comment) { + this.indent() + print("") +}) + +printer.on("error", function (error) { + console.error(error) + throw error +}) + +if (!process.argv[2]) { + throw new Error("Please provide an xml file to prettify\n"+ + "TODO: read from stdin or take a file") +} +var xmlfile = require("path").join(process.cwd(), process.argv[2]) +var fstr = fs.createReadStream(xmlfile, { encoding: "utf8" }) + +function print (c) { + if (!process.stdout.write(c)) { + fstr.pause() + } +} + +process.stdout.on("drain", function () { + fstr.resume() +}) + +fstr.pipe(printer) diff --git a/node_modules/xml2js/node_modules/sax/examples/shopping.xml b/node_modules/xml2js/node_modules/sax/examples/shopping.xml new file mode 100644 index 0000000..223c6c6 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/examples/shopping.xml @@ -0,0 +1,2 @@ + +sandbox3.1 r31.Kadu4DC.phase357782011.10.06 15:37:23 PSTp2.a121bc2aaf029435dce62011-10-21T18:38:45.982-04:00P0Y0M0DT0H0M0.169S1112You are currently using the SDC API sandbox environment! No clicks to merchant URLs from this response will be paid. Please change the host of your API requests to 'publisher.api.shopping.com' when you have finished development and testinghttp://statTest.dealtime.com/pixel/noscript?PV_EvnTyp=APPV&APPV_APITSP=10%2F21%2F11_06%3A38%3A45_PM&APPV_DSPRQSID=p2.a121bc2aaf029435dce6&APPV_IMGURL=http://img.shopping.com/sc/glb/sdc_logo_106x19.gif&APPV_LI_LNKINID=7000610&APPV_LI_SBMKYW=nikon&APPV_MTCTYP=1000&APPV_PRTID=2002&APPV_BrnID=14804http://www.shopping.com/digital-cameras/productsDigital CamerasDigital CamerasElectronicshttp://www.shopping.com/xCH-electronics-nikon~linkin_id-7000610?oq=nikonCameras and Photographyhttp://www.shopping.com/xCH-cameras_and_photography-nikon~linkin_id-7000610?oq=nikonDigital Camerashttp://www.shopping.com/digital-cameras/nikon/products?oq=nikon&linkin_id=7000610nikonnikonDigital Camerashttp://www.shopping.com/digital-cameras/nikon/products?oq=nikon&linkin_id=7000610Nikon D3100 Digital Camera14.2 Megapixel, SLR Camera, 3 in. LCD Screen, With High Definition Video, Weight: 1 lb.The Nikon D3100 digital SLR camera speaks to the growing ranks of enthusiastic D-SLR users and aspiring photographers by providing an easy-to-use and affordable entrance to the world of Nikon D-SLR’s. The 14.2-megapixel D3100 has powerful features, such as the enhanced Guide Mode that makes it easy to unleash creative potential and capture memories with still images and full HD video. Like having a personal photo tutor at your fingertips, this unique feature provides a simple graphical interface on the camera’s LCD that guides users by suggesting and/or adjusting camera settings to achieve the desired end result images. The D3100 is also the world’s first D-SLR to introduce full time auto focus (AF) in Live View and D-Movie mode to effortlessly achieve the critical focus needed when shooting Full HD 1080p video.http://di1.shopping.com/images/pi/93/bc/04/101677489-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=1http://di1.shopping.com/images/pi/93/bc/04/101677489-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=1http://di1.shopping.com/images/pi/93/bc/04/101677489-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=1http://di1.shopping.com/images/pi/93/bc/04/101677489-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=1http://di1.shopping.com/images/pi/93/bc/04/101677489-606x500-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=194.56http://img.shopping.com/sc/pr/sdc_stars_sm_4.5.gifhttp://www.shopping.com/Nikon-D3100/reviews~linkin_id-7000610429.001360.00http://www.shopping.com/Nikon-D3100/prices~linkin_id-7000610http://www.shopping.com/Nikon-D3100/info~linkin_id-7000610Nikon D3100 Digital SLR Camera with 18-55mm NIKKOR VR LensThe Nikon D3100 Digital SLR Camera is an affordable compact and lightweight photographic power-house. It features the all-purpose 18-55mm VR lens a high-resolution 14.2 MP CMOS sensor along with a feature set that's comprehensive yet easy to navigate - the intuitive onboard learn-as-you grow guide mode allows the photographer to understand what the 3100 can do quickly and easily. Capture beautiful pictures and amazing Full HD 1080p movies with sound and full-time autofocus. Availabilty: In Stock!7185Nikonhttp://di102.shopping.com/images/di/2d/5a/57/36424d5a717a366662532d61554c7767615f67-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di102.shopping.com/images/di/2d/5a/57/36424d5a717a366662532d61554c7767615f67-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di102.shopping.com/images/di/2d/5a/57/36424d5a717a366662532d61554c7767615f67-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di102.shopping.com/images/di/2d/5a/57/36424d5a717a366662532d61554c7767615f67-350x350-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1in-stockFree Shipping with Any Purchase!529.000.00799.00http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=647&BEFID=7185&aon=%5E1&MerchantID=475674&crawler_id=475674&dealId=-ZW6BMZqz6fbS-aULwga_g%3D%3D&url=http%3A%2F%2Fwww.fumfie.com%2Fproduct%2F343.5%2Fshopping-com%3F&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon+D3100+Digital+SLR+Camera+with+18-55mm+NIKKOR+VR+Lens&dlprc=529.0&crn=&istrsmrc=1&isathrsl=0&AR=1&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=101677489&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=1&cid=&semid1=&semid2=&IsLps=0&CC=1&SL=1&FS=1&code=&acode=658&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=FumFiehttp://img.shopping.com/cctool/merch_logos/475674.gif866 666 91985604.27http://img.shopping.com/sc/mr/sdc_checks_45.gifhttp://www.shopping.com/xMR-store_fumfie~MRD-475674~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUSF343C5Nikon Nikon D3100 14.2MP Digital SLR Camera with 18-55mm f/3.5-5.6 AF-S DX VR, CamerasNikon D3100 14.2MP Digital SLR Camera with 18-55mm f/3.5-5.6 AF-S DX VR7185Nikonhttp://di109.shopping.com/images/di/6d/64/31/65396c443876644f7534464851664a714b6e67-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://di109.shopping.com/images/di/6d/64/31/65396c443876644f7534464851664a714b6e67-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://di109.shopping.com/images/di/6d/64/31/65396c443876644f7534464851664a714b6e67-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://di109.shopping.com/images/di/6d/64/31/65396c443876644f7534464851664a714b6e67-385x352-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2in-stock549.000.00549.00http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=779&BEFID=7185&aon=%5E1&MerchantID=305814&crawler_id=305814&dealId=md1e9lD8vdOu4FHQfJqKng%3D%3D&url=http%3A%2F%2Fwww.electronics-expo.com%2Findex.php%3Fpage%3Ditem%26id%3DNIKD3100%26source%3DSideCar%26scpid%3D8%26scid%3Dscsho318727%26&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon+Nikon+D3100+14.2MP+Digital+SLR+Camera+with+18-55mm+f%2F3.5-5.6+AF-S+DX+VR%2C+Cameras&dlprc=549.0&crn=&istrsmrc=1&isathrsl=0&AR=9&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=101677489&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=9&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=1&code=&acode=771&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=Electronics Expohttp://img.shopping.com/cctool/merch_logos/305814.gif1-888-707-EXPO3713.90http://img.shopping.com/sc/mr/sdc_checks_4.gifhttp://www.shopping.com/xMR-store_electronics_expo~MRD-305814~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUSNIKD3100Nikon D3100 14.2-Megapixel Digital SLR Camera With 18-55mm Zoom-Nikkor Lens, BlackSplit-second shutter response captures shots other cameras may have missed Helps eliminate the frustration of shutter delay! 14.2-megapixels for enlargements worth framing and hanging. Takes breathtaking 1080p HD movies. ISO sensitivity from 100-1600 for bright or dimly lit settings. 3.0in. color LCD for beautiful, wide-angle framing and viewing. In-camera image editing lets you retouch with no PC. Automatic scene modes include Child, Sports, Night Portrait and more. Accepts SDHC memory cards. Nikon D3100 14.2-Megapixel Digital SLR Camera With 18-55mm Zoom-Nikkor Lens, Black is one of many Digital SLR Cameras available through Office Depot. Made by Nikon.7185Nikonhttp://di109.shopping.com/images/di/79/59/75/61586e4446744359377244556a6b5932616177-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://di109.shopping.com/images/di/79/59/75/61586e4446744359377244556a6b5932616177-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://di109.shopping.com/images/di/79/59/75/61586e4446744359377244556a6b5932616177-250x250-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3in-stock549.990.00699.99http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=698&BEFID=7185&aon=%5E1&MerchantID=467671&crawler_id=467671&dealId=yYuaXnDFtCY7rDUjkY2aaw%3D%3D&url=http%3A%2F%2Flink.mercent.com%2Fredirect.ashx%3Fmr%3AmerchantID%3DOfficeDepot%26mr%3AtrackingCode%3DCEC9669E-6ABC-E011-9F24-0019B9C043EB%26mr%3AtargetUrl%3Dhttp%3A%2F%2Fwww.officedepot.com%2Fa%2Fproducts%2F486292%2FNikon-D3100-142-Megapixel-Digital-SLR%2F%253fcm_mmc%253dMercent-_-Shopping-_-Cameras_and_Camcorders-_-486292&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon+D3100+14.2-Megapixel+Digital+SLR+Camera+With+18-55mm+Zoom-Nikkor+Lens%2C+Black&dlprc=549.99&crn=&istrsmrc=1&isathrsl=0&AR=10&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=101677489&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=10&cid=&semid1=&semid2=&IsLps=0&CC=1&SL=1&FS=1&code=&acode=690&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=Office Depothttp://img.shopping.com/cctool/merch_logos/467671.gif1-800-GO-DEPOT1352.37http://img.shopping.com/sc/mr/sdc_checks_25.gifhttp://www.shopping.com/xMR-store_office_depot_4158555~MRD-467671~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUS486292Nikon® D3100™ 14.2MP Digital SLR with 18-55mm LensThe Nikon D3100 DSLR will surprise you with its simplicity and impress you with superb results.7185Nikonhttp://di103.shopping.com/images/di/52/6c/35/36553743756954597348344d475a30326c7851-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://di103.shopping.com/images/di/52/6c/35/36553743756954597348344d475a30326c7851-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://di103.shopping.com/images/di/52/6c/35/36553743756954597348344d475a30326c7851-220x220-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4in-stock549.996.05549.99http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=504&BEFID=7185&aon=%5E1&MerchantID=332477&crawler_id=332477&dealId=Rl56U7CuiTYsH4MGZ02lxQ%3D%3D&url=http%3A%2F%2Ftracking.searchmarketing.com%2Fgsic.asp%3Faid%3D903483107%26&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon%C2%AE+D3100%E2%84%A2+14.2MP+Digital+SLR+with+18-55mm+Lens&dlprc=549.99&crn=&istrsmrc=0&isathrsl=0&AR=11&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=101677489&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=11&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=0&code=&acode=496&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=RadioShackhttp://img.shopping.com/cctool/merch_logos/332477.gif242.25http://img.shopping.com/sc/mr/sdc_checks_25.gifhttp://www.shopping.com/xMR-store_radioshack_9689~MRD-332477~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUS9614867Nikon D3100 SLR w/Nikon 18-55mm VR & 55-200mm VR Lenses14.2 Megapixels3" LCDLive ViewHD 1080p Video w/ Sound & Autofocus11-point Autofocus3 Frames per Second ShootingISO 100 to 3200 (Expand to 12800-Hi2)Self Cleaning SensorEXPEED 2, Image Processing EngineScene Recognition System7185Nikonhttp://di105.shopping.com/images/di/68/75/53/36785a4b444b614b4d544d5037316549364441-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://di105.shopping.com/images/di/68/75/53/36785a4b444b614b4d544d5037316549364441-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://di105.shopping.com/images/di/68/75/53/36785a4b444b614b4d544d5037316549364441-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://di105.shopping.com/images/di/68/75/53/36785a4b444b614b4d544d5037316549364441-345x345-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5in-stock695.000.00695.00http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=371&BEFID=7185&aon=%5E1&MerchantID=487342&crawler_id=487342&dealId=huS6xZKDKaKMTMP71eI6DA%3D%3D&url=http%3A%2F%2Fwww.rythercamera.com%2Fcatalog%2Fproduct_info.php%3Fcsv%3Dsh%26products_id%3D32983%26&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon+D3100+SLR+w%2FNikon+18-55mm+VR+%26+55-200mm+VR+Lenses&dlprc=695.0&crn=&istrsmrc=0&isathrsl=0&AR=15&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=101677489&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=15&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=1&code=&acode=379&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=RytherCamera.comhttp://img.shopping.com/cctool/merch_logos/487342.gif1-877-644-75930http://img.shopping.com/sc/glb/flag/US.gifUS32983Nikon COOLPIX S203 Digital Camera10 Megapixel, Ultra-Compact Camera, 2.5 in. LCD Screen, 3x Optical Zoom, With Video Capability, Weight: 0.23 lb.With 10.34 mega pixel, electronic VR vibration reduction, 5-level brightness adjustment, 3x optical zoom, and TFT LCD, Nikon Coolpix s203 fulfills all the demands of any photographer. The digital camera has an inbuilt memory of 44MB and an external memory slot made for all kinds of SD (Secure Digital) cards.http://di1.shopping.com/images/pi/c4/ef/1b/95397883-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=2http://di1.shopping.com/images/pi/c4/ef/1b/95397883-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=2http://di1.shopping.com/images/pi/c4/ef/1b/95397883-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=2http://di1.shopping.com/images/pi/c4/ef/1b/95397883-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=2http://di1.shopping.com/images/pi/c4/ef/1b/95397883-500x499-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=20139.00139.00http://www.shopping.com/Nikon-Coolpix-S203/prices~linkin_id-7000610http://www.shopping.com/Nikon-Coolpix-S203/info~linkin_id-7000610Nikon Coolpix S203 Digital Camera (Red)With 10.34 mega pixel, electronic VR vibration reduction, 5-level brightness adjustment, 3x optical zoom, and TFT LCD, Nikon Coolpix s203 fulfills all the demands of any photographer. The digital camera has an inbuilt memory of 44MB and an external memory slot made for all kinds of SD (Secure Digital) cards.7185Nikonhttp://di108.shopping.com/images/di/73/42/64/324a6e4945504d2d415f6c42414d31525a6751-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di108.shopping.com/images/di/73/42/64/324a6e4945504d2d415f6c42414d31525a6751-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di108.shopping.com/images/di/73/42/64/324a6e4945504d2d415f6c42414d31525a6751-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di108.shopping.com/images/di/73/42/64/324a6e4945504d2d415f6c42414d31525a6751-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di108.shopping.com/images/di/73/42/64/324a6e4945504d2d415f6c42414d31525a6751-500x500-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1in-stockFantastic prices with ease & comfort of Amazon.com!139.009.50139.00http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=566&BEFID=7185&aon=%5E1&MerchantID=301531&crawler_id=1903313&dealId=sBd2JnIEPM-A_lBAM1RZgQ%3D%3D&url=http%3A%2F%2Fwww.amazon.com%2Fdp%2FB002T964IM%2Fref%3Dasc_df_B002T964IM1751618%3Fsmid%3DA22UHVNXG98FAT%26tag%3Ddealtime-ce-mp01feed-20%26linkCode%3Dasn%26creative%3D395105%26creativeASIN%3DB002T964IM&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon+Coolpix+S203+Digital+Camera+%28Red%29&dlprc=139.0&crn=&istrsmrc=0&isathrsl=0&AR=63&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=95397883&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=63&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=0&code=&acode=518&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=Amazon Marketplacehttp://img.shopping.com/cctool/merch_logos/301531.gif2132.73http://img.shopping.com/sc/mr/sdc_checks_25.gifhttp://www.shopping.com/xMR-store_amazon_marketplace_9689~MRD-301531~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUSB002T964IMNikon S3100 Digital Camera14.5 Megapixel, Compact Camera, 2.7 in. LCD Screen, 5x Optical Zoom, With High Definition Video, Weight: 0.23 lb.This digital camera features a wide-angle optical Zoom-NIKKOR glass lens that allows you to capture anything from landscapes to portraits to action shots. The high-definition movie mode with one-touch recording makes it easy to capture video clips.http://di1.shopping.com/images/pi/66/2d/33/106834268-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=3http://di1.shopping.com/images/pi/66/2d/33/106834268-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=3http://di1.shopping.com/images/pi/66/2d/33/106834268-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=3http://di1.shopping.com/images/pi/66/2d/33/106834268-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=3http://di1.shopping.com/images/pi/66/2d/33/106834268-507x387-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=312.00http://img.shopping.com/sc/pr/sdc_stars_sm_2.gifhttp://www.shopping.com/nikon-s3100/reviews~linkin_id-700061099.95134.95http://www.shopping.com/nikon-s3100/prices~linkin_id-7000610http://www.shopping.com/nikon-s3100/info~linkin_id-7000610CoolPix S3100 14 Megapixel Compact Digital Camera- RedNikon Coolpix S3100 - Digital camera - compact - 14.0 Mpix - optical zoom: 5 x - supported memory: SD, SDXC, SDHC - red7185Nikonhttp://di111.shopping.com/images/di/55/55/79/476f71563872302d78726b6e2d726e474e6267-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di111.shopping.com/images/di/55/55/79/476f71563872302d78726b6e2d726e474e6267-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di111.shopping.com/images/di/55/55/79/476f71563872302d78726b6e2d726e474e6267-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di111.shopping.com/images/di/55/55/79/476f71563872302d78726b6e2d726e474e6267-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1in-stockGet 30 days FREE SHIPPING w/ ShipVantage119.956.95139.95http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=578&BEFID=7185&aon=%5E1&MerchantID=485615&crawler_id=485615&dealId=UUyGoqV8r0-xrkn-rnGNbg%3D%3D&url=http%3A%2F%2Fsears.rdr.channelintelligence.com%2Fgo.asp%3FfVhzOGNRAAQIASNiE1NbQBJpFHJ3Yx0CTAICI2BbH1lEFmgKP3QvUVpEREdlfUAUHAQPLVpFTVdtJzxAHUNYW3AhQBM0QhFvEXAbYh8EAAVmDAJeU1oyGG0GcBdhGwUGCAVqYF9SO0xSN1sZdmA7dmMdBQAJB24qX1NbQxI6AjA2ME5dVFULPDsGPFcQTTdaLTA6SR0OFlQvPAwMDxYcYlxIVkcoLTcCDA%3D%3D%26nAID%3D13736960%26&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=CoolPix+S3100+14+Megapixel+Compact+Digital+Camera-+Red&dlprc=119.95&crn=&istrsmrc=1&isathrsl=0&AR=28&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=106834268&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=28&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=1&FS=0&code=&acode=583&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=Searshttp://img.shopping.com/cctool/merch_logos/485615.gif1-800-349-43588882.85http://img.shopping.com/sc/mr/sdc_checks_3.gifhttp://www.shopping.com/xMR-store_sears_4189479~MRD-485615~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUS00337013000COOLPIX S3100 PinkNikon Coolpix S3100 - Digital camera - compact - 14.0 Mpix - optical zoom: 5 x - supported memory: SD, SDXC, SDHC - pink7185Nikonhttp://di111.shopping.com/images/di/58/38/37/4177586c573164586f4d586b34515144546f51-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://di111.shopping.com/images/di/58/38/37/4177586c573164586f4d586b34515144546f51-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://di111.shopping.com/images/di/58/38/37/4177586c573164586f4d586b34515144546f51-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://di111.shopping.com/images/di/58/38/37/4177586c573164586f4d586b34515144546f51-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2in-stockGet 30 days FREE SHIPPING w/ ShipVantage119.956.95139.95http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=578&BEFID=7185&aon=%5E1&MerchantID=485615&crawler_id=485615&dealId=X87AwXlW1dXoMXk4QQDToQ%3D%3D&url=http%3A%2F%2Fsears.rdr.channelintelligence.com%2Fgo.asp%3FfVhzOGNRAAQIASNiE1NbQBJpFHJxYx0CTAICI2BbH1lEFmgKP3QvUVpEREdlfUAUHAQPLVpFTVdtJzxAHUNYW3AhQBM0QhFvEXAbYh8EAAVmb2JcUFxDEGsPc3QDEkFZVQ0WFhdRW0MWbgYWDlxzdGMdAVQWRi0xDAwPFhw9TSobb05eWVVYKzsLTFVVQi5RICs3SA8MU1s2MQQKD1wf%26nAID%3D13736960%26&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=COOLPIX+S3100+Pink&dlprc=119.95&crn=&istrsmrc=1&isathrsl=0&AR=31&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=106834268&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=31&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=1&FS=0&code=&acode=583&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=Searshttp://img.shopping.com/cctool/merch_logos/485615.gif1-800-349-43588882.85http://img.shopping.com/sc/mr/sdc_checks_3.gifhttp://www.shopping.com/xMR-store_sears_4189479~MRD-485615~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUS00337015000Nikon Coolpix S3100 14.0 MP Digital Camera - SilverNikon Coolpix S3100 14.0 MP Digital Camera - Silver7185Nikonhttp://di109.shopping.com/images/di/6e/76/46/776e70664134726c413144626b736473613077-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://di109.shopping.com/images/di/6e/76/46/776e70664134726c413144626b736473613077-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://di109.shopping.com/images/di/6e/76/46/776e70664134726c413144626b736473613077-270x270-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3in-stock109.970.00109.97http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=803&BEFID=7185&aon=%5E&MerchantID=475774&crawler_id=475774&dealId=nvFwnpfA4rlA1Dbksdsa0w%3D%3D&url=http%3A%2F%2Fwww.thewiz.com%2Fcatalog%2Fproduct.jsp%3FmodelNo%3DS3100SILVER%26gdftrk%3DgdfV2677_a_7c996_a_7c4049_a_7c26262&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon+Coolpix+S3100+14.0+MP+Digital+Camera+-+Silver&dlprc=109.97&crn=&istrsmrc=0&isathrsl=0&AR=33&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=106834268&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=33&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=1&code=&acode=797&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=TheWiz.comhttp://img.shopping.com/cctool/merch_logos/475774.gif877-542-69880http://img.shopping.com/sc/glb/flag/US.gifUS26262Nikon� COOLPIX� S3100 14MP Digital Camera (Silver)The Nikon COOLPIX S3100 is the easy way to share your life and stay connected.7185Nikonhttp://di102.shopping.com/images/di/35/47/74/614e324e6572794b7770732d5365326c2d3467-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://di102.shopping.com/images/di/35/47/74/614e324e6572794b7770732d5365326c2d3467-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://di102.shopping.com/images/di/35/47/74/614e324e6572794b7770732d5365326c2d3467-220x220-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4in-stock119.996.05119.99http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=504&BEFID=7185&aon=%5E1&MerchantID=332477&crawler_id=332477&dealId=5GtaN2NeryKwps-Se2l-4g%3D%3D&url=http%3A%2F%2Ftracking.searchmarketing.com%2Fgsic.asp%3Faid%3D848064082%26&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon%C3%AF%C2%BF%C2%BD+COOLPIX%C3%AF%C2%BF%C2%BD+S3100+14MP+Digital+Camera+%28Silver%29&dlprc=119.99&crn=&istrsmrc=0&isathrsl=0&AR=37&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=106834268&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=37&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=0&code=&acode=509&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=RadioShackhttp://img.shopping.com/cctool/merch_logos/332477.gif242.25http://img.shopping.com/sc/mr/sdc_checks_25.gifhttp://www.shopping.com/xMR-store_radioshack_9689~MRD-332477~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUS10101095COOLPIX S3100 YellowNikon Coolpix S3100 - Digital camera - compact - 14.0 Mpix - optical zoom: 5 x - supported memory: SD, SDXC, SDHC - yellow7185Nikonhttp://di107.shopping.com/images/di/61/34/33/6d305258756c5833387a436e516a5535396a77-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://di107.shopping.com/images/di/61/34/33/6d305258756c5833387a436e516a5535396a77-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://di107.shopping.com/images/di/61/34/33/6d305258756c5833387a436e516a5535396a77-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://di107.shopping.com/images/di/61/34/33/6d305258756c5833387a436e516a5535396a77-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5in-stockGet 30 days FREE SHIPPING w/ ShipVantage119.956.95139.95http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=578&BEFID=7185&aon=%5E1&MerchantID=485615&crawler_id=485615&dealId=a43m0RXulX38zCnQjU59jw%3D%3D&url=http%3A%2F%2Fsears.rdr.channelintelligence.com%2Fgo.asp%3FfVhzOGNRAAQIASNiE1NbQBJpFHJwYx0CTAICI2BbH1lEFmgKP3QvUVpEREdlfUAUHAQPLVpFTVdtJzxAHUNYW3AhQBM0QhFvEXAbYh8EAAVmb2JcUFxDEGoPc3QDEkFZVQ0WFhdRW0MWbgYWDlxzdGMdAVQWRi0xDAwPFhw9TSobb05eWVVYKzsLTFVVQi5RICs3SA8MU1s2MQQKD1wf%26nAID%3D13736960%26&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=COOLPIX+S3100+Yellow&dlprc=119.95&crn=&istrsmrc=1&isathrsl=0&AR=38&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=106834268&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=38&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=1&FS=0&code=&acode=583&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=Searshttp://img.shopping.com/cctool/merch_logos/485615.gif1-800-349-43588882.85http://img.shopping.com/sc/mr/sdc_checks_3.gifhttp://www.shopping.com/xMR-store_sears_4189479~MRD-485615~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUS00337014000Nikon D90 Digital Camera12.3 Megapixel, Point and Shoot Camera, 3 in. LCD Screen, With Video Capability, Weight: 1.36 lb.Untitled Document Nikon D90 SLR Digital Camera With 28-80mm 75-300mm Lens Kit The Nikon D90 SLR Digital Camera, with its 12.3-megapixel DX-format CMOS, 3" High resolution LCD display, Scene Recognition System, Picture Control, Active D-Lighting, and one-button Live View, provides photo enthusiasts with the image quality and performance they need to pursue their own vision while still being intuitive enough for use as an everyday camera.http://di1.shopping.com/images/pi/52/fb/d3/99671132-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=4http://di1.shopping.com/images/pi/52/fb/d3/99671132-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=4http://di1.shopping.com/images/pi/52/fb/d3/99671132-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=4http://di1.shopping.com/images/pi/52/fb/d3/99671132-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=4http://di1.shopping.com/images/pi/52/fb/d3/99671132-499x255-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=475.00http://img.shopping.com/sc/pr/sdc_stars_sm_5.gifhttp://www.shopping.com/Nikon-D90-with-18-270mm-Lens/reviews~linkin_id-7000610689.002299.00http://www.shopping.com/Nikon-D90-with-18-270mm-Lens/prices~linkin_id-7000610http://www.shopping.com/Nikon-D90-with-18-270mm-Lens/info~linkin_id-7000610Nikon® D90 12.3MP Digital SLR Camera (Body Only)The Nikon D90 will make you rethink what a digital SLR camera can achieve.7185Nikonhttp://di106.shopping.com/images/di/47/55/35/4a4a6b70554178653548756a4237666b774141-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di106.shopping.com/images/di/47/55/35/4a4a6b70554178653548756a4237666b774141-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di106.shopping.com/images/di/47/55/35/4a4a6b70554178653548756a4237666b774141-220x220-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1in-stock1015.996.051015.99http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=504&BEFID=7185&aon=%5E1&MerchantID=332477&crawler_id=332477&dealId=GU5JJkpUAxe5HujB7fkwAA%3D%3D&url=http%3A%2F%2Ftracking.searchmarketing.com%2Fgsic.asp%3Faid%3D851830266%26&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon%C2%AE+D90+12.3MP+Digital+SLR+Camera+%28Body+Only%29&dlprc=1015.99&crn=&istrsmrc=0&isathrsl=0&AR=14&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=99671132&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=14&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=0&code=&acode=496&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=RadioShackhttp://img.shopping.com/cctool/merch_logos/332477.gif242.25http://img.shopping.com/sc/mr/sdc_checks_25.gifhttp://www.shopping.com/xMR-store_radioshack_9689~MRD-332477~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUS10148659Nikon D90 SLR Digital Camera (Camera Body)The Nikon D90 SLR Digital Camera with its 12.3-megapixel DX-format CCD 3" High resolution LCD display Scene Recognition System Picture Control Active D-Lighting and one-button Live View provides photo enthusiasts with the image quality and performance they need to pursue their own vision while still being intuitive enough for use as an everyday camera. In addition the D90 introduces the D-Movie mode allowing for the first time an interchangeable lens SLR camera that is capable of recording 720p HD movie clips. Availabilty: In Stock7185Nikonhttp://di109.shopping.com/images/di/58/68/55/527553432d73704262544944666f3471667a51-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://di109.shopping.com/images/di/58/68/55/527553432d73704262544944666f3471667a51-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://di109.shopping.com/images/di/58/68/55/527553432d73704262544944666f3471667a51-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://di109.shopping.com/images/di/58/68/55/527553432d73704262544944666f3471667a51-350x350-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2in-stockFree Shipping with Any Purchase!689.000.00900.00http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=647&BEFID=7185&aon=%5E1&MerchantID=475674&crawler_id=475674&dealId=XhURuSC-spBbTIDfo4qfzQ%3D%3D&url=http%3A%2F%2Fwww.fumfie.com%2Fproduct%2F169.5%2Fshopping-com%3F&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon+D90+SLR+Digital+Camera+%28Camera+Body%29&dlprc=689.0&crn=&istrsmrc=1&isathrsl=0&AR=16&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=99671132&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=16&cid=&semid1=&semid2=&IsLps=0&CC=1&SL=1&FS=1&code=&acode=658&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=FumFiehttp://img.shopping.com/cctool/merch_logos/475674.gif866 666 91985604.27http://img.shopping.com/sc/mr/sdc_checks_45.gifhttp://www.shopping.com/xMR-store_fumfie~MRD-475674~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUSF169C5Nikon D90 SLR w/Nikon 18-105mm VR & 55-200mm VR Lenses12.3 MegapixelDX Format CMOS Sensor3" VGA LCD DisplayLive ViewSelf Cleaning SensorD-Movie ModeHigh Sensitivity (ISO 3200)4.5 fps BurstIn-Camera Image Editing7185Nikonhttp://di101.shopping.com/images/di/6f/30/50/785f584c5744627278415952793372436d7951-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://di101.shopping.com/images/di/6f/30/50/785f584c5744627278415952793372436d7951-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://di101.shopping.com/images/di/6f/30/50/785f584c5744627278415952793372436d7951-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://di101.shopping.com/images/di/6f/30/50/785f584c5744627278415952793372436d7951-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://di101.shopping.com/images/di/6f/30/50/785f584c5744627278415952793372436d7951-500x500-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3in-stock1189.000.001189.00http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=371&BEFID=7185&aon=%5E1&MerchantID=487342&crawler_id=487342&dealId=o0Px_XLWDbrxAYRy3rCmyQ%3D%3D&url=http%3A%2F%2Fwww.rythercamera.com%2Fcatalog%2Fproduct_info.php%3Fcsv%3Dsh%26products_id%3D30619%26&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon+D90+SLR+w%2FNikon+18-105mm+VR+%26+55-200mm+VR+Lenses&dlprc=1189.0&crn=&istrsmrc=0&isathrsl=0&AR=20&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=99671132&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=20&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=1&code=&acode=379&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=RytherCamera.comhttp://img.shopping.com/cctool/merch_logos/487342.gif1-877-644-75930http://img.shopping.com/sc/glb/flag/US.gifUS30619Nikon D90 12.3 Megapixel Digital SLR Camera (Body Only)Fusing 12.3 megapixel image quality and a cinematic 24fps D-Movie Mode, the Nikon D90 exceeds the demands of passionate photographers. Coupled with Nikon's EXPEED image processing technologies and NIKKOR optics, breathtaking image fidelity is assured. Combined with fast 0.15ms power-up and split-second 65ms shooting lag, dramatic action and decisive moments are captured easily. Effective 4-frequency, ultrasonic sensor cleaning frees image degrading dust particles from the sensor's optical low pass filter.7185Nikonhttp://di110.shopping.com/images/di/34/48/67/62574a534a3873736749663842304d58497741-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://di110.shopping.com/images/di/34/48/67/62574a534a3873736749663842304d58497741-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://di110.shopping.com/images/di/34/48/67/62574a534a3873736749663842304d58497741-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4in-stockFREE FEDEX 2-3 DAY DELIVERY899.950.00899.95http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=269&BEFID=7185&aon=%5E&MerchantID=9296&crawler_id=811558&dealId=4HgbWJSJ8ssgIf8B0MXIwA%3D%3D&url=http%3A%2F%2Fwww.pcnation.com%2Foptics-gallery%2Fdetails.asp%3Faffid%3D305%26item%3D2N145P&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon+D90+12.3+Megapixel+Digital+SLR+Camera+%28Body+Only%29&dlprc=899.95&crn=&istrsmrc=1&isathrsl=0&AR=21&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=99671132&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=21&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=1&code=&acode=257&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=PCNationhttp://img.shopping.com/cctool/merch_logos/9296.gif800-470-707916224.43http://img.shopping.com/sc/mr/sdc_checks_45.gifhttp://www.shopping.com/xMR-store_pcnation_9689~MRD-9296~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUS2N145PNikon D90 12.3MP Digital SLR Camera (Body Only)Fusing 12.3-megapixel image quality inherited from the award-winning D300 with groundbreaking features, the D90's breathtaking, low-noise image quality is further advanced with EXPEED image processing. Split-second shutter response and continuous shooting at up to 4.5 frames-per-second provide the power to capture fast action and precise moments perfectly, while Nikon's exclusive Scene Recognition System contributes to faster 11-area autofocus performance, finer white balance detection and more. The D90 delivers the control passionate photographers demand, utilizing comprehensive exposure functions and the intelligence of 3D Color Matrix Metering II. Stunning results come to life on a 3-inch 920,000-dot color LCD monitor, providing accurate image review, Live View composition and brilliant playback of the D90's cinematic-quality 24-fps HD D-Movie mode.7185Nikonhttp://di102.shopping.com/images/di/55/4e/44/6133754d445a584f6e76445f377354494c5967-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://di102.shopping.com/images/di/55/4e/44/6133754d445a584f6e76445f377354494c5967-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://di102.shopping.com/images/di/55/4e/44/6133754d445a584f6e76445f377354494c5967-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://di102.shopping.com/images/di/55/4e/44/6133754d445a584f6e76445f377354494c5967-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://di102.shopping.com/images/di/55/4e/44/6133754d445a584f6e76445f377354494c5967-500x500-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5in-stockFantastic prices with ease & comfort of Amazon.com!780.000.00780.00http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=566&BEFID=7185&aon=%5E1&MerchantID=301531&crawler_id=1903313&dealId=UNDa3uMDZXOnvD_7sTILYg%3D%3D&url=http%3A%2F%2Fwww.amazon.com%2Fdp%2FB001ET5U92%2Fref%3Dasc_df_B001ET5U921751618%3Fsmid%3DAHF4SYKP09WBH%26tag%3Ddealtime-ce-mp01feed-20%26linkCode%3Dasn%26creative%3D395105%26creativeASIN%3DB001ET5U92&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon+D90+12.3MP+Digital+SLR+Camera+%28Body+Only%29&dlprc=780.0&crn=&istrsmrc=0&isathrsl=0&AR=29&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=99671132&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=29&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=1&code=&acode=520&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=Amazon Marketplacehttp://img.shopping.com/cctool/merch_logos/301531.gif2132.73http://img.shopping.com/sc/mr/sdc_checks_25.gifhttp://www.shopping.com/xMR-store_amazon_marketplace_9689~MRD-301531~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUSB001ET5U92Nikon D90 Digital Camera with 18-105mm lens12.9 Megapixel, SLR Camera, 3 in. LCD Screen, 5.8x Optical Zoom, With Video Capability, Weight: 2.3 lb.Its 12.3 megapixel DX-format CMOS image sensor and EXPEED image processing system offer outstanding image quality across a wide ISO light sensitivity range. Live View mode lets you compose and shoot via the high-resolution 3-inch LCD monitor, and an advanced Scene Recognition System and autofocus performance help capture images with astounding accuracy. Movies can be shot in Motion JPEG format using the D-Movie function. The camera’s large image sensor ensures exceptional movie image quality and you can create dramatic effects by shooting with a wide range of interchangeable NIKKOR lenses, from wide-angle to macro to fisheye, or by adjusting the lens aperture and experimenting with depth-of-field. The D90 – designed to fuel your passion for photography.http://di1.shopping.com/images/pi/57/6a/4f/70621646-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=5http://di1.shopping.com/images/pi/57/6a/4f/70621646-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=5http://di1.shopping.com/images/pi/57/6a/4f/70621646-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=5http://di1.shopping.com/images/pi/57/6a/4f/70621646-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=5http://di1.shopping.com/images/pi/57/6a/4f/70621646-490x489-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=2&c=1&l=7000610&t=111021183845&r=5324.81http://img.shopping.com/sc/pr/sdc_stars_sm_5.gifhttp://www.shopping.com/Nikon-D90-with-18-105mm-lens/reviews~linkin_id-7000610849.951599.95http://www.shopping.com/Nikon-D90-with-18-105mm-lens/prices~linkin_id-7000610http://www.shopping.com/Nikon-D90-with-18-105mm-lens/info~linkin_id-7000610Nikon D90 18-105mm VR LensThe Nikon D90 SLR Digital Camera with its 12.3-megapixel DX-format CMOS 3" High resolution LCD display Scene Recognition System Picture Control Active D-Lighting and one-button Live View prov7185Nikonhttp://di111.shopping.com/images/di/33/6f/35/6531566768674a5066684c7654314a464b5441-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di111.shopping.com/images/di/33/6f/35/6531566768674a5066684c7654314a464b5441-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1http://di111.shopping.com/images/di/33/6f/35/6531566768674a5066684c7654314a464b5441-260x260-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=1in-stock849.950.00849.95http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=419&BEFID=7185&aon=%5E1&MerchantID=9390&crawler_id=1905054&dealId=3o5e1VghgJPfhLvT1JFKTA%3D%3D&url=http%3A%2F%2Fwww.ajrichard.com%2FNikon-D90-18-105mm-VR-Lens%2Fp-292%3Frefid%3DShopping%26&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon+D90+18-105mm+VR+Lens&dlprc=849.95&crn=&istrsmrc=0&isathrsl=0&AR=2&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=70621646&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=2&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=1&code=&acode=425&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=AJRichardhttp://img.shopping.com/cctool/merch_logos/9390.gif1-888-871-125631244.48http://img.shopping.com/sc/mr/sdc_checks_45.gifhttp://www.shopping.com/xMR-store_ajrichard~MRD-9390~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUS292Nikon D90 SLR w/Nikon 18-105mm VR Lens12.3 MegapixelDX Format CMOS Sensor3" VGA LCD DisplayLive ViewSelf Cleaning SensorD-Movie ModeHigh Sensitivity (ISO 3200)4.5 fps BurstIn-Camera Image Editing7185Nikonhttp://di108.shopping.com/images/di/5f/6c/59/576a5f6a62776673536b666377556344757777-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://di108.shopping.com/images/di/5f/6c/59/576a5f6a62776673536b666377556344757777-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://di108.shopping.com/images/di/5f/6c/59/576a5f6a62776673536b666377556344757777-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://di108.shopping.com/images/di/5f/6c/59/576a5f6a62776673536b666377556344757777-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2http://di108.shopping.com/images/di/5f/6c/59/576a5f6a62776673536b666377556344757777-500x500-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=2in-stock909.000.00909.00http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=371&BEFID=7185&aon=%5E1&MerchantID=487342&crawler_id=487342&dealId=_lYWj_jbwfsSkfcwUcDuww%3D%3D&url=http%3A%2F%2Fwww.rythercamera.com%2Fcatalog%2Fproduct_info.php%3Fcsv%3Dsh%26products_id%3D30971%26&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon+D90+SLR+w%2FNikon+18-105mm+VR+Lens&dlprc=909.0&crn=&istrsmrc=0&isathrsl=0&AR=3&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=70621646&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=3&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=1&code=&acode=379&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=RytherCamera.comhttp://img.shopping.com/cctool/merch_logos/487342.gif1-877-644-75930http://img.shopping.com/sc/glb/flag/US.gifUS3097125448/D90 12.3 Megapixel Digital Camera 18-105mm Zoom Lens w/ 3" Screen - BlackNikon D90 - Digital camera - SLR - 12.3 Mpix - Nikon AF-S DX 18-105mm lens - optical zoom: 5.8 x - supported memory: SD, SDHC7185Nikonhttp://di110.shopping.com/images/di/31/4b/43/636c4347755776747932584b5539736b616467-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://di110.shopping.com/images/di/31/4b/43/636c4347755776747932584b5539736b616467-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://di110.shopping.com/images/di/31/4b/43/636c4347755776747932584b5539736b616467-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3http://di110.shopping.com/images/di/31/4b/43/636c4347755776747932584b5539736b616467-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=3in-stockGet 30 days FREE SHIPPING w/ ShipVantage1199.008.201199.00http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=578&BEFID=7185&aon=%5E1&MerchantID=485615&crawler_id=485615&dealId=1KCclCGuWvty2XKU9skadg%3D%3D&url=http%3A%2F%2Fsears.rdr.channelintelligence.com%2Fgo.asp%3FfVhzOGNRAAQIASNiE1NbQBRtFXpzYx0CTAICI2BbH1lEFmgKP3QvUVpEREdlfUAUHAQPLVpFTVdtJzxAHUNYW3AhQBM0QhFvEXAbYh8EAAVmb2JcVlhCGGkPc3QDEkFZVQ0WFhdRW0MWbgYWDlxzdGMdAVQWRi0xDAwPFhw9TSobb05eWVVYKzsLTFVVQi5RICs3SA8MU1s2MQQKD1wf%26nAID%3D13736960%26&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=25448%2FD90+12.3+Megapixel+Digital+Camera+18-105mm+Zoom+Lens+w%2F+3%22+Screen+-+Black&dlprc=1199.0&crn=&istrsmrc=1&isathrsl=0&AR=4&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=70621646&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=4&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=0&code=&acode=586&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=Searshttp://img.shopping.com/cctool/merch_logos/485615.gif1-800-349-43588882.85http://img.shopping.com/sc/mr/sdc_checks_3.gifhttp://www.shopping.com/xMR-store_sears_4189479~MRD-485615~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUS00353197000Nikon® D90 12.3MP Digital SLR with 18-105mm LensThe Nikon D90 will make you rethink what a digital SLR camera can achieve.7185Nikonhttp://di101.shopping.com/images/di/33/2d/56/4f53665656354a6f37486c41346b4a74616e41-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://di101.shopping.com/images/di/33/2d/56/4f53665656354a6f37486c41346b4a74616e41-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4http://di101.shopping.com/images/di/33/2d/56/4f53665656354a6f37486c41346b4a74616e41-220x220-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=4in-stock1350.996.051350.99http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=504&BEFID=7185&aon=%5E1&MerchantID=332477&crawler_id=332477&dealId=3-VOSfVV5Jo7HlA4kJtanA%3D%3D&url=http%3A%2F%2Ftracking.searchmarketing.com%2Fgsic.asp%3Faid%3D982673361%26&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon%C2%AE+D90+12.3MP+Digital+SLR+with+18-105mm+Lens&dlprc=1350.99&crn=&istrsmrc=0&isathrsl=0&AR=5&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=70621646&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=5&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=0&FS=0&code=&acode=496&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=RadioShackhttp://img.shopping.com/cctool/merch_logos/332477.gif242.25http://img.shopping.com/sc/mr/sdc_checks_25.gifhttp://www.shopping.com/xMR-store_radioshack_9689~MRD-332477~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUS11148905Nikon D90 Kit 12.3-megapixel Digital SLR with 18-105mm VR LensPhotographers, take your passion further!Now is the time for new creativity, and to rethink what a digital SLR camera can achieve. It's time for the D90, a camera with everything you would expect from Nikon's next-generation D-SLRs, and some unexpected surprises, as well. The stunning image quality is inherited from the D300, Nikon's DX-format flagship. The D90 also has Nikon's unmatched ergonomics and high performance, and now takes high-quality movies with beautifully cinematic results. The world of photography has changed, and with the D90 in your hands, it's time to make your own rules.AF-S DX NIKKOR 18-105mm f/3.5-5.6G ED VR LensWide-ratio 5.8x zoom Compact, versatile and ideal for a broad range of shooting situations, ranging from interiors and landscapes to beautiful portraits� a perfect everyday zoom. Nikon VR (Vibration Reduction) image stabilization Vibration Reduction is engineered specifically for each VR NIKKOR lens and enables handheld shooting at up to 3 shutter speeds slower than would7185Nikonhttp://di110.shopping.com/images/di/6b/51/6e/4236725334416a4e3564783568325f36333167-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://di110.shopping.com/images/di/6b/51/6e/4236725334416a4e3564783568325f36333167-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://di110.shopping.com/images/di/6b/51/6e/4236725334416a4e3564783568325f36333167-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5http://di110.shopping.com/images/di/6b/51/6e/4236725334416a4e3564783568325f36333167-300x232-0-0.jpg?p=p2.a121bc2aaf029435dce6&a=1&c=1&l=7000610&t=111021183845&r=5in-stockShipping Included!1050.000.001199.00http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=135&BEFID=7185&aon=%5E1&MerchantID=313162&crawler_id=313162&dealId=kQnB6rS4AjN5dx5h2_631g%3D%3D&url=http%3A%2F%2Fonecall.rdr.channelintelligence.com%2Fgo.asp%3FfVhzOGNRAAQIASNiE1pZSxNoWHFwLx8GTAICa2ZeH1sPXTZLNzRpAh1HR0BxPQEGCBJNMhFHUElsFCFCVkVTTHAcBggEHQ4aHXNpGERGH3RQODsbAgdechJtbBt8fx8JAwhtZFAzJj1oGgIWCxRlNyFOUV9UUGIxBgo0T0IyTSYqJ0RWHw4QPCIBAAQXRGMDICg6TllZVBhh%26nAID%3D13736960&linkin_id=7000610&Issdt=111021183845&searchID=p2.a121bc2aaf029435dce6&DealName=Nikon+D90+Kit+12.3-megapixel+Digital+SLR+with+18-105mm+VR+Lens&dlprc=1050.0&crn=&istrsmrc=1&isathrsl=0&AR=6&NG=20&NDP=200&PN=1&ST=7&DB=sdcprod&MT=phx-pkadudc2&FPT=DSP&NDS=&NMS=&MRS=&PD=70621646&brnId=14804&IsFtr=0&IsSmart=0&DMT=&op=&CM=&DlLng=1&RR=6&cid=&semid1=&semid2=&IsLps=0&CC=0&SL=1&FS=1&code=&acode=143&category=&HasLink=&frameId=&ND=&MN=&PT=&prjID=&GR=&lnkId=&VK=OneCallhttp://img.shopping.com/cctool/merch_logos/313162.gif1.800.398.07661804.44http://img.shopping.com/sc/mr/sdc_checks_45.gifhttp://www.shopping.com/xMR-store_onecall_9689~MRD-313162~S-1~linkin_id-7000610http://img.shopping.com/sc/glb/flag/US.gifUS92826Price rangehttp://www.shopping.com/digital-cameras/nikon/products?oq=nikon&linkin_id=7000610$24 - $4012http://www.shopping.com/digital-cameras/nikon/products?minPrice=24&maxPrice=4012&linkin_id=7000610$4012 - $7999http://www.shopping.com/digital-cameras/nikon/products?minPrice=4012&maxPrice=7999&linkin_id=7000610Brandhttp://www.shopping.com/digital-cameras/nikon/products~all-9688-brand~MS-1?oq=nikon&linkin_id=7000610Nikonhttp://www.shopping.com/digital-cameras/nikon+brand-nikon/products~linkin_id-7000610Cranehttp://www.shopping.com/digital-cameras/nikon+9688-brand-crane/products~linkin_id-7000610Ikelitehttp://www.shopping.com/digital-cameras/nikon+ikelite/products~linkin_id-7000610Bowerhttp://www.shopping.com/digital-cameras/nikon+bower/products~linkin_id-7000610FUJIFILMhttp://www.shopping.com/digital-cameras/nikon+brand-fuji/products~linkin_id-7000610Storehttp://www.shopping.com/digital-cameras/nikon/products~all-store~MS-1?oq=nikon&linkin_id=7000610Amazon Marketplacehttp://www.shopping.com/digital-cameras/nikon+store-amazon-marketplace-9689/products~linkin_id-7000610Amazonhttp://www.shopping.com/digital-cameras/nikon+store-amazon/products~linkin_id-7000610Adoramahttp://www.shopping.com/digital-cameras/nikon+store-adorama/products~linkin_id-7000610J&R Music and Computer Worldhttp://www.shopping.com/digital-cameras/nikon+store-j-r-music-and-computer-world/products~linkin_id-7000610RytherCamera.comhttp://www.shopping.com/digital-cameras/nikon+store-rythercamera-com/products~linkin_id-7000610Resolutionhttp://www.shopping.com/digital-cameras/nikon/products~all-21885-resolution~MS-1?oq=nikon&linkin_id=7000610Under 4 Megapixelhttp://www.shopping.com/digital-cameras/nikon+under-4-megapixel/products~linkin_id-7000610At least 5 Megapixelhttp://www.shopping.com/digital-cameras/nikon+5-megapixel-digital-cameras/products~linkin_id-7000610At least 6 Megapixelhttp://www.shopping.com/digital-cameras/nikon+6-megapixel-digital-cameras/products~linkin_id-7000610At least 7 Megapixelhttp://www.shopping.com/digital-cameras/nikon+7-megapixel-digital-cameras/products~linkin_id-7000610At least 8 Megapixelhttp://www.shopping.com/digital-cameras/nikon+8-megapixel-digital-cameras/products~linkin_id-7000610Featureshttp://www.shopping.com/digital-cameras/nikon/products~all-32804-features~MS-1?oq=nikon&linkin_id=7000610Shockproofhttp://www.shopping.com/digital-cameras/nikon+32804-features-shockproof/products~linkin_id-7000610Waterproofhttp://www.shopping.com/digital-cameras/nikon+32804-features-waterproof/products~linkin_id-7000610Freezeproofhttp://www.shopping.com/digital-cameras/nikon+32804-features-freezeproof/products~linkin_id-7000610Dust proofhttp://www.shopping.com/digital-cameras/nikon+32804-features-dust-proof/products~linkin_id-7000610Image Stabilizationhttp://www.shopping.com/digital-cameras/nikon+32804-features-image-stabilization/products~linkin_id-7000610hybriddigital camerag1sonycameracanonnikonkodak digital camerakodaksony cybershotkodak easyshare digital cameranikon coolpixolympuspink digital cameracanon powershot \ No newline at end of file diff --git a/node_modules/xml2js/node_modules/sax/examples/strict.dtd b/node_modules/xml2js/node_modules/sax/examples/strict.dtd new file mode 100644 index 0000000..b274559 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/examples/strict.dtd @@ -0,0 +1,870 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +%HTMLlat1; + + +%HTMLsymbol; + + +%HTMLspecial; + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/node_modules/xml2js/node_modules/sax/examples/test.html b/node_modules/xml2js/node_modules/sax/examples/test.html new file mode 100644 index 0000000..61f8f1a --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/examples/test.html @@ -0,0 +1,15 @@ + + + + + testing the parser + + + +

            hello + + + + diff --git a/node_modules/xml2js/node_modules/sax/examples/test.xml b/node_modules/xml2js/node_modules/sax/examples/test.xml new file mode 100644 index 0000000..801292d --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/examples/test.xml @@ -0,0 +1,1254 @@ + + +]> + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + + Some Text + + + + + + + are ok in here. ]]> + + Pre-Text & Inlined text Post-text. +  + + \ No newline at end of file diff --git a/node_modules/xml2js/node_modules/sax/lib/sax.js b/node_modules/xml2js/node_modules/sax/lib/sax.js new file mode 100644 index 0000000..410a507 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/lib/sax.js @@ -0,0 +1,1410 @@ +// wrapper for non-node envs +;(function (sax) { + +sax.parser = function (strict, opt) { return new SAXParser(strict, opt) } +sax.SAXParser = SAXParser +sax.SAXStream = SAXStream +sax.createStream = createStream + +// When we pass the MAX_BUFFER_LENGTH position, start checking for buffer overruns. +// When we check, schedule the next check for MAX_BUFFER_LENGTH - (max(buffer lengths)), +// since that's the earliest that a buffer overrun could occur. This way, checks are +// as rare as required, but as often as necessary to ensure never crossing this bound. +// Furthermore, buffers are only tested at most once per write(), so passing a very +// large string into write() might have undesirable effects, but this is manageable by +// the caller, so it is assumed to be safe. Thus, a call to write() may, in the extreme +// edge case, result in creating at most one complete copy of the string passed in. +// Set to Infinity to have unlimited buffers. +sax.MAX_BUFFER_LENGTH = 64 * 1024 + +var buffers = [ + "comment", "sgmlDecl", "textNode", "tagName", "doctype", + "procInstName", "procInstBody", "entity", "attribName", + "attribValue", "cdata", "script" +] + +sax.EVENTS = // for discoverability. + [ "text" + , "processinginstruction" + , "sgmldeclaration" + , "doctype" + , "comment" + , "attribute" + , "opentag" + , "closetag" + , "opencdata" + , "cdata" + , "closecdata" + , "error" + , "end" + , "ready" + , "script" + , "opennamespace" + , "closenamespace" + ] + +function SAXParser (strict, opt) { + if (!(this instanceof SAXParser)) return new SAXParser(strict, opt) + + var parser = this + clearBuffers(parser) + parser.q = parser.c = "" + parser.bufferCheckPosition = sax.MAX_BUFFER_LENGTH + parser.opt = opt || {} + parser.opt.lowercase = parser.opt.lowercase || parser.opt.lowercasetags + parser.looseCase = parser.opt.lowercase ? "toLowerCase" : "toUpperCase" + parser.tags = [] + parser.closed = parser.closedRoot = parser.sawRoot = false + parser.tag = parser.error = null + parser.strict = !!strict + parser.noscript = !!(strict || parser.opt.noscript) + parser.state = S.BEGIN + parser.ENTITIES = Object.create(sax.ENTITIES) + parser.attribList = [] + + // namespaces form a prototype chain. + // it always points at the current tag, + // which protos to its parent tag. + if (parser.opt.xmlns) parser.ns = Object.create(rootNS) + + // mostly just for error reporting + parser.trackPosition = parser.opt.position !== false + if (parser.trackPosition) { + parser.position = parser.line = parser.column = 0 + } + emit(parser, "onready") +} + +if (!Object.create) Object.create = function (o) { + function f () { this.__proto__ = o } + f.prototype = o + return new f +} + +if (!Object.getPrototypeOf) Object.getPrototypeOf = function (o) { + return o.__proto__ +} + +if (!Object.keys) Object.keys = function (o) { + var a = [] + for (var i in o) if (o.hasOwnProperty(i)) a.push(i) + return a +} + +function checkBufferLength (parser) { + var maxAllowed = Math.max(sax.MAX_BUFFER_LENGTH, 10) + , maxActual = 0 + for (var i = 0, l = buffers.length; i < l; i ++) { + var len = parser[buffers[i]].length + if (len > maxAllowed) { + // Text/cdata nodes can get big, and since they're buffered, + // we can get here under normal conditions. + // Avoid issues by emitting the text node now, + // so at least it won't get any bigger. + switch (buffers[i]) { + case "textNode": + closeText(parser) + break + + case "cdata": + emitNode(parser, "oncdata", parser.cdata) + parser.cdata = "" + break + + case "script": + emitNode(parser, "onscript", parser.script) + parser.script = "" + break + + default: + error(parser, "Max buffer length exceeded: "+buffers[i]) + } + } + maxActual = Math.max(maxActual, len) + } + // schedule the next check for the earliest possible buffer overrun. + parser.bufferCheckPosition = (sax.MAX_BUFFER_LENGTH - maxActual) + + parser.position +} + +function clearBuffers (parser) { + for (var i = 0, l = buffers.length; i < l; i ++) { + parser[buffers[i]] = "" + } +} + +function flushBuffers (parser) { + closeText(parser) + if (parser.cdata !== "") { + emitNode(parser, "oncdata", parser.cdata) + parser.cdata = "" + } + if (parser.script !== "") { + emitNode(parser, "onscript", parser.script) + parser.script = "" + } +} + +SAXParser.prototype = + { end: function () { end(this) } + , write: write + , resume: function () { this.error = null; return this } + , close: function () { return this.write(null) } + , flush: function () { flushBuffers(this) } + } + +try { + var Stream = require("stream").Stream +} catch (ex) { + var Stream = function () {} +} + + +var streamWraps = sax.EVENTS.filter(function (ev) { + return ev !== "error" && ev !== "end" +}) + +function createStream (strict, opt) { + return new SAXStream(strict, opt) +} + +function SAXStream (strict, opt) { + if (!(this instanceof SAXStream)) return new SAXStream(strict, opt) + + Stream.apply(this) + + this._parser = new SAXParser(strict, opt) + this.writable = true + this.readable = true + + + var me = this + + this._parser.onend = function () { + me.emit("end") + } + + this._parser.onerror = function (er) { + me.emit("error", er) + + // if didn't throw, then means error was handled. + // go ahead and clear error, so we can write again. + me._parser.error = null + } + + this._decoder = null; + + streamWraps.forEach(function (ev) { + Object.defineProperty(me, "on" + ev, { + get: function () { return me._parser["on" + ev] }, + set: function (h) { + if (!h) { + me.removeAllListeners(ev) + return me._parser["on"+ev] = h + } + me.on(ev, h) + }, + enumerable: true, + configurable: false + }) + }) +} + +SAXStream.prototype = Object.create(Stream.prototype, + { constructor: { value: SAXStream } }) + +SAXStream.prototype.write = function (data) { + if (typeof Buffer === 'function' && + typeof Buffer.isBuffer === 'function' && + Buffer.isBuffer(data)) { + if (!this._decoder) { + var SD = require('string_decoder').StringDecoder + this._decoder = new SD('utf8') + } + data = this._decoder.write(data); + } + + this._parser.write(data.toString()) + this.emit("data", data) + return true +} + +SAXStream.prototype.end = function (chunk) { + if (chunk && chunk.length) this.write(chunk) + this._parser.end() + return true +} + +SAXStream.prototype.on = function (ev, handler) { + var me = this + if (!me._parser["on"+ev] && streamWraps.indexOf(ev) !== -1) { + me._parser["on"+ev] = function () { + var args = arguments.length === 1 ? [arguments[0]] + : Array.apply(null, arguments) + args.splice(0, 0, ev) + me.emit.apply(me, args) + } + } + + return Stream.prototype.on.call(me, ev, handler) +} + + + +// character classes and tokens +var whitespace = "\r\n\t " + // this really needs to be replaced with character classes. + // XML allows all manner of ridiculous numbers and digits. + , number = "0124356789" + , letter = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + // (Letter | "_" | ":") + , quote = "'\"" + , entity = number+letter+"#" + , attribEnd = whitespace + ">" + , CDATA = "[CDATA[" + , DOCTYPE = "DOCTYPE" + , XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace" + , XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/" + , rootNS = { xml: XML_NAMESPACE, xmlns: XMLNS_NAMESPACE } + +// turn all the string character sets into character class objects. +whitespace = charClass(whitespace) +number = charClass(number) +letter = charClass(letter) + +// http://www.w3.org/TR/REC-xml/#NT-NameStartChar +// This implementation works on strings, a single character at a time +// as such, it cannot ever support astral-plane characters (10000-EFFFF) +// without a significant breaking change to either this parser, or the +// JavaScript language. Implementation of an emoji-capable xml parser +// is left as an exercise for the reader. +var nameStart = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/ + +var nameBody = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u00B7\u0300-\u036F\u203F-\u2040\.\d-]/ + +quote = charClass(quote) +entity = charClass(entity) +attribEnd = charClass(attribEnd) + +function charClass (str) { + return str.split("").reduce(function (s, c) { + s[c] = true + return s + }, {}) +} + +function isRegExp (c) { + return Object.prototype.toString.call(c) === '[object RegExp]' +} + +function is (charclass, c) { + return isRegExp(charclass) ? !!c.match(charclass) : charclass[c] +} + +function not (charclass, c) { + return !is(charclass, c) +} + +var S = 0 +sax.STATE = +{ BEGIN : S++ +, TEXT : S++ // general stuff +, TEXT_ENTITY : S++ // & and such. +, OPEN_WAKA : S++ // < +, SGML_DECL : S++ // +, SCRIPT : S++ // " + , expect : + [ [ "opentag", { name: "xml", attributes: {}, isSelfClosing: false } ] + , [ "opentag", { name: "script", attributes: {}, isSelfClosing: false } ] + , [ "text", "hello world" ] + , [ "closetag", "script" ] + , [ "closetag", "xml" ] + ] + , strict : false + , opt : { lowercasetags: true, noscript: true } + } + ) + +require(__dirname).test + ( { xml : "" + , expect : + [ [ "opentag", { name: "xml", attributes: {}, isSelfClosing: false } ] + , [ "opentag", { name: "script", attributes: {}, isSelfClosing: false } ] + , [ "opencdata", undefined ] + , [ "cdata", "hello world" ] + , [ "closecdata", undefined ] + , [ "closetag", "script" ] + , [ "closetag", "xml" ] + ] + , strict : false + , opt : { lowercasetags: true, noscript: true } + } + ) + diff --git a/node_modules/xml2js/node_modules/sax/test/issue-84.js b/node_modules/xml2js/node_modules/sax/test/issue-84.js new file mode 100644 index 0000000..0e7ee69 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/issue-84.js @@ -0,0 +1,13 @@ +// https://github.com/isaacs/sax-js/issues/49 +require(__dirname).test + ( { xml : "body" + , expect : + [ [ "processinginstruction", { name: "has", body: "unbalanced \"quotes" } ], + [ "opentag", { name: "xml", attributes: {}, isSelfClosing: false } ] + , [ "text", "body" ] + , [ "closetag", "xml" ] + ] + , strict : false + , opt : { lowercasetags: true, noscript: true } + } + ) diff --git a/node_modules/xml2js/node_modules/sax/test/parser-position.js b/node_modules/xml2js/node_modules/sax/test/parser-position.js new file mode 100644 index 0000000..e4a68b1 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/parser-position.js @@ -0,0 +1,28 @@ +var sax = require("../lib/sax"), + assert = require("assert") + +function testPosition(chunks, expectedEvents) { + var parser = sax.parser(); + expectedEvents.forEach(function(expectation) { + parser['on' + expectation[0]] = function() { + for (var prop in expectation[1]) { + assert.equal(parser[prop], expectation[1][prop]); + } + } + }); + chunks.forEach(function(chunk) { + parser.write(chunk); + }); +}; + +testPosition(['

            abcdefgh
            '], + [ ['opentag', { position: 5, startTagPosition: 1 }] + , ['text', { position: 19, startTagPosition: 14 }] + , ['closetag', { position: 19, startTagPosition: 14 }] + ]); + +testPosition(['
            abcde','fgh
            '], + [ ['opentag', { position: 5, startTagPosition: 1 }] + , ['text', { position: 19, startTagPosition: 14 }] + , ['closetag', { position: 19, startTagPosition: 14 }] + ]); diff --git a/node_modules/xml2js/node_modules/sax/test/script-close-better.js b/node_modules/xml2js/node_modules/sax/test/script-close-better.js new file mode 100644 index 0000000..f4887b9 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/script-close-better.js @@ -0,0 +1,12 @@ +require(__dirname).test({ + xml : "", + expect : [ + ["opentag", {"name": "HTML","attributes": {}, isSelfClosing: false}], + ["opentag", {"name": "HEAD","attributes": {}, isSelfClosing: false}], + ["opentag", {"name": "SCRIPT","attributes": {}, isSelfClosing: false}], + ["script", "'
            foo
            ", + expect : [ + ["opentag", {"name": "HTML","attributes": {}, "isSelfClosing": false}], + ["opentag", {"name": "HEAD","attributes": {}, "isSelfClosing": false}], + ["opentag", {"name": "SCRIPT","attributes": {}, "isSelfClosing": false}], + ["script", "if (1 < 0) { console.log('elo there'); }"], + ["closetag", "SCRIPT"], + ["closetag", "HEAD"], + ["closetag", "HTML"] + ] +}); diff --git a/node_modules/xml2js/node_modules/sax/test/self-closing-child-strict.js b/node_modules/xml2js/node_modules/sax/test/self-closing-child-strict.js new file mode 100644 index 0000000..3d6e985 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/self-closing-child-strict.js @@ -0,0 +1,44 @@ + +require(__dirname).test({ + xml : + ""+ + "" + + "" + + "" + + "" + + "=(|)" + + "" + + "", + expect : [ + ["opentag", { + "name": "root", + "attributes": {}, + "isSelfClosing": false + }], + ["opentag", { + "name": "child", + "attributes": {}, + "isSelfClosing": false + }], + ["opentag", { + "name": "haha", + "attributes": {}, + "isSelfClosing": true + }], + ["closetag", "haha"], + ["closetag", "child"], + ["opentag", { + "name": "monkey", + "attributes": {}, + "isSelfClosing": false + }], + ["text", "=(|)"], + ["closetag", "monkey"], + ["closetag", "root"], + ["end"], + ["ready"] + ], + strict : true, + opt : {} +}); + diff --git a/node_modules/xml2js/node_modules/sax/test/self-closing-child.js b/node_modules/xml2js/node_modules/sax/test/self-closing-child.js new file mode 100644 index 0000000..f31c366 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/self-closing-child.js @@ -0,0 +1,44 @@ + +require(__dirname).test({ + xml : + ""+ + "" + + "" + + "" + + "" + + "=(|)" + + "" + + "", + expect : [ + ["opentag", { + "name": "ROOT", + "attributes": {}, + "isSelfClosing": false + }], + ["opentag", { + "name": "CHILD", + "attributes": {}, + "isSelfClosing": false + }], + ["opentag", { + "name": "HAHA", + "attributes": {}, + "isSelfClosing": true + }], + ["closetag", "HAHA"], + ["closetag", "CHILD"], + ["opentag", { + "name": "MONKEY", + "attributes": {}, + "isSelfClosing": false + }], + ["text", "=(|)"], + ["closetag", "MONKEY"], + ["closetag", "ROOT"], + ["end"], + ["ready"] + ], + strict : false, + opt : {} +}); + diff --git a/node_modules/xml2js/node_modules/sax/test/self-closing-tag.js b/node_modules/xml2js/node_modules/sax/test/self-closing-tag.js new file mode 100644 index 0000000..d1d8b7c --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/self-closing-tag.js @@ -0,0 +1,25 @@ + +require(__dirname).test({ + xml : + " "+ + " "+ + " "+ + " "+ + "=(|) "+ + ""+ + " ", + expect : [ + ["opentag", {name:"ROOT", attributes:{}, isSelfClosing: false}], + ["opentag", {name:"HAHA", attributes:{}, isSelfClosing: true}], + ["closetag", "HAHA"], + ["opentag", {name:"HAHA", attributes:{}, isSelfClosing: true}], + ["closetag", "HAHA"], + // ["opentag", {name:"HAHA", attributes:{}}], + // ["closetag", "HAHA"], + ["opentag", {name:"MONKEY", attributes:{}, isSelfClosing: false}], + ["text", "=(|)"], + ["closetag", "MONKEY"], + ["closetag", "ROOT"] + ], + opt : { trim : true } +}); \ No newline at end of file diff --git a/node_modules/xml2js/node_modules/sax/test/stray-ending.js b/node_modules/xml2js/node_modules/sax/test/stray-ending.js new file mode 100644 index 0000000..bec467b --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/stray-ending.js @@ -0,0 +1,17 @@ +// stray ending tags should just be ignored in non-strict mode. +// https://github.com/isaacs/sax-js/issues/32 +require(__dirname).test + ( { xml : + "" + , expect : + [ [ "opentag", { name: "A", attributes: {}, isSelfClosing: false } ] + , [ "opentag", { name: "B", attributes: {}, isSelfClosing: false } ] + , [ "text", "" ] + , [ "closetag", "B" ] + , [ "closetag", "A" ] + ] + , strict : false + , opt : {} + } + ) + diff --git a/node_modules/xml2js/node_modules/sax/test/trailing-attribute-no-value.js b/node_modules/xml2js/node_modules/sax/test/trailing-attribute-no-value.js new file mode 100644 index 0000000..222837f --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/trailing-attribute-no-value.js @@ -0,0 +1,10 @@ + +require(__dirname).test({ + xml : + "", + expect : [ + ["attribute", {name:"ATTRIB", value:"attrib"}], + ["opentag", {name:"ROOT", attributes:{"ATTRIB":"attrib"}, isSelfClosing: false}] + ], + opt : { trim : true } +}); diff --git a/node_modules/xml2js/node_modules/sax/test/trailing-non-whitespace.js b/node_modules/xml2js/node_modules/sax/test/trailing-non-whitespace.js new file mode 100644 index 0000000..619578b --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/trailing-non-whitespace.js @@ -0,0 +1,18 @@ + +require(__dirname).test({ + xml : "Welcome, to monkey land", + expect : [ + ["opentag", { + "name": "SPAN", + "attributes": {}, + isSelfClosing: false + }], + ["text", "Welcome,"], + ["closetag", "SPAN"], + ["text", " to monkey land"], + ["end"], + ["ready"] + ], + strict : false, + opt : {} +}); diff --git a/node_modules/xml2js/node_modules/sax/test/unclosed-root.js b/node_modules/xml2js/node_modules/sax/test/unclosed-root.js new file mode 100644 index 0000000..f4eeac6 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/unclosed-root.js @@ -0,0 +1,11 @@ +require(__dirname).test + ( { xml : "" + + , expect : + [ [ "opentag", { name: "root", attributes: {}, isSelfClosing: false } ] + , [ "error", "Unclosed root tag\nLine: 0\nColumn: 6\nChar: " ] + ] + , strict : true + , opt : {} + } + ) diff --git a/node_modules/xml2js/node_modules/sax/test/unquoted.js b/node_modules/xml2js/node_modules/sax/test/unquoted.js new file mode 100644 index 0000000..b3a9a81 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/unquoted.js @@ -0,0 +1,18 @@ +// unquoted attributes should be ok in non-strict mode +// https://github.com/isaacs/sax-js/issues/31 +require(__dirname).test + ( { xml : + "" + , expect : + [ [ "attribute", { name: "CLASS", value: "test" } ] + , [ "attribute", { name: "HELLO", value: "world" } ] + , [ "opentag", { name: "SPAN", + attributes: { CLASS: "test", HELLO: "world" }, + isSelfClosing: false } ] + , [ "closetag", "SPAN" ] + ] + , strict : false + , opt : {} + } + ) + diff --git a/node_modules/xml2js/node_modules/sax/test/utf8-split.js b/node_modules/xml2js/node_modules/sax/test/utf8-split.js new file mode 100644 index 0000000..e22bc10 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/utf8-split.js @@ -0,0 +1,32 @@ +var assert = require('assert') +var saxStream = require('../lib/sax').createStream() + +var b = new Buffer('误') + +saxStream.on('text', function(text) { + assert.equal(text, b.toString()) +}) + +saxStream.write(new Buffer('')) +saxStream.write(b.slice(0, 1)) +saxStream.write(b.slice(1)) +saxStream.write(new Buffer('')) +saxStream.write(b.slice(0, 2)) +saxStream.write(b.slice(2)) +saxStream.write(new Buffer('')) +saxStream.write(b) +saxStream.write(new Buffer('')) +saxStream.write(Buffer.concat([new Buffer(''), b.slice(0, 1)])) +saxStream.end(Buffer.concat([b.slice(1), new Buffer('')])) + +var saxStream2 = require('../lib/sax').createStream() + +saxStream2.on('text', function(text) { + assert.equal(text, '�') +}); + +saxStream2.write(new Buffer('')); +saxStream2.write(new Buffer([0xC0])); +saxStream2.write(new Buffer('')); +saxStream2.write(Buffer.concat([new Buffer(''), b.slice(0,1)])); +saxStream2.end(); diff --git a/node_modules/xml2js/node_modules/sax/test/xmlns-as-tag-name.js b/node_modules/xml2js/node_modules/sax/test/xmlns-as-tag-name.js new file mode 100644 index 0000000..99142ca --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/xmlns-as-tag-name.js @@ -0,0 +1,15 @@ + +require(__dirname).test + ( { xml : + "" + , expect : + [ [ "opentag", { name: "xmlns", uri: "", prefix: "", local: "xmlns", + attributes: {}, ns: {}, + isSelfClosing: true} + ], + ["closetag", "xmlns"] + ] + , strict : true + , opt : { xmlns: true } + } + ); diff --git a/node_modules/xml2js/node_modules/sax/test/xmlns-issue-41.js b/node_modules/xml2js/node_modules/sax/test/xmlns-issue-41.js new file mode 100644 index 0000000..17ab45a --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/xmlns-issue-41.js @@ -0,0 +1,68 @@ +var t = require(__dirname) + + , xmls = // should be the same both ways. + [ "" + , "" ] + + , ex1 = + [ [ "opennamespace" + , { prefix: "a" + , uri: "http://ATTRIBUTE" + } + ] + , [ "attribute" + , { name: "xmlns:a" + , value: "http://ATTRIBUTE" + , prefix: "xmlns" + , local: "a" + , uri: "http://www.w3.org/2000/xmlns/" + } + ] + , [ "attribute" + , { name: "a:attr" + , local: "attr" + , prefix: "a" + , uri: "http://ATTRIBUTE" + , value: "value" + } + ] + , [ "opentag" + , { name: "parent" + , uri: "" + , prefix: "" + , local: "parent" + , attributes: + { "a:attr": + { name: "a:attr" + , local: "attr" + , prefix: "a" + , uri: "http://ATTRIBUTE" + , value: "value" + } + , "xmlns:a": + { name: "xmlns:a" + , local: "a" + , prefix: "xmlns" + , uri: "http://www.w3.org/2000/xmlns/" + , value: "http://ATTRIBUTE" + } + } + , ns: {"a": "http://ATTRIBUTE"} + , isSelfClosing: true + } + ] + , ["closetag", "parent"] + , ["closenamespace", { prefix: "a", uri: "http://ATTRIBUTE" }] + ] + + // swap the order of elements 2 and 1 + , ex2 = [ex1[0], ex1[2], ex1[1]].concat(ex1.slice(3)) + , expected = [ex1, ex2] + +xmls.forEach(function (x, i) { + t.test({ xml: x + , expect: expected[i] + , strict: true + , opt: { xmlns: true } + }) +}) diff --git a/node_modules/xml2js/node_modules/sax/test/xmlns-rebinding.js b/node_modules/xml2js/node_modules/sax/test/xmlns-rebinding.js new file mode 100644 index 0000000..07e0425 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/xmlns-rebinding.js @@ -0,0 +1,63 @@ + +require(__dirname).test + ( { xml : + ""+ + ""+ + ""+ + ""+ + ""+ + "" + + , expect : + [ [ "opennamespace", { prefix: "x", uri: "x1" } ] + , [ "opennamespace", { prefix: "y", uri: "y1" } ] + , [ "attribute", { name: "xmlns:x", value: "x1", uri: "http://www.w3.org/2000/xmlns/", prefix: "xmlns", local: "x" } ] + , [ "attribute", { name: "xmlns:y", value: "y1", uri: "http://www.w3.org/2000/xmlns/", prefix: "xmlns", local: "y" } ] + , [ "attribute", { name: "x:a", value: "x1", uri: "x1", prefix: "x", local: "a" } ] + , [ "attribute", { name: "y:a", value: "y1", uri: "y1", prefix: "y", local: "a" } ] + , [ "opentag", { name: "root", uri: "", prefix: "", local: "root", + attributes: { "xmlns:x": { name: "xmlns:x", value: "x1", uri: "http://www.w3.org/2000/xmlns/", prefix: "xmlns", local: "x" } + , "xmlns:y": { name: "xmlns:y", value: "y1", uri: "http://www.w3.org/2000/xmlns/", prefix: "xmlns", local: "y" } + , "x:a": { name: "x:a", value: "x1", uri: "x1", prefix: "x", local: "a" } + , "y:a": { name: "y:a", value: "y1", uri: "y1", prefix: "y", local: "a" } }, + ns: { x: 'x1', y: 'y1' }, + isSelfClosing: false } ] + + , [ "opennamespace", { prefix: "x", uri: "x2" } ] + , [ "attribute", { name: "xmlns:x", value: "x2", uri: "http://www.w3.org/2000/xmlns/", prefix: "xmlns", local: "x" } ] + , [ "opentag", { name: "rebind", uri: "", prefix: "", local: "rebind", + attributes: { "xmlns:x": { name: "xmlns:x", value: "x2", uri: "http://www.w3.org/2000/xmlns/", prefix: "xmlns", local: "x" } }, + ns: { x: 'x2' }, + isSelfClosing: false } ] + + , [ "attribute", { name: "x:a", value: "x2", uri: "x2", prefix: "x", local: "a" } ] + , [ "attribute", { name: "y:a", value: "y1", uri: "y1", prefix: "y", local: "a" } ] + , [ "opentag", { name: "check", uri: "", prefix: "", local: "check", + attributes: { "x:a": { name: "x:a", value: "x2", uri: "x2", prefix: "x", local: "a" } + , "y:a": { name: "y:a", value: "y1", uri: "y1", prefix: "y", local: "a" } }, + ns: { x: 'x2' }, + isSelfClosing: true } ] + + , [ "closetag", "check" ] + + , [ "closetag", "rebind" ] + , [ "closenamespace", { prefix: "x", uri: "x2" } ] + + , [ "attribute", { name: "x:a", value: "x1", uri: "x1", prefix: "x", local: "a" } ] + , [ "attribute", { name: "y:a", value: "y1", uri: "y1", prefix: "y", local: "a" } ] + , [ "opentag", { name: "check", uri: "", prefix: "", local: "check", + attributes: { "x:a": { name: "x:a", value: "x1", uri: "x1", prefix: "x", local: "a" } + , "y:a": { name: "y:a", value: "y1", uri: "y1", prefix: "y", local: "a" } }, + ns: { x: 'x1', y: 'y1' }, + isSelfClosing: true } ] + , [ "closetag", "check" ] + + , [ "closetag", "root" ] + , [ "closenamespace", { prefix: "x", uri: "x1" } ] + , [ "closenamespace", { prefix: "y", uri: "y1" } ] + ] + , strict : true + , opt : { xmlns: true } + } + ) + diff --git a/node_modules/xml2js/node_modules/sax/test/xmlns-strict.js b/node_modules/xml2js/node_modules/sax/test/xmlns-strict.js new file mode 100644 index 0000000..b5e3e51 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/xmlns-strict.js @@ -0,0 +1,74 @@ + +require(__dirname).test + ( { xml : + ""+ + ""+ + ""+ + ""+ + ""+ + ""+ + ""+ + ""+ + ""+ + "" + + , expect : + [ [ "opentag", { name: "root", prefix: "", local: "root", uri: "", + attributes: {}, ns: {}, isSelfClosing: false } ] + + , [ "attribute", { name: "attr", value: "normal", prefix: "", local: "attr", uri: "" } ] + , [ "opentag", { name: "plain", prefix: "", local: "plain", uri: "", + attributes: { "attr": { name: "attr", value: "normal", uri: "", prefix: "", local: "attr", uri: "" } }, + ns: {}, isSelfClosing: true } ] + , [ "closetag", "plain" ] + + , [ "opennamespace", { prefix: "", uri: "uri:default" } ] + + , [ "attribute", { name: "xmlns", value: "uri:default", prefix: "xmlns", local: "", uri: "http://www.w3.org/2000/xmlns/" } ] + , [ "opentag", { name: "ns1", prefix: "", local: "ns1", uri: "uri:default", + attributes: { "xmlns": { name: "xmlns", value: "uri:default", prefix: "xmlns", local: "", uri: "http://www.w3.org/2000/xmlns/" } }, + ns: { "": "uri:default" }, isSelfClosing: false } ] + + , [ "attribute", { name: "attr", value: "normal", prefix: "", local: "attr", uri: "" } ] + , [ "opentag", { name: "plain", prefix: "", local: "plain", uri: "uri:default", ns: { '': 'uri:default' }, + attributes: { "attr": { name: "attr", value: "normal", prefix: "", local: "attr", uri: "" } }, + isSelfClosing: true } ] + , [ "closetag", "plain" ] + + , [ "closetag", "ns1" ] + + , [ "closenamespace", { prefix: "", uri: "uri:default" } ] + + , [ "opennamespace", { prefix: "a", uri: "uri:nsa" } ] + + , [ "attribute", { name: "xmlns:a", value: "uri:nsa", prefix: "xmlns", local: "a", uri: "http://www.w3.org/2000/xmlns/" } ] + + , [ "opentag", { name: "ns2", prefix: "", local: "ns2", uri: "", + attributes: { "xmlns:a": { name: "xmlns:a", value: "uri:nsa", prefix: "xmlns", local: "a", uri: "http://www.w3.org/2000/xmlns/" } }, + ns: { a: "uri:nsa" }, isSelfClosing: false } ] + + , [ "attribute", { name: "attr", value: "normal", prefix: "", local: "attr", uri: "" } ] + , [ "opentag", { name: "plain", prefix: "", local: "plain", uri: "", + attributes: { "attr": { name: "attr", value: "normal", prefix: "", local: "attr", uri: "" } }, + ns: { a: 'uri:nsa' }, + isSelfClosing: true } ] + , [ "closetag", "plain" ] + + , [ "attribute", { name: "a:attr", value: "namespaced", prefix: "a", local: "attr", uri: "uri:nsa" } ] + , [ "opentag", { name: "a:ns", prefix: "a", local: "ns", uri: "uri:nsa", + attributes: { "a:attr": { name: "a:attr", value: "namespaced", prefix: "a", local: "attr", uri: "uri:nsa" } }, + ns: { a: 'uri:nsa' }, + isSelfClosing: true } ] + , [ "closetag", "a:ns" ] + + , [ "closetag", "ns2" ] + + , [ "closenamespace", { prefix: "a", uri: "uri:nsa" } ] + + , [ "closetag", "root" ] + ] + , strict : true + , opt : { xmlns: true } + } + ) + diff --git a/node_modules/xml2js/node_modules/sax/test/xmlns-unbound-element.js b/node_modules/xml2js/node_modules/sax/test/xmlns-unbound-element.js new file mode 100644 index 0000000..9d031a2 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/xmlns-unbound-element.js @@ -0,0 +1,33 @@ +require(__dirname).test( + { strict : true + , opt : { xmlns: true } + , expect : + [ [ "error", "Unbound namespace prefix: \"unbound:root\"\nLine: 0\nColumn: 15\nChar: >"] + , [ "opentag", { name: "unbound:root", uri: "unbound", prefix: "unbound", local: "root" + , attributes: {}, ns: {}, isSelfClosing: true } ] + , [ "closetag", "unbound:root" ] + ] + } +).write(""); + +require(__dirname).test( + { strict : true + , opt : { xmlns: true } + , expect : + [ [ "opennamespace", { prefix: "unbound", uri: "someuri" } ] + , [ "attribute", { name: 'xmlns:unbound', value: 'someuri' + , prefix: 'xmlns', local: 'unbound' + , uri: 'http://www.w3.org/2000/xmlns/' } ] + , [ "opentag", { name: "unbound:root", uri: "someuri", prefix: "unbound", local: "root" + , attributes: { 'xmlns:unbound': { + name: 'xmlns:unbound' + , value: 'someuri' + , prefix: 'xmlns' + , local: 'unbound' + , uri: 'http://www.w3.org/2000/xmlns/' } } + , ns: { "unbound": "someuri" }, isSelfClosing: true } ] + , [ "closetag", "unbound:root" ] + , [ "closenamespace", { prefix: 'unbound', uri: 'someuri' }] + ] + } +).write(""); diff --git a/node_modules/xml2js/node_modules/sax/test/xmlns-unbound.js b/node_modules/xml2js/node_modules/sax/test/xmlns-unbound.js new file mode 100644 index 0000000..b740e26 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/xmlns-unbound.js @@ -0,0 +1,15 @@ + +require(__dirname).test( + { strict : true + , opt : { xmlns: true } + , expect : + [ ["error", "Unbound namespace prefix: \"unbound\"\nLine: 0\nColumn: 28\nChar: >"] + + , [ "attribute", { name: "unbound:attr", value: "value", uri: "unbound", prefix: "unbound", local: "attr" } ] + , [ "opentag", { name: "root", uri: "", prefix: "", local: "root", + attributes: { "unbound:attr": { name: "unbound:attr", value: "value", uri: "unbound", prefix: "unbound", local: "attr" } }, + ns: {}, isSelfClosing: true } ] + , [ "closetag", "root" ] + ] + } +).write("") diff --git a/node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-ns.js b/node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-ns.js new file mode 100644 index 0000000..b1984d2 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-ns.js @@ -0,0 +1,31 @@ +var xmlns_attr = +{ + name: "xmlns", value: "http://foo", prefix: "xmlns", + local: "", uri : "http://www.w3.org/2000/xmlns/" +}; + +var attr_attr = +{ + name: "attr", value: "bar", prefix: "", + local : "attr", uri : "" +}; + + +require(__dirname).test + ( { xml : + "" + , expect : + [ [ "opennamespace", { prefix: "", uri: "http://foo" } ] + , [ "attribute", xmlns_attr ] + , [ "attribute", attr_attr ] + , [ "opentag", { name: "elm", prefix: "", local: "elm", uri : "http://foo", + ns : { "" : "http://foo" }, + attributes: { xmlns: xmlns_attr, attr: attr_attr }, + isSelfClosing: true } ] + , [ "closetag", "elm" ] + , [ "closenamespace", { prefix: "", uri: "http://foo"} ] + ] + , strict : true + , opt : {xmlns: true} + } + ) diff --git a/node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-prefix-attribute.js b/node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-prefix-attribute.js new file mode 100644 index 0000000..e41f218 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-prefix-attribute.js @@ -0,0 +1,36 @@ +require(__dirname).test( + { xml : "" + , expect : + [ [ "attribute" + , { name: "xml:lang" + , local: "lang" + , prefix: "xml" + , uri: "http://www.w3.org/XML/1998/namespace" + , value: "en" + } + ] + , [ "opentag" + , { name: "root" + , uri: "" + , prefix: "" + , local: "root" + , attributes: + { "xml:lang": + { name: "xml:lang" + , local: "lang" + , prefix: "xml" + , uri: "http://www.w3.org/XML/1998/namespace" + , value: "en" + } + } + , ns: {} + , isSelfClosing: true + } + ] + , ["closetag", "root"] + ] + , strict : true + , opt : { xmlns: true } + } +) + diff --git a/node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-prefix.js b/node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-prefix.js new file mode 100644 index 0000000..a85b478 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-prefix.js @@ -0,0 +1,21 @@ +require(__dirname).test( + { xml : "" + , expect : + [ + [ "opentag" + , { name: "xml:root" + , uri: "http://www.w3.org/XML/1998/namespace" + , prefix: "xml" + , local: "root" + , attributes: {} + , ns: {} + , isSelfClosing: true + } + ] + , ["closetag", "xml:root"] + ] + , strict : true + , opt : { xmlns: true } + } +) + diff --git a/node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-redefine.js b/node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-redefine.js new file mode 100644 index 0000000..d35d5a0 --- /dev/null +++ b/node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-redefine.js @@ -0,0 +1,41 @@ +require(__dirname).test( + { xml : "" + , expect : + [ ["error" + , "xml: prefix must be bound to http://www.w3.org/XML/1998/namespace\n" + + "Actual: ERROR\n" + + "Line: 0\nColumn: 27\nChar: '" + ] + , [ "attribute" + , { name: "xmlns:xml" + , local: "xml" + , prefix: "xmlns" + , uri: "http://www.w3.org/2000/xmlns/" + , value: "ERROR" + } + ] + , [ "opentag" + , { name: "xml:root" + , uri: "http://www.w3.org/XML/1998/namespace" + , prefix: "xml" + , local: "root" + , attributes: + { "xmlns:xml": + { name: "xmlns:xml" + , local: "xml" + , prefix: "xmlns" + , uri: "http://www.w3.org/2000/xmlns/" + , value: "ERROR" + } + } + , ns: {} + , isSelfClosing: true + } + ] + , ["closetag", "xml:root"] + ] + , strict : true + , opt : { xmlns: true } + } +) + diff --git a/node_modules/xml2js/node_modules/xmlbuilder/.npmignore b/node_modules/xml2js/node_modules/xmlbuilder/.npmignore new file mode 100644 index 0000000..b6ad1f6 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/.npmignore @@ -0,0 +1,5 @@ +.travis.yml +src +test +perf +coverage diff --git a/node_modules/xml2js/node_modules/xmlbuilder/LICENSE b/node_modules/xml2js/node_modules/xmlbuilder/LICENSE new file mode 100644 index 0000000..e7cbac9 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Ozgur Ozcitak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/xml2js/node_modules/xmlbuilder/README.md b/node_modules/xml2js/node_modules/xmlbuilder/README.md new file mode 100644 index 0000000..13a5b12 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/README.md @@ -0,0 +1,86 @@ +# xmlbuilder-js + +An XML builder for [node.js](https://nodejs.org/) similar to +[java-xmlbuilder](https://github.com/jmurty/java-xmlbuilder). + +[![License](http://img.shields.io/npm/l/xmlbuilder.svg?style=flat-square)](http://opensource.org/licenses/MIT) +[![NPM Version](http://img.shields.io/npm/v/xmlbuilder.svg?style=flat-square)](https://npmjs.com/package/xmlbuilder) +[![NPM Downloads](https://img.shields.io/npm/dm/xmlbuilder.svg?style=flat-square)](https://npmjs.com/package/xmlbuilder) + +[![Build Status](http://img.shields.io/travis/oozcitak/xmlbuilder-js.svg?style=flat-square)](http://travis-ci.org/oozcitak/xmlbuilder-js) +[![Dependency Status](http://img.shields.io/david/oozcitak/xmlbuilder-js.svg?style=flat-square)](https://david-dm.org/oozcitak/xmlbuilder-js) +[![Dev Dependency Status](http://img.shields.io/david/dev/oozcitak/xmlbuilder-js.svg?style=flat-square)](https://david-dm.org/oozcitak/xmlbuilder-js) +[![Code Coverage](https://img.shields.io/coveralls/oozcitak/xmlbuilder-js.svg?style=flat-square)](https://coveralls.io/github/oozcitak/xmlbuilder-js) + +### Installation: + +``` sh +npm install xmlbuilder +``` + +### Usage: + +``` js +var builder = require('xmlbuilder'); +var xml = builder.create('root') + .ele('xmlbuilder') + .ele('repo', {'type': 'git'}, 'git://github.com/oozcitak/xmlbuilder-js.git') + .end({ pretty: true}); + +console.log(xml); +``` + +will result in: + +``` xml + + + + git://github.com/oozcitak/xmlbuilder-js.git + + +``` + +It is also possible to convert objects into nodes: + +``` js +builder.create({ + root: { + xmlbuilder: { + repo: { + '@type': 'git', // attributes start with @ + '#text': 'git://github.com/oozcitak/xmlbuilder-js.git' // text node + } + } + } +}); +``` + +If you need to do some processing: + +``` js +var root = builder.create('squares'); +root.com('f(x) = x^2'); +for(var i = 1; i <= 5; i++) +{ + var item = root.ele('data'); + item.att('x', i); + item.att('y', i * i); +} +``` + +This will result in: + +``` xml + + + + + + + + + +``` + +See the [wiki](https://github.com/oozcitak/xmlbuilder-js/wiki) for details. diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLAttribute.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLAttribute.js new file mode 100644 index 0000000..247c9d1 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLAttribute.js @@ -0,0 +1,32 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLAttribute, create; + + create = require('lodash/object/create'); + + module.exports = XMLAttribute = (function() { + function XMLAttribute(parent, name, value) { + this.stringify = parent.stringify; + if (name == null) { + throw new Error("Missing attribute name of element " + parent.name); + } + if (value == null) { + throw new Error("Missing attribute value for attribute " + name + " of element " + parent.name); + } + this.name = this.stringify.attName(name); + this.value = this.stringify.attValue(value); + } + + XMLAttribute.prototype.clone = function() { + return create(XMLAttribute.prototype, this); + }; + + XMLAttribute.prototype.toString = function(options, level) { + return ' ' + this.name + '="' + this.value + '"'; + }; + + return XMLAttribute; + + })(); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLBuilder.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLBuilder.js new file mode 100644 index 0000000..4282833 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLBuilder.js @@ -0,0 +1,69 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLBuilder, XMLDeclaration, XMLDocType, XMLElement, XMLStringifier; + + XMLStringifier = require('./XMLStringifier'); + + XMLDeclaration = require('./XMLDeclaration'); + + XMLDocType = require('./XMLDocType'); + + XMLElement = require('./XMLElement'); + + module.exports = XMLBuilder = (function() { + function XMLBuilder(name, options) { + var root, temp; + if (name == null) { + throw new Error("Root element needs a name"); + } + if (options == null) { + options = {}; + } + this.options = options; + this.stringify = new XMLStringifier(options); + temp = new XMLElement(this, 'doc'); + root = temp.element(name); + root.isRoot = true; + root.documentObject = this; + this.rootObject = root; + if (!options.headless) { + root.declaration(options); + if ((options.pubID != null) || (options.sysID != null)) { + root.doctype(options); + } + } + } + + XMLBuilder.prototype.root = function() { + return this.rootObject; + }; + + XMLBuilder.prototype.end = function(options) { + return this.toString(options); + }; + + XMLBuilder.prototype.toString = function(options) { + var indent, newline, offset, pretty, r, ref, ref1, ref2; + pretty = (options != null ? options.pretty : void 0) || false; + indent = (ref = options != null ? options.indent : void 0) != null ? ref : ' '; + offset = (ref1 = options != null ? options.offset : void 0) != null ? ref1 : 0; + newline = (ref2 = options != null ? options.newline : void 0) != null ? ref2 : '\n'; + r = ''; + if (this.xmldec != null) { + r += this.xmldec.toString(options); + } + if (this.doctype != null) { + r += this.doctype.toString(options); + } + r += this.rootObject.toString(options); + if (pretty && r.slice(-newline.length) === newline) { + r = r.slice(0, -newline.length); + } + return r; + }; + + return XMLBuilder; + + })(); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLCData.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLCData.js new file mode 100644 index 0000000..00002f1 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLCData.js @@ -0,0 +1,49 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLCData, XMLNode, create, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + + create = require('lodash/object/create'); + + XMLNode = require('./XMLNode'); + + module.exports = XMLCData = (function(superClass) { + extend(XMLCData, superClass); + + function XMLCData(parent, text) { + XMLCData.__super__.constructor.call(this, parent); + if (text == null) { + throw new Error("Missing CDATA text"); + } + this.text = this.stringify.cdata(text); + } + + XMLCData.prototype.clone = function() { + return create(XMLCData.prototype, this); + }; + + XMLCData.prototype.toString = function(options, level) { + var indent, newline, offset, pretty, r, ref, ref1, ref2, space; + pretty = (options != null ? options.pretty : void 0) || false; + indent = (ref = options != null ? options.indent : void 0) != null ? ref : ' '; + offset = (ref1 = options != null ? options.offset : void 0) != null ? ref1 : 0; + newline = (ref2 = options != null ? options.newline : void 0) != null ? ref2 : '\n'; + level || (level = 0); + space = new Array(level + offset + 1).join(indent); + r = ''; + if (pretty) { + r += space; + } + r += ''; + if (pretty) { + r += newline; + } + return r; + }; + + return XMLCData; + + })(XMLNode); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLComment.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLComment.js new file mode 100644 index 0000000..ca23e95 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLComment.js @@ -0,0 +1,49 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLComment, XMLNode, create, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + + create = require('lodash/object/create'); + + XMLNode = require('./XMLNode'); + + module.exports = XMLComment = (function(superClass) { + extend(XMLComment, superClass); + + function XMLComment(parent, text) { + XMLComment.__super__.constructor.call(this, parent); + if (text == null) { + throw new Error("Missing comment text"); + } + this.text = this.stringify.comment(text); + } + + XMLComment.prototype.clone = function() { + return create(XMLComment.prototype, this); + }; + + XMLComment.prototype.toString = function(options, level) { + var indent, newline, offset, pretty, r, ref, ref1, ref2, space; + pretty = (options != null ? options.pretty : void 0) || false; + indent = (ref = options != null ? options.indent : void 0) != null ? ref : ' '; + offset = (ref1 = options != null ? options.offset : void 0) != null ? ref1 : 0; + newline = (ref2 = options != null ? options.newline : void 0) != null ? ref2 : '\n'; + level || (level = 0); + space = new Array(level + offset + 1).join(indent); + r = ''; + if (pretty) { + r += space; + } + r += ''; + if (pretty) { + r += newline; + } + return r; + }; + + return XMLComment; + + })(XMLNode); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDTDAttList.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDTDAttList.js new file mode 100644 index 0000000..62e6d8a --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDTDAttList.js @@ -0,0 +1,68 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLDTDAttList, create; + + create = require('lodash/object/create'); + + module.exports = XMLDTDAttList = (function() { + function XMLDTDAttList(parent, elementName, attributeName, attributeType, defaultValueType, defaultValue) { + this.stringify = parent.stringify; + if (elementName == null) { + throw new Error("Missing DTD element name"); + } + if (attributeName == null) { + throw new Error("Missing DTD attribute name"); + } + if (!attributeType) { + throw new Error("Missing DTD attribute type"); + } + if (!defaultValueType) { + throw new Error("Missing DTD attribute default"); + } + if (defaultValueType.indexOf('#') !== 0) { + defaultValueType = '#' + defaultValueType; + } + if (!defaultValueType.match(/^(#REQUIRED|#IMPLIED|#FIXED|#DEFAULT)$/)) { + throw new Error("Invalid default value type; expected: #REQUIRED, #IMPLIED, #FIXED or #DEFAULT"); + } + if (defaultValue && !defaultValueType.match(/^(#FIXED|#DEFAULT)$/)) { + throw new Error("Default value only applies to #FIXED or #DEFAULT"); + } + this.elementName = this.stringify.eleName(elementName); + this.attributeName = this.stringify.attName(attributeName); + this.attributeType = this.stringify.dtdAttType(attributeType); + this.defaultValue = this.stringify.dtdAttDefault(defaultValue); + this.defaultValueType = defaultValueType; + } + + XMLDTDAttList.prototype.toString = function(options, level) { + var indent, newline, offset, pretty, r, ref, ref1, ref2, space; + pretty = (options != null ? options.pretty : void 0) || false; + indent = (ref = options != null ? options.indent : void 0) != null ? ref : ' '; + offset = (ref1 = options != null ? options.offset : void 0) != null ? ref1 : 0; + newline = (ref2 = options != null ? options.newline : void 0) != null ? ref2 : '\n'; + level || (level = 0); + space = new Array(level + offset + 1).join(indent); + r = ''; + if (pretty) { + r += space; + } + r += ''; + if (pretty) { + r += newline; + } + return r; + }; + + return XMLDTDAttList; + + })(); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDTDElement.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDTDElement.js new file mode 100644 index 0000000..2d155e2 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDTDElement.js @@ -0,0 +1,46 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLDTDElement, create; + + create = require('lodash/object/create'); + + module.exports = XMLDTDElement = (function() { + function XMLDTDElement(parent, name, value) { + this.stringify = parent.stringify; + if (name == null) { + throw new Error("Missing DTD element name"); + } + if (!value) { + value = '(#PCDATA)'; + } + if (Array.isArray(value)) { + value = '(' + value.join(',') + ')'; + } + this.name = this.stringify.eleName(name); + this.value = this.stringify.dtdElementValue(value); + } + + XMLDTDElement.prototype.toString = function(options, level) { + var indent, newline, offset, pretty, r, ref, ref1, ref2, space; + pretty = (options != null ? options.pretty : void 0) || false; + indent = (ref = options != null ? options.indent : void 0) != null ? ref : ' '; + offset = (ref1 = options != null ? options.offset : void 0) != null ? ref1 : 0; + newline = (ref2 = options != null ? options.newline : void 0) != null ? ref2 : '\n'; + level || (level = 0); + space = new Array(level + offset + 1).join(indent); + r = ''; + if (pretty) { + r += space; + } + r += ''; + if (pretty) { + r += newline; + } + return r; + }; + + return XMLDTDElement; + + })(); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDTDEntity.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDTDEntity.js new file mode 100644 index 0000000..3201d19 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDTDEntity.js @@ -0,0 +1,84 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLDTDEntity, create, isObject; + + create = require('lodash/object/create'); + + isObject = require('lodash/lang/isObject'); + + module.exports = XMLDTDEntity = (function() { + function XMLDTDEntity(parent, pe, name, value) { + this.stringify = parent.stringify; + if (name == null) { + throw new Error("Missing entity name"); + } + if (value == null) { + throw new Error("Missing entity value"); + } + this.pe = !!pe; + this.name = this.stringify.eleName(name); + if (!isObject(value)) { + this.value = this.stringify.dtdEntityValue(value); + } else { + if (!value.pubID && !value.sysID) { + throw new Error("Public and/or system identifiers are required for an external entity"); + } + if (value.pubID && !value.sysID) { + throw new Error("System identifier is required for a public external entity"); + } + if (value.pubID != null) { + this.pubID = this.stringify.dtdPubID(value.pubID); + } + if (value.sysID != null) { + this.sysID = this.stringify.dtdSysID(value.sysID); + } + if (value.nData != null) { + this.nData = this.stringify.dtdNData(value.nData); + } + if (this.pe && this.nData) { + throw new Error("Notation declaration is not allowed in a parameter entity"); + } + } + } + + XMLDTDEntity.prototype.toString = function(options, level) { + var indent, newline, offset, pretty, r, ref, ref1, ref2, space; + pretty = (options != null ? options.pretty : void 0) || false; + indent = (ref = options != null ? options.indent : void 0) != null ? ref : ' '; + offset = (ref1 = options != null ? options.offset : void 0) != null ? ref1 : 0; + newline = (ref2 = options != null ? options.newline : void 0) != null ? ref2 : '\n'; + level || (level = 0); + space = new Array(level + offset + 1).join(indent); + r = ''; + if (pretty) { + r += space; + } + r += ''; + if (pretty) { + r += newline; + } + return r; + }; + + return XMLDTDEntity; + + })(); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDTDNotation.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDTDNotation.js new file mode 100644 index 0000000..cfbccf4 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDTDNotation.js @@ -0,0 +1,56 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLDTDNotation, create; + + create = require('lodash/object/create'); + + module.exports = XMLDTDNotation = (function() { + function XMLDTDNotation(parent, name, value) { + this.stringify = parent.stringify; + if (name == null) { + throw new Error("Missing notation name"); + } + if (!value.pubID && !value.sysID) { + throw new Error("Public or system identifiers are required for an external entity"); + } + this.name = this.stringify.eleName(name); + if (value.pubID != null) { + this.pubID = this.stringify.dtdPubID(value.pubID); + } + if (value.sysID != null) { + this.sysID = this.stringify.dtdSysID(value.sysID); + } + } + + XMLDTDNotation.prototype.toString = function(options, level) { + var indent, newline, offset, pretty, r, ref, ref1, ref2, space; + pretty = (options != null ? options.pretty : void 0) || false; + indent = (ref = options != null ? options.indent : void 0) != null ? ref : ' '; + offset = (ref1 = options != null ? options.offset : void 0) != null ? ref1 : 0; + newline = (ref2 = options != null ? options.newline : void 0) != null ? ref2 : '\n'; + level || (level = 0); + space = new Array(level + offset + 1).join(indent); + r = ''; + if (pretty) { + r += space; + } + r += ''; + if (pretty) { + r += newline; + } + return r; + }; + + return XMLDTDNotation; + + })(); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDeclaration.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDeclaration.js new file mode 100644 index 0000000..b2d8435 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDeclaration.js @@ -0,0 +1,65 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLDeclaration, XMLNode, create, isObject, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + + create = require('lodash/object/create'); + + isObject = require('lodash/lang/isObject'); + + XMLNode = require('./XMLNode'); + + module.exports = XMLDeclaration = (function(superClass) { + extend(XMLDeclaration, superClass); + + function XMLDeclaration(parent, version, encoding, standalone) { + var ref; + XMLDeclaration.__super__.constructor.call(this, parent); + if (isObject(version)) { + ref = version, version = ref.version, encoding = ref.encoding, standalone = ref.standalone; + } + if (!version) { + version = '1.0'; + } + this.version = this.stringify.xmlVersion(version); + if (encoding != null) { + this.encoding = this.stringify.xmlEncoding(encoding); + } + if (standalone != null) { + this.standalone = this.stringify.xmlStandalone(standalone); + } + } + + XMLDeclaration.prototype.toString = function(options, level) { + var indent, newline, offset, pretty, r, ref, ref1, ref2, space; + pretty = (options != null ? options.pretty : void 0) || false; + indent = (ref = options != null ? options.indent : void 0) != null ? ref : ' '; + offset = (ref1 = options != null ? options.offset : void 0) != null ? ref1 : 0; + newline = (ref2 = options != null ? options.newline : void 0) != null ? ref2 : '\n'; + level || (level = 0); + space = new Array(level + offset + 1).join(indent); + r = ''; + if (pretty) { + r += space; + } + r += ''; + if (pretty) { + r += newline; + } + return r; + }; + + return XMLDeclaration; + + })(XMLNode); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDocType.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDocType.js new file mode 100644 index 0000000..eec6f36 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLDocType.js @@ -0,0 +1,188 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLCData, XMLComment, XMLDTDAttList, XMLDTDElement, XMLDTDEntity, XMLDTDNotation, XMLDocType, XMLProcessingInstruction, create, isObject; + + create = require('lodash/object/create'); + + isObject = require('lodash/lang/isObject'); + + XMLCData = require('./XMLCData'); + + XMLComment = require('./XMLComment'); + + XMLDTDAttList = require('./XMLDTDAttList'); + + XMLDTDEntity = require('./XMLDTDEntity'); + + XMLDTDElement = require('./XMLDTDElement'); + + XMLDTDNotation = require('./XMLDTDNotation'); + + XMLProcessingInstruction = require('./XMLProcessingInstruction'); + + module.exports = XMLDocType = (function() { + function XMLDocType(parent, pubID, sysID) { + var ref, ref1; + this.documentObject = parent; + this.stringify = this.documentObject.stringify; + this.children = []; + if (isObject(pubID)) { + ref = pubID, pubID = ref.pubID, sysID = ref.sysID; + } + if (sysID == null) { + ref1 = [pubID, sysID], sysID = ref1[0], pubID = ref1[1]; + } + if (pubID != null) { + this.pubID = this.stringify.dtdPubID(pubID); + } + if (sysID != null) { + this.sysID = this.stringify.dtdSysID(sysID); + } + } + + XMLDocType.prototype.element = function(name, value) { + var child; + child = new XMLDTDElement(this, name, value); + this.children.push(child); + return this; + }; + + XMLDocType.prototype.attList = function(elementName, attributeName, attributeType, defaultValueType, defaultValue) { + var child; + child = new XMLDTDAttList(this, elementName, attributeName, attributeType, defaultValueType, defaultValue); + this.children.push(child); + return this; + }; + + XMLDocType.prototype.entity = function(name, value) { + var child; + child = new XMLDTDEntity(this, false, name, value); + this.children.push(child); + return this; + }; + + XMLDocType.prototype.pEntity = function(name, value) { + var child; + child = new XMLDTDEntity(this, true, name, value); + this.children.push(child); + return this; + }; + + XMLDocType.prototype.notation = function(name, value) { + var child; + child = new XMLDTDNotation(this, name, value); + this.children.push(child); + return this; + }; + + XMLDocType.prototype.cdata = function(value) { + var child; + child = new XMLCData(this, value); + this.children.push(child); + return this; + }; + + XMLDocType.prototype.comment = function(value) { + var child; + child = new XMLComment(this, value); + this.children.push(child); + return this; + }; + + XMLDocType.prototype.instruction = function(target, value) { + var child; + child = new XMLProcessingInstruction(this, target, value); + this.children.push(child); + return this; + }; + + XMLDocType.prototype.root = function() { + return this.documentObject.root(); + }; + + XMLDocType.prototype.document = function() { + return this.documentObject; + }; + + XMLDocType.prototype.toString = function(options, level) { + var child, i, indent, len, newline, offset, pretty, r, ref, ref1, ref2, ref3, space; + pretty = (options != null ? options.pretty : void 0) || false; + indent = (ref = options != null ? options.indent : void 0) != null ? ref : ' '; + offset = (ref1 = options != null ? options.offset : void 0) != null ? ref1 : 0; + newline = (ref2 = options != null ? options.newline : void 0) != null ? ref2 : '\n'; + level || (level = 0); + space = new Array(level + offset + 1).join(indent); + r = ''; + if (pretty) { + r += space; + } + r += ' 0) { + r += ' ['; + if (pretty) { + r += newline; + } + ref3 = this.children; + for (i = 0, len = ref3.length; i < len; i++) { + child = ref3[i]; + r += child.toString(options, level + 1); + } + r += ']'; + } + r += '>'; + if (pretty) { + r += newline; + } + return r; + }; + + XMLDocType.prototype.ele = function(name, value) { + return this.element(name, value); + }; + + XMLDocType.prototype.att = function(elementName, attributeName, attributeType, defaultValueType, defaultValue) { + return this.attList(elementName, attributeName, attributeType, defaultValueType, defaultValue); + }; + + XMLDocType.prototype.ent = function(name, value) { + return this.entity(name, value); + }; + + XMLDocType.prototype.pent = function(name, value) { + return this.pEntity(name, value); + }; + + XMLDocType.prototype.not = function(name, value) { + return this.notation(name, value); + }; + + XMLDocType.prototype.dat = function(value) { + return this.cdata(value); + }; + + XMLDocType.prototype.com = function(value) { + return this.comment(value); + }; + + XMLDocType.prototype.ins = function(target, value) { + return this.instruction(target, value); + }; + + XMLDocType.prototype.up = function() { + return this.root(); + }; + + XMLDocType.prototype.doc = function() { + return this.document(); + }; + + return XMLDocType; + + })(); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLElement.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLElement.js new file mode 100644 index 0000000..d5814c8 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLElement.js @@ -0,0 +1,212 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLAttribute, XMLElement, XMLNode, XMLProcessingInstruction, create, every, isFunction, isObject, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + + create = require('lodash/object/create'); + + isObject = require('lodash/lang/isObject'); + + isFunction = require('lodash/lang/isFunction'); + + every = require('lodash/collection/every'); + + XMLNode = require('./XMLNode'); + + XMLAttribute = require('./XMLAttribute'); + + XMLProcessingInstruction = require('./XMLProcessingInstruction'); + + module.exports = XMLElement = (function(superClass) { + extend(XMLElement, superClass); + + function XMLElement(parent, name, attributes) { + XMLElement.__super__.constructor.call(this, parent); + if (name == null) { + throw new Error("Missing element name"); + } + this.name = this.stringify.eleName(name); + this.children = []; + this.instructions = []; + this.attributes = {}; + if (attributes != null) { + this.attribute(attributes); + } + } + + XMLElement.prototype.clone = function() { + var att, attName, clonedSelf, i, len, pi, ref, ref1; + clonedSelf = create(XMLElement.prototype, this); + if (clonedSelf.isRoot) { + clonedSelf.documentObject = null; + } + clonedSelf.attributes = {}; + ref = this.attributes; + for (attName in ref) { + if (!hasProp.call(ref, attName)) continue; + att = ref[attName]; + clonedSelf.attributes[attName] = att.clone(); + } + clonedSelf.instructions = []; + ref1 = this.instructions; + for (i = 0, len = ref1.length; i < len; i++) { + pi = ref1[i]; + clonedSelf.instructions.push(pi.clone()); + } + clonedSelf.children = []; + this.children.forEach(function(child) { + var clonedChild; + clonedChild = child.clone(); + clonedChild.parent = clonedSelf; + return clonedSelf.children.push(clonedChild); + }); + return clonedSelf; + }; + + XMLElement.prototype.attribute = function(name, value) { + var attName, attValue; + if (name != null) { + name = name.valueOf(); + } + if (isObject(name)) { + for (attName in name) { + if (!hasProp.call(name, attName)) continue; + attValue = name[attName]; + this.attribute(attName, attValue); + } + } else { + if (isFunction(value)) { + value = value.apply(); + } + if (!this.options.skipNullAttributes || (value != null)) { + this.attributes[name] = new XMLAttribute(this, name, value); + } + } + return this; + }; + + XMLElement.prototype.removeAttribute = function(name) { + var attName, i, len; + if (name == null) { + throw new Error("Missing attribute name"); + } + name = name.valueOf(); + if (Array.isArray(name)) { + for (i = 0, len = name.length; i < len; i++) { + attName = name[i]; + delete this.attributes[attName]; + } + } else { + delete this.attributes[name]; + } + return this; + }; + + XMLElement.prototype.instruction = function(target, value) { + var i, insTarget, insValue, instruction, len; + if (target != null) { + target = target.valueOf(); + } + if (value != null) { + value = value.valueOf(); + } + if (Array.isArray(target)) { + for (i = 0, len = target.length; i < len; i++) { + insTarget = target[i]; + this.instruction(insTarget); + } + } else if (isObject(target)) { + for (insTarget in target) { + if (!hasProp.call(target, insTarget)) continue; + insValue = target[insTarget]; + this.instruction(insTarget, insValue); + } + } else { + if (isFunction(value)) { + value = value.apply(); + } + instruction = new XMLProcessingInstruction(this, target, value); + this.instructions.push(instruction); + } + return this; + }; + + XMLElement.prototype.toString = function(options, level) { + var att, child, i, indent, instruction, j, len, len1, name, newline, offset, pretty, r, ref, ref1, ref2, ref3, ref4, ref5, space; + pretty = (options != null ? options.pretty : void 0) || false; + indent = (ref = options != null ? options.indent : void 0) != null ? ref : ' '; + offset = (ref1 = options != null ? options.offset : void 0) != null ? ref1 : 0; + newline = (ref2 = options != null ? options.newline : void 0) != null ? ref2 : '\n'; + level || (level = 0); + space = new Array(level + offset + 1).join(indent); + r = ''; + ref3 = this.instructions; + for (i = 0, len = ref3.length; i < len; i++) { + instruction = ref3[i]; + r += instruction.toString(options, level); + } + if (pretty) { + r += space; + } + r += '<' + this.name; + ref4 = this.attributes; + for (name in ref4) { + if (!hasProp.call(ref4, name)) continue; + att = ref4[name]; + r += att.toString(options); + } + if (this.children.length === 0 || every(this.children, function(e) { + return e.value === ''; + })) { + r += '/>'; + if (pretty) { + r += newline; + } + } else if (pretty && this.children.length === 1 && (this.children[0].value != null)) { + r += '>'; + r += this.children[0].value; + r += ''; + r += newline; + } else { + r += '>'; + if (pretty) { + r += newline; + } + ref5 = this.children; + for (j = 0, len1 = ref5.length; j < len1; j++) { + child = ref5[j]; + r += child.toString(options, level + 1); + } + if (pretty) { + r += space; + } + r += ''; + if (pretty) { + r += newline; + } + } + return r; + }; + + XMLElement.prototype.att = function(name, value) { + return this.attribute(name, value); + }; + + XMLElement.prototype.ins = function(target, value) { + return this.instruction(target, value); + }; + + XMLElement.prototype.a = function(name, value) { + return this.attribute(name, value); + }; + + XMLElement.prototype.i = function(target, value) { + return this.instruction(target, value); + }; + + return XMLElement; + + })(XMLNode); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLNode.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLNode.js new file mode 100644 index 0000000..7b35471 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLNode.js @@ -0,0 +1,331 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLCData, XMLComment, XMLDeclaration, XMLDocType, XMLElement, XMLNode, XMLRaw, XMLText, isEmpty, isFunction, isObject, + hasProp = {}.hasOwnProperty; + + isObject = require('lodash/lang/isObject'); + + isFunction = require('lodash/lang/isFunction'); + + isEmpty = require('lodash/lang/isEmpty'); + + XMLElement = null; + + XMLCData = null; + + XMLComment = null; + + XMLDeclaration = null; + + XMLDocType = null; + + XMLRaw = null; + + XMLText = null; + + module.exports = XMLNode = (function() { + function XMLNode(parent) { + this.parent = parent; + this.options = this.parent.options; + this.stringify = this.parent.stringify; + if (XMLElement === null) { + XMLElement = require('./XMLElement'); + XMLCData = require('./XMLCData'); + XMLComment = require('./XMLComment'); + XMLDeclaration = require('./XMLDeclaration'); + XMLDocType = require('./XMLDocType'); + XMLRaw = require('./XMLRaw'); + XMLText = require('./XMLText'); + } + } + + XMLNode.prototype.element = function(name, attributes, text) { + var childNode, item, j, k, key, lastChild, len, len1, ref, val; + lastChild = null; + if (attributes == null) { + attributes = {}; + } + attributes = attributes.valueOf(); + if (!isObject(attributes)) { + ref = [attributes, text], text = ref[0], attributes = ref[1]; + } + if (name != null) { + name = name.valueOf(); + } + if (Array.isArray(name)) { + for (j = 0, len = name.length; j < len; j++) { + item = name[j]; + lastChild = this.element(item); + } + } else if (isFunction(name)) { + lastChild = this.element(name.apply()); + } else if (isObject(name)) { + for (key in name) { + if (!hasProp.call(name, key)) continue; + val = name[key]; + if (isFunction(val)) { + val = val.apply(); + } + if ((isObject(val)) && (isEmpty(val))) { + val = null; + } + if (!this.options.ignoreDecorators && this.stringify.convertAttKey && key.indexOf(this.stringify.convertAttKey) === 0) { + lastChild = this.attribute(key.substr(this.stringify.convertAttKey.length), val); + } else if (!this.options.ignoreDecorators && this.stringify.convertPIKey && key.indexOf(this.stringify.convertPIKey) === 0) { + lastChild = this.instruction(key.substr(this.stringify.convertPIKey.length), val); + } else if (!this.options.separateArrayItems && Array.isArray(val)) { + for (k = 0, len1 = val.length; k < len1; k++) { + item = val[k]; + childNode = {}; + childNode[key] = item; + lastChild = this.element(childNode); + } + } else if (isObject(val)) { + lastChild = this.element(key); + lastChild.element(val); + } else { + lastChild = this.element(key, val); + } + } + } else { + if (!this.options.ignoreDecorators && this.stringify.convertTextKey && name.indexOf(this.stringify.convertTextKey) === 0) { + lastChild = this.text(text); + } else if (!this.options.ignoreDecorators && this.stringify.convertCDataKey && name.indexOf(this.stringify.convertCDataKey) === 0) { + lastChild = this.cdata(text); + } else if (!this.options.ignoreDecorators && this.stringify.convertCommentKey && name.indexOf(this.stringify.convertCommentKey) === 0) { + lastChild = this.comment(text); + } else if (!this.options.ignoreDecorators && this.stringify.convertRawKey && name.indexOf(this.stringify.convertRawKey) === 0) { + lastChild = this.raw(text); + } else { + lastChild = this.node(name, attributes, text); + } + } + if (lastChild == null) { + throw new Error("Could not create any elements with: " + name); + } + return lastChild; + }; + + XMLNode.prototype.insertBefore = function(name, attributes, text) { + var child, i, removed; + if (this.isRoot) { + throw new Error("Cannot insert elements at root level"); + } + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i); + child = this.parent.element(name, attributes, text); + Array.prototype.push.apply(this.parent.children, removed); + return child; + }; + + XMLNode.prototype.insertAfter = function(name, attributes, text) { + var child, i, removed; + if (this.isRoot) { + throw new Error("Cannot insert elements at root level"); + } + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i + 1); + child = this.parent.element(name, attributes, text); + Array.prototype.push.apply(this.parent.children, removed); + return child; + }; + + XMLNode.prototype.remove = function() { + var i, ref; + if (this.isRoot) { + throw new Error("Cannot remove the root element"); + } + i = this.parent.children.indexOf(this); + [].splice.apply(this.parent.children, [i, i - i + 1].concat(ref = [])), ref; + return this.parent; + }; + + XMLNode.prototype.node = function(name, attributes, text) { + var child, ref; + if (name != null) { + name = name.valueOf(); + } + if (attributes == null) { + attributes = {}; + } + attributes = attributes.valueOf(); + if (!isObject(attributes)) { + ref = [attributes, text], text = ref[0], attributes = ref[1]; + } + child = new XMLElement(this, name, attributes); + if (text != null) { + child.text(text); + } + this.children.push(child); + return child; + }; + + XMLNode.prototype.text = function(value) { + var child; + child = new XMLText(this, value); + this.children.push(child); + return this; + }; + + XMLNode.prototype.cdata = function(value) { + var child; + child = new XMLCData(this, value); + this.children.push(child); + return this; + }; + + XMLNode.prototype.comment = function(value) { + var child; + child = new XMLComment(this, value); + this.children.push(child); + return this; + }; + + XMLNode.prototype.raw = function(value) { + var child; + child = new XMLRaw(this, value); + this.children.push(child); + return this; + }; + + XMLNode.prototype.declaration = function(version, encoding, standalone) { + var doc, xmldec; + doc = this.document(); + xmldec = new XMLDeclaration(doc, version, encoding, standalone); + doc.xmldec = xmldec; + return doc.root(); + }; + + XMLNode.prototype.doctype = function(pubID, sysID) { + var doc, doctype; + doc = this.document(); + doctype = new XMLDocType(doc, pubID, sysID); + doc.doctype = doctype; + return doctype; + }; + + XMLNode.prototype.up = function() { + if (this.isRoot) { + throw new Error("The root node has no parent. Use doc() if you need to get the document object."); + } + return this.parent; + }; + + XMLNode.prototype.root = function() { + var child; + if (this.isRoot) { + return this; + } + child = this.parent; + while (!child.isRoot) { + child = child.parent; + } + return child; + }; + + XMLNode.prototype.document = function() { + return this.root().documentObject; + }; + + XMLNode.prototype.end = function(options) { + return this.document().toString(options); + }; + + XMLNode.prototype.prev = function() { + var i; + if (this.isRoot) { + throw new Error("Root node has no siblings"); + } + i = this.parent.children.indexOf(this); + if (i < 1) { + throw new Error("Already at the first node"); + } + return this.parent.children[i - 1]; + }; + + XMLNode.prototype.next = function() { + var i; + if (this.isRoot) { + throw new Error("Root node has no siblings"); + } + i = this.parent.children.indexOf(this); + if (i === -1 || i === this.parent.children.length - 1) { + throw new Error("Already at the last node"); + } + return this.parent.children[i + 1]; + }; + + XMLNode.prototype.importXMLBuilder = function(xmlbuilder) { + var clonedRoot; + clonedRoot = xmlbuilder.root().clone(); + clonedRoot.parent = this; + clonedRoot.isRoot = false; + this.children.push(clonedRoot); + return this; + }; + + XMLNode.prototype.ele = function(name, attributes, text) { + return this.element(name, attributes, text); + }; + + XMLNode.prototype.nod = function(name, attributes, text) { + return this.node(name, attributes, text); + }; + + XMLNode.prototype.txt = function(value) { + return this.text(value); + }; + + XMLNode.prototype.dat = function(value) { + return this.cdata(value); + }; + + XMLNode.prototype.com = function(value) { + return this.comment(value); + }; + + XMLNode.prototype.doc = function() { + return this.document(); + }; + + XMLNode.prototype.dec = function(version, encoding, standalone) { + return this.declaration(version, encoding, standalone); + }; + + XMLNode.prototype.dtd = function(pubID, sysID) { + return this.doctype(pubID, sysID); + }; + + XMLNode.prototype.e = function(name, attributes, text) { + return this.element(name, attributes, text); + }; + + XMLNode.prototype.n = function(name, attributes, text) { + return this.node(name, attributes, text); + }; + + XMLNode.prototype.t = function(value) { + return this.text(value); + }; + + XMLNode.prototype.d = function(value) { + return this.cdata(value); + }; + + XMLNode.prototype.c = function(value) { + return this.comment(value); + }; + + XMLNode.prototype.r = function(value) { + return this.raw(value); + }; + + XMLNode.prototype.u = function() { + return this.up(); + }; + + return XMLNode; + + })(); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLProcessingInstruction.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLProcessingInstruction.js new file mode 100644 index 0000000..f5d8c6c --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLProcessingInstruction.js @@ -0,0 +1,51 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLProcessingInstruction, create; + + create = require('lodash/object/create'); + + module.exports = XMLProcessingInstruction = (function() { + function XMLProcessingInstruction(parent, target, value) { + this.stringify = parent.stringify; + if (target == null) { + throw new Error("Missing instruction target"); + } + this.target = this.stringify.insTarget(target); + if (value) { + this.value = this.stringify.insValue(value); + } + } + + XMLProcessingInstruction.prototype.clone = function() { + return create(XMLProcessingInstruction.prototype, this); + }; + + XMLProcessingInstruction.prototype.toString = function(options, level) { + var indent, newline, offset, pretty, r, ref, ref1, ref2, space; + pretty = (options != null ? options.pretty : void 0) || false; + indent = (ref = options != null ? options.indent : void 0) != null ? ref : ' '; + offset = (ref1 = options != null ? options.offset : void 0) != null ? ref1 : 0; + newline = (ref2 = options != null ? options.newline : void 0) != null ? ref2 : '\n'; + level || (level = 0); + space = new Array(level + offset + 1).join(indent); + r = ''; + if (pretty) { + r += space; + } + r += ''; + if (pretty) { + r += newline; + } + return r; + }; + + return XMLProcessingInstruction; + + })(); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLRaw.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLRaw.js new file mode 100644 index 0000000..499d0e2 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLRaw.js @@ -0,0 +1,49 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLNode, XMLRaw, create, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + + create = require('lodash/object/create'); + + XMLNode = require('./XMLNode'); + + module.exports = XMLRaw = (function(superClass) { + extend(XMLRaw, superClass); + + function XMLRaw(parent, text) { + XMLRaw.__super__.constructor.call(this, parent); + if (text == null) { + throw new Error("Missing raw text"); + } + this.value = this.stringify.raw(text); + } + + XMLRaw.prototype.clone = function() { + return create(XMLRaw.prototype, this); + }; + + XMLRaw.prototype.toString = function(options, level) { + var indent, newline, offset, pretty, r, ref, ref1, ref2, space; + pretty = (options != null ? options.pretty : void 0) || false; + indent = (ref = options != null ? options.indent : void 0) != null ? ref : ' '; + offset = (ref1 = options != null ? options.offset : void 0) != null ? ref1 : 0; + newline = (ref2 = options != null ? options.newline : void 0) != null ? ref2 : '\n'; + level || (level = 0); + space = new Array(level + offset + 1).join(indent); + r = ''; + if (pretty) { + r += space; + } + r += this.value; + if (pretty) { + r += newline; + } + return r; + }; + + return XMLRaw; + + })(XMLNode); + +}).call(this); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLStringifier.js b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLStringifier.js new file mode 100644 index 0000000..f0ab1fc --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/lib/XMLStringifier.js @@ -0,0 +1,165 @@ +// Generated by CoffeeScript 1.9.1 +(function() { + var XMLStringifier, + bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + hasProp = {}.hasOwnProperty; + + module.exports = XMLStringifier = (function() { + function XMLStringifier(options) { + this.assertLegalChar = bind(this.assertLegalChar, this); + var key, ref, value; + this.allowSurrogateChars = options != null ? options.allowSurrogateChars : void 0; + ref = (options != null ? options.stringify : void 0) || {}; + for (key in ref) { + if (!hasProp.call(ref, key)) continue; + value = ref[key]; + this[key] = value; + } + } + + XMLStringifier.prototype.eleName = function(val) { + val = '' + val || ''; + return this.assertLegalChar(val); + }; + + XMLStringifier.prototype.eleText = function(val) { + val = '' + val || ''; + return this.assertLegalChar(this.elEscape(val)); + }; + + XMLStringifier.prototype.cdata = function(val) { + val = '' + val || ''; + if (val.match(/]]>/)) { + throw new Error("Invalid CDATA text: " + val); + } + return this.assertLegalChar(val); + }; + + XMLStringifier.prototype.comment = function(val) { + val = '' + val || ''; + if (val.match(/--/)) { + throw new Error("Comment text cannot contain double-hypen: " + val); + } + return this.assertLegalChar(val); + }; + + XMLStringifier.prototype.raw = function(val) { + return '' + val || ''; + }; + + XMLStringifier.prototype.attName = function(val) { + return '' + val || ''; + }; + + XMLStringifier.prototype.attValue = function(val) { + val = '' + val || ''; + return this.attEscape(val); + }; + + XMLStringifier.prototype.insTarget = function(val) { + return '' + val || ''; + }; + + XMLStringifier.prototype.insValue = function(val) { + val = '' + val || ''; + if (val.match(/\?>/)) { + throw new Error("Invalid processing instruction value: " + val); + } + return val; + }; + + XMLStringifier.prototype.xmlVersion = function(val) { + val = '' + val || ''; + if (!val.match(/1\.[0-9]+/)) { + throw new Error("Invalid version number: " + val); + } + return val; + }; + + XMLStringifier.prototype.xmlEncoding = function(val) { + val = '' + val || ''; + if (!val.match(/^[A-Za-z](?:[A-Za-z0-9._-]|-)*$/)) { + throw new Error("Invalid encoding: " + val); + } + return val; + }; + + XMLStringifier.prototype.xmlStandalone = function(val) { + if (val) { + return "yes"; + } else { + return "no"; + } + }; + + XMLStringifier.prototype.dtdPubID = function(val) { + return '' + val || ''; + }; + + XMLStringifier.prototype.dtdSysID = function(val) { + return '' + val || ''; + }; + + XMLStringifier.prototype.dtdElementValue = function(val) { + return '' + val || ''; + }; + + XMLStringifier.prototype.dtdAttType = function(val) { + return '' + val || ''; + }; + + XMLStringifier.prototype.dtdAttDefault = function(val) { + if (val != null) { + return '' + val || ''; + } else { + return val; + } + }; + + XMLStringifier.prototype.dtdEntityValue = function(val) { + return '' + val || ''; + }; + + XMLStringifier.prototype.dtdNData = function(val) { + return '' + val || ''; + }; + + XMLStringifier.prototype.convertAttKey = '@'; + + XMLStringifier.prototype.convertPIKey = '?'; + + XMLStringifier.prototype.convertTextKey = '#text'; + + XMLStringifier.prototype.convertCDataKey = '#cdata'; + + XMLStringifier.prototype.convertCommentKey = '#comment'; + + XMLStringifier.prototype.convertRawKey = '#raw'; + + XMLStringifier.prototype.assertLegalChar = function(str) { + var chars, chr; + if (this.allowSurrogateChars) { + chars = /[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uFFFE-\uFFFF]/; + } else { + chars = /[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE-\uFFFF]/; + } + chr = str.match(chars); + if (chr) { + throw new Error("Invalid character (" + chr + ") in string: " + str + " at index " + chr.index); + } + return str; + }; + + XMLStringifier.prototype.elEscape = function(str) { + return str.replace(/&/g, '&').replace(//g, '>').replace(/\r/g, ' '); + }; + + XMLStringifier.prototype.attEscape = function(str) { + return str.replace(/&/g, '&').replace(/ +Based on Underscore.js, copyright 2009-2015 Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/README.md b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/README.md new file mode 100644 index 0000000..fd98e5c --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/README.md @@ -0,0 +1,121 @@ +# lodash v3.10.1 + +The [modern build](https://github.com/lodash/lodash/wiki/Build-Differences) of [lodash](https://lodash.com/) exported as [Node.js](http://nodejs.org/)/[io.js](https://iojs.org/) modules. + +Generated using [lodash-cli](https://www.npmjs.com/package/lodash-cli): +```bash +$ lodash modularize modern exports=node -o ./ +$ lodash modern -d -o ./index.js +``` + +## Installation + +Using npm: + +```bash +$ {sudo -H} npm i -g npm +$ npm i --save lodash +``` + +In Node.js/io.js: + +```js +// load the modern build +var _ = require('lodash'); +// or a method category +var array = require('lodash/array'); +// or a method (great for smaller builds with browserify/webpack) +var chunk = require('lodash/array/chunk'); +``` + +See the [package source](https://github.com/lodash/lodash/tree/3.10.1-npm) for more details. + +**Note:**
            +Don’t assign values to the [special variable](http://nodejs.org/api/repl.html#repl_repl_features) `_` when in the REPL.
            +Install [n_](https://www.npmjs.com/package/n_) for a REPL that includes lodash by default. + +## Module formats + +lodash is also available in a variety of other builds & module formats. + + * npm packages for [modern](https://www.npmjs.com/package/lodash), [compatibility](https://www.npmjs.com/package/lodash-compat), & [per method](https://www.npmjs.com/browse/keyword/lodash-modularized) builds + * AMD modules for [modern](https://github.com/lodash/lodash/tree/3.10.1-amd) & [compatibility](https://github.com/lodash/lodash-compat/tree/3.10.1-amd) builds + * ES modules for the [modern](https://github.com/lodash/lodash/tree/3.10.1-es) build + +## Further Reading + + * [API Documentation](https://lodash.com/docs) + * [Build Differences](https://github.com/lodash/lodash/wiki/Build-Differences) + * [Changelog](https://github.com/lodash/lodash/wiki/Changelog) + * [Roadmap](https://github.com/lodash/lodash/wiki/Roadmap) + * [More Resources](https://github.com/lodash/lodash/wiki/Resources) + +## Features + + * ~100% [code coverage](https://coveralls.io/r/lodash) + * Follows [semantic versioning](http://semver.org/) for releases + * [Lazily evaluated](http://filimanjaro.com/blog/2014/introducing-lazy-evaluation/) chaining + * [_(…)](https://lodash.com/docs#_) supports implicit chaining + * [_.ary](https://lodash.com/docs#ary) & [_.rearg](https://lodash.com/docs#rearg) to change function argument limits & order + * [_.at](https://lodash.com/docs#at) for cherry-picking collection values + * [_.attempt](https://lodash.com/docs#attempt) to execute functions which may error without a try-catch + * [_.before](https://lodash.com/docs#before) to complement [_.after](https://lodash.com/docs#after) + * [_.bindKey](https://lodash.com/docs#bindKey) for binding [*“lazy”*](http://michaux.ca/articles/lazy-function-definition-pattern) defined methods + * [_.chunk](https://lodash.com/docs#chunk) for splitting an array into chunks of a given size + * [_.clone](https://lodash.com/docs#clone) supports shallow cloning of `Date` & `RegExp` objects + * [_.cloneDeep](https://lodash.com/docs#cloneDeep) for deep cloning arrays & objects + * [_.curry](https://lodash.com/docs#curry) & [_.curryRight](https://lodash.com/docs#curryRight) for creating [curried](http://hughfdjackson.com/javascript/why-curry-helps/) functions + * [_.debounce](https://lodash.com/docs#debounce) & [_.throttle](https://lodash.com/docs#throttle) are cancelable & accept options for more control + * [_.defaultsDeep](https://lodash.com/docs#defaultsDeep) for recursively assigning default properties + * [_.fill](https://lodash.com/docs#fill) to fill arrays with values + * [_.findKey](https://lodash.com/docs#findKey) for finding keys + * [_.flow](https://lodash.com/docs#flow) to complement [_.flowRight](https://lodash.com/docs#flowRight) (a.k.a `_.compose`) + * [_.forEach](https://lodash.com/docs#forEach) supports exiting early + * [_.forIn](https://lodash.com/docs#forIn) for iterating all enumerable properties + * [_.forOwn](https://lodash.com/docs#forOwn) for iterating own properties + * [_.get](https://lodash.com/docs#get) & [_.set](https://lodash.com/docs#set) for deep property getting & setting + * [_.gt](https://lodash.com/docs#gt), [_.gte](https://lodash.com/docs#gte), [_.lt](https://lodash.com/docs#lt), & [_.lte](https://lodash.com/docs#lte) relational methods + * [_.inRange](https://lodash.com/docs#inRange) for checking whether a number is within a given range + * [_.isNative](https://lodash.com/docs#isNative) to check for native functions + * [_.isPlainObject](https://lodash.com/docs#isPlainObject) & [_.toPlainObject](https://lodash.com/docs#toPlainObject) to check for & convert to `Object` objects + * [_.isTypedArray](https://lodash.com/docs#isTypedArray) to check for typed arrays + * [_.mapKeys](https://lodash.com/docs#mapKeys) for mapping keys to an object + * [_.matches](https://lodash.com/docs#matches) supports deep object comparisons + * [_.matchesProperty](https://lodash.com/docs#matchesProperty) to complement [_.matches](https://lodash.com/docs#matches) & [_.property](https://lodash.com/docs#property) + * [_.merge](https://lodash.com/docs#merge) for a deep [_.extend](https://lodash.com/docs#extend) + * [_.method](https://lodash.com/docs#method) & [_.methodOf](https://lodash.com/docs#methodOf) to create functions that invoke methods + * [_.modArgs](https://lodash.com/docs#modArgs) for more advanced functional composition + * [_.parseInt](https://lodash.com/docs#parseInt) for consistent cross-environment behavior + * [_.pull](https://lodash.com/docs#pull), [_.pullAt](https://lodash.com/docs#pullAt), & [_.remove](https://lodash.com/docs#remove) for mutating arrays + * [_.random](https://lodash.com/docs#random) supports returning floating-point numbers + * [_.restParam](https://lodash.com/docs#restParam) & [_.spread](https://lodash.com/docs#spread) for applying rest parameters & spreading arguments to functions + * [_.runInContext](https://lodash.com/docs#runInContext) for collisionless mixins & easier mocking + * [_.slice](https://lodash.com/docs#slice) for creating subsets of array-like values + * [_.sortByAll](https://lodash.com/docs#sortByAll) & [_.sortByOrder](https://lodash.com/docs#sortByOrder) for sorting by multiple properties & orders + * [_.support](https://lodash.com/docs#support) for flagging environment features + * [_.template](https://lodash.com/docs#template) supports [*“imports”*](https://lodash.com/docs#templateSettings-imports) options & [ES template delimiters](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-template-literal-lexical-components) + * [_.transform](https://lodash.com/docs#transform) as a powerful alternative to [_.reduce](https://lodash.com/docs#reduce) for transforming objects + * [_.unzipWith](https://lodash.com/docs#unzipWith) & [_.zipWith](https://lodash.com/docs#zipWith) to specify how grouped values should be combined + * [_.valuesIn](https://lodash.com/docs#valuesIn) for getting values of all enumerable properties + * [_.xor](https://lodash.com/docs#xor) to complement [_.difference](https://lodash.com/docs#difference), [_.intersection](https://lodash.com/docs#intersection), & [_.union](https://lodash.com/docs#union) + * [_.add](https://lodash.com/docs#add), [_.round](https://lodash.com/docs#round), [_.sum](https://lodash.com/docs#sum), & + [more](https://lodash.com/docs "_.ceil & _.floor") math methods + * [_.bind](https://lodash.com/docs#bind), [_.curry](https://lodash.com/docs#curry), [_.partial](https://lodash.com/docs#partial), & + [more](https://lodash.com/docs "_.bindKey, _.curryRight, _.partialRight") support customizable argument placeholders + * [_.capitalize](https://lodash.com/docs#capitalize), [_.trim](https://lodash.com/docs#trim), & + [more](https://lodash.com/docs "_.camelCase, _.deburr, _.endsWith, _.escapeRegExp, _.kebabCase, _.pad, _.padLeft, _.padRight, _.repeat, _.snakeCase, _.startCase, _.startsWith, _.trimLeft, _.trimRight, _.trunc, _.words") string methods + * [_.clone](https://lodash.com/docs#clone), [_.isEqual](https://lodash.com/docs#isEqual), & + [more](https://lodash.com/docs "_.assign, _.cloneDeep, _.merge") accept customizer callbacks + * [_.dropWhile](https://lodash.com/docs#dropWhile), [_.takeWhile](https://lodash.com/docs#takeWhile), & + [more](https://lodash.com/docs "_.drop, _.dropRight, _.dropRightWhile, _.take, _.takeRight, _.takeRightWhile") to complement [_.first](https://lodash.com/docs#first), [_.initial](https://lodash.com/docs#initial), [_.last](https://lodash.com/docs#last), & [_.rest](https://lodash.com/docs#rest) + * [_.findLast](https://lodash.com/docs#findLast), [_.findLastKey](https://lodash.com/docs#findLastKey), & + [more](https://lodash.com/docs "_.curryRight, _.dropRight, _.dropRightWhile, _.flowRight, _.forEachRight, _.forInRight, _.forOwnRight, _.padRight, partialRight, _.takeRight, _.trimRight, _.takeRightWhile") right-associative methods + * [_.includes](https://lodash.com/docs#includes), [_.toArray](https://lodash.com/docs#toArray), & + [more](https://lodash.com/docs "_.at, _.countBy, _.every, _.filter, _.find, _.findLast, _.findWhere, _.forEach, _.forEachRight, _.groupBy, _.indexBy, _.invoke, _.map, _.max, _.min, _.partition, _.pluck, _.reduce, _.reduceRight, _.reject, _.shuffle, _.size, _.some, _.sortBy, _.sortByAll, _.sortByOrder, _.sum, _.where") accept strings + * [_#commit](https://lodash.com/docs#prototype-commit) & [_#plant](https://lodash.com/docs#prototype-plant) for working with chain sequences + * [_#thru](https://lodash.com/docs#thru) to pass values thru a chain sequence + +## Support + +Tested in Chrome 43-44, Firefox 38-39, IE 6-11, MS Edge, Safari 5-8, ChakraNode 0.12.2, io.js 2.5.0, Node.js 0.8.28, 0.10.40, & 0.12.7, PhantomJS 1.9.8, RingoJS 0.11, & Rhino 1.7.6. +Automated [browser](https://saucelabs.com/u/lodash) & [CI](https://travis-ci.org/lodash/lodash/) test runs are available. Special thanks to [Sauce Labs](https://saucelabs.com/) for providing automated browser testing. diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array.js new file mode 100644 index 0000000..e5121fa --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array.js @@ -0,0 +1,44 @@ +module.exports = { + 'chunk': require('./array/chunk'), + 'compact': require('./array/compact'), + 'difference': require('./array/difference'), + 'drop': require('./array/drop'), + 'dropRight': require('./array/dropRight'), + 'dropRightWhile': require('./array/dropRightWhile'), + 'dropWhile': require('./array/dropWhile'), + 'fill': require('./array/fill'), + 'findIndex': require('./array/findIndex'), + 'findLastIndex': require('./array/findLastIndex'), + 'first': require('./array/first'), + 'flatten': require('./array/flatten'), + 'flattenDeep': require('./array/flattenDeep'), + 'head': require('./array/head'), + 'indexOf': require('./array/indexOf'), + 'initial': require('./array/initial'), + 'intersection': require('./array/intersection'), + 'last': require('./array/last'), + 'lastIndexOf': require('./array/lastIndexOf'), + 'object': require('./array/object'), + 'pull': require('./array/pull'), + 'pullAt': require('./array/pullAt'), + 'remove': require('./array/remove'), + 'rest': require('./array/rest'), + 'slice': require('./array/slice'), + 'sortedIndex': require('./array/sortedIndex'), + 'sortedLastIndex': require('./array/sortedLastIndex'), + 'tail': require('./array/tail'), + 'take': require('./array/take'), + 'takeRight': require('./array/takeRight'), + 'takeRightWhile': require('./array/takeRightWhile'), + 'takeWhile': require('./array/takeWhile'), + 'union': require('./array/union'), + 'uniq': require('./array/uniq'), + 'unique': require('./array/unique'), + 'unzip': require('./array/unzip'), + 'unzipWith': require('./array/unzipWith'), + 'without': require('./array/without'), + 'xor': require('./array/xor'), + 'zip': require('./array/zip'), + 'zipObject': require('./array/zipObject'), + 'zipWith': require('./array/zipWith') +}; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/chunk.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/chunk.js new file mode 100644 index 0000000..c8be1fb --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/chunk.js @@ -0,0 +1,46 @@ +var baseSlice = require('../internal/baseSlice'), + isIterateeCall = require('../internal/isIterateeCall'); + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeCeil = Math.ceil, + nativeFloor = Math.floor, + nativeMax = Math.max; + +/** + * Creates an array of elements split into groups the length of `size`. + * If `collection` can't be split evenly, the final chunk will be the remaining + * elements. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to process. + * @param {number} [size=1] The length of each chunk. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the new array containing chunks. + * @example + * + * _.chunk(['a', 'b', 'c', 'd'], 2); + * // => [['a', 'b'], ['c', 'd']] + * + * _.chunk(['a', 'b', 'c', 'd'], 3); + * // => [['a', 'b', 'c'], ['d']] + */ +function chunk(array, size, guard) { + if (guard ? isIterateeCall(array, size, guard) : size == null) { + size = 1; + } else { + size = nativeMax(nativeFloor(size) || 1, 1); + } + var index = 0, + length = array ? array.length : 0, + resIndex = -1, + result = Array(nativeCeil(length / size)); + + while (index < length) { + result[++resIndex] = baseSlice(array, index, (index += size)); + } + return result; +} + +module.exports = chunk; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/compact.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/compact.js new file mode 100644 index 0000000..1dc1c55 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/compact.js @@ -0,0 +1,30 @@ +/** + * Creates an array with all falsey values removed. The values `false`, `null`, + * `0`, `""`, `undefined`, and `NaN` are falsey. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to compact. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ +function compact(array) { + var index = -1, + length = array ? array.length : 0, + resIndex = -1, + result = []; + + while (++index < length) { + var value = array[index]; + if (value) { + result[++resIndex] = value; + } + } + return result; +} + +module.exports = compact; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/difference.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/difference.js new file mode 100644 index 0000000..128932a --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/difference.js @@ -0,0 +1,29 @@ +var baseDifference = require('../internal/baseDifference'), + baseFlatten = require('../internal/baseFlatten'), + isArrayLike = require('../internal/isArrayLike'), + isObjectLike = require('../internal/isObjectLike'), + restParam = require('../function/restParam'); + +/** + * Creates an array of unique `array` values not included in the other + * provided arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The arrays of values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.difference([1, 2, 3], [4, 2]); + * // => [1, 3] + */ +var difference = restParam(function(array, values) { + return (isObjectLike(array) && isArrayLike(array)) + ? baseDifference(array, baseFlatten(values, false, true)) + : []; +}); + +module.exports = difference; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/drop.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/drop.js new file mode 100644 index 0000000..039a0b5 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/drop.js @@ -0,0 +1,39 @@ +var baseSlice = require('../internal/baseSlice'), + isIterateeCall = require('../internal/isIterateeCall'); + +/** + * Creates a slice of `array` with `n` elements dropped from the beginning. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.drop([1, 2, 3]); + * // => [2, 3] + * + * _.drop([1, 2, 3], 2); + * // => [3] + * + * _.drop([1, 2, 3], 5); + * // => [] + * + * _.drop([1, 2, 3], 0); + * // => [1, 2, 3] + */ +function drop(array, n, guard) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (guard ? isIterateeCall(array, n, guard) : n == null) { + n = 1; + } + return baseSlice(array, n < 0 ? 0 : n); +} + +module.exports = drop; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/dropRight.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/dropRight.js new file mode 100644 index 0000000..14b5eb6 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/dropRight.js @@ -0,0 +1,40 @@ +var baseSlice = require('../internal/baseSlice'), + isIterateeCall = require('../internal/isIterateeCall'); + +/** + * Creates a slice of `array` with `n` elements dropped from the end. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropRight([1, 2, 3]); + * // => [1, 2] + * + * _.dropRight([1, 2, 3], 2); + * // => [1] + * + * _.dropRight([1, 2, 3], 5); + * // => [] + * + * _.dropRight([1, 2, 3], 0); + * // => [1, 2, 3] + */ +function dropRight(array, n, guard) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (guard ? isIterateeCall(array, n, guard) : n == null) { + n = 1; + } + n = length - (+n || 0); + return baseSlice(array, 0, n < 0 ? 0 : n); +} + +module.exports = dropRight; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/dropRightWhile.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/dropRightWhile.js new file mode 100644 index 0000000..be158bd --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/dropRightWhile.js @@ -0,0 +1,59 @@ +var baseCallback = require('../internal/baseCallback'), + baseWhile = require('../internal/baseWhile'); + +/** + * Creates a slice of `array` excluding elements dropped from the end. + * Elements are dropped until `predicate` returns falsey. The predicate is + * bound to `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that match the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropRightWhile([1, 2, 3], function(n) { + * return n > 1; + * }); + * // => [1] + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.dropRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user'); + * // => ['barney', 'fred'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.dropRightWhile(users, 'active', false), 'user'); + * // => ['barney'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.dropRightWhile(users, 'active'), 'user'); + * // => ['barney', 'fred', 'pebbles'] + */ +function dropRightWhile(array, predicate, thisArg) { + return (array && array.length) + ? baseWhile(array, baseCallback(predicate, thisArg, 3), true, true) + : []; +} + +module.exports = dropRightWhile; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/dropWhile.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/dropWhile.js new file mode 100644 index 0000000..d9eabae --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/dropWhile.js @@ -0,0 +1,59 @@ +var baseCallback = require('../internal/baseCallback'), + baseWhile = require('../internal/baseWhile'); + +/** + * Creates a slice of `array` excluding elements dropped from the beginning. + * Elements are dropped until `predicate` returns falsey. The predicate is + * bound to `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropWhile([1, 2, 3], function(n) { + * return n < 3; + * }); + * // => [3] + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.dropWhile(users, { 'user': 'barney', 'active': false }), 'user'); + * // => ['fred', 'pebbles'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.dropWhile(users, 'active', false), 'user'); + * // => ['pebbles'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.dropWhile(users, 'active'), 'user'); + * // => ['barney', 'fred', 'pebbles'] + */ +function dropWhile(array, predicate, thisArg) { + return (array && array.length) + ? baseWhile(array, baseCallback(predicate, thisArg, 3), true) + : []; +} + +module.exports = dropWhile; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/fill.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/fill.js new file mode 100644 index 0000000..2c8f6da --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/fill.js @@ -0,0 +1,44 @@ +var baseFill = require('../internal/baseFill'), + isIterateeCall = require('../internal/isIterateeCall'); + +/** + * Fills elements of `array` with `value` from `start` up to, but not + * including, `end`. + * + * **Note:** This method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3]; + * + * _.fill(array, 'a'); + * console.log(array); + * // => ['a', 'a', 'a'] + * + * _.fill(Array(3), 2); + * // => [2, 2, 2] + * + * _.fill([4, 6, 8], '*', 1, 2); + * // => [4, '*', 8] + */ +function fill(array, value, start, end) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (start && typeof start != 'number' && isIterateeCall(array, value, start)) { + start = 0; + end = length; + } + return baseFill(array, value, start, end); +} + +module.exports = fill; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/findIndex.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/findIndex.js new file mode 100644 index 0000000..2a6b8e1 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/findIndex.js @@ -0,0 +1,53 @@ +var createFindIndex = require('../internal/createFindIndex'); + +/** + * This method is like `_.find` except that it returns the index of the first + * element `predicate` returns truthy for instead of the element itself. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.findIndex(users, function(chr) { + * return chr.user == 'barney'; + * }); + * // => 0 + * + * // using the `_.matches` callback shorthand + * _.findIndex(users, { 'user': 'fred', 'active': false }); + * // => 1 + * + * // using the `_.matchesProperty` callback shorthand + * _.findIndex(users, 'active', false); + * // => 0 + * + * // using the `_.property` callback shorthand + * _.findIndex(users, 'active'); + * // => 2 + */ +var findIndex = createFindIndex(); + +module.exports = findIndex; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/findLastIndex.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/findLastIndex.js new file mode 100644 index 0000000..d6d8eca --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/findLastIndex.js @@ -0,0 +1,53 @@ +var createFindIndex = require('../internal/createFindIndex'); + +/** + * This method is like `_.findIndex` except that it iterates over elements + * of `collection` from right to left. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.findLastIndex(users, function(chr) { + * return chr.user == 'pebbles'; + * }); + * // => 2 + * + * // using the `_.matches` callback shorthand + * _.findLastIndex(users, { 'user': 'barney', 'active': true }); + * // => 0 + * + * // using the `_.matchesProperty` callback shorthand + * _.findLastIndex(users, 'active', false); + * // => 2 + * + * // using the `_.property` callback shorthand + * _.findLastIndex(users, 'active'); + * // => 0 + */ +var findLastIndex = createFindIndex(true); + +module.exports = findLastIndex; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/first.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/first.js new file mode 100644 index 0000000..b3b9c79 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/first.js @@ -0,0 +1,22 @@ +/** + * Gets the first element of `array`. + * + * @static + * @memberOf _ + * @alias head + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the first element of `array`. + * @example + * + * _.first([1, 2, 3]); + * // => 1 + * + * _.first([]); + * // => undefined + */ +function first(array) { + return array ? array[0] : undefined; +} + +module.exports = first; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/flatten.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/flatten.js new file mode 100644 index 0000000..dc2eff8 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/flatten.js @@ -0,0 +1,32 @@ +var baseFlatten = require('../internal/baseFlatten'), + isIterateeCall = require('../internal/isIterateeCall'); + +/** + * Flattens a nested array. If `isDeep` is `true` the array is recursively + * flattened, otherwise it's only flattened a single level. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to flatten. + * @param {boolean} [isDeep] Specify a deep flatten. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flatten([1, [2, 3, [4]]]); + * // => [1, 2, 3, [4]] + * + * // using `isDeep` + * _.flatten([1, [2, 3, [4]]], true); + * // => [1, 2, 3, 4] + */ +function flatten(array, isDeep, guard) { + var length = array ? array.length : 0; + if (guard && isIterateeCall(array, isDeep, guard)) { + isDeep = false; + } + return length ? baseFlatten(array, isDeep) : []; +} + +module.exports = flatten; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/flattenDeep.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/flattenDeep.js new file mode 100644 index 0000000..9f775fe --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/flattenDeep.js @@ -0,0 +1,21 @@ +var baseFlatten = require('../internal/baseFlatten'); + +/** + * Recursively flattens a nested array. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to recursively flatten. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flattenDeep([1, [2, 3, [4]]]); + * // => [1, 2, 3, 4] + */ +function flattenDeep(array) { + var length = array ? array.length : 0; + return length ? baseFlatten(array, true) : []; +} + +module.exports = flattenDeep; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/head.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/head.js new file mode 100644 index 0000000..1961b08 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/head.js @@ -0,0 +1 @@ +module.exports = require('./first'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/indexOf.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/indexOf.js new file mode 100644 index 0000000..4cfc682 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/indexOf.js @@ -0,0 +1,53 @@ +var baseIndexOf = require('../internal/baseIndexOf'), + binaryIndex = require('../internal/binaryIndex'); + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeMax = Math.max; + +/** + * Gets the index at which the first occurrence of `value` is found in `array` + * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. If `fromIndex` is negative, it's used as the offset + * from the end of `array`. If `array` is sorted providing `true` for `fromIndex` + * performs a faster binary search. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {*} value The value to search for. + * @param {boolean|number} [fromIndex=0] The index to search from or `true` + * to perform a binary search on a sorted array. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.indexOf([1, 2, 1, 2], 2); + * // => 1 + * + * // using `fromIndex` + * _.indexOf([1, 2, 1, 2], 2, 2); + * // => 3 + * + * // performing a binary search + * _.indexOf([1, 1, 2, 2], 2, true); + * // => 2 + */ +function indexOf(array, value, fromIndex) { + var length = array ? array.length : 0; + if (!length) { + return -1; + } + if (typeof fromIndex == 'number') { + fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex; + } else if (fromIndex) { + var index = binaryIndex(array, value); + if (index < length && + (value === value ? (value === array[index]) : (array[index] !== array[index]))) { + return index; + } + return -1; + } + return baseIndexOf(array, value, fromIndex || 0); +} + +module.exports = indexOf; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/initial.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/initial.js new file mode 100644 index 0000000..59b7a7d --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/initial.js @@ -0,0 +1,20 @@ +var dropRight = require('./dropRight'); + +/** + * Gets all but the last element of `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.initial([1, 2, 3]); + * // => [1, 2] + */ +function initial(array) { + return dropRight(array, 1); +} + +module.exports = initial; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/intersection.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/intersection.js new file mode 100644 index 0000000..f218432 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/intersection.js @@ -0,0 +1,58 @@ +var baseIndexOf = require('../internal/baseIndexOf'), + cacheIndexOf = require('../internal/cacheIndexOf'), + createCache = require('../internal/createCache'), + isArrayLike = require('../internal/isArrayLike'), + restParam = require('../function/restParam'); + +/** + * Creates an array of unique values that are included in all of the provided + * arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of shared values. + * @example + * _.intersection([1, 2], [4, 2], [2, 1]); + * // => [2] + */ +var intersection = restParam(function(arrays) { + var othLength = arrays.length, + othIndex = othLength, + caches = Array(length), + indexOf = baseIndexOf, + isCommon = true, + result = []; + + while (othIndex--) { + var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : []; + caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null; + } + var array = arrays[0], + index = -1, + length = array ? array.length : 0, + seen = caches[0]; + + outer: + while (++index < length) { + value = array[index]; + if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) { + var othIndex = othLength; + while (--othIndex) { + var cache = caches[othIndex]; + if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) { + continue outer; + } + } + if (seen) { + seen.push(value); + } + result.push(value); + } + } + return result; +}); + +module.exports = intersection; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/last.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/last.js new file mode 100644 index 0000000..299af31 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/last.js @@ -0,0 +1,19 @@ +/** + * Gets the last element of `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the last element of `array`. + * @example + * + * _.last([1, 2, 3]); + * // => 3 + */ +function last(array) { + var length = array ? array.length : 0; + return length ? array[length - 1] : undefined; +} + +module.exports = last; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/lastIndexOf.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/lastIndexOf.js new file mode 100644 index 0000000..02b8062 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/lastIndexOf.js @@ -0,0 +1,60 @@ +var binaryIndex = require('../internal/binaryIndex'), + indexOfNaN = require('../internal/indexOfNaN'); + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeMax = Math.max, + nativeMin = Math.min; + +/** + * This method is like `_.indexOf` except that it iterates over elements of + * `array` from right to left. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {*} value The value to search for. + * @param {boolean|number} [fromIndex=array.length-1] The index to search from + * or `true` to perform a binary search on a sorted array. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.lastIndexOf([1, 2, 1, 2], 2); + * // => 3 + * + * // using `fromIndex` + * _.lastIndexOf([1, 2, 1, 2], 2, 2); + * // => 1 + * + * // performing a binary search + * _.lastIndexOf([1, 1, 2, 2], 2, true); + * // => 3 + */ +function lastIndexOf(array, value, fromIndex) { + var length = array ? array.length : 0; + if (!length) { + return -1; + } + var index = length; + if (typeof fromIndex == 'number') { + index = (fromIndex < 0 ? nativeMax(length + fromIndex, 0) : nativeMin(fromIndex || 0, length - 1)) + 1; + } else if (fromIndex) { + index = binaryIndex(array, value, true) - 1; + var other = array[index]; + if (value === value ? (value === other) : (other !== other)) { + return index; + } + return -1; + } + if (value !== value) { + return indexOfNaN(array, index, true); + } + while (index--) { + if (array[index] === value) { + return index; + } + } + return -1; +} + +module.exports = lastIndexOf; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/object.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/object.js new file mode 100644 index 0000000..f4a3453 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/object.js @@ -0,0 +1 @@ +module.exports = require('./zipObject'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/pull.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/pull.js new file mode 100644 index 0000000..7bcbb4a --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/pull.js @@ -0,0 +1,52 @@ +var baseIndexOf = require('../internal/baseIndexOf'); + +/** Used for native method references. */ +var arrayProto = Array.prototype; + +/** Native method references. */ +var splice = arrayProto.splice; + +/** + * Removes all provided values from `array` using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `_.without`, this method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to modify. + * @param {...*} [values] The values to remove. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3, 1, 2, 3]; + * + * _.pull(array, 2, 3); + * console.log(array); + * // => [1, 1] + */ +function pull() { + var args = arguments, + array = args[0]; + + if (!(array && array.length)) { + return array; + } + var index = 0, + indexOf = baseIndexOf, + length = args.length; + + while (++index < length) { + var fromIndex = 0, + value = args[index]; + + while ((fromIndex = indexOf(array, value, fromIndex)) > -1) { + splice.call(array, fromIndex, 1); + } + } + return array; +} + +module.exports = pull; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/pullAt.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/pullAt.js new file mode 100644 index 0000000..4ca2476 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/pullAt.js @@ -0,0 +1,40 @@ +var baseAt = require('../internal/baseAt'), + baseCompareAscending = require('../internal/baseCompareAscending'), + baseFlatten = require('../internal/baseFlatten'), + basePullAt = require('../internal/basePullAt'), + restParam = require('../function/restParam'); + +/** + * Removes elements from `array` corresponding to the given indexes and returns + * an array of the removed elements. Indexes may be specified as an array of + * indexes or as individual arguments. + * + * **Note:** Unlike `_.at`, this method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to modify. + * @param {...(number|number[])} [indexes] The indexes of elements to remove, + * specified as individual indexes or arrays of indexes. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = [5, 10, 15, 20]; + * var evens = _.pullAt(array, 1, 3); + * + * console.log(array); + * // => [5, 15] + * + * console.log(evens); + * // => [10, 20] + */ +var pullAt = restParam(function(array, indexes) { + indexes = baseFlatten(indexes); + + var result = baseAt(array, indexes); + basePullAt(array, indexes.sort(baseCompareAscending)); + return result; +}); + +module.exports = pullAt; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/remove.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/remove.js new file mode 100644 index 0000000..0cf979b --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/remove.js @@ -0,0 +1,64 @@ +var baseCallback = require('../internal/baseCallback'), + basePullAt = require('../internal/basePullAt'); + +/** + * Removes all elements from `array` that `predicate` returns truthy for + * and returns an array of the removed elements. The predicate is bound to + * `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * **Note:** Unlike `_.filter`, this method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to modify. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = [1, 2, 3, 4]; + * var evens = _.remove(array, function(n) { + * return n % 2 == 0; + * }); + * + * console.log(array); + * // => [1, 3] + * + * console.log(evens); + * // => [2, 4] + */ +function remove(array, predicate, thisArg) { + var result = []; + if (!(array && array.length)) { + return result; + } + var index = -1, + indexes = [], + length = array.length; + + predicate = baseCallback(predicate, thisArg, 3); + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result.push(value); + indexes.push(index); + } + } + basePullAt(array, indexes); + return result; +} + +module.exports = remove; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/rest.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/rest.js new file mode 100644 index 0000000..9bfb734 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/rest.js @@ -0,0 +1,21 @@ +var drop = require('./drop'); + +/** + * Gets all but the first element of `array`. + * + * @static + * @memberOf _ + * @alias tail + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.rest([1, 2, 3]); + * // => [2, 3] + */ +function rest(array) { + return drop(array, 1); +} + +module.exports = rest; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/slice.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/slice.js new file mode 100644 index 0000000..48ef1a1 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/slice.js @@ -0,0 +1,30 @@ +var baseSlice = require('../internal/baseSlice'), + isIterateeCall = require('../internal/isIterateeCall'); + +/** + * Creates a slice of `array` from `start` up to, but not including, `end`. + * + * **Note:** This method is used instead of `Array#slice` to support node + * lists in IE < 9 and to ensure dense arrays are returned. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */ +function slice(array, start, end) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { + start = 0; + end = length; + } + return baseSlice(array, start, end); +} + +module.exports = slice; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/sortedIndex.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/sortedIndex.js new file mode 100644 index 0000000..6903bca --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/sortedIndex.js @@ -0,0 +1,53 @@ +var createSortedIndex = require('../internal/createSortedIndex'); + +/** + * Uses a binary search to determine the lowest index at which `value` should + * be inserted into `array` in order to maintain its sort order. If an iteratee + * function is provided it's invoked for `value` and each element of `array` + * to compute their sort ranking. The iteratee is bound to `thisArg` and + * invoked with one argument; (value). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedIndex([30, 50], 40); + * // => 1 + * + * _.sortedIndex([4, 4, 5, 5], 5); + * // => 2 + * + * var dict = { 'data': { 'thirty': 30, 'forty': 40, 'fifty': 50 } }; + * + * // using an iteratee function + * _.sortedIndex(['thirty', 'fifty'], 'forty', function(word) { + * return this.data[word]; + * }, dict); + * // => 1 + * + * // using the `_.property` callback shorthand + * _.sortedIndex([{ 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x'); + * // => 1 + */ +var sortedIndex = createSortedIndex(); + +module.exports = sortedIndex; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/sortedLastIndex.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/sortedLastIndex.js new file mode 100644 index 0000000..81a4a86 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/sortedLastIndex.js @@ -0,0 +1,25 @@ +var createSortedIndex = require('../internal/createSortedIndex'); + +/** + * This method is like `_.sortedIndex` except that it returns the highest + * index at which `value` should be inserted into `array` in order to + * maintain its sort order. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedLastIndex([4, 4, 5, 5], 5); + * // => 4 + */ +var sortedLastIndex = createSortedIndex(true); + +module.exports = sortedLastIndex; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/tail.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/tail.js new file mode 100644 index 0000000..c5dfe77 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/tail.js @@ -0,0 +1 @@ +module.exports = require('./rest'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/take.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/take.js new file mode 100644 index 0000000..875917a --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/take.js @@ -0,0 +1,39 @@ +var baseSlice = require('../internal/baseSlice'), + isIterateeCall = require('../internal/isIterateeCall'); + +/** + * Creates a slice of `array` with `n` elements taken from the beginning. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.take([1, 2, 3]); + * // => [1] + * + * _.take([1, 2, 3], 2); + * // => [1, 2] + * + * _.take([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.take([1, 2, 3], 0); + * // => [] + */ +function take(array, n, guard) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (guard ? isIterateeCall(array, n, guard) : n == null) { + n = 1; + } + return baseSlice(array, 0, n < 0 ? 0 : n); +} + +module.exports = take; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/takeRight.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/takeRight.js new file mode 100644 index 0000000..6e89c87 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/takeRight.js @@ -0,0 +1,40 @@ +var baseSlice = require('../internal/baseSlice'), + isIterateeCall = require('../internal/isIterateeCall'); + +/** + * Creates a slice of `array` with `n` elements taken from the end. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeRight([1, 2, 3]); + * // => [3] + * + * _.takeRight([1, 2, 3], 2); + * // => [2, 3] + * + * _.takeRight([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.takeRight([1, 2, 3], 0); + * // => [] + */ +function takeRight(array, n, guard) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (guard ? isIterateeCall(array, n, guard) : n == null) { + n = 1; + } + n = length - (+n || 0); + return baseSlice(array, n < 0 ? 0 : n); +} + +module.exports = takeRight; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/takeRightWhile.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/takeRightWhile.js new file mode 100644 index 0000000..5464d13 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/takeRightWhile.js @@ -0,0 +1,59 @@ +var baseCallback = require('../internal/baseCallback'), + baseWhile = require('../internal/baseWhile'); + +/** + * Creates a slice of `array` with elements taken from the end. Elements are + * taken until `predicate` returns falsey. The predicate is bound to `thisArg` + * and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeRightWhile([1, 2, 3], function(n) { + * return n > 1; + * }); + * // => [2, 3] + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.takeRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user'); + * // => ['pebbles'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.takeRightWhile(users, 'active', false), 'user'); + * // => ['fred', 'pebbles'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.takeRightWhile(users, 'active'), 'user'); + * // => [] + */ +function takeRightWhile(array, predicate, thisArg) { + return (array && array.length) + ? baseWhile(array, baseCallback(predicate, thisArg, 3), false, true) + : []; +} + +module.exports = takeRightWhile; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/takeWhile.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/takeWhile.js new file mode 100644 index 0000000..f7e28a1 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/takeWhile.js @@ -0,0 +1,59 @@ +var baseCallback = require('../internal/baseCallback'), + baseWhile = require('../internal/baseWhile'); + +/** + * Creates a slice of `array` with elements taken from the beginning. Elements + * are taken until `predicate` returns falsey. The predicate is bound to + * `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeWhile([1, 2, 3], function(n) { + * return n < 3; + * }); + * // => [1, 2] + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false}, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.takeWhile(users, { 'user': 'barney', 'active': false }), 'user'); + * // => ['barney'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.takeWhile(users, 'active', false), 'user'); + * // => ['barney', 'fred'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.takeWhile(users, 'active'), 'user'); + * // => [] + */ +function takeWhile(array, predicate, thisArg) { + return (array && array.length) + ? baseWhile(array, baseCallback(predicate, thisArg, 3)) + : []; +} + +module.exports = takeWhile; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/union.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/union.js new file mode 100644 index 0000000..53cefe4 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/union.js @@ -0,0 +1,24 @@ +var baseFlatten = require('../internal/baseFlatten'), + baseUniq = require('../internal/baseUniq'), + restParam = require('../function/restParam'); + +/** + * Creates an array of unique values, in order, from all of the provided arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of combined values. + * @example + * + * _.union([1, 2], [4, 2], [2, 1]); + * // => [1, 2, 4] + */ +var union = restParam(function(arrays) { + return baseUniq(baseFlatten(arrays, false, true)); +}); + +module.exports = union; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/uniq.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/uniq.js new file mode 100644 index 0000000..ae937ef --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/uniq.js @@ -0,0 +1,71 @@ +var baseCallback = require('../internal/baseCallback'), + baseUniq = require('../internal/baseUniq'), + isIterateeCall = require('../internal/isIterateeCall'), + sortedUniq = require('../internal/sortedUniq'); + +/** + * Creates a duplicate-free version of an array, using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons, in which only the first occurence of each element + * is kept. Providing `true` for `isSorted` performs a faster search algorithm + * for sorted arrays. If an iteratee function is provided it's invoked for + * each element in the array to generate the criterion by which uniqueness + * is computed. The `iteratee` is bound to `thisArg` and invoked with three + * arguments: (value, index, array). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias unique + * @category Array + * @param {Array} array The array to inspect. + * @param {boolean} [isSorted] Specify the array is sorted. + * @param {Function|Object|string} [iteratee] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new duplicate-value-free array. + * @example + * + * _.uniq([2, 1, 2]); + * // => [2, 1] + * + * // using `isSorted` + * _.uniq([1, 1, 2], true); + * // => [1, 2] + * + * // using an iteratee function + * _.uniq([1, 2.5, 1.5, 2], function(n) { + * return this.floor(n); + * }, Math); + * // => [1, 2.5] + * + * // using the `_.property` callback shorthand + * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] + */ +function uniq(array, isSorted, iteratee, thisArg) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (isSorted != null && typeof isSorted != 'boolean') { + thisArg = iteratee; + iteratee = isIterateeCall(array, isSorted, thisArg) ? undefined : isSorted; + isSorted = false; + } + iteratee = iteratee == null ? iteratee : baseCallback(iteratee, thisArg, 3); + return (isSorted) + ? sortedUniq(array, iteratee) + : baseUniq(array, iteratee); +} + +module.exports = uniq; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/unique.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/unique.js new file mode 100644 index 0000000..396de1b --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/unique.js @@ -0,0 +1 @@ +module.exports = require('./uniq'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/unzip.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/unzip.js new file mode 100644 index 0000000..0a539fa --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/unzip.js @@ -0,0 +1,47 @@ +var arrayFilter = require('../internal/arrayFilter'), + arrayMap = require('../internal/arrayMap'), + baseProperty = require('../internal/baseProperty'), + isArrayLike = require('../internal/isArrayLike'); + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeMax = Math.max; + +/** + * This method is like `_.zip` except that it accepts an array of grouped + * elements and creates an array regrouping the elements to their pre-zip + * configuration. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array of grouped elements to process. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]); + * // => [['fred', 30, true], ['barney', 40, false]] + * + * _.unzip(zipped); + * // => [['fred', 'barney'], [30, 40], [true, false]] + */ +function unzip(array) { + if (!(array && array.length)) { + return []; + } + var index = -1, + length = 0; + + array = arrayFilter(array, function(group) { + if (isArrayLike(group)) { + length = nativeMax(group.length, length); + return true; + } + }); + var result = Array(length); + while (++index < length) { + result[index] = arrayMap(array, baseProperty(index)); + } + return result; +} + +module.exports = unzip; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/unzipWith.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/unzipWith.js new file mode 100644 index 0000000..324a2b1 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/unzipWith.js @@ -0,0 +1,41 @@ +var arrayMap = require('../internal/arrayMap'), + arrayReduce = require('../internal/arrayReduce'), + bindCallback = require('../internal/bindCallback'), + unzip = require('./unzip'); + +/** + * This method is like `_.unzip` except that it accepts an iteratee to specify + * how regrouped values should be combined. The `iteratee` is bound to `thisArg` + * and invoked with four arguments: (accumulator, value, index, group). + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array of grouped elements to process. + * @param {Function} [iteratee] The function to combine regrouped values. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip([1, 2], [10, 20], [100, 200]); + * // => [[1, 10, 100], [2, 20, 200]] + * + * _.unzipWith(zipped, _.add); + * // => [3, 30, 300] + */ +function unzipWith(array, iteratee, thisArg) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + var result = unzip(array); + if (iteratee == null) { + return result; + } + iteratee = bindCallback(iteratee, thisArg, 4); + return arrayMap(result, function(group) { + return arrayReduce(group, iteratee, undefined, true); + }); +} + +module.exports = unzipWith; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/without.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/without.js new file mode 100644 index 0000000..2ac3d11 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/without.js @@ -0,0 +1,27 @@ +var baseDifference = require('../internal/baseDifference'), + isArrayLike = require('../internal/isArrayLike'), + restParam = require('../function/restParam'); + +/** + * Creates an array excluding all provided values using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to filter. + * @param {...*} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.without([1, 2, 1, 3], 1, 2); + * // => [3] + */ +var without = restParam(function(array, values) { + return isArrayLike(array) + ? baseDifference(array, values) + : []; +}); + +module.exports = without; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/xor.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/xor.js new file mode 100644 index 0000000..04ef32a --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/xor.js @@ -0,0 +1,35 @@ +var arrayPush = require('../internal/arrayPush'), + baseDifference = require('../internal/baseDifference'), + baseUniq = require('../internal/baseUniq'), + isArrayLike = require('../internal/isArrayLike'); + +/** + * Creates an array of unique values that is the [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) + * of the provided arrays. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of values. + * @example + * + * _.xor([1, 2], [4, 2]); + * // => [1, 4] + */ +function xor() { + var index = -1, + length = arguments.length; + + while (++index < length) { + var array = arguments[index]; + if (isArrayLike(array)) { + var result = result + ? arrayPush(baseDifference(result, array), baseDifference(array, result)) + : array; + } + } + return result ? baseUniq(result) : []; +} + +module.exports = xor; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/zip.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/zip.js new file mode 100644 index 0000000..53a6f69 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/zip.js @@ -0,0 +1,21 @@ +var restParam = require('../function/restParam'), + unzip = require('./unzip'); + +/** + * Creates an array of grouped elements, the first of which contains the first + * elements of the given arrays, the second of which contains the second elements + * of the given arrays, and so on. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zip(['fred', 'barney'], [30, 40], [true, false]); + * // => [['fred', 30, true], ['barney', 40, false]] + */ +var zip = restParam(unzip); + +module.exports = zip; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/zipObject.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/zipObject.js new file mode 100644 index 0000000..dec7a21 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/zipObject.js @@ -0,0 +1,43 @@ +var isArray = require('../lang/isArray'); + +/** + * The inverse of `_.pairs`; this method returns an object composed from arrays + * of property names and values. Provide either a single two dimensional array, + * e.g. `[[key1, value1], [key2, value2]]` or two arrays, one of property names + * and one of corresponding values. + * + * @static + * @memberOf _ + * @alias object + * @category Array + * @param {Array} props The property names. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @example + * + * _.zipObject([['fred', 30], ['barney', 40]]); + * // => { 'fred': 30, 'barney': 40 } + * + * _.zipObject(['fred', 'barney'], [30, 40]); + * // => { 'fred': 30, 'barney': 40 } + */ +function zipObject(props, values) { + var index = -1, + length = props ? props.length : 0, + result = {}; + + if (length && !values && !isArray(props[0])) { + values = []; + } + while (++index < length) { + var key = props[index]; + if (values) { + result[key] = values[index]; + } else if (key) { + result[key[0]] = key[1]; + } + } + return result; +} + +module.exports = zipObject; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/zipWith.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/zipWith.js new file mode 100644 index 0000000..ad10374 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/array/zipWith.js @@ -0,0 +1,36 @@ +var restParam = require('../function/restParam'), + unzipWith = require('./unzipWith'); + +/** + * This method is like `_.zip` except that it accepts an iteratee to specify + * how grouped values should be combined. The `iteratee` is bound to `thisArg` + * and invoked with four arguments: (accumulator, value, index, group). + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @param {Function} [iteratee] The function to combine grouped values. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zipWith([1, 2], [10, 20], [100, 200], _.add); + * // => [111, 222] + */ +var zipWith = restParam(function(arrays) { + var length = arrays.length, + iteratee = length > 2 ? arrays[length - 2] : undefined, + thisArg = length > 1 ? arrays[length - 1] : undefined; + + if (length > 2 && typeof iteratee == 'function') { + length -= 2; + } else { + iteratee = (length > 1 && typeof thisArg == 'function') ? (--length, thisArg) : undefined; + thisArg = undefined; + } + arrays.length = length; + return unzipWith(arrays, iteratee, thisArg); +}); + +module.exports = zipWith; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain.js new file mode 100644 index 0000000..6439627 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain.js @@ -0,0 +1,16 @@ +module.exports = { + 'chain': require('./chain/chain'), + 'commit': require('./chain/commit'), + 'concat': require('./chain/concat'), + 'lodash': require('./chain/lodash'), + 'plant': require('./chain/plant'), + 'reverse': require('./chain/reverse'), + 'run': require('./chain/run'), + 'tap': require('./chain/tap'), + 'thru': require('./chain/thru'), + 'toJSON': require('./chain/toJSON'), + 'toString': require('./chain/toString'), + 'value': require('./chain/value'), + 'valueOf': require('./chain/valueOf'), + 'wrapperChain': require('./chain/wrapperChain') +}; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/chain.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/chain.js new file mode 100644 index 0000000..453ba1e --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/chain.js @@ -0,0 +1,35 @@ +var lodash = require('./lodash'); + +/** + * Creates a `lodash` object that wraps `value` with explicit method + * chaining enabled. + * + * @static + * @memberOf _ + * @category Chain + * @param {*} value The value to wrap. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'pebbles', 'age': 1 } + * ]; + * + * var youngest = _.chain(users) + * .sortBy('age') + * .map(function(chr) { + * return chr.user + ' is ' + chr.age; + * }) + * .first() + * .value(); + * // => 'pebbles is 1' + */ +function chain(value) { + var result = lodash(value); + result.__chain__ = true; + return result; +} + +module.exports = chain; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/commit.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/commit.js new file mode 100644 index 0000000..c732d1b --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/commit.js @@ -0,0 +1 @@ +module.exports = require('./wrapperCommit'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/concat.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/concat.js new file mode 100644 index 0000000..90607d1 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/concat.js @@ -0,0 +1 @@ +module.exports = require('./wrapperConcat'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/lodash.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/lodash.js new file mode 100644 index 0000000..1c3467e --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/lodash.js @@ -0,0 +1,125 @@ +var LazyWrapper = require('../internal/LazyWrapper'), + LodashWrapper = require('../internal/LodashWrapper'), + baseLodash = require('../internal/baseLodash'), + isArray = require('../lang/isArray'), + isObjectLike = require('../internal/isObjectLike'), + wrapperClone = require('../internal/wrapperClone'); + +/** Used for native method references. */ +var objectProto = Object.prototype; + +/** Used to check objects for own properties. */ +var hasOwnProperty = objectProto.hasOwnProperty; + +/** + * Creates a `lodash` object which wraps `value` to enable implicit chaining. + * Methods that operate on and return arrays, collections, and functions can + * be chained together. Methods that retrieve a single value or may return a + * primitive value will automatically end the chain returning the unwrapped + * value. Explicit chaining may be enabled using `_.chain`. The execution of + * chained methods is lazy, that is, execution is deferred until `_#value` + * is implicitly or explicitly called. + * + * Lazy evaluation allows several methods to support shortcut fusion. Shortcut + * fusion is an optimization strategy which merge iteratee calls; this can help + * to avoid the creation of intermediate data structures and greatly reduce the + * number of iteratee executions. + * + * Chaining is supported in custom builds as long as the `_#value` method is + * directly or indirectly included in the build. + * + * In addition to lodash methods, wrappers have `Array` and `String` methods. + * + * The wrapper `Array` methods are: + * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, + * `splice`, and `unshift` + * + * The wrapper `String` methods are: + * `replace` and `split` + * + * The wrapper methods that support shortcut fusion are: + * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`, + * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`, + * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`, + * and `where` + * + * The chainable wrapper methods are: + * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`, + * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`, + * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defaultsDeep`, + * `defer`, `delay`, `difference`, `drop`, `dropRight`, `dropRightWhile`, + * `dropWhile`, `fill`, `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, + * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, + * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, + * `invoke`, `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, + * `matchesProperty`, `memoize`, `merge`, `method`, `methodOf`, `mixin`, + * `modArgs`, `negate`, `omit`, `once`, `pairs`, `partial`, `partialRight`, + * `partition`, `pick`, `plant`, `pluck`, `property`, `propertyOf`, `pull`, + * `pullAt`, `push`, `range`, `rearg`, `reject`, `remove`, `rest`, `restParam`, + * `reverse`, `set`, `shuffle`, `slice`, `sort`, `sortBy`, `sortByAll`, + * `sortByOrder`, `splice`, `spread`, `take`, `takeRight`, `takeRightWhile`, + * `takeWhile`, `tap`, `throttle`, `thru`, `times`, `toArray`, `toPlainObject`, + * `transform`, `union`, `uniq`, `unshift`, `unzip`, `unzipWith`, `values`, + * `valuesIn`, `where`, `without`, `wrap`, `xor`, `zip`, `zipObject`, `zipWith` + * + * The wrapper methods that are **not** chainable by default are: + * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clone`, `cloneDeep`, + * `deburr`, `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, + * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, + * `floor`, `get`, `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`, + * `inRange`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, + * `isEmpty`, `isEqual`, `isError`, `isFinite` `isFunction`, `isMatch`, + * `isNative`, `isNaN`, `isNull`, `isNumber`, `isObject`, `isPlainObject`, + * `isRegExp`, `isString`, `isUndefined`, `isTypedArray`, `join`, `kebabCase`, + * `last`, `lastIndexOf`, `lt`, `lte`, `max`, `min`, `noConflict`, `noop`, + * `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`, `random`, `reduce`, + * `reduceRight`, `repeat`, `result`, `round`, `runInContext`, `shift`, `size`, + * `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`, `startCase`, + * `startsWith`, `sum`, `template`, `trim`, `trimLeft`, `trimRight`, `trunc`, + * `unescape`, `uniqueId`, `value`, and `words` + * + * The wrapper method `sample` will return a wrapped value when `n` is provided, + * otherwise an unwrapped value is returned. + * + * @name _ + * @constructor + * @category Chain + * @param {*} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var wrapped = _([1, 2, 3]); + * + * // returns an unwrapped value + * wrapped.reduce(function(total, n) { + * return total + n; + * }); + * // => 6 + * + * // returns a wrapped value + * var squares = wrapped.map(function(n) { + * return n * n; + * }); + * + * _.isArray(squares); + * // => false + * + * _.isArray(squares.value()); + * // => true + */ +function lodash(value) { + if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { + if (value instanceof LodashWrapper) { + return value; + } + if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) { + return wrapperClone(value); + } + } + return new LodashWrapper(value); +} + +// Ensure wrappers are instances of `baseLodash`. +lodash.prototype = baseLodash.prototype; + +module.exports = lodash; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/plant.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/plant.js new file mode 100644 index 0000000..04099f2 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/plant.js @@ -0,0 +1 @@ +module.exports = require('./wrapperPlant'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/reverse.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/reverse.js new file mode 100644 index 0000000..f72a64a --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/reverse.js @@ -0,0 +1 @@ +module.exports = require('./wrapperReverse'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/run.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/run.js new file mode 100644 index 0000000..5e751a2 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/run.js @@ -0,0 +1 @@ +module.exports = require('./wrapperValue'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/tap.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/tap.js new file mode 100644 index 0000000..3d0257e --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/tap.js @@ -0,0 +1,29 @@ +/** + * This method invokes `interceptor` and returns `value`. The interceptor is + * bound to `thisArg` and invoked with one argument; (value). The purpose of + * this method is to "tap into" a method chain in order to perform operations + * on intermediate results within the chain. + * + * @static + * @memberOf _ + * @category Chain + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @param {*} [thisArg] The `this` binding of `interceptor`. + * @returns {*} Returns `value`. + * @example + * + * _([1, 2, 3]) + * .tap(function(array) { + * array.pop(); + * }) + * .reverse() + * .value(); + * // => [2, 1] + */ +function tap(value, interceptor, thisArg) { + interceptor.call(thisArg, value); + return value; +} + +module.exports = tap; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/thru.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/thru.js new file mode 100644 index 0000000..a715780 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/thru.js @@ -0,0 +1,26 @@ +/** + * This method is like `_.tap` except that it returns the result of `interceptor`. + * + * @static + * @memberOf _ + * @category Chain + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @param {*} [thisArg] The `this` binding of `interceptor`. + * @returns {*} Returns the result of `interceptor`. + * @example + * + * _(' abc ') + * .chain() + * .trim() + * .thru(function(value) { + * return [value]; + * }) + * .value(); + * // => ['abc'] + */ +function thru(value, interceptor, thisArg) { + return interceptor.call(thisArg, value); +} + +module.exports = thru; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/toJSON.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/toJSON.js new file mode 100644 index 0000000..5e751a2 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/toJSON.js @@ -0,0 +1 @@ +module.exports = require('./wrapperValue'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/toString.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/toString.js new file mode 100644 index 0000000..c7bcbf9 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/toString.js @@ -0,0 +1 @@ +module.exports = require('./wrapperToString'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/value.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/value.js new file mode 100644 index 0000000..5e751a2 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/value.js @@ -0,0 +1 @@ +module.exports = require('./wrapperValue'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/valueOf.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/valueOf.js new file mode 100644 index 0000000..5e751a2 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/valueOf.js @@ -0,0 +1 @@ +module.exports = require('./wrapperValue'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperChain.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperChain.js new file mode 100644 index 0000000..3823481 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperChain.js @@ -0,0 +1,32 @@ +var chain = require('./chain'); + +/** + * Enables explicit method chaining on the wrapper object. + * + * @name chain + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * // without explicit chaining + * _(users).first(); + * // => { 'user': 'barney', 'age': 36 } + * + * // with explicit chaining + * _(users).chain() + * .first() + * .pick('user') + * .value(); + * // => { 'user': 'barney' } + */ +function wrapperChain() { + return chain(this); +} + +module.exports = wrapperChain; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperCommit.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperCommit.js new file mode 100644 index 0000000..c3d2898 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperCommit.js @@ -0,0 +1,32 @@ +var LodashWrapper = require('../internal/LodashWrapper'); + +/** + * Executes the chained sequence and returns the wrapped result. + * + * @name commit + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2]; + * var wrapped = _(array).push(3); + * + * console.log(array); + * // => [1, 2] + * + * wrapped = wrapped.commit(); + * console.log(array); + * // => [1, 2, 3] + * + * wrapped.last(); + * // => 3 + * + * console.log(array); + * // => [1, 2, 3] + */ +function wrapperCommit() { + return new LodashWrapper(this.value(), this.__chain__); +} + +module.exports = wrapperCommit; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperConcat.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperConcat.js new file mode 100644 index 0000000..799156c --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperConcat.js @@ -0,0 +1,34 @@ +var arrayConcat = require('../internal/arrayConcat'), + baseFlatten = require('../internal/baseFlatten'), + isArray = require('../lang/isArray'), + restParam = require('../function/restParam'), + toObject = require('../internal/toObject'); + +/** + * Creates a new array joining a wrapped array with any additional arrays + * and/or values. + * + * @name concat + * @memberOf _ + * @category Chain + * @param {...*} [values] The values to concatenate. + * @returns {Array} Returns the new concatenated array. + * @example + * + * var array = [1]; + * var wrapped = _(array).concat(2, [3], [[4]]); + * + * console.log(wrapped.value()); + * // => [1, 2, 3, [4]] + * + * console.log(array); + * // => [1] + */ +var wrapperConcat = restParam(function(values) { + values = baseFlatten(values); + return this.thru(function(array) { + return arrayConcat(isArray(array) ? array : [toObject(array)], values); + }); +}); + +module.exports = wrapperConcat; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperPlant.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperPlant.js new file mode 100644 index 0000000..234fe41 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperPlant.js @@ -0,0 +1,45 @@ +var baseLodash = require('../internal/baseLodash'), + wrapperClone = require('../internal/wrapperClone'); + +/** + * Creates a clone of the chained sequence planting `value` as the wrapped value. + * + * @name plant + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2]; + * var wrapped = _(array).map(function(value) { + * return Math.pow(value, 2); + * }); + * + * var other = [3, 4]; + * var otherWrapped = wrapped.plant(other); + * + * otherWrapped.value(); + * // => [9, 16] + * + * wrapped.value(); + * // => [1, 4] + */ +function wrapperPlant(value) { + var result, + parent = this; + + while (parent instanceof baseLodash) { + var clone = wrapperClone(parent); + if (result) { + previous.__wrapped__ = clone; + } else { + result = clone; + } + var previous = clone; + parent = parent.__wrapped__; + } + previous.__wrapped__ = value; + return result; +} + +module.exports = wrapperPlant; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperReverse.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperReverse.js new file mode 100644 index 0000000..6ba546d --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperReverse.js @@ -0,0 +1,43 @@ +var LazyWrapper = require('../internal/LazyWrapper'), + LodashWrapper = require('../internal/LodashWrapper'), + thru = require('./thru'); + +/** + * Reverses the wrapped array so the first element becomes the last, the + * second element becomes the second to last, and so on. + * + * **Note:** This method mutates the wrapped array. + * + * @name reverse + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new reversed `lodash` wrapper instance. + * @example + * + * var array = [1, 2, 3]; + * + * _(array).reverse().value() + * // => [3, 2, 1] + * + * console.log(array); + * // => [3, 2, 1] + */ +function wrapperReverse() { + var value = this.__wrapped__; + + var interceptor = function(value) { + return value.reverse(); + }; + if (value instanceof LazyWrapper) { + var wrapped = value; + if (this.__actions__.length) { + wrapped = new LazyWrapper(this); + } + wrapped = wrapped.reverse(); + wrapped.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined }); + return new LodashWrapper(wrapped, this.__chain__); + } + return this.thru(interceptor); +} + +module.exports = wrapperReverse; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperToString.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperToString.js new file mode 100644 index 0000000..db975a5 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperToString.js @@ -0,0 +1,17 @@ +/** + * Produces the result of coercing the unwrapped value to a string. + * + * @name toString + * @memberOf _ + * @category Chain + * @returns {string} Returns the coerced string value. + * @example + * + * _([1, 2, 3]).toString(); + * // => '1,2,3' + */ +function wrapperToString() { + return (this.value() + ''); +} + +module.exports = wrapperToString; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperValue.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperValue.js new file mode 100644 index 0000000..2734e41 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/chain/wrapperValue.js @@ -0,0 +1,20 @@ +var baseWrapperValue = require('../internal/baseWrapperValue'); + +/** + * Executes the chained sequence to extract the unwrapped value. + * + * @name value + * @memberOf _ + * @alias run, toJSON, valueOf + * @category Chain + * @returns {*} Returns the resolved unwrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ +function wrapperValue() { + return baseWrapperValue(this.__wrapped__, this.__actions__); +} + +module.exports = wrapperValue; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection.js new file mode 100644 index 0000000..0338857 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection.js @@ -0,0 +1,44 @@ +module.exports = { + 'all': require('./collection/all'), + 'any': require('./collection/any'), + 'at': require('./collection/at'), + 'collect': require('./collection/collect'), + 'contains': require('./collection/contains'), + 'countBy': require('./collection/countBy'), + 'detect': require('./collection/detect'), + 'each': require('./collection/each'), + 'eachRight': require('./collection/eachRight'), + 'every': require('./collection/every'), + 'filter': require('./collection/filter'), + 'find': require('./collection/find'), + 'findLast': require('./collection/findLast'), + 'findWhere': require('./collection/findWhere'), + 'foldl': require('./collection/foldl'), + 'foldr': require('./collection/foldr'), + 'forEach': require('./collection/forEach'), + 'forEachRight': require('./collection/forEachRight'), + 'groupBy': require('./collection/groupBy'), + 'include': require('./collection/include'), + 'includes': require('./collection/includes'), + 'indexBy': require('./collection/indexBy'), + 'inject': require('./collection/inject'), + 'invoke': require('./collection/invoke'), + 'map': require('./collection/map'), + 'max': require('./math/max'), + 'min': require('./math/min'), + 'partition': require('./collection/partition'), + 'pluck': require('./collection/pluck'), + 'reduce': require('./collection/reduce'), + 'reduceRight': require('./collection/reduceRight'), + 'reject': require('./collection/reject'), + 'sample': require('./collection/sample'), + 'select': require('./collection/select'), + 'shuffle': require('./collection/shuffle'), + 'size': require('./collection/size'), + 'some': require('./collection/some'), + 'sortBy': require('./collection/sortBy'), + 'sortByAll': require('./collection/sortByAll'), + 'sortByOrder': require('./collection/sortByOrder'), + 'sum': require('./math/sum'), + 'where': require('./collection/where') +}; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/all.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/all.js new file mode 100644 index 0000000..d0839f7 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/all.js @@ -0,0 +1 @@ +module.exports = require('./every'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/any.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/any.js new file mode 100644 index 0000000..900ac25 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/any.js @@ -0,0 +1 @@ +module.exports = require('./some'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/at.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/at.js new file mode 100644 index 0000000..29236e5 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/at.js @@ -0,0 +1,29 @@ +var baseAt = require('../internal/baseAt'), + baseFlatten = require('../internal/baseFlatten'), + restParam = require('../function/restParam'); + +/** + * Creates an array of elements corresponding to the given keys, or indexes, + * of `collection`. Keys may be specified as individual arguments or as arrays + * of keys. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {...(number|number[]|string|string[])} [props] The property names + * or indexes of elements to pick, specified individually or in arrays. + * @returns {Array} Returns the new array of picked elements. + * @example + * + * _.at(['a', 'b', 'c'], [0, 2]); + * // => ['a', 'c'] + * + * _.at(['barney', 'fred', 'pebbles'], 0, 2); + * // => ['barney', 'pebbles'] + */ +var at = restParam(function(collection, props) { + return baseAt(collection, baseFlatten(props)); +}); + +module.exports = at; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/collect.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/collect.js new file mode 100644 index 0000000..0d1e1ab --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/collect.js @@ -0,0 +1 @@ +module.exports = require('./map'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/contains.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/contains.js new file mode 100644 index 0000000..594722a --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/contains.js @@ -0,0 +1 @@ +module.exports = require('./includes'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/countBy.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/countBy.js new file mode 100644 index 0000000..e97dbb7 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/countBy.js @@ -0,0 +1,54 @@ +var createAggregator = require('../internal/createAggregator'); + +/** Used for native method references. */ +var objectProto = Object.prototype; + +/** Used to check objects for own properties. */ +var hasOwnProperty = objectProto.hasOwnProperty; + +/** + * Creates an object composed of keys generated from the results of running + * each element of `collection` through `iteratee`. The corresponding value + * of each key is the number of times the key was returned by `iteratee`. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.countBy([4.3, 6.1, 6.4], function(n) { + * return Math.floor(n); + * }); + * // => { '4': 1, '6': 2 } + * + * _.countBy([4.3, 6.1, 6.4], function(n) { + * return this.floor(n); + * }, Math); + * // => { '4': 1, '6': 2 } + * + * _.countBy(['one', 'two', 'three'], 'length'); + * // => { '3': 2, '5': 1 } + */ +var countBy = createAggregator(function(result, value, key) { + hasOwnProperty.call(result, key) ? ++result[key] : (result[key] = 1); +}); + +module.exports = countBy; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/detect.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/detect.js new file mode 100644 index 0000000..2fb6303 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/detect.js @@ -0,0 +1 @@ +module.exports = require('./find'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/each.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/each.js new file mode 100644 index 0000000..8800f42 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/each.js @@ -0,0 +1 @@ +module.exports = require('./forEach'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/eachRight.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/eachRight.js new file mode 100644 index 0000000..3252b2a --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/eachRight.js @@ -0,0 +1 @@ +module.exports = require('./forEachRight'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/every.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/every.js new file mode 100644 index 0000000..5a2d0f5 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/every.js @@ -0,0 +1,66 @@ +var arrayEvery = require('../internal/arrayEvery'), + baseCallback = require('../internal/baseCallback'), + baseEvery = require('../internal/baseEvery'), + isArray = require('../lang/isArray'), + isIterateeCall = require('../internal/isIterateeCall'); + +/** + * Checks if `predicate` returns truthy for **all** elements of `collection`. + * The predicate is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias all + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.every(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // using the `_.matchesProperty` callback shorthand + * _.every(users, 'active', false); + * // => true + * + * // using the `_.property` callback shorthand + * _.every(users, 'active'); + * // => false + */ +function every(collection, predicate, thisArg) { + var func = isArray(collection) ? arrayEvery : baseEvery; + if (thisArg && isIterateeCall(collection, predicate, thisArg)) { + predicate = undefined; + } + if (typeof predicate != 'function' || thisArg !== undefined) { + predicate = baseCallback(predicate, thisArg, 3); + } + return func(collection, predicate); +} + +module.exports = every; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/filter.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/filter.js new file mode 100644 index 0000000..7620aa7 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/filter.js @@ -0,0 +1,61 @@ +var arrayFilter = require('../internal/arrayFilter'), + baseCallback = require('../internal/baseCallback'), + baseFilter = require('../internal/baseFilter'), + isArray = require('../lang/isArray'); + +/** + * Iterates over elements of `collection`, returning an array of all elements + * `predicate` returns truthy for. The predicate is bound to `thisArg` and + * invoked with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias select + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the new filtered array. + * @example + * + * _.filter([4, 5, 6], function(n) { + * return n % 2 == 0; + * }); + * // => [4, 6] + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user'); + * // => ['barney'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.filter(users, 'active', false), 'user'); + * // => ['fred'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.filter(users, 'active'), 'user'); + * // => ['barney'] + */ +function filter(collection, predicate, thisArg) { + var func = isArray(collection) ? arrayFilter : baseFilter; + predicate = baseCallback(predicate, thisArg, 3); + return func(collection, predicate); +} + +module.exports = filter; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/find.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/find.js new file mode 100644 index 0000000..7358cfe --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/find.js @@ -0,0 +1,56 @@ +var baseEach = require('../internal/baseEach'), + createFind = require('../internal/createFind'); + +/** + * Iterates over elements of `collection`, returning the first element + * `predicate` returns truthy for. The predicate is bound to `thisArg` and + * invoked with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias detect + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false }, + * { 'user': 'pebbles', 'age': 1, 'active': true } + * ]; + * + * _.result(_.find(users, function(chr) { + * return chr.age < 40; + * }), 'user'); + * // => 'barney' + * + * // using the `_.matches` callback shorthand + * _.result(_.find(users, { 'age': 1, 'active': true }), 'user'); + * // => 'pebbles' + * + * // using the `_.matchesProperty` callback shorthand + * _.result(_.find(users, 'active', false), 'user'); + * // => 'fred' + * + * // using the `_.property` callback shorthand + * _.result(_.find(users, 'active'), 'user'); + * // => 'barney' + */ +var find = createFind(baseEach); + +module.exports = find; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/findLast.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/findLast.js new file mode 100644 index 0000000..75dbadc --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/findLast.js @@ -0,0 +1,25 @@ +var baseEachRight = require('../internal/baseEachRight'), + createFind = require('../internal/createFind'); + +/** + * This method is like `_.find` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * _.findLast([1, 2, 3, 4], function(n) { + * return n % 2 == 1; + * }); + * // => 3 + */ +var findLast = createFind(baseEachRight, true); + +module.exports = findLast; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/findWhere.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/findWhere.js new file mode 100644 index 0000000..2d62065 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/findWhere.js @@ -0,0 +1,37 @@ +var baseMatches = require('../internal/baseMatches'), + find = require('./find'); + +/** + * Performs a deep comparison between each element in `collection` and the + * source object, returning the first element that has equivalent property + * values. + * + * **Note:** This method supports comparing arrays, booleans, `Date` objects, + * numbers, `Object` objects, regexes, and strings. Objects are compared by + * their own, not inherited, enumerable properties. For comparing a single + * own or inherited property value see `_.matchesProperty`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Object} source The object of property values to match. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * _.result(_.findWhere(users, { 'age': 36, 'active': true }), 'user'); + * // => 'barney' + * + * _.result(_.findWhere(users, { 'age': 40, 'active': false }), 'user'); + * // => 'fred' + */ +function findWhere(collection, source) { + return find(collection, baseMatches(source)); +} + +module.exports = findWhere; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/foldl.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/foldl.js new file mode 100644 index 0000000..26f53cf --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/foldl.js @@ -0,0 +1 @@ +module.exports = require('./reduce'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/foldr.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/foldr.js new file mode 100644 index 0000000..8fb199e --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/foldr.js @@ -0,0 +1 @@ +module.exports = require('./reduceRight'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/forEach.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/forEach.js new file mode 100644 index 0000000..05a8e21 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/forEach.js @@ -0,0 +1,37 @@ +var arrayEach = require('../internal/arrayEach'), + baseEach = require('../internal/baseEach'), + createForEach = require('../internal/createForEach'); + +/** + * Iterates over elements of `collection` invoking `iteratee` for each element. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). Iteratee functions may exit iteration early + * by explicitly returning `false`. + * + * **Note:** As with other "Collections" methods, objects with a "length" property + * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` + * may be used for object iteration. + * + * @static + * @memberOf _ + * @alias each + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array|Object|string} Returns `collection`. + * @example + * + * _([1, 2]).forEach(function(n) { + * console.log(n); + * }).value(); + * // => logs each value from left to right and returns the array + * + * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) { + * console.log(n, key); + * }); + * // => logs each value-key pair and returns the object (iteration order is not guaranteed) + */ +var forEach = createForEach(arrayEach, baseEach); + +module.exports = forEach; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/forEachRight.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/forEachRight.js new file mode 100644 index 0000000..3499711 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/forEachRight.js @@ -0,0 +1,26 @@ +var arrayEachRight = require('../internal/arrayEachRight'), + baseEachRight = require('../internal/baseEachRight'), + createForEach = require('../internal/createForEach'); + +/** + * This method is like `_.forEach` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @alias eachRight + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array|Object|string} Returns `collection`. + * @example + * + * _([1, 2]).forEachRight(function(n) { + * console.log(n); + * }).value(); + * // => logs each value from right to left and returns the array + */ +var forEachRight = createForEach(arrayEachRight, baseEachRight); + +module.exports = forEachRight; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/groupBy.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/groupBy.js new file mode 100644 index 0000000..a925c89 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/groupBy.js @@ -0,0 +1,59 @@ +var createAggregator = require('../internal/createAggregator'); + +/** Used for native method references. */ +var objectProto = Object.prototype; + +/** Used to check objects for own properties. */ +var hasOwnProperty = objectProto.hasOwnProperty; + +/** + * Creates an object composed of keys generated from the results of running + * each element of `collection` through `iteratee`. The corresponding value + * of each key is an array of the elements responsible for generating the key. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.groupBy([4.2, 6.1, 6.4], function(n) { + * return Math.floor(n); + * }); + * // => { '4': [4.2], '6': [6.1, 6.4] } + * + * _.groupBy([4.2, 6.1, 6.4], function(n) { + * return this.floor(n); + * }, Math); + * // => { '4': [4.2], '6': [6.1, 6.4] } + * + * // using the `_.property` callback shorthand + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ +var groupBy = createAggregator(function(result, value, key) { + if (hasOwnProperty.call(result, key)) { + result[key].push(value); + } else { + result[key] = [value]; + } +}); + +module.exports = groupBy; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/include.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/include.js new file mode 100644 index 0000000..594722a --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/include.js @@ -0,0 +1 @@ +module.exports = require('./includes'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/includes.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/includes.js new file mode 100644 index 0000000..329486a --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/includes.js @@ -0,0 +1,57 @@ +var baseIndexOf = require('../internal/baseIndexOf'), + getLength = require('../internal/getLength'), + isArray = require('../lang/isArray'), + isIterateeCall = require('../internal/isIterateeCall'), + isLength = require('../internal/isLength'), + isString = require('../lang/isString'), + values = require('../object/values'); + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeMax = Math.max; + +/** + * Checks if `target` is in `collection` using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. If `fromIndex` is negative, it's used as the offset + * from the end of `collection`. + * + * @static + * @memberOf _ + * @alias contains, include + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {*} target The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. + * @returns {boolean} Returns `true` if a matching element is found, else `false`. + * @example + * + * _.includes([1, 2, 3], 1); + * // => true + * + * _.includes([1, 2, 3], 1, 2); + * // => false + * + * _.includes({ 'user': 'fred', 'age': 40 }, 'fred'); + * // => true + * + * _.includes('pebbles', 'eb'); + * // => true + */ +function includes(collection, target, fromIndex, guard) { + var length = collection ? getLength(collection) : 0; + if (!isLength(length)) { + collection = values(collection); + length = collection.length; + } + if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) { + fromIndex = 0; + } else { + fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0); + } + return (typeof collection == 'string' || !isArray(collection) && isString(collection)) + ? (fromIndex <= length && collection.indexOf(target, fromIndex) > -1) + : (!!length && baseIndexOf(collection, target, fromIndex) > -1); +} + +module.exports = includes; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/indexBy.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/indexBy.js new file mode 100644 index 0000000..34a941e --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/indexBy.js @@ -0,0 +1,53 @@ +var createAggregator = require('../internal/createAggregator'); + +/** + * Creates an object composed of keys generated from the results of running + * each element of `collection` through `iteratee`. The corresponding value + * of each key is the last element responsible for generating the key. The + * iteratee function is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * var keyData = [ + * { 'dir': 'left', 'code': 97 }, + * { 'dir': 'right', 'code': 100 } + * ]; + * + * _.indexBy(keyData, 'dir'); + * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } + * + * _.indexBy(keyData, function(object) { + * return String.fromCharCode(object.code); + * }); + * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } + * + * _.indexBy(keyData, function(object) { + * return this.fromCharCode(object.code); + * }, String); + * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } + */ +var indexBy = createAggregator(function(result, value, key) { + result[key] = value; +}); + +module.exports = indexBy; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/inject.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/inject.js new file mode 100644 index 0000000..26f53cf --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/inject.js @@ -0,0 +1 @@ +module.exports = require('./reduce'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/invoke.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/invoke.js new file mode 100644 index 0000000..6e71721 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/invoke.js @@ -0,0 +1,42 @@ +var baseEach = require('../internal/baseEach'), + invokePath = require('../internal/invokePath'), + isArrayLike = require('../internal/isArrayLike'), + isKey = require('../internal/isKey'), + restParam = require('../function/restParam'); + +/** + * Invokes the method at `path` of each element in `collection`, returning + * an array of the results of each invoked method. Any additional arguments + * are provided to each invoked method. If `methodName` is a function it's + * invoked for, and `this` bound to, each element in `collection`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Array|Function|string} path The path of the method to invoke or + * the function invoked per iteration. + * @param {...*} [args] The arguments to invoke the method with. + * @returns {Array} Returns the array of results. + * @example + * + * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invoke([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ +var invoke = restParam(function(collection, path, args) { + var index = -1, + isFunc = typeof path == 'function', + isProp = isKey(path), + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value) { + var func = isFunc ? path : ((isProp && value != null) ? value[path] : undefined); + result[++index] = func ? func.apply(value, args) : invokePath(value, path, args); + }); + return result; +}); + +module.exports = invoke; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/map.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/map.js new file mode 100644 index 0000000..5381110 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/map.js @@ -0,0 +1,68 @@ +var arrayMap = require('../internal/arrayMap'), + baseCallback = require('../internal/baseCallback'), + baseMap = require('../internal/baseMap'), + isArray = require('../lang/isArray'); + +/** + * Creates an array of values by running each element in `collection` through + * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three + * arguments: (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. + * + * The guarded methods are: + * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`, + * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`, + * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`, + * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`, + * `sum`, `uniq`, and `words` + * + * @static + * @memberOf _ + * @alias collect + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new mapped array. + * @example + * + * function timesThree(n) { + * return n * 3; + * } + * + * _.map([1, 2], timesThree); + * // => [3, 6] + * + * _.map({ 'a': 1, 'b': 2 }, timesThree); + * // => [3, 6] (iteration order is not guaranteed) + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * // using the `_.property` callback shorthand + * _.map(users, 'user'); + * // => ['barney', 'fred'] + */ +function map(collection, iteratee, thisArg) { + var func = isArray(collection) ? arrayMap : baseMap; + iteratee = baseCallback(iteratee, thisArg, 3); + return func(collection, iteratee); +} + +module.exports = map; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/max.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/max.js new file mode 100644 index 0000000..bb1d213 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/max.js @@ -0,0 +1 @@ +module.exports = require('../math/max'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/min.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/min.js new file mode 100644 index 0000000..eef13d0 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/min.js @@ -0,0 +1 @@ +module.exports = require('../math/min'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/partition.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/partition.js new file mode 100644 index 0000000..ee35f27 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/partition.js @@ -0,0 +1,66 @@ +var createAggregator = require('../internal/createAggregator'); + +/** + * Creates an array of elements split into two groups, the first of which + * contains elements `predicate` returns truthy for, while the second of which + * contains elements `predicate` returns falsey for. The predicate is bound + * to `thisArg` and invoked with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the array of grouped elements. + * @example + * + * _.partition([1, 2, 3], function(n) { + * return n % 2; + * }); + * // => [[1, 3], [2]] + * + * _.partition([1.2, 2.3, 3.4], function(n) { + * return this.floor(n) % 2; + * }, Math); + * // => [[1.2, 3.4], [2.3]] + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true }, + * { 'user': 'pebbles', 'age': 1, 'active': false } + * ]; + * + * var mapper = function(array) { + * return _.pluck(array, 'user'); + * }; + * + * // using the `_.matches` callback shorthand + * _.map(_.partition(users, { 'age': 1, 'active': false }), mapper); + * // => [['pebbles'], ['barney', 'fred']] + * + * // using the `_.matchesProperty` callback shorthand + * _.map(_.partition(users, 'active', false), mapper); + * // => [['barney', 'pebbles'], ['fred']] + * + * // using the `_.property` callback shorthand + * _.map(_.partition(users, 'active'), mapper); + * // => [['fred'], ['barney', 'pebbles']] + */ +var partition = createAggregator(function(result, value, key) { + result[key ? 0 : 1].push(value); +}, function() { return [[], []]; }); + +module.exports = partition; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/pluck.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/pluck.js new file mode 100644 index 0000000..5ee1ec8 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/pluck.js @@ -0,0 +1,31 @@ +var map = require('./map'), + property = require('../utility/property'); + +/** + * Gets the property value of `path` from all elements in `collection`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Array|string} path The path of the property to pluck. + * @returns {Array} Returns the property values. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * _.pluck(users, 'user'); + * // => ['barney', 'fred'] + * + * var userIndex = _.indexBy(users, 'user'); + * _.pluck(userIndex, 'age'); + * // => [36, 40] (iteration order is not guaranteed) + */ +function pluck(collection, path) { + return map(collection, property(path)); +} + +module.exports = pluck; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/reduce.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/reduce.js new file mode 100644 index 0000000..5d5e8c9 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/reduce.js @@ -0,0 +1,44 @@ +var arrayReduce = require('../internal/arrayReduce'), + baseEach = require('../internal/baseEach'), + createReduce = require('../internal/createReduce'); + +/** + * Reduces `collection` to a value which is the accumulated result of running + * each element in `collection` through `iteratee`, where each successive + * invocation is supplied the return value of the previous. If `accumulator` + * is not provided the first element of `collection` is used as the initial + * value. The `iteratee` is bound to `thisArg` and invoked with four arguments: + * (accumulator, value, index|key, collection). + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.reduce`, `_.reduceRight`, and `_.transform`. + * + * The guarded methods are: + * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `sortByAll`, + * and `sortByOrder` + * + * @static + * @memberOf _ + * @alias foldl, inject + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {*} Returns the accumulated value. + * @example + * + * _.reduce([1, 2], function(total, n) { + * return total + n; + * }); + * // => 3 + * + * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) { + * result[key] = n * 3; + * return result; + * }, {}); + * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed) + */ +var reduce = createReduce(arrayReduce, baseEach); + +module.exports = reduce; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/reduceRight.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/reduceRight.js new file mode 100644 index 0000000..5a5753b --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/reduceRight.js @@ -0,0 +1,29 @@ +var arrayReduceRight = require('../internal/arrayReduceRight'), + baseEachRight = require('../internal/baseEachRight'), + createReduce = require('../internal/createReduce'); + +/** + * This method is like `_.reduce` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @alias foldr + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {*} Returns the accumulated value. + * @example + * + * var array = [[0, 1], [2, 3], [4, 5]]; + * + * _.reduceRight(array, function(flattened, other) { + * return flattened.concat(other); + * }, []); + * // => [4, 5, 2, 3, 0, 1] + */ +var reduceRight = createReduce(arrayReduceRight, baseEachRight); + +module.exports = reduceRight; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/reject.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/reject.js new file mode 100644 index 0000000..5592453 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/reject.js @@ -0,0 +1,50 @@ +var arrayFilter = require('../internal/arrayFilter'), + baseCallback = require('../internal/baseCallback'), + baseFilter = require('../internal/baseFilter'), + isArray = require('../lang/isArray'); + +/** + * The opposite of `_.filter`; this method returns the elements of `collection` + * that `predicate` does **not** return truthy for. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the new filtered array. + * @example + * + * _.reject([1, 2, 3, 4], function(n) { + * return n % 2 == 0; + * }); + * // => [1, 3] + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user'); + * // => ['barney'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.reject(users, 'active', false), 'user'); + * // => ['fred'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.reject(users, 'active'), 'user'); + * // => ['barney'] + */ +function reject(collection, predicate, thisArg) { + var func = isArray(collection) ? arrayFilter : baseFilter; + predicate = baseCallback(predicate, thisArg, 3); + return func(collection, function(value, index, collection) { + return !predicate(value, index, collection); + }); +} + +module.exports = reject; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sample.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sample.js new file mode 100644 index 0000000..8e01533 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sample.js @@ -0,0 +1,50 @@ +var baseRandom = require('../internal/baseRandom'), + isIterateeCall = require('../internal/isIterateeCall'), + toArray = require('../lang/toArray'), + toIterable = require('../internal/toIterable'); + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeMin = Math.min; + +/** + * Gets a random element or `n` random elements from a collection. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to sample. + * @param {number} [n] The number of elements to sample. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {*} Returns the random sample(s). + * @example + * + * _.sample([1, 2, 3, 4]); + * // => 2 + * + * _.sample([1, 2, 3, 4], 2); + * // => [3, 1] + */ +function sample(collection, n, guard) { + if (guard ? isIterateeCall(collection, n, guard) : n == null) { + collection = toIterable(collection); + var length = collection.length; + return length > 0 ? collection[baseRandom(0, length - 1)] : undefined; + } + var index = -1, + result = toArray(collection), + length = result.length, + lastIndex = length - 1; + + n = nativeMin(n < 0 ? 0 : (+n || 0), length); + while (++index < n) { + var rand = baseRandom(index, lastIndex), + value = result[rand]; + + result[rand] = result[index]; + result[index] = value; + } + result.length = n; + return result; +} + +module.exports = sample; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/select.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/select.js new file mode 100644 index 0000000..ade80f6 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/select.js @@ -0,0 +1 @@ +module.exports = require('./filter'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/shuffle.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/shuffle.js new file mode 100644 index 0000000..949689c --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/shuffle.js @@ -0,0 +1,24 @@ +var sample = require('./sample'); + +/** Used as references for `-Infinity` and `Infinity`. */ +var POSITIVE_INFINITY = Number.POSITIVE_INFINITY; + +/** + * Creates an array of shuffled values, using a version of the + * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to shuffle. + * @returns {Array} Returns the new shuffled array. + * @example + * + * _.shuffle([1, 2, 3, 4]); + * // => [4, 1, 3, 2] + */ +function shuffle(collection) { + return sample(collection, POSITIVE_INFINITY); +} + +module.exports = shuffle; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/size.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/size.js new file mode 100644 index 0000000..78dcf4c --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/size.js @@ -0,0 +1,30 @@ +var getLength = require('../internal/getLength'), + isLength = require('../internal/isLength'), + keys = require('../object/keys'); + +/** + * Gets the size of `collection` by returning its length for array-like + * values or the number of own enumerable properties for objects. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @returns {number} Returns the size of `collection`. + * @example + * + * _.size([1, 2, 3]); + * // => 3 + * + * _.size({ 'a': 1, 'b': 2 }); + * // => 2 + * + * _.size('pebbles'); + * // => 7 + */ +function size(collection) { + var length = collection ? getLength(collection) : 0; + return isLength(length) ? length : keys(collection).length; +} + +module.exports = size; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/some.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/some.js new file mode 100644 index 0000000..d0b09a4 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/some.js @@ -0,0 +1,67 @@ +var arraySome = require('../internal/arraySome'), + baseCallback = require('../internal/baseCallback'), + baseSome = require('../internal/baseSome'), + isArray = require('../lang/isArray'), + isIterateeCall = require('../internal/isIterateeCall'); + +/** + * Checks if `predicate` returns truthy for **any** element of `collection`. + * The function returns as soon as it finds a passing value and does not iterate + * over the entire collection. The predicate is bound to `thisArg` and invoked + * with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias any + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + * @example + * + * _.some([null, 0, 'yes', false], Boolean); + * // => true + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.some(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // using the `_.matchesProperty` callback shorthand + * _.some(users, 'active', false); + * // => true + * + * // using the `_.property` callback shorthand + * _.some(users, 'active'); + * // => true + */ +function some(collection, predicate, thisArg) { + var func = isArray(collection) ? arraySome : baseSome; + if (thisArg && isIterateeCall(collection, predicate, thisArg)) { + predicate = undefined; + } + if (typeof predicate != 'function' || thisArg !== undefined) { + predicate = baseCallback(predicate, thisArg, 3); + } + return func(collection, predicate); +} + +module.exports = some; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sortBy.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sortBy.js new file mode 100644 index 0000000..4401c77 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sortBy.js @@ -0,0 +1,71 @@ +var baseCallback = require('../internal/baseCallback'), + baseMap = require('../internal/baseMap'), + baseSortBy = require('../internal/baseSortBy'), + compareAscending = require('../internal/compareAscending'), + isIterateeCall = require('../internal/isIterateeCall'); + +/** + * Creates an array of elements, sorted in ascending order by the results of + * running each element in a collection through `iteratee`. This method performs + * a stable sort, that is, it preserves the original sort order of equal elements. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new sorted array. + * @example + * + * _.sortBy([1, 2, 3], function(n) { + * return Math.sin(n); + * }); + * // => [3, 1, 2] + * + * _.sortBy([1, 2, 3], function(n) { + * return this.sin(n); + * }, Math); + * // => [3, 1, 2] + * + * var users = [ + * { 'user': 'fred' }, + * { 'user': 'pebbles' }, + * { 'user': 'barney' } + * ]; + * + * // using the `_.property` callback shorthand + * _.pluck(_.sortBy(users, 'user'), 'user'); + * // => ['barney', 'fred', 'pebbles'] + */ +function sortBy(collection, iteratee, thisArg) { + if (collection == null) { + return []; + } + if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { + iteratee = undefined; + } + var index = -1; + iteratee = baseCallback(iteratee, thisArg, 3); + + var result = baseMap(collection, function(value, key, collection) { + return { 'criteria': iteratee(value, key, collection), 'index': ++index, 'value': value }; + }); + return baseSortBy(result, compareAscending); +} + +module.exports = sortBy; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sortByAll.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sortByAll.js new file mode 100644 index 0000000..4766c20 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sortByAll.js @@ -0,0 +1,52 @@ +var baseFlatten = require('../internal/baseFlatten'), + baseSortByOrder = require('../internal/baseSortByOrder'), + isIterateeCall = require('../internal/isIterateeCall'), + restParam = require('../function/restParam'); + +/** + * This method is like `_.sortBy` except that it can sort by multiple iteratees + * or property names. + * + * If a property name is provided for an iteratee the created `_.property` + * style callback returns the property value of the given element. + * + * If an object is provided for an iteratee the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {...(Function|Function[]|Object|Object[]|string|string[])} iteratees + * The iteratees to sort by, specified as individual values or arrays of values. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 42 }, + * { 'user': 'barney', 'age': 34 } + * ]; + * + * _.map(_.sortByAll(users, ['user', 'age']), _.values); + * // => [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]] + * + * _.map(_.sortByAll(users, 'user', function(chr) { + * return Math.floor(chr.age / 10); + * }), _.values); + * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] + */ +var sortByAll = restParam(function(collection, iteratees) { + if (collection == null) { + return []; + } + var guard = iteratees[2]; + if (guard && isIterateeCall(iteratees[0], iteratees[1], guard)) { + iteratees.length = 1; + } + return baseSortByOrder(collection, baseFlatten(iteratees), []); +}); + +module.exports = sortByAll; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sortByOrder.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sortByOrder.js new file mode 100644 index 0000000..8b4fc19 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sortByOrder.js @@ -0,0 +1,55 @@ +var baseSortByOrder = require('../internal/baseSortByOrder'), + isArray = require('../lang/isArray'), + isIterateeCall = require('../internal/isIterateeCall'); + +/** + * This method is like `_.sortByAll` except that it allows specifying the + * sort orders of the iteratees to sort by. If `orders` is unspecified, all + * values are sorted in ascending order. Otherwise, a value is sorted in + * ascending order if its corresponding order is "asc", and descending if "desc". + * + * If a property name is provided for an iteratee the created `_.property` + * style callback returns the property value of the given element. + * + * If an object is provided for an iteratee the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {boolean[]} [orders] The sort orders of `iteratees`. + * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 34 }, + * { 'user': 'fred', 'age': 42 }, + * { 'user': 'barney', 'age': 36 } + * ]; + * + * // sort by `user` in ascending order and by `age` in descending order + * _.map(_.sortByOrder(users, ['user', 'age'], ['asc', 'desc']), _.values); + * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] + */ +function sortByOrder(collection, iteratees, orders, guard) { + if (collection == null) { + return []; + } + if (guard && isIterateeCall(iteratees, orders, guard)) { + orders = undefined; + } + if (!isArray(iteratees)) { + iteratees = iteratees == null ? [] : [iteratees]; + } + if (!isArray(orders)) { + orders = orders == null ? [] : [orders]; + } + return baseSortByOrder(collection, iteratees, orders); +} + +module.exports = sortByOrder; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sum.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sum.js new file mode 100644 index 0000000..a2e9380 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/sum.js @@ -0,0 +1 @@ +module.exports = require('../math/sum'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/where.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/where.js new file mode 100644 index 0000000..f603bf8 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/collection/where.js @@ -0,0 +1,37 @@ +var baseMatches = require('../internal/baseMatches'), + filter = require('./filter'); + +/** + * Performs a deep comparison between each element in `collection` and the + * source object, returning an array of all elements that have equivalent + * property values. + * + * **Note:** This method supports comparing arrays, booleans, `Date` objects, + * numbers, `Object` objects, regexes, and strings. Objects are compared by + * their own, not inherited, enumerable properties. For comparing a single + * own or inherited property value see `_.matchesProperty`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Object} source The object of property values to match. + * @returns {Array} Returns the new filtered array. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false, 'pets': ['hoppy'] }, + * { 'user': 'fred', 'age': 40, 'active': true, 'pets': ['baby puss', 'dino'] } + * ]; + * + * _.pluck(_.where(users, { 'age': 36, 'active': false }), 'user'); + * // => ['barney'] + * + * _.pluck(_.where(users, { 'pets': ['dino'] }), 'user'); + * // => ['fred'] + */ +function where(collection, source) { + return filter(collection, baseMatches(source)); +} + +module.exports = where; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/date.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/date.js new file mode 100644 index 0000000..195366e --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/date.js @@ -0,0 +1,3 @@ +module.exports = { + 'now': require('./date/now') +}; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/date/now.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/date/now.js new file mode 100644 index 0000000..ffe3060 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/date/now.js @@ -0,0 +1,24 @@ +var getNative = require('../internal/getNative'); + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeNow = getNative(Date, 'now'); + +/** + * Gets the number of milliseconds that have elapsed since the Unix epoch + * (1 January 1970 00:00:00 UTC). + * + * @static + * @memberOf _ + * @category Date + * @example + * + * _.defer(function(stamp) { + * console.log(_.now() - stamp); + * }, _.now()); + * // => logs the number of milliseconds it took for the deferred function to be invoked + */ +var now = nativeNow || function() { + return new Date().getTime(); +}; + +module.exports = now; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function.js new file mode 100644 index 0000000..71f8ebe --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function.js @@ -0,0 +1,28 @@ +module.exports = { + 'after': require('./function/after'), + 'ary': require('./function/ary'), + 'backflow': require('./function/backflow'), + 'before': require('./function/before'), + 'bind': require('./function/bind'), + 'bindAll': require('./function/bindAll'), + 'bindKey': require('./function/bindKey'), + 'compose': require('./function/compose'), + 'curry': require('./function/curry'), + 'curryRight': require('./function/curryRight'), + 'debounce': require('./function/debounce'), + 'defer': require('./function/defer'), + 'delay': require('./function/delay'), + 'flow': require('./function/flow'), + 'flowRight': require('./function/flowRight'), + 'memoize': require('./function/memoize'), + 'modArgs': require('./function/modArgs'), + 'negate': require('./function/negate'), + 'once': require('./function/once'), + 'partial': require('./function/partial'), + 'partialRight': require('./function/partialRight'), + 'rearg': require('./function/rearg'), + 'restParam': require('./function/restParam'), + 'spread': require('./function/spread'), + 'throttle': require('./function/throttle'), + 'wrap': require('./function/wrap') +}; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/after.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/after.js new file mode 100644 index 0000000..96a51fd --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/after.js @@ -0,0 +1,48 @@ +/** Used as the `TypeError` message for "Functions" methods. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeIsFinite = global.isFinite; + +/** + * The opposite of `_.before`; this method creates a function that invokes + * `func` once it's called `n` or more times. + * + * @static + * @memberOf _ + * @category Function + * @param {number} n The number of calls before `func` is invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var saves = ['profile', 'settings']; + * + * var done = _.after(saves.length, function() { + * console.log('done saving!'); + * }); + * + * _.forEach(saves, function(type) { + * asyncSave({ 'type': type, 'complete': done }); + * }); + * // => logs 'done saving!' after the two async saves have completed + */ +function after(n, func) { + if (typeof func != 'function') { + if (typeof n == 'function') { + var temp = n; + n = func; + func = temp; + } else { + throw new TypeError(FUNC_ERROR_TEXT); + } + } + n = nativeIsFinite(n = +n) ? n : 0; + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } + }; +} + +module.exports = after; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/ary.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/ary.js new file mode 100644 index 0000000..53a6913 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/ary.js @@ -0,0 +1,34 @@ +var createWrapper = require('../internal/createWrapper'), + isIterateeCall = require('../internal/isIterateeCall'); + +/** Used to compose bitmasks for wrapper metadata. */ +var ARY_FLAG = 128; + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeMax = Math.max; + +/** + * Creates a function that accepts up to `n` arguments ignoring any + * additional arguments. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to cap arguments for. + * @param {number} [n=func.length] The arity cap. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Function} Returns the new function. + * @example + * + * _.map(['6', '8', '10'], _.ary(parseInt, 1)); + * // => [6, 8, 10] + */ +function ary(func, n, guard) { + if (guard && isIterateeCall(func, n, guard)) { + n = undefined; + } + n = (func && n == null) ? func.length : nativeMax(+n || 0, 0); + return createWrapper(func, ARY_FLAG, undefined, undefined, undefined, undefined, n); +} + +module.exports = ary; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/backflow.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/backflow.js new file mode 100644 index 0000000..1954e94 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/backflow.js @@ -0,0 +1 @@ +module.exports = require('./flowRight'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/before.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/before.js new file mode 100644 index 0000000..3d94216 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/before.js @@ -0,0 +1,42 @@ +/** Used as the `TypeError` message for "Functions" methods. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/** + * Creates a function that invokes `func`, with the `this` binding and arguments + * of the created function, while it's called less than `n` times. Subsequent + * calls to the created function return the result of the last `func` invocation. + * + * @static + * @memberOf _ + * @category Function + * @param {number} n The number of calls at which `func` is no longer invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * jQuery('#add').on('click', _.before(5, addContactToList)); + * // => allows adding up to 4 contacts to the list + */ +function before(n, func) { + var result; + if (typeof func != 'function') { + if (typeof n == 'function') { + var temp = n; + n = func; + func = temp; + } else { + throw new TypeError(FUNC_ERROR_TEXT); + } + } + return function() { + if (--n > 0) { + result = func.apply(this, arguments); + } + if (n <= 1) { + func = undefined; + } + return result; + }; +} + +module.exports = before; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/bind.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/bind.js new file mode 100644 index 0000000..0de126a --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/bind.js @@ -0,0 +1,56 @@ +var createWrapper = require('../internal/createWrapper'), + replaceHolders = require('../internal/replaceHolders'), + restParam = require('./restParam'); + +/** Used to compose bitmasks for wrapper metadata. */ +var BIND_FLAG = 1, + PARTIAL_FLAG = 32; + +/** + * Creates a function that invokes `func` with the `this` binding of `thisArg` + * and prepends any additional `_.bind` arguments to those provided to the + * bound function. + * + * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for partially applied arguments. + * + * **Note:** Unlike native `Function#bind` this method does not set the "length" + * property of bound functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var greet = function(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * }; + * + * var object = { 'user': 'fred' }; + * + * var bound = _.bind(greet, object, 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * // using placeholders + * var bound = _.bind(greet, object, _, '!'); + * bound('hi'); + * // => 'hi fred!' + */ +var bind = restParam(function(func, thisArg, partials) { + var bitmask = BIND_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, bind.placeholder); + bitmask |= PARTIAL_FLAG; + } + return createWrapper(func, bitmask, thisArg, partials, holders); +}); + +// Assign default placeholders. +bind.placeholder = {}; + +module.exports = bind; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/bindAll.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/bindAll.js new file mode 100644 index 0000000..a09e948 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/bindAll.js @@ -0,0 +1,50 @@ +var baseFlatten = require('../internal/baseFlatten'), + createWrapper = require('../internal/createWrapper'), + functions = require('../object/functions'), + restParam = require('./restParam'); + +/** Used to compose bitmasks for wrapper metadata. */ +var BIND_FLAG = 1; + +/** + * Binds methods of an object to the object itself, overwriting the existing + * method. Method names may be specified as individual arguments or as arrays + * of method names. If no method names are provided all enumerable function + * properties, own and inherited, of `object` are bound. + * + * **Note:** This method does not set the "length" property of bound functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Object} object The object to bind and assign the bound methods to. + * @param {...(string|string[])} [methodNames] The object method names to bind, + * specified as individual method names or arrays of method names. + * @returns {Object} Returns `object`. + * @example + * + * var view = { + * 'label': 'docs', + * 'onClick': function() { + * console.log('clicked ' + this.label); + * } + * }; + * + * _.bindAll(view); + * jQuery('#docs').on('click', view.onClick); + * // => logs 'clicked docs' when the element is clicked + */ +var bindAll = restParam(function(object, methodNames) { + methodNames = methodNames.length ? baseFlatten(methodNames) : functions(object); + + var index = -1, + length = methodNames.length; + + while (++index < length) { + var key = methodNames[index]; + object[key] = createWrapper(object[key], BIND_FLAG, object); + } + return object; +}); + +module.exports = bindAll; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/bindKey.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/bindKey.js new file mode 100644 index 0000000..b787fe7 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/bindKey.js @@ -0,0 +1,66 @@ +var createWrapper = require('../internal/createWrapper'), + replaceHolders = require('../internal/replaceHolders'), + restParam = require('./restParam'); + +/** Used to compose bitmasks for wrapper metadata. */ +var BIND_FLAG = 1, + BIND_KEY_FLAG = 2, + PARTIAL_FLAG = 32; + +/** + * Creates a function that invokes the method at `object[key]` and prepends + * any additional `_.bindKey` arguments to those provided to the bound function. + * + * This method differs from `_.bind` by allowing bound functions to reference + * methods that may be redefined or don't yet exist. + * See [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) + * for more details. + * + * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * @static + * @memberOf _ + * @category Function + * @param {Object} object The object the method belongs to. + * @param {string} key The key of the method. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var object = { + * 'user': 'fred', + * 'greet': function(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * } + * }; + * + * var bound = _.bindKey(object, 'greet', 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * object.greet = function(greeting, punctuation) { + * return greeting + 'ya ' + this.user + punctuation; + * }; + * + * bound('!'); + * // => 'hiya fred!' + * + * // using placeholders + * var bound = _.bindKey(object, 'greet', _, '!'); + * bound('hi'); + * // => 'hiya fred!' + */ +var bindKey = restParam(function(object, key, partials) { + var bitmask = BIND_FLAG | BIND_KEY_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, bindKey.placeholder); + bitmask |= PARTIAL_FLAG; + } + return createWrapper(key, bitmask, object, partials, holders); +}); + +// Assign default placeholders. +bindKey.placeholder = {}; + +module.exports = bindKey; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/compose.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/compose.js new file mode 100644 index 0000000..1954e94 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/compose.js @@ -0,0 +1 @@ +module.exports = require('./flowRight'); diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/curry.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/curry.js new file mode 100644 index 0000000..b7db3fd --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/curry.js @@ -0,0 +1,51 @@ +var createCurry = require('../internal/createCurry'); + +/** Used to compose bitmasks for wrapper metadata. */ +var CURRY_FLAG = 8; + +/** + * Creates a function that accepts one or more arguments of `func` that when + * called either invokes `func` returning its result, if all `func` arguments + * have been provided, or returns a function that accepts one or more of the + * remaining `func` arguments, and so on. The arity of `func` may be specified + * if `func.length` is not sufficient. + * + * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for provided arguments. + * + * **Note:** This method does not set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curry(abc); + * + * curried(1)(2)(3); + * // => [1, 2, 3] + * + * curried(1, 2)(3); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // using placeholders + * curried(1)(_, 3)(2); + * // => [1, 2, 3] + */ +var curry = createCurry(CURRY_FLAG); + +// Assign default placeholders. +curry.placeholder = {}; + +module.exports = curry; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/curryRight.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/curryRight.js new file mode 100644 index 0000000..11c5403 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/curryRight.js @@ -0,0 +1,48 @@ +var createCurry = require('../internal/createCurry'); + +/** Used to compose bitmasks for wrapper metadata. */ +var CURRY_RIGHT_FLAG = 16; + +/** + * This method is like `_.curry` except that arguments are applied to `func` + * in the manner of `_.partialRight` instead of `_.partial`. + * + * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for provided arguments. + * + * **Note:** This method does not set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curryRight(abc); + * + * curried(3)(2)(1); + * // => [1, 2, 3] + * + * curried(2, 3)(1); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // using placeholders + * curried(3)(1, _)(2); + * // => [1, 2, 3] + */ +var curryRight = createCurry(CURRY_RIGHT_FLAG); + +// Assign default placeholders. +curryRight.placeholder = {}; + +module.exports = curryRight; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/debounce.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/debounce.js new file mode 100644 index 0000000..163af90 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/debounce.js @@ -0,0 +1,181 @@ +var isObject = require('../lang/isObject'), + now = require('../date/now'); + +/** Used as the `TypeError` message for "Functions" methods. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeMax = Math.max; + +/** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked. The debounced function comes with a `cancel` method to cancel + * delayed invocations. Provide an options object to indicate that `func` + * should be invoked on the leading and/or trailing edge of the `wait` timeout. + * Subsequent calls to the debounced function return the result of the last + * `func` invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked + * on the trailing edge of the timeout only if the the debounced function is + * invoked more than once during the `wait` timeout. + * + * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) + * for details over the differences between `_.debounce` and `_.throttle`. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] The number of milliseconds to delay. + * @param {Object} [options] The options object. + * @param {boolean} [options.leading=false] Specify invoking on the leading + * edge of the timeout. + * @param {number} [options.maxWait] The maximum time `func` is allowed to be + * delayed before it's invoked. + * @param {boolean} [options.trailing=true] Specify invoking on the trailing + * edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // avoid costly calculations while the window size is in flux + * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); + * + * // invoke `sendMail` when the click event is fired, debouncing subsequent calls + * jQuery('#postbox').on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })); + * + * // ensure `batchLog` is invoked once after 1 second of debounced calls + * var source = new EventSource('/stream'); + * jQuery(source).on('message', _.debounce(batchLog, 250, { + * 'maxWait': 1000 + * })); + * + * // cancel a debounced call + * var todoChanges = _.debounce(batchLog, 1000); + * Object.observe(models.todo, todoChanges); + * + * Object.observe(models, function(changes) { + * if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) { + * todoChanges.cancel(); + * } + * }, ['delete']); + * + * // ...at some point `models.todo` is changed + * models.todo.completed = true; + * + * // ...before 1 second has passed `models.todo` is deleted + * // which cancels the debounced `todoChanges` call + * delete models.todo; + */ +function debounce(func, wait, options) { + var args, + maxTimeoutId, + result, + stamp, + thisArg, + timeoutId, + trailingCall, + lastCalled = 0, + maxWait = false, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + wait = wait < 0 ? 0 : (+wait || 0); + if (options === true) { + var leading = true; + trailing = false; + } else if (isObject(options)) { + leading = !!options.leading; + maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait); + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + function cancel() { + if (timeoutId) { + clearTimeout(timeoutId); + } + if (maxTimeoutId) { + clearTimeout(maxTimeoutId); + } + lastCalled = 0; + maxTimeoutId = timeoutId = trailingCall = undefined; + } + + function complete(isCalled, id) { + if (id) { + clearTimeout(id); + } + maxTimeoutId = timeoutId = trailingCall = undefined; + if (isCalled) { + lastCalled = now(); + result = func.apply(thisArg, args); + if (!timeoutId && !maxTimeoutId) { + args = thisArg = undefined; + } + } + } + + function delayed() { + var remaining = wait - (now() - stamp); + if (remaining <= 0 || remaining > wait) { + complete(trailingCall, maxTimeoutId); + } else { + timeoutId = setTimeout(delayed, remaining); + } + } + + function maxDelayed() { + complete(trailing, timeoutId); + } + + function debounced() { + args = arguments; + stamp = now(); + thisArg = this; + trailingCall = trailing && (timeoutId || !leading); + + if (maxWait === false) { + var leadingCall = leading && !timeoutId; + } else { + if (!maxTimeoutId && !leading) { + lastCalled = stamp; + } + var remaining = maxWait - (stamp - lastCalled), + isCalled = remaining <= 0 || remaining > maxWait; + + if (isCalled) { + if (maxTimeoutId) { + maxTimeoutId = clearTimeout(maxTimeoutId); + } + lastCalled = stamp; + result = func.apply(thisArg, args); + } + else if (!maxTimeoutId) { + maxTimeoutId = setTimeout(maxDelayed, remaining); + } + } + if (isCalled && timeoutId) { + timeoutId = clearTimeout(timeoutId); + } + else if (!timeoutId && wait !== maxWait) { + timeoutId = setTimeout(delayed, wait); + } + if (leadingCall) { + isCalled = true; + result = func.apply(thisArg, args); + } + if (isCalled && !timeoutId && !maxTimeoutId) { + args = thisArg = undefined; + } + return result; + } + debounced.cancel = cancel; + return debounced; +} + +module.exports = debounce; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/defer.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/defer.js new file mode 100644 index 0000000..3accbf9 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/defer.js @@ -0,0 +1,25 @@ +var baseDelay = require('../internal/baseDelay'), + restParam = require('./restParam'); + +/** + * Defers invoking the `func` until the current call stack has cleared. Any + * additional arguments are provided to `func` when it's invoked. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to defer. + * @param {...*} [args] The arguments to invoke the function with. + * @returns {number} Returns the timer id. + * @example + * + * _.defer(function(text) { + * console.log(text); + * }, 'deferred'); + * // logs 'deferred' after one or more milliseconds + */ +var defer = restParam(function(func, args) { + return baseDelay(func, 1, args); +}); + +module.exports = defer; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/delay.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/delay.js new file mode 100644 index 0000000..d5eef27 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/delay.js @@ -0,0 +1,26 @@ +var baseDelay = require('../internal/baseDelay'), + restParam = require('./restParam'); + +/** + * Invokes `func` after `wait` milliseconds. Any additional arguments are + * provided to `func` when it's invoked. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {...*} [args] The arguments to invoke the function with. + * @returns {number} Returns the timer id. + * @example + * + * _.delay(function(text) { + * console.log(text); + * }, 1000, 'later'); + * // => logs 'later' after one second + */ +var delay = restParam(function(func, wait, args) { + return baseDelay(func, wait, args); +}); + +module.exports = delay; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/flow.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/flow.js new file mode 100644 index 0000000..a435a3d --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/flow.js @@ -0,0 +1,25 @@ +var createFlow = require('../internal/createFlow'); + +/** + * Creates a function that returns the result of invoking the provided + * functions with the `this` binding of the created function, where each + * successive invocation is supplied the return value of the previous. + * + * @static + * @memberOf _ + * @category Function + * @param {...Function} [funcs] Functions to invoke. + * @returns {Function} Returns the new function. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var addSquare = _.flow(_.add, square); + * addSquare(1, 2); + * // => 9 + */ +var flow = createFlow(); + +module.exports = flow; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/flowRight.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/flowRight.js new file mode 100644 index 0000000..23b9d76 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/flowRight.js @@ -0,0 +1,25 @@ +var createFlow = require('../internal/createFlow'); + +/** + * This method is like `_.flow` except that it creates a function that + * invokes the provided functions from right to left. + * + * @static + * @memberOf _ + * @alias backflow, compose + * @category Function + * @param {...Function} [funcs] Functions to invoke. + * @returns {Function} Returns the new function. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var addSquare = _.flowRight(square, _.add); + * addSquare(1, 2); + * // => 9 + */ +var flowRight = createFlow(true); + +module.exports = flowRight; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/memoize.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/memoize.js new file mode 100644 index 0000000..f3b8d69 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/memoize.js @@ -0,0 +1,80 @@ +var MapCache = require('../internal/MapCache'); + +/** Used as the `TypeError` message for "Functions" methods. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is coerced to a string and used as the + * cache key. The `func` is invoked with the `this` binding of the memoized + * function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object) + * method interface of `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoizing function. + * @example + * + * var upperCase = _.memoize(function(string) { + * return string.toUpperCase(); + * }); + * + * upperCase('fred'); + * // => 'FRED' + * + * // modifying the result cache + * upperCase.cache.set('fred', 'BARNEY'); + * upperCase('fred'); + * // => 'BARNEY' + * + * // replacing `_.memoize.Cache` + * var object = { 'user': 'fred' }; + * var other = { 'user': 'barney' }; + * var identity = _.memoize(_.identity); + * + * identity(object); + * // => { 'user': 'fred' } + * identity(other); + * // => { 'user': 'fred' } + * + * _.memoize.Cache = WeakMap; + * var identity = _.memoize(_.identity); + * + * identity(object); + * // => { 'user': 'fred' } + * identity(other); + * // => { 'user': 'barney' } + */ +function memoize(func, resolver) { + if (typeof func != 'function' || (resolver && typeof resolver != 'function')) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var memoized = function() { + var args = arguments, + key = resolver ? resolver.apply(this, args) : args[0], + cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result); + return result; + }; + memoized.cache = new memoize.Cache; + return memoized; +} + +// Assign cache to `_.memoize`. +memoize.Cache = MapCache; + +module.exports = memoize; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/modArgs.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/modArgs.js new file mode 100644 index 0000000..49b9b5e --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/modArgs.js @@ -0,0 +1,58 @@ +var arrayEvery = require('../internal/arrayEvery'), + baseFlatten = require('../internal/baseFlatten'), + baseIsFunction = require('../internal/baseIsFunction'), + restParam = require('./restParam'); + +/** Used as the `TypeError` message for "Functions" methods. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeMin = Math.min; + +/** + * Creates a function that runs each argument through a corresponding + * transform function. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to wrap. + * @param {...(Function|Function[])} [transforms] The functions to transform + * arguments, specified as individual functions or arrays of functions. + * @returns {Function} Returns the new function. + * @example + * + * function doubled(n) { + * return n * 2; + * } + * + * function square(n) { + * return n * n; + * } + * + * var modded = _.modArgs(function(x, y) { + * return [x, y]; + * }, square, doubled); + * + * modded(1, 2); + * // => [1, 4] + * + * modded(5, 10); + * // => [25, 20] + */ +var modArgs = restParam(function(func, transforms) { + transforms = baseFlatten(transforms); + if (typeof func != 'function' || !arrayEvery(transforms, baseIsFunction)) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var length = transforms.length; + return restParam(function(args) { + var index = nativeMin(args.length, length); + while (index--) { + args[index] = transforms[index](args[index]); + } + return func.apply(this, args); + }); +}); + +module.exports = modArgs; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/negate.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/negate.js new file mode 100644 index 0000000..8247939 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/negate.js @@ -0,0 +1,32 @@ +/** Used as the `TypeError` message for "Functions" methods. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/** + * Creates a function that negates the result of the predicate `func`. The + * `func` predicate is invoked with the `this` binding and arguments of the + * created function. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} predicate The predicate to negate. + * @returns {Function} Returns the new function. + * @example + * + * function isEven(n) { + * return n % 2 == 0; + * } + * + * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); + * // => [1, 3, 5] + */ +function negate(predicate) { + if (typeof predicate != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return function() { + return !predicate.apply(this, arguments); + }; +} + +module.exports = negate; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/once.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/once.js new file mode 100644 index 0000000..0b5bd85 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/once.js @@ -0,0 +1,24 @@ +var before = require('./before'); + +/** + * Creates a function that is restricted to invoking `func` once. Repeat calls + * to the function return the value of the first call. The `func` is invoked + * with the `this` binding and arguments of the created function. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // `initialize` invokes `createApplication` once + */ +function once(func) { + return before(2, func); +} + +module.exports = once; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/partial.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/partial.js new file mode 100644 index 0000000..fb1d04f --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/partial.js @@ -0,0 +1,43 @@ +var createPartial = require('../internal/createPartial'); + +/** Used to compose bitmasks for wrapper metadata. */ +var PARTIAL_FLAG = 32; + +/** + * Creates a function that invokes `func` with `partial` arguments prepended + * to those provided to the new function. This method is like `_.bind` except + * it does **not** alter the `this` binding. + * + * The `_.partial.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method does not set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { + * return greeting + ' ' + name; + * }; + * + * var sayHelloTo = _.partial(greet, 'hello'); + * sayHelloTo('fred'); + * // => 'hello fred' + * + * // using placeholders + * var greetFred = _.partial(greet, _, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + */ +var partial = createPartial(PARTIAL_FLAG); + +// Assign default placeholders. +partial.placeholder = {}; + +module.exports = partial; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/partialRight.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/partialRight.js new file mode 100644 index 0000000..634e6a4 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/partialRight.js @@ -0,0 +1,42 @@ +var createPartial = require('../internal/createPartial'); + +/** Used to compose bitmasks for wrapper metadata. */ +var PARTIAL_RIGHT_FLAG = 64; + +/** + * This method is like `_.partial` except that partially applied arguments + * are appended to those provided to the new function. + * + * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method does not set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { + * return greeting + ' ' + name; + * }; + * + * var greetFred = _.partialRight(greet, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + * + * // using placeholders + * var sayHelloTo = _.partialRight(greet, 'hello', _); + * sayHelloTo('fred'); + * // => 'hello fred' + */ +var partialRight = createPartial(PARTIAL_RIGHT_FLAG); + +// Assign default placeholders. +partialRight.placeholder = {}; + +module.exports = partialRight; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/rearg.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/rearg.js new file mode 100644 index 0000000..f2bd9c4 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/rearg.js @@ -0,0 +1,40 @@ +var baseFlatten = require('../internal/baseFlatten'), + createWrapper = require('../internal/createWrapper'), + restParam = require('./restParam'); + +/** Used to compose bitmasks for wrapper metadata. */ +var REARG_FLAG = 256; + +/** + * Creates a function that invokes `func` with arguments arranged according + * to the specified indexes where the argument value at the first index is + * provided as the first argument, the argument value at the second index is + * provided as the second argument, and so on. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to rearrange arguments for. + * @param {...(number|number[])} indexes The arranged argument indexes, + * specified as individual indexes or arrays of indexes. + * @returns {Function} Returns the new function. + * @example + * + * var rearged = _.rearg(function(a, b, c) { + * return [a, b, c]; + * }, 2, 0, 1); + * + * rearged('b', 'c', 'a') + * // => ['a', 'b', 'c'] + * + * var map = _.rearg(_.map, [1, 0]); + * map(function(n) { + * return n * 3; + * }, [1, 2, 3]); + * // => [3, 6, 9] + */ +var rearg = restParam(function(func, indexes) { + return createWrapper(func, REARG_FLAG, undefined, undefined, undefined, baseFlatten(indexes)); +}); + +module.exports = rearg; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/restParam.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/restParam.js new file mode 100644 index 0000000..8852286 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/restParam.js @@ -0,0 +1,58 @@ +/** Used as the `TypeError` message for "Functions" methods. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeMax = Math.max; + +/** + * Creates a function that invokes `func` with the `this` binding of the + * created function and arguments from `start` and beyond provided as an array. + * + * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/Web/JavaScript/Reference/Functions/rest_parameters). + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.restParam(function(what, names) { + * return what + ' ' + _.initial(names).join(', ') + + * (_.size(names) > 1 ? ', & ' : '') + _.last(names); + * }); + * + * say('hello', 'fred', 'barney', 'pebbles'); + * // => 'hello fred, barney, & pebbles' + */ +function restParam(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0); + return function() { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + rest = Array(length); + + while (++index < length) { + rest[index] = args[start + index]; + } + switch (start) { + case 0: return func.call(this, rest); + case 1: return func.call(this, args[0], rest); + case 2: return func.call(this, args[0], args[1], rest); + } + var otherArgs = Array(start + 1); + index = -1; + while (++index < start) { + otherArgs[index] = args[index]; + } + otherArgs[start] = rest; + return func.apply(this, otherArgs); + }; +} + +module.exports = restParam; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/spread.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/spread.js new file mode 100644 index 0000000..780f504 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/spread.js @@ -0,0 +1,44 @@ +/** Used as the `TypeError` message for "Functions" methods. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/** + * Creates a function that invokes `func` with the `this` binding of the created + * function and an array of arguments much like [`Function#apply`](https://es5.github.io/#x15.3.4.3). + * + * **Note:** This method is based on the [spread operator](https://developer.mozilla.org/Web/JavaScript/Reference/Operators/Spread_operator). + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to spread arguments over. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.spread(function(who, what) { + * return who + ' says ' + what; + * }); + * + * say(['fred', 'hello']); + * // => 'fred says hello' + * + * // with a Promise + * var numbers = Promise.all([ + * Promise.resolve(40), + * Promise.resolve(36) + * ]); + * + * numbers.then(_.spread(function(x, y) { + * return x + y; + * })); + * // => a Promise of 76 + */ +function spread(func) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return function(array) { + return func.apply(this, array); + }; +} + +module.exports = spread; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/throttle.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/throttle.js new file mode 100644 index 0000000..1dd00ea --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/throttle.js @@ -0,0 +1,62 @@ +var debounce = require('./debounce'), + isObject = require('../lang/isObject'); + +/** Used as the `TypeError` message for "Functions" methods. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/** + * Creates a throttled function that only invokes `func` at most once per + * every `wait` milliseconds. The throttled function comes with a `cancel` + * method to cancel delayed invocations. Provide an options object to indicate + * that `func` should be invoked on the leading and/or trailing edge of the + * `wait` timeout. Subsequent calls to the throttled function return the + * result of the last `func` call. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked + * on the trailing edge of the timeout only if the the throttled function is + * invoked more than once during the `wait` timeout. + * + * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) + * for details over the differences between `_.throttle` and `_.debounce`. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to throttle. + * @param {number} [wait=0] The number of milliseconds to throttle invocations to. + * @param {Object} [options] The options object. + * @param {boolean} [options.leading=true] Specify invoking on the leading + * edge of the timeout. + * @param {boolean} [options.trailing=true] Specify invoking on the trailing + * edge of the timeout. + * @returns {Function} Returns the new throttled function. + * @example + * + * // avoid excessively updating the position while scrolling + * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); + * + * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes + * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, { + * 'trailing': false + * })); + * + * // cancel a trailing throttled call + * jQuery(window).on('popstate', throttled.cancel); + */ +function throttle(func, wait, options) { + var leading = true, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (options === false) { + leading = false; + } else if (isObject(options)) { + leading = 'leading' in options ? !!options.leading : leading; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + return debounce(func, wait, { 'leading': leading, 'maxWait': +wait, 'trailing': trailing }); +} + +module.exports = throttle; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/wrap.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/wrap.js new file mode 100644 index 0000000..6a33c5e --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/function/wrap.js @@ -0,0 +1,33 @@ +var createWrapper = require('../internal/createWrapper'), + identity = require('../utility/identity'); + +/** Used to compose bitmasks for wrapper metadata. */ +var PARTIAL_FLAG = 32; + +/** + * Creates a function that provides `value` to the wrapper function as its + * first argument. Any additional arguments provided to the function are + * appended to those provided to the wrapper function. The wrapper is invoked + * with the `this` binding of the created function. + * + * @static + * @memberOf _ + * @category Function + * @param {*} value The value to wrap. + * @param {Function} wrapper The wrapper function. + * @returns {Function} Returns the new function. + * @example + * + * var p = _.wrap(_.escape, function(func, text) { + * return '

            ' + func(text) + '

            '; + * }); + * + * p('fred, barney, & pebbles'); + * // => '

            fred, barney, & pebbles

            ' + */ +function wrap(value, wrapper) { + wrapper = wrapper == null ? identity : wrapper; + return createWrapper(wrapper, PARTIAL_FLAG, undefined, [value], []); +} + +module.exports = wrap; diff --git a/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/index.js b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/index.js new file mode 100644 index 0000000..5f17319 --- /dev/null +++ b/node_modules/xml2js/node_modules/xmlbuilder/node_modules/lodash/index.js @@ -0,0 +1,12351 @@ +/** + * @license + * lodash 3.10.1 (Custom Build) + * Build: `lodash modern -d -o ./index.js` + * Copyright 2012-2015 The Dojo Foundation + * Based on Underscore.js 1.8.3 + * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license + */ +;(function() { + + /** Used as a safe reference for `undefined` in pre-ES5 environments. */ + var undefined; + + /** Used as the semantic version number. */ + var VERSION = '3.10.1'; + + /** Used to compose bitmasks for wrapper metadata. */ + var BIND_FLAG = 1, + BIND_KEY_FLAG = 2, + CURRY_BOUND_FLAG = 4, + CURRY_FLAG = 8, + CURRY_RIGHT_FLAG = 16, + PARTIAL_FLAG = 32, + PARTIAL_RIGHT_FLAG = 64, + ARY_FLAG = 128, + REARG_FLAG = 256; + + /** Used as default options for `_.trunc`. */ + var DEFAULT_TRUNC_LENGTH = 30, + DEFAULT_TRUNC_OMISSION = '...'; + + /** Used to detect when a function becomes hot. */ + var HOT_COUNT = 150, + HOT_SPAN = 16; + + /** Used as the size to enable large array optimizations. */ + var LARGE_ARRAY_SIZE = 200; + + /** Used to indicate the type of lazy iteratees. */ + var LAZY_FILTER_FLAG = 1, + LAZY_MAP_FLAG = 2; + + /** Used as the `TypeError` message for "Functions" methods. */ + var FUNC_ERROR_TEXT = 'Expected a function'; + + /** Used as the internal argument placeholder. */ + var PLACEHOLDER = '__lodash_placeholder__'; + + /** `Object#toString` result references. */ + var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + errorTag = '[object Error]', + funcTag = '[object Function]', + mapTag = '[object Map]', + numberTag = '[object Number]', + objectTag = '[object Object]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + weakMapTag = '[object WeakMap]'; + + var arrayBufferTag = '[object ArrayBuffer]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + + /** Used to match empty string literals in compiled template source. */ + var reEmptyStringLeading = /\b__p \+= '';/g, + reEmptyStringMiddle = /\b(__p \+=) '' \+/g, + reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + + /** Used to match HTML entities and HTML characters. */ + var reEscapedHtml = /&(?:amp|lt|gt|quot|#39|#96);/g, + reUnescapedHtml = /[&<>"'`]/g, + reHasEscapedHtml = RegExp(reEscapedHtml.source), + reHasUnescapedHtml = RegExp(reUnescapedHtml.source); + + /** Used to match template delimiters. */ + var reEscape = /<%-([\s\S]+?)%>/g, + reEvaluate = /<%([\s\S]+?)%>/g, + reInterpolate = /<%=([\s\S]+?)%>/g; + + /** Used to match property names within property paths. */ + var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/, + reIsPlainProp = /^\w*$/, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g; + + /** + * Used to match `RegExp` [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns) + * and those outlined by [`EscapeRegExpPattern`](http://ecma-international.org/ecma-262/6.0/#sec-escaperegexppattern). + */ + var reRegExpChars = /^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g, + reHasRegExpChars = RegExp(reRegExpChars.source); + + /** Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). */ + var reComboMark = /[\u0300-\u036f\ufe20-\ufe23]/g; + + /** Used to match backslashes in property paths. */ + var reEscapeChar = /\\(\\)?/g; + + /** Used to match [ES template delimiters](http://ecma-international.org/ecma-262/6.0/#sec-template-literal-lexical-components). */ + var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; + + /** Used to match `RegExp` flags from their coerced string values. */ + var reFlags = /\w*$/; + + /** Used to detect hexadecimal string values. */ + var reHasHexPrefix = /^0[xX]/; + + /** Used to detect host constructors (Safari > 5). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used to detect unsigned integer values. */ + var reIsUint = /^\d+$/; + + /** Used to match latin-1 supplementary letters (excluding mathematical operators). */ + var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g; + + /** Used to ensure capturing order of template delimiters. */ + var reNoMatch = /($^)/; + + /** Used to match unescaped characters in compiled string literals. */ + var reUnescapedString = /['\n\r\u2028\u2029\\]/g; + + /** Used to match words to create compound words. */ + var reWords = (function() { + var upper = '[A-Z\\xc0-\\xd6\\xd8-\\xde]', + lower = '[a-z\\xdf-\\xf6\\xf8-\\xff]+'; + + return RegExp(upper + '+(?=' + upper + lower + ')|' + upper + '?' + lower + '|' + upper + '+|[0-9]+', 'g'); + }()); + + /** Used to assign default `context` object properties. */ + var contextProps = [ + 'Array', 'ArrayBuffer', 'Date', 'Error', 'Float32Array', 'Float64Array', + 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Math', 'Number', + 'Object', 'RegExp', 'Set', 'String', '_', 'clearTimeout', 'isFinite', + 'parseFloat', 'parseInt', 'setTimeout', 'TypeError', 'Uint8Array', + 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap' + ]; + + /** Used to make template sourceURLs easier to identify. */ + var templateCounter = -1; + + /** Used to identify `toStringTag` values of typed arrays. */ + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = + typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = + typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = + typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = + typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = + typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = + typedArrayTags[dateTag] = typedArrayTags[errorTag] = + typedArrayTags[funcTag] = typedArrayTags[mapTag] = + typedArrayTags[numberTag] = typedArrayTags[objectTag] = + typedArrayTags[regexpTag] = typedArrayTags[setTag] = + typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; + + /** Used to identify `toStringTag` values supported by `_.clone`. */ + var cloneableTags = {}; + cloneableTags[argsTag] = cloneableTags[arrayTag] = + cloneableTags[arrayBufferTag] = cloneableTags[boolTag] = + cloneableTags[dateTag] = cloneableTags[float32Tag] = + cloneableTags[float64Tag] = cloneableTags[int8Tag] = + cloneableTags[int16Tag] = cloneableTags[int32Tag] = + cloneableTags[numberTag] = cloneableTags[objectTag] = + cloneableTags[regexpTag] = cloneableTags[stringTag] = + cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = + cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; + cloneableTags[errorTag] = cloneableTags[funcTag] = + cloneableTags[mapTag] = cloneableTags[setTag] = + cloneableTags[weakMapTag] = false; + + /** Used to map latin-1 supplementary letters to basic latin letters. */ + var deburredLetters = { + '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', + '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', + '\xc7': 'C', '\xe7': 'c', + '\xd0': 'D', '\xf0': 'd', + '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', + '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', + '\xcC': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', + '\xeC': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', + '\xd1': 'N', '\xf1': 'n', + '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', + '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', + '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', + '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', + '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', + '\xc6': 'Ae', '\xe6': 'ae', + '\xde': 'Th', '\xfe': 'th', + '\xdf': 'ss' + }; + + /** Used to map characters to HTML entities. */ + var htmlEscapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`' + }; + + /** Used to map HTML entities to characters. */ + var htmlUnescapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'", + '`': '`' + }; + + /** Used to determine if values are of the language type `Object`. */ + var objectTypes = { + 'function': true, + 'object': true + }; + + /** Used to escape characters for inclusion in compiled regexes. */ + var regexpEscapes = { + '0': 'x30', '1': 'x31', '2': 'x32', '3': 'x33', '4': 'x34', + '5': 'x35', '6': 'x36', '7': 'x37', '8': 'x38', '9': 'x39', + 'A': 'x41', 'B': 'x42', 'C': 'x43', 'D': 'x44', 'E': 'x45', 'F': 'x46', + 'a': 'x61', 'b': 'x62', 'c': 'x63', 'd': 'x64', 'e': 'x65', 'f': 'x66', + 'n': 'x6e', 'r': 'x72', 't': 'x74', 'u': 'x75', 'v': 'x76', 'x': 'x78' + }; + + /** Used to escape characters for inclusion in compiled string literals. */ + var stringEscapes = { + '\\': '\\', + "'": "'", + '\n': 'n', + '\r': 'r', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + /** Detect free variable `exports`. */ + var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; + + /** Detect free variable `module`. */ + var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global; + + /** Detect free variable `self`. */ + var freeSelf = objectTypes[typeof self] && self && self.Object && self; + + /** Detect free variable `window`. */ + var freeWindow = objectTypes[typeof window] && window && window.Object && window; + + /** Detect the popular CommonJS extension `module.exports`. */ + var moduleExports = freeModule && freeModule.exports === freeExports && freeExports; + + /** + * Used as a reference to the global object. + * + * The `this` value is used if it's the global object to avoid Greasemonkey's + * restricted `window` object, otherwise the `window` object is used. + */ + var root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this; + + /*--------------------------------------------------------------------------*/ + + /** + * The base implementation of `compareAscending` which compares values and + * sorts them in ascending order without guaranteeing a stable sort. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {number} Returns the sort order indicator for `value`. + */ + function baseCompareAscending(value, other) { + if (value !== other) { + var valIsNull = value === null, + valIsUndef = value === undefined, + valIsReflexive = value === value; + + var othIsNull = other === null, + othIsUndef = other === undefined, + othIsReflexive = other === other; + + if ((value > other && !othIsNull) || !valIsReflexive || + (valIsNull && !othIsUndef && othIsReflexive) || + (valIsUndef && othIsReflexive)) { + return 1; + } + if ((value < other && !valIsNull) || !othIsReflexive || + (othIsNull && !valIsUndef && valIsReflexive) || + (othIsUndef && valIsReflexive)) { + return -1; + } + } + return 0; + } + + /** + * The base implementation of `_.findIndex` and `_.findLastIndex` without + * support for callback shorthands and `this` binding. + * + * @private + * @param {Array} array The array to search. + * @param {Function} predicate The function invoked per iteration. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseFindIndex(array, predicate, fromRight) { + var length = array.length, + index = fromRight ? length : -1; + + while ((fromRight ? index-- : ++index < length)) { + if (predicate(array[index], index, array)) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.indexOf` without support for binary searches. + * + * @private + * @param {Array} array The array to search. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseIndexOf(array, value, fromIndex) { + if (value !== value) { + return indexOfNaN(array, fromIndex); + } + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.isFunction` without support for environments + * with incorrect `typeof` results. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + */ + function baseIsFunction(value) { + // Avoid a Chakra JIT bug in compatibility modes of IE 11. + // See https://github.com/jashkenas/underscore/issues/1621 for more details. + return typeof value == 'function' || false; + } + + /** + * Converts `value` to a string if it's not one. An empty string is returned + * for `null` or `undefined` values. + * + * @private + * @param {*} value The value to process. + * @returns {string} Returns the string. + */ + function baseToString(value) { + return value == null ? '' : (value + ''); + } + + /** + * Used by `_.trim` and `_.trimLeft` to get the index of the first character + * of `string` that is not found in `chars`. + * + * @private + * @param {string} string The string to inspect. + * @param {string} chars The characters to find. + * @returns {number} Returns the index of the first character not found in `chars`. + */ + function charsLeftIndex(string, chars) { + var index = -1, + length = string.length; + + while (++index < length && chars.indexOf(string.charAt(index)) > -1) {} + return index; + } + + /** + * Used by `_.trim` and `_.trimRight` to get the index of the last character + * of `string` that is not found in `chars`. + * + * @private + * @param {string} string The string to inspect. + * @param {string} chars The characters to find. + * @returns {number} Returns the index of the last character not found in `chars`. + */ + function charsRightIndex(string, chars) { + var index = string.length; + + while (index-- && chars.indexOf(string.charAt(index)) > -1) {} + return index; + } + + /** + * Used by `_.sortBy` to compare transformed elements of a collection and stable + * sort them in ascending order. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @returns {number} Returns the sort order indicator for `object`. + */ + function compareAscending(object, other) { + return baseCompareAscending(object.criteria, other.criteria) || (object.index - other.index); + } + + /** + * Used by `_.sortByOrder` to compare multiple properties of a value to another + * and stable sort them. + * + * If `orders` is unspecified, all valuess are sorted in ascending order. Otherwise, + * a value is sorted in ascending order if its corresponding order is "asc", and + * descending if "desc". + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {boolean[]} orders The order to sort by for each property. + * @returns {number} Returns the sort order indicator for `object`. + */ + function compareMultiple(object, other, orders) { + var index = -1, + objCriteria = object.criteria, + othCriteria = other.criteria, + length = objCriteria.length, + ordersLength = orders.length; + + while (++index < length) { + var result = baseCompareAscending(objCriteria[index], othCriteria[index]); + if (result) { + if (index >= ordersLength) { + return result; + } + var order = orders[index]; + return result * ((order === 'asc' || order === true) ? 1 : -1); + } + } + // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications + // that causes it, under certain circumstances, to provide the same value for + // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 + // for more details. + // + // This also ensures a stable sort in V8 and other engines. + // See https://code.google.com/p/v8/issues/detail?id=90 for more details. + return object.index - other.index; + } + + /** + * Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters. + * + * @private + * @param {string} letter The matched letter to deburr. + * @returns {string} Returns the deburred letter. + */ + function deburrLetter(letter) { + return deburredLetters[letter]; + } + + /** + * Used by `_.escape` to convert characters to HTML entities. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */ + function escapeHtmlChar(chr) { + return htmlEscapes[chr]; + } + + /** + * Used by `_.escapeRegExp` to escape characters for inclusion in compiled regexes. + * + * @private + * @param {string} chr The matched character to escape. + * @param {string} leadingChar The capture group for a leading character. + * @param {string} whitespaceChar The capture group for a whitespace character. + * @returns {string} Returns the escaped character. + */ + function escapeRegExpChar(chr, leadingChar, whitespaceChar) { + if (leadingChar) { + chr = regexpEscapes[chr]; + } else if (whitespaceChar) { + chr = stringEscapes[chr]; + } + return '\\' + chr; + } + + /** + * Used by `_.template` to escape characters for inclusion in compiled string literals. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */ + function escapeStringChar(chr) { + return '\\' + stringEscapes[chr]; + } + + /** + * Gets the index at which the first occurrence of `NaN` is found in `array`. + * + * @private + * @param {Array} array The array to search. + * @param {number} fromIndex The index to search from. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched `NaN`, else `-1`. + */ + function indexOfNaN(array, fromIndex, fromRight) { + var length = array.length, + index = fromIndex + (fromRight ? 0 : -1); + + while ((fromRight ? index-- : ++index < length)) { + var other = array[index]; + if (other !== other) { + return index; + } + } + return -1; + } + + /** + * Checks if `value` is object-like. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + */ + function isObjectLike(value) { + return !!value && typeof value == 'object'; + } + + /** + * Used by `trimmedLeftIndex` and `trimmedRightIndex` to determine if a + * character code is whitespace. + * + * @private + * @param {number} charCode The character code to inspect. + * @returns {boolean} Returns `true` if `charCode` is whitespace, else `false`. + */ + function isSpace(charCode) { + return ((charCode <= 160 && (charCode >= 9 && charCode <= 13) || charCode == 32 || charCode == 160) || charCode == 5760 || charCode == 6158 || + (charCode >= 8192 && (charCode <= 8202 || charCode == 8232 || charCode == 8233 || charCode == 8239 || charCode == 8287 || charCode == 12288 || charCode == 65279))); + } + + /** + * Replaces all `placeholder` elements in `array` with an internal placeholder + * and returns an array of their indexes. + * + * @private + * @param {Array} array The array to modify. + * @param {*} placeholder The placeholder to replace. + * @returns {Array} Returns the new array of placeholder indexes. + */ + function replaceHolders(array, placeholder) { + var index = -1, + length = array.length, + resIndex = -1, + result = []; + + while (++index < length) { + if (array[index] === placeholder) { + array[index] = PLACEHOLDER; + result[++resIndex] = index; + } + } + return result; + } + + /** + * An implementation of `_.uniq` optimized for sorted arrays without support + * for callback shorthands and `this` binding. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The function invoked per iteration. + * @returns {Array} Returns the new duplicate-value-free array. + */ + function sortedUniq(array, iteratee) { + var seen, + index = -1, + length = array.length, + resIndex = -1, + result = []; + + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value, index, array) : value; + + if (!index || seen !== computed) { + seen = computed; + result[++resIndex] = value; + } + } + return result; + } + + /** + * Used by `_.trim` and `_.trimLeft` to get the index of the first non-whitespace + * character of `string`. + * + * @private + * @param {string} string The string to inspect. + * @returns {number} Returns the index of the first non-whitespace character. + */ + function trimmedLeftIndex(string) { + var index = -1, + length = string.length; + + while (++index < length && isSpace(string.charCodeAt(index))) {} + return index; + } + + /** + * Used by `_.trim` and `_.trimRight` to get the index of the last non-whitespace + * character of `string`. + * + * @private + * @param {string} string The string to inspect. + * @returns {number} Returns the index of the last non-whitespace character. + */ + function trimmedRightIndex(string) { + var index = string.length; + + while (index-- && isSpace(string.charCodeAt(index))) {} + return index; + } + + /** + * Used by `_.unescape` to convert HTML entities to characters. + * + * @private + * @param {string} chr The matched character to unescape. + * @returns {string} Returns the unescaped character. + */ + function unescapeHtmlChar(chr) { + return htmlUnescapes[chr]; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Create a new pristine `lodash` function using the given `context` object. + * + * @static + * @memberOf _ + * @category Utility + * @param {Object} [context=root] The context object. + * @returns {Function} Returns a new `lodash` function. + * @example + * + * _.mixin({ 'foo': _.constant('foo') }); + * + * var lodash = _.runInContext(); + * lodash.mixin({ 'bar': lodash.constant('bar') }); + * + * _.isFunction(_.foo); + * // => true + * _.isFunction(_.bar); + * // => false + * + * lodash.isFunction(lodash.foo); + * // => false + * lodash.isFunction(lodash.bar); + * // => true + * + * // using `context` to mock `Date#getTime` use in `_.now` + * var mock = _.runInContext({ + * 'Date': function() { + * return { 'getTime': getTimeMock }; + * } + * }); + * + * // or creating a suped-up `defer` in Node.js + * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer; + */ + function runInContext(context) { + // Avoid issues with some ES3 environments that attempt to use values, named + // after built-in constructors like `Object`, for the creation of literals. + // ES5 clears this up by stating that literals must use built-in constructors. + // See https://es5.github.io/#x11.1.5 for more details. + context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root; + + /** Native constructor references. */ + var Array = context.Array, + Date = context.Date, + Error = context.Error, + Function = context.Function, + Math = context.Math, + Number = context.Number, + Object = context.Object, + RegExp = context.RegExp, + String = context.String, + TypeError = context.TypeError; + + /** Used for native method references. */ + var arrayProto = Array.prototype, + objectProto = Object.prototype, + stringProto = String.prototype; + + /** Used to resolve the decompiled source of functions. */ + var fnToString = Function.prototype.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Used to generate unique IDs. */ + var idCounter = 0; + + /** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ + var objToString = objectProto.toString; + + /** Used to restore the original `_` reference in `_.noConflict`. */ + var oldDash = root._; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** Native method references. */ + var ArrayBuffer = context.ArrayBuffer, + clearTimeout = context.clearTimeout, + parseFloat = context.parseFloat, + pow = Math.pow, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + Set = getNative(context, 'Set'), + setTimeout = context.setTimeout, + splice = arrayProto.splice, + Uint8Array = context.Uint8Array, + WeakMap = getNative(context, 'WeakMap'); + + /* Native method references for those with the same name as other `lodash` methods. */ + var nativeCeil = Math.ceil, + nativeCreate = getNative(Object, 'create'), + nativeFloor = Math.floor, + nativeIsArray = getNative(Array, 'isArray'), + nativeIsFinite = context.isFinite, + nativeKeys = getNative(Object, 'keys'), + nativeMax = Math.max, + nativeMin = Math.min, + nativeNow = getNative(Date, 'now'), + nativeParseInt = context.parseInt, + nativeRandom = Math.random; + + /** Used as references for `-Infinity` and `Infinity`. */ + var NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY, + POSITIVE_INFINITY = Number.POSITIVE_INFINITY; + + /** Used as references for the maximum length and index of an array. */ + var MAX_ARRAY_LENGTH = 4294967295, + MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, + HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; + + /** + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) + * of an array-like value. + */ + var MAX_SAFE_INTEGER = 9007199254740991; + + /** Used to store function metadata. */ + var metaMap = WeakMap && new WeakMap; + + /** Used to lookup unminified function names. */ + var realNames = {}; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` object which wraps `value` to enable implicit chaining. + * Methods that operate on and return arrays, collections, and functions can + * be chained together. Methods that retrieve a single value or may return a + * primitive value will automatically end the chain returning the unwrapped + * value. Explicit chaining may be enabled using `_.chain`. The execution of + * chained methods is lazy, that is, execution is deferred until `_#value` + * is implicitly or explicitly called. + * + * Lazy evaluation allows several methods to support shortcut fusion. Shortcut + * fusion is an optimization strategy which merge iteratee calls; this can help + * to avoid the creation of intermediate data structures and greatly reduce the + * number of iteratee executions. + * + * Chaining is supported in custom builds as long as the `_#value` method is + * directly or indirectly included in the build. + * + * In addition to lodash methods, wrappers have `Array` and `String` methods. + * + * The wrapper `Array` methods are: + * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, + * `splice`, and `unshift` + * + * The wrapper `String` methods are: + * `replace` and `split` + * + * The wrapper methods that support shortcut fusion are: + * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`, + * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`, + * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`, + * and `where` + * + * The chainable wrapper methods are: + * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`, + * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`, + * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defaultsDeep`, + * `defer`, `delay`, `difference`, `drop`, `dropRight`, `dropRightWhile`, + * `dropWhile`, `fill`, `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, + * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, + * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, + * `invoke`, `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, + * `matchesProperty`, `memoize`, `merge`, `method`, `methodOf`, `mixin`, + * `modArgs`, `negate`, `omit`, `once`, `pairs`, `partial`, `partialRight`, + * `partition`, `pick`, `plant`, `pluck`, `property`, `propertyOf`, `pull`, + * `pullAt`, `push`, `range`, `rearg`, `reject`, `remove`, `rest`, `restParam`, + * `reverse`, `set`, `shuffle`, `slice`, `sort`, `sortBy`, `sortByAll`, + * `sortByOrder`, `splice`, `spread`, `take`, `takeRight`, `takeRightWhile`, + * `takeWhile`, `tap`, `throttle`, `thru`, `times`, `toArray`, `toPlainObject`, + * `transform`, `union`, `uniq`, `unshift`, `unzip`, `unzipWith`, `values`, + * `valuesIn`, `where`, `without`, `wrap`, `xor`, `zip`, `zipObject`, `zipWith` + * + * The wrapper methods that are **not** chainable by default are: + * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clone`, `cloneDeep`, + * `deburr`, `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, + * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, + * `floor`, `get`, `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`, + * `inRange`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, + * `isEmpty`, `isEqual`, `isError`, `isFinite` `isFunction`, `isMatch`, + * `isNative`, `isNaN`, `isNull`, `isNumber`, `isObject`, `isPlainObject`, + * `isRegExp`, `isString`, `isUndefined`, `isTypedArray`, `join`, `kebabCase`, + * `last`, `lastIndexOf`, `lt`, `lte`, `max`, `min`, `noConflict`, `noop`, + * `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`, `random`, `reduce`, + * `reduceRight`, `repeat`, `result`, `round`, `runInContext`, `shift`, `size`, + * `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`, `startCase`, + * `startsWith`, `sum`, `template`, `trim`, `trimLeft`, `trimRight`, `trunc`, + * `unescape`, `uniqueId`, `value`, and `words` + * + * The wrapper method `sample` will return a wrapped value when `n` is provided, + * otherwise an unwrapped value is returned. + * + * @name _ + * @constructor + * @category Chain + * @param {*} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var wrapped = _([1, 2, 3]); + * + * // returns an unwrapped value + * wrapped.reduce(function(total, n) { + * return total + n; + * }); + * // => 6 + * + * // returns a wrapped value + * var squares = wrapped.map(function(n) { + * return n * n; + * }); + * + * _.isArray(squares); + * // => false + * + * _.isArray(squares.value()); + * // => true + */ + function lodash(value) { + if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { + if (value instanceof LodashWrapper) { + return value; + } + if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) { + return wrapperClone(value); + } + } + return new LodashWrapper(value); + } + + /** + * The function whose prototype all chaining wrappers inherit from. + * + * @private + */ + function baseLodash() { + // No operation performed. + } + + /** + * The base constructor for creating `lodash` wrapper objects. + * + * @private + * @param {*} value The value to wrap. + * @param {boolean} [chainAll] Enable chaining for all wrapper methods. + * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value. + */ + function LodashWrapper(value, chainAll, actions) { + this.__wrapped__ = value; + this.__actions__ = actions || []; + this.__chain__ = !!chainAll; + } + + /** + * An object environment feature flags. + * + * @static + * @memberOf _ + * @type Object + */ + var support = lodash.support = {}; + + /** + * By default, the template delimiters used by lodash are like those in + * embedded Ruby (ERB). Change the following template settings to use + * alternative delimiters. + * + * @static + * @memberOf _ + * @type Object + */ + lodash.templateSettings = { + + /** + * Used to detect `data` property values to be HTML-escaped. + * + * @memberOf _.templateSettings + * @type RegExp + */ + 'escape': reEscape, + + /** + * Used to detect code to be evaluated. + * + * @memberOf _.templateSettings + * @type RegExp + */ + 'evaluate': reEvaluate, + + /** + * Used to detect `data` property values to inject. + * + * @memberOf _.templateSettings + * @type RegExp + */ + 'interpolate': reInterpolate, + + /** + * Used to reference the data object in the template text. + * + * @memberOf _.templateSettings + * @type string + */ + 'variable': '', + + /** + * Used to import variables into the compiled template. + * + * @memberOf _.templateSettings + * @type Object + */ + 'imports': { + + /** + * A reference to the `lodash` function. + * + * @memberOf _.templateSettings.imports + * @type Function + */ + '_': lodash + } + }; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. + * + * @private + * @param {*} value The value to wrap. + */ + function LazyWrapper(value) { + this.__wrapped__ = value; + this.__actions__ = []; + this.__dir__ = 1; + this.__filtered__ = false; + this.__iteratees__ = []; + this.__takeCount__ = POSITIVE_INFINITY; + this.__views__ = []; + } + + /** + * Creates a clone of the lazy wrapper object. + * + * @private + * @name clone + * @memberOf LazyWrapper + * @returns {Object} Returns the cloned `LazyWrapper` object. + */ + function lazyClone() { + var result = new LazyWrapper(this.__wrapped__); + result.__actions__ = arrayCopy(this.__actions__); + result.__dir__ = this.__dir__; + result.__filtered__ = this.__filtered__; + result.__iteratees__ = arrayCopy(this.__iteratees__); + result.__takeCount__ = this.__takeCount__; + result.__views__ = arrayCopy(this.__views__); + return result; + } + + /** + * Reverses the direction of lazy iteration. + * + * @private + * @name reverse + * @memberOf LazyWrapper + * @returns {Object} Returns the new reversed `LazyWrapper` object. + */ + function lazyReverse() { + if (this.__filtered__) { + var result = new LazyWrapper(this); + result.__dir__ = -1; + result.__filtered__ = true; + } else { + result = this.clone(); + result.__dir__ *= -1; + } + return result; + } + + /** + * Extracts the unwrapped value from its lazy wrapper. + * + * @private + * @name value + * @memberOf LazyWrapper + * @returns {*} Returns the unwrapped value. + */ + function lazyValue() { + var array = this.__wrapped__.value(), + dir = this.__dir__, + isArr = isArray(array), + isRight = dir < 0, + arrLength = isArr ? array.length : 0, + view = getView(0, arrLength, this.__views__), + start = view.start, + end = view.end, + length = end - start, + index = isRight ? end : (start - 1), + iteratees = this.__iteratees__, + iterLength = iteratees.length, + resIndex = 0, + takeCount = nativeMin(length, this.__takeCount__); + + if (!isArr || arrLength < LARGE_ARRAY_SIZE || (arrLength == length && takeCount == length)) { + return baseWrapperValue((isRight && isArr) ? array.reverse() : array, this.__actions__); + } + var result = []; + + outer: + while (length-- && resIndex < takeCount) { + index += dir; + + var iterIndex = -1, + value = array[index]; + + while (++iterIndex < iterLength) { + var data = iteratees[iterIndex], + iteratee = data.iteratee, + type = data.type, + computed = iteratee(value); + + if (type == LAZY_MAP_FLAG) { + value = computed; + } else if (!computed) { + if (type == LAZY_FILTER_FLAG) { + continue outer; + } else { + break outer; + } + } + } + result[resIndex++] = value; + } + return result; + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates a cache object to store key/value pairs. + * + * @private + * @static + * @name Cache + * @memberOf _.memoize + */ + function MapCache() { + this.__data__ = {}; + } + + /** + * Removes `key` and its value from the cache. + * + * @private + * @name delete + * @memberOf _.memoize.Cache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed successfully, else `false`. + */ + function mapDelete(key) { + return this.has(key) && delete this.__data__[key]; + } + + /** + * Gets the cached value for `key`. + * + * @private + * @name get + * @memberOf _.memoize.Cache + * @param {string} key The key of the value to get. + * @returns {*} Returns the cached value. + */ + function mapGet(key) { + return key == '__proto__' ? undefined : this.__data__[key]; + } + + /** + * Checks if a cached value for `key` exists. + * + * @private + * @name has + * @memberOf _.memoize.Cache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function mapHas(key) { + return key != '__proto__' && hasOwnProperty.call(this.__data__, key); + } + + /** + * Sets `value` to `key` of the cache. + * + * @private + * @name set + * @memberOf _.memoize.Cache + * @param {string} key The key of the value to cache. + * @param {*} value The value to cache. + * @returns {Object} Returns the cache object. + */ + function mapSet(key, value) { + if (key != '__proto__') { + this.__data__[key] = value; + } + return this; + } + + /*------------------------------------------------------------------------*/ + + /** + * + * Creates a cache object to store unique values. + * + * @private + * @param {Array} [values] The values to cache. + */ + function SetCache(values) { + var length = values ? values.length : 0; + + this.data = { 'hash': nativeCreate(null), 'set': new Set }; + while (length--) { + this.push(values[length]); + } + } + + /** + * Checks if `value` is in `cache` mimicking the return signature of + * `_.indexOf` by returning `0` if the value is found, else `-1`. + * + * @private + * @param {Object} cache The cache to search. + * @param {*} value The value to search for. + * @returns {number} Returns `0` if `value` is found, else `-1`. + */ + function cacheIndexOf(cache, value) { + var data = cache.data, + result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value]; + + return result ? 0 : -1; + } + + /** + * Adds `value` to the cache. + * + * @private + * @name push + * @memberOf SetCache + * @param {*} value The value to cache. + */ + function cachePush(value) { + var data = this.data; + if (typeof value == 'string' || isObject(value)) { + data.set.add(value); + } else { + data.hash[value] = true; + } + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates a new array joining `array` with `other`. + * + * @private + * @param {Array} array The array to join. + * @param {Array} other The other array to join. + * @returns {Array} Returns the new concatenated array. + */ + function arrayConcat(array, other) { + var index = -1, + length = array.length, + othIndex = -1, + othLength = other.length, + result = Array(length + othLength); + + while (++index < length) { + result[index] = array[index]; + } + while (++othIndex < othLength) { + result[index++] = other[othIndex]; + } + return result; + } + + /** + * Copies the values of `source` to `array`. + * + * @private + * @param {Array} source The array to copy values from. + * @param {Array} [array=[]] The array to copy values to. + * @returns {Array} Returns `array`. + */ + function arrayCopy(source, array) { + var index = -1, + length = source.length; + + array || (array = Array(length)); + while (++index < length) { + array[index] = source[index]; + } + return array; + } + + /** + * A specialized version of `_.forEach` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEach(array, iteratee) { + var index = -1, + length = array.length; + + while (++index < length) { + if (iteratee(array[index], index, array) === false) { + break; + } + } + return array; + } + + /** + * A specialized version of `_.forEachRight` for arrays without support for + * callback shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEachRight(array, iteratee) { + var length = array.length; + + while (length--) { + if (iteratee(array[length], length, array) === false) { + break; + } + } + return array; + } + + /** + * A specialized version of `_.every` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + */ + function arrayEvery(array, predicate) { + var index = -1, + length = array.length; + + while (++index < length) { + if (!predicate(array[index], index, array)) { + return false; + } + } + return true; + } + + /** + * A specialized version of `baseExtremum` for arrays which invokes `iteratee` + * with one argument: (value). + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} comparator The function used to compare values. + * @param {*} exValue The initial extremum value. + * @returns {*} Returns the extremum value. + */ + function arrayExtremum(array, iteratee, comparator, exValue) { + var index = -1, + length = array.length, + computed = exValue, + result = computed; + + while (++index < length) { + var value = array[index], + current = +iteratee(value); + + if (comparator(current, computed)) { + computed = current; + result = value; + } + } + return result; + } + + /** + * A specialized version of `_.filter` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function arrayFilter(array, predicate) { + var index = -1, + length = array.length, + resIndex = -1, + result = []; + + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result[++resIndex] = value; + } + } + return result; + } + + /** + * A specialized version of `_.map` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ + function arrayMap(array, iteratee) { + var index = -1, + length = array.length, + result = Array(length); + + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; + } + + /** + * Appends the elements of `values` to `array`. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to append. + * @returns {Array} Returns `array`. + */ + function arrayPush(array, values) { + var index = -1, + length = values.length, + offset = array.length; + + while (++index < length) { + array[offset + index] = values[index]; + } + return array; + } + + /** + * A specialized version of `_.reduce` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initFromArray] Specify using the first element of `array` + * as the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduce(array, iteratee, accumulator, initFromArray) { + var index = -1, + length = array.length; + + if (initFromArray && length) { + accumulator = array[++index]; + } + while (++index < length) { + accumulator = iteratee(accumulator, array[index], index, array); + } + return accumulator; + } + + /** + * A specialized version of `_.reduceRight` for arrays without support for + * callback shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initFromArray] Specify using the last element of `array` + * as the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduceRight(array, iteratee, accumulator, initFromArray) { + var length = array.length; + if (initFromArray && length) { + accumulator = array[--length]; + } + while (length--) { + accumulator = iteratee(accumulator, array[length], length, array); + } + return accumulator; + } + + /** + * A specialized version of `_.some` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function arraySome(array, predicate) { + var index = -1, + length = array.length; + + while (++index < length) { + if (predicate(array[index], index, array)) { + return true; + } + } + return false; + } + + /** + * A specialized version of `_.sum` for arrays without support for callback + * shorthands and `this` binding.. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {number} Returns the sum. + */ + function arraySum(array, iteratee) { + var length = array.length, + result = 0; + + while (length--) { + result += +iteratee(array[length]) || 0; + } + return result; + } + + /** + * Used by `_.defaults` to customize its `_.assign` use. + * + * @private + * @param {*} objectValue The destination object property value. + * @param {*} sourceValue The source object property value. + * @returns {*} Returns the value to assign to the destination object. + */ + function assignDefaults(objectValue, sourceValue) { + return objectValue === undefined ? sourceValue : objectValue; + } + + /** + * Used by `_.template` to customize its `_.assign` use. + * + * **Note:** This function is like `assignDefaults` except that it ignores + * inherited property values when checking if a property is `undefined`. + * + * @private + * @param {*} objectValue The destination object property value. + * @param {*} sourceValue The source object property value. + * @param {string} key The key associated with the object and source values. + * @param {Object} object The destination object. + * @returns {*} Returns the value to assign to the destination object. + */ + function assignOwnDefaults(objectValue, sourceValue, key, object) { + return (objectValue === undefined || !hasOwnProperty.call(object, key)) + ? sourceValue + : objectValue; + } + + /** + * A specialized version of `_.assign` for customizing assigned values without + * support for argument juggling, multiple sources, and `this` binding `customizer` + * functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {Function} customizer The function to customize assigned values. + * @returns {Object} Returns `object`. + */ + function assignWith(object, source, customizer) { + var index = -1, + props = keys(source), + length = props.length; + + while (++index < length) { + var key = props[index], + value = object[key], + result = customizer(value, source[key], key, object, source); + + if ((result === result ? (result !== value) : (value === value)) || + (value === undefined && !(key in object))) { + object[key] = result; + } + } + return object; + } + + /** + * The base implementation of `_.assign` without support for argument juggling, + * multiple sources, and `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssign(object, source) { + return source == null + ? object + : baseCopy(source, keys(source), object); + } + + /** + * The base implementation of `_.at` without support for string collections + * and individual key arguments. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {number[]|string[]} props The property names or indexes of elements to pick. + * @returns {Array} Returns the new array of picked elements. + */ + function baseAt(collection, props) { + var index = -1, + isNil = collection == null, + isArr = !isNil && isArrayLike(collection), + length = isArr ? collection.length : 0, + propsLength = props.length, + result = Array(propsLength); + + while(++index < propsLength) { + var key = props[index]; + if (isArr) { + result[index] = isIndex(key, length) ? collection[key] : undefined; + } else { + result[index] = isNil ? undefined : collection[key]; + } + } + return result; + } + + /** + * Copies properties of `source` to `object`. + * + * @private + * @param {Object} source The object to copy properties from. + * @param {Array} props The property names to copy. + * @param {Object} [object={}] The object to copy properties to. + * @returns {Object} Returns `object`. + */ + function baseCopy(source, props, object) { + object || (object = {}); + + var index = -1, + length = props.length; + + while (++index < length) { + var key = props[index]; + object[key] = source[key]; + } + return object; + } + + /** + * The base implementation of `_.callback` which supports specifying the + * number of arguments to provide to `func`. + * + * @private + * @param {*} [func=_.identity] The value to convert to a callback. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {number} [argCount] The number of arguments to provide to `func`. + * @returns {Function} Returns the callback. + */ + function baseCallback(func, thisArg, argCount) { + var type = typeof func; + if (type == 'function') { + return thisArg === undefined + ? func + : bindCallback(func, thisArg, argCount); + } + if (func == null) { + return identity; + } + if (type == 'object') { + return baseMatches(func); + } + return thisArg === undefined + ? property(func) + : baseMatchesProperty(func, thisArg); + } + + /** + * The base implementation of `_.clone` without support for argument juggling + * and `this` binding `customizer` functions. + * + * @private + * @param {*} value The value to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @param {Function} [customizer] The function to customize cloning values. + * @param {string} [key] The key of `value`. + * @param {Object} [object] The object `value` belongs to. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates clones with source counterparts. + * @returns {*} Returns the cloned value. + */ + function baseClone(value, isDeep, customizer, key, object, stackA, stackB) { + var result; + if (customizer) { + result = object ? customizer(value, key, object) : customizer(value); + } + if (result !== undefined) { + return result; + } + if (!isObject(value)) { + return value; + } + var isArr = isArray(value); + if (isArr) { + result = initCloneArray(value); + if (!isDeep) { + return arrayCopy(value, result); + } + } else { + var tag = objToString.call(value), + isFunc = tag == funcTag; + + if (tag == objectTag || tag == argsTag || (isFunc && !object)) { + result = initCloneObject(isFunc ? {} : value); + if (!isDeep) { + return baseAssign(result, value); + } + } else { + return cloneableTags[tag] + ? initCloneByTag(value, tag, isDeep) + : (object ? value : {}); + } + } + // Check for circular references and return its corresponding clone. + stackA || (stackA = []); + stackB || (stackB = []); + + var length = stackA.length; + while (length--) { + if (stackA[length] == value) { + return stackB[length]; + } + } + // Add the source value to the stack of traversed objects and associate it with its clone. + stackA.push(value); + stackB.push(result); + + // Recursively populate clone (susceptible to call stack limits). + (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) { + result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB); + }); + return result; + } + + /** + * The base implementation of `_.create` without support for assigning + * properties to the created object. + * + * @private + * @param {Object} prototype The object to inherit from. + * @returns {Object} Returns the new object. + */ + var baseCreate = (function() { + function object() {} + return function(prototype) { + if (isObject(prototype)) { + object.prototype = prototype; + var result = new object; + object.prototype = undefined; + } + return result || {}; + }; + }()); + + /** + * The base implementation of `_.delay` and `_.defer` which accepts an index + * of where to slice the arguments to provide to `func`. + * + * @private + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {Object} args The arguments provide to `func`. + * @returns {number} Returns the timer id. + */ + function baseDelay(func, wait, args) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return setTimeout(function() { func.apply(undefined, args); }, wait); + } + + /** + * The base implementation of `_.difference` which accepts a single array + * of values to exclude. + * + * @private + * @param {Array} array The array to inspect. + * @param {Array} values The values to exclude. + * @returns {Array} Returns the new array of filtered values. + */ + function baseDifference(array, values) { + var length = array ? array.length : 0, + result = []; + + if (!length) { + return result; + } + var index = -1, + indexOf = getIndexOf(), + isCommon = indexOf == baseIndexOf, + cache = (isCommon && values.length >= LARGE_ARRAY_SIZE) ? createCache(values) : null, + valuesLength = values.length; + + if (cache) { + indexOf = cacheIndexOf; + isCommon = false; + values = cache; + } + outer: + while (++index < length) { + var value = array[index]; + + if (isCommon && value === value) { + var valuesIndex = valuesLength; + while (valuesIndex--) { + if (values[valuesIndex] === value) { + continue outer; + } + } + result.push(value); + } + else if (indexOf(values, value, 0) < 0) { + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.forEach` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object|string} Returns `collection`. + */ + var baseEach = createBaseEach(baseForOwn); + + /** + * The base implementation of `_.forEachRight` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object|string} Returns `collection`. + */ + var baseEachRight = createBaseEach(baseForOwnRight, true); + + /** + * The base implementation of `_.every` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false` + */ + function baseEvery(collection, predicate) { + var result = true; + baseEach(collection, function(value, index, collection) { + result = !!predicate(value, index, collection); + return result; + }); + return result; + } + + /** + * Gets the extremum value of `collection` invoking `iteratee` for each value + * in `collection` to generate the criterion by which the value is ranked. + * The `iteratee` is invoked with three arguments: (value, index|key, collection). + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} comparator The function used to compare values. + * @param {*} exValue The initial extremum value. + * @returns {*} Returns the extremum value. + */ + function baseExtremum(collection, iteratee, comparator, exValue) { + var computed = exValue, + result = computed; + + baseEach(collection, function(value, index, collection) { + var current = +iteratee(value, index, collection); + if (comparator(current, computed) || (current === exValue && current === result)) { + computed = current; + result = value; + } + }); + return result; + } + + /** + * The base implementation of `_.fill` without an iteratee call guard. + * + * @private + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + */ + function baseFill(array, value, start, end) { + var length = array.length; + + start = start == null ? 0 : (+start || 0); + if (start < 0) { + start = -start > length ? 0 : (length + start); + } + end = (end === undefined || end > length) ? length : (+end || 0); + if (end < 0) { + end += length; + } + length = start > end ? 0 : (end >>> 0); + start >>>= 0; + + while (start < length) { + array[start++] = value; + } + return array; + } + + /** + * The base implementation of `_.filter` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function baseFilter(collection, predicate) { + var result = []; + baseEach(collection, function(value, index, collection) { + if (predicate(value, index, collection)) { + result.push(value); + } + }); + return result; + } + + /** + * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`, + * without support for callback shorthands and `this` binding, which iterates + * over `collection` using the provided `eachFunc`. + * + * @private + * @param {Array|Object|string} collection The collection to search. + * @param {Function} predicate The function invoked per iteration. + * @param {Function} eachFunc The function to iterate over `collection`. + * @param {boolean} [retKey] Specify returning the key of the found element + * instead of the element itself. + * @returns {*} Returns the found element or its key, else `undefined`. + */ + function baseFind(collection, predicate, eachFunc, retKey) { + var result; + eachFunc(collection, function(value, key, collection) { + if (predicate(value, key, collection)) { + result = retKey ? key : value; + return false; + } + }); + return result; + } + + /** + * The base implementation of `_.flatten` with added support for restricting + * flattening and specifying the start index. + * + * @private + * @param {Array} array The array to flatten. + * @param {boolean} [isDeep] Specify a deep flatten. + * @param {boolean} [isStrict] Restrict flattening to arrays-like objects. + * @param {Array} [result=[]] The initial result value. + * @returns {Array} Returns the new flattened array. + */ + function baseFlatten(array, isDeep, isStrict, result) { + result || (result = []); + + var index = -1, + length = array.length; + + while (++index < length) { + var value = array[index]; + if (isObjectLike(value) && isArrayLike(value) && + (isStrict || isArray(value) || isArguments(value))) { + if (isDeep) { + // Recursively flatten arrays (susceptible to call stack limits). + baseFlatten(value, isDeep, isStrict, result); + } else { + arrayPush(result, value); + } + } else if (!isStrict) { + result[result.length] = value; + } + } + return result; + } + + /** + * The base implementation of `baseForIn` and `baseForOwn` which iterates + * over `object` properties returned by `keysFunc` invoking `iteratee` for + * each property. Iteratee functions may exit iteration early by explicitly + * returning `false`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseFor = createBaseFor(); + + /** + * This function is like `baseFor` except that it iterates over properties + * in the opposite order. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseForRight = createBaseFor(true); + + /** + * The base implementation of `_.forIn` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForIn(object, iteratee) { + return baseFor(object, iteratee, keysIn); + } + + /** + * The base implementation of `_.forOwn` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForOwn(object, iteratee) { + return baseFor(object, iteratee, keys); + } + + /** + * The base implementation of `_.forOwnRight` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForOwnRight(object, iteratee) { + return baseForRight(object, iteratee, keys); + } + + /** + * The base implementation of `_.functions` which creates an array of + * `object` function property names filtered from those provided. + * + * @private + * @param {Object} object The object to inspect. + * @param {Array} props The property names to filter. + * @returns {Array} Returns the new array of filtered property names. + */ + function baseFunctions(object, props) { + var index = -1, + length = props.length, + resIndex = -1, + result = []; + + while (++index < length) { + var key = props[index]; + if (isFunction(object[key])) { + result[++resIndex] = key; + } + } + return result; + } + + /** + * The base implementation of `get` without support for string paths + * and default values. + * + * @private + * @param {Object} object The object to query. + * @param {Array} path The path of the property to get. + * @param {string} [pathKey] The key representation of path. + * @returns {*} Returns the resolved value. + */ + function baseGet(object, path, pathKey) { + if (object == null) { + return; + } + if (pathKey !== undefined && pathKey in toObject(object)) { + path = [pathKey]; + } + var index = 0, + length = path.length; + + while (object != null && index < length) { + object = object[path[index++]]; + } + return (index && index == length) ? object : undefined; + } + + /** + * The base implementation of `_.isEqual` without support for `this` binding + * `customizer` functions. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize comparing values. + * @param {boolean} [isLoose] Specify performing partial comparisons. + * @param {Array} [stackA] Tracks traversed `value` objects. + * @param {Array} [stackB] Tracks traversed `other` objects. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + */ + function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) { + if (value === other) { + return true; + } + if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) { + return value !== value && other !== other; + } + return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB); + } + + /** + * A specialized version of `baseIsEqual` for arrays and objects which performs + * deep comparisons and tracks traversed objects enabling objects with circular + * references to be compared. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} [customizer] The function to customize comparing objects. + * @param {boolean} [isLoose] Specify performing partial comparisons. + * @param {Array} [stackA=[]] Tracks traversed `value` objects. + * @param {Array} [stackB=[]] Tracks traversed `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) { + var objIsArr = isArray(object), + othIsArr = isArray(other), + objTag = arrayTag, + othTag = arrayTag; + + if (!objIsArr) { + objTag = objToString.call(object); + if (objTag == argsTag) { + objTag = objectTag; + } else if (objTag != objectTag) { + objIsArr = isTypedArray(object); + } + } + if (!othIsArr) { + othTag = objToString.call(other); + if (othTag == argsTag) { + othTag = objectTag; + } else if (othTag != objectTag) { + othIsArr = isTypedArray(other); + } + } + var objIsObj = objTag == objectTag, + othIsObj = othTag == objectTag, + isSameTag = objTag == othTag; + + if (isSameTag && !(objIsArr || objIsObj)) { + return equalByTag(object, other, objTag); + } + if (!isLoose) { + var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), + othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); + + if (objIsWrapped || othIsWrapped) { + return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB); + } + } + if (!isSameTag) { + return false; + } + // Assume cyclic values are equal. + // For more information on detecting circular references see https://es5.github.io/#JO. + stackA || (stackA = []); + stackB || (stackB = []); + + var length = stackA.length; + while (length--) { + if (stackA[length] == object) { + return stackB[length] == other; + } + } + // Add `object` and `other` to the stack of traversed objects. + stackA.push(object); + stackB.push(other); + + var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB); + + stackA.pop(); + stackB.pop(); + + return result; + } + + /** + * The base implementation of `_.isMatch` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Object} object The object to inspect. + * @param {Array} matchData The propery names, values, and compare flags to match. + * @param {Function} [customizer] The function to customize comparing objects. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + */ + function baseIsMatch(object, matchData, customizer) { + var index = matchData.length, + length = index, + noCustomizer = !customizer; + + if (object == null) { + return !length; + } + object = toObject(object); + while (index--) { + var data = matchData[index]; + if ((noCustomizer && data[2]) + ? data[1] !== object[data[0]] + : !(data[0] in object) + ) { + return false; + } + } + while (++index < length) { + data = matchData[index]; + var key = data[0], + objValue = object[key], + srcValue = data[1]; + + if (noCustomizer && data[2]) { + if (objValue === undefined && !(key in object)) { + return false; + } + } else { + var result = customizer ? customizer(objValue, srcValue, key) : undefined; + if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, true) : result)) { + return false; + } + } + } + return true; + } + + /** + * The base implementation of `_.map` without support for callback shorthands + * and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ + function baseMap(collection, iteratee) { + var index = -1, + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value, key, collection) { + result[++index] = iteratee(value, key, collection); + }); + return result; + } + + /** + * The base implementation of `_.matches` which does not clone `source`. + * + * @private + * @param {Object} source The object of property values to match. + * @returns {Function} Returns the new function. + */ + function baseMatches(source) { + var matchData = getMatchData(source); + if (matchData.length == 1 && matchData[0][2]) { + var key = matchData[0][0], + value = matchData[0][1]; + + return function(object) { + if (object == null) { + return false; + } + return object[key] === value && (value !== undefined || (key in toObject(object))); + }; + } + return function(object) { + return baseIsMatch(object, matchData); + }; + } + + /** + * The base implementation of `_.matchesProperty` which does not clone `srcValue`. + * + * @private + * @param {string} path The path of the property to get. + * @param {*} srcValue The value to compare. + * @returns {Function} Returns the new function. + */ + function baseMatchesProperty(path, srcValue) { + var isArr = isArray(path), + isCommon = isKey(path) && isStrictComparable(srcValue), + pathKey = (path + ''); + + path = toPath(path); + return function(object) { + if (object == null) { + return false; + } + var key = pathKey; + object = toObject(object); + if ((isArr || !isCommon) && !(key in object)) { + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + if (object == null) { + return false; + } + key = last(path); + object = toObject(object); + } + return object[key] === srcValue + ? (srcValue !== undefined || (key in object)) + : baseIsEqual(srcValue, object[key], undefined, true); + }; + } + + /** + * The base implementation of `_.merge` without support for argument juggling, + * multiple sources, and `this` binding `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {Function} [customizer] The function to customize merged values. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates values with source counterparts. + * @returns {Object} Returns `object`. + */ + function baseMerge(object, source, customizer, stackA, stackB) { + if (!isObject(object)) { + return object; + } + var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)), + props = isSrcArr ? undefined : keys(source); + + arrayEach(props || source, function(srcValue, key) { + if (props) { + key = srcValue; + srcValue = source[key]; + } + if (isObjectLike(srcValue)) { + stackA || (stackA = []); + stackB || (stackB = []); + baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB); + } + else { + var value = object[key], + result = customizer ? customizer(value, srcValue, key, object, source) : undefined, + isCommon = result === undefined; + + if (isCommon) { + result = srcValue; + } + if ((result !== undefined || (isSrcArr && !(key in object))) && + (isCommon || (result === result ? (result !== value) : (value === value)))) { + object[key] = result; + } + } + }); + return object; + } + + /** + * A specialized version of `baseMerge` for arrays and objects which performs + * deep merges and tracks traversed objects enabling objects with circular + * references to be merged. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {string} key The key of the value to merge. + * @param {Function} mergeFunc The function to merge values. + * @param {Function} [customizer] The function to customize merged values. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates values with source counterparts. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) { + var length = stackA.length, + srcValue = source[key]; + + while (length--) { + if (stackA[length] == srcValue) { + object[key] = stackB[length]; + return; + } + } + var value = object[key], + result = customizer ? customizer(value, srcValue, key, object, source) : undefined, + isCommon = result === undefined; + + if (isCommon) { + result = srcValue; + if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) { + result = isArray(value) + ? value + : (isArrayLike(value) ? arrayCopy(value) : []); + } + else if (isPlainObject(srcValue) || isArguments(srcValue)) { + result = isArguments(value) + ? toPlainObject(value) + : (isPlainObject(value) ? value : {}); + } + else { + isCommon = false; + } + } + // Add the source value to the stack of traversed objects and associate + // it with its merged value. + stackA.push(srcValue); + stackB.push(result); + + if (isCommon) { + // Recursively merge objects and arrays (susceptible to call stack limits). + object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB); + } else if (result === result ? (result !== value) : (value === value)) { + object[key] = result; + } + } + + /** + * The base implementation of `_.property` without support for deep paths. + * + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new function. + */ + function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; + } + + /** + * A specialized version of `baseProperty` which supports deep paths. + * + * @private + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new function. + */ + function basePropertyDeep(path) { + var pathKey = (path + ''); + path = toPath(path); + return function(object) { + return baseGet(object, path, pathKey); + }; + } + + /** + * The base implementation of `_.pullAt` without support for individual + * index arguments and capturing the removed elements. + * + * @private + * @param {Array} array The array to modify. + * @param {number[]} indexes The indexes of elements to remove. + * @returns {Array} Returns `array`. + */ + function basePullAt(array, indexes) { + var length = array ? indexes.length : 0; + while (length--) { + var index = indexes[length]; + if (index != previous && isIndex(index)) { + var previous = index; + splice.call(array, index, 1); + } + } + return array; + } + + /** + * The base implementation of `_.random` without support for argument juggling + * and returning floating-point numbers. + * + * @private + * @param {number} min The minimum possible value. + * @param {number} max The maximum possible value. + * @returns {number} Returns the random number. + */ + function baseRandom(min, max) { + return min + nativeFloor(nativeRandom() * (max - min + 1)); + } + + /** + * The base implementation of `_.reduce` and `_.reduceRight` without support + * for callback shorthands and `this` binding, which iterates over `collection` + * using the provided `eachFunc`. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} accumulator The initial value. + * @param {boolean} initFromCollection Specify using the first or last element + * of `collection` as the initial value. + * @param {Function} eachFunc The function to iterate over `collection`. + * @returns {*} Returns the accumulated value. + */ + function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) { + eachFunc(collection, function(value, index, collection) { + accumulator = initFromCollection + ? (initFromCollection = false, value) + : iteratee(accumulator, value, index, collection); + }); + return accumulator; + } + + /** + * The base implementation of `setData` without support for hot loop detection. + * + * @private + * @param {Function} func The function to associate metadata with. + * @param {*} data The metadata. + * @returns {Function} Returns `func`. + */ + var baseSetData = !metaMap ? identity : function(func, data) { + metaMap.set(func, data); + return func; + }; + + /** + * The base implementation of `_.slice` without an iteratee call guard. + * + * @private + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */ + function baseSlice(array, start, end) { + var index = -1, + length = array.length; + + start = start == null ? 0 : (+start || 0); + if (start < 0) { + start = -start > length ? 0 : (length + start); + } + end = (end === undefined || end > length) ? length : (+end || 0); + if (end < 0) { + end += length; + } + length = start > end ? 0 : ((end - start) >>> 0); + start >>>= 0; + + var result = Array(length); + while (++index < length) { + result[index] = array[index + start]; + } + return result; + } + + /** + * The base implementation of `_.some` without support for callback shorthands + * and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function baseSome(collection, predicate) { + var result; + + baseEach(collection, function(value, index, collection) { + result = predicate(value, index, collection); + return !result; + }); + return !!result; + } + + /** + * The base implementation of `_.sortBy` which uses `comparer` to define + * the sort order of `array` and replaces criteria objects with their + * corresponding values. + * + * @private + * @param {Array} array The array to sort. + * @param {Function} comparer The function to define sort order. + * @returns {Array} Returns `array`. + */ + function baseSortBy(array, comparer) { + var length = array.length; + + array.sort(comparer); + while (length--) { + array[length] = array[length].value; + } + return array; + } + + /** + * The base implementation of `_.sortByOrder` without param guards. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {boolean[]} orders The sort orders of `iteratees`. + * @returns {Array} Returns the new sorted array. + */ + function baseSortByOrder(collection, iteratees, orders) { + var callback = getCallback(), + index = -1; + + iteratees = arrayMap(iteratees, function(iteratee) { return callback(iteratee); }); + + var result = baseMap(collection, function(value) { + var criteria = arrayMap(iteratees, function(iteratee) { return iteratee(value); }); + return { 'criteria': criteria, 'index': ++index, 'value': value }; + }); + + return baseSortBy(result, function(object, other) { + return compareMultiple(object, other, orders); + }); + } + + /** + * The base implementation of `_.sum` without support for callback shorthands + * and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {number} Returns the sum. + */ + function baseSum(collection, iteratee) { + var result = 0; + baseEach(collection, function(value, index, collection) { + result += +iteratee(value, index, collection) || 0; + }); + return result; + } + + /** + * The base implementation of `_.uniq` without support for callback shorthands + * and `this` binding. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The function invoked per iteration. + * @returns {Array} Returns the new duplicate-value-free array. + */ + function baseUniq(array, iteratee) { + var index = -1, + indexOf = getIndexOf(), + length = array.length, + isCommon = indexOf == baseIndexOf, + isLarge = isCommon && length >= LARGE_ARRAY_SIZE, + seen = isLarge ? createCache() : null, + result = []; + + if (seen) { + indexOf = cacheIndexOf; + isCommon = false; + } else { + isLarge = false; + seen = iteratee ? [] : result; + } + outer: + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value, index, array) : value; + + if (isCommon && value === value) { + var seenIndex = seen.length; + while (seenIndex--) { + if (seen[seenIndex] === computed) { + continue outer; + } + } + if (iteratee) { + seen.push(computed); + } + result.push(value); + } + else if (indexOf(seen, computed, 0) < 0) { + if (iteratee || isLarge) { + seen.push(computed); + } + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.values` and `_.valuesIn` which creates an + * array of `object` property values corresponding to the property names + * of `props`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the array of property values. + */ + function baseValues(object, props) { + var index = -1, + length = props.length, + result = Array(length); + + while (++index < length) { + result[index] = object[props[index]]; + } + return result; + } + + /** + * The base implementation of `_.dropRightWhile`, `_.dropWhile`, `_.takeRightWhile`, + * and `_.takeWhile` without support for callback shorthands and `this` binding. + * + * @private + * @param {Array} array The array to query. + * @param {Function} predicate The function invoked per iteration. + * @param {boolean} [isDrop] Specify dropping elements instead of taking them. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Array} Returns the slice of `array`. + */ + function baseWhile(array, predicate, isDrop, fromRight) { + var length = array.length, + index = fromRight ? length : -1; + + while ((fromRight ? index-- : ++index < length) && predicate(array[index], index, array)) {} + return isDrop + ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length)) + : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index)); + } + + /** + * The base implementation of `wrapperValue` which returns the result of + * performing a sequence of actions on the unwrapped `value`, where each + * successive action is supplied the return value of the previous. + * + * @private + * @param {*} value The unwrapped value. + * @param {Array} actions Actions to peform to resolve the unwrapped value. + * @returns {*} Returns the resolved value. + */ + function baseWrapperValue(value, actions) { + var result = value; + if (result instanceof LazyWrapper) { + result = result.value(); + } + var index = -1, + length = actions.length; + + while (++index < length) { + var action = actions[index]; + result = action.func.apply(action.thisArg, arrayPush([result], action.args)); + } + return result; + } + + /** + * Performs a binary search of `array` to determine the index at which `value` + * should be inserted into `array` in order to maintain its sort order. + * + * @private + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + */ + function binaryIndex(array, value, retHighest) { + var low = 0, + high = array ? array.length : low; + + if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { + while (low < high) { + var mid = (low + high) >>> 1, + computed = array[mid]; + + if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) { + low = mid + 1; + } else { + high = mid; + } + } + return high; + } + return binaryIndexBy(array, value, identity, retHighest); + } + + /** + * This function is like `binaryIndex` except that it invokes `iteratee` for + * `value` and each element of `array` to compute their sort ranking. The + * iteratee is invoked with one argument; (value). + * + * @private + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} iteratee The function invoked per iteration. + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + */ + function binaryIndexBy(array, value, iteratee, retHighest) { + value = iteratee(value); + + var low = 0, + high = array ? array.length : 0, + valIsNaN = value !== value, + valIsNull = value === null, + valIsUndef = value === undefined; + + while (low < high) { + var mid = nativeFloor((low + high) / 2), + computed = iteratee(array[mid]), + isDef = computed !== undefined, + isReflexive = computed === computed; + + if (valIsNaN) { + var setLow = isReflexive || retHighest; + } else if (valIsNull) { + setLow = isReflexive && isDef && (retHighest || computed != null); + } else if (valIsUndef) { + setLow = isReflexive && (retHighest || isDef); + } else if (computed == null) { + setLow = false; + } else { + setLow = retHighest ? (computed <= value) : (computed < value); + } + if (setLow) { + low = mid + 1; + } else { + high = mid; + } + } + return nativeMin(high, MAX_ARRAY_INDEX); + } + + /** + * A specialized version of `baseCallback` which only supports `this` binding + * and specifying the number of arguments to provide to `func`. + * + * @private + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {number} [argCount] The number of arguments to provide to `func`. + * @returns {Function} Returns the callback. + */ + function bindCallback(func, thisArg, argCount) { + if (typeof func != 'function') { + return identity; + } + if (thisArg === undefined) { + return func; + } + switch (argCount) { + case 1: return function(value) { + return func.call(thisArg, value); + }; + case 3: return function(value, index, collection) { + return func.call(thisArg, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(thisArg, accumulator, value, index, collection); + }; + case 5: return function(value, other, key, object, source) { + return func.call(thisArg, value, other, key, object, source); + }; + } + return function() { + return func.apply(thisArg, arguments); + }; + } + + /** + * Creates a clone of the given array buffer. + * + * @private + * @param {ArrayBuffer} buffer The array buffer to clone. + * @returns {ArrayBuffer} Returns the cloned array buffer. + */ + function bufferClone(buffer) { + var result = new ArrayBuffer(buffer.byteLength), + view = new Uint8Array(result); + + view.set(new Uint8Array(buffer)); + return result; + } + + /** + * Creates an array that is the composition of partially applied arguments, + * placeholders, and provided arguments into a single array of arguments. + * + * @private + * @param {Array|Object} args The provided arguments. + * @param {Array} partials The arguments to prepend to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @returns {Array} Returns the new array of composed arguments. + */ + function composeArgs(args, partials, holders) { + var holdersLength = holders.length, + argsIndex = -1, + argsLength = nativeMax(args.length - holdersLength, 0), + leftIndex = -1, + leftLength = partials.length, + result = Array(leftLength + argsLength); + + while (++leftIndex < leftLength) { + result[leftIndex] = partials[leftIndex]; + } + while (++argsIndex < holdersLength) { + result[holders[argsIndex]] = args[argsIndex]; + } + while (argsLength--) { + result[leftIndex++] = args[argsIndex++]; + } + return result; + } + + /** + * This function is like `composeArgs` except that the arguments composition + * is tailored for `_.partialRight`. + * + * @private + * @param {Array|Object} args The provided arguments. + * @param {Array} partials The arguments to append to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @returns {Array} Returns the new array of composed arguments. + */ + function composeArgsRight(args, partials, holders) { + var holdersIndex = -1, + holdersLength = holders.length, + argsIndex = -1, + argsLength = nativeMax(args.length - holdersLength, 0), + rightIndex = -1, + rightLength = partials.length, + result = Array(argsLength + rightLength); + + while (++argsIndex < argsLength) { + result[argsIndex] = args[argsIndex]; + } + var offset = argsIndex; + while (++rightIndex < rightLength) { + result[offset + rightIndex] = partials[rightIndex]; + } + while (++holdersIndex < holdersLength) { + result[offset + holders[holdersIndex]] = args[argsIndex++]; + } + return result; + } + + /** + * Creates a `_.countBy`, `_.groupBy`, `_.indexBy`, or `_.partition` function. + * + * @private + * @param {Function} setter The function to set keys and values of the accumulator object. + * @param {Function} [initializer] The function to initialize the accumulator object. + * @returns {Function} Returns the new aggregator function. + */ + function createAggregator(setter, initializer) { + return function(collection, iteratee, thisArg) { + var result = initializer ? initializer() : {}; + iteratee = getCallback(iteratee, thisArg, 3); + + if (isArray(collection)) { + var index = -1, + length = collection.length; + + while (++index < length) { + var value = collection[index]; + setter(result, value, iteratee(value, index, collection), collection); + } + } else { + baseEach(collection, function(value, key, collection) { + setter(result, value, iteratee(value, key, collection), collection); + }); + } + return result; + }; + } + + /** + * Creates a `_.assign`, `_.defaults`, or `_.merge` function. + * + * @private + * @param {Function} assigner The function to assign values. + * @returns {Function} Returns the new assigner function. + */ + function createAssigner(assigner) { + return restParam(function(object, sources) { + var index = -1, + length = object == null ? 0 : sources.length, + customizer = length > 2 ? sources[length - 2] : undefined, + guard = length > 2 ? sources[2] : undefined, + thisArg = length > 1 ? sources[length - 1] : undefined; + + if (typeof customizer == 'function') { + customizer = bindCallback(customizer, thisArg, 5); + length -= 2; + } else { + customizer = typeof thisArg == 'function' ? thisArg : undefined; + length -= (customizer ? 1 : 0); + } + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? undefined : customizer; + length = 1; + } + while (++index < length) { + var source = sources[index]; + if (source) { + assigner(object, source, customizer); + } + } + return object; + }); + } + + /** + * Creates a `baseEach` or `baseEachRight` function. + * + * @private + * @param {Function} eachFunc The function to iterate over a collection. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseEach(eachFunc, fromRight) { + return function(collection, iteratee) { + var length = collection ? getLength(collection) : 0; + if (!isLength(length)) { + return eachFunc(collection, iteratee); + } + var index = fromRight ? length : -1, + iterable = toObject(collection); + + while ((fromRight ? index-- : ++index < length)) { + if (iteratee(iterable[index], index, iterable) === false) { + break; + } + } + return collection; + }; + } + + /** + * Creates a base function for `_.forIn` or `_.forInRight`. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseFor(fromRight) { + return function(object, iteratee, keysFunc) { + var iterable = toObject(object), + props = keysFunc(object), + length = props.length, + index = fromRight ? length : -1; + + while ((fromRight ? index-- : ++index < length)) { + var key = props[index]; + if (iteratee(iterable[key], key, iterable) === false) { + break; + } + } + return object; + }; + } + + /** + * Creates a function that wraps `func` and invokes it with the `this` + * binding of `thisArg`. + * + * @private + * @param {Function} func The function to bind. + * @param {*} [thisArg] The `this` binding of `func`. + * @returns {Function} Returns the new bound function. + */ + function createBindWrapper(func, thisArg) { + var Ctor = createCtorWrapper(func); + + function wrapper() { + var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + return fn.apply(thisArg, arguments); + } + return wrapper; + } + + /** + * Creates a `Set` cache object to optimize linear searches of large arrays. + * + * @private + * @param {Array} [values] The values to cache. + * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`. + */ + function createCache(values) { + return (nativeCreate && Set) ? new SetCache(values) : null; + } + + /** + * Creates a function that produces compound words out of the words in a + * given string. + * + * @private + * @param {Function} callback The function to combine each word. + * @returns {Function} Returns the new compounder function. + */ + function createCompounder(callback) { + return function(string) { + var index = -1, + array = words(deburr(string)), + length = array.length, + result = ''; + + while (++index < length) { + result = callback(result, array[index], index); + } + return result; + }; + } + + /** + * Creates a function that produces an instance of `Ctor` regardless of + * whether it was invoked as part of a `new` expression or by `call` or `apply`. + * + * @private + * @param {Function} Ctor The constructor to wrap. + * @returns {Function} Returns the new wrapped function. + */ + function createCtorWrapper(Ctor) { + return function() { + // Use a `switch` statement to work with class constructors. + // See http://ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist + // for more details. + var args = arguments; + switch (args.length) { + case 0: return new Ctor; + case 1: return new Ctor(args[0]); + case 2: return new Ctor(args[0], args[1]); + case 3: return new Ctor(args[0], args[1], args[2]); + case 4: return new Ctor(args[0], args[1], args[2], args[3]); + case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]); + case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]); + case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); + } + var thisBinding = baseCreate(Ctor.prototype), + result = Ctor.apply(thisBinding, args); + + // Mimic the constructor's `return` behavior. + // See https://es5.github.io/#x13.2.2 for more details. + return isObject(result) ? result : thisBinding; + }; + } + + /** + * Creates a `_.curry` or `_.curryRight` function. + * + * @private + * @param {boolean} flag The curry bit flag. + * @returns {Function} Returns the new curry function. + */ + function createCurry(flag) { + function curryFunc(func, arity, guard) { + if (guard && isIterateeCall(func, arity, guard)) { + arity = undefined; + } + var result = createWrapper(func, flag, undefined, undefined, undefined, undefined, undefined, arity); + result.placeholder = curryFunc.placeholder; + return result; + } + return curryFunc; + } + + /** + * Creates a `_.defaults` or `_.defaultsDeep` function. + * + * @private + * @param {Function} assigner The function to assign values. + * @param {Function} customizer The function to customize assigned values. + * @returns {Function} Returns the new defaults function. + */ + function createDefaults(assigner, customizer) { + return restParam(function(args) { + var object = args[0]; + if (object == null) { + return object; + } + args.push(customizer); + return assigner.apply(undefined, args); + }); + } + + /** + * Creates a `_.max` or `_.min` function. + * + * @private + * @param {Function} comparator The function used to compare values. + * @param {*} exValue The initial extremum value. + * @returns {Function} Returns the new extremum function. + */ + function createExtremum(comparator, exValue) { + return function(collection, iteratee, thisArg) { + if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { + iteratee = undefined; + } + iteratee = getCallback(iteratee, thisArg, 3); + if (iteratee.length == 1) { + collection = isArray(collection) ? collection : toIterable(collection); + var result = arrayExtremum(collection, iteratee, comparator, exValue); + if (!(collection.length && result === exValue)) { + return result; + } + } + return baseExtremum(collection, iteratee, comparator, exValue); + }; + } + + /** + * Creates a `_.find` or `_.findLast` function. + * + * @private + * @param {Function} eachFunc The function to iterate over a collection. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new find function. + */ + function createFind(eachFunc, fromRight) { + return function(collection, predicate, thisArg) { + predicate = getCallback(predicate, thisArg, 3); + if (isArray(collection)) { + var index = baseFindIndex(collection, predicate, fromRight); + return index > -1 ? collection[index] : undefined; + } + return baseFind(collection, predicate, eachFunc); + }; + } + + /** + * Creates a `_.findIndex` or `_.findLastIndex` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new find function. + */ + function createFindIndex(fromRight) { + return function(array, predicate, thisArg) { + if (!(array && array.length)) { + return -1; + } + predicate = getCallback(predicate, thisArg, 3); + return baseFindIndex(array, predicate, fromRight); + }; + } + + /** + * Creates a `_.findKey` or `_.findLastKey` function. + * + * @private + * @param {Function} objectFunc The function to iterate over an object. + * @returns {Function} Returns the new find function. + */ + function createFindKey(objectFunc) { + return function(object, predicate, thisArg) { + predicate = getCallback(predicate, thisArg, 3); + return baseFind(object, predicate, objectFunc, true); + }; + } + + /** + * Creates a `_.flow` or `_.flowRight` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new flow function. + */ + function createFlow(fromRight) { + return function() { + var wrapper, + length = arguments.length, + index = fromRight ? length : -1, + leftIndex = 0, + funcs = Array(length); + + while ((fromRight ? index-- : ++index < length)) { + var func = funcs[leftIndex++] = arguments[index]; + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (!wrapper && LodashWrapper.prototype.thru && getFuncName(func) == 'wrapper') { + wrapper = new LodashWrapper([], true); + } + } + index = wrapper ? -1 : length; + while (++index < length) { + func = funcs[index]; + + var funcName = getFuncName(func), + data = funcName == 'wrapper' ? getData(func) : undefined; + + if (data && isLaziable(data[0]) && data[1] == (ARY_FLAG | CURRY_FLAG | PARTIAL_FLAG | REARG_FLAG) && !data[4].length && data[9] == 1) { + wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]); + } else { + wrapper = (func.length == 1 && isLaziable(func)) ? wrapper[funcName]() : wrapper.thru(func); + } + } + return function() { + var args = arguments, + value = args[0]; + + if (wrapper && args.length == 1 && isArray(value) && value.length >= LARGE_ARRAY_SIZE) { + return wrapper.plant(value).value(); + } + var index = 0, + result = length ? funcs[index].apply(this, args) : value; + + while (++index < length) { + result = funcs[index].call(this, result); + } + return result; + }; + }; + } + + /** + * Creates a function for `_.forEach` or `_.forEachRight`. + * + * @private + * @param {Function} arrayFunc The function to iterate over an array. + * @param {Function} eachFunc The function to iterate over a collection. + * @returns {Function} Returns the new each function. + */ + function createForEach(arrayFunc, eachFunc) { + return function(collection, iteratee, thisArg) { + return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) + ? arrayFunc(collection, iteratee) + : eachFunc(collection, bindCallback(iteratee, thisArg, 3)); + }; + } + + /** + * Creates a function for `_.forIn` or `_.forInRight`. + * + * @private + * @param {Function} objectFunc The function to iterate over an object. + * @returns {Function} Returns the new each function. + */ + function createForIn(objectFunc) { + return function(object, iteratee, thisArg) { + if (typeof iteratee != 'function' || thisArg !== undefined) { + iteratee = bindCallback(iteratee, thisArg, 3); + } + return objectFunc(object, iteratee, keysIn); + }; + } + + /** + * Creates a function for `_.forOwn` or `_.forOwnRight`. + * + * @private + * @param {Function} objectFunc The function to iterate over an object. + * @returns {Function} Returns the new each function. + */ + function createForOwn(objectFunc) { + return function(object, iteratee, thisArg) { + if (typeof iteratee != 'function' || thisArg !== undefined) { + iteratee = bindCallback(iteratee, thisArg, 3); + } + return objectFunc(object, iteratee); + }; + } + + /** + * Creates a function for `_.mapKeys` or `_.mapValues`. + * + * @private + * @param {boolean} [isMapKeys] Specify mapping keys instead of values. + * @returns {Function} Returns the new map function. + */ + function createObjectMapper(isMapKeys) { + return function(object, iteratee, thisArg) { + var result = {}; + iteratee = getCallback(iteratee, thisArg, 3); + + baseForOwn(object, function(value, key, object) { + var mapped = iteratee(value, key, object); + key = isMapKeys ? mapped : key; + value = isMapKeys ? value : mapped; + result[key] = value; + }); + return result; + }; + } + + /** + * Creates a function for `_.padLeft` or `_.padRight`. + * + * @private + * @param {boolean} [fromRight] Specify padding from the right. + * @returns {Function} Returns the new pad function. + */ + function createPadDir(fromRight) { + return function(string, length, chars) { + string = baseToString(string); + return (fromRight ? string : '') + createPadding(string, length, chars) + (fromRight ? '' : string); + }; + } + + /** + * Creates a `_.partial` or `_.partialRight` function. + * + * @private + * @param {boolean} flag The partial bit flag. + * @returns {Function} Returns the new partial function. + */ + function createPartial(flag) { + var partialFunc = restParam(function(func, partials) { + var holders = replaceHolders(partials, partialFunc.placeholder); + return createWrapper(func, flag, undefined, partials, holders); + }); + return partialFunc; + } + + /** + * Creates a function for `_.reduce` or `_.reduceRight`. + * + * @private + * @param {Function} arrayFunc The function to iterate over an array. + * @param {Function} eachFunc The function to iterate over a collection. + * @returns {Function} Returns the new each function. + */ + function createReduce(arrayFunc, eachFunc) { + return function(collection, iteratee, accumulator, thisArg) { + var initFromArray = arguments.length < 3; + return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) + ? arrayFunc(collection, iteratee, accumulator, initFromArray) + : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc); + }; + } + + /** + * Creates a function that wraps `func` and invokes it with optional `this` + * binding of, partial application, and currying. + * + * @private + * @param {Function|string} func The function or method name to reference. + * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to prepend to those provided to the new function. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [partialsRight] The arguments to append to those provided to the new function. + * @param {Array} [holdersRight] The `partialsRight` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { + var isAry = bitmask & ARY_FLAG, + isBind = bitmask & BIND_FLAG, + isBindKey = bitmask & BIND_KEY_FLAG, + isCurry = bitmask & CURRY_FLAG, + isCurryBound = bitmask & CURRY_BOUND_FLAG, + isCurryRight = bitmask & CURRY_RIGHT_FLAG, + Ctor = isBindKey ? undefined : createCtorWrapper(func); + + function wrapper() { + // Avoid `arguments` object use disqualifying optimizations by + // converting it to an array before providing it to other functions. + var length = arguments.length, + index = length, + args = Array(length); + + while (index--) { + args[index] = arguments[index]; + } + if (partials) { + args = composeArgs(args, partials, holders); + } + if (partialsRight) { + args = composeArgsRight(args, partialsRight, holdersRight); + } + if (isCurry || isCurryRight) { + var placeholder = wrapper.placeholder, + argsHolders = replaceHolders(args, placeholder); + + length -= argsHolders.length; + if (length < arity) { + var newArgPos = argPos ? arrayCopy(argPos) : undefined, + newArity = nativeMax(arity - length, 0), + newsHolders = isCurry ? argsHolders : undefined, + newHoldersRight = isCurry ? undefined : argsHolders, + newPartials = isCurry ? args : undefined, + newPartialsRight = isCurry ? undefined : args; + + bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG); + bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG); + + if (!isCurryBound) { + bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG); + } + var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity], + result = createHybridWrapper.apply(undefined, newData); + + if (isLaziable(func)) { + setData(result, newData); + } + result.placeholder = placeholder; + return result; + } + } + var thisBinding = isBind ? thisArg : this, + fn = isBindKey ? thisBinding[func] : func; + + if (argPos) { + args = reorder(args, argPos); + } + if (isAry && ary < args.length) { + args.length = ary; + } + if (this && this !== root && this instanceof wrapper) { + fn = Ctor || createCtorWrapper(func); + } + return fn.apply(thisBinding, args); + } + return wrapper; + } + + /** + * Creates the padding required for `string` based on the given `length`. + * The `chars` string is truncated if the number of characters exceeds `length`. + * + * @private + * @param {string} string The string to create padding for. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the pad for `string`. + */ + function createPadding(string, length, chars) { + var strLength = string.length; + length = +length; + + if (strLength >= length || !nativeIsFinite(length)) { + return ''; + } + var padLength = length - strLength; + chars = chars == null ? ' ' : (chars + ''); + return repeat(chars, nativeCeil(padLength / chars.length)).slice(0, padLength); + } + + /** + * Creates a function that wraps `func` and invokes it with the optional `this` + * binding of `thisArg` and the `partials` prepended to those provided to + * the wrapper. + * + * @private + * @param {Function} func The function to partially apply arguments to. + * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} partials The arguments to prepend to those provided to the new function. + * @returns {Function} Returns the new bound function. + */ + function createPartialWrapper(func, bitmask, thisArg, partials) { + var isBind = bitmask & BIND_FLAG, + Ctor = createCtorWrapper(func); + + function wrapper() { + // Avoid `arguments` object use disqualifying optimizations by + // converting it to an array before providing it `func`. + var argsIndex = -1, + argsLength = arguments.length, + leftIndex = -1, + leftLength = partials.length, + args = Array(leftLength + argsLength); + + while (++leftIndex < leftLength) { + args[leftIndex] = partials[leftIndex]; + } + while (argsLength--) { + args[leftIndex++] = arguments[++argsIndex]; + } + var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + return fn.apply(isBind ? thisArg : this, args); + } + return wrapper; + } + + /** + * Creates a `_.ceil`, `_.floor`, or `_.round` function. + * + * @private + * @param {string} methodName The name of the `Math` method to use when rounding. + * @returns {Function} Returns the new round function. + */ + function createRound(methodName) { + var func = Math[methodName]; + return function(number, precision) { + precision = precision === undefined ? 0 : (+precision || 0); + if (precision) { + precision = pow(10, precision); + return func(number * precision) / precision; + } + return func(number); + }; + } + + /** + * Creates a `_.sortedIndex` or `_.sortedLastIndex` function. + * + * @private + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {Function} Returns the new index function. + */ + function createSortedIndex(retHighest) { + return function(array, value, iteratee, thisArg) { + var callback = getCallback(iteratee); + return (iteratee == null && callback === baseCallback) + ? binaryIndex(array, value, retHighest) + : binaryIndexBy(array, value, callback(iteratee, thisArg, 1), retHighest); + }; + } + + /** + * Creates a function that either curries or invokes `func` with optional + * `this` binding and partially applied arguments. + * + * @private + * @param {Function|string} func The function or method name to reference. + * @param {number} bitmask The bitmask of flags. + * The bitmask may be composed of the following flags: + * 1 - `_.bind` + * 2 - `_.bindKey` + * 4 - `_.curry` or `_.curryRight` of a bound function + * 8 - `_.curry` + * 16 - `_.curryRight` + * 32 - `_.partial` + * 64 - `_.partialRight` + * 128 - `_.rearg` + * 256 - `_.ary` + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to be partially applied. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { + var isBindKey = bitmask & BIND_KEY_FLAG; + if (!isBindKey && typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + var length = partials ? partials.length : 0; + if (!length) { + bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG); + partials = holders = undefined; + } + length -= (holders ? holders.length : 0); + if (bitmask & PARTIAL_RIGHT_FLAG) { + var partialsRight = partials, + holdersRight = holders; + + partials = holders = undefined; + } + var data = isBindKey ? undefined : getData(func), + newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity]; + + if (data) { + mergeData(newData, data); + bitmask = newData[1]; + arity = newData[9]; + } + newData[9] = arity == null + ? (isBindKey ? 0 : func.length) + : (nativeMax(arity - length, 0) || 0); + + if (bitmask == BIND_FLAG) { + var result = createBindWrapper(newData[0], newData[2]); + } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) { + result = createPartialWrapper.apply(undefined, newData); + } else { + result = createHybridWrapper.apply(undefined, newData); + } + var setter = data ? baseSetData : setData; + return setter(result, newData); + } + + /** + * A specialized version of `baseIsEqualDeep` for arrays with support for + * partial deep comparisons. + * + * @private + * @param {Array} array The array to compare. + * @param {Array} other The other array to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} [customizer] The function to customize comparing arrays. + * @param {boolean} [isLoose] Specify performing partial comparisons. + * @param {Array} [stackA] Tracks traversed `value` objects. + * @param {Array} [stackB] Tracks traversed `other` objects. + * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. + */ + function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) { + var index = -1, + arrLength = array.length, + othLength = other.length; + + if (arrLength != othLength && !(isLoose && othLength > arrLength)) { + return false; + } + // Ignore non-index properties. + while (++index < arrLength) { + var arrValue = array[index], + othValue = other[index], + result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined; + + if (result !== undefined) { + if (result) { + continue; + } + return false; + } + // Recursively compare arrays (susceptible to call stack limits). + if (isLoose) { + if (!arraySome(other, function(othValue) { + return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB); + })) { + return false; + } + } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) { + return false; + } + } + return true; + } + + /** + * A specialized version of `baseIsEqualDeep` for comparing objects of + * the same `toStringTag`. + * + * **Note:** This function only supports comparing values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {string} tag The `toStringTag` of the objects to compare. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalByTag(object, other, tag) { + switch (tag) { + case boolTag: + case dateTag: + // Coerce dates and booleans to numbers, dates to milliseconds and booleans + // to `1` or `0` treating invalid dates coerced to `NaN` as not equal. + return +object == +other; + + case errorTag: + return object.name == other.name && object.message == other.message; + + case numberTag: + // Treat `NaN` vs. `NaN` as equal. + return (object != +object) + ? other != +other + : object == +other; + + case regexpTag: + case stringTag: + // Coerce regexes to strings and treat strings primitives and string + // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details. + return object == (other + ''); + } + return false; + } + + /** + * A specialized version of `baseIsEqualDeep` for objects with support for + * partial deep comparisons. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} [customizer] The function to customize comparing values. + * @param {boolean} [isLoose] Specify performing partial comparisons. + * @param {Array} [stackA] Tracks traversed `value` objects. + * @param {Array} [stackB] Tracks traversed `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) { + var objProps = keys(object), + objLength = objProps.length, + othProps = keys(other), + othLength = othProps.length; + + if (objLength != othLength && !isLoose) { + return false; + } + var index = objLength; + while (index--) { + var key = objProps[index]; + if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) { + return false; + } + } + var skipCtor = isLoose; + while (++index < objLength) { + key = objProps[index]; + var objValue = object[key], + othValue = other[key], + result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined; + + // Recursively compare objects (susceptible to call stack limits). + if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) { + return false; + } + skipCtor || (skipCtor = key == 'constructor'); + } + if (!skipCtor) { + var objCtor = object.constructor, + othCtor = other.constructor; + + // Non `Object` object instances with different constructors are not equal. + if (objCtor != othCtor && + ('constructor' in object && 'constructor' in other) && + !(typeof objCtor == 'function' && objCtor instanceof objCtor && + typeof othCtor == 'function' && othCtor instanceof othCtor)) { + return false; + } + } + return true; + } + + /** + * Gets the appropriate "callback" function. If the `_.callback` method is + * customized this function returns the custom method, otherwise it returns + * the `baseCallback` function. If arguments are provided the chosen function + * is invoked with them and its result is returned. + * + * @private + * @returns {Function} Returns the chosen function or its result. + */ + function getCallback(func, thisArg, argCount) { + var result = lodash.callback || callback; + result = result === callback ? baseCallback : result; + return argCount ? result(func, thisArg, argCount) : result; + } + + /** + * Gets metadata for `func`. + * + * @private + * @param {Function} func The function to query. + * @returns {*} Returns the metadata for `func`. + */ + var getData = !metaMap ? noop : function(func) { + return metaMap.get(func); + }; + + /** + * Gets the name of `func`. + * + * @private + * @param {Function} func The function to query. + * @returns {string} Returns the function name. + */ + function getFuncName(func) { + var result = func.name, + array = realNames[result], + length = array ? array.length : 0; + + while (length--) { + var data = array[length], + otherFunc = data.func; + if (otherFunc == null || otherFunc == func) { + return data.name; + } + } + return result; + } + + /** + * Gets the appropriate "indexOf" function. If the `_.indexOf` method is + * customized this function returns the custom method, otherwise it returns + * the `baseIndexOf` function. If arguments are provided the chosen function + * is invoked with them and its result is returned. + * + * @private + * @returns {Function|number} Returns the chosen function or its result. + */ + function getIndexOf(collection, target, fromIndex) { + var result = lodash.indexOf || indexOf; + result = result === indexOf ? baseIndexOf : result; + return collection ? result(collection, target, fromIndex) : result; + } + + /** + * Gets the "length" property value of `object`. + * + * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) + * that affects Safari on at least iOS 8.1-8.3 ARM64. + * + * @private + * @param {Object} object The object to query. + * @returns {*} Returns the "length" value. + */ + var getLength = baseProperty('length'); + + /** + * Gets the propery names, values, and compare flags of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the match data of `object`. + */ + function getMatchData(object) { + var result = pairs(object), + length = result.length; + + while (length--) { + result[length][2] = isStrictComparable(result[length][1]); + } + return result; + } + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = object == null ? undefined : object[key]; + return isNative(value) ? value : undefined; + } + + /** + * Gets the view, applying any `transforms` to the `start` and `end` positions. + * + * @private + * @param {number} start The start of the view. + * @param {number} end The end of the view. + * @param {Array} transforms The transformations to apply to the view. + * @returns {Object} Returns an object containing the `start` and `end` + * positions of the view. + */ + function getView(start, end, transforms) { + var index = -1, + length = transforms.length; + + while (++index < length) { + var data = transforms[index], + size = data.size; + + switch (data.type) { + case 'drop': start += size; break; + case 'dropRight': end -= size; break; + case 'take': end = nativeMin(end, start + size); break; + case 'takeRight': start = nativeMax(start, end - size); break; + } + } + return { 'start': start, 'end': end }; + } + + /** + * Initializes an array clone. + * + * @private + * @param {Array} array The array to clone. + * @returns {Array} Returns the initialized clone. + */ + function initCloneArray(array) { + var length = array.length, + result = new array.constructor(length); + + // Add array properties assigned by `RegExp#exec`. + if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { + result.index = array.index; + result.input = array.input; + } + return result; + } + + /** + * Initializes an object clone. + * + * @private + * @param {Object} object The object to clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneObject(object) { + var Ctor = object.constructor; + if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) { + Ctor = Object; + } + return new Ctor; + } + + /** + * Initializes an object clone based on its `toStringTag`. + * + * **Note:** This function only supports cloning values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to clone. + * @param {string} tag The `toStringTag` of the object to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneByTag(object, tag, isDeep) { + var Ctor = object.constructor; + switch (tag) { + case arrayBufferTag: + return bufferClone(object); + + case boolTag: + case dateTag: + return new Ctor(+object); + + case float32Tag: case float64Tag: + case int8Tag: case int16Tag: case int32Tag: + case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: + var buffer = object.buffer; + return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length); + + case numberTag: + case stringTag: + return new Ctor(object); + + case regexpTag: + var result = new Ctor(object.source, reFlags.exec(object)); + result.lastIndex = object.lastIndex; + } + return result; + } + + /** + * Invokes the method at `path` on `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {Array} args The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + */ + function invokePath(object, path, args) { + if (object != null && !isKey(path, object)) { + path = toPath(path); + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + path = last(path); + } + var func = object == null ? object : object[path]; + return func == null ? undefined : func.apply(object, args); + } + + /** + * Checks if `value` is array-like. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + */ + function isArrayLike(value) { + return value != null && isLength(getLength(value)); + } + + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + function isIndex(value, length) { + value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1; + length = length == null ? MAX_SAFE_INTEGER : length; + return value > -1 && value % 1 == 0 && value < length; + } + + /** + * Checks if the provided arguments are from an iteratee call. + * + * @private + * @param {*} value The potential iteratee value argument. + * @param {*} index The potential iteratee index or key argument. + * @param {*} object The potential iteratee object argument. + * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`. + */ + function isIterateeCall(value, index, object) { + if (!isObject(object)) { + return false; + } + var type = typeof index; + if (type == 'number' + ? (isArrayLike(object) && isIndex(index, object.length)) + : (type == 'string' && index in object)) { + var other = object[index]; + return value === value ? (value === other) : (other !== other); + } + return false; + } + + /** + * Checks if `value` is a property name and not a property path. + * + * @private + * @param {*} value The value to check. + * @param {Object} [object] The object to query keys on. + * @returns {boolean} Returns `true` if `value` is a property name, else `false`. + */ + function isKey(value, object) { + var type = typeof value; + if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') { + return true; + } + if (isArray(value)) { + return false; + } + var result = !reIsDeepProp.test(value); + return result || (object != null && value in toObject(object)); + } + + /** + * Checks if `func` has a lazy counterpart. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`. + */ + function isLaziable(func) { + var funcName = getFuncName(func); + if (!(funcName in LazyWrapper.prototype)) { + return false; + } + var other = lodash[funcName]; + if (func === other) { + return true; + } + var data = getData(other); + return !!data && func === data[0]; + } + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + */ + function isLength(value) { + return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` if suitable for strict + * equality comparisons, else `false`. + */ + function isStrictComparable(value) { + return value === value && !isObject(value); + } + + /** + * Merges the function metadata of `source` into `data`. + * + * Merging metadata reduces the number of wrappers required to invoke a function. + * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` + * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg` + * augment function arguments, making the order in which they are executed important, + * preventing the merging of metadata. However, we make an exception for a safe + * common case where curried functions have `_.ary` and or `_.rearg` applied. + * + * @private + * @param {Array} data The destination metadata. + * @param {Array} source The source metadata. + * @returns {Array} Returns `data`. + */ + function mergeData(data, source) { + var bitmask = data[1], + srcBitmask = source[1], + newBitmask = bitmask | srcBitmask, + isCommon = newBitmask < ARY_FLAG; + + var isCombo = + (srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) || + (srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) || + (srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG); + + // Exit early if metadata can't be merged. + if (!(isCommon || isCombo)) { + return data; + } + // Use source `thisArg` if available. + if (srcBitmask & BIND_FLAG) { + data[2] = source[2]; + // Set when currying a bound function. + newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG; + } + // Compose partial arguments. + var value = source[3]; + if (value) { + var partials = data[3]; + data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value); + data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]); + } + // Compose partial right arguments. + value = source[5]; + if (value) { + partials = data[5]; + data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value); + data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]); + } + // Use source `argPos` if available. + value = source[7]; + if (value) { + data[7] = arrayCopy(value); + } + // Use source `ary` if it's smaller. + if (srcBitmask & ARY_FLAG) { + data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]); + } + // Use source `arity` if one is not provided. + if (data[9] == null) { + data[9] = source[9]; + } + // Use source `func` and merge bitmasks. + data[0] = source[0]; + data[1] = newBitmask; + + return data; + } + + /** + * Used by `_.defaultsDeep` to customize its `_.merge` use. + * + * @private + * @param {*} objectValue The destination object property value. + * @param {*} sourceValue The source object property value. + * @returns {*} Returns the value to assign to the destination object. + */ + function mergeDefaults(objectValue, sourceValue) { + return objectValue === undefined ? sourceValue : merge(objectValue, sourceValue, mergeDefaults); + } + + /** + * A specialized version of `_.pick` which picks `object` properties specified + * by `props`. + * + * @private + * @param {Object} object The source object. + * @param {string[]} props The property names to pick. + * @returns {Object} Returns the new object. + */ + function pickByArray(object, props) { + object = toObject(object); + + var index = -1, + length = props.length, + result = {}; + + while (++index < length) { + var key = props[index]; + if (key in object) { + result[key] = object[key]; + } + } + return result; + } + + /** + * A specialized version of `_.pick` which picks `object` properties `predicate` + * returns truthy for. + * + * @private + * @param {Object} object The source object. + * @param {Function} predicate The function invoked per iteration. + * @returns {Object} Returns the new object. + */ + function pickByCallback(object, predicate) { + var result = {}; + baseForIn(object, function(value, key, object) { + if (predicate(value, key, object)) { + result[key] = value; + } + }); + return result; + } + + /** + * Reorder `array` according to the specified indexes where the element at + * the first index is assigned as the first element, the element at + * the second index is assigned as the second element, and so on. + * + * @private + * @param {Array} array The array to reorder. + * @param {Array} indexes The arranged array indexes. + * @returns {Array} Returns `array`. + */ + function reorder(array, indexes) { + var arrLength = array.length, + length = nativeMin(indexes.length, arrLength), + oldArray = arrayCopy(array); + + while (length--) { + var index = indexes[length]; + array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; + } + return array; + } + + /** + * Sets metadata for `func`. + * + * **Note:** If this function becomes hot, i.e. is invoked a lot in a short + * period of time, it will trip its breaker and transition to an identity function + * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070) + * for more details. + * + * @private + * @param {Function} func The function to associate metadata with. + * @param {*} data The metadata. + * @returns {Function} Returns `func`. + */ + var setData = (function() { + var count = 0, + lastCalled = 0; + + return function(key, value) { + var stamp = now(), + remaining = HOT_SPAN - (stamp - lastCalled); + + lastCalled = stamp; + if (remaining > 0) { + if (++count >= HOT_COUNT) { + return key; + } + } else { + count = 0; + } + return baseSetData(key, value); + }; + }()); + + /** + * A fallback implementation of `Object.keys` which creates an array of the + * own enumerable property names of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function shimKeys(object) { + var props = keysIn(object), + propsLength = props.length, + length = propsLength && object.length; + + var allowIndexes = !!length && isLength(length) && + (isArray(object) || isArguments(object)); + + var index = -1, + result = []; + + while (++index < propsLength) { + var key = props[index]; + if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) { + result.push(key); + } + } + return result; + } + + /** + * Converts `value` to an array-like object if it's not one. + * + * @private + * @param {*} value The value to process. + * @returns {Array|Object} Returns the array-like object. + */ + function toIterable(value) { + if (value == null) { + return []; + } + if (!isArrayLike(value)) { + return values(value); + } + return isObject(value) ? value : Object(value); + } + + /** + * Converts `value` to an object if it's not one. + * + * @private + * @param {*} value The value to process. + * @returns {Object} Returns the object. + */ + function toObject(value) { + return isObject(value) ? value : Object(value); + } + + /** + * Converts `value` to property path array if it's not one. + * + * @private + * @param {*} value The value to process. + * @returns {Array} Returns the property path array. + */ + function toPath(value) { + if (isArray(value)) { + return value; + } + var result = []; + baseToString(value).replace(rePropName, function(match, number, quote, string) { + result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; + } + + /** + * Creates a clone of `wrapper`. + * + * @private + * @param {Object} wrapper The wrapper to clone. + * @returns {Object} Returns the cloned wrapper. + */ + function wrapperClone(wrapper) { + return wrapper instanceof LazyWrapper + ? wrapper.clone() + : new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__, arrayCopy(wrapper.__actions__)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates an array of elements split into groups the length of `size`. + * If `collection` can't be split evenly, the final chunk will be the remaining + * elements. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to process. + * @param {number} [size=1] The length of each chunk. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the new array containing chunks. + * @example + * + * _.chunk(['a', 'b', 'c', 'd'], 2); + * // => [['a', 'b'], ['c', 'd']] + * + * _.chunk(['a', 'b', 'c', 'd'], 3); + * // => [['a', 'b', 'c'], ['d']] + */ + function chunk(array, size, guard) { + if (guard ? isIterateeCall(array, size, guard) : size == null) { + size = 1; + } else { + size = nativeMax(nativeFloor(size) || 1, 1); + } + var index = 0, + length = array ? array.length : 0, + resIndex = -1, + result = Array(nativeCeil(length / size)); + + while (index < length) { + result[++resIndex] = baseSlice(array, index, (index += size)); + } + return result; + } + + /** + * Creates an array with all falsey values removed. The values `false`, `null`, + * `0`, `""`, `undefined`, and `NaN` are falsey. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to compact. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var index = -1, + length = array ? array.length : 0, + resIndex = -1, + result = []; + + while (++index < length) { + var value = array[index]; + if (value) { + result[++resIndex] = value; + } + } + return result; + } + + /** + * Creates an array of unique `array` values not included in the other + * provided arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The arrays of values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.difference([1, 2, 3], [4, 2]); + * // => [1, 3] + */ + var difference = restParam(function(array, values) { + return (isObjectLike(array) && isArrayLike(array)) + ? baseDifference(array, baseFlatten(values, false, true)) + : []; + }); + + /** + * Creates a slice of `array` with `n` elements dropped from the beginning. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.drop([1, 2, 3]); + * // => [2, 3] + * + * _.drop([1, 2, 3], 2); + * // => [3] + * + * _.drop([1, 2, 3], 5); + * // => [] + * + * _.drop([1, 2, 3], 0); + * // => [1, 2, 3] + */ + function drop(array, n, guard) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (guard ? isIterateeCall(array, n, guard) : n == null) { + n = 1; + } + return baseSlice(array, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` with `n` elements dropped from the end. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropRight([1, 2, 3]); + * // => [1, 2] + * + * _.dropRight([1, 2, 3], 2); + * // => [1] + * + * _.dropRight([1, 2, 3], 5); + * // => [] + * + * _.dropRight([1, 2, 3], 0); + * // => [1, 2, 3] + */ + function dropRight(array, n, guard) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (guard ? isIterateeCall(array, n, guard) : n == null) { + n = 1; + } + n = length - (+n || 0); + return baseSlice(array, 0, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` excluding elements dropped from the end. + * Elements are dropped until `predicate` returns falsey. The predicate is + * bound to `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that match the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropRightWhile([1, 2, 3], function(n) { + * return n > 1; + * }); + * // => [1] + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.dropRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user'); + * // => ['barney', 'fred'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.dropRightWhile(users, 'active', false), 'user'); + * // => ['barney'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.dropRightWhile(users, 'active'), 'user'); + * // => ['barney', 'fred', 'pebbles'] + */ + function dropRightWhile(array, predicate, thisArg) { + return (array && array.length) + ? baseWhile(array, getCallback(predicate, thisArg, 3), true, true) + : []; + } + + /** + * Creates a slice of `array` excluding elements dropped from the beginning. + * Elements are dropped until `predicate` returns falsey. The predicate is + * bound to `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropWhile([1, 2, 3], function(n) { + * return n < 3; + * }); + * // => [3] + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.dropWhile(users, { 'user': 'barney', 'active': false }), 'user'); + * // => ['fred', 'pebbles'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.dropWhile(users, 'active', false), 'user'); + * // => ['pebbles'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.dropWhile(users, 'active'), 'user'); + * // => ['barney', 'fred', 'pebbles'] + */ + function dropWhile(array, predicate, thisArg) { + return (array && array.length) + ? baseWhile(array, getCallback(predicate, thisArg, 3), true) + : []; + } + + /** + * Fills elements of `array` with `value` from `start` up to, but not + * including, `end`. + * + * **Note:** This method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3]; + * + * _.fill(array, 'a'); + * console.log(array); + * // => ['a', 'a', 'a'] + * + * _.fill(Array(3), 2); + * // => [2, 2, 2] + * + * _.fill([4, 6, 8], '*', 1, 2); + * // => [4, '*', 8] + */ + function fill(array, value, start, end) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (start && typeof start != 'number' && isIterateeCall(array, value, start)) { + start = 0; + end = length; + } + return baseFill(array, value, start, end); + } + + /** + * This method is like `_.find` except that it returns the index of the first + * element `predicate` returns truthy for instead of the element itself. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.findIndex(users, function(chr) { + * return chr.user == 'barney'; + * }); + * // => 0 + * + * // using the `_.matches` callback shorthand + * _.findIndex(users, { 'user': 'fred', 'active': false }); + * // => 1 + * + * // using the `_.matchesProperty` callback shorthand + * _.findIndex(users, 'active', false); + * // => 0 + * + * // using the `_.property` callback shorthand + * _.findIndex(users, 'active'); + * // => 2 + */ + var findIndex = createFindIndex(); + + /** + * This method is like `_.findIndex` except that it iterates over elements + * of `collection` from right to left. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.findLastIndex(users, function(chr) { + * return chr.user == 'pebbles'; + * }); + * // => 2 + * + * // using the `_.matches` callback shorthand + * _.findLastIndex(users, { 'user': 'barney', 'active': true }); + * // => 0 + * + * // using the `_.matchesProperty` callback shorthand + * _.findLastIndex(users, 'active', false); + * // => 2 + * + * // using the `_.property` callback shorthand + * _.findLastIndex(users, 'active'); + * // => 0 + */ + var findLastIndex = createFindIndex(true); + + /** + * Gets the first element of `array`. + * + * @static + * @memberOf _ + * @alias head + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the first element of `array`. + * @example + * + * _.first([1, 2, 3]); + * // => 1 + * + * _.first([]); + * // => undefined + */ + function first(array) { + return array ? array[0] : undefined; + } + + /** + * Flattens a nested array. If `isDeep` is `true` the array is recursively + * flattened, otherwise it is only flattened a single level. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to flatten. + * @param {boolean} [isDeep] Specify a deep flatten. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flatten([1, [2, 3, [4]]]); + * // => [1, 2, 3, [4]] + * + * // using `isDeep` + * _.flatten([1, [2, 3, [4]]], true); + * // => [1, 2, 3, 4] + */ + function flatten(array, isDeep, guard) { + var length = array ? array.length : 0; + if (guard && isIterateeCall(array, isDeep, guard)) { + isDeep = false; + } + return length ? baseFlatten(array, isDeep) : []; + } + + /** + * Recursively flattens a nested array. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to recursively flatten. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flattenDeep([1, [2, 3, [4]]]); + * // => [1, 2, 3, 4] + */ + function flattenDeep(array) { + var length = array ? array.length : 0; + return length ? baseFlatten(array, true) : []; + } + + /** + * Gets the index at which the first occurrence of `value` is found in `array` + * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. If `fromIndex` is negative, it is used as the offset + * from the end of `array`. If `array` is sorted providing `true` for `fromIndex` + * performs a faster binary search. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {*} value The value to search for. + * @param {boolean|number} [fromIndex=0] The index to search from or `true` + * to perform a binary search on a sorted array. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.indexOf([1, 2, 1, 2], 2); + * // => 1 + * + * // using `fromIndex` + * _.indexOf([1, 2, 1, 2], 2, 2); + * // => 3 + * + * // performing a binary search + * _.indexOf([1, 1, 2, 2], 2, true); + * // => 2 + */ + function indexOf(array, value, fromIndex) { + var length = array ? array.length : 0; + if (!length) { + return -1; + } + if (typeof fromIndex == 'number') { + fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex; + } else if (fromIndex) { + var index = binaryIndex(array, value); + if (index < length && + (value === value ? (value === array[index]) : (array[index] !== array[index]))) { + return index; + } + return -1; + } + return baseIndexOf(array, value, fromIndex || 0); + } + + /** + * Gets all but the last element of `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.initial([1, 2, 3]); + * // => [1, 2] + */ + function initial(array) { + return dropRight(array, 1); + } + + /** + * Creates an array of unique values that are included in all of the provided + * arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of shared values. + * @example + * _.intersection([1, 2], [4, 2], [2, 1]); + * // => [2] + */ + var intersection = restParam(function(arrays) { + var othLength = arrays.length, + othIndex = othLength, + caches = Array(length), + indexOf = getIndexOf(), + isCommon = indexOf == baseIndexOf, + result = []; + + while (othIndex--) { + var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : []; + caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null; + } + var array = arrays[0], + index = -1, + length = array ? array.length : 0, + seen = caches[0]; + + outer: + while (++index < length) { + value = array[index]; + if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) { + var othIndex = othLength; + while (--othIndex) { + var cache = caches[othIndex]; + if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) { + continue outer; + } + } + if (seen) { + seen.push(value); + } + result.push(value); + } + } + return result; + }); + + /** + * Gets the last element of `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the last element of `array`. + * @example + * + * _.last([1, 2, 3]); + * // => 3 + */ + function last(array) { + var length = array ? array.length : 0; + return length ? array[length - 1] : undefined; + } + + /** + * This method is like `_.indexOf` except that it iterates over elements of + * `array` from right to left. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {*} value The value to search for. + * @param {boolean|number} [fromIndex=array.length-1] The index to search from + * or `true` to perform a binary search on a sorted array. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.lastIndexOf([1, 2, 1, 2], 2); + * // => 3 + * + * // using `fromIndex` + * _.lastIndexOf([1, 2, 1, 2], 2, 2); + * // => 1 + * + * // performing a binary search + * _.lastIndexOf([1, 1, 2, 2], 2, true); + * // => 3 + */ + function lastIndexOf(array, value, fromIndex) { + var length = array ? array.length : 0; + if (!length) { + return -1; + } + var index = length; + if (typeof fromIndex == 'number') { + index = (fromIndex < 0 ? nativeMax(length + fromIndex, 0) : nativeMin(fromIndex || 0, length - 1)) + 1; + } else if (fromIndex) { + index = binaryIndex(array, value, true) - 1; + var other = array[index]; + if (value === value ? (value === other) : (other !== other)) { + return index; + } + return -1; + } + if (value !== value) { + return indexOfNaN(array, index, true); + } + while (index--) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * Removes all provided values from `array` using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `_.without`, this method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to modify. + * @param {...*} [values] The values to remove. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3, 1, 2, 3]; + * + * _.pull(array, 2, 3); + * console.log(array); + * // => [1, 1] + */ + function pull() { + var args = arguments, + array = args[0]; + + if (!(array && array.length)) { + return array; + } + var index = 0, + indexOf = getIndexOf(), + length = args.length; + + while (++index < length) { + var fromIndex = 0, + value = args[index]; + + while ((fromIndex = indexOf(array, value, fromIndex)) > -1) { + splice.call(array, fromIndex, 1); + } + } + return array; + } + + /** + * Removes elements from `array` corresponding to the given indexes and returns + * an array of the removed elements. Indexes may be specified as an array of + * indexes or as individual arguments. + * + * **Note:** Unlike `_.at`, this method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to modify. + * @param {...(number|number[])} [indexes] The indexes of elements to remove, + * specified as individual indexes or arrays of indexes. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = [5, 10, 15, 20]; + * var evens = _.pullAt(array, 1, 3); + * + * console.log(array); + * // => [5, 15] + * + * console.log(evens); + * // => [10, 20] + */ + var pullAt = restParam(function(array, indexes) { + indexes = baseFlatten(indexes); + + var result = baseAt(array, indexes); + basePullAt(array, indexes.sort(baseCompareAscending)); + return result; + }); + + /** + * Removes all elements from `array` that `predicate` returns truthy for + * and returns an array of the removed elements. The predicate is bound to + * `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * **Note:** Unlike `_.filter`, this method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to modify. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = [1, 2, 3, 4]; + * var evens = _.remove(array, function(n) { + * return n % 2 == 0; + * }); + * + * console.log(array); + * // => [1, 3] + * + * console.log(evens); + * // => [2, 4] + */ + function remove(array, predicate, thisArg) { + var result = []; + if (!(array && array.length)) { + return result; + } + var index = -1, + indexes = [], + length = array.length; + + predicate = getCallback(predicate, thisArg, 3); + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result.push(value); + indexes.push(index); + } + } + basePullAt(array, indexes); + return result; + } + + /** + * Gets all but the first element of `array`. + * + * @static + * @memberOf _ + * @alias tail + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.rest([1, 2, 3]); + * // => [2, 3] + */ + function rest(array) { + return drop(array, 1); + } + + /** + * Creates a slice of `array` from `start` up to, but not including, `end`. + * + * **Note:** This method is used instead of `Array#slice` to support node + * lists in IE < 9 and to ensure dense arrays are returned. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */ + function slice(array, start, end) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { + start = 0; + end = length; + } + return baseSlice(array, start, end); + } + + /** + * Uses a binary search to determine the lowest index at which `value` should + * be inserted into `array` in order to maintain its sort order. If an iteratee + * function is provided it is invoked for `value` and each element of `array` + * to compute their sort ranking. The iteratee is bound to `thisArg` and + * invoked with one argument; (value). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedIndex([30, 50], 40); + * // => 1 + * + * _.sortedIndex([4, 4, 5, 5], 5); + * // => 2 + * + * var dict = { 'data': { 'thirty': 30, 'forty': 40, 'fifty': 50 } }; + * + * // using an iteratee function + * _.sortedIndex(['thirty', 'fifty'], 'forty', function(word) { + * return this.data[word]; + * }, dict); + * // => 1 + * + * // using the `_.property` callback shorthand + * _.sortedIndex([{ 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x'); + * // => 1 + */ + var sortedIndex = createSortedIndex(); + + /** + * This method is like `_.sortedIndex` except that it returns the highest + * index at which `value` should be inserted into `array` in order to + * maintain its sort order. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedLastIndex([4, 4, 5, 5], 5); + * // => 4 + */ + var sortedLastIndex = createSortedIndex(true); + + /** + * Creates a slice of `array` with `n` elements taken from the beginning. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.take([1, 2, 3]); + * // => [1] + * + * _.take([1, 2, 3], 2); + * // => [1, 2] + * + * _.take([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.take([1, 2, 3], 0); + * // => [] + */ + function take(array, n, guard) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (guard ? isIterateeCall(array, n, guard) : n == null) { + n = 1; + } + return baseSlice(array, 0, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` with `n` elements taken from the end. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeRight([1, 2, 3]); + * // => [3] + * + * _.takeRight([1, 2, 3], 2); + * // => [2, 3] + * + * _.takeRight([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.takeRight([1, 2, 3], 0); + * // => [] + */ + function takeRight(array, n, guard) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (guard ? isIterateeCall(array, n, guard) : n == null) { + n = 1; + } + n = length - (+n || 0); + return baseSlice(array, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` with elements taken from the end. Elements are + * taken until `predicate` returns falsey. The predicate is bound to `thisArg` + * and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeRightWhile([1, 2, 3], function(n) { + * return n > 1; + * }); + * // => [2, 3] + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.takeRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user'); + * // => ['pebbles'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.takeRightWhile(users, 'active', false), 'user'); + * // => ['fred', 'pebbles'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.takeRightWhile(users, 'active'), 'user'); + * // => [] + */ + function takeRightWhile(array, predicate, thisArg) { + return (array && array.length) + ? baseWhile(array, getCallback(predicate, thisArg, 3), false, true) + : []; + } + + /** + * Creates a slice of `array` with elements taken from the beginning. Elements + * are taken until `predicate` returns falsey. The predicate is bound to + * `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeWhile([1, 2, 3], function(n) { + * return n < 3; + * }); + * // => [1, 2] + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false}, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.takeWhile(users, { 'user': 'barney', 'active': false }), 'user'); + * // => ['barney'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.takeWhile(users, 'active', false), 'user'); + * // => ['barney', 'fred'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.takeWhile(users, 'active'), 'user'); + * // => [] + */ + function takeWhile(array, predicate, thisArg) { + return (array && array.length) + ? baseWhile(array, getCallback(predicate, thisArg, 3)) + : []; + } + + /** + * Creates an array of unique values, in order, from all of the provided arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of combined values. + * @example + * + * _.union([1, 2], [4, 2], [2, 1]); + * // => [1, 2, 4] + */ + var union = restParam(function(arrays) { + return baseUniq(baseFlatten(arrays, false, true)); + }); + + /** + * Creates a duplicate-free version of an array, using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons, in which only the first occurence of each element + * is kept. Providing `true` for `isSorted` performs a faster search algorithm + * for sorted arrays. If an iteratee function is provided it is invoked for + * each element in the array to generate the criterion by which uniqueness + * is computed. The `iteratee` is bound to `thisArg` and invoked with three + * arguments: (value, index, array). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias unique + * @category Array + * @param {Array} array The array to inspect. + * @param {boolean} [isSorted] Specify the array is sorted. + * @param {Function|Object|string} [iteratee] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new duplicate-value-free array. + * @example + * + * _.uniq([2, 1, 2]); + * // => [2, 1] + * + * // using `isSorted` + * _.uniq([1, 1, 2], true); + * // => [1, 2] + * + * // using an iteratee function + * _.uniq([1, 2.5, 1.5, 2], function(n) { + * return this.floor(n); + * }, Math); + * // => [1, 2.5] + * + * // using the `_.property` callback shorthand + * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] + */ + function uniq(array, isSorted, iteratee, thisArg) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (isSorted != null && typeof isSorted != 'boolean') { + thisArg = iteratee; + iteratee = isIterateeCall(array, isSorted, thisArg) ? undefined : isSorted; + isSorted = false; + } + var callback = getCallback(); + if (!(iteratee == null && callback === baseCallback)) { + iteratee = callback(iteratee, thisArg, 3); + } + return (isSorted && getIndexOf() == baseIndexOf) + ? sortedUniq(array, iteratee) + : baseUniq(array, iteratee); + } + + /** + * This method is like `_.zip` except that it accepts an array of grouped + * elements and creates an array regrouping the elements to their pre-zip + * configuration. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array of grouped elements to process. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]); + * // => [['fred', 30, true], ['barney', 40, false]] + * + * _.unzip(zipped); + * // => [['fred', 'barney'], [30, 40], [true, false]] + */ + function unzip(array) { + if (!(array && array.length)) { + return []; + } + var index = -1, + length = 0; + + array = arrayFilter(array, function(group) { + if (isArrayLike(group)) { + length = nativeMax(group.length, length); + return true; + } + }); + var result = Array(length); + while (++index < length) { + result[index] = arrayMap(array, baseProperty(index)); + } + return result; + } + + /** + * This method is like `_.unzip` except that it accepts an iteratee to specify + * how regrouped values should be combined. The `iteratee` is bound to `thisArg` + * and invoked with four arguments: (accumulator, value, index, group). + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array of grouped elements to process. + * @param {Function} [iteratee] The function to combine regrouped values. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip([1, 2], [10, 20], [100, 200]); + * // => [[1, 10, 100], [2, 20, 200]] + * + * _.unzipWith(zipped, _.add); + * // => [3, 30, 300] + */ + function unzipWith(array, iteratee, thisArg) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + var result = unzip(array); + if (iteratee == null) { + return result; + } + iteratee = bindCallback(iteratee, thisArg, 4); + return arrayMap(result, function(group) { + return arrayReduce(group, iteratee, undefined, true); + }); + } + + /** + * Creates an array excluding all provided values using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to filter. + * @param {...*} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.without([1, 2, 1, 3], 1, 2); + * // => [3] + */ + var without = restParam(function(array, values) { + return isArrayLike(array) + ? baseDifference(array, values) + : []; + }); + + /** + * Creates an array of unique values that is the [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) + * of the provided arrays. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of values. + * @example + * + * _.xor([1, 2], [4, 2]); + * // => [1, 4] + */ + function xor() { + var index = -1, + length = arguments.length; + + while (++index < length) { + var array = arguments[index]; + if (isArrayLike(array)) { + var result = result + ? arrayPush(baseDifference(result, array), baseDifference(array, result)) + : array; + } + } + return result ? baseUniq(result) : []; + } + + /** + * Creates an array of grouped elements, the first of which contains the first + * elements of the given arrays, the second of which contains the second elements + * of the given arrays, and so on. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zip(['fred', 'barney'], [30, 40], [true, false]); + * // => [['fred', 30, true], ['barney', 40, false]] + */ + var zip = restParam(unzip); + + /** + * The inverse of `_.pairs`; this method returns an object composed from arrays + * of property names and values. Provide either a single two dimensional array, + * e.g. `[[key1, value1], [key2, value2]]` or two arrays, one of property names + * and one of corresponding values. + * + * @static + * @memberOf _ + * @alias object + * @category Array + * @param {Array} props The property names. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @example + * + * _.zipObject([['fred', 30], ['barney', 40]]); + * // => { 'fred': 30, 'barney': 40 } + * + * _.zipObject(['fred', 'barney'], [30, 40]); + * // => { 'fred': 30, 'barney': 40 } + */ + function zipObject(props, values) { + var index = -1, + length = props ? props.length : 0, + result = {}; + + if (length && !values && !isArray(props[0])) { + values = []; + } + while (++index < length) { + var key = props[index]; + if (values) { + result[key] = values[index]; + } else if (key) { + result[key[0]] = key[1]; + } + } + return result; + } + + /** + * This method is like `_.zip` except that it accepts an iteratee to specify + * how grouped values should be combined. The `iteratee` is bound to `thisArg` + * and invoked with four arguments: (accumulator, value, index, group). + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @param {Function} [iteratee] The function to combine grouped values. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zipWith([1, 2], [10, 20], [100, 200], _.add); + * // => [111, 222] + */ + var zipWith = restParam(function(arrays) { + var length = arrays.length, + iteratee = length > 2 ? arrays[length - 2] : undefined, + thisArg = length > 1 ? arrays[length - 1] : undefined; + + if (length > 2 && typeof iteratee == 'function') { + length -= 2; + } else { + iteratee = (length > 1 && typeof thisArg == 'function') ? (--length, thisArg) : undefined; + thisArg = undefined; + } + arrays.length = length; + return unzipWith(arrays, iteratee, thisArg); + }); + + /*------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` object that wraps `value` with explicit method + * chaining enabled. + * + * @static + * @memberOf _ + * @category Chain + * @param {*} value The value to wrap. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'pebbles', 'age': 1 } + * ]; + * + * var youngest = _.chain(users) + * .sortBy('age') + * .map(function(chr) { + * return chr.user + ' is ' + chr.age; + * }) + * .first() + * .value(); + * // => 'pebbles is 1' + */ + function chain(value) { + var result = lodash(value); + result.__chain__ = true; + return result; + } + + /** + * This method invokes `interceptor` and returns `value`. The interceptor is + * bound to `thisArg` and invoked with one argument; (value). The purpose of + * this method is to "tap into" a method chain in order to perform operations + * on intermediate results within the chain. + * + * @static + * @memberOf _ + * @category Chain + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @param {*} [thisArg] The `this` binding of `interceptor`. + * @returns {*} Returns `value`. + * @example + * + * _([1, 2, 3]) + * .tap(function(array) { + * array.pop(); + * }) + * .reverse() + * .value(); + * // => [2, 1] + */ + function tap(value, interceptor, thisArg) { + interceptor.call(thisArg, value); + return value; + } + + /** + * This method is like `_.tap` except that it returns the result of `interceptor`. + * + * @static + * @memberOf _ + * @category Chain + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @param {*} [thisArg] The `this` binding of `interceptor`. + * @returns {*} Returns the result of `interceptor`. + * @example + * + * _(' abc ') + * .chain() + * .trim() + * .thru(function(value) { + * return [value]; + * }) + * .value(); + * // => ['abc'] + */ + function thru(value, interceptor, thisArg) { + return interceptor.call(thisArg, value); + } + + /** + * Enables explicit method chaining on the wrapper object. + * + * @name chain + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * // without explicit chaining + * _(users).first(); + * // => { 'user': 'barney', 'age': 36 } + * + * // with explicit chaining + * _(users).chain() + * .first() + * .pick('user') + * .value(); + * // => { 'user': 'barney' } + */ + function wrapperChain() { + return chain(this); + } + + /** + * Executes the chained sequence and returns the wrapped result. + * + * @name commit + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2]; + * var wrapped = _(array).push(3); + * + * console.log(array); + * // => [1, 2] + * + * wrapped = wrapped.commit(); + * console.log(array); + * // => [1, 2, 3] + * + * wrapped.last(); + * // => 3 + * + * console.log(array); + * // => [1, 2, 3] + */ + function wrapperCommit() { + return new LodashWrapper(this.value(), this.__chain__); + } + + /** + * Creates a new array joining a wrapped array with any additional arrays + * and/or values. + * + * @name concat + * @memberOf _ + * @category Chain + * @param {...*} [values] The values to concatenate. + * @returns {Array} Returns the new concatenated array. + * @example + * + * var array = [1]; + * var wrapped = _(array).concat(2, [3], [[4]]); + * + * console.log(wrapped.value()); + * // => [1, 2, 3, [4]] + * + * console.log(array); + * // => [1] + */ + var wrapperConcat = restParam(function(values) { + values = baseFlatten(values); + return this.thru(function(array) { + return arrayConcat(isArray(array) ? array : [toObject(array)], values); + }); + }); + + /** + * Creates a clone of the chained sequence planting `value` as the wrapped value. + * + * @name plant + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2]; + * var wrapped = _(array).map(function(value) { + * return Math.pow(value, 2); + * }); + * + * var other = [3, 4]; + * var otherWrapped = wrapped.plant(other); + * + * otherWrapped.value(); + * // => [9, 16] + * + * wrapped.value(); + * // => [1, 4] + */ + function wrapperPlant(value) { + var result, + parent = this; + + while (parent instanceof baseLodash) { + var clone = wrapperClone(parent); + if (result) { + previous.__wrapped__ = clone; + } else { + result = clone; + } + var previous = clone; + parent = parent.__wrapped__; + } + previous.__wrapped__ = value; + return result; + } + + /** + * Reverses the wrapped array so the first element becomes the last, the + * second element becomes the second to last, and so on. + * + * **Note:** This method mutates the wrapped array. + * + * @name reverse + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new reversed `lodash` wrapper instance. + * @example + * + * var array = [1, 2, 3]; + * + * _(array).reverse().value() + * // => [3, 2, 1] + * + * console.log(array); + * // => [3, 2, 1] + */ + function wrapperReverse() { + var value = this.__wrapped__; + + var interceptor = function(value) { + return (wrapped && wrapped.__dir__ < 0) ? value : value.reverse(); + }; + if (value instanceof LazyWrapper) { + var wrapped = value; + if (this.__actions__.length) { + wrapped = new LazyWrapper(this); + } + wrapped = wrapped.reverse(); + wrapped.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined }); + return new LodashWrapper(wrapped, this.__chain__); + } + return this.thru(interceptor); + } + + /** + * Produces the result of coercing the unwrapped value to a string. + * + * @name toString + * @memberOf _ + * @category Chain + * @returns {string} Returns the coerced string value. + * @example + * + * _([1, 2, 3]).toString(); + * // => '1,2,3' + */ + function wrapperToString() { + return (this.value() + ''); + } + + /** + * Executes the chained sequence to extract the unwrapped value. + * + * @name value + * @memberOf _ + * @alias run, toJSON, valueOf + * @category Chain + * @returns {*} Returns the resolved unwrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperValue() { + return baseWrapperValue(this.__wrapped__, this.__actions__); + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates an array of elements corresponding to the given keys, or indexes, + * of `collection`. Keys may be specified as individual arguments or as arrays + * of keys. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {...(number|number[]|string|string[])} [props] The property names + * or indexes of elements to pick, specified individually or in arrays. + * @returns {Array} Returns the new array of picked elements. + * @example + * + * _.at(['a', 'b', 'c'], [0, 2]); + * // => ['a', 'c'] + * + * _.at(['barney', 'fred', 'pebbles'], 0, 2); + * // => ['barney', 'pebbles'] + */ + var at = restParam(function(collection, props) { + return baseAt(collection, baseFlatten(props)); + }); + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` through `iteratee`. The corresponding value + * of each key is the number of times the key was returned by `iteratee`. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.countBy([4.3, 6.1, 6.4], function(n) { + * return Math.floor(n); + * }); + * // => { '4': 1, '6': 2 } + * + * _.countBy([4.3, 6.1, 6.4], function(n) { + * return this.floor(n); + * }, Math); + * // => { '4': 1, '6': 2 } + * + * _.countBy(['one', 'two', 'three'], 'length'); + * // => { '3': 2, '5': 1 } + */ + var countBy = createAggregator(function(result, value, key) { + hasOwnProperty.call(result, key) ? ++result[key] : (result[key] = 1); + }); + + /** + * Checks if `predicate` returns truthy for **all** elements of `collection`. + * The predicate is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias all + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.every(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // using the `_.matchesProperty` callback shorthand + * _.every(users, 'active', false); + * // => true + * + * // using the `_.property` callback shorthand + * _.every(users, 'active'); + * // => false + */ + function every(collection, predicate, thisArg) { + var func = isArray(collection) ? arrayEvery : baseEvery; + if (thisArg && isIterateeCall(collection, predicate, thisArg)) { + predicate = undefined; + } + if (typeof predicate != 'function' || thisArg !== undefined) { + predicate = getCallback(predicate, thisArg, 3); + } + return func(collection, predicate); + } + + /** + * Iterates over elements of `collection`, returning an array of all elements + * `predicate` returns truthy for. The predicate is bound to `thisArg` and + * invoked with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias select + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the new filtered array. + * @example + * + * _.filter([4, 5, 6], function(n) { + * return n % 2 == 0; + * }); + * // => [4, 6] + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user'); + * // => ['barney'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.filter(users, 'active', false), 'user'); + * // => ['fred'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.filter(users, 'active'), 'user'); + * // => ['barney'] + */ + function filter(collection, predicate, thisArg) { + var func = isArray(collection) ? arrayFilter : baseFilter; + predicate = getCallback(predicate, thisArg, 3); + return func(collection, predicate); + } + + /** + * Iterates over elements of `collection`, returning the first element + * `predicate` returns truthy for. The predicate is bound to `thisArg` and + * invoked with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias detect + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false }, + * { 'user': 'pebbles', 'age': 1, 'active': true } + * ]; + * + * _.result(_.find(users, function(chr) { + * return chr.age < 40; + * }), 'user'); + * // => 'barney' + * + * // using the `_.matches` callback shorthand + * _.result(_.find(users, { 'age': 1, 'active': true }), 'user'); + * // => 'pebbles' + * + * // using the `_.matchesProperty` callback shorthand + * _.result(_.find(users, 'active', false), 'user'); + * // => 'fred' + * + * // using the `_.property` callback shorthand + * _.result(_.find(users, 'active'), 'user'); + * // => 'barney' + */ + var find = createFind(baseEach); + + /** + * This method is like `_.find` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * _.findLast([1, 2, 3, 4], function(n) { + * return n % 2 == 1; + * }); + * // => 3 + */ + var findLast = createFind(baseEachRight, true); + + /** + * Performs a deep comparison between each element in `collection` and the + * source object, returning the first element that has equivalent property + * values. + * + * **Note:** This method supports comparing arrays, booleans, `Date` objects, + * numbers, `Object` objects, regexes, and strings. Objects are compared by + * their own, not inherited, enumerable properties. For comparing a single + * own or inherited property value see `_.matchesProperty`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Object} source The object of property values to match. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * _.result(_.findWhere(users, { 'age': 36, 'active': true }), 'user'); + * // => 'barney' + * + * _.result(_.findWhere(users, { 'age': 40, 'active': false }), 'user'); + * // => 'fred' + */ + function findWhere(collection, source) { + return find(collection, baseMatches(source)); + } + + /** + * Iterates over elements of `collection` invoking `iteratee` for each element. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). Iteratee functions may exit iteration early + * by explicitly returning `false`. + * + * **Note:** As with other "Collections" methods, objects with a "length" property + * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` + * may be used for object iteration. + * + * @static + * @memberOf _ + * @alias each + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array|Object|string} Returns `collection`. + * @example + * + * _([1, 2]).forEach(function(n) { + * console.log(n); + * }).value(); + * // => logs each value from left to right and returns the array + * + * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) { + * console.log(n, key); + * }); + * // => logs each value-key pair and returns the object (iteration order is not guaranteed) + */ + var forEach = createForEach(arrayEach, baseEach); + + /** + * This method is like `_.forEach` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @alias eachRight + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array|Object|string} Returns `collection`. + * @example + * + * _([1, 2]).forEachRight(function(n) { + * console.log(n); + * }).value(); + * // => logs each value from right to left and returns the array + */ + var forEachRight = createForEach(arrayEachRight, baseEachRight); + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` through `iteratee`. The corresponding value + * of each key is an array of the elements responsible for generating the key. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.groupBy([4.2, 6.1, 6.4], function(n) { + * return Math.floor(n); + * }); + * // => { '4': [4.2], '6': [6.1, 6.4] } + * + * _.groupBy([4.2, 6.1, 6.4], function(n) { + * return this.floor(n); + * }, Math); + * // => { '4': [4.2], '6': [6.1, 6.4] } + * + * // using the `_.property` callback shorthand + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + var groupBy = createAggregator(function(result, value, key) { + if (hasOwnProperty.call(result, key)) { + result[key].push(value); + } else { + result[key] = [value]; + } + }); + + /** + * Checks if `value` is in `collection` using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. If `fromIndex` is negative, it is used as the offset + * from the end of `collection`. + * + * @static + * @memberOf _ + * @alias contains, include + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {*} target The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. + * @returns {boolean} Returns `true` if a matching element is found, else `false`. + * @example + * + * _.includes([1, 2, 3], 1); + * // => true + * + * _.includes([1, 2, 3], 1, 2); + * // => false + * + * _.includes({ 'user': 'fred', 'age': 40 }, 'fred'); + * // => true + * + * _.includes('pebbles', 'eb'); + * // => true + */ + function includes(collection, target, fromIndex, guard) { + var length = collection ? getLength(collection) : 0; + if (!isLength(length)) { + collection = values(collection); + length = collection.length; + } + if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) { + fromIndex = 0; + } else { + fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0); + } + return (typeof collection == 'string' || !isArray(collection) && isString(collection)) + ? (fromIndex <= length && collection.indexOf(target, fromIndex) > -1) + : (!!length && getIndexOf(collection, target, fromIndex) > -1); + } + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` through `iteratee`. The corresponding value + * of each key is the last element responsible for generating the key. The + * iteratee function is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * var keyData = [ + * { 'dir': 'left', 'code': 97 }, + * { 'dir': 'right', 'code': 100 } + * ]; + * + * _.indexBy(keyData, 'dir'); + * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } + * + * _.indexBy(keyData, function(object) { + * return String.fromCharCode(object.code); + * }); + * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } + * + * _.indexBy(keyData, function(object) { + * return this.fromCharCode(object.code); + * }, String); + * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } + */ + var indexBy = createAggregator(function(result, value, key) { + result[key] = value; + }); + + /** + * Invokes the method at `path` of each element in `collection`, returning + * an array of the results of each invoked method. Any additional arguments + * are provided to each invoked method. If `methodName` is a function it is + * invoked for, and `this` bound to, each element in `collection`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Array|Function|string} path The path of the method to invoke or + * the function invoked per iteration. + * @param {...*} [args] The arguments to invoke the method with. + * @returns {Array} Returns the array of results. + * @example + * + * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invoke([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ + var invoke = restParam(function(collection, path, args) { + var index = -1, + isFunc = typeof path == 'function', + isProp = isKey(path), + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value) { + var func = isFunc ? path : ((isProp && value != null) ? value[path] : undefined); + result[++index] = func ? func.apply(value, args) : invokePath(value, path, args); + }); + return result; + }); + + /** + * Creates an array of values by running each element in `collection` through + * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three + * arguments: (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. + * + * The guarded methods are: + * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`, + * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`, + * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`, + * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`, + * `sum`, `uniq`, and `words` + * + * @static + * @memberOf _ + * @alias collect + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new mapped array. + * @example + * + * function timesThree(n) { + * return n * 3; + * } + * + * _.map([1, 2], timesThree); + * // => [3, 6] + * + * _.map({ 'a': 1, 'b': 2 }, timesThree); + * // => [3, 6] (iteration order is not guaranteed) + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * // using the `_.property` callback shorthand + * _.map(users, 'user'); + * // => ['barney', 'fred'] + */ + function map(collection, iteratee, thisArg) { + var func = isArray(collection) ? arrayMap : baseMap; + iteratee = getCallback(iteratee, thisArg, 3); + return func(collection, iteratee); + } + + /** + * Creates an array of elements split into two groups, the first of which + * contains elements `predicate` returns truthy for, while the second of which + * contains elements `predicate` returns falsey for. The predicate is bound + * to `thisArg` and invoked with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the array of grouped elements. + * @example + * + * _.partition([1, 2, 3], function(n) { + * return n % 2; + * }); + * // => [[1, 3], [2]] + * + * _.partition([1.2, 2.3, 3.4], function(n) { + * return this.floor(n) % 2; + * }, Math); + * // => [[1.2, 3.4], [2.3]] + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true }, + * { 'user': 'pebbles', 'age': 1, 'active': false } + * ]; + * + * var mapper = function(array) { + * return _.pluck(array, 'user'); + * }; + * + * // using the `_.matches` callback shorthand + * _.map(_.partition(users, { 'age': 1, 'active': false }), mapper); + * // => [['pebbles'], ['barney', 'fred']] + * + * // using the `_.matchesProperty` callback shorthand + * _.map(_.partition(users, 'active', false), mapper); + * // => [['barney', 'pebbles'], ['fred']] + * + * // using the `_.property` callback shorthand + * _.map(_.partition(users, 'active'), mapper); + * // => [['fred'], ['barney', 'pebbles']] + */ + var partition = createAggregator(function(result, value, key) { + result[key ? 0 : 1].push(value); + }, function() { return [[], []]; }); + + /** + * Gets the property value of `path` from all elements in `collection`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Array|string} path The path of the property to pluck. + * @returns {Array} Returns the property values. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * _.pluck(users, 'user'); + * // => ['barney', 'fred'] + * + * var userIndex = _.indexBy(users, 'user'); + * _.pluck(userIndex, 'age'); + * // => [36, 40] (iteration order is not guaranteed) + */ + function pluck(collection, path) { + return map(collection, property(path)); + } + + /** + * Reduces `collection` to a value which is the accumulated result of running + * each element in `collection` through `iteratee`, where each successive + * invocation is supplied the return value of the previous. If `accumulator` + * is not provided the first element of `collection` is used as the initial + * value. The `iteratee` is bound to `thisArg` and invoked with four arguments: + * (accumulator, value, index|key, collection). + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.reduce`, `_.reduceRight`, and `_.transform`. + * + * The guarded methods are: + * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `sortByAll`, + * and `sortByOrder` + * + * @static + * @memberOf _ + * @alias foldl, inject + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {*} Returns the accumulated value. + * @example + * + * _.reduce([1, 2], function(total, n) { + * return total + n; + * }); + * // => 3 + * + * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) { + * result[key] = n * 3; + * return result; + * }, {}); + * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed) + */ + var reduce = createReduce(arrayReduce, baseEach); + + /** + * This method is like `_.reduce` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @alias foldr + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {*} Returns the accumulated value. + * @example + * + * var array = [[0, 1], [2, 3], [4, 5]]; + * + * _.reduceRight(array, function(flattened, other) { + * return flattened.concat(other); + * }, []); + * // => [4, 5, 2, 3, 0, 1] + */ + var reduceRight = createReduce(arrayReduceRight, baseEachRight); + + /** + * The opposite of `_.filter`; this method returns the elements of `collection` + * that `predicate` does **not** return truthy for. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the new filtered array. + * @example + * + * _.reject([1, 2, 3, 4], function(n) { + * return n % 2 == 0; + * }); + * // => [1, 3] + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user'); + * // => ['barney'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.reject(users, 'active', false), 'user'); + * // => ['fred'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.reject(users, 'active'), 'user'); + * // => ['barney'] + */ + function reject(collection, predicate, thisArg) { + var func = isArray(collection) ? arrayFilter : baseFilter; + predicate = getCallback(predicate, thisArg, 3); + return func(collection, function(value, index, collection) { + return !predicate(value, index, collection); + }); + } + + /** + * Gets a random element or `n` random elements from a collection. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to sample. + * @param {number} [n] The number of elements to sample. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {*} Returns the random sample(s). + * @example + * + * _.sample([1, 2, 3, 4]); + * // => 2 + * + * _.sample([1, 2, 3, 4], 2); + * // => [3, 1] + */ + function sample(collection, n, guard) { + if (guard ? isIterateeCall(collection, n, guard) : n == null) { + collection = toIterable(collection); + var length = collection.length; + return length > 0 ? collection[baseRandom(0, length - 1)] : undefined; + } + var index = -1, + result = toArray(collection), + length = result.length, + lastIndex = length - 1; + + n = nativeMin(n < 0 ? 0 : (+n || 0), length); + while (++index < n) { + var rand = baseRandom(index, lastIndex), + value = result[rand]; + + result[rand] = result[index]; + result[index] = value; + } + result.length = n; + return result; + } + + /** + * Creates an array of shuffled values, using a version of the + * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to shuffle. + * @returns {Array} Returns the new shuffled array. + * @example + * + * _.shuffle([1, 2, 3, 4]); + * // => [4, 1, 3, 2] + */ + function shuffle(collection) { + return sample(collection, POSITIVE_INFINITY); + } + + /** + * Gets the size of `collection` by returning its length for array-like + * values or the number of own enumerable properties for objects. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @returns {number} Returns the size of `collection`. + * @example + * + * _.size([1, 2, 3]); + * // => 3 + * + * _.size({ 'a': 1, 'b': 2 }); + * // => 2 + * + * _.size('pebbles'); + * // => 7 + */ + function size(collection) { + var length = collection ? getLength(collection) : 0; + return isLength(length) ? length : keys(collection).length; + } + + /** + * Checks if `predicate` returns truthy for **any** element of `collection`. + * The function returns as soon as it finds a passing value and does not iterate + * over the entire collection. The predicate is bound to `thisArg` and invoked + * with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias any + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + * @example + * + * _.some([null, 0, 'yes', false], Boolean); + * // => true + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.some(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // using the `_.matchesProperty` callback shorthand + * _.some(users, 'active', false); + * // => true + * + * // using the `_.property` callback shorthand + * _.some(users, 'active'); + * // => true + */ + function some(collection, predicate, thisArg) { + var func = isArray(collection) ? arraySome : baseSome; + if (thisArg && isIterateeCall(collection, predicate, thisArg)) { + predicate = undefined; + } + if (typeof predicate != 'function' || thisArg !== undefined) { + predicate = getCallback(predicate, thisArg, 3); + } + return func(collection, predicate); + } + + /** + * Creates an array of elements, sorted in ascending order by the results of + * running each element in a collection through `iteratee`. This method performs + * a stable sort, that is, it preserves the original sort order of equal elements. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new sorted array. + * @example + * + * _.sortBy([1, 2, 3], function(n) { + * return Math.sin(n); + * }); + * // => [3, 1, 2] + * + * _.sortBy([1, 2, 3], function(n) { + * return this.sin(n); + * }, Math); + * // => [3, 1, 2] + * + * var users = [ + * { 'user': 'fred' }, + * { 'user': 'pebbles' }, + * { 'user': 'barney' } + * ]; + * + * // using the `_.property` callback shorthand + * _.pluck(_.sortBy(users, 'user'), 'user'); + * // => ['barney', 'fred', 'pebbles'] + */ + function sortBy(collection, iteratee, thisArg) { + if (collection == null) { + return []; + } + if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { + iteratee = undefined; + } + var index = -1; + iteratee = getCallback(iteratee, thisArg, 3); + + var result = baseMap(collection, function(value, key, collection) { + return { 'criteria': iteratee(value, key, collection), 'index': ++index, 'value': value }; + }); + return baseSortBy(result, compareAscending); + } + + /** + * This method is like `_.sortBy` except that it can sort by multiple iteratees + * or property names. + * + * If a property name is provided for an iteratee the created `_.property` + * style callback returns the property value of the given element. + * + * If an object is provided for an iteratee the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {...(Function|Function[]|Object|Object[]|string|string[])} iteratees + * The iteratees to sort by, specified as individual values or arrays of values. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 42 }, + * { 'user': 'barney', 'age': 34 } + * ]; + * + * _.map(_.sortByAll(users, ['user', 'age']), _.values); + * // => [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]] + * + * _.map(_.sortByAll(users, 'user', function(chr) { + * return Math.floor(chr.age / 10); + * }), _.values); + * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] + */ + var sortByAll = restParam(function(collection, iteratees) { + if (collection == null) { + return []; + } + var guard = iteratees[2]; + if (guard && isIterateeCall(iteratees[0], iteratees[1], guard)) { + iteratees.length = 1; + } + return baseSortByOrder(collection, baseFlatten(iteratees), []); + }); + + /** + * This method is like `_.sortByAll` except that it allows specifying the + * sort orders of the iteratees to sort by. If `orders` is unspecified, all + * values are sorted in ascending order. Otherwise, a value is sorted in + * ascending order if its corresponding order is "asc", and descending if "desc". + * + * If a property name is provided for an iteratee the created `_.property` + * style callback returns the property value of the given element. + * + * If an object is provided for an iteratee the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {boolean[]} [orders] The sort orders of `iteratees`. + * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 34 }, + * { 'user': 'fred', 'age': 42 }, + * { 'user': 'barney', 'age': 36 } + * ]; + * + * // sort by `user` in ascending order and by `age` in descending order + * _.map(_.sortByOrder(users, ['user', 'age'], ['asc', 'desc']), _.values); + * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] + */ + function sortByOrder(collection, iteratees, orders, guard) { + if (collection == null) { + return []; + } + if (guard && isIterateeCall(iteratees, orders, guard)) { + orders = undefined; + } + if (!isArray(iteratees)) { + iteratees = iteratees == null ? [] : [iteratees]; + } + if (!isArray(orders)) { + orders = orders == null ? [] : [orders]; + } + return baseSortByOrder(collection, iteratees, orders); + } + + /** + * Performs a deep comparison between each element in `collection` and the + * source object, returning an array of all elements that have equivalent + * property values. + * + * **Note:** This method supports comparing arrays, booleans, `Date` objects, + * numbers, `Object` objects, regexes, and strings. Objects are compared by + * their own, not inherited, enumerable properties. For comparing a single + * own or inherited property value see `_.matchesProperty`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Object} source The object of property values to match. + * @returns {Array} Returns the new filtered array. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false, 'pets': ['hoppy'] }, + * { 'user': 'fred', 'age': 40, 'active': true, 'pets': ['baby puss', 'dino'] } + * ]; + * + * _.pluck(_.where(users, { 'age': 36, 'active': false }), 'user'); + * // => ['barney'] + * + * _.pluck(_.where(users, { 'pets': ['dino'] }), 'user'); + * // => ['fred'] + */ + function where(collection, source) { + return filter(collection, baseMatches(source)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Gets the number of milliseconds that have elapsed since the Unix epoch + * (1 January 1970 00:00:00 UTC). + * + * @static + * @memberOf _ + * @category Date + * @example + * + * _.defer(function(stamp) { + * console.log(_.now() - stamp); + * }, _.now()); + * // => logs the number of milliseconds it took for the deferred function to be invoked + */ + var now = nativeNow || function() { + return new Date().getTime(); + }; + + /*------------------------------------------------------------------------*/ + + /** + * The opposite of `_.before`; this method creates a function that invokes + * `func` once it is called `n` or more times. + * + * @static + * @memberOf _ + * @category Function + * @param {number} n The number of calls before `func` is invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var saves = ['profile', 'settings']; + * + * var done = _.after(saves.length, function() { + * console.log('done saving!'); + * }); + * + * _.forEach(saves, function(type) { + * asyncSave({ 'type': type, 'complete': done }); + * }); + * // => logs 'done saving!' after the two async saves have completed + */ + function after(n, func) { + if (typeof func != 'function') { + if (typeof n == 'function') { + var temp = n; + n = func; + func = temp; + } else { + throw new TypeError(FUNC_ERROR_TEXT); + } + } + n = nativeIsFinite(n = +n) ? n : 0; + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } + }; + } + + /** + * Creates a function that accepts up to `n` arguments ignoring any + * additional arguments. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to cap arguments for. + * @param {number} [n=func.length] The arity cap. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Function} Returns the new function. + * @example + * + * _.map(['6', '8', '10'], _.ary(parseInt, 1)); + * // => [6, 8, 10] + */ + function ary(func, n, guard) { + if (guard && isIterateeCall(func, n, guard)) { + n = undefined; + } + n = (func && n == null) ? func.length : nativeMax(+n || 0, 0); + return createWrapper(func, ARY_FLAG, undefined, undefined, undefined, undefined, n); + } + + /** + * Creates a function that invokes `func`, with the `this` binding and arguments + * of the created function, while it is called less than `n` times. Subsequent + * calls to the created function return the result of the last `func` invocation. + * + * @static + * @memberOf _ + * @category Function + * @param {number} n The number of calls at which `func` is no longer invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * jQuery('#add').on('click', _.before(5, addContactToList)); + * // => allows adding up to 4 contacts to the list + */ + function before(n, func) { + var result; + if (typeof func != 'function') { + if (typeof n == 'function') { + var temp = n; + n = func; + func = temp; + } else { + throw new TypeError(FUNC_ERROR_TEXT); + } + } + return function() { + if (--n > 0) { + result = func.apply(this, arguments); + } + if (n <= 1) { + func = undefined; + } + return result; + }; + } + + /** + * Creates a function that invokes `func` with the `this` binding of `thisArg` + * and prepends any additional `_.bind` arguments to those provided to the + * bound function. + * + * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for partially applied arguments. + * + * **Note:** Unlike native `Function#bind` this method does not set the "length" + * property of bound functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var greet = function(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * }; + * + * var object = { 'user': 'fred' }; + * + * var bound = _.bind(greet, object, 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * // using placeholders + * var bound = _.bind(greet, object, _, '!'); + * bound('hi'); + * // => 'hi fred!' + */ + var bind = restParam(function(func, thisArg, partials) { + var bitmask = BIND_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, bind.placeholder); + bitmask |= PARTIAL_FLAG; + } + return createWrapper(func, bitmask, thisArg, partials, holders); + }); + + /** + * Binds methods of an object to the object itself, overwriting the existing + * method. Method names may be specified as individual arguments or as arrays + * of method names. If no method names are provided all enumerable function + * properties, own and inherited, of `object` are bound. + * + * **Note:** This method does not set the "length" property of bound functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Object} object The object to bind and assign the bound methods to. + * @param {...(string|string[])} [methodNames] The object method names to bind, + * specified as individual method names or arrays of method names. + * @returns {Object} Returns `object`. + * @example + * + * var view = { + * 'label': 'docs', + * 'onClick': function() { + * console.log('clicked ' + this.label); + * } + * }; + * + * _.bindAll(view); + * jQuery('#docs').on('click', view.onClick); + * // => logs 'clicked docs' when the element is clicked + */ + var bindAll = restParam(function(object, methodNames) { + methodNames = methodNames.length ? baseFlatten(methodNames) : functions(object); + + var index = -1, + length = methodNames.length; + + while (++index < length) { + var key = methodNames[index]; + object[key] = createWrapper(object[key], BIND_FLAG, object); + } + return object; + }); + + /** + * Creates a function that invokes the method at `object[key]` and prepends + * any additional `_.bindKey` arguments to those provided to the bound function. + * + * This method differs from `_.bind` by allowing bound functions to reference + * methods that may be redefined or don't yet exist. + * See [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) + * for more details. + * + * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * @static + * @memberOf _ + * @category Function + * @param {Object} object The object the method belongs to. + * @param {string} key The key of the method. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var object = { + * 'user': 'fred', + * 'greet': function(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * } + * }; + * + * var bound = _.bindKey(object, 'greet', 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * object.greet = function(greeting, punctuation) { + * return greeting + 'ya ' + this.user + punctuation; + * }; + * + * bound('!'); + * // => 'hiya fred!' + * + * // using placeholders + * var bound = _.bindKey(object, 'greet', _, '!'); + * bound('hi'); + * // => 'hiya fred!' + */ + var bindKey = restParam(function(object, key, partials) { + var bitmask = BIND_FLAG | BIND_KEY_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, bindKey.placeholder); + bitmask |= PARTIAL_FLAG; + } + return createWrapper(key, bitmask, object, partials, holders); + }); + + /** + * Creates a function that accepts one or more arguments of `func` that when + * called either invokes `func` returning its result, if all `func` arguments + * have been provided, or returns a function that accepts one or more of the + * remaining `func` arguments, and so on. The arity of `func` may be specified + * if `func.length` is not sufficient. + * + * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for provided arguments. + * + * **Note:** This method does not set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curry(abc); + * + * curried(1)(2)(3); + * // => [1, 2, 3] + * + * curried(1, 2)(3); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // using placeholders + * curried(1)(_, 3)(2); + * // => [1, 2, 3] + */ + var curry = createCurry(CURRY_FLAG); + + /** + * This method is like `_.curry` except that arguments are applied to `func` + * in the manner of `_.partialRight` instead of `_.partial`. + * + * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for provided arguments. + * + * **Note:** This method does not set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curryRight(abc); + * + * curried(3)(2)(1); + * // => [1, 2, 3] + * + * curried(2, 3)(1); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // using placeholders + * curried(3)(1, _)(2); + * // => [1, 2, 3] + */ + var curryRight = createCurry(CURRY_RIGHT_FLAG); + + /** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked. The debounced function comes with a `cancel` method to cancel + * delayed invocations. Provide an options object to indicate that `func` + * should be invoked on the leading and/or trailing edge of the `wait` timeout. + * Subsequent calls to the debounced function return the result of the last + * `func` invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked + * on the trailing edge of the timeout only if the the debounced function is + * invoked more than once during the `wait` timeout. + * + * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) + * for details over the differences between `_.debounce` and `_.throttle`. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] The number of milliseconds to delay. + * @param {Object} [options] The options object. + * @param {boolean} [options.leading=false] Specify invoking on the leading + * edge of the timeout. + * @param {number} [options.maxWait] The maximum time `func` is allowed to be + * delayed before it is invoked. + * @param {boolean} [options.trailing=true] Specify invoking on the trailing + * edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // avoid costly calculations while the window size is in flux + * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); + * + * // invoke `sendMail` when the click event is fired, debouncing subsequent calls + * jQuery('#postbox').on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })); + * + * // ensure `batchLog` is invoked once after 1 second of debounced calls + * var source = new EventSource('/stream'); + * jQuery(source).on('message', _.debounce(batchLog, 250, { + * 'maxWait': 1000 + * })); + * + * // cancel a debounced call + * var todoChanges = _.debounce(batchLog, 1000); + * Object.observe(models.todo, todoChanges); + * + * Object.observe(models, function(changes) { + * if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) { + * todoChanges.cancel(); + * } + * }, ['delete']); + * + * // ...at some point `models.todo` is changed + * models.todo.completed = true; + * + * // ...before 1 second has passed `models.todo` is deleted + * // which cancels the debounced `todoChanges` call + * delete models.todo; + */ + function debounce(func, wait, options) { + var args, + maxTimeoutId, + result, + stamp, + thisArg, + timeoutId, + trailingCall, + lastCalled = 0, + maxWait = false, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + wait = wait < 0 ? 0 : (+wait || 0); + if (options === true) { + var leading = true; + trailing = false; + } else if (isObject(options)) { + leading = !!options.leading; + maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait); + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + function cancel() { + if (timeoutId) { + clearTimeout(timeoutId); + } + if (maxTimeoutId) { + clearTimeout(maxTimeoutId); + } + lastCalled = 0; + maxTimeoutId = timeoutId = trailingCall = undefined; + } + + function complete(isCalled, id) { + if (id) { + clearTimeout(id); + } + maxTimeoutId = timeoutId = trailingCall = undefined; + if (isCalled) { + lastCalled = now(); + result = func.apply(thisArg, args); + if (!timeoutId && !maxTimeoutId) { + args = thisArg = undefined; + } + } + } + + function delayed() { + var remaining = wait - (now() - stamp); + if (remaining <= 0 || remaining > wait) { + complete(trailingCall, maxTimeoutId); + } else { + timeoutId = setTimeout(delayed, remaining); + } + } + + function maxDelayed() { + complete(trailing, timeoutId); + } + + function debounced() { + args = arguments; + stamp = now(); + thisArg = this; + trailingCall = trailing && (timeoutId || !leading); + + if (maxWait === false) { + var leadingCall = leading && !timeoutId; + } else { + if (!maxTimeoutId && !leading) { + lastCalled = stamp; + } + var remaining = maxWait - (stamp - lastCalled), + isCalled = remaining <= 0 || remaining > maxWait; + + if (isCalled) { + if (maxTimeoutId) { + maxTimeoutId = clearTimeout(maxTimeoutId); + } + lastCalled = stamp; + result = func.apply(thisArg, args); + } + else if (!maxTimeoutId) { + maxTimeoutId = setTimeout(maxDelayed, remaining); + } + } + if (isCalled && timeoutId) { + timeoutId = clearTimeout(timeoutId); + } + else if (!timeoutId && wait !== maxWait) { + timeoutId = setTimeout(delayed, wait); + } + if (leadingCall) { + isCalled = true; + result = func.apply(thisArg, args); + } + if (isCalled && !timeoutId && !maxTimeoutId) { + args = thisArg = undefined; + } + return result; + } + debounced.cancel = cancel; + return debounced; + } + + /** + * Defers invoking the `func` until the current call stack has cleared. Any + * additional arguments are provided to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to defer. + * @param {...*} [args] The arguments to invoke the function with. + * @returns {number} Returns the timer id. + * @example + * + * _.defer(function(text) { + * console.log(text); + * }, 'deferred'); + * // logs 'deferred' after one or more milliseconds + */ + var defer = restParam(function(func, args) { + return baseDelay(func, 1, args); + }); + + /** + * Invokes `func` after `wait` milliseconds. Any additional arguments are + * provided to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {...*} [args] The arguments to invoke the function with. + * @returns {number} Returns the timer id. + * @example + * + * _.delay(function(text) { + * console.log(text); + * }, 1000, 'later'); + * // => logs 'later' after one second + */ + var delay = restParam(function(func, wait, args) { + return baseDelay(func, wait, args); + }); + + /** + * Creates a function that returns the result of invoking the provided + * functions with the `this` binding of the created function, where each + * successive invocation is supplied the return value of the previous. + * + * @static + * @memberOf _ + * @category Function + * @param {...Function} [funcs] Functions to invoke. + * @returns {Function} Returns the new function. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var addSquare = _.flow(_.add, square); + * addSquare(1, 2); + * // => 9 + */ + var flow = createFlow(); + + /** + * This method is like `_.flow` except that it creates a function that + * invokes the provided functions from right to left. + * + * @static + * @memberOf _ + * @alias backflow, compose + * @category Function + * @param {...Function} [funcs] Functions to invoke. + * @returns {Function} Returns the new function. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var addSquare = _.flowRight(square, _.add); + * addSquare(1, 2); + * // => 9 + */ + var flowRight = createFlow(true); + + /** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is coerced to a string and used as the + * cache key. The `func` is invoked with the `this` binding of the memoized + * function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object) + * method interface of `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoizing function. + * @example + * + * var upperCase = _.memoize(function(string) { + * return string.toUpperCase(); + * }); + * + * upperCase('fred'); + * // => 'FRED' + * + * // modifying the result cache + * upperCase.cache.set('fred', 'BARNEY'); + * upperCase('fred'); + * // => 'BARNEY' + * + * // replacing `_.memoize.Cache` + * var object = { 'user': 'fred' }; + * var other = { 'user': 'barney' }; + * var identity = _.memoize(_.identity); + * + * identity(object); + * // => { 'user': 'fred' } + * identity(other); + * // => { 'user': 'fred' } + * + * _.memoize.Cache = WeakMap; + * var identity = _.memoize(_.identity); + * + * identity(object); + * // => { 'user': 'fred' } + * identity(other); + * // => { 'user': 'barney' } + */ + function memoize(func, resolver) { + if (typeof func != 'function' || (resolver && typeof resolver != 'function')) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var memoized = function() { + var args = arguments, + key = resolver ? resolver.apply(this, args) : args[0], + cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result); + return result; + }; + memoized.cache = new memoize.Cache; + return memoized; + } + + /** + * Creates a function that runs each argument through a corresponding + * transform function. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to wrap. + * @param {...(Function|Function[])} [transforms] The functions to transform + * arguments, specified as individual functions or arrays of functions. + * @returns {Function} Returns the new function. + * @example + * + * function doubled(n) { + * return n * 2; + * } + * + * function square(n) { + * return n * n; + * } + * + * var modded = _.modArgs(function(x, y) { + * return [x, y]; + * }, square, doubled); + * + * modded(1, 2); + * // => [1, 4] + * + * modded(5, 10); + * // => [25, 20] + */ + var modArgs = restParam(function(func, transforms) { + transforms = baseFlatten(transforms); + if (typeof func != 'function' || !arrayEvery(transforms, baseIsFunction)) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var length = transforms.length; + return restParam(function(args) { + var index = nativeMin(args.length, length); + while (index--) { + args[index] = transforms[index](args[index]); + } + return func.apply(this, args); + }); + }); + + /** + * Creates a function that negates the result of the predicate `func`. The + * `func` predicate is invoked with the `this` binding and arguments of the + * created function. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} predicate The predicate to negate. + * @returns {Function} Returns the new function. + * @example + * + * function isEven(n) { + * return n % 2 == 0; + * } + * + * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); + * // => [1, 3, 5] + */ + function negate(predicate) { + if (typeof predicate != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return function() { + return !predicate.apply(this, arguments); + }; + } + + /** + * Creates a function that is restricted to invoking `func` once. Repeat calls + * to the function return the value of the first call. The `func` is invoked + * with the `this` binding and arguments of the created function. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // `initialize` invokes `createApplication` once + */ + function once(func) { + return before(2, func); + } + + /** + * Creates a function that invokes `func` with `partial` arguments prepended + * to those provided to the new function. This method is like `_.bind` except + * it does **not** alter the `this` binding. + * + * The `_.partial.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method does not set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { + * return greeting + ' ' + name; + * }; + * + * var sayHelloTo = _.partial(greet, 'hello'); + * sayHelloTo('fred'); + * // => 'hello fred' + * + * // using placeholders + * var greetFred = _.partial(greet, _, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + */ + var partial = createPartial(PARTIAL_FLAG); + + /** + * This method is like `_.partial` except that partially applied arguments + * are appended to those provided to the new function. + * + * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method does not set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { + * return greeting + ' ' + name; + * }; + * + * var greetFred = _.partialRight(greet, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + * + * // using placeholders + * var sayHelloTo = _.partialRight(greet, 'hello', _); + * sayHelloTo('fred'); + * // => 'hello fred' + */ + var partialRight = createPartial(PARTIAL_RIGHT_FLAG); + + /** + * Creates a function that invokes `func` with arguments arranged according + * to the specified indexes where the argument value at the first index is + * provided as the first argument, the argument value at the second index is + * provided as the second argument, and so on. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to rearrange arguments for. + * @param {...(number|number[])} indexes The arranged argument indexes, + * specified as individual indexes or arrays of indexes. + * @returns {Function} Returns the new function. + * @example + * + * var rearged = _.rearg(function(a, b, c) { + * return [a, b, c]; + * }, 2, 0, 1); + * + * rearged('b', 'c', 'a') + * // => ['a', 'b', 'c'] + * + * var map = _.rearg(_.map, [1, 0]); + * map(function(n) { + * return n * 3; + * }, [1, 2, 3]); + * // => [3, 6, 9] + */ + var rearg = restParam(function(func, indexes) { + return createWrapper(func, REARG_FLAG, undefined, undefined, undefined, baseFlatten(indexes)); + }); + + /** + * Creates a function that invokes `func` with the `this` binding of the + * created function and arguments from `start` and beyond provided as an array. + * + * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters). + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.restParam(function(what, names) { + * return what + ' ' + _.initial(names).join(', ') + + * (_.size(names) > 1 ? ', & ' : '') + _.last(names); + * }); + * + * say('hello', 'fred', 'barney', 'pebbles'); + * // => 'hello fred, barney, & pebbles' + */ + function restParam(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0); + return function() { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + rest = Array(length); + + while (++index < length) { + rest[index] = args[start + index]; + } + switch (start) { + case 0: return func.call(this, rest); + case 1: return func.call(this, args[0], rest); + case 2: return func.call(this, args[0], args[1], rest); + } + var otherArgs = Array(start + 1); + index = -1; + while (++index < start) { + otherArgs[index] = args[index]; + } + otherArgs[start] = rest; + return func.apply(this, otherArgs); + }; + } + + /** + * Creates a function that invokes `func` with the `this` binding of the created + * function and an array of arguments much like [`Function#apply`](https://es5.github.io/#x15.3.4.3). + * + * **Note:** This method is based on the [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator). + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to spread arguments over. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.spread(function(who, what) { + * return who + ' says ' + what; + * }); + * + * say(['fred', 'hello']); + * // => 'fred says hello' + * + * // with a Promise + * var numbers = Promise.all([ + * Promise.resolve(40), + * Promise.resolve(36) + * ]); + * + * numbers.then(_.spread(function(x, y) { + * return x + y; + * })); + * // => a Promise of 76 + */ + function spread(func) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return function(array) { + return func.apply(this, array); + }; + } + + /** + * Creates a throttled function that only invokes `func` at most once per + * every `wait` milliseconds. The throttled function comes with a `cancel` + * method to cancel delayed invocations. Provide an options object to indicate + * that `func` should be invoked on the leading and/or trailing edge of the + * `wait` timeout. Subsequent calls to the throttled function return the + * result of the last `func` call. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked + * on the trailing edge of the timeout only if the the throttled function is + * invoked more than once during the `wait` timeout. + * + * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) + * for details over the differences between `_.throttle` and `_.debounce`. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to throttle. + * @param {number} [wait=0] The number of milliseconds to throttle invocations to. + * @param {Object} [options] The options object. + * @param {boolean} [options.leading=true] Specify invoking on the leading + * edge of the timeout. + * @param {boolean} [options.trailing=true] Specify invoking on the trailing + * edge of the timeout. + * @returns {Function} Returns the new throttled function. + * @example + * + * // avoid excessively updating the position while scrolling + * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); + * + * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes + * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, { + * 'trailing': false + * })); + * + * // cancel a trailing throttled call + * jQuery(window).on('popstate', throttled.cancel); + */ + function throttle(func, wait, options) { + var leading = true, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (options === false) { + leading = false; + } else if (isObject(options)) { + leading = 'leading' in options ? !!options.leading : leading; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + return debounce(func, wait, { 'leading': leading, 'maxWait': +wait, 'trailing': trailing }); + } + + /** + * Creates a function that provides `value` to the wrapper function as its + * first argument. Any additional arguments provided to the function are + * appended to those provided to the wrapper function. The wrapper is invoked + * with the `this` binding of the created function. + * + * @static + * @memberOf _ + * @category Function + * @param {*} value The value to wrap. + * @param {Function} wrapper The wrapper function. + * @returns {Function} Returns the new function. + * @example + * + * var p = _.wrap(_.escape, function(func, text) { + * return '

            ' + func(text) + '

            '; + * }); + * + * p('fred, barney, & pebbles'); + * // => '

            fred, barney, & pebbles

            ' + */ + function wrap(value, wrapper) { + wrapper = wrapper == null ? identity : wrapper; + return createWrapper(wrapper, PARTIAL_FLAG, undefined, [value], []); + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned, + * otherwise they are assigned by reference. If `customizer` is provided it is + * invoked to produce the cloned values. If `customizer` returns `undefined` + * cloning is handled by the method instead. The `customizer` is bound to + * `thisArg` and invoked with two argument; (value [, index|key, object]). + * + * **Note:** This method is loosely based on the + * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm). + * The enumerable properties of `arguments` objects and objects created by + * constructors other than `Object` are cloned to plain `Object` objects. An + * empty object is returned for uncloneable values such as functions, DOM nodes, + * Maps, Sets, and WeakMaps. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @param {Function} [customizer] The function to customize cloning values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {*} Returns the cloned value. + * @example + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * var shallow = _.clone(users); + * shallow[0] === users[0]; + * // => true + * + * var deep = _.clone(users, true); + * deep[0] === users[0]; + * // => false + * + * // using a customizer callback + * var el = _.clone(document.body, function(value) { + * if (_.isElement(value)) { + * return value.cloneNode(false); + * } + * }); + * + * el === document.body + * // => false + * el.nodeName + * // => BODY + * el.childNodes.length; + * // => 0 + */ + function clone(value, isDeep, customizer, thisArg) { + if (isDeep && typeof isDeep != 'boolean' && isIterateeCall(value, isDeep, customizer)) { + isDeep = false; + } + else if (typeof isDeep == 'function') { + thisArg = customizer; + customizer = isDeep; + isDeep = false; + } + return typeof customizer == 'function' + ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 1)) + : baseClone(value, isDeep); + } + + /** + * Creates a deep clone of `value`. If `customizer` is provided it is invoked + * to produce the cloned values. If `customizer` returns `undefined` cloning + * is handled by the method instead. The `customizer` is bound to `thisArg` + * and invoked with two argument; (value [, index|key, object]). + * + * **Note:** This method is loosely based on the + * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm). + * The enumerable properties of `arguments` objects and objects created by + * constructors other than `Object` are cloned to plain `Object` objects. An + * empty object is returned for uncloneable values such as functions, DOM nodes, + * Maps, Sets, and WeakMaps. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to deep clone. + * @param {Function} [customizer] The function to customize cloning values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {*} Returns the deep cloned value. + * @example + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * var deep = _.cloneDeep(users); + * deep[0] === users[0]; + * // => false + * + * // using a customizer callback + * var el = _.cloneDeep(document.body, function(value) { + * if (_.isElement(value)) { + * return value.cloneNode(true); + * } + * }); + * + * el === document.body + * // => false + * el.nodeName + * // => BODY + * el.childNodes.length; + * // => 20 + */ + function cloneDeep(value, customizer, thisArg) { + return typeof customizer == 'function' + ? baseClone(value, true, bindCallback(customizer, thisArg, 1)) + : baseClone(value, true); + } + + /** + * Checks if `value` is greater than `other`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, else `false`. + * @example + * + * _.gt(3, 1); + * // => true + * + * _.gt(3, 3); + * // => false + * + * _.gt(1, 3); + * // => false + */ + function gt(value, other) { + return value > other; + } + + /** + * Checks if `value` is greater than or equal to `other`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than or equal to `other`, else `false`. + * @example + * + * _.gte(3, 1); + * // => true + * + * _.gte(3, 3); + * // => true + * + * _.gte(1, 3); + * // => false + */ + function gte(value, other) { + return value >= other; + } + + /** + * Checks if `value` is classified as an `arguments` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + function isArguments(value) { + return isObjectLike(value) && isArrayLike(value) && + hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); + } + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(function() { return arguments; }()); + * // => false + */ + var isArray = nativeIsArray || function(value) { + return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag; + }; + + /** + * Checks if `value` is classified as a boolean primitive or object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isBoolean(false); + * // => true + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || (isObjectLike(value) && objToString.call(value) == boolTag); + } + + /** + * Checks if `value` is classified as a `Date` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + * + * _.isDate('Mon April 23 2012'); + * // => false + */ + function isDate(value) { + return isObjectLike(value) && objToString.call(value) == dateTag; + } + + /** + * Checks if `value` is a DOM element. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + * + * _.isElement(''); + * // => false + */ + function isElement(value) { + return !!value && value.nodeType === 1 && isObjectLike(value) && !isPlainObject(value); + } + + /** + * Checks if `value` is empty. A value is considered empty unless it is an + * `arguments` object, array, string, or jQuery-like collection with a length + * greater than `0` or an object with own enumerable properties. + * + * @static + * @memberOf _ + * @category Lang + * @param {Array|Object|string} value The value to inspect. + * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @example + * + * _.isEmpty(null); + * // => true + * + * _.isEmpty(true); + * // => true + * + * _.isEmpty(1); + * // => true + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({ 'a': 1 }); + * // => false + */ + function isEmpty(value) { + if (value == null) { + return true; + } + if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) || + (isObjectLike(value) && isFunction(value.splice)))) { + return !value.length; + } + return !keys(value).length; + } + + /** + * Performs a deep comparison between two values to determine if they are + * equivalent. If `customizer` is provided it is invoked to compare values. + * If `customizer` returns `undefined` comparisons are handled by the method + * instead. The `customizer` is bound to `thisArg` and invoked with three + * arguments: (value, other [, index|key]). + * + * **Note:** This method supports comparing arrays, booleans, `Date` objects, + * numbers, `Object` objects, regexes, and strings. Objects are compared by + * their own, not inherited, enumerable properties. Functions and DOM nodes + * are **not** supported. Provide a customizer function to extend support + * for comparing other values. + * + * @static + * @memberOf _ + * @alias eq + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize value comparisons. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'user': 'fred' }; + * var other = { 'user': 'fred' }; + * + * object == other; + * // => false + * + * _.isEqual(object, other); + * // => true + * + * // using a customizer callback + * var array = ['hello', 'goodbye']; + * var other = ['hi', 'goodbye']; + * + * _.isEqual(array, other, function(value, other) { + * if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) { + * return true; + * } + * }); + * // => true + */ + function isEqual(value, other, customizer, thisArg) { + customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined; + var result = customizer ? customizer(value, other) : undefined; + return result === undefined ? baseIsEqual(value, other, customizer) : !!result; + } + + /** + * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, + * `SyntaxError`, `TypeError`, or `URIError` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an error object, else `false`. + * @example + * + * _.isError(new Error); + * // => true + * + * _.isError(Error); + * // => false + */ + function isError(value) { + return isObjectLike(value) && typeof value.message == 'string' && objToString.call(value) == errorTag; + } + + /** + * Checks if `value` is a finite primitive number. + * + * **Note:** This method is based on [`Number.isFinite`](http://ecma-international.org/ecma-262/6.0/#sec-number.isfinite). + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. + * @example + * + * _.isFinite(10); + * // => true + * + * _.isFinite('10'); + * // => false + * + * _.isFinite(true); + * // => false + * + * _.isFinite(Object(10)); + * // => false + * + * _.isFinite(Infinity); + * // => false + */ + function isFinite(value) { + return typeof value == 'number' && nativeIsFinite(value); + } + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in older versions of Chrome and Safari which return 'function' for regexes + // and Safari 8 equivalents which return 'object' for typed array constructors. + return isObject(value) && objToString.call(value) == funcTag; + } + + /** + * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(1); + * // => false + */ + function isObject(value) { + // Avoid a V8 JIT bug in Chrome 19-20. + // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + } + + /** + * Performs a deep comparison between `object` and `source` to determine if + * `object` contains equivalent property values. If `customizer` is provided + * it is invoked to compare values. If `customizer` returns `undefined` + * comparisons are handled by the method instead. The `customizer` is bound + * to `thisArg` and invoked with three arguments: (value, other, index|key). + * + * **Note:** This method supports comparing properties of arrays, booleans, + * `Date` objects, numbers, `Object` objects, regexes, and strings. Functions + * and DOM nodes are **not** supported. Provide a customizer function to extend + * support for comparing other values. + * + * @static + * @memberOf _ + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Function} [customizer] The function to customize value comparisons. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * var object = { 'user': 'fred', 'age': 40 }; + * + * _.isMatch(object, { 'age': 40 }); + * // => true + * + * _.isMatch(object, { 'age': 36 }); + * // => false + * + * // using a customizer callback + * var object = { 'greeting': 'hello' }; + * var source = { 'greeting': 'hi' }; + * + * _.isMatch(object, source, function(value, other) { + * return _.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/) || undefined; + * }); + * // => true + */ + function isMatch(object, source, customizer, thisArg) { + customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined; + return baseIsMatch(object, getMatchData(source), customizer); + } + + /** + * Checks if `value` is `NaN`. + * + * **Note:** This method is not the same as [`isNaN`](https://es5.github.io/#x15.1.2.4) + * which returns `true` for `undefined` and other non-numeric values. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + function isNaN(value) { + // An `NaN` primitive is the only value that is not equal to itself. + // Perform the `toStringTag` check first to avoid errors with some host objects in IE. + return isNumber(value) && value != +value; + } + + /** + * Checks if `value` is a native function. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, else `false`. + * @example + * + * _.isNative(Array.prototype.push); + * // => true + * + * _.isNative(_); + * // => false + */ + function isNative(value) { + if (value == null) { + return false; + } + if (isFunction(value)) { + return reIsNative.test(fnToString.call(value)); + } + return isObjectLike(value) && reIsHostCtor.test(value); + } + + /** + * Checks if `value` is `null`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(void 0); + * // => false + */ + function isNull(value) { + return value === null; + } + + /** + * Checks if `value` is classified as a `Number` primitive or object. + * + * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are classified + * as numbers, use the `_.isFinite` method. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isNumber(8.4); + * // => true + * + * _.isNumber(NaN); + * // => true + * + * _.isNumber('8.4'); + * // => false + */ + function isNumber(value) { + return typeof value == 'number' || (isObjectLike(value) && objToString.call(value) == numberTag); + } + + /** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * **Note:** This method assumes objects created by the `Object` constructor + * have no inherited enumerable properties. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ + function isPlainObject(value) { + var Ctor; + + // Exit early for non `Object` objects. + if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isArguments(value)) || + (!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) { + return false; + } + // IE < 9 iterates inherited properties before own properties. If the first + // iterated property is an object's own property then there are no inherited + // enumerable properties. + var result; + // In most environments an object's own properties are iterated before + // its inherited properties. If the last iterated property is an object's + // own property then there are no inherited enumerable properties. + baseForIn(value, function(subValue, key) { + result = key; + }); + return result === undefined || hasOwnProperty.call(value, result); + } + + /** + * Checks if `value` is classified as a `RegExp` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isRegExp(/abc/); + * // => true + * + * _.isRegExp('/abc/'); + * // => false + */ + function isRegExp(value) { + return isObject(value) && objToString.call(value) == regexpTag; + } + + /** + * Checks if `value` is classified as a `String` primitive or object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isString('abc'); + * // => true + * + * _.isString(1); + * // => false + */ + function isString(value) { + return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag); + } + + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + function isTypedArray(value) { + return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)]; + } + + /** + * Checks if `value` is `undefined`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + * + * _.isUndefined(null); + * // => false + */ + function isUndefined(value) { + return value === undefined; + } + + /** + * Checks if `value` is less than `other`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, else `false`. + * @example + * + * _.lt(1, 3); + * // => true + * + * _.lt(3, 3); + * // => false + * + * _.lt(3, 1); + * // => false + */ + function lt(value, other) { + return value < other; + } + + /** + * Checks if `value` is less than or equal to `other`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than or equal to `other`, else `false`. + * @example + * + * _.lte(1, 3); + * // => true + * + * _.lte(3, 3); + * // => true + * + * _.lte(3, 1); + * // => false + */ + function lte(value, other) { + return value <= other; + } + + /** + * Converts `value` to an array. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to convert. + * @returns {Array} Returns the converted array. + * @example + * + * (function() { + * return _.toArray(arguments).slice(1); + * }(1, 2, 3)); + * // => [2, 3] + */ + function toArray(value) { + var length = value ? getLength(value) : 0; + if (!isLength(length)) { + return values(value); + } + if (!length) { + return []; + } + return arrayCopy(value); + } + + /** + * Converts `value` to a plain object flattening inherited enumerable + * properties of `value` to own properties of the plain object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to convert. + * @returns {Object} Returns the converted plain object. + * @example + * + * function Foo() { + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.assign({ 'a': 1 }, new Foo); + * // => { 'a': 1, 'b': 2 } + * + * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); + * // => { 'a': 1, 'b': 2, 'c': 3 } + */ + function toPlainObject(value) { + return baseCopy(value, keysIn(value)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Recursively merges own enumerable properties of the source object(s), that + * don't resolve to `undefined` into the destination object. Subsequent sources + * overwrite property assignments of previous sources. If `customizer` is + * provided it is invoked to produce the merged values of the destination and + * source properties. If `customizer` returns `undefined` merging is handled + * by the method instead. The `customizer` is bound to `thisArg` and invoked + * with five arguments: (objectValue, sourceValue, key, object, source). + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {Object} Returns `object`. + * @example + * + * var users = { + * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }] + * }; + * + * var ages = { + * 'data': [{ 'age': 36 }, { 'age': 40 }] + * }; + * + * _.merge(users, ages); + * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] } + * + * // using a customizer callback + * var object = { + * 'fruits': ['apple'], + * 'vegetables': ['beet'] + * }; + * + * var other = { + * 'fruits': ['banana'], + * 'vegetables': ['carrot'] + * }; + * + * _.merge(object, other, function(a, b) { + * if (_.isArray(a)) { + * return a.concat(b); + * } + * }); + * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] } + */ + var merge = createAssigner(baseMerge); + + /** + * Assigns own enumerable properties of source object(s) to the destination + * object. Subsequent sources overwrite property assignments of previous sources. + * If `customizer` is provided it is invoked to produce the assigned values. + * The `customizer` is bound to `thisArg` and invoked with five arguments: + * (objectValue, sourceValue, key, object, source). + * + * **Note:** This method mutates `object` and is based on + * [`Object.assign`](http://ecma-international.org/ecma-262/6.0/#sec-object.assign). + * + * @static + * @memberOf _ + * @alias extend + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {Object} Returns `object`. + * @example + * + * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' }); + * // => { 'user': 'fred', 'age': 40 } + * + * // using a customizer callback + * var defaults = _.partialRight(_.assign, function(value, other) { + * return _.isUndefined(value) ? other : value; + * }); + * + * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); + * // => { 'user': 'barney', 'age': 36 } + */ + var assign = createAssigner(function(object, source, customizer) { + return customizer + ? assignWith(object, source, customizer) + : baseAssign(object, source); + }); + + /** + * Creates an object that inherits from the given `prototype` object. If a + * `properties` object is provided its own enumerable properties are assigned + * to the created object. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} prototype The object to inherit from. + * @param {Object} [properties] The properties to assign to the object. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Object} Returns the new object. + * @example + * + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * function Circle() { + * Shape.call(this); + * } + * + * Circle.prototype = _.create(Shape.prototype, { + * 'constructor': Circle + * }); + * + * var circle = new Circle; + * circle instanceof Circle; + * // => true + * + * circle instanceof Shape; + * // => true + */ + function create(prototype, properties, guard) { + var result = baseCreate(prototype); + if (guard && isIterateeCall(prototype, properties, guard)) { + properties = undefined; + } + return properties ? baseAssign(result, properties) : result; + } + + /** + * Assigns own enumerable properties of source object(s) to the destination + * object for all destination properties that resolve to `undefined`. Once a + * property is set, additional values of the same property are ignored. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * _.defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); + * // => { 'user': 'barney', 'age': 36 } + */ + var defaults = createDefaults(assign, assignDefaults); + + /** + * This method is like `_.defaults` except that it recursively assigns + * default properties. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * _.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } }); + * // => { 'user': { 'name': 'barney', 'age': 36 } } + * + */ + var defaultsDeep = createDefaults(merge, mergeDefaults); + + /** + * This method is like `_.find` except that it returns the key of the first + * element `predicate` returns truthy for instead of the element itself. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {string|undefined} Returns the key of the matched element, else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findKey(users, function(chr) { + * return chr.age < 40; + * }); + * // => 'barney' (iteration order is not guaranteed) + * + * // using the `_.matches` callback shorthand + * _.findKey(users, { 'age': 1, 'active': true }); + * // => 'pebbles' + * + * // using the `_.matchesProperty` callback shorthand + * _.findKey(users, 'active', false); + * // => 'fred' + * + * // using the `_.property` callback shorthand + * _.findKey(users, 'active'); + * // => 'barney' + */ + var findKey = createFindKey(baseForOwn); + + /** + * This method is like `_.findKey` except that it iterates over elements of + * a collection in the opposite order. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {string|undefined} Returns the key of the matched element, else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findLastKey(users, function(chr) { + * return chr.age < 40; + * }); + * // => returns `pebbles` assuming `_.findKey` returns `barney` + * + * // using the `_.matches` callback shorthand + * _.findLastKey(users, { 'age': 36, 'active': true }); + * // => 'barney' + * + * // using the `_.matchesProperty` callback shorthand + * _.findLastKey(users, 'active', false); + * // => 'fred' + * + * // using the `_.property` callback shorthand + * _.findLastKey(users, 'active'); + * // => 'pebbles' + */ + var findLastKey = createFindKey(baseForOwnRight); + + /** + * Iterates over own and inherited enumerable properties of an object invoking + * `iteratee` for each property. The `iteratee` is bound to `thisArg` and invoked + * with three arguments: (value, key, object). Iteratee functions may exit + * iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns `object`. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forIn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => logs 'a', 'b', and 'c' (iteration order is not guaranteed) + */ + var forIn = createForIn(baseFor); + + /** + * This method is like `_.forIn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns `object`. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forInRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => logs 'c', 'b', and 'a' assuming `_.forIn ` logs 'a', 'b', and 'c' + */ + var forInRight = createForIn(baseForRight); + + /** + * Iterates over own enumerable properties of an object invoking `iteratee` + * for each property. The `iteratee` is bound to `thisArg` and invoked with + * three arguments: (value, key, object). Iteratee functions may exit iteration + * early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns `object`. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => logs 'a' and 'b' (iteration order is not guaranteed) + */ + var forOwn = createForOwn(baseForOwn); + + /** + * This method is like `_.forOwn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns `object`. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwnRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => logs 'b' and 'a' assuming `_.forOwn` logs 'a' and 'b' + */ + var forOwnRight = createForOwn(baseForOwnRight); + + /** + * Creates an array of function property names from all enumerable properties, + * own and inherited, of `object`. + * + * @static + * @memberOf _ + * @alias methods + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the new array of property names. + * @example + * + * _.functions(_); + * // => ['after', 'ary', 'assign', ...] + */ + function functions(object) { + return baseFunctions(object, keysIn(object)); + } + + /** + * Gets the property value at `path` of `object`. If the resolved value is + * `undefined` the `defaultValue` is used in its place. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.get(object, 'a[0].b.c'); + * // => 3 + * + * _.get(object, ['a', '0', 'b', 'c']); + * // => 3 + * + * _.get(object, 'a.b.c', 'default'); + * // => 'default' + */ + function get(object, path, defaultValue) { + var result = object == null ? undefined : baseGet(object, toPath(path), path + ''); + return result === undefined ? defaultValue : result; + } + + /** + * Checks if `path` is a direct property. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` is a direct property, else `false`. + * @example + * + * var object = { 'a': { 'b': { 'c': 3 } } }; + * + * _.has(object, 'a'); + * // => true + * + * _.has(object, 'a.b.c'); + * // => true + * + * _.has(object, ['a', 'b', 'c']); + * // => true + */ + function has(object, path) { + if (object == null) { + return false; + } + var result = hasOwnProperty.call(object, path); + if (!result && !isKey(path)) { + path = toPath(path); + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + if (object == null) { + return false; + } + path = last(path); + result = hasOwnProperty.call(object, path); + } + return result || (isLength(object.length) && isIndex(path, object.length) && + (isArray(object) || isArguments(object))); + } + + /** + * Creates an object composed of the inverted keys and values of `object`. + * If `object` contains duplicate values, subsequent values overwrite property + * assignments of previous values unless `multiValue` is `true`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to invert. + * @param {boolean} [multiValue] Allow multiple values per key. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Object} Returns the new inverted object. + * @example + * + * var object = { 'a': 1, 'b': 2, 'c': 1 }; + * + * _.invert(object); + * // => { '1': 'c', '2': 'b' } + * + * // with `multiValue` + * _.invert(object, true); + * // => { '1': ['a', 'c'], '2': ['b'] } + */ + function invert(object, multiValue, guard) { + if (guard && isIterateeCall(object, multiValue, guard)) { + multiValue = undefined; + } + var index = -1, + props = keys(object), + length = props.length, + result = {}; + + while (++index < length) { + var key = props[index], + value = object[key]; + + if (multiValue) { + if (hasOwnProperty.call(result, value)) { + result[value].push(key); + } else { + result[value] = [key]; + } + } + else { + result[value] = key; + } + } + return result; + } + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys) + * for more details. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + var keys = !nativeKeys ? shimKeys : function(object) { + var Ctor = object == null ? undefined : object.constructor; + if ((typeof Ctor == 'function' && Ctor.prototype === object) || + (typeof object != 'function' && isArrayLike(object))) { + return shimKeys(object); + } + return isObject(object) ? nativeKeys(object) : []; + }; + + /** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */ + function keysIn(object) { + if (object == null) { + return []; + } + if (!isObject(object)) { + object = Object(object); + } + var length = object.length; + length = (length && isLength(length) && + (isArray(object) || isArguments(object)) && length) || 0; + + var Ctor = object.constructor, + index = -1, + isProto = typeof Ctor == 'function' && Ctor.prototype === object, + result = Array(length), + skipIndexes = length > 0; + + while (++index < length) { + result[index] = (index + ''); + } + for (var key in object) { + if (!(skipIndexes && isIndex(key, length)) && + !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { + result.push(key); + } + } + return result; + } + + /** + * The opposite of `_.mapValues`; this method creates an object with the + * same values as `object` and keys generated by running each own enumerable + * property of `object` through `iteratee`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the new mapped object. + * @example + * + * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { + * return key + value; + * }); + * // => { 'a1': 1, 'b2': 2 } + */ + var mapKeys = createObjectMapper(true); + + /** + * Creates an object with the same keys as `object` and values generated by + * running each own enumerable property of `object` through `iteratee`. The + * iteratee function is bound to `thisArg` and invoked with three arguments: + * (value, key, object). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the new mapped object. + * @example + * + * _.mapValues({ 'a': 1, 'b': 2 }, function(n) { + * return n * 3; + * }); + * // => { 'a': 3, 'b': 6 } + * + * var users = { + * 'fred': { 'user': 'fred', 'age': 40 }, + * 'pebbles': { 'user': 'pebbles', 'age': 1 } + * }; + * + * // using the `_.property` callback shorthand + * _.mapValues(users, 'age'); + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + */ + var mapValues = createObjectMapper(); + + /** + * The opposite of `_.pick`; this method creates an object composed of the + * own and inherited enumerable properties of `object` that are not omitted. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {Function|...(string|string[])} [predicate] The function invoked per + * iteration or property names to omit, specified as individual property + * names or arrays of property names. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'user': 'fred', 'age': 40 }; + * + * _.omit(object, 'age'); + * // => { 'user': 'fred' } + * + * _.omit(object, _.isNumber); + * // => { 'user': 'fred' } + */ + var omit = restParam(function(object, props) { + if (object == null) { + return {}; + } + if (typeof props[0] != 'function') { + var props = arrayMap(baseFlatten(props), String); + return pickByArray(object, baseDifference(keysIn(object), props)); + } + var predicate = bindCallback(props[0], props[1], 3); + return pickByCallback(object, function(value, key, object) { + return !predicate(value, key, object); + }); + }); + + /** + * Creates a two dimensional array of the key-value pairs for `object`, + * e.g. `[[key1, value1], [key2, value2]]`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the new array of key-value pairs. + * @example + * + * _.pairs({ 'barney': 36, 'fred': 40 }); + * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed) + */ + function pairs(object) { + object = toObject(object); + + var index = -1, + props = keys(object), + length = props.length, + result = Array(length); + + while (++index < length) { + var key = props[index]; + result[index] = [key, object[key]]; + } + return result; + } + + /** + * Creates an object composed of the picked `object` properties. Property + * names may be specified as individual arguments or as arrays of property + * names. If `predicate` is provided it is invoked for each property of `object` + * picking the properties `predicate` returns truthy for. The predicate is + * bound to `thisArg` and invoked with three arguments: (value, key, object). + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {Function|...(string|string[])} [predicate] The function invoked per + * iteration or property names to pick, specified as individual property + * names or arrays of property names. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'user': 'fred', 'age': 40 }; + * + * _.pick(object, 'user'); + * // => { 'user': 'fred' } + * + * _.pick(object, _.isString); + * // => { 'user': 'fred' } + */ + var pick = restParam(function(object, props) { + if (object == null) { + return {}; + } + return typeof props[0] == 'function' + ? pickByCallback(object, bindCallback(props[0], props[1], 3)) + : pickByArray(object, baseFlatten(props)); + }); + + /** + * This method is like `_.get` except that if the resolved value is a function + * it is invoked with the `this` binding of its parent object and its result + * is returned. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to resolve. + * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; + * + * _.result(object, 'a[0].b.c1'); + * // => 3 + * + * _.result(object, 'a[0].b.c2'); + * // => 4 + * + * _.result(object, 'a.b.c', 'default'); + * // => 'default' + * + * _.result(object, 'a.b.c', _.constant('default')); + * // => 'default' + */ + function result(object, path, defaultValue) { + var result = object == null ? undefined : object[path]; + if (result === undefined) { + if (object != null && !isKey(path, object)) { + path = toPath(path); + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + result = object == null ? undefined : object[last(path)]; + } + result = result === undefined ? defaultValue : result; + } + return isFunction(result) ? result.call(object) : result; + } + + /** + * Sets the property value of `path` on `object`. If a portion of `path` + * does not exist it is created. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to augment. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.set(object, 'a[0].b.c', 4); + * console.log(object.a[0].b.c); + * // => 4 + * + * _.set(object, 'x[0].y.z', 5); + * console.log(object.x[0].y.z); + * // => 5 + */ + function set(object, path, value) { + if (object == null) { + return object; + } + var pathKey = (path + ''); + path = (object[pathKey] != null || isKey(path, object)) ? [pathKey] : toPath(path); + + var index = -1, + length = path.length, + lastIndex = length - 1, + nested = object; + + while (nested != null && ++index < length) { + var key = path[index]; + if (isObject(nested)) { + if (index == lastIndex) { + nested[key] = value; + } else if (nested[key] == null) { + nested[key] = isIndex(path[index + 1]) ? [] : {}; + } + } + nested = nested[key]; + } + return object; + } + + /** + * An alternative to `_.reduce`; this method transforms `object` to a new + * `accumulator` object which is the result of running each of its own enumerable + * properties through `iteratee`, with each invocation potentially mutating + * the `accumulator` object. The `iteratee` is bound to `thisArg` and invoked + * with four arguments: (accumulator, value, key, object). Iteratee functions + * may exit iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Array|Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The custom accumulator value. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {*} Returns the accumulated value. + * @example + * + * _.transform([2, 3, 4], function(result, n) { + * result.push(n *= n); + * return n % 2 == 0; + * }); + * // => [4, 9] + * + * _.transform({ 'a': 1, 'b': 2 }, function(result, n, key) { + * result[key] = n * 3; + * }); + * // => { 'a': 3, 'b': 6 } + */ + function transform(object, iteratee, accumulator, thisArg) { + var isArr = isArray(object) || isTypedArray(object); + iteratee = getCallback(iteratee, thisArg, 4); + + if (accumulator == null) { + if (isArr || isObject(object)) { + var Ctor = object.constructor; + if (isArr) { + accumulator = isArray(object) ? new Ctor : []; + } else { + accumulator = baseCreate(isFunction(Ctor) ? Ctor.prototype : undefined); + } + } else { + accumulator = {}; + } + } + (isArr ? arrayEach : baseForOwn)(object, function(value, index, object) { + return iteratee(accumulator, value, index, object); + }); + return accumulator; + } + + /** + * Creates an array of the own enumerable property values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.values(new Foo); + * // => [1, 2] (iteration order is not guaranteed) + * + * _.values('hi'); + * // => ['h', 'i'] + */ + function values(object) { + return baseValues(object, keys(object)); + } + + /** + * Creates an array of the own and inherited enumerable property values + * of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.valuesIn(new Foo); + * // => [1, 2, 3] (iteration order is not guaranteed) + */ + function valuesIn(object) { + return baseValues(object, keysIn(object)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Checks if `n` is between `start` and up to but not including, `end`. If + * `end` is not specified it is set to `start` with `start` then set to `0`. + * + * @static + * @memberOf _ + * @category Number + * @param {number} n The number to check. + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @returns {boolean} Returns `true` if `n` is in the range, else `false`. + * @example + * + * _.inRange(3, 2, 4); + * // => true + * + * _.inRange(4, 8); + * // => true + * + * _.inRange(4, 2); + * // => false + * + * _.inRange(2, 2); + * // => false + * + * _.inRange(1.2, 2); + * // => true + * + * _.inRange(5.2, 4); + * // => false + */ + function inRange(value, start, end) { + start = +start || 0; + if (end === undefined) { + end = start; + start = 0; + } else { + end = +end || 0; + } + return value >= nativeMin(start, end) && value < nativeMax(start, end); + } + + /** + * Produces a random number between `min` and `max` (inclusive). If only one + * argument is provided a number between `0` and the given number is returned. + * If `floating` is `true`, or either `min` or `max` are floats, a floating-point + * number is returned instead of an integer. + * + * @static + * @memberOf _ + * @category Number + * @param {number} [min=0] The minimum possible value. + * @param {number} [max=1] The maximum possible value. + * @param {boolean} [floating] Specify returning a floating-point number. + * @returns {number} Returns the random number. + * @example + * + * _.random(0, 5); + * // => an integer between 0 and 5 + * + * _.random(5); + * // => also an integer between 0 and 5 + * + * _.random(5, true); + * // => a floating-point number between 0 and 5 + * + * _.random(1.2, 5.2); + * // => a floating-point number between 1.2 and 5.2 + */ + function random(min, max, floating) { + if (floating && isIterateeCall(min, max, floating)) { + max = floating = undefined; + } + var noMin = min == null, + noMax = max == null; + + if (floating == null) { + if (noMax && typeof min == 'boolean') { + floating = min; + min = 1; + } + else if (typeof max == 'boolean') { + floating = max; + noMax = true; + } + } + if (noMin && noMax) { + max = 1; + noMax = false; + } + min = +min || 0; + if (noMax) { + max = min; + min = 0; + } else { + max = +max || 0; + } + if (floating || min % 1 || max % 1) { + var rand = nativeRandom(); + return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand + '').length - 1)))), max); + } + return baseRandom(min, max); + } + + /*------------------------------------------------------------------------*/ + + /** + * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the camel cased string. + * @example + * + * _.camelCase('Foo Bar'); + * // => 'fooBar' + * + * _.camelCase('--foo-bar'); + * // => 'fooBar' + * + * _.camelCase('__foo_bar__'); + * // => 'fooBar' + */ + var camelCase = createCompounder(function(result, word, index) { + word = word.toLowerCase(); + return result + (index ? (word.charAt(0).toUpperCase() + word.slice(1)) : word); + }); + + /** + * Capitalizes the first character of `string`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to capitalize. + * @returns {string} Returns the capitalized string. + * @example + * + * _.capitalize('fred'); + * // => 'Fred' + */ + function capitalize(string) { + string = baseToString(string); + return string && (string.charAt(0).toUpperCase() + string.slice(1)); + } + + /** + * Deburrs `string` by converting [latin-1 supplementary letters](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) + * to basic latin letters and removing [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to deburr. + * @returns {string} Returns the deburred string. + * @example + * + * _.deburr('déjà vu'); + * // => 'deja vu' + */ + function deburr(string) { + string = baseToString(string); + return string && string.replace(reLatin1, deburrLetter).replace(reComboMark, ''); + } + + /** + * Checks if `string` ends with the given target string. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to search. + * @param {string} [target] The string to search for. + * @param {number} [position=string.length] The position to search from. + * @returns {boolean} Returns `true` if `string` ends with `target`, else `false`. + * @example + * + * _.endsWith('abc', 'c'); + * // => true + * + * _.endsWith('abc', 'b'); + * // => false + * + * _.endsWith('abc', 'b', 2); + * // => true + */ + function endsWith(string, target, position) { + string = baseToString(string); + target = (target + ''); + + var length = string.length; + position = position === undefined + ? length + : nativeMin(position < 0 ? 0 : (+position || 0), length); + + position -= target.length; + return position >= 0 && string.indexOf(target, position) == position; + } + + /** + * Converts the characters "&", "<", ">", '"', "'", and "\`", in `string` to + * their corresponding HTML entities. + * + * **Note:** No other characters are escaped. To escape additional characters + * use a third-party library like [_he_](https://mths.be/he). + * + * Though the ">" character is escaped for symmetry, characters like + * ">" and "/" don't need escaping in HTML and have no special meaning + * unless they're part of a tag or unquoted attribute value. + * See [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) + * (under "semi-related fun fact") for more details. + * + * Backticks are escaped because in Internet Explorer < 9, they can break out + * of attribute values or HTML comments. See [#59](https://html5sec.org/#59), + * [#102](https://html5sec.org/#102), [#108](https://html5sec.org/#108), and + * [#133](https://html5sec.org/#133) of the [HTML5 Security Cheatsheet](https://html5sec.org/) + * for more details. + * + * When working with HTML you should always [quote attribute values](http://wonko.com/post/html-escaping) + * to reduce XSS vectors. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escape('fred, barney, & pebbles'); + * // => 'fred, barney, & pebbles' + */ + function escape(string) { + // Reset `lastIndex` because in IE < 9 `String#replace` does not. + string = baseToString(string); + return (string && reHasUnescapedHtml.test(string)) + ? string.replace(reUnescapedHtml, escapeHtmlChar) + : string; + } + + /** + * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?", + * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escapeRegExp('[lodash](https://lodash.com/)'); + * // => '\[lodash\]\(https:\/\/lodash\.com\/\)' + */ + function escapeRegExp(string) { + string = baseToString(string); + return (string && reHasRegExpChars.test(string)) + ? string.replace(reRegExpChars, escapeRegExpChar) + : (string || '(?:)'); + } + + /** + * Converts `string` to [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the kebab cased string. + * @example + * + * _.kebabCase('Foo Bar'); + * // => 'foo-bar' + * + * _.kebabCase('fooBar'); + * // => 'foo-bar' + * + * _.kebabCase('__foo_bar__'); + * // => 'foo-bar' + */ + var kebabCase = createCompounder(function(result, word, index) { + return result + (index ? '-' : '') + word.toLowerCase(); + }); + + /** + * Pads `string` on the left and right sides if it's shorter than `length`. + * Padding characters are truncated if they can't be evenly divided by `length`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.pad('abc', 8); + * // => ' abc ' + * + * _.pad('abc', 8, '_-'); + * // => '_-abc_-_' + * + * _.pad('abc', 3); + * // => 'abc' + */ + function pad(string, length, chars) { + string = baseToString(string); + length = +length; + + var strLength = string.length; + if (strLength >= length || !nativeIsFinite(length)) { + return string; + } + var mid = (length - strLength) / 2, + leftLength = nativeFloor(mid), + rightLength = nativeCeil(mid); + + chars = createPadding('', rightLength, chars); + return chars.slice(0, leftLength) + string + chars; + } + + /** + * Pads `string` on the left side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padLeft('abc', 6); + * // => ' abc' + * + * _.padLeft('abc', 6, '_-'); + * // => '_-_abc' + * + * _.padLeft('abc', 3); + * // => 'abc' + */ + var padLeft = createPadDir(); + + /** + * Pads `string` on the right side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padRight('abc', 6); + * // => 'abc ' + * + * _.padRight('abc', 6, '_-'); + * // => 'abc_-_' + * + * _.padRight('abc', 3); + * // => 'abc' + */ + var padRight = createPadDir(true); + + /** + * Converts `string` to an integer of the specified radix. If `radix` is + * `undefined` or `0`, a `radix` of `10` is used unless `value` is a hexadecimal, + * in which case a `radix` of `16` is used. + * + * **Note:** This method aligns with the [ES5 implementation](https://es5.github.io/#E) + * of `parseInt`. + * + * @static + * @memberOf _ + * @category String + * @param {string} string The string to convert. + * @param {number} [radix] The radix to interpret `value` by. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {number} Returns the converted integer. + * @example + * + * _.parseInt('08'); + * // => 8 + * + * _.map(['6', '08', '10'], _.parseInt); + * // => [6, 8, 10] + */ + function parseInt(string, radix, guard) { + // Firefox < 21 and Opera < 15 follow ES3 for `parseInt`. + // Chrome fails to trim leading whitespace characters. + // See https://code.google.com/p/v8/issues/detail?id=3109 for more details. + if (guard ? isIterateeCall(string, radix, guard) : radix == null) { + radix = 0; + } else if (radix) { + radix = +radix; + } + string = trim(string); + return nativeParseInt(string, radix || (reHasHexPrefix.test(string) ? 16 : 10)); + } + + /** + * Repeats the given string `n` times. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to repeat. + * @param {number} [n=0] The number of times to repeat the string. + * @returns {string} Returns the repeated string. + * @example + * + * _.repeat('*', 3); + * // => '***' + * + * _.repeat('abc', 2); + * // => 'abcabc' + * + * _.repeat('abc', 0); + * // => '' + */ + function repeat(string, n) { + var result = ''; + string = baseToString(string); + n = +n; + if (n < 1 || !string || !nativeIsFinite(n)) { + return result; + } + // Leverage the exponentiation by squaring algorithm for a faster repeat. + // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. + do { + if (n % 2) { + result += string; + } + n = nativeFloor(n / 2); + string += string; + } while (n); + + return result; + } + + /** + * Converts `string` to [snake case](https://en.wikipedia.org/wiki/Snake_case). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the snake cased string. + * @example + * + * _.snakeCase('Foo Bar'); + * // => 'foo_bar' + * + * _.snakeCase('fooBar'); + * // => 'foo_bar' + * + * _.snakeCase('--foo-bar'); + * // => 'foo_bar' + */ + var snakeCase = createCompounder(function(result, word, index) { + return result + (index ? '_' : '') + word.toLowerCase(); + }); + + /** + * Converts `string` to [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the start cased string. + * @example + * + * _.startCase('--foo-bar'); + * // => 'Foo Bar' + * + * _.startCase('fooBar'); + * // => 'Foo Bar' + * + * _.startCase('__foo_bar__'); + * // => 'Foo Bar' + */ + var startCase = createCompounder(function(result, word, index) { + return result + (index ? ' ' : '') + (word.charAt(0).toUpperCase() + word.slice(1)); + }); + + /** + * Checks if `string` starts with the given target string. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to search. + * @param {string} [target] The string to search for. + * @param {number} [position=0] The position to search from. + * @returns {boolean} Returns `true` if `string` starts with `target`, else `false`. + * @example + * + * _.startsWith('abc', 'a'); + * // => true + * + * _.startsWith('abc', 'b'); + * // => false + * + * _.startsWith('abc', 'b', 1); + * // => true + */ + function startsWith(string, target, position) { + string = baseToString(string); + position = position == null + ? 0 + : nativeMin(position < 0 ? 0 : (+position || 0), string.length); + + return string.lastIndexOf(target, position) == position; + } + + /** + * Creates a compiled template function that can interpolate data properties + * in "interpolate" delimiters, HTML-escape interpolated data properties in + * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data + * properties may be accessed as free variables in the template. If a setting + * object is provided it takes precedence over `_.templateSettings` values. + * + * **Note:** In the development build `_.template` utilizes + * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) + * for easier debugging. + * + * For more information on precompiling templates see + * [lodash's custom builds documentation](https://lodash.com/custom-builds). + * + * For more information on Chrome extension sandboxes see + * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The template string. + * @param {Object} [options] The options object. + * @param {RegExp} [options.escape] The HTML "escape" delimiter. + * @param {RegExp} [options.evaluate] The "evaluate" delimiter. + * @param {Object} [options.imports] An object to import into the template as free variables. + * @param {RegExp} [options.interpolate] The "interpolate" delimiter. + * @param {string} [options.sourceURL] The sourceURL of the template's compiled source. + * @param {string} [options.variable] The data object variable name. + * @param- {Object} [otherOptions] Enables the legacy `options` param signature. + * @returns {Function} Returns the compiled template function. + * @example + * + * // using the "interpolate" delimiter to create a compiled template + * var compiled = _.template('hello <%= user %>!'); + * compiled({ 'user': 'fred' }); + * // => 'hello fred!' + * + * // using the HTML "escape" delimiter to escape data property values + * var compiled = _.template('<%- value %>'); + * compiled({ 'value': ''; + 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 index c88c05d..8765153 100644 --- a/test/_base_test.js +++ b/test/_base_test.js @@ -1,16 +1,19 @@ -// Copyright 2014 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 assert = require('assert'), fs = require('fs'), @@ -58,7 +61,11 @@ function runClosureTest(file) { describe(name, function() { var context = new base.Context(true); context.closure.document.title = name; - if (process.env.VERBOSE != '1') { + 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; } @@ -86,7 +93,8 @@ function runClosureTest(file) { } var results = tc.getTestResults(); done(Error('\n' + Object.keys(results).map(function(name) { - var msg = [name + ': ' + (results[name].length ? 'FAILED' : 'PASSED')]; + var msg = + [name + ': ' + (results[name].length ? 'FAILED' : 'PASSED')]; if (results[name].length) { msg = msg.concat(results[name]); } 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 0a372c3..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); }); @@ -192,14 +200,20 @@ 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.driver = new chrome.Driver(options); + driver = env.builder(). + setChromeOptions(options). + build(); driver.get(test.Pages.ajaxyPage); 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 index dbfae96..50936f7 100644 --- a/test/firefox/extension_test.js +++ b/test/firefox/extension_test.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/test/firefox/firefox_test.js b/test/firefox/firefox_test.js index a5774d6..3fd7443 100644 --- a/test/firefox/firefox_test.js +++ b/test/firefox/firefox_test.js @@ -1,23 +1,26 @@ -// 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'; var path = require('path'); var firefox = require('../../firefox'), + io = require('../../io'), test = require('../../lib/test'), assert = require('../../testing/assert'); @@ -29,12 +32,18 @@ var NORMAL_EXTENSION = path.join(__dirname, test.suite(function(env) { - env.autoCreateDriver = false; - describe('firefox', function() { describe('Options', function() { + var driver; + + test.beforeEach(function() { + driver = null; + }); + test.afterEach(function() { - return env.dispose(); + if (driver) { + driver.quit(); + } }); test.it('can start Firefox with custom preferences', function() { @@ -43,7 +52,10 @@ test.suite(function(env) { var options = new firefox.Options().setProfile(profile); - var driver = env.driver = new firefox.Driver(options); + driver = env.builder(). + setFirefoxOptions(options). + build(); + driver.get('data:text/html,
            content
            '); var userAgent = driver.executeScript( @@ -57,7 +69,10 @@ test.suite(function(env) { var options = new firefox.Options().setProfile(profile); - var driver = env.driver = new firefox.Driver(options); + driver = env.builder(). + setFirefoxOptions(options). + build(); + loadJetpackPage(driver, 'data:text/html;charset=UTF-8,
            content
            '); assert(driver.findElement({id: 'jetpack-sample-banner'}).getText()) @@ -70,7 +85,10 @@ test.suite(function(env) { var options = new firefox.Options().setProfile(profile); - var driver = env.driver = new firefox.Driver(options); + driver = env.builder(). + setFirefoxOptions(options). + build(); + driver.get('data:text/html,
            content
            '); assert(driver.findElement({id: 'sample-extension-footer'}).getText()) .equalTo('Goodbye'); @@ -83,7 +101,9 @@ test.suite(function(env) { var options = new firefox.Options().setProfile(profile); - var driver = env.driver = new firefox.Driver(options); + driver = env.builder(). + setFirefoxOptions(options). + build(); loadJetpackPage(driver, 'data:text/html;charset=UTF-8,
            content
            '); @@ -103,5 +123,37 @@ test.suite(function(env) { }, 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(); + }); + }); + }); }); }, {browsers: ['firefox']}); diff --git a/test/firefox/profile_test.js b/test/firefox/profile_test.js index 588bdad..feaa42f 100644 --- a/test/firefox/profile_test.js +++ b/test/firefox/profile_test.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/test/http/http_test.js b/test/http/http_test.js index 9f12ac4..358dad5 100644 --- a/test/http/http_test.js +++ b/test/http/http_test.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. var assert = require('assert'); var http = require('http'); @@ -23,6 +25,8 @@ 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); @@ -40,14 +44,27 @@ describe('HttpClient', function() { res.writeHead(303, {}); res.end(); + } 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(server.start.bind(server)); - test.after(server.stop.bind(server)); + 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'); @@ -83,10 +100,33 @@ describe('HttpClient', function() { 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) { + 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 4791fb1..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'; @@ -20,7 +22,7 @@ var assert = require('assert'), var util = require('../../http/util'); -describe('browserstack-webdriver/http/util', function() { +describe('selenium-webdriver/http/util', function() { var server, baseUrl; @@ -125,12 +127,12 @@ describe('browserstack-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('browserstack-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 index 0066ffb..2fe4475 100644 --- a/test/io_test.js +++ b/test/io_test.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'; @@ -174,4 +176,52 @@ describe('io', function() { }); }); }); + + 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 index 154bfe5..fb8cdb2 100644 --- a/test/logging_test.js +++ b/test/logging_test.js @@ -1,43 +1,54 @@ -// 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'; -var By = require('..').By, +var Browser = require('..').Browser, + By = require('..').By, logging = require('..').logging, assert = require('../testing/assert'), test = require('../lib/test'); test.suite(function(env) { - env.autoCreateDriver = false; - // 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 - test.ignore(env.browsers(test.Browser.PHANTOMJS)). + // 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() { - env.dispose(); + if (driver) { + driver.quit(); + } }); test.it('can be disabled', function() { var prefs = new logging.Preferences(); prefs.setLevel(logging.Type.BROWSER, logging.Level.OFF); - var driver = env.builder() + driver = env.builder() .setLoggingPrefs(prefs) .build(); @@ -53,12 +64,12 @@ test.suite(function(env) { }); // Firefox does not capture JS error console log messages. - test.ignore(env.browsers(test.Browser.FIREFOX)). + 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); - var driver = env.builder() + driver = env.builder() .setLoggingPrefs(prefs) .build(); @@ -76,12 +87,12 @@ test.suite(function(env) { }); // Firefox does not capture JS error console log messages. - test.ignore(env.browsers(test.Browser.FIREFOX)). + 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); - var driver = env.builder() + driver = env.builder() .setLoggingPrefs(prefs) .build(); @@ -105,12 +116,12 @@ test.suite(function(env) { }); // Firefox does not capture JS error console log messages. - test.ignore(env.browsers(test.Browser.FIREFOX)). + 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); - var driver = env.builder() + driver = env.builder() .setLoggingPrefs(prefs) .build(); @@ -133,7 +144,7 @@ test.suite(function(env) { prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG); prefs.setLevel(logging.Type.DRIVER, logging.Level.SEVERE); - var driver = env.builder() + driver = env.builder() .setLoggingPrefs(prefs) .build(); 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 cd747a4..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() { 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..b641296 --- /dev/null +++ b/test/promise_aplus_test.js @@ -0,0 +1,46 @@ +// 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'); + + var nullFunction = function() {}; + + before(function() { + promise.controlFlow().on('uncaughtException', nullFunction); + }); + + after(function() { + promise.controlFlow().removeListener('uncaughtException', nullFunction); + }); + + require('promises-aplus-tests').mocha({ + resolved: promise.fulfilled, + rejected: promise.rejected, + deferred: function() { + var d = promise.defer(); + return { + resolve: d.fulfill, + reject: d.reject, + promise: d.promise + }; + } + }); + +}); \ No newline at end of file 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 c8e1ea8..7a4b265 100644 --- a/test/testing/index_test.js +++ b/test/testing/index_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'; @@ -40,49 +42,139 @@ describe('Mocha Integration', function() { afterEach(function() { assert.equal(this.x, 2); }); }); - describe('it properly allows timeouts and cancels control flow', function() { - var timeoutErr, flowReset; + 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().on(promise.ControlFlow.EventType.RESET, function() { - flowReset = true; + beforeEach(function() { + flowReset = false; + promise.controlFlow().once( + promise.ControlFlow.EventType.RESET, onreset); + }); + + test.it('', function() { + this.timeout(100); + return promise.delayed(50); }); - }); - test.it('', function() { - var mochaCallback = this.runnable().callback.mochaCallback; - this.runnable().callback.mochaCallback = function(err) { - timeoutErr = err; - // We do not pass along the arguments because we want this it block - // to always pass, we apply the tests that ensure the timeout was - // successfully called and that the controlFlow promise were reset - // in the afterEach block. - return mochaCallback.apply(this); - }; - - this.timeout(1000); - var unresolvedPromise = promise.defer(); - return unresolvedPromise.promise; + afterEach(function() { + assert.ok(!flowReset); + promise.controlFlow().removeListener( + promise.ControlFlow.EventType.RESET, onreset); + }); + + function onreset() { + flowReset = true; + } }); - afterEach(function() { - assert.equal( - flowReset, - true, - 'the controlFlow for the test block should be cancelled on timeout' - ); - - assert.equal( - timeoutErr instanceof Error, - true, - 'the testing error should be propegated back up to the mocha test runner' - ); - - assert.equal( - timeoutErr.message, - 'timeout of 1000ms exceeded' - ); + 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 3cc3082..2a1c4cf 100644 --- a/testing/index.js +++ b/testing/index.js @@ -1,83 +1,69 @@ -// 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): + * + * - after + * - afterEach + * - before + * - beforeEach + * - it + * - it.only + * - it.skip + * - xit * - *

            The provided wrappers leverage the {@link webdriver.promise.ControlFlow} + * The provided wrappers leverage the {@link webdriver.promise.ControlFlow} * to simplify writing asynchronous tests: - *

            
            - * var webdriver = require('browserstack-webdriver'),
            - *     portprober = require('browserstack-webdriver/net/portprober'),
            - *     remote = require('browserstack-webdriver/remote'),
            - *     test = require('browserstack-webdriver/testing');
              *
            - * test.describe('Google Search', function() {
            - *   var driver, server;
            + *     var By = require('selenium-webdriver').By,
            + *         until = require('selenium-webdriver').until,
            + *         firefox = require('selenium-webdriver/firefox'),
            + *         test = require('selenium-webdriver/testing');
              *
            - *   test.before(function() {
            - *     server = new remote.SeleniumServer(
            - *         'path/to/selenium-server-standalone.jar',
            - *         {port: portprober.findFreePort()});
            - *     server.start();
            + *     test.describe('Google Search', function() {
            + *       var driver;
              *
            - *     driver = new webdriver.Builder().
            - *         withCapabilities({'browserName': 'firefox'}).
            - *         usingServer(server.address()).
            - *         build();
            - *   });
            + *       test.before(function() {
            + *         driver = new firefox.Driver();
            + *       });
              *
            - *   test.after(function() {
            - *     driver.quit();
            - *     server.stop();
            - *   });
            + *       test.after(function() {
            + *         driver.quit();
            + *       });
              *
            - *   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.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);
              *       });
            - *     }, 1000, 'Waiting for title to update');
            - *   });
            - * });
            - * 
            + * }); * - *

            You may conditionally suppress a test function using the exported + * 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 promise = require('..').promise; @@ -106,42 +92,66 @@ function seal(fn) { function wrapped(globalFn) { return function() { if (arguments.length === 1) { - return globalFn(asyncTestFn(arguments[0])); + return globalFn(makeAsyncTestFn(arguments[0])); } else if (arguments.length === 2) { - return globalFn(arguments[0], asyncTestFn(arguments[1])); + return globalFn(arguments[0], makeAsyncTestFn(arguments[1])); } else { throw Error('Invalid # arguments: ' + arguments.length); } }; +} - function asyncTestFn(fn) { - var ret = function(done) { - function cleanupBeforeCallback() { - flow.reset(); - return cleanupBeforeCallback.mochaCallback.apply(this, arguments); - } - // We set this as an attribute of the callback function to allow us to - // test this properly. - cleanupBeforeCallback.mochaCallback = this.runnable().callback; - - this.runnable().callback = cleanupBeforeCallback; - - var testFn = fn.bind(this); - flow.execute(function() { - var done = promise.defer(); - promise.asap(testFn(done.reject), done.fulfill, done.reject); - return done.promise; - }).then(seal(done), done); - }; +/** + * 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" - ret.toString = function() { - return fn.toString(); + var ret = function(done) { + var runnable = this.runnable(); + var mochaCallback = runnable.callback; + runnable.callback = function() { + flow.reset(); + return mochaCallback.apply(this, arguments); }; - return ret; - } + 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; }